1use aes::cipher::{BlockModeDecrypt, BlockModeEncrypt, KeyIvInit, block_padding::Pkcs7};
7use hmac::{KeyInit, Mac};
8use subtle::ConstantTimeEq;
9
10use super::SymmetricEncryptionError;
11
12type HmacSha256 = hmac::Hmac<sha2::Sha256>;
13
14pub(crate) const IV_SIZE: usize = 16;
15pub(crate) const ENC_KEY_SIZE: usize = 32;
16pub(crate) const MAC_KEY_SIZE: usize = 32;
17pub(crate) const KEY_SIZE: usize = ENC_KEY_SIZE + MAC_KEY_SIZE;
18pub(crate) const MAC_SIZE: usize = 32;
19
20pub(crate) struct Aes256CbcHmacSha256;
24
25impl Aes256CbcHmacSha256 {
26 pub(crate) fn encrypt(
32 iv: &[u8; IV_SIZE],
33 plaintext: &[u8],
34 key: &[u8; KEY_SIZE],
35 ) -> ([u8; MAC_SIZE], Vec<u8>) {
36 let (enc_key, mac_key) = split_to_subkeys(key);
37
38 let data = cbc::Encryptor::<aes::Aes256>::new(enc_key.into(), iv.into())
39 .encrypt_padded_vec::<Pkcs7>(plaintext);
40 let mac = calculate_mac(iv, &data, mac_key);
41
42 (mac, data)
43 }
44
45 pub(crate) fn decrypt(
47 iv: &[u8; IV_SIZE],
48 ciphertext: &[u8],
49 mac: &[u8; MAC_SIZE],
50 key: &[u8; KEY_SIZE],
51 ) -> Result<Vec<u8>, SymmetricEncryptionError> {
52 let (enc_key, mac_key) = split_to_subkeys(key);
53
54 let expected_mac = calculate_mac(iv, ciphertext, mac_key);
55 if expected_mac.ct_ne(mac).into() {
56 return Err(SymmetricEncryptionError::IntegrityCheckFailed);
57 }
58
59 let mut data = ciphertext.to_vec();
61 let decrypted_slice = cbc::Decryptor::<aes::Aes256>::new(enc_key.into(), iv.into())
62 .decrypt_padded::<Pkcs7>(&mut data)
63 .map_err(|_| SymmetricEncryptionError::FormatWrong)?;
64
65 let decrypted_len = decrypted_slice.len();
68 data.truncate(decrypted_len);
69
70 Ok(data)
71 }
72}
73
74fn split_to_subkeys(key: &[u8; KEY_SIZE]) -> (&[u8; ENC_KEY_SIZE], &[u8; MAC_KEY_SIZE]) {
76 let (enc_key, mac_key) = key.split_at(ENC_KEY_SIZE);
77 let enc_key = enc_key
78 .try_into()
79 .expect("first half of a 64-byte key is always 32 bytes");
80 let mac_key = mac_key
81 .try_into()
82 .expect("second half of a 64-byte key is always 32 bytes");
83 (enc_key, mac_key)
84}
85
86fn calculate_mac(iv: &[u8], data: &[u8], mac_key: &[u8; MAC_KEY_SIZE]) -> [u8; MAC_SIZE] {
89 let mut hmac =
90 HmacSha256::new_from_slice(mac_key).expect("hmac new_from_slice should not fail");
91 hmac.update(iv);
92 hmac.update(data);
93 let mac: [u8; MAC_SIZE] = (*hmac.finalize().into_bytes())
94 .try_into()
95 .expect("HMAC output size to be correct");
97 mac
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 const TESTVECTOR_PLAINTEXT: &[u8] = b"Bitwarden SDK test vector";
105 const TESTVECTOR_IV: [u8; IV_SIZE] = [
106 216, 218, 36, 0, 196, 186, 150, 85, 49, 147, 110, 168, 185, 227, 42, 172,
107 ];
108 const TESTVECTOR_MAC: [u8; MAC_SIZE] = [
109 60, 78, 44, 111, 72, 233, 3, 6, 86, 250, 217, 242, 62, 229, 184, 221, 231, 150, 189, 44,
110 99, 189, 220, 55, 196, 194, 101, 60, 102, 195, 149, 130,
111 ];
112 const TESTVECTOR_DATA: &[u8] = &[
113 234, 77, 16, 15, 189, 82, 36, 188, 182, 88, 64, 67, 145, 94, 30, 178, 36, 235, 130, 67,
114 255, 207, 183, 168, 73, 231, 82, 122, 193, 139, 25, 129,
115 ];
116
117 const TESTVECTOR_KEY: [u8; KEY_SIZE] = [
118 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
119 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
120 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
121 ];
122
123 #[test]
124 #[ignore = "Generates test vectors; run manually"]
125 fn generate_test_vectors() {
126 let (mac, data) =
127 Aes256CbcHmacSha256::encrypt(&TESTVECTOR_IV, TESTVECTOR_PLAINTEXT, &TESTVECTOR_KEY);
128 let decrypted =
129 Aes256CbcHmacSha256::decrypt(&TESTVECTOR_IV, &data, &mac, &TESTVECTOR_KEY).unwrap();
130 assert_eq!(decrypted, TESTVECTOR_PLAINTEXT);
131
132 println!("const TESTVECTOR_MAC: [u8; MAC_SIZE] = {mac:?};");
133 println!("const TESTVECTOR_DATA: &[u8] = &{data:?};");
134 }
135
136 #[test]
139 fn test_decrypt_testvector() {
140 let decrypted = Aes256CbcHmacSha256::decrypt(
141 &TESTVECTOR_IV,
142 TESTVECTOR_DATA,
143 &TESTVECTOR_MAC,
144 &TESTVECTOR_KEY,
145 )
146 .unwrap();
147
148 assert_eq!(decrypted, TESTVECTOR_PLAINTEXT);
149 }
150
151 #[test]
152 fn test_encrypt_is_deterministic() {
153 const IV: [u8; IV_SIZE] = [
154 62, 0, 239, 47, 137, 95, 64, 214, 127, 91, 184, 232, 31, 9, 165, 161,
155 ];
156
157 let (_mac, data) =
158 Aes256CbcHmacSha256::encrypt(&IV, "EncryptMe!".as_bytes(), &TESTVECTOR_KEY);
159 assert_eq!(
160 data,
161 vec![
162 214, 76, 187, 97, 58, 146, 212, 140, 95, 164, 177, 204, 179, 133, 172, 148
163 ]
164 );
165 }
166
167 #[test]
168 fn test_calculate_mac() {
169 const MAC_KEY: [u8; MAC_KEY_SIZE] = [
170 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128, 136, 144, 152,
171 160, 168, 176, 184, 192, 200, 208, 216, 224, 232, 240, 248,
172 ];
173 const IV: [u8; IV_SIZE] = [
174 0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240,
175 ];
176 const DATA: [u8; 16] = [
177 0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240,
178 ];
179
180 let mac = calculate_mac(&IV, &DATA, &MAC_KEY);
181 assert!(mac.iter().any(|&b| b != 0));
182 }
183
184 #[test]
185 fn test_encrypt_decrypt_roundtrip() {
186 let plaintext = b"EncryptMe!";
187
188 let (mac, data) = Aes256CbcHmacSha256::encrypt(&TESTVECTOR_IV, plaintext, &TESTVECTOR_KEY);
189 let decrypted =
190 Aes256CbcHmacSha256::decrypt(&TESTVECTOR_IV, &data, &mac, &TESTVECTOR_KEY).unwrap();
191
192 assert_eq!(decrypted, plaintext);
193 }
194
195 #[test]
196 fn test_decrypt_fails_when_mac_changed() {
197 let plaintext = b"EncryptMe!";
198
199 let (mut mac, data) =
200 Aes256CbcHmacSha256::encrypt(&TESTVECTOR_IV, plaintext, &TESTVECTOR_KEY);
201 mac[0] = mac[0].wrapping_add(1);
202 let result = Aes256CbcHmacSha256::decrypt(&TESTVECTOR_IV, &data, &mac, &TESTVECTOR_KEY);
203
204 assert_eq!(result, Err(SymmetricEncryptionError::IntegrityCheckFailed));
205 }
206}