Skip to content

Commit 1a31a1f

Browse files
authored
Add-userid-to-encryption-methods (#278)
This is not the owning entity of the key, but the user that is unlocked that triggered the request ## 🎟️ Tracking <!-- Paste the link to the Jira or GitHub issue or otherwise describe / point to where this change is coming from. --> ## 📔 Objective <!-- Describe what the purpose of this PR is, for example what bug you're fixing or new feature you're adding. --> ## ⏰ 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
1 parent c0f7f72 commit 1a31a1f

File tree

20 files changed

+115
-21
lines changed

20 files changed

+115
-21
lines changed

crates/bitwarden-core/src/auth/auth_request.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ mod tests {
243243
new_device
244244
.crypto()
245245
.initialize_user_crypto(InitUserCryptoRequest {
246+
user_id: Some(uuid::Uuid::new_v4()),
246247
kdf_params: kdf,
247248
email: email.to_owned(),
248249
private_key: private_key.to_owned(),

crates/bitwarden-core/src/auth/login/auth_request.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ pub(crate) async fn complete_auth_request(
115115
client
116116
.crypto()
117117
.initialize_user_crypto(InitUserCryptoRequest {
118+
user_id: None,
118119
kdf_params: kdf,
119120
email: auth_req.email,
120121
private_key: require!(r.private_key),

crates/bitwarden-core/src/client/client.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::sync::{Arc, RwLock};
1+
use std::sync::{Arc, OnceLock, RwLock};
22

33
use bitwarden_crypto::KeyStore;
44
use reqwest::header::{self, HeaderValue};
@@ -75,6 +75,7 @@ impl Client {
7575

7676
Self {
7777
internal: Arc::new(InternalClient {
78+
user_id: OnceLock::new(),
7879
tokens: RwLock::new(Tokens::default()),
7980
login_method: RwLock::new(None),
8081
#[cfg(feature = "internal")]

crates/bitwarden-core/src/client/encryption_settings.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use thiserror::Error;
66
use uuid::Uuid;
77

88
use crate::{
9+
error::UserIdAlreadySetError,
910
key_management::{AsymmetricKeyId, KeyIds, SymmetricKeyId},
1011
MissingPrivateKeyError, VaultLockedError,
1112
};
@@ -27,6 +28,9 @@ pub enum EncryptionSettingsError {
2728

2829
#[error(transparent)]
2930
MissingPrivateKey(#[from] MissingPrivateKeyError),
31+
32+
#[error(transparent)]
33+
UserIdAlreadySetError(#[from] UserIdAlreadySetError),
3034
}
3135

3236
pub struct EncryptionSettings {}

crates/bitwarden-core/src/client/internal.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::sync::{Arc, RwLock};
1+
use std::sync::{Arc, OnceLock, RwLock};
22

33
use bitwarden_crypto::KeyStore;
44
#[cfg(any(feature = "internal", feature = "secrets"))]
@@ -13,6 +13,7 @@ use super::login_method::ServiceAccountLoginMethod;
1313
use crate::{
1414
auth::renew::renew_token,
1515
client::{encryption_settings::EncryptionSettings, login_method::LoginMethod},
16+
error::UserIdAlreadySetError,
1617
key_management::KeyIds,
1718
DeviceType,
1819
};
@@ -44,6 +45,7 @@ pub(crate) struct Tokens {
4445

4546
#[derive(Debug)]
4647
pub struct InternalClient {
48+
pub(crate) user_id: OnceLock<Uuid>,
4749
pub(crate) tokens: RwLock<Tokens>,
4850
pub(crate) login_method: RwLock<Option<Arc<LoginMethod>>>,
4951

@@ -172,6 +174,14 @@ impl InternalClient {
172174
&self.key_store
173175
}
174176

177+
pub fn init_user_id(&self, user_id: Uuid) -> Result<(), UserIdAlreadySetError> {
178+
self.user_id.set(user_id).map_err(|_| UserIdAlreadySetError)
179+
}
180+
181+
pub fn get_user_id(&self) -> Option<Uuid> {
182+
self.user_id.get().copied()
183+
}
184+
175185
#[cfg(feature = "internal")]
176186
pub(crate) fn initialize_user_crypto_master_key(
177187
&self,

crates/bitwarden-core/src/client/test_accounts.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ pub struct TestAccount {
117117
pub fn test_bitwarden_com_account() -> TestAccount {
118118
TestAccount {
119119
user: InitUserCryptoRequest {
120+
user_id: Some(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8")),
120121
kdf_params: Kdf::PBKDF2 {
121122
iterations: 600_000.try_into().unwrap(),
122123
},
@@ -174,6 +175,7 @@ pub fn test_bitwarden_com_account() -> TestAccount {
174175
pub fn test_legacy_user_key_account() -> TestAccount {
175176
TestAccount {
176177
user: InitUserCryptoRequest {
178+
user_id: Some(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8")),
177179
kdf_params: Kdf::PBKDF2 {
178180
iterations: 600_000.try_into().unwrap(),
179181
},

crates/bitwarden-core/src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ impl_bitwarden_error!(IdentityError, ApiError);
4848
#[error("The client is not authenticated or the session has expired")]
4949
pub struct NotAuthenticatedError;
5050

51+
/// Client's user ID is already set.
52+
#[derive(Debug, Error)]
53+
#[error("The client user ID is already set")]
54+
pub struct UserIdAlreadySetError;
55+
5156
/// Missing required field.
5257
#[derive(Debug, Error)]
5358
#[error("The response received was missing a required field: {0}")]

crates/bitwarden-core/src/mobile/crypto.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub enum MobileCryptoError {
3939
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
4040
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
4141
pub struct InitUserCryptoRequest {
42+
pub user_id: Option<uuid::Uuid>,
4243
/// The user's KDF parameters, as received from the prelogin request
4344
pub kdf_params: Kdf,
4445
/// The user's email address
@@ -131,6 +132,10 @@ pub async fn initialize_user_crypto(
131132

132133
let private_key: EncString = req.private_key.parse()?;
133134

135+
if let Some(user_id) = req.user_id {
136+
client.internal.init_user_id(user_id)?;
137+
}
138+
134139
match req.method {
135140
InitUserCryptoMethod::Password { password, user_key } => {
136141
let user_key: EncString = user_key.parse()?;
@@ -564,6 +569,7 @@ mod tests {
564569
initialize_user_crypto(
565570
& client,
566571
InitUserCryptoRequest {
572+
user_id: Some(uuid::Uuid::new_v4()),
567573
kdf_params: kdf.clone(),
568574
email: "[email protected]".into(),
569575
private_key: priv_key.to_owned(),
@@ -583,6 +589,7 @@ mod tests {
583589
initialize_user_crypto(
584590
&client2,
585591
InitUserCryptoRequest {
592+
user_id: Some(uuid::Uuid::new_v4()),
586593
kdf_params: kdf.clone(),
587594
email: "[email protected]".into(),
588595
private_key: priv_key.to_owned(),
@@ -638,6 +645,7 @@ mod tests {
638645
initialize_user_crypto(
639646
& client,
640647
InitUserCryptoRequest {
648+
user_id: Some(uuid::Uuid::new_v4()),
641649
kdf_params: Kdf::PBKDF2 {
642650
iterations: 100_000.try_into().unwrap(),
643651
},
@@ -659,6 +667,7 @@ mod tests {
659667
initialize_user_crypto(
660668
&client2,
661669
InitUserCryptoRequest {
670+
user_id: Some(uuid::Uuid::new_v4()),
662671
kdf_params: Kdf::PBKDF2 {
663672
iterations: 100_000.try_into().unwrap(),
664673
},
@@ -701,6 +710,7 @@ mod tests {
701710
initialize_user_crypto(
702711
&client3,
703712
InitUserCryptoRequest {
713+
user_id: Some(uuid::Uuid::new_v4()),
704714
kdf_params: Kdf::PBKDF2 {
705715
iterations: 100_000.try_into().unwrap(),
706716
},

crates/bitwarden-core/tests/register.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ async fn test_register_initialize_crypto() {
2929
client
3030
.crypto()
3131
.initialize_user_crypto(InitUserCryptoRequest {
32+
user_id: Some(uuid::Uuid::new_v4()),
3233
kdf_params: kdf,
3334
email: email.to_owned(),
3435
private_key: register_response.keys.private.to_string(),

crates/bitwarden-fido/src/authenticator.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::sync::Mutex;
22

33
use bitwarden_core::{Client, VaultLockedError};
44
use bitwarden_crypto::CryptoError;
5-
use bitwarden_vault::{CipherError, CipherView};
5+
use bitwarden_vault::{CipherError, CipherView, EncryptionContext};
66
use itertools::Itertools;
77
use log::error;
88
use passkey::{
@@ -431,6 +431,8 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> {
431431
) -> Result<(), StatusCode> {
432432
#[derive(Debug, Error)]
433433
enum InnerError {
434+
#[error("Client User Id has not been set")]
435+
MissingUserId,
434436
#[error(transparent)]
435437
VaultLocked(#[from] VaultLockedError),
436438
#[error(transparent)]
@@ -454,6 +456,12 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> {
454456
rp: passkey::types::ctap2::make_credential::PublicKeyCredentialRpEntity,
455457
options: passkey::types::ctap2::get_assertion::Options,
456458
) -> Result<(), InnerError> {
459+
let user_id = this
460+
.authenticator
461+
.client
462+
.internal
463+
.get_user_id()
464+
.ok_or(InnerError::MissingUserId)?;
457465
let cred = try_from_credential_full(cred, user, rp, options)?;
458466

459467
// Get the previously selected cipher and add the new credential to it
@@ -481,7 +489,10 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> {
481489

482490
this.authenticator
483491
.credential_store
484-
.save_credential(encrypted)
492+
.save_credential(EncryptionContext {
493+
cipher: encrypted,
494+
encrypted_for: user_id,
495+
})
485496
.await?;
486497

487498
Ok(())
@@ -498,6 +509,8 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> {
498509
async fn update_credential(&mut self, cred: Passkey) -> Result<(), StatusCode> {
499510
#[derive(Debug, Error)]
500511
enum InnerError {
512+
#[error("Client User Id has not been set")]
513+
MissingUserId,
501514
#[error(transparent)]
502515
VaultLocked(#[from] VaultLockedError),
503516
#[error(transparent)]
@@ -521,6 +534,12 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> {
521534
this: &mut CredentialStoreImpl<'_>,
522535
cred: Passkey,
523536
) -> Result<(), InnerError> {
537+
let user_id = this
538+
.authenticator
539+
.client
540+
.internal
541+
.get_user_id()
542+
.ok_or(InnerError::MissingUserId)?;
524543
// Get the previously selected cipher and update the credential
525544
let selected = this.authenticator.get_selected_credential()?;
526545

@@ -550,7 +569,10 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> {
550569

551570
this.authenticator
552571
.credential_store
553-
.save_credential(encrypted)
572+
.save_credential(EncryptionContext {
573+
cipher: encrypted,
574+
encrypted_for: user_id,
575+
})
554576
.await?;
555577

556578
Ok(())

crates/bitwarden-fido/src/traits.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use bitwarden_vault::{Cipher, CipherListView, CipherView, Fido2CredentialNewView};
1+
use bitwarden_vault::{CipherListView, CipherView, EncryptionContext, Fido2CredentialNewView};
22
use passkey::authenticator::UIHint;
33
use thiserror::Error;
44

@@ -43,7 +43,7 @@ pub trait Fido2CredentialStore: Send + Sync {
4343

4444
async fn all_credentials(&self) -> Result<Vec<CipherListView>, Fido2CallbackError>;
4545

46-
async fn save_credential(&self, cred: Cipher) -> Result<(), Fido2CallbackError>;
46+
async fn save_credential(&self, cred: EncryptionContext) -> Result<(), Fido2CallbackError>;
4747
}
4848

4949
#[derive(Clone)]

crates/bitwarden-uniffi/kotlin/app/src/main/java/com/bitwarden/myapplication/MainActivity.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ class MainActivity : FragmentActivity() {
250250

251251
client.crypto().initializeUserCrypto(
252252
InitUserCryptoRequest(
253+
userId = null,
253254
kdfParams = kdf,
254255
email = EMAIL,
255256
privateKey = loginBody.PrivateKey,
@@ -334,6 +335,7 @@ class MainActivity : FragmentActivity() {
334335
GlobalScope.launch {
335336
client.crypto().initializeUserCrypto(
336337
InitUserCryptoRequest(
338+
userId = null,
337339
kdfParams = kdf,
338340
email = EMAIL,
339341
privateKey = privateKey!!,
@@ -371,6 +373,7 @@ class MainActivity : FragmentActivity() {
371373
GlobalScope.launch {
372374
client.crypto().initializeUserCrypto(
373375
InitUserCryptoRequest(
376+
userId = null,
374377
kdfParams = kdf,
375378
email = EMAIL,
376379
privateKey = privateKey!!,

crates/bitwarden-uniffi/src/platform/fido2.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use bitwarden_fido::{
77
PublicKeyCredentialAuthenticatorAttestationResponse, PublicKeyCredentialRpEntity,
88
PublicKeyCredentialUserEntity,
99
};
10-
use bitwarden_vault::{Cipher, CipherListView, CipherView, Fido2CredentialNewView};
10+
use bitwarden_vault::{CipherListView, CipherView, EncryptionContext, Fido2CredentialNewView};
1111

1212
use crate::error::{Error, Result};
1313

@@ -245,7 +245,7 @@ pub trait Fido2CredentialStore: Send + Sync {
245245

246246
async fn all_credentials(&self) -> Result<Vec<CipherListView>, Fido2CallbackError>;
247247

248-
async fn save_credential(&self, cred: Cipher) -> Result<(), Fido2CallbackError>;
248+
async fn save_credential(&self, cred: EncryptionContext) -> Result<(), Fido2CallbackError>;
249249
}
250250

251251
// Because uniffi doesn't support external traits, we have to make a copy of the trait here.
@@ -271,7 +271,7 @@ impl bitwarden_fido::Fido2CredentialStore for UniffiTraitBridge<&dyn Fido2Creden
271271
self.0.all_credentials().await.map_err(Into::into)
272272
}
273273

274-
async fn save_credential(&self, cred: Cipher) -> Result<(), BitFido2CallbackError> {
274+
async fn save_credential(&self, cred: EncryptionContext) -> Result<(), BitFido2CallbackError> {
275275
self.0.save_credential(cred).await.map_err(Into::into)
276276
}
277277
}

crates/bitwarden-uniffi/src/vault/ciphers.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use bitwarden_vault::{Cipher, CipherListView, CipherView, Fido2CredentialView};
1+
use bitwarden_vault::{Cipher, CipherListView, CipherView, EncryptionContext, Fido2CredentialView};
22
use uuid::Uuid;
33

44
use crate::{error::Error, Result};
@@ -9,7 +9,7 @@ pub struct CiphersClient(pub(crate) bitwarden_vault::CiphersClient);
99
#[uniffi::export]
1010
impl CiphersClient {
1111
/// Encrypt cipher
12-
pub fn encrypt(&self, cipher_view: CipherView) -> Result<Cipher> {
12+
pub fn encrypt(&self, cipher_view: CipherView) -> Result<EncryptionContext> {
1313
Ok(self.0.encrypt(cipher_view).map_err(Error::Encrypt)?)
1414
}
1515

crates/bitwarden-uniffi/swift/iOS/App/ContentView.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ struct ContentView: View {
189189

190190
try await clientCrypto.initializeUserCrypto(
191191
req: InitUserCryptoRequest(
192+
userId: nil,
192193
kdfParams: kdf,
193194
email: EMAIL,
194195
privateKey: loginData.PrivateKey,
@@ -246,6 +247,7 @@ struct ContentView: View {
246247
let key = biometricRetrieveValue()!
247248

248249
try await clientCrypto.initializeUserCrypto(req: InitUserCryptoRequest(
250+
userId: nil,
249251
kdfParams: kdf,
250252
email: EMAIL,
251253
privateKey: privateKey,
@@ -272,6 +274,7 @@ struct ContentView: View {
272274
let pinProtectedUserKey = defaults.string(forKey: "pinProtectedUserKey")!
273275

274276
try await clientCrypto.initializeUserCrypto(req: InitUserCryptoRequest(
277+
userId: nil,
275278
kdfParams: kdf,
276279
email: EMAIL,
277280
privateKey: privateKey,
@@ -417,7 +420,7 @@ class Fido2CredentialStoreImpl: Fido2CredentialStore {
417420
abort()
418421
}
419422

420-
func saveCredential(cred: BitwardenSdk.Cipher) async throws {
423+
func saveCredential(cred: BitwardenSdk.EncryptionContext) async throws {
421424
print("SAVED CREDENTIAL")
422425
}
423426
}

crates/bitwarden-vault/src/cipher/cipher.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ pub enum CipherRepromptType {
6464
Password = 1,
6565
}
6666

67+
#[derive(Serialize, Deserialize, Debug, Clone)]
68+
#[serde(rename_all = "camelCase", deny_unknown_fields)]
69+
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
70+
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
71+
pub struct EncryptionContext {
72+
/// The Id of the user that encrypted the cipher. It should always represent a UserId, even for
73+
/// Organization-owned ciphers
74+
pub encrypted_for: Uuid,
75+
pub cipher: Cipher,
76+
}
77+
6778
#[derive(Serialize, Deserialize, Debug, Clone)]
6879
#[serde(rename_all = "camelCase", deny_unknown_fields)]
6980
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]

0 commit comments

Comments
 (0)