Skip to content

Commit 0364659

Browse files
authored
[PM-26534] Remove non-generic wrapper for PasswordProtectedKeyEnvelope (#488)
## 🎟️ Tracking https://bitwarden.atlassian.net/browse/PM-26534 ## 📔 Objective The initial implementation assumed that the struct of of the PasswordProtectedKeyEnvelope in crypto had to be generic, because the key context is generic w.r.t. the keys used, and the keys are only then provided by core. Because uniffy / wasm-bindgen can't handle generics, I added a non-generic wrapper struct in core, leaving us with two different "PasswordProtectedKeyEnvelopes". This understanding was not correct. You don't actually need to make the struct generic, having just two specific functions on the impl generic to the key id's suffices, allowing us to drop the "non generic wrapper" entirely, vastly simplifing our code here. The changes are mostly removing code and moving the uniffi mapping to the crypto crate. ## ⏰ 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 ac71502 commit 0364659

File tree

9 files changed

+73
-95
lines changed

9 files changed

+73
-95
lines changed

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use bitwarden_crypto::KeyStore;
44
#[cfg(any(feature = "internal", feature = "secrets"))]
55
use bitwarden_crypto::SymmetricCryptoKey;
66
#[cfg(feature = "internal")]
7-
use bitwarden_crypto::{CryptoError, EncString, Kdf, MasterKey, PinKey, UnsignedSharedKey};
7+
use bitwarden_crypto::{
8+
CryptoError, EncString, Kdf, MasterKey, PinKey, UnsignedSharedKey,
9+
safe::PasswordProtectedKeyEnvelope,
10+
};
811
#[cfg(feature = "internal")]
912
use bitwarden_state::registry::StateRegistry;
1013
use chrono::Utc;
@@ -26,8 +29,7 @@ use crate::{
2629
},
2730
error::NotAuthenticatedError,
2831
key_management::{
29-
MasterPasswordUnlockData, PasswordProtectedKeyEnvelope, SecurityState, SignedSecurityState,
30-
crypto::InitUserCryptoRequest,
32+
MasterPasswordUnlockData, SecurityState, SignedSecurityState, crypto::InitUserCryptoRequest,
3133
},
3234
};
3335

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

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use bitwarden_crypto::{
1010
AsymmetricCryptoKey, CoseSerializable, CryptoError, EncString, Kdf, KeyDecryptable,
1111
KeyEncryptable, MasterKey, Pkcs8PrivateKeyBytes, PrimitiveEncryptable, SignatureAlgorithm,
1212
SignedPublicKey, SigningKey, SpkiPublicKeyBytes, SymmetricCryptoKey, UnsignedSharedKey,
13-
UserKey, dangerous_get_v2_rotated_account_keys, safe::PasswordProtectedKeyEnvelopeError,
13+
UserKey, dangerous_get_v2_rotated_account_keys,
14+
safe::{PasswordProtectedKeyEnvelope, PasswordProtectedKeyEnvelopeError},
1415
};
1516
use bitwarden_encoding::B64;
1617
use bitwarden_error::bitwarden_error;
@@ -26,7 +27,6 @@ use crate::{
2627
key_management::{
2728
AsymmetricKeyId, SecurityState, SignedSecurityState, SigningKeyId, SymmetricKeyId,
2829
master_password::{MasterPasswordAuthenticationData, MasterPasswordUnlockData},
29-
non_generic_wrappers::PasswordProtectedKeyEnvelope,
3030
},
3131
};
3232

