mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-06 23:15:08 -04:00
refactor(crypto): Remove the dead pickle key
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<u8>,
|
||||
/// The encrypted pickle key.
|
||||
ciphertext: Vec<u8>,
|
||||
},
|
||||
}
|
||||
|
||||
/// 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<u8>,
|
||||
}
|
||||
|
||||
/// 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<u8>,
|
||||
}
|
||||
|
||||
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<Vec<u8>> for PickleKey {
|
||||
type Error = ();
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
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<Vec<u8>> {
|
||||
let mut key = Zeroizing::from(vec![0u8; KEY_SIZE]);
|
||||
pbkdf2::<Hmac<Sha256>>(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<Self, DecryptionError> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user