Skip to main content

embedded_cal/
aead.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// SPDX-FileCopyrightText: Inria-AIO, Cryspen, and Christian Amsüss
3
4// FIXME: Document that we don't do variable length tags (or more precisely, overhead of encryption
5// like in plain AES), and that we expect the tag to be separate (although we could consider
6// changing interfaces if it turns out that everyone appends the tag to the ciphertext anyway, to
7// the point where it's easier to just take a longe buffer, especially if future algorithms start
8// *expecing* that).
9
10/// Symmetric encryption with authentication and additional data.
11///
12/// This trait is modelled after the
13/// [`aead::AeadInPlace`](https://docs.rs/aead/latest/aead/trait.AeadInPlace.html) trait,
14/// but does not use it directly because
15/// - `embedded-cal` passes around an exclusive reference to its engine, and
16/// - its operation is cryptographically agile rather than monomorphized over algorithms.
17pub trait AeadProvider {
18    type Algorithm: AeadAlgorithm;
19    type Key: Sized;
20    type Tag: Sized + AsRef<[u8]>;
21
22    /// Loads a key from the key's bytes.
23    ///
24    /// # Panics
25    ///
26    /// … if key's length is not `alg.key_length()`.
27    fn load_from_keydata(&mut self, alg: Self::Algorithm, key: &[u8]) -> Self::Key;
28
29    /// Encrypts data in place.
30    ///
31    /// The AEAD tag is returned separately; depending on the higher-layer protocol it is appended
32    /// to the message or gets sent separately.
33    ///
34    /// # Panics
35    ///
36    /// … if nonce's length is not `alg.nonce_length()` of the algorithm that generated the key.
37    // Potential for enhancement: Create a key-and-nonce type that moves the nonce length check
38    // from encryption time to preparation time?
39    fn encrypt_in_place(
40        &mut self,
41        key: &Self::Key,
42        nonce: &[u8],
43        message: &mut [u8],
44        aad: impl AadGenerator,
45    ) -> Self::Tag;
46
47    /// Decrypts data in place.
48    ///
49    /// The AEAD tag is returned separately; depending on the higher-layer protocol it is appended
50    /// to the message or gets sent separately.
51    ///
52    /// # Panics
53    ///
54    /// … if nonce's length is not `alg.nonce_length()` of the algorithm that generated the key, or
55    /// the tag's length is not `alg.tag_length()`.
56    ///
57    /// # Implementation guidance
58    ///
59    /// As the message is passed in in a buffer that is available even in case of error, it is best
60    /// practice to zero the message when verification fails, to make sure that even when the error
61    /// is handled badly, an attacker can not hope to place crafted content in a place that might
62    /// be mistaken for verified data.
63    #[must_use = "message must not be accessed after a failed decryption"]
64    fn decrypt_in_place(
65        &mut self,
66        key: &Self::Key,
67        nonce: &[u8],
68        message: &mut [u8],
69        tag: &[u8],
70        aad: impl AadGenerator,
71    ) -> Result<(), DecryptionFailed>;
72}
73
74/// Error indicating that an AEAD decryption failed.
75///
76/// AEAD algorithms generally do not report structured errors; this always indicates some form of
77/// "the calculated AEAD tag mismatched".
78#[derive(Debug)]
79pub struct DecryptionFailed;
80
81impl core::fmt::Display for DecryptionFailed {
82    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
83        f.write_str("decryption failed")
84    }
85}
86
87impl core::error::Error for DecryptionFailed {}
88
89/// An AEAD algorithm identifier.
90pub trait AeadAlgorithm: Sized + PartialEq + Eq + core::fmt::Debug + Clone {
91    /// Length of a key in bytes.
92    fn key_length(&self) -> usize;
93
94    /// Length of the cryptographic tag in bytes.
95    fn tag_length(&self) -> usize;
96
97    /// Length of the nonce (called IV in some algorithms) in bytes.
98    fn nonce_length(&self) -> usize;
99
100    /// Selects an AEAD algorithm from its COSE number.
101    ///
102    /// The algorithm number comes from the ["COSE Algorithms"
103    /// registry](https://www.iana.org/assignments/cose/cose.xhtml#algorithms) maintained by IANA.
104    #[inline]
105    #[allow(
106        unused_variables,
107        reason = "Argument names are part of the documentation"
108    )]
109    fn from_cose_number(number: impl Into<i128>) -> Option<Self> {
110        None
111    }
112}
113
114/// Tool for providing the AAD (Additional Authenticated Data) in a scatter-gather fashion.
115pub trait AadGenerator {
116    // FIXME: What precise guarantees do we want to ask/give?
117    fn items(&self) -> impl Iterator<Item = &[u8]>;
118}
119
120impl AadGenerator for &[u8] {
121    fn items(&self) -> impl Iterator<Item = &[u8]> {
122        [*self].into_iter()
123    }
124}
125
126impl AadGenerator for &[&[u8]] {
127    fn items(&self) -> impl Iterator<Item = &[u8]> {
128        self.iter().copied()
129    }
130}