Skip to content

Commit

Permalink
Merge branch 'main' into ps/exporters
Browse files Browse the repository at this point in the history
  • Loading branch information
Hinton authored Feb 2, 2024
2 parents 27cd78d + 32be569 commit bd2fe96
Show file tree
Hide file tree
Showing 23 changed files with 740 additions and 55 deletions.
33 changes: 33 additions & 0 deletions crates/bitwarden-crypto/src/enc_string/asymmetric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ impl schemars::JsonSchema for AsymmetricEncString {

#[cfg(test)]
mod tests {
use schemars::schema_for;

use super::{AsymmetricCryptoKey, AsymmetricEncString, KeyDecryptable};

const RSA_PRIVATE_KEY: &str = "-----BEGIN PRIVATE KEY-----
Expand Down Expand Up @@ -288,4 +290,35 @@ XKZBokBGnjFnTnKcs7nv/O8=
assert_eq!(t.key.to_string(), cipher);
assert_eq!(serde_json::to_string(&t).unwrap(), serialized);
}

#[test]
fn test_from_str_invalid() {
let enc_str = "7.ABC";
let enc_string: Result<AsymmetricEncString, _> = enc_str.parse();

let err = enc_string.unwrap_err();
assert_eq!(
err.to_string(),
"EncString error, Invalid asymmetric type, got type 7 with 1 parts"
);
}

#[test]
fn test_debug_format() {
let enc_str: &str = "4.ZheRb3PCfAunyFdQYPfyrFqpuvmln9H9w5nDjt88i5A7ug1XE0LJdQHCIYJl0YOZ1gCOGkhFu/CRY2StiLmT3iRKrrVBbC1+qRMjNNyDvRcFi91LWsmRXhONVSPjywzrJJXglsztDqGkLO93dKXNhuKpcmtBLsvgkphk/aFvxbaOvJ/FHdK/iV0dMGNhc/9tbys8laTdwBlI5xIChpRcrfH+XpSFM88+Bu03uK67N9G6eU1UmET+pISJwJvMuIDMqH+qkT7OOzgL3t6I0H2LDj+CnsumnQmDsvQzDiNfTR0IgjpoE9YH2LvPXVP2wVUkiTwXD9cG/E7XeoiduHyHjw==";
let enc_string: AsymmetricEncString = enc_str.parse().unwrap();

let debug_string = format!("{:?}", enc_string);
assert_eq!(debug_string, "AsymmetricEncString");
}

#[test]
fn test_json_schema() {
let schema = schema_for!(AsymmetricEncString);

assert_eq!(
serde_json::to_string(&schema).unwrap(),
r#"{"$schema":"http://json-schema.org/draft-07/schema#","title":"AsymmetricEncString","type":"string"}"#
);
}
}
53 changes: 53 additions & 0 deletions crates/bitwarden-crypto/src/enc_string/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,56 @@ where
T::from_str(v).map_err(|e| E::custom(format!("{:?}", e)))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_check_length_less_than_expected() {
let buf = [1, 2, 3];
let expected = 5;
let result = check_length(&buf, expected);
assert!(result.is_err());
}

#[test]
fn test_check_length_equal_to_expected() {
let buf = [1, 2, 3, 4, 5];
let expected = 5;
let result = check_length(&buf, expected);
assert!(result.is_ok());
}

#[test]
fn test_check_length_greater_than_expected() {
let buf = [1, 2, 3, 4, 5, 6];
let expected = 5;
let result = check_length(&buf, expected);
assert!(result.is_ok());
}

#[test]
fn test_split_enc_string_new_format() {
let s = "2.abc|def|ghi";
let (header, parts) = split_enc_string(s);
assert_eq!(header, "2");
assert_eq!(parts, vec!["abc", "def", "ghi"]);
}

#[test]
fn test_split_enc_string_old_format_three_parts() {
let s = "abc|def|ghi";
let (header, parts) = split_enc_string(s);
assert_eq!(header, "1");
assert_eq!(parts, vec!["abc", "def", "ghi"]);
}

