1#![allow(clippy::needless_range_loop, clippy::explicit_counter_loop)]
10
11use crate::BlockCipher;
35
36pub fn ecb_encrypt<C: BlockCipher>(cipher: &C, data: &mut [u8]) {
51 let bs = C::BLOCK_LEN;
52 assert!(
53 data.len() % bs == 0,
54 "ECB: data length must be a multiple of {} (got {})",
55 bs,
56 data.len()
57 );
58 for chunk in data.chunks_mut(bs) {
59 cipher.encrypt_block(chunk);
60 }
61}
62
63pub fn ecb_decrypt<C: BlockCipher>(cipher: &C, data: &mut [u8]) {
69 let bs = C::BLOCK_LEN;
70 assert!(
71 data.len() % bs == 0,
72 "ECB: data length must be a multiple of {} (got {})",
73 bs,
74 data.len()
75 );
76 for chunk in data.chunks_mut(bs) {
77 cipher.decrypt_block(chunk);
78 }
79}
80
81pub fn cbc_encrypt<C: BlockCipher>(cipher: &C, iv: &[u8], data: &mut [u8]) {
92 let bs = C::BLOCK_LEN;
93 assert_eq!(iv.len(), bs, "CBC: IV must be {} bytes", bs);
94 assert!(
95 data.len() % bs == 0,
96 "CBC: data length must be a multiple of {} (got {})",
97 bs,
98 data.len()
99 );
100
101 let mut prev = vec![0u8; bs];
102 prev.copy_from_slice(iv);
103
104 for chunk in data.chunks_mut(bs) {
105 for i in 0..bs {
107 chunk[i] ^= prev[i];
108 }
109 cipher.encrypt_block(chunk);
110 prev.copy_from_slice(chunk);
111 }
112}
113
114pub fn cbc_decrypt<C: BlockCipher>(cipher: &C, iv: &[u8], data: &mut [u8]) {
121 let bs = C::BLOCK_LEN;
122 assert_eq!(iv.len(), bs, "CBC: IV must be {} bytes", bs);
123 assert!(
124 data.len() % bs == 0,
125 "CBC: data length must be a multiple of {} (got {})",
126 bs,
127 data.len()
128 );
129
130 let mut prev = vec![0u8; bs];
131 prev.copy_from_slice(iv);
132
133 for chunk in data.chunks_mut(bs) {
134 let ct_copy: Vec<u8> = chunk.to_vec();
135 cipher.decrypt_block(chunk);
136 for i in 0..bs {
138 chunk[i] ^= prev[i];
139 }
140 prev.copy_from_slice(&ct_copy);
141 }
142}
143
144pub fn ctr_encrypt<C: BlockCipher>(cipher: &C, nonce: &[u8], data: &mut [u8]) {
157 let bs = C::BLOCK_LEN;
158 assert!(
159 nonce.len() < bs,
160 "CTR: nonce must be shorter than block size ({} bytes)",
161 bs
162 );
163
164 let counter_bytes = bs - nonce.len();
165 let mut counter_block = vec![0u8; bs];
166 counter_block[..nonce.len()].copy_from_slice(nonce);
167
168 let mut counter: u64 = 1;
169
170 for chunk in data.chunks_mut(bs) {
171 let counter_be = counter.to_be_bytes();
173 let start = 8usize.saturating_sub(counter_bytes);
174 for i in 0..counter_bytes {
175 counter_block[nonce.len() + i] = if i + start < 8 { counter_be[i + start] } else { 0 };
176 }
177
178 let mut keystream = vec![0u8; bs];
179 keystream.copy_from_slice(&counter_block);
180 cipher.encrypt_block(&mut keystream);
181
182 for i in 0..chunk.len() {
183 chunk[i] ^= keystream[i];
184 }
185
186 counter += 1;
187 }
188}
189
190pub struct Gcm;
198
199impl Gcm {
200 pub fn encrypt<C: BlockCipher>(cipher: &C, nonce: &[u8; 12], aad: &[u8], plaintext: &[u8]) -> (Vec<u8>, [u8; 16]) {
208 assert_eq!(C::BLOCK_LEN, 16, "GCM requires a 128-bit block cipher");
209
210 let mut h = [0u8; 16];
212 cipher.encrypt_block(&mut h);
213
214 let mut j0 = [0u8; 16];
216 j0[..12].copy_from_slice(nonce);
217 j0[15] = 1;
218
219 let mut ciphertext = plaintext.to_vec();
221 gctr(cipher, &inc32(&j0), &mut ciphertext);
222
223 let tag = ghash_compute(&h, aad, &ciphertext);
225
226 let mut e_j0 = j0;
228 cipher.encrypt_block(&mut e_j0);
229
230 let mut final_tag = [0u8; 16];
231 for i in 0..16 {
232 final_tag[i] = tag[i] ^ e_j0[i];
233 }
234
235 (ciphertext, final_tag)
236 }
237
238 pub fn decrypt<C: BlockCipher>(
246 cipher: &C,
247 nonce: &[u8; 12],
248 aad: &[u8],
249 ciphertext: &[u8],
250 tag: &[u8; 16],
251 ) -> Option<Vec<u8>> {
252 assert_eq!(C::BLOCK_LEN, 16, "GCM requires a 128-bit block cipher");
253
254 let mut h = [0u8; 16];
256 cipher.encrypt_block(&mut h);
257
258 let mut j0 = [0u8; 16];
260 j0[..12].copy_from_slice(nonce);
261 j0[15] = 1;
262
263 let ghash_tag = ghash_compute(&h, aad, ciphertext);
265
266 let mut e_j0 = j0;
268 cipher.encrypt_block(&mut e_j0);
269
270 let mut expected_tag = [0u8; 16];
271 for i in 0..16 {
272 expected_tag[i] = ghash_tag[i] ^ e_j0[i];
273 }
274
275 let mut diff = 0u8;
277 for i in 0..16 {
278 diff |= tag[i] ^ expected_tag[i];
279 }
280 if diff != 0 {
281 return None;
282 }
283
284 let mut plaintext = ciphertext.to_vec();
286 gctr(cipher, &inc32(&j0), &mut plaintext);
287
288 Some(plaintext)
289 }
290}
291
292fn inc32(block: &[u8; 16]) -> [u8; 16] {
294 let mut out = *block;
295 let ctr = u32::from_be_bytes([out[12], out[13], out[14], out[15]]);
296 let new_ctr = ctr.wrapping_add(1);
297 out[12..16].copy_from_slice(&new_ctr.to_be_bytes());
298 out
299}
300
301fn gctr<C: BlockCipher>(cipher: &C, icb: &[u8; 16], data: &mut [u8]) {
303 if data.is_empty() {
304 return;
305 }
306
307 let mut cb = *icb;
308
309 for chunk in data.chunks_mut(16) {
310 let mut keystream = cb;
311 cipher.encrypt_block(&mut keystream);
312 for i in 0..chunk.len() {
313 chunk[i] ^= keystream[i];
314 }
315 cb = inc32(&cb);
316 }
317}
318
319pub(crate) fn gf128_mul(x: &[u8; 16], y: &[u8; 16]) -> [u8; 16] {
324 let mut z = [0u8; 16];
325 let mut v = *x;
326
327 for i in 0..128 {
328 let byte_idx = i / 8;
330 let bit_idx = 7 - (i % 8);
331 if (y[byte_idx] >> bit_idx) & 1 == 1 {
332 for j in 0..16 {
333 z[j] ^= v[j];
334 }
335 }
336
337 let lsb = v[15] & 1;
339 for j in (1..16).rev() {
340 v[j] = (v[j] >> 1) | (v[j - 1] << 7);
341 }
342 v[0] >>= 1;
343
344 if lsb == 1 {
346 v[0] ^= 0xE1;
347 }
348 }
349
350 z
351}
352
353fn ghash_compute(h: &[u8; 16], aad: &[u8], ciphertext: &[u8]) -> [u8; 16] {
355 let mut y = [0u8; 16];
356
357 ghash_update(&mut y, h, aad);
359
360 ghash_update(&mut y, h, ciphertext);
362
363 let mut len_block = [0u8; 16];
365 let a_bits = (aad.len() as u64) * 8;
366 let c_bits = (ciphertext.len() as u64) * 8;
367 len_block[0..8].copy_from_slice(&a_bits.to_be_bytes());
368 len_block[8..16].copy_from_slice(&c_bits.to_be_bytes());
369
370 for i in 0..16 {
371 y[i] ^= len_block[i];
372 }
373 y = gf128_mul(&y, h);
374
375 y
376}
377
378pub(crate) fn ghash_update(y: &mut [u8; 16], h: &[u8; 16], data: &[u8]) {
380 for chunk in data.chunks(16) {
381 let mut block = [0u8; 16];
382 block[..chunk.len()].copy_from_slice(chunk);
383 for i in 0..16 {
384 y[i] ^= block[i];
385 }
386 *y = gf128_mul(y, h);
387 }
388}
389
390#[cfg(test)]
395mod tests {
396 use super::*;
397 use crate::cipher::aes::Aes128;
398
399 fn hex_to_bytes(s: &str) -> Vec<u8> {
400 (0..s.len())
401 .step_by(2)
402 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
403 .collect()
404 }
405
406 #[test]
407 fn ecb_aes128_round_trip() {
408 let key = hex_to_bytes("2b7e151628aed2a6abf7158809cf4f3c");
409 let cipher = Aes128::new(&key);
410 let plaintext = hex_to_bytes("3243f6a8885a308d313198a2e03707343243f6a8885a308d313198a2e0370734");
411
412 let mut data = plaintext.clone();
413 ecb_encrypt(&cipher, &mut data);
414 assert_ne!(data, plaintext);
415
416 ecb_decrypt(&cipher, &mut data);
417 assert_eq!(data, plaintext);
418 }
419
420 #[test]
421 fn cbc_aes128_round_trip() {
422 let key = hex_to_bytes("2b7e151628aed2a6abf7158809cf4f3c");
423 let iv = hex_to_bytes("000102030405060708090a0b0c0d0e0f");
424 let cipher = Aes128::new(&key);
425 let plaintext = hex_to_bytes("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51");
426
427 let mut data = plaintext.clone();
428 cbc_encrypt(&cipher, &iv, &mut data);
429 assert_ne!(data, plaintext);
430
431 cbc_decrypt(&cipher, &iv, &mut data);
432 assert_eq!(data, plaintext);
433 }
434
435 #[test]
437 fn ctr_aes128_round_trip() {
438 let key = hex_to_bytes("2b7e151628aed2a6abf7158809cf4f3c");
439 let nonce = hex_to_bytes("f0f1f2f3f4f5f6f7f8f9fafb");
440 let cipher = Aes128::new(&key);
441 let plaintext = hex_to_bytes("6bc1bee22e409f96e93d7e117393172a");
442
443 let mut data = plaintext.clone();
444 ctr_encrypt(&cipher, &nonce, &mut data);
445 assert_ne!(data, plaintext);
446
447 ctr_encrypt(&cipher, &nonce, &mut data);
449 assert_eq!(data, plaintext);
450 }
451
452 #[test]
455 fn gcm_aes128_test_case_2() {
456 let key = [0u8; 16];
457 let nonce = [0u8; 12];
458 let cipher = Aes128::new(&key);
459
460 let plaintext = [0u8; 16];
461 let (ct, tag) = Gcm::encrypt(&cipher, &nonce, &[], &plaintext);
462
463 let pt = Gcm::decrypt(&cipher, &nonce, &[], &ct, &tag);
465 assert!(pt.is_some());
466 assert_eq!(pt.unwrap(), plaintext);
467 }
468
469 #[test]
471 fn gcm_bad_tag() {
472 let key = [0u8; 16];
473 let nonce = [0u8; 12];
474 let cipher = Aes128::new(&key);
475
476 let (ct, mut tag) = Gcm::encrypt(&cipher, &nonce, &[], b"hello world12345");
477 tag[0] ^= 0xFF; assert!(Gcm::decrypt(&cipher, &nonce, &[], &ct, &tag).is_none());
479 }
480
481 #[test]
483 fn gcm_aad_affects_tag() {
484 let key = hex_to_bytes("feffe9928665731c6d6a8f9467308308");
485 let nonce = [0u8; 12];
486 let cipher = Aes128::new(&key);
487
488 let (ct1, tag1) = Gcm::encrypt(&cipher, &nonce, b"aad1", b"plaintext1234567");
489 let (ct2, tag2) = Gcm::encrypt(&cipher, &nonce, b"aad2", b"plaintext1234567");
490
491 assert_eq!(ct1, ct2); assert_ne!(tag1, tag2); }
495
496 #[test]
498 fn gcm_nist_test_case_3() {
499 let key = hex_to_bytes("feffe9928665731c6d6a8f9467308308");
500 let nonce_bytes = hex_to_bytes("cafebabefacedbaddecaf888");
501 let nonce: [u8; 12] = nonce_bytes.try_into().unwrap();
502 let pt = hex_to_bytes(
503 "d9313225f88406e5a55909c5aff5269a\
504 86a7a9531534f7da2e4c303d8a318a72\
505 1c3c0c95956809532fcf0e2449a6b525\
506 b16aedf5aa0de657ba637b391aafd255",
507 );
508
509 let expected_ct = hex_to_bytes(
510 "42831ec2217774244b7221b784d0d49c\
511 e3aa212f2c02a4e035c17e2329aca12e\
512 21d514b25466931c7d8f6a5aac84aa05\
513 1ba30b396a0aac973d58e091473f5985",
514 );
515 let expected_tag = hex_to_bytes("4d5c2af327cd64a62cf35abd2ba6fab4");
516
517 let cipher = Aes128::new(&key);
518 let (ct, tag) = Gcm::encrypt(&cipher, &nonce, &[], &pt);
519
520 assert_eq!(ct, expected_ct, "GCM ciphertext mismatch");
521 assert_eq!(tag.to_vec(), expected_tag, "GCM tag mismatch");
522
523 let decrypted = Gcm::decrypt(&cipher, &nonce, &[], &ct, &tag).unwrap();
525 assert_eq!(decrypted, pt);
526 }
527
528 #[test]
530 fn gcm_nist_test_case_4() {
531 let key = hex_to_bytes("feffe9928665731c6d6a8f9467308308");
532 let nonce_bytes = hex_to_bytes("cafebabefacedbaddecaf888");
533 let nonce: [u8; 12] = nonce_bytes.try_into().unwrap();
534 let pt = hex_to_bytes(
535 "d9313225f88406e5a55909c5aff5269a\
536 86a7a9531534f7da2e4c303d8a318a72\
537 1c3c0c95956809532fcf0e2449a6b525\
538 b16aedf5aa0de657ba637b39",
539 );
540 let aad = hex_to_bytes(
541 "feedfacedeadbeeffeedfacedeadbeef\
542 abaddad2",
543 );
544
545 let expected_ct = hex_to_bytes(
546 "42831ec2217774244b7221b784d0d49c\
547 e3aa212f2c02a4e035c17e2329aca12e\
548 21d514b25466931c7d8f6a5aac84aa05\
549 1ba30b396a0aac973d58e091",
550 );
551 let expected_tag = hex_to_bytes("5bc94fbc3221a5db94fae95ae7121a47");
552
553 let cipher = Aes128::new(&key);
554 let (ct, tag) = Gcm::encrypt(&cipher, &nonce, &aad, &pt);
555
556 assert_eq!(ct, expected_ct, "GCM TC4 ciphertext mismatch");
557 assert_eq!(tag.to_vec(), expected_tag, "GCM TC4 tag mismatch");
558
559 let decrypted = Gcm::decrypt(&cipher, &nonce, &aad, &ct, &tag).unwrap();
560 assert_eq!(decrypted, pt);
561 }
562}