mls_rs_core/
crypto.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Copyright by contributors to this project.
3// SPDX-License-Identifier: (Apache-2.0 OR MIT)
4
5use crate::error::IntoAnyError;
6use alloc::vec;
7use alloc::vec::Vec;
8use core::{
9    fmt::{self, Debug},
10    ops::Deref,
11};
12use mls_rs_codec::{MlsDecode, MlsEncode, MlsSize};
13use zeroize::{ZeroizeOnDrop, Zeroizing};
14
15mod cipher_suite;
16pub use self::cipher_suite::*;
17
18#[cfg(feature = "test_suite")]
19pub mod test_suite;
20
21#[derive(Clone, PartialEq, Eq, MlsSize, MlsEncode, MlsDecode)]
22#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(
25    all(feature = "ffi", not(test)),
26    safer_ffi_gen::ffi_type(clone, opaque)
27)]
28/// Ciphertext produced by [`CipherSuiteProvider::hpke_seal`]
29pub struct HpkeCiphertext {
30    #[mls_codec(with = "mls_rs_codec::byte_vec")]
31    #[cfg_attr(feature = "serde", serde(with = "crate::vec_serde"))]
32    pub kem_output: Vec<u8>,
33    #[mls_codec(with = "mls_rs_codec::byte_vec")]
34    #[cfg_attr(feature = "serde", serde(with = "crate::vec_serde"))]
35    pub ciphertext: Vec<u8>,
36}
37
38impl Debug for HpkeCiphertext {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        f.debug_struct("HpkeCiphertext")
41            .field("kem_output", &crate::debug::pretty_bytes(&self.kem_output))
42            .field("ciphertext", &crate::debug::pretty_bytes(&self.ciphertext))
43            .finish()
44    }
45}
46
47/// Byte representation of an HPKE public key. For ciphersuites using elliptic curves,
48/// the public key should be represented in the uncompressed format.
49#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, MlsSize, MlsDecode, MlsEncode)]
50#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
51#[cfg_attr(
52    all(feature = "ffi", not(test)),
53    safer_ffi_gen::ffi_type(clone, opaque)
54)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
56pub struct HpkePublicKey(
57    #[mls_codec(with = "mls_rs_codec::byte_vec")]
58    #[cfg_attr(feature = "serde", serde(with = "crate::vec_serde"))]
59    Vec<u8>,
60);
61
62impl Debug for HpkePublicKey {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        crate::debug::pretty_bytes(&self.0)
65            .named("HpkePublicKey")
66            .fmt(f)
67    }
68}
69
70impl From<Vec<u8>> for HpkePublicKey {
71    fn from(data: Vec<u8>) -> Self {
72        Self(data)
73    }
74}
75
76impl From<HpkePublicKey> for Vec<u8> {
77    fn from(data: HpkePublicKey) -> Self {
78        data.0
79    }
80}
81
82impl Deref for HpkePublicKey {
83    type Target = [u8];
84
85    fn deref(&self) -> &Self::Target {
86        &self.0
87    }
88}
89
90impl AsRef<[u8]> for HpkePublicKey {
91    fn as_ref(&self) -> &[u8] {
92        &self.0
93    }
94}
95
96/// Byte representation of an HPKE secret key.
97#[derive(Clone, PartialEq, Eq, MlsSize, MlsEncode, MlsDecode, ZeroizeOnDrop)]
98#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
99#[cfg_attr(
100    all(feature = "ffi", not(test)),
101    safer_ffi_gen::ffi_type(clone, opaque)
102)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
104pub struct HpkeSecretKey(
105    #[mls_codec(with = "mls_rs_codec::byte_vec")]
106    #[cfg_attr(feature = "serde", serde(with = "crate::vec_serde"))]
107    Vec<u8>,
108);
109
110impl Debug for HpkeSecretKey {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        crate::debug::pretty_bytes(&self.0)
113            .named("HpkeSecretKey")
114            .fmt(f)
115    }
116}
117
118impl From<Vec<u8>> for HpkeSecretKey {
119    fn from(data: Vec<u8>) -> Self {
120        Self(data)
121    }
122}
123
124impl Deref for HpkeSecretKey {
125    type Target = [u8];
126
127    fn deref(&self) -> &Self::Target {
128        &self.0
129    }
130}
131
132impl AsRef<[u8]> for HpkeSecretKey {
133    fn as_ref(&self) -> &[u8] {
134        &self.0
135    }
136}
137
138/// The HPKE context for sender outputted by [hpke_setup_s](CipherSuiteProvider::hpke_setup_s).
139/// The context internally stores the secrets generated by [hpke_setup_s](CipherSuiteProvider::hpke_setup_s).
140///
141/// This trait corresponds to ContextS from RFC 9180.
142#[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
143#[cfg_attr(all(target_arch = "wasm32", mls_build_async), maybe_async::must_be_async(?Send))]
144#[cfg_attr(
145    all(not(target_arch = "wasm32"), mls_build_async),
146    maybe_async::must_be_async
147)]
148pub trait HpkeContextS {
149    type Error: IntoAnyError;
150
151    /// Encrypt `data` using the cipher key of the context with optional `aad`.
152    /// This function should internally increment the sequence number.
153    async fn seal(&mut self, aad: Option<&[u8]>, data: &[u8]) -> Result<Vec<u8>, Self::Error>;
154
155    /// Export a secret from the context for the given `exporter_context`.
156    async fn export(&self, exporter_context: &[u8], len: usize) -> Result<Vec<u8>, Self::Error>;
157}
158
159/// The HPKE context for receiver outputted by [hpke_setup_r](CipherSuiteProvider::hpke_setup_r).
160/// The context internally stores secrets received from the sender by [hpke_setup_r](CipherSuiteProvider::hpke_setup_r).
161///
162/// This trait corresponds to ContextR from RFC 9180.
163#[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
164#[cfg_attr(all(target_arch = "wasm32", mls_build_async), maybe_async::must_be_async(?Send))]
165#[cfg_attr(
166    all(not(target_arch = "wasm32"), mls_build_async),
167    maybe_async::must_be_async
168)]
169pub trait HpkeContextR {
170    type Error: IntoAnyError;
171
172    /// Decrypt `ciphertext` using the cipher key of the context with optional `aad`.
173    /// This function should internally increment the sequence number.
174    async fn open(&mut self, aad: Option<&[u8]>, ciphertext: &[u8])
175        -> Result<Vec<u8>, Self::Error>;
176
177    /// Export a secret from the context for the given `exporter_context`.
178    async fn export(&self, exporter_context: &[u8], len: usize) -> Result<Vec<u8>, Self::Error>;
179}
180
181/// Byte representation of a signature public key. For ciphersuites using elliptic curves,
182/// the public key should be represented in the uncompressed format.
183#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd, MlsSize, MlsEncode, MlsDecode)]
184#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
185#[cfg_attr(all(feature = "ffi", not(test)), ::safer_ffi_gen::ffi_type(opaque))]
186#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
187pub struct SignaturePublicKey(
188    #[mls_codec(with = "mls_rs_codec::byte_vec")]
189    #[cfg_attr(feature = "serde", serde(with = "crate::vec_serde"))]
190    Vec<u8>,
191);
192
193impl Debug for SignaturePublicKey {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        crate::debug::pretty_bytes(&self.0)
196            .named("SignaturePublicKey")
197            .fmt(f)
198    }
199}
200
201#[cfg_attr(all(feature = "ffi", not(test)), ::safer_ffi_gen::safer_ffi_gen)]
202impl SignaturePublicKey {
203    pub fn new(bytes: Vec<u8>) -> Self {
204        bytes.into()
205    }
206
207    pub fn new_slice(data: &[u8]) -> Self {
208        Self(data.to_vec())
209    }
210
211    pub fn as_bytes(&self) -> &[u8] {
212        &self.0
213    }
214}
215
216impl Deref for SignaturePublicKey {
217    type Target = [u8];
218
219    fn deref(&self) -> &Self::Target {
220        &self.0
221    }
222}
223
224impl AsRef<[u8]> for SignaturePublicKey {
225    fn as_ref(&self) -> &[u8] {
226        &self.0
227    }
228}
229
230impl From<Vec<u8>> for SignaturePublicKey {
231    fn from(data: Vec<u8>) -> Self {
232        SignaturePublicKey(data)
233    }
234}
235
236impl From<SignaturePublicKey> for Vec<u8> {
237    fn from(value: SignaturePublicKey) -> Self {
238        value.0
239    }
240}
241
242/// Byte representation of a signature key.
243#[cfg_attr(
244    all(feature = "ffi", not(test)),
245    ::safer_ffi_gen::ffi_type(clone, opaque)
246)]
247#[derive(Clone, PartialEq, Eq, ZeroizeOnDrop, MlsSize, MlsEncode, MlsDecode)]
248#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
249pub struct SignatureSecretKey {
250    #[mls_codec(with = "mls_rs_codec::byte_vec")]
251    #[cfg_attr(feature = "serde", serde(with = "crate::vec_serde"))]
252    bytes: Vec<u8>,
253}
254
255impl Debug for SignatureSecretKey {
256    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257        crate::debug::pretty_bytes(&self.bytes)
258            .named("SignatureSecretKey")
259            .fmt(f)
260    }
261}
262
263#[cfg_attr(all(feature = "ffi", not(test)), ::safer_ffi_gen::safer_ffi_gen)]
264impl SignatureSecretKey {
265    pub fn new(bytes: Vec<u8>) -> Self {
266        bytes.into()
267    }
268
269    pub fn new_slice(data: &[u8]) -> Self {
270        Self {
271            bytes: data.to_vec(),
272        }
273    }
274
275    pub fn as_bytes(&self) -> &[u8] {
276        &self.bytes
277    }
278}
279
280impl From<Vec<u8>> for SignatureSecretKey {
281    fn from(bytes: Vec<u8>) -> Self {
282        Self { bytes }
283    }
284}
285
286impl Deref for SignatureSecretKey {
287    type Target = Vec<u8>;
288
289    fn deref(&self) -> &Self::Target {
290        &self.bytes
291    }
292}
293
294impl AsRef<[u8]> for SignatureSecretKey {
295    fn as_ref(&self) -> &[u8] {
296        &self.bytes
297    }
298}
299
300/// Provides implementations for several ciphersuites via [`CipherSuiteProvider`].
301pub trait CryptoProvider: Send + Sync {
302    type CipherSuiteProvider: CipherSuiteProvider + Clone;
303
304    /// Return the list of all supported ciphersuites.
305    fn supported_cipher_suites(&self) -> Vec<CipherSuite>;
306
307    /// Generate a [CipherSuiteProvider] for the given `cipher_suite`.
308    fn cipher_suite_provider(&self, cipher_suite: CipherSuite)
309        -> Option<Self::CipherSuiteProvider>;
310}
311
312/// Provides all cryptographic operations required by MLS for a given cipher suite.
313#[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
314#[cfg_attr(all(target_arch = "wasm32", mls_build_async), maybe_async::must_be_async(?Send))]
315#[cfg_attr(
316    all(not(target_arch = "wasm32"), mls_build_async),
317    maybe_async::must_be_async
318)]
319pub trait CipherSuiteProvider: Send + Sync {
320    type Error: IntoAnyError;
321
322    type HpkeContextS: HpkeContextS + Send + Sync;
323    type HpkeContextR: HpkeContextR + Send + Sync;
324
325    /// Return the implemented MLS [CipherSuite](CipherSuite).
326    fn cipher_suite(&self) -> CipherSuite;
327
328    /// Compute the hash of `data`.
329    async fn hash(&self, data: &[u8]) -> Result<Vec<u8>, Self::Error>;
330
331    /// Compute the MAC tag of `data` using the `key` of length [kdf_extract_size](CipherSuiteProvider::kdf_extract_size).
332    /// Verifying a MAC tag of `data` using `key` is done by calling this function
333    /// and checking that the result matches the tag.
334    async fn mac(&self, key: &[u8], data: &[u8]) -> Result<Vec<u8>, Self::Error>;
335
336    /// Encrypt `data` with public additional authenticated data `aad`, using additional `nonce`
337    /// (sometimes called the initialization vector, IV). The output should include
338    /// the authentication tag, if used by the given AEAD implementation (for example,
339    /// the tag can be appended to the ciphertext).
340    async fn aead_seal(
341        &self,
342        key: &[u8],
343        data: &[u8],
344        aad: Option<&[u8]>,
345        nonce: &[u8],
346    ) -> Result<Vec<u8>, Self::Error>;
347
348    /// Decrypt the `ciphertext` generated by [aead_seal](CipherSuiteProvider::aead_seal).
349    /// This function should return an error if any of the inputs `key`, `aad` or `nonce` does not match
350    /// the corresponding input passed to [aead_seal](CipherSuiteProvider::aead_seal) to generate `ciphertext`.
351    async fn aead_open(
352        &self,
353        key: &[u8],
354        ciphertext: &[u8],
355        aad: Option<&[u8]>,
356        nonce: &[u8],
357    ) -> Result<Zeroizing<Vec<u8>>, Self::Error>;
358
359    /// Return the length of the secret key `key` passed to [aead_seal](CipherSuiteProvider::aead_seal)
360    /// and [aead_open](CipherSuiteProvider::aead_open).
361    fn aead_key_size(&self) -> usize;
362
363    /// Return the length of the `nonce` passed to [aead_seal](CipherSuiteProvider::aead_seal)
364    /// and [aead_open](CipherSuiteProvider::aead_open).
365    fn aead_nonce_size(&self) -> usize;
366
367    /// Generate a pseudo-random key `prk` extracted from the initial key
368    /// material `ikm`, using an optional random `salt`. The outputted `prk` should have
369    /// [kdf_extract_size](CipherSuiteProvider::kdf_extract_size) bytes. It can be used
370    /// as input to [kdf_expand](CipherSuiteProvider::kdf_expand).
371    ///
372    /// This function corresponds to the HKDF-Extract function from RFC 5869.
373    async fn kdf_extract(&self, salt: &[u8], ikm: &[u8])
374        -> Result<Zeroizing<Vec<u8>>, Self::Error>;
375
376    /// Generate key material of the desired length `len` by expanding the given pseudo-random key
377    /// `prk` of length [kdf_extract_size](CipherSuiteProvider::kdf_extract_size).
378    /// The additional input `info` contains optional context data.
379    ///
380    /// This function corresponds to the HKDF-Expand function from RFC 5869.
381    async fn kdf_expand(
382        &self,
383        prk: &[u8],
384        info: &[u8],
385        len: usize,
386    ) -> Result<Zeroizing<Vec<u8>>, Self::Error>;
387
388    /// Return the size of pseudo-random key `prk` outputted by [kdf_extract](CipherSuiteProvider::kdf_extract)
389    /// and inputted to [kdf_expand](CipherSuiteProvider::kdf_expand).
390    fn kdf_extract_size(&self) -> usize;
391
392    /// Encrypt the plaintext `pt` with optional public additional authenticated data `aad` to the
393    /// public key `remote_key` using additional context information `info` (which can be empty if
394    /// not needed). This function combines the action
395    /// of the [hpke_setup_s](CipherSuiteProvider::hpke_setup_s) and then calling [seal](HpkeContextS::seal)
396    /// on the resulting [HpkeContextS](self::HpkeContextS).
397    ///
398    /// This function corresponds to the one-shot API in base mode in RFC 9180.
399    async fn hpke_seal(
400        &self,
401        remote_key: &HpkePublicKey,
402        info: &[u8],
403        aad: Option<&[u8]>,
404        pt: &[u8],
405    ) -> Result<HpkeCiphertext, Self::Error>;
406
407    /// Decrypt the `ciphertext` generated by [hpke_seal](CipherSuiteProvider::hpke_seal).
408    /// This function combines the action of the [hpke_setup_r](CipherSuiteProvider::hpke_setup_r)
409    /// and then calling [open](HpkeContextR::open) on the resulting [HpkeContextR](self::HpkeContextR).
410    ///
411    /// This function corresponds to the one-shot API in base mode in RFC 9180.
412    async fn hpke_open(
413        &self,
414        ciphertext: &HpkeCiphertext,
415        local_secret: &HpkeSecretKey,
416        local_public: &HpkePublicKey,
417        info: &[u8],
418        aad: Option<&[u8]>,
419    ) -> Result<Vec<u8>, Self::Error>;
420
421    /// Generate a tuple containing the ciphertext `kem_output` that can
422    /// be used as the input to [hpke_setup_r](CipherSuiteProvider::hpke_setup_r),
423    /// as well as the sender context [HpkeContextS](self::HpkeContextS) that can be
424    /// used to generate AEAD ciphertexts and export keys.
425    ///
426    /// The inputted `remote_key` will normally be generated using
427    /// [kem_derive](CipherSuiteProvider::kem_derive) or
428    /// [kem_generate](CipherSuiteProvider::kem_generate). However, the function
429    /// should return an error if the format is incorrect.
430    ///
431    /// This function corresponds to the SetupBaseS function from RFC 9180.
432    async fn hpke_setup_s(
433        &self,
434        remote_key: &HpkePublicKey,
435        info: &[u8],
436    ) -> Result<(Vec<u8>, Self::HpkeContextS), Self::Error>;
437
438    /// Receive the ciphertext `kem_output` generated by [hpke_setup_s](CipherSuiteProvider::hpke_setup_s)
439    /// and the `local_secret` corresponding to the `remote_key` used as input to
440    /// [hpke_setup_s](CipherSuiteProvider::hpke_setup_s). The ouput is the receiver context
441    /// [HpkeContextR](self::HpkeContextR) that can be used to decrypt AEAD ciphertexts
442    /// generated by the sender context [HpkeContextS](self::HpkeContextS) outputted by
443    /// [hpke_setup_r](CipherSuiteProvider::hpke_setup_r)
444    /// and export the same keys as that context.
445    ///
446    /// The inputted `local_secret` will normally be generated using
447    /// [kem_derive](CipherSuiteProvider::kem_derive) or
448    /// [kem_generate](CipherSuiteProvider::kem_generate). However, the function
449    /// should return an error if the format is incorrect.
450    ///
451    /// This function corresponds to the SetupBaseR function from RFC 9180.
452    async fn hpke_setup_r(
453        &self,
454        kem_output: &[u8],
455        local_secret: &HpkeSecretKey,
456        local_public: &HpkePublicKey,
457
458        info: &[u8],
459    ) -> Result<Self::HpkeContextR, Self::Error>;
460
461    /// Derive from the initial key material `ikm` the KEM keys used as inputs to
462    /// [hpke_setup_r](CipherSuiteProvider::hpke_setup_r),
463    /// [hpke_setup_s](CipherSuiteProvider::hpke_setup_s), [hpke_seal](CipherSuiteProvider::hpke_seal)
464    /// and [hpke_open](CipherSuiteProvider::hpke_open).
465    async fn kem_derive(&self, ikm: &[u8]) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error>;
466
467    /// Generate fresh KEM keys to be used as inputs to [hpke_setup_r](CipherSuiteProvider::hpke_setup_r),
468    /// [hpke_setup_s](CipherSuiteProvider::hpke_setup_s), [hpke_seal](CipherSuiteProvider::hpke_seal)
469    /// and [hpke_open](CipherSuiteProvider::hpke_open).
470    async fn kem_generate(&self) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error>;
471
472    /// Verify that the given byte vector `key` can be decoded as an HPKE public key.
473    fn kem_public_key_validate(&self, key: &HpkePublicKey) -> Result<(), Self::Error>;
474
475    /// Fill `out` with random bytes.
476    fn random_bytes(&self, out: &mut [u8]) -> Result<(), Self::Error>;
477
478    /// Generate `count` bytes of pseudorandom bytes as a vector. This is a shortcut for
479    /// creating a `Vec<u8>` of `count` bytes and calling [random_bytes](CipherSuiteProvider::random_bytes).
480    fn random_bytes_vec(&self, count: usize) -> Result<Vec<u8>, Self::Error> {
481        let mut vec = vec![0u8; count];
482        self.random_bytes(&mut vec)?;
483
484        Ok(vec)
485    }
486
487    /// Generate fresh signature keys to be used as inputs to [sign](CipherSuiteProvider::sign)
488    /// and [verify](CipherSuiteProvider::verify)
489    async fn signature_key_generate(
490        &self,
491    ) -> Result<(SignatureSecretKey, SignaturePublicKey), Self::Error>;
492
493    /// Output a public key corresponding to `secret_key`.
494    async fn signature_key_derive_public(
495        &self,
496        secret_key: &SignatureSecretKey,
497    ) -> Result<SignaturePublicKey, Self::Error>;
498
499    /// Sign `data` using `secret_key`.
500    async fn sign(
501        &self,
502        secret_key: &SignatureSecretKey,
503        data: &[u8],
504    ) -> Result<Vec<u8>, Self::Error>;
505
506    /// Verify that the secret key corresponding to `public_key` created the `signature` over `data`.
507    async fn verify(
508        &self,
509        public_key: &SignaturePublicKey,
510        signature: &[u8],
511        data: &[u8],
512    ) -> Result<(), Self::Error>;
513}