From e121007d2002da016c099a6d0096fe8d455659f4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 28 Mar 2022 18:15:46 +0200 Subject: [PATCH] Remove rust dependencies and use published aar --- build.gradle | 9 + dependencies_groups.gradle | 17 +- matrix-sdk-android/build.gradle | 19 +- rust-sdk/Cargo.toml | 50 - rust-sdk/Makefile | 21 - rust-sdk/README.md | 96 -- rust-sdk/build.rs | 3 - rust-sdk/src/backup_recovery_key.rs | 162 --- rust-sdk/src/device.rs | 46 - rust-sdk/src/error.rs | 63 -- rust-sdk/src/lib.rs | 163 --- rust-sdk/src/logger.rs | 59 -- rust-sdk/src/machine.rs | 1430 --------------------------- rust-sdk/src/olm.udl | 426 -------- rust-sdk/src/responses.rs | 365 ------- rust-sdk/src/users.rs | 61 -- rust-sdk/src/verification.rs | 232 ----- 17 files changed, 26 insertions(+), 3196 deletions(-) delete mode 100644 rust-sdk/Cargo.toml delete mode 100644 rust-sdk/Makefile delete mode 100644 rust-sdk/README.md delete mode 100644 rust-sdk/build.rs delete mode 100644 rust-sdk/src/backup_recovery_key.rs delete mode 100644 rust-sdk/src/device.rs delete mode 100644 rust-sdk/src/error.rs delete mode 100644 rust-sdk/src/lib.rs delete mode 100644 rust-sdk/src/logger.rs delete mode 100644 rust-sdk/src/machine.rs delete mode 100644 rust-sdk/src/olm.udl delete mode 100644 rust-sdk/src/responses.rs delete mode 100644 rust-sdk/src/users.rs delete mode 100644 rust-sdk/src/verification.rs diff --git a/build.gradle b/build.gradle index 0055fa5c8f..89a2eed0bf 100644 --- a/build.gradle +++ b/build.gradle @@ -72,6 +72,15 @@ allprojects { groups.jcenter.group.each { includeGroup it } } } + + maven { + url 'https://s01.oss.sonatype.org/content/repositories/snapshots' + content { + groups.mavenSnapshots.regex.each { includeGroupByRegex it } + groups.mavenSnapshots.group.each { includeGroup it } + } + } + } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { diff --git a/dependencies_groups.gradle b/dependencies_groups.gradle index 723941157d..89e7ed7169 100644 --- a/dependencies_groups.gradle +++ b/dependencies_groups.gradle @@ -1,5 +1,5 @@ ext.groups = [ - jitpack : [ + jitpack : [ regex: [ ], group: [ @@ -13,7 +13,7 @@ ext.groups = [ 'com.github.Zhuinden', ] ], - jitsi : [ + jitsi : [ regex: [ ], group: [ @@ -22,7 +22,7 @@ ext.groups = [ 'org.webkit', ] ], - google : [ + google : [ regex: [ 'androidx\\..*', 'com\\.android\\.tools\\..*', @@ -35,7 +35,14 @@ ext.groups = [ 'com.google.testing.platform', ] ], - mavenCentral: [ + mavenSnapshots: [ + regex: [ + ], + group: [ + 'org.matrix.rustcomponents' + ] + ], + mavenCentral : [ regex: [ ], group: [ @@ -187,7 +194,7 @@ ext.groups = [ 'xml-apis', ] ], - jcenter : [ + jcenter : [ regex: [ ], group: [ diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index b2b08646b4..b1b7986bf5 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -86,20 +86,6 @@ android { } } -android.libraryVariants.all { variant -> - def t = tasks.register("generate${variant.name.capitalize()}UniffiBindings", Exec) { - // Runs the bindings generation, note that you must have uniffi-bindgen installed - // and in your PATH environment variable - commandLine 'uniffi-bindgen', 'generate', '../rust-sdk/src/olm.udl', - '--language', 'kotlin', - '--out-dir', "${buildDir}/generated/source/uniffi/${variant.name}/java" - } - - variant.javaCompileProvider.get().dependsOn(t) - def sourceSet = variant.sourceSets.find { it.name == variant.name } - sourceSet.java.srcDir new File(buildDir, "generated/source/uniffi/${variant.name}/java") -} - static def gitRevision() { def cmd = "git rev-parse --short=8 HEAD" return cmd.execute().text.trim() @@ -115,10 +101,15 @@ static def gitRevisionDate() { return cmd.execute().text.trim() } +configurations.all { + resolutionStrategy.cacheChangingModulesFor 0, 'seconds' +} + dependencies { implementation libs.jetbrains.coroutinesCore implementation libs.jetbrains.coroutinesAndroid + implementation 'org.matrix.rustcomponents:crypto-android:0.1.1-SNAPSHOT' implementation 'net.java.dev.jna:jna:5.10.0@aar' implementation libs.androidx.appCompat diff --git a/rust-sdk/Cargo.toml b/rust-sdk/Cargo.toml deleted file mode 100644 index 11c5d47ccd..0000000000 --- a/rust-sdk/Cargo.toml +++ /dev/null @@ -1,50 +0,0 @@ -[package] -name = "matrix-crypto-bindings" -version = "0.1.0" -authors = ["Damir Jelić "] -edition = "2018" - -[lib] -crate-type = ["cdylib", "lib"] -name = "matrix_crypto" - -[dependencies] -serde = "1.0.126" -serde_json = "1.0.64" -http = "0.2.4" -base64 = "0.13.0" - -thiserror = "1.0.25" -tracing = "0.1.26" -tracing-subscriber = "0.2.18" -uniffi = "0.15.1" -pbkdf2 = "0.8.0" -sha2 = "0.9.5" -rand = "0.8.4" -hmac = "0.11.0" - -[dependencies.js_int] -version = "0.2.1" -features = ["lax_deserialize"] - -[dependencies.matrix-sdk-common] -git = "https://github.com/matrix-org/matrix-rust-sdk/" -rev = "009ead2eeaf365e1fb0f790557f20d4eaf6874ae" - -[dependencies.matrix-sdk-crypto] -git = "https://github.com/matrix-org/matrix-rust-sdk/" -rev = "009ead2eeaf365e1fb0f790557f20d4eaf6874ae" -features = ["sled_cryptostore", "qrcode", "backups_v1"] - -[dependencies.tokio] -version = "1.7.1" -default_features = false -features = ["rt-multi-thread"] - -[dependencies.ruma] -git = "https://github.com/ruma/ruma" -rev = "fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d" -features = ["client-api-c"] - -[build-dependencies] -uniffi_build = "0.15.1" diff --git a/rust-sdk/Makefile b/rust-sdk/Makefile deleted file mode 100644 index beeab61b22..0000000000 --- a/rust-sdk/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -all: x86 aarch64 armv7-linux-androideabi - -# x86_64: -# cargo build --release --target x86_64-linux-android -# mkdir -p ../matrix-sdk-android/src/main/jniLibs/x86_64/ -# cp target/x86_64-linux-android/release/libmatrix_crypto.so ../matrix-sdk-android/src/main/jniLibs/x86_64/libuniffi_olm.so - -x86: - cargo build --release --target i686-linux-android - mkdir -p ../matrix-sdk-android/src/main/jniLibs/x86/ - cp target/i686-linux-android/release/libmatrix_crypto.so ../matrix-sdk-android/src/main/jniLibs/x86/libuniffi_olm.so - -aarch64: - cargo build --release --target aarch64-linux-android - mkdir -p ../matrix-sdk-android/src/main/jniLibs/arm64-v8a/ - cp target/aarch64-linux-android/release/libmatrix_crypto.so ../matrix-sdk-android/src/main/jniLibs/arm64-v8a/libuniffi_olm.so - -armv7-linux-androideabi: - cargo build --release --target armv7-linux-androideabi - mkdir -p ../matrix-sdk-android/src/main/jniLibs/armeabi-v7a/ - cp target/armv7-linux-androideabi/release/libmatrix_crypto.so ../matrix-sdk-android/src/main/jniLibs/armeabi-v7a/libuniffi_olm.so \ No newline at end of file diff --git a/rust-sdk/README.md b/rust-sdk/README.md deleted file mode 100644 index 57fecd4c92..0000000000 --- a/rust-sdk/README.md +++ /dev/null @@ -1,96 +0,0 @@ -# Kotlin bindings for the Rust SDK crypto layer. - -## Prerequisites - -### Rust - -To build the bindings [Rust] will be needed it can be either installed using an -OS specific package manager or directly with the provided [installer](https://rustup.rs/). - - -### Android NDK - -The Android NDK will be required as well, it can be installed either through -Android Studio or directly using an [installer](https://developer.android.com/ndk/downloads). - -### Uniffi - -The bindings are using [uniffi] to generate the C translation layer between Rust -and Kotlin. Uniffi is a Rust project and can be installed with our freshly -installed Rust setup using: - -``` -$ cargo install uniffi_bindgen -``` - -### Configuring Rust for cross compilation - -First we'll need to install the Rust target for our desired Android architecture, -for example: - -``` -# rustup target add aarch64-linux-android -``` - -This will add support to cross-compile for the aarch64-linux-android target, -Rust supports many different [targets], you'll have to make sure to pick the -right one for your device or emulator. - -After this is done, we'll have to configure [Cargo] to use the correct linker -for our target. Cargo is configured using a TOML file that will be found in -`%USERPROFILE%\.cargo\config.toml` on Windows or `$HOME/.cargo/config` on Unix -platforms. More details and configuration options for Cargo can be found in the -official docs over [here](https://doc.rust-lang.org/cargo/reference/config.html). - -``` -[target.aarch64-linux-android] -ar = "NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/ar" -linker = "NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang" -``` - -## Building - -A `Makefile` is provided that builds and installs the dynamic library into the -appropriate target specific `jniLibs` directory. But before we can get started -we'll need to tweak our Rust setup to allow cross compilation. - -To enable cross compilation fro `olm-sys` which builds our libolm C library -we'll need to set the `ANDROID_NDK` environment variable to the location of our -Android NDK installation. - -``` -$ export ANDROID_NDK=$HOME/Android/Sdk/ndk/22.0.7026061/ -``` - -### Makefile build - -After the prerequisites have been installed and the environment variable has -been set a build for the `aarch64` target can be build using: - -``` -make aarch64 -``` - -### Manual build - -If the `Makefile` doesn't work on your system, the bindings can built for the `aarch64` -target with: - -``` -$ cargo build --target aarch64-linux-android -``` - -After that, a dynamic library can be found in the `target/aarch64-linux-android/debug` directory. -The library will be called `libmatrix_crypto.so` and needs to be renamed and -copied into the `jniLibs` directory: - -``` -$ cp target/aarch64-linux-android/debug/libmatrix_crypto.so \ - ../matrix-sdk-android/src/main/jniLibs/aarch64/libuniffi_olm.so -``` - -[Rust]: https://www.rust-lang.org/ -[installer]: https://rustup.rs/ -[targets]: https://doc.rust-lang.org/nightly/rustc/platform-support.html -[Cargo]: https://doc.rust-lang.org/cargo/ -[uniffi]: https://github.com/mozilla/uniffi-rs/ diff --git a/rust-sdk/build.rs b/rust-sdk/build.rs deleted file mode 100644 index bfce95467f..0000000000 --- a/rust-sdk/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - uniffi_build::generate_scaffolding("./src/olm.udl").unwrap(); -} diff --git a/rust-sdk/src/backup_recovery_key.rs b/rust-sdk/src/backup_recovery_key.rs deleted file mode 100644 index 4a867294cf..0000000000 --- a/rust-sdk/src/backup_recovery_key.rs +++ /dev/null @@ -1,162 +0,0 @@ -use hmac::Hmac; -use pbkdf2::pbkdf2; -use rand::{distributions::Alphanumeric, thread_rng, Rng}; -use sha2::Sha512; -use std::{collections::HashMap, iter}; -use thiserror::Error; - -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 { - pub(crate) inner: RecoveryKey, - passphrase_info: Option, -} - -/// Error type for the decryption of backed up room keys. -#[derive(Debug, Error)] -pub enum PkDecryptionError { - /// An internal libolm error happened during decryption. - #[error("Error decryption a PkMessage {0}")] - Olm(#[from] OlmPkDecryptionError), -} - -#[derive(Debug, Error)] -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 -/// passphrase. -#[derive(Debug, Clone)] -pub struct PassphraseInfo { - /// The salt that was used during key derivation. - pub private_key_salt: String, - /// The number of PBKDF rounds that were used for key derivation. - pub private_key_iterations: i32, -} - -/// The public part of the backup key. -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 { - const KEY_SIZE: usize = 32; - const SALT_SIZE: usize = 32; - const PBKDF_ROUNDS: i32 = 500_000; - - /// Create a new random [`BackupRecoveryKey`]. - pub fn new() -> Self { - Self { - inner: RecoveryKey::new() - .expect("Can't gather enough randomness to create a recovery key"), - passphrase_info: None, - } - } - - /// Try to create a [`BackupRecoveryKey`] from a base 64 encoded string. - pub fn from_base64(key: String) -> Result { - Ok(Self { - inner: RecoveryKey::from_base64(&key)?, - passphrase_info: None, - }) - } - - /// Try to create a [`BackupRecoveryKey`] from a base 58 encoded string. - pub fn from_base58(key: String) -> Result { - Ok(Self { - inner: RecoveryKey::from_base58(&key)?, - passphrase_info: None, - }) - } - - /// Create a new [`BackupRecoveryKey`] from the given passphrase. - pub fn new_from_passphrase(passphrase: String) -> Self { - let mut rng = thread_rng(); - let salt: String = iter::repeat(()) - .map(|()| rng.sample(Alphanumeric)) - .map(char::from) - .take(Self::SALT_SIZE) - .collect(); - - Self::from_passphrase(passphrase, salt, Self::PBKDF_ROUNDS) - } - - /// Restore a [`BackupRecoveryKey`] from the given passphrase. - pub fn from_passphrase(passphrase: String, salt: String, rounds: i32) -> Self { - let mut key = [0u8; Self::KEY_SIZE]; - let rounds = rounds as u32; - - pbkdf2::>(passphrase.as_bytes(), salt.as_bytes(), rounds, &mut key); - - Self { - inner: RecoveryKey::from_bytes(key), - passphrase_info: Some(PassphraseInfo { - private_key_salt: salt, - private_key_iterations: rounds as i32, - }), - } - } - - /// Get the public part of the backup key. - pub fn megolm_v1_public_key(&self) -> MegolmV1BackupKey { - let public_key = self.inner.megolm_v1_public_key(); - - let signatures: HashMap> = public_key - .signatures() - .into_iter() - .map(|(k, v)| { - ( - k.to_string(), - v.into_iter().map(|(k, v)| (k.to_string(), v)).collect(), - ) - }) - .collect(); - - MegolmV1BackupKey { - public_key: public_key.to_base64(), - signatures, - passphrase_info: self.passphrase_info.clone(), - backup_algorithm: public_key.backup_algorithm().to_owned(), - } - } - - /// Convert the recovery key to a base 58 encoded string. - pub fn to_base58(&self) -> String { - self.inner.to_base58() - } - - /// Convert the recovery key to a base 64 encoded string. - pub fn to_base64(&self) -> String { - self.inner.to_base64() - } - - /// Try to decrypt a message that was encrypted using the public part of the - /// backup key. - pub fn decrypt_v1( - &self, - ephemeral_key: String, - mac: String, - ciphertext: String, - ) -> Result { - self.inner - .decrypt_v1(ephemeral_key, mac, ciphertext) - .map_err(|e| e.into()) - } -} diff --git a/rust-sdk/src/device.rs b/rust-sdk/src/device.rs deleted file mode 100644 index ecd571c118..0000000000 --- a/rust-sdk/src/device.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::collections::HashMap; - -use matrix_sdk_crypto::Device as InnerDevice; - -/// An E2EE capable Matrix device. -pub struct Device { - /// The device owner. - pub user_id: String, - /// The unique ID of the device. - pub device_id: String, - /// The published public identity keys of the devices - /// - /// A map from the key type (e.g. curve25519) to the base64 encoded key. - pub keys: HashMap, - /// The supported algorithms of the device. - pub algorithms: Vec, - /// The human readable name of the device. - pub display_name: Option, - /// A flag indicating if the device has been blocked, blocked devices don't - /// receive any room keys from us. - pub is_blocked: bool, - /// Is the device locally trusted - pub locally_trusted: bool, - /// Is our cross signing identity trusted and does the identity trust the - /// device. - pub cross_signing_trusted: bool, -} - -impl From for Device { - fn from(d: InnerDevice) -> Self { - Device { - user_id: d.user_id().to_string(), - device_id: d.device_id().to_string(), - keys: d - .keys() - .iter() - .map(|(k, v)| (k.to_string(), v.to_string())) - .collect(), - algorithms: d.algorithms().iter().map(|a| a.to_string()).collect(), - display_name: d.display_name().map(|d| d.to_owned()), - is_blocked: d.is_blacklisted(), - locally_trusted: d.is_locally_trusted(), - cross_signing_trusted: d.is_cross_signing_trusted(), - } - } -} diff --git a/rust-sdk/src/error.rs b/rust-sdk/src/error.rs deleted file mode 100644 index 1a79c5f6b9..0000000000 --- a/rust-sdk/src/error.rs +++ /dev/null @@ -1,63 +0,0 @@ -#![allow(missing_docs)] - -use matrix_sdk_crypto::{ - store::CryptoStoreError as InnerStoreError, KeyExportError, MegolmError, OlmError, - SecretImportError as RustSecretImportError, SignatureError as InnerSignatureError, -}; -use ruma::identifiers::Error as RumaIdentifierError; - -#[derive(Debug, thiserror::Error)] -pub enum KeyImportError { - #[error(transparent)] - Export(#[from] KeyExportError), - #[error(transparent)] - CryptoStore(#[from] InnerStoreError), - #[error(transparent)] - Json(#[from] serde_json::Error), -} - -#[derive(Debug, thiserror::Error)] -pub enum SecretImportError { - #[error(transparent)] - CryptoStore(#[from] InnerStoreError), - #[error(transparent)] - Import(#[from] RustSecretImportError), -} - -#[derive(Debug, thiserror::Error)] -pub enum SignatureError { - #[error(transparent)] - Signature(#[from] InnerSignatureError), - #[error(transparent)] - Identifier(#[from] RumaIdentifierError), - #[error(transparent)] - CryptoStore(#[from] InnerStoreError), - #[error("Unknown device {0} {1}")] - UnknownDevice(String, String), - #[error("Unknown user identity {0}")] - UnknownUserIdentity(String), -} - -#[derive(Debug, thiserror::Error)] -pub enum CryptoStoreError { - #[error(transparent)] - CryptoStore(#[from] InnerStoreError), - #[error(transparent)] - OlmError(#[from] OlmError), - #[error(transparent)] - Serialization(#[from] serde_json::Error), - #[error("The given string is not a valid user ID: source {0}, error {1}")] - InvalidUserId(String, RumaIdentifierError), - #[error(transparent)] - Identifier(#[from] RumaIdentifierError), -} - -#[derive(Debug, thiserror::Error)] -pub enum DecryptionError { - #[error(transparent)] - Serialization(#[from] serde_json::Error), - #[error(transparent)] - Identifier(#[from] RumaIdentifierError), - #[error(transparent)] - Megolm(#[from] MegolmError), -} diff --git a/rust-sdk/src/lib.rs b/rust-sdk/src/lib.rs deleted file mode 100644 index 533636d0b5..0000000000 --- a/rust-sdk/src/lib.rs +++ /dev/null @@ -1,163 +0,0 @@ -#![deny( - dead_code, - trivial_casts, - trivial_numeric_casts, - unused_extern_crates, - unused_import_braces -)] - -//! TODO - -mod backup_recovery_key; -mod device; -mod error; -mod logger; -mod machine; -mod responses; -mod users; -mod verification; - -use std::convert::TryFrom; - -pub use backup_recovery_key::{ - BackupRecoveryKey, DecodeError, MegolmV1BackupKey, PassphraseInfo, PkDecryptionError, -}; -pub use device::Device; -pub use error::{ - CryptoStoreError, DecryptionError, KeyImportError, SecretImportError, SignatureError, -}; -pub use logger::{set_logger, Logger}; -pub use machine::{KeyRequestPair, OlmMachine}; -pub use responses::{ - BootstrapCrossSigningResult, DeviceLists, KeysImportResult, OutgoingVerificationRequest, - Request, RequestType, SignatureUploadRequest, UploadSigningKeysRequest, -}; -pub use users::UserIdentity; -pub use verification::{ - CancelInfo, ConfirmVerificationResult, QrCode, RequestVerificationResult, Sas, ScanResult, - StartSasResult, Verification, VerificationRequest, -}; - -/// Callback that will be passed over the FFI to report progress -pub trait ProgressListener { - /// The callback that should be called on the Rust side - /// - /// # Arguments - /// - /// * `progress` - The current number of items that have been handled - /// - /// * `total` - The total number of items that will be handled - fn on_progress(&self, progress: i32, total: i32); -} - -/// An event that was successfully decrypted. -pub struct DecryptedEvent { - /// The decrypted version of the event. - pub clear_event: String, - /// The claimed curve25519 key of the sender. - pub sender_curve25519_key: String, - /// The claimed ed25519 key of the sender. - pub claimed_ed25519_key: Option, - /// The curve25519 chain of the senders that forwarded the Megolm decryption - /// key to us. Is empty if the key came directly from the sender of the - /// event. - pub forwarding_curve25519_chain: Vec, -} - -/// Struct representing the state of our private cross signing keys, it shows -/// which private cross signing keys we have locally stored. -#[derive(Debug, Clone)] -pub struct CrossSigningStatus { - /// Do we have the master key. - pub has_master: bool, - /// Do we have the self signing key, this one is necessary to sign our own - /// devices. - pub has_self_signing: bool, - /// Do we have the user signing key, this one is necessary to sign other - /// users. - pub has_user_signing: bool, -} - -/// A struct containing private cross signing keys that can be backed up or -/// uploaded to the secret store. -pub struct CrossSigningKeyExport { - /// The seed of the master key encoded as unpadded base64. - pub master_key: Option, - /// The seed of the self signing key encoded as unpadded base64. - pub self_signing_key: Option, - /// The seed of the user signing key encoded as unpadded base64. - pub user_signing_key: Option, -} - -/// Struct holding the number of room keys we have. -pub struct RoomKeyCounts { - /// The total number of room keys. - pub total: i64, - /// The number of backed up room keys. - pub backed_up: i64, -} - -/// Backup keys and information we load from the store. -pub struct BackupKeys { - /// The recovery key as a base64 encoded string. - pub recovery_key: String, - /// The version that is used with the recovery key. - pub backup_version: String, -} - -impl std::convert::TryFrom for BackupKeys { - type Error = (); - - fn try_from(keys: matrix_sdk_crypto::store::BackupKeys) -> Result { - Ok(Self { - recovery_key: keys.recovery_key.ok_or(())?.to_base64(), - backup_version: keys.backup_version.ok_or(())?, - }) - } -} - -impl From for RoomKeyCounts { - fn from(count: matrix_sdk_crypto::store::RoomKeyCounts) -> Self { - Self { - total: count.total as i64, - backed_up: count.backed_up as i64, - } - } -} - -impl From for CrossSigningKeyExport { - fn from(e: matrix_sdk_crypto::CrossSigningKeyExport) -> Self { - Self { - master_key: e.master_key.clone(), - self_signing_key: e.self_signing_key.clone(), - user_signing_key: e.user_signing_key.clone(), - } - } -} - -impl From for matrix_sdk_crypto::CrossSigningKeyExport { - fn from(e: CrossSigningKeyExport) -> Self { - matrix_sdk_crypto::CrossSigningKeyExport { - master_key: e.master_key, - self_signing_key: e.self_signing_key, - user_signing_key: e.user_signing_key, - } - } -} - -impl From for CrossSigningStatus { - fn from(s: matrix_sdk_crypto::CrossSigningStatus) -> Self { - Self { - has_master: s.has_master, - has_self_signing: s.has_self_signing, - has_user_signing: s.has_user_signing, - } - } -} - -fn parse_user_id(user_id: &str) -> Result, CryptoStoreError> { - Box::::try_from(user_id) - .map_err(|e| CryptoStoreError::InvalidUserId(user_id.to_owned(), e)) -} - -include!(concat!(env!("OUT_DIR"), "/olm.uniffi.rs")); diff --git a/rust-sdk/src/logger.rs b/rust-sdk/src/logger.rs deleted file mode 100644 index a29b040572..0000000000 --- a/rust-sdk/src/logger.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::{ - io::{Result, Write}, - sync::{Arc, Mutex}, -}; -use tracing_subscriber::{fmt::MakeWriter, EnvFilter}; - -/// Trait that can be used to forward Rust logs over FFI to a language specific -/// logger. -pub trait Logger: Send { - /// Called every time the Rust side wants to post a log line. - fn log(&self, log_line: String); - // TODO add support for different log levels, do this by adding more methods - // to the trait. -} - -impl Write for LoggerWrapper { - fn write(&mut self, buf: &[u8]) -> Result { - let data = String::from_utf8_lossy(buf).to_string(); - self.inner.lock().unwrap().log(data); - - Ok(buf.len()) - } - - fn flush(&mut self) -> Result<()> { - Ok(()) - } -} - -impl MakeWriter for LoggerWrapper { - type Writer = LoggerWrapper; - - fn make_writer(&self) -> Self::Writer { - self.clone() - } -} - -#[derive(Clone)] -pub struct LoggerWrapper { - inner: Arc>>, -} - -/// Set the logger that should be used to forward Rust logs over FFI. -pub fn set_logger(logger: Box) { - let logger = LoggerWrapper { - inner: Arc::new(Mutex::new(logger)), - }; - - let filter = EnvFilter::from_default_env().add_directive( - "matrix_sdk_crypto=trace" - .parse() - .expect("Can't parse logging filter directive"), - ); - - let _ = tracing_subscriber::fmt() - .with_writer(logger) - .with_env_filter(filter) - .without_time() - .try_init(); -} diff --git a/rust-sdk/src/machine.rs b/rust-sdk/src/machine.rs deleted file mode 100644 index 3de5800aae..0000000000 --- a/rust-sdk/src/machine.rs +++ /dev/null @@ -1,1430 +0,0 @@ -use std::{ - collections::{BTreeMap, HashMap}, - convert::{TryFrom, TryInto}, - io::Cursor, - ops::Deref, -}; - -use base64::{decode_config, encode, STANDARD_NO_PAD}; -use js_int::UInt; -use ruma::{ - api::{ - client::r0::{ - backup::add_backup_keys::Response as KeysBackupResponse, - keys::{ - claim_keys::Response as KeysClaimResponse, get_keys::Response as KeysQueryResponse, - upload_keys::Response as KeysUploadResponse, - upload_signatures::Response as SignatureUploadResponse, - }, - sync::sync_events::{DeviceLists as RumaDeviceLists, ToDevice}, - to_device::send_event_to_device::Response as ToDeviceResponse, - }, - IncomingResponse, - }, - events::{ - key::verification::VerificationMethod, room::encrypted::RoomEncryptedEventContent, - AnyMessageEventContent, EventContent, SyncMessageEvent, - }, - DeviceKeyAlgorithm, EventId, RoomId, UserId, -}; -use serde::{Deserialize, Serialize}; -use serde_json::{value::RawValue, Value}; -use tokio::runtime::Runtime; - -use matrix_sdk_common::{deserialized_responses::AlgorithmInfo, uuid::Uuid}; -use matrix_sdk_crypto::{ - backups::{MegolmV1BackupKey as RustBackupKey, RecoveryKey}, - decrypt_key_export, encrypt_key_export, - matrix_qrcode::QrVerificationData, - olm::ExportedRoomKey, - EncryptionSettings, LocalTrust, OlmMachine as InnerMachine, UserIdentities, - Verification as RustVerification, -}; - -use crate::{ - error::{CryptoStoreError, DecryptionError, SecretImportError, SignatureError}, - responses::{response_from_string, OutgoingVerificationRequest, OwnedResponse}, - BackupKeys, BootstrapCrossSigningResult, ConfirmVerificationResult, CrossSigningKeyExport, - CrossSigningStatus, DecodeError, DecryptedEvent, Device, DeviceLists, KeyImportError, - KeysImportResult, MegolmV1BackupKey, ProgressListener, QrCode, Request, RequestType, - RequestVerificationResult, RoomKeyCounts, ScanResult, SignatureUploadRequest, StartSasResult, - UserIdentity, Verification, VerificationRequest, parse_user_id, -}; - -/// A high level state machine that handles E2EE for Matrix. -pub struct OlmMachine { - inner: InnerMachine, - runtime: Runtime, -} - -/// A pair of outgoing room key requests, both of those are sendToDevice -/// requests. -pub struct KeyRequestPair { - /// The optional cancellation, this is None if no previous key request was - /// sent out for this key, thus it doesn't need to be cancelled. - pub cancellation: Option, - /// The actual key request. - pub key_request: Request, -} - -impl OlmMachine { - /// Create a new `OlmMachine` - /// - /// # Arguments - /// - /// * `user_id` - The unique ID of the user that owns this machine. - /// - /// * `device_id` - The unique ID of the device that owns this machine. - /// - /// * `path` - The path where the state of the machine should be persisted. - pub fn new(user_id: &str, device_id: &str, path: &str) -> Result { - let user_id = parse_user_id(user_id)?; - let device_id = device_id.into(); - let runtime = Runtime::new().expect("Couldn't create a tokio runtime"); - - Ok(OlmMachine { - inner: runtime.block_on(InnerMachine::new_with_default_store( - &user_id, device_id, path, None, - ))?, - runtime, - }) - } - - /// Get the user ID of the owner of this `OlmMachine`. - pub fn user_id(&self) -> String { - self.inner.user_id().to_string() - } - - /// Get the device ID of the device of this `OlmMachine`. - pub fn device_id(&self) -> String { - self.inner.device_id().to_string() - } - - /// Get the display name of our own device. - pub fn display_name(&self) -> Result, CryptoStoreError> { - Ok(self.runtime.block_on(self.inner.display_name())?) - } - - /// Get a cross signing user identity for the given user ID. - pub fn get_identity(&self, user_id: &str) -> Result, CryptoStoreError> { - let user_id = parse_user_id(user_id)?; - - Ok( - if let Some(identity) = self.runtime.block_on(self.inner.get_identity(&user_id))? { - Some(self.runtime.block_on(UserIdentity::from_rust(identity))?) - } else { - None - }, - ) - } - - /// Check if a user identity is considered to be verified by us. - pub fn is_identity_verified(&self, user_id: &str) -> Result { - let user_id = parse_user_id(user_id)?; - - Ok( - if let Some(identity) = self.runtime.block_on(self.inner.get_identity(&user_id))? { - match identity { - UserIdentities::Own(i) => i.is_verified(), - UserIdentities::Other(i) => i.verified(), - } - } else { - false - }, - ) - } - - /// Manually the user with the given user ID. - /// - /// This method will attempt to sign the user identity using either our - /// private cross signing key, for other user identities, or our device keys - /// for our own user identity. - /// - /// This methid can fail if we don't have the private part of our user-signing - /// key. - /// - /// Returns a request that needs to be sent out for the user identity to be - /// marked as verified. - pub fn verify_identity(&self, user_id: &str) -> Result { - let user_id = Box::::try_from(user_id)?; - - let user_identity = self.runtime.block_on(self.inner.get_identity(&user_id))?; - - if let Some(user_identity) = user_identity { - Ok(match user_identity { - UserIdentities::Own(i) => self.runtime.block_on(i.verify())?, - UserIdentities::Other(i) => self.runtime.block_on(i.verify())?, - } - .into()) - } else { - Err(SignatureError::UnknownUserIdentity(user_id.to_string())) - } - } - - /// Get a `Device` from the store. - /// - /// # Arguments - /// - /// * `user_id` - The id of the device owner. - /// - /// * `device_id` - The id of the device itself. - pub fn get_device( - &self, - user_id: &str, - device_id: &str, - ) -> Result, CryptoStoreError> { - let user_id = parse_user_id(user_id)?; - - Ok(self - .runtime - .block_on(self.inner.get_device(&user_id, device_id.into()))? - .map(|d| d.into())) - } - - /// Manually the device of the given user with the given device ID. - /// - /// This method will attempt to sign the device using our private cross - /// signing key. - /// - /// This method will always fail if the device belongs to someone else, we - /// can only sign our own devices. - /// - /// It can also fail if we don't have the private part of our self-signing - /// key. - /// - /// Returns a request that needs to be sent out for the device to be marked - /// as verified. - pub fn verify_device( - &self, - user_id: &str, - device_id: &str, - ) -> Result { - let user_id = Box::::try_from(user_id)?; - let device = self - .runtime - .block_on(self.inner.get_device(&user_id, device_id.into()))?; - - if let Some(device) = device { - Ok(self.runtime.block_on(device.verify())?.into()) - } else { - Err(SignatureError::UnknownDevice( - user_id.to_string(), - device_id.to_string(), - )) - } - } - - /// Mark the device of the given user with the given device id as trusted. - pub fn mark_device_as_trusted( - &self, - user_id: &str, - device_id: &str, - ) -> Result<(), CryptoStoreError> { - let user_id = parse_user_id(user_id)?; - - let device = self - .runtime - .block_on(self.inner.get_device(&user_id, device_id.into()))?; - - if let Some(device) = device { - self.runtime - .block_on(device.set_local_trust(LocalTrust::Verified))?; - } - - Ok(()) - } - - /// Get all devices of an user. - /// - /// # Arguments - /// - /// * `user_id` - The id of the device owner. - pub fn get_user_devices(&self, user_id: &str) -> Result, CryptoStoreError> { - let user_id = parse_user_id(user_id)?; - - Ok(self - .runtime - .block_on(self.inner.get_user_devices(&user_id))? - .devices() - .map(|d| d.into()) - .collect()) - } - - /// Get our own identity keys. - pub fn identity_keys(&self) -> HashMap { - self.inner - .identity_keys() - .iter() - .map(|(k, v)| (k.to_owned(), v.to_owned())) - .collect() - } - - /// Get the list of outgoing requests that need to be sent to the - /// homeserver. - /// - /// After the request was sent out and a successful response was received - /// the response body should be passed back to the state machine using the - /// [mark_request_as_sent()](#method.mark_request_as_sent) method. - /// - /// **Note**: This method call should be locked per call. - pub fn outgoing_requests(&self) -> Result, CryptoStoreError> { - Ok(self - .runtime - .block_on(self.inner.outgoing_requests())? - .into_iter() - .map(|r| r.into()) - .collect()) - } - - /// Mark a request that was sent to the server as sent. - /// - /// # Arguments - /// - /// * `request_id` - The unique ID of the request that was sent out. This - /// needs to be an UUID. - /// - /// * `request_type` - The type of the request that was sent out. - /// - /// * `response_body` - The body of the response that was received. - pub fn mark_request_as_sent( - &self, - request_id: &str, - request_type: RequestType, - response_body: &str, - ) -> Result<(), CryptoStoreError> { - let id = Uuid::parse_str(request_id).expect("Can't parse request id"); - - let response = response_from_string(response_body); - - let response: OwnedResponse = match request_type { - RequestType::KeysUpload => { - KeysUploadResponse::try_from_http_response(response).map(Into::into) - } - RequestType::KeysQuery => { - KeysQueryResponse::try_from_http_response(response).map(Into::into) - } - RequestType::ToDevice => { - ToDeviceResponse::try_from_http_response(response).map(Into::into) - } - RequestType::KeysClaim => { - KeysClaimResponse::try_from_http_response(response).map(Into::into) - } - RequestType::SignatureUpload => { - SignatureUploadResponse::try_from_http_response(response).map(Into::into) - } - RequestType::KeysBackup => { - KeysBackupResponse::try_from_http_response(response).map(Into::into) - } - } - .expect("Can't convert json string to response"); - - self.runtime - .block_on(self.inner.mark_request_as_sent(&id, &response))?; - - Ok(()) - } - - /// Let the state machine know about E2EE related sync changes that we - /// received from the server. - /// - /// This needs to be called after every sync, ideally before processing - /// any other sync changes. - /// - /// # Arguments - /// - /// * `events` - A serialized array of to-device events we received in the - /// current sync response. - /// - /// * `device_changes` - The list of devices that have changed in some way - /// since the previous sync. - /// - /// * `key_counts` - The map of uploaded one-time key types and counts. - pub fn receive_sync_changes( - &self, - events: &str, - device_changes: DeviceLists, - key_counts: HashMap, - unused_fallback_keys: Option>, - ) -> Result { - let events: ToDevice = serde_json::from_str(events)?; - let device_changes: RumaDeviceLists = device_changes.into(); - let key_counts: BTreeMap = key_counts - .into_iter() - .map(|(k, v)| { - ( - DeviceKeyAlgorithm::from(k), - v.clamp(0, i32::MAX) - .try_into() - .expect("Couldn't convert key counts into an UInt"), - ) - }) - .collect(); - - let unused_fallback_keys: Option> = - unused_fallback_keys.map(|u| u.into_iter().map(DeviceKeyAlgorithm::from).collect()); - - let events = self.runtime.block_on(self.inner.receive_sync_changes( - events, - &device_changes, - &key_counts, - unused_fallback_keys.as_deref(), - ))?; - - Ok(serde_json::to_string(&events)?) - } - - /// Add the given list of users to be tracked, triggering a key query request - /// for them. - /// - /// *Note*: Only users that aren't already tracked will be considered for an - /// update. It's safe to call this with already tracked users, it won't - /// result in excessive keys query requests. - /// - /// # Arguments - /// - /// `users` - The users that should be queued up for a key query. - pub fn update_tracked_users(&self, users: Vec) { - let users: Vec> = users - .into_iter() - .filter_map(|u| Box::::try_from(u).ok()) - .collect(); - - self.runtime.block_on( - self.inner - .update_tracked_users(users.iter().map(Deref::deref)), - ); - } - - /// Check if the given user is considered to be tracked. - /// - /// A user can be marked for tracking using the - /// [`OlmMachine::update_tracked_users()`] method. - pub fn is_user_tracked(&self, user_id: &str) -> Result { - let user_id = parse_user_id(user_id)?; - Ok(self.inner.tracked_users().contains(&user_id)) - } - - /// Generate one-time key claiming requests for all the users we are missing - /// sessions for. - /// - /// After the request was sent out and a successful response was received - /// the response body should be passed back to the state machine using the - /// [mark_request_as_sent()](#method.mark_request_as_sent) method. - /// - /// This method should be called every time before a call to - /// [`share_group_session()`](#method.share_group_session) is made. - /// - /// # Arguments - /// - /// * `users` - The list of users for which we would like to establish 1:1 - /// Olm sessions for. - pub fn get_missing_sessions( - &self, - users: Vec, - ) -> Result, CryptoStoreError> { - let users: Vec> = users - .into_iter() - .filter_map(|u| Box::::try_from(u).ok()) - .collect(); - - Ok(self - .runtime - .block_on( - self.inner - .get_missing_sessions(users.iter().map(Deref::deref)), - )? - .map(|r| r.into())) - } - - /// Share a room key with the given list of users for the given room. - /// - /// After the request was sent out and a successful response was received - /// the response body should be passed back to the state machine using the - /// [mark_request_as_sent()](#method.mark_request_as_sent) method. - /// - /// This method should be called every time before a call to - /// [`encrypt()`](#method.encrypt) with the given `room_id` is made. - /// - /// # Arguments - /// - /// * `room_id` - The unique id of the room, note that this doesn't strictly - /// need to be a Matrix room, it just needs to be an unique identifier for - /// the group that will participate in the conversation. - /// - /// * `users` - The list of users which are considered to be members of the - /// room and should receive the room key. - pub fn share_room_key( - &self, - room_id: &str, - users: Vec, - ) -> Result, CryptoStoreError> { - let users: Vec> = users - .into_iter() - .filter_map(|u| Box::::try_from(u).ok()) - .collect(); - - let room_id = Box::::try_from(room_id)?; - let requests = self.runtime.block_on(self.inner.share_group_session( - &room_id, - users.iter().map(Deref::deref), - EncryptionSettings::default(), - ))?; - - Ok(requests.into_iter().map(|r| (&*r).into()).collect()) - } - - /// Encrypt the given event with the given type and content for the given - /// room. - /// - /// **Note**: A room key needs to be shared with the group of users that are - /// members in the given room. If this is not done this method will panic. - /// - /// The usual flow to encrypt an event using this state machine is as - /// follows: - /// - /// 1. Get the one-time key claim request to establish 1:1 Olm sessions for - /// the room members of the room we wish to participate in. This is done - /// using the [`get_missing_sessions()`](#method.get_missing_sessions) - /// method. This method call should be locked per call. - /// - /// 2. Share a room key with all the room members using the - /// [`share_group_session()`](#method.share_group_session). This method - /// call should be locked per room. - /// - /// 3. Encrypt the event using this method. - /// - /// 4. Send the encrypted event to the server. - /// - /// After the room key is shared steps 1 and 2 will become noops, unless - /// there's some changes in the room membership or in the list of devices a - /// member has. - /// - /// # Arguments - /// - /// * `room_id` - The unique id of the room where the event will be sent to. - /// - /// * `even_type` - The type of the event. - /// - /// * `content` - The serialized content of the event. - pub fn encrypt( - &self, - room_id: &str, - event_type: &str, - content: &str, - ) -> Result { - let room_id = Box::::try_from(room_id)?; - let content: Box = serde_json::from_str(content)?; - - let content = AnyMessageEventContent::from_parts(event_type, &content)?; - let encrypted_content = self - .runtime - .block_on(self.inner.encrypt(&room_id, content)) - .expect("Encrypting an event produced an error"); - - Ok(serde_json::to_string(&encrypted_content)?) - } - - /// Decrypt the given event that was sent in the given room. - /// - /// # Arguments - /// - /// * `event` - The serialized encrypted version of the event. - /// - /// * `room_id` - The unique id of the room where the event was sent to. - pub fn decrypt_room_event( - &self, - event: &str, - room_id: &str, - ) -> Result { - // Element Android wants only the content and the type and will create a - // decrypted event with those two itself, this struct makes sure we - // throw away all the other fields. - #[derive(Deserialize, Serialize)] - struct Event<'a> { - #[serde(rename = "type")] - event_type: String, - #[serde(borrow)] - content: &'a RawValue, - } - - let event: SyncMessageEvent = serde_json::from_str(event)?; - let room_id = Box::::try_from(room_id)?; - - let decrypted = self - .runtime - .block_on(self.inner.decrypt_room_event(&event, &room_id))?; - - let encryption_info = decrypted - .encryption_info - .expect("Decrypted event didn't contain any encryption info"); - - let event_json: Event = serde_json::from_str(decrypted.event.json().get())?; - - Ok(match &encryption_info.algorithm_info { - AlgorithmInfo::MegolmV1AesSha2 { - curve25519_key, - sender_claimed_keys, - forwarding_curve25519_key_chain, - } => DecryptedEvent { - clear_event: serde_json::to_string(&event_json)?, - sender_curve25519_key: curve25519_key.to_owned(), - claimed_ed25519_key: sender_claimed_keys - .get(&DeviceKeyAlgorithm::Ed25519) - .cloned(), - forwarding_curve25519_chain: forwarding_curve25519_key_chain.to_owned(), - }, - }) - } - - /// Request or re-request a room key that was used to encrypt the given - /// event. - /// - /// # Arguments - /// - /// * `event` - The undecryptable event that we would wish to request a room - /// key for. - /// - /// * `room_id` - The id of the room the event was sent to. - pub fn request_room_key( - &self, - event: &str, - room_id: &str, - ) -> Result { - let event: SyncMessageEvent = serde_json::from_str(event)?; - let room_id = Box::::try_from(room_id)?; - - let (cancel, request) = self - .runtime - .block_on(self.inner.request_room_key(&event, &room_id))?; - - let cancellation = cancel.map(|r| r.into()); - let key_request = request.into(); - - Ok(KeyRequestPair { - cancellation, - key_request, - }) - } - - /// Export all of our room keys. - /// - /// # Arguments - /// - /// * `passphrase` - The passphrase that should be used to encrypt the key - /// export. - /// - /// * `rounds` - The number of rounds that should be used when expanding the - /// passphrase into an key. - pub fn export_keys(&self, passphrase: &str, rounds: i32) -> Result { - let keys = self.runtime.block_on(self.inner.export_keys(|_| true))?; - - let encrypted = encrypt_key_export(&keys, passphrase, rounds as u32) - .map_err(CryptoStoreError::Serialization)?; - - Ok(encrypted) - } - - fn import_keys_helper( - &self, - keys: Vec, - from_backup: bool, - progress_listener: Box, - ) -> Result { - let listener = |progress: usize, total: usize| { - progress_listener.on_progress(progress as i32, total as i32) - }; - - let result = self - .runtime - .block_on(self.inner.import_keys(keys, from_backup, listener))?; - - Ok(KeysImportResult { - imported: result.imported_count as i64, - total: result.total_count as i64, - keys: result - .keys - .into_iter() - .map(|(r, m)| { - ( - r.to_string(), - m.into_iter() - .map(|(s, k)| (s, k.into_iter().collect())) - .collect(), - ) - }) - .collect(), - }) - } - - /// Import room keys from the given serialized key export. - /// - /// # Arguments - /// - /// * `keys` - The serialized version of the key export. - /// - /// * `passphrase` - The passphrase that was used to encrypt the key export. - /// - /// * `progress_listener` - A callback that can be used to introspect the - /// progress of the key import. - pub fn import_keys( - &self, - keys: &str, - passphrase: &str, - progress_listener: Box, - ) -> Result { - let keys = Cursor::new(keys); - let keys = decrypt_key_export(keys, passphrase)?; - self.import_keys_helper(keys, false, progress_listener) - } - - /// 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 - /// should be used if the room keys are comming from the server-side backup, - /// the method will mark all imported room keys as backed up. - /// - /// # 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)?; - - let keys = keys - .into_iter() - .map(serde_json::from_value) - .filter_map(|k| k.ok()) - .collect(); - - self.import_keys_helper(keys, true, progress_listener) - } - - /// Discard the currently active room key for the given room if there is - /// one. - pub fn discard_room_key(&self, room_id: &str) -> Result<(), CryptoStoreError> { - let room_id = Box::::try_from(room_id)?; - - self.runtime - .block_on(self.inner.invalidate_group_session(&room_id))?; - - Ok(()) - } - - /// Get all the verification requests that we share with the given user. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to fetch the - /// verification requests. - pub fn get_verification_requests(&self, user_id: &str) -> Vec { - let user_id = if let Ok(user_id) = Box::::try_from(user_id) { - user_id - } else { - return vec![]; - }; - - self.inner - .get_verification_requests(&user_id) - .into_iter() - .map(|v| v.into()) - .collect() - } - - /// Get a verification requests that we share with the given user with the - /// given flow id. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to fetch the - /// verification requests. - /// - /// * `flow_id` - The ID that uniquely identifies the verification flow. - pub fn get_verification_request( - &self, - user_id: &str, - flow_id: &str, - ) -> Option { - let user_id = Box::::try_from(user_id).ok()?; - - self.inner - .get_verification_request(&user_id, flow_id) - .map(|v| v.into()) - } - - /// Accept a verification requests that we share with the given user with the - /// given flow id. - /// - /// This will move the verification request into the ready state. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to accept the - /// verification requests. - /// - /// * `flow_id` - The ID that uniquely identifies the verification flow. - /// - /// * `methods` - A list of verification methods that we want to advertise - /// as supported. - pub fn accept_verification_request( - &self, - user_id: &str, - flow_id: &str, - methods: Vec, - ) -> Option { - let user_id = Box::::try_from(user_id).ok()?; - let methods = methods.into_iter().map(VerificationMethod::from).collect(); - - if let Some(verification) = self.inner.get_verification_request(&user_id, flow_id) { - verification.accept_with_methods(methods).map(|r| r.into()) - } else { - None - } - } - - /// Get an m.key.verification.request content for the given user. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user which we would like to request to - /// verify. - /// - /// * `methods` - The list of verification methods we want to advertise to - /// support. - pub fn verification_request_content( - &self, - user_id: &str, - methods: Vec, - ) -> Result, CryptoStoreError> { - let user_id = parse_user_id(user_id)?; - - let identity = self.runtime.block_on(self.inner.get_identity(&user_id))?; - - let methods = methods.into_iter().map(VerificationMethod::from).collect(); - - Ok(if let Some(identity) = identity.and_then(|i| i.other()) { - let content = self - .runtime - .block_on(identity.verification_request_content(Some(methods))); - Some(serde_json::to_string(&content)?) - } else { - None - }) - } - - /// Request a verification flow to begin with the given user in the given - /// room. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user which we would like to request to - /// verify. - /// - /// * `room_id` - The ID of the room that represents a DM with the given - /// user. - /// - /// * `event_id` - The event ID of the `m.key.verification.request` event - /// that we sent out to request the verification to begin. The content for - /// this request can be created using the [verification_request_content()] - /// method. - /// - /// * `methods` - The list of verification methods we advertised as - /// supported in the `m.key.verification.request` event. - /// - /// [verification_request_content()]: #method.verification_request_content - pub fn request_verification( - &self, - user_id: &str, - room_id: &str, - event_id: &str, - methods: Vec, - ) -> Result, CryptoStoreError> { - let user_id = parse_user_id(user_id)?; - let event_id = Box::::try_from(event_id)?; - let room_id = Box::::try_from(room_id)?; - - let identity = self.runtime.block_on(self.inner.get_identity(&user_id))?; - - let methods = methods.into_iter().map(VerificationMethod::from).collect(); - - Ok(if let Some(identity) = identity.and_then(|i| i.other()) { - let request = self.runtime.block_on(identity.request_verification( - &room_id, - &event_id, - Some(methods), - )); - - Some(request.into()) - } else { - None - }) - } - - /// Request a verification flow to begin with the given user's device. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user which we would like to request to - /// verify. - /// - /// * `device_id` - The ID of the device that we wish to verify. - /// - /// * `methods` - The list of verification methods we advertised as - /// supported in the `m.key.verification.request` event. - pub fn request_verification_with_device( - &self, - user_id: &str, - device_id: &str, - methods: Vec, - ) -> Result, CryptoStoreError> { - let user_id = parse_user_id(user_id)?; - - let methods = methods.into_iter().map(VerificationMethod::from).collect(); - - Ok( - if let Some(device) = self - .runtime - .block_on(self.inner.get_device(&user_id, device_id.into()))? - { - let (verification, request) = self - .runtime - .block_on(device.request_verification_with_methods(methods)); - - Some(RequestVerificationResult { - verification: verification.into(), - request: request.into(), - }) - } else { - None - }, - ) - } - - /// Request a verification flow to begin with our other devices. - /// - /// # Arguments - /// - /// `methods` - The list of verification methods we want to advertise to - /// support. - pub fn request_self_verification( - &self, - methods: Vec, - ) -> Result, CryptoStoreError> { - let identity = self - .runtime - .block_on(self.inner.get_identity(self.inner.user_id()))?; - - let methods = methods.into_iter().map(VerificationMethod::from).collect(); - - Ok(if let Some(identity) = identity.and_then(|i| i.own()) { - let (verification, request) = self - .runtime - .block_on(identity.request_verification_with_methods(methods))?; - Some(RequestVerificationResult { - verification: verification.into(), - request: request.into(), - }) - } else { - None - }) - } - - /// Get a verification flow object for the given user with the given flow id. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to fetch the - /// verification. - /// - /// * `flow_id` - The ID that uniquely identifies the verification flow. - pub fn get_verification(&self, user_id: &str, flow_id: &str) -> Option { - let user_id = Box::::try_from(user_id).ok()?; - - self.inner - .get_verification(&user_id, flow_id) - .map(|v| match v { - RustVerification::SasV1(s) => Verification::SasV1 { sas: s.into() }, - RustVerification::QrV1(qr) => Verification::QrCodeV1 { qrcode: qr.into() }, - }) - } - - /// Cancel a verification for the given user with the given flow id using - /// the given cancel code. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to cancel the - /// verification. - /// - /// * `flow_id` - The ID that uniquely identifies the verification flow. - /// - /// * `cancel_code` - The error code for why the verification was cancelled, - /// manual cancellatio usually happens with `m.user` cancel code. The full - /// list of cancel codes can be found in the [spec] - /// - /// [spec]: https://spec.matrix.org/unstable/client-server-api/#mkeyverificationcancel - pub fn cancel_verification( - &self, - user_id: &str, - flow_id: &str, - cancel_code: &str, - ) -> Option { - let user_id = Box::::try_from(user_id).ok()?; - - if let Some(request) = self.inner.get_verification_request(&user_id, flow_id) { - request.cancel().map(|r| r.into()) - } else if let Some(verification) = self.inner.get_verification(&user_id, flow_id) { - match verification { - RustVerification::SasV1(v) => { - v.cancel_with_code(cancel_code.into()).map(|r| r.into()) - } - RustVerification::QrV1(v) => { - v.cancel_with_code(cancel_code.into()).map(|r| r.into()) - } - } - } else { - None - } - } - - /// Confirm a verification was successful. - /// - /// This method should be called either if a short auth string should be - /// confirmed as matching, or if we want to confirm that the other side has - /// scanned our QR code. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to confirm the - /// verification. - /// - /// * `flow_id` - The ID that uniquely identifies the verification flow. - pub fn confirm_verification( - &self, - user_id: &str, - flow_id: &str, - ) -> Result, CryptoStoreError> { - let user_id = parse_user_id(user_id)?; - - Ok( - if let Some(verification) = self.inner.get_verification(&user_id, flow_id) { - match verification { - RustVerification::SasV1(v) => { - let (request, signature_request) = self.runtime.block_on(v.confirm())?; - - request.map(|r| ConfirmVerificationResult { - request: r.into(), - signature_request: signature_request.map(|s| s.into()), - }) - } - RustVerification::QrV1(v) => { - v.confirm_scanning().map(|r| ConfirmVerificationResult { - request: r.into(), - signature_request: None, - }) - } - } - } else { - None - }, - ) - } - - /// Transition from a verification request into QR code verification. - /// - /// This method should be called when one wants to display a QR code so the - /// other side can scan it and move the QR code verification forward. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to start the - /// QR code verification. - /// - /// * `flow_id` - The ID of the verification request that initated the - /// verification flow. - pub fn start_qr_verification( - &self, - user_id: &str, - flow_id: &str, - ) -> Result, CryptoStoreError> { - let user_id = parse_user_id(user_id)?; - - if let Some(verification) = self.inner.get_verification_request(&user_id, flow_id) { - Ok(self - .runtime - .block_on(verification.generate_qr_code())? - .map(|qr| qr.into())) - } else { - Ok(None) - } - } - - /// Generate data that should be encoded as a QR code. - /// - /// This method should be called right before a QR code should be displayed, - /// the returned data is base64 encoded (without padding) and needs to be - /// decoded on the other side before it can be put through a QR code - /// generator. - /// - /// *Note*: You'll need to call [start_qr_verification()] before calling this - /// method, otherwise `None` will be returned. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to start the - /// QR code verification. - /// - /// * `flow_id` - The ID that uniquely identifies the verification flow. - /// - /// [start_qr_verification()]: #method.start_qr_verification - pub fn generate_qr_code(&self, user_id: &str, flow_id: &str) -> Option { - let user_id = Box::::try_from(user_id).ok()?; - self.inner - .get_verification(&user_id, flow_id) - .and_then(|v| v.qr_v1().and_then(|qr| qr.to_bytes().map(encode).ok())) - } - - /// Pass data from a scanned QR code to an active verification request and - /// transition into QR code verification. - /// - /// This requires an active `VerificationRequest` to succeed, returns `None` - /// if no `VerificationRequest` is found or if the QR code data is invalid. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to start the - /// QR code verification. - /// - /// * `flow_id` - The ID of the verification request that initated the - /// verification flow. - /// - /// * `data` - The data that was extracted from the scanned QR code as an - /// base64 encoded string, without padding. - pub fn scan_qr_code(&self, user_id: &str, flow_id: &str, data: &str) -> Option { - let user_id = Box::::try_from(user_id).ok()?; - let data = decode_config(data, STANDARD_NO_PAD).ok()?; - let data = QrVerificationData::from_bytes(data).ok()?; - - if let Some(verification) = self.inner.get_verification_request(&user_id, flow_id) { - if let Some(qr) = self - .runtime - .block_on(verification.scan_qr_code(data)) - .ok()? - { - let request = qr.reciprocate()?; - - Some(ScanResult { - qr: qr.into(), - request: request.into(), - }) - } else { - None - } - } else { - None - } - } - - /// Transition from a verification request into short auth string based - /// verification. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to start the - /// SAS verification. - /// - /// * `flow_id` - The ID of the verification request that initated the - /// verification flow. - pub fn start_sas_verification( - &self, - user_id: &str, - flow_id: &str, - ) -> Result, CryptoStoreError> { - let user_id = parse_user_id(user_id)?; - - Ok( - if let Some(verification) = self.inner.get_verification_request(&user_id, flow_id) { - self.runtime - .block_on(verification.start_sas())? - .map(|(sas, r)| StartSasResult { - sas: sas.into(), - request: r.into(), - }) - } else { - None - }, - ) - } - - /// Start short auth string verification with a device without going - /// through a verification request first. - /// - /// **Note**: This has been largely deprecated and the - /// [request_verification_with_device()] method should be used instead. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to start the - /// SAS verification. - /// - /// * `device_id` - The ID of device we would like to verify. - /// - /// [request_verification_with_device()]: #method.request_verification_with_device - pub fn start_sas_with_device( - &self, - user_id: &str, - device_id: &str, - ) -> Result, CryptoStoreError> { - let user_id = parse_user_id(user_id)?; - - Ok( - if let Some(device) = self - .runtime - .block_on(self.inner.get_device(&user_id, device_id.into()))? - { - let (sas, request) = self.runtime.block_on(device.start_verification())?; - - Some(StartSasResult { - sas: sas.into(), - request: request.into(), - }) - } else { - None - }, - ) - } - - /// Accept that we're going forward with the short auth string verification. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to accept the - /// SAS verification. - /// - /// * `flow_id` - The ID that uniquely identifies the verification flow. - pub fn accept_sas_verification( - &self, - user_id: &str, - flow_id: &str, - ) -> Option { - let user_id = Box::::try_from(user_id).ok()?; - - self.inner - .get_verification(&user_id, flow_id) - .and_then(|s| s.sas_v1()) - .and_then(|s| s.accept().map(|r| r.into())) - } - - /// Get a list of emoji indices of the emoji representation of the short - /// auth string. - /// - /// *Note*: A SAS verification needs to be started and in the presentable - /// state for this to return the list of emoji indices, otherwise returns - /// `None`. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to get the - /// short auth string. - /// - /// * `flow_id` - The ID that uniquely identifies the verification flow. - pub fn get_emoji_index(&self, user_id: &str, flow_id: &str) -> Option> { - let user_id = Box::::try_from(user_id).ok()?; - - self.inner - .get_verification(&user_id, flow_id) - .and_then(|s| { - s.sas_v1().and_then(|s| { - s.emoji_index() - .map(|v| v.iter().map(|i| (*i).into()).collect()) - }) - }) - } - - /// Get the decimal representation of the short auth string. - /// - /// *Note*: A SAS verification needs to be started and in the presentable - /// state for this to return the list of decimals, otherwise returns - /// `None`. - /// - /// # Arguments - /// - /// * `user_id` - The ID of the user for which we would like to get the - /// short auth string. - /// - /// * `flow_id` - The ID that uniquely identifies the verification flow. - pub fn get_decimals(&self, user_id: &str, flow_id: &str) -> Option> { - let user_id = Box::::try_from(user_id).ok()?; - - self.inner - .get_verification(&user_id, flow_id) - .and_then(|s| { - s.sas_v1().and_then(|s| { - s.decimals() - .map(|v| [v.0.into(), v.1.into(), v.2.into()].to_vec()) - }) - }) - } - - /// Create a new private cross signing identity and create a request to - /// upload the public part of it to the server. - pub fn bootstrap_cross_signing(&self) -> Result { - Ok(self - .runtime - .block_on(self.inner.bootstrap_cross_signing(true))? - .into()) - } - - /// Get the status of the private cross signing keys. - /// - /// This can be used to check which private cross signing keys we have - /// stored locally. - pub fn cross_signing_status(&self) -> CrossSigningStatus { - self.runtime - .block_on(self.inner.cross_signing_status()) - .into() - } - - /// Export all our private cross signing keys. - /// - /// The export will contain the seed for the ed25519 keys as a base64 - /// encoded string. - /// - /// This method returns `None` if we don't have any private cross signing keys. - pub fn export_cross_signing_keys(&self) -> Option { - self.runtime - .block_on(self.inner.export_cross_signing_keys()) - .map(|e| e.into()) - } - - /// Import our private cross signing keys. - /// - /// The export needs to contain the seed for the ed25519 keys as a base64 - /// encoded string. - pub fn import_cross_signing_keys( - &self, - export: CrossSigningKeyExport, - ) -> Result<(), SecretImportError> { - self.runtime - .block_on(self.inner.import_cross_signing_keys(export.into()))?; - - Ok(()) - } - - /// 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_v1(backup_key))?; - - Ok(()) - } - - /// 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()) - } - - /// 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())?) - } - - /// 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 - .block_on(self.inner.backup_machine().backup())?; - - let request = request.map(|r| r.into()); - - Ok(request) - } - - /// 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 - .block_on(self.inner.backup_machine().room_key_counts())? - .into()) - } - - /// 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, - version: Option, - ) -> Result<(), CryptoStoreError> { - let key = key - .map(|k| RecoveryKey::from_base64(&k)) - .transpose() - .ok() - .flatten(); - Ok(self - .runtime - .block_on(self.inner.backup_machine().save_recovery_key(key, version))?) - } - - /// Get the backup keys we have saved in our crypto store. - pub fn get_backup_keys(&self) -> Result, CryptoStoreError> { - Ok(self - .runtime - .block_on(self.inner.backup_machine().get_backup_keys())? - .try_into() - .ok()) - } - - /// 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)) - .into_iter() - .map(|(k, v)| { - ( - k.to_string(), - v.into_iter().map(|(k, v)| (k.to_string(), v)).collect(), - ) - }) - .collect() - } - - /// 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 - .runtime - .block_on(self.inner.backup_machine().verify_backup(auth_data))?) - } -} diff --git a/rust-sdk/src/olm.udl b/rust-sdk/src/olm.udl deleted file mode 100644 index b93a398233..0000000000 --- a/rust-sdk/src/olm.udl +++ /dev/null @@ -1,426 +0,0 @@ -namespace olm { - void set_logger(Logger logger); -}; - -callback interface Logger { - void log(string log_line); -}; - -callback interface ProgressListener { - void on_progress(i32 progress, i32 total); -}; - -[Error] -enum PkDecryptionError { - "Olm", -}; - -[Error] -enum KeyImportError { - "Export", - "CryptoStore", - "Json", -}; - -[Error] -enum SignatureError { - "Signature", - "Identifier", - "CryptoStore", - "UnknownDevice", - "UnknownUserIdentity", -}; - -[Error] -enum SecretImportError { - "Import", - "CryptoStore", -}; - - -[Error] -enum CryptoStoreError { - "CryptoStore", - "OlmError", - "Serialization", - "Identifier", - "InvalidUserId", -}; - -[Error] -enum DecryptionError { - "Identifier", - "Serialization", - "Megolm", -}; - -dictionary DeviceLists { - sequence changed; - sequence left; -}; - -dictionary KeysImportResult { - i64 imported; - i64 total; - record>> keys; -}; - -dictionary DecryptedEvent { - string clear_event; - string sender_curve25519_key; - string? claimed_ed25519_key; - sequence forwarding_curve25519_chain; -}; - -dictionary Device { - string user_id; - string device_id; - record keys; - sequence algorithms; - string? display_name; - boolean is_blocked; - boolean locally_trusted; - boolean cross_signing_trusted; -}; - -[Enum] -interface UserIdentity { - Own( - string user_id, - boolean trusts_our_own_device, - string master_key, - string self_signing_key, - string user_signing_key - ); - Other( - string user_id, - string master_key, - string self_signing_key - ); -}; - -dictionary CrossSigningStatus { - boolean has_master; - boolean has_self_signing; - boolean has_user_signing; -}; - -dictionary CrossSigningKeyExport { - string? master_key; - string? self_signing_key; - string? user_signing_key; -}; - -dictionary UploadSigningKeysRequest { - string master_key; - string self_signing_key; - string user_signing_key; -}; - -dictionary BootstrapCrossSigningResult { - UploadSigningKeysRequest upload_signing_keys_request; - SignatureUploadRequest signature_request; -}; - -dictionary CancelInfo { - string cancel_code; - string reason; - boolean cancelled_by_us; -}; - -dictionary StartSasResult { - Sas sas; - OutgoingVerificationRequest request; -}; - -dictionary Sas { - string other_user_id; - string other_device_id; - string flow_id; - string? room_id; - boolean we_started; - boolean has_been_accepted; - boolean can_be_presented; - boolean supports_emoji; - boolean have_we_confirmed; - boolean is_done; - boolean is_cancelled; - CancelInfo? cancel_info; -}; - -dictionary ScanResult { - QrCode qr; - OutgoingVerificationRequest request; -}; - -dictionary QrCode { - string other_user_id; - string other_device_id; - string flow_id; - string? room_id; - boolean we_started; - boolean other_side_scanned; - boolean has_been_confirmed; - boolean reciprocated; - boolean is_done; - boolean is_cancelled; - CancelInfo? cancel_info; -}; - -dictionary VerificationRequest { - string other_user_id; - string? other_device_id; - string flow_id; - string? room_id; - boolean we_started; - boolean is_ready; - boolean is_passive; - boolean is_done; - boolean is_cancelled; - CancelInfo? cancel_info; - sequence? their_methods; - sequence? our_methods; - -}; - -dictionary RequestVerificationResult { - VerificationRequest verification; - OutgoingVerificationRequest request; -}; - -dictionary ConfirmVerificationResult { - OutgoingVerificationRequest request; - SignatureUploadRequest? signature_request; -}; - -[Enum] -interface Verification { - SasV1(Sas sas); - QrCodeV1(QrCode qrcode); -}; - -dictionary KeyRequestPair { - Request? cancellation; - Request key_request; -}; - -[Enum] -interface OutgoingVerificationRequest { - ToDevice(string request_id, string event_type, string body); - InRoom(string request_id, string room_id, string event_type, string content); -}; - -[Enum] -interface Request { - ToDevice(string request_id, string event_type, string body); - KeysUpload(string request_id, string body); - KeysQuery(string request_id, sequence users); - KeysClaim(string request_id, record> one_time_keys); - KeysBackup(string request_id, string version, string rooms); - RoomMessage(string request_id, string room_id, string event_type, string content); - SignatureUpload(string request_id, string body); -}; - -dictionary SignatureUploadRequest { - string body; -}; - -enum RequestType { - "KeysQuery", - "KeysClaim", - "KeysUpload", - "ToDevice", - "SignatureUpload", - "KeysBackup", -}; - -interface OlmMachine { - [Throws=CryptoStoreError] - constructor([ByRef] string user_id, [ByRef] string device_id, [ByRef] string path); - - record identity_keys(); - string user_id(); - string device_id(); - - [Throws=CryptoStoreError] - string receive_sync_changes([ByRef] string events, - DeviceLists device_changes, - record key_counts, - sequence? unused_fallback_keys); - [Throws=CryptoStoreError] - sequence outgoing_requests(); - [Throws=CryptoStoreError] - void mark_request_as_sent( - [ByRef] string request_id, - RequestType request_type, - [ByRef] string response - ); - - [Throws=DecryptionError] - DecryptedEvent decrypt_room_event([ByRef] string event, [ByRef] string room_id); - [Throws=CryptoStoreError] - string encrypt([ByRef] string room_id, [ByRef] string event_type, [ByRef] string content); - - [Throws=CryptoStoreError] - UserIdentity? get_identity([ByRef] string user_id); - [Throws=SignatureError] - SignatureUploadRequest verify_identity([ByRef] string user_id); - [Throws=CryptoStoreError] - Device? get_device([ByRef] string user_id, [ByRef] string device_id); - [Throws=CryptoStoreError] - void mark_device_as_trusted([ByRef] string user_id, [ByRef] string device_id); - [Throws=SignatureError] - SignatureUploadRequest verify_device([ByRef] string user_id, [ByRef] string device_id); - [Throws=CryptoStoreError] - sequence get_user_devices([ByRef] string user_id); - - [Throws=CryptoStoreError] - boolean is_user_tracked([ByRef] string user_id); - void update_tracked_users(sequence users); - [Throws=CryptoStoreError] - Request? get_missing_sessions(sequence users); - [Throws=CryptoStoreError] - sequence share_room_key([ByRef] string room_id, sequence users); - - sequence get_verification_requests([ByRef] string user_id); - VerificationRequest? get_verification_request([ByRef] string user_id, [ByRef] string flow_id); - Verification? get_verification([ByRef] string user_id, [ByRef] string flow_id); - - [Throws=CryptoStoreError] - VerificationRequest? request_verification( - [ByRef] string user_id, - [ByRef] string room_id, - [ByRef] string event_id, - sequence methods - ); - [Throws=CryptoStoreError] - string? verification_request_content( - [ByRef] string user_id, - sequence methods - ); - [Throws=CryptoStoreError] - RequestVerificationResult? request_self_verification(sequence methods); - [Throws=CryptoStoreError] - RequestVerificationResult? request_verification_with_device( - [ByRef] string user_id, - [ByRef] string device_id, - sequence methods - ); - - OutgoingVerificationRequest? accept_verification_request( - [ByRef] string user_id, - [ByRef] string flow_id, - sequence methods - ); - - [Throws=CryptoStoreError] - ConfirmVerificationResult? confirm_verification([ByRef] string user_id, [ByRef] string flow_id); - OutgoingVerificationRequest? cancel_verification( - [ByRef] string user_id, - [ByRef] string flow_id, - [ByRef] string cancel_code - ); - - [Throws=CryptoStoreError] - StartSasResult? start_sas_with_device([ByRef] string user_id, [ByRef] string device_id); - [Throws=CryptoStoreError] - StartSasResult? start_sas_verification([ByRef] string user_id, [ByRef] string flow_id); - OutgoingVerificationRequest? accept_sas_verification([ByRef] string user_id, [ByRef] string flow_id); - sequence? get_emoji_index([ByRef] string user_id, [ByRef] string flow_id); - sequence? get_decimals([ByRef] string user_id, [ByRef] string flow_id); - - [Throws=CryptoStoreError] - QrCode? start_qr_verification([ByRef] string user_id, [ByRef] string flow_id); - ScanResult? scan_qr_code([ByRef] string user_id, [ByRef] string flow_id, [ByRef] string data); - string? generate_qr_code([ByRef] string user_id, [ByRef] string flow_id); - - [Throws=DecryptionError] - KeyRequestPair request_room_key([ByRef] string event, [ByRef] string room_id); - - [Throws=CryptoStoreError] - string export_keys([ByRef] string passphrase, i32 rounds); - [Throws=KeyImportError] - KeysImportResult import_keys( - [ByRef] string keys, - [ByRef] string passphrase, - ProgressListener progress_listener - ); - [Throws=KeyImportError] - KeysImportResult import_decrypted_keys( - [ByRef] string keys, - ProgressListener progress_listener - ); - [Throws=CryptoStoreError] - void discard_room_key([ByRef] string room_id); - - CrossSigningStatus cross_signing_status(); - [Throws=CryptoStoreError] - BootstrapCrossSigningResult bootstrap_cross_signing(); - CrossSigningKeyExport? export_cross_signing_keys(); - [Throws=SecretImportError] - void import_cross_signing_keys(CrossSigningKeyExport export); - [Throws=CryptoStoreError] - boolean is_identity_verified([ByRef] string user_id); - - record> sign([ByRef] string message); - [Throws=DecodeError] - void enable_backup_v1(MegolmV1BackupKey key, string version); - [Throws=CryptoStoreError] - void disable_backup(); - [Throws=CryptoStoreError] - Request? backup_room_keys(); - [Throws=CryptoStoreError] - void save_recovery_key(string? key, string? version); - [Throws=CryptoStoreError] - RoomKeyCounts room_key_counts(); - [Throws=CryptoStoreError] - BackupKeys? get_backup_keys(); - boolean backup_enabled(); - [Throws=CryptoStoreError] - boolean verify_backup([ByRef] string auth_data); -}; - -dictionary PassphraseInfo { - string private_key_salt; - i32 private_key_iterations; -}; - -dictionary MegolmV1BackupKey { - string public_key; - record> signatures; - PassphraseInfo? passphrase_info; - string backup_algorithm; -}; - -dictionary BackupKeys { - string recovery_key; - string backup_version; -}; - -dictionary RoomKeyCounts { - i64 total; - i64 backed_up; -}; - -[Error] -enum DecodeError { - "Decode", - "CryptoStore", -}; - -interface BackupRecoveryKey { - constructor(); - [Name=from_passphrase] - constructor(string passphrase, string salt, i32 rounds); - [Name=new_from_passphrase] - constructor(string passphrase); - [Name=from_base64, Throws=DecodeError] - constructor(string key); - [Name=from_base58, Throws=DecodeError] - constructor(string key); - string to_base58(); - string to_base64(); - MegolmV1BackupKey megolm_v1_public_key(); - [Throws=PkDecryptionError] - string decrypt_v1(string ephemeral_key, string mac, string ciphertext); -}; diff --git a/rust-sdk/src/responses.rs b/rust-sdk/src/responses.rs deleted file mode 100644 index 9901c7114f..0000000000 --- a/rust-sdk/src/responses.rs +++ /dev/null @@ -1,365 +0,0 @@ -#![allow(missing_docs)] - -use std::{collections::HashMap, convert::TryFrom}; - -use http::Response; -use matrix_sdk_common::uuid::Uuid; -use serde_json::json; - -use ruma::{ - api::client::r0::{ - backup::add_backup_keys::Response as KeysBackupResponse, - keys::{ - claim_keys::{Request as KeysClaimRequest, Response as KeysClaimResponse}, - get_keys::Response as KeysQueryResponse, - upload_keys::Response as KeysUploadResponse, - upload_signatures::{ - Request as RustSignatureUploadRequest, Response as SignatureUploadResponse, - }, - }, - sync::sync_events::DeviceLists as RumaDeviceLists, - to_device::send_event_to_device::Response as ToDeviceResponse, - }, - assign, - events::EventContent, - identifiers::UserId, -}; - -use matrix_sdk_crypto::{ - IncomingResponse, OutgoingRequest, OutgoingVerificationRequest as SdkVerificationRequest, - RoomMessageRequest, ToDeviceRequest, UploadSigningKeysRequest as RustUploadSigningKeysRequest, -}; - -pub struct SignatureUploadRequest { - pub body: String, -} - -impl From for SignatureUploadRequest { - fn from(r: RustSignatureUploadRequest) -> Self { - Self { - body: serde_json::to_string(&r.signed_keys) - .expect("Can't serialize signature upload request"), - } - } -} - -pub struct UploadSigningKeysRequest { - pub master_key: String, - pub self_signing_key: String, - pub user_signing_key: String, -} - -impl From for UploadSigningKeysRequest { - fn from(r: RustUploadSigningKeysRequest) -> Self { - Self { - master_key: serde_json::to_string( - &r.master_key.expect("Request didn't contain a master key"), - ) - .expect("Can't serialize cross signing master key"), - self_signing_key: serde_json::to_string( - &r.self_signing_key - .expect("Request didn't contain a self-signing key"), - ) - .expect("Can't serialize cross signing self-signing key"), - user_signing_key: serde_json::to_string( - &r.user_signing_key - .expect("Request didn't contain a user-signing key"), - ) - .expect("Can't serialize cross signing user-signing key"), - } - } -} - -pub struct BootstrapCrossSigningResult { - pub upload_signing_keys_request: UploadSigningKeysRequest, - pub signature_request: SignatureUploadRequest, -} - -impl From<(RustUploadSigningKeysRequest, RustSignatureUploadRequest)> - for BootstrapCrossSigningResult -{ - fn from(requests: (RustUploadSigningKeysRequest, RustSignatureUploadRequest)) -> Self { - Self { - upload_signing_keys_request: requests.0.into(), - signature_request: requests.1.into(), - } - } -} - -pub enum OutgoingVerificationRequest { - ToDevice { - request_id: String, - event_type: String, - body: String, - }, - InRoom { - request_id: String, - room_id: String, - event_type: String, - content: String, - }, -} - -impl From for OutgoingVerificationRequest { - fn from(r: SdkVerificationRequest) -> Self { - match r { - SdkVerificationRequest::ToDevice(r) => r.into(), - SdkVerificationRequest::InRoom(r) => Self::InRoom { - request_id: r.txn_id.to_string(), - room_id: r.room_id.to_string(), - content: serde_json::to_string(&r.content) - .expect("Can't serialize message content"), - event_type: r.content.event_type().to_string(), - }, - } - } -} - -impl From for OutgoingVerificationRequest { - fn from(r: ToDeviceRequest) -> Self { - Self::ToDevice { - request_id: r.txn_id_string(), - event_type: r.event_type.to_string(), - body: serde_json::to_string(&r.messages).expect("Can't serialize to-device body"), - } - } -} - -#[derive(Debug)] -pub enum Request { - ToDevice { - request_id: String, - event_type: String, - body: String, - }, - KeysUpload { - request_id: String, - body: String, - }, - KeysQuery { - request_id: String, - users: Vec, - }, - KeysClaim { - request_id: String, - one_time_keys: HashMap>, - }, - RoomMessage { - request_id: String, - room_id: String, - event_type: String, - content: String, - }, - SignatureUpload { - request_id: String, - body: String, - }, - KeysBackup { - request_id: String, - version: String, - rooms: String, - }, -} - -impl From for Request { - fn from(r: OutgoingRequest) -> Self { - use matrix_sdk_crypto::OutgoingRequests::*; - - match r.request() { - KeysUpload(u) => { - let body = json!({ - "device_keys": u.device_keys, - "one_time_keys": u.one_time_keys, - }); - - Request::KeysUpload { - request_id: r.request_id().to_string(), - body: serde_json::to_string(&body) - .expect("Can't serialize keys upload request"), - } - } - KeysQuery(k) => { - let users: Vec = k.device_keys.keys().map(|u| u.to_string()).collect(); - Request::KeysQuery { - request_id: r.request_id().to_string(), - users, - } - } - ToDeviceRequest(t) => Request::from(t), - SignatureUpload(t) => Request::SignatureUpload { - request_id: r.request_id().to_string(), - body: serde_json::to_string(&t.signed_keys) - .expect("Can't serialize signature upload request"), - }, - RoomMessage(r) => Request::from(r), - KeysClaim(c) => (*r.request_id(), c.clone()).into(), - KeysBackup(b) => Request::KeysBackup { - request_id: r.request_id().to_string(), - version: b.version.to_owned(), - rooms: serde_json::to_string(&b.rooms) - .expect("Can't serialize keys backup request"), - }, - } - } -} - -impl From for Request { - fn from(r: ToDeviceRequest) -> Self { - Request::ToDevice { - request_id: r.txn_id_string(), - event_type: r.event_type.to_string(), - body: serde_json::to_string(&r.messages).expect("Can't serialize to-device body"), - } - } -} - -impl From<(Uuid, KeysClaimRequest)> for Request { - fn from(request_tuple: (Uuid, KeysClaimRequest)) -> Self { - let (request_id, request) = request_tuple; - - Request::KeysClaim { - request_id: request_id.to_string(), - one_time_keys: request - .one_time_keys - .into_iter() - .map(|(u, d)| { - ( - u.to_string(), - d.into_iter() - .map(|(k, v)| (k.to_string(), v.to_string())) - .collect(), - ) - }) - .collect(), - } - } -} - -impl From<&ToDeviceRequest> for Request { - fn from(r: &ToDeviceRequest) -> Self { - Request::ToDevice { - request_id: r.txn_id_string(), - event_type: r.event_type.to_string(), - body: serde_json::to_string(&r.messages).expect("Can't serialize to-device body"), - } - } -} - -impl From<&RoomMessageRequest> for Request { - fn from(r: &RoomMessageRequest) -> Self { - Self::RoomMessage { - request_id: r.txn_id.to_string(), - room_id: r.room_id.to_string(), - event_type: r.content.event_type().to_string(), - content: serde_json::to_string(&r.content).expect("Can't serialize message content"), - } - } -} - -pub(crate) fn response_from_string(body: &str) -> Response> { - Response::builder() - .status(200) - .body(body.as_bytes().to_vec()) - .expect("Can't create HTTP response") -} - -pub enum RequestType { - KeysQuery, - KeysClaim, - KeysUpload, - ToDevice, - SignatureUpload, - KeysBackup, -} - -pub struct DeviceLists { - pub changed: Vec, - pub left: Vec, -} - -impl From for RumaDeviceLists { - fn from(d: DeviceLists) -> Self { - assign!(RumaDeviceLists::new(), { - changed: d - .changed - .into_iter() - .filter_map(|u| Box::::try_from(u).ok()) - .collect(), - left: d - .left - .into_iter() - .filter_map(|u| Box::::try_from(u).ok()) - .collect(), - }) - } -} - -pub struct KeysImportResult { - /// The number of room keys that were imported. - pub imported: i64, - /// The total number of room keys that were found in the export. - pub total: i64, - /// The map of keys that were imported. - /// - /// It's a map from room id to a map of the sender key to a list of session - /// ids. - pub keys: HashMap>>, -} - -pub(crate) enum OwnedResponse { - KeysClaim(KeysClaimResponse), - KeysUpload(KeysUploadResponse), - KeysQuery(KeysQueryResponse), - ToDevice(ToDeviceResponse), - SignatureUpload(SignatureUploadResponse), - KeysBackup(KeysBackupResponse), -} - -impl From for OwnedResponse { - fn from(response: KeysClaimResponse) -> Self { - OwnedResponse::KeysClaim(response) - } -} - -impl From for OwnedResponse { - fn from(response: KeysQueryResponse) -> Self { - OwnedResponse::KeysQuery(response) - } -} - -impl From for OwnedResponse { - fn from(response: KeysUploadResponse) -> Self { - OwnedResponse::KeysUpload(response) - } -} - -impl From for OwnedResponse { - fn from(response: ToDeviceResponse) -> Self { - OwnedResponse::ToDevice(response) - } -} - -impl From for OwnedResponse { - fn from(response: SignatureUploadResponse) -> Self { - Self::SignatureUpload(response) - } -} - -impl From for OwnedResponse { - fn from(r: KeysBackupResponse) -> Self { - Self::KeysBackup(r) - } -} - -impl<'a> From<&'a OwnedResponse> for IncomingResponse<'a> { - fn from(r: &'a OwnedResponse) -> Self { - match r { - OwnedResponse::KeysClaim(r) => IncomingResponse::KeysClaim(r), - OwnedResponse::KeysQuery(r) => IncomingResponse::KeysQuery(r), - OwnedResponse::KeysUpload(r) => IncomingResponse::KeysUpload(r), - OwnedResponse::ToDevice(r) => IncomingResponse::ToDevice(r), - OwnedResponse::SignatureUpload(r) => IncomingResponse::SignatureUpload(r), - OwnedResponse::KeysBackup(r) => IncomingResponse::KeysBackup(r), - } - } -} diff --git a/rust-sdk/src/users.rs b/rust-sdk/src/users.rs deleted file mode 100644 index 4c1a05a55a..0000000000 --- a/rust-sdk/src/users.rs +++ /dev/null @@ -1,61 +0,0 @@ -use matrix_sdk_crypto::UserIdentities; -use ruma::encryption::CrossSigningKey; - -use crate::CryptoStoreError; - -/// Enum representing cross signing identities of our own user or some other -/// user. -pub enum UserIdentity { - /// Our own user identity. - Own { - /// The unique id of our own user. - user_id: String, - /// Does our own user identity trust our own device. - trusts_our_own_device: bool, - /// The public master key of our identity. - master_key: String, - /// The public user-signing key of our identity. - user_signing_key: String, - /// The public self-signing key of our identity. - self_signing_key: String, - }, - /// The user identity of other users. - Other { - /// The unique id of the user. - user_id: String, - /// The public master key of the identity. - master_key: String, - /// The public self-signing key of our identity. - self_signing_key: String, - }, -} - -impl UserIdentity { - pub(crate) async fn from_rust(i: UserIdentities) -> Result { - Ok(match i { - UserIdentities::Own(i) => { - let master: CrossSigningKey = i.master_key().to_owned().into(); - let user_signing: CrossSigningKey = i.user_signing_key().to_owned().into(); - let self_signing: CrossSigningKey = i.self_signing_key().to_owned().into(); - - UserIdentity::Own { - user_id: i.user_id().to_string(), - trusts_our_own_device: i.trusts_our_own_device().await?, - master_key: serde_json::to_string(&master)?, - user_signing_key: serde_json::to_string(&user_signing)?, - self_signing_key: serde_json::to_string(&self_signing)?, - } - } - UserIdentities::Other(i) => { - let master: CrossSigningKey = i.master_key().to_owned().into(); - let self_signing: CrossSigningKey = i.self_signing_key().to_owned().into(); - - UserIdentity::Other { - user_id: i.user_id().to_string(), - master_key: serde_json::to_string(&master)?, - self_signing_key: serde_json::to_string(&self_signing)?, - } - } - }) - } -} diff --git a/rust-sdk/src/verification.rs b/rust-sdk/src/verification.rs deleted file mode 100644 index 13d7204d92..0000000000 --- a/rust-sdk/src/verification.rs +++ /dev/null @@ -1,232 +0,0 @@ -use matrix_sdk_crypto::{ - CancelInfo as RustCancelInfo, QrVerification as InnerQr, Sas as InnerSas, - VerificationRequest as InnerVerificationRequest, -}; - -use crate::{OutgoingVerificationRequest, SignatureUploadRequest}; - -/// Enum representing the different verification flows we support. -pub enum Verification { - /// The `m.sas.v1` verification flow. - SasV1 { - #[allow(missing_docs)] - 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, - }, -} - -/// The `m.sas.v1` verification flow. -pub struct Sas { - /// The other user that is participating in the verification flow - pub other_user_id: String, - /// The other user's device that is participating in the verification flow - pub other_device_id: String, - /// The unique ID of this verification flow, will be a random string for - /// to-device events or a event ID for in-room events. - pub flow_id: String, - /// The room ID where this verification is happening, will be `None` if the - /// verification is going through to-device messages - pub room_id: Option, - /// Did we initiate the verification flow - pub we_started: bool, - /// Has the non-initiating side accepted the verification flow - pub has_been_accepted: bool, - /// Can the short auth string be presented - pub can_be_presented: bool, - /// Does the flow support the emoji representation of the short auth string - pub supports_emoji: bool, - /// Have we confirmed that the short auth strings match - pub have_we_confirmed: bool, - /// Has the verification completed successfully - pub is_done: bool, - /// Has the flow been cancelled - pub is_cancelled: bool, - /// Information about the cancellation of the flow, will be `None` if the - /// flow hasn't been cancelled - pub cancel_info: Option, -} - -/// The `m.qr_code.scan.v1`, `m.qr_code.show.v1`, and `m.reciprocate.v1` -/// verification flow. -pub struct QrCode { - /// The other user that is participating in the verification flow - pub other_user_id: String, - /// The other user's device that is participating in the verification flow - pub other_device_id: String, - /// The unique ID of this verification flow, will be a random string for - /// to-device events or a event ID for in-room events. - pub flow_id: String, - /// The room ID where this verification is happening, will be `None` if the - /// verification is going through to-device messages - pub room_id: Option, - /// Did we initiate the verification flow - pub we_started: bool, - /// Has the QR code been scanned by the other side - pub other_side_scanned: bool, - /// Has the scanning of the QR code been confirmed by us - pub has_been_confirmed: bool, - /// Did we scan the QR code and sent out a reciprocation - pub reciprocated: bool, - /// Has the verification completed successfully - pub is_done: bool, - /// Has the flow been cancelled - pub is_cancelled: bool, - /// Information about the cancellation of the flow, will be `None` if the - /// flow hasn't been cancelled - pub cancel_info: Option, -} - -impl From for QrCode { - fn from(qr: InnerQr) -> Self { - Self { - other_user_id: qr.other_user_id().to_string(), - flow_id: qr.flow_id().as_str().to_owned(), - is_cancelled: qr.is_cancelled(), - is_done: qr.is_done(), - cancel_info: qr.cancel_info().map(|c| c.into()), - reciprocated: qr.reciprocated(), - we_started: qr.we_started(), - other_side_scanned: qr.has_been_scanned(), - has_been_confirmed: qr.has_been_confirmed(), - other_device_id: qr.other_device_id().to_string(), - room_id: qr.room_id().map(|r| r.to_string()), - } - } -} - -/// Information on why a verification flow has been cancelled and by whom. -pub struct CancelInfo { - /// The textual representation of the cancel reason - pub reason: String, - /// The code describing the cancel reason - pub cancel_code: String, - /// Was the verification flow cancelled by us - pub cancelled_by_us: bool, -} - -impl From for CancelInfo { - fn from(c: RustCancelInfo) -> Self { - Self { - reason: c.reason().to_owned(), - cancel_code: c.cancel_code().to_string(), - cancelled_by_us: c.cancelled_by_us(), - } - } -} - -/// A result type for starting SAS verifications. -pub struct StartSasResult { - /// The SAS verification object that got created. - pub sas: Sas, - /// The request that needs to be sent out to notify the other side that a - /// SAS verification should start. - pub request: OutgoingVerificationRequest, -} - -/// A result type for scanning QR codes. -pub struct ScanResult { - /// The QR code verification object that got created. - pub qr: QrCode, - /// The request that needs to be sent out to notify the other side that a - /// QR code verification should start. - pub request: OutgoingVerificationRequest, -} - -impl From for Sas { - fn from(sas: InnerSas) -> Self { - Self { - other_user_id: sas.other_user_id().to_string(), - other_device_id: sas.other_device_id().to_string(), - flow_id: sas.flow_id().as_str().to_owned(), - is_cancelled: sas.is_cancelled(), - is_done: sas.is_done(), - can_be_presented: sas.can_be_presented(), - supports_emoji: sas.supports_emoji(), - have_we_confirmed: sas.have_we_confirmed(), - we_started: sas.we_started(), - room_id: sas.room_id().map(|r| r.to_string()), - has_been_accepted: sas.has_been_accepted(), - cancel_info: sas.cancel_info().map(|c| c.into()), - } - } -} - -/// A result type for requesting verifications. -pub struct RequestVerificationResult { - /// The verification request object that got created. - pub verification: VerificationRequest, - /// The request that needs to be sent out to notify the other side that - /// we're requesting verification to begin. - pub request: OutgoingVerificationRequest, -} - -/// A result type for confirming verifications. -pub struct ConfirmVerificationResult { - /// The request that needs to be sent out to notify the other side that we - /// confirmed the verification. - pub request: OutgoingVerificationRequest, - /// A request that will upload signatures of the verified device or user, if - /// the verification is completed and we're able to sign devices or users - pub signature_request: Option, -} - -/// The verificatoin request object which then can transition into some concrete -/// verification method -pub struct VerificationRequest { - /// The other user that is participating in the verification flow - pub other_user_id: String, - /// The other user's device that is participating in the verification flow - pub other_device_id: Option, - /// The unique ID of this verification flow, will be a random string for - /// to-device events or a event ID for in-room events. - pub flow_id: String, - /// The room ID where this verification is happening, will be `None` if the - /// verification is going through to-device messages - pub room_id: Option, - /// Did we initiate the verification flow - pub we_started: bool, - /// Did both parties aggree to verification - pub is_ready: bool, - /// Did another device respond to the verification request - pub is_passive: bool, - /// Has the verification completed successfully - pub is_done: bool, - /// Has the flow been cancelled - pub is_cancelled: bool, - /// The list of verification methods that the other side advertised as - /// supported - pub their_methods: Option>, - /// The list of verification methods that we advertised as supported - pub our_methods: Option>, - /// Information about the cancellation of the flow, will be `None` if the - /// flow hasn't been cancelled - pub cancel_info: Option, -} - -impl From for VerificationRequest { - fn from(v: InnerVerificationRequest) -> Self { - Self { - other_user_id: v.other_user().to_string(), - other_device_id: v.other_device_id().map(|d| d.to_string()), - flow_id: v.flow_id().as_str().to_owned(), - is_cancelled: v.is_cancelled(), - is_done: v.is_done(), - is_ready: v.is_ready(), - room_id: v.room_id().map(|r| r.to_string()), - we_started: v.we_started(), - is_passive: v.is_passive(), - cancel_info: v.cancel_info().map(|c| c.into()), - their_methods: v - .their_supported_methods() - .map(|v| v.into_iter().map(|m| m.to_string()).collect()), - our_methods: v - .our_supported_methods() - .map(|v| v.into_iter().map(|m| m.to_string()).collect()), - } - } -}