diff --git a/rust-sdk/Cargo.toml b/rust-sdk/Cargo.toml index 66723de16a..2e5618d727 100644 --- a/rust-sdk/Cargo.toml +++ b/rust-sdk/Cargo.toml @@ -25,11 +25,11 @@ features = ["lax_deserialize"] [dependencies.matrix-sdk-common] git = "https://github.com/matrix-org/matrix-rust-sdk/" -rev = "3a8ff2f6b43f312b7582146ed712ff245ef9d5aa" +rev = "b2ff6cb6ae3d1983a510262021cba27a1bc70d77" [dependencies.matrix-sdk-crypto] git = "https://github.com/matrix-org/matrix-rust-sdk/" -rev = "3a8ff2f6b43f312b7582146ed712ff245ef9d5aa" +rev = "b2ff6cb6ae3d1983a510262021cba27a1bc70d77" features = ["sled_cryptostore"] [dependencies.tokio] @@ -38,8 +38,12 @@ default_features = false features = ["rt-multi-thread"] [dependencies.ruma] -version = "0.2.0" +version = "0.3.0" features = ["client-api"] [build-dependencies] uniffi_build = "0.12.0" + +[patch.crates-io] +ruma = { git = "https://github.com/matrix-org/ruma/", branch = "secrets" } +ruma-identifiers = { git = "https://github.com/matrix-org/ruma", branch = "secrets" } diff --git a/rust-sdk/src/error.rs b/rust-sdk/src/error.rs index 8355bd14dd..dff404f456 100644 --- a/rust-sdk/src/error.rs +++ b/rust-sdk/src/error.rs @@ -2,17 +2,10 @@ use matrix_sdk_crypto::{ store::CryptoStoreError as InnerStoreError, KeyExportError, MegolmError, OlmError, + SignatureError as InnerSignatureError, SecretImportError as RustSecretImportError, }; use ruma::identifiers::Error as RumaIdentifierError; -#[derive(Debug, thiserror::Error)] -pub enum MachineCreationError { - #[error(transparent)] - Identifier(#[from] RumaIdentifierError), - #[error(transparent)] - CryptoStore(#[from] InnerStoreError), -} - #[derive(Debug, thiserror::Error)] pub enum KeyImportError { #[error(transparent)] @@ -21,6 +14,28 @@ pub enum KeyImportError { CryptoStore(#[from] InnerStoreError), } +#[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)] diff --git a/rust-sdk/src/lib.rs b/rust-sdk/src/lib.rs index eb2cb64e8b..41842f7b6d 100644 --- a/rust-sdk/src/lib.rs +++ b/rust-sdk/src/lib.rs @@ -15,15 +15,18 @@ mod error; mod logger; mod machine; mod responses; +mod users; mod verification; pub use device::Device; -pub use error::{CryptoStoreError, DecryptionError, KeyImportError, MachineCreationError}; +pub use error::{CryptoStoreError, DecryptionError, KeyImportError, SignatureError, SecretImportError}; pub use logger::{set_logger, Logger}; pub use machine::{KeyRequestPair, OlmMachine}; pub use responses::{ - DeviceLists, KeysImportResult, OutgoingVerificationRequest, Request, RequestType, + DeviceLists, KeysImportResult, OutgoingVerificationRequest, Request, RequestType, SignatureUploadRequest, + BootstrapCrossSigningResult, UploadSigningKeysRequest, }; +pub use users::UserIdentity; pub use verification::{ CancelInfo, QrCode, RequestVerificationResult, Sas, ScanResult, StartSasResult, Verification, VerificationRequest, @@ -55,4 +58,59 @@ pub struct DecryptedEvent { 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, +} + +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 Into for CrossSigningKeyExport { + fn into(self) -> matrix_sdk_crypto::CrossSigningKeyExport { + matrix_sdk_crypto::CrossSigningKeyExport { + master_key: self.master_key, + self_signing_key: self.self_signing_key, + user_signing_key: self.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, + } + } +} + include!(concat!(env!("OUT_DIR"), "/olm.uniffi.rs")); diff --git a/rust-sdk/src/machine.rs b/rust-sdk/src/machine.rs index 048f417228..0be6f0fde1 100644 --- a/rust-sdk/src/machine.rs +++ b/rust-sdk/src/machine.rs @@ -12,6 +12,7 @@ use ruma::{ 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, @@ -31,14 +32,15 @@ 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, EncryptionSettings, - LocalTrust, OlmMachine as InnerMachine, Verification as RustVerification, + LocalTrust, OlmMachine as InnerMachine, UserIdentities, Verification as RustVerification, }; use crate::{ - error::{CryptoStoreError, DecryptionError, MachineCreationError}, + error::{CryptoStoreError, DecryptionError, SecretImportError, SignatureError}, responses::{response_from_string, OutgoingVerificationRequest, OwnedResponse}, - DecryptedEvent, Device, DeviceLists, KeyImportError, KeysImportResult, ProgressListener, - QrCode, Request, RequestType, RequestVerificationResult, ScanResult, StartSasResult, + BootstrapCrossSigningResult, CrossSigningKeyExport, CrossSigningStatus, DecryptedEvent, Device, + DeviceLists, KeyImportError, KeysImportResult, ProgressListener, QrCode, Request, RequestType, + RequestVerificationResult, ScanResult, SignatureUploadRequest, StartSasResult, UserIdentity, Verification, VerificationRequest, }; @@ -68,7 +70,7 @@ impl OlmMachine { /// * `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 { + pub fn new(user_id: &str, device_id: &str, path: &str) -> Result { let user_id = UserId::try_from(user_id)?; let device_id = device_id.into(); let runtime = Runtime::new().unwrap(); @@ -91,6 +93,67 @@ impl OlmMachine { 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.dislpay_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 = UserId::try_from(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 = UserId::try_from(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 = UserId::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 @@ -111,6 +174,39 @@ impl OlmMachine { .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 = UserId::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, @@ -206,6 +302,9 @@ impl OlmMachine { RequestType::KeysClaim => { KeysClaimResponse::try_from_http_response(response).map(Into::into) } + RequestType::SignatureUpload => { + SignatureUploadResponse::try_from_http_response(response).map(Into::into) + } } .expect("Can't convert json string to response"); @@ -305,21 +404,7 @@ impl OlmMachine { Ok(self .runtime .block_on(self.inner.get_missing_sessions(users.iter()))? - .map(|(request_id, request)| 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(), - })) + .map(|r| r.into())) } /// Share a room key with the given list of users for the given room. @@ -867,6 +952,8 @@ impl OlmMachine { if let Some(verification) = self.inner.get_verification(&user_id, flow_id) { match verification { RustVerification::SasV1(v) => { + // TODO there's a signature upload request here, we'll + // want to return that one as well. self.runtime.block_on(v.confirm())?.0.map(|r| r.into()) } RustVerification::QrV1(v) => v.confirm_scanning().map(|r| r.into()), @@ -1114,4 +1201,49 @@ impl OlmMachine { }) }) } + + /// 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(()) + } } diff --git a/rust-sdk/src/olm.udl b/rust-sdk/src/olm.udl index ef0bdd3495..f54c00cba2 100644 --- a/rust-sdk/src/olm.udl +++ b/rust-sdk/src/olm.udl @@ -10,18 +10,28 @@ callback interface ProgressListener { void on_progress(i32 progress, i32 total); }; -[Error] -enum MachineCreationError { - "Identifier", - "CryptoStore", -}; - [Error] enum KeyImportError { "Export", "CryptoStore", }; +[Error] +enum SignatureError { + "Signature", + "Identifier", + "CryptoStore", + "UnknownDevice", + "UnknownUserIdentity", +}; + +[Error] +enum SecretImportError { + "Import", + "CryptoStore", +}; + + [Error] enum CryptoStoreError { "CryptoStore", @@ -65,6 +75,45 @@ dictionary Device { 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; @@ -155,6 +204,11 @@ interface Request { KeysQuery(string request_id, sequence users); KeysClaim(string request_id, record> one_time_keys); RoomMessage(string request_id, string room_id, string event_type, string content); + SignatureUpload(string request_id, string body); +}; + +dictionary SignatureUploadRequest { + string body; }; enum RequestType { @@ -162,10 +216,11 @@ enum RequestType { "KeysClaim", "KeysUpload", "ToDevice", + "SignatureUpload", }; interface OlmMachine { - [Throws=MachineCreationError] + [Throws=CryptoStoreError] constructor([ByRef] string user_id, [ByRef] string device_id, [ByRef] string path); record identity_keys(); @@ -190,10 +245,16 @@ interface OlmMachine { [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); @@ -268,4 +329,13 @@ interface OlmMachine { ); [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); }; diff --git a/rust-sdk/src/responses.rs b/rust-sdk/src/responses.rs index 7ddeb87af7..122324761c 100644 --- a/rust-sdk/src/responses.rs +++ b/rust-sdk/src/responses.rs @@ -3,13 +3,18 @@ use std::{collections::HashMap, convert::TryFrom}; use http::Response; +use matrix_sdk_common::uuid::Uuid; use serde_json::json; use ruma::{ api::client::r0::{ keys::{ - claim_keys::Response as KeysClaimResponse, get_keys::Response as KeysQueryResponse, + 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, @@ -21,9 +26,65 @@ use ruma::{ use matrix_sdk_crypto::{ IncomingResponse, OutgoingRequest, OutgoingVerificationRequest as SdkVerificationRequest, - RoomMessageRequest, ToDeviceRequest, + 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, @@ -87,6 +148,10 @@ pub enum Request { event_type: String, content: String, }, + SignatureUpload { + request_id: String, + body: String, + }, } impl From for Request { @@ -114,8 +179,13 @@ impl From for Request { } } ToDeviceRequest(t) => Request::from(t), - SignatureUpload(_) => todo!("Uploading signatures isn't yet supported"), + 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(), } } } @@ -130,6 +200,28 @@ impl From for Request { } } +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 { @@ -163,6 +255,7 @@ pub enum RequestType { KeysClaim, KeysUpload, ToDevice, + SignatureUpload, } pub struct DeviceLists { @@ -197,6 +290,7 @@ pub(crate) enum OwnedResponse { KeysUpload(KeysUploadResponse), KeysQuery(KeysQueryResponse), ToDevice(ToDeviceResponse), + SignatureUpload(SignatureUploadResponse), } impl From for OwnedResponse { @@ -223,6 +317,12 @@ impl From for OwnedResponse { } } +impl From for OwnedResponse { + fn from(response: SignatureUploadResponse) -> Self { + Self::SignatureUpload(response) + } +} + impl<'a> Into> for &'a OwnedResponse { fn into(self) -> IncomingResponse<'a> { match self { @@ -230,6 +330,7 @@ impl<'a> Into> for &'a OwnedResponse { OwnedResponse::KeysQuery(r) => IncomingResponse::KeysQuery(r), OwnedResponse::KeysUpload(r) => IncomingResponse::KeysUpload(r), OwnedResponse::ToDevice(r) => IncomingResponse::ToDevice(r), + OwnedResponse::SignatureUpload(r) => IncomingResponse::SignatureUpload(r), } } } diff --git a/rust-sdk/src/users.rs b/rust-sdk/src/users.rs new file mode 100644 index 0000000000..4c1a05a55a --- /dev/null +++ b/rust-sdk/src/users.rs @@ -0,0 +1,61 @@ +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)?, + } + } + }) + } +}