Skip to content

Unify WASM crypto client with mobile #226

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

Merged
merged 9 commits into from
Jun 11, 2025
Merged
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/bitwarden-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ uniffi = ["bitwarden-crypto/uniffi", "dep:uniffi"] # Uniffi bindings
wasm = [
"bitwarden-error/wasm",
"dep:wasm-bindgen",
"dep:wasm-bindgen-futures",
"dep:tsify-next"
] # WASM support

Expand All @@ -50,6 +51,7 @@ tsify-next = { workspace = true, optional = true }
uniffi = { workspace = true, optional = true, features = ["tokio"] }
uuid = { workspace = true }
wasm-bindgen = { workspace = true, optional = true }
wasm-bindgen-futures = { workspace = true, optional = true }
zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] }
zxcvbn = { version = ">=3.0.1, <4.0", optional = true }

Expand Down
6 changes: 3 additions & 3 deletions crates/bitwarden-core/src/auth/auth_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ mod tests {
use bitwarden_crypto::{Kdf, MasterKey};

use super::*;
use crate::{
key_management::SymmetricKeyId,
mobile::crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
use crate::key_management::{
crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
SymmetricKeyId,
};

#[test]
Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden-core/src/auth/login/auth_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
auth_request::new_auth_request,
},
client::{LoginMethod, UserLoginMethod},
mobile::crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
key_management::crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
require, ApiError, Client,
};

Expand Down
13 changes: 7 additions & 6 deletions crates/bitwarden-core/src/client/test_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ use std::collections::HashMap;
use bitwarden_crypto::{EncString, Kdf};

use crate::{
mobile::crypto::{
initialize_org_crypto, initialize_user_crypto, InitOrgCryptoRequest, InitUserCryptoMethod,
InitUserCryptoRequest,
},
key_management::crypto::{InitOrgCryptoRequest, InitUserCryptoMethod, InitUserCryptoRequest},
Client,
};

Expand All @@ -20,10 +17,14 @@ impl Client {
true,
)]));

initialize_user_crypto(&client, account.user).await.unwrap();
client
.crypto()
.initialize_user_crypto(account.user)
.await
.unwrap();

if let Some(org) = account.org {
initialize_org_crypto(&client, org).await.unwrap();
client.crypto().initialize_org_crypto(org).await.unwrap();
}

client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
AsymmetricCryptoKey, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey,
SymmetricCryptoKey, UnsignedSharedKey, UserKey,
};
use bitwarden_error::bitwarden_error;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[cfg(feature = "wasm")]
use {tsify_next::Tsify, wasm_bindgen::prelude::*};
Expand All @@ -23,8 +25,9 @@

/// Catch all error for mobile crypto operations.
#[allow(missing_docs)]
#[bitwarden_error(flat)]

Check warning on line 28 in crates/bitwarden-core/src/key_management/crypto.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/key_management/crypto.rs#L28

