Skip to content

Commit

Permalink
Merge branch 'main' into run-command-with-secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
tangowithfoxtrot authored Aug 8, 2024
2 parents 87fc76d + 3621c25 commit bc1a6ff
Show file tree
Hide file tree
Showing 52 changed files with 1,044 additions and 714 deletions.
441 changes: 233 additions & 208 deletions Cargo.lock

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,21 @@ are currently open as well as what it's like to work at Bitwarden.

## Getting Started

### Linux / Mac / Windows

```bash
cargo build
```

### Windows on ARM

To build, you will need the following in your PATH:

- [Python](https://www.python.org)
- [Clang](https://clang.llvm.org)
- We recommend installing this via the
[Visual Studio Build Tools](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022)

## Crates

The project is structured as a monorepo using cargo workspaces. Some of the more noteworthy crates
Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ serde_repr = ">=0.1.12, <0.2"
sha1 = ">=0.10.5, <0.11"
sha2 = ">=0.10.6, <0.11"
thiserror = ">=1.0.40, <2.0"
uniffi = { version = "=0.27.2", optional = true, features = ["tokio"] }
uniffi = { version = "=0.28.0", optional = true, features = ["tokio"] }
uuid = { version = ">=1.3.3, <2.0", features = ["serde"] }
validator = { version = "0.18.1", features = ["derive"] }
zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] }
Expand Down
7 changes: 6 additions & 1 deletion crates/bitwarden-core/src/auth/client_auth.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#[cfg(feature = "internal")]
use bitwarden_crypto::{AsymmetricEncString, DeviceKey, Kdf, TrustDeviceResponse};
use bitwarden_crypto::{AsymmetricEncString, DeviceKey, EncString, Kdf, TrustDeviceResponse};

#[cfg(feature = "internal")]
use crate::auth::login::NewAuthRequestResponse;
Expand All @@ -16,6 +16,7 @@ use crate::auth::{
password_strength, satisfies_policy, validate_password, validate_password_user_key,
MasterPasswordPolicyOptions,
},
pin::validate_pin,
register::{make_register_keys, register},
tde::{make_register_tde_keys, RegisterTdeKeyResponse},
AuthRequestResponse, RegisterKeyResponse, RegisterRequest,
Expand Down Expand Up @@ -116,6 +117,10 @@ impl<'a> ClientAuth<'a> {
validate_password_user_key(self.client, password, encrypted_user_key)
}

pub fn validate_pin(&self, pin: String, pin_protected_user_key: EncString) -> Result<bool> {
validate_pin(self.client, pin, pin_protected_user_key)
}

pub fn new_auth_request(&self, email: &str) -> Result<AuthRequestResponse> {
new_auth_request(email)
}
Expand Down
2 changes: 2 additions & 0 deletions crates/bitwarden-core/src/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ mod jwt_token;
pub mod login;
#[cfg(feature = "internal")]
pub mod password;
#[cfg(feature = "internal")]
pub mod pin;
pub mod renew;
pub use access_token::AccessToken;
pub use jwt_token::JWTToken;
Expand Down
101 changes: 101 additions & 0 deletions crates/bitwarden-core/src/auth/pin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use bitwarden_crypto::{EncString, PinKey};

use crate::{
client::{LoginMethod, UserLoginMethod},
error::{Error, Result},
Client,
};

pub(crate) fn validate_pin(
client: &Client,
pin: String,
pin_protected_user_key: EncString,
) -> Result<bool> {
let login_method = client
.internal
.get_login_method()
.ok_or(Error::NotAuthenticated)?;

#[allow(irrefutable_let_patterns)]
let LoginMethod::User(login_method) = login_method.as_ref() else {
return Err(Error::NotAuthenticated);
};

match login_method {
UserLoginMethod::Username { email, kdf, .. }
| UserLoginMethod::ApiKey { email, kdf, .. } => {
let enc = client.internal.get_encryption_settings()?;
let user_key = enc.get_key(&None)?;

let pin_key = PinKey::derive(pin.as_bytes(), email.as_bytes(), kdf)?;

let Ok(decrypted_key) = pin_key.decrypt_user_key(pin_protected_user_key) else {
return Ok(false);
};

Ok(user_key.to_vec() == decrypted_key.to_vec())
}
}
}

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

use bitwarden_crypto::{Kdf, MasterKey};

use super::*;
use crate::client::{Client, LoginMethod, UserLoginMethod};

fn init_client() -> Client {
let client = Client::new(None);

let password = "asdfasdfasdf";
let email = "[email protected]";
let kdf = Kdf::PBKDF2 {
iterations: NonZeroU32::new(600_000).unwrap(),
};

client
.internal
.set_login_method(LoginMethod::User(UserLoginMethod::Username {
email: email.to_string(),
kdf: kdf.clone(),
client_id: "1".to_string(),
}));

let master_key = MasterKey::derive(password, email, &kdf).unwrap();

let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
let private_key = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=".parse().unwrap();

client
.internal
.initialize_user_crypto_master_key(master_key, user_key.parse().unwrap(), private_key)
.unwrap();

client
}

