From 931e630bfd228abaefd08144158889892eea4b51 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 8 May 2025 19:05:27 +0200 Subject: [PATCH 01/12] Add missing purecrypto functions --- .../src/pure_crypto.rs | 190 +++++++++++++++++- 1 file changed, 189 insertions(+), 1 deletion(-) diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 259460734..1cd8ade37 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -1,7 +1,8 @@ use std::str::FromStr; +use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, SymmetricCryptoKey, + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, CryptoError, Decryptable, EncString, Encryptable, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, SymmetricCryptoKey, UnsignedSharedKey }; use wasm_bindgen::prelude::*; @@ -107,6 +108,110 @@ impl PureCrypto { pub fn generate_user_key_xchacha20_poly1305() -> Vec { SymmetricCryptoKey::make_xchacha20_poly1305_key().to_encoded() } + + // Key wrap + pub fn wrap_symmetric_key( + key_to_be_wrapped: Vec, + wrapping_key: Vec, + ) -> Result { + let tmp_store: KeyStore = KeyStore::default(); + let mut context = tmp_store.context(); + #[allow(deprecated)] + context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?)?; + #[allow(deprecated)] + context.set_symmetric_key(SymmetricKeyId::Local("key_to_wrap"), SymmetricCryptoKey::try_from(key_to_be_wrapped)?)?; + // Note: The order of arguments is different here, and should probably be refactored + Ok(context.wrap_symmetric_key( + SymmetricKeyId::Local("wrapping_key"), + SymmetricKeyId::Local("key_to_wrap"), + )? + .to_string()) + } + + pub fn unwrap_symmetric_key( + wrapped_key: String, + wrapping_key: Vec, + ) -> Result, CryptoError> { + let tmp_store: KeyStore = KeyStore::default(); + let mut context = tmp_store.context(); + #[allow(deprecated)] + context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?)?; + // Note: The order of arguments is different here, and should probably be refactored + context.unwrap_symmetric_key( + SymmetricKeyId::Local("wrapping_key"), + SymmetricKeyId::Local("wrapped_key"), + &EncString::from_str(wrapped_key.as_str())?, + )?; + #[allow(deprecated)] + let key = context + .dangerous_get_symmetric_key(SymmetricKeyId::Local("wrapped_key"))?; + Ok(key.to_encoded()) + } + + pub fn wrap_encapsulation_key( + encapsulation_key: Vec, + wrapping_key: Vec, + ) -> Result { + let tmp_store: KeyStore = KeyStore::default(); + let mut context = tmp_store.context(); + #[allow(deprecated)] + context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?)?; + // Note: The order of arguments is different here, and should probably be refactored + Ok(encapsulation_key.encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?.to_string()) + } + + pub fn unwrap_encapsulation_key( + wrapped_key: String, + wrapping_key: Vec, + ) -> Result, CryptoError> { + let tmp_store: KeyStore = KeyStore::default(); + let mut context = tmp_store.context(); + #[allow(deprecated)] + context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?)?; + // Note: The order of arguments is different here, and should probably be refactored + Ok(EncString::from_str(wrapped_key.as_str())?.decrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?) + } + + pub fn wrap_decapsulation_key( + decapsulation_key: Vec, + wrapping_key: Vec, + ) -> Result { + let tmp_store: KeyStore = KeyStore::default(); + let mut context = tmp_store.context(); + #[allow(deprecated)] + context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?)?; + // Note: The order of arguments is different here, and should probably be refactored + Ok(decapsulation_key.encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?.to_string()) + } + + pub fn unwrap_decapsulation_key( + wrapped_key: String, + wrapping_key: Vec, + ) -> Result, CryptoError> { + let tmp_store: KeyStore = KeyStore::default(); + let mut context = tmp_store.context(); + #[allow(deprecated)] + context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?)?; + // Note: The order of arguments is different here, and should probably be refactored + Ok(EncString::from_str(wrapped_key.as_str())?.decrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?) + } + + // Key encapsulation + pub fn encapsulate_key_unsigned( + shared_key: Vec, + encapsulation_key: Vec + ) -> Result { + let encapsulation_key = AsymmetricPublicCryptoKey::from_der(encapsulation_key.as_slice())?; + Ok(UnsignedSharedKey::encapsulate_key_unsigned(&SymmetricCryptoKey::try_from(shared_key)?, &encapsulation_key)?.to_string()) + } + + pub fn decapsulate_key_unsigned( + encapsulated_key: String, + decapsulation_key: Vec + ) -> Result, CryptoError> { + Ok(UnsignedSharedKey::from_str(encapsulated_key.as_str())? + .decapsulate_key_unsigned(&AsymmetricCryptoKey::from_der(decapsulation_key.as_slice())?)?.to_encoded()) + } } #[cfg(test)] @@ -133,6 +238,35 @@ mod tests { 2, 89, 112, 178, 83, 25, 77, 130, 187, 127, 85, 179, 211, 159, 186, 111, 44, 109, 211, 18, 120, 104, 144, 4, 76, 3, ]; + + const PEM_KEY: &str = "-----BEGIN PRIVATE KEY----- +MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDiTQVuzhdygFz5 +qv14i+XFDGTnDravzUQT1hPKPGUZOUSZ1gwdNgkWqOIaOnR65BHEnL0sp4bnuiYc +afeK2JAW5Sc8Z7IxBNSuAwhQmuKx3RochMIiuCkI2/p+JvUQoJu6FBNm8OoJ4Cwm +qqHGZESMfnpQDCuDrB3JdJEdXhtmnl0C48sGjOk3WaBMcgGqn8LbJDUlyu1zdqyv +b0waJf0iV4PJm2fkUl7+57D/2TkpbCqURVnZK1FFIEg8mr6FzSN1F2pOfktkNYZw +P7MSNR7o81CkRSCMr7EkIVa+MZYMBx106BMK7FXgWB7nbSpsWKxBk7ZDHkID2fam +rEcVtrzDAgMBAAECggEBAKwq9OssGGKgjhvUnyrLJHAZ0dqIMyzk+dotkLjX4gKi +szJmyqiep6N5sStLNbsZMPtoU/RZMCW0VbJgXFhiEp2YkZU/Py5UAoqw++53J+kx +0d/IkPphKbb3xUec0+1mg5O6GljDCQuiZXS1dIa/WfeZcezclW6Dz9WovY6ePjJ+ +8vEBR1icbNKzyeINd6MtPtpcgQPHtDwHvhPyUDbKDYGbLvjh9nui8h4+ZUlXKuVR +jB0ChxiKV1xJRjkrEVoulOOicd5r597WfB2ghax3pvRZ4MdXemCXm3gQYqPVKach +vGU+1cPQR/MBJZpxT+EZA97xwtFS3gqwbxJaNFcoE8ECgYEA9OaeYZhQPDo485tI +1u/Z7L/3PNape9hBQIXoW7+MgcQ5NiWqYh8Jnj43EIYa0wM/ECQINr1Za8Q5e6KR +J30FcU+kfyjuQ0jeXdNELGU/fx5XXNg/vV8GevHwxRlwzqZTCg6UExUZzbYEQqd7 +l+wPyETGeua5xCEywA1nX/D101kCgYEA7I6aMFjhEjO71RmzNhqjKJt6DOghoOfQ +TjhaaanNEhLYSbenFz1mlb21mW67ulmz162saKdIYLxQNJIP8ZPmxh4ummOJI8w9 +ClHfo8WuCI2hCjJ19xbQJocSbTA5aJg6lA1IDVZMDbQwsnAByPRGpaLHBT/Q9Bye +KvCMB+9amXsCgYEAx65yXSkP4sumPBrVHUub6MntERIGRxBgw/drKcPZEMWp0FiN +wEuGUBxyUWrG3F69QK/gcqGZE6F/LSu0JvptQaKqgXQiMYJsrRvhbkFvsHpQyUcZ +UZL1ebFjm5HOxPAgrQaN/bEqxOwwNRjSUWEMzUImg3c06JIZCzbinvudtKECgYEA +kY3JF/iIPI/yglP27lKDlCfeeHSYxI3+oTKRhzSAxx8rUGidenJAXeDGDauR/T7W +pt3pGNfddBBK9Z3uC4Iq3DqUCFE4f/taj7ADAJ1Q0Vh7/28/IJM77ojr8J1cpZwN +Zy2o6PPxhfkagaDjqEeN9Lrs5LD4nEvDkr5CG1vOjmMCgYEAvIBFKRm31NyF8jLi +CVuPwC5PzrW5iThDmsWTaXFpB3esUsbICO2pEz872oeQS+Em4GO5vXUlpbbFPzup +PFhA8iMJ8TAvemhvc7oM0OZqpU6p3K4seHf6BkwLxumoA3vDJfovu9RuXVcJVOnf +DnqOsltgPomWZ7xVfMkm9niL2OA= +-----END PRIVATE KEY-----"; #[test] fn test_symmetric_decrypt() { @@ -228,4 +362,58 @@ mod tests { .unwrap(); assert_eq!(user_key, decrypted_user_key); } + + #[test] + fn test_wrap_unwrap_symmetric_key() { + let key_to_be_wrapped = PureCrypto::generate_user_key_aes256_cbc_hmac(); + let wrapping_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); + let wrapped_key = PureCrypto::wrap_symmetric_key( + key_to_be_wrapped.clone(), + wrapping_key.clone(), + ) + .unwrap(); + let unwrapped_key = PureCrypto::unwrap_symmetric_key(wrapped_key, wrapping_key).unwrap(); + assert_eq!(key_to_be_wrapped, unwrapped_key); + } + + #[test] + fn test_wrap_encapsulation_key() { + let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); + let encapsulation_key = decapsulation_key.to_public_der().unwrap(); + let wrapping_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); + let wrapped_key = PureCrypto::wrap_encapsulation_key( + encapsulation_key.clone(), + wrapping_key.clone(), + ) + .unwrap(); + let unwrapped_key = PureCrypto::unwrap_encapsulation_key(wrapped_key, wrapping_key).unwrap(); + assert_eq!(encapsulation_key, unwrapped_key); + } + + #[test] + fn test_wrap_decapsulation_key() { + let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); + let wrapping_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); + let wrapped_key = PureCrypto::wrap_decapsulation_key( + decapsulation_key.to_der().unwrap(), + wrapping_key.clone(), + ) + .unwrap(); + let unwrapped_key = PureCrypto::unwrap_decapsulation_key(wrapped_key, wrapping_key).unwrap(); + assert_eq!(decapsulation_key.to_der().unwrap(), unwrapped_key); + } + + #[test] + fn test_encapsulate_key_unsigned() { + let shared_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); + let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); + let encapsulation_key = decapsulation_key.to_public_der().unwrap(); + let encapsulated_key = PureCrypto::encapsulate_key_unsigned( + shared_key.clone(), + encapsulation_key.clone(), + ) + .unwrap(); + let unwrapped_key = PureCrypto::decapsulate_key_unsigned(encapsulated_key, decapsulation_key.to_der().unwrap()).unwrap(); + assert_eq!(shared_key, unwrapped_key); + } } From 77ac6fae985c8f02cfd1acc6928c5fbc07ba856b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 8 May 2025 19:07:15 +0200 Subject: [PATCH 02/12] Cargo fmt --- .../src/pure_crypto.rs | 116 ++++++++++++------ 1 file changed, 76 insertions(+), 40 deletions(-) diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 1cd8ade37..4b15f3469 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -2,7 +2,9 @@ use std::str::FromStr; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - AsymmetricCryptoKey, AsymmetricPublicCryptoKey, CryptoError, Decryptable, EncString, Encryptable, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, SymmetricCryptoKey, UnsignedSharedKey + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, CryptoError, Decryptable, EncString, + Encryptable, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, SymmetricCryptoKey, + UnsignedSharedKey, }; use wasm_bindgen::prelude::*; @@ -117,15 +119,22 @@ impl PureCrypto { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); #[allow(deprecated)] - context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?)?; - #[allow(deprecated)] - context.set_symmetric_key(SymmetricKeyId::Local("key_to_wrap"), SymmetricCryptoKey::try_from(key_to_be_wrapped)?)?; - // Note: The order of arguments is different here, and should probably be refactored - Ok(context.wrap_symmetric_key( + context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), + SymmetricCryptoKey::try_from(wrapping_key)?, + )?; + #[allow(deprecated)] + context.set_symmetric_key( SymmetricKeyId::Local("key_to_wrap"), - )? - .to_string()) + SymmetricCryptoKey::try_from(key_to_be_wrapped)?, + )?; + // Note: The order of arguments is different here, and should probably be refactored + Ok(context + .wrap_symmetric_key( + SymmetricKeyId::Local("wrapping_key"), + SymmetricKeyId::Local("key_to_wrap"), + )? + .to_string()) } pub fn unwrap_symmetric_key( @@ -135,7 +144,10 @@ impl PureCrypto { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); #[allow(deprecated)] - context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?)?; + context.set_symmetric_key( + SymmetricKeyId::Local("wrapping_key"), + SymmetricCryptoKey::try_from(wrapping_key)?, + )?; // Note: The order of arguments is different here, and should probably be refactored context.unwrap_symmetric_key( SymmetricKeyId::Local("wrapping_key"), @@ -143,8 +155,7 @@ impl PureCrypto { &EncString::from_str(wrapped_key.as_str())?, )?; #[allow(deprecated)] - let key = context - .dangerous_get_symmetric_key(SymmetricKeyId::Local("wrapped_key"))?; + let key = context.dangerous_get_symmetric_key(SymmetricKeyId::Local("wrapped_key"))?; Ok(key.to_encoded()) } @@ -155,9 +166,14 @@ impl PureCrypto { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); #[allow(deprecated)] - context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?)?; + context.set_symmetric_key( + SymmetricKeyId::Local("wrapping_key"), + SymmetricCryptoKey::try_from(wrapping_key)?, + )?; // Note: The order of arguments is different here, and should probably be refactored - Ok(encapsulation_key.encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?.to_string()) + Ok(encapsulation_key + .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))? + .to_string()) } pub fn unwrap_encapsulation_key( @@ -167,9 +183,13 @@ impl PureCrypto { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); #[allow(deprecated)] - context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?)?; + context.set_symmetric_key( + SymmetricKeyId::Local("wrapping_key"), + SymmetricCryptoKey::try_from(wrapping_key)?, + )?; // Note: The order of arguments is different here, and should probably be refactored - Ok(EncString::from_str(wrapped_key.as_str())?.decrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?) + Ok(EncString::from_str(wrapped_key.as_str())? + .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?) } pub fn wrap_decapsulation_key( @@ -179,9 +199,14 @@ impl PureCrypto { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); #[allow(deprecated)] - context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?)?; + context.set_symmetric_key( + SymmetricKeyId::Local("wrapping_key"), + SymmetricCryptoKey::try_from(wrapping_key)?, + )?; // Note: The order of arguments is different here, and should probably be refactored - Ok(decapsulation_key.encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?.to_string()) + Ok(decapsulation_key + .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))? + .to_string()) } pub fn unwrap_decapsulation_key( @@ -191,26 +216,37 @@ impl PureCrypto { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); #[allow(deprecated)] - context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?)?; + context.set_symmetric_key( + SymmetricKeyId::Local("wrapping_key"), + SymmetricCryptoKey::try_from(wrapping_key)?, + )?; // Note: The order of arguments is different here, and should probably be refactored - Ok(EncString::from_str(wrapped_key.as_str())?.decrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?) + Ok(EncString::from_str(wrapped_key.as_str())? + .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?) } // Key encapsulation pub fn encapsulate_key_unsigned( shared_key: Vec, - encapsulation_key: Vec + encapsulation_key: Vec, ) -> Result { let encapsulation_key = AsymmetricPublicCryptoKey::from_der(encapsulation_key.as_slice())?; - Ok(UnsignedSharedKey::encapsulate_key_unsigned(&SymmetricCryptoKey::try_from(shared_key)?, &encapsulation_key)?.to_string()) + Ok(UnsignedSharedKey::encapsulate_key_unsigned( + &SymmetricCryptoKey::try_from(shared_key)?, + &encapsulation_key, + )? + .to_string()) } pub fn decapsulate_key_unsigned( encapsulated_key: String, - decapsulation_key: Vec + decapsulation_key: Vec, ) -> Result, CryptoError> { Ok(UnsignedSharedKey::from_str(encapsulated_key.as_str())? - .decapsulate_key_unsigned(&AsymmetricCryptoKey::from_der(decapsulation_key.as_slice())?)?.to_encoded()) + .decapsulate_key_unsigned(&AsymmetricCryptoKey::from_der( + decapsulation_key.as_slice(), + )?)? + .to_encoded()) } } @@ -238,7 +274,7 @@ mod tests { 2, 89, 112, 178, 83, 25, 77, 130, 187, 127, 85, 179, 211, 159, 186, 111, 44, 109, 211, 18, 120, 104, 144, 4, 76, 3, ]; - + const PEM_KEY: &str = "-----BEGIN PRIVATE KEY----- MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDiTQVuzhdygFz5 qv14i+XFDGTnDravzUQT1hPKPGUZOUSZ1gwdNgkWqOIaOnR65BHEnL0sp4bnuiYc @@ -367,11 +403,9 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= fn test_wrap_unwrap_symmetric_key() { let key_to_be_wrapped = PureCrypto::generate_user_key_aes256_cbc_hmac(); let wrapping_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); - let wrapped_key = PureCrypto::wrap_symmetric_key( - key_to_be_wrapped.clone(), - wrapping_key.clone(), - ) - .unwrap(); + let wrapped_key = + PureCrypto::wrap_symmetric_key(key_to_be_wrapped.clone(), wrapping_key.clone()) + .unwrap(); let unwrapped_key = PureCrypto::unwrap_symmetric_key(wrapped_key, wrapping_key).unwrap(); assert_eq!(key_to_be_wrapped, unwrapped_key); } @@ -381,12 +415,11 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); let encapsulation_key = decapsulation_key.to_public_der().unwrap(); let wrapping_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); - let wrapped_key = PureCrypto::wrap_encapsulation_key( - encapsulation_key.clone(), - wrapping_key.clone(), - ) - .unwrap(); - let unwrapped_key = PureCrypto::unwrap_encapsulation_key(wrapped_key, wrapping_key).unwrap(); + let wrapped_key = + PureCrypto::wrap_encapsulation_key(encapsulation_key.clone(), wrapping_key.clone()) + .unwrap(); + let unwrapped_key = + PureCrypto::unwrap_encapsulation_key(wrapped_key, wrapping_key).unwrap(); assert_eq!(encapsulation_key, unwrapped_key); } @@ -399,7 +432,8 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= wrapping_key.clone(), ) .unwrap(); - let unwrapped_key = PureCrypto::unwrap_decapsulation_key(wrapped_key, wrapping_key).unwrap(); + let unwrapped_key = + PureCrypto::unwrap_decapsulation_key(wrapped_key, wrapping_key).unwrap(); assert_eq!(decapsulation_key.to_der().unwrap(), unwrapped_key); } @@ -408,12 +442,14 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= let shared_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); let encapsulation_key = decapsulation_key.to_public_der().unwrap(); - let encapsulated_key = PureCrypto::encapsulate_key_unsigned( - shared_key.clone(), - encapsulation_key.clone(), + let encapsulated_key = + PureCrypto::encapsulate_key_unsigned(shared_key.clone(), encapsulation_key.clone()) + .unwrap(); + let unwrapped_key = PureCrypto::decapsulate_key_unsigned( + encapsulated_key, + decapsulation_key.to_der().unwrap(), ) .unwrap(); - let unwrapped_key = PureCrypto::decapsulate_key_unsigned(encapsulated_key, decapsulation_key.to_der().unwrap()).unwrap(); assert_eq!(shared_key, unwrapped_key); } } From 1e0bd4104db9b1ef2598af2b9a237b8544a29b46 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 8 May 2025 19:24:10 +0200 Subject: [PATCH 03/12] Cleanup --- crates/bitwarden-wasm-internal/src/pure_crypto.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 4b15f3469..df0aaa5d3 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -188,8 +188,8 @@ impl PureCrypto { SymmetricCryptoKey::try_from(wrapping_key)?, )?; // Note: The order of arguments is different here, and should probably be refactored - Ok(EncString::from_str(wrapped_key.as_str())? - .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?) + EncString::from_str(wrapped_key.as_str())? + .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) } pub fn wrap_decapsulation_key( @@ -221,8 +221,8 @@ impl PureCrypto { SymmetricCryptoKey::try_from(wrapping_key)?, )?; // Note: The order of arguments is different here, and should probably be refactored - Ok(EncString::from_str(wrapped_key.as_str())? - .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))?) + EncString::from_str(wrapped_key.as_str())? + .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) } // Key encapsulation From 85ebb660b9b9f7120b2ec38782a7c188a7ebf55b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 12:35:09 +0200 Subject: [PATCH 04/12] Improve docs --- .vscode/settings.json | 4 ++ .../src/pure_crypto.rs | 39 ++++++++++++------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 697a9c0a5..0df252c66 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,8 @@ "cloc", "COSE", "dealloc", + "Decapsulates", + "decapsulation", "decryptable", "dylib", "encryptable", @@ -20,7 +22,9 @@ "repr", "reprompt", "reqwest", + "rotateable", "schemars", + "spki", "totp", "uniffi", "wordlist", diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index df0aaa5d3..ae7ba015c 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -75,7 +75,6 @@ impl PureCrypto { .to_buffer() } - // Userkey encryption with password pub fn decrypt_user_key_with_master_password( encrypted_user_key: String, master_password: String, @@ -102,16 +101,15 @@ impl PureCrypto { Ok(result.to_string()) } - // Generate userkey - pub fn generate_user_key_aes256_cbc_hmac() -> Vec { + pub fn make_user_key_aes256_cbc_hmac() -> Vec { SymmetricCryptoKey::make_aes256_cbc_hmac_key().to_encoded() } - pub fn generate_user_key_xchacha20_poly1305() -> Vec { + pub fn make_user_key_xchacha20_poly1305() -> Vec { SymmetricCryptoKey::make_xchacha20_poly1305_key().to_encoded() } - // Key wrap + /// Wraps (encrypts) a symmetric key using a symmetric wrapping key, returning the wrapped key as an EncString. pub fn wrap_symmetric_key( key_to_be_wrapped: Vec, wrapping_key: Vec, @@ -137,6 +135,8 @@ impl PureCrypto { .to_string()) } + /// Unwraps (decrypts) a wrapped symmetric key using a symmetric wrapping key, returning the unwrapped key as a + /// serialized byte array. pub fn unwrap_symmetric_key( wrapped_key: String, wrapping_key: Vec, @@ -159,6 +159,10 @@ impl PureCrypto { Ok(key.to_encoded()) } + /// Wraps (encrypts) a spki der encoded encapsulation (public) key using a symmetric wrapping key. + /// Note: Usually, a public key is - by definition - public, so this should not be used. The specific use-case + /// for this function is to enable rotateable key sets, where the "public key" is not public, with the intent + /// of preventing the server from being able to overwrite the user key unlocked by the rotateable keyset. pub fn wrap_encapsulation_key( encapsulation_key: Vec, wrapping_key: Vec, @@ -176,6 +180,7 @@ impl PureCrypto { .to_string()) } + /// Unwraps (decrypts) a wrapped spki der encoded encapsulation (public) key using a symmetric wrapping key. pub fn unwrap_encapsulation_key( wrapped_key: String, wrapping_key: Vec, @@ -192,6 +197,7 @@ impl PureCrypto { .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) } + /// Wraps (encrypts) a pkcs8 der encoded decapsulation (private) key using a symmetric wrapping key, pub fn wrap_decapsulation_key( decapsulation_key: Vec, wrapping_key: Vec, @@ -209,6 +215,7 @@ impl PureCrypto { .to_string()) } + /// Unwraps (decrypts) a wrapped pkcs8 der encoded decapsulation (private) key using a symmetric wrapping key. pub fn unwrap_decapsulation_key( wrapped_key: String, wrapping_key: Vec, @@ -225,7 +232,9 @@ impl PureCrypto { .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) } - // Key encapsulation + /// Encapsulates (encrypts) a symmetric key using an asymmetric encapsulation key (public key) in spki format, + /// returning the encapsulated key as a string. Note: This is unsigned, so the sender authenticity cannot + /// be verified by the recipient. pub fn encapsulate_key_unsigned( shared_key: Vec, encapsulation_key: Vec, @@ -238,6 +247,8 @@ impl PureCrypto { .to_string()) } + /// Decapsulates (decrypts) a symmetric key using an decapsulation key (private key) in pkcs8 der format. Note: + /// This is unsigned, so the sender authenticity cannot be verified by the recipient. pub fn decapsulate_key_unsigned( encapsulated_key: String, decapsulation_key: Vec, @@ -364,13 +375,13 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= #[test] fn test_make_aes256_cbc_hmac_key() { - let key = PureCrypto::generate_user_key_aes256_cbc_hmac(); + let key = PureCrypto::make_user_key_aes256_cbc_hmac(); assert_eq!(key.len(), 64); } #[test] fn test_make_xchacha20_poly1305_key() { - let key = PureCrypto::generate_user_key_xchacha20_poly1305(); + let key = PureCrypto::make_user_key_xchacha20_poly1305(); assert!(key.len() > 64); } @@ -381,7 +392,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= let kdf = Kdf::PBKDF2 { iterations: NonZero::try_from(600000).unwrap(), }; - let user_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); + let user_key = PureCrypto::make_user_key_aes256_cbc_hmac(); let encrypted_user_key = PureCrypto::encrypt_user_key_with_master_password( user_key.clone(), master_password.to_string(), @@ -401,8 +412,8 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= #[test] fn test_wrap_unwrap_symmetric_key() { - let key_to_be_wrapped = PureCrypto::generate_user_key_aes256_cbc_hmac(); - let wrapping_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); + let key_to_be_wrapped = PureCrypto::make_user_key_aes256_cbc_hmac(); + let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac(); let wrapped_key = PureCrypto::wrap_symmetric_key(key_to_be_wrapped.clone(), wrapping_key.clone()) .unwrap(); @@ -414,7 +425,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= fn test_wrap_encapsulation_key() { let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); let encapsulation_key = decapsulation_key.to_public_der().unwrap(); - let wrapping_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); + let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac(); let wrapped_key = PureCrypto::wrap_encapsulation_key(encapsulation_key.clone(), wrapping_key.clone()) .unwrap(); @@ -426,7 +437,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= #[test] fn test_wrap_decapsulation_key() { let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); - let wrapping_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); + let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac(); let wrapped_key = PureCrypto::wrap_decapsulation_key( decapsulation_key.to_der().unwrap(), wrapping_key.clone(), @@ -439,7 +450,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= #[test] fn test_encapsulate_key_unsigned() { - let shared_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); + let shared_key = PureCrypto::make_user_key_aes256_cbc_hmac(); let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); let encapsulation_key = decapsulation_key.to_public_der().unwrap(); let encapsulated_key = From 9662ce4f6fd6ea6a2d972f05e548ff14d52a1ce5 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 12:41:47 +0200 Subject: [PATCH 05/12] Cargo fmt --- .../src/pure_crypto.rs | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index ae7ba015c..bbf4ae617 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -109,7 +109,8 @@ impl PureCrypto { SymmetricCryptoKey::make_xchacha20_poly1305_key().to_encoded() } - /// Wraps (encrypts) a symmetric key using a symmetric wrapping key, returning the wrapped key as an EncString. + /// Wraps (encrypts) a symmetric key using a symmetric wrapping key, returning the wrapped key + /// as an EncString. pub fn wrap_symmetric_key( key_to_be_wrapped: Vec, wrapping_key: Vec, @@ -135,8 +136,8 @@ impl PureCrypto { .to_string()) } - /// Unwraps (decrypts) a wrapped symmetric key using a symmetric wrapping key, returning the unwrapped key as a - /// serialized byte array. + /// Unwraps (decrypts) a wrapped symmetric key using a symmetric wrapping key, returning the + /// unwrapped key as a serialized byte array. pub fn unwrap_symmetric_key( wrapped_key: String, wrapping_key: Vec, @@ -159,10 +160,11 @@ impl PureCrypto { Ok(key.to_encoded()) } - /// Wraps (encrypts) a spki der encoded encapsulation (public) key using a symmetric wrapping key. - /// Note: Usually, a public key is - by definition - public, so this should not be used. The specific use-case - /// for this function is to enable rotateable key sets, where the "public key" is not public, with the intent - /// of preventing the server from being able to overwrite the user key unlocked by the rotateable keyset. + /// Wraps (encrypts) a spki der encoded encapsulation (public) key using a symmetric wrapping + /// key. Note: Usually, a public key is - by definition - public, so this should not be + /// used. The specific use-case for this function is to enable rotateable key sets, where + /// the "public key" is not public, with the intent of preventing the server from being able + /// to overwrite the user key unlocked by the rotateable keyset. pub fn wrap_encapsulation_key( encapsulation_key: Vec, wrapping_key: Vec, @@ -180,7 +182,8 @@ impl PureCrypto { .to_string()) } - /// Unwraps (decrypts) a wrapped spki der encoded encapsulation (public) key using a symmetric wrapping key. + /// Unwraps (decrypts) a wrapped spki der encoded encapsulation (public) key using a symmetric + /// wrapping key. pub fn unwrap_encapsulation_key( wrapped_key: String, wrapping_key: Vec, @@ -197,7 +200,8 @@ impl PureCrypto { .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) } - /// Wraps (encrypts) a pkcs8 der encoded decapsulation (private) key using a symmetric wrapping key, + /// Wraps (encrypts) a pkcs8 der encoded decapsulation (private) key using a symmetric wrapping + /// key, pub fn wrap_decapsulation_key( decapsulation_key: Vec, wrapping_key: Vec, @@ -215,7 +219,8 @@ impl PureCrypto { .to_string()) } - /// Unwraps (decrypts) a wrapped pkcs8 der encoded decapsulation (private) key using a symmetric wrapping key. + /// Unwraps (decrypts) a wrapped pkcs8 der encoded decapsulation (private) key using a symmetric + /// wrapping key. pub fn unwrap_decapsulation_key( wrapped_key: String, wrapping_key: Vec, @@ -232,9 +237,9 @@ impl PureCrypto { .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) } - /// Encapsulates (encrypts) a symmetric key using an asymmetric encapsulation key (public key) in spki format, - /// returning the encapsulated key as a string. Note: This is unsigned, so the sender authenticity cannot - /// be verified by the recipient. + /// Encapsulates (encrypts) a symmetric key using an asymmetric encapsulation key (public key) + /// in spki format, returning the encapsulated key as a string. Note: This is unsigned, so + /// the sender authenticity cannot be verified by the recipient. pub fn encapsulate_key_unsigned( shared_key: Vec, encapsulation_key: Vec, @@ -247,8 +252,9 @@ impl PureCrypto { .to_string()) } - /// Decapsulates (decrypts) a symmetric key using an decapsulation key (private key) in pkcs8 der format. Note: - /// This is unsigned, so the sender authenticity cannot be verified by the recipient. + /// Decapsulates (decrypts) a symmetric key using an decapsulation key (private key) in pkcs8 + /// der format. Note: This is unsigned, so the sender authenticity cannot be verified by the + /// recipient. pub fn decapsulate_key_unsigned( encapsulated_key: String, decapsulation_key: Vec, From 3ff240c129d442fdbaa0f985f1c8954667a32e41 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 13:15:51 +0200 Subject: [PATCH 06/12] Update crates/bitwarden-wasm-internal/src/pure_crypto.rs Co-authored-by: Andreas Coroiu --- crates/bitwarden-wasm-internal/src/pure_crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index bbf4ae617..e21a838d8 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -160,7 +160,7 @@ impl PureCrypto { Ok(key.to_encoded()) } - /// Wraps (encrypts) a spki der encoded encapsulation (public) key using a symmetric wrapping + /// Wraps (encrypts) an spki der encoded encapsulation (public) key using a symmetric wrapping /// key. Note: Usually, a public key is - by definition - public, so this should not be /// used. The specific use-case for this function is to enable rotateable key sets, where /// the "public key" is not public, with the intent of preventing the server from being able From 41997d176e53e2f3287f6a2c7a2f7a921a99be59 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 13:16:00 +0200 Subject: [PATCH 07/12] Update crates/bitwarden-wasm-internal/src/pure_crypto.rs Co-authored-by: Andreas Coroiu --- crates/bitwarden-wasm-internal/src/pure_crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index e21a838d8..7be0f0d9d 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -239,7 +239,7 @@ impl PureCrypto { /// Encapsulates (encrypts) a symmetric key using an asymmetric encapsulation key (public key) /// in spki format, returning the encapsulated key as a string. Note: This is unsigned, so - /// the sender authenticity cannot be verified by the recipient. + /// the sender's authenticity cannot be verified by the recipient. pub fn encapsulate_key_unsigned( shared_key: Vec, encapsulation_key: Vec, From 7c882e5c5f11f36a56ad29ddaea72050fda5fa39 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 13:16:11 +0200 Subject: [PATCH 08/12] Update crates/bitwarden-wasm-internal/src/pure_crypto.rs Co-authored-by: Andreas Coroiu --- crates/bitwarden-wasm-internal/src/pure_crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 7be0f0d9d..3a28e7e29 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -253,7 +253,7 @@ impl PureCrypto { } /// Decapsulates (decrypts) a symmetric key using an decapsulation key (private key) in pkcs8 - /// der format. Note: This is unsigned, so the sender authenticity cannot be verified by the + /// der format. Note: This is unsigned, so the sender's authenticity cannot be verified by the /// recipient. pub fn decapsulate_key_unsigned( encapsulated_key: String, From e7d10f75fed382c081b729f5b185715c8c9bcdde Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 13:19:13 +0200 Subject: [PATCH 09/12] Make acronyms uppercase --- crates/bitwarden-wasm-internal/src/pure_crypto.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 3a28e7e29..64102461b 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -182,7 +182,7 @@ impl PureCrypto { .to_string()) } - /// Unwraps (decrypts) a wrapped spki der encoded encapsulation (public) key using a symmetric + /// Unwraps (decrypts) a wrapped SPKI der encoded encapsulation (public) key using a symmetric /// wrapping key. pub fn unwrap_encapsulation_key( wrapped_key: String, @@ -200,7 +200,7 @@ impl PureCrypto { .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) } - /// Wraps (encrypts) a pkcs8 der encoded decapsulation (private) key using a symmetric wrapping + /// Wraps (encrypts) a PKCS8 DER encoded decapsulation (private) key using a symmetric wrapping /// key, pub fn wrap_decapsulation_key( decapsulation_key: Vec, @@ -238,7 +238,7 @@ impl PureCrypto { } /// Encapsulates (encrypts) a symmetric key using an asymmetric encapsulation key (public key) - /// in spki format, returning the encapsulated key as a string. Note: This is unsigned, so + /// in SPKI format, returning the encapsulated key as a string. Note: This is unsigned, so /// the sender's authenticity cannot be verified by the recipient. pub fn encapsulate_key_unsigned( shared_key: Vec, @@ -252,8 +252,8 @@ impl PureCrypto { .to_string()) } - /// Decapsulates (decrypts) a symmetric key using an decapsulation key (private key) in pkcs8 - /// der format. Note: This is unsigned, so the sender's authenticity cannot be verified by the + /// Decapsulates (decrypts) a symmetric key using an decapsulation key (private key) in PKCS8 + /// DER format. Note: This is unsigned, so the sender's authenticity cannot be verified by the /// recipient. pub fn decapsulate_key_unsigned( encapsulated_key: String, From 236f5fd77bc5d5f7d3bd8ba913994536fc994f0e Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 13:20:37 +0200 Subject: [PATCH 10/12] Make spki der uppercase --- crates/bitwarden-wasm-internal/src/pure_crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 64102461b..be4eff247 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -160,7 +160,7 @@ impl PureCrypto { Ok(key.to_encoded()) } - /// Wraps (encrypts) an spki der encoded encapsulation (public) key using a symmetric wrapping + /// Wraps (encrypts) an SPKI DER encoded encapsulation (public) key using a symmetric wrapping /// key. Note: Usually, a public key is - by definition - public, so this should not be /// used. The specific use-case for this function is to enable rotateable key sets, where /// the "public key" is not public, with the intent of preventing the server from being able From ccefb9596be5186e0862861668598c154d381db5 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 13:21:50 +0200 Subject: [PATCH 11/12] Make pkcs8 der uppercase --- crates/bitwarden-wasm-internal/src/pure_crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index be4eff247..9244d99d2 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -219,7 +219,7 @@ impl PureCrypto { .to_string()) } - /// Unwraps (decrypts) a wrapped pkcs8 der encoded decapsulation (private) key using a symmetric + /// Unwraps (decrypts) a wrapped PKCS8 DER encoded decapsulation (private) key using a symmetric /// wrapping key. pub fn unwrap_decapsulation_key( wrapped_key: String, From 003ce952c917dab7db9db2f245f670a229e48acc Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 14:23:27 +0200 Subject: [PATCH 12/12] Update crates/bitwarden-wasm-internal/src/pure_crypto.rs Co-authored-by: Andreas Coroiu --- crates/bitwarden-wasm-internal/src/pure_crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 9244d99d2..85a34b49b 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -182,7 +182,7 @@ impl PureCrypto { .to_string()) } - /// Unwraps (decrypts) a wrapped SPKI der encoded encapsulation (public) key using a symmetric + /// Unwraps (decrypts) a wrapped SPKI DER encoded encapsulation (public) key using a symmetric /// wrapping key. pub fn unwrap_encapsulation_key( wrapped_key: String,