Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Update crypto deps to prerelease versions #731

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
566 changes: 281 additions & 285 deletions Cargo.lock

Large diffs are not rendered by default.

24 changes: 14 additions & 10 deletions crates/bitwarden-crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,30 @@ default = []
mobile = ["dep:uniffi"] # Mobile-specific features

[dependencies]
aes = { version = ">=0.8.2, <0.9", features = ["zeroize"] }
argon2 = { version = ">=0.5.0, <0.6", features = [
aes = { version = "0.9.0-pre", features = ["zeroize"] }
argon2 = { version = "0.6.0-pre.0", features = [
"std",
"zeroize",
], default-features = false }
base64 = ">=0.21.2, <0.22"
cbc = { version = ">=0.1.2, <0.2", features = ["alloc", "zeroize"] }
generic-array = { version = ">=0.14.7, <1.0", features = ["zeroize"] }
hkdf = ">=0.12.3, <0.13"
hmac = ">=0.12.1, <0.13"
blake2 = { version = "0.11.0-pre.3", features = ["zeroize"] }
cbc = { version = "0.2.0-pre", features = [
"alloc",
"zeroize",
], git = "https://github.com/RustCrypto/block-modes", rev = "532a46166bcc74bf718ca351cc3b5a86a2fcb2a3" }
hkdf = "0.13.0-pre.3"
hmac = "0.13.0-pre.3"
hybrid-array = { version = "0.2.0-rc.8", features = ["zeroize"] }
num-bigint = ">=0.4, <0.5"
num-traits = ">=0.2.15, <0.3"
pbkdf2 = { version = ">=0.12.1, <0.13", default-features = false }
pbkdf2 = { version = "0.13.0-pre.0", default-features = false }
rand = ">=0.8.5, <0.9"
rayon = ">=1.8.1, <2.0"
rsa = ">=0.9.2, <0.10"
rsa = "0.10.0-pre.1"
schemars = { version = ">=0.8, <0.9", features = ["uuid1"] }
serde = { version = ">=1.0, <2.0", features = ["derive"] }
sha1 = ">=0.10.5, <0.11"
sha2 = ">=0.10.6, <0.11"
sha1 = { version = "0.11.0-pre.3", features = ["zeroize"] }
sha2 = { version = "0.11.0-pre.3", features = ["zeroize"] }
subtle = ">=2.5.0, <3.0"
thiserror = ">=1.0.40, <2.0"
uniffi = { version = "=0.26.1", optional = true }
Expand Down
62 changes: 26 additions & 36 deletions crates/bitwarden-crypto/src/aes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@
//! In most cases you should use the [EncString][crate::EncString] with
//! [KeyEncryptable][crate::KeyEncryptable] & [KeyDecryptable][crate::KeyDecryptable] instead.

use aes::cipher::{
block_padding::Pkcs7,
typenum::{U16, U32},
BlockDecryptMut, BlockEncryptMut, KeyIvInit,
};
use generic_array::GenericArray;
use hmac::Mac;
use aes::cipher::{BlockModeDecrypt, BlockModeEncrypt, KeyIvInit};
use cbc::cipher::block_padding::Pkcs7;
use hmac::{KeyInit, Mac};
use subtle::ConstantTimeEq;

use crate::{
Expand All @@ -22,16 +18,11 @@ use crate::{
/// Decrypt using AES-256 in CBC mode.
///
/// Behaves similar to [decrypt_aes256_hmac], but does not validate the MAC.
pub(crate) fn decrypt_aes256(
iv: &[u8; 16],
data: Vec<u8>,
key: &GenericArray<u8, U32>,
) -> Result<Vec<u8>> {
pub(crate) fn decrypt_aes256(iv: &[u8; 16], data: Vec<u8>, key: &[u8; 32]) -> Result<Vec<u8>> {
// Decrypt data
let iv = GenericArray::from_slice(iv);
let mut data = data;
let decrypted_key_slice = cbc::Decryptor::<aes::Aes256>::new(key, iv)
.decrypt_padded_mut::<Pkcs7>(&mut data)
let decrypted_key_slice = cbc::Decryptor::<aes::Aes256>::new(key.as_ref(), iv.as_ref())
.decrypt_padded::<Pkcs7>(&mut data)
.map_err(|_| CryptoError::KeyDecrypt)?;

// Data is decrypted in place and returns a subslice of the original Vec, to avoid cloning it,
Expand All @@ -49,8 +40,8 @@ pub(crate) fn decrypt_aes256_hmac(
iv: &[u8; 16],
mac: &[u8; 32],
data: Vec<u8>,
mac_key: &GenericArray<u8, U32>,
key: &GenericArray<u8, U32>,
mac_key: &[u8; 32],
key: &[u8; 32],
) -> Result<Vec<u8>> {
let res = generate_mac(mac_key, iv, &data)?;
if res.ct_ne(mac).into() {
Expand All @@ -67,7 +58,7 @@ pub(crate) fn decrypt_aes256_hmac(
///
/// A AesCbc256_B64 EncString
#[allow(unused)]
pub(crate) fn encrypt_aes256(data_dec: &[u8], key: &GenericArray<u8, U32>) -> ([u8; 16], Vec<u8>) {
pub(crate) fn encrypt_aes256(data_dec: &[u8], key: &[u8; 32]) -> ([u8; 16], Vec<u8>) {
let rng = rand::thread_rng();
let (iv, data) = encrypt_aes256_internal(rng, data_dec, key);

Expand All @@ -83,8 +74,8 @@ pub(crate) fn encrypt_aes256(data_dec: &[u8], key: &GenericArray<u8, U32>) -> ([
/// A AesCbc256_HmacSha256_B64 EncString
pub(crate) fn encrypt_aes256_hmac(
data_dec: &[u8],
mac_key: &GenericArray<u8, U32>,
key: &GenericArray<u8, U32>,
mac_key: &[u8; 32],
key: &[u8; 32],
) -> Result<([u8; 16], [u8; 32], Vec<u8>)> {
let rng = rand::thread_rng();
let (iv, data) = encrypt_aes256_internal(rng, data_dec, key);
Expand All @@ -101,25 +92,24 @@ pub(crate) fn encrypt_aes256_hmac(
fn encrypt_aes256_internal(
mut rng: impl rand::RngCore,
data_dec: &[u8],
key: &GenericArray<u8, U32>,
key: &[u8; 32],
) -> ([u8; 16], Vec<u8>) {
let mut iv = [0u8; 16];
rng.fill_bytes(&mut iv);
let data = cbc::Encryptor::<aes::Aes256>::new(key, &iv.into())
.encrypt_padded_vec_mut::<Pkcs7>(data_dec);
let data = cbc::Encryptor::<aes::Aes256>::new(key.as_ref(), &iv.into())
.encrypt_padded_vec::<Pkcs7>(data_dec);

(iv, data)
}

/// Decrypt using AES-128 in CBC mode.
///
/// Behaves similar to [decrypt_aes128_hmac], but does not validate the MAC.
fn decrypt_aes128(iv: &[u8; 16], data: Vec<u8>, key: &GenericArray<u8, U16>) -> Result<Vec<u8>> {
fn decrypt_aes128(iv: &[u8; 16], data: Vec<u8>, key: &[u8; 16]) -> Result<Vec<u8>> {
// Decrypt data
let iv = GenericArray::from_slice(iv);
let mut data = data;
let decrypted_key_slice = cbc::Decryptor::<aes::Aes128>::new(key, iv)
.decrypt_padded_mut::<Pkcs7>(&mut data)
let decrypted_key_slice = cbc::Decryptor::<aes::Aes128>::new(key.as_ref(), iv.as_ref())
.decrypt_padded::<Pkcs7>(&mut data)
.map_err(|_| CryptoError::KeyDecrypt)?;

// Data is decrypted in place and returns a subslice of the original Vec, to avoid cloning it,
Expand All @@ -137,8 +127,8 @@ pub fn decrypt_aes128_hmac(
iv: &[u8; 16],
mac: &[u8; 32],
data: Vec<u8>,
mac_key: &GenericArray<u8, U16>,
key: &GenericArray<u8, U16>,
mac_key: &[u8; 16],
key: &[u8; 16],
) -> Result<Vec<u8>> {
let res = generate_mac(mac_key, iv, &data)?;
if res.ct_ne(mac).into() {
Expand All @@ -163,18 +153,18 @@ fn generate_mac(mac_key: &[u8], iv: &[u8], data: &[u8]) -> Result<[u8; 32]> {
#[cfg(test)]
mod tests {
use base64::{engine::general_purpose::STANDARD, Engine};
use generic_array::{sequence::GenericSequence, ArrayLength};
use rand::SeedableRng;

use super::*;

/// Helper function for generating a `GenericArray` of size 32 with each element being
/// Helper function for generating a `Array` of size 32 with each element being
/// a multiple of a given increment, starting from a given offset.
fn generate_generic_array<N: ArrayLength<u8>>(
offset: u8,
increment: u8,
) -> GenericArray<u8, N> {
GenericArray::generate(|i| offset + i as u8 * increment)
fn generate_generic_array<const N: usize>(offset: u8, increment: u8) -> [u8; N] {
let mut arr = [0u8; N];
for (i, item) in arr.iter_mut().enumerate() {
*item = offset + i as u8 * increment;
}
arr
}

/// Helper function for generating a vector of a given size with each element being
Expand Down
10 changes: 4 additions & 6 deletions crates/bitwarden-crypto/src/enc_string/symmetric.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use std::{fmt::Display, str::FromStr};

use aes::cipher::typenum::U32;
use base64::{engine::general_purpose::STANDARD, Engine};
use generic_array::GenericArray;
use serde::Deserialize;

use super::{check_length, from_b64, from_b64_vec, split_enc_string};
Expand Down Expand Up @@ -205,8 +203,8 @@ impl serde::Serialize for EncString {
impl EncString {
pub fn encrypt_aes256_hmac(
data_dec: &[u8],
mac_key: &GenericArray<u8, U32>,
key: &GenericArray<u8, U32>,
mac_key: &[u8; 32],
key: &[u8; 32],
) -> Result<EncString> {
let (iv, mac, data) = crate::aes::encrypt_aes256_hmac(data_dec, mac_key, key)?;
Ok(EncString::AesCbc256_HmacSha256_B64 { iv, mac, data })
Expand Down Expand Up @@ -245,8 +243,8 @@ impl KeyDecryptable<SymmetricCryptoKey, Vec<u8>> for EncString {
// variant uses a 16 byte key This means the key+mac are going to be
// parsed as a single 32 byte key, at the moment we split it manually
// When refactoring the key handling, this should be fixed.
let enc_key = key.key[0..16].into();
let mac_key = key.key[16..32].into();
let enc_key: &[u8; 16] = (&key.key[0..16]).try_into().expect("Valid size");
let mac_key: &[u8; 16] = (&key.key[16..32]).try_into().expect("Valid size");
let dec = crate::aes::decrypt_aes128_hmac(iv, mac, data.clone(), mac_key, enc_key)?;
Ok(dec)
}
Expand Down
32 changes: 16 additions & 16 deletions crates/bitwarden-crypto/src/keys/master_key.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use std::num::NonZeroU32;

use base64::{engine::general_purpose::STANDARD, Engine};
use base64::engine::general_purpose::STANDARD;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use super::utils::{derive_kdf_key, stretch_kdf_key};
use crate::{
util, CryptoError, EncString, KeyDecryptable, Result, SensitiveVec, SymmetricCryptoKey, UserKey,
util, CryptoError, EncString, KeyDecryptable, Result, SensitiveString, SensitiveVec,
SymmetricCryptoKey, UserKey,
};

/// Key Derivation Function for Bitwarden Account
Expand Down Expand Up @@ -63,6 +64,7 @@ pub enum HashPurpose {
/// Master Key.
///
/// Derived from the users master password, used to protect the [UserKey].
#[derive(Debug)]
pub struct MasterKey(SymmetricCryptoKey);

impl MasterKey {
Expand All @@ -72,18 +74,17 @@ impl MasterKey {

/// Derives a users master key from their password, email and KDF.
pub fn derive(password: &SensitiveVec, email: &[u8], kdf: &Kdf) -> Result<Self> {
derive_kdf_key(password.expose(), email, kdf).map(Self)
derive_kdf_key(password, email, kdf).map(Self)
}

/// Derive the master key hash, used for local and remote password validation.
pub fn derive_master_key_hash(
&self,
password: &SensitiveVec,
purpose: HashPurpose,
) -> Result<String> {
let hash = util::pbkdf2(&self.0.key, password.expose(), purpose as u32);

Ok(STANDARD.encode(hash))
) -> Result<SensitiveString> {
let hash = util::pbkdf2(self.0.key.as_slice(), password.expose(), purpose as u32);
Ok(hash.encode_base64(STANDARD))
}

/// Generate a new random user key and encrypt it with the master key.
Expand Down Expand Up @@ -192,7 +193,8 @@ mod tests {
"ZF6HjxUTSyBHsC+HXSOhZoXN+UuMnygV5YkWXCY4VmM=",
master_key
.derive_master_key_hash(&password, HashPurpose::ServerAuthorization)
.unwrap(),
.unwrap()
.expose(),
);
}

Expand All @@ -212,7 +214,8 @@ mod tests {
"PR6UjYmjmppTYcdyTiNbAhPJuQQOmynKbdEl1oyi/iQ=",
master_key
.derive_master_key_hash(&password, HashPurpose::ServerAuthorization)
.unwrap(),
.unwrap()
.expose(),
);
}

Expand All @@ -221,13 +224,10 @@ mod tests {
let mut rng = rand_chacha::ChaCha8Rng::from_seed([0u8; 32]);

let master_key = MasterKey(SymmetricCryptoKey::new(
Box::pin(
[
31, 79, 104, 226, 150, 71, 177, 90, 194, 80, 172, 209, 17, 129, 132, 81, 138,
167, 69, 167, 254, 149, 2, 27, 39, 197, 64, 42, 22, 195, 86, 75,
]
.into(),
),
Box::pin([
31, 79, 104, 226, 150, 71, 177, 90, 194, 80, 172, 209, 17, 129, 132, 81, 138, 167,
69, 167, 254, 149, 2, 27, 39, 197, 64, 42, 22, 195, 86, 75,
]),
None,
));

Expand Down
4 changes: 2 additions & 2 deletions crates/bitwarden-crypto/src/keys/pin_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
key_encryptable::CryptoKey,
utils::{derive_kdf_key, stretch_kdf_key},
},
EncString, Kdf, KeyEncryptable, Result, SymmetricCryptoKey,
EncString, Kdf, KeyEncryptable, Result, SensitiveVec, SymmetricCryptoKey,
};

/// Pin Key.
Expand All @@ -17,7 +17,7 @@ impl PinKey {
}

/// Derives a users pin key from their password, email and KDF.
pub fn derive(password: &[u8], salt: &[u8], kdf: &Kdf) -> Result<Self> {
pub fn derive(password: &SensitiveVec, salt: &[u8], kdf: &Kdf) -> Result<Self> {
derive_kdf_key(password, salt, kdf).map(Self)
}
}
Expand Down
7 changes: 2 additions & 5 deletions crates/bitwarden-crypto/src/keys/shareable_key.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use std::pin::Pin;

use aes::cipher::typenum::U64;
use generic_array::GenericArray;
use hmac::Mac;
use hmac::{KeyInit, Mac};
use zeroize::Zeroize;

use crate::{
Expand All @@ -27,8 +25,7 @@ pub fn derive_shareable_key(
.finalize()
.into_bytes();

let mut key: Pin<Box<GenericArray<u8, U64>>> =
hkdf_expand(&res, info).expect("Input is a valid size");
let mut key: Pin<Box<[u8; 64]>> = hkdf_expand(&res, info).expect("Input is a valid size");

// Zeroize the temporary buffer
res.zeroize();
Expand Down
Loading
Loading