#[test]
fn test_validate_valid_pin() {
let pin = "1234".to_string();
let pin_protected_user_key = "2.BXgvdBUeEMyvumqAJkAzPA==|JScDPoqOkVdrC1X755Ubt8tS9pC/thvrvNf5CyNcRg8HZtZ466EcRo7aCqwUzLyTVNRkbCYtFYT+09acGGHur8tGuS7Kmg/pYeaUo4K0UKI=|NpIFg5P9z0SN1MffbixD9OQE0l+NiNmnRQJs/kTsyoQ="
.parse()
.unwrap();

let client = init_client();
assert!(validate_pin(&client, pin.clone(), pin_protected_user_key).unwrap());
}

#[test]
fn test_validate_invalid_pin() {
let pin = "1234".to_string();
let pin_protected_user_key = "2.BXgvdBUeEMyvumqAJkAyPA==|JScDPoqOkVdrC1X755Ubt8tS9pC/thvrvNf5CyNcRg8HZtZ466EcRo7aCqwUzLyTVNRkbCYtFYT+09acGGHur8tGuS7Kmg/pYeaUo4K0UKI=|NpIFg5P9z0SN1MffbixD9OQE0l+NiNmnRQJs/kTsyoQ="
.parse()
.unwrap();

let client = init_client();
assert!(!validate_pin(&client, pin.clone(), pin_protected_user_key).unwrap());
}
}
13 changes: 4 additions & 9 deletions crates/bitwarden-core/src/client/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,9 @@ impl InternalClient {
.write()
.expect("RwLock is not poisoned");

let mut inner: ApiConfigurations = guard.as_ref().clone();
let inner = Arc::make_mut(&mut guard);
inner.identity.oauth_access_token = Some(token.clone());
inner.api.oauth_access_token = Some(token);

*guard = Arc::new(inner);
}

#[cfg(feature = "internal")]
Expand Down Expand Up @@ -244,12 +242,9 @@ impl InternalClient {
return Err(VaultLocked.into());
};

let mut enc: EncryptionSettings = enc.as_ref().clone();
enc.set_org_keys(org_keys)?;
let enc = Arc::new(enc);

*guard = Some(enc.clone());
let inner = Arc::make_mut(enc);
inner.set_org_keys(org_keys)?;

