Skip to content
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

Split initializeCrypto #329

Merged
merged 3 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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 crates/bitwarden-uniffi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
```bash
cargo +nightly rustdoc -p bitwarden -- -Zunstable-options --output-format json
cargo +nightly rustdoc -p bitwarden-uniffi -- -Zunstable-options --output-format json
npm run schemas

npx ts-node ./support/docs/docs.ts > languages/kotlin/doc.md
```
35 changes: 35 additions & 0 deletions crates/bitwarden-uniffi/src/crypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use std::sync::Arc;

use bitwarden::mobile::crypto::{InitOrgCryptoRequest, InitUserCryptoRequest};

use crate::{error::Result, Client};

#[derive(uniffi::Object)]
pub struct ClientCrypto(pub(crate) Arc<Client>);

#[uniffi::export]
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
.0
.write()
.await
.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
.0
.write()
.await
.crypto()
.initialize_org_crypto(req)
.await?)
}
}
5 changes: 3 additions & 2 deletions crates/bitwarden-uniffi/src/docs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use bitwarden::{
auth::password::MasterPasswordPolicyOptions,
client::kdf::Kdf,
mobile::crypto::InitCryptoRequest,
mobile::crypto::{InitOrgCryptoRequest, InitUserCryptoRequest},
tool::{ExportFormat, PassphraseGeneratorRequest, PasswordGeneratorRequest},
vault::{
Cipher, CipherView, Collection, Folder, FolderView, Send, SendListView, SendView,
Expand All @@ -24,7 +24,8 @@ pub enum DocRef {
SendListView(SendListView),

// Crypto
InitCryptoRequest(InitCryptoRequest),
InitUserCryptoRequest(InitUserCryptoRequest),
InitOrgCryptoRequest(InitOrgCryptoRequest),

// Generators
PasswordGeneratorRequest(PasswordGeneratorRequest),
Expand Down
22 changes: 3 additions & 19 deletions crates/bitwarden-uniffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ use std::sync::Arc;

use async_lock::RwLock;
use auth::ClientAuth;
use bitwarden::{client::client_settings::ClientSettings, mobile::crypto::InitCryptoRequest};
use bitwarden::client::client_settings::ClientSettings;

pub mod auth;
pub mod crypto;
mod error;
pub mod tool;
mod uniffi_support;
Expand All @@ -15,16 +16,14 @@ pub mod vault;
#[cfg(feature = "docs")]
pub mod docs;

use crypto::ClientCrypto;
use error::Result;
use tool::ClientGenerators;
use vault::ClientVault;

#[derive(uniffi::Object)]
pub struct Client(RwLock<bitwarden::Client>);

#[derive(uniffi::Object)]
pub struct ClientCrypto(Arc<Client>);

#[uniffi::export]
impl Client {
/// Initialize a new instance of the SDK client
Expand Down Expand Up @@ -58,18 +57,3 @@ impl Client {
msg
}
}

#[uniffi::export]
impl ClientCrypto {
/// Initialization method for the crypto. Needs to be called before any other crypto operations.
pub async fn initialize_crypto(&self, req: InitCryptoRequest) -> Result<()> {
Ok(self
.0
.0
.write()
.await
.crypto()
.initialize_crypto(req)
.await?)
}
}
3 changes: 3 additions & 0 deletions crates/bitwarden/src/client/encryption_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ impl EncryptionSettings {

let private_key = self.private_key.as_ref().ok_or(Error::VaultLocked)?;

// Clear the previous keys, if there are any
Hinton marked this conversation as resolved.
Show resolved Hide resolved
self.org_keys.clear();

// Decrypt the org keys with the private key
for (org_id, org_enc_key) in org_enc_keys {
let data = match org_enc_key {
Expand Down
13 changes: 10 additions & 3 deletions crates/bitwarden/src/mobile/client_crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use crate::Client;
#[cfg(feature = "internal")]
use crate::{
error::Result,
mobile::crypto::{initialize_crypto, InitCryptoRequest},
mobile::crypto::{
initialize_org_crypto, initialize_user_crypto, InitOrgCryptoRequest, InitUserCryptoRequest,
},
};

pub struct ClientCrypto<'a> {
Expand All @@ -11,8 +13,13 @@ pub struct ClientCrypto<'a> {

impl<'a> ClientCrypto<'a> {
#[cfg(feature = "internal")]
pub async fn initialize_crypto(&mut self, req: InitCryptoRequest) -> Result<()> {
initialize_crypto(self.client, req).await
pub async fn initialize_user_crypto(&mut self, req: InitUserCryptoRequest) -> Result<()> {
initialize_user_crypto(self.client, req).await
}

#[cfg(feature = "internal")]
pub async fn initialize_org_crypto(&mut self, req: InitOrgCryptoRequest) -> Result<()> {
initialize_org_crypto(self.client, req).await
}
}

Expand Down
57 changes: 39 additions & 18 deletions crates/bitwarden/src/mobile/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,63 @@ use crate::{client::kdf::Kdf, crypto::EncString, error::Result, Client};
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "mobile", derive(uniffi::Record))]
pub struct InitCryptoRequest {
pub struct InitUserCryptoRequest {
/// The user's KDF parameters, as received from the prelogin request
pub kdf_params: Kdf,
/// The user's email address
pub email: String,
/// The user's master password
pub password: String,
/// The user's encrypted symmetric crypto key
pub user_key: String,
/// The user's encryptred private key
/// The user's encrypted private key
pub private_key: String,
/// The encryption keys for all the organizations the user is a part of
pub organization_keys: HashMap<uuid::Uuid, String>,
/// The initialization method to use
pub method: InitUserCryptoMethod,
}

#[cfg(feature = "internal")]
pub async fn initialize_crypto(client: &mut Client, req: InitCryptoRequest) -> Result<()> {
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "mobile", derive(uniffi::Enum))]
pub enum InitUserCryptoMethod {
Password {
/// The user's master password
password: String,
/// The user's encrypted symmetric crypto key
user_key: String,
},
Hinton marked this conversation as resolved.
Show resolved Hide resolved
}

#[cfg(feature = "internal")]
pub async fn initialize_user_crypto(client: &mut Client, req: InitUserCryptoRequest) -> Result<()> {
let login_method = crate::client::LoginMethod::User(crate::client::UserLoginMethod::Username {
client_id: "".to_string(),
email: req.email,
kdf: req.kdf_params,
});
client.set_login_method(login_method);

let user_key = req.user_key.parse::<EncString>()?;
let private_key = req.private_key.parse::<EncString>()?;
let private_key: EncString = req.private_key.parse()?;

client.initialize_user_crypto(&req.password, user_key, private_key)?;
match req.method {
InitUserCryptoMethod::Password { password, user_key } => {
let user_key: EncString = user_key.parse()?;
client.initialize_user_crypto(&password, user_key, private_key)?;
}
}

let organization_keys = req
.organization_keys
.into_iter()
.map(|(k, v)| Ok((k, v.parse::<EncString>()?)))
.collect::<Result<Vec<_>>>()?;
Ok(())
}

client.initialize_org_crypto(organization_keys)?;
#[cfg(feature = "internal")]
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "mobile", derive(uniffi::Record))]
pub struct InitOrgCryptoRequest {
/// The encryption keys for all the organizations the user is a part of
pub organization_keys: HashMap<uuid::Uuid, EncString>,
}

#[cfg(feature = "internal")]
pub async fn initialize_org_crypto(client: &mut Client, req: InitOrgCryptoRequest) -> Result<()> {
let organization_keys = req.organization_keys.into_iter().collect();
client.initialize_org_crypto(organization_keys)?;
Ok(())
}
16 changes: 11 additions & 5 deletions crates/bitwarden/tests/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
async fn test_register_initialize_crypto() {
use std::num::NonZeroU32;

use bitwarden::{client::kdf::Kdf, mobile::crypto::InitCryptoRequest, Client};
use bitwarden::{
client::kdf::Kdf,
mobile::crypto::{InitUserCryptoMethod, InitUserCryptoRequest},
Client,
};

let mut client = Client::new(None);

Expand All @@ -22,13 +26,15 @@ async fn test_register_initialize_crypto() {
// Ensure we can initialize the crypto with the new keys
client
.crypto()
.initialize_crypto(InitCryptoRequest {
.initialize_user_crypto(InitUserCryptoRequest {
kdf_params: kdf,
email: email.to_owned(),
password: password.to_owned(),
user_key: register_response.encrypted_user_key,
private_key: register_response.keys.private.to_string(),
organization_keys: Default::default(),

method: InitUserCryptoMethod::Password {
password: password.to_owned(),
user_key: register_response.encrypted_user_key,
},
})
.await
.unwrap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.bitwarden.core.DateTime
import com.bitwarden.core.Folder
import com.bitwarden.core.InitCryptoRequest
import com.bitwarden.core.InitOrgCryptoRequest
import com.bitwarden.core.InitUserCryptoMethod
import com.bitwarden.core.InitUserCryptoRequest
import com.bitwarden.core.Kdf
import com.bitwarden.core.Uuid
import com.bitwarden.myapplication.ui.theme.MyApplicationTheme
Expand Down Expand Up @@ -117,7 +119,7 @@ class MainActivity : ComponentActivity() {
}.body<JsonObject>()

val folders = (syncBody["folders"] as JsonArray).map {
val o = it as JsonObject;
val o = it as JsonObject
Folder(
(o["id"] as JsonPrimitive).content,
(o["name"] as JsonPrimitive).content,
Expand All @@ -136,13 +138,20 @@ class MainActivity : ComponentActivity() {
orgKeys[(o["id"] as JsonPrimitive).content] = (o["key"] as JsonPrimitive).content
}

client.crypto().initializeCrypto(
InitCryptoRequest(
client.crypto().initializeUserCrypto(
InitUserCryptoRequest(
kdfParams = kdf,
email = EMAIL,
password = PASSWORD,
userKey = loginBody.Key,
privateKey = loginBody.PrivateKey,
method = InitUserCryptoMethod.Password(
password = PASSWORD,
userKey = loginBody.Key
)
)
)

client.crypto().initializeOrgCrypto(
InitOrgCryptoRequest(
organizationKeys = orgKeys
)
)
Expand Down
Loading