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