Skip to main content

craft_codec/
crypto.rs

1use crate::{Encryption, Identity, Result};
2
3/// Sealed encryption implementation used for static dispatch.
4/// Applications normally pass [`Identity`] or, with feature `aes-gcm`, `Aes256Gcm`.
5pub trait EncryptionCodec: crate::sealed::Sealed {
6    /// Metadata format implemented by this codec.
7    const FORMAT: Encryption;
8    /// Encrypt a payload and append its tag.
9    #[doc(hidden)]
10    fn seal(&self, index: u64, buffer: &mut Vec<u8>) -> Result<()>;
11    /// Authenticate/decrypt a stored frame and remove its tag.
12    #[doc(hidden)]
13    fn open(&self, index: u64, buffer: &mut Vec<u8>) -> Result<()>;
14}
15
16impl EncryptionCodec for Identity {
17    const FORMAT: Encryption = Encryption::None;
18    fn seal(&self, _: u64, _: &mut Vec<u8>) -> Result<()> {
19        Ok(())
20    }
21    fn open(&self, _: u64, _: &mut Vec<u8>) -> Result<()> {
22        Ok(())
23    }
24}
25
26/// AES-256-GCM with nonce `0u32 || index.to_be_bytes()`, empty AAD and a suffix tag.
27///
28/// Use one independent key per immutable object. Never encrypt distinct payloads
29/// under the same key/index, even across encoder instances. Metadata must be
30/// trusted. Key generation and persistence belong to the caller.
31#[cfg(feature = "aes-gcm")]
32pub struct Aes256Gcm(aes_gcm::Aes256Gcm);
33
34#[cfg(feature = "aes-gcm")]
35impl Aes256Gcm {
36    /// Initialize the expanded key once. The caller retains responsibility for
37    /// its original key bytes. The underlying cipher enables its zeroize feature.
38    pub fn new(key: &[u8; 32]) -> Self {
39        use aes_gcm::KeyInit;
40        Self(aes_gcm::Aes256Gcm::new(key.into()))
41    }
42}
43
44#[cfg(feature = "aes-gcm")]
45impl crate::sealed::Sealed for Aes256Gcm {}
46
47#[cfg(feature = "aes-gcm")]
48fn nonce(index: u64) -> aes_gcm::Nonce<aes_gcm::aead::consts::U12> {
49    let mut bytes = [0; 12];
50    bytes[4..].copy_from_slice(&index.to_be_bytes());
51    bytes.into()
52}
53
54#[cfg(feature = "aes-gcm")]
55impl EncryptionCodec for Aes256Gcm {
56    const FORMAT: Encryption = Encryption::Aes256Gcm;
57
58    fn seal(&self, index: u64, buffer: &mut Vec<u8>) -> Result<()> {
59        use aes_gcm::aead::AeadInOut;
60        buffer
61            .try_reserve(crate::TAG_LEN)
62            .map_err(|_| crate::Error::Allocation)?;
63        let tag = self
64            .0
65            .encrypt_inout_detached(&nonce(index), b"", buffer.as_mut_slice().into())
66            .map_err(|_| crate::Error::EncryptionFailed)?;
67        buffer.extend_from_slice(&tag);
68        Ok(())
69    }
70
71    fn open(&self, index: u64, buffer: &mut Vec<u8>) -> Result<()> {
72        use aes_gcm::aead::AeadInOut;
73        let payload_len = buffer
74            .len()
75            .checked_sub(crate::TAG_LEN)
76            .ok_or(crate::Error::InvalidStoredLength)?;
77        let tag: [u8; crate::TAG_LEN] = buffer[payload_len..].try_into().unwrap();
78        self.0
79            .decrypt_inout_detached(
80                &nonce(index),
81                b"",
82                (&mut buffer[..payload_len]).into(),
83                &tag.into(),
84            )
85            .map_err(|_| crate::Error::AuthenticationFailed)?;
86        buffer.truncate(payload_len);
87        Ok(())
88    }
89}