Skip to content

Commit

Permalink
Add support for generating the users fingerprint (#474)
Browse files Browse the repository at this point in the history
Add support for generating the users fingerprint using the private key
from EncSettings.
  • Loading branch information
Hinton authored Jan 5, 2024
1 parent f3cf39b commit 68236d3
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 25 deletions.
2 changes: 1 addition & 1 deletion crates/bitwarden-json/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl Client {
#[cfg(feature = "internal")]
Command::Sync(req) => self.0.sync(&req).await.into_string(),
#[cfg(feature = "internal")]
Command::Fingerprint(req) => self.0.fingerprint(&req).into_string(),
Command::Fingerprint(req) => self.0.platform().fingerprint(&req).into_string(),

#[cfg(feature = "secrets")]
Command::Secrets(cmd) => match cmd {
Expand Down
22 changes: 20 additions & 2 deletions crates/bitwarden-uniffi/src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,26 @@ pub struct ClientPlatform(pub(crate) Arc<Client>);

#[uniffi::export(async_runtime = "tokio")]
impl ClientPlatform {
/// Fingerprint
/// Fingerprint (public key)
pub async fn fingerprint(&self, req: FingerprintRequest) -> Result<String> {
Ok(self.0 .0.read().await.fingerprint(&req)?.fingerprint)
Ok(self
.0
.0
.write()
.await
.platform()
.fingerprint(&req)?
.fingerprint)
}

/// Fingerprint using logged in user's public key
pub async fn user_fingerprint(&self, fingerprint_material: String) -> Result<String> {
Ok(self
.0
.0
.write()
.await
.platform()
.user_fingerprint(fingerprint_material)?)
}
}
3 changes: 3 additions & 0 deletions crates/bitwarden-uniffi/src/uniffi_support.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use bitwarden::crypto::EncString;

// Forward the type definitions to the main bitwarden crate
type DateTime = chrono::DateTime<chrono::Utc>;
uniffi::ffi_converter_forward!(DateTime, bitwarden::UniFfiTag, crate::UniFfiTag);
uniffi::ffi_converter_forward!(EncString, bitwarden::UniFfiTag, crate::UniFfiTag);
12 changes: 3 additions & 9 deletions crates/bitwarden/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ use chrono::Utc;
use reqwest::header::{self};
use uuid::Uuid;

use super::AccessToken;
#[cfg(feature = "secrets")]
use crate::auth::login::{AccessTokenLoginRequest, AccessTokenLoginResponse};
#[cfg(feature = "internal")]
use crate::{
client::kdf::Kdf,
crypto::{AsymmEncString, EncString},
platform::{
generate_fingerprint, get_user_api_key, sync, FingerprintRequest, FingerprintResponse,
SecretVerificationRequest, SyncRequest, SyncResponse, UserApiKeyResponse,
get_user_api_key, sync, SecretVerificationRequest, SyncRequest, SyncResponse,
UserApiKeyResponse,
},
};
use crate::{
Expand All @@ -24,8 +25,6 @@ use crate::{
error::{Error, Result},
};

use super::AccessToken;

#[derive(Debug)]
pub(crate) struct ApiConfigurations {
pub identity: bitwarden_api_identity::apis::configuration::Configuration,
Expand Down Expand Up @@ -285,9 +284,4 @@ impl Client {
enc.set_org_keys(org_keys)?;
Ok(self.encryption_settings.as_ref().unwrap())
}

#[cfg(feature = "internal")]
pub fn fingerprint(&self, input: &FingerprintRequest) -> Result<FingerprintResponse> {
generate_fingerprint(input)
}
}
2 changes: 1 addition & 1 deletion crates/bitwarden/src/client/encryption_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::crypto::SymmetricCryptoKey;

pub struct EncryptionSettings {
user_key: SymmetricCryptoKey,
private_key: Option<RsaPrivateKey>,
pub(crate) private_key: Option<RsaPrivateKey>,
org_keys: HashMap<Uuid, SymmetricCryptoKey>,
}

Expand Down
25 changes: 25 additions & 0 deletions crates/bitwarden/src/platform/client_platform.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use super::{
generate_fingerprint::{generate_fingerprint, generate_user_fingerprint},
FingerprintRequest, FingerprintResponse,
};
use crate::{error::Result, Client};

pub struct ClientPlatform<'a> {
pub(crate) client: &'a mut Client,
}

impl<'a> ClientPlatform<'a> {
pub fn fingerprint(&self, input: &FingerprintRequest) -> Result<FingerprintResponse> {
generate_fingerprint(input)
}

pub fn user_fingerprint(self, fingerprint_material: String) -> Result<String> {
generate_user_fingerprint(self.client, fingerprint_material)
}
}

impl<'a> Client {
pub fn platform(&'a mut self) -> ClientPlatform<'a> {
ClientPlatform { client: self }
}
}
66 changes: 64 additions & 2 deletions crates/bitwarden/src/platform/generate_fingerprint.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use base64::Engine;
use log::{debug, info};
use log::info;
use rsa::pkcs8::EncodePublicKey;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

Expand All @@ -23,11 +24,72 @@ pub struct FingerprintResponse {

pub(crate) fn generate_fingerprint(input: &FingerprintRequest) -> Result<FingerprintResponse> {
info!("Generating fingerprint");
debug!("{:?}", input);

let key = BASE64_ENGINE.decode(&input.public_key)?;

Ok(FingerprintResponse {
fingerprint: fingerprint(&input.fingerprint_material, &key)?,
})
}

pub(crate) fn generate_user_fingerprint(
client: &mut crate::Client,
fingerprint_material: String,
) -> Result<String> {
info!("Generating fingerprint");

let enc_settings = client.get_encryption_settings()?;
let private_key = enc_settings
.private_key
.as_ref()
.ok_or("Missing private key")?;

let public_key = private_key
.to_public_key()
.to_public_key_der()
.map_err(|_| "Invalid key")?;

let fingerprint = fingerprint(&fingerprint_material, public_key.as_bytes())?;

Ok(fingerprint)
}

#[cfg(test)]
mod tests {
use std::num::NonZeroU32;

use crate::{
client::{kdf::Kdf, LoginMethod, UserLoginMethod},
Client,
};

use super::*;

#[test]
fn test_generate_user_fingerprint() {
let user_key = "2.oZg5RYpU2HjUAKI1DUQCkg==|PyRzI9kZpt66P2OedH8CHOeU0/lgKLkhIJiKDijdyFqIemBSIBoslhfQh/P1TK9xgZp0smgD6+5+yNbZfOpBaCVrsT3WWAO78xOWizduRe4=|xfDLDZSJ+yZAdh388flVg7SMDBJuMs0+CHTjutKs4uQ=";
let private_key = "2.tY6WsWKUbBwNU8wROuipiQ==|DNFL1d19xVojUKTTy2gxT+9J1VXbMQLcbMnx1HSeA6U3yZhsLR6DPaGibb3Bp8doIHtrsxzL/JeLb4gLDZ8RnDhFfE4iLRaPakX14kbBXrKH9/uW/zc7TqIVciWhI1PaeFlu8wnVuGt3e5Ysx6Y7Uw7RS8pRT5aE3sX3aDPGZTAdTutLn1VUfkShS5OK5HJl9CdiwV2wOcrf4w/WqtaNUUqGdsJ8C4ELlpBzHxqs+lEm+8pGPYmuGQIjVc0eOR9Tza9GTk3ih1XGc1znOCoKUZbtA29RfbwfmJy/yGi/3RLWZFQGCCij4cLC5OpldiX4JWL5Dhox44p/5IVF3rfxTVz3GCyDOoHevRG/06sUBq6nhbdCQf3lJvxwcQJhoQg4rsapM3rgol+u+TbXRiwWPbfswuLkRlvGFKtKUWMa4S57gj0CFYgSBPdTyhZTB44D7JQ2bd901Ur1dYWcDe4Kn3ZawpxL0cX2ZPlE3v8FXFJf2s8DJytL8yu73GasDzVmaGHxueWWVz7EHjh+pmB4oaAHARcY8d3LActAyl/+bcFRPYQJ68ae6DJhYYJGHIBWMImf2BifGgUX8vUFfUAYjne3D82lRyZQHs3xbl+ZxEPgWiPYRWUtxGXLLP4f9mbl+LeJdehtHNjC8kOduBL0CsP4gmugzNNUXI+Izc/9svno6kFr6SU0LA3MGrOU8ao7UCQbf/Pj/RKnG1gRmBDQqf7IMm6jOyTwdde9NpfQb32iH11PkuAKBvEtUuq9BeAKWjoZku+ycsN2jZH0hzd/QrU2c+E4+yHwX3wSxxorNOXt5EZkJbEDBlpRyE1zWoyy0wIYfcChYLvFN8QFHchlw5wmHxL+OOgdgndAtV/2DCx+NB6caY31qLictME+1GPPlQ7QvicMLgmpSWq83rs4ex/My6p3hCRSrJJiLvjEDZLYWKHHLd5tsPRAjX8ADNWB1VeIeiJrj1wpOCc1PbWpbljbbTsBmVPo6iKm/UDGAHBdQ//0j3FQg8f5w/j+McsoaMpDNHNTiLvjWERR+RBmsEA0lEL00wZz/DHlzOAYHLYYqFMT7GBCQD+Wk/l1TL+X2agUy7Irlk7QbZ4ivfdNIpSW8Ct9MGE6o4wV+nIpXURojgBBTcP85RTBLXXGrIprnK1G/VE8ONag3+nkqIyChjYyk5QMsxqOqSHsbiOxhCdXypbCbY4g9yKJtBJ/ADjxmELj0X7pqsTFqC0eRT7rk9qTBcYBBu6rwlAfq8AKjDB7WjNjzLaMi6lBoe4petBn1xcLkXD5hHra0TULxcYrq8MIb+Vk4CBZZdwwyVm/28SwSjHBIBpRysPAonDDsp3KlahwXEFvRDQR/oFww172GI7cx8SoPn93Qh0JfpTAAowsO3meR8bzUSyd7v3rmtaBPsWHE9zUXye/6nloMU5joEcD6uJaxd0kdaWWIoKLH++zHW1R776wJrS6u+TIWZgHqiIJoCd9fV25BnQcbZRKd6mnfNQkchJ6c6ozXKrFaa8DLdERdfh84+isw5mzW2zMJwHEwtKt6LUTyieC2exzPAwPxJT1+IMjuzuwiLnvGKOq+kwE/LWBSB0ZfGuCP/3jMM8OCfe7Hbpt1TfXcUxUzj6sSjkjQB6qBt+TINRdOFA=|fppguME86utsAOKrBYn6XU95q7daVbZ+3dD9OVkQlAw=";
let fingerprint_material = "a09726a0-9590-49d1-a5f5-afe300b6a515";

let mut client = Client::new(None);
client.set_login_method(LoginMethod::User(UserLoginMethod::Username {
client_id: "a09726a0-9590-49d1-a5f5-afe300b6a515".to_owned(),
email: "[email protected]".to_owned(),
kdf: Kdf::PBKDF2 {
iterations: NonZeroU32::new(600_000).unwrap(),
},
}));
client
.initialize_user_crypto(
"asdfasdfasdf",
user_key.parse().unwrap(),
private_key.parse().unwrap(),
)
.unwrap();

let fingerprint =
generate_user_fingerprint(&mut client, fingerprint_material.to_string()).unwrap();

assert_eq!(fingerprint, "turban-deftly-anime-chatroom-unselfish");
}
}
2 changes: 1 addition & 1 deletion crates/bitwarden/src/platform/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
pub mod client_platform;
mod domain;
mod generate_fingerprint;
mod get_user_api_key;
mod secret_verification_request;
mod sync;

pub(crate) use generate_fingerprint::generate_fingerprint;
pub use generate_fingerprint::{FingerprintRequest, FingerprintResponse};
pub(crate) use get_user_api_key::get_user_api_key;
pub use get_user_api_key::UserApiKeyResponse;
Expand Down
40 changes: 31 additions & 9 deletions languages/kotlin/doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Test method, echoes back the input

### `satisfies_policy`

**API Draft:** Evaluate if the provided password satisfies the provided policy
Evaluate if the provided password satisfies the provided policy

**Arguments**:

Expand Down Expand Up @@ -333,6 +333,17 @@ Decrypt folder list

**Output**: std::result::Result<String,BitwardenError>

### `username`

**API Draft:** Generate Username

**Arguments**:

- self:
- settings: UsernameGeneratorRequest

**Output**: std::result::Result<String,BitwardenError>

## ClientPasswordHistory

### `encrypt`
Expand Down Expand Up @@ -361,7 +372,7 @@ Decrypt password history

### `fingerprint`

Fingerprint
Fingerprint (public key)

**Arguments**:

Expand All @@ -370,6 +381,17 @@ Fingerprint

**Output**: std::result::Result<String,BitwardenError>

### `user_fingerprint`

Fingerprint using logged in user&#x27;s public key

**Arguments**:

- self:
- fingerprint_material: String

**Output**: std::result::Result<String,BitwardenError>

## ClientSends

### `encrypt`
Expand Down Expand Up @@ -800,7 +822,7 @@ implementations.
</tr>
<tr>
<th>id</th>
<th>string</th>
<th>string,null</th>
<th></th>
</tr>
<tr>
Expand Down Expand Up @@ -891,7 +913,7 @@ implementations.
</tr>
<tr>
<th>id</th>
<th>string</th>
<th>string,null</th>
<th></th>
</tr>
<tr>
Expand All @@ -916,7 +938,7 @@ implementations.
</tr>
<tr>
<th>id</th>
<th>string</th>
<th>string,null</th>
<th></th>
</tr>
<tr>
Expand Down Expand Up @@ -1296,12 +1318,12 @@ implementations.
</tr>
<tr>
<th>id</th>
<th>string</th>
<th>string,null</th>
<th></th>
</tr>
<tr>
<th>accessId</th>
<th>string</th>
<th>string,null</th>
<th></th>
</tr>
<tr>
Expand Down Expand Up @@ -1386,12 +1408,12 @@ implementations.
</tr>
<tr>
<th>id</th>
<th>string</th>
<th>string,null</th>
<th></th>
</tr>
<tr>
<th>accessId</th>
<th>string</th>
<th>string,null</th>
<th></th>
</tr>
<tr>
Expand Down

0 comments on commit 68236d3

Please sign in to comment.