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 self.encrypt_cbc_in_place(&mut buf, iv)?;
54 Ok(buf)
55 }
56
57 pub fn encrypt_cbc_in_place(&self, buf: &mut [u8], iv: &[u8; 16]) -> Result<(), CbcError> {
59 if !buf.len().is_multiple_of(BLOCK_SIZE) {
60 return Err(CbcError::UnalignedLength);
61 }
62 let message_len = buf.len();
63 Aes256CbcEnc::new(&self.key.into(), iv.into())
64 .encrypt_padded::<NoPadding>(buf, message_len)
65 .map_err(|_| CbcError::UnalignedLength)?;
66 Ok(())
67 }
68
69 pub(crate) fn cbc_decryptor(&self, iv: &[u8; 16]) -> cbc::Decryptor<aes::Aes256> {
72 Aes256CbcDec::new(&self.key.into(), iv.into())
73 }
74
75 pub fn decrypt_cbc(&self, ciphertext: &[u8], iv: &[u8; 16]) -> Result<Vec<u8>, CbcError> {
80 if !ciphertext.len().is_multiple_of(BLOCK_SIZE) {
81 return Err(CbcError::UnalignedLength);
82 }
83 let mut buf = ciphertext.to_vec();
84 Aes256CbcDec::new(&self.key.into(), iv.into())
85 .decrypt_padded::<NoPadding>(&mut buf)
86 .map_err(|_| CbcError::UnalignedLength)?;
87 Ok(buf)
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_aes256_encrypt_decrypt_block() {
97 let key = [0u8; 32];
98 let iv = [0u8; 16];
99 let cipher = Aes256::new(&key);
100 let plaintext = [0u8; 16];
101 let encrypted = cipher.encrypt_cbc(&plaintext, &iv).unwrap();
102 let decrypted = cipher.decrypt_cbc(&encrypted, &iv).unwrap();
103 assert_eq!(decrypted, plaintext.to_vec());
104 }
105
106 #[test]
107 fn test_aes256_cbc_roundtrip() {
108 let key = [0x01u8; 32];
109 let iv = [0x02u8; 16];
110 let cipher = Aes256::new(&key);
111 let plaintext = [0x03u8; 32];
112 let encrypted = cipher.encrypt_cbc(&plaintext, &iv).unwrap();
113 let decrypted = cipher.decrypt_cbc(&encrypted, &iv).unwrap();
114 assert_eq!(decrypted, plaintext.to_vec());
115 }
116
117 #[test]
118 fn test_aes256_known_vector() {
119 let key: [u8; 32] = [
121 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d,
122 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3,
123 0x09, 0x14, 0xdf, 0xf4,
124 ];
125 let plaintext: [u8; 16] = [
126 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93,
127 0x17, 0x2a,
128 ];
129 let expected: [u8; 16] = [
130 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1,
131 0x81, 0xf8,
132 ];
133 let cipher = Aes256::new(&key);
134 let iv = [0u8; 16];
135 let result = cipher.encrypt_cbc(&plaintext, &iv).unwrap();
136 assert_eq!(&result[..16], &expected);
137 }
138
139 #[test]
140 fn test_aes256_nist_sp800_38a_cbc_four_block_vector() {
141 let key: [u8; 32] = decode_hex(concat!(
142 "603deb1015ca71be2b73aef0857d7781",
143 "1f352c073b6108d72d9810a30914dff4"
144 ))
145 .try_into()
146 .unwrap();
147 let iv: [u8; 16] = decode_hex("000102030405060708090a0b0c0d0e0f")
148 .try_into()
149 .unwrap();
150 let plaintext = decode_hex(concat!(
151 "6bc1bee22e409f96e93d7e117393172a",
152 "ae2d8a571e03ac9c9eb76fac45af8e51",
153 "30c81c46a35ce411e5fbc1191a0a52ef",
154 "f69f2445df4f9b17ad2b417be66c3710"
155 ));
156 let expected = decode_hex(concat!(
157 "f58c4c04d6e5f1ba779eabfb5f7bfbd6",
158 "9cfc4e967edb808d679f777bc6702c7d",
159 "39f23369a9d9bacfa530e26304231461",
160 "b2eb05e2c39be9fcda6c19078c6a9d1b"
161 ));
162 let cipher = Aes256::new(&key);
163
164 assert_eq!(cipher.encrypt_cbc(&plaintext, &iv).unwrap(), expected);
165 assert_eq!(cipher.decrypt_cbc(&expected, &iv).unwrap(), plaintext);
166 }
167
168 #[test]
169 fn test_aes256_empty_cbc_payload_is_stable() {
170 let cipher = Aes256::new(&[0x11; 32]);
171 assert!(cipher.encrypt_cbc(&[], &[0x22; 16]).unwrap().is_empty());
172 assert!(cipher.decrypt_cbc(&[], &[0x22; 16]).unwrap().is_empty());
173 }
174
175 #[test]
176 fn test_aes256_rejects_unaligned_ciphertext() {
177 assert_eq!(
178 Aes256::new(&[0; 32]).decrypt_cbc(&[0; 17], &[0; 16]),
179 Err(CbcError::UnalignedLength)
180 );
181 }
182
183 #[test]
184 fn test_aes256_rejects_unaligned_plaintext() {
185 assert_eq!(
186 Aes256::new(&[0; 32]).encrypt_cbc(&[0; 15], &[0; 16]),
187 Err(CbcError::UnalignedLength)
188 );
189 }
190
191 fn decode_hex(hex: &str) -> Vec<u8> {
192 (0..hex.len())
193 .step_by(2)
194 .map(|index| u8::from_str_radix(&hex[index..index + 2], 16).unwrap())
195 .collect()
196 }
197}