1use alloc::vec::Vec;
2use core::fmt;
3
4use aes::cipher::{block_padding::NoPadding, BlockModeDecrypt, BlockModeEncrypt, KeyIvInit};
5
6const BLOCK_SIZE: usize = 16;
7
8#[derive(Debug, PartialEq)]
10pub enum CbcError {
11 UnalignedLength,
13}
14
15impl fmt::Display for CbcError {
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 match self {
18 CbcError::UnalignedLength => {
19 write!(f, "CBC input length is not a multiple of the block size")
20 }
21 }
22 }
23}
24
25#[cfg(feature = "std")]
26impl std::error::Error for CbcError {}
27
28type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
29type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
30
31pub struct Aes256 {
32 key: [u8; 32],
33}
34
35impl Aes256 {
36 pub fn new(key: &[u8; 32]) -> Self {
41 Aes256 { key: *key }
42 }
43
44 pub fn encrypt_cbc(&self, plaintext: &[u8], iv: &[u8; 16]) -> Result<Vec<u8>, CbcError> {
49 if !plaintext.len().is_multiple_of(BLOCK_SIZE) {
50 return Err(CbcError::UnalignedLength);
51 }
52 let mut buf = plaintext.to_vec();
53 let message_len = buf.len();
54 Aes256CbcEnc::new(&self.key.into(), iv.into())
55 .encrypt_padded::<NoPadding>(&mut buf, message_len)
56 .map_err(|_| CbcError::UnalignedLength)?;
57 Ok(buf)
58 }
59
60 pub fn decrypt_cbc(&self, ciphertext: &[u8], iv: &[u8; 16]) -> Result<Vec<u8>, CbcError> {
65 if !ciphertext.len().is_multiple_of(BLOCK_SIZE) {
66 return Err(CbcError::UnalignedLength);
67 }
68 let mut buf = ciphertext.to_vec();
69 Aes256CbcDec::new(&self.key.into(), iv.into())
70 .decrypt_padded::<NoPadding>(&mut buf)
71 .map_err(|_| CbcError::UnalignedLength)?;
72 Ok(buf)
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_aes256_encrypt_decrypt_block() {
82 let key = [0u8; 32];
83 let iv = [0u8; 16];
84 let cipher = Aes256::new(&key);
85 let plaintext = [0u8; 16];
86 let encrypted = cipher.encrypt_cbc(&plaintext, &iv).unwrap();
87 let decrypted = cipher.decrypt_cbc(&encrypted, &iv).unwrap();
88 assert_eq!(decrypted, plaintext.to_vec());
89 }
90
91 #[test]
92 fn test_aes256_cbc_roundtrip() {
93 let key = [0x01u8; 32];
94 let iv = [0x02u8; 16];
95 let cipher = Aes256::new(&key);
96 let plaintext = [0x03u8; 32];
97 let encrypted = cipher.encrypt_cbc(&plaintext, &iv).unwrap();
98 let decrypted = cipher.decrypt_cbc(&encrypted, &iv).unwrap();
99 assert_eq!(decrypted, plaintext.to_vec());
100 }
101
102 #[test]
103 fn test_aes256_known_vector() {
104 let key: [u8; 32] = [
106 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d,
107 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3,
108 0x09, 0x14, 0xdf, 0xf4,
109 ];
110 let plaintext: [u8; 16] = [
111 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93,
112 0x17, 0x2a,
113 ];
114 let expected: [u8; 16] = [
115 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1,
116 0x81, 0xf8,
117 ];
118 let cipher = Aes256::new(&key);
119 let iv = [0u8; 16];
120 let result = cipher.encrypt_cbc(&plaintext, &iv).unwrap();
121 assert_eq!(&result[..16], &expected);
122 }
123
124 #[test]
125 fn test_aes256_nist_sp800_38a_cbc_four_block_vector() {
126 let key: [u8; 32] = decode_hex(concat!(
127 "603deb1015ca71be2b73aef0857d7781",
128 "1f352c073b6108d72d9810a30914dff4"
129 ))
130 .try_into()
131 .unwrap();
132 let iv: [u8; 16] = decode_hex("000102030405060708090a0b0c0d0e0f")
133 .try_into()
134 .unwrap();
135 let plaintext = decode_hex(concat!(
136 "6bc1bee22e409f96e93d7e117393172a",
137 "ae2d8a571e03ac9c9eb76fac45af8e51",
138 "30c81c46a35ce411e5fbc1191a0a52ef",
139 "f69f2445df4f9b17ad2b417be66c3710"
140 ));
141 let expected = decode_hex(concat!(
142 "f58c4c04d6e5f1ba779eabfb5f7bfbd6",
143 "9cfc4e967edb808d679f777bc6702c7d",
144 "39f23369a9d9bacfa530e26304231461",
145 "b2eb05e2c39be9fcda6c19078c6a9d1b"
146 ));
147 let cipher = Aes256::new(&key);
148
149 assert_eq!(cipher.encrypt_cbc(&plaintext, &iv).unwrap(), expected);
150 assert_eq!(cipher.decrypt_cbc(&expected, &iv).unwrap(), plaintext);
151 }
152
153 #[test]
154 fn test_aes256_empty_cbc_payload_is_stable() {
155 let cipher = Aes256::new(&[0x11; 32]);
156 assert!(cipher.encrypt_cbc(&[], &[0x22; 16]).unwrap().is_empty());
157 assert!(cipher.decrypt_cbc(&[], &[0x22; 16]).unwrap().is_empty());
158 }
159
160 #[test]
161 fn test_aes256_rejects_unaligned_ciphertext() {
162 assert_eq!(
163 Aes256::new(&[0; 32]).decrypt_cbc(&[0; 17], &[0; 16]),
164 Err(CbcError::UnalignedLength)
165 );
166 }
167
168 #[test]
169 fn test_aes256_rejects_unaligned_plaintext() {
170 assert_eq!(
171 Aes256::new(&[0; 32]).encrypt_cbc(&[0; 15], &[0; 16]),
172 Err(CbcError::UnalignedLength)
173 );
174 }
175
176 fn decode_hex(hex: &str) -> Vec<u8> {
177 (0..hex.len())
178 .step_by(2)
179 .map(|index| u8::from_str_radix(&hex[index..index + 2], 16).unwrap())
180 .collect()
181 }
182}