Ok(enc)
Ok(enc.clone())
}
}
2 changes: 1 addition & 1 deletion crates/bitwarden-crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ sha1 = ">=0.10.5, <0.11"
sha2 = ">=0.10.6, <0.11"
subtle = ">=2.5.0, <3.0"
thiserror = ">=1.0.40, <2.0"
uniffi = { version = "=0.27.2", optional = true }
uniffi = { version = "=0.28.0", optional = true }
uuid = { version = ">=1.3.3, <2.0", features = ["serde"] }
zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] }

Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden-crypto/src/enc_string/symmetric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use crate::{
/// - `[iv]`: (optional) is the initialization vector used for encryption.
/// - `[data]`: is the encrypted data.
/// - `[mac]`: (optional) is the MAC used to validate the integrity of the data.
#[derive(Clone, zeroize::ZeroizeOnDrop)]
#[derive(Clone, zeroize::ZeroizeOnDrop, PartialEq)]
#[allow(unused, non_camel_case_types)]
pub enum EncString {
/// 0
Expand Down
2 changes: 2 additions & 0 deletions crates/bitwarden-crypto/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub enum CryptoError {
InvalidUtf8String,
#[error("Missing Key for organization with ID {0}")]
MissingKey(Uuid),
#[error("The item was missing a required field: {0}")]
MissingField(&'static str),

#[error("EncString error, {0}")]
EncString(#[from] EncStringParseError),
Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden-exporters/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ schemars = { version = ">=0.8.9, <0.9", features = ["uuid1", "chrono"] }
serde = { version = ">=1.0, <2.0", features = ["derive"] }
serde_json = ">=1.0.96, <2.0"
thiserror = ">=1.0.40, <2.0"
uniffi = { version = "=0.27.2", optional = true }
uniffi = { version = "=0.28.0", optional = true }
uuid = { version = ">=1.3.3, <2.0", features = ["serde", "v4"] }

[lints]
Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden-fido/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ schemars = { version = "0.8.21", features = ["uuid1", "chrono"] }
serde = { version = ">=1.0, <2.0", features = ["derive"] }
serde_json = ">=1.0.96, <2.0"
thiserror = ">=1.0.40, <2.0"
uniffi = { version = "=0.27.2", optional = true }
uniffi = { version = "=0.28.0", optional = true }
uuid = { version = ">=1.3.3, <2.0", features = ["serde"] }

[lints]
Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden-generators/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ schemars = { version = ">=0.8.9, <0.9", features = ["uuid1", "chrono"] }
serde = { version = ">=1.0, <2.0", features = ["derive"] }
serde_json = ">=1.0.96, <2.0"
thiserror = ">=1.0.40, <2.0"
uniffi = { version = "=0.27.2", optional = true }
uniffi = { version = "=0.28.0", optional = true }

[dev-dependencies]
rand_chacha = "0.3.1"
Expand Down
5 changes: 1 addition & 4 deletions crates/bitwarden-napi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ const accessToken = "-- REDACTED --";
const client = new BitwardenClient(settings, LogLevel.Info);

// Authenticating using a machine account access token
const result = await client.loginWithAccessToken(accessToken);
if (!result.success) {
throw Error("Authentication failed");
}
await client.accessTokenLogin(accessToken);

// List secrets
const secrets = await client.secrets().list();
Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden-napi/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const enum LogLevel {
Warn = 3,
Error = 4,
}
export class BitwardenClient {
export declare class BitwardenClient {
constructor(settingsInput?: string | undefined | null, logLevel?: LogLevel | undefined | null);
runCommand(commandInput: string): Promise<string>;
}
76 changes: 71 additions & 5 deletions crates/bitwarden-napi/binding.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
const { existsSync, readFileSync } = require("fs");
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */

/* auto-generated by NAPI-RS */

const { existsSync, readFileSync } = require('fs')
const { join } = require("path");

const { platform, arch } = process;
Expand All @@ -11,7 +17,8 @@ function isMusl() {
// For Node 10
if (!process.report || typeof process.report.getReport !== "function") {
try {
return readFileSync("/usr/bin/ldd", "utf8").includes("musl");
const lddPath = require("child_process").execSync("which ldd").toString().trim();
return readFileSync(lddPath, "utf8").includes("musl");
} catch (e) {
return true;
}
Expand Down Expand Up @@ -95,6 +102,15 @@ switch (platform) {
}
break;
case "darwin":
localFileExisted = existsSync(join(__dirname, "sdk-napi.darwin-universal.node"));
try {
if (localFileExisted) {
nativeBinding = require("./sdk-napi.darwin-universal.node");
} else {
nativeBinding = require("@bitwarden/sdk-napi-darwin-universal");
}
break;
} catch {}
switch (arch) {
case "x64":
localFileExisted = existsSync(join(__dirname, "sdk-napi.darwin-x64.node"));
Expand Down Expand Up @@ -192,12 +208,62 @@ switch (platform) {
}
break;
case "arm":
localFileExisted = existsSync(join(__dirname, "sdk-napi.linux-arm-gnueabihf.node"));
if (isMusl()) {
localFileExisted = existsSync(join(__dirname, "sdk-napi.linux-arm-musleabihf.node"));
try {
if (localFileExisted) {
nativeBinding = require("./sdk-napi.linux-arm-musleabihf.node");
} else {
nativeBinding = require("@bitwarden/sdk-napi-linux-arm-musleabihf");
}
} catch (e) {
loadError = e;
}
} else {
localFileExisted = existsSync(join(__dirname, "sdk-napi.linux-arm-gnueabihf.node"));
try {
if (localFileExisted) {
nativeBinding = require("./sdk-napi.linux-arm-gnueabihf.node");
} else {
nativeBinding = require("@bitwarden/sdk-napi-linux-arm-gnueabihf");
}
} catch (e) {
loadError = e;
}
}
break;
case "riscv64":
if (isMusl()) {
localFileExisted = existsSync(join(__dirname, "sdk-napi.linux-riscv64-musl.node"));
try {
if (localFileExisted) {
nativeBinding = require("./sdk-napi.linux-riscv64-musl.node");
} else {
nativeBinding = require("@bitwarden/sdk-napi-linux-riscv64-musl");
}
} catch (e) {
loadError = e;
}
} else {
localFileExisted = existsSync(join(__dirname, "sdk-napi.linux-riscv64-gnu.node"));
try {
if (localFileExisted) {
nativeBinding = require("./sdk-napi.linux-riscv64-gnu.node");
} else {
nativeBinding = require("@bitwarden/sdk-napi-linux-riscv64-gnu");
}
} catch (e) {
loadError = e;
}
}
break;
case "s390x":
localFileExisted = existsSync(join(__dirname, "sdk-napi.linux-s390x-gnu.node"));
try {
if (localFileExisted) {
nativeBinding = require("./sdk-napi.linux-arm-gnueabihf.node");
nativeBinding = require("./sdk-napi.linux-s390x-gnu.node");
} else {
nativeBinding = require("@bitwarden/sdk-napi-linux-arm-gnueabihf");
nativeBinding = require("@bitwarden/sdk-napi-linux-s390x-gnu");
}
} catch (e) {
loadError = e;
Expand Down
Loading

0 comments on commit bc1a6ff

Please sign in to comment.