matrix_sdk_crypto_ffi/
dehydrated_devices.rs1use std::{mem::ManuallyDrop, sync::Arc};
2
3use matrix_sdk_common::executor::Handle;
4use matrix_sdk_crypto::{
5 dehydrated_devices::{
6 DehydratedDevice as InnerDehydratedDevice, DehydratedDevices as InnerDehydratedDevices,
7 RehydratedDevice as InnerRehydratedDevice,
8 },
9 store::types::DehydratedDeviceKey as InnerDehydratedDeviceKey,
10 DecryptionSettings,
11};
12use ruma::{api::client::dehydrated_device, events::AnyToDeviceEvent, serde::Raw, OwnedDeviceId};
13use serde_json::json;
14
15use crate::{CryptoStoreError, DehydratedDeviceKey};
16
17#[derive(Debug, thiserror::Error, uniffi::Error)]
18#[uniffi(flat_error)]
19pub enum DehydrationError {
20 #[error(transparent)]
21 Pickle(#[from] matrix_sdk_crypto::vodozemac::DehydratedDeviceError),
22 #[error(transparent)]
23 LegacyPickle(#[from] matrix_sdk_crypto::vodozemac::LibolmPickleError),
24 #[error(transparent)]
25 MissingSigningKey(#[from] matrix_sdk_crypto::SignatureError),
26 #[error(transparent)]
27 Json(#[from] serde_json::Error),
28 #[error(transparent)]
29 Store(#[from] matrix_sdk_crypto::CryptoStoreError),
30 #[error("The pickle key has an invalid length, expected 32 bytes, got {0}")]
31 PickleKeyLength(usize),
32 #[error(transparent)]
33 Rand(#[from] rand::Error),
34}
35
36impl From<matrix_sdk_crypto::dehydrated_devices::DehydrationError> for DehydrationError {
37 fn from(value: matrix_sdk_crypto::dehydrated_devices::DehydrationError) -> Self {
38 match value {
39 matrix_sdk_crypto::dehydrated_devices::DehydrationError::Json(e) => Self::Json(e),
40 matrix_sdk_crypto::dehydrated_devices::DehydrationError::Pickle(e) => Self::Pickle(e),
41 matrix_sdk_crypto::dehydrated_devices::DehydrationError::LegacyPickle(e) => {
42 Self::LegacyPickle(e)
43 }
44 matrix_sdk_crypto::dehydrated_devices::DehydrationError::MissingSigningKey(e) => {
45 Self::MissingSigningKey(e)
46 }
47 matrix_sdk_crypto::dehydrated_devices::DehydrationError::Store(e) => Self::Store(e),
48 matrix_sdk_crypto::dehydrated_devices::DehydrationError::PickleKeyLength(l) => {
49 Self::PickleKeyLength(l)
50 }
51 }
52 }
53}
54
55#[derive(uniffi::Object)]
56pub struct DehydratedDevices {
57 pub(crate) runtime: Handle,
58 pub(crate) inner: ManuallyDrop<InnerDehydratedDevices>,
59}
60
61impl Drop for DehydratedDevices {
62 fn drop(&mut self) {
63 let _guard = self.runtime.enter();
65 unsafe {
66 ManuallyDrop::drop(&mut self.inner);
67 }
68 }
69}
70
71#[matrix_sdk_ffi_macros::export]
72impl DehydratedDevices {
73 pub fn create(&self) -> Result<Arc<DehydratedDevice>, DehydrationError> {
74 let inner = self.runtime.block_on(self.inner.create())?;
75
76 Ok(Arc::new(DehydratedDevice {
77 inner: ManuallyDrop::new(inner),
78 runtime: self.runtime.to_owned(),
79 }))
80 }
81
82 pub fn rehydrate(
83 &self,
84 pickle_key: &DehydratedDeviceKey,
85 device_id: String,
86 device_data: String,
87 ) -> Result<Arc<RehydratedDevice>, DehydrationError> {
88 let device_data: Raw<_> = serde_json::from_str(&device_data)?;
89 let device_id: OwnedDeviceId = device_id.into();
90
91 let key = InnerDehydratedDeviceKey::from_slice(&pickle_key.inner)?;
92
93 let ret = RehydratedDevice {
94 runtime: self.runtime.to_owned(),
95 inner: ManuallyDrop::new(self.runtime.block_on(self.inner.rehydrate(
96 &key,
97 &device_id,
98 device_data,
99 ))?),
100 }
101 .into();
102
103 Ok(ret)
104 }
105
106 pub fn get_dehydrated_device_key(
114 &self,
115 ) -> Result<Option<crate::DehydratedDeviceKey>, CryptoStoreError> {
116 Ok(self
117 .runtime
118 .block_on(self.inner.get_dehydrated_device_pickle_key())?
119 .map(crate::DehydratedDeviceKey::from))
120 }
121
122 pub fn save_dehydrated_device_key(
127 &self,
128 pickle_key: &crate::DehydratedDeviceKey,
129 ) -> Result<(), CryptoStoreError> {
130 let pickle_key = InnerDehydratedDeviceKey::from_slice(&pickle_key.inner)?;
131 Ok(self.runtime.block_on(self.inner.save_dehydrated_device_pickle_key(&pickle_key))?)
132 }
133
134 pub fn delete_dehydrated_device_key(&self) -> Result<(), CryptoStoreError> {
136 Ok(self.runtime.block_on(self.inner.delete_dehydrated_device_pickle_key())?)
137 }
138}
139
140#[derive(uniffi::Object)]
141pub struct RehydratedDevice {
142 inner: ManuallyDrop<InnerRehydratedDevice>,
143 runtime: Handle,
144}
145
146impl Drop for RehydratedDevice {
147 fn drop(&mut self) {
148 let _guard = self.runtime.enter();
150 unsafe {
151 ManuallyDrop::drop(&mut self.inner);
152 }
153 }
154}
155
156#[matrix_sdk_ffi_macros::export]
157impl RehydratedDevice {
158 pub fn receive_events(
159 &self,
160 events: String,
161 decryption_settings: &DecryptionSettings,
162 ) -> Result<(), crate::CryptoStoreError> {
163 let events: Vec<Raw<AnyToDeviceEvent>> = serde_json::from_str(&events)?;
164 self.runtime.block_on(self.inner.receive_events(events, decryption_settings))?;
165
166 Ok(())
167 }
168}
169
170#[derive(uniffi::Object)]
171pub struct DehydratedDevice {
172 pub(crate) runtime: Handle,
173 pub(crate) inner: ManuallyDrop<InnerDehydratedDevice>,
174}
175
176impl Drop for DehydratedDevice {
177 fn drop(&mut self) {
178 let _guard = self.runtime.enter();
180 unsafe {
181 ManuallyDrop::drop(&mut self.inner);
182 }
183 }
184}
185
186#[matrix_sdk_ffi_macros::export]
187impl DehydratedDevice {
188 pub fn keys_for_upload(
189 &self,
190 device_display_name: String,
191 pickle_key: &DehydratedDeviceKey,
192 ) -> Result<UploadDehydratedDeviceRequest, DehydrationError> {
193 let key = InnerDehydratedDeviceKey::from_slice(&pickle_key.inner)?;
194
195 let request =
196 self.runtime.block_on(self.inner.keys_for_upload(device_display_name, &key))?;
197
198 Ok(request.into())
199 }
200}
201
202#[derive(Debug, uniffi::Record)]
203pub struct UploadDehydratedDeviceRequest {
204 body: String,
206}
207
208impl From<dehydrated_device::put_dehydrated_device::unstable::Request>
209 for UploadDehydratedDeviceRequest
210{
211 fn from(value: dehydrated_device::put_dehydrated_device::unstable::Request) -> Self {
212 let body = json!({
213 "device_id": value.device_id,
214 "device_data": value.device_data,
215 "initial_device_display_name": value.initial_device_display_name,
216 "device_keys": value.device_keys,
217 "one_time_keys": value.one_time_keys,
218 "fallback_keys": value.fallback_keys,
219 });
220
221 let body = serde_json::to_string(&body)
222 .expect("We should be able to serialize the PUT dehydrated devices request body");
223
224 Self { body }
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use crate::{dehydrated_devices::DehydrationError, DehydratedDeviceKey};
231
232 #[test]
233 fn test_creating_dehydrated_key() {
234 let result = DehydratedDeviceKey::new();
235 assert!(result.is_ok());
236 let dehydrated_device_key = result.unwrap();
237 let base_64 = dehydrated_device_key.to_base64();
238 let inner_bytes = dehydrated_device_key.inner;
239
240 let copy = DehydratedDeviceKey::from_slice(&inner_bytes).unwrap();
241
242 assert_eq!(base_64, copy.to_base64());
243 }
244
245 #[test]
246 fn test_creating_dehydrated_key_failure() {
247 let bytes = [0u8; 24];
248
249 let pickle_key = DehydratedDeviceKey::from_slice(&bytes);
250
251 assert!(pickle_key.is_err());
252
253 match pickle_key {
254 Err(DehydrationError::PickleKeyLength(pickle_key_length)) => {
255 assert_eq!(bytes.len(), pickle_key_length);
256 }
257 _ => panic!("Should have failed!"),
258 }
259 }
260}