matrix_sdk_crypto/
ciphers.rs1use aes::{
16 cipher::{generic_array::GenericArray, IvSizeUser, KeyIvInit, KeySizeUser, StreamCipher},
17 Aes256,
18};
19use ctr::Ctr128BE;
20use hkdf::Hkdf;
21use hmac::{
22 digest::{FixedOutput, MacError},
23 Hmac, Mac as _,
24};
25use pbkdf2::pbkdf2;
26use rand::{thread_rng, RngCore};
27use sha2::{Sha256, Sha512};
28use zeroize::{Zeroize, ZeroizeOnDrop};
29
30pub(crate) const IV_SIZE: usize = 16;
33pub(crate) const KEY_SIZE: usize = 32;
34pub(crate) const SALT_SIZE: usize = 16;
35pub(crate) const MAC_SIZE: usize = 32;
36
37type Aes256Ctr = Ctr128BE<Aes256>;
38
39type Aes256Key = GenericArray<u8, <Aes256Ctr as KeySizeUser>::KeySize>;
40type Aes256Iv = GenericArray<u8, <Aes256Ctr as IvSizeUser>::IvSize>;
41type HmacSha256Key = [u8; KEY_SIZE];
42
43#[derive(Debug)]
45pub(crate) struct HmacSha256Mac([u8; MAC_SIZE]);
46
47impl HmacSha256Mac {
48 pub(crate) fn as_bytes(&self) -> &[u8; MAC_SIZE] {
50 &self.0
51 }
52
53 pub(crate) fn into_bytes(self) -> [u8; MAC_SIZE] {
55 self.0
56 }
57
58 pub(crate) fn from_slice(bytes: &[u8]) -> Option<Self> {
62 if bytes.len() != MAC_SIZE {
63 None
64 } else {
65 let mut mac = [0u8; MAC_SIZE];
66 mac.copy_from_slice(bytes);
67
68 Some(HmacSha256Mac(mac))
69 }
70 }
71}
72
73#[derive(Zeroize, ZeroizeOnDrop)]
85pub(crate) struct AesHmacSha2Key {
86 aes_key: Box<[u8; KEY_SIZE]>,
87 mac_key: Box<[u8; KEY_SIZE]>,
88}
89
90impl AesHmacSha2Key {
91 const ZERO_SALT: &'static [u8; 32] = &[0u8; 32];
98
99 pub(crate) fn from_secret_storage_key(
106 secret_storage_key: &[u8; KEY_SIZE],
107 secret_name: &str,
108 ) -> Self {
109 let mut expanded_keys = [0u8; KEY_SIZE * 2];
110 let hkdf: Hkdf<Sha256> = Hkdf::new(Some(Self::ZERO_SALT), secret_storage_key);
111
112 hkdf.expand(secret_name.as_bytes(), &mut expanded_keys)
113 .expect("We should be able to expand 64 bytes of output key material.");
114
115 let (aes_key, mac_key) = Self::split_keys(&expanded_keys);
116
117 expanded_keys.zeroize();
118
119 Self { aes_key, mac_key }
120 }
121
122 pub(crate) fn from_passphrase(
123 passphrase: &str,
124 pbkdf_rounds: u32,
125 salt: &[u8; SALT_SIZE],
126 ) -> Self {
127 let mut expanded_keys = [0u8; KEY_SIZE * 2];
128
129 pbkdf2::<Hmac<Sha512>>(passphrase.as_bytes(), salt, pbkdf_rounds, &mut expanded_keys)
130 .expect(
131 "We should be able to expand a passphrase of any length due to \
132 HMAC being able to be initialized with any input size",
133 );
134
135 let (aes_key, mac_key) = Self::split_keys(&expanded_keys);
136
137 expanded_keys.zeroize();
138
139 Self { aes_key, mac_key }
140 }
141
142 pub(crate) fn encrypt(&self, plaintext: Vec<u8>) -> (Vec<u8>, [u8; IV_SIZE]) {
151 let initialization_vector = Self::generate_iv();
152 let ciphertext = self.apply_keystream(plaintext, &initialization_vector);
153
154 (ciphertext, initialization_vector)
155 }
156
157 pub(crate) fn apply_keystream(
172 &self,
173 mut plaintext: Vec<u8>,
174 initialization_vector: &[u8; IV_SIZE],
175 ) -> Vec<u8> {
176 let mut cipher =
177 Aes256Ctr::new(self.aes_key(), Aes256Iv::from_slice(initialization_vector));
178 cipher.apply_keystream(&mut plaintext);
179
180 plaintext
181 }
182
183 pub(crate) fn create_mac_tag(&self, ciphertext: &[u8]) -> HmacSha256Mac {
191 let mut mac = [0u8; 32];
192 let mac_array = GenericArray::from_mut_slice(&mut mac);
193
194 let mut hmac = Hmac::<Sha256>::new_from_slice(self.mac_key())
195 .expect("We should be able to create a new HMAC object from our 32 byte MAC key");
196
197 hmac.update(ciphertext);
198 hmac.finalize_into(mac_array);
199
200 HmacSha256Mac(mac)
201 }
202
203 pub(crate) fn verify_mac(&self, message: &[u8], mac: &[u8; MAC_SIZE]) -> Result<(), MacError> {
211 let mac_array = GenericArray::from_slice(mac);
212
213 let mut hmac = Hmac::<Sha256>::new_from_slice(self.mac_key())
214 .expect("We should be able to create a new HMAC object from our 32 byte MAC key");
215
216 hmac.update(message);
217 hmac.verify(mac_array)
218 }
219
220 pub(crate) fn decrypt(
226 &self,
227 ciphertext: Vec<u8>,
228 initialization_vector: &[u8; IV_SIZE],
229 ) -> Vec<u8> {
230 self.apply_keystream(ciphertext, initialization_vector)
231 }
232
233 fn split_keys(
234 expanded_keys: &[u8; KEY_SIZE * 2],
235 ) -> (Box<[u8; KEY_SIZE]>, Box<[u8; KEY_SIZE]>) {
236 let mut aes_key = Box::new([0u8; KEY_SIZE]);
237 let mut mac_key = Box::new([0u8; KEY_SIZE]);
238
239 aes_key.copy_from_slice(&expanded_keys[0..32]);
240 mac_key.copy_from_slice(&expanded_keys[32..64]);
241
242 (aes_key, mac_key)
243 }
244
245 fn generate_iv() -> [u8; IV_SIZE] {
250 let mut rng = thread_rng();
251 let mut iv = [0u8; IV_SIZE];
252
253 rng.fill_bytes(&mut iv);
254
255 Self::clamp_iv(iv)
256 }
257
258 fn clamp_iv(iv: [u8; 16]) -> [u8; IV_SIZE] {
267 let mut iv = u128::from_be_bytes(iv);
268 iv &= !(1 << 63);
269 iv.to_be_bytes()
270 }
271
272 fn aes_key(&self) -> &Aes256Key {
274 Aes256Key::from_slice(self.aes_key.as_slice())
275 }
276
277 fn mac_key(&self) -> &HmacSha256Key {
279 &self.mac_key
280 }
281}
282
283#[cfg(test)]
284mod test {
285 use super::*;
286
287 #[test]
288 fn encryption_roundtrip() {
289 let plaintext = "It's a secret to everybody";
290
291 let salt = [0u8; SALT_SIZE];
292 let key = AesHmacSha2Key::from_passphrase("My passphrase", 10, &salt);
293
294 let (ciphertext, iv) = key.encrypt(plaintext.as_bytes().to_vec());
295 let mac = key.create_mac_tag(&ciphertext);
296
297 key.verify_mac(&ciphertext, mac.as_bytes())
298 .expect("The MAC tag should be successfully verified");
299 let decrypted = key.decrypt(ciphertext, &iv);
300
301 assert_eq!(
302 plaintext.as_bytes(),
303 decrypted,
304 "An encryption roundtrip should produce the same plaintext"
305 );
306 }
307
308 #[test]
309 fn mac_decoding() {
310 let invalid_mac = [0u8; 10];
311
312 assert!(
313 HmacSha256Mac::from_slice(&invalid_mac).is_none(),
314 "We should return an error if the MAC is too short"
315 );
316
317 let mac = [0u8; 32];
318
319 HmacSha256Mac::from_slice(&mac)
320 .expect("We should be able to create a MAC from a 32 byte long slice");
321 }
322}