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.
This commit is contained in:
parent
0a6b0e5804
commit
2bcc0afb91
3 changed files with 48 additions and 31 deletions
|
@ -36,7 +36,7 @@ matrix-sdk-crypto = { version = "0.2.0", path = "../matrix_sdk_crypto", optional
|
|||
thiserror = "1.0.23"
|
||||
sled = "0.34.6"
|
||||
chacha20poly1305 = "0.7.1"
|
||||
getrandom = "0.2.1"
|
||||
rand = "0.8.2"
|
||||
zeroize = { version = "1.2.0", features = ["zeroize_derive"] }
|
||||
pbkdf2 = { version = "0.6.0", default-features = false }
|
||||
hmac = "0.10.1"
|
||||
|
|
|
@ -67,6 +67,7 @@ impl From<SerializationError> for StoreError {
|
|||
match e {
|
||||
SerializationError::Json(e) => StoreError::Json(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::Encryption(e) => StoreError::Encryption(e),
|
||||
},
|
||||
|
@ -153,8 +154,11 @@ impl SledStore {
|
|||
return Err(StoreError::UnencryptedStore);
|
||||
}
|
||||
} else {
|
||||
let key = StoreKey::new();
|
||||
let encrypted_key = DatabaseType::Encrypted(key.export(passphrase));
|
||||
let key = StoreKey::new().map_err::<StoreError, _>(|e| e.into())?;
|
||||
let encrypted_key = DatabaseType::Encrypted(
|
||||
key.export(passphrase)
|
||||
.map_err::<StoreError, _>(|e| e.into())?,
|
||||
);
|
||||
db.insert("store_key", serde_json::to_vec(&encrypted_key)?)?;
|
||||
key
|
||||
};
|
||||
|
|
|
@ -20,14 +20,16 @@ use chacha20poly1305::{
|
|||
aead::{Aead, Error as EncryptionError, NewAead},
|
||||
ChaCha20Poly1305, Key, Nonce, XChaCha20Poly1305, XNonce,
|
||||
};
|
||||
use getrandom::getrandom;
|
||||
use hmac::Hmac;
|
||||
use pbkdf2::pbkdf2;
|
||||
use rand::{thread_rng, Error as RngError, Fill};
|
||||
use sha2::Sha256;
|
||||
use zeroize::{Zeroize, Zeroizing};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::StoreError;
|
||||
|
||||
const VERSION: u8 = 1;
|
||||
const KEY_SIZE: usize = 32;
|
||||
const NONCE_SIZE: usize = 12;
|
||||
|
@ -44,6 +46,18 @@ pub enum Error {
|
|||
Serialization(#[from] serde_json::Error),
|
||||
#[error("Error encrypting or decrypting an event {0}")]
|
||||
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 {
|
||||
|
@ -100,15 +114,6 @@ pub struct StoreKey {
|
|||
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 {
|
||||
type Error = ();
|
||||
|
||||
|
@ -123,8 +128,12 @@ impl TryFrom<Vec<u8>> for StoreKey {
|
|||
|
||||
impl StoreKey {
|
||||
/// Generate a new random store key.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
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.
|
||||
|
@ -145,40 +154,44 @@ impl StoreKey {
|
|||
///
|
||||
/// * `passphrase` - The passphrase that should be used to encrypt the
|
||||
/// 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];
|
||||
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 = Key::from_slice(key.as_ref());
|
||||
let cipher = ChaCha20Poly1305::new(&key);
|
||||
|
||||
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
|
||||
.encrypt(Nonce::from_slice(nonce.as_ref()), self.inner.as_slice())
|
||||
.expect("Can't encrypt store key");
|
||||
let ciphertext =
|
||||
cipher.encrypt(Nonce::from_slice(nonce.as_ref()), self.inner.as_slice())?;
|
||||
|
||||
EncryptedStoreKey {
|
||||
Ok(EncryptedStoreKey {
|
||||
kdf_info: KdfInfo::Pbkdf2ToChaCha20Poly1305 {
|
||||
rounds: KDF_ROUNDS,
|
||||
kdf_salt: salt,
|
||||
},
|
||||
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];
|
||||
getrandom(&mut nonce).expect("Can't get random nonce");
|
||||
nonce
|
||||
let mut rng = thread_rng();
|
||||
|
||||
nonce.try_fill(&mut rng)?;
|
||||
|
||||
Ok(nonce)
|
||||
}
|
||||
|
||||
pub fn encrypt(&self, event: &impl Serialize) -> Result<EncryptedEvent, Error> {
|
||||
let event = serde_json::to_vec(event)?;
|
||||
|
||||
let nonce = StoreKey::get_nonce();
|
||||
let nonce = StoreKey::get_nonce()?;
|
||||
let cipher = XChaCha20Poly1305::new(self.key());
|
||||
let xnonce = XNonce::from_slice(&nonce);
|
||||
|
||||
|
@ -241,15 +254,15 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn generating() {
|
||||
StoreKey::new();
|
||||
StoreKey::new().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypting() {
|
||||
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();
|
||||
|
||||
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 decrypted: Value = store_key.decrypt(encrypted).unwrap();
|
||||
|
|
Loading…
Reference in a new issue