enigma_aead/
aead.rs

1use chacha20poly1305::aead::{Aead, KeyInit, Payload};
2use chacha20poly1305::{XChaCha20Poly1305, XNonce};
3
4use crate::error::{EnigmaAeadError, EnigmaAeadResult};
5use crate::nonce::NonceGenerator;
6use crate::packet::{Packet, PacketHeader, PacketParts, HEADER_LEN, MAX_PACKET_SIZE, TAG_LEN};
7use crate::types::{AeadKey, KeyId, Nonce24};
8
9fn seal_raw(
10    key_bytes: &[u8; 32],
11    nonce: &[u8; 24],
12    plaintext: &[u8],
13    aad: &[u8],
14) -> EnigmaAeadResult<Vec<u8>> {
15    let cipher =
16        XChaCha20Poly1305::new_from_slice(key_bytes).map_err(|_| EnigmaAeadError::CryptoError)?;
17    let nonce_bytes = XNonce::from_slice(nonce);
18    cipher
19        .encrypt(
20            nonce_bytes,
21            Payload {
22                msg: plaintext,
23                aad,
24            },
25        )
26        .map_err(EnigmaAeadError::from)
27}
28
29fn open_raw(
30    key_bytes: &[u8; 32],
31    nonce: &[u8; 24],
32    ciphertext: &[u8],
33    aad: &[u8],
34) -> EnigmaAeadResult<Vec<u8>> {
35    if ciphertext.len() < TAG_LEN {
36        return Err(EnigmaAeadError::InvalidPacket("ciphertext too short"));
37    }
38    let cipher =
39        XChaCha20Poly1305::new_from_slice(key_bytes).map_err(|_| EnigmaAeadError::CryptoError)?;
40    let nonce_bytes = XNonce::from_slice(nonce);
41    cipher
42        .decrypt(
43            nonce_bytes,
44            Payload {
45                msg: ciphertext,
46                aad,
47            },
48        )
49        .map_err(EnigmaAeadError::from)
50}
51
52pub fn seal(
53    key: [u8; 32],
54    nonce: [u8; 24],
55    plaintext: &[u8],
56    aad: &[u8],
57) -> EnigmaAeadResult<Vec<u8>> {
58    seal_raw(&key, &nonce, plaintext, aad)
59}
60
61pub fn open(
62    key: [u8; 32],
63    nonce: [u8; 24],
64    ciphertext: &[u8],
65    aad: &[u8],
66) -> EnigmaAeadResult<Vec<u8>> {
67    open_raw(&key, &nonce, ciphertext, aad)
68}
69
70pub struct AeadBox {
71    key: AeadKey,
72    key_id: KeyId,
73}
74
75impl AeadBox {
76    pub fn new(key: [u8; 32]) -> Self {
77        Self {
78            key: AeadKey::new(key),
79            key_id: KeyId::zero(),
80        }
81    }
82
83    pub fn with_key_id(key: [u8; 32], key_id: [u8; 8]) -> Self {
84        Self {
85            key: AeadKey::new(key),
86            key_id: KeyId(key_id),
87        }
88    }
89
90    pub fn encrypt(&self, plaintext: &[u8], user_ad: &[u8]) -> EnigmaAeadResult<Vec<u8>> {
91        let nonce = NonceGenerator::random_nonce24();
92        self.encrypt_with_nonce_internal(plaintext, user_ad, nonce)
93    }
94
95    pub fn encrypt_with_nonce(
96        &self,
97        plaintext: &[u8],
98        user_ad: &[u8],
99        nonce: [u8; 24],
100    ) -> EnigmaAeadResult<Vec<u8>> {
101        self.encrypt_with_nonce_internal(plaintext, user_ad, Nonce24::new(nonce))
102    }
103
104    pub fn decrypt(&self, packet: &[u8], user_ad: &[u8]) -> EnigmaAeadResult<Vec<u8>> {
105        let parts = Packet::decode(packet)?;
106        self.decrypt_parts(parts, user_ad)
107    }
108
109    pub fn seal(&self, nonce: [u8; 24], plaintext: &[u8], aad: &[u8]) -> EnigmaAeadResult<Vec<u8>> {
110        seal_raw(self.key.as_bytes(), &nonce, plaintext, aad)
111    }
112
113    pub fn open(
114        &self,
115        nonce: [u8; 24],
116        ciphertext: &[u8],
117        aad: &[u8],
118    ) -> EnigmaAeadResult<Vec<u8>> {
119        open_raw(self.key.as_bytes(), &nonce, ciphertext, aad)
120    }
121
122    pub fn parse_packet(packet: &[u8]) -> EnigmaAeadResult<PacketParts> {
123        Packet::decode(packet)
124    }
125
126    fn encrypt_with_nonce_internal(
127        &self,
128        plaintext: &[u8],
129        user_ad: &[u8],
130        nonce: Nonce24,
131    ) -> EnigmaAeadResult<Vec<u8>> {
132        let projected = HEADER_LEN + plaintext.len() + TAG_LEN;
133        if projected > MAX_PACKET_SIZE {
134            return Err(EnigmaAeadError::SizeLimitExceeded);
135        }
136        let header = PacketHeader::new(self.key_id, nonce);
137        let header_bytes = header.to_bytes();
138        let mut ad = Vec::with_capacity(user_ad.len() + header_bytes.len());
139        ad.extend_from_slice(user_ad);
140        ad.extend_from_slice(&header_bytes);
141        let cipher = XChaCha20Poly1305::new_from_slice(self.key.as_bytes())
142            .map_err(|_| EnigmaAeadError::CryptoError)?;
143        let nonce_bytes = XNonce::from_slice(header.nonce.as_slice());
144        let ciphertext = cipher.encrypt(
145            nonce_bytes,
146            Payload {
147                msg: plaintext,
148                aad: &ad,
149            },
150        )?;
151        Packet::encode(&header, &ciphertext)
152    }
153
154    fn decrypt_parts(&self, parts: PacketParts, user_ad: &[u8]) -> EnigmaAeadResult<Vec<u8>> {
155        let header_bytes = parts.header_bytes();
156        let mut ad = Vec::with_capacity(user_ad.len() + header_bytes.len());
157        ad.extend_from_slice(user_ad);
158        ad.extend_from_slice(&header_bytes);
159        let cipher = XChaCha20Poly1305::new_from_slice(self.key.as_bytes())
160            .map_err(|_| EnigmaAeadError::CryptoError)?;
161        let nonce = XNonce::from_slice(parts.header.nonce.as_slice());
162        cipher
163            .decrypt(
164                nonce,
165                Payload {
166                    msg: parts.ciphertext.as_slice(),
167                    aad: &ad,
168                },
169            )
170            .map_err(EnigmaAeadError::from)
171    }
172}