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