Skip to main content

core_crypto_keystore/
mls.rs

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, StoredCredential, StoredEncryptionKeyPair, StoredEpochEncryptionKeypair,
9        StoredHpkePrivateKey, StoredKeyPackage, StoredPskBundle,
10    },
11    traits::{Entity, EntityDatabaseMutation, EntityDeleteBorrowed, FetchFromDatabase as _},
12};
13
14/// convenience methods to modify the in-flight transaction
15impl Database {
16    async fn save<E>(&self, entity: E) -> CryptoKeystoreResult<E::AutoGeneratedFields>
17    where
18        E: 'static + 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: EntityDeleteBorrowed,
26    {
27        self.with_transaction(async |tx| tx.remove_borrowed::<E>(id).await)
28            .await
29    }
30}
31
32#[inline(always)]
33pub fn deser<T: MlsEntity>(bytes: &[u8]) -> Result<T, CryptoKeystoreError> {
34    Ok(postcard::from_bytes(bytes)?)
35}
36
37#[inline(always)]
38pub fn ser<T: MlsEntity>(value: &T) -> Result<Vec<u8>, CryptoKeystoreError> {
39    Ok(postcard::to_stdvec(value)?)
40}
41
42#[cfg_attr(target_os = "unknown", async_trait::async_trait(?Send))]
43#[cfg_attr(not(target_os = "unknown"), async_trait::async_trait)]
44impl openmls_traits::key_store::OpenMlsKeyStore for Database {
45    type Error = CryptoKeystoreError;
46
47    async fn store<V: MlsEntity + Sync>(&self, id: &[u8], value: &V) -> Result<(), Self::Error>
48    where
49        Self: Sized,
50    {
51        if id.is_empty() {
52            return Err(CryptoKeystoreError::MlsKeyStoreError(
53                "The provided key is empty".into(),
54            ));
55        }
56
57        let data = ser(value)?;
58
59        match V::ID {
60            MlsEntityId::GroupState => {
61                return Err(CryptoKeystoreError::IncorrectApiUsage(
62                    "Groups must not be saved using OpenMLS's APIs. You should use the keystore's provided methods",
63                ));
64            }
65            MlsEntityId::SignatureKeyPair => {
66                return Err(CryptoKeystoreError::IncorrectApiUsage(
67                    "Signature keys must not be saved using OpenMLS's APIs. Save a credential via the keystore API
68                    instead.",
69                ));
70            }
71            MlsEntityId::KeyPackage => {
72                let kp = StoredKeyPackage {
73                    key_package_ref: id.into(),
74                    key_package: data,
75                };
76                self.save(kp).await?;
77            }
78            MlsEntityId::HpkePrivateKey => {
79                let kp = StoredHpkePrivateKey {
80                    pk: id.into(),
81                    sk: data,
82                };
83                self.save(kp).await?;
84            }
85            MlsEntityId::PskBundle => {
86                let kp = StoredPskBundle {
87                    psk_id: id.into(),
88                    psk: data,
89                };
90                self.save(kp).await?;
91            }
92            MlsEntityId::EncryptionKeyPair => {
93                let kp = StoredEncryptionKeyPair {
94                    pk: id.into(),
95                    sk: data,
96                };
97                self.save(kp).await?;
98            }
99            MlsEntityId::EpochEncryptionKeyPair => {
100                let kp = StoredEpochEncryptionKeypair {
101                    id: id.into(),
102                    keypairs: data,
103                };
104                self.save(kp).await?;
105            }
106        }
107
108        Ok(())
109    }
110
111    async fn read<V: MlsEntity>(&self, id: &[u8]) -> Option<V>
112    where
113        Self: Sized,
114    {
115        if id.is_empty() {
116            return None;
117        }
118
119        match V::ID {
120            MlsEntityId::GroupState => {
121                let v = self.get_borrowed::<PersistedMlsGroup>(id).await.ok().flatten()?;
122                deser(&v.state).ok()
123            }
124            MlsEntityId::SignatureKeyPair => {
125                let conn = &*self.conn().await;
126                let hash = Sha256Hash::from_existing_hash(id).ok()?;
127                let stored_credential = StoredCredential::get(conn, &hash).ok().flatten()?;
128                let ciphersuite = Ciphersuite::try_from(stored_credential.ciphersuite).ok()?;
129                let signature_scheme = ciphersuite.signature_algorithm();
130
131                let mls_keypair = SignatureKeyPair::from_raw(
132                    signature_scheme,
133                    stored_credential.private_key.to_vec(),
134                    stored_credential.public_key.to_vec(),
135                );
136
137                // In a well designed interface, something like this should not be necessary. However, we don't have
138                // a well-designed interface.
139                let data = ser(&mls_keypair).ok()?;
140                deser(&data).ok()
141            }
142            MlsEntityId::KeyPackage => {
143                let v = self.get_borrowed::<StoredKeyPackage>(id).await.ok().flatten()?;
144                deser(&v.key_package).ok()
145            }
146            MlsEntityId::HpkePrivateKey => {
147                let v = self.get_borrowed::<StoredHpkePrivateKey>(id).await.ok().flatten()?;
148                deser(&v.sk).ok()
149            }
150            MlsEntityId::PskBundle => {
151                let v = self.get_borrowed::<StoredPskBundle>(id).await.ok().flatten()?;
152                deser(&v.psk).ok()
153            }
154            MlsEntityId::EncryptionKeyPair => {
155                let v = self.get_borrowed::<StoredEncryptionKeyPair>(id).await.ok().flatten()?;
156                deser(&v.sk).ok()
157            }
158            MlsEntityId::EpochEncryptionKeyPair => {
159                let v = self
160                    .get_borrowed::<StoredEpochEncryptionKeypair>(id)
161                    .await
162                    .ok()
163                    .flatten()?;
164                deser(&v.keypairs).ok()
165            }
166        }
167    }
168
169    async fn delete<V: MlsEntity>(&self, id: &[u8]) -> Result<(), Self::Error> {
170        match V::ID {
171            MlsEntityId::GroupState => self.remove_borrowed::<PersistedMlsGroup>(id).await?,
172            MlsEntityId::SignatureKeyPair => unimplemented!(
173                "Deleting a signature key pair should not be done through this API, any keypair should be deleted via
174                deleting a credential."
175            ),
176            MlsEntityId::HpkePrivateKey => self.remove_borrowed::<StoredHpkePrivateKey>(id).await?,
177            MlsEntityId::KeyPackage => self.remove_borrowed::<StoredKeyPackage>(id).await?,
178            MlsEntityId::PskBundle => self.remove_borrowed::<StoredPskBundle>(id).await?,
179            MlsEntityId::EncryptionKeyPair => self.remove_borrowed::<StoredEncryptionKeyPair>(id).await?,
180            MlsEntityId::EpochEncryptionKeyPair => self.remove_borrowed::<StoredEpochEncryptionKeypair>(id).await?,
181        }
182
183        Ok(())
184    }
185}