1use crate::aws_lc::{
7 AES_cbc_encrypt, AES_cfb128_encrypt, AES_cfb1_encrypt, AES_cfb8_encrypt, AES_ctr128_encrypt,
8 AES_ecb_encrypt, AES_ofb128_encrypt, AES_DECRYPT, AES_ENCRYPT, AES_KEY,
9};
10use crate::cipher::block::Block;
11use crate::error::Unspecified;
12use crate::fips::indicator_check;
13use zeroize::Zeroize;
14
15use super::{DecryptionContext, EncryptionContext, OperatingMode, SymmetricCipherKey};
16
17pub const AES_128_KEY_LEN: usize = 16;
19
20pub const AES_192_KEY_LEN: usize = 24;
22
23pub const AES_256_KEY_LEN: usize = 32;
25
26pub const AES_CBC_IV_LEN: usize = 16;
28
29pub const AES_CTR_IV_LEN: usize = 16;
31
32pub const AES_CFB_IV_LEN: usize = 16;
34
35pub const AES_OFB_IV_LEN: usize = 16;
37
38pub const AES_BLOCK_LEN: usize = 16;
39
40#[inline]
41pub(crate) fn encrypt_block(aes_key: &AES_KEY, mut block: Block) -> Block {
42 {
43 let block_ref = block.as_mut();
44 debug_assert_eq!(block_ref.len(), AES_BLOCK_LEN);
45 aes_ecb_encrypt(aes_key, block_ref);
46 }
47 block
48}
49
50pub(super) fn encrypt_ctr_mode(
51 key: &SymmetricCipherKey,
52 context: EncryptionContext,
53 in_out: &mut [u8],
54) -> Result<DecryptionContext, Unspecified> {
55 let (SymmetricCipherKey::Aes128 { enc_key, .. }
56 | SymmetricCipherKey::Aes192 { enc_key, .. }
57 | SymmetricCipherKey::Aes256 { enc_key, .. }) = &key
58 else {
59 unreachable!()
60 };
61
62 let mut iv = {
63 let mut iv = [0u8; AES_CTR_IV_LEN];
64 iv.copy_from_slice((&context).try_into()?);
65 iv
66 };
67
68 let mut buffer = [0u8; AES_BLOCK_LEN];
69
70 aes_ctr128_encrypt(enc_key, &mut iv, &mut buffer, in_out);
71 iv.zeroize();
72
73 Ok(context.into())
74}
75
76pub(super) fn decrypt_ctr_mode<'in_out>(
77 key: &SymmetricCipherKey,
78 context: DecryptionContext,
79 in_out: &'in_out mut [u8],
80) -> Result<&'in_out mut [u8], Unspecified> {
81 encrypt_ctr_mode(key, context.into(), in_out).map(|_| in_out)
83}
84
85pub(super) fn encrypt_cbc_mode(
86 key: &SymmetricCipherKey,
87 context: EncryptionContext,
88 in_out: &mut [u8],
89) -> Result<DecryptionContext, Unspecified> {
90 let (SymmetricCipherKey::Aes128 { enc_key, .. }
91 | SymmetricCipherKey::Aes192 { enc_key, .. }
92 | SymmetricCipherKey::Aes256 { enc_key, .. }) = &key
93 else {
94 unreachable!()
95 };
96
97 let mut iv = {
98 let mut iv = [0u8; AES_CBC_IV_LEN];
99 iv.copy_from_slice((&context).try_into()?);
100 iv
101 };
102
103 aes_cbc_encrypt(enc_key, &mut iv, in_out);
104 iv.zeroize();
105
106 Ok(context.into())
107}
108
109#[allow(clippy::needless_pass_by_value)]
110pub(super) fn decrypt_cbc_mode<'in_out>(
111 key: &SymmetricCipherKey,
112 context: DecryptionContext,
113 in_out: &'in_out mut [u8],
114) -> Result<&'in_out mut [u8], Unspecified> {
115 let (SymmetricCipherKey::Aes128 { dec_key, .. }
116 | SymmetricCipherKey::Aes192 { dec_key, .. }
117 | SymmetricCipherKey::Aes256 { dec_key, .. }) = &key
118 else {
119 unreachable!()
120 };
121
122 let mut iv = {
123 let mut iv = [0u8; AES_CBC_IV_LEN];
124 iv.copy_from_slice((&context).try_into()?);
125 iv
126 };
127
128 aes_cbc_decrypt(dec_key, &mut iv, in_out);
129 iv.zeroize();
130
131 Ok(in_out)
132}
133
134pub(super) fn encrypt_cfb_mode(
135 key: &SymmetricCipherKey,
136 mode: OperatingMode,
137 context: EncryptionContext,
138 in_out: &mut [u8],
139) -> Result<DecryptionContext, Unspecified> {
140 let (SymmetricCipherKey::Aes128 { enc_key, .. }
141 | SymmetricCipherKey::Aes192 { enc_key, .. }
142 | SymmetricCipherKey::Aes256 { enc_key, .. }) = &key
143 else {
144 unreachable!()
145 };
146
147 let mut iv = {
148 let mut iv = [0u8; AES_CFB_IV_LEN];
149 iv.copy_from_slice((&context).try_into()?);
150 iv
151 };
152
153 let cfb_encrypt: fn(&AES_KEY, &mut [u8], &mut [u8]) = match mode {
154 OperatingMode::CFB128 => aes_cfb128_encrypt,
155 OperatingMode::CFB8 => aes_cfb8_encrypt,
156 OperatingMode::CFB1 => aes_cfb1_encrypt,
157 _ => unreachable!(),
158 };
159
160 cfb_encrypt(enc_key, &mut iv, in_out);
161 iv.zeroize();
162
163 Ok(context.into())
164}
165
166#[allow(clippy::needless_pass_by_value)]
167pub(super) fn decrypt_cfb_mode<'in_out>(
168 key: &SymmetricCipherKey,
169 mode: OperatingMode,
170 context: DecryptionContext,
171 in_out: &'in_out mut [u8],
172) -> Result<&'in_out mut [u8], Unspecified> {
173 let (SymmetricCipherKey::Aes128 { enc_key, .. }
174 | SymmetricCipherKey::Aes192 { enc_key, .. }
175 | SymmetricCipherKey::Aes256 { enc_key, .. }) = &key
176 else {
177 unreachable!()
178 };
179
180 let mut iv = {
181 let mut iv = [0u8; AES_CFB_IV_LEN];
182 iv.copy_from_slice((&context).try_into()?);
183 iv
184 };
185
186 let cfb_decrypt: fn(&AES_KEY, &mut [u8], &mut [u8]) = match mode {
187 OperatingMode::CFB128 => aes_cfb128_decrypt,
188 OperatingMode::CFB8 => aes_cfb8_decrypt,
189 OperatingMode::CFB1 => aes_cfb1_decrypt,
190 _ => unreachable!(),
191 };
192
193 cfb_decrypt(enc_key, &mut iv, in_out);
194
195 iv.zeroize();
196
197 Ok(in_out)
198}
199
200pub(super) fn encrypt_ecb_mode(
201 key: &SymmetricCipherKey,
202 context: EncryptionContext,
203 in_out: &mut [u8],
204) -> Result<DecryptionContext, Unspecified> {
205 if !matches!(context, EncryptionContext::None) {
206 return Err(Unspecified);
207 }
208
209 let (SymmetricCipherKey::Aes128 { enc_key, .. }
210 | SymmetricCipherKey::Aes192 { enc_key, .. }
211 | SymmetricCipherKey::Aes256 { enc_key, .. }) = &key
212 else {
213 unreachable!()
214 };
215
216 let mut in_out_iter = in_out.chunks_exact_mut(AES_BLOCK_LEN);
217
218 for block in in_out_iter.by_ref() {
219 aes_ecb_encrypt(enc_key, block);
220 }
221
222 debug_assert_eq!(in_out_iter.into_remainder(), []);
225
226 Ok(context.into())
227}
228
229#[allow(clippy::needless_pass_by_value)]
230pub(super) fn decrypt_ecb_mode<'in_out>(
231 key: &SymmetricCipherKey,
232 context: DecryptionContext,
233 in_out: &'in_out mut [u8],
234) -> Result<&'in_out mut [u8], Unspecified> {
235 if !matches!(context, DecryptionContext::None) {
236 return Err(Unspecified);
237 }
238
239 let (SymmetricCipherKey::Aes128 { dec_key, .. }
240 | SymmetricCipherKey::Aes192 { dec_key, .. }
241 | SymmetricCipherKey::Aes256 { dec_key, .. }) = &key
242 else {
243 unreachable!()
244 };
245
246 {
247 let mut in_out_iter = in_out.chunks_exact_mut(AES_BLOCK_LEN);
248
249 for block in in_out_iter.by_ref() {
250 aes_ecb_decrypt(dec_key, block);
251 }
252
253 debug_assert_eq!(in_out_iter.into_remainder(), []);
256 }
257
258 Ok(in_out)
259}
260
261fn aes_ecb_encrypt(key: &AES_KEY, in_out: &mut [u8]) {
262 indicator_check!(unsafe {
263 AES_ecb_encrypt(in_out.as_ptr(), in_out.as_mut_ptr(), key, AES_ENCRYPT);
264 });
265}
266
267fn aes_ecb_decrypt(key: &AES_KEY, in_out: &mut [u8]) {
268 indicator_check!(unsafe {
269 AES_ecb_encrypt(in_out.as_ptr(), in_out.as_mut_ptr(), key, AES_DECRYPT);
270 });
271}
272
273fn aes_ctr128_encrypt(key: &AES_KEY, iv: &mut [u8], block_buffer: &mut [u8], in_out: &mut [u8]) {
274 let mut num: u32 = 0;
275
276 indicator_check!(unsafe {
277 AES_ctr128_encrypt(
278 in_out.as_ptr(),
279 in_out.as_mut_ptr(),
280 in_out.len(),
281 key,
282 iv.as_mut_ptr(),
283 block_buffer.as_mut_ptr(),
284 &mut num,
285 );
286 });
287
288 Zeroize::zeroize(block_buffer);
289}
290
291fn aes_cbc_encrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
292 indicator_check!(unsafe {
293 AES_cbc_encrypt(
294 in_out.as_ptr(),
295 in_out.as_mut_ptr(),
296 in_out.len(),
297 key,
298 iv.as_mut_ptr(),
299 AES_ENCRYPT,
300 );
301 });
302}
303
304fn aes_cbc_decrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
305 indicator_check!(unsafe {
306 AES_cbc_encrypt(
307 in_out.as_ptr(),
308 in_out.as_mut_ptr(),
309 in_out.len(),
310 key,
311 iv.as_mut_ptr(),
312 AES_DECRYPT,
313 );
314 });
315}
316
317fn aes_cfb128_encrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
318 let mut num: i32 = 0;
319 indicator_check!(unsafe {
320 AES_cfb128_encrypt(
321 in_out.as_ptr(),
322 in_out.as_mut_ptr(),
323 in_out.len(),
324 key,
325 iv.as_mut_ptr(),
326 &mut num,
327 AES_ENCRYPT,
328 );
329 });
330}
331
332fn aes_cfb128_decrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
333 let mut num: i32 = 0;
334 indicator_check!(unsafe {
335 AES_cfb128_encrypt(
336 in_out.as_ptr(),
337 in_out.as_mut_ptr(),
338 in_out.len(),
339 key,
340 iv.as_mut_ptr(),
341 &mut num,
342 AES_DECRYPT,
343 );
344 });
345}
346
347fn aes_cfb8_encrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
349 let mut num: i32 = 0;
350 indicator_check!(unsafe {
351 AES_cfb8_encrypt(
352 in_out.as_ptr(),
353 in_out.as_mut_ptr(),
354 in_out.len(),
355 key,
356 iv.as_mut_ptr(),
357 &mut num,
358 AES_ENCRYPT,
359 );
360 });
361}
362
363fn aes_cfb8_decrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
364 let mut num: i32 = 0;
365 indicator_check!(unsafe {
366 AES_cfb8_encrypt(
367 in_out.as_ptr(),
368 in_out.as_mut_ptr(),
369 in_out.len(),
370 key,
371 iv.as_mut_ptr(),
372 &mut num,
373 AES_DECRYPT,
374 );
375 });
376}
377
378const MAX_CFB1_CHUNK_LEN: usize = 1 << (usize::BITS - 4);
382
383fn cfb1_cipher(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8], enc: i32) {
386 cfb1_cipher_in_chunks(key, iv, in_out, enc, MAX_CFB1_CHUNK_LEN);
387}
388
389fn cfb1_cipher_in_chunks(
391 key: &AES_KEY,
392 iv: &mut [u8],
393 in_out: &mut [u8],
394 enc: i32,
395 chunk_len: usize,
396) {
397 debug_assert!(chunk_len > 0 && chunk_len <= MAX_CFB1_CHUNK_LEN);
398 let mut num: i32 = 0;
399 indicator_check!({
400 for chunk in in_out.chunks_mut(chunk_len) {
401 unsafe {
402 AES_cfb1_encrypt(
403 chunk.as_ptr(),
404 chunk.as_mut_ptr(),
405 chunk.len() * 8,
406 key,
407 iv.as_mut_ptr(),
408 &mut num,
409 enc,
410 );
411 }
412 }
413 });
414}
415
416fn aes_cfb1_encrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
417 cfb1_cipher(key, iv, in_out, AES_ENCRYPT);
418}
419
420fn aes_cfb1_decrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
421 cfb1_cipher(key, iv, in_out, AES_DECRYPT);
422}
423
424fn aes_ofb128_encrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
427 let mut num: i32 = 0;
428 indicator_check!(unsafe {
429 AES_ofb128_encrypt(
430 in_out.as_ptr(),
431 in_out.as_mut_ptr(),
432 in_out.len(),
433 key,
434 iv.as_mut_ptr(),
435 &mut num,
436 );
437 });
438}
439
440#[allow(clippy::needless_pass_by_value)]
441pub(super) fn encrypt_ofb_mode(
442 key: &SymmetricCipherKey,
443 context: EncryptionContext,
444 in_out: &mut [u8],
445) -> Result<DecryptionContext, Unspecified> {
446 let (SymmetricCipherKey::Aes128 { enc_key, .. }
447 | SymmetricCipherKey::Aes192 { enc_key, .. }
448 | SymmetricCipherKey::Aes256 { enc_key, .. }) = &key
449 else {
450 unreachable!()
451 };
452
453 let mut iv = {
454 let mut iv = [0u8; AES_OFB_IV_LEN];
455 iv.copy_from_slice((&context).try_into()?);
456 iv
457 };
458
459 aes_ofb128_encrypt(enc_key, &mut iv, in_out);
460 iv.zeroize();
461
462 Ok(context.into())
463}
464
465pub(super) fn decrypt_ofb_mode<'in_out>(
466 key: &SymmetricCipherKey,
467 context: DecryptionContext,
468 in_out: &'in_out mut [u8],
469) -> Result<&'in_out mut [u8], Unspecified> {
470 encrypt_ofb_mode(key, context.into(), in_out).map(|_| in_out)
472}
473
474#[cfg(test)]
475mod tests {
476 use super::{cfb1_cipher_in_chunks, MAX_CFB1_CHUNK_LEN};
477 use crate::aws_lc::{AES_DECRYPT, AES_ENCRYPT};
478 use crate::cipher::key::SymmetricCipherKey;
479
480 #[test]
483 fn cfb1_chunking_does_not_change_the_result() {
484 let key_bytes = [
485 0x2bu8, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
486 0x4f, 0x3c,
487 ];
488 let key = SymmetricCipherKey::aes128(&key_bytes).unwrap();
489 let SymmetricCipherKey::Aes128 { enc_key, .. } = &key else {
490 unreachable!()
491 };
492 let plaintext: Vec<u8> = (0..64u8).collect();
493
494 let run = |input: &[u8], enc: i32, chunk_len: usize| {
495 let mut iv = [0u8; 16];
496 let mut buf = input.to_vec();
497 cfb1_cipher_in_chunks(enc_key, &mut iv, &mut buf, enc, chunk_len);
498 (buf, iv)
499 };
500
501 let (ciphertext, final_iv) = run(&plaintext, AES_ENCRYPT, MAX_CFB1_CHUNK_LEN);
502
503 for chunk_len in [1usize, 2, 3, 7, 16, 17, 63, 64] {
504 assert_eq!(
505 run(&plaintext, AES_ENCRYPT, chunk_len),
506 (ciphertext.clone(), final_iv),
507 "chunked encryption at {chunk_len} bytes changed the ciphertext or final IV"
508 );
509 assert_eq!(
510 run(&ciphertext, AES_DECRYPT, chunk_len),
511 (plaintext.clone(), final_iv),
512 "chunked decryption at {chunk_len} bytes changed the plaintext or final IV"
513 );
514 }
515 }
516}