crypto: Initial logic for encrypting key exports.

master
Damir Jelić 2020-09-04 17:59:56 +02:00
parent 8dbc7c38e5
commit f57447527d
2 changed files with 53 additions and 4 deletions

View File

@ -25,7 +25,8 @@ async-trait = "0.1.40"
matrix-sdk-common-macros = { version = "0.1.0", path = "../matrix_sdk_common_macros" } matrix-sdk-common-macros = { version = "0.1.0", path = "../matrix_sdk_common_macros" }
matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" } matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" }
olm-rs = { git = 'https://gitlab.gnome.org/jhaye/olm-rs/', features = ["serde"]} olm-rs = { version = "0.6.0", features = ["serde"] }
getrandom = "0.1.14"
serde = { version = "1.0.115", features = ["derive", "rc"] } serde = { version = "1.0.115", features = ["derive", "rc"] }
serde_json = "1.0.57" serde_json = "1.0.57"
cjson = "0.1.1" cjson = "0.1.1"

View File

@ -16,6 +16,7 @@ use std::io::{Cursor, Read, Seek, SeekFrom};
use base64::{decode_config, encode_config, DecodeError, STANDARD_NO_PAD}; use base64::{decode_config, encode_config, DecodeError, STANDARD_NO_PAD};
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use getrandom::getrandom;
use aes_ctr::{ use aes_ctr::{
stream_cipher::{NewStreamCipher, SyncStreamCipher}, stream_cipher::{NewStreamCipher, SyncStreamCipher},
@ -38,6 +39,42 @@ pub fn encode(input: impl AsRef<[u8]>) -> String {
encode_config(input, STANDARD_NO_PAD) encode_config(input, STANDARD_NO_PAD)
} }
pub fn encrypt(mut plaintext: &mut [u8], passphrase: &str, rounds: u32) -> String {
let mut salt = [0u8; SALT_SIZE];
let mut iv = [0u8; IV_SIZE];
let mut derived_keys = [0u8; KEY_SIZE * 2];
let version: u8 = 1;
getrandom(&mut salt).expect("Can't generate randomness");
getrandom(&mut iv).expect("Can't generate randomness");
let mut iv = u128::from_be_bytes(iv);
iv &= !(1 << 63);
pbkdf2::<Hmac<Sha512>>(passphrase.as_bytes(), &salt, rounds, &mut derived_keys);
let (key, hmac_key) = derived_keys.split_at(KEY_SIZE);
let mut aes = Aes256Ctr::new_var(&key, &iv.to_be_bytes()).expect("Can't create AES");
aes.apply_keystream(&mut plaintext);
let mut payload: Vec<u8> = vec![];
payload.extend(&version.to_be_bytes());
payload.extend(&salt);
payload.extend(&iv.to_be_bytes());
payload.extend(&rounds.to_be_bytes());
payload.extend_from_slice(&plaintext);
let mut hmac = Hmac::<Sha256>::new_varkey(hmac_key).unwrap();
hmac.update(&payload);
let mac = hmac.finalize();
payload.extend(mac.into_bytes());
encode(payload)
}
pub fn decrypt(ciphertext: &str, passphrase: &str) -> Result<String, DecodeError> { pub fn decrypt(ciphertext: &str, passphrase: &str) -> Result<String, DecodeError> {
let decoded = decode(ciphertext)?; let decoded = decode(ciphertext)?;
@ -84,7 +121,7 @@ pub fn decrypt(ciphertext: &str, passphrase: &str) -> Result<String, DecodeError
mod test { mod test {
use indoc::indoc; use indoc::indoc;
use super::{decode, decrypt}; use super::{decode, decrypt, encrypt};
const PASSPHRASE: &str = "1234"; const PASSPHRASE: &str = "1234";
@ -125,8 +162,19 @@ mod test {
} }
#[test] #[test]
fn test_decrypt() { fn test_encrypt_decrypt() {
let data = "It's a secret to everybody";
let mut bytes = data.to_owned().into_bytes();
let encrypted = encrypt(&mut bytes, PASSPHRASE, 10);
let decrypted = decrypt(&encrypted, PASSPHRASE).unwrap();
assert_eq!(data, decrypted);
}
#[test]
fn test_real_decrypt() {
let export = export_wihtout_headers(); let export = export_wihtout_headers();
let decrypted = decrypt(&export, PASSPHRASE).expect("Can't decrypt key export"); decrypt(&export, PASSPHRASE).expect("Can't decrypt key export");
} }
} }