1use cbc::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, block_padding::Pkcs7};
7use cbc::{Decryptor, Encryptor};
8use rand_core::{OsRng, RngCore};
9use sm4::Sm4;
10
11use crate::contract::{Identified, SymmetricCipher};
12use crate::error::{Error, Result};
13use crate::internal::etm;
14use crate::key::Key;
15
16const LABEL: &str = "SM4";
17const IV_LEN: usize = 16;
18
19#[derive(Debug)]
21pub struct Sm4CbcEncryptor {
22 key: Key<16>,
23}
24
25impl Sm4CbcEncryptor {
26 pub const IDENTIFIER: &'static str = "sm4-cbc";
27
28 pub fn new(key: [u8; 16]) -> Self {
29 Self { key: Key::new(key) }
30 }
31
32 pub fn from_slice(key: &[u8]) -> Result<Self> {
33 Ok(Self {
34 key: Key::from_slice(key, LABEL)?,
35 })
36 }
37}
38
39impl Identified for Sm4CbcEncryptor {
40 fn identifier(&self) -> &str {
41 Self::IDENTIFIER
42 }
43}
44
45impl SymmetricCipher for Sm4CbcEncryptor {
46 fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
47 let mut iv = [0u8; IV_LEN];
48 OsRng.fill_bytes(&mut iv);
49
50 let ciphertext = Encryptor::<Sm4>::new_from_slices(self.key.as_bytes(), &iv)
51 .expect("key and iv lengths are fixed")
52 .encrypt_padded_vec_mut::<Pkcs7>(plaintext);
53
54 Ok(etm::pack(&iv, &ciphertext, self.key.as_bytes()))
55 }
56
57 fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
58 let (iv, body) = etm::unpack_and_verify(ciphertext, IV_LEN, self.key.as_bytes(), LABEL)?;
59
60 Decryptor::<Sm4>::new_from_slices(self.key.as_bytes(), iv)
61 .expect("key and iv lengths were verified")
62 .decrypt_padded_vec_mut::<Pkcs7>(body)
63 .map_err(|_| Error::DecryptionFailed { label: LABEL })
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn roundtrip_and_tamper_detection() {
73 let encryptor = Sm4CbcEncryptor::new([4u8; 16]);
74 for plaintext in [b"".as_slice(), b"plain", b"0123456789abcdef"] {
75 let blob = encryptor.encrypt(plaintext).unwrap();
76 assert_eq!(encryptor.decrypt(&blob).unwrap(), plaintext);
77 }
78
79 let mut blob = encryptor.encrypt(b"data").unwrap();
80 let last = blob.len() - 1;
81 blob[last] ^= 0x01;
82 assert_eq!(
83 encryptor.decrypt(&blob).unwrap_err(),
84 Error::MacVerificationFailed { label: LABEL }
85 );
86 }
87
88 #[test]
89 fn key_length_is_checked() {
90 assert_eq!(
91 Sm4CbcEncryptor::from_slice(&[0u8; 32]).unwrap_err(),
92 Error::InvalidKeyLength {
93 label: LABEL,
94 expected: 16,
95 got: 32
96 }
97 );
98 }
99}