matrix-rust-sdk/matrix_sdk_crypto/src/file_encryption/attachments.rs

336 lines
10 KiB
Rust

// 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<u8>,
sha: Sha256,
aes: Aes256Ctr,
}
impl<'a, R: Read> Read for AttachmentDecryptor<'a, R> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
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<AttachmentDecryptor<'a, R>, 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<String, String>,
aes: Aes256Ctr,
sha: Sha256,
}
impl<'a, R: Read + 'a> Read for AttachmentEncryptor<'a, R> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
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<String, String>,
}
#[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);
}
}