Skip to main content

openmls/treesync/node/
encryption_keys.rs

1use std::fmt::Debug;
2
3use openmls_traits::{
4    crypto::OpenMlsCrypto,
5    random::OpenMlsRand,
6    storage::{StorageProvider as StorageProviderTrait, CURRENT_VERSION},
7    types::{Ciphersuite, HpkeCiphertext, HpkeKeyPair},
8};
9use serde::{Deserialize, Serialize};
10use tls_codec::{TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize, VLBytes};
11
12use crate::{
13    ciphersuite::{hpke, HpkePrivateKey, HpkePublicKey, Secret},
14    error::LibraryError,
15    storage::{OpenMlsProvider, StorageProvider},
16};
17
18/// [`EncryptionKey`] contains an HPKE public key that allows the encryption of
19/// path secrets in MLS commits.
20#[derive(
21    Debug,
22    Clone,
23    Serialize,
24    Deserialize,
25    TlsSerialize,
26    TlsDeserialize,
27    TlsDeserializeBytes,
28    TlsSize,
29    PartialEq,
30    Eq,
31    Hash,
32)]
33pub struct EncryptionKey {
34    key: HpkePublicKey,
35}
36
37impl EncryptionKey {
38    /// Return the internal [`HpkePublicKey`].
39    pub(crate) fn key(&self) -> &HpkePublicKey {
40        &self.key
41    }
42
43    /// Return the internal [`HpkePublicKey`] as slice.
44    pub(crate) fn as_slice(&self) -> &[u8] {
45        self.key.as_slice()
46    }
47
48    /// Encrypt to this HPKE public key.
49    pub(crate) fn encrypt(
50        &self,
51        crypto: &impl OpenMlsCrypto,
52        ciphersuite: Ciphersuite,
53        context: &[u8],
54        plaintext: &[u8],
55    ) -> Result<HpkeCiphertext, LibraryError> {
56        hpke::encrypt_with_label(
57            self.as_slice(),
58            "UpdatePathNode",
59            context,
60            plaintext,
61            ciphersuite,
62            crypto,
63        )
64        .map_err(|_| LibraryError::custom("Encryption failed. A serialization issue really"))
65    }
66}
67
68#[cfg(feature = "targeted-messages-draft")]
69impl EncryptionKey {
70    pub(crate) fn encrypt_with_label_psk_resolved_aad<F>(
71        &self,
72        params: hpke::PskEncryptParams,
73        plaintext: &[u8],
74        crypto: &impl OpenMlsCrypto,
75        aad_builder: F,
76    ) -> Result<HpkeCiphertext, LibraryError>
77    where
78        F: FnOnce(&[u8]) -> Result<Vec<u8>, LibraryError>,
79    {
80        hpke::encrypt_with_label_psk_resolved_aad(
81            self.as_slice(),
82            params,
83            plaintext,
84            crypto,
85            aad_builder,
86        )
87    }
88}
89
90impl From<Vec<u8>> for EncryptionKey {
91    fn from(key: Vec<u8>) -> Self {
92        Self { key: key.into() }
93    }
94}
95
96#[derive(
97    Clone, Serialize, Deserialize, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize,
98)]
99#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Eq))]
100pub struct EncryptionPrivateKey {
101    key: HpkePrivateKey,
102}
103
104impl Debug for EncryptionPrivateKey {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        let mut ds = f.debug_struct("EncryptionPrivateKey");
107
108        #[cfg(feature = "crypto-debug")]
109        ds.field("key", &self.key);
110        #[cfg(not(feature = "crypto-debug"))]
111        ds.field("key", &"***");
112
113        ds.finish()
114    }
115}
116
117impl From<Vec<u8>> for EncryptionPrivateKey {
118    fn from(key: Vec<u8>) -> Self {
119        Self { key: key.into() }
120    }
121}
122
123impl From<HpkePrivateKey> for EncryptionPrivateKey {
124    fn from(key: HpkePrivateKey) -> Self {
125        Self { key }
126    }
127}
128
129impl EncryptionPrivateKey {
130    /// Decrypt a given `HpkeCiphertext` using this [`EncryptionPrivateKey`] and
131    /// `group_context`.
132    ///
133    /// Returns the decrypted [`Secret`]. Returns an error if the decryption was
134    /// unsuccessful.
135    pub(crate) fn decrypt(
136        &self,
137        crypto: &impl OpenMlsCrypto,
138        ciphersuite: Ciphersuite,
139        ciphertext: &HpkeCiphertext,
140        group_context: &[u8],
141    ) -> Result<Secret, hpke::Error> {
142        // ValSem203: Path secrets must decrypt correctly
143        hpke::decrypt_with_label(
144            &self.key,
145            "UpdatePathNode",
146            group_context,
147            ciphertext,
148            ciphersuite,
149            crypto,
150        )
151        .map(|secret_bytes| Secret::from_slice(&secret_bytes))
152    }
153
154    #[cfg(feature = "targeted-messages-draft")]
155    pub(crate) fn decrypt_with_label_psk_aad(
156        &self,
157        params: hpke::PskEncryptParams,
158        aad: &[u8],
159        ciphertext: &HpkeCiphertext,
160        crypto: &impl OpenMlsCrypto,
161    ) -> Result<Vec<u8>, hpke::Error> {
162        hpke::decrypt_with_label_psk_aad(&self.key, params, aad, ciphertext, crypto)
163    }
164}
165
166#[cfg(any(test, feature = "test-utils"))]
167impl EncryptionPrivateKey {
168    pub(crate) fn key(&self) -> &HpkePrivateKey {
169        &self.key
170    }
171}
172
173impl From<HpkePublicKey> for EncryptionKey {
174    fn from(key: HpkePublicKey) -> Self {
175        Self { key }
176    }
177}
178
179#[derive(
180    Debug, Clone, Serialize, Deserialize, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize,
181)]
182#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Eq))]
183pub(crate) struct EncryptionKeyPair {
184    public_key: EncryptionKey,
185    private_key: EncryptionPrivateKey,
186}
187
188impl EncryptionKeyPair {
189    /// Write the [`EncryptionKeyPair`] to the store of the `provider`.
190    ///
191    /// This must only be used for encryption key pairs that are generated for
192    /// update leaf nodes. All other encryption key pairs are stored as part
193    /// of the key package or the epoch encryption key pairs.
194    pub(crate) fn write<Storage: StorageProvider>(
195        &self,
196        store: &Storage,
197    ) -> Result<(), Storage::Error> {
198        store.write_encryption_key_pair(self.public_key(), self)
199    }
200
201    /// Read the [`EncryptionKeyPair`] from the key store of the `provider`. This
202    /// function is meant to read standalone keypairs, not ones that are
203    /// already in use with an MLS group.
204    ///
205    /// This must only be used for encryption key pairs that are generated for
206    /// update leaf nodes. All other encryption key pairs are stored as part
207    /// of the key package or the epoch encryption key pairs.
208    ///
209    /// Returns `None` if the keypair cannot be read from the store.
210    pub(crate) fn read(
211        provider: &impl OpenMlsProvider,
212        encryption_key: &EncryptionKey,
213    ) -> Option<EncryptionKeyPair> {
214        provider
215            .storage()
216            .encryption_key_pair(encryption_key)
217            .ok()
218            .flatten()
219    }
220
221    /// Delete the [`EncryptionKeyPair`] from the store of the `provider`.
222    ///
223    /// This must only be used for encryption key pairs that are generated for
224    /// update leaf nodes. All other encryption key pairs are stored as part
225    /// of the key package or the epoch encryption key pairs.
226    pub(crate) fn delete<Storage: StorageProviderTrait<CURRENT_VERSION>>(
227        &self,
228        store: &Storage,
229    ) -> Result<(), Storage::Error> {
230        store.delete_encryption_key_pair(self.public_key())
231    }
232
233    pub(crate) fn public_key(&self) -> &EncryptionKey {
234        &self.public_key
235    }
236
237    pub(crate) fn private_key(&self) -> &EncryptionPrivateKey {
238        &self.private_key
239    }
240
241    pub(crate) fn random(
242        rand: &impl OpenMlsRand,
243        crypto: &impl OpenMlsCrypto,
244        ciphersuite: Ciphersuite,
245    ) -> Result<Self, LibraryError> {
246        let ikm =
247            Secret::random(ciphersuite, rand).map_err(LibraryError::unexpected_crypto_error)?;
248        Ok(crypto
249            .derive_hpke_keypair(ciphersuite.hpke_config(), ikm.as_slice())
250            .map_err(LibraryError::unexpected_crypto_error)?
251            .into())
252    }
253}
254
255#[cfg(feature = "test-utils")]
256pub mod test_utils {
257    use super::*;
258
259    pub fn read_keys_from_key_store(
260        provider: &impl OpenMlsProvider,
261        encryption_key: &EncryptionKey,
262    ) -> HpkeKeyPair {
263        let keys = EncryptionKeyPair::read(provider, encryption_key).unwrap();
264
265        HpkeKeyPair {
266            private: keys.private_key.key,
267            public: keys.public_key.key.as_slice().to_vec(),
268        }
269    }
270
271    pub fn write_keys_from_key_store(provider: &impl OpenMlsProvider, encryption_key: HpkeKeyPair) {
272        let keypair = EncryptionKeyPair::from(encryption_key);
273
274        keypair.write(provider.storage()).unwrap();
275    }
276}
277
278#[cfg(test)]
279impl EncryptionKeyPair {
280    /// Build a key pair from raw bytes for testing.
281    pub(crate) fn from_raw(public_key: Vec<u8>, private_key: Vec<u8>) -> Self {
282        Self {
283            public_key: EncryptionKey {
284                key: public_key.into(),
285            },
286            private_key: EncryptionPrivateKey {
287                key: private_key.into(),
288            },
289        }
290    }
291}
292
293impl From<(HpkePublicKey, HpkePrivateKey)> for EncryptionKeyPair {
294    fn from((public_key, private_key): (HpkePublicKey, HpkePrivateKey)) -> Self {
295        Self {
296            public_key: public_key.into(),
297            private_key: private_key.into(),
298        }
299    }
300}
301
302impl From<HpkeKeyPair> for EncryptionKeyPair {
303    fn from(hpke_keypair: HpkeKeyPair) -> Self {
304        let public_bytes: VLBytes = hpke_keypair.public.into();
305        let private_bytes = hpke_keypair.private;
306        Self {
307            public_key: public_bytes.into(),
308            private_key: private_bytes.into(),
309        }
310    }
311}
312
313impl From<(EncryptionKey, EncryptionPrivateKey)> for EncryptionKeyPair {
314    fn from((public_key, private_key): (EncryptionKey, EncryptionPrivateKey)) -> Self {
315        Self {
316            public_key,
317            private_key,
318        }
319    }
320}