Skip to content

Commit

Permalink
[PM-11764] Implement account switching and sdk initialization (#1116)
Browse files Browse the repository at this point in the history
## 🎟️ Tracking

<!-- Paste the link to the Jira or GitHub issue or otherwise describe /
point to where this change is coming from. -->

## 📔 Objective

- Expose crypto client
- Add error handling

## ⏰ Reminders before review

- Contributor guidelines followed
- All formatters and local linters executed and passed
- Written new unit and / or integration tests where applicable
- Protected functional changes with optionality (feature flags)
- Used internationalization (i18n) for all UI strings
- CI builds passed
- Communicated to DevOps any deployment requirements
- Updated any necessary documentation (Confluence, contributing docs) or
informed the documentation
  team

## 🦮 Reviewer guidelines

<!-- Suggested interactions but feel free to use (or not) as you desire!
-->

- 👍 (`:+1:`) or similar for great changes
- 📝 (`:memo:`) or ℹ️ (`:information_source:`) for notes or general info
- ❓ (`:question:`) for questions
- 🤔 (`:thinking:`) or 💭 (`:thought_balloon:`) for more open inquiry
that's not quite a confirmed
  issue and could potentially benefit from discussion
- 🎨 (`:art:`) for suggestions / improvements
- ❌ (`:x:`) or ⚠️ (`:warning:`) for more significant problems or
concerns needing attention
- 🌱 (`:seedling:`) or ♻️ (`:recycle:`) for future improvements or
indications of technical debt
- ⛏ (`:pick:`) for minor or nitpick changes
  • Loading branch information
coroiu authored Oct 14, 2024
1 parent 639667c commit a0f4530
Show file tree
Hide file tree
Showing 18 changed files with 202 additions and 2 deletions.
6 changes: 6 additions & 0 deletions Cargo.lock

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

6 changes: 6 additions & 0 deletions crates/bitwarden-core/src/mobile/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use bitwarden_crypto::{
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[cfg(feature = "wasm")]
use {tsify_next::Tsify, wasm_bindgen::prelude::*};

use crate::{
client::{encryption_settings::EncryptionSettingsError, LoginMethod, UserLoginMethod},
Expand All @@ -16,6 +18,7 @@ use crate::{
#[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 InitUserCryptoRequest {
/// The user's KDF parameters, as received from the prelogin request
pub kdf_params: Kdf,
Expand All @@ -30,6 +33,7 @@ pub struct InitUserCryptoRequest {
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum InitUserCryptoMethod {
Password {
/// The user's master password
Expand Down Expand Up @@ -73,6 +77,7 @@ pub enum InitUserCryptoMethod {
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum AuthRequestMethod {
UserKey {
/// User Key protected by the private key provided in `AuthRequestResponse`.
Expand Down Expand Up @@ -185,6 +190,7 @@ pub async fn initialize_user_crypto(
#[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 InitOrgCryptoRequest {
/// The encryption keys for all the organizations the user is a part of
pub organization_keys: HashMap<uuid::Uuid, AsymmetricEncString>,
Expand Down
3 changes: 3 additions & 0 deletions crates/bitwarden-crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ keywords.workspace = true

[features]
default = []
wasm = ["dep:tsify-next", "dep:wasm-bindgen"] # WASM support

uniffi = ["dep:uniffi"] # Uniffi bindings
no-memory-hardening = [] # Disable memory hardening features
Expand Down Expand Up @@ -42,8 +43,10 @@ sha1 = ">=0.10.5, <0.11"
sha2 = ">=0.10.6, <0.11"
subtle = ">=2.5.0, <3.0"
thiserror = { workspace = true }
tsify-next = { workspace = true, optional = true }
uniffi = { workspace = true, optional = true }
uuid = { workspace = true }
wasm-bindgen = { workspace = true, optional = true }
zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] }

[dev-dependencies]
Expand Down
7 changes: 6 additions & 1 deletion crates/bitwarden-crypto/src/enc_string/asymmetric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ use crate::{
rsa::encrypt_rsa2048_oaep_sha1,
AsymmetricCryptoKey, AsymmetricEncryptable, KeyDecryptable,
};

// This module is a workaround to avoid deprecated warnings that come from the ZeroizeOnDrop
// macro expansion
#[allow(deprecated)]
mod internal {
#[cfg(feature = "wasm")]
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_CUSTOM_TYPES: &'static str = r#"
export type AsymmetricEncString = string;
"#;

/// # Encrypted string primitive
///
/// [AsymmetricEncString] is a Bitwarden specific primitive that represents an asymmetrically
Expand Down
6 changes: 6 additions & 0 deletions crates/bitwarden-crypto/src/enc_string/symmetric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ use crate::{
KeyDecryptable, KeyEncryptable, LocateKey, SymmetricCryptoKey,
};

#[cfg(feature = "wasm")]
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_CUSTOM_TYPES: &'static str = r#"
export type EncString = string;
"#;

/// # Encrypted string primitive
///
/// [EncString] is a Bitwarden specific primitive that represents a symmetrically encrypted string.
Expand Down
3 changes: 3 additions & 0 deletions crates/bitwarden-crypto/src/keys/master_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use generic_array::{typenum::U32, GenericArray};
use rand::Rng;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[cfg(feature = "wasm")]
use {tsify_next::Tsify, wasm_bindgen::prelude::*};

use super::utils::{derive_kdf_key, stretch_kdf_key};
use crate::{util, CryptoError, EncString, KeyDecryptable, Result, SymmetricCryptoKey, UserKey};
Expand All @@ -16,6 +18,7 @@ use crate::{util, CryptoError, EncString, KeyDecryptable, Result, SymmetricCrypt
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub enum Kdf {
PBKDF2 {
iterations: NonZeroU32,
Expand Down
3 changes: 3 additions & 0 deletions crates/bitwarden-vault/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ uniffi = [
"bitwarden-crypto/uniffi",
"dep:uniffi",
] # Uniffi bindings
wasm = ["dep:tsify-next", "dep:wasm-bindgen"] # WASM support

[dependencies]
base64 = ">=0.22.1, <0.23"
Expand All @@ -38,6 +39,8 @@ sha2 = ">=0.10.6, <0.11"
thiserror = { workspace = true }
uniffi = { version = "=0.28.1", optional = true }
uuid = { workspace = true }
tsify-next = { workspace = true, optional = true }
wasm-bindgen = { workspace = true, optional = true }

[dev-dependencies]
tokio = { workspace = true, features = ["rt"] }
Expand Down
4 changes: 4 additions & 0 deletions crates/bitwarden-vault/src/folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ use chrono::{DateTime, Utc};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[cfg(feature = "wasm")]
use {tsify_next::Tsify, wasm_bindgen::prelude::*};

use crate::VaultParseError;

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct Folder {
id: Option<Uuid>,
name: EncString,
Expand All @@ -22,6 +25,7 @@ pub struct Folder {
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
pub struct FolderView {
pub id: Option<Uuid>,
pub name: String,
Expand Down
2 changes: 2 additions & 0 deletions crates/bitwarden-wasm-internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ crate-type = ["cdylib"]

[dependencies]
bitwarden-core = { workspace = true, features = ["wasm", "internal"] }
bitwarden-crypto = { workspace = true, features = ["wasm"] }
bitwarden-vault = { workspace = true, features = ["wasm"] }
console_error_panic_hook = "0.1.7"
console_log = { version = "1.0.0", features = ["color"] }
js-sys = "0.3.68"
Expand Down
12 changes: 11 additions & 1 deletion crates/bitwarden-wasm-internal/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use bitwarden_core::{Client, ClientSettings};
use log::{set_max_level, Level};
use wasm_bindgen::prelude::*;

use crate::{vault::ClientVault, ClientCrypto};

#[wasm_bindgen]
pub enum LogLevel {
Trace,
Expand All @@ -27,7 +29,7 @@ fn convert_level(level: LogLevel) -> Level {
// Rc<...> is to avoid needing to take ownership of the Client during our async run_command
// function https://github.com/rustwasm/wasm-bindgen/issues/2195#issuecomment-799588401
#[wasm_bindgen]
pub struct BitwardenClient(Rc<Client>);
pub struct BitwardenClient(pub(crate) Rc<Client>);

#[wasm_bindgen]
impl BitwardenClient {
Expand All @@ -54,4 +56,12 @@ impl BitwardenClient {

res.text().await.map_err(|e| e.to_string())
}

pub fn crypto(&self) -> ClientCrypto {
ClientCrypto::new(self.0.clone())
}

pub fn vault(&self) -> ClientVault {
ClientVault::new(self.0.clone())
}
}
33 changes: 33 additions & 0 deletions crates/bitwarden-wasm-internal/src/crypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::rc::Rc;

use bitwarden_core::{
mobile::crypto::{InitOrgCryptoRequest, InitUserCryptoRequest},
Client,
};
use wasm_bindgen::prelude::*;

use crate::error::Result;

#[wasm_bindgen]
pub struct ClientCrypto(Rc<Client>);

impl ClientCrypto {
pub fn new(client: Rc<Client>) -> Self {
Self(client)
}
}

#[wasm_bindgen]
impl ClientCrypto {
/// Initialization method for the user crypto. Needs to be called before any other crypto
/// operations.
pub async fn initialize_user_crypto(&self, req: InitUserCryptoRequest) -> Result<()> {
Ok(self.0.crypto().initialize_user_crypto(req).await?)
}

/// Initialization method for the organization crypto. Needs to be called after
/// `initialize_user_crypto` but before any other crypto operations.
pub async fn initialize_org_crypto(&self, req: InitOrgCryptoRequest) -> Result<()> {
Ok(self.0.crypto().initialize_org_crypto(req).await?)
}
}
22 changes: 22 additions & 0 deletions crates/bitwarden-wasm-internal/src/custom_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// This file contains custom TypeScript for types defined by external crates.
/// Everything in the string below is appended to the generated TypeScript definition file.
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_CUSTOM_TYPES: &'static str = r#"
export type Uuid = string;
/**
* RFC3339 compliant date-time string.
* @typeParam T - Not used in JavaScript.
*/
export type DateTime<T = unknown> = string;
/**
* UTC date-time string. Not used in JavaScript.
*/
export type Utc = unknown;
/**
* An integer that is known not to equal zero.
*/
export type NonZeroU32 = number;
"#;
13 changes: 13 additions & 0 deletions crates/bitwarden-wasm-internal/src/error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Error thrown by the WASM module.
* @param {string} message - Error message.
* @extends Error
*/
class WasmError extends Error {
constructor(message) {
super(message);
this.name = "WasmError";
}
}

exports.WasmError = WasmError;
27 changes: 27 additions & 0 deletions crates/bitwarden-wasm-internal/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use wasm_bindgen::prelude::*;

// Importing an error class defined in JavaScript instead of defining it in Rust
// allows us to extend the `Error` class. It also provides much better console output.
#[wasm_bindgen(module = "/src/error.js")]
extern "C" {
type WasmError;

#[wasm_bindgen(constructor)]
fn new(message: String) -> WasmError;
}

pub type Result<T, E = GenericError> = std::result::Result<T, E>;

pub struct GenericError(pub String);

impl<T: ToString> From<T> for GenericError {
fn from(error: T) -> Self {
GenericError(error.to_string())
}
}

impl From<GenericError> for JsValue {
fn from(error: GenericError) -> Self {
WasmError::new(error.0).into()
}
}
8 changes: 8 additions & 0 deletions crates/bitwarden-wasm-internal/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
mod client;
mod crypto;
mod custom_types;
mod error;
mod vault;

pub use client::BitwardenClient;
pub use crypto::ClientCrypto;
pub use vault::{folders::ClientFolders, ClientVault};
24 changes: 24 additions & 0 deletions crates/bitwarden-wasm-internal/src/vault/folders.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use std::rc::Rc;

use bitwarden_core::Client;
use bitwarden_vault::{ClientVaultExt, Folder, FolderView};
use wasm_bindgen::prelude::*;

use crate::error::Result;

#[wasm_bindgen]
pub struct ClientFolders(Rc<Client>);

impl ClientFolders {
pub fn new(client: Rc<Client>) -> Self {
Self(client)
}
}

#[wasm_bindgen]
impl ClientFolders {
/// Decrypt folder
pub fn decrypt(&self, folder: Folder) -> Result<FolderView> {
Ok(self.0.vault().folders().decrypt(folder)?)
}
}
24 changes: 24 additions & 0 deletions crates/bitwarden-wasm-internal/src/vault/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
pub mod folders;

use std::rc::Rc;

use bitwarden_core::Client;
use wasm_bindgen::prelude::*;

use crate::ClientFolders;

#[wasm_bindgen]
pub struct ClientVault(Rc<Client>);

impl ClientVault {
pub fn new(client: Rc<Client>) -> Self {
Self(client)
}
}

#[wasm_bindgen]
impl ClientVault {
pub fn folders(&self) -> ClientFolders {
ClientFolders::new(self.0.clone())
}
}
1 change: 1 addition & 0 deletions languages/js/sdk-internal/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
**/snippets/**/*.js
bitwarden_wasm_internal_bg.js
bitwarden_wasm_internal_bg.wasm
bitwarden_wasm_internal_bg.wasm.d.ts
Expand Down

0 comments on commit a0f4530

Please sign in to comment.