// Copyright 2020 The Matrix.org Foundation C.I.C. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::{ collections::BTreeMap, io::{Error as IoError, ErrorKind, Read}, }; use thiserror::Error; use zeroize::Zeroizing; use serde::{Deserialize, Serialize}; use matrix_sdk_common::events::room::JsonWebKey; use getrandom::getrandom; use aes_ctr::{ stream_cipher::{NewStreamCipher, SyncStreamCipher}, Aes256Ctr, }; use base64::DecodeError; use sha2::{Digest, Sha256}; use super::{decode, decode_url_safe, encode, encode_url_safe}; const IV_SIZE: usize = 16; const KEY_SIZE: usize = 32; const VERSION: &str = "v2"; /// A wrapper that transparently encrypts anything that implements `Read` as an /// Matrix attachment. #[derive(Debug)] pub struct AttachmentDecryptor<'a, R: 'a + Read> { inner_reader: &'a mut R, expected_hash: Vec, sha: Sha256, aes: Aes256Ctr, } impl<'a, R: Read> Read for AttachmentDecryptor<'a, R> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let read_bytes = self.inner_reader.read(buf)?; if read_bytes == 0 { let hash = self.sha.finalize_reset(); if hash.as_slice() == self.expected_hash.as_slice() { Ok(0) } else { Err(IoError::new( ErrorKind::Other, "Hash missmatch while decrypting", )) } } else { self.sha.update(&buf[0..read_bytes]); self.aes.apply_keystream(&mut buf[0..read_bytes]); Ok(read_bytes) } } } /// Error type for attachment decryption. #[derive(Error, Debug)] pub enum DecryptorError { /// Some data in the encrypted attachment coldn't be decoded, this may be a /// hash, the secret key, or the initialization vector. #[error(transparent)] Decode(#[from] DecodeError), /// A hash is missing from the encryption info. #[error("The encryption info is missing a hash")] MissingHash, /// The supplied key or IV has an invalid length. #[error("The supplied key or IV has an invalid length.")] KeyNonceLength, /// The supplied data was encrypted with an unknown version of the /// attachment encryption spec. #[error("Unknown version for the encrypted attachment.")] UnknownVersion, } impl<'a, R: Read + 'a> AttachmentDecryptor<'a, R> { /// Wrap the given reader decrypting all the data we read from it. /// /// # Arguments /// /// * `reader` - The `Reader` that should be wrapped and decrypted. /// /// * `info` - The encryption info that is necessary to decrypt data from /// the reader. /// /// # Examples /// ``` /// # use std::io::{Cursor, Read}; /// # use matrix_sdk_crypto::{AttachmentEncryptor, AttachmentDecryptor}; /// let data = "Hello world".to_owned(); /// let mut cursor = Cursor::new(data.clone()); /// /// let mut encryptor = AttachmentEncryptor::new(&mut cursor); /// /// let mut encrypted = Vec::new(); /// encryptor.read_to_end(&mut encrypted).unwrap(); /// let info = encryptor.finish(); /// /// let mut cursor = Cursor::new(encrypted); /// let mut decryptor = AttachmentDecryptor::new(&mut cursor, info).unwrap(); /// let mut decrypted_data = Vec::new(); /// decryptor.read_to_end(&mut decrypted_data).unwrap(); /// /// let decrypted = String::from_utf8(decrypted_data).unwrap(); /// ``` pub fn new( input: &'a mut R, info: EncryptionInfo, ) -> Result, DecryptorError> { if info.version != VERSION { return Err(DecryptorError::UnknownVersion); } let hash = decode( info.hashes .get("sha256") .ok_or(DecryptorError::MissingHash)?, )?; let key = Zeroizing::from(decode_url_safe(info.web_key.k)?); let iv = decode(info.iv)?; let sha = Sha256::default(); let aes = Aes256Ctr::new_var(&key, &iv).map_err(|_| DecryptorError::KeyNonceLength)?; Ok(AttachmentDecryptor { inner_reader: input, expected_hash: hash, sha, aes, }) } } /// A wrapper that transparently encrypts anything that implements `Read`. #[derive(Debug)] pub struct AttachmentEncryptor<'a, R: Read + 'a> { finished: bool, inner_reader: &'a mut R, web_key: JsonWebKey, iv: String, hashes: BTreeMap, aes: Aes256Ctr, sha: Sha256, } impl<'a, R: Read + 'a> Read for AttachmentEncryptor<'a, R> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let read_bytes = self.inner_reader.read(buf)?; if read_bytes == 0 { let hash = self.sha.finalize_reset(); self.hashes .entry("sha256".to_owned()) .or_insert_with(|| encode(hash)); Ok(0) } else { self.aes.apply_keystream(&mut buf[0..read_bytes]); self.sha.update(&buf[0..read_bytes]); Ok(read_bytes) } } } impl<'a, R: Read + 'a> AttachmentEncryptor<'a, R> { /// Wrap the given reader encrypting all the data we read from it. /// /// After all the reads are done, and all the data is encrypted that we wish /// to encrypt a call to [`finish()`](#method.finish) is necessary to get /// the decryption key for the data. /// /// # Arguments /// /// * `reader` - The `Reader` that should be wrapped and enrypted. /// /// # Panics /// /// Panics if we can't generate enough random data to create a fresh /// encryption key. /// /// # Examples /// ``` /// # use std::io::{Cursor, Read}; /// # use matrix_sdk_crypto::AttachmentEncryptor; /// let data = "Hello world".to_owned(); /// let mut cursor = Cursor::new(data.clone()); /// /// let mut encryptor = AttachmentEncryptor::new(&mut cursor); /// /// let mut encrypted = Vec::new(); /// encryptor.read_to_end(&mut encrypted).unwrap(); /// let key = encryptor.finish(); /// ``` pub fn new(reader: &'a mut R) -> Self { let mut key = Zeroizing::new([0u8; KEY_SIZE]); let mut iv = Zeroizing::new([0u8; IV_SIZE]); getrandom(&mut *key).expect("Can't generate randomness"); // Only populate the the first 8 bits with randomness, the rest is 0 // initialized. getrandom(&mut iv[0..8]).expect("Can't generate randomness"); let web_key = JsonWebKey { kty: "oct".to_owned(), key_ops: vec!["encrypt".to_owned(), "decrypt".to_owned()], alg: "A256CTR".to_owned(), k: encode_url_safe(&*key), ext: true, }; let encoded_iv = encode(&*iv); let aes = Aes256Ctr::new_var(&*key, &*iv).expect("Cannot create AES encryption object."); AttachmentEncryptor { finished: false, inner_reader: reader, iv: encoded_iv, web_key, hashes: BTreeMap::new(), aes, sha: Sha256::default(), } } /// Consume the encryptor and get the encryption key. pub fn finish(mut self) -> EncryptionInfo { let hash = self.sha.finalize(); self.hashes .entry("sha256".to_owned()) .or_insert_with(|| encode(hash)); EncryptionInfo { version: VERSION.to_string(), hashes: self.hashes, iv: self.iv, web_key: self.web_key, } } } #[derive(Debug, Serialize, Deserialize)] pub struct EncryptionInfo { #[serde(rename = "v")] pub version: String, pub web_key: JsonWebKey, pub iv: String, pub hashes: BTreeMap, } #[cfg(test)] mod test { use super::{AttachmentDecryptor, AttachmentEncryptor, EncryptionInfo}; use serde_json::json; use std::io::{Cursor, Read}; const EXAMPLE_DATA: &[u8] = &[ 179, 154, 118, 127, 186, 127, 110, 33, 203, 33, 33, 134, 67, 100, 173, 46, 235, 27, 215, 172, 36, 26, 75, 47, 33, 160, ]; fn example_key() -> EncryptionInfo { let info = json!({ "v": "v2", "web_key": { "kty": "oct", "alg": "A256CTR", "ext": true, "k": "Voq2nkPme_x8no5-Tjq_laDAdxE6iDbxnlQXxwFPgE4", "key_ops": ["encrypt", "decrypt"] }, "iv": "i0DovxYdJEcAAAAAAAAAAA", "hashes": { "sha256": "ANdt819a8bZl4jKy3Z+jcqtiNICa2y0AW4BBJ/iQRAU" } }); serde_json::from_value(info).unwrap() } #[test] fn encrypt_decrypt_cycle() { let data = "Hello world".to_owned(); let mut cursor = Cursor::new(data.clone()); let mut encryptor = AttachmentEncryptor::new(&mut cursor); let mut encrypted = Vec::new(); encryptor.read_to_end(&mut encrypted).unwrap(); let key = encryptor.finish(); assert_ne!(encrypted.as_slice(), data.as_bytes()); let mut cursor = Cursor::new(encrypted); let mut decryptor = AttachmentDecryptor::new(&mut cursor, key).unwrap(); let mut decrypted_data = Vec::new(); decryptor.read_to_end(&mut decrypted_data).unwrap(); let decrypted = String::from_utf8(decrypted_data).unwrap(); assert_eq!(data, decrypted); } #[test] fn real_decrypt() { let mut cursor = Cursor::new(EXAMPLE_DATA.to_vec()); let key = example_key(); let mut decryptor = AttachmentDecryptor::new(&mut cursor, key).unwrap(); let mut decrypted_data = Vec::new(); decryptor.read_to_end(&mut decrypted_data).unwrap(); let decrypted = String::from_utf8(decrypted_data).unwrap(); assert_eq!("It's a secret to everybody", decrypted); } }