diff --git a/rust-sdk/src/error.rs b/rust-sdk/src/error.rs index 1d0128a809..8355bd14dd 100644 --- a/rust-sdk/src/error.rs +++ b/rust-sdk/src/error.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use matrix_sdk_crypto::{ store::CryptoStoreError as InnerStoreError, KeyExportError, MegolmError, OlmError, }; diff --git a/rust-sdk/src/lib.rs b/rust-sdk/src/lib.rs index 3eb18a96b3..eb2cb64e8b 100644 --- a/rust-sdk/src/lib.rs +++ b/rust-sdk/src/lib.rs @@ -1,21 +1,43 @@ +#![deny( + dead_code, + missing_docs, + trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications +)] + +//! TODO + mod device; mod error; mod logger; mod machine; mod responses; +mod verification; pub use device::Device; pub use error::{CryptoStoreError, DecryptionError, KeyImportError, MachineCreationError}; pub use logger::{set_logger, Logger}; -pub use machine::{ - CancelInfo, KeyRequestPair, OlmMachine, QrCode, RequestVerificationResult, Sas, ScanResult, - StartSasResult, Verification, VerificationRequest, -}; +pub use machine::{KeyRequestPair, OlmMachine}; pub use responses::{ DeviceLists, KeysImportResult, OutgoingVerificationRequest, Request, RequestType, }; +pub use verification::{ + CancelInfo, 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); } diff --git a/rust-sdk/src/logger.rs b/rust-sdk/src/logger.rs index 3ddef223fd..a29b040572 100644 --- a/rust-sdk/src/logger.rs +++ b/rust-sdk/src/logger.rs @@ -4,8 +4,13 @@ use std::{ }; 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 { @@ -34,6 +39,7 @@ 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)), diff --git a/rust-sdk/src/machine.rs b/rust-sdk/src/machine.rs index 72f269ddce..048f417228 100644 --- a/rust-sdk/src/machine.rs +++ b/rust-sdk/src/machine.rs @@ -30,17 +30,16 @@ use tokio::runtime::Runtime; use matrix_sdk_common::{deserialized_responses::AlgorithmInfo, uuid::Uuid}; use matrix_sdk_crypto::{ - decrypt_key_export, encrypt_key_export, matrix_qrcode::QrVerificationData, - CancelInfo as RustCancelInfo, EncryptionSettings, LocalTrust, OlmMachine as InnerMachine, - QrVerification as InnerQr, Sas as InnerSas, Verification as RustVerification, - VerificationRequest as InnerVerificationRequest, + decrypt_key_export, encrypt_key_export, matrix_qrcode::QrVerificationData, EncryptionSettings, + LocalTrust, OlmMachine as InnerMachine, Verification as RustVerification, }; use crate::{ error::{CryptoStoreError, DecryptionError, MachineCreationError}, responses::{response_from_string, OutgoingVerificationRequest, OwnedResponse}, DecryptedEvent, Device, DeviceLists, KeyImportError, KeysImportResult, ProgressListener, - Request, RequestType, + QrCode, Request, RequestType, RequestVerificationResult, ScanResult, StartSasResult, + Verification, VerificationRequest, }; /// A high level state machine that handles E2EE for Matrix. @@ -49,148 +48,6 @@ pub struct OlmMachine { runtime: Runtime, } -pub enum Verification { - SasV1 { sas: Sas }, - QrCodeV1 { qrcode: QrCode }, -} - -pub struct Sas { - pub other_user_id: String, - pub other_device_id: String, - pub flow_id: String, - pub room_id: Option, - pub have_we_confirmed: bool, - pub is_cancelled: bool, - pub is_done: bool, - pub cancel_info: Option, - pub has_been_accepted: bool, - pub we_started: bool, - pub can_be_presented: bool, - pub supports_emoji: bool, - pub timed_out: bool, -} - -pub struct QrCode { - pub other_user_id: String, - pub flow_id: String, - pub other_device_id: String, - pub room_id: Option, - pub is_cancelled: bool, - pub is_done: bool, - pub we_started: bool, - pub other_side_scanned: bool, - pub has_been_confirmed: bool, - pub reciprocated: bool, - 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()), - } - } -} - -pub struct CancelInfo { - pub reason: String, - pub cancel_code: String, - 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(), - } - } -} - -pub struct StartSasResult { - pub sas: Sas, - pub request: OutgoingVerificationRequest, -} - -pub struct ScanResult { - pub qr: QrCode, - 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(), - timed_out: sas.timed_out(), - 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()), - } - } -} - -pub struct RequestVerificationResult { - pub verification: VerificationRequest, - pub request: OutgoingVerificationRequest, -} - -pub struct VerificationRequest { - pub other_user_id: String, - pub other_device_id: Option, - pub flow_id: String, - pub is_cancelled: bool, - pub is_done: bool, - pub is_ready: bool, - pub room_id: Option, - pub we_started: bool, - pub is_passive: bool, - pub their_methods: Option>, - pub our_methods: Option>, - 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()), - } - } -} - /// A pair of outgoing room key requests, both of those are sendToDevice /// requests. pub struct KeyRequestPair { @@ -254,6 +111,7 @@ impl OlmMachine { .map(|d| d.into())) } + /// Mark the device of the given user with the given device id as trusted. pub fn mark_device_as_trusted( &self, user_id: &str, @@ -696,6 +554,12 @@ impl OlmMachine { 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) = UserId::try_from(user_id) { user_id @@ -710,6 +574,15 @@ impl OlmMachine { .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, @@ -722,6 +595,20 @@ impl OlmMachine { .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, @@ -741,69 +628,59 @@ impl OlmMachine { } } - pub fn get_verification(&self, user_id: &str, flow_id: &str) -> Option { - let user_id = UserId::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() }, - }) - } - - pub fn start_qr_verification( + /// 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, - flow_id: &str, - ) -> Result, CryptoStoreError> { + methods: Vec, + ) -> Result, CryptoStoreError> { let user_id = UserId::try_from(user_id)?; - if let Some(verification) = self.inner.get_verification_request(&user_id, flow_id) { - Ok(self + let identity = self.runtime.block_on(self.inner.get_identity(&user_id))?; + + let methods = methods + .into_iter() + .map(|m| VerificationMethod::from(m)) + .collect(); + + Ok(if let Some(identity) = identity.and_then(|i| i.other()) { + let content = self .runtime - .block_on(verification.generate_qr_code())? - .map(|qr| qr.into())) - } else { - Ok(None) - } - } - - pub fn generate_qr_code(&self, user_id: &str, flow_id: &str) -> Option { - let user_id = UserId::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(|b| encode(b)).ok()) - }) - } - - pub fn scan_qr_code(&self, user_id: &str, flow_id: &str, data: &str) -> Option { - let user_id = UserId::try_from(user_id).ok()?; - // TODO create a custom error type - 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 - } + .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, @@ -835,30 +712,55 @@ impl OlmMachine { }) } - pub fn verification_request_content( + /// 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> { + ) -> Result, CryptoStoreError> { let user_id = UserId::try_from(user_id)?; - let identity = self.runtime.block_on(self.inner.get_identity(&user_id))?; - let methods = methods .into_iter() .map(|m| VerificationMethod::from(m)) .collect(); - Ok(if let Some(identity) = identity.and_then(|i| i.other()) { - let content = self + Ok( + if let Some(device) = self .runtime - .block_on(identity.verification_request_content(Some(methods))); - Some(serde_json::to_string(&content)?) - } else { - None - }) + .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, @@ -885,6 +787,239 @@ impl OlmMachine { }) } + /// 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 = UserId::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 = UserId::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 = UserId::try_from(user_id)?; + + Ok( + if let Some(verification) = self.inner.get_verification(&user_id, flow_id) { + match verification { + RustVerification::SasV1(v) => { + self.runtime.block_on(v.confirm())?.0.map(|r| r.into()) + } + RustVerification::QrV1(v) => v.confirm_scanning().map(|r| r.into()), + } + } 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 = UserId::try_from(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 = UserId::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(|b| encode(b)).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 = UserId::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 = UserId::try_from(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, @@ -909,27 +1044,14 @@ impl OlmMachine { ) } - pub fn start_sas_verification( - &self, - user_id: &str, - flow_id: &str, - ) -> Result, CryptoStoreError> { - let user_id = UserId::try_from(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 - }, - ) - } - + /// 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, @@ -942,49 +1064,19 @@ impl OlmMachine { .and_then(|s| s.accept().map(|r| r.into())) } - pub fn cancel_verification( - &self, - user_id: &str, - flow_id: &str, - cancel_code: &str, - ) -> Option { - let user_id = UserId::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().map(|r| r.into()), - } - } else { - None - } - } - - pub fn confirm_verification( - &self, - user_id: &str, - flow_id: &str, - ) -> Result, CryptoStoreError> { - let user_id = UserId::try_from(user_id)?; - - Ok( - if let Some(verification) = self.inner.get_verification(&user_id, flow_id) { - match verification { - RustVerification::SasV1(v) => { - self.runtime.block_on(v.confirm())?.0.map(|r| r.into()) - } - RustVerification::QrV1(v) => v.confirm_scanning().map(|r| r.into()), - } - } else { - None - }, - ) - } - + /// 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 = UserId::try_from(user_id).ok()?; @@ -998,6 +1090,18 @@ impl OlmMachine { }) } + /// 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 = UserId::try_from(user_id).ok()?; diff --git a/rust-sdk/src/olm.udl b/rust-sdk/src/olm.udl index ac4618b22d..ef0bdd3495 100644 --- a/rust-sdk/src/olm.udl +++ b/rust-sdk/src/olm.udl @@ -83,12 +83,11 @@ dictionary Sas { 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; - boolean can_be_presented; - boolean timed_out; - boolean supports_emoji; CancelInfo? cancel_info; }; @@ -97,18 +96,17 @@ dictionary ScanResult { OutgoingVerificationRequest request; }; - dictionary QrCode { string other_user_id; string other_device_id; string flow_id; string? room_id; - boolean reciprocated; boolean we_started; + boolean other_side_scanned; boolean has_been_confirmed; + boolean reciprocated; boolean is_done; boolean is_cancelled; - boolean other_side_scanned; CancelInfo? cancel_info; }; @@ -116,12 +114,12 @@ dictionary VerificationRequest { string other_user_id; string? other_device_id; string flow_id; - boolean is_cancelled; - boolean is_done; - boolean is_ready; - boolean we_started; - boolean is_passive; 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; @@ -209,12 +207,6 @@ interface OlmMachine { VerificationRequest? get_verification_request([ByRef] string user_id, [ByRef] string flow_id); Verification? get_verification([ByRef] string user_id, [ByRef] string flow_id); - OutgoingVerificationRequest? accept_verification_request( - [ByRef] string user_id, - [ByRef] string flow_id, - sequence methods - ); - [Throws=CryptoStoreError] VerificationRequest? request_verification( [ByRef] string user_id, @@ -230,9 +222,18 @@ interface OlmMachine { [Throws=CryptoStoreError] RequestVerificationResult? request_self_verification(sequence methods); [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); + 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] OutgoingVerificationRequest? confirm_verification([ByRef] string user_id, [ByRef] string flow_id); OutgoingVerificationRequest? cancel_verification( @@ -240,15 +241,20 @@ interface OlmMachine { [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); - sequence? get_emoji_index([ByRef] string user_id, [ByRef] string flow_id); - sequence? get_decimals([ByRef] string user_id, [ByRef] string flow_id); - [Throws=DecryptionError] KeyRequestPair request_room_key([ByRef] string event, [ByRef] string room_id); diff --git a/rust-sdk/src/responses.rs b/rust-sdk/src/responses.rs index b2af4b162a..7ddeb87af7 100644 --- a/rust-sdk/src/responses.rs +++ b/rust-sdk/src/responses.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use std::{collections::HashMap, convert::TryFrom}; use http::Response; diff --git a/rust-sdk/src/verification.rs b/rust-sdk/src/verification.rs new file mode 100644 index 0000000000..d75bc01d3c --- /dev/null +++ b/rust-sdk/src/verification.rs @@ -0,0 +1,222 @@ +use matrix_sdk_crypto::{ + CancelInfo as RustCancelInfo, QrVerification as InnerQr, Sas as InnerSas, + VerificationRequest as InnerVerificationRequest, +}; + +use crate::OutgoingVerificationRequest; + +/// 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, +} + +/// 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()), + } + } +}