1use cbc::cipher::{
16 Block, BlockCipher, BlockDecryptMut, BlockEncryptMut, BlockSizeUser, KeyIvInit, Unsigned,
17 block_padding::Pkcs7,
18};
19use cbc::{Decryptor, Encryptor};
20use rand_core::{OsRng, RngCore};
21
22use crate::contract::{Identified, SymmetricCipher};
23use crate::error::{Error, Result};
24use crate::internal::etm::{self, EtmKeys};
25
26pub struct CbcHmacEncryptor<
31 C: BlockCipher + BlockEncryptMut + BlockDecryptMut + cbc::cipher::KeyInit,
32 const KEY_LEN: usize,
33> {
34 keys: EtmKeys<KEY_LEN>,
35 identifier: &'static str,
36 label: &'static str,
37 _cipher: std::marker::PhantomData<fn() -> C>,
41}
42
43impl<C, const KEY_LEN: usize> CbcHmacEncryptor<C, KEY_LEN>
44where
45 C: BlockCipher + BlockEncryptMut + BlockDecryptMut + cbc::cipher::KeyInit,
46{
47 pub fn with_names(key: [u8; KEY_LEN], identifier: &'static str, label: &'static str) -> Self {
49 Self {
50 keys: EtmKeys::new(key),
51 identifier,
52 label,
53 _cipher: std::marker::PhantomData,
54 }
55 }
56
57 pub fn with_names_from_slice(
59 key: &[u8],
60 identifier: &'static str,
61 label: &'static str,
62 ) -> Result<Self> {
63 Ok(Self {
64 keys: EtmKeys::from_slice(key, label)?,
65 identifier,
66 label,
67 _cipher: std::marker::PhantomData,
68 })
69 }
70
71 pub const fn iv_len() -> usize {
74 <C as BlockSizeUser>::BlockSize::USIZE
75 }
76}
77
78impl<C, const KEY_LEN: usize> SymmetricCipher for CbcHmacEncryptor<C, KEY_LEN>
79where
80 C: BlockCipher + BlockEncryptMut + BlockDecryptMut + cbc::cipher::KeyInit,
81{
82 fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
83 let mut iv = Block::<C>::default();
84 OsRng.fill_bytes(&mut iv);
85
86 let ciphertext = Encryptor::<C>::new_from_slices(self.keys.cipher(), &iv)
87 .expect("key and iv lengths are fixed")
88 .encrypt_padded_vec_mut::<Pkcs7>(plaintext);
89
90 Ok(etm::pack(&iv, &ciphertext, self.keys.mac()))
91 }
92
93 fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
94 let (iv, body) =
95 etm::unpack_and_verify(ciphertext, Self::iv_len(), self.keys.mac(), self.label)?;
96
97 Decryptor::<C>::new_from_slices(self.keys.cipher(), iv)
98 .expect("key and iv lengths were verified")
99 .decrypt_padded_vec_mut::<Pkcs7>(body)
100 .map_err(|_| Error::DecryptionFailed { label: self.label })
101 }
102}
103
104impl<C, const KEY_LEN: usize> Identified for CbcHmacEncryptor<C, KEY_LEN>
105where
106 C: BlockCipher + BlockEncryptMut + BlockDecryptMut + cbc::cipher::KeyInit,
107{
108 fn identifier(&self) -> &str {
109 self.identifier
110 }
111}
112
113impl<C, const KEY_LEN: usize> std::fmt::Debug for CbcHmacEncryptor<C, KEY_LEN>
115where
116 C: BlockCipher + BlockEncryptMut + BlockDecryptMut + cbc::cipher::KeyInit,
117{
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 f.debug_struct("CbcHmacEncryptor")
120 .field("identifier", &self.identifier)
121 .finish_non_exhaustive()
122 }
123}
124
125macro_rules! cbc_hmac_aliases {
129 ($( $(#[$doc:meta])* $name:ident = $cipher:ty, $key_len:literal, $label:literal, $identifier:literal; )*) => {
130 $(
131 $(#[$doc])*
132 pub type $name = CbcHmacEncryptor<$cipher, $key_len>;
133
134 impl CbcHmacEncryptor<$cipher, $key_len> {
135 pub const IDENTIFIER: &'static str = $identifier;
137
138 pub fn new(key: [u8; $key_len]) -> Self {
140 Self::with_names(key, $identifier, $label)
141 }
142
143 pub fn from_slice(key: &[u8]) -> Result<Self> {
145 Self::with_names_from_slice(key, $identifier, $label)
146 }
147 }
148 )*
149 };
150}
151
152cbc_hmac_aliases! {
153 Aes256CbcEncryptor = aes::Aes256, 32, "AES-256-CBC", "aes-256-cbc-hmac";
155
156 Camellia256CbcEncryptor = camellia::Camellia256, 32, "Camellia-256-CBC", "camellia-256-cbc-hmac";
158
159 Aria256CbcEncryptor = aria::Aria256, 32, "ARIA-256-CBC", "aria-256-cbc-hmac";
161
162 Kuznyechik256CbcEncryptor = kuznyechik::Kuznyechik, 32, "Kuznyechik-256-CBC", "kuznyechik-256-cbc-hmac";
164
165 Threefish512CbcEncryptor = threefish::Threefish512, 64, "Threefish-512-CBC", "threefish-512-cbc-hmac";
167
168 Sm4CbcEncryptor = sm4::Sm4, 16, "SM4", "sm4-cbc";
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
182 fn every_cbc_hmac_alias_roundtrips_and_detects_tampering() {
183 fn check<E: SymmetricCipher>(cipher: &E, label: &str, block_len: usize) {
184 let exact = vec![b'0'; block_len];
185 let one_over = vec![b'1'; block_len + 1];
186 let multi = vec![0xa5u8; block_len * 3 + 7];
187
188 for plaintext in [
189 b"".as_slice(),
190 b"x",
191 exact.as_slice(),
192 one_over.as_slice(),
193 multi.as_slice(),
194 ] {
195 let blob = cipher.encrypt(plaintext).unwrap();
196 assert_eq!(cipher.decrypt(&blob).unwrap(), plaintext, "{label}");
197
198 let mut tampered = blob.clone();
199 let last = tampered.len() - 1;
200 tampered[last] ^= 0x01;
201 assert!(cipher.decrypt(&tampered).is_err(), "{label} 篡改未被发现");
202 }
203 }
204
205 check(&Sm4CbcEncryptor::new([5u8; 16]), "sm4-cbc", 16);
206 check(&Aes256CbcEncryptor::new([5u8; 32]), "aes-256-cbc-hmac", 16);
207 check(&Camellia256CbcEncryptor::new([5u8; 32]), "camellia", 16);
208 check(&Aria256CbcEncryptor::new([5u8; 32]), "aria", 16);
209 check(&Kuznyechik256CbcEncryptor::new([5u8; 32]), "kuznyechik", 16);
210 check(
211 &Threefish512CbcEncryptor::new([5u8; 64]),
212 "threefish-512",
213 64,
214 );
215 }
216
217 #[test]
219 fn iv_length_follows_the_block_cipher_block_size() {
220 assert_eq!(Aes256CbcEncryptor::iv_len(), 16);
221 assert_eq!(Camellia256CbcEncryptor::iv_len(), 16);
222 assert_eq!(Aria256CbcEncryptor::iv_len(), 16);
223 assert_eq!(Kuznyechik256CbcEncryptor::iv_len(), 16);
224 assert_eq!(Sm4CbcEncryptor::iv_len(), 16);
225 assert_eq!(Threefish512CbcEncryptor::iv_len(), 64);
226 }
227
228 #[test]
230 fn identifiers_are_stable() {
231 assert_eq!(
232 Aes256CbcEncryptor::new([0u8; 32]).identifier(),
233 "aes-256-cbc-hmac"
234 );
235 assert_eq!(
236 Camellia256CbcEncryptor::new([0u8; 32]).identifier(),
237 "camellia-256-cbc-hmac"
238 );
239 assert_eq!(
240 Aria256CbcEncryptor::new([0u8; 32]).identifier(),
241 "aria-256-cbc-hmac"
242 );
243 assert_eq!(
244 Kuznyechik256CbcEncryptor::new([0u8; 32]).identifier(),
245 "kuznyechik-256-cbc-hmac"
246 );
247 assert_eq!(
248 Threefish512CbcEncryptor::new([0u8; 64]).identifier(),
249 "threefish-512-cbc-hmac"
250 );
251 assert_eq!(Sm4CbcEncryptor::new([0u8; 16]).identifier(), "sm4-cbc");
252 }
253
254 #[test]
256 fn iv_is_fresh_per_encryption() {
257 let cipher = Aes256CbcEncryptor::new([5u8; 32]);
258 let first = cipher.encrypt(b"same").unwrap();
259 let second = cipher.encrypt(b"same").unwrap();
260 assert_ne!(first, second);
261 assert_ne!(first[2..18], second[2..18]);
262 }
263
264 #[test]
266 fn wrong_key_and_bad_frames_are_rejected() {
267 let blob = Aes256CbcEncryptor::new([5u8; 32])
268 .encrypt(b"secret")
269 .unwrap();
270 let other = Aes256CbcEncryptor::new([6u8; 32]);
271 assert_eq!(
272 other.decrypt(&blob).unwrap_err(),
273 Error::MacVerificationFailed {
274 label: "AES-256-CBC"
275 }
276 );
277 assert_eq!(
278 other.decrypt(b"v9 nope").unwrap_err(),
279 Error::InvalidPrefix {
280 label: "AES-256-CBC"
281 }
282 );
283 assert_eq!(
284 other.decrypt(b"v1 short").unwrap_err(),
285 Error::TooShort {
286 label: "AES-256-CBC"
287 }
288 );
289 }
290
291 #[test]
292 fn key_length_is_checked_from_slice() {
293 assert_eq!(
294 Aes256CbcEncryptor::from_slice(&[0u8; 16]).unwrap_err(),
295 Error::InvalidKeyLength {
296 label: "AES-256-CBC",
297 expected: 32,
298 got: 16
299 }
300 );
301 assert_eq!(
302 Sm4CbcEncryptor::from_slice(&[0u8; 32]).unwrap_err(),
303 Error::InvalidKeyLength {
304 label: "SM4",
305 expected: 16,
306 got: 32
307 }
308 );
309 assert_eq!(
310 Threefish512CbcEncryptor::from_slice(&[0u8; 32]).unwrap_err(),
311 Error::InvalidKeyLength {
312 label: "Threefish-512-CBC",
313 expected: 64,
314 got: 32
315 }
316 );
317 }
318
319 #[test]
321 fn bare_block_ciphers_match_their_standard_vectors() {
322 use cbc::cipher::{BlockEncrypt, KeyInit};
323
324 fn unhex(text: &str) -> Vec<u8> {
325 crate::internal::hex_decode(text).unwrap()
326 }
327 fn block_encrypt<C: BlockEncrypt + KeyInit>(key: &[u8], plaintext: &[u8]) -> Vec<u8> {
328 let cipher = C::new_from_slice(key).unwrap();
329 let mut block = Block::<C>::clone_from_slice(plaintext);
330 cipher.encrypt_block(&mut block);
331 block.to_vec()
332 }
333
334 assert_eq!(
336 block_encrypt::<sm4::Sm4>(
337 &unhex("0123456789abcdeffedcba9876543210"),
338 &unhex("0123456789abcdeffedcba9876543210")
339 ),
340 unhex("681edf34d206965e86b3e94f536e4246")
341 );
342
343 assert_eq!(
346 block_encrypt::<camellia::Camellia256>(
347 &unhex("0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff"),
348 &unhex("0123456789abcdeffedcba9876543210")
349 ),
350 unhex("9acc237dff16d76c20ef7c919e3a7509")
351 );
352
353 assert_eq!(
355 block_encrypt::<aria::Aria256>(
356 &unhex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"),
357 &unhex("00112233445566778899aabbccddeeff")
358 ),
359 unhex("f92bd7c79fb72e2f2b8f80c1972d24fc")
360 );
361
362 assert_eq!(
364 block_encrypt::<kuznyechik::Kuznyechik>(
365 &unhex("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef"),
366 &unhex("1122334455667700ffeeddccbbaa9988")
367 ),
368 unhex("7f679d90bebc24305a468d42b9d4edcd")
369 );
370
371 let threefish_vectors = [
377 (
378 "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
379 "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
380 concat!(
381 "b1a2bbc6ef6025bc40eb3822161f36e375d1bb0aee3186fbd19e47c5d479947b",
382 "7bc2f8586e35f0cff7e7f03084b0b7b1f1ab3961a580a3e97eb41ea14a6d7bbe"
383 ),
384 ),
385 (
386 concat!(
387 "b1a2bbc6ef6025bc40eb3822161f36e375d1bb0aee3186fbd19e47c5d479947b",
388 "7bc2f8586e35f0cff7e7f03084b0b7b1f1ab3961a580a3e97eb41ea14a6d7bbe"
389 ),
390 "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
391 concat!(
392 "f13ca06760dd9bbeab87b6c56f3bbbdb",
393 "e9d08a77978b942ac02d471dc10268f2",
394 "261c3d4330d6ca341f4bd4115dee16a2",
395 "1dcda2a34a0a76fba976174e4cf1e306"
396 ),
397 ),
398 ];
399 for (key, plaintext, expected) in threefish_vectors {
400 assert_eq!(
401 block_encrypt::<threefish::Threefish512>(&unhex(key), &unhex(plaintext)),
402 unhex(expected),
403 "Threefish-512 official KAT"
404 );
405 }
406 }
407}