Added line #L28 was not covered by tests
#[derive(Debug, thiserror::Error)]
pub enum MobileCryptoError {
pub enum CryptoClientError {
#[error(transparent)]
NotAuthenticated(#[from] NotAuthenticatedError),
#[error(transparent)]
Expand Down Expand Up @@ -123,7 +126,7 @@
}

/// Initialize the user's cryptographic state.
pub async fn initialize_user_crypto(
pub(super) async fn initialize_user_crypto(
client: &Client,
req: InitUserCryptoRequest,
) -> Result<(), EncryptionSettingsError> {
Expand Down Expand Up @@ -236,7 +239,7 @@
}

/// Initialize the user's organizational cryptographic state.
pub(crate) async fn initialize_org_crypto(
pub(super) async fn initialize_org_crypto(

Check warning on line 242 in crates/bitwarden-core/src/key_management/crypto.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/key_management/crypto.rs#L242

Added line #L242 was not covered by tests
client: &Client,
req: InitOrgCryptoRequest,
) -> Result<(), EncryptionSettingsError> {
Expand All @@ -245,7 +248,7 @@
Ok(())
}

pub(super) async fn get_user_encryption_key(client: &Client) -> Result<String, MobileCryptoError> {
pub(super) async fn get_user_encryption_key(client: &Client) -> Result<String, CryptoClientError> {

Check warning on line 251 in crates/bitwarden-core/src/key_management/crypto.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/key_management/crypto.rs#L251

Added line #L251 was not covered by tests
let key_store = client.internal.get_key_store();
let ctx = key_store.context();
// This is needed because the mobile clients need access to the user encryption key
Expand All @@ -259,6 +262,7 @@
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct UpdatePasswordResponse {
/// Hash of the new password
password_hash: String,
Expand All @@ -269,7 +273,7 @@
pub(super) fn update_password(
client: &Client,
new_password: String,
) -> Result<UpdatePasswordResponse, MobileCryptoError> {
) -> Result<UpdatePasswordResponse, CryptoClientError> {
let key_store = client.internal.get_key_store();
let ctx = key_store.context();
// FIXME: [PM-18099] Once MasterKey deals with KeyIds, this should be updated
Expand Down Expand Up @@ -308,6 +312,7 @@
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct DerivePinKeyResponse {
/// [UserKey] protected by PIN
pin_protected_user_key: EncString,
Expand All @@ -318,7 +323,7 @@
pub(super) fn derive_pin_key(
client: &Client,
pin: String,
) -> Result<DerivePinKeyResponse, MobileCryptoError> {
) -> Result<DerivePinKeyResponse, CryptoClientError> {
let key_store = client.internal.get_key_store();
let ctx = key_store.context();
// FIXME: [PM-18099] Once PinKey deals with KeyIds, this should be updated
Expand All @@ -341,7 +346,7 @@
pub(super) fn derive_pin_user_key(
client: &Client,
encrypted_pin: EncString,
) -> Result<EncString, MobileCryptoError> {
) -> Result<EncString, CryptoClientError> {
let key_store = client.internal.get_key_store();
let ctx = key_store.context();
// FIXME: [PM-18099] Once PinKey deals with KeyIds, this should be updated
Expand All @@ -361,7 +366,7 @@
pin: &str,
login_method: &LoginMethod,
user_key: &SymmetricCryptoKey,
) -> Result<EncString, MobileCryptoError> {
) -> Result<EncString, CryptoClientError> {
use bitwarden_crypto::PinKey;

let derived_key = match login_method {
Expand All @@ -377,6 +382,7 @@
}

#[allow(missing_docs)]
#[bitwarden_error(flat)]

Check warning on line 385 in crates/bitwarden-core/src/key_management/crypto.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/key_management/crypto.rs#L385

Added line #L385 was not covered by tests
#[derive(Debug, thiserror::Error)]
pub enum EnrollAdminPasswordResetError {
#[error(transparent)]
Expand Down Expand Up @@ -408,7 +414,10 @@
}

/// Request for migrating an account from password to key connector.
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct DeriveKeyConnectorRequest {
/// Encrypted user key, used to validate the master key
pub user_key_encrypted: EncString,
Expand All @@ -421,6 +430,7 @@
}

#[allow(missing_docs)]
#[bitwarden_error(flat)]

Check warning on line 433 in crates/bitwarden-core/src/key_management/crypto.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/key_management/crypto.rs#L433

Added line #L433 was not covered by tests
#[derive(Debug, thiserror::Error)]
pub enum DeriveKeyConnectorError {
#[error(transparent)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
use bitwarden_crypto::CryptoError;
#[cfg(feature = "internal")]
use bitwarden_crypto::{EncString, UnsignedSharedKey};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;

use super::crypto::{
derive_key_connector, make_key_pair, verify_asymmetric_keys, DeriveKeyConnectorError,
DeriveKeyConnectorRequest, EnrollAdminPasswordResetError, MakeKeyPairResponse,
MobileCryptoError, VerifyAsymmetricKeysRequest, VerifyAsymmetricKeysResponse,
derive_key_connector, make_key_pair, verify_asymmetric_keys, CryptoClientError,
DeriveKeyConnectorError, DeriveKeyConnectorRequest, EnrollAdminPasswordResetError,
MakeKeyPairResponse, VerifyAsymmetricKeysRequest, VerifyAsymmetricKeysResponse,
};
#[cfg(feature = "internal")]
use crate::mobile::crypto::{
use crate::key_management::crypto::{
derive_pin_key, derive_pin_user_key, enroll_admin_password_reset, get_user_encryption_key,
initialize_org_crypto, initialize_user_crypto, update_password, DerivePinKeyResponse,
InitOrgCryptoRequest, InitUserCryptoRequest, UpdatePasswordResponse,
};
use crate::{client::encryption_settings::EncryptionSettingsError, Client};

/// A client for the crypto operations.
#[cfg_attr(feature = "wasm", wasm_bindgen)]
pub struct CryptoClient {
pub(crate) client: crate::Client,
}

#[cfg_attr(feature = "wasm", wasm_bindgen)]
impl CryptoClient {
/// Initialization method for the user crypto. Needs to be called before any other crypto
/// operations.
Expand All @@ -39,9 +43,27 @@
initialize_org_crypto(&self.client, req).await
}

/// Generates a new key pair and encrypts the private key with the provided user key.
/// Crypto initialization not required.
pub fn make_key_pair(&self, user_key: String) -> Result<MakeKeyPairResponse, CryptoError> {
make_key_pair(user_key)
}

Check warning on line 50 in crates/bitwarden-core/src/key_management/crypto_client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/key_management/crypto_client.rs#L48-L50

Added lines #L48 - L50 were not covered by tests

/// Verifies a user's asymmetric keys by decrypting the private key with the provided user
/// key. Returns if the private key is decryptable and if it is a valid matching key.
/// Crypto initialization not required.
pub fn verify_asymmetric_keys(
&self,
request: VerifyAsymmetricKeysRequest,
) -> Result<VerifyAsymmetricKeysResponse, CryptoError> {
verify_asymmetric_keys(request)
}

Check warning on line 60 in crates/bitwarden-core/src/key_management/crypto_client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/key_management/crypto_client.rs#L55-L60

Added lines #L55 - L60 were not covered by tests
}

impl CryptoClient {
/// Get the uses's decrypted encryption key. Note: It's very important
/// to keep this key safe, as it can be used to decrypt all of the user's data
pub async fn get_user_encryption_key(&self) -> Result<String, MobileCryptoError> {
pub async fn get_user_encryption_key(&self) -> Result<String, CryptoClientError> {

Check warning on line 66 in crates/bitwarden-core/src/key_management/crypto_client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/key_management/crypto_client.rs#L66

Added line #L66 was not covered by tests
get_user_encryption_key(&self.client).await
}

Expand All @@ -50,14 +72,14 @@
pub fn update_password(
&self,
new_password: String,
) -> Result<UpdatePasswordResponse, MobileCryptoError> {
) -> Result<UpdatePasswordResponse, CryptoClientError> {

Check warning on line 75 in crates/bitwarden-core/src/key_management/crypto_client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/key_management/crypto_client.rs#L75

Added line #L75 was not covered by tests
update_password(&self.client, new_password)
}

/// Generates a PIN protected user key from the provided PIN. The result can be stored and later
/// used to initialize another client instance by using the PIN and the PIN key with
/// `initialize_user_crypto`.
pub fn derive_pin_key(&self, pin: String) -> Result<DerivePinKeyResponse, MobileCryptoError> {
pub fn derive_pin_key(&self, pin: String) -> Result<DerivePinKeyResponse, CryptoClientError> {

Check warning on line 82 in crates/bitwarden-core/src/key_management/crypto_client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/key_management/crypto_client.rs#L82

Added line #L82 was not covered by tests
derive_pin_key(&self.client, pin)
}

Expand All @@ -66,7 +88,7 @@
pub fn derive_pin_user_key(
&self,
encrypted_pin: EncString,
) -> Result<EncString, MobileCryptoError> {
) -> Result<EncString, CryptoClientError> {

Check warning on line 91 in crates/bitwarden-core/src/key_management/crypto_client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/key_management/crypto_client.rs#L91

Added line #L91 was not covered by tests
derive_pin_user_key(&self.client, encrypted_pin)
}

Expand All @@ -86,21 +108,6 @@
) -> Result<String, DeriveKeyConnectorError> {
derive_key_connector(request)
}

/// Generates a new key pair and encrypts the private key with the provided user key.
pub fn make_key_pair(&self, user_key: String) -> Result<MakeKeyPairResponse, CryptoError> {
make_key_pair(user_key)
}

/// Verifies a user's asymmetric keys by decrypting the private key with the provided user
/// key. Returns if the private key is decryptable and if it is a valid matching key.
/// Crypto initialization not required.
pub fn verify_asymmetric_keys(
&self,
request: VerifyAsymmetricKeysRequest,
) -> Result<VerifyAsymmetricKeysResponse, CryptoError> {
verify_asymmetric_keys(request)
}
}

impl Client {
Expand Down
5 changes: 5 additions & 0 deletions crates/bitwarden-core/src/key_management/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
//! [Encryptable](bitwarden_crypto::Encryptable) and [Decryptable](bitwarden_crypto::Encryptable).
use bitwarden_crypto::{key_ids, KeyStore, SymmetricCryptoKey};

pub mod crypto;
mod crypto_client;

pub use crypto_client::CryptoClient;

key_ids! {
#[symmetric]
pub enum SymmetricKeyId {
Expand Down
3 changes: 0 additions & 3 deletions crates/bitwarden-core/src/mobile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@
//! This module consists of stop-gap functionality for the mobile clients until the SDK owns it's
//! own state.

pub mod crypto;
mod kdf;

mod client_kdf;
mod crypto_client;

pub use client_kdf::KdfClient;
pub use crypto_client::CryptoClient;
2 changes: 1 addition & 1 deletion crates/bitwarden-core/tests/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ async fn test_register_initialize_crypto() {
use std::num::NonZeroU32;

use bitwarden_core::{
mobile::crypto::{InitUserCryptoMethod, InitUserCryptoRequest},
key_management::crypto::{InitUserCryptoMethod, InitUserCryptoRequest},
Client,
};
use bitwarden_crypto::Kdf;
Expand Down
4 changes: 2 additions & 2 deletions crates/bitwarden-uniffi/src/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bitwarden_core::mobile::crypto::{
use bitwarden_core::key_management::crypto::{
DeriveKeyConnectorRequest, DerivePinKeyResponse, InitOrgCryptoRequest, InitUserCryptoRequest,
UpdatePasswordResponse,
};
Expand All @@ -8,7 +8,7 @@ use crate::error::{Error, Result};

#[allow(missing_docs)]
#[derive(uniffi::Object)]
pub struct CryptoClient(pub(crate) bitwarden_core::mobile::CryptoClient);
pub struct CryptoClient(pub(crate) bitwarden_core::key_management::CryptoClient);

#[uniffi::export(async_runtime = "tokio")]
impl CryptoClient {
Expand Down
8 changes: 5 additions & 3 deletions crates/bitwarden-uniffi/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,17 @@ pub enum Error {
#[error(transparent)]
Api(#[from] bitwarden_core::ApiError),
#[error(transparent)]
DeriveKeyConnector(#[from] bitwarden_core::mobile::crypto::DeriveKeyConnectorError),
DeriveKeyConnector(#[from] bitwarden_core::key_management::crypto::DeriveKeyConnectorError),
#[error(transparent)]
EncryptionSettings(
#[from] bitwarden_core::client::encryption_settings::EncryptionSettingsError,
),
#[error(transparent)]
EnrollAdminPasswordReset(#[from] bitwarden_core::mobile::crypto::EnrollAdminPasswordResetError),
EnrollAdminPasswordReset(
#[from] bitwarden_core::key_management::crypto::EnrollAdminPasswordResetError,
),
#[error(transparent)]
MobileCrypto(#[from] bitwarden_core::mobile::crypto::MobileCryptoError),
MobileCrypto(#[from] bitwarden_core::key_management::crypto::CryptoClientError),
#[error(transparent)]
AuthValidate(#[from] bitwarden_core::auth::AuthValidateError),
#[error(transparent)]
Expand Down
Loading
Loading