diff --git a/crates/matrix-crypto-ffi/Cargo.toml b/crates/matrix-crypto-ffi/Cargo.toml index dabe70d45..e71c4012f 100644 --- a/crates/matrix-crypto-ffi/Cargo.toml +++ b/crates/matrix-crypto-ffi/Cargo.toml @@ -22,7 +22,7 @@ thiserror = "1.0.25" tracing = "0.1.26" tracing-subscriber = "0.2.18" uniffi = "0.17.0" -zeroize = "1.3.0" +zeroize = { version = "1.3.0", features = ["zeroize_derive"] } [dependencies.js_int] version = "0.2.1" @@ -40,6 +40,10 @@ path = "../matrix-sdk-sled" default_features = false features = ["crypto-store"] +[dependencies.vodozemac] +git = "https://github.com/matrix-org/vodozemac" +rev = "e09c93f2c8df9770793abeec57ed984d5e1f3834" + [dependencies.tokio] version = "1.7.1" default_features = false @@ -52,3 +56,6 @@ features = ["client-api-c"] [build-dependencies] uniffi_build = { version = "0.17.0", features = ["builtin-bindgen"] } + +[dev-dependencies] +tempfile = "3.3.0" diff --git a/crates/matrix-crypto-ffi/src/lib.rs b/crates/matrix-crypto-ffi/src/lib.rs index 562e27b8b..39930ae62 100644 --- a/crates/matrix-crypto-ffi/src/lib.rs +++ b/crates/matrix-crypto-ffi/src/lib.rs @@ -1,10 +1,10 @@ -#![warn(missing_docs)] - //! Uniffi based bindings for the `matrix-sdk-crypto` crate. //! //! This crate can be used to introduce E2EE support into an existing Matrix //! client or client library in any of the language targets Uniffi supports. +#![warn(missing_docs)] + mod backup_recovery_key; mod device; mod error; @@ -14,7 +14,7 @@ mod responses; mod users; mod verification; -use std::convert::TryFrom; +use std::{collections::HashMap, convert::TryFrom, str::FromStr, sync::Arc}; pub use backup_recovery_key::{ BackupRecoveryKey, DecodeError, MegolmV1BackupKey, PassphraseInfo, PkDecryptionError, @@ -23,18 +23,251 @@ pub use device::Device; pub use error::{ CryptoStoreError, DecryptionError, KeyImportError, SecretImportError, SignatureError, }; +use js_int::UInt; pub use logger::{set_logger, Logger}; pub use machine::{KeyRequestPair, OlmMachine}; pub use responses::{ BootstrapCrossSigningResult, DeviceLists, KeysImportResult, OutgoingVerificationRequest, Request, RequestType, SignatureUploadRequest, UploadSigningKeysRequest, }; +use ruma::{DeviceId, DeviceKeyAlgorithm, RoomId, SecondsSinceUnixEpoch, UserId}; +use serde::{Deserialize, Serialize}; pub use users::UserIdentity; pub use verification::{ CancelInfo, ConfirmVerificationResult, QrCode, RequestVerificationResult, Sas, ScanResult, StartSasResult, Verification, VerificationRequest, }; +/// Struct collecting data that is important to migrate to the rust-sdk +#[derive(Deserialize, Serialize)] +pub struct MigrationData { + /// The pickled version of the Olm Account + account: PickledAccount, + /// The list of pickleds Olm Sessions. + sessions: Vec, + /// The list of Megolm inbound group sessions. + inbound_group_sessions: Vec, + /// The Olm pickle key that was used to pickle all the Olm objects. + pickle_key: String, + /// The backup version that is currently active. + backup_version: Option, + // The backup recovery key, as a base58 encoded string. + backup_recovery_key: Option, + /// The private cross signing keys. + cross_signing: CrossSigningKeyExport, + /// The list of users that the Rust SDK should track. + tracked_users: Vec, +} + +/// A pickled version of an `Account`. +/// +/// Holds all the information that needs to be stored in a database to restore +/// an account. +#[derive(Debug, Deserialize, Serialize)] +pub struct PickledAccount { + /// The user id of the account owner. + pub user_id: String, + /// The device id of the account owner. + pub device_id: String, + /// The pickled version of the Olm account. + pub pickle: String, + /// Was the account shared. + pub shared: bool, + /// The number of uploaded one-time keys we have on the server. + pub uploaded_signed_key_count: i64, +} + +/// A pickled version of a `Session`. +/// +/// Holds all the information that needs to be stored in a database to restore +/// a Session. +#[derive(Debug, Deserialize, Serialize)] +pub struct PickledSession { + /// The pickle string holding the Olm Session. + pub pickle: String, + /// The curve25519 key of the other user that we share this session with. + pub sender_key: String, + /// Was the session created using a fallback key. + pub created_using_fallback_key: bool, + /// The Unix timestamp when the session was created. + pub creation_time: String, + /// The Unix timestamp when the session was last used. + pub last_use_time: String, +} + +/// A pickled version of an `InboundGroupSession`. +/// +/// Holds all the information that needs to be stored in a database to restore +/// an InboundGroupSession. +#[derive(Debug, Deserialize, Serialize)] +pub struct PickledInboundGroupSession { + /// The pickle string holding the InboundGroupSession. + pub pickle: String, + /// The public curve25519 key of the account that sent us the session + pub sender_key: String, + /// The public ed25519 key of the account that sent us the session. + pub signing_key: HashMap, + /// The id of the room that the session is used in. + pub room_id: String, + /// The list of claimed ed25519 that forwarded us this key. Will be empty if + /// we directly received this session. + pub forwarding_chains: Vec, + /// Flag remembering if the session was directly sent to us by the sender + /// or if it was imported. + pub imported: bool, + /// Flag remembering if the session has been backed up. + pub backed_up: bool, +} + +/// Error type for the migration process. +#[derive(thiserror::Error, Debug)] +pub enum MigrationError { + /// Generic catch all error variant. + #[error("error migrating database: {message}")] + Generic { + /// The error message + message: String, + }, +} + +impl From for MigrationError { + fn from(e: anyhow::Error) -> MigrationError { + MigrationError::Generic { message: e.to_string() } + } +} + +/// TODO +pub fn migrate( + mut data: MigrationData, + path: &str, + passphrase: Option, +) -> Result<(), anyhow::Error> { + use matrix_sdk_crypto::{ + olm::PrivateCrossSigningIdentity, + store::{Changes as RustChanges, CryptoStore, RecoveryKey}, + }; + use matrix_sdk_sled::CryptoStore as SledStore; + use tokio::runtime::Runtime; + use vodozemac::{ + megolm::InboundGroupSession, + olm::{Account, Session}, + Curve25519PublicKey, + }; + use zeroize::Zeroize; + + let store = SledStore::open_with_passphrase(path, passphrase.as_deref())?; + let runtime = Runtime::new()?; + + let user_id: Arc = parse_user_id(&data.account.user_id)?.into(); + let device_id: Box = data.account.device_id.into(); + let device_id: Arc = device_id.into(); + + let account = Account::from_libolm_pickle(&data.account.pickle, &data.pickle_key)?; + let pickle = account.pickle(); + + let identity_keys = Arc::new(account.identity_keys()); + + let pickled_account = matrix_sdk_crypto::olm::PickledAccount { + user_id: parse_user_id(&data.account.user_id)?, + device_id: device_id.as_ref().to_owned(), + pickle, + shared: data.account.shared, + uploaded_signed_key_count: data.account.uploaded_signed_key_count as u64, + }; + + let account = matrix_sdk_crypto::olm::ReadOnlyAccount::from_pickle(pickled_account)?; + + let mut sessions = Vec::new(); + + for session_pickle in data.sessions { + let pickle = + Session::from_libolm_pickle(&session_pickle.pickle, &data.pickle_key)?.pickle(); + + let creation_time = SecondsSinceUnixEpoch(UInt::from_str(&session_pickle.creation_time)?); + let last_use_time = SecondsSinceUnixEpoch(UInt::from_str(&session_pickle.last_use_time)?); + + let pickle = matrix_sdk_crypto::olm::PickledSession { + pickle, + sender_key: Curve25519PublicKey::from_base64(&session_pickle.sender_key)?, + created_using_fallback_key: session_pickle.created_using_fallback_key, + creation_time, + last_use_time, + }; + + let session = matrix_sdk_crypto::olm::Session::from_pickle( + user_id.clone(), + device_id.clone(), + identity_keys.clone(), + pickle, + ); + + sessions.push(session); + } + + let mut inbound_group_sessions = Vec::new(); + + for session in data.inbound_group_sessions { + let pickle = + InboundGroupSession::from_libolm_pickle(&session.pickle, &data.pickle_key)?.pickle(); + + let pickle = matrix_sdk_crypto::olm::PickledInboundGroupSession { + pickle, + sender_key: session.sender_key, + signing_key: session + .signing_key + .into_iter() + .map(|(k, v)| Ok((DeviceKeyAlgorithm::try_from(k)?, v))) + .collect::>()?, + room_id: RoomId::parse(session.room_id)?, + forwarding_chains: session.forwarding_chains, + imported: session.imported, + backed_up: session.backed_up, + history_visibility: None, + }; + + let session = matrix_sdk_crypto::olm::InboundGroupSession::from_pickle(pickle)?; + + inbound_group_sessions.push(session); + } + + let recovery_key = + data.backup_recovery_key.map(|k| RecoveryKey::from_base58(k.as_str())).transpose()?; + + let cross_signing = PrivateCrossSigningIdentity::empty((*user_id).into()); + runtime.block_on(cross_signing.import_secrets_unchecked( + data.cross_signing.master_key.as_deref(), + data.cross_signing.self_signing_key.as_deref(), + data.cross_signing.user_signing_key.as_deref(), + ))?; + + data.cross_signing.master_key.zeroize(); + data.cross_signing.self_signing_key.zeroize(); + data.cross_signing.user_signing_key.zeroize(); + + let tracked_users = data + .tracked_users + .into_iter() + .map(|u| Ok(((parse_user_id(&u)?), true))) + .collect::, bool)>, anyhow::Error>>()?; + + let tracked_users: Vec<(&UserId, bool)> = + tracked_users.iter().map(|(u, d)| (&**u, *d)).collect(); + + runtime.block_on(store.save_tracked_users(tracked_users.as_slice()))?; + + let changes = RustChanges { + account: Some(account), + private_identity: Some(cross_signing), + sessions, + inbound_group_sessions, + recovery_key, + backup_version: data.backup_version, + ..Default::default() + }; + + Ok(runtime.block_on(store.save_changes(changes))?) +} + /// Callback that will be passed over the FFI to report progress pub trait ProgressListener { /// The callback that should be called on the Rust side @@ -77,6 +310,7 @@ pub struct CrossSigningStatus { /// A struct containing private cross signing keys that can be backed up or /// uploaded to the secret store. +#[derive(Deserialize, Serialize)] pub struct CrossSigningKeyExport { /// The seed of the master key encoded as unpadded base64. pub master_key: Option, @@ -149,8 +383,8 @@ impl From for CrossSigningStatus { } } -fn parse_user_id(user_id: &str) -> Result, CryptoStoreError> { - ruma::UserId::parse(user_id).map_err(|e| CryptoStoreError::InvalidUserId(user_id.to_owned(), e)) +fn parse_user_id(user_id: &str) -> Result, CryptoStoreError> { + UserId::parse(user_id).map_err(|e| CryptoStoreError::InvalidUserId(user_id.to_owned(), e)) } #[allow(warnings)] @@ -160,3 +394,124 @@ mod generated { } pub use generated::*; + +#[cfg(test)] +mod test { + use anyhow::Result; + use serde_json::{json, Value}; + use tempfile::tempdir; + + use super::MigrationData; + use crate::{migrate, OlmMachine}; + + #[test] + fn android_migration() -> Result<()> { + let data: Value = json!({ + "account":{ + "user_id":"@ganfra146:matrix.org", + "device_id":"DEWRCMENGS", + "pickle":"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", + "shared":true, + "uploaded_signed_key_count":50 + + }, + "sessions":[ + { + "pickle":"cryZlFaQv0hwWe6tTgv75RExFKGnC8tMHBXJYMHOw4s+SdrKUYAMUdGcYD7QukrPklEOy7fJho9YGK/jV04QdA8JABiOfD+ngJTR4V8eZdmDuG08+Q5EL79V81hQwU2fKndP0y/9nAXPUIADYq0Zrg4EsOnXz7aE+hAeBAm0IBog1s8RYUvynZ15uwjbd/OTLP+gpqpX33DwVg2leiBkQetiUSpOpZCuQ8CcZwIA0MoGCqvaT7h76VHX9JxJx+2fCMhsJMx1nhd99WJH1W9ge5CtdbC4KUP92OSxIrPOnMrNcOPJPp/paZP+HFNQ3PDL+z8pGKXmCnrXGSbd7iPHurPYESrVkBzr", + "sender_key":"WJ6Ce7U67a6jqkHYHd8o0+5H4bqdi9hInZdk0+swuXs", + "created_using_fallback_key":false, + "creation_time":"1649425011424", + "last_use_time":"1649425011424" + }, + { + "pickle":"cryZlFaQv0hwWe6tTgv75RExFKGnC8tMHBXJYMHOw4t2W/lowyrV6SXVZp+uG59im0AAfNSKjhjZuiOpQlX7MS+AOJkCNvyujJ2g3KSjLZ94IkoHxkBDHLWSjwaLPu40rfOzJPDpm0XZsR6bQrsxKOmXLGEw2qw5jOTouzMVL2gvuuTix97nSYSU8j3XvTMRUoh0AF/tUpRLcvEFZeGrdUYmTMlyTv4na+FVUalUZ+jrk8t1/sM99JNq3SY1IBSjrBq/0rCOHieiippz0sw2fe2b87id4rqj1g3R9w2MWTWEdOz3ugjMGYF1YDBQZA1tJZ/hmgppk2AU2xKQXE2X3DgSC6fC66D4", + "sender_key":"RzRROfmHNlBfzxnNCUYBfn/5oZNQ11XYjDg59hS+mV0", + "created_using_fallback_key":false, + "creation_time":"1649425011503", + "last_use_time":"1649425011503" + }, + { + "pickle":"cryZlFaQv0hwWe6tTgv75RExFKGnC8tMHBXJYMHOw4titbL3SS12PYHpcBPJc6hXnOnZXqrjtjYOD545fck+3utEo8cqqwWubc9tsvxGW3tOWPttLBdAW30Vn8V1M8ebqVCNVWEAb1GKjV4ni8xG7G9SlEcCjLjnF4lJpddSZkqVMFoN0ITr9aSz/eJwXpc3HLreUFXwc8LuQp7krQ4Vt1e5EE/klduqsdurZf5V14RHsmWz2lKjt7nVgtIz/dhtF5F/sGJdg8kCGaHIMSbGAPuPPpa4/Laicb/5otrYt4pg4W4KdFpSGJIcvUQNjXaOZMx3cu/RPJIOyNhx7whG1QiYAUBqAJvr", + "sender_key":"IXSZugAHig1v8MowE1jxi2wDDDfuZBeJynHlegJVwUc", + "created_using_fallback_key":false, + "creation_time":"1649425011566", + "last_use_time":"1649425011566" + }, + { + "pickle":"SmkDiFZjNukiarQ7XHQo25FILHsuhNOnxy56cMSQU/Y71jaGbJes4YrvN4Dfy4RSONfejEDXDkbW2JudlHHRP/rWEmnfJiGbK6ArbrG2puqIZgOecPnOUgPfCisr49p1Gmf36dPaO5lm/ZSrngfSoxahoeJJE/CcJN98sYM15XytRk2LBwc+CyYDqr4V1qxfsBt6tzJ4+tsAZeRdD0UtipQgysgH56o8N7nKTCkaZz5lfpYCl3FEgwXpLJ0MGQvtQmbORFvOLqR1jZ/EbmNGKiqDDIYsqG0sf78ii1jqfpLDBXLuYDccsg", + "sender_key":"EB9SC4jVAydKhM6/GcwMc9biKwVNywqW3TerNTrtb1M", + "created_using_fallback_key":false, + "creation_time":"1649542063182", + "last_use_time":"1649542063182" + } + ], + "inbound_group_sessions":[ + { + "pickle":"KoA0sxDNQ7lz0vylU9zlmar0VCVQRMCfRrIfTh1bdMhlAgy8/D2ToT+oKRaKy1HiW6H862bzdpgxprlseSjmip9OfLbIuyd2YZcEinwc2666oEI/hpk4oTlE61uE1M+ThfdFf41yGCmaAP7mhjwF234ZrZ6i/F/qx42TLQ8Unc30wDJaJgyheO5eW85SD/0g0cdg2WnEKrx2/wl7Vg/YijT3JMDZ+OsdfJfSZtxBNjlG+PQ/9D31qb1eHfaovc8vFZh5QLfAFg/5rBrF1PhRsC7xOAZbUNdrTbvypNfMM4mznf84C2SzZRSMeAfg5v/YticM3Keg4eHuEj1WO9DrmRXYl6b/pITdf1xuk5euVT0pyxJpXmq41AoAZKAo1l94HGy1LG1RpruD1uQPhiponh5PGHSOf43Q", + "sender_key":"vJfH7wiYmGos3C8U1XcJ//YWSmkueAYqrmUA6/ukfAM", + "signing_key":{ + "ed25519":"JGgPQRuYj3ScMdPS+A0P+k/1qS9Hr3qeKXLscI+hS78" + }, + "room_id":"!AZkqtjvtwPAuyNOXEt:matrix.org", + "forwarding_chains":[ + ], + "imported":true, + "backed_up":true + }, + { + "pickle":"9RF6GBu9CvjZZx2hxIlw2gMdKs36LFhXhLTHAPrLSjT2OTbeE/jK263+iiFdSpF7Cblp/lXzljPKJN6sL8JGzoT7ssYh56nI0kKsp7/y88z+tTOH/5NYYTmZzHYw6yy4Cmaxh0pdHDs+RQpSSIe9jhF/EJJna5jcKYXxDY52m8H4LECQzVuDlYfblCr9zoYWhQrVhiRDGy7eLhk4X6Rp0Yoek4YUKcCQArDfZ/Vf43qfHUpOJgRpm5Oyj42HA/j4xZBb5U0Fmo6YHRPt0/KuWrDfpgJSGiN0zza7641IfADg8f3WdhlPAWMyri7k4vOZMBjlwFNcMpc0wM2TaTmbi2zqXEKZy9Oh/eJqBapFx0oNWaQ1VQ++iXxGUbZhwy7x2vd6UkqUTwYeym+aP23ee3TCtnNWN0aC", + "sender_key":"EB9SC4jVAydKhM6/GcwMc9biKwVNywqW3TerNTrtb1M", + "signing_key":{ + "ed25519":"1NXa5GyJ+p2ruAClEque+TL1VktrBzMW4dZFNfNGrvc" + }, + "room_id":"!CWLUCoEWXSFyTCOtfL:matrix.org", + "forwarding_chains":[], + "imported":true, + "backed_up":true + } + ], + "pickle_key":"\u{0011}$xJ_N8$>{\u{0005}iJoF03eBVt\u{000e}rUU\\,GYc7J", + "backup_version":"3", + "backup_recovery_key":"EsTHScmRV5oT1WBhe2mj2Gn3odeYantZ4NEk7L51p6L8hrmB", + "cross_signing":{ + "master_key":"trnK/dBv/M2x2zZt8lnORHQqmFHWvjYE6rdlAONRUPY", + "self_signing_key":"SJhsj9jXC4hxhqS/1B3RZ65zWMHuF+1fUjWHrzVRh6w", + "user_signing_key":"LPYrV11T9Prm4ZIUxrq2a8Y/F64R1+NaGNyo6GlXjGg" + }, + "tracked_users":[ + "@ganfra146:matrix.org", + "@this-is-me:matrix.org", + "@Amandine:matrix.org", + "@ganfra:matrix.org" + ] + }); + + let migration_data: MigrationData = serde_json::from_value(data)?; + + let dir = tempdir()?; + let path = + dir.path().to_str().expect("Creating a string from the tempdir path should not fail"); + + migrate(migration_data, path, None)?; + + let machine = OlmMachine::new("@ganfra146:matrix.org", "DEWRCMENGS", path)?; + + assert_eq!( + machine.identity_keys()["ed25519"], + "JGgPQRuYj3ScMdPS+A0P+k/1qS9Hr3qeKXLscI+hS78" + ); + + let room_keys = machine.runtime.block_on(machine.inner.export_keys(|_| true))?; + assert_eq!(room_keys.len(), 2); + + let cross_signing_status = machine.cross_signing_status(); + assert!(cross_signing_status.has_master); + assert!(cross_signing_status.has_user_signing); + assert!(cross_signing_status.has_self_signing); + + let backup_keys = machine.get_backup_keys()?; + assert!(backup_keys.is_some()); + + Ok(()) + } +} diff --git a/crates/matrix-crypto-ffi/src/olm.udl b/crates/matrix-crypto-ffi/src/olm.udl index ea16aa2d9..a20346150 100644 --- a/crates/matrix-crypto-ffi/src/olm.udl +++ b/crates/matrix-crypto-ffi/src/olm.udl @@ -1,5 +1,12 @@ namespace olm { void set_logger(Logger logger); + [Throws=MigrationError] + void migrate(MigrationData data, [ByRef] string path, string? passphrase); +}; + +[Error] +interface MigrationError { + Generic(string message); }; callback interface Logger { @@ -424,3 +431,40 @@ interface BackupRecoveryKey { [Throws=PkDecryptionError] string decrypt_v1(string ephemeral_key, string mac, string ciphertext); }; + +dictionary MigrationData { + PickledAccount account; + sequence sessions; + sequence inbound_group_sessions; + string? backup_version; + string? backup_recovery_key; + string pickle_key; + CrossSigningKeyExport cross_signing; + sequence tracked_users; +}; + +dictionary PickledAccount { + string user_id; + string device_id; + string pickle; + boolean shared; + i64 uploaded_signed_key_count; +}; + +dictionary PickledSession { + string pickle; + string sender_key; + boolean created_using_fallback_key; + string creation_time; + string last_use_time; +}; + +dictionary PickledInboundGroupSession { + string pickle; + string sender_key; + record signing_key; + string room_id; + sequence forwarding_chains; + boolean imported; + boolean backed_up; +};