1use crate::BlockCipher;
25
26pub fn ecb_encrypt<C: BlockCipher>(cipher: &C, data: &mut [u8]) {
41 let bs = C::BLOCK_LEN;
42 assert!(
43 data.len() % bs == 0,
44 "ECB: data length must be a multiple of {} (got {})",
45 bs,
46 data.len()
47 );
48 for chunk in data.chunks_mut(bs) {
49 cipher.encrypt_block(chunk);
50 }
51}
52
53pub fn ecb_decrypt<C: BlockCipher>(cipher: &C, data: &mut [u8]) {
59 let bs = C::BLOCK_LEN;
60 assert!(
61 data.len() % bs == 0,
62 "ECB: data length must be a multiple of {} (got {})",
63 bs,
64 data.len()
65 );
66 for chunk in data.chunks_mut(bs) {
67 cipher.decrypt_block(chunk);
68 }
69}
70
71pub fn cbc_encrypt<C: BlockCipher>(cipher: &C, iv: &[u8], data: &mut [u8]) {
82 let bs = C::BLOCK_LEN;
83 assert_eq!(iv.len(), bs, "CBC: IV must be {} bytes", bs);
84 assert!(
85 data.len() % bs == 0,
86 "CBC: data length must be a multiple of {} (got {})",
87 bs,
88 data.len()
89 );
90
91 let mut prev = vec![0u8; bs];
92 prev.copy_from_slice(iv);
93
94 for chunk in data.chunks_mut(bs) {
95 for i in 0..bs {
97 chunk[i] ^= prev[i];
98 }
99 cipher.encrypt_block(chunk);
100 prev.copy_from_slice(chunk);
101 }
102}
103
104pub fn cbc_decrypt<C: BlockCipher>(cipher: &C, iv: &[u8], data: &mut [u8]) {
111 let bs = C::BLOCK_LEN;
112 assert_eq!(iv.len(), bs, "CBC: IV must be {} bytes", bs);
113 assert!(
114 data.len() % bs == 0,
115 "CBC: data length must be a multiple of {} (got {})",
116 bs,
117 data.len()
118 );
119
120 let mut prev = vec![0u8; bs];
121 prev.copy_from_slice(iv);
122
123 for chunk in data.chunks_mut(bs) {
124 let ct_copy: Vec<u8> = chunk.to_vec();
125 cipher.decrypt_block(chunk);
126 for i in 0..bs {
128 chunk[i] ^= prev[i];
129 }
130 prev.copy_from_slice(&ct_copy);
131 }
132}
133
134pub fn ctr_encrypt<C: BlockCipher>(cipher: &C, nonce: &[u8], data: &mut [u8]) {
147 let bs = C::BLOCK_LEN;
148 assert!(
149 nonce.len() < bs,
150 "CTR: nonce must be shorter than block size ({} bytes)",
151 bs
152 );
153
154 let counter_bytes = bs - nonce.len();
155 let mut counter_block = vec![0u8; bs];
156 counter_block[..nonce.len()].copy_from_slice(nonce);
157
158 let mut counter: u64 = 1;
159
160 for chunk in data.chunks_mut(bs) {
161 let counter_be = counter.to_be_bytes();
163 let start = 8usize.saturating_sub(counter_bytes);
164 for i in 0..counter_bytes {
165 counter_block[nonce.len() + i] = if i + start < 8 { counter_be[i + start] } else { 0 };
166 }
167
168 let mut keystream = vec![0u8; bs];
169 keystream.copy_from_slice(&counter_block);
170 cipher.encrypt_block(&mut keystream);
171
172 for i in 0..chunk.len() {
173 chunk[i] ^= keystream[i];
174 }
175
176 counter += 1;
177 }
178}
179
180pub struct Gcm;
188
189impl Gcm {
190 pub fn encrypt<C: BlockCipher>(cipher: &C, nonce: &[u8; 12], aad: &[u8], plaintext: &[u8]) -> (Vec<u8>, [u8; 16]) {
198 assert_eq!(C::BLOCK_LEN, 16, "GCM requires a 128-bit block cipher");
199
200 let mut h = [0u8; 16];
202 cipher.encrypt_block(&mut h);
203
204 let mut j0 = [0u8; 16];
206 j0[..12].copy_from_slice(nonce);
207 j0[15] = 1;
208
209 let mut ciphertext = plaintext.to_vec();
211 gctr(cipher, &inc32(&j0), &mut ciphertext);
212
213 let tag = ghash_compute(&h, aad, &ciphertext);
215
216 let mut e_j0 = j0;
218 cipher.encrypt_block(&mut e_j0);
219
220 let mut final_tag = [0u8; 16];
221 for i in 0..16 {
222 final_tag[i] = tag[i] ^ e_j0[i];
223 }
224
225 (ciphertext, final_tag)
226 }
227
228 pub fn decrypt<C: BlockCipher>(
236 cipher: &C,
237 nonce: &[u8; 12],
238 aad: &[u8],
239 ciphertext: &[u8],
240 tag: &[u8; 16],
241 ) -> Option<Vec<u8>> {
242 assert_eq!(C::BLOCK_LEN, 16, "GCM requires a 128-bit block cipher");
243
244 let mut h = [0u8; 16];
246 cipher.encrypt_block(&mut h);
247
248 let mut j0 = [0u8; 16];
250 j0[..12].copy_from_slice(nonce);
251 j0[15] = 1;
252
253 let ghash_tag = ghash_compute(&h, aad, ciphertext);
255
256 let mut e_j0 = j0;
258 cipher.encrypt_block(&mut e_j0);
259
260 let mut expected_tag = [0u8; 16];
261 for i in 0..16 {
262 expected_tag[i] = ghash_tag[i] ^ e_j0[i];
263 }
264
265 let mut diff = 0u8;
267 for i in 0..16 {
268 diff |= tag[i] ^ expected_tag[i];
269 }
270 if diff != 0 {
271 return None;
272 }
273
274 let mut plaintext = ciphertext.to_vec();
276 gctr(cipher, &inc32(&j0), &mut plaintext);
277
278 Some(plaintext)
279 }
280}
281
282fn inc32(block: &[u8; 16]) -> [u8; 16] {
284 let mut out = *block;
285 let ctr = u32::from_be_bytes([out[12], out[13], out[14], out[15]]);
286 let new_ctr = ctr.wrapping_add(1);
287 out[12..16].copy_from_slice(&new_ctr.to_be_bytes());
288 out
289}
290
291fn gctr<C: BlockCipher>(cipher: &C, icb: &[u8; 16], data: &mut [u8]) {
293 if data.is_empty() {
294 return;
295 }
296
297 let mut cb = *icb;
298
299 for chunk in data.chunks_mut(16) {
300 let mut keystream = cb;
301 cipher.encrypt_block(&mut keystream);
302 for i in 0..chunk.len() {
303 chunk[i] ^= keystream[i];
304 }
305 cb = inc32(&cb);
306 }
307}
308
309pub(crate) fn gf128_mul(x: &[u8; 16], y: &[u8; 16]) -> [u8; 16] {
314 let mut z = [0u8; 16];
315 let mut v = *x;
316
317 for i in 0..128 {
318 let byte_idx = i / 8;
320 let bit_idx = 7 - (i % 8);
321 if (y[byte_idx] >> bit_idx) & 1 == 1 {
322 for j in 0..16 {
323 z[j] ^= v[j];
324 }
325 }
326
327 let lsb = v[15] & 1;
329 for j in (1..16).rev() {
330 v[j] = (v[j] >> 1) | (v[j - 1] << 7);
331 }
332 v[0] >>= 1;
333
334 if lsb == 1 {
336 v[0] ^= 0xE1;
337 }
338 }
339
340 z
341}
342
343fn ghash_compute(h: &[u8; 16], aad: &[u8], ciphertext: &[u8]) -> [u8; 16] {
345 let mut y = [0u8; 16];
346
347 ghash_update(&mut y, h, aad);
349
350 ghash_update(&mut y, h, ciphertext);
352
353 let mut len_block = [0u8; 16];
355 let a_bits = (aad.len() as u64) * 8;
356 let c_bits = (ciphertext.len() as u64) * 8;
357 len_block[0..8].copy_from_slice(&a_bits.to_be_bytes());
358 len_block[8..16].copy_from_slice(&c_bits.to_be_bytes());
359
360 for i in 0..16 {
361 y[i] ^= len_block[i];
362 }
363 y = gf128_mul(&y, h);
364
365 y
366}
367
368pub(crate) fn ghash_update(y: &mut [u8; 16], h: &[u8; 16], data: &[u8]) {
370 for chunk in data.chunks(16) {
371 let mut block = [0u8; 16];
372 block[..chunk.len()].copy_from_slice(chunk);
373 for i in 0..16 {
374 y[i] ^= block[i];
375 }
376 *y = gf128_mul(y, h);
377 }
378}
379
380#[cfg(test)]
385mod tests {
386 use super::*;
387 use crate::cipher::aes::Aes128;
388
389 fn hex_to_bytes(s: &str) -> Vec<u8> {
390 (0..s.len())
391 .step_by(2)
392 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
393 .collect()
394 }
395
396 #[test]
397 fn ecb_aes128_round_trip() {
398 let key = hex_to_bytes("2b7e151628aed2a6abf7158809cf4f3c");
399 let cipher = Aes128::new(&key);
400 let plaintext = hex_to_bytes("3243f6a8885a308d313198a2e03707343243f6a8885a308d313198a2e0370734");
401
402 let mut data = plaintext.clone();
403 ecb_encrypt(&cipher, &mut data);
404 assert_ne!(data, plaintext);
405
406 ecb_decrypt(&cipher, &mut data);
407 assert_eq!(data, plaintext);
408 }
409
410 #[test]
411 fn cbc_aes128_round_trip() {
412 let key = hex_to_bytes("2b7e151628aed2a6abf7158809cf4f3c");
413 let iv = hex_to_bytes("000102030405060708090a0b0c0d0e0f");
414 let cipher = Aes128::new(&key);
415 let plaintext = hex_to_bytes("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51");
416
417 let mut data = plaintext.clone();
418 cbc_encrypt(&cipher, &iv, &mut data);
419 assert_ne!(data, plaintext);
420
421 cbc_decrypt(&cipher, &iv, &mut data);
422 assert_eq!(data, plaintext);
423 }
424
425 #[test]
427 fn ctr_aes128_round_trip() {
428 let key = hex_to_bytes("2b7e151628aed2a6abf7158809cf4f3c");
429 let nonce = hex_to_bytes("f0f1f2f3f4f5f6f7f8f9fafb");
430 let cipher = Aes128::new(&key);
431 let plaintext = hex_to_bytes("6bc1bee22e409f96e93d7e117393172a");
432
433 let mut data = plaintext.clone();
434 ctr_encrypt(&cipher, &nonce, &mut data);
435 assert_ne!(data, plaintext);
436
437 ctr_encrypt(&cipher, &nonce, &mut data);
439 assert_eq!(data, plaintext);
440 }
441
442 #[test]
445 fn gcm_aes128_test_case_2() {
446 let key = [0u8; 16];
447 let nonce = [0u8; 12];
448 let cipher = Aes128::new(&key);
449
450 let plaintext = [0u8; 16];
451 let (ct, tag) = Gcm::encrypt(&cipher, &nonce, &[], &plaintext);
452
453 let pt = Gcm::decrypt(&cipher, &nonce, &[], &ct, &tag);
455 assert!(pt.is_some());
456 assert_eq!(pt.unwrap(), plaintext);
457 }
458
459 #[test]
461 fn gcm_bad_tag() {
462 let key = [0u8; 16];
463 let nonce = [0u8; 12];
464 let cipher = Aes128::new(&key);
465
466 let (ct, mut tag) = Gcm::encrypt(&cipher, &nonce, &[], b"hello world12345");
467 tag[0] ^= 0xFF; assert!(Gcm::decrypt(&cipher, &nonce, &[], &ct, &tag).is_none());
469 }
470
471 #[test]
473 fn gcm_aad_affects_tag() {
474 let key = hex_to_bytes("feffe9928665731c6d6a8f9467308308");
475 let nonce = [0u8; 12];
476 let cipher = Aes128::new(&key);
477
478 let (ct1, tag1) = Gcm::encrypt(&cipher, &nonce, b"aad1", b"plaintext1234567");
479 let (ct2, tag2) = Gcm::encrypt(&cipher, &nonce, b"aad2", b"plaintext1234567");
480
481 assert_eq!(ct1, ct2); assert_ne!(tag1, tag2); }
485
486 #[test]
488 fn gcm_nist_test_case_3() {
489 let key = hex_to_bytes("feffe9928665731c6d6a8f9467308308");
490 let nonce_bytes = hex_to_bytes("cafebabefacedbaddecaf888");
491 let nonce: [u8; 12] = nonce_bytes.try_into().unwrap();
492 let pt = hex_to_bytes(
493 "d9313225f88406e5a55909c5aff5269a\
494 86a7a9531534f7da2e4c303d8a318a72\
495 1c3c0c95956809532fcf0e2449a6b525\
496 b16aedf5aa0de657ba637b391aafd255",
497 );
498
499 let expected_ct = hex_to_bytes(
500 "42831ec2217774244b7221b784d0d49c\
501 e3aa212f2c02a4e035c17e2329aca12e\
502 21d514b25466931c7d8f6a5aac84aa05\
503 1ba30b396a0aac973d58e091473f5985",
504 );
505 let expected_tag = hex_to_bytes("4d5c2af327cd64a62cf35abd2ba6fab4");
506
507 let cipher = Aes128::new(&key);
508 let (ct, tag) = Gcm::encrypt(&cipher, &nonce, &[], &pt);
509
510 assert_eq!(ct, expected_ct, "GCM ciphertext mismatch");
511 assert_eq!(tag.to_vec(), expected_tag, "GCM tag mismatch");
512
513 let decrypted = Gcm::decrypt(&cipher, &nonce, &[], &ct, &tag).unwrap();
515 assert_eq!(decrypted, pt);
516 }
517
518 #[test]
520 fn gcm_nist_test_case_4() {
521 let key = hex_to_bytes("feffe9928665731c6d6a8f9467308308");
522 let nonce_bytes = hex_to_bytes("cafebabefacedbaddecaf888");
523 let nonce: [u8; 12] = nonce_bytes.try_into().unwrap();
524 let pt = hex_to_bytes(
525 "d9313225f88406e5a55909c5aff5269a\
526 86a7a9531534f7da2e4c303d8a318a72\
527 1c3c0c95956809532fcf0e2449a6b525\
528 b16aedf5aa0de657ba637b39",
529 );
530 let aad = hex_to_bytes(
531 "feedfacedeadbeeffeedfacedeadbeef\
532 abaddad2",
533 );
534
535 let expected_ct = hex_to_bytes(
536 "42831ec2217774244b7221b784d0d49c\
537 e3aa212f2c02a4e035c17e2329aca12e\
538 21d514b25466931c7d8f6a5aac84aa05\
539 1ba30b396a0aac973d58e091",
540 );
541 let expected_tag = hex_to_bytes("5bc94fbc3221a5db94fae95ae7121a47");
542
543 let cipher = Aes128::new(&key);
544 let (ct, tag) = Gcm::encrypt(&cipher, &nonce, &aad, &pt);
545
546 assert_eq!(ct, expected_ct, "GCM TC4 ciphertext mismatch");
547 assert_eq!(tag.to_vec(), expected_tag, "GCM TC4 tag mismatch");
548
549 let decrypted = Gcm::decrypt(&cipher, &nonce, &aad, &ct, &tag).unwrap();
550 assert_eq!(decrypted, pt);
551 }
552}