1use openmls::prelude::Ciphersuite;
2use openmls_basic_credential::SignatureKeyPair;
3use openmls_traits::key_store::{MlsEntity, MlsEntityId};
4
5use crate::{
6 CryptoKeystoreError, CryptoKeystoreResult, Database, Sha256Hash,
7 entities::{
8 PersistedMlsGroup, PersistedMlsPendingGroup, StoredCredential, StoredEncryptionKeyPair,
9 StoredEpochEncryptionKeypair, StoredHpkePrivateKey, StoredKeypackage, StoredPskBundle,
10 },
11 traits::{BorrowPrimaryKey, Entity, EntityDatabaseMutation, EntityDeleteBorrowed, FetchFromDatabase as _},
12};
13
14impl Database {
16 async fn save<E>(&self, entity: E) -> CryptoKeystoreResult<E::AutoGeneratedFields>
17 where
18 E: Entity + EntityDatabaseMutation + Send + Sync,
19 {
20 self.with_transaction(async |tx| tx.save(entity).await).await
21 }
22
23 async fn remove_borrowed<E>(&self, id: &E::BorrowedPrimaryKey) -> CryptoKeystoreResult<()>
24 where
25 E: Entity + EntityDatabaseMutation + BorrowPrimaryKey + EntityDeleteBorrowed,
26 {
27 self.with_transaction(async |tx| tx.remove_borrowed::<E>(id).await)
28 .await
29 }
30}
31
32impl Database {
33 pub async fn mls_fetch_key_packages<V: MlsEntity>(&self, count: u32) -> CryptoKeystoreResult<Vec<V>> {
42 let keypackages = StoredKeypackage::load_all(&*self.conn().await)?;
43 Ok(keypackages
44 .into_iter()
45 .filter_map(|kpb| postcard::from_bytes(&kpb.keypackage).ok())
46 .take(count as _)
47 .collect())
48 }
49
50 pub async fn mls_group_exists(&self, group_id: impl AsRef<[u8]> + Send) -> bool {
56 matches!(
57 self.get_borrowed::<PersistedMlsGroup>(group_id.as_ref()).await,
58 Ok(Some(_))
59 )
60 }
61
62 pub async fn mls_group_persist(
72 &self,
73 group_id: impl AsRef<[u8]> + Send,
74 state: &[u8],
75 sender_nonce: u32,
76 parent_group_id: Option<&[u8]>,
77 ) -> CryptoKeystoreResult<()> {
78 self.save(PersistedMlsGroup {
79 id: group_id.as_ref().to_owned(),
80 state: state.into(),
81 sender_nonce,
82 parent_id: parent_group_id.map(Into::into),
83 })
84 .await?;
85 Ok(())
86 }
87
88 pub async fn mls_groups_restore(
95 &self,
96 ) -> CryptoKeystoreResult<std::collections::HashMap<Vec<u8>, (Option<Vec<u8>>, Vec<u8>)>> {
97 let groups = PersistedMlsGroup::load_all(&*self.conn().await)?;
98 Ok(groups
99 .into_iter()
100 .map(|mut group| {
101 let id = std::mem::take(&mut group.id);
102 let parent_id = std::mem::take(&mut group.parent_id);
103 let state = std::mem::take(&mut group.state);
104 (id, (parent_id, state))
105 })
106 .collect())
107 }
108
109 pub async fn mls_group_delete(&self, group_id: impl AsRef<[u8]> + Send) -> CryptoKeystoreResult<()> {
114 self.remove_borrowed::<PersistedMlsGroup>(group_id.as_ref()).await?;
115 Ok(())
116 }
117
118 pub async fn mls_pending_groups_save(
130 &self,
131 group_id: impl AsRef<[u8]> + Send,
132 mls_group: &[u8],
133 custom_configuration: &[u8],
134 parent_group_id: Option<&[u8]>,
135 ) -> CryptoKeystoreResult<()> {
136 self.save(PersistedMlsPendingGroup {
137 id: group_id.as_ref().to_owned(),
138 state: mls_group.into(),
139 custom_configuration: custom_configuration.into(),
140 parent_id: parent_group_id.map(Into::into),
141 })
142 .await?;
143 Ok(())
144 }
145
146 pub async fn mls_pending_groups_load(
155 &self,
156 group_id: impl AsRef<[u8]> + Send,
157 ) -> CryptoKeystoreResult<Option<(Vec<u8>, Vec<u8>)>> {
158 let optional = self.get_borrowed::<PersistedMlsPendingGroup>(group_id.as_ref()).await?;
159 Ok(optional.map(|pending_group| (pending_group.state.clone(), pending_group.custom_configuration.clone())))
160 }
161
162 pub async fn mls_pending_groups_delete(&self, group_id: impl AsRef<[u8]> + Send) -> CryptoKeystoreResult<()> {
171 self.remove_borrowed::<PersistedMlsPendingGroup>(group_id.as_ref())
172 .await?;
173 Ok(())
174 }
175}
176
177#[inline(always)]
178pub fn deser<T: MlsEntity>(bytes: &[u8]) -> Result<T, CryptoKeystoreError> {
179 Ok(postcard::from_bytes(bytes)?)
180}
181
182#[inline(always)]
183pub fn ser<T: MlsEntity>(value: &T) -> Result<Vec<u8>, CryptoKeystoreError> {
184 Ok(postcard::to_stdvec(value)?)
185}
186
187#[cfg_attr(target_os = "unknown", async_trait::async_trait(?Send))]
188#[cfg_attr(not(target_os = "unknown"), async_trait::async_trait)]
189impl openmls_traits::key_store::OpenMlsKeyStore for Database {
190 type Error = CryptoKeystoreError;
191
192 async fn store<V: MlsEntity + Sync>(&self, id: &[u8], value: &V) -> Result<(), Self::Error>
193 where
194 Self: Sized,
195 {
196 if id.is_empty() {
197 return Err(CryptoKeystoreError::MlsKeyStoreError(
198 "The provided key is empty".into(),
199 ));
200 }
201
202 let data = ser(value)?;
203
204 match V::ID {
205 MlsEntityId::GroupState => {
206 return Err(CryptoKeystoreError::IncorrectApiUsage(
207 "Groups must not be saved using OpenMLS's APIs. You should use the keystore's provided methods",
208 ));
209 }
210 MlsEntityId::SignatureKeyPair => {
211 return Err(CryptoKeystoreError::IncorrectApiUsage(
212 "Signature keys must not be saved using OpenMLS's APIs. Save a credential via the keystore API
213 instead.",
214 ));
215 }
216 MlsEntityId::KeyPackage => {
217 let kp = StoredKeypackage {
218 keypackage_ref: id.into(),
219 keypackage: data,
220 };
221 self.save(kp).await?;
222 }
223 MlsEntityId::HpkePrivateKey => {
224 let kp = StoredHpkePrivateKey {
225 pk: id.into(),
226 sk: data,
227 };
228 self.save(kp).await?;
229 }
230 MlsEntityId::PskBundle => {
231 let kp = StoredPskBundle {
232 psk_id: id.into(),
233 psk: data,
234 };
235 self.save(kp).await?;
236 }
237 MlsEntityId::EncryptionKeyPair => {
238 let kp = StoredEncryptionKeyPair {
239 pk: id.into(),
240 sk: data,
241 };
242 self.save(kp).await?;
243 }
244 MlsEntityId::EpochEncryptionKeyPair => {
245 let kp = StoredEpochEncryptionKeypair {
246 id: id.into(),
247 keypairs: data,
248 };
249 self.save(kp).await?;
250 }
251 }
252
253 Ok(())
254 }
255
256 async fn read<V: MlsEntity>(&self, id: &[u8]) -> Option<V>
257 where
258 Self: Sized,
259 {
260 if id.is_empty() {
261 return None;
262 }
263
264 match V::ID {
265 MlsEntityId::GroupState => {
266 let v = self.get_borrowed::<PersistedMlsGroup>(id).await.ok().flatten()?;
267 deser(&v.state).ok()
268 }
269 MlsEntityId::SignatureKeyPair => {
270 let conn = &*self.conn().await;
271 let hash = Sha256Hash::from_existing_hash(id).ok()?;
272 let stored_credential = StoredCredential::get(conn, &hash).ok().flatten()?;
273 let ciphersuite = Ciphersuite::try_from(stored_credential.ciphersuite).ok()?;
274 let signature_scheme = ciphersuite.signature_algorithm();
275
276 let mls_keypair = SignatureKeyPair::from_raw(
277 signature_scheme,
278 stored_credential.private_key.to_vec(),
279 stored_credential.public_key.to_vec(),
280 );
281
282 let data = ser(&mls_keypair).ok()?;
285 deser(&data).ok()
286 }
287 MlsEntityId::KeyPackage => {
288 let v = self.get_borrowed::<StoredKeypackage>(id).await.ok().flatten()?;
289 deser(&v.keypackage).ok()
290 }
291 MlsEntityId::HpkePrivateKey => {
292 let v = self.get_borrowed::<StoredHpkePrivateKey>(id).await.ok().flatten()?;
293 deser(&v.sk).ok()
294 }
295 MlsEntityId::PskBundle => {
296 let v = self.get_borrowed::<StoredPskBundle>(id).await.ok().flatten()?;
297 deser(&v.psk).ok()
298 }
299 MlsEntityId::EncryptionKeyPair => {
300 let v = self.get_borrowed::<StoredEncryptionKeyPair>(id).await.ok().flatten()?;
301 deser(&v.sk).ok()
302 }
303 MlsEntityId::EpochEncryptionKeyPair => {
304 let v = self
305 .get_borrowed::<StoredEpochEncryptionKeypair>(id)
306 .await
307 .ok()
308 .flatten()?;
309 deser(&v.keypairs).ok()
310 }
311 }
312 }
313
314 async fn delete<V: MlsEntity>(&self, id: &[u8]) -> Result<(), Self::Error> {
315 match V::ID {
316 MlsEntityId::GroupState => self.remove_borrowed::<PersistedMlsGroup>(id).await?,
317 MlsEntityId::SignatureKeyPair => unimplemented!(
318 "Deleting a signature key pair should not be done through this API, any keypair should be deleted via
319 deleting a credential."
320 ),
321 MlsEntityId::HpkePrivateKey => self.remove_borrowed::<StoredHpkePrivateKey>(id).await?,
322 MlsEntityId::KeyPackage => self.remove_borrowed::<StoredKeypackage>(id).await?,
323 MlsEntityId::PskBundle => self.remove_borrowed::<StoredPskBundle>(id).await?,
324 MlsEntityId::EncryptionKeyPair => self.remove_borrowed::<StoredEncryptionKeyPair>(id).await?,
325 MlsEntityId::EpochEncryptionKeyPair => self.remove_borrowed::<StoredEpochEncryptionKeypair>(id).await?,
326 }
327
328 Ok(())
329 }
330}