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 in_copy: [u8; BLOCKLEN] = iv.to_be_bytes();
34  let mut in_copy_copy: [u8; BLOCKLEN] = [0; BLOCKLEN];
35
36  for block in data.chunks_mut(BLOCKLEN) {
37    // For encryption perform XOR
38    if is_encrypt {
39      block.iter_mut().enumerate().for_each(|(i, byte)| {
40        *byte ^= in_copy[i];
41      });
42    }
43    // For decryption create copies of ciphertext blocks
44    else {
45      in_copy_copy.copy_from_slice(&in_copy);
46      in_copy.copy_from_slice(&block);
47    }
48
49    // Perform forward/inverse cipher
50    let temp_block = match key.len() {
51      16 => aes_128(
52        key.try_into().unwrap(),
53        block.try_into().unwrap(),
54        is_encrypt,
55      ),
56      24 => aes_192(
57        key.try_into().unwrap(),
58        block.try_into().unwrap(),
59        is_encrypt,
60      ),
61      32 => aes_256(
62        key.try_into().unwrap(),
63        block.try_into().unwrap(),
64        is_encrypt,
65      ),
66      _ => panic!("Invalid key length. Must be 128, 192, or 256 bits."),
67    };
68    block.copy_from_slice(&temp_block);
69
70    // For encryption, copy the current block to the xor_copy for the next iteration
71    if is_encrypt {
72      in_copy.copy_from_slice(block);
73    }
74    // For decryption perform XOR
75    else {
76      block.iter_mut().enumerate().for_each(|(i, byte)| {
77        *byte ^= in_copy_copy[i];
78      });
79    }
80  }
81}
82
83// ------------------------------------------ Unit Tests ------------------------------------------
84#[cfg(test)]
85mod tests {
86  use super::*;
87
88  /// Examples from: SP800-38A, Appendix F
89
90  #[test]
91  fn test_aes_128_cbc() {
92    let key: [u8; 16] = 0x2b7e151628aed2a6abf7158809cf4f3c_u128.to_be_bytes();
93    let iv: u128 = 0x00010203_04050607_08090a0b_0c0d0e0f;
94    let plaintext: [u8; 64] = [
95      0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17,
96      0x2A, 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF,
97      0x8E, 0x51, 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A,
98      0x0A, 0x52, 0xEF, 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B,
99      0xE6, 0x6C, 0x37, 0x10,
100    ];
101    let exp_ciphertext: [u8; 64] = [
102      0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19,
103      0x7d, 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76,
104      0x78, 0xb2, 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22,
105      0x22, 0x95, 0x16, 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30,
106      0x75, 0x86, 0xe1, 0xa7,
107    ];
108    test_aes_cbc_comm(&key, plaintext, exp_ciphertext, iv);
109  }
110
111  #[test]
112  fn test_aes_192_cbc() {
113    let key: [u8; 24] = [
114      0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79,
115      0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
116    ];
117    let iv: u128 = 0x00010203_04050607_08090a0b_0c0d0e0f;
118    let plaintext: [u8; 64] = [
119      0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17,
120      0x2A, 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF,
121      0x8E, 0x51, 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A,
122      0x0A, 0x52, 0xEF, 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B,
123      0xE6, 0x6C, 0x37, 0x10,
124    ];
125    let exp_ciphertext: [u8; 64] = [
126      0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78, 0x18, 0x3a, 0x9f, 0xa0, 0x71,
127      0xe8, 0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7, 0x38, 0x76, 0x3f, 0x69,
128      0x14, 0x5a, 0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9, 0xba, 0xac, 0x3d,
129      0xf1, 0x02, 0xe0, 0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20, 0xa9, 0xe6,
130      0x4f, 0x56, 0x15, 0xcd,
131    ];
132    test_aes_cbc_comm(&key, plaintext, exp_ciphertext, iv);
133  }
134
135  #[test]
136  fn test_aes_256_cbc() {
137    let key: [u8; 32] = [
138      0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77,
139      0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14,
140      0xdf, 0xf4,
141    ];
142    let iv: u128 = 0x00010203_04050607_08090a0b_0c0d0e0f;
143    let plaintext: [u8; 64] = [
144      0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17,
145      0x2A, 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF,
146      0x8E, 0x51, 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A,
147      0x0A, 0x52, 0xEF, 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B,
148      0xE6, 0x6C, 0x37, 0x10,
149    ];
150    let exp_ciphertext: [u8; 64] = [
151      0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb,
152      0xd6, 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70,
153      0x2c, 0x7d, 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30, 0xe2, 0x63, 0x04,
154      0x23, 0x14, 0x61, 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c, 0x19, 0x07,
155      0x8c, 0x6a, 0x9d, 0x1b,
156    ];
157    test_aes_cbc_comm(&key, plaintext, exp_ciphertext, iv);
158  }
159
160  fn test_aes_cbc_comm(key: &[u8], plaintext: [u8; 64], exp_ciphertext: [u8; 64], iv: u128) {
161    println!("---------------------Before Encryption:---------------------\n");
162    println!("plaintext: {:x?}", plaintext);
163    println!("key: {:x?}", key);
164    println!("iv: {:x?}\n", iv);
165
166    let mut act_ciphertext: [u8; 64] = plaintext;
167    aes_cbc(key, &mut act_ciphertext, iv, true);
168
169    println!("---------------------After Encryption:---------------------\n");
170    println!("actual ciphertext: {:x?}", act_ciphertext);
171    println!("expected ciphertext: {:x?}\n", exp_ciphertext);
172    assert_eq!(exp_ciphertext, act_ciphertext);
173
174    let mut act_plaintext = act_ciphertext;
175    aes_cbc(key, &mut act_plaintext, iv, false);
176
177    println!("---------------------After Decryption:---------------------\n");
178    println!("actual plaintext: {:x?}", act_plaintext);
179    println!("expected plaintext: {:x?}", plaintext);
180    assert_eq!(plaintext, act_plaintext);
181  }
182}