Skip to main content

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