diff --git a/crates/matrix-sdk-crypto/Cargo.toml b/crates/matrix-sdk-crypto/Cargo.toml index 152ccc9e3..5b1be9973 100644 --- a/crates/matrix-sdk-crypto/Cargo.toml +++ b/crates/matrix-sdk-crypto/Cargo.toml @@ -26,7 +26,6 @@ testing = ["http"] [dependencies] aes = "0.8.1" -aes-gcm = "0.9.4" atomic = "0.5.1" async-trait = "0.1.53" base64 = "0.13.0" diff --git a/crates/matrix-sdk-crypto/src/store/mod.rs b/crates/matrix-sdk-crypto/src/store/mod.rs index e83621d6e..890202f70 100644 --- a/crates/matrix-sdk-crypto/src/store/mod.rs +++ b/crates/matrix-sdk-crypto/src/store/mod.rs @@ -39,7 +39,6 @@ pub mod caches; mod memorystore; -mod pickle_key; #[cfg(any(test, feature = "testing"))] #[macro_use] @@ -57,7 +56,6 @@ use std::{ use async_trait::async_trait; use matrix_sdk_common::{locks::Mutex, AsyncTraitDeps}; pub use memorystore::MemoryStore; -pub use pickle_key::{EncryptedPickleKey, PickleKey}; use ruma::{ events::secret::request::SecretName, DeviceId, IdParseError, OwnedDeviceId, OwnedUserId, RoomId, TransactionId, UserId, diff --git a/crates/matrix-sdk-crypto/src/store/pickle_key.rs b/crates/matrix-sdk-crypto/src/store/pickle_key.rs deleted file mode 100644 index 35be2afdd..000000000 --- a/crates/matrix-sdk-crypto/src/store/pickle_key.rs +++ /dev/null @@ -1,198 +0,0 @@ -// 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::convert::TryFrom; - -use aes_gcm::{ - aead::{generic_array::GenericArray, Aead, NewAead}, - Aes256Gcm, Error as DecryptionError, -}; -use hmac::Hmac; -use pbkdf2::pbkdf2; -use rand::{thread_rng, RngCore}; -use serde::{Deserialize, Serialize}; -use sha2::Sha256; -use zeroize::{Zeroize, Zeroizing}; - -const KEY_SIZE: usize = 32; -const NONCE_SIZE: usize = 12; -const KDF_SALT_SIZE: usize = 32; -#[cfg(not(test))] -const KDF_ROUNDS: u32 = 200_000; -#[cfg(test)] -const KDF_ROUNDS: u32 = 1000; - -/// Version specific info for the key derivation method that is used. -#[derive(Debug, Serialize, Deserialize, PartialEq)] -pub enum KdfInfo { - Pbkdf2 { - /// The number of PBKDF rounds that were used when deriving the AES key. - rounds: u32, - }, -} - -/// Version specific info for encryption method that is used to encrypt our -/// pickle key. -#[derive(Debug, Serialize, Deserialize, PartialEq)] -pub enum CipherTextInfo { - Aes256Gcm { - /// The nonce that was used to encrypt the ciphertext. - nonce: Vec, - /// The encrypted pickle key. - ciphertext: Vec, - }, -} - -/// An encrypted version of our pickle key, this can be safely stored in a -/// database. -#[derive(Debug, Serialize, Deserialize, PartialEq)] -pub struct EncryptedPickleKey { - /// Info about the key derivation method that was used to expand the - /// passphrase into an encryption key. - pub kdf_info: KdfInfo, - /// The ciphertext with it's accompanying additional data that is needed to - /// decrypt the pickle key. - pub ciphertext_info: CipherTextInfo, - /// The salt that was used when the passphrase was expanded into a AES key. - kdf_salt: Vec, -} - -/// A pickle key that will be used to encrypt all the private keys for Olm. -/// -/// Olm uses AES256 to encrypt accounts, sessions, inbound group sessions. We -/// also implement our own pickling for the cross-signing types using -/// AES256-GCM so the key sizes match. -#[derive(Debug, Zeroize, PartialEq)] -pub struct PickleKey { - aes256_key: Vec, -} - -impl Default for PickleKey { - fn default() -> Self { - let mut key = vec![0u8; KEY_SIZE]; - let mut rng = thread_rng(); - rng.fill_bytes(&mut key); - - Self { aes256_key: key } - } -} - -impl TryFrom> for PickleKey { - type Error = (); - fn try_from(value: Vec) -> Result { - if value.len() != KEY_SIZE { - Err(()) - } else { - Ok(Self { aes256_key: value }) - } - } -} - -impl PickleKey { - /// Generate a new random pickle key. - pub fn new() -> Self { - Default::default() - } - - fn expand_key(passphrase: &str, salt: &[u8], rounds: u32) -> Zeroizing> { - let mut key = Zeroizing::from(vec![0u8; KEY_SIZE]); - pbkdf2::>(passphrase.as_bytes(), salt, rounds, &mut *key); - key - } - - /// Get the raw AES256 key. - pub fn key(&self) -> &[u8] { - &self.aes256_key - } - - /// Encrypt and export our pickle key using the given passphrase. - /// - /// # Arguments - /// - /// * `passphrase` - The passphrase that should be used to encrypt the - /// pickle key. - pub fn encrypt(&self, passphrase: &str) -> EncryptedPickleKey { - let mut rng = thread_rng(); - let mut salt = vec![0u8; KDF_SALT_SIZE]; - - rng.fill_bytes(&mut salt); - - let key = PickleKey::expand_key(passphrase, &salt, KDF_ROUNDS); - let key = GenericArray::from_slice(key.as_ref()); - let cipher = Aes256Gcm::new(key); - - let mut nonce = vec![0u8; NONCE_SIZE]; - rng.fill_bytes(&mut nonce); - - let ciphertext = cipher - .encrypt(GenericArray::from_slice(nonce.as_ref()), self.aes256_key.as_slice()) - .expect("Can't encrypt pickle key"); - - EncryptedPickleKey { - kdf_info: KdfInfo::Pbkdf2 { rounds: KDF_ROUNDS }, - kdf_salt: salt, - ciphertext_info: CipherTextInfo::Aes256Gcm { nonce, ciphertext }, - } - } - - /// Restore a pickle key from an encrypted export. - /// - /// # Arguments - /// - /// * `passphrase` - The passphrase that should be used to encrypt the - /// pickle key. - /// - /// * `encrypted` - The exported and encrypted version of the pickle key. - pub fn from_encrypted( - passphrase: &str, - encrypted: EncryptedPickleKey, - ) -> Result { - let key = match encrypted.kdf_info { - KdfInfo::Pbkdf2 { rounds } => Self::expand_key(passphrase, &encrypted.kdf_salt, rounds), - }; - - let key = GenericArray::from_slice(key.as_ref()); - - let decrypted = match encrypted.ciphertext_info { - CipherTextInfo::Aes256Gcm { nonce, ciphertext } => { - let cipher = Aes256Gcm::new(key); - let nonce = GenericArray::from_slice(&nonce); - cipher.decrypt(nonce, ciphertext.as_ref())? - } - }; - - Ok(Self { aes256_key: decrypted }) - } -} - -#[cfg(test)] -mod tests { - use super::PickleKey; - - #[test] - fn generating() { - PickleKey::new(); - } - - #[test] - fn encrypting() { - let passphrase = "it's a secret to everybody"; - let pickle_key = PickleKey::new(); - - let encrypted = pickle_key.encrypt(passphrase); - let decrypted = PickleKey::from_encrypted(passphrase, encrypted).unwrap(); - - assert_eq!(pickle_key, decrypted); - } -}