#[test]
fn test_split_enc_string_old_format_fewer_parts() {
let s = "abc|def";
let (header, parts) = split_enc_string(s);
assert_eq!(header, "0");
assert_eq!(parts, vec!["abc", "def"]);
}
}
76 changes: 76 additions & 0 deletions crates/bitwarden-crypto/src/enc_string/symmetric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ impl schemars::JsonSchema for EncString {

#[cfg(test)]
mod tests {
use schemars::schema_for;

use super::EncString;
use crate::{derive_symmetric_key, KeyDecryptable, KeyEncryptable};

Expand Down Expand Up @@ -325,4 +327,78 @@ mod tests {

assert_eq!(enc_string_new.to_string(), enc_str)
}

#[test]
fn test_from_str_cbc256() {
let enc_str = "0.pMS6/icTQABtulw52pq2lg==|XXbxKxDTh+mWiN1HjH2N1w==";
let enc_string: EncString = enc_str.parse().unwrap();

assert_eq!(enc_string.enc_type(), 0);
if let EncString::AesCbc256_B64 { iv, data } = &enc_string {
assert_eq!(
iv,
&[164, 196, 186, 254, 39, 19, 64, 0, 109, 186, 92, 57, 218, 154, 182, 150]
);
assert_eq!(
data,
&[93, 118, 241, 43, 16, 211, 135, 233, 150, 136, 221, 71, 140, 125, 141, 215]
);
}
}

#[test]
fn test_from_str_cbc128_hmac() {
let enc_str = "1.Hh8gISIjJCUmJygpKissLQ==|MjM0NTY3ODk6Ozw9Pj9AQUJDREU=|KCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkc=";
let enc_string: EncString = enc_str.parse().unwrap();

assert_eq!(enc_string.enc_type(), 1);
if let EncString::AesCbc128_HmacSha256_B64 { iv, mac, data } = &enc_string {
assert_eq!(
iv,
&[30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45]
);
assert_eq!(
mac,
&[
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71
]
);
assert_eq!(
data,
&[50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69]
);
}
}

#[test]
fn test_from_str_invalid() {
let enc_str = "7.ABC";
let enc_string: Result<EncString, _> = enc_str.parse();

let err = enc_string.unwrap_err();
assert_eq!(
err.to_string(),
"EncString error, Invalid symmetric type, got type 7 with 1 parts"
);
}

#[test]
fn test_debug_format() {
let enc_str = "2.pMS6/icTQABtulw52pq2lg==|XXbxKxDTh+mWiN1HjH2N1w==|Q6PkuT+KX/axrgN9ubD5Ajk2YNwxQkgs3WJM0S0wtG8=";
let enc_string: EncString = enc_str.parse().unwrap();

let debug_string = format!("{:?}", enc_string);
assert_eq!(debug_string, "EncString");
}

#[test]
fn test_json_schema() {
let schema = schema_for!(EncString);

assert_eq!(
serde_json::to_string(&schema).unwrap(),
r#"{"$schema":"http://json-schema.org/draft-07/schema#","title":"EncString","type":"string"}"#
);
}
}
4 changes: 4 additions & 0 deletions crates/bitwarden-crypto/src/keys/master_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ pub enum HashPurpose {
pub struct MasterKey(SymmetricCryptoKey);

impl MasterKey {
pub fn new(key: SymmetricCryptoKey) -> MasterKey {
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_key(password, email, kdf).map(Self)
Expand Down
23 changes: 21 additions & 2 deletions crates/bitwarden-uniffi/src/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,27 @@ impl ClientAuth {
.write()
.await
.auth()
.validate_password(password, password_hash.to_string())
.await?)
.validate_password(password, password_hash.to_string())?)
}

/// Validate the user password without knowing the password hash
///
/// Used for accounts that we know have master passwords but that have not logged in with a
/// password. Some example are login with device or TDE.
///
/// This works by comparing the provided password against the encrypted user key.
pub async fn validate_password_user_key(
&self,
password: String,
encrypted_user_key: String,
) -> Result<String> {
Ok(self
.0
.0
.write()
.await
.auth()
.validate_password_user_key(password, encrypted_user_key)?)
}

/// Initialize a new auth request
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use log::debug;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::{
auth::api::response::IdentityTokenResponse,
client::{client_settings::DeviceType, ApiConfigurations},
error::Result,
};

#[derive(Serialize, Deserialize, Debug)]
pub struct AuthRequestTokenRequest {
scope: String,
client_id: String,
#[serde(rename = "deviceType")]
device_type: u8,
#[serde(rename = "deviceIdentifier")]
device_identifier: String,
#[serde(rename = "deviceName")]
device_name: String,
grant_type: String,
#[serde(rename = "username")]
email: String,
#[serde(rename = "authRequest")]
auth_request_id: Uuid,
#[serde(rename = "password")]
access_code: String,
}

impl AuthRequestTokenRequest {
pub fn new(
email: &str,
auth_request_id: &Uuid,
access_code: &str,
device_type: DeviceType,
device_identifier: &str,
) -> Self {
let obj = Self {
scope: "api offline_access".to_string(),
client_id: "web".to_string(),
device_type: device_type as u8,
device_identifier: device_identifier.to_string(),
device_name: "chrome".to_string(),
grant_type: "password".to_string(),
email: email.to_string(),
auth_request_id: *auth_request_id,
access_code: access_code.to_string(),
};
debug!("initializing {:?}", obj);
obj
}

pub(crate) async fn send(
&self,
configurations: &ApiConfigurations,
) -> Result<IdentityTokenResponse> {
super::send_identity_connect_request(configurations, Some(&self.email), &self).await
}
}
5 changes: 5 additions & 0 deletions crates/bitwarden/src/auth/api/request/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ pub(crate) use password_token_request::*;
#[cfg(feature = "internal")]
pub(crate) use renew_token_request::*;

#[cfg(feature = "mobile")]
mod auth_request_token_request;
#[cfg(feature = "mobile")]
pub(crate) use auth_request_token_request::*;

use crate::{
auth::api::response::{parse_identity_response, IdentityTokenResponse},
client::ApiConfigurations,
Expand Down
14 changes: 10 additions & 4 deletions crates/bitwarden/src/auth/api/request/password_token_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
api::response::IdentityTokenResponse,
login::{TwoFactorProvider, TwoFactorRequest},
},
client::ApiConfigurations,
client::{client_settings::DeviceType, ApiConfigurations},
error::Result,
};

Expand Down Expand Up @@ -35,13 +35,19 @@ pub struct PasswordTokenRequest {
}

impl PasswordTokenRequest {
pub fn new(email: &str, password_hash: &String, two_factor: &Option<TwoFactorRequest>) -> Self {
pub fn new(
email: &str,
password_hash: &str,
device_type: DeviceType,
device_identifier: &str,
two_factor: &Option<TwoFactorRequest>,
) -> Self {
let tf = two_factor.as_ref();
let obj = Self {
scope: "api offline_access".to_string(),
client_id: "web".to_string(),
device_type: 10,
device_identifier: "b86dd6ab-4265-4ddf-a7f1-eb28d5677f33".to_string(),
device_type: device_type as u8,
device_identifier: device_identifier.to_string(),
device_name: "firefox".to_string(),
grant_type: "password".to_string(),
master_password_hash: password_hash.to_string(),
Expand Down
Loading

0 comments on commit bd2fe96

Please sign in to comment.