From a2bfa08e0984c4a083b20a41ef0968379dc3c003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 19 Aug 2020 09:23:03 +0200 Subject: [PATCH] crypto: Initial decryption method for key exports. --- matrix_sdk_crypto/Cargo.toml | 6 ++ matrix_sdk_crypto/src/key_export.rs | 85 +++++++++++++++++++++++++++++ matrix_sdk_crypto/src/lib.rs | 2 + 3 files changed, 93 insertions(+) create mode 100644 matrix_sdk_crypto/src/key_export.rs diff --git a/matrix_sdk_crypto/Cargo.toml b/matrix_sdk_crypto/Cargo.toml index e1ec78b2..08b544b2 100644 --- a/matrix_sdk_crypto/Cargo.toml +++ b/matrix_sdk_crypto/Cargo.toml @@ -37,6 +37,12 @@ thiserror = "1.0.20" tracing = "0.1.19" atomic = "0.5.0" dashmap = "3.11.10" +sha2 = "0.9.1" +aes-ctr = "0.4.0" +pbkdf2 = { version = "0.5.0", default-features = false } +hmac = "0.9.0" +base64 = "0.12.3" +byteorder = "1.3.4" [dependencies.tracing-futures] version = "0.2.4" diff --git a/matrix_sdk_crypto/src/key_export.rs b/matrix_sdk_crypto/src/key_export.rs new file mode 100644 index 00000000..e8492be6 --- /dev/null +++ b/matrix_sdk_crypto/src/key_export.rs @@ -0,0 +1,85 @@ +// 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::io::{Cursor, Read, Seek, SeekFrom}; + +use base64::{decode_config, encode_config, DecodeError, STANDARD_NO_PAD}; +use byteorder::{BigEndian, ReadBytesExt}; + +use aes_ctr::{ + stream_cipher::{NewStreamCipher, SyncStreamCipher}, + Aes128Ctr, +}; +use hmac::{Hmac, Mac, NewMac}; +use pbkdf2::pbkdf2; +use sha2::{Sha256, Sha512}; + +const SALT_SIZE: usize = 16; +const IV_SIZE: usize = 16; +const MAC_SIZE: usize = 32; +const KEY_SIZE: usize = 16; + +pub fn decode(input: impl AsRef<[u8]>) -> Result, DecodeError> { + decode_config(input, STANDARD_NO_PAD) +} + +pub fn encode(input: impl AsRef<[u8]>) -> String { + encode_config(input, STANDARD_NO_PAD) +} + +pub fn decrypt(ciphertext: &str, passphrase: String) -> Result { + let decoded = decode(ciphertext)?; + + let mut decoded = Cursor::new(decoded); + + let mut salt = [0u8; SALT_SIZE]; + let mut iv = [0u8; IV_SIZE]; + let mut mac = [0u8; MAC_SIZE]; + let mut derived_keys = [0u8; KEY_SIZE * 2]; + + let version = decoded.read_u8().unwrap(); + + decoded.read_exact(&mut salt).unwrap(); + decoded.read_exact(&mut iv).unwrap(); + + let rounds = decoded.read_u32::().unwrap(); + let ciphertext_start = decoded.position() as usize; + + decoded.seek(SeekFrom::End(-32)).unwrap(); + let ciphertext_end = decoded.position() as usize; + + decoded.read_exact(&mut mac).unwrap(); + + let mut decoded = decoded.into_inner(); + let ciphertext = &decoded[0..ciphertext_end]; + + if version != 1u8 { + panic!("Unsupported version") + } + + pbkdf2::>(passphrase.as_bytes(), &salt, rounds, &mut derived_keys); + let (key, hmac_key) = derived_keys.split_at(KEY_SIZE / 2); + + let mut hmac = Hmac::::new_varkey(hmac_key).unwrap(); + hmac.update(ciphertext); + hmac.verify(&mac).expect("MAC DOESN'T MATCH"); + + let mut ciphertext = &mut decoded[ciphertext_start..ciphertext_end]; + + let mut aes = Aes128Ctr::new_var(&key, &iv).expect("Can't create AES"); + + aes.apply_keystream(&mut ciphertext); + + Ok(String::from_utf8(ciphertext.to_owned()).expect("Invalid utf-8")) +} diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index 38630f32..bf05600f 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -29,6 +29,8 @@ mod device; mod error; +#[allow(dead_code)] +mod key_export; mod machine; pub mod memory_stores; pub mod olm;