1mod aes_cbc;
23mod aes_gcm;
24
25use crate::error::*;
26pub use aes_cbc::LEGACY_SYNC_AES_256_CBC_HMAC_SHA256;
27pub use aes_gcm::{AES_128_GCM, AES_256_GCM};
28use nss::aes;
29
30pub fn open(
31 key: &OpeningKey,
32 nonce: Nonce,
33 aad: Aad<'_>,
34 ciphertext_and_tag: &[u8],
35) -> Result<Vec<u8>> {
36 (key.algorithm().open)(&key.key, nonce, &aad, ciphertext_and_tag)
37}
38
39pub fn seal(key: &SealingKey, nonce: Nonce, aad: Aad<'_>, plaintext: &[u8]) -> Result<Vec<u8>> {
40 (key.algorithm().seal)(&key.key, nonce, &aad, plaintext)
41}
42
43#[repr(transparent)]
48pub struct Aad<'a>(&'a [u8]);
49
50impl<'a> Aad<'a> {
51 #[inline]
53 pub fn from(aad: &'a [u8]) -> Self {
54 Aad(aad)
55 }
56}
57
58impl Aad<'static> {
59 pub fn empty() -> Self {
61 Self::from(&[])
62 }
63}
64
65pub struct Nonce(Vec<u8>);
69
70impl Nonce {
71 #[inline]
72 pub fn try_assume_unique_for_key(algorithm: &'static Algorithm, value: &[u8]) -> Result<Self> {
73 if value.len() != algorithm.nonce_len() {
74 return Err(ErrorKind::InternalError.into());
75 }
76 Ok(Self(value.to_vec()))
77 }
78}
79
80pub struct OpeningKey {
81 key: Key,
82}
83
84impl OpeningKey {
85 #[inline]
89 pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self> {
90 Ok(Self {
91 key: Key::new(algorithm, key_bytes)?,
92 })
93 }
94
95 #[inline]
97 pub fn algorithm(&self) -> &'static Algorithm {
98 self.key.algorithm()
99 }
100}
101
102pub struct SealingKey {
103 key: Key,
104}
105
106impl SealingKey {
107 #[inline]
111 pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self> {
112 Ok(Self {
113 key: Key::new(algorithm, key_bytes)?,
114 })
115 }
116
117 #[inline]
119 pub fn algorithm(&self) -> &'static Algorithm {
120 self.key.algorithm()
121 }
122}
123
124pub(crate) struct Key {
126 key_value: Vec<u8>,
127 algorithm: &'static Algorithm,
128}
129
130impl Key {
131 fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self> {
132 if key_bytes.len() != algorithm.key_len() {
133 return Err(ErrorKind::InternalError.into());
134 }
135 Ok(Key {
136 key_value: key_bytes.to_vec(),
137 algorithm,
138 })
139 }
140
141 #[inline]
142 pub fn algorithm(&self) -> &'static Algorithm {
143 self.algorithm
144 }
145}
146
147#[allow(clippy::type_complexity)]
149pub struct Algorithm {
150 tag_len: usize,
151 key_len: usize,
152 nonce_len: usize,
153 open: fn(key: &Key, nonce: Nonce, aad: &Aad<'_>, ciphertext_and_tag: &[u8]) -> Result<Vec<u8>>,
154 seal: fn(key: &Key, nonce: Nonce, aad: &Aad<'_>, plaintext: &[u8]) -> Result<Vec<u8>>,
155}
156
157impl Algorithm {
158 #[inline]
160 pub const fn key_len(&self) -> usize {
161 self.key_len
162 }
163
164 #[inline]
166 pub const fn tag_len(&self) -> usize {
167 self.tag_len
168 }
169
170 #[inline]
172 pub const fn nonce_len(&self) -> usize {
173 self.nonce_len
174 }
175}
176
177pub(crate) enum Direction {
178 Opening,
179 Sealing,
180}
181
182impl Direction {
183 fn to_nss_operation(&self) -> aes::Operation {
184 match self {
185 Direction::Opening => aes::Operation::Decrypt,
186 Direction::Sealing => aes::Operation::Encrypt,
187 }
188 }
189}
190
191#[cfg(test)]
192mod test {
193 use super::*;
194 use nss::ensure_initialized;
195
196 static ALL_ALGORITHMS: &[&Algorithm] = &[
197 &LEGACY_SYNC_AES_256_CBC_HMAC_SHA256,
198 &AES_128_GCM,
199 &AES_256_GCM,
200 ];
201 static ALL_ALGORITHMS_THAT_SUPPORT_AAD: &[&Algorithm] = &[&AES_128_GCM, &AES_256_GCM];
202
203 #[test]
204 fn test_roundtrip() {
205 ensure_initialized();
206 for algorithm in ALL_ALGORITHMS {
207 let mut cleartext_bytes = vec![0u8; 127];
208 crate::rand::fill(&mut cleartext_bytes).unwrap();
209
210 let mut key_bytes = vec![0u8; algorithm.key_len()];
211 crate::rand::fill(&mut key_bytes).unwrap();
212
213 let nonce_bytes = vec![0u8; algorithm.nonce_len()];
214
215 let key = SealingKey::new(algorithm, &key_bytes).unwrap();
216 let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
217 let ciphertext_bytes = seal(&key, nonce, Aad::empty(), &cleartext_bytes).unwrap();
218
219 let key = OpeningKey::new(algorithm, &key_bytes).unwrap();
220 let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
221 let roundtriped_cleartext_bytes =
222 open(&key, nonce, Aad::empty(), &ciphertext_bytes).unwrap();
223 assert_eq!(roundtriped_cleartext_bytes, cleartext_bytes);
224 }
225 }
226
227 #[test]
228 fn test_cant_open_with_mismatched_key() {
229 ensure_initialized();
230 let mut key_bytes_1 = vec![0u8; AES_256_GCM.key_len()];
231 crate::rand::fill(&mut key_bytes_1).unwrap();
232
233 let mut key_bytes_2 = vec![0u8; AES_128_GCM.key_len()];
234 crate::rand::fill(&mut key_bytes_2).unwrap();
235
236 let nonce_bytes = vec![0u8; AES_256_GCM.nonce_len()];
237
238 let key = SealingKey::new(&AES_256_GCM, &key_bytes_1).unwrap();
239 let nonce = Nonce::try_assume_unique_for_key(&AES_256_GCM, &nonce_bytes).unwrap();
240 let ciphertext_bytes = seal(&key, nonce, Aad::empty(), &[0u8; 0]).unwrap();
241
242 let key = OpeningKey::new(&AES_128_GCM, &key_bytes_2).unwrap();
243 let nonce = Nonce::try_assume_unique_for_key(&AES_128_GCM, &nonce_bytes).unwrap();
244 let result = open(&key, nonce, Aad::empty(), &ciphertext_bytes);
245 assert!(result.is_err());
246 }
247
248 #[test]
249 fn test_cant_open_modified_ciphertext() {
250 ensure_initialized();
251 for algorithm in ALL_ALGORITHMS {
252 let mut key_bytes = vec![0u8; algorithm.key_len()];
253 crate::rand::fill(&mut key_bytes).unwrap();
254
255 let nonce_bytes = vec![0u8; algorithm.nonce_len()];
256
257 let key = SealingKey::new(algorithm, &key_bytes).unwrap();
258 let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
259 let ciphertext_bytes = seal(&key, nonce, Aad::empty(), &[0u8; 0]).unwrap();
260
261 for i in 0..ciphertext_bytes.len() {
262 let mut modified_ciphertext = ciphertext_bytes.clone();
263 modified_ciphertext[i] = modified_ciphertext[i].wrapping_add(1);
264
265 let key = OpeningKey::new(algorithm, &key_bytes).unwrap();
266 let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
267 let result = open(&key, nonce, Aad::empty(), &modified_ciphertext);
268 assert!(result.is_err());
269 }
270 }
271 }
272
273 #[test]
274 fn test_cant_open_with_incorrect_associated_data() {
275 ensure_initialized();
276 for algorithm in ALL_ALGORITHMS_THAT_SUPPORT_AAD {
277 let mut key_bytes = vec![0u8; algorithm.key_len()];
278 crate::rand::fill(&mut key_bytes).unwrap();
279
280 let nonce_bytes = vec![0u8; algorithm.nonce_len()];
281
282 let key = SealingKey::new(algorithm, &key_bytes).unwrap();
283 let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
284 let ciphertext_bytes = seal(&key, nonce, Aad::from(&[1, 2, 3]), &[0u8; 0]).unwrap();
285
286 let key = OpeningKey::new(algorithm, &key_bytes).unwrap();
287 let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
288 let result = open(&key, nonce, Aad::empty(), &ciphertext_bytes);
289 assert!(result.is_err());
290
291 let nonce = Nonce::try_assume_unique_for_key(&AES_256_GCM, &nonce_bytes).unwrap();
292 let result = open(&key, nonce, Aad::from(&[2, 3, 4]), &ciphertext_bytes);
293 assert!(result.is_err());
294 }
295 }
296
297 #[test]
298 fn test_cant_use_incorrectly_sized_key() {
299 ensure_initialized();
300 for algorithm in ALL_ALGORITHMS {
301 let key_bytes = vec![0u8; algorithm.key_len() - 1];
302 let result = Key::new(algorithm, &key_bytes);
303 assert!(result.is_err());
304
305 let key_bytes = vec![0u8; algorithm.key_len() + 1];
306 let result = Key::new(algorithm, &key_bytes);
307 assert!(result.is_err());
308 }
309 }
310
311 #[test]
312 fn test_cant_use_incorrectly_sized_nonce() {
313 ensure_initialized();
314 for algorithm in ALL_ALGORITHMS {
315 let nonce_bytes = vec![0u8; algorithm.nonce_len() - 1];
316 let result = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes);
317 assert!(result.is_err());
318
319 let nonce_bytes = vec![0u8; algorithm.nonce_len() + 1];
320 let result = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes);
321 assert!(result.is_err());
322 }
323 }
324}