matrix_sdk_crypto/file_encryption/
attachments.rs1use std::io::{Error as IoError, Read};
16
17use aes::{
18 Aes256,
19 cipher::{KeyIvInit, StreamCipher},
20};
21use rand::{Rng, rng};
22use ruma::{
23 events::room::{
24 EncryptedFile, EncryptedFileHash, EncryptedFileHashAlgorithm, EncryptedFileHashes,
25 EncryptedFileInfo, V2EncryptedFileInfo,
26 },
27 serde::Base64,
28};
29use serde::{Deserialize, Serialize};
30use sha2::{Digest, Sha256};
31use thiserror::Error;
32
33const IV_SIZE: usize = 16;
34const KEY_SIZE: usize = 32;
35const HASH_SIZE: usize = 32;
36
37type Aes256Ctr = ctr::Ctr128BE<Aes256>;
38
39pub struct AttachmentDecryptor<'a, R: Read> {
42 inner: &'a mut R,
43 expected_hash: [u8; HASH_SIZE],
44 sha: Sha256,
45 aes: Aes256Ctr,
46}
47
48#[cfg(not(tarpaulin_include))]
49impl<'a, R: 'a + Read + std::fmt::Debug> std::fmt::Debug for AttachmentDecryptor<'a, R> {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 f.debug_struct("AttachmentDecryptor")
52 .field("inner", &self.inner)
53 .field("expected_hash", &self.expected_hash)
54 .finish()
55 }
56}
57
58impl<R: Read> Read for AttachmentDecryptor<'_, R> {
59 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
60 let read_bytes = self.inner.read(buf)?;
61
62 if read_bytes == 0 {
63 let hash = self.sha.finalize_reset();
64
65 if hash.as_slice() == self.expected_hash.as_slice() {
66 Ok(0)
67 } else {
68 Err(IoError::other("Hash mismatch while decrypting"))
69 }
70 } else {
71 self.sha.update(&buf[0..read_bytes]);
72 self.aes.apply_keystream(&mut buf[0..read_bytes]);
73
74 Ok(read_bytes)
75 }
76 }
77}
78
79#[derive(Error, Debug)]
81pub enum DecryptorError {
82 #[error(transparent)]
85 Decode(#[from] vodozemac::Base64DecodeError),
86 #[error("The encryption info is missing a hash")]
88 MissingHash,
89 #[error("Unknown version for the encrypted attachment.")]
92 UnknownVersion,
93}
94
95impl<'a, R: Read + 'a> AttachmentDecryptor<'a, R> {
96 pub fn new(
126 input: &'a mut R,
127 info: MediaEncryptionInfo,
128 ) -> Result<AttachmentDecryptor<'a, R>, DecryptorError> {
129 let EncryptedFileInfo::V2(encryption_info) = info.encryption_info else {
130 return Err(DecryptorError::UnknownVersion);
131 };
132
133 let Some(EncryptedFileHash::Sha256(hash)) =
134 info.hashes.get(&EncryptedFileHashAlgorithm::Sha256)
135 else {
136 return Err(DecryptorError::MissingHash);
137 };
138 let hash = hash.clone().into_inner();
139 let key = encryption_info.k.as_inner();
140 let iv = encryption_info.iv.as_inner();
141
142 let sha = Sha256::default();
143
144 let aes = Aes256Ctr::new(key.into(), iv.into());
145
146 Ok(AttachmentDecryptor { inner: input, expected_hash: hash, sha, aes })
147 }
148}
149
150pub struct AttachmentEncryptor<'a, R: Read + ?Sized> {
152 finished: bool,
153 inner: &'a mut R,
154 key: [u8; KEY_SIZE],
155 iv: [u8; IV_SIZE],
156 hashes: EncryptedFileHashes,
157 aes: Aes256Ctr,
158 sha: Sha256,
159}
160
161#[cfg(not(tarpaulin_include))]
162impl<'a, R: 'a + Read + std::fmt::Debug + ?Sized> std::fmt::Debug for AttachmentEncryptor<'a, R> {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 f.debug_struct("AttachmentEncryptor")
165 .field("inner", &self.inner)
166 .field("finished", &self.finished)
167 .finish()
168 }
169}
170
171impl<'a, R: Read + ?Sized + 'a> Read for AttachmentEncryptor<'a, R> {
172 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
173 let read_bytes = self.inner.read(buf)?;
174
175 if read_bytes == 0 {
176 Ok(0)
177 } else {
178 self.aes.apply_keystream(&mut buf[0..read_bytes]);
179 self.sha.update(&buf[0..read_bytes]);
180
181 Ok(read_bytes)
182 }
183 }
184}
185
186impl<'a, R: Read + ?Sized + 'a> AttachmentEncryptor<'a, R> {
187 pub fn new(reader: &'a mut R) -> Self {
217 let mut key = [0u8; KEY_SIZE];
218 let mut iv = [0u8; IV_SIZE];
219
220 let mut rng = rng();
221
222 rng.fill_bytes(&mut key);
223 rng.fill_bytes(&mut iv[0..8]);
226
227 let key_array = &key.into();
228
229 let aes = Aes256Ctr::new(key_array, &iv.into());
230
231 AttachmentEncryptor {
232 finished: false,
233 inner: reader,
234 iv,
235 key,
236 hashes: EncryptedFileHashes::new(),
237 aes,
238 sha: Sha256::default(),
239 }
240 }
241
242 pub fn finish(mut self) -> MediaEncryptionInfo {
244 let hash = self.sha.finalize();
245 self.hashes.insert(EncryptedFileHash::Sha256(Base64::new(hash.into())));
246
247 MediaEncryptionInfo {
248 encryption_info: V2EncryptedFileInfo::encode(self.key, self.iv).into(),
249 hashes: self.hashes,
250 }
251 }
252}
253
254#[derive(Debug, Serialize, Deserialize)]
257pub struct MediaEncryptionInfo {
258 #[serde(flatten)]
260 pub encryption_info: EncryptedFileInfo,
261 pub hashes: EncryptedFileHashes,
263}
264
265impl From<EncryptedFile> for MediaEncryptionInfo {
266 fn from(file: EncryptedFile) -> Self {
267 Self { encryption_info: file.info, hashes: file.hashes }
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use std::io::{Cursor, Read};
274
275 use serde_json::json;
276
277 use super::{AttachmentDecryptor, AttachmentEncryptor, MediaEncryptionInfo};
278
279 const EXAMPLE_DATA: &[u8] = &[
280 179, 154, 118, 127, 186, 127, 110, 33, 203, 33, 33, 134, 67, 100, 173, 46, 235, 27, 215,
281 172, 36, 26, 75, 47, 33, 160,
282 ];
283
284 fn example_key_json() -> serde_json::Value {
285 json!({
286 "v": "v2",
287 "key": {
288 "kty": "oct",
289 "alg": "A256CTR",
290 "ext": true,
291 "k": "Voq2nkPme_x8no5-Tjq_laDAdxE6iDbxnlQXxwFPgE4",
292 "key_ops": ["decrypt", "encrypt"]
293 },
294 "iv": "i0DovxYdJEcAAAAAAAAAAA",
295 "hashes": {
296 "sha256": "ANdt819a8bZl4jKy3Z+jcqtiNICa2y0AW4BBJ/iQRAU"
297 }
298 })
299 }
300
301 fn example_key() -> MediaEncryptionInfo {
302 serde_json::from_value(example_key_json()).unwrap()
303 }
304
305 #[test]
306 fn media_encryption_info_serde_roundtrip() {
307 let json = example_key_json();
308
309 let info = serde_json::from_value::<MediaEncryptionInfo>(json.clone()).unwrap();
310
311 let serialized_info = serde_json::to_value(&info).unwrap();
312 assert_eq!(serialized_info, json);
313 }
314
315 #[test]
316 fn encrypt_decrypt_cycle() {
317 let data = "Hello world".to_owned();
318 let mut cursor = Cursor::new(data.clone());
319
320 let mut encryptor = AttachmentEncryptor::new(&mut cursor);
321
322 let mut encrypted = Vec::new();
323
324 encryptor.read_to_end(&mut encrypted).unwrap();
325 let key = encryptor.finish();
326 assert_ne!(encrypted.as_slice(), data.as_bytes());
327
328 let mut cursor = Cursor::new(encrypted);
329 let mut decryptor = AttachmentDecryptor::new(&mut cursor, key).unwrap();
330 let mut decrypted_data = Vec::new();
331
332 decryptor.read_to_end(&mut decrypted_data).unwrap();
333
334 let decrypted = String::from_utf8(decrypted_data).unwrap();
335
336 assert_eq!(data, decrypted);
337 }
338
339 #[test]
340 fn real_decrypt() {
341 let mut cursor = Cursor::new(EXAMPLE_DATA.to_vec());
342 let key = example_key();
343
344 let mut decryptor = AttachmentDecryptor::new(&mut cursor, key).unwrap();
345 let mut decrypted_data = Vec::new();
346
347 decryptor.read_to_end(&mut decrypted_data).unwrap();
348 let decrypted = String::from_utf8(decrypted_data).unwrap();
349
350 assert_eq!("It's a secret to everybody", decrypted);
351 }
352
353 #[test]
354 fn decrypt_invalid_hash() {
355 let mut cursor = Cursor::new("fake message");
356 let key = example_key();
357
358 let mut decryptor = AttachmentDecryptor::new(&mut cursor, key).unwrap();
359 let mut decrypted_data = Vec::new();
360
361 decryptor.read_to_end(&mut decrypted_data).unwrap_err();
362 }
363}