encryption/encryptor/
camellia256cbc.rs1use camellia::Camellia256;
7use cbc::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, block_padding::Pkcs7};
8use cbc::{Decryptor, Encryptor};
9use rand_core::{OsRng, RngCore};
10
11use crate::contract::{Identified, SymmetricCipher};
12use crate::error::{Error, Result};
13use crate::internal::etm;
14use crate::key::Key;
15
16const LABEL: &str = "Camellia-256-CBC";
17const IV_LEN: usize = 16;
18
19#[derive(Debug)]
21pub struct Camellia256CbcEncryptor {
22 key: Key<32>,
23}
24
25impl Camellia256CbcEncryptor {
26 pub const IDENTIFIER: &'static str = "camellia-256-cbc-hmac";
27
28 pub fn new(key: [u8; 32]) -> 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 Camellia256CbcEncryptor {
40 fn identifier(&self) -> &str {
41 Self::IDENTIFIER
42 }
43}
44
45impl SymmetricCipher for Camellia256CbcEncryptor {
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::<Camellia256>::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::<Camellia256>::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 fn unhex(s: &str) -> Vec<u8> {
73 (0..s.len() / 2)
74 .map(|i| u8::from_str_radix(&s[2 * i..2 * i + 2], 16).unwrap())
75 .collect()
76 }
77
78 #[test]
81 fn bare_block_cipher_matches_rfc3713_vector() {
82 use cbc::cipher::{Block, BlockEncrypt, KeyInit};
83
84 let key = unhex("0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff");
85 let plaintext = unhex("0123456789abcdeffedcba9876543210");
86 let expected = unhex("9acc237dff16d76c20ef7c919e3a7509");
87
88 let cipher = Camellia256::new_from_slice(&key).unwrap();
89 let mut block = Block::<Camellia256>::clone_from_slice(&plaintext);
90 cipher.encrypt_block(&mut block);
91 assert_eq!(block.as_slice(), expected.as_slice());
92 }
93
94 #[test]
95 fn roundtrip_at_padding_boundaries() {
96 let encryptor = Camellia256CbcEncryptor::new([5u8; 32]);
97 for plaintext in [
98 b"".as_slice(),
99 b"x",
100 b"0123456789abcdef",
101 b"0123456789abcdefg",
102 ] {
103 let blob = encryptor.encrypt(plaintext).unwrap();
104 assert_eq!(encryptor.decrypt(&blob).unwrap(), plaintext);
105 }
106 }
107
108 #[test]
109 fn tampered_blob_fails_mac_before_decrypt() {
110 let encryptor = Camellia256CbcEncryptor::new([5u8; 32]);
111 let mut blob = encryptor.encrypt(b"secret").unwrap();
112 let last = blob.len() - 1;
113 blob[last] ^= 0x01;
114 assert_eq!(
115 encryptor.decrypt(&blob).unwrap_err(),
116 Error::MacVerificationFailed { label: LABEL }
117 );
118 }
119
120 #[test]
121 fn wrong_key_fails_mac() {
122 let blob = Camellia256CbcEncryptor::new([5u8; 32])
123 .encrypt(b"secret")
124 .unwrap();
125 let other = Camellia256CbcEncryptor::new([6u8; 32]);
126 assert_eq!(
127 other.decrypt(&blob).unwrap_err(),
128 Error::MacVerificationFailed { label: LABEL }
129 );
130 }
131
132 #[test]
133 fn iv_is_fresh_per_encryption() {
134 let encryptor = Camellia256CbcEncryptor::new([5u8; 32]);
135 let first = encryptor.encrypt(b"same plaintext").unwrap();
136 let second = encryptor.encrypt(b"same plaintext").unwrap();
137 assert_ne!(first, second);
138 }
139
140 #[test]
141 fn invalid_prefix_and_too_short_are_rejected() {
142 let encryptor = Camellia256CbcEncryptor::new([5u8; 32]);
143 assert_eq!(
144 encryptor.decrypt(b"v9 not a blob").unwrap_err(),
145 Error::InvalidPrefix { label: LABEL }
146 );
147 assert_eq!(
148 encryptor.decrypt(b"v1 short").unwrap_err(),
149 Error::TooShort { label: LABEL }
150 );
151 }
152
153 #[test]
154 fn key_length_is_checked() {
155 assert_eq!(
156 Camellia256CbcEncryptor::from_slice(&[0u8; 16]).unwrap_err(),
157 Error::InvalidKeyLength {
158 label: LABEL,
159 expected: 32,
160 got: 16
161 }
162 );
163 }
164}