1use alloc::vec::Vec;
4
5use crate::HpkeError;
6use crate::sealed::Sealed;
7
8pub trait Aead: Sealed {
19 const ID: u16;
21 const KEY_LEN: usize;
23 const NONCE_LEN: usize;
25 const TAG_LEN: usize;
27
28 type Cipher;
35
36 fn init(key: &[u8]) -> Result<Self::Cipher, HpkeError>;
38}
39
40pub trait SealingAead: Aead {
47 fn seal(
50 cipher: &Self::Cipher,
51 nonce: &[u8],
52 aad: &[u8],
53 pt: &[u8],
54 ) -> Result<Vec<u8>, HpkeError>;
55 fn open(
58 cipher: &Self::Cipher,
59 nonce: &[u8],
60 aad: &[u8],
61 ct: &[u8],
62 ) -> Result<Vec<u8>, HpkeError>;
63}
64
65#[derive(Debug, Clone, Copy, Default)]
67pub struct ChaCha20Poly1305;
68
69impl Sealed for ChaCha20Poly1305 {}
70impl Aead for ChaCha20Poly1305 {
71 const ID: u16 = 0x0003;
72 const KEY_LEN: usize = 32;
73 const NONCE_LEN: usize = 12;
74 const TAG_LEN: usize = 16;
75
76 type Cipher = chacha20poly1305::ChaCha20Poly1305;
77
78 fn init(key: &[u8]) -> Result<Self::Cipher, HpkeError> {
79 use chacha20poly1305::KeyInit;
80 chacha20poly1305::ChaCha20Poly1305::new_from_slice(key)
81 .map_err(|_| HpkeError::AeadInitError)
82 }
83}
84impl SealingAead for ChaCha20Poly1305 {
85 fn seal(
86 cipher: &Self::Cipher,
87 nonce: &[u8],
88 aad: &[u8],
89 pt: &[u8],
90 ) -> Result<Vec<u8>, HpkeError> {
91 use chacha20poly1305::{
92 Nonce,
93 aead::{Aead as _, Payload},
94 };
95 cipher
96 .encrypt(Nonce::from_slice(nonce), Payload { msg: pt, aad })
97 .map_err(|_| HpkeError::SealError)
98 }
99 fn open(
100 cipher: &Self::Cipher,
101 nonce: &[u8],
102 aad: &[u8],
103 ct: &[u8],
104 ) -> Result<Vec<u8>, HpkeError> {
105 use chacha20poly1305::{
106 Nonce,
107 aead::{Aead as _, Payload},
108 };
109 cipher
110 .decrypt(Nonce::from_slice(nonce), Payload { msg: ct, aad })
111 .map_err(|_| HpkeError::OpenError)
112 }
113}
114
115#[derive(Debug, Clone, Copy, Default)]
120pub struct Aes128Gcm;
121
122impl Sealed for Aes128Gcm {}
123impl Aead for Aes128Gcm {
124 const ID: u16 = 0x0001;
125 const KEY_LEN: usize = 16;
126 const NONCE_LEN: usize = 12;
127 const TAG_LEN: usize = 16;
128
129 type Cipher = aes_gcm::Aes128Gcm;
130
131 fn init(key: &[u8]) -> Result<Self::Cipher, HpkeError> {
132 use aes_gcm::KeyInit;
133 aes_gcm::Aes128Gcm::new_from_slice(key).map_err(|_| HpkeError::AeadInitError)
134 }
135}
136impl SealingAead for Aes128Gcm {
137 fn seal(
138 cipher: &Self::Cipher,
139 nonce: &[u8],
140 aad: &[u8],
141 pt: &[u8],
142 ) -> Result<Vec<u8>, HpkeError> {
143 use aes_gcm::aead::Aead as _;
144 cipher
145 .encrypt(
146 aes_gcm::Nonce::from_slice(nonce),
147 aead::Payload { msg: pt, aad },
148 )
149 .map_err(|_| HpkeError::SealError)
150 }
151 fn open(
152 cipher: &Self::Cipher,
153 nonce: &[u8],
154 aad: &[u8],
155 ct: &[u8],
156 ) -> Result<Vec<u8>, HpkeError> {
157 use aes_gcm::aead::Aead as _;
158 cipher
159 .decrypt(
160 aes_gcm::Nonce::from_slice(nonce),
161 aead::Payload { msg: ct, aad },
162 )
163 .map_err(|_| HpkeError::OpenError)
164 }
165}
166
167#[derive(Debug, Clone, Copy, Default)]
171pub struct Aes256Gcm;
172
173impl Sealed for Aes256Gcm {}
174impl Aead for Aes256Gcm {
175 const ID: u16 = 0x0002;
176 const KEY_LEN: usize = 32;
177 const NONCE_LEN: usize = 12;
178 const TAG_LEN: usize = 16;
179
180 type Cipher = aes_gcm::Aes256Gcm;
181
182 fn init(key: &[u8]) -> Result<Self::Cipher, HpkeError> {
183 use aes_gcm::KeyInit;
184 aes_gcm::Aes256Gcm::new_from_slice(key).map_err(|_| HpkeError::AeadInitError)
185 }
186}
187impl SealingAead for Aes256Gcm {
188 fn seal(
189 cipher: &Self::Cipher,
190 nonce: &[u8],
191 aad: &[u8],
192 pt: &[u8],
193 ) -> Result<Vec<u8>, HpkeError> {
194 use aes_gcm::aead::Aead as _;
195 cipher
196 .encrypt(
197 aes_gcm::Nonce::from_slice(nonce),
198 aead::Payload { msg: pt, aad },
199 )
200 .map_err(|_| HpkeError::SealError)
201 }
202 fn open(
203 cipher: &Self::Cipher,
204 nonce: &[u8],
205 aad: &[u8],
206 ct: &[u8],
207 ) -> Result<Vec<u8>, HpkeError> {
208 use aes_gcm::aead::Aead as _;
209 cipher
210 .decrypt(
211 aes_gcm::Nonce::from_slice(nonce),
212 aead::Payload { msg: ct, aad },
213 )
214 .map_err(|_| HpkeError::OpenError)
215 }
216}
217
218#[derive(Debug, Clone, Copy, Default)]
224pub struct ExportOnly;
225
226impl Sealed for ExportOnly {}
227impl Aead for ExportOnly {
228 const ID: u16 = 0xFFFF;
229 const KEY_LEN: usize = 0;
230 const NONCE_LEN: usize = 0;
231 const TAG_LEN: usize = 0;
232
233 type Cipher = ();
234
235 fn init(_key: &[u8]) -> Result<Self::Cipher, HpkeError> {
236 Ok(())
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use hex::FromHex;
244
245 #[test]
247 fn rfc8439_chacha20poly1305_test_vector() {
248 let key = Vec::from_hex("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f")
249 .unwrap();
250 let nonce = Vec::from_hex("070000004041424344454647").unwrap();
251 let aad = Vec::from_hex("50515253c0c1c2c3c4c5c6c7").unwrap();
252 let pt = b"Ladies and Gentlemen of the class of '99: \
253 If I could offer you only one tip for the future, sunscreen would be it.";
254
255 let cipher = ChaCha20Poly1305::init(&key).unwrap();
256 let ct = ChaCha20Poly1305::seal(&cipher, &nonce, &aad, pt).unwrap();
257 let recovered = ChaCha20Poly1305::open(&cipher, &nonce, &aad, &ct).unwrap();
258 assert_eq!(recovered, pt);
259
260 let mut bad = ct.clone();
261 bad[0] ^= 1;
262 assert_eq!(
263 ChaCha20Poly1305::open(&cipher, &nonce, &aad, &bad),
264 Err(HpkeError::OpenError)
265 );
266 }
267
268 #[test]
269 fn aes128gcm_roundtrip_short() {
270 let key = [0u8; 16];
271 let nonce = [0u8; 12];
272 let aad = b"";
273 let pt = b"hello";
274 let cipher = Aes128Gcm::init(&key).unwrap();
275 let ct = Aes128Gcm::seal(&cipher, &nonce, aad, pt).unwrap();
276 assert_eq!(ct.len(), pt.len() + 16);
277 assert_eq!(Aes128Gcm::open(&cipher, &nonce, aad, &ct).unwrap(), pt);
278 }
279
280 #[test]
281 fn aes256gcm_roundtrip_short() {
282 let key = [0u8; 32];
283 let nonce = [0u8; 12];
284 let pt = b"world";
285 let cipher = Aes256Gcm::init(&key).unwrap();
286 let ct = Aes256Gcm::seal(&cipher, &nonce, b"aad", pt).unwrap();
287 assert_eq!(Aes256Gcm::open(&cipher, &nonce, b"aad", &ct).unwrap(), pt);
288 }
289
290 #[test]
291 fn aes128gcm_rejects_bad_key_len() {
292 let r = Aes128Gcm::init(&[0u8; 15]);
293 assert_eq!(r.err(), Some(HpkeError::AeadInitError));
294 }
295
296 #[test]
297 fn export_only_implements_aead_only() {
298 fn assert_aead<A: Aead>() {}
299 assert_aead::<ExportOnly>();
300 assert_eq!(ExportOnly::ID, 0xFFFF);
301 assert_eq!(ExportOnly::KEY_LEN, 0);
302 assert_eq!(ExportOnly::NONCE_LEN, 0);
303 assert_eq!(ExportOnly::TAG_LEN, 0);
304 assert!(ExportOnly::init(&[]).is_ok());
306 }
307}