dbids_aes/
aes_ctr.rs

1//! AES Counter Mode (CTR) Implementation
2//! Devin Bidstrup 7/15/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/// Encrypts or decrypts data using AES in CTR mode.
10///
11/// Counter mode (CTR) encrypts a nonce value (the counter) and XORs the result with the plaintext
12/// to produce ciphertext, or vice versa. For the last block, which may be a partial block of u bits, the most
13/// significant u bits of the last output block are used for the exclusive-OR operation; the remaining
14/// block length-u bits of the last output block are discarded.
15///
16/// # Parameters
17/// - `key`: The AES key (128-bit, 192-bit, or 256-bit).
18/// - `nonce`: A unique nonce to start at(128-bit).  The counter increments this nonce value for each block.
19/// - `data`: The input data to be encrypted or decrypted given as a slice of bytes. Can be of any length.
20/// There is no functional difference between encryption and decryption in CTR mode, so no input is given to distinguish the two.
21///
22/// # Returns
23/// The resulting encrypted or decrypted data.
24pub fn aes_ctr(key: &[u8], data: &mut [u8], nonce: u128) {
25  let mut counter = 0u128;
26  for block in data.chunks_mut(BLOCKLEN) {
27    // Generate the counter block (nonce + counter)
28    let counter_block: [u8; BLOCKLEN] = (nonce.wrapping_add(counter)).to_be_bytes();
29
30    // Encrypt the counter block using the appropriate AES function
31    println!("Counter Block: {:x?}", counter_block);
32    let encrypted_counter_block = match key.len() {
33      16 => aes_128(key.try_into().unwrap(), counter_block, true),
34      24 => aes_192(key.try_into().unwrap(), counter_block, true),
35      32 => aes_256(key.try_into().unwrap(), counter_block, true),
36      _ => panic!("Invalid key length. Must be 128, 192, or 256 bits."),
37    };
38    println!("Encrypted Counter Block: {:x?}", encrypted_counter_block);
39
40    // XOR the encrypted counter block with the data block to form the ciphertext/plaintext
41    println!("Data Block Before XOR: {:x?}", block);
42    block.iter_mut().enumerate().for_each(|(i, byte)| {
43      *byte ^= encrypted_counter_block[i];
44    });
45    println!("Data Block After XOR: {:x?}", block);
46
47    // Increment the counter for the next block
48    counter += 1;
49  }
50}
51
52#[cfg(test)]
53mod tests {
54  use super::*;
55
56  // Examples from: SP800-38A, Appendix F
57
58  #[test]
59  fn test_aes_128_ctr() {
60    let key: [u8; 16] = [
61      0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
62      0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C
63    ];
64    let plaintext: [u8; 64] = [
65      0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A,
66      0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51,
67      0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF,
68      0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10
69    ];
70    let nonce: u128 = 0xF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF;
71
72    let exp_ciphertext: [u8; 64] = [
73      0x87, 0x4D, 0x61, 0x91, 0xB6, 0x20, 0xE3, 0x26, 0x1B, 0xEF, 0x68, 0x64, 0x99, 0x0D, 0xB6, 0xCE,
74      0x98, 0x06, 0xF6, 0x6B, 0x79, 0x70, 0xFD, 0xFF, 0x86, 0x17, 0x18, 0x7B, 0xB9, 0xFF, 0xFD, 0xFF,
75      0x5A, 0xE4, 0xDF, 0x3E, 0xDB, 0xD5, 0xD3, 0x5E, 0x5B, 0x4F, 0x09, 0x02, 0x0D, 0xB0, 0x3E, 0xAB,
76      0x1E, 0x03, 0x1D, 0xDA, 0x2F, 0xBE, 0x03, 0xD1, 0x79, 0x21, 0x70, 0xA0, 0xF3, 0x00, 0x9C, 0xEE
77    ];
78    test_aes_ctr_comm(&key, plaintext, exp_ciphertext, nonce);
79  }
80
81  #[test]
82  fn test_aes_192_ctr() {
83    let key: [u8; 24] = [
84      0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, 0xC8, 0x10, 0xF3, 0x2B,
85      0x80, 0x90, 0x79, 0xE5, 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B
86    ];
87    let plaintext: [u8; 64] = [
88      0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A,
89      0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51,
90      0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF,
91      0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10
92    ];
93    let nonce: u128 = 0xF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF;
94
95    let exp_ciphertext: [u8; 64] = [
96      0x1A, 0xBC, 0x93, 0x24, 0x17, 0x52, 0x1C, 0xA2, 0x4F, 0x2B, 0x04, 0x59, 0xFE, 0x7E, 0x6E, 0x0B,
97      0x09, 0x03, 0x39, 0xEC, 0x0A, 0xA6, 0xFA, 0xEF, 0xD5, 0xCC, 0xC2, 0xC6, 0xF4, 0xCE, 0x8E, 0x94,
98      0x1E, 0x36, 0xB2, 0x6B, 0xD1, 0xEB, 0xC6, 0x70, 0xD1, 0xBD, 0x1D, 0x66, 0x56, 0x20, 0xAB, 0xF7,
99      0x4F, 0x78, 0xA7, 0xF6, 0xD2, 0x98, 0x09, 0x58, 0x5A, 0x97, 0xDA, 0xEC, 0x58, 0xC6, 0xB0, 0x50
100    ];
101    test_aes_ctr_comm(&key, plaintext, exp_ciphertext, nonce);
102  }
103
104  #[test]
105  fn test_aes_256_ctr() {
106    let key: [u8; 32] = [
107      0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81,
108      0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4
109    ];
110    let plaintext: [u8; 64] = [
111      0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A,
112      0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51,
113      0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF,
114      0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10
115    ];
116    let nonce: u128 = 0xF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF;
117
118    let exp_ciphertext: [u8; 64] = [
119      0x60, 0x1E, 0xC3, 0x13, 0x77, 0x57, 0x89, 0xA5, 0xB7, 0xA7, 0xF5, 0x04, 0xBB, 0xF3, 0xD2, 0x28,
120      0xF4, 0x43, 0xE3, 0xCA, 0x4D, 0x62, 0xB5, 0x9A, 0xCA, 0x84, 0xE9, 0x90, 0xCA, 0xCA, 0xF5, 0xC5,
121      0x2B, 0x09, 0x30, 0xDA, 0xA2, 0x3D, 0xE9, 0x4C, 0xE8, 0x70, 0x17, 0xBA, 0x2D, 0x84, 0x98, 0x8D,
122      0xDF, 0xC9, 0xC5, 0x8D, 0xB6, 0x7A, 0xAD, 0xA6, 0x13, 0xC2, 0xDD, 0x08, 0x45, 0x79, 0x41, 0xA6
123    ];
124    test_aes_ctr_comm(&key, plaintext, exp_ciphertext, nonce);
125  }
126
127  fn test_aes_ctr_comm(key: &[u8], plaintext: [u8; 64], exp_ciphertext: [u8; 64], nonce: u128) {
128    println!("---------------------Before Encryption:---------------------\n");
129    println!("plaintext: {:x?}", plaintext);
130    println!("key: {:x?}", key);
131    println!("nonce: {:x?}", nonce);
132
133    let mut act_ciphertext: [u8; 64] = plaintext;
134    aes_ctr(key, &mut act_ciphertext, nonce);
135
136    println!("---------------------After Encryption:---------------------\n");
137    println!("actual ciphertext: {:x?}", act_ciphertext);
138    println!("expected ciphertext: {:x?}\n", exp_ciphertext);
139    assert_eq!(exp_ciphertext, act_ciphertext);
140
141    let mut act_plaintext = act_ciphertext;
142    aes_ctr(key, &mut act_plaintext, nonce);
143
144    println!("---------------------After Decryption:---------------------\n");
145    println!("actual plaintext: {:x?}", act_plaintext);
146    println!("expected plaintext: {:x?}", plaintext);
147    assert_eq!(plaintext, act_plaintext);
148  }
149}