From 50cdbaf04134b9ed4ae2a030f9e965fd95577b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 17 Nov 2021 13:22:15 +0100 Subject: [PATCH] crypto: Update to the latest rust-sdk version --- .../android/sdk/internal/crypto/OlmMachine.kt | 8 +- .../crypto/keysbackup/RustKeyBackupService.kt | 15 ++-- rust-sdk/Cargo.toml | 4 +- rust-sdk/src/backup_recovery_key.rs | 23 ++++-- rust-sdk/src/error.rs | 4 +- rust-sdk/src/lib.rs | 14 ++-- rust-sdk/src/machine.rs | 77 +++++++++++++------ rust-sdk/src/olm.udl | 13 ++-- rust-sdk/src/verification.rs | 4 +- 9 files changed, 105 insertions(+), 57 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt index c2001931d6..ee83f7f602 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt @@ -47,7 +47,6 @@ import org.matrix.android.sdk.internal.session.sync.model.DeviceListResponse import org.matrix.android.sdk.internal.session.sync.model.DeviceOneTimeKeysCountSyncResponse import org.matrix.android.sdk.internal.session.sync.model.ToDeviceSyncResponse import timber.log.Timber -import uniffi.olm.BackupKey import uniffi.olm.BackupKeys import uniffi.olm.CrossSigningKeyExport import uniffi.olm.CrossSigningStatus @@ -56,6 +55,7 @@ import uniffi.olm.DecryptionException import uniffi.olm.DeviceLists import uniffi.olm.KeyRequestPair import uniffi.olm.Logger +import uniffi.olm.MegolmV1BackupKey import uniffi.olm.Request import uniffi.olm.RequestType import uniffi.olm.RoomKeyCounts @@ -790,10 +790,10 @@ internal class OlmMachine( } @Throws(CryptoStoreException::class) - suspend fun enableBackup(key: String, version: String) { + suspend fun enableBackupV1(key: String, version: String) { return withContext(Dispatchers.Default) { - val backupKey = BackupKey(key, mapOf(), null) - inner.enableBackup(backupKey, version) + val backupKey = MegolmV1BackupKey(key, mapOf(), null, MXCRYPTO_ALGORITHM_MEGOLM_BACKUP) + inner.enableBackupV1(backupKey, version) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/RustKeyBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/RustKeyBackupService.kt index f378fae189..af5c0a4d41 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/RustKeyBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/RustKeyBackupService.kt @@ -125,7 +125,7 @@ internal class RustKeyBackupService @Inject constructor( BackupRecoveryKey() } - val publicKey = key.publicKey() + val publicKey = key.megolmV1PublicKey() val backupAuthData = SignalableMegolmBackupAuthData( publicKey = publicKey.publicKey, privateKeySalt = publicKey.passphraseInfo?.privateKeySalt, @@ -144,7 +144,7 @@ internal class RustKeyBackupService @Inject constructor( ) MegolmBackupCreationInfo( - algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP, + algorithm = publicKey.backupAlgorithm, authData = signedMegolmBackupAuthData, recoveryKey = key.toBase58() ) @@ -264,7 +264,8 @@ internal class RustKeyBackupService @Inject constructor( override fun backupAllGroupSessions(progressListener: ProgressListener?, callback: MatrixCallback?) { - // This is only used in tests? + // This is only used in tests? While it's fine have methods that are + // only used for tests, this one has a lot of logic that is nowhere else used. TODO() } @@ -354,7 +355,7 @@ internal class RustKeyBackupService @Inject constructor( // Check that the recovery key matches to the public key that we downloaded from the server. // If they match, we can trust the public key and enable backups since we have the private key. private fun checkRecoveryKey(recoveryKey: BackupRecoveryKey, keysBackupData: KeysVersionResult) { - val backupKey = recoveryKey.publicKey() + val backupKey = recoveryKey.megolmV1PublicKey() val authData = getMegolmBackupAuthData(keysBackupData) when { @@ -474,7 +475,7 @@ internal class RustKeyBackupService @Inject constructor( if (ciphertext != null && mac != null && ephemeralKey != null) { try { - val decrypted = key.decrypt(ephemeralKey, mac, ciphertext) + val decrypted = key.decryptV1(ephemeralKey, mac, ciphertext) val moshi = MoshiProvider.providesMoshi() val adapter = moshi.adapter(MegolmSessionData::class.java) @@ -729,7 +730,7 @@ internal class RustKeyBackupService @Inject constructor( } private fun isValidRecoveryKey(recoveryKey: BackupRecoveryKey, version: KeysVersionResult): Boolean { - val publicKey = recoveryKey.publicKey().publicKey + val publicKey = recoveryKey.megolmV1PublicKey().publicKey val authData = getMegolmBackupAuthData(version) ?: return false return authData.publicKey == publicKey } @@ -800,7 +801,7 @@ internal class RustKeyBackupService @Inject constructor( if (retrievedMegolmBackupAuthData != null) { try { - olmMachine.enableBackup(retrievedMegolmBackupAuthData.publicKey, keysVersionResult.version) + olmMachine.enableBackupV1(retrievedMegolmBackupAuthData.publicKey, keysVersionResult.version) keysBackupVersion = keysVersionResult } catch (e: OlmException) { Timber.e(e, "OlmException") diff --git a/rust-sdk/Cargo.toml b/rust-sdk/Cargo.toml index 014ec17454..e1f5b84001 100644 --- a/rust-sdk/Cargo.toml +++ b/rust-sdk/Cargo.toml @@ -29,11 +29,11 @@ features = ["lax_deserialize"] [dependencies.matrix-sdk-common] git = "https://github.com/matrix-org/matrix-rust-sdk/" -rev = "1943840b82fd323455d9ca9ce27243d37a660569" +rev = "2e4d5f25cba03bd26415b91defd6e762e8c31b63" [dependencies.matrix-sdk-crypto] git = "https://github.com/matrix-org/matrix-rust-sdk/" -rev = "1943840b82fd323455d9ca9ce27243d37a660569" +rev = "2e4d5f25cba03bd26415b91defd6e762e8c31b63" features = ["sled_cryptostore", "qrcode", "backups_v1"] [dependencies.tokio] diff --git a/rust-sdk/src/backup_recovery_key.rs b/rust-sdk/src/backup_recovery_key.rs index 67b9c16290..4a867294cf 100644 --- a/rust-sdk/src/backup_recovery_key.rs +++ b/rust-sdk/src/backup_recovery_key.rs @@ -5,7 +5,10 @@ use sha2::Sha512; use std::{collections::HashMap, iter}; use thiserror::Error; -use matrix_sdk_crypto::backups::{OlmPkDecryptionError, RecoveryKey}; +use matrix_sdk_crypto::{ + backups::{OlmPkDecryptionError, RecoveryKey}, + store::CryptoStoreError as InnerStoreError, +}; /// The private part of the backup key, the one used for recovery. pub struct BackupRecoveryKey { @@ -26,6 +29,9 @@ pub enum DecodeError { /// An error happened while decoding the recovery key. #[error(transparent)] Decode(#[from] matrix_sdk_crypto::backups::DecodeError), + /// An error happened in the storage layer, + #[error(transparent)] + CryptoStore(#[from] InnerStoreError), } /// Struct containing info about the way the backup key got derived from a @@ -39,13 +45,15 @@ pub struct PassphraseInfo { } /// The public part of the backup key. -pub struct BackupKey { +pub struct MegolmV1BackupKey { /// The actuall base64 encoded public key. pub public_key: String, /// Signatures that have signed our backup key. pub signatures: HashMap>, /// The passphrase info, if the key was derived from one. pub passphrase_info: Option, + /// Get the full name of the backup algorithm this backup key supports. + pub backup_algorithm: String, } impl BackupRecoveryKey { @@ -107,8 +115,8 @@ impl BackupRecoveryKey { } /// Get the public part of the backup key. - pub fn public_key(&self) -> BackupKey { - let public_key = self.inner.public_key(); + pub fn megolm_v1_public_key(&self) -> MegolmV1BackupKey { + let public_key = self.inner.megolm_v1_public_key(); let signatures: HashMap> = public_key .signatures() @@ -121,10 +129,11 @@ impl BackupRecoveryKey { }) .collect(); - BackupKey { + MegolmV1BackupKey { public_key: public_key.to_base64(), signatures, passphrase_info: self.passphrase_info.clone(), + backup_algorithm: public_key.backup_algorithm().to_owned(), } } @@ -140,14 +149,14 @@ impl BackupRecoveryKey { /// Try to decrypt a message that was encrypted using the public part of the /// backup key. - pub fn decrypt( + pub fn decrypt_v1( &self, ephemeral_key: String, mac: String, ciphertext: String, ) -> Result { self.inner - .decrypt(ephemeral_key, mac, ciphertext) + .decrypt_v1(ephemeral_key, mac, ciphertext) .map_err(|e| e.into()) } } diff --git a/rust-sdk/src/error.rs b/rust-sdk/src/error.rs index dff404f456..caa7c44983 100644 --- a/rust-sdk/src/error.rs +++ b/rust-sdk/src/error.rs @@ -2,7 +2,7 @@ use matrix_sdk_crypto::{ store::CryptoStoreError as InnerStoreError, KeyExportError, MegolmError, OlmError, - SignatureError as InnerSignatureError, SecretImportError as RustSecretImportError, + SecretImportError as RustSecretImportError, SignatureError as InnerSignatureError, }; use ruma::identifiers::Error as RumaIdentifierError; @@ -12,6 +12,8 @@ pub enum KeyImportError { Export(#[from] KeyExportError), #[error(transparent)] CryptoStore(#[from] InnerStoreError), + #[error(transparent)] + Json(#[from] serde_json::Error), } #[derive(Debug, thiserror::Error)] diff --git a/rust-sdk/src/lib.rs b/rust-sdk/src/lib.rs index 0add76b18c..35f62fca4e 100644 --- a/rust-sdk/src/lib.rs +++ b/rust-sdk/src/lib.rs @@ -18,7 +18,7 @@ mod users; mod verification; pub use backup_recovery_key::{ - BackupKey, BackupRecoveryKey, DecodeError, PassphraseInfo, PkDecryptionError, + BackupRecoveryKey, DecodeError, MegolmV1BackupKey, PassphraseInfo, PkDecryptionError, }; pub use device::Device; pub use error::{ @@ -87,19 +87,19 @@ pub struct CrossSigningKeyExport { pub user_signing_key: Option, } -/// TODO +/// Struct holding the number of room keys we have. pub struct RoomKeyCounts { - /// TODO + /// The total number of room keys. pub total: i64, - /// TODO + /// The number of backed up room keys. pub backed_up: i64, } -/// TODO +/// Backup keys and information we load from the store. pub struct BackupKeys { - /// TODO + /// The recovery key as a base64 encoded string. pub recovery_key: String, - /// TODO + /// The version that is used with the recovery key. pub backup_version: String, } diff --git a/rust-sdk/src/machine.rs b/rust-sdk/src/machine.rs index f3859cffdd..7581c2b4e7 100644 --- a/rust-sdk/src/machine.rs +++ b/rust-sdk/src/machine.rs @@ -32,7 +32,7 @@ use tokio::runtime::Runtime; use matrix_sdk_common::{deserialized_responses::AlgorithmInfo, uuid::Uuid}; use matrix_sdk_crypto::{ - backups::{MegolmV1BackupKey, RecoveryKey}, + backups::{MegolmV1BackupKey as RustBackupKey, RecoveryKey}, decrypt_key_export, encrypt_key_export, matrix_qrcode::QrVerificationData, olm::ExportedRoomKey, @@ -43,11 +43,11 @@ use matrix_sdk_crypto::{ use crate::{ error::{CryptoStoreError, DecryptionError, SecretImportError, SignatureError}, responses::{response_from_string, OutgoingVerificationRequest, OwnedResponse}, - BackupKey, BackupKeys, BootstrapCrossSigningResult, ConfirmVerificationResult, - CrossSigningKeyExport, CrossSigningStatus, DecryptedEvent, Device, DeviceLists, KeyImportError, - KeysImportResult, ProgressListener, QrCode, Request, RequestType, RequestVerificationResult, - RoomKeyCounts, ScanResult, SignatureUploadRequest, StartSasResult, UserIdentity, Verification, - VerificationRequest, + BackupKeys, BootstrapCrossSigningResult, ConfirmVerificationResult, CrossSigningKeyExport, + CrossSigningStatus, DecodeError, DecryptedEvent, Device, DeviceLists, KeyImportError, + KeysImportResult, MegolmV1BackupKey, ProgressListener, QrCode, Request, RequestType, + RequestVerificationResult, RoomKeyCounts, ScanResult, SignatureUploadRequest, StartSasResult, + UserIdentity, Verification, VerificationRequest, }; /// A high level state machine that handles E2EE for Matrix. @@ -79,7 +79,7 @@ impl OlmMachine { pub fn new(user_id: &str, device_id: &str, path: &str) -> Result { let user_id = UserId::try_from(user_id)?; let device_id = device_id.into(); - let runtime = Runtime::new().unwrap(); + let runtime = Runtime::new().expect("Couldn't create a tokio runtime"); Ok(OlmMachine { inner: runtime.block_on(InnerMachine::new_with_default_store( @@ -499,7 +499,7 @@ impl OlmMachine { let encrypted_content = self .runtime .block_on(self.inner.encrypt(&room_id, content)) - .unwrap(); + .expect("Encrypting an event produced an error"); Ok(serde_json::to_string(&encrypted_content)?) } @@ -644,13 +644,24 @@ impl OlmMachine { self.impor_keys_helper(keys, progress_listener) } - /// TODO + /// Import room keys from the given serialized unencrypted key export. + /// + /// This method is the same as [`OlmMachine::import_keys`] but the + /// decryption step is skipped and should be performed by the caller. This + /// may be useful for custom handling or for server-side key backups. + /// + /// # Arguments + /// + /// * `keys` - The serialized version of the unencrypted key export. + /// + /// * `progress_listener` - A callback that can be used to introspect the + /// progress of the key import. pub fn import_decrypted_keys( &self, keys: &str, progress_listener: Box, ) -> Result { - let keys: Vec = serde_json::from_str(keys).unwrap(); + let keys: Vec = serde_json::from_str(keys)?; self.impor_keys_helper(keys, progress_listener) } @@ -1263,30 +1274,47 @@ impl OlmMachine { Ok(()) } - /// TODO - pub fn enable_backup(&self, key: BackupKey, version: String) -> Result<(), CryptoStoreError> { - let backup_key = MegolmV1BackupKey::from_base64(&key.public_key).unwrap(); + /// Activate the given backup key to be used with the given backup version. + /// + /// **Warning**: The caller needs to make sure that the given `BackupKey` is + /// trusted, otherwise we might be encrypting room keys that a malicious + /// party could decrypt. + /// + /// The [`OlmMachine::verify_backup`] method can be used to so. + pub fn enable_backup_v1( + &self, + key: MegolmV1BackupKey, + version: String, + ) -> Result<(), DecodeError> { + let backup_key = RustBackupKey::from_base64(&key.public_key)?; backup_key.set_version(version); self.runtime - .block_on(self.inner.backup_machine().enable_backup(backup_key))?; + .block_on(self.inner.backup_machine().enable_backup_v1(backup_key))?; Ok(()) } - /// TODO + /// Are we able to encrypt room keys. + /// + /// This returns true if we have an active `BackupKey` and backup version + /// registered with the state machine. pub fn backup_enabled(&self) -> bool { self.runtime.block_on(self.inner.backup_machine().enabled()) } - /// TODO + /// Disable and reset our backup state. + /// + /// This will remove any pending backup request, remove the backup key and + /// reset the backup state of each room key we have. pub fn disable_backup(&self) -> Result<(), CryptoStoreError> { Ok(self .runtime .block_on(self.inner.backup_machine().disable_backup())?) } - /// TODO + /// Encrypt a batch of room keys and return a request that needs to be sent + /// out to backup the room keys. pub fn backup_room_keys(&self) -> Result, CryptoStoreError> { let request = self .runtime @@ -1297,7 +1325,7 @@ impl OlmMachine { Ok(request) } - /// TODO + /// Get the number of backed up room keys and the total number of room keys. pub fn room_key_counts(&self) -> Result { Ok(self .runtime @@ -1305,7 +1333,10 @@ impl OlmMachine { .into()) } - /// TODO + /// Store the recovery key in the cryptostore. + /// + /// This is useful if the client wants to support gossiping of the backup + /// key. pub fn save_recovery_key( &self, key: Option, @@ -1321,7 +1352,7 @@ impl OlmMachine { .block_on(self.inner.backup_machine().save_recovery_key(key, version))?) } - /// TODO + /// Get the backup keys we have saved in our crypto store. pub fn get_backup_keys(&self) -> Result, CryptoStoreError> { Ok(self .runtime @@ -1330,7 +1361,8 @@ impl OlmMachine { .ok()) } - /// TODO + /// Sign the given message using our device key and if available cross + /// signing master key. pub fn sign(&self, message: &str) -> HashMap> { self.runtime .block_on(self.inner.sign(message)) @@ -1344,7 +1376,8 @@ impl OlmMachine { .collect() } - /// TODO + /// Check if the given backup has been verified by us or by another of our + /// devices that we trust. pub fn verify_backup(&self, auth_data: &str) -> Result { let auth_data = serde_json::from_str(auth_data)?; Ok(self diff --git a/rust-sdk/src/olm.udl b/rust-sdk/src/olm.udl index 10ca65fc0c..2590afbed7 100644 --- a/rust-sdk/src/olm.udl +++ b/rust-sdk/src/olm.udl @@ -19,6 +19,7 @@ enum PkDecryptionError { enum KeyImportError { "Export", "CryptoStore", + "Json", }; [Error] @@ -357,8 +358,8 @@ interface OlmMachine { boolean is_identity_verified([ByRef] string user_id); record> sign([ByRef] string message); - [Throws=CryptoStoreError] - void enable_backup(BackupKey key, string version); + [Throws=DecodeError] + void enable_backup_v1(MegolmV1BackupKey key, string version); [Throws=CryptoStoreError] void disable_backup(); [Throws=CryptoStoreError] @@ -379,10 +380,11 @@ dictionary PassphraseInfo { i32 private_key_iterations; }; -dictionary BackupKey { +dictionary MegolmV1BackupKey { string public_key; record> signatures; PassphraseInfo? passphrase_info; + string backup_algorithm; }; dictionary BackupKeys { @@ -398,6 +400,7 @@ dictionary RoomKeyCounts { [Error] enum DecodeError { "Decode", + "CryptoStore", }; interface BackupRecoveryKey { @@ -412,7 +415,7 @@ interface BackupRecoveryKey { constructor(string key); string to_base58(); string to_base64(); - BackupKey public_key(); + MegolmV1BackupKey megolm_v1_public_key(); [Throws=PkDecryptionError] - string decrypt(string ephemeral_key, string mac, string ciphertext); + string decrypt_v1(string ephemeral_key, string mac, string ciphertext); }; diff --git a/rust-sdk/src/verification.rs b/rust-sdk/src/verification.rs index 39f3012622..13d7204d92 100644 --- a/rust-sdk/src/verification.rs +++ b/rust-sdk/src/verification.rs @@ -10,13 +10,13 @@ pub enum Verification { /// The `m.sas.v1` verification flow. SasV1 { #[allow(missing_docs)] - sas: Sas + sas: Sas, }, /// The `m.qr_code.scan.v1`, `m.qr_code.show.v1`, and `m.reciprocate.v1` /// verification flow. QrCodeV1 { #[allow(missing_docs)] - qrcode: QrCode + qrcode: QrCode, }, }