|
| 1 | +use std::{mem::ManuallyDrop, sync::Arc}; |
| 2 | + |
| 3 | +use matrix_sdk_crypto::dehydrated_devices::{ |
| 4 | + DehydratedDevice as InnerDehydratedDevice, DehydratedDevices as InnerDehydratedDevices, |
| 5 | + RehydratedDevice as InnerRehydratedDevice, |
| 6 | +}; |
| 7 | +use ruma::{api::client::dehydrated_device, events::AnyToDeviceEvent, serde::Raw, OwnedDeviceId}; |
| 8 | +use serde_json::json; |
| 9 | +use tokio::runtime::Handle; |
| 10 | +use zeroize::Zeroize; |
| 11 | + |
| 12 | +#[derive(Debug, thiserror::Error, uniffi::Error)] |
| 13 | +#[uniffi(flat_error)] |
| 14 | +pub enum DehydrationError { |
| 15 | + #[error(transparent)] |
| 16 | + Pickle(#[from] matrix_sdk_crypto::vodozemac::LibolmPickleError), |
| 17 | + #[error(transparent)] |
| 18 | + MissingSigningKey(#[from] matrix_sdk_crypto::SignatureError), |
| 19 | + #[error(transparent)] |
| 20 | + Json(#[from] serde_json::Error), |
| 21 | + #[error("The pickle key has an invalid length, expected 32 bytes, got {0}")] |
| 22 | + PickleKeyLength(usize), |
| 23 | +} |
| 24 | + |
| 25 | +impl From<matrix_sdk_crypto::dehydrated_devices::DehydrationError> for DehydrationError { |
| 26 | + fn from(value: matrix_sdk_crypto::dehydrated_devices::DehydrationError) -> Self { |
| 27 | + match value { |
| 28 | + matrix_sdk_crypto::dehydrated_devices::DehydrationError::Json(e) => Self::Json(e), |
| 29 | + matrix_sdk_crypto::dehydrated_devices::DehydrationError::Pickle(e) => Self::Pickle(e), |
| 30 | + matrix_sdk_crypto::dehydrated_devices::DehydrationError::MissingSigningKey(e) => { |
| 31 | + Self::MissingSigningKey(e) |
| 32 | + } |
| 33 | + } |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +#[derive(uniffi::Object)] |
| 38 | +pub struct DehydratedDevices { |
| 39 | + pub(crate) runtime: Handle, |
| 40 | + pub(crate) inner: ManuallyDrop<InnerDehydratedDevices>, |
| 41 | +} |
| 42 | + |
| 43 | +impl Drop for DehydratedDevices { |
| 44 | + fn drop(&mut self) { |
| 45 | + // See the drop implementation for the `crate::OlmMachine` for an explanation. |
| 46 | + let inner = unsafe { ManuallyDrop::take(&mut self.inner) }; |
| 47 | + let _guard = self.runtime.enter(); |
| 48 | + drop(inner); |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +#[uniffi::export] |
| 53 | +impl DehydratedDevices { |
| 54 | + pub fn create(&self) -> Arc<DehydratedDevice> { |
| 55 | + DehydratedDevice { |
| 56 | + inner: ManuallyDrop::new(self.inner.create()), |
| 57 | + runtime: self.runtime.to_owned(), |
| 58 | + } |
| 59 | + .into() |
| 60 | + } |
| 61 | + |
| 62 | + pub fn rehydrate( |
| 63 | + &self, |
| 64 | + pickle_key: Vec<u8>, |
| 65 | + device_id: String, |
| 66 | + device_data: String, |
| 67 | + ) -> Result<Arc<RehydratedDevice>, DehydrationError> { |
| 68 | + let device_data: Raw<_> = serde_json::from_str(&device_data)?; |
| 69 | + let device_id: OwnedDeviceId = device_id.into(); |
| 70 | + |
| 71 | + let mut key = get_pickle_key(&pickle_key)?; |
| 72 | + |
| 73 | + let ret = RehydratedDevice { |
| 74 | + runtime: self.runtime.to_owned(), |
| 75 | + inner: ManuallyDrop::new(self.runtime.block_on(self.inner.rehydrate( |
| 76 | + &key, |
| 77 | + &device_id, |
| 78 | + device_data, |
| 79 | + ))?), |
| 80 | + } |
| 81 | + .into(); |
| 82 | + |
| 83 | + key.zeroize(); |
| 84 | + |
| 85 | + Ok(ret) |
| 86 | + } |
| 87 | +} |
| 88 | + |
| 89 | +#[derive(uniffi::Object)] |
| 90 | +pub struct RehydratedDevice { |
| 91 | + inner: ManuallyDrop<InnerRehydratedDevice>, |
| 92 | + runtime: Handle, |
| 93 | +} |
| 94 | + |
| 95 | +impl Drop for RehydratedDevice { |
| 96 | + fn drop(&mut self) { |
| 97 | + // See the drop implementation for the `crate::OlmMachine` for an explanation. |
| 98 | + let inner = unsafe { ManuallyDrop::take(&mut self.inner) }; |
| 99 | + let _guard = self.runtime.enter(); |
| 100 | + drop(inner); |
| 101 | + } |
| 102 | +} |
| 103 | + |
| 104 | +#[uniffi::export] |
| 105 | +impl RehydratedDevice { |
| 106 | + pub fn receive_events(&self, events: String) -> Result<(), crate::CryptoStoreError> { |
| 107 | + let events: Vec<Raw<AnyToDeviceEvent>> = serde_json::from_str(&events)?; |
| 108 | + self.runtime.block_on(self.inner.receive_events(events))?; |
| 109 | + |
| 110 | + Ok(()) |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +#[derive(uniffi::Object)] |
| 115 | +pub struct DehydratedDevice { |
| 116 | + pub(crate) runtime: Handle, |
| 117 | + pub(crate) inner: ManuallyDrop<InnerDehydratedDevice>, |
| 118 | +} |
| 119 | + |
| 120 | +impl Drop for DehydratedDevice { |
| 121 | + fn drop(&mut self) { |
| 122 | + // See the drop implementation for the `crate::OlmMachine` for an explanation. |
| 123 | + let inner = unsafe { ManuallyDrop::take(&mut self.inner) }; |
| 124 | + let _guard = self.runtime.enter(); |
| 125 | + drop(inner); |
| 126 | + } |
| 127 | +} |
| 128 | + |
| 129 | +#[uniffi::export] |
| 130 | +impl DehydratedDevice { |
| 131 | + pub fn keys_for_upload( |
| 132 | + &self, |
| 133 | + device_display_name: String, |
| 134 | + pickle_key: Vec<u8>, |
| 135 | + ) -> Result<UploadDehydratedDeviceRequest, DehydrationError> { |
| 136 | + let mut key = get_pickle_key(&pickle_key)?; |
| 137 | + |
| 138 | + let request = |
| 139 | + self.runtime.block_on(self.inner.keys_for_upload(device_display_name, &key))?; |
| 140 | + |
| 141 | + key.zeroize(); |
| 142 | + |
| 143 | + Ok(request.into()) |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +#[derive(Debug, uniffi::Record)] |
| 148 | +pub struct UploadDehydratedDeviceRequest { |
| 149 | + /// The serialized JSON body of the request. |
| 150 | + body: String, |
| 151 | +} |
| 152 | + |
| 153 | +impl From<dehydrated_device::put_dehydrated_device::unstable::Request> |
| 154 | + for UploadDehydratedDeviceRequest |
| 155 | +{ |
| 156 | + fn from(value: dehydrated_device::put_dehydrated_device::unstable::Request) -> Self { |
| 157 | + let body = json!({ |
| 158 | + "device_id": value.device_id, |
| 159 | + "device_data": value.device_data, |
| 160 | + "initial_device_display_name": value.initial_device_display_name, |
| 161 | + "device_keys": value.device_keys, |
| 162 | + "one_time_keys": value.one_time_keys, |
| 163 | + "fallback_keys": value.fallback_keys, |
| 164 | + }); |
| 165 | + |
| 166 | + let body = serde_json::to_string(&body) |
| 167 | + .expect("We should be able to serialize the PUT dehydrated devices request body"); |
| 168 | + |
| 169 | + Self { body } |
| 170 | + } |
| 171 | +} |
| 172 | + |
| 173 | +fn get_pickle_key(pickle_key: &[u8]) -> Result<Box<[u8; 32]>, DehydrationError> { |
| 174 | + let pickle_key_length = pickle_key.len(); |
| 175 | + |
| 176 | + if pickle_key_length == 32 { |
| 177 | + let mut key = Box::new([0u8; 32]); |
| 178 | + key.copy_from_slice(pickle_key); |
| 179 | + |
| 180 | + Ok(key) |
| 181 | + } else { |
| 182 | + Err(DehydrationError::PickleKeyLength(pickle_key_length)) |
| 183 | + } |
| 184 | +} |
0 commit comments