base: Use a CSPRNG to get our randomness and handle randomness errors

Since we're going to encrypt a lot of small objects separately we're
gonna need a lot of random nonces, it doesn't help that our nonces are
24 bytes long either. So use a CSPRNG to random data faster, also don't
panic if there wasn't enough randomness.
master
Damir Jelić 2021-01-20 16:52:32 +01:00
parent 0a6b0e5804
commit 2bcc0afb91
3 changed files with 48 additions and 31 deletions

View File

@ -36,7 +36,7 @@ matrix-sdk-crypto = { version = "0.2.0", path = "../matrix_sdk_crypto", optional
thiserror = "1.0.23" thiserror = "1.0.23"
sled = "0.34.6" sled = "0.34.6"
chacha20poly1305 = "0.7.1" chacha20poly1305 = "0.7.1"
getrandom = "0.2.1" rand = "0.8.2"
zeroize = { version = "1.2.0", features = ["zeroize_derive"] } zeroize = { version = "1.2.0", features = ["zeroize_derive"] }
pbkdf2 = { version = "0.6.0", default-features = false } pbkdf2 = { version = "0.6.0", default-features = false }
hmac = "0.10.1" hmac = "0.10.1"

View File

@ -67,6 +67,7 @@ impl From<SerializationError> for StoreError {
match e { match e {
SerializationError::Json(e) => StoreError::Json(e), SerializationError::Json(e) => StoreError::Json(e),
SerializationError::Encryption(e) => match e { SerializationError::Encryption(e) => match e {
store_key::Error::Random(e) => StoreError::Encryption(e.to_string()),
store_key::Error::Serialization(e) => StoreError::Json(e), store_key::Error::Serialization(e) => StoreError::Json(e),
store_key::Error::Encryption(e) => StoreError::Encryption(e), store_key::Error::Encryption(e) => StoreError::Encryption(e),
}, },
@ -153,8 +154,11 @@ impl SledStore {
return Err(StoreError::UnencryptedStore); return Err(StoreError::UnencryptedStore);
} }
} else { } else {
let key = StoreKey::new(); let key = StoreKey::new().map_err::<StoreError, _>(|e| e.into())?;
let encrypted_key = DatabaseType::Encrypted(key.export(passphrase)); let encrypted_key = DatabaseType::Encrypted(
key.export(passphrase)
.map_err::<StoreError, _>(|e| e.into())?,
);
db.insert("store_key", serde_json::to_vec(&encrypted_key)?)?; db.insert("store_key", serde_json::to_vec(&encrypted_key)?)?;
key key
}; };

View File

@ -20,14 +20,16 @@ use chacha20poly1305::{
aead::{Aead, Error as EncryptionError, NewAead}, aead::{Aead, Error as EncryptionError, NewAead},
ChaCha20Poly1305, Key, Nonce, XChaCha20Poly1305, XNonce, ChaCha20Poly1305, Key, Nonce, XChaCha20Poly1305, XNonce,
}; };
use getrandom::getrandom;
use hmac::Hmac; use hmac::Hmac;
use pbkdf2::pbkdf2; use pbkdf2::pbkdf2;
use rand::{thread_rng, Error as RngError, Fill};
use sha2::Sha256; use sha2::Sha256;
use zeroize::{Zeroize, Zeroizing}; use zeroize::{Zeroize, Zeroizing};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::StoreError;
const VERSION: u8 = 1; const VERSION: u8 = 1;
const KEY_SIZE: usize = 32; const KEY_SIZE: usize = 32;
const NONCE_SIZE: usize = 12; const NONCE_SIZE: usize = 12;
@ -44,6 +46,18 @@ pub enum Error {
Serialization(#[from] serde_json::Error), Serialization(#[from] serde_json::Error),
#[error("Error encrypting or decrypting an event {0}")] #[error("Error encrypting or decrypting an event {0}")]
Encryption(String), Encryption(String),
#[error("Error generating enough random data for a cryptographic operation")]
Random(#[from] RngError),
}
impl Into<StoreError> for Error {
fn into(self) -> StoreError {
match self {
Error::Serialization(e) => StoreError::Json(e),
Error::Encryption(e) => StoreError::Encryption(e),
Error::Random(_) => StoreError::Encryption(self.to_string()),
}
}
} }
impl From<EncryptionError> for Error { impl From<EncryptionError> for Error {
@ -100,15 +114,6 @@ pub struct StoreKey {
inner: Vec<u8>, inner: Vec<u8>,
} }
impl Default for StoreKey {
fn default() -> Self {
let mut key = vec![0u8; KEY_SIZE];
getrandom(&mut key).expect("Can't generate new store key");
Self { inner: key }
}
}
impl TryFrom<Vec<u8>> for StoreKey { impl TryFrom<Vec<u8>> for StoreKey {
type Error = (); type Error = ();
@ -123,8 +128,12 @@ impl TryFrom<Vec<u8>> for StoreKey {
impl StoreKey { impl StoreKey {
/// Generate a new random store key. /// Generate a new random store key.
pub fn new() -> Self { pub fn new() -> Result<Self, Error> {
Default::default() let mut key = vec![0u8; KEY_SIZE];
let mut rng = thread_rng();
key.try_fill(&mut rng)?;
Ok(Self { inner: key })
} }
/// Expand the given passphrase into a KEY_SIZE long key. /// Expand the given passphrase into a KEY_SIZE long key.
@ -145,40 +154,44 @@ impl StoreKey {
/// ///
/// * `passphrase` - The passphrase that should be used to encrypt the /// * `passphrase` - The passphrase that should be used to encrypt the
/// store key. /// store key.
pub fn export(&self, passphrase: &str) -> EncryptedStoreKey { pub fn export(&self, passphrase: &str) -> Result<EncryptedStoreKey, Error> {
let mut rng = thread_rng();
let mut salt = vec![0u8; KDF_SALT_SIZE]; let mut salt = vec![0u8; KDF_SALT_SIZE];
getrandom(&mut salt).expect("Can't generate new random store key"); salt.try_fill(&mut rng)?;
let key = StoreKey::expand_key(passphrase, &salt, KDF_ROUNDS); let key = StoreKey::expand_key(passphrase, &salt, KDF_ROUNDS);
let key = Key::from_slice(key.as_ref()); let key = Key::from_slice(key.as_ref());
let cipher = ChaCha20Poly1305::new(&key); let cipher = ChaCha20Poly1305::new(&key);
let mut nonce = vec![0u8; NONCE_SIZE]; let mut nonce = vec![0u8; NONCE_SIZE];
getrandom(&mut nonce).expect("Can't generate new random nonce for the store key"); nonce.try_fill(&mut rng)?;
let ciphertext = cipher let ciphertext =
.encrypt(Nonce::from_slice(nonce.as_ref()), self.inner.as_slice()) cipher.encrypt(Nonce::from_slice(nonce.as_ref()), self.inner.as_slice())?;
.expect("Can't encrypt store key");
EncryptedStoreKey { Ok(EncryptedStoreKey {
kdf_info: KdfInfo::Pbkdf2ToChaCha20Poly1305 { kdf_info: KdfInfo::Pbkdf2ToChaCha20Poly1305 {
rounds: KDF_ROUNDS, rounds: KDF_ROUNDS,
kdf_salt: salt, kdf_salt: salt,
}, },
ciphertext_info: CipherTextInfo::ChaCha20Poly1305 { nonce, ciphertext }, ciphertext_info: CipherTextInfo::ChaCha20Poly1305 { nonce, ciphertext },
} })
} }
fn get_nonce() -> Vec<u8> { fn get_nonce() -> Result<Vec<u8>, RngError> {
let mut nonce = vec![0u8; XNONCE_SIZE]; let mut nonce = vec![0u8; XNONCE_SIZE];
getrandom(&mut nonce).expect("Can't get random nonce"); let mut rng = thread_rng();
nonce
nonce.try_fill(&mut rng)?;
Ok(nonce)
} }
pub fn encrypt(&self, event: &impl Serialize) -> Result<EncryptedEvent, Error> { pub fn encrypt(&self, event: &impl Serialize) -> Result<EncryptedEvent, Error> {
let event = serde_json::to_vec(event)?; let event = serde_json::to_vec(event)?;
let nonce = StoreKey::get_nonce(); let nonce = StoreKey::get_nonce()?;
let cipher = XChaCha20Poly1305::new(self.key()); let cipher = XChaCha20Poly1305::new(self.key());
let xnonce = XNonce::from_slice(&nonce); let xnonce = XNonce::from_slice(&nonce);
@ -241,15 +254,15 @@ mod test {
#[test] #[test]
fn generating() { fn generating() {
StoreKey::new(); StoreKey::new().unwrap();
} }
#[test] #[test]
fn encrypting() { fn encrypting() {
let passphrase = "it's a secret to everybody"; let passphrase = "it's a secret to everybody";
let store_key = StoreKey::new(); let store_key = StoreKey::new().unwrap();
let encrypted = store_key.export(passphrase); let encrypted = store_key.export(passphrase).unwrap();
let decrypted = StoreKey::import(passphrase, encrypted).unwrap(); let decrypted = StoreKey::import(passphrase, encrypted).unwrap();
assert_eq!(store_key, decrypted); assert_eq!(store_key, decrypted);
@ -270,7 +283,7 @@ mod test {
}, },
}); });
let store_key = StoreKey::new(); let store_key = StoreKey::new().unwrap();
let encrypted = store_key.encrypt(&event).unwrap(); let encrypted = store_key.encrypt(&event).unwrap();
let decrypted: Value = store_key.decrypt(encrypted).unwrap(); let decrypted: Value = store_key.decrypt(encrypted).unwrap();