Skip to content

Commit

Permalink
Create a separate NewType, PinKey
Browse files Browse the repository at this point in the history
  • Loading branch information
Hinton committed Feb 6, 2024
1 parent 301c6f4 commit 747a772
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 43 deletions.
40 changes: 2 additions & 38 deletions crates/bitwarden-crypto/src/keys/master_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use base64::{engine::general_purpose::STANDARD, Engine};
use generic_array::GenericArray;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use sha2::Digest;

use super::utils::derive_kdf_key;
use crate::{
util::{self, hkdf_expand},
EncString, KeyDecryptable, Result, SymmetricCryptoKey, UserKey,
Expand Down Expand Up @@ -45,7 +45,7 @@ impl MasterKey {

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

/// Derive the master key hash, used for local and remote password validation.
Expand Down Expand Up @@ -99,42 +99,6 @@ fn make_user_key(
Ok((UserKey::new(user_key), protected))
}

/// Derive a generic key from a secret and salt using the provided KDF.
fn derive_key(secret: &[u8], salt: &[u8], kdf: &Kdf) -> Result<SymmetricCryptoKey> {
let mut hash = match kdf {
Kdf::PBKDF2 { iterations } => crate::util::pbkdf2(secret, salt, iterations.get()),

Kdf::Argon2id {
iterations,
memory,
parallelism,
} => {
use argon2::*;

let argon = Argon2::new(
Algorithm::Argon2id,
Version::V0x13,
Params::new(
memory.get() * 1024, // Convert MiB to KiB
iterations.get(),
parallelism.get(),
Some(32),
)
.unwrap(),
);

let salt_sha = sha2::Sha256::new().chain_update(salt).finalize();

let mut hash = [0u8; 32];
argon
.hash_password_into(secret, &salt_sha, &mut hash)
.unwrap();
hash
}
};
SymmetricCryptoKey::try_from(hash.as_mut_slice())
}

fn stretch_master_key(master_key: &MasterKey) -> Result<SymmetricCryptoKey> {
let key: Pin<Box<GenericArray<u8, U32>>> = hkdf_expand(&master_key.0.key, Some("enc"))?;
let mac_key: Pin<Box<GenericArray<u8, U32>>> = hkdf_expand(&master_key.0.key, Some("mac"))?;
Expand Down
4 changes: 3 additions & 1 deletion crates/bitwarden-crypto/src/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ mod asymmetric_crypto_key;
pub use asymmetric_crypto_key::{
AsymmetricCryptoKey, AsymmetricEncryptable, AsymmetricPublicCryptoKey,
};

mod user_key;
pub use user_key::UserKey;
mod device_key;
pub use device_key::{DeviceKey, TrustDeviceResponse};
mod pin_key;
pub use pin_key::PinKey;
mod utils;
34 changes: 34 additions & 0 deletions crates/bitwarden-crypto/src/keys/pin_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::{
keys::{key_encryptable::CryptoKey, utils::derive_kdf_key},
EncString, Kdf, KeyEncryptable, Result, SymmetricCryptoKey,
};

/// Pin Key.
///
/// Derived from a specific password, used for pin encryption and exports.
pub struct PinKey(SymmetricCryptoKey);

impl PinKey {
pub fn new(key: SymmetricCryptoKey) -> Self {
Self(key)
}

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

impl CryptoKey for PinKey {}

impl KeyEncryptable<PinKey, EncString> for &[u8] {
fn encrypt_with_key(self, key: &PinKey) -> Result<EncString> {
self.encrypt_with_key(&key.0)
}
}

impl KeyEncryptable<PinKey, EncString> for String {
fn encrypt_with_key(self, key: &PinKey) -> Result<EncString> {
self.as_bytes().encrypt_with_key(key)
}
}
39 changes: 39 additions & 0 deletions crates/bitwarden-crypto/src/keys/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use sha2::Digest;

use crate::{Kdf, Result, SymmetricCryptoKey};

/// Derive a generic key from a secret and salt using the provided KDF.
pub(super) fn derive_kdf_key(secret: &[u8], salt: &[u8], kdf: &Kdf) -> Result<SymmetricCryptoKey> {
let mut hash = match kdf {
Kdf::PBKDF2 { iterations } => crate::util::pbkdf2(secret, salt, iterations.get()),

Kdf::Argon2id {
iterations,
memory,
parallelism,
} => {
use argon2::*;

let argon = Argon2::new(
Algorithm::Argon2id,
Version::V0x13,
Params::new(
memory.get() * 1024, // Convert MiB to KiB
iterations.get(),
parallelism.get(),
Some(32),
)
.unwrap(),
);

let salt_sha = sha2::Sha256::new().chain_update(salt).finalize();

let mut hash = [0u8; 32];
argon
.hash_password_into(secret, &salt_sha, &mut hash)
.unwrap();
hash
}
};
SymmetricCryptoKey::try_from(hash.as_mut_slice())
}
8 changes: 4 additions & 4 deletions crates/bitwarden-exporters/src/encrypted_json.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use base64::{engine::general_purpose::STANDARD, Engine};
use bitwarden_crypto::{generate_random_bytes, Kdf, MasterKey};
use bitwarden_crypto::{generate_random_bytes, Kdf, KeyEncryptable, PinKey};
use serde::Serialize;
use thiserror::Error;
use uuid::Uuid;
Expand Down Expand Up @@ -45,7 +45,7 @@ pub(crate) fn export_encrypted_json(

let salt: [u8; 16] = generate_random_bytes();
let salt = STANDARD.encode(salt);
let key = MasterKey::derive(password.as_bytes(), salt.as_bytes(), &kdf)?;
let key = PinKey::derive(password.as_bytes(), salt.as_bytes(), &kdf)?;

let enc_key_validation = Uuid::new_v4().to_string();

Expand All @@ -57,8 +57,8 @@ pub(crate) fn export_encrypted_json(
kdf_iterations,
kdf_memory,
kdf_parallelism,
enc_key_validation: key.encrypt(enc_key_validation.as_bytes())?.to_string(),
data: key.encrypt(decrypted_export.as_bytes())?.to_string(),
enc_key_validation: enc_key_validation.encrypt_with_key(&key)?.to_string(),
data: decrypted_export.encrypt_with_key(&key)?.to_string(),
};

Ok(serde_json::to_string_pretty(&encrypted_export)?)
Expand Down

0 comments on commit 747a772

Please sign in to comment.