dbids_aes/
aes_cbc.rs

1//! AES Cipher Block Chaining (CBC) Mode of Operation
2//  Devin Bidstrup 6/27/25
3
4use crate::aes::{aes_128, aes_192, aes_256};
5use std::convert::TryInto;
6
7const BLOCKLEN: usize = 16; // Block length in bytes - AES is 128b block only
8
9/// This function applies AES-128-CBC encryption or decryption in place to each block in the input data
10/// using the specified key. The operation is determined by the `is_encrypt` parameter.
11///
12/// For encrpytion in AES-CBC mode each block of plaintext is XORed with the previous ciphertext block before being encrypted.
13/// The first block is the only execption where, the initialization vector (IV) is used to XOR'd with the plaintext instead.
14/// For decryption in AES-CBC mode each block of ciphertext is decrypted and then XOR'd with the previous ciphertext block to produce the plaintext.
15/// Again the IV is used for the first block instead.
16///
17/// # Arguments
18/// * `key` - The AES key to use for encryption or decryption. Given as a slice of bytes.  Must be either 16, 24, or 32 bytes long (128, 192, or 256 bits).
19/// * `data` - An immutable reference to a slice of bytes to process.  Length must be a multiple of 16 bytes.
20/// * `iv` - The initialization vector (IV) used for the first block in CBC mode.  Can only be a 128-bit value.
21/// * `is_encrypt` - A boolean indicating whether to encrypt (true) or decrypt (false).
22///
23/// # Returns
24/// A vector of processed blocks after applying AES in CBC mode.
25pub fn aes_cbc(key: &[u8], data: &mut [u8], iv: u128, is_encrypt: bool) {
26  assert!(
27    data.len() % BLOCKLEN == 0,
28    "Data length must be a multiple of {} bytes.",
29    BLOCKLEN
30  );
31
32  // Create copy of data with IV prepended
33    let mut iv_copy: [u8; BLOCKLEN] = iv.to_be_bytes();
34    let mut xor_copy: Vec<u8> = Vec::with_capacity(data.len());
35    xor_copy.extend_from_slice(&iv.to_be_bytes());
36    xor_copy.extend_from_slice(data.split_at(data.len() - BLOCKLEN).0);
37
38  for block in data.chunks_mut(BLOCKLEN) {
39    // For encryption perform XOR
40    if is_encrypt {
41      block.iter_mut().enumerate().for_each(|(i, byte)| {
42        *byte ^= iv_copy[i];
43      });
44    }
45
46    // Perform forward/inverse cipher
47    let temp_block = match key.len() {
48      16 => aes_128(
49        key.try_into().unwrap(),
50        block.try_into().unwrap(),
51        is_encrypt,
52      ),
53      24 => aes_192(
54        key.try_into().unwrap(),
55        block.try_into().unwrap(),
56        is_encrypt,
57      ),
58      32 => aes_256(
59        key.try_into().unwrap(),
60        block.try_into().unwrap(),
61        is_encrypt,
62      ),
63      _ => panic!("Invalid key length. Must be 128, 192, or 256 bits."),
64    };
65    block.copy_from_slice(&temp_block);
66
67    // For encryption, copy the current block to the xor_copy for the next iteration
68    if is_encrypt {
69      iv_copy.copy_from_slice(block);
70    }
71  }
72
73  // For decryption perform XOR
74  if !is_encrypt {
75    data.iter_mut().enumerate().for_each(|(i, byte)| {
76      *byte ^= xor_copy[i];
77    });
78  }
79}
80
81// ------------------------------------------ Unit Tests ------------------------------------------
82#[cfg(test)]
83mod tests {
84  use super::*;
85
86  /// Example from: SP800-38A, Appendix F
87  #[test]
88  fn test_aes_128_cbc() {
89    let key: [u8; 16] = 0x2b7e151628aed2a6abf7158809cf4f3c_u128.to_be_bytes();
90    let iv: u128 = 0x00010203_04050607_08090a0b_0c0d0e0f;
91    let plaintext: [u8; 64] = [
92      0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17,
93      0x2A, 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF,
94      0x8E, 0x51, 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A,
95      0x0A, 0x52, 0xEF, 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B,
96      0xE6, 0x6C, 0x37, 0x10,
97    ];
98    let exp_ciphertext: [u8; 64] = [
99      0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19,
100      0x7d, 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76,
101      0x78, 0xb2, 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22,
102      0x22, 0x95, 0x16, 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30,
103      0x75, 0x86, 0xe1, 0xa7,
104    ];
105    test_aes_cbc_comm(&key, plaintext, exp_ciphertext, iv);
106  }
107
108  #[test]
109  fn test_aes_192_cbc() {
110    let key: [u8; 24] = [
111      0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79,
112      0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
113    ];
114    let iv: u128 = 0x00010203_04050607_08090a0b_0c0d0e0f;
115    let plaintext: [u8; 64] = [
116      0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17,
117      0x2A, 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF,
118      0x8E, 0x51, 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A,
119      0x0A, 0x52, 0xEF, 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B,
120      0xE6, 0x6C, 0x37, 0x10,
121    ];
122    let exp_ciphertext: [u8; 64] = [
123      0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78, 0x18, 0x3a, 0x9f, 0xa0, 0x71,
124      0xe8, 0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7, 0x38, 0x76, 0x3f, 0x69,
125      0x14, 0x5a, 0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9, 0xba, 0xac, 0x3d,
126      0xf1, 0x02, 0xe0, 0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20, 0xa9, 0xe6,
127      0x4f, 0x56, 0x15, 0xcd,
128    ];
129    test_aes_cbc_comm(&key, plaintext, exp_ciphertext, iv);
130  }
131
132  #[test]
133  fn test_aes_256_cbc() {
134    let key: [u8; 32] = [
135      0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77,
136      0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14,
137      0xdf, 0xf4,
138    ];
139    let iv: u128 = 0x00010203_04050607_08090a0b_0c0d0e0f;
140    let plaintext: [u8; 64] = [
141      0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17,
142      0x2A, 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF,
143      0x8E, 0x51, 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A,
144      0x0A, 0x52, 0xEF, 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B,
145      0xE6, 0x6C, 0x37, 0x10,
146    ];
147    let exp_ciphertext: [u8; 64] = [
148      0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb,
149      0xd6, 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70,
150      0x2c, 0x7d, 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30, 0xe2, 0x63, 0x04,
151      0x23, 0x14, 0x61, 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c, 0x19, 0x07,
152      0x8c, 0x6a, 0x9d, 0x1b,
153    ];
154    test_aes_cbc_comm(&key, plaintext, exp_ciphertext, iv);
155  }
156
157  fn test_aes_cbc_comm(key: &[u8], plaintext: [u8; 64], exp_ciphertext: [u8; 64], iv: u128) {
158    println!("---------------------Before Encryption:---------------------\n");
159    println!("plaintext: {:x?}", plaintext);
160    println!("key: {:x?}", key);
161
162    let mut act_ciphertext: [u8; 64] = plaintext;
163    aes_cbc(key, &mut act_ciphertext, iv, true);
164
165    println!("---------------------After Encryption:---------------------\n");
166    println!("actual ciphertext: {:x?}", act_ciphertext);
167    println!("expected ciphertext: {:x?}\n", exp_ciphertext);
168    assert_eq!(exp_ciphertext, act_ciphertext);
169
170    let mut act_plaintext = act_ciphertext;
171    aes_cbc(key, &mut act_plaintext, iv, false);
172
173    println!("---------------------After Decryption:---------------------\n");
174    println!("actual plaintext: {:x?}", act_plaintext);
175    println!("expected plaintext: {:x?}", plaintext);
176    assert_eq!(plaintext, act_plaintext);
177  }
178}