@@ -429,12 +429,7 @@ pub(super) fn enroll_pin(
429429
let key_store = client.internal.get_key_store();
430430
let mut ctx = key_store.context_mut();
431431

432-
let key_envelope =
433-
PasswordProtectedKeyEnvelope(bitwarden_crypto::safe::PasswordProtectedKeyEnvelope::seal(
434-
SymmetricKeyId::User,
435-
&pin,
436-
&ctx,
437-
)?);
432+
let key_envelope = PasswordProtectedKeyEnvelope::seal(SymmetricKeyId::User, &pin, &ctx)?;
438433
let encrypted_pin = pin.encrypt(&mut ctx, SymmetricKeyId::User)?;
439434
Ok(EnrollPinResponse {
440435
pin_protected_user_key_envelope: key_envelope,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#[cfg(feature = "wasm")]
2+
use bitwarden_crypto::safe::PasswordProtectedKeyEnvelope;
13
use bitwarden_crypto::{CryptoError, Decryptable, Kdf};
24
#[cfg(feature = "internal")]
35
use bitwarden_crypto::{EncString, UnsignedSharedKey};
@@ -10,8 +12,6 @@ use super::crypto::{
1012
MakeKeyPairResponse, VerifyAsymmetricKeysRequest, VerifyAsymmetricKeysResponse,
1113
derive_key_connector, make_key_pair, verify_asymmetric_keys,
1214
};
13-
#[cfg(any(feature = "wasm", test))]
14-
use crate::key_management::PasswordProtectedKeyEnvelope;
1515
#[cfg(feature = "internal")]
1616
use crate::key_management::{
1717
SymmetricKeyId,

crates/bitwarden-core/src/key_management/mod.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ pub use master_password::MasterPasswordError;
2626
#[cfg(feature = "internal")]
2727
pub(crate) use master_password::{MasterPasswordAuthenticationData, MasterPasswordUnlockData};
2828
#[cfg(feature = "internal")]
29-
mod non_generic_wrappers;
30-
#[cfg(feature = "internal")]
31-
pub(crate) use non_generic_wrappers::*;
32-
#[cfg(feature = "internal")]
3329
mod security_state;
3430
#[cfg(feature = "internal")]
3531
pub use security_state::{SecurityState, SignedSecurityState};

crates/bitwarden-core/src/key_management/non_generic_wrappers.rs

Lines changed: 0 additions & 36 deletions
This file was deleted.

crates/bitwarden-core/src/uniffi_support.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
33
use std::{num::NonZeroU32, str::FromStr};
44

5+
use bitwarden_crypto::safe;
56
use bitwarden_uniffi_error::convert_result;
67
use uuid::Uuid;
78

8-
use crate::key_management::{PasswordProtectedKeyEnvelope, SignedSecurityState};
9+
use crate::key_management::SignedSecurityState;
910

1011
uniffi::use_remote_type!(bitwarden_crypto::NonZeroU32);
12+
uniffi::use_remote_type!(bitwarden_crypto::safe::PasswordProtectedKeyEnvelope);
1113

1214
type DateTime = chrono::DateTime<chrono::Utc>;
1315
uniffi::custom_type!(DateTime, std::time::SystemTime, { remote });
@@ -33,10 +35,3 @@ uniffi::custom_type!(SignedSecurityState, String, {
3335
},
3436
lower: |obj| obj.into(),
3537
});
36-
37-
uniffi::custom_type!(PasswordProtectedKeyEnvelope, String, {
38-
remote,
39-
try_lift: |val| convert_result(bitwarden_crypto::safe::PasswordProtectedKeyEnvelope::from_str(&val)
40-
.map(PasswordProtectedKeyEnvelope)),
41-
lower: |obj| obj.0.into(),
42-
});

crates/bitwarden-crypto/examples/protect_key_with_password.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,11 @@ fn main() {
3434
ctx.clear_local();
3535

3636
// Load the envelope from disk and unseal it with the PIN, and store it in the context.
37-
let deserialized: PasswordProtectedKeyEnvelope<ExampleIds> =
38-
PasswordProtectedKeyEnvelope::try_from(
39-
disk.load("vault_key_envelope")
40-
.expect("Loading from disk should work"),
41-
)
42-
.expect("Deserializing envelope should work");
37+
let deserialized: PasswordProtectedKeyEnvelope = PasswordProtectedKeyEnvelope::try_from(
38+
disk.load("vault_key_envelope")
39+
.expect("Loading from disk should work"),
40+
)
41+
.expect("Deserializing envelope should work");
4342
deserialized
4443
.unseal(pin, &mut ctx)
4544
.expect("Unsealing should work");

crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
//! single recipient's unprotected headers. The output from the KDF - "envelope key", is used to
1414
//! wrap the symmetric key, that is sealed by the envelope.
1515
16-
use std::{marker::PhantomData, num::TryFromIntError, str::FromStr};
16+
use std::{num::TryFromIntError, str::FromStr};
1717

1818
use argon2::Params;
1919
use bitwarden_encoding::{B64, FromStrVisitor};
@@ -22,6 +22,8 @@ use coset::{CborSerializable, CoseError, Header, HeaderBuilder};
2222
use rand::RngCore;
2323
use serde::{Deserialize, Serialize};
2424
use thiserror::Error;
25+
#[cfg(feature = "wasm")]
26+
use wasm_bindgen::convert::FromWasmAbi;
2527

2628
use crate::{
2729
BitwardenLegacyKeyBytes, ContentFormat, CoseKeyBytes, EncodedSymmetricKey, KeyIds,
@@ -47,17 +49,16 @@ const ENVELOPE_ARGON2_OUTPUT_KEY_SIZE: usize = 32;
4749
/// be provided.
4850
///
4951
/// Internally, Argon2 is used as the KDF and XChaCha20-Poly1305 is used to encrypt the key.
50-
pub struct PasswordProtectedKeyEnvelope<Ids: KeyIds> {
51-
_phantom: PhantomData<Ids>,
52+
pub struct PasswordProtectedKeyEnvelope {
5253
cose_encrypt: coset::CoseEncrypt,
5354
}
5455

55-
impl<Ids: KeyIds> PasswordProtectedKeyEnvelope<Ids> {
56+
impl PasswordProtectedKeyEnvelope {
5657
/// Seals a symmetric key with a password, using the current default KDF parameters and a random
5758
/// salt.
5859
///
5960
/// This should never fail, except for memory allocation error, when running the KDF.
60-
pub fn seal(
61+
pub fn seal<Ids: KeyIds>(
6162
key_to_seal: Ids::Symmetric,
6263
password: &str,
6364
ctx: &KeyStoreContext<Ids>,
@@ -129,15 +130,12 @@ impl<Ids: KeyIds> PasswordProtectedKeyEnvelope<Ids> {
129130
.build();
130131
cose_encrypt.unprotected.iv = nonce.into();
131132

132-
Ok(PasswordProtectedKeyEnvelope {
133-
_phantom: PhantomData,
134-
cose_encrypt,
135-
})
133+
Ok(PasswordProtectedKeyEnvelope { cose_encrypt })
136134
}
137135

138136
/// Unseals a symmetric key from the password-protected envelope, and stores it in the key store
139137
/// context.
140-
pub fn unseal(
138+
pub fn unseal<Ids: KeyIds>(
141139
&self,
142140
password: &str,
143141
ctx: &mut KeyStoreContext<Ids>,
@@ -225,36 +223,33 @@ impl<Ids: KeyIds> PasswordProtectedKeyEnvelope<Ids> {
225223
}
226224
}
227225

228-
impl<Ids: KeyIds> From<&PasswordProtectedKeyEnvelope<Ids>> for Vec<u8> {
229-
fn from(val: &PasswordProtectedKeyEnvelope<Ids>) -> Self {
226+
impl From<&PasswordProtectedKeyEnvelope> for Vec<u8> {
227+
fn from(val: &PasswordProtectedKeyEnvelope) -> Self {
230228
val.cose_encrypt
231229
.clone()
232230
.to_vec()
233231
.expect("Serialization to cose should not fail")
234232
}
235233
}
236234

237-
impl<Ids: KeyIds> TryFrom<&Vec<u8>> for PasswordProtectedKeyEnvelope<Ids> {
235+
impl TryFrom<&Vec<u8>> for PasswordProtectedKeyEnvelope {
238236
type Error = CoseError;
239237

240238
fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
241239
let cose_encrypt = coset::CoseEncrypt::from_slice(value)?;
242-
Ok(PasswordProtectedKeyEnvelope {
243-
_phantom: PhantomData,
244-
cose_encrypt,
245-
})
240+
Ok(PasswordProtectedKeyEnvelope { cose_encrypt })
246241
}
247242
}
248243

249-
impl<Ids: KeyIds> std::fmt::Debug for PasswordProtectedKeyEnvelope<Ids> {
244+
impl std::fmt::Debug for PasswordProtectedKeyEnvelope {
250245
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251246
f.debug_struct("PasswordProtectedKeyEnvelope")
252247
.field("cose_encrypt", &self.cose_encrypt)
253248
.finish()
254249
}
255250
}
256251

257-
impl<Ids: KeyIds> FromStr for PasswordProtectedKeyEnvelope<Ids> {
252+
impl FromStr for PasswordProtectedKeyEnvelope {
258253
type Err = PasswordProtectedKeyEnvelopeError;
259254

260255
fn from_str(s: &str) -> Result<Self, Self::Err> {
@@ -271,14 +266,14 @@ impl<Ids: KeyIds> FromStr for PasswordProtectedKeyEnvelope<Ids> {
271266
}
272267
}
273268

274-
impl<Ids: KeyIds> From<PasswordProtectedKeyEnvelope<Ids>> for String {
275-
fn from(val: PasswordProtectedKeyEnvelope<Ids>) -> Self {
269+
impl From<PasswordProtectedKeyEnvelope> for String {
270+
fn from(val: PasswordProtectedKeyEnvelope) -> Self {
276271
let serialized: Vec<u8> = (&val).into();
277272
B64::from(serialized).to_string()
278273
}
279274
}
280275

281-
impl<'de, Ids: KeyIds> Deserialize<'de> for PasswordProtectedKeyEnvelope<Ids> {
276+
impl<'de> Deserialize<'de> for PasswordProtectedKeyEnvelope {
282277
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
283278
where
284279
D: serde::Deserializer<'de>,
@@ -287,7 +282,7 @@ impl<'de, Ids: KeyIds> Deserialize<'de> for PasswordProtectedKeyEnvelope<Ids> {
287282
}
288283
}
289284

290-
impl<Ids: KeyIds> Serialize for PasswordProtectedKeyEnvelope<Ids> {
285+
impl Serialize for PasswordProtectedKeyEnvelope {
291286
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
292287
where
293288
S: serde::Serializer,
@@ -443,6 +438,30 @@ impl From<TryFromIntError> for PasswordProtectedKeyEnvelopeError {
443438
}
444439
}
445440

441+
#[cfg(feature = "wasm")]
442+
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
443+
const TS_CUSTOM_TYPES: &'static str = r#"
444+
export type PasswordProtectedKeyEnvelope = Tagged<string, "PasswordProtectedKeyEnvelope">;
445+
"#;
446+
447+
#[cfg(feature = "wasm")]
448+
impl wasm_bindgen::describe::WasmDescribe for PasswordProtectedKeyEnvelope {
449+
fn describe() {
450+
<String as wasm_bindgen::describe::WasmDescribe>::describe();
451+
}
452+
}
453+
454+
#[cfg(feature = "wasm")]
455+
impl FromWasmAbi for PasswordProtectedKeyEnvelope {
456+
type Abi = <String as FromWasmAbi>::Abi;
457+
458+
unsafe fn from_abi(abi: Self::Abi) -> Self {
459+
use wasm_bindgen::UnwrapThrowExt;
460+
let string = unsafe { String::from_abi(abi) };
461+
PasswordProtectedKeyEnvelope::from_str(&string).unwrap_throw()
462+
}
463+
}
464+
446465
#[cfg(test)]
447466
mod tests {
448467
use super::*;
@@ -543,7 +562,7 @@ mod tests {
543562
let serialized: Vec<u8> = (&envelope).into();
544563

545564
// Unseal the key from the envelope
546-
let deserialized: PasswordProtectedKeyEnvelope<TestIds> =
565+
let deserialized: PasswordProtectedKeyEnvelope =
547566
PasswordProtectedKeyEnvelope::try_from(&serialized).unwrap();
548567
let key = deserialized.unseal(password, &mut ctx).unwrap();
549568

@@ -574,7 +593,7 @@ mod tests {
574593
let serialized: Vec<u8> = (&envelope).into();
575594

576595
// Unseal the key from the envelope
577-
let deserialized: PasswordProtectedKeyEnvelope<TestIds> =
596+
let deserialized: PasswordProtectedKeyEnvelope =
578597
PasswordProtectedKeyEnvelope::try_from(&serialized).unwrap();
579598
let key = deserialized.unseal(password, &mut ctx).unwrap();
580599

@@ -599,7 +618,7 @@ mod tests {
599618
let new_password = "new_test_password";
600619

601620
// Seal the key with a password
602-
let envelope: PasswordProtectedKeyEnvelope<TestIds> =
621+
let envelope: PasswordProtectedKeyEnvelope =
603622
PasswordProtectedKeyEnvelope::seal_ref(&key, password).expect("Sealing should work");
604623

605624
// Reseal
@@ -627,7 +646,7 @@ mod tests {
627646
let envelope = PasswordProtectedKeyEnvelope::seal(test_key, password, &ctx).unwrap();
628647

629648
// Attempt to unseal with the wrong password
630-
let deserialized: PasswordProtectedKeyEnvelope<TestIds> =
649+
let deserialized: PasswordProtectedKeyEnvelope =
631650
PasswordProtectedKeyEnvelope::try_from(&(&envelope).into()).unwrap();
632651
assert!(matches!(
633652
deserialized.unseal(wrong_password, &mut ctx),

crates/bitwarden-crypto/src/uniffi_support.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use std::{num::NonZeroU32, str::FromStr};
22

33
use bitwarden_uniffi_error::convert_result;
44

5-
use crate::{CryptoError, EncString, SignedPublicKey, UnsignedSharedKey};
5+
use crate::{
6+
CryptoError, EncString, SignedPublicKey, UnsignedSharedKey, safe::PasswordProtectedKeyEnvelope,
7+
};
68

79
uniffi::custom_type!(NonZeroU32, u32, {
810
remote,
@@ -32,3 +34,9 @@ uniffi::custom_type!(SignedPublicKey, String, {
3234
},
3335
lower: |obj| obj.into(),
3436
});
37+
38+
uniffi::custom_type!(PasswordProtectedKeyEnvelope, String, {
39+
remote,
40+
try_lift: |val| convert_result(PasswordProtectedKeyEnvelope::from_str(&val)),
41+
lower: |obj| obj.into(),
42+
});

0 commit comments

Comments
 (0)