crypto: Correctly split the 2 keys in the key export logic.

master
Damir Jelić 2020-09-04 16:34:19 +02:00
parent 5a069a8721
commit 8dbc7c38e5
2 changed files with 58 additions and 10 deletions

View File

@ -63,3 +63,4 @@ serde_json = "1.0.57"
tempfile = "3.1.0" tempfile = "3.1.0"
http = "0.2.1" http = "0.2.1"
matrix-sdk-test = { version = "0.1.0", path = "../matrix_sdk_test" } matrix-sdk-test = { version = "0.1.0", path = "../matrix_sdk_test" }
indoc = "1.0.2"

View File

@ -19,7 +19,7 @@ use byteorder::{BigEndian, ReadBytesExt};
use aes_ctr::{ use aes_ctr::{
stream_cipher::{NewStreamCipher, SyncStreamCipher}, stream_cipher::{NewStreamCipher, SyncStreamCipher},
Aes128Ctr, Aes256Ctr,
}; };
use hmac::{Hmac, Mac, NewMac}; use hmac::{Hmac, Mac, NewMac};
use pbkdf2::pbkdf2; use pbkdf2::pbkdf2;
@ -28,7 +28,7 @@ use sha2::{Sha256, Sha512};
const SALT_SIZE: usize = 16; const SALT_SIZE: usize = 16;
const IV_SIZE: usize = 16; const IV_SIZE: usize = 16;
const MAC_SIZE: usize = 32; const MAC_SIZE: usize = 32;
const KEY_SIZE: usize = 16; const KEY_SIZE: usize = 32;
pub fn decode(input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> { pub fn decode(input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> {
decode_config(input, STANDARD_NO_PAD) decode_config(input, STANDARD_NO_PAD)
@ -38,7 +38,7 @@ pub fn encode(input: impl AsRef<[u8]>) -> String {
encode_config(input, STANDARD_NO_PAD) encode_config(input, STANDARD_NO_PAD)
} }
pub fn decrypt(ciphertext: &str, passphrase: String) -> Result<String, DecodeError> { pub fn decrypt(ciphertext: &str, passphrase: &str) -> Result<String, DecodeError> {
let decoded = decode(ciphertext)?; let decoded = decode(ciphertext)?;
let mut decoded = Cursor::new(decoded); let mut decoded = Cursor::new(decoded);
@ -49,7 +49,6 @@ pub fn decrypt(ciphertext: &str, passphrase: String) -> Result<String, DecodeErr
let mut derived_keys = [0u8; KEY_SIZE * 2]; let mut derived_keys = [0u8; KEY_SIZE * 2];
let version = decoded.read_u8().unwrap(); let version = decoded.read_u8().unwrap();
decoded.read_exact(&mut salt).unwrap(); decoded.read_exact(&mut salt).unwrap();
decoded.read_exact(&mut iv).unwrap(); decoded.read_exact(&mut iv).unwrap();
@ -62,24 +61,72 @@ pub fn decrypt(ciphertext: &str, passphrase: String) -> Result<String, DecodeErr
decoded.read_exact(&mut mac).unwrap(); decoded.read_exact(&mut mac).unwrap();
let mut decoded = decoded.into_inner(); let mut decoded = decoded.into_inner();
let ciphertext = &decoded[0..ciphertext_end];
if version != 1u8 { if version != 1u8 {
panic!("Unsupported version") panic!("Unsupported version")
} }
pbkdf2::<Hmac<Sha512>>(passphrase.as_bytes(), &salt, rounds, &mut derived_keys); pbkdf2::<Hmac<Sha512>>(passphrase.as_bytes(), &salt, rounds, &mut derived_keys);
let (key, hmac_key) = derived_keys.split_at(KEY_SIZE / 2); let (key, hmac_key) = derived_keys.split_at(KEY_SIZE);
let mut hmac = Hmac::<Sha256>::new_varkey(hmac_key).unwrap(); let mut hmac = Hmac::<Sha256>::new_varkey(hmac_key).unwrap();
hmac.update(ciphertext); hmac.update(&decoded[0..ciphertext_end]);
hmac.verify(&mac).expect("MAC DOESN'T MATCH"); hmac.verify(&mac).expect("MAC DOESN'T MATCH");
let mut ciphertext = &mut decoded[ciphertext_start..ciphertext_end]; let mut ciphertext = &mut decoded[ciphertext_start..ciphertext_end];
let mut aes = Aes256Ctr::new_var(&key, &iv).expect("Can't create AES");
let mut aes = Aes128Ctr::new_var(&key, &iv).expect("Can't create AES");
aes.apply_keystream(&mut ciphertext); aes.apply_keystream(&mut ciphertext);
Ok(String::from_utf8(ciphertext.to_owned()).expect("Invalid utf-8")) Ok(String::from_utf8(ciphertext.to_owned()).expect("Invalid utf-8"))
} }
#[cfg(test)]
mod test {
use indoc::indoc;
use super::{decode, decrypt};
const PASSPHRASE: &str = "1234";
const TEST_EXPORT: &str = indoc! {"
-----BEGIN MEGOLM SESSION DATA-----
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
-----END MEGOLM SESSION DATA-----
"};
fn export_wihtout_headers() -> String {
TEST_EXPORT
.lines()
.filter(|l| !l.starts_with("-----"))
.collect()
}
#[test]
fn test_decode() {
let export = export_wihtout_headers();
assert!(decode(export).is_ok());
}
#[test]
fn test_decrypt() {
let export = export_wihtout_headers();
let decrypted = decrypt(&export, PASSPHRASE).expect("Can't decrypt key export");
}
}