From bc5900bf36585677a284fac982e5abdbc4f61efd Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 11:27:37 +0200 Subject: [PATCH 001/124] Implement cose content format --- .../bitwarden-sm/src/projects/create.rs | 4 +- .../bitwarden-sm/src/projects/update.rs | 4 +- .../bitwarden-sm/src/secrets/create.rs | 17 +- .../bitwarden-sm/src/secrets/update.rs | 17 +- crates/bitwarden-core/src/mobile/crypto.rs | 6 +- .../src/secrets_manager/state.rs | 4 +- crates/bitwarden-crypto/README.md | 2 +- crates/bitwarden-crypto/src/cose.rs | 58 +++++- .../src/enc_string/symmetric.rs | 43 +++-- .../bitwarden-crypto/src/keys/device_key.rs | 4 +- .../src/keys/key_encryptable.rs | 25 ++- crates/bitwarden-crypto/src/keys/pin_key.rs | 11 +- crates/bitwarden-crypto/src/lib.rs | 1 + crates/bitwarden-crypto/src/store/context.rs | 22 ++- crates/bitwarden-crypto/src/store/mod.rs | 13 +- .../src/traits/encryptable.rs | 59 ++++-- .../bitwarden-exporters/src/encrypted_json.rs | 10 +- crates/bitwarden-exporters/src/export.rs | 6 +- crates/bitwarden-send/src/send.rs | 22 ++- crates/bitwarden-send/src/send_client.rs | 4 +- .../bitwarden-vault/src/cipher/attachment.rs | 12 +- crates/bitwarden-vault/src/cipher/card.rs | 19 +- crates/bitwarden-vault/src/cipher/cipher.rs | 172 +++++++++++++----- crates/bitwarden-vault/src/cipher/field.rs | 9 +- crates/bitwarden-vault/src/cipher/identity.rs | 43 +++-- .../bitwarden-vault/src/cipher/local_data.rs | 3 +- crates/bitwarden-vault/src/cipher/login.rs | 70 ++++--- .../bitwarden-vault/src/cipher/secure_note.rs | 3 +- crates/bitwarden-vault/src/cipher/ssh_key.rs | 11 +- crates/bitwarden-vault/src/folder.rs | 5 +- .../bitwarden-vault/src/password_history.rs | 5 +- .../src/pure_crypto.rs | 15 +- 32 files changed, 477 insertions(+), 222 deletions(-) diff --git a/bitwarden_license/bitwarden-sm/src/projects/create.rs b/bitwarden_license/bitwarden-sm/src/projects/create.rs index e7cb488ac..aafc4fb7a 100644 --- a/bitwarden_license/bitwarden-sm/src/projects/create.rs +++ b/bitwarden_license/bitwarden-sm/src/projects/create.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::ProjectCreateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::Encryptable; +use bitwarden_crypto::{ContentFormat, Encryptable}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -34,7 +34,7 @@ pub(crate) async fn create_project( .name .clone() .trim() - .encrypt(&mut key_store.context(), key)? + .encrypt(&mut key_store.context(), key, ContentFormat::Utf8)? .to_string(), }); diff --git a/bitwarden_license/bitwarden-sm/src/projects/update.rs b/bitwarden_license/bitwarden-sm/src/projects/update.rs index b1da9aade..ae79c670f 100644 --- a/bitwarden_license/bitwarden-sm/src/projects/update.rs +++ b/bitwarden_license/bitwarden-sm/src/projects/update.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::ProjectUpdateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::Encryptable; +use bitwarden_crypto::{ContentFormat, Encryptable}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -36,7 +36,7 @@ pub(crate) async fn update_project( .name .clone() .trim() - .encrypt(&mut key_store.context(), key)? + .encrypt(&mut key_store.context(), key, ContentFormat::Utf8)? .to_string(), }); diff --git a/bitwarden_license/bitwarden-sm/src/secrets/create.rs b/bitwarden_license/bitwarden-sm/src/secrets/create.rs index bd3c96215..75e8837b9 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/create.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/create.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::SecretCreateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::Encryptable; +use bitwarden_crypto::{ContentFormat, Encryptable}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -40,13 +40,22 @@ pub(crate) async fn create_secret( let secret = { let mut ctx = key_store.context(); Some(SecretCreateRequestModel { - key: input.key.clone().trim().encrypt(&mut ctx, key)?.to_string(), - value: input.value.clone().encrypt(&mut ctx, key)?.to_string(), + key: input + .key + .clone() + .trim() + .encrypt(&mut ctx, key, ContentFormat::Utf8)? + .to_string(), + value: input + .value + .clone() + .encrypt(&mut ctx, key, ContentFormat::Utf8)? + .to_string(), note: input .note .clone() .trim() - .encrypt(&mut ctx, key)? + .encrypt(&mut ctx, key, ContentFormat::Utf8)? .to_string(), project_ids: input.project_ids.clone(), access_policies_requests: None, diff --git a/bitwarden_license/bitwarden-sm/src/secrets/update.rs b/bitwarden_license/bitwarden-sm/src/secrets/update.rs index 462320d62..03151e8ca 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/update.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/update.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::SecretUpdateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::Encryptable; +use bitwarden_crypto::{ContentFormat, Encryptable}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -39,13 +39,22 @@ pub(crate) async fn update_secret( let secret = { let mut ctx = key_store.context(); Some(SecretUpdateRequestModel { - key: input.key.clone().trim().encrypt(&mut ctx, key)?.to_string(), - value: input.value.clone().encrypt(&mut ctx, key)?.to_string(), + key: input + .key + .clone() + .trim() + .encrypt(&mut ctx, key, ContentFormat::Utf8)? + .to_string(), + value: input + .value + .clone() + .encrypt(&mut ctx, key, ContentFormat::Utf8)? + .to_string(), note: input .note .clone() .trim() - .encrypt(&mut ctx, key)? + .encrypt(&mut ctx, key, ContentFormat::Utf8)? .to_string(), project_ids: input.project_ids.clone(), access_policies_requests: None, diff --git a/crates/bitwarden-core/src/mobile/crypto.rs b/crates/bitwarden-core/src/mobile/crypto.rs index 6e2905ce8..83fcb0b06 100644 --- a/crates/bitwarden-core/src/mobile/crypto.rs +++ b/crates/bitwarden-core/src/mobile/crypto.rs @@ -33,7 +33,7 @@ pub enum MobileCryptoError { Crypto(#[from] bitwarden_crypto::CryptoError), } -/// State used for initializing the user cryptographic state. +/// thState used for initializing the user cryptographic state. #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] @@ -329,7 +329,7 @@ pub(super) fn derive_pin_key( Ok(DerivePinKeyResponse { pin_protected_user_key, - encrypted_pin: pin.encrypt_with_key(user_key)?, + encrypted_pin: pin.encrypt_with_key(user_key, &bitwarden_crypto::ContentFormat::Utf8)?, }) } @@ -860,7 +860,7 @@ mod tests { let invalid_private_key = "bad_key" .to_string() .into_bytes() - .encrypt_with_key(&user_key.0) + .encrypt_with_key(&user_key.0, &bitwarden_crypto::ContentFormat::Utf8) .unwrap(); let request = VerifyAsymmetricKeysRequest { diff --git a/crates/bitwarden-core/src/secrets_manager/state.rs b/crates/bitwarden-core/src/secrets_manager/state.rs index 39e25ab78..45b298403 100644 --- a/crates/bitwarden-core/src/secrets_manager/state.rs +++ b/crates/bitwarden-core/src/secrets_manager/state.rs @@ -5,7 +5,7 @@ use std::{fmt::Debug, path::Path}; -use bitwarden_crypto::{EncString, KeyDecryptable, KeyEncryptable}; +use bitwarden_crypto::{ContentFormat, EncString, KeyDecryptable, KeyEncryptable}; use serde::{Deserialize, Serialize}; use crate::auth::AccessToken; @@ -73,7 +73,7 @@ pub(crate) fn set( ) -> Result<(), StateFileError> { let serialized_state: String = serde_json::to_string(&state)?; let encrypted_state: EncString = - serialized_state.encrypt_with_key(&access_token.encryption_key)?; + serialized_state.encrypt_with_key(&access_token.encryption_key, &ContentFormat::Utf8)?; let state_string: String = encrypted_state.to_string(); Ok(std::fs::write(state_file, state_string)?) diff --git a/crates/bitwarden-crypto/README.md b/crates/bitwarden-crypto/README.md index c568f475c..ca96c5d11 100644 --- a/crates/bitwarden-crypto/README.md +++ b/crates/bitwarden-crypto/README.md @@ -13,7 +13,7 @@ secure. ## Example: ```rust -use bitwarden_crypto::{SymmetricCryptoKey, KeyEncryptable, KeyDecryptable, CryptoError}; +use bitwarden_crypto::{SymmetricCryptoKey, TypedKeyEncryptable, KeyDecryptable, CryptoError}; async fn example() -> Result<(), CryptoError> { let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index c98a9f89d..b29a71130 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -3,8 +3,14 @@ //! unless there is a a clear benefit, such as a clear cryptographic benefit, which MUST //! be documented publicly. -use coset::{iana, CborSerializable, Label}; +use coset::{ + iana::{self, CoapContentFormat}, + CborSerializable, Label, +}; use generic_array::GenericArray; +use serde::{Deserialize, Serialize}; +#[cfg(feature = "wasm")] +use tsify_next::Tsify; use typenum::U32; use crate::{ @@ -15,22 +21,65 @@ use crate::{ /// to be able to randomly generate nonces, and to not have to worry about key wearout. Since /// the draft was never published as an RFC, we use a private-use value for the algorithm. pub(crate) const XCHACHA20_POLY1305: i64 = -70000; +const XCHACHA20_TEXT_PAD_BLOCK_SIZE: usize = 32; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] +pub enum ContentFormat { + Utf8, + Pkcs8, + CoseKey, + OctetStream, + /// Domain object should never be serialized. It is used to indicate when we call an encrypt + /// operation on a complex object that consists of multiple, individually encrypted fields + DomainObject, +} + +/// Pads bytes to a minimum length using PKCS7-like padding +fn pad_bytes(bytes: &mut Vec, block_size: usize) { + let padding_len = block_size - (bytes.len() % block_size); + let padded_length = padding_len + bytes.len(); + bytes.resize(padded_length, padding_len as u8); +} /// Encrypts a plaintext message using XChaCha20Poly1305 and returns a COSE Encrypt0 message pub(crate) fn encrypt_xchacha20_poly1305( plaintext: &[u8], key: &crate::XChaCha20Poly1305Key, + content_format: &ContentFormat, ) -> Result, CryptoError> { - let mut protected_header = coset::HeaderBuilder::new().build(); + let protected_header = coset::HeaderBuilder::new(); + let protected_header = match content_format { + // UTF-8 directly would leak the plaintext size. This is not acceptable for certain data + // (passwords). + ContentFormat::Utf8 => protected_header.content_type("application/utf8-padded".to_string()), + ContentFormat::Pkcs8 => protected_header.content_format(CoapContentFormat::Pkcs8), + ContentFormat::CoseKey => protected_header.content_format(CoapContentFormat::CoseKey), + ContentFormat::OctetStream => { + protected_header.content_format(CoapContentFormat::OctetStream) + } + // This should panic, and should never be implemented to be reachable! + ContentFormat::DomainObject => unreachable!(), + }; + let mut protected_header = protected_header.build(); // This should be adjusted to use the builder pattern once implemented in coset. // The related coset upstream issue is: // https://github.com/google/coset/issues/105 protected_header.alg = Some(coset::Algorithm::PrivateUse(XCHACHA20_POLY1305)); + let encoded_plaintext = if *content_format == ContentFormat::Utf8 { + // Pad the data to a block size in order to hide plaintext length + let mut plaintext = plaintext.to_vec(); + pad_bytes(&mut plaintext, XCHACHA20_TEXT_PAD_BLOCK_SIZE); + plaintext + } else { + plaintext.to_vec() + }; + let mut nonce = [0u8; xchacha20::NONCE_SIZE]; let cose_encrypt0 = coset::CoseEncrypt0Builder::new() .protected(protected_header) - .create_ciphertext(plaintext, &[], |data, aad| { + .create_ciphertext(&encoded_plaintext, &[], |data, aad| { let ciphertext = crate::xchacha20::encrypt_xchacha20_poly1305(&(*key.enc_key).into(), data, aad); nonce = ciphertext.nonce(); @@ -125,7 +174,8 @@ mod test { }; let plaintext = b"Hello, world!"; - let encrypted = encrypt_xchacha20_poly1305(plaintext, key).unwrap(); + let encrypted = + encrypt_xchacha20_poly1305(plaintext, key, &ContentFormat::OctetStream).unwrap(); let decrypted = decrypt_xchacha20_poly1305(&encrypted, key).unwrap(); assert_eq!(decrypted, plaintext); } diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index 33d287e58..9c9b86d42 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -7,7 +7,8 @@ use serde::Deserialize; use super::{check_length, from_b64, from_b64_vec, split_enc_string}; use crate::{ error::{CryptoError, EncStringParseError, Result, UnsupportedOperation}, - Aes256CbcHmacKey, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, XChaCha20Poly1305Key, + Aes256CbcHmacKey, ContentFormat, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, + XChaCha20Poly1305Key, }; #[cfg(feature = "wasm")] @@ -258,8 +259,9 @@ impl EncString { pub(crate) fn encrypt_xchacha20_poly1305( data_dec: &[u8], key: &XChaCha20Poly1305Key, + content_format: &ContentFormat, ) -> Result { - let data = crate::cose::encrypt_xchacha20_poly1305(data_dec, key)?; + let data = crate::cose::encrypt_xchacha20_poly1305(data_dec, key, content_format)?; Ok(EncString::Cose_Encrypt0_B64 { data }) } @@ -274,11 +276,15 @@ impl EncString { } impl KeyEncryptable for &[u8] { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { + fn encrypt_with_key( + self, + key: &SymmetricCryptoKey, + content_format: &ContentFormat, + ) -> Result { match key { SymmetricCryptoKey::Aes256CbcHmacKey(key) => EncString::encrypt_aes256_hmac(self, key), SymmetricCryptoKey::XChaCha20Poly1305Key(inner_key) => { - EncString::encrypt_xchacha20_poly1305(self, inner_key) + EncString::encrypt_xchacha20_poly1305(self, inner_key, content_format) } SymmetricCryptoKey::Aes256CbcKey(_) => Err(CryptoError::OperationNotSupported( UnsupportedOperation::EncryptionNotImplementedForKey, @@ -311,14 +317,14 @@ impl KeyDecryptable> for EncString { } impl KeyEncryptable for String { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { - self.as_bytes().encrypt_with_key(key) + fn encrypt_with_key(self, key: &SymmetricCryptoKey, content_format: &ContentFormat) -> Result { + self.as_bytes().encrypt_with_key(key, content_format) } } impl KeyEncryptable for &str { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { - self.as_bytes().encrypt_with_key(key) + fn encrypt_with_key(self, key: &SymmetricCryptoKey, content_format: &ContentFormat) -> Result { + self.as_bytes().encrypt_with_key(key, content_format) } } @@ -347,8 +353,8 @@ mod tests { use super::EncString; use crate::{ - derive_symmetric_key, CryptoError, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, - KEY_ID_SIZE, + derive_symmetric_key, ContentFormat, CryptoError, KeyDecryptable, KeyEncryptable, + SymmetricCryptoKey, KEY_ID_SIZE, }; #[test] @@ -361,7 +367,10 @@ mod tests { }); let test_string = "encrypted_test_string"; - let cipher = test_string.to_owned().encrypt_with_key(&key).unwrap(); + let cipher = test_string + .to_owned() + .encrypt_with_key(&key, &ContentFormat::Utf8) + .unwrap(); let decrypted_str: String = cipher.decrypt_with_key(&key).unwrap(); assert_eq!(decrypted_str, test_string); } @@ -371,7 +380,10 @@ mod tests { let key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test")); let test_string = "encrypted_test_string"; - let cipher = test_string.to_owned().encrypt_with_key(&key).unwrap(); + let cipher = test_string + .to_string() + .encrypt_with_key(&key, &ContentFormat::Utf8) + .unwrap(); let decrypted_str: String = cipher.decrypt_with_key(&key).unwrap(); assert_eq!(decrypted_str, test_string); @@ -381,8 +393,11 @@ mod tests { fn test_enc_string_ref_roundtrip() { let key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test")); - let test_string = "encrypted_test_string"; - let cipher = test_string.encrypt_with_key(&key).unwrap(); + let test_string: &'static str = "encrypted_test_string"; + let cipher = test_string + .to_string() + .encrypt_with_key(&key, &ContentFormat::Utf8) + .unwrap(); let decrypted_str: String = cipher.decrypt_with_key(&key).unwrap(); assert_eq!(decrypted_str, test_string); diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index 6574ea91b..8d5456e87 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -39,11 +39,11 @@ impl DeviceKey { let protected_device_public_key = device_private_key .to_public_der()? - .encrypt_with_key(user_key)?; + .encrypt_with_key(user_key, &crate::cose::ContentFormat::OctetStream)?; let protected_device_private_key = device_private_key .to_der()? - .encrypt_with_key(&device_key.0)?; + .encrypt_with_key(&device_key.0, &crate::cose::ContentFormat::Pkcs8)?; Ok(TrustDeviceResponse { device_key: device_key.to_base64(), diff --git a/crates/bitwarden-crypto/src/keys/key_encryptable.rs b/crates/bitwarden-crypto/src/keys/key_encryptable.rs index 7ddf689d8..63b24f070 100644 --- a/crates/bitwarden-crypto/src/keys/key_encryptable.rs +++ b/crates/bitwarden-crypto/src/keys/key_encryptable.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, hash::Hash, sync::Arc}; use rayon::prelude::*; use uuid::Uuid; -use crate::{error::Result, CryptoError, SymmetricCryptoKey}; +use crate::{cose::ContentFormat, error::Result, CryptoError, SymmetricCryptoKey}; pub trait KeyContainer: Send + Sync { fn get_key(&self, org_id: &Option) -> Result<&SymmetricCryptoKey, CryptoError>; @@ -18,7 +18,7 @@ impl KeyContainer for Arc { pub trait CryptoKey {} pub trait KeyEncryptable { - fn encrypt_with_key(self, key: &Key) -> Result; + fn encrypt_with_key(self, key: &Key, content_format: &ContentFormat) -> Result; } pub trait KeyDecryptable { @@ -28,8 +28,9 @@ pub trait KeyDecryptable { impl, Key: CryptoKey, Output> KeyEncryptable> for Option { - fn encrypt_with_key(self, key: &Key) -> Result> { - self.map(|e| e.encrypt_with_key(key)).transpose() + fn encrypt_with_key(self, key: &Key, content_format: &ContentFormat) -> Result> { + self.map(|e| e.encrypt_with_key(key, content_format)) + .transpose() } } @@ -44,8 +45,8 @@ impl, Key: CryptoKey, Output> KeyDecryptable, Key: CryptoKey, Output> KeyEncryptable for Box { - fn encrypt_with_key(self, key: &Key) -> Result { - (*self).encrypt_with_key(key) + fn encrypt_with_key(self, key: &Key, content_format: &ContentFormat) -> Result { + (*self).encrypt_with_key(key, content_format) } } @@ -63,9 +64,9 @@ impl< Output: Send + Sync, > KeyEncryptable> for Vec { - fn encrypt_with_key(self, key: &Key) -> Result> { + fn encrypt_with_key(self, key: &Key, content_format: &ContentFormat) -> Result> { self.into_par_iter() - .map(|e| e.encrypt_with_key(key)) + .map(|e| e.encrypt_with_key(key, content_format)) .collect() } } @@ -90,9 +91,13 @@ impl< Id: Hash + Eq + Send + Sync, > KeyEncryptable> for HashMap { - fn encrypt_with_key(self, key: &Key) -> Result> { + fn encrypt_with_key( + self, + key: &Key, + content_format: &ContentFormat, + ) -> Result> { self.into_par_iter() - .map(|(id, e)| Ok((id, e.encrypt_with_key(key)?))) + .map(|(id, e)| Ok((id, e.encrypt_with_key(key, content_format)?))) .collect() } } diff --git a/crates/bitwarden-crypto/src/keys/pin_key.rs b/crates/bitwarden-crypto/src/keys/pin_key.rs index e069a4c62..c8421e244 100644 --- a/crates/bitwarden-crypto/src/keys/pin_key.rs +++ b/crates/bitwarden-crypto/src/keys/pin_key.rs @@ -4,7 +4,8 @@ use super::{ utils::stretch_key, }; use crate::{ - keys::key_encryptable::CryptoKey, EncString, KeyEncryptable, Result, SymmetricCryptoKey, + keys::key_encryptable::CryptoKey, ContentFormat, EncString, KeyEncryptable, Result, + SymmetricCryptoKey, }; /// Pin Key. @@ -32,14 +33,14 @@ impl PinKey { impl CryptoKey for PinKey {} impl KeyEncryptable for &[u8] { - fn encrypt_with_key(self, key: &PinKey) -> Result { + fn encrypt_with_key(self, key: &PinKey, content_format: &ContentFormat) -> Result { let stretched_key = SymmetricCryptoKey::Aes256CbcHmacKey(stretch_key(&key.0 .0)?); - self.encrypt_with_key(&stretched_key) + self.encrypt_with_key(&stretched_key, content_format) } } impl KeyEncryptable for String { - fn encrypt_with_key(self, key: &PinKey) -> Result { - self.as_bytes().encrypt_with_key(key) + fn encrypt_with_key(self, key: &PinKey, content_format: &ContentFormat) -> Result { + self.as_bytes().encrypt_with_key(key, content_format) } } diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index d3a0e304d..33ca433ed 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -32,6 +32,7 @@ mod store; pub use store::{KeyStore, KeyStoreContext}; mod cose; mod traits; +pub use cose::ContentFormat; mod xchacha20; pub use traits::{Decryptable, Encryptable, IdentifyKey, KeyId, KeyIds}; pub use zeroizing_alloc::ZeroAlloc as ZeroizingAllocator; diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 261067af8..3bbecb342 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -8,8 +8,8 @@ use zeroize::Zeroizing; use super::KeyStoreInner; use crate::{ derive_shareable_key, error::UnsupportedOperation, store::backend::StoreBackend, - AsymmetricCryptoKey, CryptoError, EncString, KeyId, KeyIds, Result, SymmetricCryptoKey, - UnsignedSharedKey, + AsymmetricCryptoKey, ContentFormat, CryptoError, EncString, KeyId, KeyIds, Result, + SymmetricCryptoKey, UnsignedSharedKey, }; /// The context of a crypto operation using [super::KeyStore] @@ -182,6 +182,7 @@ impl KeyStoreContext<'_, Ids> { .encrypt_data_with_symmetric_key( wrapping_key, key_to_wrap_instance.to_encoded().as_slice(), + ContentFormat::OctetStream, ), _ => Err(CryptoError::OperationNotSupported( UnsupportedOperation::EncryptionNotImplementedForKey, @@ -362,6 +363,7 @@ impl KeyStoreContext<'_, Ids> { &self, key: Ids::Symmetric, data: &[u8], + content_format: ContentFormat, ) -> Result { let key = self.get_symmetric_key(key)?; match key { @@ -370,7 +372,7 @@ impl KeyStoreContext<'_, Ids> { )), SymmetricCryptoKey::Aes256CbcHmacKey(key) => EncString::encrypt_aes256_hmac(data, key), SymmetricCryptoKey::XChaCha20Poly1305Key(key) => { - EncString::encrypt_xchacha20_poly1305(data, key) + EncString::encrypt_xchacha20_poly1305(data, key, &content_format) } } } @@ -380,7 +382,11 @@ impl KeyStoreContext<'_, Ids> { #[allow(deprecated)] mod tests { use crate::{ - store::{tests::DataView, KeyStore}, + cose::ContentFormat, + store::{ + tests::{Data, DataView}, + KeyStore, + }, traits::tests::{TestIds, TestSymmKey}, Decryptable, Encryptable, SymmetricCryptoKey, }; @@ -402,7 +408,9 @@ mod tests { // Encrypt some data with the key let data = DataView("Hello, World!".to_string(), key_a0_id); - let _encrypted = data.encrypt(&mut store.context(), key_a0_id).unwrap(); + let _encrypted: Data = data + .encrypt(&mut store.context(), key_a0_id, ContentFormat::DomainObject) + .unwrap(); } #[test] @@ -440,7 +448,9 @@ mod tests { // with one and decrypt with the other let data = DataView("Hello, World!".to_string(), key_2_id); - let encrypted = data.encrypt(&mut ctx, key_2_id).unwrap(); + let encrypted = data + .encrypt(&mut ctx, key_2_id, ContentFormat::OctetStream) + .unwrap(); let decrypted1 = encrypted.decrypt(&mut ctx, key_2_id).unwrap(); let decrypted2 = encrypted.decrypt(&mut ctx, new_key_id).unwrap(); diff --git a/crates/bitwarden-crypto/src/store/mod.rs b/crates/bitwarden-crypto/src/store/mod.rs index f447f58b2..d9a6b6c7d 100644 --- a/crates/bitwarden-crypto/src/store/mod.rs +++ b/crates/bitwarden-crypto/src/store/mod.rs @@ -26,7 +26,7 @@ use std::sync::{Arc, RwLock}; use rayon::prelude::*; -use crate::{Decryptable, Encryptable, IdentifyKey, KeyId, KeyIds}; +use crate::{cose::ContentFormat, Decryptable, Encryptable, IdentifyKey, KeyId, KeyIds}; mod backend; mod context; @@ -75,8 +75,8 @@ pub use context::KeyStoreContext; /// } /// } /// impl Encryptable for Data { -/// fn encrypt(&self, ctx: &mut KeyStoreContext, key: SymmKeyId) -> Result { -/// self.0.encrypt(ctx, key) +/// fn encrypt(&self, ctx: &mut KeyStoreContext, key: SymmKeyId, content_format: ContentFormat) -> Result { +/// self.0.encrypt(ctx, key, content_format) /// } /// } /// @@ -214,7 +214,7 @@ impl KeyStore { data: Data, ) -> Result { let key = data.key_identifier(); - data.encrypt(&mut self.context(), key) + data.encrypt(&mut self.context(), key, ContentFormat::OctetStream) } /// Decrypt a list of items using this key store. The keys returned by @@ -272,7 +272,7 @@ impl KeyStore { for item in chunk { let key = item.key_identifier(); - result.push(item.encrypt(&mut ctx, key)); + result.push(item.encrypt(&mut ctx, key, ContentFormat::DomainObject)); ctx.clear_local(); } @@ -329,8 +329,9 @@ pub(crate) mod tests { &self, ctx: &mut KeyStoreContext, key: TestSymmKey, + _content_format: crate::cose::ContentFormat, ) -> Result { - Ok(Data(self.0.encrypt(ctx, key)?, key)) + Ok(Data(self.0.encrypt(ctx, key, _content_format)?, key)) } } diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index 84d4b7bdc..259015ed8 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -4,7 +4,12 @@ use crate::{store::KeyStoreContext, CryptoError, EncString, KeyId, KeyIds}; /// Implementations should generally consist of calling [Encryptable::encrypt] for all the fields of /// the type. pub trait Encryptable { - fn encrypt(&self, ctx: &mut KeyStoreContext, key: Key) -> Result; + fn encrypt( + &self, + ctx: &mut KeyStoreContext, + key: Key, + content_format: crate::cose::ContentFormat, + ) -> Result; } impl Encryptable for &[u8] { @@ -12,8 +17,9 @@ impl Encryptable for &[u8] { &self, ctx: &mut KeyStoreContext, key: Ids::Symmetric, + content_format: crate::cose::ContentFormat, ) -> Result { - ctx.encrypt_data_with_symmetric_key(key, self) + ctx.encrypt_data_with_symmetric_key(key, self, content_format) } } @@ -22,8 +28,9 @@ impl Encryptable for Vec { &self, ctx: &mut KeyStoreContext, key: Ids::Symmetric, + content_format: crate::cose::ContentFormat, ) -> Result { - ctx.encrypt_data_with_symmetric_key(key, self) + ctx.encrypt_data_with_symmetric_key(key, self, content_format) } } @@ -32,8 +39,9 @@ impl Encryptable for &str { &self, ctx: &mut KeyStoreContext, key: Ids::Symmetric, + content_format: crate::cose::ContentFormat, ) -> Result { - self.as_bytes().encrypt(ctx, key) + self.as_bytes().encrypt(ctx, key, content_format) } } @@ -42,8 +50,9 @@ impl Encryptable for String { &self, ctx: &mut KeyStoreContext, key: Ids::Symmetric, + content_format: crate::cose::ContentFormat, ) -> Result { - self.as_bytes().encrypt(ctx, key) + self.as_bytes().encrypt(ctx, key, content_format) } } @@ -54,9 +63,10 @@ impl, Output> &self, ctx: &mut KeyStoreContext, key: Key, + content_format: crate::cose::ContentFormat, ) -> Result, CryptoError> { self.as_ref() - .map(|value| value.encrypt(ctx, key)) + .map(|value| value.encrypt(ctx, key, content_format)) .transpose() } } @@ -68,16 +78,19 @@ impl, Output> &self, ctx: &mut KeyStoreContext, key: Key, + content_format: crate::cose::ContentFormat, ) -> Result, CryptoError> { - self.iter().map(|value| value.encrypt(ctx, key)).collect() + self.iter() + .map(|value| value.encrypt(ctx, key, content_format)) + .collect() } } #[cfg(test)] mod tests { use crate::{ - traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, KeyStore, - SymmetricCryptoKey, + cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, + KeyStore, SymmetricCryptoKey, }; fn test_store() -> KeyStore { @@ -109,8 +122,12 @@ mod tests { let vec_data = vec![1, 2, 3, 4, 5]; let slice_data: &[u8] = &vec_data; - let vec_encrypted = vec_data.encrypt(&mut ctx, key).unwrap(); - let slice_encrypted = slice_data.encrypt(&mut ctx, key).unwrap(); + let vec_encrypted = vec_data + .encrypt(&mut ctx, key, ContentFormat::OctetStream) + .unwrap(); + let slice_encrypted = slice_data + .encrypt(&mut ctx, key, ContentFormat::OctetStream) + .unwrap(); let vec_decrypted: Vec = vec_encrypted.decrypt(&mut ctx, key).unwrap(); let slice_decrypted: Vec = slice_encrypted.decrypt(&mut ctx, key).unwrap(); @@ -128,8 +145,12 @@ mod tests { let string_data = "Hello, World!".to_string(); let str_data: &str = string_data.as_str(); - let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap(); - let str_encrypted = str_data.encrypt(&mut ctx, key).unwrap(); + let string_encrypted = string_data + .encrypt(&mut ctx, key, ContentFormat::OctetStream) + .unwrap(); + let str_encrypted = str_data + .encrypt(&mut ctx, key, ContentFormat::OctetStream) + .unwrap(); let string_decrypted: String = string_encrypted.decrypt(&mut ctx, key).unwrap(); let str_decrypted: String = str_encrypted.decrypt(&mut ctx, key).unwrap(); @@ -146,7 +167,9 @@ mod tests { let string_data = Some("Hello, World!".to_string()); - let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap(); + let string_encrypted = string_data + .encrypt(&mut ctx, key, ContentFormat::OctetStream) + .unwrap(); let string_decrypted: Option = string_encrypted.decrypt(&mut ctx, key).unwrap(); @@ -160,13 +183,17 @@ mod tests { let key = TestSymmKey::A(0); let none_data: Option = None; - let string_encrypted = none_data.encrypt(&mut ctx, key).unwrap(); + let string_encrypted = none_data + .encrypt(&mut ctx, key, ContentFormat::OctetStream) + .unwrap(); assert_eq!(string_encrypted, None); // The None implementation will not do any decrypt operations, so it won't fail even if the // key doesn't exist let bad_key = TestSymmKey::B((0, 1)); - let string_encrypted_bad = none_data.encrypt(&mut ctx, bad_key).unwrap(); + let string_encrypted_bad = none_data + .encrypt(&mut ctx, bad_key, ContentFormat::OctetStream) + .unwrap(); assert_eq!(string_encrypted_bad, None); } } diff --git a/crates/bitwarden-exporters/src/encrypted_json.rs b/crates/bitwarden-exporters/src/encrypted_json.rs index 32f1c9307..081dc5d8f 100644 --- a/crates/bitwarden-exporters/src/encrypted_json.rs +++ b/crates/bitwarden-exporters/src/encrypted_json.rs @@ -1,5 +1,5 @@ use base64::{engine::general_purpose::STANDARD, Engine}; -use bitwarden_crypto::{generate_random_bytes, Kdf, KeyEncryptable, PinKey}; +use bitwarden_crypto::{generate_random_bytes, ContentFormat, Kdf, KeyEncryptable, PinKey}; use serde::Serialize; use thiserror::Error; use uuid::Uuid; @@ -57,8 +57,12 @@ pub(crate) fn export_encrypted_json( kdf_iterations, kdf_memory, kdf_parallelism, - enc_key_validation: enc_key_validation.encrypt_with_key(&key)?.to_string(), - data: decrypted_export.encrypt_with_key(&key)?.to_string(), + enc_key_validation: enc_key_validation + .encrypt_with_key(&key, &ContentFormat::Utf8)? + .to_string(), + data: decrypted_export + .encrypt_with_key(&key, &ContentFormat::Utf8)? + .to_string(), }; Ok(serde_json::to_string_pretty(&encrypted_export)?) diff --git a/crates/bitwarden-exporters/src/export.rs b/crates/bitwarden-exporters/src/export.rs index 234e1b3ca..f1a510638 100644 --- a/crates/bitwarden-exporters/src/export.rs +++ b/crates/bitwarden-exporters/src/export.rs @@ -80,7 +80,11 @@ fn encrypt_import( view.set_new_fido2_credentials(ctx, passkeys)?; } - let new_cipher = view.encrypt(ctx, view.key_identifier())?; + let new_cipher = view.encrypt( + ctx, + view.key_identifier(), + bitwarden_crypto::ContentFormat::DomainObject, + )?; Ok(new_cipher) } diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index a9f54ed4c..7c8bb4019 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -8,8 +8,8 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - generate_random_bytes, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, - KeyStoreContext, + generate_random_bytes, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, + IdentifyKey, KeyStoreContext, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -194,9 +194,10 @@ impl Encryptable for SendTextView { &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { Ok(SendText { - text: self.text.encrypt(ctx, key)?, + text: self.text.encrypt(ctx, key, ContentFormat::Utf8)?, hidden: self.hidden, }) } @@ -222,10 +223,11 @@ impl Encryptable for SendFileView { &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { Ok(SendFile { id: self.id.clone(), - file_name: self.file_name.encrypt(ctx, key)?, + file_name: self.file_name.encrypt(ctx, key, ContentFormat::Utf8)?, size: self.size.clone(), size_name: self.size_name.clone(), }) @@ -302,6 +304,7 @@ impl Encryptable for SendView { &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { // For sends, we first decrypt the send key with the user key, and stretch it to it's full // size For the rest of the fields, we ignore the provided SymmetricCryptoKey and @@ -325,17 +328,18 @@ impl Encryptable for SendView { id: self.id, access_id: self.access_id.clone(), - name: self.name.encrypt(ctx, send_key)?, - notes: self.notes.encrypt(ctx, send_key)?, - key: k.encrypt(ctx, key)?, + name: self.name.encrypt(ctx, send_key, ContentFormat::Utf8)?, + notes: self.notes.encrypt(ctx, send_key, ContentFormat::Utf8)?, + // In the future, this should support cose key content type + key: k.encrypt(ctx, key, ContentFormat::OctetStream)?, password: self.new_password.as_ref().map(|password| { let password = bitwarden_crypto::pbkdf2(password.as_bytes(), &k, SEND_ITERATIONS); STANDARD.encode(password) }), r#type: self.r#type, - file: self.file.encrypt(ctx, send_key)?, - text: self.text.encrypt(ctx, send_key)?, + file: self.file.encrypt(ctx, send_key, ContentFormat::Utf8)?, + text: self.text.encrypt(ctx, send_key, ContentFormat::Utf8)?, max_access_count: self.max_access_count, access_count: self.access_count, diff --git a/crates/bitwarden-send/src/send_client.rs b/crates/bitwarden-send/src/send_client.rs index b30bb6f02..8095ce95c 100644 --- a/crates/bitwarden-send/src/send_client.rs +++ b/crates/bitwarden-send/src/send_client.rs @@ -1,7 +1,7 @@ use std::path::Path; use bitwarden_core::Client; -use bitwarden_crypto::{Decryptable, EncString, Encryptable, IdentifyKey}; +use bitwarden_crypto::{ContentFormat, Decryptable, EncString, Encryptable, IdentifyKey}; use thiserror::Error; use crate::{Send, SendListView, SendView}; @@ -115,7 +115,7 @@ impl SendClient { let key = Send::get_key(&mut ctx, &send.key, send.key_identifier())?; - let encrypted = buffer.encrypt(&mut ctx, key)?; + let encrypted = buffer.encrypt(&mut ctx, key, ContentFormat::OctetStream)?; Ok(encrypted.to_buffer()?) } } diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index 5d997f631..86229765d 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,6 +1,6 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, + ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -78,6 +78,7 @@ impl Encryptable for Attachment &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { let ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.cipher.key)?; @@ -86,7 +87,9 @@ impl Encryptable for Attachment // Because this is a new attachment, we have to generate a key for it, encrypt the contents // with it, and then encrypt the key with the cipher key let attachment_key = ctx.generate_symmetric_key(ATTACHMENT_KEY)?; - let encrypted_contents = self.contents.encrypt(ctx, attachment_key)?; + let encrypted_contents = + self.contents + .encrypt(ctx, attachment_key, ContentFormat::OctetStream)?; attachment.key = Some(ctx.wrap_symmetric_key(ciphers_key, attachment_key)?); let contents = encrypted_contents.to_buffer()?; @@ -96,7 +99,7 @@ impl Encryptable for Attachment attachment.size_name = Some(size_name(contents.len())); Ok(AttachmentEncryptResult { - attachment: attachment.encrypt(ctx, ciphers_key)?, + attachment: attachment.encrypt(ctx, ciphers_key, ContentFormat::DomainObject)?, contents, }) } @@ -137,13 +140,14 @@ impl Encryptable for AttachmentView { &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { Ok(Attachment { id: self.id.clone(), url: self.url.clone(), size: self.size.clone(), size_name: self.size_name.clone(), - file_name: self.file_name.encrypt(ctx, key)?, + file_name: self.file_name.encrypt(ctx, key, ContentFormat::Utf8)?, key: self.key.clone(), }) } diff --git a/crates/bitwarden-vault/src/cipher/card.rs b/crates/bitwarden-vault/src/cipher/card.rs index c33da8066..a1be406ee 100644 --- a/crates/bitwarden-vault/src/cipher/card.rs +++ b/crates/bitwarden-vault/src/cipher/card.rs @@ -1,6 +1,8 @@ use bitwarden_api_api::models::CipherCardModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; -use bitwarden_crypto::{CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext}; +use bitwarden_crypto::{ + ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, +}; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] use tsify_next::Tsify; @@ -55,14 +57,17 @@ impl Encryptable for CardView { &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { Ok(Card { - cardholder_name: self.cardholder_name.encrypt(ctx, key)?, - exp_month: self.exp_month.encrypt(ctx, key)?, - exp_year: self.exp_year.encrypt(ctx, key)?, - code: self.code.encrypt(ctx, key)?, - brand: self.brand.encrypt(ctx, key)?, - number: self.number.encrypt(ctx, key)?, + cardholder_name: self + .cardholder_name + .encrypt(ctx, key, ContentFormat::Utf8)?, + exp_month: self.exp_month.encrypt(ctx, key, ContentFormat::Utf8)?, + exp_year: self.exp_year.encrypt(ctx, key, ContentFormat::Utf8)?, + code: self.code.encrypt(ctx, key, ContentFormat::Utf8)?, + brand: self.brand.encrypt(ctx, key, ContentFormat::Utf8)?, + number: self.number.encrypt(ctx, key, ContentFormat::Utf8)?, }) } } diff --git a/crates/bitwarden-vault/src/cipher/cipher.rs b/crates/bitwarden-vault/src/cipher/cipher.rs index 36a1b011b..36f7c6f2e 100644 --- a/crates/bitwarden-vault/src/cipher/cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher.rs @@ -4,7 +4,7 @@ use bitwarden_core::{ require, MissingFieldError, VaultLockedError, }; use bitwarden_crypto::{ - CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, + ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, }; use bitwarden_error::bitwarden_error; use chrono::{DateTime, Utc}; @@ -215,6 +215,7 @@ impl Encryptable for CipherView { &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { let ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.key)?; @@ -231,23 +232,47 @@ impl Encryptable for CipherView { folder_id: cipher_view.folder_id, collection_ids: cipher_view.collection_ids, key: cipher_view.key, - name: cipher_view.name.encrypt(ctx, ciphers_key)?, - notes: cipher_view.notes.encrypt(ctx, ciphers_key)?, + name: cipher_view + .name + .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + notes: cipher_view + .notes + .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, r#type: cipher_view.r#type, - login: cipher_view.login.encrypt(ctx, ciphers_key)?, - identity: cipher_view.identity.encrypt(ctx, ciphers_key)?, - card: cipher_view.card.encrypt(ctx, ciphers_key)?, - secure_note: cipher_view.secure_note.encrypt(ctx, ciphers_key)?, - ssh_key: cipher_view.ssh_key.encrypt(ctx, ciphers_key)?, + login: cipher_view + .login + .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + identity: cipher_view + .identity + .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + card: cipher_view + .card + .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + secure_note: cipher_view + .secure_note + .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + ssh_key: cipher_view + .ssh_key + .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, favorite: cipher_view.favorite, reprompt: cipher_view.reprompt, organization_use_totp: cipher_view.organization_use_totp, edit: cipher_view.edit, view_password: cipher_view.view_password, - local_data: cipher_view.local_data.encrypt(ctx, ciphers_key)?, - attachments: cipher_view.attachments.encrypt(ctx, ciphers_key)?, - fields: cipher_view.fields.encrypt(ctx, ciphers_key)?, - password_history: cipher_view.password_history.encrypt(ctx, ciphers_key)?, + local_data: cipher_view + .local_data + .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + attachments: cipher_view + .attachments + .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + fields: cipher_view + .fields + .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + password_history: cipher_view.password_history.encrypt( + ctx, + ciphers_key, + ContentFormat::Utf8, + )?, creation_date: cipher_view.creation_date, deleted_date: cipher_view.deleted_date, revision_date: cipher_view.revision_date, @@ -453,7 +478,7 @@ impl CipherView { ctx: &mut KeyStoreContext, key: SymmetricKeyId, ) -> Result<(), CryptoError> { - let old_ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.key)?; + let old_ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.key.take())?; const NEW_KEY: SymmetricKeyId = SymmetricKeyId::Local("new_cipher_key"); @@ -489,8 +514,9 @@ impl CipherView { if let Some(attachments) = &mut self.attachments { for attachment in attachments { if let Some(attachment_key) = &mut attachment.key { - let dec_attachment_key: Vec = attachment_key.decrypt(ctx, old_key)?; - *attachment_key = dec_attachment_key.encrypt(ctx, new_key)?; + let tmp_attachment_key_id = SymmetricKeyId::Local("attachment_key"); + ctx.unwrap_symmetric_key(old_key, tmp_attachment_key_id, attachment_key)?; + *attachment_key = ctx.wrap_symmetric_key(new_key, tmp_attachment_key_id)?; } } } @@ -523,7 +549,8 @@ impl CipherView { if let Some(fido2_credentials) = &mut login.fido2_credentials { let dec_fido2_credentials: Vec = fido2_credentials.decrypt(ctx, old_key)?; - *fido2_credentials = dec_fido2_credentials.encrypt(ctx, new_key)?; + *fido2_credentials = + dec_fido2_credentials.encrypt(ctx, new_key, ContentFormat::DomainObject)?; } } Ok(()) @@ -544,8 +571,9 @@ impl CipherView { // If the cipher has a key, we need to re-encrypt it with the new organization key if let Some(cipher_key) = &mut self.key { - let dec_cipher_key: Vec = cipher_key.decrypt(ctx, old_key)?; - *cipher_key = dec_cipher_key.encrypt(ctx, new_key)?; + let tmp_cipher_key_id = SymmetricKeyId::Local("cipher_key"); + ctx.unwrap_symmetric_key(old_key, tmp_cipher_key_id, cipher_key)?; + *cipher_key = ctx.wrap_symmetric_key(new_key, tmp_cipher_key_id)?; } else { // If the cipher does not have a key, we need to reencrypt all attachment keys self.reencrypt_attachment_keys(ctx, old_key, new_key)?; @@ -565,7 +593,8 @@ impl CipherView { let ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.key)?; - require!(self.login.as_mut()).fido2_credentials = Some(creds.encrypt(ctx, ciphers_key)?); + require!(self.login.as_mut()).fido2_credentials = + Some(creds.encrypt(ctx, ciphers_key, ContentFormat::DomainObject)?); Ok(()) } @@ -796,18 +825,42 @@ mod tests { fn generate_fido2(ctx: &mut KeyStoreContext, key: SymmetricKeyId) -> Fido2Credential { Fido2Credential { - credential_id: "123".to_string().encrypt(ctx, key).unwrap(), - key_type: "public-key".to_string().encrypt(ctx, key).unwrap(), - key_algorithm: "ECDSA".to_string().encrypt(ctx, key).unwrap(), - key_curve: "P-256".to_string().encrypt(ctx, key).unwrap(), - key_value: "123".to_string().encrypt(ctx, key).unwrap(), - rp_id: "123".to_string().encrypt(ctx, key).unwrap(), + credential_id: "123" + .to_string() + .encrypt(ctx, key, ContentFormat::Utf8) + .unwrap(), + key_type: "public-key" + .to_string() + .encrypt(ctx, key, ContentFormat::Utf8) + .unwrap(), + key_algorithm: "ECDSA" + .to_string() + .encrypt(ctx, key, ContentFormat::Utf8) + .unwrap(), + key_curve: "P-256" + .to_string() + .encrypt(ctx, key, ContentFormat::Utf8) + .unwrap(), + key_value: "123" + .to_string() + .encrypt(ctx, key, ContentFormat::Utf8) + .unwrap(), + rp_id: "123" + .to_string() + .encrypt(ctx, key, ContentFormat::Utf8) + .unwrap(), user_handle: None, user_name: None, - counter: "123".to_string().encrypt(ctx, key).unwrap(), + counter: "123" + .to_string() + .encrypt(ctx, key, ContentFormat::Utf8) + .unwrap(), rp_name: None, user_display_name: None, - discoverable: "true".to_string().encrypt(ctx, key).unwrap(), + discoverable: "true" + .to_string() + .encrypt(ctx, key, ContentFormat::Utf8) + .unwrap(), creation_date: "2024-06-07T14:12:36.150Z".parse().unwrap(), } } @@ -944,11 +997,14 @@ mod tests { .unwrap(); // Make sure that the cipher key is decryptable - let _: Vec = original_cipher - .key - .unwrap() - .decrypt(&mut key_store.context(), SymmetricKeyId::User) - .unwrap(); + let wrapped_key = original_cipher.key.unwrap(); + let mut ctx = key_store.context(); + ctx.unwrap_symmetric_key( + SymmetricKeyId::User, + SymmetricKeyId::Local("test_cipher_key"), + &wrapped_key, + ) + .unwrap(); } #[test] @@ -1090,12 +1146,20 @@ mod tests { // Check that the attachment key has been re-encrypted with the org key, // and the value matches with the original attachment key let new_attachment_key = cipher.attachments.unwrap()[0].key.clone().unwrap(); - let new_attachment_key_dec: Vec<_> = new_attachment_key - .decrypt(&mut key_store.context(), org_key) + let mut ctx = key_store.context(); + let new_attachment_key_id = ctx + .unwrap_symmetric_key( + org_key, + SymmetricKeyId::Local("test_attachment_key"), + &new_attachment_key, + ) + .unwrap(); + #[allow(deprecated)] + let new_attachment_key_dec = ctx + .dangerous_get_symmetric_key(new_attachment_key_id) .unwrap(); - let new_attachment_key_dec: SymmetricCryptoKey = new_attachment_key_dec.try_into().unwrap(); - assert_eq!(new_attachment_key_dec, attachment_key_val); + assert_eq!(*new_attachment_key_dec, attachment_key_val); let cred2: Fido2CredentialFullView = cipher .login @@ -1152,19 +1216,20 @@ mod tests { cipher.move_to_organization(&mut ctx, org).unwrap(); // Check that the cipher key has been re-encrypted with the org key, - let new_cipher_key_dec: Vec<_> = cipher - .key - .clone() - .unwrap() - .decrypt(&mut ctx, org_key) + let wrapped_new_cipher_key = cipher.key.clone().unwrap(); + let new_cipher_key_dec = ctx + .unwrap_symmetric_key( + org_key, + SymmetricKeyId::Local("test_cipher_key"), + &wrapped_new_cipher_key, + ) .unwrap(); - - let new_cipher_key_dec: SymmetricCryptoKey = new_cipher_key_dec.try_into().unwrap(); - + #[allow(deprecated)] + let new_cipher_key_dec = ctx.dangerous_get_symmetric_key(new_cipher_key_dec).unwrap(); #[allow(deprecated)] let cipher_key_val = ctx.dangerous_get_symmetric_key(cipher_key).unwrap(); - assert_eq!(new_cipher_key_dec, *cipher_key_val); + assert_eq!(new_cipher_key_dec, cipher_key_val); // Check that the attachment key hasn't changed assert_eq!( @@ -1289,9 +1354,18 @@ mod tests { let mut ctx = key_store.context(); let original_subtitle = "SHA256:1JjFjvPRkj1Gbf2qRP1dgHiIzEuNAEvp+92x99jw3K0".to_string(); - let fingerprint_encrypted = original_subtitle.to_owned().encrypt(&mut ctx, key).unwrap(); - let private_key_encrypted = "".to_string().encrypt(&mut ctx, key).unwrap(); - let public_key_encrypted = "".to_string().encrypt(&mut ctx, key).unwrap(); + let fingerprint_encrypted = original_subtitle + .to_owned() + .encrypt(&mut ctx, key, ContentFormat::Utf8) + .unwrap(); + let private_key_encrypted = "" + .to_string() + .encrypt(&mut ctx, key, ContentFormat::Utf8) + .unwrap(); + let public_key_encrypted = "" + .to_string() + .encrypt(&mut ctx, key, ContentFormat::Utf8) + .unwrap(); let ssh_key_cipher = Cipher { id: Some("090c19ea-a61a-4df6-8963-262b97bc6266".parse().unwrap()), organization_id: None, @@ -1301,7 +1375,7 @@ mod tests { key: None, name: "My test ssh key" .to_string() - .encrypt(&mut ctx, key) + .encrypt(&mut ctx, key, ContentFormat::Utf8) .unwrap(), notes: None, login: None, diff --git a/crates/bitwarden-vault/src/cipher/field.rs b/crates/bitwarden-vault/src/cipher/field.rs index 30df96a21..57002d541 100644 --- a/crates/bitwarden-vault/src/cipher/field.rs +++ b/crates/bitwarden-vault/src/cipher/field.rs @@ -3,7 +3,9 @@ use bitwarden_core::{ key_management::{KeyIds, SymmetricKeyId}, require, }; -use bitwarden_crypto::{CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext}; +use bitwarden_crypto::{ + ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, +}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; #[cfg(feature = "wasm")] @@ -54,10 +56,11 @@ impl Encryptable for FieldView { &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { Ok(Field { - name: self.name.encrypt(ctx, key)?, - value: self.value.encrypt(ctx, key)?, + name: self.name.encrypt(ctx, key, ContentFormat::Utf8)?, + value: self.value.encrypt(ctx, key, ContentFormat::Utf8)?, r#type: self.r#type, linked_id: self.linked_id, }) diff --git a/crates/bitwarden-vault/src/cipher/identity.rs b/crates/bitwarden-vault/src/cipher/identity.rs index 88edecb40..066ea338a 100644 --- a/crates/bitwarden-vault/src/cipher/identity.rs +++ b/crates/bitwarden-vault/src/cipher/identity.rs @@ -1,6 +1,8 @@ use bitwarden_api_api::models::CipherIdentityModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; -use bitwarden_crypto::{CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext}; +use bitwarden_crypto::{ + ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, +}; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] use tsify_next::Tsify; @@ -62,26 +64,29 @@ impl Encryptable for IdentityView { &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { Ok(Identity { - title: self.title.encrypt(ctx, key)?, - first_name: self.first_name.encrypt(ctx, key)?, - middle_name: self.middle_name.encrypt(ctx, key)?, - last_name: self.last_name.encrypt(ctx, key)?, - address1: self.address1.encrypt(ctx, key)?, - address2: self.address2.encrypt(ctx, key)?, - address3: self.address3.encrypt(ctx, key)?, - city: self.city.encrypt(ctx, key)?, - state: self.state.encrypt(ctx, key)?, - postal_code: self.postal_code.encrypt(ctx, key)?, - country: self.country.encrypt(ctx, key)?, - company: self.company.encrypt(ctx, key)?, - email: self.email.encrypt(ctx, key)?, - phone: self.phone.encrypt(ctx, key)?, - ssn: self.ssn.encrypt(ctx, key)?, - username: self.username.encrypt(ctx, key)?, - passport_number: self.passport_number.encrypt(ctx, key)?, - license_number: self.license_number.encrypt(ctx, key)?, + title: self.title.encrypt(ctx, key, ContentFormat::Utf8)?, + first_name: self.first_name.encrypt(ctx, key, ContentFormat::Utf8)?, + middle_name: self.middle_name.encrypt(ctx, key, ContentFormat::Utf8)?, + last_name: self.last_name.encrypt(ctx, key, ContentFormat::Utf8)?, + address1: self.address1.encrypt(ctx, key, ContentFormat::Utf8)?, + address2: self.address2.encrypt(ctx, key, ContentFormat::Utf8)?, + address3: self.address3.encrypt(ctx, key, ContentFormat::Utf8)?, + city: self.city.encrypt(ctx, key, ContentFormat::Utf8)?, + state: self.state.encrypt(ctx, key, ContentFormat::Utf8)?, + postal_code: self.postal_code.encrypt(ctx, key, ContentFormat::Utf8)?, + country: self.country.encrypt(ctx, key, ContentFormat::Utf8)?, + company: self.company.encrypt(ctx, key, ContentFormat::Utf8)?, + email: self.email.encrypt(ctx, key, ContentFormat::Utf8)?, + phone: self.phone.encrypt(ctx, key, ContentFormat::Utf8)?, + ssn: self.ssn.encrypt(ctx, key, ContentFormat::Utf8)?, + username: self.username.encrypt(ctx, key, ContentFormat::Utf8)?, + passport_number: self + .passport_number + .encrypt(ctx, key, ContentFormat::Utf8)?, + license_number: self.license_number.encrypt(ctx, key, ContentFormat::Utf8)?, }) } } diff --git a/crates/bitwarden-vault/src/cipher/local_data.rs b/crates/bitwarden-vault/src/cipher/local_data.rs index b8f302b80..13e3cb4f5 100644 --- a/crates/bitwarden-vault/src/cipher/local_data.rs +++ b/crates/bitwarden-vault/src/cipher/local_data.rs @@ -1,5 +1,5 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; -use bitwarden_crypto::{CryptoError, Decryptable, Encryptable, KeyStoreContext}; +use bitwarden_crypto::{ContentFormat, CryptoError, Decryptable, Encryptable, KeyStoreContext}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -28,6 +28,7 @@ impl Encryptable for LocalDataView { &self, _ctx: &mut KeyStoreContext, _key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { Ok(LocalData { last_used_date: self.last_used_date, diff --git a/crates/bitwarden-vault/src/cipher/login.rs b/crates/bitwarden-vault/src/cipher/login.rs index 07591c1ca..6f7c94385 100644 --- a/crates/bitwarden-vault/src/cipher/login.rs +++ b/crates/bitwarden-vault/src/cipher/login.rs @@ -4,7 +4,9 @@ use bitwarden_core::{ key_management::{KeyIds, SymmetricKeyId}, require, }; -use bitwarden_crypto::{CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext}; +use bitwarden_crypto::{ + ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, +}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; @@ -195,24 +197,27 @@ impl Encryptable for Fido2CredentialFul &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { Ok(Fido2Credential { - credential_id: self.credential_id.encrypt(ctx, key)?, - key_type: self.key_type.encrypt(ctx, key)?, - key_algorithm: self.key_algorithm.encrypt(ctx, key)?, - key_curve: self.key_curve.encrypt(ctx, key)?, - key_value: self.key_value.encrypt(ctx, key)?, - rp_id: self.rp_id.encrypt(ctx, key)?, + credential_id: self.credential_id.encrypt(ctx, key, ContentFormat::Utf8)?, + key_type: self.key_type.encrypt(ctx, key, ContentFormat::Utf8)?, + key_algorithm: self.key_algorithm.encrypt(ctx, key, ContentFormat::Utf8)?, + key_curve: self.key_curve.encrypt(ctx, key, ContentFormat::Utf8)?, + key_value: self.key_value.encrypt(ctx, key, ContentFormat::Utf8)?, + rp_id: self.rp_id.encrypt(ctx, key, ContentFormat::Utf8)?, user_handle: self .user_handle .as_ref() - .map(|h| h.encrypt(ctx, key)) + .map(|h| h.encrypt(ctx, key, ContentFormat::Utf8)) .transpose()?, - user_name: self.user_name.encrypt(ctx, key)?, - counter: self.counter.encrypt(ctx, key)?, - rp_name: self.rp_name.encrypt(ctx, key)?, - user_display_name: self.user_display_name.encrypt(ctx, key)?, - discoverable: self.discoverable.encrypt(ctx, key)?, + user_name: self.user_name.encrypt(ctx, key, ContentFormat::Utf8)?, + counter: self.counter.encrypt(ctx, key, ContentFormat::Utf8)?, + rp_name: self.rp_name.encrypt(ctx, key, ContentFormat::Utf8)?, + user_display_name: self + .user_display_name + .encrypt(ctx, key, ContentFormat::Utf8)?, + discoverable: self.discoverable.encrypt(ctx, key, ContentFormat::Utf8)?, creation_date: self.creation_date, }) } @@ -317,11 +322,12 @@ impl Encryptable for LoginUriView { &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { Ok(LoginUri { - uri: self.uri.encrypt(ctx, key)?, + uri: self.uri.encrypt(ctx, key, ContentFormat::Utf8)?, r#match: self.r#match, - uri_checksum: self.uri_checksum.encrypt(ctx, key)?, + uri_checksum: self.uri_checksum.encrypt(ctx, key, ContentFormat::Utf8)?, }) } } @@ -331,13 +337,14 @@ impl Encryptable for LoginView { &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { Ok(Login { - username: self.username.encrypt(ctx, key)?, - password: self.password.encrypt(ctx, key)?, + username: self.username.encrypt(ctx, key, ContentFormat::Utf8)?, + password: self.password.encrypt(ctx, key, ContentFormat::Utf8)?, password_revision_date: self.password_revision_date, - uris: self.uris.encrypt(ctx, key)?, - totp: self.totp.encrypt(ctx, key)?, + uris: self.uris.encrypt(ctx, key, ContentFormat::Utf8)?, + totp: self.totp.encrypt(ctx, key, ContentFormat::Utf8)?, autofill_on_page_load: self.autofill_on_page_load, fido2_credentials: self.fido2_credentials.clone(), }) @@ -401,28 +408,31 @@ impl Encryptable for Fido2CredentialVie &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { Ok(Fido2Credential { - credential_id: self.credential_id.encrypt(ctx, key)?, - key_type: self.key_type.encrypt(ctx, key)?, - key_algorithm: self.key_algorithm.encrypt(ctx, key)?, - key_curve: self.key_curve.encrypt(ctx, key)?, + credential_id: self.credential_id.encrypt(ctx, key, ContentFormat::Utf8)?, + key_type: self.key_type.encrypt(ctx, key, ContentFormat::Utf8)?, + key_algorithm: self.key_algorithm.encrypt(ctx, key, ContentFormat::Utf8)?, + key_curve: self.key_curve.encrypt(ctx, key, ContentFormat::Utf8)?, key_value: self.key_value.clone(), - rp_id: self.rp_id.encrypt(ctx, key)?, + rp_id: self.rp_id.encrypt(ctx, key, ContentFormat::Utf8)?, user_handle: self .user_handle .as_ref() - .map(|h| h.encrypt(ctx, key)) + .map(|h| h.encrypt(ctx, key, ContentFormat::Utf8)) .transpose()?, user_name: self .user_name .as_ref() - .map(|n| n.encrypt(ctx, key)) + .map(|n| n.encrypt(ctx, key, ContentFormat::Utf8)) .transpose()?, - counter: self.counter.encrypt(ctx, key)?, - rp_name: self.rp_name.encrypt(ctx, key)?, - user_display_name: self.user_display_name.encrypt(ctx, key)?, - discoverable: self.discoverable.encrypt(ctx, key)?, + counter: self.counter.encrypt(ctx, key, ContentFormat::Utf8)?, + rp_name: self.rp_name.encrypt(ctx, key, ContentFormat::Utf8)?, + user_display_name: self + .user_display_name + .encrypt(ctx, key, ContentFormat::Utf8)?, + discoverable: self.discoverable.encrypt(ctx, key, ContentFormat::Utf8)?, creation_date: self.creation_date, }) } diff --git a/crates/bitwarden-vault/src/cipher/secure_note.rs b/crates/bitwarden-vault/src/cipher/secure_note.rs index 223a3a35e..19b4a2718 100644 --- a/crates/bitwarden-vault/src/cipher/secure_note.rs +++ b/crates/bitwarden-vault/src/cipher/secure_note.rs @@ -3,7 +3,7 @@ use bitwarden_core::{ key_management::{KeyIds, SymmetricKeyId}, require, }; -use bitwarden_crypto::{CryptoError, Decryptable, Encryptable, KeyStoreContext}; +use bitwarden_crypto::{ContentFormat, CryptoError, Decryptable, Encryptable, KeyStoreContext}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; #[cfg(feature = "wasm")] @@ -42,6 +42,7 @@ impl Encryptable for SecureNoteView { &self, _ctx: &mut KeyStoreContext, _key: SymmetricKeyId, + _content_format: ContentFormat, ) -> Result { Ok(SecureNote { r#type: self.r#type, diff --git a/crates/bitwarden-vault/src/cipher/ssh_key.rs b/crates/bitwarden-vault/src/cipher/ssh_key.rs index c2318d02a..286b8cf36 100644 --- a/crates/bitwarden-vault/src/cipher/ssh_key.rs +++ b/crates/bitwarden-vault/src/cipher/ssh_key.rs @@ -1,5 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; -use bitwarden_crypto::{CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext}; +use bitwarden_crypto::{ + ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, +}; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] use tsify_next::Tsify; @@ -35,11 +37,12 @@ impl Encryptable for SshKeyView { &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + content_format: ContentFormat, ) -> Result { Ok(SshKey { - private_key: self.private_key.encrypt(ctx, key)?, - public_key: self.public_key.encrypt(ctx, key)?, - fingerprint: self.fingerprint.encrypt(ctx, key)?, + private_key: self.private_key.encrypt(ctx, key, content_format)?, + public_key: self.public_key.encrypt(ctx, key, content_format)?, + fingerprint: self.fingerprint.encrypt(ctx, key, content_format)?, }) } } diff --git a/crates/bitwarden-vault/src/folder.rs b/crates/bitwarden-vault/src/folder.rs index d398b986c..c664e366e 100644 --- a/crates/bitwarden-vault/src/folder.rs +++ b/crates/bitwarden-vault/src/folder.rs @@ -4,7 +4,7 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, + ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -50,10 +50,11 @@ impl Encryptable for FolderView { &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + content_format: ContentFormat, ) -> Result { Ok(Folder { id: self.id, - name: self.name.encrypt(ctx, key)?, + name: self.name.encrypt(ctx, key, content_format)?, revision_date: self.revision_date, }) } diff --git a/crates/bitwarden-vault/src/password_history.rs b/crates/bitwarden-vault/src/password_history.rs index 31d1cad7e..0fba1fa41 100644 --- a/crates/bitwarden-vault/src/password_history.rs +++ b/crates/bitwarden-vault/src/password_history.rs @@ -1,7 +1,7 @@ use bitwarden_api_api::models::CipherPasswordHistoryModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, + ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -44,9 +44,10 @@ impl Encryptable for PasswordHistoryVie &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, + content_format: ContentFormat, ) -> Result { Ok(PasswordHistory { - password: self.password.encrypt(ctx, key)?, + password: self.password.encrypt(ctx, key, content_format)?, last_used_date: self.last_used_date, }) } diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 259460734..5f5bdfc15 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_crypto::{ - CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, SymmetricCryptoKey, + ContentFormat, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, + SymmetricCryptoKey, }; use wasm_bindgen::prelude::*; @@ -53,13 +54,16 @@ impl PureCrypto { pub fn symmetric_encrypt_string(plain: String, key: Vec) -> Result { plain - .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?) + .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?, &ContentFormat::Utf8) .map(|enc| enc.to_string()) } pub fn symmetric_encrypt_bytes(plain: Vec, key: Vec) -> Result { plain - .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?) + .encrypt_with_key( + &SymmetricCryptoKey::try_from(key)?, + &ContentFormat::OctetStream, + ) .map(|enc| enc.to_string()) } @@ -68,7 +72,10 @@ impl PureCrypto { key: Vec, ) -> Result, CryptoError> { plain - .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)? + .encrypt_with_key( + &SymmetricCryptoKey::try_from(key)?, + &ContentFormat::OctetStream, + )? .to_buffer() } From 2e905c49f5be62d45be3ea5376a64e30699e95a6 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 11:29:49 +0200 Subject: [PATCH 002/124] Cargo fmt --- crates/bitwarden-crypto/src/enc_string/symmetric.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index 9c9b86d42..7d06a7dbd 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -317,13 +317,21 @@ impl KeyDecryptable> for EncString { } impl KeyEncryptable for String { - fn encrypt_with_key(self, key: &SymmetricCryptoKey, content_format: &ContentFormat) -> Result { + fn encrypt_with_key( + self, + key: &SymmetricCryptoKey, + content_format: &ContentFormat, + ) -> Result { self.as_bytes().encrypt_with_key(key, content_format) } } impl KeyEncryptable for &str { - fn encrypt_with_key(self, key: &SymmetricCryptoKey, content_format: &ContentFormat) -> Result { + fn encrypt_with_key( + self, + key: &SymmetricCryptoKey, + content_format: &ContentFormat, + ) -> Result { self.as_bytes().encrypt_with_key(key, content_format) } } From b41f5effc7dccd62b5b49050d4495f25b7236a29 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 12:06:42 +0200 Subject: [PATCH 003/124] Fix docs --- crates/bitwarden-crypto/README.md | 4 +- crates/bitwarden-crypto/src/cose.rs | 23 +++++------ crates/bitwarden-crypto/src/error.rs | 3 ++ crates/bitwarden-crypto/src/keys/mod.rs | 2 +- .../src/keys/symmetric_crypto_key.rs | 13 ++----- crates/bitwarden-crypto/src/keys/utils.rs | 38 ++++++++++++++++++- crates/bitwarden-crypto/src/store/context.rs | 4 +- 7 files changed, 59 insertions(+), 28 deletions(-) diff --git a/crates/bitwarden-crypto/README.md b/crates/bitwarden-crypto/README.md index ca96c5d11..fe89f72e4 100644 --- a/crates/bitwarden-crypto/README.md +++ b/crates/bitwarden-crypto/README.md @@ -13,13 +13,13 @@ secure. ## Example: ```rust -use bitwarden_crypto::{SymmetricCryptoKey, TypedKeyEncryptable, KeyDecryptable, CryptoError}; +use bitwarden_crypto::{SymmetricCryptoKey, KeyEncryptable, KeyDecryptable, CryptoError, ContentFormat}; async fn example() -> Result<(), CryptoError> { let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let data = "Hello, World!".to_owned(); - let encrypted = data.clone().encrypt_with_key(&key)?; + let encrypted = data.clone().encrypt_with_key(&key, &ContentFormat::Utf8)?; let decrypted: String = encrypted.decrypt_with_key(&key)?; assert_eq!(data, decrypted); diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index b29a71130..bdd583571 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -5,7 +5,7 @@ use coset::{ iana::{self, CoapContentFormat}, - CborSerializable, Label, + CborSerializable, ContentType, Label, }; use generic_array::GenericArray; use serde::{Deserialize, Serialize}; @@ -22,6 +22,7 @@ use crate::{ /// the draft was never published as an RFC, we use a private-use value for the algorithm. pub(crate) const XCHACHA20_POLY1305: i64 = -70000; const XCHACHA20_TEXT_PAD_BLOCK_SIZE: usize = 32; +const CONTENT_TYPE_PADDED_UTF8: &str = "application/utf8-padded"; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] @@ -35,13 +36,6 @@ pub enum ContentFormat { DomainObject, } -/// Pads bytes to a minimum length using PKCS7-like padding -fn pad_bytes(bytes: &mut Vec, block_size: usize) { - let padding_len = block_size - (bytes.len() % block_size); - let padded_length = padding_len + bytes.len(); - bytes.resize(padded_length, padding_len as u8); -} - /// Encrypts a plaintext message using XChaCha20Poly1305 and returns a COSE Encrypt0 message pub(crate) fn encrypt_xchacha20_poly1305( plaintext: &[u8], @@ -52,7 +46,7 @@ pub(crate) fn encrypt_xchacha20_poly1305( let protected_header = match content_format { // UTF-8 directly would leak the plaintext size. This is not acceptable for certain data // (passwords). - ContentFormat::Utf8 => protected_header.content_type("application/utf8-padded".to_string()), + ContentFormat::Utf8 => protected_header.content_type(CONTENT_TYPE_PADDED_UTF8.to_string()), ContentFormat::Pkcs8 => protected_header.content_format(CoapContentFormat::Pkcs8), ContentFormat::CoseKey => protected_header.content_format(CoapContentFormat::CoseKey), ContentFormat::OctetStream => { @@ -70,7 +64,7 @@ pub(crate) fn encrypt_xchacha20_poly1305( let encoded_plaintext = if *content_format == ContentFormat::Utf8 { // Pad the data to a block size in order to hide plaintext length let mut plaintext = plaintext.to_vec(); - pad_bytes(&mut plaintext, XCHACHA20_TEXT_PAD_BLOCK_SIZE); + crate::keys::utils::pad_bytes(&mut plaintext, XCHACHA20_TEXT_PAD_BLOCK_SIZE); plaintext } else { plaintext.to_vec() @@ -120,7 +114,14 @@ pub(crate) fn decrypt_xchacha20_poly1305( aad, ) })?; - Ok(decrypted_message) + + if let Some(ref content_type) = msg.protected.header.content_type { + if *content_type == ContentType::Text(CONTENT_TYPE_PADDED_UTF8.to_string()) { + // Unpad the data to get the original plaintext + return crate::keys::utils::unpad_bytes(&decrypted_message).map(|bytes| bytes.to_vec()); + } + } + return Ok(decrypted_message); } const SYMMETRIC_KEY: Label = Label::Int(iana::SymmetricKeyParameter::K as i64); diff --git a/crates/bitwarden-crypto/src/error.rs b/crates/bitwarden-crypto/src/error.rs index d2604b123..0e9b617ad 100644 --- a/crates/bitwarden-crypto/src/error.rs +++ b/crates/bitwarden-crypto/src/error.rs @@ -56,6 +56,9 @@ pub enum CryptoError { #[error("Invalid nonce length")] InvalidNonceLength, + + #[error("Invalid padding")] + InvalidPadding, } #[derive(Debug, Error)] diff --git a/crates/bitwarden-crypto/src/keys/mod.rs b/crates/bitwarden-crypto/src/keys/mod.rs index efdbc52d4..3841ccc47 100644 --- a/crates/bitwarden-crypto/src/keys/mod.rs +++ b/crates/bitwarden-crypto/src/keys/mod.rs @@ -28,4 +28,4 @@ pub use kdf::{ }; #[cfg(test)] pub(crate) use key_id::KEY_ID_SIZE; -mod utils; +pub(crate) mod utils; diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index 91e001290..8d208405a 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -1,4 +1,4 @@ -use std::{cmp::max, pin::Pin}; +use std::pin::Pin; use base64::{engine::general_purpose::STANDARD, Engine}; use coset::{iana::KeyOperation, CborSerializable, RegisteredLabelWithPrivate}; @@ -347,10 +347,7 @@ impl std::fmt::Debug for XChaCha20Poly1305Key { /// size of the byte array. The previous key types [SymmetricCryptoKey::Aes256CbcHmacKey] and /// [SymmetricCryptoKey::Aes256CbcKey] are 64 and 32 bytes long respectively. fn pad_key(key_bytes: &mut Vec, min_length: usize) { - // at least 1 byte of padding is required - let pad_bytes = min_length.saturating_sub(key_bytes.len()).max(1); - let padded_length = max(min_length, key_bytes.len() + 1); - key_bytes.resize(padded_length, pad_bytes as u8); + crate::keys::utils::pad_bytes(key_bytes, min_length); } /// Unpad a key that is padded using the PKCS7-like padding defined by [pad_key]. @@ -364,11 +361,7 @@ fn pad_key(key_bytes: &mut Vec, min_length: usize) { /// size of the byte array the previous key types [SymmetricCryptoKey::Aes256CbcHmacKey] and /// [SymmetricCryptoKey::Aes256CbcKey] are 64 and 32 bytes long respectively. fn unpad_key(key_bytes: &[u8]) -> Result<&[u8], CryptoError> { - let pad_len = *key_bytes.last().ok_or(CryptoError::InvalidKey)? as usize; - if pad_len >= key_bytes.len() { - return Err(CryptoError::InvalidKey); - } - Ok(key_bytes[..key_bytes.len() - pad_len].as_ref()) + crate::keys::utils::unpad_bytes(key_bytes).map_err(|_| CryptoError::InvalidKey) } #[cfg(test)] diff --git a/crates/bitwarden-crypto/src/keys/utils.rs b/crates/bitwarden-crypto/src/keys/utils.rs index 21edd60af..4bf8e41a2 100644 --- a/crates/bitwarden-crypto/src/keys/utils.rs +++ b/crates/bitwarden-crypto/src/keys/utils.rs @@ -1,10 +1,10 @@ -use std::pin::Pin; +use std::{cmp::max, pin::Pin}; use generic_array::GenericArray; use typenum::U32; use super::Aes256CbcHmacKey; -use crate::{util::hkdf_expand, Result}; +use crate::{util::hkdf_expand, CryptoError, Result}; /// Stretch the given key using HKDF. /// This can be either a kdf-derived key (PIN/Master password) or @@ -16,6 +16,27 @@ pub(super) fn stretch_key(key: &Pin>>) -> Result, min_length: usize) { + // at least 1 byte of padding is required + let pad_bytes = min_length.saturating_sub(bytes.len()).max(1); + let padded_length = max(min_length, bytes.len() + 1); + bytes.resize(padded_length, pad_bytes as u8); +} + +/// Unpads bytes that is padded using the PKCS7-like padding defined by [pad_key]. +/// The last N bytes of the padded bytes all have the value N. +/// For example, padded to size 4, the value 0,0 becomes 0,0,2,2. +pub(crate) fn unpad_bytes(padded_bytes: &[u8]) -> Result<&[u8], CryptoError> { + let pad_len = *padded_bytes.last().ok_or(CryptoError::InvalidPadding)? as usize; + if pad_len >= padded_bytes.len() { + return Err(CryptoError::InvalidPadding); + } + Ok(padded_bytes[..padded_bytes.len() - pad_len].as_ref()) +} + #[cfg(test)] mod tests { use super::*; @@ -46,4 +67,17 @@ mod tests { stretched.mac_key.as_slice() ); } + + #[test] + fn test_pad_bytes_roundtrip() { + let original_bytes = vec![1u8; 10]; + let mut cloned_bytes = original_bytes.clone(); + let mut encoded_bytes = vec![1u8; 12]; + encoded_bytes[10] = 2; + encoded_bytes[11] = 2; + pad_bytes(&mut cloned_bytes, 12); + assert_eq!(encoded_bytes, cloned_bytes); + let unpadded_bytes = unpad_bytes(&cloned_bytes).unwrap(); + assert_eq!(original_bytes, unpadded_bytes); + } } diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 3bbecb342..3804b977c 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -54,9 +54,9 @@ use crate::{ /// const LOCAL_KEY: SymmKeyId = SymmKeyId::Local("local_key_id"); /// /// impl Encryptable for Data { -/// fn encrypt(&self, ctx: &mut KeyStoreContext, key: SymmKeyId) -> Result { +/// fn encrypt(&self, ctx: &mut KeyStoreContext, key: SymmKeyId, content_format: ContentFormat) -> Result { /// let local_key_id = ctx.unwrap_symmetric_key(key, LOCAL_KEY, &self.key)?; -/// self.name.encrypt(ctx, local_key_id) +/// self.name.encrypt(ctx, local_key_id, content_format) /// } /// } /// ``` From eb70dfcc45de5e378e671eb5908107529409b667 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 12:13:44 +0200 Subject: [PATCH 004/124] Fix formatting --- crates/bitwarden-crypto/src/cose.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index bdd583571..58f565804 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -121,7 +121,7 @@ pub(crate) fn decrypt_xchacha20_poly1305( return crate::keys::utils::unpad_bytes(&decrypted_message).map(|bytes| bytes.to_vec()); } } - return Ok(decrypted_message); + Ok(decrypted_message) } const SYMMETRIC_KEY: Label = Label::Int(iana::SymmetricKeyParameter::K as i64); From 68be6e69521b5c01b65d33304919a2a31befb4bd Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 13:04:42 +0200 Subject: [PATCH 005/124] Fix formatting --- .../bitwarden-wasm-internal/src/pure_crypto.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index a1d94752a..848f68978 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, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, SymmetricCryptoKey, UnsignedSharedKey + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, ContentFormat, CryptoError, Decryptable, + EncString, Encryptable, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, + SymmetricCryptoKey, UnsignedSharedKey, }; use wasm_bindgen::prelude::*; @@ -176,7 +178,11 @@ impl PureCrypto { )?; // Note: The order of arguments is different here, and should probably be refactored Ok(encapsulation_key - .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"), ContentFormat::OctetStream)? + .encrypt( + &mut context, + SymmetricKeyId::Local("wrapping_key"), + ContentFormat::OctetStream, + )? .to_string()) } @@ -209,7 +215,11 @@ impl PureCrypto { )?; // Note: The order of arguments is different here, and should probably be refactored Ok(decapsulation_key - .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"), ContentFormat::Pkcs8)? + .encrypt( + &mut context, + SymmetricKeyId::Local("wrapping_key"), + ContentFormat::Pkcs8, + )? .to_string()) } From d6a18a4dd8cd42bceaad69b7e8ca2a69350e84e5 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 May 2025 13:10:38 +0200 Subject: [PATCH 006/124] Fix comment --- crates/bitwarden-crypto/src/keys/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/keys/utils.rs b/crates/bitwarden-crypto/src/keys/utils.rs index 4bf8e41a2..35f39a1d8 100644 --- a/crates/bitwarden-crypto/src/keys/utils.rs +++ b/crates/bitwarden-crypto/src/keys/utils.rs @@ -26,7 +26,7 @@ pub(crate) fn pad_bytes(bytes: &mut Vec, min_length: usize) { bytes.resize(padded_length, pad_bytes as u8); } -/// Unpads bytes that is padded using the PKCS7-like padding defined by [pad_key]. +/// Unpads bytes that is padded using the PKCS7-like padding defined by [pad_bytes]. /// The last N bytes of the padded bytes all have the value N. /// For example, padded to size 4, the value 0,0 becomes 0,0,2,2. pub(crate) fn unpad_bytes(padded_bytes: &[u8]) -> Result<&[u8], CryptoError> { From 6e9e526c20c305b735e6e2317c54d062fa687998 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 21 May 2025 14:49:47 +0200 Subject: [PATCH 007/124] Cleanup --- crates/bitwarden-crypto/src/traits/encryptable.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index 259015ed8..abd06729c 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -1,4 +1,4 @@ -use crate::{store::KeyStoreContext, CryptoError, EncString, KeyId, KeyIds}; +use crate::{store::KeyStoreContext, ContentFormat, CryptoError, EncString, KeyId, KeyIds}; /// An encryption operation that takes the input value and encrypts it into the output value. /// Implementations should generally consist of calling [Encryptable::encrypt] for all the fields of @@ -8,7 +8,7 @@ pub trait Encryptable { &self, ctx: &mut KeyStoreContext, key: Key, - content_format: crate::cose::ContentFormat, + content_format: ContentFormat, ) -> Result; } @@ -17,7 +17,7 @@ impl Encryptable for &[u8] { &self, ctx: &mut KeyStoreContext, key: Ids::Symmetric, - content_format: crate::cose::ContentFormat, + content_format: ContentFormat, ) -> Result { ctx.encrypt_data_with_symmetric_key(key, self, content_format) } @@ -28,7 +28,7 @@ impl Encryptable for Vec { &self, ctx: &mut KeyStoreContext, key: Ids::Symmetric, - content_format: crate::cose::ContentFormat, + content_format: ContentFormat, ) -> Result { ctx.encrypt_data_with_symmetric_key(key, self, content_format) } @@ -39,7 +39,7 @@ impl Encryptable for &str { &self, ctx: &mut KeyStoreContext, key: Ids::Symmetric, - content_format: crate::cose::ContentFormat, + content_format: ContentFormat, ) -> Result { self.as_bytes().encrypt(ctx, key, content_format) } @@ -78,7 +78,7 @@ impl, Output> &self, ctx: &mut KeyStoreContext, key: Key, - content_format: crate::cose::ContentFormat, + content_format: ContentFormat, ) -> Result, CryptoError> { self.iter() .map(|value| value.encrypt(ctx, key, content_format)) From 50d8f70ec320ba0f0689b3e479326d8c55722d42 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 21 May 2025 15:13:04 +0200 Subject: [PATCH 008/124] Switch from pass by ref to pass by value for enum --- crates/bitwarden-core/src/mobile/crypto.rs | 5 ++--- crates/bitwarden-core/src/secrets_manager/state.rs | 2 +- crates/bitwarden-crypto/README.md | 2 +- crates/bitwarden-crypto/src/cose.rs | 6 +++--- .../bitwarden-crypto/src/enc_string/symmetric.rs | 14 +++++++------- crates/bitwarden-crypto/src/keys/device_key.rs | 7 +++---- .../bitwarden-crypto/src/keys/key_encryptable.rs | 10 +++++----- crates/bitwarden-crypto/src/keys/pin_key.rs | 4 ++-- crates/bitwarden-crypto/src/store/context.rs | 2 +- crates/bitwarden-exporters/src/encrypted_json.rs | 4 ++-- crates/bitwarden-wasm-internal/src/pure_crypto.rs | 6 +++--- 11 files changed, 30 insertions(+), 32 deletions(-) diff --git a/crates/bitwarden-core/src/mobile/crypto.rs b/crates/bitwarden-core/src/mobile/crypto.rs index 83fcb0b06..4b33520c0 100644 --- a/crates/bitwarden-core/src/mobile/crypto.rs +++ b/crates/bitwarden-core/src/mobile/crypto.rs @@ -8,8 +8,7 @@ use std::collections::HashMap; use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ - AsymmetricCryptoKey, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, - SymmetricCryptoKey, UnsignedSharedKey, UserKey, + AsymmetricCryptoKey, ContentFormat, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -329,7 +328,7 @@ pub(super) fn derive_pin_key( Ok(DerivePinKeyResponse { pin_protected_user_key, - encrypted_pin: pin.encrypt_with_key(user_key, &bitwarden_crypto::ContentFormat::Utf8)?, + encrypted_pin: pin.encrypt_with_key(user_key, ContentFormat::Utf8)?, }) } diff --git a/crates/bitwarden-core/src/secrets_manager/state.rs b/crates/bitwarden-core/src/secrets_manager/state.rs index 45b298403..a4a405482 100644 --- a/crates/bitwarden-core/src/secrets_manager/state.rs +++ b/crates/bitwarden-core/src/secrets_manager/state.rs @@ -73,7 +73,7 @@ pub(crate) fn set( ) -> Result<(), StateFileError> { let serialized_state: String = serde_json::to_string(&state)?; let encrypted_state: EncString = - serialized_state.encrypt_with_key(&access_token.encryption_key, &ContentFormat::Utf8)?; + serialized_state.encrypt_with_key(&access_token.encryption_key, ContentFormat::Utf8)?; let state_string: String = encrypted_state.to_string(); Ok(std::fs::write(state_file, state_string)?) diff --git a/crates/bitwarden-crypto/README.md b/crates/bitwarden-crypto/README.md index fe89f72e4..c81a8bdb6 100644 --- a/crates/bitwarden-crypto/README.md +++ b/crates/bitwarden-crypto/README.md @@ -19,7 +19,7 @@ async fn example() -> Result<(), CryptoError> { let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let data = "Hello, World!".to_owned(); - let encrypted = data.clone().encrypt_with_key(&key, &ContentFormat::Utf8)?; + let encrypted = data.clone().encrypt_with_key(&key, ContentFormat::Utf8)?; let decrypted: String = encrypted.decrypt_with_key(&key)?; assert_eq!(data, decrypted); diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 58f565804..d9c5376d2 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -40,7 +40,7 @@ pub enum ContentFormat { pub(crate) fn encrypt_xchacha20_poly1305( plaintext: &[u8], key: &crate::XChaCha20Poly1305Key, - content_format: &ContentFormat, + content_format: ContentFormat, ) -> Result, CryptoError> { let protected_header = coset::HeaderBuilder::new(); let protected_header = match content_format { @@ -61,7 +61,7 @@ pub(crate) fn encrypt_xchacha20_poly1305( // https://github.com/google/coset/issues/105 protected_header.alg = Some(coset::Algorithm::PrivateUse(XCHACHA20_POLY1305)); - let encoded_plaintext = if *content_format == ContentFormat::Utf8 { + let encoded_plaintext = if content_format == ContentFormat::Utf8 { // Pad the data to a block size in order to hide plaintext length let mut plaintext = plaintext.to_vec(); crate::keys::utils::pad_bytes(&mut plaintext, XCHACHA20_TEXT_PAD_BLOCK_SIZE); @@ -176,7 +176,7 @@ mod test { let plaintext = b"Hello, world!"; let encrypted = - encrypt_xchacha20_poly1305(plaintext, key, &ContentFormat::OctetStream).unwrap(); + encrypt_xchacha20_poly1305(plaintext, key, ContentFormat::OctetStream).unwrap(); let decrypted = decrypt_xchacha20_poly1305(&encrypted, key).unwrap(); assert_eq!(decrypted, plaintext); } diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index 7d06a7dbd..5a662e289 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -259,7 +259,7 @@ impl EncString { pub(crate) fn encrypt_xchacha20_poly1305( data_dec: &[u8], key: &XChaCha20Poly1305Key, - content_format: &ContentFormat, + content_format: ContentFormat, ) -> Result { let data = crate::cose::encrypt_xchacha20_poly1305(data_dec, key, content_format)?; Ok(EncString::Cose_Encrypt0_B64 { data }) @@ -279,7 +279,7 @@ impl KeyEncryptable for &[u8] { fn encrypt_with_key( self, key: &SymmetricCryptoKey, - content_format: &ContentFormat, + content_format: ContentFormat, ) -> Result { match key { SymmetricCryptoKey::Aes256CbcHmacKey(key) => EncString::encrypt_aes256_hmac(self, key), @@ -320,7 +320,7 @@ impl KeyEncryptable for String { fn encrypt_with_key( self, key: &SymmetricCryptoKey, - content_format: &ContentFormat, + content_format: ContentFormat, ) -> Result { self.as_bytes().encrypt_with_key(key, content_format) } @@ -330,7 +330,7 @@ impl KeyEncryptable for &str { fn encrypt_with_key( self, key: &SymmetricCryptoKey, - content_format: &ContentFormat, + content_format: ContentFormat, ) -> Result { self.as_bytes().encrypt_with_key(key, content_format) } @@ -377,7 +377,7 @@ mod tests { let test_string = "encrypted_test_string"; let cipher = test_string .to_owned() - .encrypt_with_key(&key, &ContentFormat::Utf8) + .encrypt_with_key(&key, ContentFormat::Utf8) .unwrap(); let decrypted_str: String = cipher.decrypt_with_key(&key).unwrap(); assert_eq!(decrypted_str, test_string); @@ -390,7 +390,7 @@ mod tests { let test_string = "encrypted_test_string"; let cipher = test_string .to_string() - .encrypt_with_key(&key, &ContentFormat::Utf8) + .encrypt_with_key(&key, ContentFormat::Utf8) .unwrap(); let decrypted_str: String = cipher.decrypt_with_key(&key).unwrap(); @@ -404,7 +404,7 @@ mod tests { let test_string: &'static str = "encrypted_test_string"; let cipher = test_string .to_string() - .encrypt_with_key(&key, &ContentFormat::Utf8) + .encrypt_with_key(&key, ContentFormat::Utf8) .unwrap(); let decrypted_str: String = cipher.decrypt_with_key(&key).unwrap(); diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index 8d5456e87..a67dbd98f 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -1,6 +1,5 @@ use crate::{ - error::Result, AsymmetricCryptoKey, CryptoError, EncString, KeyDecryptable, KeyEncryptable, - SymmetricCryptoKey, UnsignedSharedKey, + error::Result, AsymmetricCryptoKey, ContentFormat, CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, UnsignedSharedKey }; /// Device Key @@ -39,11 +38,11 @@ impl DeviceKey { let protected_device_public_key = device_private_key .to_public_der()? - .encrypt_with_key(user_key, &crate::cose::ContentFormat::OctetStream)?; + .encrypt_with_key(user_key, ContentFormat::OctetStream)?; let protected_device_private_key = device_private_key .to_der()? - .encrypt_with_key(&device_key.0, &crate::cose::ContentFormat::Pkcs8)?; + .encrypt_with_key(&device_key.0, ContentFormat::Pkcs8)?; Ok(TrustDeviceResponse { device_key: device_key.to_base64(), diff --git a/crates/bitwarden-crypto/src/keys/key_encryptable.rs b/crates/bitwarden-crypto/src/keys/key_encryptable.rs index 63b24f070..0b319f079 100644 --- a/crates/bitwarden-crypto/src/keys/key_encryptable.rs +++ b/crates/bitwarden-crypto/src/keys/key_encryptable.rs @@ -18,7 +18,7 @@ impl KeyContainer for Arc { pub trait CryptoKey {} pub trait KeyEncryptable { - fn encrypt_with_key(self, key: &Key, content_format: &ContentFormat) -> Result; + fn encrypt_with_key(self, key: &Key, content_format: ContentFormat) -> Result; } pub trait KeyDecryptable { @@ -28,7 +28,7 @@ pub trait KeyDecryptable { impl, Key: CryptoKey, Output> KeyEncryptable> for Option { - fn encrypt_with_key(self, key: &Key, content_format: &ContentFormat) -> Result> { + fn encrypt_with_key(self, key: &Key, content_format: ContentFormat) -> Result> { self.map(|e| e.encrypt_with_key(key, content_format)) .transpose() } @@ -45,7 +45,7 @@ impl, Key: CryptoKey, Output> KeyDecryptable, Key: CryptoKey, Output> KeyEncryptable for Box { - fn encrypt_with_key(self, key: &Key, content_format: &ContentFormat) -> Result { + fn encrypt_with_key(self, key: &Key, content_format: ContentFormat) -> Result { (*self).encrypt_with_key(key, content_format) } } @@ -64,7 +64,7 @@ impl< Output: Send + Sync, > KeyEncryptable> for Vec { - fn encrypt_with_key(self, key: &Key, content_format: &ContentFormat) -> Result> { + fn encrypt_with_key(self, key: &Key, content_format: ContentFormat) -> Result> { self.into_par_iter() .map(|e| e.encrypt_with_key(key, content_format)) .collect() @@ -94,7 +94,7 @@ impl< fn encrypt_with_key( self, key: &Key, - content_format: &ContentFormat, + content_format: ContentFormat, ) -> Result> { self.into_par_iter() .map(|(id, e)| Ok((id, e.encrypt_with_key(key, content_format)?))) diff --git a/crates/bitwarden-crypto/src/keys/pin_key.rs b/crates/bitwarden-crypto/src/keys/pin_key.rs index c8421e244..ecbc929fc 100644 --- a/crates/bitwarden-crypto/src/keys/pin_key.rs +++ b/crates/bitwarden-crypto/src/keys/pin_key.rs @@ -33,14 +33,14 @@ impl PinKey { impl CryptoKey for PinKey {} impl KeyEncryptable for &[u8] { - fn encrypt_with_key(self, key: &PinKey, content_format: &ContentFormat) -> Result { + fn encrypt_with_key(self, key: &PinKey, content_format: ContentFormat) -> Result { let stretched_key = SymmetricCryptoKey::Aes256CbcHmacKey(stretch_key(&key.0 .0)?); self.encrypt_with_key(&stretched_key, content_format) } } impl KeyEncryptable for String { - fn encrypt_with_key(self, key: &PinKey, content_format: &ContentFormat) -> Result { + fn encrypt_with_key(self, key: &PinKey, content_format: ContentFormat) -> Result { self.as_bytes().encrypt_with_key(key, content_format) } } diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 3804b977c..79b4b2946 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -372,7 +372,7 @@ impl KeyStoreContext<'_, Ids> { )), SymmetricCryptoKey::Aes256CbcHmacKey(key) => EncString::encrypt_aes256_hmac(data, key), SymmetricCryptoKey::XChaCha20Poly1305Key(key) => { - EncString::encrypt_xchacha20_poly1305(data, key, &content_format) + EncString::encrypt_xchacha20_poly1305(data, key, content_format) } } } diff --git a/crates/bitwarden-exporters/src/encrypted_json.rs b/crates/bitwarden-exporters/src/encrypted_json.rs index 081dc5d8f..b5c11736b 100644 --- a/crates/bitwarden-exporters/src/encrypted_json.rs +++ b/crates/bitwarden-exporters/src/encrypted_json.rs @@ -58,10 +58,10 @@ pub(crate) fn export_encrypted_json( kdf_memory, kdf_parallelism, enc_key_validation: enc_key_validation - .encrypt_with_key(&key, &ContentFormat::Utf8)? + .encrypt_with_key(&key, ContentFormat::Utf8)? .to_string(), data: decrypted_export - .encrypt_with_key(&key, &ContentFormat::Utf8)? + .encrypt_with_key(&key, ContentFormat::Utf8)? .to_string(), }; diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 848f68978..978c7e24a 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -56,7 +56,7 @@ impl PureCrypto { pub fn symmetric_encrypt_string(plain: String, key: Vec) -> Result { plain - .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?, &ContentFormat::Utf8) + .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?, ContentFormat::Utf8) .map(|enc| enc.to_string()) } @@ -64,7 +64,7 @@ impl PureCrypto { plain .encrypt_with_key( &SymmetricCryptoKey::try_from(key)?, - &ContentFormat::OctetStream, + ContentFormat::OctetStream, ) .map(|enc| enc.to_string()) } @@ -76,7 +76,7 @@ impl PureCrypto { plain .encrypt_with_key( &SymmetricCryptoKey::try_from(key)?, - &ContentFormat::OctetStream, + ContentFormat::OctetStream, )? .to_buffer() } From e5bd2511332e8ad56a03d23a0182d5b7d61687cd Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 21 May 2025 15:46:48 +0200 Subject: [PATCH 009/124] Add CompositeEncryptable trait --- crates/bitwarden-core/src/mobile/crypto.rs | 2 +- crates/bitwarden-crypto/src/cose.rs | 5 --- crates/bitwarden-crypto/src/lib.rs | 2 +- crates/bitwarden-crypto/src/store/context.rs | 15 +++---- crates/bitwarden-crypto/src/store/mod.rs | 27 +++++------- .../src/traits/encryptable.rs | 44 +++++++++++++++++-- crates/bitwarden-crypto/src/traits/mod.rs | 2 +- crates/bitwarden-exporters/src/export.rs | 5 +-- crates/bitwarden-send/src/send.rs | 22 ++++------ .../bitwarden-vault/src/cipher/attachment.rs | 14 +++--- crates/bitwarden-vault/src/cipher/card.rs | 7 ++- crates/bitwarden-vault/src/cipher/cipher.rs | 30 ++++++------- crates/bitwarden-vault/src/cipher/field.rs | 7 ++- crates/bitwarden-vault/src/cipher/identity.rs | 7 ++- .../bitwarden-vault/src/cipher/local_data.rs | 7 ++- crates/bitwarden-vault/src/cipher/login.rs | 24 +++++----- .../bitwarden-vault/src/cipher/secure_note.rs | 7 ++- crates/bitwarden-vault/src/cipher/ssh_key.rs | 13 +++--- crates/bitwarden-vault/src/folder.rs | 9 ++-- .../bitwarden-vault/src/password_history.rs | 9 ++-- 20 files changed, 132 insertions(+), 126 deletions(-) diff --git a/crates/bitwarden-core/src/mobile/crypto.rs b/crates/bitwarden-core/src/mobile/crypto.rs index 4b33520c0..aff2755b7 100644 --- a/crates/bitwarden-core/src/mobile/crypto.rs +++ b/crates/bitwarden-core/src/mobile/crypto.rs @@ -859,7 +859,7 @@ mod tests { let invalid_private_key = "bad_key" .to_string() .into_bytes() - .encrypt_with_key(&user_key.0, &bitwarden_crypto::ContentFormat::Utf8) + .encrypt_with_key(&user_key.0, bitwarden_crypto::ContentFormat::Utf8) .unwrap(); let request = VerifyAsymmetricKeysRequest { diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index d9c5376d2..ed01ddf32 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -31,9 +31,6 @@ pub enum ContentFormat { Pkcs8, CoseKey, OctetStream, - /// Domain object should never be serialized. It is used to indicate when we call an encrypt - /// operation on a complex object that consists of multiple, individually encrypted fields - DomainObject, } /// Encrypts a plaintext message using XChaCha20Poly1305 and returns a COSE Encrypt0 message @@ -52,8 +49,6 @@ pub(crate) fn encrypt_xchacha20_poly1305( ContentFormat::OctetStream => { protected_header.content_format(CoapContentFormat::OctetStream) } - // This should panic, and should never be implemented to be reachable! - ContentFormat::DomainObject => unreachable!(), }; let mut protected_header = protected_header.build(); // This should be adjusted to use the builder pattern once implemented in coset. diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index 33ca433ed..9da1a02e3 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -34,7 +34,7 @@ mod cose; mod traits; pub use cose::ContentFormat; mod xchacha20; -pub use traits::{Decryptable, Encryptable, IdentifyKey, KeyId, KeyIds}; +pub use traits::{Decryptable, Encryptable, CompositeEncryptable, IdentifyKey, KeyId, KeyIds}; pub use zeroizing_alloc::ZeroAlloc as ZeroizingAllocator; #[cfg(feature = "uniffi")] diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 79b4b2946..f1cfd419e 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -53,8 +53,8 @@ use crate::{ /// /// const LOCAL_KEY: SymmKeyId = SymmKeyId::Local("local_key_id"); /// -/// impl Encryptable for Data { -/// fn encrypt(&self, ctx: &mut KeyStoreContext, key: SymmKeyId, content_format: ContentFormat) -> Result { +/// impl CompositeEncryptable for Data { +/// fn encrypt_composite(&self, ctx: &mut KeyStoreContext, key: SymmKeyId) -> Result { /// let local_key_id = ctx.unwrap_symmetric_key(key, LOCAL_KEY, &self.key)?; /// self.name.encrypt(ctx, local_key_id, content_format) /// } @@ -382,13 +382,10 @@ impl KeyStoreContext<'_, Ids> { #[allow(deprecated)] mod tests { use crate::{ - cose::ContentFormat, - store::{ + cose::ContentFormat, store::{ tests::{Data, DataView}, KeyStore, - }, - traits::tests::{TestIds, TestSymmKey}, - Decryptable, Encryptable, SymmetricCryptoKey, + }, traits::tests::{TestIds, TestSymmKey}, CompositeEncryptable, Decryptable, Encryptable, SymmetricCryptoKey }; #[test] @@ -409,7 +406,7 @@ mod tests { // Encrypt some data with the key let data = DataView("Hello, World!".to_string(), key_a0_id); let _encrypted: Data = data - .encrypt(&mut store.context(), key_a0_id, ContentFormat::DomainObject) + .encrypt_composite(&mut store.context(), key_a0_id) .unwrap(); } @@ -449,7 +446,7 @@ mod tests { let data = DataView("Hello, World!".to_string(), key_2_id); let encrypted = data - .encrypt(&mut ctx, key_2_id, ContentFormat::OctetStream) + .encrypt_composite(&mut ctx, key_2_id) .unwrap(); let decrypted1 = encrypted.decrypt(&mut ctx, key_2_id).unwrap(); diff --git a/crates/bitwarden-crypto/src/store/mod.rs b/crates/bitwarden-crypto/src/store/mod.rs index d9a6b6c7d..95d4c4ee1 100644 --- a/crates/bitwarden-crypto/src/store/mod.rs +++ b/crates/bitwarden-crypto/src/store/mod.rs @@ -26,7 +26,7 @@ use std::sync::{Arc, RwLock}; use rayon::prelude::*; -use crate::{cose::ContentFormat, Decryptable, Encryptable, IdentifyKey, KeyId, KeyIds}; +use crate::{CompositeEncryptable, Decryptable, IdentifyKey, KeyId, KeyIds}; mod backend; mod context; @@ -74,9 +74,9 @@ pub use context::KeyStoreContext; /// SymmKeyId::User /// } /// } -/// impl Encryptable for Data { -/// fn encrypt(&self, ctx: &mut KeyStoreContext, key: SymmKeyId, content_format: ContentFormat) -> Result { -/// self.0.encrypt(ctx, key, content_format) +/// impl CompositeEncryptable for Data { +/// fn encrypt_composite(&self, ctx: &mut KeyStoreContext, key: SymmKeyId) -> Result { +/// self.0.encrypt(ctx, key) /// } /// } /// @@ -209,12 +209,12 @@ impl KeyStore { /// already be present in the store, otherwise this will return an error. /// This method is not parallelized, and is meant for single item encryption. /// If you need to encrypt multiple items, use `encrypt_list` instead. - pub fn encrypt + IdentifyKey, Output>( + pub fn encrypt + IdentifyKey, Output>( &self, data: Data, ) -> Result { let key = data.key_identifier(); - data.encrypt(&mut self.context(), key, ContentFormat::OctetStream) + data.encrypt_composite(&mut self.context(), key) } /// Decrypt a list of items using this key store. The keys returned by @@ -257,7 +257,7 @@ impl KeyStore { /// single item encryption. pub fn encrypt_list< Key: KeyId, - Data: Encryptable + IdentifyKey + Send + Sync, + Data: CompositeEncryptable + IdentifyKey + Send + Sync, Output: Send + Sync, >( &self, @@ -272,7 +272,7 @@ impl KeyStore { for item in chunk { let key = item.key_identifier(); - result.push(item.encrypt(&mut ctx, key, ContentFormat::DomainObject)); + result.push(item.encrypt_composite(&mut ctx, key)); ctx.clear_local(); } @@ -304,9 +304,7 @@ fn batch_chunk_size(len: usize) -> usize { #[cfg(test)] pub(crate) mod tests { use crate::{ - store::{KeyStore, KeyStoreContext}, - traits::tests::{TestIds, TestSymmKey}, - EncString, SymmetricCryptoKey, + store::{KeyStore, KeyStoreContext}, traits::tests::{TestIds, TestSymmKey}, EncString, Encryptable, SymmetricCryptoKey }; pub struct DataView(pub String, pub TestSymmKey); @@ -324,14 +322,13 @@ pub(crate) mod tests { } } - impl crate::Encryptable for DataView { - fn encrypt( + impl crate::CompositeEncryptable for DataView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: TestSymmKey, - _content_format: crate::cose::ContentFormat, ) -> Result { - Ok(Data(self.0.encrypt(ctx, key, _content_format)?, key)) + Ok(Data(self.0.encrypt(ctx, key, crate::ContentFormat::Utf8)?, key)) } } diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index abd06729c..76cb6d137 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -1,8 +1,47 @@ use crate::{store::KeyStoreContext, ContentFormat, CryptoError, EncString, KeyId, KeyIds}; -/// An encryption operation that takes the input value and encrypts it into the output value. +/// An encryption operation that takes the input value and encrypts the fields on it recursively. /// Implementations should generally consist of calling [Encryptable::encrypt] for all the fields of /// the type. +pub trait CompositeEncryptable { + /// For a struct made up of many small encstrings, such as a cipher, this takes the struct + /// and recursively encrypts all the fields / sub-structs. + fn encrypt_composite( + &self, + ctx: &mut KeyStoreContext, + key: Key, + ) -> Result; +} + +impl, Output> + CompositeEncryptable> for Option +{ + fn encrypt_composite( + &self, + ctx: &mut KeyStoreContext, + key: Key, + ) -> Result, CryptoError> { + self.as_ref() + .map(|value| value.encrypt_composite(ctx, key)) + .transpose() + } +} + +impl , Output> + CompositeEncryptable> for Vec +{ + fn encrypt_composite( + &self, + ctx: &mut KeyStoreContext, + key: Key, + ) -> Result, CryptoError> { + self.iter() + .map(|value| value.encrypt_composite(ctx, key)) + .collect() + } +} + +/// An encryption operation that takes the input value - a primitive such as `Vec`, `String` - and encrypts it into the output value. pub trait Encryptable { fn encrypt( &self, @@ -89,8 +128,7 @@ impl, Output> #[cfg(test)] mod tests { use crate::{ - cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, - KeyStore, SymmetricCryptoKey, + cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, KeyStore, SymmetricCryptoKey }; fn test_store() -> KeyStore { diff --git a/crates/bitwarden-crypto/src/traits/mod.rs b/crates/bitwarden-crypto/src/traits/mod.rs index 28b811e36..6fc882566 100644 --- a/crates/bitwarden-crypto/src/traits/mod.rs +++ b/crates/bitwarden-crypto/src/traits/mod.rs @@ -1,5 +1,5 @@ mod encryptable; -pub use encryptable::Encryptable; +pub use encryptable::{CompositeEncryptable, Encryptable}; mod decryptable; pub use decryptable::Decryptable; diff --git a/crates/bitwarden-exporters/src/export.rs b/crates/bitwarden-exporters/src/export.rs index f1a510638..a6d7da1f2 100644 --- a/crates/bitwarden-exporters/src/export.rs +++ b/crates/bitwarden-exporters/src/export.rs @@ -1,5 +1,5 @@ use bitwarden_core::{key_management::KeyIds, Client}; -use bitwarden_crypto::{Encryptable, IdentifyKey, KeyStoreContext}; +use bitwarden_crypto::{CompositeEncryptable, IdentifyKey, KeyStoreContext}; use bitwarden_vault::{Cipher, CipherView, Collection, Folder, FolderView}; use crate::{ @@ -80,10 +80,9 @@ fn encrypt_import( view.set_new_fido2_credentials(ctx, passkeys)?; } - let new_cipher = view.encrypt( + let new_cipher = view.encrypt_composite( ctx, view.key_identifier(), - bitwarden_crypto::ContentFormat::DomainObject, )?; Ok(new_cipher) diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index 7c8bb4019..f75c3ffa8 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -8,8 +8,7 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - generate_random_bytes, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, - IdentifyKey, KeyStoreContext, + generate_random_bytes, CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -189,12 +188,11 @@ impl Decryptable for SendText { } } -impl Encryptable for SendTextView { - fn encrypt( +impl CompositeEncryptable for SendTextView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { Ok(SendText { text: self.text.encrypt(ctx, key, ContentFormat::Utf8)?, @@ -218,12 +216,11 @@ impl Decryptable for SendFile { } } -impl Encryptable for SendFileView { - fn encrypt( +impl CompositeEncryptable for SendFileView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { Ok(SendFile { id: self.id.clone(), @@ -299,12 +296,11 @@ impl Decryptable for Send { } } -impl Encryptable for SendView { - fn encrypt( +impl CompositeEncryptable for SendView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { // For sends, we first decrypt the send key with the user key, and stretch it to it's full // size For the rest of the fields, we ignore the provided SymmetricCryptoKey and @@ -338,8 +334,8 @@ impl Encryptable for SendView { }), r#type: self.r#type, - file: self.file.encrypt(ctx, send_key, ContentFormat::Utf8)?, - text: self.text.encrypt(ctx, send_key, ContentFormat::Utf8)?, + file: self.file.encrypt_composite(ctx, send_key)?, + text: self.text.encrypt_composite(ctx, send_key)?, max_access_count: self.max_access_count, access_count: self.access_count, diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index 86229765d..3ab4b974b 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,6 +1,6 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, + CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -73,12 +73,11 @@ impl IdentifyKey for AttachmentFile { } } -impl Encryptable for AttachmentFileView<'_> { - fn encrypt( +impl CompositeEncryptable for AttachmentFileView<'_> { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { let ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.cipher.key)?; @@ -99,7 +98,7 @@ impl Encryptable for Attachment attachment.size_name = Some(size_name(contents.len())); Ok(AttachmentEncryptResult { - attachment: attachment.encrypt(ctx, ciphers_key, ContentFormat::DomainObject)?, + attachment: attachment.encrypt_composite(ctx, ciphers_key)?, contents, }) } @@ -135,12 +134,11 @@ impl Decryptable> for AttachmentFile { } } -impl Encryptable for AttachmentView { - fn encrypt( +impl CompositeEncryptable for AttachmentView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { Ok(Attachment { id: self.id.clone(), diff --git a/crates/bitwarden-vault/src/cipher/card.rs b/crates/bitwarden-vault/src/cipher/card.rs index a1be406ee..39a0e8fb5 100644 --- a/crates/bitwarden-vault/src/cipher/card.rs +++ b/crates/bitwarden-vault/src/cipher/card.rs @@ -1,7 +1,7 @@ use bitwarden_api_api::models::CipherCardModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -52,12 +52,11 @@ pub enum CardBrand { Other, } -impl Encryptable for CardView { - fn encrypt( +impl CompositeEncryptable for CardView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { Ok(Card { cardholder_name: self diff --git a/crates/bitwarden-vault/src/cipher/cipher.rs b/crates/bitwarden-vault/src/cipher/cipher.rs index 36f7c6f2e..4de9c329a 100644 --- a/crates/bitwarden-vault/src/cipher/cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher.rs @@ -4,7 +4,7 @@ use bitwarden_core::{ require, MissingFieldError, VaultLockedError, }; use bitwarden_crypto::{ - ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, + CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext }; use bitwarden_error::bitwarden_error; use chrono::{DateTime, Utc}; @@ -210,12 +210,11 @@ impl CipherListView { } } -impl Encryptable for CipherView { - fn encrypt( +impl CompositeEncryptable for CipherView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { let ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.key)?; @@ -241,19 +240,19 @@ impl Encryptable for CipherView { r#type: cipher_view.r#type, login: cipher_view .login - .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + .encrypt_composite(ctx, ciphers_key)?, identity: cipher_view .identity - .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + .encrypt_composite(ctx, ciphers_key)?, card: cipher_view .card - .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + .encrypt_composite(ctx, ciphers_key)?, secure_note: cipher_view .secure_note - .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + .encrypt_composite(ctx, ciphers_key)?, ssh_key: cipher_view .ssh_key - .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + .encrypt_composite(ctx, ciphers_key)?, favorite: cipher_view.favorite, reprompt: cipher_view.reprompt, organization_use_totp: cipher_view.organization_use_totp, @@ -261,17 +260,16 @@ impl Encryptable for CipherView { view_password: cipher_view.view_password, local_data: cipher_view .local_data - .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + .encrypt_composite(ctx, ciphers_key)?, attachments: cipher_view .attachments - .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + .encrypt_composite(ctx, ciphers_key)?, fields: cipher_view .fields - .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, - password_history: cipher_view.password_history.encrypt( + .encrypt_composite(ctx, ciphers_key)?, + password_history: cipher_view.password_history.encrypt_composite( ctx, ciphers_key, - ContentFormat::Utf8, )?, creation_date: cipher_view.creation_date, deleted_date: cipher_view.deleted_date, @@ -550,7 +548,7 @@ impl CipherView { let dec_fido2_credentials: Vec = fido2_credentials.decrypt(ctx, old_key)?; *fido2_credentials = - dec_fido2_credentials.encrypt(ctx, new_key, ContentFormat::DomainObject)?; + dec_fido2_credentials.encrypt_composite(ctx, new_key)?; } } Ok(()) @@ -594,7 +592,7 @@ impl CipherView { let ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.key)?; require!(self.login.as_mut()).fido2_credentials = - Some(creds.encrypt(ctx, ciphers_key, ContentFormat::DomainObject)?); + Some(creds.encrypt_composite(ctx, ciphers_key)?); Ok(()) } diff --git a/crates/bitwarden-vault/src/cipher/field.rs b/crates/bitwarden-vault/src/cipher/field.rs index 57002d541..1edf74ee5 100644 --- a/crates/bitwarden-vault/src/cipher/field.rs +++ b/crates/bitwarden-vault/src/cipher/field.rs @@ -4,7 +4,7 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext }; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; @@ -51,12 +51,11 @@ pub struct FieldView { pub linked_id: Option, } -impl Encryptable for FieldView { - fn encrypt( +impl CompositeEncryptable for FieldView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { Ok(Field { name: self.name.encrypt(ctx, key, ContentFormat::Utf8)?, diff --git a/crates/bitwarden-vault/src/cipher/identity.rs b/crates/bitwarden-vault/src/cipher/identity.rs index 066ea338a..0771b4318 100644 --- a/crates/bitwarden-vault/src/cipher/identity.rs +++ b/crates/bitwarden-vault/src/cipher/identity.rs @@ -1,7 +1,7 @@ use bitwarden_api_api::models::CipherIdentityModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -59,12 +59,11 @@ pub struct IdentityView { pub license_number: Option, } -impl Encryptable for IdentityView { - fn encrypt( +impl CompositeEncryptable for IdentityView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { Ok(Identity { title: self.title.encrypt(ctx, key, ContentFormat::Utf8)?, diff --git a/crates/bitwarden-vault/src/cipher/local_data.rs b/crates/bitwarden-vault/src/cipher/local_data.rs index 13e3cb4f5..f4fd14325 100644 --- a/crates/bitwarden-vault/src/cipher/local_data.rs +++ b/crates/bitwarden-vault/src/cipher/local_data.rs @@ -1,5 +1,5 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; -use bitwarden_crypto::{ContentFormat, CryptoError, Decryptable, Encryptable, KeyStoreContext}; +use bitwarden_crypto::{CompositeEncryptable, CryptoError, Decryptable, KeyStoreContext}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -23,12 +23,11 @@ pub struct LocalDataView { last_launched: Option>, } -impl Encryptable for LocalDataView { - fn encrypt( +impl CompositeEncryptable for LocalDataView { + fn encrypt_composite( &self, _ctx: &mut KeyStoreContext, _key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { Ok(LocalData { last_used_date: self.last_used_date, diff --git a/crates/bitwarden-vault/src/cipher/login.rs b/crates/bitwarden-vault/src/cipher/login.rs index 6f7c94385..858a55459 100644 --- a/crates/bitwarden-vault/src/cipher/login.rs +++ b/crates/bitwarden-vault/src/cipher/login.rs @@ -5,7 +5,7 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -192,12 +192,11 @@ impl From for Fido2CredentialNewView { } } -impl Encryptable for Fido2CredentialFullView { - fn encrypt( +impl CompositeEncryptable for Fido2CredentialFullView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { Ok(Fido2Credential { credential_id: self.credential_id.encrypt(ctx, key, ContentFormat::Utf8)?, @@ -317,12 +316,11 @@ pub struct LoginListView { pub uris: Option>, } -impl Encryptable for LoginUriView { - fn encrypt( +impl CompositeEncryptable for LoginUriView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { Ok(LoginUri { uri: self.uri.encrypt(ctx, key, ContentFormat::Utf8)?, @@ -332,18 +330,17 @@ impl Encryptable for LoginUriView { } } -impl Encryptable for LoginView { - fn encrypt( +impl CompositeEncryptable for LoginView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { Ok(Login { username: self.username.encrypt(ctx, key, ContentFormat::Utf8)?, password: self.password.encrypt(ctx, key, ContentFormat::Utf8)?, password_revision_date: self.password_revision_date, - uris: self.uris.encrypt(ctx, key, ContentFormat::Utf8)?, + uris: self.uris.encrypt_composite(ctx, key)?, totp: self.totp.encrypt(ctx, key, ContentFormat::Utf8)?, autofill_on_page_load: self.autofill_on_page_load, fido2_credentials: self.fido2_credentials.clone(), @@ -403,12 +400,11 @@ impl Decryptable for Login { } } -impl Encryptable for Fido2CredentialView { - fn encrypt( +impl CompositeEncryptable for Fido2CredentialView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { Ok(Fido2Credential { credential_id: self.credential_id.encrypt(ctx, key, ContentFormat::Utf8)?, diff --git a/crates/bitwarden-vault/src/cipher/secure_note.rs b/crates/bitwarden-vault/src/cipher/secure_note.rs index 19b4a2718..0ade86ccc 100644 --- a/crates/bitwarden-vault/src/cipher/secure_note.rs +++ b/crates/bitwarden-vault/src/cipher/secure_note.rs @@ -3,7 +3,7 @@ use bitwarden_core::{ key_management::{KeyIds, SymmetricKeyId}, require, }; -use bitwarden_crypto::{ContentFormat, CryptoError, Decryptable, Encryptable, KeyStoreContext}; +use bitwarden_crypto::{CompositeEncryptable, CryptoError, Decryptable, KeyStoreContext}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; #[cfg(feature = "wasm")] @@ -37,12 +37,11 @@ pub struct SecureNoteView { pub r#type: SecureNoteType, } -impl Encryptable for SecureNoteView { - fn encrypt( +impl CompositeEncryptable for SecureNoteView { + fn encrypt_composite( &self, _ctx: &mut KeyStoreContext, _key: SymmetricKeyId, - _content_format: ContentFormat, ) -> Result { Ok(SecureNote { r#type: self.r#type, diff --git a/crates/bitwarden-vault/src/cipher/ssh_key.rs b/crates/bitwarden-vault/src/cipher/ssh_key.rs index 286b8cf36..5ece59020 100644 --- a/crates/bitwarden-vault/src/cipher/ssh_key.rs +++ b/crates/bitwarden-vault/src/cipher/ssh_key.rs @@ -1,6 +1,6 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -32,17 +32,16 @@ pub struct SshKeyView { pub fingerprint: String, } -impl Encryptable for SshKeyView { - fn encrypt( +impl CompositeEncryptable for SshKeyView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - content_format: ContentFormat, ) -> Result { Ok(SshKey { - private_key: self.private_key.encrypt(ctx, key, content_format)?, - public_key: self.public_key.encrypt(ctx, key, content_format)?, - fingerprint: self.fingerprint.encrypt(ctx, key, content_format)?, + private_key: self.private_key.encrypt(ctx, key, ContentFormat::Utf8)?, + public_key: self.public_key.encrypt(ctx, key, ContentFormat::Utf8)?, + fingerprint: self.fingerprint.encrypt(ctx, key, ContentFormat::Utf8)?, }) } } diff --git a/crates/bitwarden-vault/src/folder.rs b/crates/bitwarden-vault/src/folder.rs index c664e366e..5fe2029a5 100644 --- a/crates/bitwarden-vault/src/folder.rs +++ b/crates/bitwarden-vault/src/folder.rs @@ -4,7 +4,7 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, + CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -45,16 +45,15 @@ impl IdentifyKey for FolderView { } } -impl Encryptable for FolderView { - fn encrypt( +impl CompositeEncryptable for FolderView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - content_format: ContentFormat, ) -> Result { Ok(Folder { id: self.id, - name: self.name.encrypt(ctx, key, content_format)?, + name: self.name.encrypt(ctx, key, ContentFormat::Utf8)?, revision_date: self.revision_date, }) } diff --git a/crates/bitwarden-vault/src/password_history.rs b/crates/bitwarden-vault/src/password_history.rs index 0fba1fa41..a3653342e 100644 --- a/crates/bitwarden-vault/src/password_history.rs +++ b/crates/bitwarden-vault/src/password_history.rs @@ -1,7 +1,7 @@ use bitwarden_api_api::models::CipherPasswordHistoryModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, + CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -39,15 +39,14 @@ impl IdentifyKey for PasswordHistoryView { } } -impl Encryptable for PasswordHistoryView { - fn encrypt( +impl CompositeEncryptable for PasswordHistoryView { + fn encrypt_composite( &self, ctx: &mut KeyStoreContext, key: SymmetricKeyId, - content_format: ContentFormat, ) -> Result { Ok(PasswordHistory { - password: self.password.encrypt(ctx, key, content_format)?, + password: self.password.encrypt(ctx, key, ContentFormat::Utf8)?, last_used_date: self.last_used_date, }) } From b92010bdf0fe2e42a7de75541ff0b7ad25f90794 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 21 May 2025 15:50:21 +0200 Subject: [PATCH 010/124] Cleanup --- crates/bitwarden-core/src/mobile/crypto.rs | 2 +- crates/bitwarden-crypto/src/cose.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/bitwarden-core/src/mobile/crypto.rs b/crates/bitwarden-core/src/mobile/crypto.rs index aff2755b7..56a3dbeb9 100644 --- a/crates/bitwarden-core/src/mobile/crypto.rs +++ b/crates/bitwarden-core/src/mobile/crypto.rs @@ -859,7 +859,7 @@ mod tests { let invalid_private_key = "bad_key" .to_string() .into_bytes() - .encrypt_with_key(&user_key.0, bitwarden_crypto::ContentFormat::Utf8) + .encrypt_with_key(&user_key.0, ContentFormat::Utf8) .unwrap(); let request = VerifyAsymmetricKeysRequest { diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index ed01ddf32..4e6f5afe4 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -24,6 +24,10 @@ pub(crate) const XCHACHA20_POLY1305: i64 = -70000; const XCHACHA20_TEXT_PAD_BLOCK_SIZE: usize = 32; const CONTENT_TYPE_PADDED_UTF8: &str = "application/utf8-padded"; +/// The content format describes the format of the contained bytes. Message encryption always happens on the byte level, and this +/// allows determining what format the contained data has. For instance, an `EncString` in most cases contains UTF-8 encoded +/// text. In some cases it may contain a Pkcs8 private key, or a COSE key. Specifically, for COSE keys, this allows distinguishing between +/// the old symmetric key format, represented as `ContentFormat::OctetStream`, and the new COSE key format, represented as `ContentFormat::CoseKey`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] pub enum ContentFormat { From b8056c235fcce4e2141f338e6b3d8f499bbc0da5 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 21 May 2025 15:51:55 +0200 Subject: [PATCH 011/124] Fix typo --- crates/bitwarden-core/src/mobile/crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-core/src/mobile/crypto.rs b/crates/bitwarden-core/src/mobile/crypto.rs index 56a3dbeb9..f5db8239a 100644 --- a/crates/bitwarden-core/src/mobile/crypto.rs +++ b/crates/bitwarden-core/src/mobile/crypto.rs @@ -32,7 +32,7 @@ pub enum MobileCryptoError { Crypto(#[from] bitwarden_crypto::CryptoError), } -/// thState used for initializing the user cryptographic state. +/// State used for initializing the user cryptographic state. #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] From 9440825d79134379291a9f1868e5fbd8f46ff01a Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 21 May 2025 19:45:42 +0200 Subject: [PATCH 012/124] Typed encryptable --- .../bitwarden-sm/src/projects/create.rs | 4 +- .../bitwarden-sm/src/projects/update.rs | 4 +- .../bitwarden-sm/src/secrets/create.rs | 8 +-- .../bitwarden-sm/src/secrets/update.rs | 8 +-- crates/bitwarden-crypto/src/lib.rs | 2 +- crates/bitwarden-crypto/src/store/context.rs | 4 +- crates/bitwarden-crypto/src/store/mod.rs | 4 +- .../src/traits/encryptable.rs | 64 +++++++++++++------ crates/bitwarden-crypto/src/traits/mod.rs | 2 +- crates/bitwarden-send/src/send.rs | 10 +-- .../bitwarden-vault/src/cipher/attachment.rs | 4 +- crates/bitwarden-vault/src/cipher/card.rs | 14 ++-- crates/bitwarden-vault/src/cipher/cipher.rs | 30 ++++----- crates/bitwarden-vault/src/cipher/field.rs | 6 +- crates/bitwarden-vault/src/cipher/identity.rs | 38 +++++------ crates/bitwarden-vault/src/cipher/login.rs | 58 ++++++++--------- crates/bitwarden-vault/src/cipher/ssh_key.rs | 8 +-- crates/bitwarden-vault/src/folder.rs | 4 +- .../bitwarden-vault/src/password_history.rs | 4 +- 19 files changed, 150 insertions(+), 126 deletions(-) diff --git a/bitwarden_license/bitwarden-sm/src/projects/create.rs b/bitwarden_license/bitwarden-sm/src/projects/create.rs index aafc4fb7a..0c876e5b8 100644 --- a/bitwarden_license/bitwarden-sm/src/projects/create.rs +++ b/bitwarden_license/bitwarden-sm/src/projects/create.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::ProjectCreateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::{ContentFormat, Encryptable}; +use bitwarden_crypto::TypedEncryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -34,7 +34,7 @@ pub(crate) async fn create_project( .name .clone() .trim() - .encrypt(&mut key_store.context(), key, ContentFormat::Utf8)? + .encrypt(&mut key_store.context(), key)? .to_string(), }); diff --git a/bitwarden_license/bitwarden-sm/src/projects/update.rs b/bitwarden_license/bitwarden-sm/src/projects/update.rs index ae79c670f..43730b4ef 100644 --- a/bitwarden_license/bitwarden-sm/src/projects/update.rs +++ b/bitwarden_license/bitwarden-sm/src/projects/update.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::ProjectUpdateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::{ContentFormat, Encryptable}; +use bitwarden_crypto::{Encryptable, TypedEncryptable}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -36,7 +36,7 @@ pub(crate) async fn update_project( .name .clone() .trim() - .encrypt(&mut key_store.context(), key, ContentFormat::Utf8)? + .encrypt(&mut key_store.context(), key)? .to_string(), }); diff --git a/bitwarden_license/bitwarden-sm/src/secrets/create.rs b/bitwarden_license/bitwarden-sm/src/secrets/create.rs index 75e8837b9..30f278b12 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/create.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/create.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::SecretCreateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::{ContentFormat, Encryptable}; +use bitwarden_crypto::{Encryptable, TypedEncryptable}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -44,18 +44,18 @@ pub(crate) async fn create_secret( .key .clone() .trim() - .encrypt(&mut ctx, key, ContentFormat::Utf8)? + .encrypt(&mut ctx, key)? .to_string(), value: input .value .clone() - .encrypt(&mut ctx, key, ContentFormat::Utf8)? + .encrypt(&mut ctx, key)? .to_string(), note: input .note .clone() .trim() - .encrypt(&mut ctx, key, ContentFormat::Utf8)? + .encrypt(&mut ctx, key)? .to_string(), project_ids: input.project_ids.clone(), access_policies_requests: None, diff --git a/bitwarden_license/bitwarden-sm/src/secrets/update.rs b/bitwarden_license/bitwarden-sm/src/secrets/update.rs index 03151e8ca..8920b4cdd 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/update.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/update.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::SecretUpdateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::{ContentFormat, Encryptable}; +use bitwarden_crypto::{Encryptable, TypedEncryptable}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -43,18 +43,18 @@ pub(crate) async fn update_secret( .key .clone() .trim() - .encrypt(&mut ctx, key, ContentFormat::Utf8)? + .encrypt(&mut ctx, key)? .to_string(), value: input .value .clone() - .encrypt(&mut ctx, key, ContentFormat::Utf8)? + .encrypt(&mut ctx, key)? .to_string(), note: input .note .clone() .trim() - .encrypt(&mut ctx, key, ContentFormat::Utf8)? + .encrypt(&mut ctx, key)? .to_string(), project_ids: input.project_ids.clone(), access_policies_requests: None, diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index 9da1a02e3..05de266de 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -34,7 +34,7 @@ mod cose; mod traits; pub use cose::ContentFormat; mod xchacha20; -pub use traits::{Decryptable, Encryptable, CompositeEncryptable, IdentifyKey, KeyId, KeyIds}; +pub use traits::{Decryptable, Encryptable, CompositeEncryptable, TypedEncryptable, IdentifyKey, KeyId, KeyIds}; pub use zeroizing_alloc::ZeroAlloc as ZeroizingAllocator; #[cfg(feature = "uniffi")] diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index f1cfd419e..df12cded1 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -382,10 +382,10 @@ impl KeyStoreContext<'_, Ids> { #[allow(deprecated)] mod tests { use crate::{ - cose::ContentFormat, store::{ + store::{ tests::{Data, DataView}, KeyStore, - }, traits::tests::{TestIds, TestSymmKey}, CompositeEncryptable, Decryptable, Encryptable, SymmetricCryptoKey + }, traits::tests::{TestIds, TestSymmKey}, CompositeEncryptable, Decryptable, SymmetricCryptoKey }; #[test] diff --git a/crates/bitwarden-crypto/src/store/mod.rs b/crates/bitwarden-crypto/src/store/mod.rs index 95d4c4ee1..44345fb30 100644 --- a/crates/bitwarden-crypto/src/store/mod.rs +++ b/crates/bitwarden-crypto/src/store/mod.rs @@ -304,7 +304,7 @@ fn batch_chunk_size(len: usize) -> usize { #[cfg(test)] pub(crate) mod tests { use crate::{ - store::{KeyStore, KeyStoreContext}, traits::tests::{TestIds, TestSymmKey}, EncString, Encryptable, SymmetricCryptoKey + store::{KeyStore, KeyStoreContext}, traits::tests::{TestIds, TestSymmKey}, EncString, Encryptable, SymmetricCryptoKey, TypedEncryptable }; pub struct DataView(pub String, pub TestSymmKey); @@ -328,7 +328,7 @@ pub(crate) mod tests { ctx: &mut KeyStoreContext, key: TestSymmKey, ) -> Result { - Ok(Data(self.0.encrypt(ctx, key, crate::ContentFormat::Utf8)?, key)) + Ok(Data(self.0.encrypt(ctx, key)?, key)) } } diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index 76cb6d137..f189afc1c 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -41,57 +41,81 @@ impl , Output } } -/// An encryption operation that takes the input value - a primitive such as `Vec`, `String` - and encrypts it into the output value. -pub trait Encryptable { +/// An encryption operation that takes the input value - a primitive such as `String` and encrypts it into the output value. +/// The implementation decides the content format. +pub trait TypedEncryptable { fn encrypt( &self, ctx: &mut KeyStoreContext, key: Key, - content_format: ContentFormat, ) -> Result; } -impl Encryptable for &[u8] { +impl, Output> + TypedEncryptable> for Option +{ + fn encrypt( + &self, + ctx: &mut KeyStoreContext, + key: Key, + ) -> Result, CryptoError> { + self.as_ref() + .map(|value| value.encrypt(ctx, key)) + .transpose() + } +} + +impl TypedEncryptable for &str +{ fn encrypt( &self, ctx: &mut KeyStoreContext, key: Ids::Symmetric, - content_format: ContentFormat, ) -> Result { - ctx.encrypt_data_with_symmetric_key(key, self, content_format) + self.as_bytes().encrypt(ctx, key, ContentFormat::Utf8) } } -impl Encryptable for Vec { +impl TypedEncryptable for String +{ fn encrypt( &self, ctx: &mut KeyStoreContext, key: Ids::Symmetric, - content_format: ContentFormat, ) -> Result { - ctx.encrypt_data_with_symmetric_key(key, self, content_format) + self.as_bytes().encrypt(ctx, key, ContentFormat::Utf8) } } -impl Encryptable for &str { +/// An encryption operation that takes the input value - a primitive such as `Vec` - and encrypts it into the output value. +pub trait Encryptable { + fn encrypt( + &self, + ctx: &mut KeyStoreContext, + key: Key, + content_format: ContentFormat, + ) -> Result; +} + +impl Encryptable for &[u8] { fn encrypt( &self, ctx: &mut KeyStoreContext, key: Ids::Symmetric, content_format: ContentFormat, ) -> Result { - self.as_bytes().encrypt(ctx, key, content_format) + ctx.encrypt_data_with_symmetric_key(key, self, content_format) } } -impl Encryptable for String { +impl Encryptable for Vec { fn encrypt( &self, ctx: &mut KeyStoreContext, key: Ids::Symmetric, - content_format: crate::cose::ContentFormat, + content_format: ContentFormat, ) -> Result { - self.as_bytes().encrypt(ctx, key, content_format) + ctx.encrypt_data_with_symmetric_key(key, self, content_format) } } @@ -128,7 +152,7 @@ impl, Output> #[cfg(test)] mod tests { use crate::{ - cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, KeyStore, SymmetricCryptoKey + cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, KeyStore, SymmetricCryptoKey, TypedEncryptable }; fn test_store() -> KeyStore { @@ -184,10 +208,10 @@ mod tests { let str_data: &str = string_data.as_str(); let string_encrypted = string_data - .encrypt(&mut ctx, key, ContentFormat::OctetStream) + .encrypt(&mut ctx, key) .unwrap(); let str_encrypted = str_data - .encrypt(&mut ctx, key, ContentFormat::OctetStream) + .encrypt(&mut ctx, key) .unwrap(); let string_decrypted: String = string_encrypted.decrypt(&mut ctx, key).unwrap(); @@ -206,7 +230,7 @@ mod tests { let string_data = Some("Hello, World!".to_string()); let string_encrypted = string_data - .encrypt(&mut ctx, key, ContentFormat::OctetStream) + .encrypt(&mut ctx, key) .unwrap(); let string_decrypted: Option = string_encrypted.decrypt(&mut ctx, key).unwrap(); @@ -222,7 +246,7 @@ mod tests { let key = TestSymmKey::A(0); let none_data: Option = None; let string_encrypted = none_data - .encrypt(&mut ctx, key, ContentFormat::OctetStream) + .encrypt(&mut ctx, key) .unwrap(); assert_eq!(string_encrypted, None); @@ -230,7 +254,7 @@ mod tests { // key doesn't exist let bad_key = TestSymmKey::B((0, 1)); let string_encrypted_bad = none_data - .encrypt(&mut ctx, bad_key, ContentFormat::OctetStream) + .encrypt(&mut ctx, bad_key) .unwrap(); assert_eq!(string_encrypted_bad, None); } diff --git a/crates/bitwarden-crypto/src/traits/mod.rs b/crates/bitwarden-crypto/src/traits/mod.rs index 6fc882566..122088827 100644 --- a/crates/bitwarden-crypto/src/traits/mod.rs +++ b/crates/bitwarden-crypto/src/traits/mod.rs @@ -1,5 +1,5 @@ mod encryptable; -pub use encryptable::{CompositeEncryptable, Encryptable}; +pub use encryptable::{CompositeEncryptable, Encryptable, TypedEncryptable}; mod decryptable; pub use decryptable::Decryptable; diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index f75c3ffa8..54019767b 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -8,7 +8,7 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - generate_random_bytes, CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext + generate_random_bytes, CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, TypedEncryptable }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -195,7 +195,7 @@ impl CompositeEncryptable for SendTextView { key: SymmetricKeyId, ) -> Result { Ok(SendText { - text: self.text.encrypt(ctx, key, ContentFormat::Utf8)?, + text: self.text.encrypt(ctx, key)?, hidden: self.hidden, }) } @@ -224,7 +224,7 @@ impl CompositeEncryptable for SendFileView { ) -> Result { Ok(SendFile { id: self.id.clone(), - file_name: self.file_name.encrypt(ctx, key, ContentFormat::Utf8)?, + file_name: self.file_name.encrypt(ctx, key)?, size: self.size.clone(), size_name: self.size_name.clone(), }) @@ -324,8 +324,8 @@ impl CompositeEncryptable for SendView { id: self.id, access_id: self.access_id.clone(), - name: self.name.encrypt(ctx, send_key, ContentFormat::Utf8)?, - notes: self.notes.encrypt(ctx, send_key, ContentFormat::Utf8)?, + name: self.name.encrypt(ctx, send_key)?, + notes: self.notes.encrypt(ctx, send_key)?, // In the future, this should support cose key content type key: k.encrypt(ctx, key, ContentFormat::OctetStream)?, password: self.new_password.as_ref().map(|password| { diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index 3ab4b974b..8c5109715 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,6 +1,6 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext + CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, TypedEncryptable }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -145,7 +145,7 @@ impl CompositeEncryptable for AttachmentView url: self.url.clone(), size: self.size.clone(), size_name: self.size_name.clone(), - file_name: self.file_name.encrypt(ctx, key, ContentFormat::Utf8)?, + file_name: self.file_name.encrypt(ctx, key)?, key: self.key.clone(), }) } diff --git a/crates/bitwarden-vault/src/cipher/card.rs b/crates/bitwarden-vault/src/cipher/card.rs index 39a0e8fb5..335889a07 100644 --- a/crates/bitwarden-vault/src/cipher/card.rs +++ b/crates/bitwarden-vault/src/cipher/card.rs @@ -1,7 +1,7 @@ use bitwarden_api_api::models::CipherCardModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, TypedEncryptable }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -61,12 +61,12 @@ impl CompositeEncryptable for CardView { Ok(Card { cardholder_name: self .cardholder_name - .encrypt(ctx, key, ContentFormat::Utf8)?, - exp_month: self.exp_month.encrypt(ctx, key, ContentFormat::Utf8)?, - exp_year: self.exp_year.encrypt(ctx, key, ContentFormat::Utf8)?, - code: self.code.encrypt(ctx, key, ContentFormat::Utf8)?, - brand: self.brand.encrypt(ctx, key, ContentFormat::Utf8)?, - number: self.number.encrypt(ctx, key, ContentFormat::Utf8)?, + .encrypt(ctx, key)?, + exp_month: self.exp_month.encrypt(ctx, key)?, + exp_year: self.exp_year.encrypt(ctx, key)?, + code: self.code.encrypt(ctx, key)?, + brand: self.brand.encrypt(ctx, key)?, + number: self.number.encrypt(ctx, key)?, }) } } diff --git a/crates/bitwarden-vault/src/cipher/cipher.rs b/crates/bitwarden-vault/src/cipher/cipher.rs index 4de9c329a..f3d0bef5b 100644 --- a/crates/bitwarden-vault/src/cipher/cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher.rs @@ -4,7 +4,7 @@ use bitwarden_core::{ require, MissingFieldError, VaultLockedError, }; use bitwarden_crypto::{ - CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, TypedEncryptable }; use bitwarden_error::bitwarden_error; use chrono::{DateTime, Utc}; @@ -233,10 +233,10 @@ impl CompositeEncryptable for CipherView { key: cipher_view.key, name: cipher_view .name - .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + .encrypt(ctx, ciphers_key)?, notes: cipher_view .notes - .encrypt(ctx, ciphers_key, ContentFormat::Utf8)?, + .encrypt(ctx, ciphers_key)?, r#type: cipher_view.r#type, login: cipher_view .login @@ -825,39 +825,39 @@ mod tests { Fido2Credential { credential_id: "123" .to_string() - .encrypt(ctx, key, ContentFormat::Utf8) + .encrypt(ctx, key) .unwrap(), key_type: "public-key" .to_string() - .encrypt(ctx, key, ContentFormat::Utf8) + .encrypt(ctx, key) .unwrap(), key_algorithm: "ECDSA" .to_string() - .encrypt(ctx, key, ContentFormat::Utf8) + .encrypt(ctx, key) .unwrap(), key_curve: "P-256" .to_string() - .encrypt(ctx, key, ContentFormat::Utf8) + .encrypt(ctx, key) .unwrap(), key_value: "123" .to_string() - .encrypt(ctx, key, ContentFormat::Utf8) + .encrypt(ctx, key) .unwrap(), rp_id: "123" .to_string() - .encrypt(ctx, key, ContentFormat::Utf8) + .encrypt(ctx, key) .unwrap(), user_handle: None, user_name: None, counter: "123" .to_string() - .encrypt(ctx, key, ContentFormat::Utf8) + .encrypt(ctx, key) .unwrap(), rp_name: None, user_display_name: None, discoverable: "true" .to_string() - .encrypt(ctx, key, ContentFormat::Utf8) + .encrypt(ctx, key) .unwrap(), creation_date: "2024-06-07T14:12:36.150Z".parse().unwrap(), } @@ -1354,15 +1354,15 @@ mod tests { let original_subtitle = "SHA256:1JjFjvPRkj1Gbf2qRP1dgHiIzEuNAEvp+92x99jw3K0".to_string(); let fingerprint_encrypted = original_subtitle .to_owned() - .encrypt(&mut ctx, key, ContentFormat::Utf8) + .encrypt(&mut ctx, key) .unwrap(); let private_key_encrypted = "" .to_string() - .encrypt(&mut ctx, key, ContentFormat::Utf8) + .encrypt(&mut ctx, key) .unwrap(); let public_key_encrypted = "" .to_string() - .encrypt(&mut ctx, key, ContentFormat::Utf8) + .encrypt(&mut ctx, key) .unwrap(); let ssh_key_cipher = Cipher { id: Some("090c19ea-a61a-4df6-8963-262b97bc6266".parse().unwrap()), @@ -1373,7 +1373,7 @@ mod tests { key: None, name: "My test ssh key" .to_string() - .encrypt(&mut ctx, key, ContentFormat::Utf8) + .encrypt(&mut ctx, key) .unwrap(), notes: None, login: None, diff --git a/crates/bitwarden-vault/src/cipher/field.rs b/crates/bitwarden-vault/src/cipher/field.rs index 1edf74ee5..d6af8b39a 100644 --- a/crates/bitwarden-vault/src/cipher/field.rs +++ b/crates/bitwarden-vault/src/cipher/field.rs @@ -4,7 +4,7 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, TypedEncryptable }; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; @@ -58,8 +58,8 @@ impl CompositeEncryptable for FieldView { key: SymmetricKeyId, ) -> Result { Ok(Field { - name: self.name.encrypt(ctx, key, ContentFormat::Utf8)?, - value: self.value.encrypt(ctx, key, ContentFormat::Utf8)?, + name: self.name.encrypt(ctx, key)?, + value: self.value.encrypt(ctx, key)?, r#type: self.r#type, linked_id: self.linked_id, }) diff --git a/crates/bitwarden-vault/src/cipher/identity.rs b/crates/bitwarden-vault/src/cipher/identity.rs index 0771b4318..6e4f5e836 100644 --- a/crates/bitwarden-vault/src/cipher/identity.rs +++ b/crates/bitwarden-vault/src/cipher/identity.rs @@ -1,7 +1,7 @@ use bitwarden_api_api::models::CipherIdentityModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, TypedEncryptable }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -66,26 +66,26 @@ impl CompositeEncryptable for IdentityView { key: SymmetricKeyId, ) -> Result { Ok(Identity { - title: self.title.encrypt(ctx, key, ContentFormat::Utf8)?, - first_name: self.first_name.encrypt(ctx, key, ContentFormat::Utf8)?, - middle_name: self.middle_name.encrypt(ctx, key, ContentFormat::Utf8)?, - last_name: self.last_name.encrypt(ctx, key, ContentFormat::Utf8)?, - address1: self.address1.encrypt(ctx, key, ContentFormat::Utf8)?, - address2: self.address2.encrypt(ctx, key, ContentFormat::Utf8)?, - address3: self.address3.encrypt(ctx, key, ContentFormat::Utf8)?, - city: self.city.encrypt(ctx, key, ContentFormat::Utf8)?, - state: self.state.encrypt(ctx, key, ContentFormat::Utf8)?, - postal_code: self.postal_code.encrypt(ctx, key, ContentFormat::Utf8)?, - country: self.country.encrypt(ctx, key, ContentFormat::Utf8)?, - company: self.company.encrypt(ctx, key, ContentFormat::Utf8)?, - email: self.email.encrypt(ctx, key, ContentFormat::Utf8)?, - phone: self.phone.encrypt(ctx, key, ContentFormat::Utf8)?, - ssn: self.ssn.encrypt(ctx, key, ContentFormat::Utf8)?, - username: self.username.encrypt(ctx, key, ContentFormat::Utf8)?, + title: self.title.encrypt(ctx, key)?, + first_name: self.first_name.encrypt(ctx, key)?, + middle_name: self.middle_name.encrypt(ctx, key)?, + last_name: self.last_name.encrypt(ctx, key)?, + address1: self.address1.encrypt(ctx, key)?, + address2: self.address2.encrypt(ctx, key)?, + address3: self.address3.encrypt(ctx, key)?, + city: self.city.encrypt(ctx, key)?, + state: self.state.encrypt(ctx, key)?, + postal_code: self.postal_code.encrypt(ctx, key)?, + country: self.country.encrypt(ctx, key)?, + company: self.company.encrypt(ctx, key)?, + email: self.email.encrypt(ctx, key)?, + phone: self.phone.encrypt(ctx, key)?, + ssn: self.ssn.encrypt(ctx, key)?, + username: self.username.encrypt(ctx, key)?, passport_number: self .passport_number - .encrypt(ctx, key, ContentFormat::Utf8)?, - license_number: self.license_number.encrypt(ctx, key, ContentFormat::Utf8)?, + .encrypt(ctx, key)?, + license_number: self.license_number.encrypt(ctx, key)?, }) } } diff --git a/crates/bitwarden-vault/src/cipher/login.rs b/crates/bitwarden-vault/src/cipher/login.rs index 858a55459..2687071fe 100644 --- a/crates/bitwarden-vault/src/cipher/login.rs +++ b/crates/bitwarden-vault/src/cipher/login.rs @@ -5,7 +5,7 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, TypedEncryptable }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -199,24 +199,24 @@ impl CompositeEncryptable for Fido2Cred key: SymmetricKeyId, ) -> Result { Ok(Fido2Credential { - credential_id: self.credential_id.encrypt(ctx, key, ContentFormat::Utf8)?, - key_type: self.key_type.encrypt(ctx, key, ContentFormat::Utf8)?, - key_algorithm: self.key_algorithm.encrypt(ctx, key, ContentFormat::Utf8)?, - key_curve: self.key_curve.encrypt(ctx, key, ContentFormat::Utf8)?, - key_value: self.key_value.encrypt(ctx, key, ContentFormat::Utf8)?, - rp_id: self.rp_id.encrypt(ctx, key, ContentFormat::Utf8)?, + credential_id: self.credential_id.encrypt(ctx, key)?, + key_type: self.key_type.encrypt(ctx, key)?, + key_algorithm: self.key_algorithm.encrypt(ctx, key)?, + key_curve: self.key_curve.encrypt(ctx, key)?, + key_value: self.key_value.encrypt(ctx, key)?, + rp_id: self.rp_id.encrypt(ctx, key)?, user_handle: self .user_handle .as_ref() - .map(|h| h.encrypt(ctx, key, ContentFormat::Utf8)) + .map(|h| h.encrypt(ctx, key)) .transpose()?, - user_name: self.user_name.encrypt(ctx, key, ContentFormat::Utf8)?, - counter: self.counter.encrypt(ctx, key, ContentFormat::Utf8)?, - rp_name: self.rp_name.encrypt(ctx, key, ContentFormat::Utf8)?, + user_name: self.user_name.encrypt(ctx, key)?, + counter: self.counter.encrypt(ctx, key)?, + rp_name: self.rp_name.encrypt(ctx, key)?, user_display_name: self .user_display_name - .encrypt(ctx, key, ContentFormat::Utf8)?, - discoverable: self.discoverable.encrypt(ctx, key, ContentFormat::Utf8)?, + .encrypt(ctx, key)?, + discoverable: self.discoverable.encrypt(ctx, key)?, creation_date: self.creation_date, }) } @@ -323,9 +323,9 @@ impl CompositeEncryptable for LoginUriView { key: SymmetricKeyId, ) -> Result { Ok(LoginUri { - uri: self.uri.encrypt(ctx, key, ContentFormat::Utf8)?, + uri: self.uri.encrypt(ctx, key)?, r#match: self.r#match, - uri_checksum: self.uri_checksum.encrypt(ctx, key, ContentFormat::Utf8)?, + uri_checksum: self.uri_checksum.encrypt(ctx, key)?, }) } } @@ -337,11 +337,11 @@ impl CompositeEncryptable for LoginView { key: SymmetricKeyId, ) -> Result { Ok(Login { - username: self.username.encrypt(ctx, key, ContentFormat::Utf8)?, - password: self.password.encrypt(ctx, key, ContentFormat::Utf8)?, + username: self.username.encrypt(ctx, key)?, + password: self.password.encrypt(ctx, key)?, password_revision_date: self.password_revision_date, uris: self.uris.encrypt_composite(ctx, key)?, - totp: self.totp.encrypt(ctx, key, ContentFormat::Utf8)?, + totp: self.totp.encrypt(ctx, key)?, autofill_on_page_load: self.autofill_on_page_load, fido2_credentials: self.fido2_credentials.clone(), }) @@ -407,28 +407,28 @@ impl CompositeEncryptable for Fido2Cred key: SymmetricKeyId, ) -> Result { Ok(Fido2Credential { - credential_id: self.credential_id.encrypt(ctx, key, ContentFormat::Utf8)?, - key_type: self.key_type.encrypt(ctx, key, ContentFormat::Utf8)?, - key_algorithm: self.key_algorithm.encrypt(ctx, key, ContentFormat::Utf8)?, - key_curve: self.key_curve.encrypt(ctx, key, ContentFormat::Utf8)?, + credential_id: self.credential_id.encrypt(ctx, key)?, + key_type: self.key_type.encrypt(ctx, key)?, + key_algorithm: self.key_algorithm.encrypt(ctx, key)?, + key_curve: self.key_curve.encrypt(ctx, key)?, key_value: self.key_value.clone(), - rp_id: self.rp_id.encrypt(ctx, key, ContentFormat::Utf8)?, + rp_id: self.rp_id.encrypt(ctx, key)?, user_handle: self .user_handle .as_ref() - .map(|h| h.encrypt(ctx, key, ContentFormat::Utf8)) + .map(|h| h.encrypt(ctx, key)) .transpose()?, user_name: self .user_name .as_ref() - .map(|n| n.encrypt(ctx, key, ContentFormat::Utf8)) + .map(|n| n.encrypt(ctx, key)) .transpose()?, - counter: self.counter.encrypt(ctx, key, ContentFormat::Utf8)?, - rp_name: self.rp_name.encrypt(ctx, key, ContentFormat::Utf8)?, + counter: self.counter.encrypt(ctx, key)?, + rp_name: self.rp_name.encrypt(ctx, key)?, user_display_name: self .user_display_name - .encrypt(ctx, key, ContentFormat::Utf8)?, - discoverable: self.discoverable.encrypt(ctx, key, ContentFormat::Utf8)?, + .encrypt(ctx, key)?, + discoverable: self.discoverable.encrypt(ctx, key)?, creation_date: self.creation_date, }) } diff --git a/crates/bitwarden-vault/src/cipher/ssh_key.rs b/crates/bitwarden-vault/src/cipher/ssh_key.rs index 5ece59020..3735dfd05 100644 --- a/crates/bitwarden-vault/src/cipher/ssh_key.rs +++ b/crates/bitwarden-vault/src/cipher/ssh_key.rs @@ -1,6 +1,6 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, TypedEncryptable }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -39,9 +39,9 @@ impl CompositeEncryptable for SshKeyView { key: SymmetricKeyId, ) -> Result { Ok(SshKey { - private_key: self.private_key.encrypt(ctx, key, ContentFormat::Utf8)?, - public_key: self.public_key.encrypt(ctx, key, ContentFormat::Utf8)?, - fingerprint: self.fingerprint.encrypt(ctx, key, ContentFormat::Utf8)?, + private_key: self.private_key.encrypt(ctx, key)?, + public_key: self.public_key.encrypt(ctx, key)?, + fingerprint: self.fingerprint.encrypt(ctx, key)?, }) } } diff --git a/crates/bitwarden-vault/src/folder.rs b/crates/bitwarden-vault/src/folder.rs index 5fe2029a5..2a5c9bfdb 100644 --- a/crates/bitwarden-vault/src/folder.rs +++ b/crates/bitwarden-vault/src/folder.rs @@ -4,7 +4,7 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, TypedEncryptable }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -53,7 +53,7 @@ impl CompositeEncryptable for FolderView { ) -> Result { Ok(Folder { id: self.id, - name: self.name.encrypt(ctx, key, ContentFormat::Utf8)?, + name: self.name.encrypt(ctx, key)?, revision_date: self.revision_date, }) } diff --git a/crates/bitwarden-vault/src/password_history.rs b/crates/bitwarden-vault/src/password_history.rs index a3653342e..df216816d 100644 --- a/crates/bitwarden-vault/src/password_history.rs +++ b/crates/bitwarden-vault/src/password_history.rs @@ -1,7 +1,7 @@ use bitwarden_api_api::models::CipherPasswordHistoryModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, TypedEncryptable }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -46,7 +46,7 @@ impl CompositeEncryptable for PasswordH key: SymmetricKeyId, ) -> Result { Ok(PasswordHistory { - password: self.password.encrypt(ctx, key, ContentFormat::Utf8)?, + password: self.password.encrypt(ctx, key)?, last_used_date: self.last_used_date, }) } From f8cb8041fe8117e5a983cc9fe427f9b7e60e2fbd Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 22 May 2025 10:41:47 +0200 Subject: [PATCH 013/124] Apply cargo fmt --- .../bitwarden-sm/src/secrets/create.rs | 13 +-- .../bitwarden-sm/src/secrets/update.rs | 13 +-- crates/bitwarden-core/src/mobile/crypto.rs | 3 +- crates/bitwarden-crypto/src/cose.rs | 10 +- .../bitwarden-crypto/src/keys/device_key.rs | 3 +- crates/bitwarden-crypto/src/lib.rs | 4 +- crates/bitwarden-crypto/src/store/context.rs | 8 +- crates/bitwarden-crypto/src/store/mod.rs | 10 +- .../src/traits/encryptable.rs | 44 +++------ crates/bitwarden-exporters/src/export.rs | 5 +- crates/bitwarden-send/src/send.rs | 3 +- .../bitwarden-vault/src/cipher/attachment.rs | 7 +- crates/bitwarden-vault/src/cipher/card.rs | 7 +- crates/bitwarden-vault/src/cipher/cipher.rs | 98 +++++-------------- crates/bitwarden-vault/src/cipher/field.rs | 3 +- crates/bitwarden-vault/src/cipher/identity.rs | 7 +- crates/bitwarden-vault/src/cipher/login.rs | 11 +-- crates/bitwarden-vault/src/cipher/ssh_key.rs | 3 +- crates/bitwarden-vault/src/folder.rs | 3 +- .../bitwarden-vault/src/password_history.rs | 3 +- 20 files changed, 94 insertions(+), 164 deletions(-) diff --git a/bitwarden_license/bitwarden-sm/src/secrets/create.rs b/bitwarden_license/bitwarden-sm/src/secrets/create.rs index 30f278b12..1f3e117b2 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/create.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/create.rs @@ -40,17 +40,8 @@ pub(crate) async fn create_secret( let secret = { let mut ctx = key_store.context(); Some(SecretCreateRequestModel { - key: input - .key - .clone() - .trim() - .encrypt(&mut ctx, key)? - .to_string(), - value: input - .value - .clone() - .encrypt(&mut ctx, key)? - .to_string(), + key: input.key.clone().trim().encrypt(&mut ctx, key)?.to_string(), + value: input.value.clone().encrypt(&mut ctx, key)?.to_string(), note: input .note .clone() diff --git a/bitwarden_license/bitwarden-sm/src/secrets/update.rs b/bitwarden_license/bitwarden-sm/src/secrets/update.rs index 8920b4cdd..af751d383 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/update.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/update.rs @@ -39,17 +39,8 @@ pub(crate) async fn update_secret( let secret = { let mut ctx = key_store.context(); Some(SecretUpdateRequestModel { - key: input - .key - .clone() - .trim() - .encrypt(&mut ctx, key)? - .to_string(), - value: input - .value - .clone() - .encrypt(&mut ctx, key)? - .to_string(), + key: input.key.clone().trim().encrypt(&mut ctx, key)?.to_string(), + value: input.value.clone().encrypt(&mut ctx, key)?.to_string(), note: input .note .clone() diff --git a/crates/bitwarden-core/src/mobile/crypto.rs b/crates/bitwarden-core/src/mobile/crypto.rs index f5db8239a..bf0030362 100644 --- a/crates/bitwarden-core/src/mobile/crypto.rs +++ b/crates/bitwarden-core/src/mobile/crypto.rs @@ -8,7 +8,8 @@ use std::collections::HashMap; use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ - AsymmetricCryptoKey, ContentFormat, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey + AsymmetricCryptoKey, ContentFormat, CryptoError, EncString, Kdf, KeyDecryptable, + KeyEncryptable, MasterKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 4e6f5afe4..bf76df39d 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -24,10 +24,12 @@ pub(crate) const XCHACHA20_POLY1305: i64 = -70000; const XCHACHA20_TEXT_PAD_BLOCK_SIZE: usize = 32; const CONTENT_TYPE_PADDED_UTF8: &str = "application/utf8-padded"; -/// The content format describes the format of the contained bytes. Message encryption always happens on the byte level, and this -/// allows determining what format the contained data has. For instance, an `EncString` in most cases contains UTF-8 encoded -/// text. In some cases it may contain a Pkcs8 private key, or a COSE key. Specifically, for COSE keys, this allows distinguishing between -/// the old symmetric key format, represented as `ContentFormat::OctetStream`, and the new COSE key format, represented as `ContentFormat::CoseKey`. +/// The content format describes the format of the contained bytes. Message encryption always +/// happens on the byte level, and this allows determining what format the contained data has. For +/// instance, an `EncString` in most cases contains UTF-8 encoded text. In some cases it may contain +/// a Pkcs8 private key, or a COSE key. Specifically, for COSE keys, this allows distinguishing +/// between the old symmetric key format, represented as `ContentFormat::OctetStream`, and the new +/// COSE key format, represented as `ContentFormat::CoseKey`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] pub enum ContentFormat { diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index a67dbd98f..fd5ee2ba9 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -1,5 +1,6 @@ use crate::{ - error::Result, AsymmetricCryptoKey, ContentFormat, CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, UnsignedSharedKey + error::Result, AsymmetricCryptoKey, ContentFormat, CryptoError, EncString, KeyDecryptable, + KeyEncryptable, SymmetricCryptoKey, UnsignedSharedKey, }; /// Device Key diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index 05de266de..367ff86e5 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -34,7 +34,9 @@ mod cose; mod traits; pub use cose::ContentFormat; mod xchacha20; -pub use traits::{Decryptable, Encryptable, CompositeEncryptable, TypedEncryptable, IdentifyKey, KeyId, KeyIds}; +pub use traits::{ + CompositeEncryptable, Decryptable, Encryptable, IdentifyKey, KeyId, KeyIds, TypedEncryptable, +}; pub use zeroizing_alloc::ZeroAlloc as ZeroizingAllocator; #[cfg(feature = "uniffi")] diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index df12cded1..2e0c404a3 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -385,7 +385,9 @@ mod tests { store::{ tests::{Data, DataView}, KeyStore, - }, traits::tests::{TestIds, TestSymmKey}, CompositeEncryptable, Decryptable, SymmetricCryptoKey + }, + traits::tests::{TestIds, TestSymmKey}, + CompositeEncryptable, Decryptable, SymmetricCryptoKey, }; #[test] @@ -445,9 +447,7 @@ mod tests { // with one and decrypt with the other let data = DataView("Hello, World!".to_string(), key_2_id); - let encrypted = data - .encrypt_composite(&mut ctx, key_2_id) - .unwrap(); + let encrypted = data.encrypt_composite(&mut ctx, key_2_id).unwrap(); let decrypted1 = encrypted.decrypt(&mut ctx, key_2_id).unwrap(); let decrypted2 = encrypted.decrypt(&mut ctx, new_key_id).unwrap(); diff --git a/crates/bitwarden-crypto/src/store/mod.rs b/crates/bitwarden-crypto/src/store/mod.rs index 44345fb30..fec65ef45 100644 --- a/crates/bitwarden-crypto/src/store/mod.rs +++ b/crates/bitwarden-crypto/src/store/mod.rs @@ -209,7 +209,11 @@ impl KeyStore { /// already be present in the store, otherwise this will return an error. /// This method is not parallelized, and is meant for single item encryption. /// If you need to encrypt multiple items, use `encrypt_list` instead. - pub fn encrypt + IdentifyKey, Output>( + pub fn encrypt< + Key: KeyId, + Data: CompositeEncryptable + IdentifyKey, + Output, + >( &self, data: Data, ) -> Result { @@ -304,7 +308,9 @@ fn batch_chunk_size(len: usize) -> usize { #[cfg(test)] pub(crate) mod tests { use crate::{ - store::{KeyStore, KeyStoreContext}, traits::tests::{TestIds, TestSymmKey}, EncString, Encryptable, SymmetricCryptoKey, TypedEncryptable + store::{KeyStore, KeyStoreContext}, + traits::tests::{TestIds, TestSymmKey}, + EncString, Encryptable, SymmetricCryptoKey, TypedEncryptable, }; pub struct DataView(pub String, pub TestSymmKey); diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index f189afc1c..7ca18ec93 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -27,7 +27,7 @@ impl, Output> } } -impl , Output> +impl, Output> CompositeEncryptable> for Vec { fn encrypt_composite( @@ -41,14 +41,10 @@ impl , Output } } -/// An encryption operation that takes the input value - a primitive such as `String` and encrypts it into the output value. -/// The implementation decides the content format. +/// An encryption operation that takes the input value - a primitive such as `String` and encrypts +/// it into the output value. The implementation decides the content format. pub trait TypedEncryptable { - fn encrypt( - &self, - ctx: &mut KeyStoreContext, - key: Key, - ) -> Result; + fn encrypt(&self, ctx: &mut KeyStoreContext, key: Key) -> Result; } impl, Output> @@ -65,8 +61,7 @@ impl, Output> } } -impl TypedEncryptable for &str -{ +impl TypedEncryptable for &str { fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -76,8 +71,7 @@ impl TypedEncryptable for &str } } -impl TypedEncryptable for String -{ +impl TypedEncryptable for String { fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -87,7 +81,8 @@ impl TypedEncryptable for String } } -/// An encryption operation that takes the input value - a primitive such as `Vec` - and encrypts it into the output value. +/// An encryption operation that takes the input value - a primitive such as `Vec` - and +/// encrypts it into the output value. pub trait Encryptable { fn encrypt( &self, @@ -152,7 +147,8 @@ impl, Output> #[cfg(test)] mod tests { use crate::{ - cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, KeyStore, SymmetricCryptoKey, TypedEncryptable + cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, + KeyStore, SymmetricCryptoKey, TypedEncryptable, }; fn test_store() -> KeyStore { @@ -207,12 +203,8 @@ mod tests { let string_data = "Hello, World!".to_string(); let str_data: &str = string_data.as_str(); - let string_encrypted = string_data - .encrypt(&mut ctx, key) - .unwrap(); - let str_encrypted = str_data - .encrypt(&mut ctx, key) - .unwrap(); + let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap(); + let str_encrypted = str_data.encrypt(&mut ctx, key).unwrap(); let string_decrypted: String = string_encrypted.decrypt(&mut ctx, key).unwrap(); let str_decrypted: String = str_encrypted.decrypt(&mut ctx, key).unwrap(); @@ -229,9 +221,7 @@ mod tests { let string_data = Some("Hello, World!".to_string()); - let string_encrypted = string_data - .encrypt(&mut ctx, key) - .unwrap(); + let string_encrypted = string_data.encrypt(&mut ctx, key).unwrap(); let string_decrypted: Option = string_encrypted.decrypt(&mut ctx, key).unwrap(); @@ -245,17 +235,13 @@ mod tests { let key = TestSymmKey::A(0); let none_data: Option = None; - let string_encrypted = none_data - .encrypt(&mut ctx, key) - .unwrap(); + let string_encrypted = none_data.encrypt(&mut ctx, key).unwrap(); assert_eq!(string_encrypted, None); // The None implementation will not do any decrypt operations, so it won't fail even if the // key doesn't exist let bad_key = TestSymmKey::B((0, 1)); - let string_encrypted_bad = none_data - .encrypt(&mut ctx, bad_key) - .unwrap(); + let string_encrypted_bad = none_data.encrypt(&mut ctx, bad_key).unwrap(); assert_eq!(string_encrypted_bad, None); } } diff --git a/crates/bitwarden-exporters/src/export.rs b/crates/bitwarden-exporters/src/export.rs index a6d7da1f2..475916b79 100644 --- a/crates/bitwarden-exporters/src/export.rs +++ b/crates/bitwarden-exporters/src/export.rs @@ -80,10 +80,7 @@ fn encrypt_import( view.set_new_fido2_credentials(ctx, passkeys)?; } - let new_cipher = view.encrypt_composite( - ctx, - view.key_identifier(), - )?; + let new_cipher = view.encrypt_composite(ctx, view.key_identifier())?; Ok(new_cipher) } diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index 54019767b..952b87dad 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -8,7 +8,8 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - generate_random_bytes, CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, TypedEncryptable + generate_random_bytes, CompositeEncryptable, ContentFormat, CryptoError, Decryptable, + EncString, Encryptable, IdentifyKey, KeyStoreContext, TypedEncryptable, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index 8c5109715..159da97e3 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,6 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, TypedEncryptable + CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, + IdentifyKey, KeyStoreContext, TypedEncryptable, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -73,7 +74,9 @@ impl IdentifyKey for AttachmentFile { } } -impl CompositeEncryptable for AttachmentFileView<'_> { +impl CompositeEncryptable + for AttachmentFileView<'_> +{ fn encrypt_composite( &self, ctx: &mut KeyStoreContext, diff --git a/crates/bitwarden-vault/src/cipher/card.rs b/crates/bitwarden-vault/src/cipher/card.rs index 335889a07..323de3a61 100644 --- a/crates/bitwarden-vault/src/cipher/card.rs +++ b/crates/bitwarden-vault/src/cipher/card.rs @@ -1,7 +1,8 @@ use bitwarden_api_api::models::CipherCardModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, TypedEncryptable + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + TypedEncryptable, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -59,9 +60,7 @@ impl CompositeEncryptable for CardView { key: SymmetricKeyId, ) -> Result { Ok(Card { - cardholder_name: self - .cardholder_name - .encrypt(ctx, key)?, + cardholder_name: self.cardholder_name.encrypt(ctx, key)?, exp_month: self.exp_month.encrypt(ctx, key)?, exp_year: self.exp_year.encrypt(ctx, key)?, code: self.code.encrypt(ctx, key)?, diff --git a/crates/bitwarden-vault/src/cipher/cipher.rs b/crates/bitwarden-vault/src/cipher/cipher.rs index f3d0bef5b..de34b6efc 100644 --- a/crates/bitwarden-vault/src/cipher/cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher.rs @@ -4,7 +4,8 @@ use bitwarden_core::{ require, MissingFieldError, VaultLockedError, }; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, TypedEncryptable + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, + KeyStoreContext, TypedEncryptable, }; use bitwarden_error::bitwarden_error; use chrono::{DateTime, Utc}; @@ -231,46 +232,29 @@ impl CompositeEncryptable for CipherView { folder_id: cipher_view.folder_id, collection_ids: cipher_view.collection_ids, key: cipher_view.key, - name: cipher_view - .name - .encrypt(ctx, ciphers_key)?, - notes: cipher_view - .notes - .encrypt(ctx, ciphers_key)?, + name: cipher_view.name.encrypt(ctx, ciphers_key)?, + notes: cipher_view.notes.encrypt(ctx, ciphers_key)?, r#type: cipher_view.r#type, - login: cipher_view - .login - .encrypt_composite(ctx, ciphers_key)?, - identity: cipher_view - .identity - .encrypt_composite(ctx, ciphers_key)?, - card: cipher_view - .card - .encrypt_composite(ctx, ciphers_key)?, + login: cipher_view.login.encrypt_composite(ctx, ciphers_key)?, + identity: cipher_view.identity.encrypt_composite(ctx, ciphers_key)?, + card: cipher_view.card.encrypt_composite(ctx, ciphers_key)?, secure_note: cipher_view .secure_note .encrypt_composite(ctx, ciphers_key)?, - ssh_key: cipher_view - .ssh_key - .encrypt_composite(ctx, ciphers_key)?, + ssh_key: cipher_view.ssh_key.encrypt_composite(ctx, ciphers_key)?, favorite: cipher_view.favorite, reprompt: cipher_view.reprompt, organization_use_totp: cipher_view.organization_use_totp, edit: cipher_view.edit, view_password: cipher_view.view_password, - local_data: cipher_view - .local_data - .encrypt_composite(ctx, ciphers_key)?, + local_data: cipher_view.local_data.encrypt_composite(ctx, ciphers_key)?, attachments: cipher_view .attachments .encrypt_composite(ctx, ciphers_key)?, - fields: cipher_view - .fields + fields: cipher_view.fields.encrypt_composite(ctx, ciphers_key)?, + password_history: cipher_view + .password_history .encrypt_composite(ctx, ciphers_key)?, - password_history: cipher_view.password_history.encrypt_composite( - ctx, - ciphers_key, - )?, creation_date: cipher_view.creation_date, deleted_date: cipher_view.deleted_date, revision_date: cipher_view.revision_date, @@ -547,8 +531,7 @@ impl CipherView { if let Some(fido2_credentials) = &mut login.fido2_credentials { let dec_fido2_credentials: Vec = fido2_credentials.decrypt(ctx, old_key)?; - *fido2_credentials = - dec_fido2_credentials.encrypt_composite(ctx, new_key)?; + *fido2_credentials = dec_fido2_credentials.encrypt_composite(ctx, new_key)?; } } Ok(()) @@ -823,42 +806,18 @@ mod tests { fn generate_fido2(ctx: &mut KeyStoreContext, key: SymmetricKeyId) -> Fido2Credential { Fido2Credential { - credential_id: "123" - .to_string() - .encrypt(ctx, key) - .unwrap(), - key_type: "public-key" - .to_string() - .encrypt(ctx, key) - .unwrap(), - key_algorithm: "ECDSA" - .to_string() - .encrypt(ctx, key) - .unwrap(), - key_curve: "P-256" - .to_string() - .encrypt(ctx, key) - .unwrap(), - key_value: "123" - .to_string() - .encrypt(ctx, key) - .unwrap(), - rp_id: "123" - .to_string() - .encrypt(ctx, key) - .unwrap(), + credential_id: "123".to_string().encrypt(ctx, key).unwrap(), + key_type: "public-key".to_string().encrypt(ctx, key).unwrap(), + key_algorithm: "ECDSA".to_string().encrypt(ctx, key).unwrap(), + key_curve: "P-256".to_string().encrypt(ctx, key).unwrap(), + key_value: "123".to_string().encrypt(ctx, key).unwrap(), + rp_id: "123".to_string().encrypt(ctx, key).unwrap(), user_handle: None, user_name: None, - counter: "123" - .to_string() - .encrypt(ctx, key) - .unwrap(), + counter: "123".to_string().encrypt(ctx, key).unwrap(), rp_name: None, user_display_name: None, - discoverable: "true" - .to_string() - .encrypt(ctx, key) - .unwrap(), + discoverable: "true".to_string().encrypt(ctx, key).unwrap(), creation_date: "2024-06-07T14:12:36.150Z".parse().unwrap(), } } @@ -1352,18 +1311,9 @@ mod tests { let mut ctx = key_store.context(); let original_subtitle = "SHA256:1JjFjvPRkj1Gbf2qRP1dgHiIzEuNAEvp+92x99jw3K0".to_string(); - let fingerprint_encrypted = original_subtitle - .to_owned() - .encrypt(&mut ctx, key) - .unwrap(); - let private_key_encrypted = "" - .to_string() - .encrypt(&mut ctx, key) - .unwrap(); - let public_key_encrypted = "" - .to_string() - .encrypt(&mut ctx, key) - .unwrap(); + let fingerprint_encrypted = original_subtitle.to_owned().encrypt(&mut ctx, key).unwrap(); + let private_key_encrypted = "".to_string().encrypt(&mut ctx, key).unwrap(); + let public_key_encrypted = "".to_string().encrypt(&mut ctx, key).unwrap(); let ssh_key_cipher = Cipher { id: Some("090c19ea-a61a-4df6-8963-262b97bc6266".parse().unwrap()), organization_id: None, diff --git a/crates/bitwarden-vault/src/cipher/field.rs b/crates/bitwarden-vault/src/cipher/field.rs index d6af8b39a..1c7f4c8d6 100644 --- a/crates/bitwarden-vault/src/cipher/field.rs +++ b/crates/bitwarden-vault/src/cipher/field.rs @@ -4,7 +4,8 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, TypedEncryptable + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + TypedEncryptable, }; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; diff --git a/crates/bitwarden-vault/src/cipher/identity.rs b/crates/bitwarden-vault/src/cipher/identity.rs index 6e4f5e836..2a10be5ff 100644 --- a/crates/bitwarden-vault/src/cipher/identity.rs +++ b/crates/bitwarden-vault/src/cipher/identity.rs @@ -1,7 +1,8 @@ use bitwarden_api_api::models::CipherIdentityModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, TypedEncryptable + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + TypedEncryptable, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -82,9 +83,7 @@ impl CompositeEncryptable for IdentityView { phone: self.phone.encrypt(ctx, key)?, ssn: self.ssn.encrypt(ctx, key)?, username: self.username.encrypt(ctx, key)?, - passport_number: self - .passport_number - .encrypt(ctx, key)?, + passport_number: self.passport_number.encrypt(ctx, key)?, license_number: self.license_number.encrypt(ctx, key)?, }) } diff --git a/crates/bitwarden-vault/src/cipher/login.rs b/crates/bitwarden-vault/src/cipher/login.rs index 2687071fe..8a1d551d6 100644 --- a/crates/bitwarden-vault/src/cipher/login.rs +++ b/crates/bitwarden-vault/src/cipher/login.rs @@ -5,7 +5,8 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, TypedEncryptable + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + TypedEncryptable, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -213,9 +214,7 @@ impl CompositeEncryptable for Fido2Cred user_name: self.user_name.encrypt(ctx, key)?, counter: self.counter.encrypt(ctx, key)?, rp_name: self.rp_name.encrypt(ctx, key)?, - user_display_name: self - .user_display_name - .encrypt(ctx, key)?, + user_display_name: self.user_display_name.encrypt(ctx, key)?, discoverable: self.discoverable.encrypt(ctx, key)?, creation_date: self.creation_date, }) @@ -425,9 +424,7 @@ impl CompositeEncryptable for Fido2Cred .transpose()?, counter: self.counter.encrypt(ctx, key)?, rp_name: self.rp_name.encrypt(ctx, key)?, - user_display_name: self - .user_display_name - .encrypt(ctx, key)?, + user_display_name: self.user_display_name.encrypt(ctx, key)?, discoverable: self.discoverable.encrypt(ctx, key)?, creation_date: self.creation_date, }) diff --git a/crates/bitwarden-vault/src/cipher/ssh_key.rs b/crates/bitwarden-vault/src/cipher/ssh_key.rs index 3735dfd05..edb4506b0 100644 --- a/crates/bitwarden-vault/src/cipher/ssh_key.rs +++ b/crates/bitwarden-vault/src/cipher/ssh_key.rs @@ -1,6 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, TypedEncryptable + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + TypedEncryptable, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] diff --git a/crates/bitwarden-vault/src/folder.rs b/crates/bitwarden-vault/src/folder.rs index 2a5c9bfdb..17bf7f8dc 100644 --- a/crates/bitwarden-vault/src/folder.rs +++ b/crates/bitwarden-vault/src/folder.rs @@ -4,7 +4,8 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, TypedEncryptable + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, + KeyStoreContext, TypedEncryptable, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-vault/src/password_history.rs b/crates/bitwarden-vault/src/password_history.rs index df216816d..174825a04 100644 --- a/crates/bitwarden-vault/src/password_history.rs +++ b/crates/bitwarden-vault/src/password_history.rs @@ -1,7 +1,8 @@ use bitwarden_api_api::models::CipherPasswordHistoryModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, TypedEncryptable + CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, + KeyStoreContext, TypedEncryptable, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; From e6939e6b534c326934dacb15a9b66e58c7888b4c Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 22 May 2025 10:46:13 +0200 Subject: [PATCH 014/124] Fix build --- crates/bitwarden-crypto/src/store/context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 2e0c404a3..ad2197fae 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -15,7 +15,7 @@ use crate::{ /// The context of a crypto operation using [super::KeyStore] /// /// This will usually be accessed from an implementation of [crate::Decryptable] or -/// [crate::Encryptable], but can also be obtained through [super::KeyStore::context] +/// [crate::Encryptable], [crate::TypedEncryptable], but can also be obtained through [super::KeyStore::context] /// /// This context contains access to the user keys stored in the [super::KeyStore] (sometimes /// referred to as `global keys`) and it also contains it's own individual secure backend for key @@ -56,7 +56,7 @@ use crate::{ /// impl CompositeEncryptable for Data { /// fn encrypt_composite(&self, ctx: &mut KeyStoreContext, key: SymmKeyId) -> Result { /// let local_key_id = ctx.unwrap_symmetric_key(key, LOCAL_KEY, &self.key)?; -/// self.name.encrypt(ctx, local_key_id, content_format) +/// self.name.encrypt(ctx, local_key_id) /// } /// } /// ``` From 2a202c1a358504bb4c9764fdb6c8de6ffdd55924 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 22 May 2025 10:47:58 +0200 Subject: [PATCH 015/124] Remove unused imports --- bitwarden_license/bitwarden-sm/src/projects/update.rs | 2 +- bitwarden_license/bitwarden-sm/src/secrets/create.rs | 2 +- bitwarden_license/bitwarden-sm/src/secrets/update.rs | 2 +- crates/bitwarden-crypto/src/store/mod.rs | 2 +- crates/bitwarden-vault/src/cipher/card.rs | 2 +- crates/bitwarden-vault/src/cipher/cipher.rs | 2 +- crates/bitwarden-vault/src/cipher/field.rs | 2 +- crates/bitwarden-vault/src/cipher/identity.rs | 2 +- crates/bitwarden-vault/src/cipher/login.rs | 2 +- crates/bitwarden-vault/src/cipher/ssh_key.rs | 2 +- crates/bitwarden-vault/src/folder.rs | 2 +- crates/bitwarden-vault/src/password_history.rs | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bitwarden_license/bitwarden-sm/src/projects/update.rs b/bitwarden_license/bitwarden-sm/src/projects/update.rs index 43730b4ef..defe95663 100644 --- a/bitwarden_license/bitwarden-sm/src/projects/update.rs +++ b/bitwarden_license/bitwarden-sm/src/projects/update.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::ProjectUpdateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::{Encryptable, TypedEncryptable}; +use bitwarden_crypto::TypedEncryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/bitwarden_license/bitwarden-sm/src/secrets/create.rs b/bitwarden_license/bitwarden-sm/src/secrets/create.rs index 1f3e117b2..0488ee15b 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/create.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/create.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::SecretCreateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::{Encryptable, TypedEncryptable}; +use bitwarden_crypto::TypedEncryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/bitwarden_license/bitwarden-sm/src/secrets/update.rs b/bitwarden_license/bitwarden-sm/src/secrets/update.rs index af751d383..74b2b8fc8 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/update.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/update.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::SecretUpdateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::{Encryptable, TypedEncryptable}; +use bitwarden_crypto::TypedEncryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/crates/bitwarden-crypto/src/store/mod.rs b/crates/bitwarden-crypto/src/store/mod.rs index fec65ef45..002aea942 100644 --- a/crates/bitwarden-crypto/src/store/mod.rs +++ b/crates/bitwarden-crypto/src/store/mod.rs @@ -310,7 +310,7 @@ pub(crate) mod tests { use crate::{ store::{KeyStore, KeyStoreContext}, traits::tests::{TestIds, TestSymmKey}, - EncString, Encryptable, SymmetricCryptoKey, TypedEncryptable, + EncString, SymmetricCryptoKey, TypedEncryptable, }; pub struct DataView(pub String, pub TestSymmKey); diff --git a/crates/bitwarden-vault/src/cipher/card.rs b/crates/bitwarden-vault/src/cipher/card.rs index 323de3a61..241c8d8b3 100644 --- a/crates/bitwarden-vault/src/cipher/card.rs +++ b/crates/bitwarden-vault/src/cipher/card.rs @@ -1,7 +1,7 @@ use bitwarden_api_api::models::CipherCardModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, TypedEncryptable, }; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-vault/src/cipher/cipher.rs b/crates/bitwarden-vault/src/cipher/cipher.rs index de34b6efc..643dd295b 100644 --- a/crates/bitwarden-vault/src/cipher/cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher.rs @@ -4,7 +4,7 @@ use bitwarden_core::{ require, MissingFieldError, VaultLockedError, }; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, + CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, TypedEncryptable, }; use bitwarden_error::bitwarden_error; diff --git a/crates/bitwarden-vault/src/cipher/field.rs b/crates/bitwarden-vault/src/cipher/field.rs index 1c7f4c8d6..d4177df09 100644 --- a/crates/bitwarden-vault/src/cipher/field.rs +++ b/crates/bitwarden-vault/src/cipher/field.rs @@ -4,7 +4,7 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, TypedEncryptable, }; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-vault/src/cipher/identity.rs b/crates/bitwarden-vault/src/cipher/identity.rs index 2a10be5ff..cd8e2dffc 100644 --- a/crates/bitwarden-vault/src/cipher/identity.rs +++ b/crates/bitwarden-vault/src/cipher/identity.rs @@ -1,7 +1,7 @@ use bitwarden_api_api::models::CipherIdentityModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, TypedEncryptable, }; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-vault/src/cipher/login.rs b/crates/bitwarden-vault/src/cipher/login.rs index 8a1d551d6..ca65d6c2b 100644 --- a/crates/bitwarden-vault/src/cipher/login.rs +++ b/crates/bitwarden-vault/src/cipher/login.rs @@ -5,7 +5,7 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, TypedEncryptable, }; use chrono::{DateTime, Utc}; diff --git a/crates/bitwarden-vault/src/cipher/ssh_key.rs b/crates/bitwarden-vault/src/cipher/ssh_key.rs index edb4506b0..c499c632e 100644 --- a/crates/bitwarden-vault/src/cipher/ssh_key.rs +++ b/crates/bitwarden-vault/src/cipher/ssh_key.rs @@ -1,6 +1,6 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext, + CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, TypedEncryptable, }; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-vault/src/folder.rs b/crates/bitwarden-vault/src/folder.rs index 17bf7f8dc..e72a07714 100644 --- a/crates/bitwarden-vault/src/folder.rs +++ b/crates/bitwarden-vault/src/folder.rs @@ -4,7 +4,7 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, + CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, TypedEncryptable, }; use chrono::{DateTime, Utc}; diff --git a/crates/bitwarden-vault/src/password_history.rs b/crates/bitwarden-vault/src/password_history.rs index 174825a04..5da4269f4 100644 --- a/crates/bitwarden-vault/src/password_history.rs +++ b/crates/bitwarden-vault/src/password_history.rs @@ -1,7 +1,7 @@ use bitwarden_api_api::models::CipherPasswordHistoryModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, + CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, TypedEncryptable, }; use chrono::{DateTime, Utc}; From ba9e2c385f2118992d295b4ed6f831aa5203892a Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 22 May 2025 10:57:07 +0200 Subject: [PATCH 016/124] Rename to primitiveencryptablewithcontenttype --- .../bitwarden-sm/src/projects/create.rs | 2 +- .../bitwarden-sm/src/projects/update.rs | 2 +- bitwarden_license/bitwarden-sm/src/secrets/create.rs | 2 +- bitwarden_license/bitwarden-sm/src/secrets/update.rs | 2 +- crates/bitwarden-crypto/src/lib.rs | 3 ++- crates/bitwarden-crypto/src/store/context.rs | 3 ++- crates/bitwarden-crypto/src/store/mod.rs | 2 +- crates/bitwarden-crypto/src/traits/encryptable.rs | 12 ++++++------ crates/bitwarden-crypto/src/traits/mod.rs | 2 +- crates/bitwarden-send/src/send.rs | 2 +- crates/bitwarden-vault/src/cipher/attachment.rs | 2 +- crates/bitwarden-vault/src/cipher/card.rs | 2 +- crates/bitwarden-vault/src/cipher/cipher.rs | 4 ++-- crates/bitwarden-vault/src/cipher/field.rs | 2 +- crates/bitwarden-vault/src/cipher/identity.rs | 2 +- crates/bitwarden-vault/src/cipher/login.rs | 2 +- crates/bitwarden-vault/src/cipher/ssh_key.rs | 2 +- crates/bitwarden-vault/src/folder.rs | 4 ++-- crates/bitwarden-vault/src/password_history.rs | 4 ++-- 19 files changed, 29 insertions(+), 27 deletions(-) diff --git a/bitwarden_license/bitwarden-sm/src/projects/create.rs b/bitwarden_license/bitwarden-sm/src/projects/create.rs index 0c876e5b8..7a3b02467 100644 --- a/bitwarden_license/bitwarden-sm/src/projects/create.rs +++ b/bitwarden_license/bitwarden-sm/src/projects/create.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::ProjectCreateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::TypedEncryptable; +use bitwarden_crypto::PrimitiveEncryptableWithContentType; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/bitwarden_license/bitwarden-sm/src/projects/update.rs b/bitwarden_license/bitwarden-sm/src/projects/update.rs index defe95663..5d603aa6a 100644 --- a/bitwarden_license/bitwarden-sm/src/projects/update.rs +++ b/bitwarden_license/bitwarden-sm/src/projects/update.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::ProjectUpdateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::TypedEncryptable; +use bitwarden_crypto::PrimitiveEncryptableWithContentType; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/bitwarden_license/bitwarden-sm/src/secrets/create.rs b/bitwarden_license/bitwarden-sm/src/secrets/create.rs index 0488ee15b..8f2b981c9 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/create.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/create.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::SecretCreateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::TypedEncryptable; +use bitwarden_crypto::PrimitiveEncryptableWithContentType; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/bitwarden_license/bitwarden-sm/src/secrets/update.rs b/bitwarden_license/bitwarden-sm/src/secrets/update.rs index 74b2b8fc8..a34a84b00 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/update.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/update.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::SecretUpdateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::TypedEncryptable; +use bitwarden_crypto::PrimitiveEncryptableWithContentType; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index 367ff86e5..2727716d4 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -35,7 +35,8 @@ mod traits; pub use cose::ContentFormat; mod xchacha20; pub use traits::{ - CompositeEncryptable, Decryptable, Encryptable, IdentifyKey, KeyId, KeyIds, TypedEncryptable, + CompositeEncryptable, Decryptable, Encryptable, IdentifyKey, KeyId, KeyIds, + PrimitiveEncryptableWithContentType, }; pub use zeroizing_alloc::ZeroAlloc as ZeroizingAllocator; diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index ad2197fae..05ef27c90 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -15,7 +15,8 @@ use crate::{ /// The context of a crypto operation using [super::KeyStore] /// /// This will usually be accessed from an implementation of [crate::Decryptable] or -/// [crate::Encryptable], [crate::TypedEncryptable], but can also be obtained through [super::KeyStore::context] +/// [crate::Encryptable], [crate::TypedEncryptable], but can also be obtained through +/// [super::KeyStore::context] /// /// This context contains access to the user keys stored in the [super::KeyStore] (sometimes /// referred to as `global keys`) and it also contains it's own individual secure backend for key diff --git a/crates/bitwarden-crypto/src/store/mod.rs b/crates/bitwarden-crypto/src/store/mod.rs index 002aea942..45fcc9836 100644 --- a/crates/bitwarden-crypto/src/store/mod.rs +++ b/crates/bitwarden-crypto/src/store/mod.rs @@ -310,7 +310,7 @@ pub(crate) mod tests { use crate::{ store::{KeyStore, KeyStoreContext}, traits::tests::{TestIds, TestSymmKey}, - EncString, SymmetricCryptoKey, TypedEncryptable, + EncString, PrimitiveEncryptableWithContentType, SymmetricCryptoKey, }; pub struct DataView(pub String, pub TestSymmKey); diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index 7ca18ec93..381b7b394 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -43,12 +43,12 @@ impl, Output> /// An encryption operation that takes the input value - a primitive such as `String` and encrypts /// it into the output value. The implementation decides the content format. -pub trait TypedEncryptable { +pub trait PrimitiveEncryptableWithContentType { fn encrypt(&self, ctx: &mut KeyStoreContext, key: Key) -> Result; } -impl, Output> - TypedEncryptable> for Option +impl, Output> + PrimitiveEncryptableWithContentType> for Option { fn encrypt( &self, @@ -61,7 +61,7 @@ impl, Output> } } -impl TypedEncryptable for &str { +impl PrimitiveEncryptableWithContentType for &str { fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -71,7 +71,7 @@ impl TypedEncryptable for &str { } } -impl TypedEncryptable for String { +impl PrimitiveEncryptableWithContentType for String { fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -148,7 +148,7 @@ impl, Output> mod tests { use crate::{ cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, - KeyStore, SymmetricCryptoKey, TypedEncryptable, + KeyStore, PrimitiveEncryptableWithContentType, SymmetricCryptoKey, }; fn test_store() -> KeyStore { diff --git a/crates/bitwarden-crypto/src/traits/mod.rs b/crates/bitwarden-crypto/src/traits/mod.rs index 122088827..9f9105645 100644 --- a/crates/bitwarden-crypto/src/traits/mod.rs +++ b/crates/bitwarden-crypto/src/traits/mod.rs @@ -1,5 +1,5 @@ mod encryptable; -pub use encryptable::{CompositeEncryptable, Encryptable, TypedEncryptable}; +pub use encryptable::{CompositeEncryptable, Encryptable, PrimitiveEncryptableWithContentType}; mod decryptable; pub use decryptable::Decryptable; diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index 952b87dad..2d81c0206 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -9,7 +9,7 @@ use bitwarden_core::{ }; use bitwarden_crypto::{ generate_random_bytes, CompositeEncryptable, ContentFormat, CryptoError, Decryptable, - EncString, Encryptable, IdentifyKey, KeyStoreContext, TypedEncryptable, + EncString, Encryptable, IdentifyKey, KeyStoreContext, PrimitiveEncryptableWithContentType, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index 159da97e3..75c54a208 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,7 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, - IdentifyKey, KeyStoreContext, TypedEncryptable, + IdentifyKey, KeyStoreContext, PrimitiveEncryptableWithContentType, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] diff --git a/crates/bitwarden-vault/src/cipher/card.rs b/crates/bitwarden-vault/src/cipher/card.rs index 241c8d8b3..8d69edfee 100644 --- a/crates/bitwarden-vault/src/cipher/card.rs +++ b/crates/bitwarden-vault/src/cipher/card.rs @@ -2,7 +2,7 @@ use bitwarden_api_api::models::CipherCardModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, - TypedEncryptable, + PrimitiveEncryptableWithContentType, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] diff --git a/crates/bitwarden-vault/src/cipher/cipher.rs b/crates/bitwarden-vault/src/cipher/cipher.rs index 643dd295b..18a94ac34 100644 --- a/crates/bitwarden-vault/src/cipher/cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher.rs @@ -4,8 +4,8 @@ use bitwarden_core::{ require, MissingFieldError, VaultLockedError, }; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, - KeyStoreContext, TypedEncryptable, + CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, + PrimitiveEncryptableWithContentType, }; use bitwarden_error::bitwarden_error; use chrono::{DateTime, Utc}; diff --git a/crates/bitwarden-vault/src/cipher/field.rs b/crates/bitwarden-vault/src/cipher/field.rs index d4177df09..fc98671d1 100644 --- a/crates/bitwarden-vault/src/cipher/field.rs +++ b/crates/bitwarden-vault/src/cipher/field.rs @@ -5,7 +5,7 @@ use bitwarden_core::{ }; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, - TypedEncryptable, + PrimitiveEncryptableWithContentType, }; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; diff --git a/crates/bitwarden-vault/src/cipher/identity.rs b/crates/bitwarden-vault/src/cipher/identity.rs index cd8e2dffc..a72ab50ff 100644 --- a/crates/bitwarden-vault/src/cipher/identity.rs +++ b/crates/bitwarden-vault/src/cipher/identity.rs @@ -2,7 +2,7 @@ use bitwarden_api_api::models::CipherIdentityModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, - TypedEncryptable, + PrimitiveEncryptableWithContentType, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] diff --git a/crates/bitwarden-vault/src/cipher/login.rs b/crates/bitwarden-vault/src/cipher/login.rs index ca65d6c2b..77294e37b 100644 --- a/crates/bitwarden-vault/src/cipher/login.rs +++ b/crates/bitwarden-vault/src/cipher/login.rs @@ -6,7 +6,7 @@ use bitwarden_core::{ }; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, - TypedEncryptable, + PrimitiveEncryptableWithContentType, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-vault/src/cipher/ssh_key.rs b/crates/bitwarden-vault/src/cipher/ssh_key.rs index c499c632e..5f9098b63 100644 --- a/crates/bitwarden-vault/src/cipher/ssh_key.rs +++ b/crates/bitwarden-vault/src/cipher/ssh_key.rs @@ -1,7 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, - TypedEncryptable, + PrimitiveEncryptableWithContentType, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] diff --git a/crates/bitwarden-vault/src/folder.rs b/crates/bitwarden-vault/src/folder.rs index e72a07714..f88c1116d 100644 --- a/crates/bitwarden-vault/src/folder.rs +++ b/crates/bitwarden-vault/src/folder.rs @@ -4,8 +4,8 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, - KeyStoreContext, TypedEncryptable, + CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, + PrimitiveEncryptableWithContentType, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-vault/src/password_history.rs b/crates/bitwarden-vault/src/password_history.rs index 5da4269f4..657fcdb56 100644 --- a/crates/bitwarden-vault/src/password_history.rs +++ b/crates/bitwarden-vault/src/password_history.rs @@ -1,8 +1,8 @@ use bitwarden_api_api::models::CipherPasswordHistoryModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, - KeyStoreContext, TypedEncryptable, + CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, + PrimitiveEncryptableWithContentType, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; From a42b1e77357bb9b803c3adcd191dd4b8aee1c9af Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 22 May 2025 12:24:30 +0200 Subject: [PATCH 017/124] Set correct content type for pkcs8 --- 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 2fb1dc263..7c81a034c 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -187,7 +187,7 @@ impl PureCrypto { .encrypt( &mut context, SymmetricKeyId::Local("wrapping_key"), - ContentFormat::OctetStream, + ContentFormat::Pkcs8, )? .to_string()) } From 5447cbb6c6429991d7ef360b92ca94ed1fdb359f Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 22 May 2025 12:52:16 +0200 Subject: [PATCH 018/124] Add cose keywrap --- crates/bitwarden-crypto/src/store/context.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 05ef27c90..0a2eeea1e 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -185,6 +185,18 @@ impl KeyStoreContext<'_, Ids> { key_to_wrap_instance.to_encoded().as_slice(), ContentFormat::OctetStream, ), + (XChaCha20Poly1305Key(_), Aes256CbcHmacKey(_) | Aes256CbcKey(_)) => self + .encrypt_data_with_symmetric_key( + wrapping_key, + key_to_wrap_instance.to_encoded().as_slice(), + ContentFormat::OctetStream, + ), + (XChaCha20Poly1305Key(_), XChaCha20Poly1305Key(_)) => self + .encrypt_data_with_symmetric_key( + wrapping_key, + key_to_wrap_instance.to_encoded_raw().as_slice(), + ContentFormat::CoseKey, + ), _ => Err(CryptoError::OperationNotSupported( UnsupportedOperation::EncryptionNotImplementedForKey, )), From 66c345ff79e22da8c799d9bb6610ccf85abeec04 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 22 May 2025 13:53:18 +0200 Subject: [PATCH 019/124] Fix keywrap --- crates/bitwarden-crypto/src/cose.rs | 75 +++++++++++++++-- .../src/enc_string/symmetric.rs | 2 +- crates/bitwarden-crypto/src/error.rs | 2 + .../src/keys/symmetric_crypto_key.rs | 11 ++- crates/bitwarden-crypto/src/store/context.rs | 80 +++++++++++++++++-- 5 files changed, 151 insertions(+), 19 deletions(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index bf76df39d..e7ef23586 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -92,7 +92,7 @@ pub(crate) fn encrypt_xchacha20_poly1305( pub(crate) fn decrypt_xchacha20_poly1305( cose_encrypt0_message: &[u8], key: &crate::XChaCha20Poly1305Key, -) -> Result, CryptoError> { +) -> Result<(Vec, ContentFormat), CryptoError> { let msg = coset::CoseEncrypt0::from_slice(cose_encrypt0_message) .map_err(|err| CryptoError::EncString(EncStringParseError::InvalidCoseEncoding(err)))?; let Some(ref alg) = msg.protected.header.alg else { @@ -117,12 +117,30 @@ pub(crate) fn decrypt_xchacha20_poly1305( })?; if let Some(ref content_type) = msg.protected.header.content_type { - if *content_type == ContentType::Text(CONTENT_TYPE_PADDED_UTF8.to_string()) { - // Unpad the data to get the original plaintext - return crate::keys::utils::unpad_bytes(&decrypted_message).map(|bytes| bytes.to_vec()); + match content_type { + ContentType::Text(format) if format == CONTENT_TYPE_PADDED_UTF8 => { + if *content_type == ContentType::Text(CONTENT_TYPE_PADDED_UTF8.to_string()) { + // Unpad the data to get the original plaintext + let data = crate::keys::utils::unpad_bytes(&decrypted_message) + .map_err(|_| CryptoError::InvalidPadding)?; + return Ok((data.to_vec(), ContentFormat::Utf8)); + } + }, + ContentType::Assigned(content_format) if *content_format == CoapContentFormat::Pkcs8 => { + return Ok((decrypted_message.to_vec(), ContentFormat::Pkcs8)); + } + ContentType::Assigned(content_format) if *content_format == CoapContentFormat::CoseKey => { + return Ok((decrypted_message.to_vec(), ContentFormat::CoseKey)); + } + ContentType::Assigned(content_format) if *content_format == CoapContentFormat::OctetStream => { + return Ok((decrypted_message.to_vec(), ContentFormat::OctetStream)); + } + _ => {} } } - Ok(decrypted_message) + Err(CryptoError::EncString( + EncStringParseError::CoseMissingContentType, + )) } const SYMMETRIC_KEY: Label = Label::Int(iana::SymmetricKeyParameter::K as i64); @@ -168,7 +186,7 @@ mod test { use super::*; #[test] - fn test_encrypt_decrypt_roundtrip() { + fn test_encrypt_decrypt_roundtrip_octetstream() { let SymmetricCryptoKey::XChaCha20Poly1305Key(ref key) = SymmetricCryptoKey::make_xchacha20_poly1305_key() else { @@ -179,6 +197,49 @@ mod test { let encrypted = encrypt_xchacha20_poly1305(plaintext, key, ContentFormat::OctetStream).unwrap(); let decrypted = decrypt_xchacha20_poly1305(&encrypted, key).unwrap(); - assert_eq!(decrypted, plaintext); + assert_eq!(decrypted, (plaintext.to_vec(), ContentFormat::OctetStream)); + } + + #[test] + fn test_encrypt_decrypt_roundtrip_utf8() { + let SymmetricCryptoKey::XChaCha20Poly1305Key(ref key) = + SymmetricCryptoKey::make_xchacha20_poly1305_key() + else { + panic!("Failed to create XChaCha20Poly1305Key"); + }; + + let plaintext = b"Hello, world!"; + let encrypted = encrypt_xchacha20_poly1305(plaintext, key, ContentFormat::Utf8).unwrap(); + let decrypted = decrypt_xchacha20_poly1305(&encrypted, key).unwrap(); + assert_eq!(decrypted, (plaintext.to_vec(), ContentFormat::Utf8)); } + + #[test] + fn test_encrypt_decrypt_roundtrip_pkcs8() { + let SymmetricCryptoKey::XChaCha20Poly1305Key(ref key) = + SymmetricCryptoKey::make_xchacha20_poly1305_key() + else { + panic!("Failed to create XChaCha20Poly1305Key"); + }; + + let plaintext = b"Hello, world!"; + let encrypted = encrypt_xchacha20_poly1305(plaintext, key, ContentFormat::Pkcs8).unwrap(); + let decrypted = decrypt_xchacha20_poly1305(&encrypted, key).unwrap(); + assert_eq!(decrypted, (plaintext.to_vec(), ContentFormat::Pkcs8)); + } + + #[test] + fn test_encrypt_decrypt_roundtrip_cosekey() { + let SymmetricCryptoKey::XChaCha20Poly1305Key(ref key) = + SymmetricCryptoKey::make_xchacha20_poly1305_key() + else { + panic!("Failed to create XChaCha20Poly1305Key"); + }; + + let plaintext = b"Hello, world!"; + let encrypted = encrypt_xchacha20_poly1305(plaintext, key, ContentFormat::CoseKey).unwrap(); + let decrypted = decrypt_xchacha20_poly1305(&encrypted, key).unwrap(); + assert_eq!(decrypted, (plaintext.to_vec(), ContentFormat::CoseKey)); + } + } diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index 5a662e289..0d7ee2ba3 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -307,7 +307,7 @@ impl KeyDecryptable> for EncString { EncString::Cose_Encrypt0_B64 { data }, SymmetricCryptoKey::XChaCha20Poly1305Key(key), ) => { - let decrypted_message = + let (decrypted_message, _) = crate::cose::decrypt_xchacha20_poly1305(data.as_slice(), key)?; Ok(decrypted_message) } diff --git a/crates/bitwarden-crypto/src/error.rs b/crates/bitwarden-crypto/src/error.rs index 0e9b617ad..27fe61ae1 100644 --- a/crates/bitwarden-crypto/src/error.rs +++ b/crates/bitwarden-crypto/src/error.rs @@ -83,6 +83,8 @@ pub enum EncStringParseError { InvalidCoseEncoding(coset::CoseError), #[error("Algorithm missing in COSE header")] CoseMissingAlgorithm, + #[error("Content type missing in COSE header")] + CoseMissingContentType, } #[derive(Debug, Error)] diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index 8d208405a..35f241cef 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -208,6 +208,13 @@ impl SymmetricCryptoKey { } } + pub(crate) fn try_from_cose(serialized_key: &[u8]) -> Result { + let cose_key = coset::CoseKey::from_slice(serialized_key) + .map_err(|_| CryptoError::InvalidKey)?; + let key = SymmetricCryptoKey::try_from(&cose_key)?; + Ok(key) + } + pub fn to_base64(&self) -> String { STANDARD.encode(self.to_encoded()) } @@ -286,9 +293,7 @@ impl TryFrom<&mut [u8]> for SymmetricCryptoKey { Ok(Self::Aes256CbcKey(Aes256CbcKey { enc_key })) } else if value.len() > Self::AES256_CBC_HMAC_KEY_LEN { let unpadded_value = unpad_key(value)?; - let cose_key = - coset::CoseKey::from_slice(unpadded_value).map_err(|_| CryptoError::InvalidKey)?; - SymmetricCryptoKey::try_from(&cose_key) + Ok(Self::try_from_cose(unpadded_value)?) } else { Err(CryptoError::InvalidKeyLen) }; diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 0a2eeea1e..b96e0c08b 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -132,24 +132,44 @@ impl KeyStoreContext<'_, Ids> { /// /// # Arguments /// - /// * `encryption_key` - The key id used to decrypt the `encrypted_key`. It must already exist + /// * `wrapping_key` - The key id used to decrypt the `wrapped_key`. It must already exist /// in the context /// * `new_key_id` - The key id where the decrypted key will be stored. If it already exists, it /// will be overwritten - /// * `encrypted_key` - The key to decrypt + /// * `wrapped_key` - The key to decrypt pub fn unwrap_symmetric_key( &mut self, - encryption_key: Ids::Symmetric, + wrapping_key: Ids::Symmetric, new_key_id: Ids::Symmetric, - encrypted_key: &EncString, + wrapped_key: &EncString, ) -> Result { - let mut new_key_material = - self.decrypt_data_with_symmetric_key(encryption_key, encrypted_key)?; + let wrapping_key = self.get_symmetric_key(wrapping_key)?; + + let key = match (wrapped_key, wrapping_key) { + (EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => { + SymmetricCryptoKey::try_from(crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)?)? + } + ( + EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data }, + SymmetricCryptoKey::Aes256CbcHmacKey(key), + ) => { + SymmetricCryptoKey::try_from(crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key)?)? + } + (EncString::Cose_Encrypt0_B64 { data }, SymmetricCryptoKey::XChaCha20Poly1305Key(key)) => { + let (content_bytes, content_format) = crate::cose::decrypt_xchacha20_poly1305(data, key)?; + match content_format { + ContentFormat::OctetStream => SymmetricCryptoKey::try_from(content_bytes)?, + ContentFormat::CoseKey => SymmetricCryptoKey::try_from_cose(&content_bytes)?, + _ => return Err(CryptoError::InvalidKey) + } + } + _ => return Err(CryptoError::InvalidKey), + }; #[allow(deprecated)] self.set_symmetric_key( new_key_id, - SymmetricCryptoKey::try_from(new_key_material.as_mut_slice())?, + key, )?; // Returning the new key identifier for convenience @@ -179,7 +199,7 @@ impl KeyStoreContext<'_, Ids> { // or `Aes256CbcKey`, or by specifying the content format to be CoseKey, in case the // wrapped key is a `XChaCha20Poly1305Key`. match (wrapping_key_instance, key_to_wrap_instance) { - (Aes256CbcHmacKey(_), Aes256CbcHmacKey(_) | Aes256CbcKey(_)) => self + (Aes256CbcHmacKey(_), Aes256CbcHmacKey(_) | Aes256CbcKey(_) | XChaCha20Poly1305Key(_)) => self .encrypt_data_with_symmetric_key( wrapping_key, key_to_wrap_instance.to_encoded().as_slice(), @@ -368,6 +388,10 @@ impl KeyStoreContext<'_, Ids> { EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data }, SymmetricCryptoKey::Aes256CbcHmacKey(key), ) => crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key), + (EncString::Cose_Encrypt0_B64 { data }, SymmetricCryptoKey::XChaCha20Poly1305Key(key)) => { + let (data, _) = crate::cose::decrypt_xchacha20_poly1305(data, key)?; + Ok(data) + } _ => Err(CryptoError::InvalidKey), } } @@ -468,4 +492,44 @@ mod tests { // Assert that the decrypted data is the same assert_eq!(decrypted1.0, decrypted2.0); } + + #[test] + fn test_wrap_unwrap_aes256_cbc_hmac() { + let store: KeyStore = KeyStore::default(); + let mut ctx = store.context_mut(); + + // Aes256 CBC HMAC keys + let key_aes_1_id = TestSymmKey::A(1); + let key_aes_1 = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); + ctx.set_symmetric_key(key_aes_1_id, key_aes_1.clone()).unwrap(); + let key_aes_2_id = TestSymmKey::A(2); + let key_aes_2 = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); + ctx.set_symmetric_key(key_aes_2_id, key_aes_2.clone()).unwrap(); + + // XChaCha20 Poly1305 keys + let key_xchacha_3_id = TestSymmKey::A(3); + let key_xchacha_3 = SymmetricCryptoKey::make_xchacha20_poly1305_key(); + ctx.set_symmetric_key(key_xchacha_3_id, key_xchacha_3.clone()).unwrap(); + let key_xchacha_4_id = TestSymmKey::A(4); + let key_xchacha_4 = SymmetricCryptoKey::make_xchacha20_poly1305_key(); + ctx.set_symmetric_key(key_xchacha_4_id, key_xchacha_4.clone()).unwrap(); + + // Wrap and unwrap the keys + let wrapped_key_1_2 = ctx.wrap_symmetric_key(key_aes_1_id, key_aes_2_id).unwrap(); + let wrapped_key_1_3 = ctx.wrap_symmetric_key(key_aes_1_id, key_xchacha_3_id).unwrap(); + let wrapped_key_3_1 = ctx.wrap_symmetric_key(key_xchacha_3_id, key_aes_1_id).unwrap(); + let wrapped_key_3_4 = ctx.wrap_symmetric_key(key_xchacha_3_id, key_xchacha_4_id).unwrap(); + + // Unwrap the keys + let unwrapped_key_2 = ctx.unwrap_symmetric_key(key_aes_1_id, key_aes_2_id, &wrapped_key_1_2).unwrap(); + let unwrapped_key_3 = ctx.unwrap_symmetric_key(key_aes_1_id, key_xchacha_3_id, &wrapped_key_1_3).unwrap(); + let unwrapped_key_1 = ctx.unwrap_symmetric_key(key_xchacha_3_id, key_aes_1_id, &wrapped_key_3_1).unwrap(); + let unwrapped_key_4 = ctx.unwrap_symmetric_key(key_xchacha_3_id, key_xchacha_4_id, &wrapped_key_3_4).unwrap(); + + // Assert that the unwrapped keys are the same as the original keys + assert_eq!(unwrapped_key_2, key_aes_2_id); + assert_eq!(unwrapped_key_3, key_xchacha_3_id); + assert_eq!(unwrapped_key_1, key_aes_1_id); + assert_eq!(unwrapped_key_4, key_xchacha_4_id); + } } From 9b2f6c9f73462875772965b70b70100f6c4492f4 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 22 May 2025 13:55:56 +0200 Subject: [PATCH 020/124] Run cargo fmt --- crates/bitwarden-crypto/src/cose.rs | 15 ++- .../src/keys/symmetric_crypto_key.rs | 4 +- crates/bitwarden-crypto/src/store/context.rs | 92 +++++++++++++------ 3 files changed, 74 insertions(+), 37 deletions(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index e7ef23586..9cfa5909f 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -125,14 +125,20 @@ pub(crate) fn decrypt_xchacha20_poly1305( .map_err(|_| CryptoError::InvalidPadding)?; return Ok((data.to_vec(), ContentFormat::Utf8)); } - }, - ContentType::Assigned(content_format) if *content_format == CoapContentFormat::Pkcs8 => { + } + ContentType::Assigned(content_format) + if *content_format == CoapContentFormat::Pkcs8 => + { return Ok((decrypted_message.to_vec(), ContentFormat::Pkcs8)); } - ContentType::Assigned(content_format) if *content_format == CoapContentFormat::CoseKey => { + ContentType::Assigned(content_format) + if *content_format == CoapContentFormat::CoseKey => + { return Ok((decrypted_message.to_vec(), ContentFormat::CoseKey)); } - ContentType::Assigned(content_format) if *content_format == CoapContentFormat::OctetStream => { + ContentType::Assigned(content_format) + if *content_format == CoapContentFormat::OctetStream => + { return Ok((decrypted_message.to_vec(), ContentFormat::OctetStream)); } _ => {} @@ -241,5 +247,4 @@ mod test { let decrypted = decrypt_xchacha20_poly1305(&encrypted, key).unwrap(); assert_eq!(decrypted, (plaintext.to_vec(), ContentFormat::CoseKey)); } - } diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index 35f241cef..4b33f2b6a 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -209,8 +209,8 @@ impl SymmetricCryptoKey { } pub(crate) fn try_from_cose(serialized_key: &[u8]) -> Result { - let cose_key = coset::CoseKey::from_slice(serialized_key) - .map_err(|_| CryptoError::InvalidKey)?; + let cose_key = + coset::CoseKey::from_slice(serialized_key).map_err(|_| CryptoError::InvalidKey)?; let key = SymmetricCryptoKey::try_from(&cose_key)?; Ok(key) } diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index b96e0c08b..730847940 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -132,8 +132,8 @@ impl KeyStoreContext<'_, Ids> { /// /// # Arguments /// - /// * `wrapping_key` - The key id used to decrypt the `wrapped_key`. It must already exist - /// in the context + /// * `wrapping_key` - The key id used to decrypt the `wrapped_key`. It must already exist in + /// the context /// * `new_key_id` - The key id where the decrypted key will be stored. If it already exists, it /// will be overwritten /// * `wrapped_key` - The key to decrypt @@ -147,30 +147,39 @@ impl KeyStoreContext<'_, Ids> { let key = match (wrapped_key, wrapping_key) { (EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => { - SymmetricCryptoKey::try_from(crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)?)? + SymmetricCryptoKey::try_from(crate::aes::decrypt_aes256( + iv, + data.clone(), + &key.enc_key, + )?)? } ( EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data }, SymmetricCryptoKey::Aes256CbcHmacKey(key), + ) => SymmetricCryptoKey::try_from(crate::aes::decrypt_aes256_hmac( + iv, + mac, + data.clone(), + &key.mac_key, + &key.enc_key, + )?)?, + ( + EncString::Cose_Encrypt0_B64 { data }, + SymmetricCryptoKey::XChaCha20Poly1305Key(key), ) => { - SymmetricCryptoKey::try_from(crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key)?)? - } - (EncString::Cose_Encrypt0_B64 { data }, SymmetricCryptoKey::XChaCha20Poly1305Key(key)) => { - let (content_bytes, content_format) = crate::cose::decrypt_xchacha20_poly1305(data, key)?; + let (content_bytes, content_format) = + crate::cose::decrypt_xchacha20_poly1305(data, key)?; match content_format { ContentFormat::OctetStream => SymmetricCryptoKey::try_from(content_bytes)?, ContentFormat::CoseKey => SymmetricCryptoKey::try_from_cose(&content_bytes)?, - _ => return Err(CryptoError::InvalidKey) + _ => return Err(CryptoError::InvalidKey), } } _ => return Err(CryptoError::InvalidKey), }; #[allow(deprecated)] - self.set_symmetric_key( - new_key_id, - key, - )?; + self.set_symmetric_key(new_key_id, key)?; // Returning the new key identifier for convenience Ok(new_key_id) @@ -199,12 +208,14 @@ impl KeyStoreContext<'_, Ids> { // or `Aes256CbcKey`, or by specifying the content format to be CoseKey, in case the // wrapped key is a `XChaCha20Poly1305Key`. match (wrapping_key_instance, key_to_wrap_instance) { - (Aes256CbcHmacKey(_), Aes256CbcHmacKey(_) | Aes256CbcKey(_) | XChaCha20Poly1305Key(_)) => self - .encrypt_data_with_symmetric_key( - wrapping_key, - key_to_wrap_instance.to_encoded().as_slice(), - ContentFormat::OctetStream, - ), + ( + Aes256CbcHmacKey(_), + Aes256CbcHmacKey(_) | Aes256CbcKey(_) | XChaCha20Poly1305Key(_), + ) => self.encrypt_data_with_symmetric_key( + wrapping_key, + key_to_wrap_instance.to_encoded().as_slice(), + ContentFormat::OctetStream, + ), (XChaCha20Poly1305Key(_), Aes256CbcHmacKey(_) | Aes256CbcKey(_)) => self .encrypt_data_with_symmetric_key( wrapping_key, @@ -388,7 +399,10 @@ impl KeyStoreContext<'_, Ids> { EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data }, SymmetricCryptoKey::Aes256CbcHmacKey(key), ) => crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key), - (EncString::Cose_Encrypt0_B64 { data }, SymmetricCryptoKey::XChaCha20Poly1305Key(key)) => { + ( + EncString::Cose_Encrypt0_B64 { data }, + SymmetricCryptoKey::XChaCha20Poly1305Key(key), + ) => { let (data, _) = crate::cose::decrypt_xchacha20_poly1305(data, key)?; Ok(data) } @@ -501,30 +515,48 @@ mod tests { // Aes256 CBC HMAC keys let key_aes_1_id = TestSymmKey::A(1); let key_aes_1 = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); - ctx.set_symmetric_key(key_aes_1_id, key_aes_1.clone()).unwrap(); + ctx.set_symmetric_key(key_aes_1_id, key_aes_1.clone()) + .unwrap(); let key_aes_2_id = TestSymmKey::A(2); let key_aes_2 = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); - ctx.set_symmetric_key(key_aes_2_id, key_aes_2.clone()).unwrap(); + ctx.set_symmetric_key(key_aes_2_id, key_aes_2.clone()) + .unwrap(); // XChaCha20 Poly1305 keys let key_xchacha_3_id = TestSymmKey::A(3); let key_xchacha_3 = SymmetricCryptoKey::make_xchacha20_poly1305_key(); - ctx.set_symmetric_key(key_xchacha_3_id, key_xchacha_3.clone()).unwrap(); + ctx.set_symmetric_key(key_xchacha_3_id, key_xchacha_3.clone()) + .unwrap(); let key_xchacha_4_id = TestSymmKey::A(4); let key_xchacha_4 = SymmetricCryptoKey::make_xchacha20_poly1305_key(); - ctx.set_symmetric_key(key_xchacha_4_id, key_xchacha_4.clone()).unwrap(); + ctx.set_symmetric_key(key_xchacha_4_id, key_xchacha_4.clone()) + .unwrap(); // Wrap and unwrap the keys let wrapped_key_1_2 = ctx.wrap_symmetric_key(key_aes_1_id, key_aes_2_id).unwrap(); - let wrapped_key_1_3 = ctx.wrap_symmetric_key(key_aes_1_id, key_xchacha_3_id).unwrap(); - let wrapped_key_3_1 = ctx.wrap_symmetric_key(key_xchacha_3_id, key_aes_1_id).unwrap(); - let wrapped_key_3_4 = ctx.wrap_symmetric_key(key_xchacha_3_id, key_xchacha_4_id).unwrap(); + let wrapped_key_1_3 = ctx + .wrap_symmetric_key(key_aes_1_id, key_xchacha_3_id) + .unwrap(); + let wrapped_key_3_1 = ctx + .wrap_symmetric_key(key_xchacha_3_id, key_aes_1_id) + .unwrap(); + let wrapped_key_3_4 = ctx + .wrap_symmetric_key(key_xchacha_3_id, key_xchacha_4_id) + .unwrap(); // Unwrap the keys - let unwrapped_key_2 = ctx.unwrap_symmetric_key(key_aes_1_id, key_aes_2_id, &wrapped_key_1_2).unwrap(); - let unwrapped_key_3 = ctx.unwrap_symmetric_key(key_aes_1_id, key_xchacha_3_id, &wrapped_key_1_3).unwrap(); - let unwrapped_key_1 = ctx.unwrap_symmetric_key(key_xchacha_3_id, key_aes_1_id, &wrapped_key_3_1).unwrap(); - let unwrapped_key_4 = ctx.unwrap_symmetric_key(key_xchacha_3_id, key_xchacha_4_id, &wrapped_key_3_4).unwrap(); + let unwrapped_key_2 = ctx + .unwrap_symmetric_key(key_aes_1_id, key_aes_2_id, &wrapped_key_1_2) + .unwrap(); + let unwrapped_key_3 = ctx + .unwrap_symmetric_key(key_aes_1_id, key_xchacha_3_id, &wrapped_key_1_3) + .unwrap(); + let unwrapped_key_1 = ctx + .unwrap_symmetric_key(key_xchacha_3_id, key_aes_1_id, &wrapped_key_3_1) + .unwrap(); + let unwrapped_key_4 = ctx + .unwrap_symmetric_key(key_xchacha_3_id, key_xchacha_4_id, &wrapped_key_3_4) + .unwrap(); // Assert that the unwrapped keys are the same as the original keys assert_eq!(unwrapped_key_2, key_aes_2_id); From 88b96b7906555dcf891ae5831119b8e8fbd0443d Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 22 May 2025 14:08:52 +0200 Subject: [PATCH 021/124] Fix encryptable --- crates/bitwarden-crypto/src/store/context.rs | 2 +- crates/bitwarden-crypto/src/store/mod.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 730847940..95c09c2b7 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -15,7 +15,7 @@ use crate::{ /// The context of a crypto operation using [super::KeyStore] /// /// This will usually be accessed from an implementation of [crate::Decryptable] or -/// [crate::Encryptable], [crate::TypedEncryptable], but can also be obtained through +/// [crate::Encryptable], [crate::PrimitiveEncryptableWithContentType], but can also be obtained through /// [super::KeyStore::context] /// /// This context contains access to the user keys stored in the [super::KeyStore] (sometimes diff --git a/crates/bitwarden-crypto/src/store/mod.rs b/crates/bitwarden-crypto/src/store/mod.rs index 45fcc9836..3331f9104 100644 --- a/crates/bitwarden-crypto/src/store/mod.rs +++ b/crates/bitwarden-crypto/src/store/mod.rs @@ -129,7 +129,7 @@ impl KeyStore { /// context-local store will be cleared when the context is dropped. /// /// If you are only looking to encrypt or decrypt items, you should implement - /// [Encryptable]/[Decryptable] and use the [KeyStore::encrypt], [KeyStore::decrypt], + /// [CompositeEncryptable]/[Decryptable] and use the [KeyStore::encrypt], [KeyStore::decrypt], /// [KeyStore::encrypt_list] and [KeyStore::decrypt_list] methods instead. /// /// The current implementation of context only clears the keys automatically when the context is @@ -143,10 +143,10 @@ impl KeyStore { /// future to also not be [Send]. /// /// Some other possible use cases for this API and alternative recommendations are: - /// - Decrypting or encrypting multiple [Decryptable] or [Encryptable] items while sharing any + /// - Decrypting or encrypting multiple [Decryptable] or [CompositeEncryptable] items while sharing any /// local keys. This is not recommended as it can lead to fragile and flaky /// decryption/encryption operations. We recommend any local keys to be used only in the - /// context of a single [Encryptable] or [Decryptable] implementation. In the future we might + /// context of a single [CompositeEncryptable] or [Decryptable] implementation. In the future we might /// enforce this. /// - Obtaining the key material directly. We strongly recommend against doing this as it can /// lead to key material being leaked, but we need to support it for backwards compatibility. @@ -310,7 +310,7 @@ pub(crate) mod tests { use crate::{ store::{KeyStore, KeyStoreContext}, traits::tests::{TestIds, TestSymmKey}, - EncString, PrimitiveEncryptableWithContentType, SymmetricCryptoKey, + EncString, SymmetricCryptoKey, PrimitiveEncryptableWithContentType, }; pub struct DataView(pub String, pub TestSymmKey); From 7e69b7bb57f2cb024b3ca0683de8f0af28166cd7 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 22 May 2025 14:23:50 +0200 Subject: [PATCH 022/124] Run cargo fmt --- crates/bitwarden-crypto/src/store/context.rs | 4 ++-- crates/bitwarden-crypto/src/store/mod.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 95c09c2b7..8f62c5a85 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -15,8 +15,8 @@ use crate::{ /// The context of a crypto operation using [super::KeyStore] /// /// This will usually be accessed from an implementation of [crate::Decryptable] or -/// [crate::Encryptable], [crate::PrimitiveEncryptableWithContentType], but can also be obtained through -/// [super::KeyStore::context] +/// [crate::Encryptable], [crate::PrimitiveEncryptableWithContentType], but can also be obtained +/// through [super::KeyStore::context] /// /// This context contains access to the user keys stored in the [super::KeyStore] (sometimes /// referred to as `global keys`) and it also contains it's own individual secure backend for key diff --git a/crates/bitwarden-crypto/src/store/mod.rs b/crates/bitwarden-crypto/src/store/mod.rs index 3331f9104..cb4f9ef72 100644 --- a/crates/bitwarden-crypto/src/store/mod.rs +++ b/crates/bitwarden-crypto/src/store/mod.rs @@ -143,11 +143,11 @@ impl KeyStore { /// future to also not be [Send]. /// /// Some other possible use cases for this API and alternative recommendations are: - /// - Decrypting or encrypting multiple [Decryptable] or [CompositeEncryptable] items while sharing any - /// local keys. This is not recommended as it can lead to fragile and flaky + /// - Decrypting or encrypting multiple [Decryptable] or [CompositeEncryptable] items while + /// sharing any local keys. This is not recommended as it can lead to fragile and flaky /// decryption/encryption operations. We recommend any local keys to be used only in the - /// context of a single [CompositeEncryptable] or [Decryptable] implementation. In the future we might - /// enforce this. + /// context of a single [CompositeEncryptable] or [Decryptable] implementation. In the future + /// we might enforce this. /// - Obtaining the key material directly. We strongly recommend against doing this as it can /// lead to key material being leaked, but we need to support it for backwards compatibility. /// If you want to access the key material to encrypt it or derive a new key from it, we @@ -310,7 +310,7 @@ pub(crate) mod tests { use crate::{ store::{KeyStore, KeyStoreContext}, traits::tests::{TestIds, TestSymmKey}, - EncString, SymmetricCryptoKey, PrimitiveEncryptableWithContentType, + EncString, PrimitiveEncryptableWithContentType, SymmetricCryptoKey, }; pub struct DataView(pub String, pub TestSymmKey); From b7d2bc84f3ab0a8a81ea0ac811572b4523edbbeb Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 29 May 2025 13:32:18 +0200 Subject: [PATCH 023/124] Cleanup --- crates/bitwarden-crypto/src/cose.rs | 114 +++++++++++++++------------- 1 file changed, 61 insertions(+), 53 deletions(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 9cfa5909f..448118b17 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -45,36 +45,23 @@ pub(crate) fn encrypt_xchacha20_poly1305( key: &crate::XChaCha20Poly1305Key, content_format: ContentFormat, ) -> Result, CryptoError> { - let protected_header = coset::HeaderBuilder::new(); - let protected_header = match content_format { - // UTF-8 directly would leak the plaintext size. This is not acceptable for certain data - // (passwords). - ContentFormat::Utf8 => protected_header.content_type(CONTENT_TYPE_PADDED_UTF8.to_string()), - ContentFormat::Pkcs8 => protected_header.content_format(CoapContentFormat::Pkcs8), - ContentFormat::CoseKey => protected_header.content_format(CoapContentFormat::CoseKey), - ContentFormat::OctetStream => { - protected_header.content_format(CoapContentFormat::OctetStream) - } - }; - let mut protected_header = protected_header.build(); + let mut plaintext = plaintext.to_vec(); + + let mut protected_header: coset::Header = content_format.into(); + + if should_pad_content(&content_format) { + // Pad the data to a block size in order to hide plaintext length + crate::keys::utils::pad_bytes(&mut plaintext, XCHACHA20_TEXT_PAD_BLOCK_SIZE); + } // This should be adjusted to use the builder pattern once implemented in coset. // The related coset upstream issue is: // https://github.com/google/coset/issues/105 protected_header.alg = Some(coset::Algorithm::PrivateUse(XCHACHA20_POLY1305)); - let encoded_plaintext = if content_format == ContentFormat::Utf8 { - // Pad the data to a block size in order to hide plaintext length - let mut plaintext = plaintext.to_vec(); - crate::keys::utils::pad_bytes(&mut plaintext, XCHACHA20_TEXT_PAD_BLOCK_SIZE); - plaintext - } else { - plaintext.to_vec() - }; - let mut nonce = [0u8; xchacha20::NONCE_SIZE]; let cose_encrypt0 = coset::CoseEncrypt0Builder::new() .protected(protected_header) - .create_ciphertext(&encoded_plaintext, &[], |data, aad| { + .create_ciphertext(&plaintext, &[], |data, aad| { let ciphertext = crate::xchacha20::encrypt_xchacha20_poly1305(&(*key.enc_key).into(), data, aad); nonce = ciphertext.nonce(); @@ -95,6 +82,7 @@ pub(crate) fn decrypt_xchacha20_poly1305( ) -> Result<(Vec, ContentFormat), CryptoError> { let msg = coset::CoseEncrypt0::from_slice(cose_encrypt0_message) .map_err(|err| CryptoError::EncString(EncStringParseError::InvalidCoseEncoding(err)))?; + let Some(ref alg) = msg.protected.header.alg else { return Err(CryptoError::EncString( EncStringParseError::CoseMissingAlgorithm, @@ -103,8 +91,10 @@ pub(crate) fn decrypt_xchacha20_poly1305( if *alg != coset::Algorithm::PrivateUse(XCHACHA20_POLY1305) { return Err(CryptoError::WrongKeyType); } + let content_format = ContentFormat::try_from(&msg.protected.header) + .map_err(|_| CryptoError::EncString(EncStringParseError::CoseMissingContentType))?; - let decrypted_message = msg.decrypt(&[], |data, aad| { + let mut decrypted_message = msg.decrypt(&[], |data, aad| { let nonce = msg.unprotected.iv.as_slice(); crate::xchacha20::decrypt_xchacha20_poly1305( nonce @@ -116,37 +106,13 @@ pub(crate) fn decrypt_xchacha20_poly1305( ) })?; - if let Some(ref content_type) = msg.protected.header.content_type { - match content_type { - ContentType::Text(format) if format == CONTENT_TYPE_PADDED_UTF8 => { - if *content_type == ContentType::Text(CONTENT_TYPE_PADDED_UTF8.to_string()) { - // Unpad the data to get the original plaintext - let data = crate::keys::utils::unpad_bytes(&decrypted_message) - .map_err(|_| CryptoError::InvalidPadding)?; - return Ok((data.to_vec(), ContentFormat::Utf8)); - } - } - ContentType::Assigned(content_format) - if *content_format == CoapContentFormat::Pkcs8 => - { - return Ok((decrypted_message.to_vec(), ContentFormat::Pkcs8)); - } - ContentType::Assigned(content_format) - if *content_format == CoapContentFormat::CoseKey => - { - return Ok((decrypted_message.to_vec(), ContentFormat::CoseKey)); - } - ContentType::Assigned(content_format) - if *content_format == CoapContentFormat::OctetStream => - { - return Ok((decrypted_message.to_vec(), ContentFormat::OctetStream)); - } - _ => {} - } + if should_pad_content(&content_format) { + // Unpad the data to get the original plaintext + let data = crate::keys::utils::unpad_bytes(&mut decrypted_message)?; + return Ok((data.to_vec(), content_format)); } - Err(CryptoError::EncString( - EncStringParseError::CoseMissingContentType, - )) + + return Ok((decrypted_message, content_format)); } const SYMMETRIC_KEY: Label = Label::Int(iana::SymmetricKeyParameter::K as i64); @@ -187,6 +153,48 @@ impl TryFrom<&coset::CoseKey> for SymmetricCryptoKey { } } +impl From for coset::Header { + fn from(format: ContentFormat) -> Self { + let header = coset::HeaderBuilder::new(); + let header = match format { + ContentFormat::Utf8 => header.content_type(CONTENT_TYPE_PADDED_UTF8.to_string()), + ContentFormat::Pkcs8 => header.content_format(CoapContentFormat::Pkcs8), + ContentFormat::CoseKey => header.content_format(CoapContentFormat::CoseKey), + ContentFormat::OctetStream => header.content_format(CoapContentFormat::OctetStream), + }; + let mut header = header.build(); + // This should be adjusted to use the builder pattern once implemented in coset. + // The related coset upstream issue is: + // https://github.com/google/coset/issues/105 + header.alg = Some(coset::Algorithm::PrivateUse(XCHACHA20_POLY1305)); + header + } +} + +impl TryFrom<&coset::Header> for ContentFormat { + type Error = CryptoError; + + fn try_from(header: &coset::Header) -> Result { + match header.content_type.as_ref() { + Some(ContentType::Text(format)) if format == CONTENT_TYPE_PADDED_UTF8 => { + Ok(ContentFormat::Utf8) + } + Some(ContentType::Assigned(CoapContentFormat::Pkcs8)) => Ok(ContentFormat::Pkcs8), + Some(ContentType::Assigned(CoapContentFormat::CoseKey)) => Ok(ContentFormat::CoseKey), + Some(ContentType::Assigned(CoapContentFormat::OctetStream)) => { + Ok(ContentFormat::OctetStream) + } + _ => Err(CryptoError::EncString( + EncStringParseError::CoseMissingContentType, + )), + } + } +} + +fn should_pad_content(format: &ContentFormat) -> bool { + matches!(format, ContentFormat::Utf8) +} + #[cfg(test)] mod test { use super::*; From acb6d12e8760e4364542af4f29e62bd82df472a9 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 29 May 2025 13:40:43 +0200 Subject: [PATCH 024/124] Rename to PrimitiveEncryptableWithoutContentFormat --- crates/bitwarden-crypto/src/lib.rs | 4 +-- .../src/traits/encryptable.rs | 33 +++++++++++++------ crates/bitwarden-crypto/src/traits/mod.rs | 5 ++- crates/bitwarden-send/src/send.rs | 3 +- crates/bitwarden-send/src/send_client.rs | 4 ++- .../bitwarden-vault/src/cipher/attachment.rs | 4 +-- .../src/pure_crypto.rs | 4 +-- 7 files changed, 38 insertions(+), 19 deletions(-) diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index 2727716d4..2158779a4 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -35,8 +35,8 @@ mod traits; pub use cose::ContentFormat; mod xchacha20; pub use traits::{ - CompositeEncryptable, Decryptable, Encryptable, IdentifyKey, KeyId, KeyIds, - PrimitiveEncryptableWithContentType, + CompositeEncryptable, Decryptable, IdentifyKey, KeyId, KeyIds, + PrimitiveEncryptableWithContentType, PrimitiveEncryptableWithoutContentType, }; pub use zeroizing_alloc::ZeroAlloc as ZeroizingAllocator; diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index 381b7b394..d70daebcb 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -82,8 +82,8 @@ impl PrimitiveEncryptableWithContentType` - and -/// encrypts it into the output value. -pub trait Encryptable { +/// encrypts it into the output value. The caller must specify the content format. +pub trait PrimitiveEncryptableWithoutContentType { fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -92,7 +92,9 @@ pub trait Encryptable { ) -> Result; } -impl Encryptable for &[u8] { +impl PrimitiveEncryptableWithoutContentType + for &[u8] +{ fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -103,7 +105,9 @@ impl Encryptable for &[u8] { } } -impl Encryptable for Vec { +impl PrimitiveEncryptableWithoutContentType + for Vec +{ fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -114,8 +118,12 @@ impl Encryptable for Vec { } } -impl, Output> - Encryptable> for Option +impl< + Ids: KeyIds, + Key: KeyId, + T: PrimitiveEncryptableWithoutContentType, + Output, + > PrimitiveEncryptableWithoutContentType> for Option { fn encrypt( &self, @@ -129,8 +137,12 @@ impl, Output> } } -impl, Output> - Encryptable> for Vec +impl< + Ids: KeyIds, + Key: KeyId, + T: PrimitiveEncryptableWithoutContentType, + Output, + > PrimitiveEncryptableWithoutContentType> for Vec { fn encrypt( &self, @@ -147,8 +159,9 @@ impl, Output> #[cfg(test)] mod tests { use crate::{ - cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, - KeyStore, PrimitiveEncryptableWithContentType, SymmetricCryptoKey, + cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, KeyStore, + PrimitiveEncryptableWithContentType, PrimitiveEncryptableWithoutContentType, + SymmetricCryptoKey, }; fn test_store() -> KeyStore { diff --git a/crates/bitwarden-crypto/src/traits/mod.rs b/crates/bitwarden-crypto/src/traits/mod.rs index 9f9105645..e2a9f89fa 100644 --- a/crates/bitwarden-crypto/src/traits/mod.rs +++ b/crates/bitwarden-crypto/src/traits/mod.rs @@ -1,5 +1,8 @@ mod encryptable; -pub use encryptable::{CompositeEncryptable, Encryptable, PrimitiveEncryptableWithContentType}; +pub use encryptable::{ + CompositeEncryptable, PrimitiveEncryptableWithContentType, + PrimitiveEncryptableWithoutContentType, +}; mod decryptable; pub use decryptable::Decryptable; diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index 2d81c0206..f9823e656 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -9,7 +9,8 @@ use bitwarden_core::{ }; use bitwarden_crypto::{ generate_random_bytes, CompositeEncryptable, ContentFormat, CryptoError, Decryptable, - EncString, Encryptable, IdentifyKey, KeyStoreContext, PrimitiveEncryptableWithContentType, + EncString, IdentifyKey, KeyStoreContext, PrimitiveEncryptableWithContentType, + PrimitiveEncryptableWithoutContentType, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-send/src/send_client.rs b/crates/bitwarden-send/src/send_client.rs index 8095ce95c..c1dd2c1dc 100644 --- a/crates/bitwarden-send/src/send_client.rs +++ b/crates/bitwarden-send/src/send_client.rs @@ -1,7 +1,9 @@ use std::path::Path; use bitwarden_core::Client; -use bitwarden_crypto::{ContentFormat, Decryptable, EncString, Encryptable, IdentifyKey}; +use bitwarden_crypto::{ + ContentFormat, Decryptable, EncString, IdentifyKey, PrimitiveEncryptableWithoutContentType, +}; use thiserror::Error; use crate::{Send, SendListView, SendView}; diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index 75c54a208..214374d78 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,7 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, Encryptable, - IdentifyKey, KeyStoreContext, PrimitiveEncryptableWithContentType, + CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, IdentifyKey, + KeyStoreContext, PrimitiveEncryptableWithContentType, PrimitiveEncryptableWithoutContentType, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 7c81a034c..b8cc93496 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -3,8 +3,8 @@ use std::str::FromStr; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ AsymmetricCryptoKey, AsymmetricPublicCryptoKey, ContentFormat, CryptoError, Decryptable, - EncString, Encryptable, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, - SymmetricCryptoKey, UnsignedSharedKey, + EncString, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, + PrimitiveEncryptableWithoutContentType, SymmetricCryptoKey, UnsignedSharedKey, }; use wasm_bindgen::prelude::*; From 723ec6340b47d32a4bbac99b4b8ac7c60ef5f3a0 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 29 May 2025 13:41:44 +0200 Subject: [PATCH 025/124] Rename to PrimitiveEncryptable --- crates/bitwarden-crypto/src/lib.rs | 4 +-- .../src/traits/encryptable.rs | 29 +++++-------------- crates/bitwarden-crypto/src/traits/mod.rs | 3 +- crates/bitwarden-send/src/send.rs | 4 +-- crates/bitwarden-send/src/send_client.rs | 4 +-- .../bitwarden-vault/src/cipher/attachment.rs | 2 +- .../src/pure_crypto.rs | 4 +-- 7 files changed, 17 insertions(+), 33 deletions(-) diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index 2158779a4..cc57b6346 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -35,8 +35,8 @@ mod traits; pub use cose::ContentFormat; mod xchacha20; pub use traits::{ - CompositeEncryptable, Decryptable, IdentifyKey, KeyId, KeyIds, - PrimitiveEncryptableWithContentType, PrimitiveEncryptableWithoutContentType, + CompositeEncryptable, Decryptable, IdentifyKey, KeyId, KeyIds, PrimitiveEncryptable, + PrimitiveEncryptableWithContentType, }; pub use zeroizing_alloc::ZeroAlloc as ZeroizingAllocator; diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index d70daebcb..3f711152f 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -83,7 +83,7 @@ impl PrimitiveEncryptableWithContentType` - and /// encrypts it into the output value. The caller must specify the content format. -pub trait PrimitiveEncryptableWithoutContentType { +pub trait PrimitiveEncryptable { fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -92,9 +92,7 @@ pub trait PrimitiveEncryptableWithoutContentType Result; } -impl PrimitiveEncryptableWithoutContentType - for &[u8] -{ +impl PrimitiveEncryptable for &[u8] { fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -105,9 +103,7 @@ impl PrimitiveEncryptableWithoutContentType PrimitiveEncryptableWithoutContentType - for Vec -{ +impl PrimitiveEncryptable for Vec { fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -118,12 +114,8 @@ impl PrimitiveEncryptableWithoutContentType, - Output, - > PrimitiveEncryptableWithoutContentType> for Option +impl, Output> + PrimitiveEncryptable> for Option { fn encrypt( &self, @@ -137,12 +129,8 @@ impl< } } -impl< - Ids: KeyIds, - Key: KeyId, - T: PrimitiveEncryptableWithoutContentType, - Output, - > PrimitiveEncryptableWithoutContentType> for Vec +impl, Output> + PrimitiveEncryptable> for Vec { fn encrypt( &self, @@ -160,8 +148,7 @@ impl< mod tests { use crate::{ cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, KeyStore, - PrimitiveEncryptableWithContentType, PrimitiveEncryptableWithoutContentType, - SymmetricCryptoKey, + PrimitiveEncryptable, PrimitiveEncryptableWithContentType, SymmetricCryptoKey, }; fn test_store() -> KeyStore { diff --git a/crates/bitwarden-crypto/src/traits/mod.rs b/crates/bitwarden-crypto/src/traits/mod.rs index e2a9f89fa..8a6566ce4 100644 --- a/crates/bitwarden-crypto/src/traits/mod.rs +++ b/crates/bitwarden-crypto/src/traits/mod.rs @@ -1,7 +1,6 @@ mod encryptable; pub use encryptable::{ - CompositeEncryptable, PrimitiveEncryptableWithContentType, - PrimitiveEncryptableWithoutContentType, + CompositeEncryptable, PrimitiveEncryptable, PrimitiveEncryptableWithContentType, }; mod decryptable; pub use decryptable::Decryptable; diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index f9823e656..8278e673a 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -9,8 +9,8 @@ use bitwarden_core::{ }; use bitwarden_crypto::{ generate_random_bytes, CompositeEncryptable, ContentFormat, CryptoError, Decryptable, - EncString, IdentifyKey, KeyStoreContext, PrimitiveEncryptableWithContentType, - PrimitiveEncryptableWithoutContentType, + EncString, IdentifyKey, KeyStoreContext, PrimitiveEncryptable, + PrimitiveEncryptableWithContentType, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-send/src/send_client.rs b/crates/bitwarden-send/src/send_client.rs index c1dd2c1dc..3ff8b56f2 100644 --- a/crates/bitwarden-send/src/send_client.rs +++ b/crates/bitwarden-send/src/send_client.rs @@ -1,9 +1,7 @@ use std::path::Path; use bitwarden_core::Client; -use bitwarden_crypto::{ - ContentFormat, Decryptable, EncString, IdentifyKey, PrimitiveEncryptableWithoutContentType, -}; +use bitwarden_crypto::{ContentFormat, Decryptable, EncString, IdentifyKey, PrimitiveEncryptable}; use thiserror::Error; use crate::{Send, SendListView, SendView}; diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index 214374d78..7c8a44fd2 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,7 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, IdentifyKey, - KeyStoreContext, PrimitiveEncryptableWithContentType, PrimitiveEncryptableWithoutContentType, + KeyStoreContext, PrimitiveEncryptable, PrimitiveEncryptableWithContentType, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index b8cc93496..f96c46b5c 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -3,8 +3,8 @@ use std::str::FromStr; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ AsymmetricCryptoKey, AsymmetricPublicCryptoKey, ContentFormat, CryptoError, Decryptable, - EncString, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, - PrimitiveEncryptableWithoutContentType, SymmetricCryptoKey, UnsignedSharedKey, + EncString, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, PrimitiveEncryptable, + SymmetricCryptoKey, UnsignedSharedKey, }; use wasm_bindgen::prelude::*; From 33da07b7405baaa7048e30f6761048b512ac60be Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 29 May 2025 13:54:39 +0200 Subject: [PATCH 026/124] Add documentation for the encryptable traits --- crates/bitwarden-crypto/src/traits/encryptable.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index 3f711152f..408e458c9 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -1,3 +1,17 @@ +//! This module defines traits for encrypting data. There are three categories here. +//! +//! Some (legacy) encryptables are made up of many small individually encrypted items. For instance, +//! a cipher is currently made up of many small `EncString`s and some further json objects that +//! themselves contain `EncString`s. The use of this is generally discouraged for new designs. +//! Still, this is generally the only trait that should be implemented outside of the crypto crate. +//! +//! Encrypting data directly, a content type must be provided, since an encrypted byte array alone +//! is not enough to tell the decryption code how to interpret the decrypted bytes. For this, there +//! are two traits, `PrimitiveEncryptable` and `PrimitiveEncryptableWithContentType`. The former +//! assumes that the caller provides the content format when encrypting. The latter is a convenience +//! trait that pre-defines the content format based on the type of data that is encrypted. This is +//! currently done exactly for `String` and `&str`, which are encrypted as UTF-8, + use crate::{store::KeyStoreContext, ContentFormat, CryptoError, EncString, KeyId, KeyIds}; /// An encryption operation that takes the input value and encrypts the fields on it recursively. From 1681df93a35b503c03559d8af585b7c21abbee37 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 2 Jun 2025 10:50:27 +0200 Subject: [PATCH 027/124] Fix clippy errors --- crates/bitwarden-crypto/src/cose.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 448118b17..4b8145127 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -94,7 +94,7 @@ pub(crate) fn decrypt_xchacha20_poly1305( let content_format = ContentFormat::try_from(&msg.protected.header) .map_err(|_| CryptoError::EncString(EncStringParseError::CoseMissingContentType))?; - let mut decrypted_message = msg.decrypt(&[], |data, aad| { + let decrypted_message = msg.decrypt(&[], |data, aad| { let nonce = msg.unprotected.iv.as_slice(); crate::xchacha20::decrypt_xchacha20_poly1305( nonce @@ -108,11 +108,11 @@ pub(crate) fn decrypt_xchacha20_poly1305( if should_pad_content(&content_format) { // Unpad the data to get the original plaintext - let data = crate::keys::utils::unpad_bytes(&mut decrypted_message)?; + let data = crate::keys::utils::unpad_bytes(&decrypted_message)?; return Ok((data.to_vec(), content_format)); } - return Ok((decrypted_message, content_format)); + Ok((decrypted_message, content_format)) } const SYMMETRIC_KEY: Label = Label::Int(iana::SymmetricKeyParameter::K as i64); From 0cd85fa29cef76ab0e7fd5070ecee52e6a7bf81d Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 2 Jun 2025 18:45:41 +0200 Subject: [PATCH 028/124] Fix docs --- crates/bitwarden-core/src/key_management/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/bitwarden-core/src/key_management/mod.rs b/crates/bitwarden-core/src/key_management/mod.rs index dd13ab21c..305019c59 100644 --- a/crates/bitwarden-core/src/key_management/mod.rs +++ b/crates/bitwarden-core/src/key_management/mod.rs @@ -6,7 +6,10 @@ //! - [KeyIds] is a helper type that combines both symmetric and asymmetric key identifiers. This is //! usually used in the type bounds of [KeyStore], //! [KeyStoreContext](bitwarden_crypto::KeyStoreContext), -//! [Encryptable](bitwarden_crypto::Encryptable) and [Decryptable](bitwarden_crypto::Encryptable). +//! [PrimitiveEncryptable](bitwarden_crypto::PrimitiveEncryptable), +//! [PrimitiveEncryptableWithContentType](bitwarden_crypto::PrimitiveEncryptableWithContentType), +//! [CompositeEncryptable](bitwarden_crypto::CompositeEncryptable), +//! and [Decryptable](bitwarden_crypto::Encryptable). use bitwarden_crypto::{key_ids, KeyStore, SymmetricCryptoKey}; key_ids! { From f91f0b8c136ef03b6c3fffd4afba8bc998c94328 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 2 Jun 2025 18:47:15 +0200 Subject: [PATCH 029/124] Cargo fmt --- crates/bitwarden-core/src/key_management/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bitwarden-core/src/key_management/mod.rs b/crates/bitwarden-core/src/key_management/mod.rs index 305019c59..57d53f5fd 100644 --- a/crates/bitwarden-core/src/key_management/mod.rs +++ b/crates/bitwarden-core/src/key_management/mod.rs @@ -8,8 +8,8 @@ //! [KeyStoreContext](bitwarden_crypto::KeyStoreContext), //! [PrimitiveEncryptable](bitwarden_crypto::PrimitiveEncryptable), //! [PrimitiveEncryptableWithContentType](bitwarden_crypto::PrimitiveEncryptableWithContentType), -//! [CompositeEncryptable](bitwarden_crypto::CompositeEncryptable), -//! and [Decryptable](bitwarden_crypto::Encryptable). +//! [CompositeEncryptable](bitwarden_crypto::CompositeEncryptable), and +//! [Decryptable](bitwarden_crypto::Encryptable). use bitwarden_crypto::{key_ids, KeyStore, SymmetricCryptoKey}; key_ids! { From 34ee00ed9d4763931bb8029782a5285eca41f02f Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 2 Jun 2025 18:56:48 +0200 Subject: [PATCH 030/124] Fix docs --- crates/bitwarden-crypto/src/store/context.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 8f62c5a85..71b661415 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -15,7 +15,8 @@ use crate::{ /// The context of a crypto operation using [super::KeyStore] /// /// This will usually be accessed from an implementation of [crate::Decryptable] or -/// [crate::Encryptable], [crate::PrimitiveEncryptableWithContentType], but can also be obtained +/// [crate::CompositeEncryptable], [crate::PrimitiveEncryptable], +/// [crate::PrimitiveEncryptableWithContentType], but can also be obtained /// through [super::KeyStore::context] /// /// This context contains access to the user keys stored in the [super::KeyStore] (sometimes From 485b6d87e45c9bd93fcdc80bc8399a53a98d7b9e Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 2 Jun 2025 19:03:28 +0200 Subject: [PATCH 031/124] Fix docs --- crates/bitwarden-crypto/src/traits/encryptable.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index 408e458c9..ca03c9c6c 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -15,8 +15,9 @@ use crate::{store::KeyStoreContext, ContentFormat, CryptoError, EncString, KeyId, KeyIds}; /// An encryption operation that takes the input value and encrypts the fields on it recursively. -/// Implementations should generally consist of calling [Encryptable::encrypt] for all the fields of -/// the type. +/// Implementations should generally consist of calling [PrimitiveEncryptable::encrypt] or +/// [PrimitiveEncryptableWithContentType::encrypt] for all the fields of the type. Sometimes, it is +/// necessary to call [CompositeEncryptable::encrypt_composite], if the object is not a flat struct. pub trait CompositeEncryptable { /// For a struct made up of many small encstrings, such as a cipher, this takes the struct /// and recursively encrypts all the fields / sub-structs. From f122ff07d033f11c1e234f4ae3659d5d785b9fd8 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 2 Jun 2025 19:09:26 +0200 Subject: [PATCH 032/124] Fix docs --- crates/bitwarden-core/src/key_management/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-core/src/key_management/mod.rs b/crates/bitwarden-core/src/key_management/mod.rs index 57d53f5fd..f7cdc939f 100644 --- a/crates/bitwarden-core/src/key_management/mod.rs +++ b/crates/bitwarden-core/src/key_management/mod.rs @@ -9,7 +9,7 @@ //! [PrimitiveEncryptable](bitwarden_crypto::PrimitiveEncryptable), //! [PrimitiveEncryptableWithContentType](bitwarden_crypto::PrimitiveEncryptableWithContentType), //! [CompositeEncryptable](bitwarden_crypto::CompositeEncryptable), and -//! [Decryptable](bitwarden_crypto::Encryptable). +//! [Decryptable](bitwarden_crypto::Decryptable). use bitwarden_crypto::{key_ids, KeyStore, SymmetricCryptoKey}; key_ids! { From f22531c3d5eecc4506b25b8149ba109734fd8d0b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 4 Jun 2025 10:09:50 +0200 Subject: [PATCH 033/124] Update crates/bitwarden-crypto/src/keys/utils.rs Co-authored-by: Matt Gibson --- crates/bitwarden-crypto/src/keys/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/keys/utils.rs b/crates/bitwarden-crypto/src/keys/utils.rs index 35f39a1d8..74a6cfa82 100644 --- a/crates/bitwarden-crypto/src/keys/utils.rs +++ b/crates/bitwarden-crypto/src/keys/utils.rs @@ -27,7 +27,7 @@ pub(crate) fn pad_bytes(bytes: &mut Vec, min_length: usize) { } /// Unpads bytes that is padded using the PKCS7-like padding defined by [pad_bytes]. -/// The last N bytes of the padded bytes all have the value N. +/// The last N bytes of the padded bytes all have the value N. Minimum of 1 padding byte. /// For example, padded to size 4, the value 0,0 becomes 0,0,2,2. pub(crate) fn unpad_bytes(padded_bytes: &[u8]) -> Result<&[u8], CryptoError> { let pad_len = *padded_bytes.last().ok_or(CryptoError::InvalidPadding)? as usize; From ba10631a360b9cce9b6c95662e6a0b238be2f154 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 4 Jun 2025 10:09:58 +0200 Subject: [PATCH 034/124] Update crates/bitwarden-crypto/src/keys/utils.rs Co-authored-by: Matt Gibson --- crates/bitwarden-crypto/src/keys/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/keys/utils.rs b/crates/bitwarden-crypto/src/keys/utils.rs index 74a6cfa82..97dc6a1be 100644 --- a/crates/bitwarden-crypto/src/keys/utils.rs +++ b/crates/bitwarden-crypto/src/keys/utils.rs @@ -17,7 +17,7 @@ pub(super) fn stretch_key(key: &Pin>>) -> Result, min_length: usize) { // at least 1 byte of padding is required From 69d8c57e87dba41908a3a7d2e69c949ae10fa9f7 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 4 Jun 2025 10:16:40 +0200 Subject: [PATCH 035/124] Update crates/bitwarden-crypto/src/keys/utils.rs Co-authored-by: Matt Gibson --- crates/bitwarden-crypto/src/keys/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/keys/utils.rs b/crates/bitwarden-crypto/src/keys/utils.rs index 97dc6a1be..e81bf61d1 100644 --- a/crates/bitwarden-crypto/src/keys/utils.rs +++ b/crates/bitwarden-crypto/src/keys/utils.rs @@ -34,7 +34,7 @@ pub(crate) fn unpad_bytes(padded_bytes: &[u8]) -> Result<&[u8], CryptoError> { if pad_len >= padded_bytes.len() { return Err(CryptoError::InvalidPadding); } - Ok(padded_bytes[..padded_bytes.len() - pad_len].as_ref()) + Ok(padded_bytes[..(padded_bytes.len() - pad_len)].as_ref()) } #[cfg(test)] From 42dccb0b46c2c687666dbf89f07c11807c8fcf26 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 6 Jun 2025 17:11:36 +0200 Subject: [PATCH 036/124] Add docs --- crates/bitwarden-crypto/src/cose.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 4b8145127..8b42a25da 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -33,9 +33,13 @@ const CONTENT_TYPE_PADDED_UTF8: &str = "application/utf8-padded"; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] pub enum ContentFormat { + /// UTF-8 encoded text Utf8, + /// Pkcs8 private key DER Pkcs8, + /// COSE serialized CoseKey CoseKey, + /// Stream of bytes OctetStream, } From 9eceb322b8c2aba60c53c20f78e50c006480e03e Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 16 Jun 2025 18:03:58 +0200 Subject: [PATCH 037/124] Cargo fmt --- .../bitwarden-core/src/key_management/crypto.rs | 8 +++----- .../bitwarden-crypto/src/enc_string/symmetric.rs | 3 +-- crates/bitwarden-crypto/src/keys/device_key.rs | 5 ++--- crates/bitwarden-crypto/src/store/context.rs | 16 ++++++---------- .../bitwarden-crypto/src/traits/encryptable.rs | 7 +++---- .../bitwarden-wasm-internal/src/pure_crypto.rs | 10 ++++------ 6 files changed, 19 insertions(+), 30 deletions(-) diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index 634a8e278..2db719570 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -8,11 +8,9 @@ use std::collections::HashMap; use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ - AsymmetricCryptoKey, AsymmetricCryptoKey, ContentFormat, CoseSerializable, CryptoError, - CryptoError, EncString, EncString, Encryptable, Kdf, Kdf, KeyDecryptable, KeyDecryptable, - KeyEncryptable, KeyEncryptable, MasterKey, MasterKey, SignatureAlgorithm, SignedPublicKey, - SigningKey, SymmetricCryptoKey, SymmetricCryptoKey, UnsignedSharedKey, UnsignedSharedKey, - UserKey, UserKey, + AsymmetricCryptoKey, ContentFormat, CoseSerializable, CryptoError, EncString, Encryptable, Kdf, + KeyDecryptable, KeyEncryptable, MasterKey, SignatureAlgorithm, SignedPublicKey, SigningKey, + SymmetricCryptoKey, UnsignedSharedKey, UserKey, }; use bitwarden_error::bitwarden_error; use schemars::JsonSchema; diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index 83b19b2ec..c3b03bb3b 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -8,8 +8,7 @@ use super::{check_length, from_b64, from_b64_vec, split_enc_string}; use crate::{ error::{CryptoError, EncStringParseError, Result, UnsupportedOperation}, util::FromStrVisitor, - Aes256CbcHmacKey, Aes256CbcHmacKey, ContentFormat, KeyDecryptable, KeyDecryptable, - KeyEncryptable, KeyEncryptable, SymmetricCryptoKey, SymmetricCryptoKey, XChaCha20Poly1305Key, + Aes256CbcHmacKey, ContentFormat, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, XChaCha20Poly1305Key, }; diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index 27f20b59a..657ed6e49 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -1,8 +1,7 @@ use super::{AsymmetricCryptoKey, PublicKeyEncryptionAlgorithm}; use crate::{ - error::Result, error::Result, AsymmetricCryptoKey, ContentFormat, CryptoError, CryptoError, - EncString, EncString, KeyDecryptable, KeyDecryptable, KeyEncryptable, KeyEncryptable, - SymmetricCryptoKey, SymmetricCryptoKey, UnsignedSharedKey, UnsignedSharedKey, + error::Result, AsymmetricCryptoKey, ContentFormat, CryptoError, EncString, KeyDecryptable, + KeyEncryptable, SymmetricCryptoKey, UnsignedSharedKey, }; /// Device Key diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index d6ea4d78d..627d38970 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -8,12 +8,10 @@ use zeroize::Zeroizing; use super::KeyStoreInner; use crate::{ - derive_shareable_key, derive_shareable_key, error::UnsupportedOperation, - error::UnsupportedOperation, signing, store::backend::StoreBackend, - store::backend::StoreBackend, AsymmetricCryptoKey, AsymmetricCryptoKey, ContentFormat, - CryptoError, CryptoError, EncString, EncString, KeyId, KeyId, KeyIds, KeyIds, Result, Result, - Signature, SignatureAlgorithm, SignedObject, SignedPublicKey, SignedPublicKeyMessage, - SigningKey, SymmetricCryptoKey, UnsignedSharedKey, + derive_shareable_key, error::UnsupportedOperation, signing, store::backend::StoreBackend, + AsymmetricCryptoKey, ContentFormat, CryptoError, EncString, KeyId, KeyIds, Result, Signature, + SignatureAlgorithm, SignedObject, SignedPublicKey, SignedPublicKeyMessage, SigningKey, + SymmetricCryptoKey, UnsignedSharedKey, }; /// The context of a crypto operation using [super::KeyStore] @@ -525,15 +523,13 @@ mod tests { use serde::{Deserialize, Serialize}; use crate::{ - store::{tests::DataView, KeyStore}, store::{ tests::{Data, DataView}, KeyStore, }, traits::tests::{TestIds, TestSigningKey, TestSymmKey}, - traits::tests::{TestIds, TestSymmKey}, - CompositeEncryptable, CryptoError, Decryptable, Decryptable, Encryptable, - SignatureAlgorithm, SigningKey, SigningNamespace, SymmetricCryptoKey, SymmetricCryptoKey, + CompositeEncryptable, CryptoError, Decryptable, Encryptable, SignatureAlgorithm, + SigningKey, SigningNamespace, SymmetricCryptoKey, }; #[test] diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index cca4229a1..f73f64b0b 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -164,10 +164,9 @@ impl, Output> #[cfg(test)] mod tests { use crate::{ - cose::ContentFormat, traits::tests::*, traits::tests::*, AsymmetricCryptoKey, - AsymmetricCryptoKey, Decryptable, Decryptable, Encryptable, KeyStore, KeyStore, - PrimitiveEncryptable, PrimitiveEncryptableWithContentType, PublicKeyEncryptionAlgorithm, - SymmetricCryptoKey, SymmetricCryptoKey, + cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, + KeyStore, PrimitiveEncryptable, PrimitiveEncryptableWithContentType, + PublicKeyEncryptionAlgorithm, SymmetricCryptoKey, }; fn test_store() -> KeyStore { diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 62e369f86..f7fb22845 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -2,12 +2,10 @@ use std::str::FromStr; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - AsymmetricCryptoKey, AsymmetricCryptoKey, AsymmetricPublicCryptoKey, AsymmetricPublicCryptoKey, - ContentFormat, CoseSerializable, CryptoError, CryptoError, Decryptable, Decryptable, EncString, - EncString, Encryptable, Kdf, Kdf, KeyDecryptable, KeyDecryptable, KeyEncryptable, - KeyEncryptable, KeyStore, KeyStore, MasterKey, MasterKey, PrimitiveEncryptable, - SignatureAlgorithm, SignedPublicKey, SigningKey, SymmetricCryptoKey, SymmetricCryptoKey, - UnsignedSharedKey, UnsignedSharedKey, VerifyingKey, + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, ContentFormat, CoseSerializable, CryptoError, + Decryptable, EncString, Encryptable, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, + PrimitiveEncryptable, SignatureAlgorithm, SignedPublicKey, SigningKey, SymmetricCryptoKey, + UnsignedSharedKey, VerifyingKey, }; use wasm_bindgen::prelude::*; From f30c3ce0b0bf7144388154a84e9c4aed4469ec67 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 16 Jun 2025 18:05:49 +0200 Subject: [PATCH 038/124] Apply fixes --- crates/bitwarden-crypto/src/cose.rs | 11 +++++------ crates/bitwarden-crypto/src/keys/device_key.rs | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index d80c253ec..6369e6439 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -57,18 +57,17 @@ pub(crate) fn encrypt_xchacha20_poly1305( ) -> Result, CryptoError> { let mut plaintext = plaintext.to_vec(); - let mut header_builder: coset::HeaderBuilder = content_format.into(); + let header_builder: coset::HeaderBuilder = content_format.into(); let mut protected_header = header_builder.key_id(key.key_id.to_vec()).build(); + // This should be adjusted to use the builder pattern once implemented in coset. + // The related coset upstream issue is: + // https://github.com/google/coset/issues/105 protected_header.alg = Some(coset::Algorithm::PrivateUse(XCHACHA20_POLY1305)); if should_pad_content(&content_format) { // Pad the data to a block size in order to hide plaintext length crate::keys::utils::pad_bytes(&mut plaintext, XCHACHA20_TEXT_PAD_BLOCK_SIZE); } - // This should be adjusted to use the builder pattern once implemented in coset. - // The related coset upstream issue is: - // https://github.com/google/coset/issues/105 - protected_header.alg = Some(coset::Algorithm::PrivateUse(XCHACHA20_POLY1305)); let mut nonce = [0u8; xchacha20::NONCE_SIZE]; let cose_encrypt0 = coset::CoseEncrypt0Builder::new() @@ -171,7 +170,7 @@ impl TryFrom<&coset::CoseKey> for SymmetricCryptoKey { } } -impl From for coset::Header { +impl From for coset::HeaderBuilder { fn from(format: ContentFormat) -> Self { let header_builder = coset::HeaderBuilder::new(); let header_builder = match format { diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index 657ed6e49..d8e16b587 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -1,7 +1,7 @@ use super::{AsymmetricCryptoKey, PublicKeyEncryptionAlgorithm}; use crate::{ - error::Result, AsymmetricCryptoKey, ContentFormat, CryptoError, EncString, KeyDecryptable, - KeyEncryptable, SymmetricCryptoKey, UnsignedSharedKey, + error::Result, ContentFormat, CryptoError, EncString, KeyDecryptable, KeyEncryptable, + SymmetricCryptoKey, UnsignedSharedKey, }; /// Device Key From 5f4dc3a874ef915ab908e75b49e48e249f485449 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 16 Jun 2025 18:06:42 +0200 Subject: [PATCH 039/124] Cleanup --- crates/bitwarden-crypto/src/cose.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 6369e6439..ecfe64441 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -173,7 +173,8 @@ impl TryFrom<&coset::CoseKey> for SymmetricCryptoKey { impl From for coset::HeaderBuilder { fn from(format: ContentFormat) -> Self { let header_builder = coset::HeaderBuilder::new(); - let header_builder = match format { + + match format { ContentFormat::Utf8 => { header_builder.content_type(CONTENT_TYPE_PADDED_UTF8.to_string()) } @@ -182,8 +183,7 @@ impl From for coset::HeaderBuilder { ContentFormat::OctetStream => { header_builder.content_format(CoapContentFormat::OctetStream) } - }; - header_builder + } } } From 7b17ca332be8abe4bb61628b07f0e28540a975e9 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 16 Jun 2025 18:06:55 +0200 Subject: [PATCH 040/124] Cleanup --- crates/bitwarden-crypto/src/store/context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 627d38970..d81a1ec59 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -528,8 +528,8 @@ mod tests { KeyStore, }, traits::tests::{TestIds, TestSigningKey, TestSymmKey}, - CompositeEncryptable, CryptoError, Decryptable, Encryptable, SignatureAlgorithm, - SigningKey, SigningNamespace, SymmetricCryptoKey, + CompositeEncryptable, CryptoError, Decryptable, SignatureAlgorithm, SigningKey, + SigningNamespace, SymmetricCryptoKey, }; #[test] From 38c89457ec67405ff57e51eefc70998ba261c441 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 16 Jun 2025 18:07:43 +0200 Subject: [PATCH 041/124] Cleanup --- crates/bitwarden-core/src/key_management/crypto.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index 2db719570..037dc4ec3 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -8,9 +8,9 @@ use std::collections::HashMap; use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ - AsymmetricCryptoKey, ContentFormat, CoseSerializable, CryptoError, EncString, Encryptable, Kdf, - KeyDecryptable, KeyEncryptable, MasterKey, SignatureAlgorithm, SignedPublicKey, SigningKey, - SymmetricCryptoKey, UnsignedSharedKey, UserKey, + AsymmetricCryptoKey, ContentFormat, CoseSerializable, CryptoError, EncString, Kdf, + KeyDecryptable, KeyEncryptable, MasterKey, PrimitiveEncryptable, SignatureAlgorithm, + SignedPublicKey, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey, }; use bitwarden_error::bitwarden_error; use schemars::JsonSchema; @@ -604,9 +604,11 @@ pub fn make_user_signing_keys_for_enrollment( verifying_key: STANDARD.encode(signature_keypair.to_verifying_key().to_cose()), // This needs to be changed to use the correct COSE content format before rolling out to // users: https://bitwarden.atlassian.net/browse/PM-22189 - signing_key: signature_keypair - .to_cose() - .encrypt(&mut ctx, SymmetricKeyId::User)?, + signing_key: signature_keypair.to_cose().encrypt( + &mut ctx, + SymmetricKeyId::User, + ContentFormat::CoseKey, + )?, signed_public_key, }) } From b5dd86239aff7f5a68dd6bcf7d22995418a64ec5 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 16 Jun 2025 18:08:21 +0200 Subject: [PATCH 042/124] Apply more fixes --- crates/bitwarden-crypto/src/cose.rs | 7 +++++-- crates/bitwarden-crypto/src/traits/encryptable.rs | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index ecfe64441..ee6fd774e 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -173,7 +173,7 @@ impl TryFrom<&coset::CoseKey> for SymmetricCryptoKey { impl From for coset::HeaderBuilder { fn from(format: ContentFormat) -> Self { let header_builder = coset::HeaderBuilder::new(); - + match format { ContentFormat::Utf8 => { header_builder.content_type(CONTENT_TYPE_PADDED_UTF8.to_string()) @@ -303,7 +303,10 @@ mod test { enc_key: Box::pin(*GenericArray::from_slice(&KEY_DATA)), }; let decrypted = decrypt_xchacha20_poly1305(TEST_VECTOR_COSE_ENCRYPT0, &key).unwrap(); - assert_eq!(decrypted, TEST_VECTOR_PLAINTEXT); + assert_eq!( + decrypted, + (TEST_VECTOR_PLAINTEXT.to_vec(), ContentFormat::OctetStream) + ); } #[test] diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index f73f64b0b..05386dbd2 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -164,9 +164,9 @@ impl, Output> #[cfg(test)] mod tests { use crate::{ - cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, Encryptable, - KeyStore, PrimitiveEncryptable, PrimitiveEncryptableWithContentType, - PublicKeyEncryptionAlgorithm, SymmetricCryptoKey, + cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, KeyStore, + PrimitiveEncryptable, PrimitiveEncryptableWithContentType, PublicKeyEncryptionAlgorithm, + SymmetricCryptoKey, }; fn test_store() -> KeyStore { From 47c7764c684848fb0e7c4b659640cded95cbd2e6 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 16 Jun 2025 18:09:51 +0200 Subject: [PATCH 043/124] Apply fixes --- crates/bitwarden-crypto/src/store/context.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index d81a1ec59..1ed6eb3fa 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -669,6 +669,7 @@ mod tests { assert_eq!(unwrapped_key_4, key_xchacha_4_id); } + #[test] fn test_signing() { let store: KeyStore = KeyStore::default(); From b009c818192e8492b0994e8c4a8e7390704b606b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 16 Jun 2025 18:10:11 +0200 Subject: [PATCH 044/124] Apply fixes --- 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 f7fb22845..08b8c968f 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ AsymmetricCryptoKey, AsymmetricPublicCryptoKey, ContentFormat, CoseSerializable, CryptoError, - Decryptable, EncString, Encryptable, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, + Decryptable, EncString, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, PrimitiveEncryptable, SignatureAlgorithm, SignedPublicKey, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, VerifyingKey, }; From c9f6111cb8b1d2774f3eab4a69e9ef8fcee6f8c8 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 17 Jun 2025 10:29:29 +0200 Subject: [PATCH 045/124] Update test vector to include content type --- crates/bitwarden-crypto/src/cose.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index ee6fd774e..102c6e7cc 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -232,11 +232,11 @@ mod test { ]; const TEST_VECTOR_PLAINTEXT: &[u8] = b"Message test vector"; const TEST_VECTOR_COSE_ENCRYPT0: &[u8] = &[ - 131, 88, 25, 162, 1, 58, 0, 1, 17, 111, 4, 80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 161, 5, 88, 24, 39, 48, 159, 48, 215, 77, 21, 100, 241, 209, 216, 65, 99, 221, - 83, 63, 118, 204, 200, 175, 126, 202, 53, 33, 88, 35, 218, 136, 132, 223, 131, 246, 169, - 120, 134, 49, 56, 173, 169, 133, 232, 109, 248, 101, 59, 226, 90, 97, 210, 181, 76, 68, - 158, 159, 94, 65, 67, 23, 112, 253, 83, + 131, 88, 28, 163, 1, 58, 0, 1, 17, 111, 3, 24, 42, 4, 80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 161, 5, 88, 24, 78, 20, 28, 157, 180, 246, 131, 220, 82, 104, 72, 73, + 75, 43, 69, 139, 216, 167, 145, 220, 67, 168, 144, 173, 88, 35, 127, 234, 194, 83, 189, + 172, 65, 29, 156, 73, 98, 87, 231, 87, 129, 15, 235, 127, 125, 97, 211, 51, 212, 211, 2, + 13, 36, 123, 53, 12, 31, 191, 40, 13, 175, ]; #[test] From b9b0f6ec63acbefd4b2ed3843c6532a5e1468734 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 17 Jun 2025 11:28:36 +0200 Subject: [PATCH 046/124] Add bitwarden legacy content type --- crates/bitwarden-crypto/src/cose.rs | 18 +++++++++++++++++- crates/bitwarden-crypto/src/store/context.rs | 10 ++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 102c6e7cc..ba3550e79 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -23,7 +23,11 @@ use crate::{ /// the draft was never published as an RFC, we use a private-use value for the algorithm. pub(crate) const XCHACHA20_POLY1305: i64 = -70000; const XCHACHA20_TEXT_PAD_BLOCK_SIZE: usize = 32; -const CONTENT_TYPE_PADDED_UTF8: &str = "application/utf8-padded"; + +// Note: These are in the "unregistered" tree: https://datatracker.ietf.org/doc/html/rfc6838#section-3.4 +// These are only used within Bitwarden, and not meant for exchange with other systems. +const CONTENT_TYPE_PADDED_UTF8: &str = "application/x.bitwarden.utf8-padded"; +const CONTENT_TYPE_BITWARDEN_LEGACY_KEY: &str = "application/x.bitwarden.legacy-key"; /// The content format describes the format of the contained bytes. Message encryption always /// happens on the byte level, and this allows determining what format the contained data has. For @@ -40,6 +44,12 @@ pub enum ContentFormat { Pkcs8, /// COSE serialized CoseKey CoseKey, + /// Bitwarden Legacy Key + /// There are three permissible byte values here: + /// - [u8; 32] - AES-CBC (no hmac) key. This is to be removed and banned. + /// - [u8; 64] - AES-CBC with HMAC key. This is the v1 userkey key type + /// - [u8; >64] - COSE key. Padded to be larger than 64 bytes. + BitwardenLegacyKey, /// Stream of bytes OctetStream, } @@ -180,6 +190,9 @@ impl From for coset::HeaderBuilder { } ContentFormat::Pkcs8 => header_builder.content_format(CoapContentFormat::Pkcs8), ContentFormat::CoseKey => header_builder.content_format(CoapContentFormat::CoseKey), + ContentFormat::BitwardenLegacyKey => { + header_builder.content_type(CONTENT_TYPE_BITWARDEN_LEGACY_KEY.to_string()) + } ContentFormat::OctetStream => { header_builder.content_format(CoapContentFormat::OctetStream) } @@ -195,6 +208,9 @@ impl TryFrom<&coset::Header> for ContentFormat { Some(ContentType::Text(format)) if format == CONTENT_TYPE_PADDED_UTF8 => { Ok(ContentFormat::Utf8) } + Some(ContentType::Text(format)) if format == CONTENT_TYPE_BITWARDEN_LEGACY_KEY => { + Ok(ContentFormat::BitwardenLegacyKey) + } Some(ContentType::Assigned(CoapContentFormat::Pkcs8)) => Ok(ContentFormat::Pkcs8), Some(ContentType::Assigned(CoapContentFormat::CoseKey)) => Ok(ContentFormat::CoseKey), Some(ContentType::Assigned(CoapContentFormat::OctetStream)) => { diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 1ed6eb3fa..78149935c 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -179,7 +179,9 @@ impl KeyStoreContext<'_, Ids> { let (content_bytes, content_format) = crate::cose::decrypt_xchacha20_poly1305(data, key)?; match content_format { - ContentFormat::OctetStream => SymmetricCryptoKey::try_from(content_bytes)?, + ContentFormat::BitwardenLegacyKey => { + SymmetricCryptoKey::try_from(content_bytes)? + } ContentFormat::CoseKey => SymmetricCryptoKey::try_from_cose(&content_bytes)?, _ => return Err(CryptoError::InvalidKey), } @@ -223,13 +225,13 @@ impl KeyStoreContext<'_, Ids> { ) => self.encrypt_data_with_symmetric_key( wrapping_key, key_to_wrap_instance.to_encoded().as_slice(), - ContentFormat::OctetStream, + ContentFormat::BitwardenLegacyKey, ), (XChaCha20Poly1305Key(_), Aes256CbcHmacKey(_) | Aes256CbcKey(_)) => self .encrypt_data_with_symmetric_key( wrapping_key, key_to_wrap_instance.to_encoded().as_slice(), - ContentFormat::OctetStream, + ContentFormat::BitwardenLegacyKey, ), (XChaCha20Poly1305Key(_), XChaCha20Poly1305Key(_)) => self .encrypt_data_with_symmetric_key( @@ -612,7 +614,7 @@ mod tests { } #[test] - fn test_wrap_unwrap_aes256_cbc_hmac() { + fn test_wrap_unwrap() { let store: KeyStore = KeyStore::default(); let mut ctx = store.context_mut(); From 67dd5e981916c1ab262e992cba4093171f07a83e Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 17 Jun 2025 11:40:43 +0200 Subject: [PATCH 047/124] Move content format to separate file --- crates/bitwarden-crypto/src/content_format.rs | 28 +++++++++++++++++ crates/bitwarden-crypto/src/cose.rs | 31 ++----------------- .../src/keys/key_encryptable.rs | 2 +- crates/bitwarden-crypto/src/lib.rs | 3 +- .../src/traits/encryptable.rs | 4 +-- 5 files changed, 35 insertions(+), 33 deletions(-) create mode 100644 crates/bitwarden-crypto/src/content_format.rs diff --git a/crates/bitwarden-crypto/src/content_format.rs b/crates/bitwarden-crypto/src/content_format.rs new file mode 100644 index 000000000..540f07117 --- /dev/null +++ b/crates/bitwarden-crypto/src/content_format.rs @@ -0,0 +1,28 @@ +use serde::{Deserialize, Serialize}; +#[cfg(feature = "wasm")] +use tsify_next::Tsify; + +/// The content format describes the format of the contained bytes. Message encryption always +/// happens on the byte level, and this allows determining what format the contained data has. For +/// instance, an `EncString` in most cases contains UTF-8 encoded text. In some cases it may contain +/// a Pkcs8 private key, or a COSE key. Specifically, for COSE keys, this allows distinguishing +/// between the old symmetric key format, represented as `ContentFormat::OctetStream`, and the new +/// COSE key format, represented as `ContentFormat::CoseKey`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] +pub enum ContentFormat { + /// UTF-8 encoded text + Utf8, + /// Pkcs8 private key DER + Pkcs8, + /// COSE serialized CoseKey + CoseKey, + /// Bitwarden Legacy Key + /// There are three permissible byte values here: + /// - [u8; 32] - AES-CBC (no hmac) key. This is to be removed and banned. + /// - [u8; 64] - AES-CBC with HMAC key. This is the v1 userkey key type + /// - [u8; >64] - COSE key. Padded to be larger than 64 bytes. + BitwardenLegacyKey, + /// Stream of bytes + OctetStream, +} diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index ba3550e79..3c955104b 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -15,7 +15,7 @@ use typenum::U32; use crate::{ error::{EncStringParseError, EncodingError}, - xchacha20, CryptoError, SymmetricCryptoKey, XChaCha20Poly1305Key, + xchacha20, ContentFormat, CryptoError, SymmetricCryptoKey, XChaCha20Poly1305Key, }; /// XChaCha20 is used over ChaCha20 @@ -29,31 +29,6 @@ const XCHACHA20_TEXT_PAD_BLOCK_SIZE: usize = 32; const CONTENT_TYPE_PADDED_UTF8: &str = "application/x.bitwarden.utf8-padded"; const CONTENT_TYPE_BITWARDEN_LEGACY_KEY: &str = "application/x.bitwarden.legacy-key"; -/// The content format describes the format of the contained bytes. Message encryption always -/// happens on the byte level, and this allows determining what format the contained data has. For -/// instance, an `EncString` in most cases contains UTF-8 encoded text. In some cases it may contain -/// a Pkcs8 private key, or a COSE key. Specifically, for COSE keys, this allows distinguishing -/// between the old symmetric key format, represented as `ContentFormat::OctetStream`, and the new -/// COSE key format, represented as `ContentFormat::CoseKey`. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] -pub enum ContentFormat { - /// UTF-8 encoded text - Utf8, - /// Pkcs8 private key DER - Pkcs8, - /// COSE serialized CoseKey - CoseKey, - /// Bitwarden Legacy Key - /// There are three permissible byte values here: - /// - [u8; 32] - AES-CBC (no hmac) key. This is to be removed and banned. - /// - [u8; 64] - AES-CBC with HMAC key. This is the v1 userkey key type - /// - [u8; >64] - COSE key. Padded to be larger than 64 bytes. - BitwardenLegacyKey, - /// Stream of bytes - OctetStream, -} - // Labels // /// The label used for the namespace ensuring strong domain separation when using signatures. @@ -208,9 +183,7 @@ impl TryFrom<&coset::Header> for ContentFormat { Some(ContentType::Text(format)) if format == CONTENT_TYPE_PADDED_UTF8 => { Ok(ContentFormat::Utf8) } - Some(ContentType::Text(format)) if format == CONTENT_TYPE_BITWARDEN_LEGACY_KEY => { - Ok(ContentFormat::BitwardenLegacyKey) - } + Some(ContentType::Assigned(CoapContentFormat::Pkcs8)) => Ok(ContentFormat::Pkcs8), Some(ContentType::Assigned(CoapContentFormat::CoseKey)) => Ok(ContentFormat::CoseKey), Some(ContentType::Assigned(CoapContentFormat::OctetStream)) => { diff --git a/crates/bitwarden-crypto/src/keys/key_encryptable.rs b/crates/bitwarden-crypto/src/keys/key_encryptable.rs index 063196e4a..ee57a31b9 100644 --- a/crates/bitwarden-crypto/src/keys/key_encryptable.rs +++ b/crates/bitwarden-crypto/src/keys/key_encryptable.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, hash::Hash, sync::Arc}; use rayon::prelude::*; use uuid::Uuid; -use crate::{cose::ContentFormat, error::Result, CryptoError, SymmetricCryptoKey}; +use crate::{error::Result, ContentFormat, CryptoError, SymmetricCryptoKey}; #[allow(missing_docs)] pub trait KeyContainer: Send + Sync { diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index efa3e4d6b..ef2736fb1 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -13,6 +13,8 @@ static ALLOC: ZeroizingAllocator = ZeroizingAllocator(std::alloc::System); mod aes; +mod content_format; +pub use content_format::ContentFormat; mod enc_string; pub use enc_string::{EncString, UnsignedSharedKey}; mod error; @@ -35,7 +37,6 @@ pub use cose::CoseSerializable; mod signing; pub use signing::*; mod traits; -pub use cose::ContentFormat; mod xchacha20; pub use traits::{ CompositeEncryptable, Decryptable, IdentifyKey, KeyId, KeyIds, PrimitiveEncryptable, diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index 05386dbd2..997461467 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -138,7 +138,7 @@ impl, Output> &self, ctx: &mut KeyStoreContext, key: Key, - content_format: crate::cose::ContentFormat, + content_format: crate::ContentFormat, ) -> Result, CryptoError> { self.as_ref() .map(|value| value.encrypt(ctx, key, content_format)) @@ -164,7 +164,7 @@ impl, Output> #[cfg(test)] mod tests { use crate::{ - cose::ContentFormat, traits::tests::*, AsymmetricCryptoKey, Decryptable, KeyStore, + traits::tests::*, AsymmetricCryptoKey, ContentFormat, Decryptable, KeyStore, PrimitiveEncryptable, PrimitiveEncryptableWithContentType, PublicKeyEncryptionAlgorithm, SymmetricCryptoKey, }; From 7635cd0442606c12e5ac6b4f8e14aac26221ab2f Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 17 Jun 2025 11:42:01 +0200 Subject: [PATCH 048/124] Remove unused import --- crates/bitwarden-crypto/src/cose.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 3c955104b..7deba44d2 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -8,9 +8,6 @@ use coset::{ CborSerializable, ContentType, Label, }; use generic_array::GenericArray; -use serde::{Deserialize, Serialize}; -#[cfg(feature = "wasm")] -use tsify_next::Tsify; use typenum::U32; use crate::{ From ec14e51e5ce1d0ce09593c364d6deb0c02d2ae41 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 17 Jun 2025 12:09:09 +0200 Subject: [PATCH 049/124] Fix missing parsing for content type --- crates/bitwarden-crypto/src/cose.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 7deba44d2..63b2df6aa 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -180,7 +180,9 @@ impl TryFrom<&coset::Header> for ContentFormat { Some(ContentType::Text(format)) if format == CONTENT_TYPE_PADDED_UTF8 => { Ok(ContentFormat::Utf8) } - + Some(ContentType::Text(format)) if format == CONTENT_TYPE_BITWARDEN_LEGACY_KEY => { + Ok(ContentFormat::BitwardenLegacyKey) + } Some(ContentType::Assigned(CoapContentFormat::Pkcs8)) => Ok(ContentFormat::Pkcs8), Some(ContentType::Assigned(CoapContentFormat::CoseKey)) => Ok(ContentFormat::CoseKey), Some(ContentType::Assigned(CoapContentFormat::OctetStream)) => { From 6d8bb8feeb74c8d2916dca4b8e103756b269b5b0 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 17 Jun 2025 12:43:42 +0200 Subject: [PATCH 050/124] Fix doc error --- crates/bitwarden-crypto/src/content_format.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-crypto/src/content_format.rs b/crates/bitwarden-crypto/src/content_format.rs index 540f07117..06c94a97c 100644 --- a/crates/bitwarden-crypto/src/content_format.rs +++ b/crates/bitwarden-crypto/src/content_format.rs @@ -19,9 +19,9 @@ pub enum ContentFormat { CoseKey, /// Bitwarden Legacy Key /// There are three permissible byte values here: - /// - [u8; 32] - AES-CBC (no hmac) key. This is to be removed and banned. - /// - [u8; 64] - AES-CBC with HMAC key. This is the v1 userkey key type - /// - [u8; >64] - COSE key. Padded to be larger than 64 bytes. + /// - `[u8; 32]` - AES-CBC (no hmac) key. This is to be removed and banned. + /// - `[u8; 64]` - AES-CBC with HMAC key. This is the v1 userkey key type + /// - `[u8; >64]` - COSE key. Padded to be larger than 64 bytes. BitwardenLegacyKey, /// Stream of bytes OctetStream, From 38b89586156f42a147a895655f853678a698dc22 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 19 Jun 2025 12:48:30 +0200 Subject: [PATCH 051/124] Typed byte arrays --- .../bitwarden-sm/src/projects/create.rs | 2 +- .../bitwarden-sm/src/projects/update.rs | 2 +- .../bitwarden-sm/src/secrets/create.rs | 2 +- .../bitwarden-sm/src/secrets/update.rs | 2 +- .../bitwarden-core/src/auth/auth_request.rs | 14 +- .../src/client/encryption_settings.rs | 21 +-- .../src/key_management/crypto.rs | 32 ++-- .../src/platform/generate_fingerprint.rs | 4 +- .../src/secrets_manager/state.rs | 4 +- crates/bitwarden-crypto/examples/signature.rs | 50 +++--- .../examples/signed_object.rs | 20 ++- crates/bitwarden-crypto/src/content_format.rs | 153 +++++++++++++++++- crates/bitwarden-crypto/src/cose.rs | 32 +++- .../src/enc_string/symmetric.rs | 42 ++--- crates/bitwarden-crypto/src/fingerprint.rs | 20 ++- .../src/keys/asymmetric_crypto_key.rs | 32 ++-- .../bitwarden-crypto/src/keys/device_key.rs | 11 +- .../src/keys/key_encryptable.rs | 26 +-- crates/bitwarden-crypto/src/keys/mod.rs | 1 + crates/bitwarden-crypto/src/keys/pin_key.rs | 10 +- .../src/keys/signed_public_key.rs | 17 +- crates/bitwarden-crypto/src/lib.rs | 3 +- .../bitwarden-crypto/src/signing/signature.rs | 30 ++-- .../src/signing/signed_object.rs | 32 +++- .../src/signing/signing_key.rs | 10 +- .../src/signing/verifying_key.rs | 29 ++-- crates/bitwarden-crypto/src/store/mod.rs | 2 +- .../src/traits/encryptable.rs | 30 ++-- crates/bitwarden-crypto/src/traits/mod.rs | 5 +- .../bitwarden-exporters/src/encrypted_json.rs | 10 +- crates/bitwarden-send/src/send.rs | 8 +- crates/bitwarden-send/src/send_client.rs | 8 +- .../bitwarden-vault/src/cipher/attachment.rs | 9 +- crates/bitwarden-vault/src/cipher/card.rs | 2 +- crates/bitwarden-vault/src/cipher/cipher.rs | 2 +- crates/bitwarden-vault/src/cipher/field.rs | 2 +- crates/bitwarden-vault/src/cipher/identity.rs | 2 +- crates/bitwarden-vault/src/cipher/login.rs | 2 +- crates/bitwarden-vault/src/cipher/ssh_key.rs | 2 +- crates/bitwarden-vault/src/folder.rs | 2 +- .../bitwarden-vault/src/password_history.rs | 2 +- .../src/pure_crypto.rs | 119 ++++++++------ 42 files changed, 526 insertions(+), 282 deletions(-) diff --git a/bitwarden_license/bitwarden-sm/src/projects/create.rs b/bitwarden_license/bitwarden-sm/src/projects/create.rs index 4ea0abf06..c4a73d363 100644 --- a/bitwarden_license/bitwarden-sm/src/projects/create.rs +++ b/bitwarden_license/bitwarden-sm/src/projects/create.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::ProjectCreateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::PrimitiveEncryptableWithContentType; +use bitwarden_crypto::PrimitiveEncryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/bitwarden_license/bitwarden-sm/src/projects/update.rs b/bitwarden_license/bitwarden-sm/src/projects/update.rs index 66dbf5228..f7d318234 100644 --- a/bitwarden_license/bitwarden-sm/src/projects/update.rs +++ b/bitwarden_license/bitwarden-sm/src/projects/update.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::ProjectUpdateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::PrimitiveEncryptableWithContentType; +use bitwarden_crypto::PrimitiveEncryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/bitwarden_license/bitwarden-sm/src/secrets/create.rs b/bitwarden_license/bitwarden-sm/src/secrets/create.rs index b611b5b4b..ab59568ec 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/create.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/create.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::SecretCreateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::PrimitiveEncryptableWithContentType; +use bitwarden_crypto::PrimitiveEncryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/bitwarden_license/bitwarden-sm/src/secrets/update.rs b/bitwarden_license/bitwarden-sm/src/secrets/update.rs index 737227896..f5422c468 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/update.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/update.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::SecretUpdateRequestModel; use bitwarden_core::{key_management::SymmetricKeyId, Client}; -use bitwarden_crypto::PrimitiveEncryptableWithContentType; +use bitwarden_crypto::PrimitiveEncryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/crates/bitwarden-core/src/auth/auth_request.rs b/crates/bitwarden-core/src/auth/auth_request.rs index 5655f2110..941aeebc1 100644 --- a/crates/bitwarden-core/src/auth/auth_request.rs +++ b/crates/bitwarden-core/src/auth/auth_request.rs @@ -52,7 +52,7 @@ pub(crate) fn auth_request_decrypt_user_key( private_key: String, user_key: UnsignedSharedKey, ) -> Result { - let key = AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key)?)?; + let key = AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key)?.into())?; let key: SymmetricCryptoKey = user_key.decapsulate_key_unsigned(&key)?; Ok(key) } @@ -66,7 +66,7 @@ pub(crate) fn auth_request_decrypt_master_key( ) -> Result { use bitwarden_crypto::MasterKey; - let key = AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key)?)?; + let key = AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key)?.into())?; let master_key: SymmetricCryptoKey = master_key.decapsulate_key_unsigned(&key)?; let master_key = MasterKey::try_from(&master_key)?; @@ -118,7 +118,8 @@ fn test_auth_request() { ]; let private_key = - AsymmetricCryptoKey::from_der(&STANDARD.decode(&request.private_key).unwrap()).unwrap(); + AsymmetricCryptoKey::from_der(&STANDARD.decode(&request.private_key).unwrap().into()) + .unwrap(); let encrypted = UnsignedSharedKey::encapsulate_key_unsigned( &SymmetricCryptoKey::try_from(secret.clone()).unwrap(), @@ -135,7 +136,7 @@ fn test_auth_request() { mod tests { use std::num::NonZeroU32; - use bitwarden_crypto::{Kdf, MasterKey}; + use bitwarden_crypto::{Kdf, MasterKey, SerializedBytes, SpkiPublicKeyDerContentFormat}; use super::*; use crate::key_management::{ @@ -166,8 +167,9 @@ mod tests { let public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvyLRDUwXB4BfQ507D4meFPmwn5zwy3IqTPJO4plrrhnclWahXa240BzyFW9gHgYu+Jrgms5xBfRTBMcEsqqNm7+JpB6C1B6yvnik0DpJgWQw1rwvy4SUYidpR/AWbQi47n/hvnmzI/sQxGddVfvWu1iTKOlf5blbKYAXnUE5DZBGnrWfacNXwRRdtP06tFB0LwDgw+91CeLSJ9py6dm1qX5JIxoO8StJOQl65goLCdrTWlox+0Jh4xFUfCkb+s3px+OhSCzJbvG/hlrSRcUz5GnwlCEyF3v5lfUtV96MJD+78d8pmH6CfFAp2wxKRAbGdk+JccJYO6y6oIXd3Fm7twIDAQAB"; // Verify fingerprint - let pbkey = STANDARD.decode(public_key).unwrap(); - let fingerprint = fingerprint("test@bitwarden.com", &pbkey).unwrap(); + let pubkey = STANDARD.decode(public_key).unwrap(); + let pubkey = SerializedBytes::::from(pubkey.clone()); + let fingerprint = fingerprint("test@bitwarden.com", &pubkey).unwrap(); assert_eq!(fingerprint, "childless-unfair-prowler-dropbox-designate"); approve_auth_request(&client, public_key.to_owned()).unwrap(); diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index ab8d84cd4..26d8d5f29 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -49,16 +49,16 @@ impl EncryptionSettings { signing_key: Option, store: &KeyStore, ) -> Result<(), EncryptionSettingsError> { - use bitwarden_crypto::{ - AsymmetricCryptoKey, CoseSerializable, CryptoError, KeyDecryptable, SigningKey, - }; + use bitwarden_crypto::{AsymmetricCryptoKey, CoseSerializable, KeyDecryptable, SigningKey}; use log::warn; use crate::key_management::{AsymmetricKeyId, SigningKeyId, SymmetricKeyId}; let private_key = { - let dec: Vec = private_key.decrypt_with_key(&user_key)?; + use bitwarden_crypto::{Pkcs8PrivateKeyDerContentFormat, SerializedBytes}; + let dec: Vec = private_key.decrypt_with_key(&user_key)?; + let dec: SerializedBytes = SerializedBytes::from(dec); // FIXME: [PM-11690] - Temporarily ignore invalid private keys until we have a recovery // process in place. AsymmetricCryptoKey::from_der(&dec) @@ -72,12 +72,13 @@ impl EncryptionSettings { // .map_err(|_| EncryptionSettingsError::InvalidPrivateKey)?, // ) }; - let signing_key = signing_key - .map(|key| { - let dec: Vec = key.decrypt_with_key(&user_key)?; - SigningKey::from_cose(dec.as_slice()).map_err(Into::::into) - }) - .transpose()?; + let signing_key = signing_key.map(|key| { + use bitwarden_crypto::{CoseKeyContentFormat, SerializedBytes}; + + let dec: Vec = key.decrypt_with_key(&user_key).unwrap(); + let dec: SerializedBytes = SerializedBytes::from(dec); + SigningKey::from_cose(&dec).unwrap() + }); // FIXME: [PM-18098] When this is part of crypto we won't need to use deprecated methods #[allow(deprecated)] diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index 037dc4ec3..bfd9859e5 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -8,9 +8,10 @@ use std::collections::HashMap; use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ - AsymmetricCryptoKey, ContentFormat, CoseSerializable, CryptoError, EncString, Kdf, - KeyDecryptable, KeyEncryptable, MasterKey, PrimitiveEncryptable, SignatureAlgorithm, - SignedPublicKey, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey, + AsymmetricCryptoKey, CoseSerializable, CryptoError, EncString, Kdf, KeyDecryptable, + KeyEncryptable, MasterKey, Pkcs8PrivateKeyDerContentFormat, PrimitiveEncryptable, + SerializedBytes, SignatureAlgorithm, SignedPublicKey, SigningKey, SymmetricCryptoKey, + UnsignedSharedKey, UserKey, }; use bitwarden_error::bitwarden_error; use schemars::JsonSchema; @@ -351,7 +352,7 @@ pub(super) fn derive_pin_key( Ok(DerivePinKeyResponse { pin_protected_user_key, - encrypted_pin: pin.encrypt_with_key(user_key, ContentFormat::Utf8)?, + encrypted_pin: pin.encrypt_with_key(user_key)?, }) } @@ -536,6 +537,8 @@ pub(super) fn verify_asymmetric_keys( .decrypt_with_key(user_key) .map_err(VerifyError::DecryptFailed)?; + let decrypted_private_key: SerializedBytes = + SerializedBytes::from(decrypted_private_key); let private_key = AsymmetricCryptoKey::from_der(&decrypted_private_key) .map_err(VerifyError::ParseFailed)?; @@ -602,13 +605,9 @@ pub fn make_user_signing_keys_for_enrollment( Ok(MakeUserSigningKeysResponse { verifying_key: STANDARD.encode(signature_keypair.to_verifying_key().to_cose()), - // This needs to be changed to use the correct COSE content format before rolling out to - // users: https://bitwarden.atlassian.net/browse/PM-22189 - signing_key: signature_keypair.to_cose().encrypt( - &mut ctx, - SymmetricKeyId::User, - ContentFormat::CoseKey, - )?, + signing_key: signature_keypair + .to_cose() + .encrypt(&mut ctx, SymmetricKeyId::User)?, signed_public_key, }) } @@ -844,8 +843,9 @@ mod tests { let encrypted = enroll_admin_password_reset(&client, public_key.to_owned()).unwrap(); let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; - let private_key = - AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key).unwrap()).unwrap(); + let private_key = STANDARD.decode(private_key).unwrap(); + let private_key = SerializedBytes::::from(private_key); + let private_key = AsymmetricCryptoKey::from_der(&private_key).unwrap(); let decrypted: SymmetricCryptoKey = encrypted.decapsulate_key_unsigned(&private_key).unwrap(); @@ -937,11 +937,7 @@ mod tests { fn test_verify_asymmetric_keys_parse_failed() { let (user_key, key_pair) = setup_asymmetric_keys_test(); - let invalid_private_key = "bad_key" - .to_string() - .into_bytes() - .encrypt_with_key(&user_key.0, ContentFormat::Utf8) - .unwrap(); + let invalid_private_key = "bad_key".to_string().encrypt_with_key(&user_key.0).unwrap(); let request = VerifyAsymmetricKeysRequest { user_key: user_key.0.to_base64(), diff --git a/crates/bitwarden-core/src/platform/generate_fingerprint.rs b/crates/bitwarden-core/src/platform/generate_fingerprint.rs index 311039d74..a0cb5fc0c 100644 --- a/crates/bitwarden-core/src/platform/generate_fingerprint.rs +++ b/crates/bitwarden-core/src/platform/generate_fingerprint.rs @@ -3,7 +3,7 @@ //! This module contains the logic for generating fingerprints. use base64::{engine::general_purpose::STANDARD, Engine}; -use bitwarden_crypto::fingerprint; +use bitwarden_crypto::{fingerprint, SerializedBytes, SpkiPublicKeyDerContentFormat}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -42,7 +42,7 @@ pub enum FingerprintError { pub(crate) fn generate_fingerprint(input: &FingerprintRequest) -> Result { let key = STANDARD.decode(&input.public_key)?; - + let key = SerializedBytes::::from(key); Ok(fingerprint(&input.fingerprint_material, &key)?) } diff --git a/crates/bitwarden-core/src/secrets_manager/state.rs b/crates/bitwarden-core/src/secrets_manager/state.rs index a4a405482..39e25ab78 100644 --- a/crates/bitwarden-core/src/secrets_manager/state.rs +++ b/crates/bitwarden-core/src/secrets_manager/state.rs @@ -5,7 +5,7 @@ use std::{fmt::Debug, path::Path}; -use bitwarden_crypto::{ContentFormat, EncString, KeyDecryptable, KeyEncryptable}; +use bitwarden_crypto::{EncString, KeyDecryptable, KeyEncryptable}; use serde::{Deserialize, Serialize}; use crate::auth::AccessToken; @@ -73,7 +73,7 @@ pub(crate) fn set( ) -> Result<(), StateFileError> { let serialized_state: String = serde_json::to_string(&state)?; let encrypted_state: EncString = - serialized_state.encrypt_with_key(&access_token.encryption_key, ContentFormat::Utf8)?; + serialized_state.encrypt_with_key(&access_token.encryption_key)?; let state_string: String = encrypted_state.to_string(); Ok(std::fs::write(state_file, state_string)?) diff --git a/crates/bitwarden-crypto/examples/signature.rs b/crates/bitwarden-crypto/examples/signature.rs index 8524e2943..73b44d924 100644 --- a/crates/bitwarden-crypto/examples/signature.rs +++ b/crates/bitwarden-crypto/examples/signature.rs @@ -1,7 +1,9 @@ //! This example demonstrates how to create signatures and countersignatures for a message, and how //! to verify them. -use bitwarden_crypto::{CoseSerializable, SigningNamespace}; +use bitwarden_crypto::{ + CoseSerializable, CoseSign1ContentFormat, SerializedBytes, SigningNamespace, +}; use serde::{Deserialize, Serialize}; const EXAMPLE_NAMESPACE: &SigningNamespace = &SigningNamespace::SignedPublicKey; @@ -38,16 +40,18 @@ fn main() { .expect("Failed to sign message"); // Alice sends the signed object to Bob - mock_server.upload("signature", signature.to_cose()); + mock_server.upload("signature", signature.to_cose().as_ref().to_vec()); mock_server.upload("serialized_message", serialized_message.as_bytes().to_vec()); // Bob retrieves the signed object from the server - let retrieved_signature = bitwarden_crypto::Signature::from_cose( - mock_server - .download("signature") - .expect("Failed to download signature"), - ) - .expect("Failed to deserialize signature"); + let retrieved_signature = + bitwarden_crypto::Signature::from_cose(&SerializedBytes::::from( + mock_server + .download("signature") + .expect("Failed to download signature") + .clone(), + )) + .expect("Failed to deserialize signature"); let retrieved_serialized_message = bitwarden_crypto::SerializedMessage::from_bytes( mock_server .download("serialized_message") @@ -76,7 +80,7 @@ fn main() { ) .expect("Failed to counter sign message"); // Bob sends the counter signature to Charlie - mock_server.upload("bobs_signature", bobs_signature.to_cose()); + mock_server.upload("bobs_signature", bobs_signature.to_cose().as_ref().to_vec()); // Charlie retrieves the signatures, and the message let retrieved_serialized_message = bitwarden_crypto::SerializedMessage::from_bytes( @@ -88,18 +92,22 @@ fn main() { .content_type() .expect("Failed to get content type from signature"), ); - let retrieved_alice_signature = bitwarden_crypto::Signature::from_cose( - mock_server - .download("signature") - .expect("Failed to download Alice's signature"), - ) - .expect("Failed to deserialize Alice's signature"); - let retrieved_bobs_signature = bitwarden_crypto::Signature::from_cose( - mock_server - .download("bobs_signature") - .expect("Failed to download Bob's signature"), - ) - .expect("Failed to deserialize Bob's signature"); + let retrieved_alice_signature = + bitwarden_crypto::Signature::from_cose(&SerializedBytes::::from( + mock_server + .download("signature") + .expect("Failed to download Alice's signature") + .clone(), + )) + .expect("Failed to deserialize Alice's signature"); + let retrieved_bobs_signature = + bitwarden_crypto::Signature::from_cose(&SerializedBytes::::from( + mock_server + .download("bobs_signature") + .expect("Failed to download Bob's signature") + .clone(), + )) + .expect("Failed to deserialize Bob's signature"); // Charlie verifies Alice's signature if !retrieved_alice_signature.verify( diff --git a/crates/bitwarden-crypto/examples/signed_object.rs b/crates/bitwarden-crypto/examples/signed_object.rs index c49b8ef1b..9b84ef84f 100644 --- a/crates/bitwarden-crypto/examples/signed_object.rs +++ b/crates/bitwarden-crypto/examples/signed_object.rs @@ -1,6 +1,8 @@ //! This example demonstrates how to sign and verify structs. -use bitwarden_crypto::{CoseSerializable, SignedObject, SigningNamespace}; +use bitwarden_crypto::{ + CoseSerializable, CoseSign1ContentFormat, SerializedBytes, SignedObject, SigningNamespace, +}; use serde::{Deserialize, Serialize}; const EXAMPLE_NAMESPACE: &SigningNamespace = &SigningNamespace::SignedPublicKey; @@ -34,15 +36,17 @@ fn main() { .expect("Failed to sign message"); // Alice sends the signed object to Bob - mock_server.upload("signed_object", signed_object.to_cose()); + mock_server.upload("signed_object", signed_object.to_cose().as_ref().to_vec()); // Bob retrieves the signed object from the server - let retrieved_signed_object = SignedObject::from_cose( - mock_server - .download("signed_object") - .expect("Failed to download signed object"), - ) - .expect("Failed to deserialize signed object"); + let retrieved_signed_object = + SignedObject::from_cose(&SerializedBytes::::from( + mock_server + .download("signed_object") + .expect("Failed to download signed object") + .clone(), + )) + .expect("Failed to deserialize signed object"); // Bob verifies the signed object using Alice's verifying key let verified_message: MessageToBob = retrieved_signed_object .verify_and_unwrap(&alice_verifying_key, EXAMPLE_NAMESPACE) diff --git a/crates/bitwarden-crypto/src/content_format.rs b/crates/bitwarden-crypto/src/content_format.rs index 06c94a97c..fb7605414 100644 --- a/crates/bitwarden-crypto/src/content_format.rs +++ b/crates/bitwarden-crypto/src/content_format.rs @@ -2,6 +2,12 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] use tsify_next::Tsify; +use crate::{ + traits::PrimitiveEncryptableWithContentType, CryptoError, EncString, KeyEncryptable, + KeyEncryptableWithContentType, KeyIds, KeyStoreContext, PrimitiveEncryptable, + SymmetricCryptoKey, +}; + /// The content format describes the format of the contained bytes. Message encryption always /// happens on the byte level, and this allows determining what format the contained data has. For /// instance, an `EncString` in most cases contains UTF-8 encoded text. In some cases it may contain @@ -14,9 +20,13 @@ pub enum ContentFormat { /// UTF-8 encoded text Utf8, /// Pkcs8 private key DER - Pkcs8, + Pkcs8PrivateKey, + /// SPKI public key DER + SPKIPublicKeyDer, /// COSE serialized CoseKey CoseKey, + /// CoseSign1 message + CoseSign1, /// Bitwarden Legacy Key /// There are three permissible byte values here: /// - `[u8; 32]` - AES-CBC (no hmac) key. This is to be removed and banned. @@ -25,4 +35,145 @@ pub enum ContentFormat { BitwardenLegacyKey, /// Stream of bytes OctetStream, + /// CBOR serialized data + Cbor, +} + +/// This trait is used to instantiate different typed byte vectors with a specific content format, +/// using `SerializedBytes`. This allows for compile-time guarantees about the content format +/// of the serialized bytes. The exception here is the escape hatch using e.g. `from(Vec)`, +/// which can still be mis-used, but has to be misused explicitly. +pub trait ConstContentFormat { + /// Returns the content format as a `ContentFormat` enum. + fn content_format() -> ContentFormat; +} + +/// A serialized byte array with a specific content format. This is used to represent data that has +/// a specific format, such as UTF-8 encoded text, raw bytes, or COSE keys. The content +/// format is used to determine how the bytes should be interpreted when encrypting or decrypting +/// the data. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SerializedBytes { + inner: Vec, + _marker: std::marker::PhantomData, +} + +impl From> for SerializedBytes { + fn from(inner: Vec) -> Self { + Self { + inner, + _marker: std::marker::PhantomData, + } + } +} + +impl From<&[u8]> for SerializedBytes { + fn from(inner: &[u8]) -> Self { + Self::from(inner.to_vec()) + } +} + +impl AsRef<[u8]> for SerializedBytes { + fn as_ref(&self) -> &[u8] { + &self.inner + } +} + +/// Content format for UTF-8 encoded text. Used for most text messages. +#[derive(PartialEq, Eq, Clone, Debug)] +pub(crate) struct Utf8ContentFormat; +impl ConstContentFormat for Utf8ContentFormat { + fn content_format() -> ContentFormat { + ContentFormat::Utf8 + } +} + +/// Content format for raw bytes. Used for attachments and send seed keys. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct OctetStreamContentFormat; +impl ConstContentFormat for OctetStreamContentFormat { + fn content_format() -> ContentFormat { + ContentFormat::OctetStream + } +} + +/// Content format for PKCS8 private keys in DER format. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct Pkcs8PrivateKeyDerContentFormat; +impl ConstContentFormat for Pkcs8PrivateKeyDerContentFormat { + fn content_format() -> ContentFormat { + ContentFormat::Pkcs8PrivateKey + } +} + +/// Content format for SPKI public keys in DER format. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct SpkiPublicKeyDerContentFormat; +impl ConstContentFormat for SpkiPublicKeyDerContentFormat { + fn content_format() -> ContentFormat { + ContentFormat::SPKIPublicKeyDer + } +} + +/// Content format for COSE keys. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct CoseKeyContentFormat; +impl ConstContentFormat for CoseKeyContentFormat { + fn content_format() -> ContentFormat { + ContentFormat::CoseKey + } +} +impl CoseContentFormat for CoseKeyContentFormat {} + +/// A legacy content format for Bitwarden keys. See `ContentFormat::BitwardenLegacyKey` +#[allow(unused)] +#[derive(PartialEq, Eq, Clone, Debug)] +pub(crate) struct BitwardenLegacyKeyContentFormat; +impl ConstContentFormat for BitwardenLegacyKeyContentFormat { + fn content_format() -> ContentFormat { + ContentFormat::BitwardenLegacyKey + } +} + +/// Content format for COSE Sign1 messages. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct CoseSign1ContentFormat; +impl ConstContentFormat for CoseSign1ContentFormat { + fn content_format() -> ContentFormat { + ContentFormat::CoseSign1 + } +} +impl CoseContentFormat for CoseSign1ContentFormat {} + +/// A marker trait for COSE content formats. +pub trait CoseContentFormat {} + +impl PrimitiveEncryptable + for SerializedBytes +{ + fn encrypt( + &self, + ctx: &mut KeyStoreContext, + key: Ids::Symmetric, + ) -> Result { + self.inner.encrypt(ctx, key, T::content_format()) + } +} + +impl KeyEncryptable for &SerializedBytes { + fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { + self.as_ref().encrypt_with_key(key, T::content_format()) + } +} + +impl Into> for String { + fn into(self) -> SerializedBytes { + SerializedBytes::from(self.into_bytes()) + } +} + +impl Into> for &str { + fn into(self) -> SerializedBytes { + SerializedBytes::from(self.as_bytes().to_vec()) + } } diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 63b2df6aa..59d523242 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -11,6 +11,7 @@ use generic_array::GenericArray; use typenum::U32; use crate::{ + content_format::{ConstContentFormat, CoseContentFormat, SerializedBytes}, error::{EncStringParseError, EncodingError}, xchacha20, ContentFormat, CryptoError, SymmetricCryptoKey, XChaCha20Poly1305Key, }; @@ -25,6 +26,7 @@ const XCHACHA20_TEXT_PAD_BLOCK_SIZE: usize = 32; // These are only used within Bitwarden, and not meant for exchange with other systems. const CONTENT_TYPE_PADDED_UTF8: &str = "application/x.bitwarden.utf8-padded"; const CONTENT_TYPE_BITWARDEN_LEGACY_KEY: &str = "application/x.bitwarden.legacy-key"; +const CONTENT_TYPE_SPKI_PUBLIC_KEY: &str = "application/x.bitwarden.spki-public-key"; // Labels // @@ -160,7 +162,13 @@ impl From for coset::HeaderBuilder { ContentFormat::Utf8 => { header_builder.content_type(CONTENT_TYPE_PADDED_UTF8.to_string()) } - ContentFormat::Pkcs8 => header_builder.content_format(CoapContentFormat::Pkcs8), + ContentFormat::Pkcs8PrivateKey => { + header_builder.content_format(CoapContentFormat::Pkcs8) + } + ContentFormat::SPKIPublicKeyDer => { + header_builder.content_type(CONTENT_TYPE_SPKI_PUBLIC_KEY.to_string()) + } + ContentFormat::CoseSign1 => header_builder.content_format(CoapContentFormat::CoseSign1), ContentFormat::CoseKey => header_builder.content_format(CoapContentFormat::CoseKey), ContentFormat::BitwardenLegacyKey => { header_builder.content_type(CONTENT_TYPE_BITWARDEN_LEGACY_KEY.to_string()) @@ -168,6 +176,7 @@ impl From for coset::HeaderBuilder { ContentFormat::OctetStream => { header_builder.content_format(CoapContentFormat::OctetStream) } + ContentFormat::Cbor => header_builder.content_format(CoapContentFormat::Cbor), } } } @@ -183,7 +192,12 @@ impl TryFrom<&coset::Header> for ContentFormat { Some(ContentType::Text(format)) if format == CONTENT_TYPE_BITWARDEN_LEGACY_KEY => { Ok(ContentFormat::BitwardenLegacyKey) } - Some(ContentType::Assigned(CoapContentFormat::Pkcs8)) => Ok(ContentFormat::Pkcs8), + Some(ContentType::Text(format)) if format == CONTENT_TYPE_SPKI_PUBLIC_KEY => { + Ok(ContentFormat::SPKIPublicKeyDer) + } + Some(ContentType::Assigned(CoapContentFormat::Pkcs8)) => { + Ok(ContentFormat::Pkcs8PrivateKey) + } Some(ContentType::Assigned(CoapContentFormat::CoseKey)) => Ok(ContentFormat::CoseKey), Some(ContentType::Assigned(CoapContentFormat::OctetStream)) => { Ok(ContentFormat::OctetStream) @@ -200,11 +214,11 @@ fn should_pad_content(format: &ContentFormat) -> bool { } /// Trait for structs that are serializable to COSE objects. -pub trait CoseSerializable { +pub trait CoseSerializable { /// Serializes the struct to COSE serialization - fn to_cose(&self) -> Vec; + fn to_cose(&self) -> SerializedBytes; /// Deserializes a serialized COSE object to a struct - fn from_cose(bytes: &[u8]) -> Result + fn from_cose(bytes: &SerializedBytes) -> Result where Self: Sized; } @@ -265,9 +279,13 @@ mod test { }; let plaintext = b"Hello, world!"; - let encrypted = encrypt_xchacha20_poly1305(plaintext, key, ContentFormat::Pkcs8).unwrap(); + let encrypted = + encrypt_xchacha20_poly1305(plaintext, key, ContentFormat::Pkcs8PrivateKey).unwrap(); let decrypted = decrypt_xchacha20_poly1305(&encrypted, key).unwrap(); - assert_eq!(decrypted, (plaintext.to_vec(), ContentFormat::Pkcs8)); + assert_eq!( + decrypted, + (plaintext.to_vec(), ContentFormat::Pkcs8PrivateKey) + ); } #[test] diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index c3b03bb3b..5874f8abf 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -6,10 +6,11 @@ use serde::Deserialize; use super::{check_length, from_b64, from_b64_vec, split_enc_string}; use crate::{ + content_format::{SerializedBytes, Utf8ContentFormat}, error::{CryptoError, EncStringParseError, Result, UnsupportedOperation}, util::FromStrVisitor, - Aes256CbcHmacKey, ContentFormat, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, - XChaCha20Poly1305Key, + Aes256CbcHmacKey, ContentFormat, KeyDecryptable, KeyEncryptable, KeyEncryptableWithContentType, + SymmetricCryptoKey, XChaCha20Poly1305Key, }; #[cfg(feature = "wasm")] @@ -279,7 +280,7 @@ impl EncString { } } -impl KeyEncryptable for &[u8] { +impl KeyEncryptableWithContentType for &[u8] { fn encrypt_with_key( self, key: &SymmetricCryptoKey, @@ -321,22 +322,14 @@ impl KeyDecryptable> for EncString { } impl KeyEncryptable for String { - fn encrypt_with_key( - self, - key: &SymmetricCryptoKey, - content_format: ContentFormat, - ) -> Result { - self.as_bytes().encrypt_with_key(key, content_format) + fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { + Into::>::into(self).encrypt_with_key(key) } } impl KeyEncryptable for &str { - fn encrypt_with_key( - self, - key: &SymmetricCryptoKey, - content_format: ContentFormat, - ) -> Result { - self.as_bytes().encrypt_with_key(key, content_format) + fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { + Into::>::into(self).encrypt_with_key(key) } } @@ -365,8 +358,8 @@ mod tests { use super::EncString; use crate::{ - derive_symmetric_key, ContentFormat, CryptoError, KeyDecryptable, KeyEncryptable, - SymmetricCryptoKey, KEY_ID_SIZE, + derive_symmetric_key, CryptoError, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, + KEY_ID_SIZE, }; #[test] @@ -379,10 +372,7 @@ mod tests { }); let test_string = "encrypted_test_string"; - let cipher = test_string - .to_owned() - .encrypt_with_key(&key, ContentFormat::Utf8) - .unwrap(); + let cipher = test_string.to_owned().encrypt_with_key(&key).unwrap(); let decrypted_str: String = cipher.decrypt_with_key(&key).unwrap(); assert_eq!(decrypted_str, test_string); } @@ -392,10 +382,7 @@ mod tests { let key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test")); let test_string = "encrypted_test_string"; - let cipher = test_string - .to_string() - .encrypt_with_key(&key, ContentFormat::Utf8) - .unwrap(); + let cipher = test_string.to_string().encrypt_with_key(&key).unwrap(); let decrypted_str: String = cipher.decrypt_with_key(&key).unwrap(); assert_eq!(decrypted_str, test_string); @@ -406,10 +393,7 @@ mod tests { let key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test")); let test_string: &'static str = "encrypted_test_string"; - let cipher = test_string - .to_string() - .encrypt_with_key(&key, ContentFormat::Utf8) - .unwrap(); + let cipher = test_string.to_string().encrypt_with_key(&key).unwrap(); let decrypted_str: String = cipher.decrypt_with_key(&key).unwrap(); assert_eq!(decrypted_str, test_string); diff --git a/crates/bitwarden-crypto/src/fingerprint.rs b/crates/bitwarden-crypto/src/fingerprint.rs index 2e7200344..06a3e2260 100644 --- a/crates/bitwarden-crypto/src/fingerprint.rs +++ b/crates/bitwarden-crypto/src/fingerprint.rs @@ -8,16 +8,22 @@ use num_bigint::BigUint; use num_traits::cast::ToPrimitive; use thiserror::Error; -use crate::{error::Result, wordlist::EFF_LONG_WORD_LIST, CryptoError}; +use crate::{ + content_format::SpkiPublicKeyDerContentFormat, error::Result, wordlist::EFF_LONG_WORD_LIST, + CryptoError, SerializedBytes, +}; /// Computes a fingerprint of the given `fingerprint_material` using the given `public_key`. /// /// This is commonly used for account fingerprints. With the following arguments: /// - `fingerprint_material`: user's id. /// - `public_key`: user's public key. -pub fn fingerprint(fingerprint_material: &str, public_key: &[u8]) -> Result { - let hkdf = - hkdf::Hkdf::::from_prk(public_key).map_err(|_| CryptoError::InvalidKeyLen)?; +pub fn fingerprint( + fingerprint_material: &str, + public_key: &SerializedBytes, +) -> Result { + let hkdf = hkdf::Hkdf::::from_prk(public_key.as_ref()) + .map_err(|_| CryptoError::InvalidKeyLen)?; let mut user_fingerprint = [0u8; 32]; hkdf.expand(fingerprint_material.as_bytes(), &mut user_fingerprint) @@ -64,6 +70,7 @@ pub enum FingerprintError { #[cfg(test)] mod tests { use super::fingerprint; + use crate::{SerializedBytes, SpkiPublicKeyDerContentFormat}; #[test] fn test_fingerprint() { @@ -86,10 +93,11 @@ mod tests { 197, 3, 219, 56, 77, 109, 47, 72, 251, 131, 36, 240, 96, 169, 31, 82, 93, 166, 242, 3, 33, 213, 2, 3, 1, 0, 1, ]; - + let key: SerializedBytes = + SerializedBytes::from(key.to_vec()); assert_eq!( "turban-deftly-anime-chatroom-unselfish", - fingerprint(user_id, key).unwrap() + fingerprint(user_id, &key).unwrap() ); } } diff --git a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs index 8ef598e33..572ead9f9 100644 --- a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs @@ -4,7 +4,12 @@ use rsa::{pkcs8::DecodePublicKey, RsaPrivateKey, RsaPublicKey}; use serde_repr::{Deserialize_repr, Serialize_repr}; use super::key_encryptable::CryptoKey; -use crate::error::{CryptoError, Result}; +use crate::{ + content_format::{ + Pkcs8PrivateKeyDerContentFormat, SerializedBytes, SpkiPublicKeyDerContentFormat, + }, + error::{CryptoError, Result}, +}; /// Algorithm / public key encryption scheme used for encryption/decryption. #[derive(Serialize_repr, Deserialize_repr)] @@ -41,14 +46,15 @@ impl AsymmetricPublicCryptoKey { } /// Makes a SubjectPublicKeyInfo DER serialized version of the public key. - pub fn to_der(&self) -> Result> { + pub fn to_der(&self) -> Result> { use rsa::pkcs8::EncodePublicKey; match &self.inner { RawPublicKey::RsaOaepSha1(public_key) => Ok(public_key .to_public_key_der() .map_err(|_| CryptoError::InvalidKey)? .as_bytes() - .to_owned()), + .to_owned() + .into()), } } } @@ -110,17 +116,17 @@ impl AsymmetricCryptoKey { } #[allow(missing_docs)] - pub fn from_der(der: &[u8]) -> Result { + pub fn from_der(der: &SerializedBytes) -> Result { use rsa::pkcs8::DecodePrivateKey; Ok(Self { inner: RawPrivateKey::RsaOaepSha1(Box::pin( - RsaPrivateKey::from_pkcs8_der(der).map_err(|_| CryptoError::InvalidKey)?, + RsaPrivateKey::from_pkcs8_der(der.as_ref()).map_err(|_| CryptoError::InvalidKey)?, )), }) } #[allow(missing_docs)] - pub fn to_der(&self) -> Result> { + pub fn to_der(&self) -> Result> { match &self.inner { RawPrivateKey::RsaOaepSha1(private_key) => { use rsa::pkcs8::EncodePrivateKey; @@ -128,7 +134,8 @@ impl AsymmetricCryptoKey { .to_pkcs8_der() .map_err(|_| CryptoError::InvalidKey)? .as_bytes() - .to_owned()) + .to_owned() + .into()) } } } @@ -160,6 +167,7 @@ mod tests { use base64::{engine::general_purpose::STANDARD, Engine}; use crate::{ + content_format::{Pkcs8PrivateKeyDerContentFormat, SerializedBytes}, AsymmetricCryptoKey, AsymmetricPublicCryptoKey, SymmetricCryptoKey, UnsignedSharedKey, }; @@ -198,12 +206,15 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= // Load the two different formats and check they are the same key let pem_key = AsymmetricCryptoKey::from_pem(pem_key_str).unwrap(); - let der_key = AsymmetricCryptoKey::from_der(&der_key_vec).unwrap(); + let der_key = AsymmetricCryptoKey::from_der(&SerializedBytes::< + Pkcs8PrivateKeyDerContentFormat, + >::from(der_key_vec.clone())) + .unwrap(); assert_eq!(pem_key.to_der().unwrap(), der_key.to_der().unwrap()); // Check that the keys can be converted back to DER - assert_eq!(der_key.to_der().unwrap(), der_key_vec); - assert_eq!(pem_key.to_der().unwrap(), der_key_vec); + assert_eq!(der_key.to_der().unwrap().as_ref().to_vec(), der_key_vec); + assert_eq!(pem_key.to_der().unwrap().as_ref().to_vec(), der_key_vec); } #[test] @@ -251,6 +262,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= )) .unwrap(); + let private_key = SerializedBytes::::from(private_key); let private_key = AsymmetricCryptoKey::from_der(&private_key).unwrap(); let public_key = AsymmetricPublicCryptoKey::from_der(&public_key).unwrap(); diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index d8e16b587..0948b06a6 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -1,7 +1,8 @@ use super::{AsymmetricCryptoKey, PublicKeyEncryptionAlgorithm}; use crate::{ - error::Result, ContentFormat, CryptoError, EncString, KeyDecryptable, KeyEncryptable, - SymmetricCryptoKey, UnsignedSharedKey, + content_format::{Pkcs8PrivateKeyDerContentFormat, SerializedBytes}, + error::Result, + CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, UnsignedSharedKey, }; /// Device Key @@ -44,11 +45,11 @@ impl DeviceKey { let protected_device_public_key = device_private_key .to_public_key() .to_der()? - .encrypt_with_key(user_key, ContentFormat::OctetStream)?; + .encrypt_with_key(user_key)?; let protected_device_private_key = device_private_key .to_der()? - .encrypt_with_key(&device_key.0, ContentFormat::Pkcs8)?; + .encrypt_with_key(&device_key.0)?; Ok(TrustDeviceResponse { device_key: device_key.to_base64(), @@ -65,6 +66,8 @@ impl DeviceKey { protected_user_key: UnsignedSharedKey, ) -> Result { let device_private_key: Vec = protected_device_private_key.decrypt_with_key(&self.0)?; + let device_private_key: SerializedBytes = + SerializedBytes::from(device_private_key); let device_private_key = AsymmetricCryptoKey::from_der(&device_private_key)?; let user_key: SymmetricCryptoKey = diff --git a/crates/bitwarden-crypto/src/keys/key_encryptable.rs b/crates/bitwarden-crypto/src/keys/key_encryptable.rs index ee57a31b9..190be2a9d 100644 --- a/crates/bitwarden-crypto/src/keys/key_encryptable.rs +++ b/crates/bitwarden-crypto/src/keys/key_encryptable.rs @@ -21,6 +21,11 @@ pub trait CryptoKey {} #[allow(missing_docs)] pub trait KeyEncryptable { + fn encrypt_with_key(self, key: &Key) -> Result; +} + +#[allow(missing_docs)] +pub(crate) trait KeyEncryptableWithContentType { fn encrypt_with_key(self, key: &Key, content_format: ContentFormat) -> Result; } @@ -32,9 +37,8 @@ pub trait KeyDecryptable { impl, Key: CryptoKey, Output> KeyEncryptable> for Option { - fn encrypt_with_key(self, key: &Key, content_format: ContentFormat) -> Result> { - self.map(|e| e.encrypt_with_key(key, content_format)) - .transpose() + fn encrypt_with_key(self, key: &Key) -> Result> { + self.map(|e| e.encrypt_with_key(key)).transpose() } } @@ -49,8 +53,8 @@ impl, Key: CryptoKey, Output> KeyDecryptable, Key: CryptoKey, Output> KeyEncryptable for Box { - fn encrypt_with_key(self, key: &Key, content_format: ContentFormat) -> Result { - (*self).encrypt_with_key(key, content_format) + fn encrypt_with_key(self, key: &Key) -> Result { + (*self).encrypt_with_key(key) } } @@ -68,9 +72,9 @@ impl< Output: Send + Sync, > KeyEncryptable> for Vec { - fn encrypt_with_key(self, key: &Key, content_format: ContentFormat) -> Result> { + fn encrypt_with_key(self, key: &Key) -> Result> { self.into_par_iter() - .map(|e| e.encrypt_with_key(key, content_format)) + .map(|e| e.encrypt_with_key(key)) .collect() } } @@ -95,13 +99,9 @@ impl< Id: Hash + Eq + Send + Sync, > KeyEncryptable> for HashMap { - fn encrypt_with_key( - self, - key: &Key, - content_format: ContentFormat, - ) -> Result> { + fn encrypt_with_key(self, key: &Key) -> Result> { self.into_par_iter() - .map(|(id, e)| Ok((id, e.encrypt_with_key(key, content_format)?))) + .map(|(id, e)| Ok((id, e.encrypt_with_key(key)?))) .collect() } } diff --git a/crates/bitwarden-crypto/src/keys/mod.rs b/crates/bitwarden-crypto/src/keys/mod.rs index c90f28320..c5dd18bb6 100644 --- a/crates/bitwarden-crypto/src/keys/mod.rs +++ b/crates/bitwarden-crypto/src/keys/mod.rs @@ -1,4 +1,5 @@ mod key_encryptable; +pub(crate) use key_encryptable::KeyEncryptableWithContentType; pub use key_encryptable::{CryptoKey, KeyContainer, KeyDecryptable, KeyEncryptable}; mod master_key; pub use master_key::{HashPurpose, MasterKey}; diff --git a/crates/bitwarden-crypto/src/keys/pin_key.rs b/crates/bitwarden-crypto/src/keys/pin_key.rs index ecbc929fc..247971f1e 100644 --- a/crates/bitwarden-crypto/src/keys/pin_key.rs +++ b/crates/bitwarden-crypto/src/keys/pin_key.rs @@ -4,8 +4,8 @@ use super::{ utils::stretch_key, }; use crate::{ - keys::key_encryptable::CryptoKey, ContentFormat, EncString, KeyEncryptable, Result, - SymmetricCryptoKey, + keys::key_encryptable::CryptoKey, ContentFormat, EncString, KeyEncryptable, + KeyEncryptableWithContentType, Result, SymmetricCryptoKey, }; /// Pin Key. @@ -32,7 +32,7 @@ impl PinKey { impl CryptoKey for PinKey {} -impl KeyEncryptable for &[u8] { +impl KeyEncryptableWithContentType for &[u8] { fn encrypt_with_key(self, key: &PinKey, content_format: ContentFormat) -> Result { let stretched_key = SymmetricCryptoKey::Aes256CbcHmacKey(stretch_key(&key.0 .0)?); self.encrypt_with_key(&stretched_key, content_format) @@ -40,7 +40,7 @@ impl KeyEncryptable for &[u8] { } impl KeyEncryptable for String { - fn encrypt_with_key(self, key: &PinKey, content_format: ContentFormat) -> Result { - self.as_bytes().encrypt_with_key(key, content_format) + fn encrypt_with_key(self, key: &PinKey) -> Result { + self.as_bytes().encrypt_with_key(key, ContentFormat::Utf8) } } diff --git a/crates/bitwarden-crypto/src/keys/signed_public_key.rs b/crates/bitwarden-crypto/src/keys/signed_public_key.rs index ad2597b95..7eb0c1992 100644 --- a/crates/bitwarden-crypto/src/keys/signed_public_key.rs +++ b/crates/bitwarden-crypto/src/keys/signed_public_key.rs @@ -11,9 +11,12 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use super::AsymmetricPublicCryptoKey; use crate::{ - cose::CoseSerializable, error::EncodingError, util::FromStrVisitor, CryptoError, - PublicKeyEncryptionAlgorithm, RawPublicKey, SignedObject, SigningKey, SigningNamespace, - VerifyingKey, + content_format::{CoseSign1ContentFormat, SerializedBytes}, + cose::CoseSerializable, + error::EncodingError, + util::FromStrVisitor, + CryptoError, PublicKeyEncryptionAlgorithm, RawPublicKey, SignedObject, SigningKey, + SigningNamespace, VerifyingKey, }; #[cfg(feature = "wasm")] @@ -57,7 +60,7 @@ impl SignedPublicKeyMessage { RawPublicKey::RsaOaepSha1(_) => Ok(SignedPublicKeyMessage { algorithm: PublicKeyEncryptionAlgorithm::RsaOaepSha1, content_format: PublicKeyFormat::Spki, - public_key: ByteBuf::from(public_key.to_der()?), + public_key: ByteBuf::from(public_key.to_der()?.as_ref()), }), } } @@ -79,14 +82,16 @@ pub struct SignedPublicKey(pub(crate) SignedObject); impl From for Vec { fn from(val: SignedPublicKey) -> Self { - val.0.to_cose() + val.0.to_cose().as_ref().to_vec() } } impl TryFrom> for SignedPublicKey { type Error = EncodingError; fn try_from(bytes: Vec) -> Result { - Ok(SignedPublicKey(SignedObject::from_cose(&bytes)?)) + Ok(SignedPublicKey(SignedObject::from_cose( + &SerializedBytes::::from(bytes), + )?)) } } diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index ef2736fb1..60428d8a4 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -14,7 +14,7 @@ static ALLOC: ZeroizingAllocator = ZeroizingAllocator(std::a mod aes; mod content_format; -pub use content_format::ContentFormat; +pub use content_format::*; mod enc_string; pub use enc_string::{EncString, UnsignedSharedKey}; mod error; @@ -40,7 +40,6 @@ mod traits; mod xchacha20; pub use traits::{ CompositeEncryptable, Decryptable, IdentifyKey, KeyId, KeyIds, PrimitiveEncryptable, - PrimitiveEncryptableWithContentType, }; pub use zeroizing_alloc::ZeroAlloc as ZeroizingAllocator; diff --git a/crates/bitwarden-crypto/src/signing/signature.rs b/crates/bitwarden-crypto/src/signing/signature.rs index 47bbbe6be..62b9b763c 100644 --- a/crates/bitwarden-crypto/src/signing/signature.rs +++ b/crates/bitwarden-crypto/src/signing/signature.rs @@ -7,6 +7,7 @@ use super::{ VerifyingKey, }; use crate::{ + content_format::{CoseSign1ContentFormat, SerializedBytes}, cose::{CoseSerializable, SIGNING_NAMESPACE}, error::{EncodingError, SignatureError}, CryptoError, @@ -172,25 +173,29 @@ impl SigningKey { } } -impl CoseSerializable for Signature { - fn from_cose(bytes: &[u8]) -> Result { - let cose_sign1 = - CoseSign1::from_slice(bytes).map_err(|_| EncodingError::InvalidCoseEncoding)?; +impl CoseSerializable for Signature { + fn from_cose(bytes: &SerializedBytes) -> Result { + let cose_sign1 = CoseSign1::from_slice(bytes.as_ref()) + .map_err(|_| EncodingError::InvalidCoseEncoding)?; Ok(Signature(cose_sign1)) } - fn to_cose(&self) -> Vec { + fn to_cose(&self) -> SerializedBytes { self.0 .clone() .to_vec() .expect("Signature is always serializable") + .into() } } #[cfg(test)] mod tests { use super::*; - use crate::SignatureAlgorithm; + use crate::{ + content_format::{CoseKeyContentFormat, SerializedBytes}, + SignatureAlgorithm, + }; const VERIFYING_KEY: &[u8] = &[ 166, 1, 1, 2, 80, 55, 131, 40, 191, 230, 137, 76, 182, 184, 139, 94, 152, 45, 63, 13, 71, @@ -212,7 +217,9 @@ mod tests { #[test] fn test_cose_roundtrip_encode_signature() { - let signature = Signature::from_cose(SIGNATURE).unwrap(); + let signature = + Signature::from_cose(&SerializedBytes::::from(SIGNATURE)) + .unwrap(); let cose_bytes = signature.to_cose(); let decoded_signature = Signature::from_cose(&cose_bytes).unwrap(); assert_eq!(signature.inner(), decoded_signature.inner()); @@ -220,8 +227,13 @@ mod tests { #[test] fn test_verify_testvector() { - let verifying_key = VerifyingKey::from_cose(VERIFYING_KEY).unwrap(); - let signature = Signature::from_cose(SIGNATURE).unwrap(); + let verifying_key = VerifyingKey::from_cose( + &SerializedBytes::::from(VERIFYING_KEY), + ) + .unwrap(); + let signature = + Signature::from_cose(&SerializedBytes::::from(SIGNATURE)) + .unwrap(); let serialized_message = SerializedMessage::from_bytes(SERIALIZED_MESSAGE.to_vec(), CoapContentFormat::Cbor); diff --git a/crates/bitwarden-crypto/src/signing/signed_object.rs b/crates/bitwarden-crypto/src/signing/signed_object.rs index 289e52a23..8c4a364e3 100644 --- a/crates/bitwarden-crypto/src/signing/signed_object.rs +++ b/crates/bitwarden-crypto/src/signing/signed_object.rs @@ -7,6 +7,7 @@ use super::{ verifying_key::VerifyingKey, SigningNamespace, }; use crate::{ + content_format::{CoseSign1ContentFormat, SerializedBytes}, cose::{CoseSerializable, SIGNING_NAMESPACE}, error::{EncodingError, SignatureError}, CryptoError, @@ -149,18 +150,20 @@ impl SigningKey { } } -impl CoseSerializable for SignedObject { - fn from_cose(bytes: &[u8]) -> Result { +impl CoseSerializable for SignedObject { + fn from_cose(bytes: &SerializedBytes) -> Result { Ok(SignedObject( - CoseSign1::from_slice(bytes).map_err(|_| EncodingError::InvalidCoseEncoding)?, + CoseSign1::from_slice(bytes.as_ref()) + .map_err(|_| EncodingError::InvalidCoseEncoding)?, )) } - fn to_cose(&self) -> Vec { + fn to_cose(&self) -> SerializedBytes { self.0 .clone() .to_vec() .expect("SignedObject is always serializable") + .into() } } @@ -169,6 +172,7 @@ mod tests { use serde::{Deserialize, Serialize}; use crate::{ + content_format::{CoseKeyContentFormat, CoseSign1ContentFormat, SerializedBytes}, CoseSerializable, CryptoError, SignatureAlgorithm, SignedObject, SigningKey, SigningNamespace, VerifyingKey, }; @@ -196,13 +200,19 @@ mod tests { #[test] fn test_roundtrip_cose() { - let signed_object = SignedObject::from_cose(SIGNED_OBJECT).unwrap(); + let signed_object = SignedObject::from_cose( + &Into::>::into(SIGNED_OBJECT), + ) + .unwrap(); assert_eq!( signed_object.content_type().unwrap(), coset::iana::CoapContentFormat::Cbor ); let cose_bytes = signed_object.to_cose(); - assert_eq!(cose_bytes, SIGNED_OBJECT); + assert_eq!( + cose_bytes, + SerializedBytes::::from(SIGNED_OBJECT) + ); } #[test] @@ -210,8 +220,14 @@ mod tests { let test_message = TestMessage { field1: "Test message".to_string(), }; - let signed_object = SignedObject::from_cose(SIGNED_OBJECT).unwrap(); - let verifying_key = VerifyingKey::from_cose(VERIFYING_KEY).unwrap(); + let signed_object = SignedObject::from_cose( + &Into::>::into(SIGNED_OBJECT), + ) + .unwrap(); + let verifying_key = VerifyingKey::from_cose( + &Into::>::into(VERIFYING_KEY), + ) + .unwrap(); let namespace = SigningNamespace::ExampleNamespace; let payload: TestMessage = signed_object .verify_and_unwrap(&verifying_key, &namespace) diff --git a/crates/bitwarden-crypto/src/signing/signing_key.rs b/crates/bitwarden-crypto/src/signing/signing_key.rs index c8567ad34..544a32e03 100644 --- a/crates/bitwarden-crypto/src/signing/signing_key.rs +++ b/crates/bitwarden-crypto/src/signing/signing_key.rs @@ -13,6 +13,7 @@ use super::{ SignatureAlgorithm, }; use crate::{ + content_format::{CoseKeyContentFormat, SerializedBytes}, cose::CoseSerializable, error::{EncodingError, Result}, keys::KeyId, @@ -86,9 +87,9 @@ impl SigningKey { } } -impl CoseSerializable for SigningKey { +impl CoseSerializable for SigningKey { /// Serializes the signing key to a COSE-formatted byte array. - fn to_cose(&self) -> Vec { + fn to_cose(&self) -> SerializedBytes { match &self.inner { RawSigningKey::Ed25519(key) => { coset::CoseKeyBuilder::new_okp_key() @@ -107,14 +108,15 @@ impl CoseSerializable for SigningKey { .build() .to_vec() .expect("Signing key is always serializable") + .into() } } } /// Deserializes a COSE-formatted byte array into a signing key. - fn from_cose(bytes: &[u8]) -> Result { + fn from_cose(bytes: &SerializedBytes) -> Result { let cose_key = - CoseKey::from_slice(bytes).map_err(|_| EncodingError::InvalidCoseEncoding)?; + CoseKey::from_slice(bytes.as_ref()).map_err(|_| EncodingError::InvalidCoseEncoding)?; match (&cose_key.alg, &cose_key.kty) { ( diff --git a/crates/bitwarden-crypto/src/signing/verifying_key.rs b/crates/bitwarden-crypto/src/signing/verifying_key.rs index 372fa0cd7..fef2da03d 100644 --- a/crates/bitwarden-crypto/src/signing/verifying_key.rs +++ b/crates/bitwarden-crypto/src/signing/verifying_key.rs @@ -11,6 +11,7 @@ use coset::{ use super::{ed25519_verifying_key, key_id, SignatureAlgorithm}; use crate::{ + content_format::{CoseKeyContentFormat, SerializedBytes}, cose::CoseSerializable, error::{EncodingError, SignatureError}, keys::KeyId, @@ -57,8 +58,8 @@ impl VerifyingKey { } } -impl CoseSerializable for VerifyingKey { - fn to_cose(&self) -> Vec { +impl CoseSerializable for VerifyingKey { + fn to_cose(&self) -> SerializedBytes { match &self.inner { RawVerifyingKey::Ed25519(key) => coset::CoseKeyBuilder::new_okp_key() .key_id((&self.id).into()) @@ -78,16 +79,17 @@ impl CoseSerializable for VerifyingKey { .add_key_op(KeyOperation::Verify) .build() .to_vec() - .expect("Verifying key is always serializable"), + .expect("Verifying key is always serializable") + .into(), } } - fn from_cose(bytes: &[u8]) -> Result + fn from_cose(bytes: &SerializedBytes) -> Result where Self: Sized, { - let cose_key = - coset::CoseKey::from_slice(bytes).map_err(|_| EncodingError::InvalidCoseEncoding)?; + let cose_key = coset::CoseKey::from_slice(bytes.as_ref()) + .map_err(|_| EncodingError::InvalidCoseEncoding)?; let algorithm = cose_key .alg @@ -127,7 +129,10 @@ mod tests { #[test] fn test_cose_roundtrip_encode_verifying() { - let verifying_key = VerifyingKey::from_cose(VERIFYING_KEY).unwrap(); + let verifying_key = VerifyingKey::from_cose( + &SerializedBytes::::from(VERIFYING_KEY.to_vec()), + ) + .unwrap(); let cose = verifying_key.to_cose(); let parsed_key = VerifyingKey::from_cose(&cose).unwrap(); @@ -136,7 +141,10 @@ mod tests { #[test] fn test_testvector() { - let verifying_key = VerifyingKey::from_cose(VERIFYING_KEY).unwrap(); + let verifying_key = VerifyingKey::from_cose( + &SerializedBytes::::from(VERIFYING_KEY.to_vec()), + ) + .unwrap(); assert_eq!(verifying_key.algorithm(), SignatureAlgorithm::Ed25519); verifying_key @@ -146,7 +154,10 @@ mod tests { #[test] fn test_invalid_testvector() { - let verifying_key = VerifyingKey::from_cose(VERIFYING_KEY).unwrap(); + let verifying_key = VerifyingKey::from_cose( + &SerializedBytes::::from(VERIFYING_KEY.to_vec()), + ) + .unwrap(); assert_eq!(verifying_key.algorithm(), SignatureAlgorithm::Ed25519); // This should fail, as the signed object is not valid for the given verifying key. diff --git a/crates/bitwarden-crypto/src/store/mod.rs b/crates/bitwarden-crypto/src/store/mod.rs index 3fb831149..765762bac 100644 --- a/crates/bitwarden-crypto/src/store/mod.rs +++ b/crates/bitwarden-crypto/src/store/mod.rs @@ -319,7 +319,7 @@ pub(crate) mod tests { use crate::{ store::{KeyStore, KeyStoreContext}, traits::tests::{TestIds, TestSymmKey}, - EncString, PrimitiveEncryptableWithContentType, SymmetricCryptoKey, + EncString, PrimitiveEncryptable, SymmetricCryptoKey, }; pub struct DataView(pub String, pub TestSymmKey); diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index 997461467..6b5debafe 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -58,13 +58,13 @@ impl, Output> /// An encryption operation that takes the input value - a primitive such as `String` and encrypts /// it into the output value. The implementation decides the content format. -pub trait PrimitiveEncryptableWithContentType { +pub trait PrimitiveEncryptable { /// Encrypts a primitive without requiring an externally provided content type fn encrypt(&self, ctx: &mut KeyStoreContext, key: Key) -> Result; } -impl, Output> - PrimitiveEncryptableWithContentType> for Option +impl, Output> + PrimitiveEncryptable> for Option { fn encrypt( &self, @@ -77,7 +77,7 @@ impl PrimitiveEncryptableWithContentType for &str { +impl PrimitiveEncryptable for &str { fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -87,7 +87,7 @@ impl PrimitiveEncryptableWithContentType PrimitiveEncryptableWithContentType for String { +impl PrimitiveEncryptable for String { fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -99,7 +99,7 @@ impl PrimitiveEncryptableWithContentType` - and /// encrypts it into the output value. The caller must specify the content format. -pub trait PrimitiveEncryptable { +pub(crate) trait PrimitiveEncryptableWithContentType { /// Encrypts a primitive, given an externally provided content type fn encrypt( &self, @@ -109,7 +109,7 @@ pub trait PrimitiveEncryptable { ) -> Result; } -impl PrimitiveEncryptable for &[u8] { +impl PrimitiveEncryptableWithContentType for &[u8] { fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -120,7 +120,7 @@ impl PrimitiveEncryptable for &[u8] } } -impl PrimitiveEncryptable for Vec { +impl PrimitiveEncryptableWithContentType for Vec { fn encrypt( &self, ctx: &mut KeyStoreContext, @@ -131,8 +131,8 @@ impl PrimitiveEncryptable for Vec, Output> - PrimitiveEncryptable> for Option +impl, Output> + PrimitiveEncryptableWithContentType> for Option { fn encrypt( &self, @@ -146,8 +146,8 @@ impl, Output> } } -impl, Output> - PrimitiveEncryptable> for Vec +impl, Output> + PrimitiveEncryptableWithContentType> for Vec { fn encrypt( &self, @@ -164,9 +164,9 @@ impl, Output> #[cfg(test)] mod tests { use crate::{ - traits::tests::*, AsymmetricCryptoKey, ContentFormat, Decryptable, KeyStore, - PrimitiveEncryptable, PrimitiveEncryptableWithContentType, PublicKeyEncryptionAlgorithm, - SymmetricCryptoKey, + traits::{encryptable::PrimitiveEncryptableWithContentType, tests::*}, + AsymmetricCryptoKey, ContentFormat, Decryptable, KeyStore, PrimitiveEncryptable, + PublicKeyEncryptionAlgorithm, SymmetricCryptoKey, }; fn test_store() -> KeyStore { diff --git a/crates/bitwarden-crypto/src/traits/mod.rs b/crates/bitwarden-crypto/src/traits/mod.rs index e31dd8124..4b9bab2b6 100644 --- a/crates/bitwarden-crypto/src/traits/mod.rs +++ b/crates/bitwarden-crypto/src/traits/mod.rs @@ -1,7 +1,6 @@ mod encryptable; -pub use encryptable::{ - CompositeEncryptable, PrimitiveEncryptable, PrimitiveEncryptableWithContentType, -}; +pub(crate) use encryptable::PrimitiveEncryptableWithContentType; +pub use encryptable::{CompositeEncryptable, PrimitiveEncryptable}; mod decryptable; pub use decryptable::Decryptable; diff --git a/crates/bitwarden-exporters/src/encrypted_json.rs b/crates/bitwarden-exporters/src/encrypted_json.rs index b5c11736b..32f1c9307 100644 --- a/crates/bitwarden-exporters/src/encrypted_json.rs +++ b/crates/bitwarden-exporters/src/encrypted_json.rs @@ -1,5 +1,5 @@ use base64::{engine::general_purpose::STANDARD, Engine}; -use bitwarden_crypto::{generate_random_bytes, ContentFormat, Kdf, KeyEncryptable, PinKey}; +use bitwarden_crypto::{generate_random_bytes, Kdf, KeyEncryptable, PinKey}; use serde::Serialize; use thiserror::Error; use uuid::Uuid; @@ -57,12 +57,8 @@ pub(crate) fn export_encrypted_json( kdf_iterations, kdf_memory, kdf_parallelism, - enc_key_validation: enc_key_validation - .encrypt_with_key(&key, ContentFormat::Utf8)? - .to_string(), - data: decrypted_export - .encrypt_with_key(&key, ContentFormat::Utf8)? - .to_string(), + enc_key_validation: enc_key_validation.encrypt_with_key(&key)?.to_string(), + data: decrypted_export.encrypt_with_key(&key)?.to_string(), }; Ok(serde_json::to_string_pretty(&encrypted_export)?) diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index cead59615..42c6c080f 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -8,9 +8,8 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - generate_random_bytes, CompositeEncryptable, ContentFormat, CryptoError, Decryptable, - EncString, IdentifyKey, KeyStoreContext, PrimitiveEncryptable, - PrimitiveEncryptableWithContentType, + generate_random_bytes, CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, + KeyStoreContext, OctetStreamContentFormat, PrimitiveEncryptable, SerializedBytes, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -332,8 +331,7 @@ impl CompositeEncryptable for SendView { name: self.name.encrypt(ctx, send_key)?, notes: self.notes.encrypt(ctx, send_key)?, - // In the future, this should support cose key content type - key: k.encrypt(ctx, key, ContentFormat::OctetStream)?, + key: SerializedBytes::::from(k.clone()).encrypt(ctx, key)?, password: self.new_password.as_ref().map(|password| { let password = bitwarden_crypto::pbkdf2(password.as_bytes(), &k, SEND_ITERATIONS); STANDARD.encode(password) diff --git a/crates/bitwarden-send/src/send_client.rs b/crates/bitwarden-send/src/send_client.rs index 69e8e2dc8..d5a5f8a27 100644 --- a/crates/bitwarden-send/src/send_client.rs +++ b/crates/bitwarden-send/src/send_client.rs @@ -1,7 +1,10 @@ use std::path::Path; use bitwarden_core::Client; -use bitwarden_crypto::{ContentFormat, Decryptable, EncString, IdentifyKey, PrimitiveEncryptable}; +use bitwarden_crypto::{ + Decryptable, EncString, IdentifyKey, OctetStreamContentFormat, PrimitiveEncryptable, + SerializedBytes, +}; use thiserror::Error; use crate::{Send, SendListView, SendView}; @@ -127,7 +130,8 @@ impl SendClient { let key = Send::get_key(&mut ctx, &send.key, send.key_identifier())?; - let encrypted = buffer.encrypt(&mut ctx, key, ContentFormat::OctetStream)?; + let encrypted = + SerializedBytes::::from(buffer).encrypt(&mut ctx, key)?; Ok(encrypted.to_buffer()?) } } diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index 5c62df63e..4b165729a 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,7 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, ContentFormat, CryptoError, Decryptable, EncString, IdentifyKey, - KeyStoreContext, PrimitiveEncryptable, PrimitiveEncryptableWithContentType, + CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, + OctetStreamContentFormat, PrimitiveEncryptable, SerializedBytes, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -94,9 +94,8 @@ impl CompositeEncryptable // Because this is a new attachment, we have to generate a key for it, encrypt the contents // with it, and then encrypt the key with the cipher key let attachment_key = ctx.generate_symmetric_key(ATTACHMENT_KEY)?; - let encrypted_contents = - self.contents - .encrypt(ctx, attachment_key, ContentFormat::OctetStream)?; + let encrypted_contents = SerializedBytes::::from(self.contents) + .encrypt(ctx, attachment_key)?; attachment.key = Some(ctx.wrap_symmetric_key(ciphers_key, attachment_key)?); let contents = encrypted_contents.to_buffer()?; diff --git a/crates/bitwarden-vault/src/cipher/card.rs b/crates/bitwarden-vault/src/cipher/card.rs index 5ee740a2d..5f88c3ec3 100644 --- a/crates/bitwarden-vault/src/cipher/card.rs +++ b/crates/bitwarden-vault/src/cipher/card.rs @@ -2,7 +2,7 @@ use bitwarden_api_api::models::CipherCardModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, - PrimitiveEncryptableWithContentType, + PrimitiveEncryptable, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] diff --git a/crates/bitwarden-vault/src/cipher/cipher.rs b/crates/bitwarden-vault/src/cipher/cipher.rs index 57a26136d..6d0c4d068 100644 --- a/crates/bitwarden-vault/src/cipher/cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher.rs @@ -5,7 +5,7 @@ use bitwarden_core::{ }; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, - PrimitiveEncryptableWithContentType, + PrimitiveEncryptable, }; use bitwarden_error::bitwarden_error; use chrono::{DateTime, Utc}; diff --git a/crates/bitwarden-vault/src/cipher/field.rs b/crates/bitwarden-vault/src/cipher/field.rs index 688ca6c3a..d6c988415 100644 --- a/crates/bitwarden-vault/src/cipher/field.rs +++ b/crates/bitwarden-vault/src/cipher/field.rs @@ -5,7 +5,7 @@ use bitwarden_core::{ }; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, - PrimitiveEncryptableWithContentType, + PrimitiveEncryptable, }; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; diff --git a/crates/bitwarden-vault/src/cipher/identity.rs b/crates/bitwarden-vault/src/cipher/identity.rs index 7c5a542c1..d2a68bede 100644 --- a/crates/bitwarden-vault/src/cipher/identity.rs +++ b/crates/bitwarden-vault/src/cipher/identity.rs @@ -2,7 +2,7 @@ use bitwarden_api_api::models::CipherIdentityModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, - PrimitiveEncryptableWithContentType, + PrimitiveEncryptable, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] diff --git a/crates/bitwarden-vault/src/cipher/login.rs b/crates/bitwarden-vault/src/cipher/login.rs index 9f2911b5c..a0230b8c0 100644 --- a/crates/bitwarden-vault/src/cipher/login.rs +++ b/crates/bitwarden-vault/src/cipher/login.rs @@ -6,7 +6,7 @@ use bitwarden_core::{ }; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, - PrimitiveEncryptableWithContentType, + PrimitiveEncryptable, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-vault/src/cipher/ssh_key.rs b/crates/bitwarden-vault/src/cipher/ssh_key.rs index 8ece3991c..0a0c5d699 100644 --- a/crates/bitwarden-vault/src/cipher/ssh_key.rs +++ b/crates/bitwarden-vault/src/cipher/ssh_key.rs @@ -1,7 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, - PrimitiveEncryptableWithContentType, + PrimitiveEncryptable, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] diff --git a/crates/bitwarden-vault/src/folder.rs b/crates/bitwarden-vault/src/folder.rs index 9781b6a99..8168b97aa 100644 --- a/crates/bitwarden-vault/src/folder.rs +++ b/crates/bitwarden-vault/src/folder.rs @@ -5,7 +5,7 @@ use bitwarden_core::{ }; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, - PrimitiveEncryptableWithContentType, + PrimitiveEncryptable, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-vault/src/password_history.rs b/crates/bitwarden-vault/src/password_history.rs index 0fec69f14..14fe469bf 100644 --- a/crates/bitwarden-vault/src/password_history.rs +++ b/crates/bitwarden-vault/src/password_history.rs @@ -2,7 +2,7 @@ use bitwarden_api_api::models::CipherPasswordHistoryModel; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, - PrimitiveEncryptableWithContentType, + PrimitiveEncryptable, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 08b8c968f..aa9f44654 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -2,10 +2,11 @@ use std::str::FromStr; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - AsymmetricCryptoKey, AsymmetricPublicCryptoKey, ContentFormat, CoseSerializable, CryptoError, - Decryptable, EncString, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, - PrimitiveEncryptable, SignatureAlgorithm, SignedPublicKey, SigningKey, SymmetricCryptoKey, - UnsignedSharedKey, VerifyingKey, + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, CoseKeyContentFormat, CoseSerializable, + CryptoError, Decryptable, EncString, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, + OctetStreamContentFormat, Pkcs8PrivateKeyDerContentFormat, PrimitiveEncryptable, + SerializedBytes, SignatureAlgorithm, SignedPublicKey, SigningKey, + SpkiPublicKeyDerContentFormat, SymmetricCryptoKey, UnsignedSharedKey, VerifyingKey, }; use wasm_bindgen::prelude::*; @@ -57,16 +58,14 @@ impl PureCrypto { pub fn symmetric_encrypt_string(plain: String, key: Vec) -> Result { plain - .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?, ContentFormat::Utf8) + .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?) .map(|enc| enc.to_string()) } + /// DEPRECATED: Only used by send keys pub fn symmetric_encrypt_bytes(plain: Vec, key: Vec) -> Result { - plain - .encrypt_with_key( - &SymmetricCryptoKey::try_from(key)?, - ContentFormat::OctetStream, - ) + SerializedBytes::::from(plain) + .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?) .map(|enc| enc.to_string()) } @@ -74,11 +73,8 @@ impl PureCrypto { plain: Vec, key: Vec, ) -> Result, CryptoError> { - plain - .encrypt_with_key( - &SymmetricCryptoKey::try_from(key)?, - ContentFormat::OctetStream, - )? + SerializedBytes::::from(plain) + .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)? .to_buffer() } @@ -183,14 +179,11 @@ impl PureCrypto { 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"), - ContentFormat::Pkcs8, - )? - .to_string()) + Ok( + SerializedBytes::::from(encapsulation_key) + .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))? + .to_string(), + ) } /// Unwraps (decrypts) a wrapped SPKI DER encoded encapsulation (public) key using a symmetric @@ -206,7 +199,6 @@ impl PureCrypto { SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?, )?; - // Note: The order of arguments is different here, and should probably be refactored EncString::from_str(wrapped_key.as_str())? .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) } @@ -224,14 +216,11 @@ impl PureCrypto { 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"), - ContentFormat::Pkcs8, - )? - .to_string()) + Ok( + SerializedBytes::::from(decapsulation_key) + .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))? + .to_string(), + ) } /// Unwraps (decrypts) a wrapped PKCS8 DER encoded decapsulation (private) key using a symmetric @@ -247,7 +236,6 @@ impl PureCrypto { SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from(wrapping_key)?, )?; - // Note: The order of arguments is different here, and should probably be refactored EncString::from_str(wrapped_key.as_str())? .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) } @@ -275,9 +263,11 @@ impl PureCrypto { decapsulation_key: Vec, ) -> Result, CryptoError> { Ok(UnsignedSharedKey::from_str(encapsulated_key.as_str())? - .decapsulate_key_unsigned(&AsymmetricCryptoKey::from_der( - decapsulation_key.as_slice(), - )?)? + .decapsulate_key_unsigned(&AsymmetricCryptoKey::from_der(&SerializedBytes::< + Pkcs8PrivateKeyDerContentFormat, + >::from( + decapsulation_key + ))?)? .to_encoded()) } @@ -288,16 +278,19 @@ impl PureCrypto { wrapping_key: Vec, ) -> Result, CryptoError> { let bytes = Self::symmetric_decrypt_bytes(signing_key, wrapping_key)?; - let signing_key = SigningKey::from_cose(&bytes)?; + let signing_key = + SigningKey::from_cose(&SerializedBytes::::from(bytes))?; let verifying_key = signing_key.to_verifying_key(); - Ok(verifying_key.to_cose()) + Ok(verifying_key.to_cose().as_ref().to_vec()) } /// Returns the algorithm used for the given verifying key. pub fn key_algorithm_for_verifying_key( verifying_key: Vec, ) -> Result { - let verifying_key = VerifyingKey::from_cose(verifying_key.as_slice())?; + let verifying_key = VerifyingKey::from_cose( + &SerializedBytes::::from(verifying_key), + )?; let algorithm = verifying_key.algorithm(); Ok(algorithm) } @@ -311,10 +304,13 @@ impl PureCrypto { verifying_key: Vec, ) -> Result, CryptoError> { let signed_public_key = SignedPublicKey::try_from(signed_public_key)?; - let verifying_key = VerifyingKey::from_cose(verifying_key.as_slice())?; + let verifying_key = VerifyingKey::from_cose( + &SerializedBytes::::from(verifying_key), + )?; signed_public_key .verify_and_unwrap(&verifying_key) .map(|public_key| public_key.to_der())? + .map(|pk| pk.as_ref().to_vec()) } } @@ -540,7 +536,12 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= #[test] fn test_wrap_encapsulation_key() { let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); - let encapsulation_key = decapsulation_key.to_public_key().to_der().unwrap(); + let encapsulation_key = decapsulation_key + .to_public_key() + .to_der() + .unwrap() + .as_ref() + .to_vec(); let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac(); let wrapped_key = PureCrypto::wrap_encapsulation_key(encapsulation_key.clone(), wrapping_key.clone()) @@ -555,13 +556,16 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac(); let wrapped_key = PureCrypto::wrap_decapsulation_key( - decapsulation_key.to_der().unwrap(), + decapsulation_key.to_der().unwrap().as_ref().to_vec(), 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); + assert_eq!( + decapsulation_key.to_der().unwrap().as_ref().to_vec(), + unwrapped_key + ); } #[test] @@ -569,12 +573,14 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= 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_key().to_der().unwrap(); - let encapsulated_key = - PureCrypto::encapsulate_key_unsigned(shared_key.clone(), encapsulation_key.clone()) - .unwrap(); + let encapsulated_key = PureCrypto::encapsulate_key_unsigned( + shared_key.clone(), + encapsulation_key.clone().as_ref().to_vec(), + ) + .unwrap(); let unwrapped_key = PureCrypto::decapsulate_key_unsigned( encapsulated_key, - decapsulation_key.to_der().unwrap(), + decapsulation_key.to_der().unwrap().as_ref().to_vec(), ) .unwrap(); assert_eq!(shared_key, unwrapped_key); @@ -582,9 +588,13 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= #[test] fn test_key_algorithm_for_verifying_key() { - let verifying_key = VerifyingKey::from_cose(VERIFYING_KEY).unwrap(); + let verifying_key = VerifyingKey::from_cose( + &SerializedBytes::::from(VERIFYING_KEY.to_vec()), + ) + .unwrap(); let algorithm = - PureCrypto::key_algorithm_for_verifying_key(verifying_key.to_cose()).unwrap(); + PureCrypto::key_algorithm_for_verifying_key(verifying_key.to_cose().as_ref().to_vec()) + .unwrap(); assert_eq!(algorithm, SignatureAlgorithm::Ed25519); } @@ -595,14 +605,19 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= SIGNING_KEY_WRAPPING_KEY.to_vec(), ) .unwrap(); - let verifying_key = VerifyingKey::from_cose(VERIFYING_KEY).unwrap(); + let verifying_key = VerifyingKey::from_cose( + &SerializedBytes::::from(VERIFYING_KEY.to_vec()), + ) + .unwrap(); let verifying_key_derived = PureCrypto::verifying_key_for_signing_key( wrapped_signing_key.to_string(), SIGNING_KEY_WRAPPING_KEY.to_vec(), ) .unwrap(); - let verifying_key_derived = - VerifyingKey::from_cose(verifying_key_derived.as_slice()).unwrap(); + let verifying_key_derived = VerifyingKey::from_cose( + &SerializedBytes::::from(verifying_key_derived), + ) + .unwrap(); assert_eq!(verifying_key.to_cose(), verifying_key_derived.to_cose()); } From 9f334fb3f65c3b9b550329517b27e09a2d87b7f9 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 19 Jun 2025 12:54:32 +0200 Subject: [PATCH 052/124] Fix readme --- crates/bitwarden-crypto/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/README.md b/crates/bitwarden-crypto/README.md index c81a8bdb6..60ea5836f 100644 --- a/crates/bitwarden-crypto/README.md +++ b/crates/bitwarden-crypto/README.md @@ -19,7 +19,7 @@ async fn example() -> Result<(), CryptoError> { let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let data = "Hello, World!".to_owned(); - let encrypted = data.clone().encrypt_with_key(&key, ContentFormat::Utf8)?; + let encrypted = data.clone().encrypt_with_key(&key).unwrap(); let decrypted: String = encrypted.decrypt_with_key(&key)?; assert_eq!(data, decrypted); From 02cc7b328c474ed584298f489e507a76ac6cb781 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 19 Jun 2025 13:03:22 +0200 Subject: [PATCH 053/124] Clippy cleanup --- crates/bitwarden-crypto/src/content_format.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bitwarden-crypto/src/content_format.rs b/crates/bitwarden-crypto/src/content_format.rs index fb7605414..40e248699 100644 --- a/crates/bitwarden-crypto/src/content_format.rs +++ b/crates/bitwarden-crypto/src/content_format.rs @@ -166,14 +166,14 @@ impl KeyEncryptable for &S } } -impl Into> for String { - fn into(self) -> SerializedBytes { - SerializedBytes::from(self.into_bytes()) +impl From for SerializedBytes { + fn from(val: String) -> Self { + SerializedBytes::from(val.into_bytes()) } } -impl Into> for &str { - fn into(self) -> SerializedBytes { - SerializedBytes::from(self.as_bytes().to_vec()) +impl From<&str> for SerializedBytes { + fn from(val: &str) -> Self { + SerializedBytes::from(val.as_bytes().to_vec()) } } From f145eb3b4d8a002540285ca9a1f211cff67ed29f Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 19 Jun 2025 13:08:23 +0200 Subject: [PATCH 054/124] Fix clippy errors --- .../src/client/encryption_settings.rs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index 26d8d5f29..7d7e40722 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -27,6 +27,9 @@ pub enum EncryptionSettingsError { #[error("Invalid private key")] InvalidPrivateKey, + #[error("Invalid signing key")] + InvalidSigningKey, + #[error(transparent)] MissingPrivateKey(#[from] MissingPrivateKeyError), @@ -72,26 +75,26 @@ impl EncryptionSettings { // .map_err(|_| EncryptionSettingsError::InvalidPrivateKey)?, // ) }; - let signing_key = signing_key.map(|key| { - use bitwarden_crypto::{CoseKeyContentFormat, SerializedBytes}; - - let dec: Vec = key.decrypt_with_key(&user_key).unwrap(); - let dec: SerializedBytes = SerializedBytes::from(dec); - SigningKey::from_cose(&dec).unwrap() - }); // FIXME: [PM-18098] When this is part of crypto we won't need to use deprecated methods #[allow(deprecated)] { let mut ctx = store.context_mut(); - ctx.set_symmetric_key(SymmetricKeyId::User, user_key)?; - if let Some(private_key) = private_key { - ctx.set_asymmetric_key(AsymmetricKeyId::UserPrivateKey, private_key)?; - } if let Some(signing_key) = signing_key { + use bitwarden_crypto::SerializedBytes; + + let dec: Vec = signing_key.decrypt_with_key(&user_key).unwrap(); + let signing_key = SigningKey::from_cose(&SerializedBytes::from(dec)) + .map_err(|_| EncryptionSettingsError::InvalidSigningKey)?; ctx.set_signing_key(SigningKeyId::UserSigningKey, signing_key)?; } + + if let Some(private_key) = private_key { + ctx.set_asymmetric_key(AsymmetricKeyId::UserPrivateKey, private_key)?; + } + + ctx.set_symmetric_key(SymmetricKeyId::User, user_key)?; } Ok(()) From 99d909cc8a459631c91fde7f4e5baf657d90ca0b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 19 Jun 2025 13:08:53 +0200 Subject: [PATCH 055/124] Fix clippy errors --- crates/bitwarden-core/src/client/encryption_settings.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index 7d7e40722..35a23d833 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -84,7 +84,9 @@ impl EncryptionSettings { if let Some(signing_key) = signing_key { use bitwarden_crypto::SerializedBytes; - let dec: Vec = signing_key.decrypt_with_key(&user_key).unwrap(); + let dec: Vec = signing_key + .decrypt_with_key(&user_key) + .map_err(|_| EncryptionSettingsError::InvalidSigningKey)?; let signing_key = SigningKey::from_cose(&SerializedBytes::from(dec)) .map_err(|_| EncryptionSettingsError::InvalidSigningKey)?; ctx.set_signing_key(SigningKeyId::UserSigningKey, signing_key)?; From d47e8c0a410c7624791a05e9c72f36e54247ab55 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 19 Jun 2025 14:34:53 +0200 Subject: [PATCH 056/124] Switch bitwarden symmetric crypto key bytes to serialized bytes generic --- .../bitwarden-core/src/auth/auth_request.rs | 62 +++---- .../src/auth/login/access_token.rs | 8 +- crates/bitwarden-crypto/src/content_format.rs | 9 +- .../src/enc_string/asymmetric.rs | 12 +- .../bitwarden-crypto/src/keys/device_key.rs | 18 +- .../bitwarden-crypto/src/keys/master_key.rs | 15 +- .../src/keys/symmetric_crypto_key.rs | 161 ++++++++++++------ crates/bitwarden-crypto/src/store/context.rs | 65 ++++--- .../src/pure_crypto.rs | 77 ++++++--- 9 files changed, 271 insertions(+), 156 deletions(-) diff --git a/crates/bitwarden-core/src/auth/auth_request.rs b/crates/bitwarden-core/src/auth/auth_request.rs index 941aeebc1..0bc3e7513 100644 --- a/crates/bitwarden-core/src/auth/auth_request.rs +++ b/crates/bitwarden-core/src/auth/auth_request.rs @@ -106,37 +106,14 @@ pub(crate) fn approve_auth_request( )?) } -#[test] -fn test_auth_request() { - let request = new_auth_request("test@bitwarden.com").unwrap(); - - let secret = vec![ - 111, 32, 97, 169, 4, 241, 174, 74, 239, 206, 113, 86, 174, 68, 216, 238, 52, 85, 156, 27, - 134, 149, 54, 55, 91, 147, 45, 130, 131, 237, 51, 31, 191, 106, 155, 14, 160, 82, 47, 40, - 96, 31, 114, 127, 212, 187, 167, 110, 205, 116, 198, 243, 218, 72, 137, 53, 248, 43, 255, - 67, 35, 61, 245, 93, - ]; - - let private_key = - AsymmetricCryptoKey::from_der(&STANDARD.decode(&request.private_key).unwrap().into()) - .unwrap(); - - let encrypted = UnsignedSharedKey::encapsulate_key_unsigned( - &SymmetricCryptoKey::try_from(secret.clone()).unwrap(), - &private_key.to_public_key(), - ) - .unwrap(); - - let decrypted = auth_request_decrypt_user_key(request.private_key, encrypted).unwrap(); - - assert_eq!(decrypted.to_encoded(), secret); -} - #[cfg(test)] mod tests { use std::num::NonZeroU32; - use bitwarden_crypto::{Kdf, MasterKey, SerializedBytes, SpkiPublicKeyDerContentFormat}; + use bitwarden_crypto::{ + BitwardenLegacyKeyContentFormat, Kdf, MasterKey, SerializedBytes, + SpkiPublicKeyDerContentFormat, + }; use super::*; use crate::key_management::{ @@ -144,6 +121,33 @@ mod tests { SymmetricKeyId, }; + #[test] + fn test_auth_request() { + let request = new_auth_request("test@bitwarden.com").unwrap(); + + let secret = vec![ + 111, 32, 97, 169, 4, 241, 174, 74, 239, 206, 113, 86, 174, 68, 216, 238, 52, 85, 156, + 27, 134, 149, 54, 55, 91, 147, 45, 130, 131, 237, 51, 31, 191, 106, 155, 14, 160, 82, + 47, 40, 96, 31, 114, 127, 212, 187, 167, 110, 205, 116, 198, 243, 218, 72, 137, 53, + 248, 43, 255, 67, 35, 61, 245, 93, + ]; + + let private_key = + AsymmetricCryptoKey::from_der(&STANDARD.decode(&request.private_key).unwrap().into()) + .unwrap(); + + let secret = SerializedBytes::::from(secret); + let encrypted = UnsignedSharedKey::encapsulate_key_unsigned( + &SymmetricCryptoKey::try_from(&secret).unwrap(), + &private_key.to_public_key(), + ) + .unwrap(); + + let decrypted = auth_request_decrypt_user_key(request.private_key, encrypted).unwrap(); + + assert_eq!(decrypted.to_encoded().to_vec(), secret.to_vec()); + } + #[test] fn test_approve() { let client = Client::new(None); @@ -183,7 +187,7 @@ mod tests { let dec = auth_request_decrypt_user_key(private_key.to_owned(), enc_user_key).unwrap(); assert_eq!( - &dec.to_encoded(), + &dec.to_encoded().to_vec(), &[ 201, 37, 234, 213, 21, 75, 40, 70, 149, 213, 234, 16, 19, 251, 162, 245, 161, 74, 34, 245, 211, 151, 211, 192, 95, 10, 117, 50, 88, 223, 23, 157 @@ -202,7 +206,7 @@ mod tests { .unwrap(); assert_eq!( - &dec.to_encoded(), + &dec.to_encoded().to_vec(), &[ 109, 128, 172, 147, 206, 123, 134, 95, 16, 36, 155, 113, 201, 18, 186, 230, 216, 212, 173, 188, 74, 11, 134, 131, 137, 242, 105, 178, 105, 126, 52, 139, 248, 91, diff --git a/crates/bitwarden-core/src/auth/login/access_token.rs b/crates/bitwarden-core/src/auth/login/access_token.rs index 1563bc10b..2888156a9 100644 --- a/crates/bitwarden-core/src/auth/login/access_token.rs +++ b/crates/bitwarden-core/src/auth/login/access_token.rs @@ -1,7 +1,9 @@ use std::path::{Path, PathBuf}; use base64::{engine::general_purpose::STANDARD, Engine}; -use bitwarden_crypto::{EncString, KeyDecryptable, SymmetricCryptoKey}; +use bitwarden_crypto::{ + BitwardenLegacyKeyContentFormat, EncString, KeyDecryptable, SerializedBytes, SymmetricCryptoKey, +}; use chrono::Utc; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -67,7 +69,9 @@ pub(crate) async fn login_access_token( let payload: Payload = serde_json::from_slice(&decrypted_payload)?; let encryption_key = STANDARD.decode(&payload.encryption_key)?; - let encryption_key = SymmetricCryptoKey::try_from(encryption_key)?; + let encryption_key = + SerializedBytes::::from(encryption_key); + let encryption_key = SymmetricCryptoKey::try_from(&encryption_key)?; let access_token_obj: JwtToken = r.access_token.parse()?; diff --git a/crates/bitwarden-crypto/src/content_format.rs b/crates/bitwarden-crypto/src/content_format.rs index 40e248699..c2f41258f 100644 --- a/crates/bitwarden-crypto/src/content_format.rs +++ b/crates/bitwarden-crypto/src/content_format.rs @@ -79,6 +79,13 @@ impl AsRef<[u8]> for SerializedBytes { } } +impl SerializedBytes { + /// Returns the serialized bytes as a `Vec`. + pub fn to_vec(&self) -> Vec { + self.inner.clone() + } +} + /// Content format for UTF-8 encoded text. Used for most text messages. #[derive(PartialEq, Eq, Clone, Debug)] pub(crate) struct Utf8ContentFormat; @@ -128,7 +135,7 @@ impl CoseContentFormat for CoseKeyContentFormat {} /// A legacy content format for Bitwarden keys. See `ContentFormat::BitwardenLegacyKey` #[allow(unused)] #[derive(PartialEq, Eq, Clone, Debug)] -pub(crate) struct BitwardenLegacyKeyContentFormat; +pub struct BitwardenLegacyKeyContentFormat; impl ConstContentFormat for BitwardenLegacyKeyContentFormat { fn content_format() -> ContentFormat { ContentFormat::BitwardenLegacyKey diff --git a/crates/bitwarden-crypto/src/enc_string/asymmetric.rs b/crates/bitwarden-crypto/src/enc_string/asymmetric.rs index 4ce4b0252..457e41b42 100644 --- a/crates/bitwarden-crypto/src/enc_string/asymmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/asymmetric.rs @@ -10,8 +10,8 @@ use crate::{ error::{CryptoError, EncStringParseError, Result}, rsa::encrypt_rsa2048_oaep_sha1, util::FromStrVisitor, - AsymmetricCryptoKey, AsymmetricPublicCryptoKey, RawPrivateKey, RawPublicKey, - SymmetricCryptoKey, + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyContentFormat, RawPrivateKey, + RawPublicKey, SerializedBytes, SymmetricCryptoKey, }; // This module is a workaround to avoid deprecated warnings that come from the ZeroizeOnDrop // macro expansion @@ -169,7 +169,7 @@ impl UnsignedSharedKey { Ok(UnsignedSharedKey::Rsa2048_OaepSha1_B64 { data: encrypt_rsa2048_oaep_sha1( rsa_public_key, - &encapsulated_key.to_encoded(), + &encapsulated_key.to_encoded().as_ref().to_vec(), )?, }) } @@ -200,7 +200,7 @@ impl UnsignedSharedKey { match decapsulation_key.inner() { RawPrivateKey::RsaOaepSha1(rsa_private_key) => { use UnsignedSharedKey::*; - let mut key_data = match self { + let key_data = match self { Rsa2048_OaepSha256_B64 { data } => { rsa_private_key.decrypt(Oaep::new::(), data) } @@ -217,7 +217,9 @@ impl UnsignedSharedKey { } } .map_err(|_| CryptoError::KeyDecrypt)?; - SymmetricCryptoKey::try_from(key_data.as_mut_slice()) + SymmetricCryptoKey::try_from( + &SerializedBytes::::from(key_data), + ) } } } diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index 0948b06a6..37f6c1f69 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -91,7 +91,7 @@ impl TryFrom for DeviceKey { #[cfg(test)] mod tests { use super::*; - use crate::derive_symmetric_key; + use crate::{derive_symmetric_key, BitwardenLegacyKeyContentFormat}; #[test] fn test_trust_device() { @@ -114,21 +114,29 @@ mod tests { #[test] fn test_decrypt_user_key() { // Example keys from desktop app - let user_key: &mut [u8] = &mut [ + let user_key: &[u8] = &[ 109, 128, 172, 147, 206, 123, 134, 95, 16, 36, 155, 113, 201, 18, 186, 230, 216, 212, 173, 188, 74, 11, 134, 131, 137, 242, 105, 178, 105, 126, 52, 139, 248, 91, 215, 21, 128, 91, 226, 222, 165, 67, 251, 34, 83, 81, 77, 147, 225, 76, 13, 41, 102, 45, 183, 218, 106, 89, 254, 208, 251, 101, 130, 10, ]; - let user_key = SymmetricCryptoKey::try_from(user_key).unwrap(); + let user_key = SymmetricCryptoKey::try_from(&SerializedBytes::< + BitwardenLegacyKeyContentFormat, + >::from(user_key)) + .unwrap(); - let key_data: &mut [u8] = &mut [ + let key_data: &[u8] = &[ 114, 235, 60, 115, 172, 156, 203, 145, 195, 130, 215, 250, 88, 146, 215, 230, 12, 109, 245, 222, 54, 217, 255, 211, 221, 105, 230, 236, 65, 52, 209, 133, 76, 208, 113, 254, 194, 216, 156, 19, 230, 62, 32, 93, 87, 7, 144, 156, 117, 142, 250, 32, 182, 118, 187, 8, 247, 7, 203, 201, 65, 147, 206, 247, ]; - let device_key = DeviceKey(key_data.try_into().unwrap()); + let device_key = DeviceKey( + SymmetricCryptoKey::try_from( + &SerializedBytes::::from(key_data), + ) + .unwrap(), + ); let protected_user_key: UnsignedSharedKey = "4.f+VbbacRhO2q4MOUSdt1AIjQ2FuLAvg4aDxJMXAh3VxvbmUADj8Ct/R7XEpPUqApmbRS566jS0eRVy8Sk08ogoCdj1IFN9VsIky2i2X1WHK1fUnr3UBmXE3tl2NPBbx56U+h73S2jNTSyet2W18Jg2q7/w8KIhR3J41QrG9aGoOTN93to3hb5W4z6rdrSI0e7GkizbwcIA0NH7Z1JyAhrjPm9+tjRjg060YbEbGaWTAOkZWfgbLjr8bY455DteO2xxG139cOx7EBo66N+YhjsLi0ozkeUyPQkoWBdKMcQllS7jCfB4fDyJA05ALTbk74syKkvqFxqwmQbg+aVn+dcw==".parse().unwrap(); diff --git a/crates/bitwarden-crypto/src/keys/master_key.rs b/crates/bitwarden-crypto/src/keys/master_key.rs index 2c15806e5..37e8062e3 100644 --- a/crates/bitwarden-crypto/src/keys/master_key.rs +++ b/crates/bitwarden-crypto/src/keys/master_key.rs @@ -4,7 +4,7 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use generic_array::GenericArray; use rand::Rng; use typenum::U32; -use zeroize::{Zeroize, Zeroizing}; +use zeroize::Zeroize; use super::{ kdf::{Kdf, KdfDerivedKeyMaterial}, @@ -12,7 +12,8 @@ use super::{ }; use crate::{ util::{self}, - CryptoError, EncString, KeyDecryptable, Result, SymmetricCryptoKey, UserKey, + BitwardenLegacyKeyContentFormat, CryptoError, EncString, KeyDecryptable, Result, + SerializedBytes, SymmetricCryptoKey, UserKey, }; #[allow(missing_docs)] @@ -129,8 +130,8 @@ pub(super) fn encrypt_user_key( user_key: &SymmetricCryptoKey, ) -> Result { let stretched_master_key = stretch_key(master_key)?; - let user_key_bytes = Zeroizing::new(user_key.to_encoded()); - EncString::encrypt_aes256_hmac(&user_key_bytes, &stretched_master_key) + let user_key_bytes = user_key.to_encoded(); + EncString::encrypt_aes256_hmac(user_key_bytes.as_ref(), &stretched_master_key) } /// Helper function to decrypt a user key with a master or pin key or key-connector-key. @@ -138,7 +139,7 @@ pub(super) fn decrypt_user_key( key: &Pin>>, user_key: EncString, ) -> Result { - let mut dec: Vec = match user_key { + let dec: Vec = match user_key { // Legacy. user_keys were encrypted using `Aes256Cbc_B64` a long time ago. We've since // moved to using `Aes256Cbc_HmacSha256_B64`. However, we still need to support // decrypting these old keys. @@ -159,7 +160,9 @@ pub(super) fn decrypt_user_key( } }; - SymmetricCryptoKey::try_from(dec.as_mut_slice()) + SymmetricCryptoKey::try_from(&SerializedBytes::::from( + dec, + )) } /// Generate a new random user key and encrypt it with the master key. diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index 17e959079..f33b0f094 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -18,7 +18,10 @@ use super::{ key_encryptable::CryptoKey, key_id::{KeyId, KEY_ID_SIZE}, }; -use crate::{cose, CryptoError}; +use crate::{ + cose, BitwardenLegacyKeyContentFormat, ContentFormat, CoseKeyContentFormat, CryptoError, + SerializedBytes, +}; /// [Aes256CbcKey] is a symmetric encryption key, consisting of one 256-bit key, /// used to decrypt legacy type 0 enc strings. The data is not authenticated @@ -145,13 +148,17 @@ impl SymmetricCryptoKey { /// This can be used for storage and transmission in the old byte array format. /// When the wrapping key is a COSE key, and the wrapped key is a COSE key, then this should /// not use the byte representation but instead use the COSE key representation. - pub fn to_encoded(&self) -> Vec { - let mut encoded_key = self.to_encoded_raw(); - match self { - Self::Aes256CbcKey(_) | Self::Aes256CbcHmacKey(_) => encoded_key, - Self::XChaCha20Poly1305Key(_) => { + pub fn to_encoded(&self) -> SerializedBytes { + let encoded_key = self.to_encoded_raw(); + match encoded_key { + EncodedSymmetricKey::Aes256CbcKey(_) | EncodedSymmetricKey::Aes256CbcHmacKey(_) => { + let encoded_key: Vec = encoded_key.into(); + SerializedBytes::from(encoded_key) + } + EncodedSymmetricKey::XChaCha20Poly1305Key(_) => { + let mut encoded_key: Vec = encoded_key.into(); pad_key(&mut encoded_key, Self::AES256_CBC_HMAC_KEY_LEN + 1); - encoded_key + SerializedBytes::from(encoded_key) } } } @@ -182,14 +189,16 @@ impl SymmetricCryptoKey { /// format hints an the key type, or can be represented as a byte array, if padded to be /// larger than the byte array representation of the other key types using the /// aforementioned [SymmetricCryptoKey::to_encoded] function. - pub(crate) fn to_encoded_raw(&self) -> Vec { + pub(crate) fn to_encoded_raw(&self) -> EncodedSymmetricKey { match self { - Self::Aes256CbcKey(key) => key.enc_key.to_vec(), + Self::Aes256CbcKey(key) => { + EncodedSymmetricKey::Aes256CbcKey(key.enc_key.to_vec().into()) + } Self::Aes256CbcHmacKey(key) => { let mut buf = Vec::with_capacity(64); buf.extend_from_slice(&key.enc_key); buf.extend_from_slice(&key.mac_key); - buf + EncodedSymmetricKey::Aes256CbcHmacKey(buf.into()) } Self::XChaCha20Poly1305Key(key) => { let builder = coset::CoseKeyBuilder::new_symmetric_key(key.enc_key.to_vec()); @@ -203,9 +212,12 @@ impl SymmetricCryptoKey { cose_key.alg = Some(RegisteredLabelWithPrivate::PrivateUse( cose::XCHACHA20_POLY1305, )); - cose_key - .to_vec() - .expect("cose key serialization should not fail") + EncodedSymmetricKey::XChaCha20Poly1305Key( + cose_key + .to_vec() + .expect("cose key serialization should not fail") + .into(), + ) } } } @@ -252,60 +264,69 @@ impl TryFrom for SymmetricCryptoKey { type Error = CryptoError; fn try_from(value: String) -> Result { - let b = STANDARD + let bytes = STANDARD .decode(value) .map_err(|_| CryptoError::InvalidKey)?; - Self::try_from(b) + Self::try_from(&SerializedBytes::::from( + bytes, + )) } } -impl TryFrom> for SymmetricCryptoKey { +impl TryFrom<&SerializedBytes> for SymmetricCryptoKey { type Error = CryptoError; - fn try_from(mut value: Vec) -> Result { - Self::try_from(value.as_mut_slice()) - } -} - -impl TryFrom<&mut [u8]> for SymmetricCryptoKey { - type Error = CryptoError; + fn try_from( + value: &SerializedBytes, + ) -> Result { + let slice = value.as_ref(); - /// Note: This function takes the byte slice by mutable reference and will zero out all - /// the data in it. This is to prevent the key from being left in memory. - fn try_from(value: &mut [u8]) -> Result { // Raw byte serialized keys are either 32, 64, or more bytes long. If they are 32/64, they // are the raw serializations of the AES256-CBC, and AES256-CBC-HMAC keys. If they // are longer, they are COSE keys. The COSE keys are padded to the minimum length of // 65 bytes, when serialized to raw byte arrays. - let result = if value.len() == Self::AES256_CBC_HMAC_KEY_LEN { - let mut enc_key = Box::pin(GenericArray::::default()); - let mut mac_key = Box::pin(GenericArray::::default()); - - enc_key.copy_from_slice(&value[..32]); - mac_key.copy_from_slice(&value[32..]); - - Ok(Self::Aes256CbcHmacKey(Aes256CbcHmacKey { - enc_key, - mac_key, - })) - } else if value.len() == Self::AES256_CBC_KEY_LEN { - let mut enc_key = Box::pin(GenericArray::::default()); - - enc_key.copy_from_slice(&value[..Self::AES256_CBC_KEY_LEN]); - - Ok(Self::Aes256CbcKey(Aes256CbcKey { enc_key })) - } else if value.len() > Self::AES256_CBC_HMAC_KEY_LEN { - let unpadded_value = unpad_key(value)?; + let result = if slice.len() == Self::AES256_CBC_HMAC_KEY_LEN { + Self::try_from(EncodedSymmetricKey::Aes256CbcHmacKey(value.clone())) + } else if slice.len() == Self::AES256_CBC_KEY_LEN { + Self::try_from(EncodedSymmetricKey::Aes256CbcKey(value.clone())) + } else if slice.len() > Self::AES256_CBC_HMAC_KEY_LEN { + let unpadded_value = unpad_key(slice)?; Ok(Self::try_from_cose(unpadded_value)?) } else { Err(CryptoError::InvalidKeyLen) }; - value.zeroize(); result } } +impl TryFrom for SymmetricCryptoKey { + type Error = CryptoError; + + fn try_from(value: EncodedSymmetricKey) -> Result { + match value { + EncodedSymmetricKey::Aes256CbcKey(key) => { + let mut enc_key = Box::pin(GenericArray::::default()); + enc_key.copy_from_slice(&key.as_ref()[..Self::AES256_CBC_KEY_LEN]); + Ok(Self::Aes256CbcKey(Aes256CbcKey { enc_key })) + } + EncodedSymmetricKey::Aes256CbcHmacKey(key) => { + let mut enc_key = Box::pin(GenericArray::::default()); + enc_key.copy_from_slice(&key.as_ref()[..32]); + + let mut mac_key = Box::pin(GenericArray::::default()); + mac_key.copy_from_slice(&key.as_ref()[32..]); + + Ok(Self::Aes256CbcHmacKey(Aes256CbcHmacKey { + enc_key, + mac_key, + })) + } + EncodedSymmetricKey::XChaCha20Poly1305Key(key) => Self::try_from_cose(key.as_ref()), + } + } +} + impl CryptoKey for SymmetricCryptoKey {} // We manually implement these to make sure we don't print any sensitive data @@ -372,6 +393,32 @@ fn unpad_key(key_bytes: &[u8]) -> Result<&[u8], CryptoError> { crate::keys::utils::unpad_bytes(key_bytes).map_err(|_| CryptoError::InvalidKey) } +/// An enum to represent the different encodings of symmetric crypto keys. +pub enum EncodedSymmetricKey { + Aes256CbcKey(SerializedBytes), + Aes256CbcHmacKey(SerializedBytes), + XChaCha20Poly1305Key(SerializedBytes), +} +impl Into> for EncodedSymmetricKey { + fn into(self) -> Vec { + match self { + EncodedSymmetricKey::Aes256CbcKey(key) => key.as_ref().to_vec(), + EncodedSymmetricKey::Aes256CbcHmacKey(key) => key.as_ref().to_vec(), + EncodedSymmetricKey::XChaCha20Poly1305Key(key) => key.as_ref().to_vec(), + } + } +} +impl EncodedSymmetricKey { + pub fn content_format(&self) -> ContentFormat { + match self { + EncodedSymmetricKey::Aes256CbcKey(_) | EncodedSymmetricKey::Aes256CbcHmacKey(_) => { + ContentFormat::BitwardenLegacyKey + } + EncodedSymmetricKey::XChaCha20Poly1305Key(_) => ContentFormat::CoseKey, + } + } +} + /// Test only helper for deriving a symmetric key. #[cfg(test)] pub fn derive_symmetric_key(name: &str) -> Aes256CbcHmacKey { @@ -392,7 +439,8 @@ mod tests { use super::{derive_symmetric_key, SymmetricCryptoKey}; use crate::{ keys::symmetric_crypto_key::{pad_key, unpad_key}, - Aes256CbcHmacKey, Aes256CbcKey, XChaCha20Poly1305Key, + Aes256CbcHmacKey, Aes256CbcKey, BitwardenLegacyKeyContentFormat, SerializedBytes, + XChaCha20Poly1305Key, }; #[test] @@ -411,14 +459,15 @@ mod tests { fn test_encode_decode_old_symmetric_crypto_key() { let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let encoded = key.to_encoded(); - let decoded = SymmetricCryptoKey::try_from(encoded).unwrap(); + let decoded = SymmetricCryptoKey::try_from(&encoded).unwrap(); assert_eq!(key, decoded); } #[test] fn test_decode_new_symmetric_crypto_key() { - let key_b64 = STANDARD.decode("pQEEAlDib+JxbqMBlcd3KTUesbufAzoAARFvBIQDBAUGIFggt79surJXmqhPhYuuqi9ZyPfieebmtw2OsmN5SDrb4yUB").unwrap(); - let key = SymmetricCryptoKey::try_from(key_b64).unwrap(); + let key = STANDARD.decode("pQEEAlDib+JxbqMBlcd3KTUesbufAzoAARFvBIQDBAUGIFggt79surJXmqhPhYuuqi9ZyPfieebmtw2OsmN5SDrb4yUB").unwrap(); + let key = SerializedBytes::::from(key); + let key = SymmetricCryptoKey::try_from(&key).unwrap(); match key { SymmetricCryptoKey::XChaCha20Poly1305Key(_) => (), _ => panic!("Invalid key type"), @@ -429,7 +478,7 @@ mod tests { fn test_encode_xchacha20_poly1305_key() { let key = SymmetricCryptoKey::make_xchacha20_poly1305_key(); let encoded = key.to_encoded(); - let decoded = SymmetricCryptoKey::try_from(encoded).unwrap(); + let decoded = SymmetricCryptoKey::try_from(&encoded).unwrap(); assert_eq!(key, decoded); } @@ -481,8 +530,14 @@ mod tests { #[test] fn test_eq_aes_cbc() { - let key1 = SymmetricCryptoKey::try_from(vec![1u8; 32]).unwrap(); - let key2 = SymmetricCryptoKey::try_from(vec![2u8; 32]).unwrap(); + let key1 = SymmetricCryptoKey::try_from( + &SerializedBytes::::from(vec![1u8; 32]), + ) + .unwrap(); + let key2 = SymmetricCryptoKey::try_from( + &SerializedBytes::::from(vec![2u8; 32]), + ) + .unwrap(); assert_ne!(key1, key2); let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap(); assert_eq!(key1, key3); diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 78149935c..83e440ef7 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -9,9 +9,9 @@ use zeroize::Zeroizing; use super::KeyStoreInner; use crate::{ derive_shareable_key, error::UnsupportedOperation, signing, store::backend::StoreBackend, - AsymmetricCryptoKey, ContentFormat, CryptoError, EncString, KeyId, KeyIds, Result, Signature, - SignatureAlgorithm, SignedObject, SignedPublicKey, SignedPublicKeyMessage, SigningKey, - SymmetricCryptoKey, UnsignedSharedKey, + AsymmetricCryptoKey, BitwardenLegacyKeyContentFormat, ContentFormat, CryptoError, EncString, + KeyId, KeyIds, Result, SerializedBytes, Signature, SignatureAlgorithm, SignedObject, + SignedPublicKey, SignedPublicKeyMessage, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, }; /// The context of a crypto operation using [super::KeyStore] @@ -156,22 +156,26 @@ impl KeyStoreContext<'_, Ids> { let key = match (wrapped_key, wrapping_key) { (EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => { - SymmetricCryptoKey::try_from(crate::aes::decrypt_aes256( - iv, - data.clone(), - &key.enc_key, - )?)? + SymmetricCryptoKey::try_from( + &SerializedBytes::::from( + crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)?, + ), + )? } ( EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data }, SymmetricCryptoKey::Aes256CbcHmacKey(key), - ) => SymmetricCryptoKey::try_from(crate::aes::decrypt_aes256_hmac( - iv, - mac, - data.clone(), - &key.mac_key, - &key.enc_key, - )?)?, + ) => SymmetricCryptoKey::try_from( + &SerializedBytes::::from( + crate::aes::decrypt_aes256_hmac( + iv, + mac, + data.clone(), + &key.mac_key, + &key.enc_key, + )?, + ), + )?, ( EncString::Cose_Encrypt0_B64 { data }, SymmetricCryptoKey::XChaCha20Poly1305Key(key), @@ -180,7 +184,11 @@ impl KeyStoreContext<'_, Ids> { crate::cose::decrypt_xchacha20_poly1305(data, key)?; match content_format { ContentFormat::BitwardenLegacyKey => { - SymmetricCryptoKey::try_from(content_bytes)? + SymmetricCryptoKey::try_from(&SerializedBytes::< + BitwardenLegacyKeyContentFormat, + >::from( + content_bytes + ))? } ContentFormat::CoseKey => SymmetricCryptoKey::try_from_cose(&content_bytes)?, _ => return Err(CryptoError::InvalidKey), @@ -224,21 +232,22 @@ impl KeyStoreContext<'_, Ids> { Aes256CbcHmacKey(_) | Aes256CbcKey(_) | XChaCha20Poly1305Key(_), ) => self.encrypt_data_with_symmetric_key( wrapping_key, - key_to_wrap_instance.to_encoded().as_slice(), + key_to_wrap_instance + .to_encoded() + .as_ref() + .to_vec() + .as_slice(), ContentFormat::BitwardenLegacyKey, ), - (XChaCha20Poly1305Key(_), Aes256CbcHmacKey(_) | Aes256CbcKey(_)) => self - .encrypt_data_with_symmetric_key( - wrapping_key, - key_to_wrap_instance.to_encoded().as_slice(), - ContentFormat::BitwardenLegacyKey, - ), - (XChaCha20Poly1305Key(_), XChaCha20Poly1305Key(_)) => self - .encrypt_data_with_symmetric_key( + (XChaCha20Poly1305Key(_), _) => { + let encoded = key_to_wrap_instance.to_encoded_raw(); + let content_format = encoded.content_format(); + self.encrypt_data_with_symmetric_key( wrapping_key, - key_to_wrap_instance.to_encoded_raw().as_slice(), - ContentFormat::CoseKey, - ), + Into::>::into(encoded).as_slice(), + content_format, + ) + } _ => Err(CryptoError::OperationNotSupported( UnsupportedOperation::EncryptionNotImplementedForKey, )), diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index aa9f44654..811cbf916 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -2,11 +2,12 @@ use std::str::FromStr; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - AsymmetricCryptoKey, AsymmetricPublicCryptoKey, CoseKeyContentFormat, CoseSerializable, - CryptoError, Decryptable, EncString, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, - OctetStreamContentFormat, Pkcs8PrivateKeyDerContentFormat, PrimitiveEncryptable, - SerializedBytes, SignatureAlgorithm, SignedPublicKey, SigningKey, - SpkiPublicKeyDerContentFormat, SymmetricCryptoKey, UnsignedSharedKey, VerifyingKey, + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyContentFormat, + CoseKeyContentFormat, CoseSerializable, CryptoError, Decryptable, EncString, Kdf, + KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, OctetStreamContentFormat, + Pkcs8PrivateKeyDerContentFormat, PrimitiveEncryptable, SerializedBytes, SignatureAlgorithm, + SignedPublicKey, SigningKey, SpkiPublicKeyDerContentFormat, SymmetricCryptoKey, + UnsignedSharedKey, VerifyingKey, }; use wasm_bindgen::prelude::*; @@ -30,6 +31,7 @@ impl PureCrypto { enc_string: String, key: Vec, ) -> Result { + let key = &SerializedBytes::::from(key); EncString::from_str(&enc_string)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?) } @@ -37,6 +39,7 @@ impl PureCrypto { enc_string: String, key: Vec, ) -> Result, CryptoError> { + let key = &SerializedBytes::::from(key); EncString::from_str(&enc_string)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?) } @@ -53,10 +56,12 @@ impl PureCrypto { enc_bytes: Vec, key: Vec, ) -> Result, CryptoError> { + let key = &SerializedBytes::::from(key); EncString::from_buffer(&enc_bytes)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?) } pub fn symmetric_encrypt_string(plain: String, key: Vec) -> Result { + let key = &SerializedBytes::::from(key); plain .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?) .map(|enc| enc.to_string()) @@ -64,6 +69,7 @@ impl PureCrypto { /// DEPRECATED: Only used by send keys pub fn symmetric_encrypt_bytes(plain: Vec, key: Vec) -> Result { + let key = &SerializedBytes::::from(key); SerializedBytes::::from(plain) .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?) .map(|enc| enc.to_string()) @@ -73,6 +79,7 @@ impl PureCrypto { plain: Vec, key: Vec, ) -> Result, CryptoError> { + let key = &SerializedBytes::::from(key); SerializedBytes::::from(plain) .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)? .to_buffer() @@ -89,7 +96,7 @@ impl PureCrypto { let result = master_key .decrypt_user_key(encrypted_user_key) .map_err(|_| CryptoError::InvalidKey)?; - Ok(result.to_encoded()) + Ok(result.to_encoded().to_vec()) } pub fn encrypt_user_key_with_master_password( @@ -99,17 +106,22 @@ impl PureCrypto { kdf: Kdf, ) -> Result { let master_key = MasterKey::derive(master_password.as_str(), email.as_str(), &kdf)?; + let user_key = &SerializedBytes::::from(user_key); let user_key = SymmetricCryptoKey::try_from(user_key)?; let result = master_key.encrypt_user_key(&user_key)?; Ok(result.to_string()) } pub fn make_user_key_aes256_cbc_hmac() -> Vec { - SymmetricCryptoKey::make_aes256_cbc_hmac_key().to_encoded() + SymmetricCryptoKey::make_aes256_cbc_hmac_key() + .to_encoded() + .to_vec() } pub fn make_user_key_xchacha20_poly1305() -> Vec { - SymmetricCryptoKey::make_xchacha20_poly1305_key().to_encoded() + SymmetricCryptoKey::make_xchacha20_poly1305_key() + .to_encoded() + .to_vec() } /// Wraps (encrypts) a symmetric key using a symmetric wrapping key, returning the wrapped key @@ -120,16 +132,16 @@ impl PureCrypto { ) -> Result { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); + let wrapping_key = SymmetricCryptoKey::try_from(&SerializedBytes::< + BitwardenLegacyKeyContentFormat, + >::from(wrapping_key))?; #[allow(deprecated)] - context.set_symmetric_key( - SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from(wrapping_key)?, - )?; + context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), wrapping_key)?; + let key_to_be_wrapped = SymmetricCryptoKey::try_from(&SerializedBytes::< + BitwardenLegacyKeyContentFormat, + >::from(key_to_be_wrapped))?; #[allow(deprecated)] - context.set_symmetric_key( - SymmetricKeyId::Local("key_to_wrap"), - SymmetricCryptoKey::try_from(key_to_be_wrapped)?, - )?; + context.set_symmetric_key(SymmetricKeyId::Local("key_to_wrap"), key_to_be_wrapped)?; // Note: The order of arguments is different here, and should probably be refactored Ok(context .wrap_symmetric_key( @@ -147,11 +159,11 @@ impl PureCrypto { ) -> Result, CryptoError> { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); + let wrapping_key = SymmetricCryptoKey::try_from(&SerializedBytes::< + BitwardenLegacyKeyContentFormat, + >::from(wrapping_key))?; #[allow(deprecated)] - context.set_symmetric_key( - SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from(wrapping_key)?, - )?; + context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), wrapping_key)?; // Note: The order of arguments is different here, and should probably be refactored context.unwrap_symmetric_key( SymmetricKeyId::Local("wrapping_key"), @@ -160,7 +172,7 @@ impl PureCrypto { )?; #[allow(deprecated)] let key = context.dangerous_get_symmetric_key(SymmetricKeyId::Local("wrapped_key"))?; - Ok(key.to_encoded()) + Ok(key.to_encoded().to_vec()) } /// Wraps (encrypts) an SPKI DER encoded encapsulation (public) key using a symmetric wrapping @@ -177,7 +189,9 @@ impl PureCrypto { #[allow(deprecated)] context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from(wrapping_key)?, + SymmetricCryptoKey::try_from( + &SerializedBytes::::from(wrapping_key), + )?, )?; Ok( SerializedBytes::::from(encapsulation_key) @@ -197,7 +211,9 @@ impl PureCrypto { #[allow(deprecated)] context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from(wrapping_key)?, + SymmetricCryptoKey::try_from( + &SerializedBytes::::from(wrapping_key), + )?, )?; EncString::from_str(wrapped_key.as_str())? .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) @@ -214,7 +230,9 @@ impl PureCrypto { #[allow(deprecated)] context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from(wrapping_key)?, + SymmetricCryptoKey::try_from( + &SerializedBytes::::from(wrapping_key), + )?, )?; Ok( SerializedBytes::::from(decapsulation_key) @@ -234,7 +252,9 @@ impl PureCrypto { #[allow(deprecated)] context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from(wrapping_key)?, + SymmetricCryptoKey::try_from( + &SerializedBytes::::from(wrapping_key), + )?, )?; EncString::from_str(wrapped_key.as_str())? .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) @@ -249,7 +269,9 @@ impl PureCrypto { ) -> Result { let encapsulation_key = AsymmetricPublicCryptoKey::from_der(encapsulation_key.as_slice())?; Ok(UnsignedSharedKey::encapsulate_key_unsigned( - &SymmetricCryptoKey::try_from(shared_key)?, + &SymmetricCryptoKey::try_from( + &SerializedBytes::::from(shared_key), + )?, &encapsulation_key, )? .to_string()) @@ -268,7 +290,8 @@ impl PureCrypto { >::from( decapsulation_key ))?)? - .to_encoded()) + .to_encoded() + .to_vec()) } /// Given a wrapped signing key and the symmetric key it is wrapped with, this returns From 1920a492fc72835522417df93d23888d3d9374de Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 19 Jun 2025 14:35:09 +0200 Subject: [PATCH 057/124] Rename to bytes --- .../bitwarden-core/src/auth/auth_request.rs | 6 +-- .../src/auth/login/access_token.rs | 4 +- .../src/client/encryption_settings.rs | 8 +-- .../src/key_management/crypto.rs | 8 +-- .../src/platform/generate_fingerprint.rs | 4 +- crates/bitwarden-crypto/examples/signature.rs | 8 +-- .../examples/signed_object.rs | 17 +++--- crates/bitwarden-crypto/src/content_format.rs | 22 ++++---- crates/bitwarden-crypto/src/cose.rs | 6 +-- .../src/enc_string/asymmetric.rs | 4 +- .../src/enc_string/symmetric.rs | 6 +-- crates/bitwarden-crypto/src/fingerprint.rs | 10 ++-- .../src/keys/asymmetric_crypto_key.rs | 14 ++--- .../bitwarden-crypto/src/keys/device_key.rs | 19 +++---- .../bitwarden-crypto/src/keys/master_key.rs | 4 +- .../src/keys/signed_public_key.rs | 4 +- .../src/keys/symmetric_crypto_key.rs | 28 +++++----- .../bitwarden-crypto/src/signing/signature.rs | 14 ++--- .../src/signing/signed_object.rs | 16 +++--- .../src/signing/signing_key.rs | 6 +-- .../src/signing/verifying_key.rs | 12 ++--- crates/bitwarden-crypto/src/store/context.rs | 8 +-- crates/bitwarden-send/src/send.rs | 4 +- crates/bitwarden-send/src/send_client.rs | 4 +- .../bitwarden-vault/src/cipher/attachment.rs | 4 +- .../src/pure_crypto.rs | 54 +++++++++---------- 26 files changed, 145 insertions(+), 149 deletions(-) diff --git a/crates/bitwarden-core/src/auth/auth_request.rs b/crates/bitwarden-core/src/auth/auth_request.rs index 0bc3e7513..976d8f8b9 100644 --- a/crates/bitwarden-core/src/auth/auth_request.rs +++ b/crates/bitwarden-core/src/auth/auth_request.rs @@ -111,7 +111,7 @@ mod tests { use std::num::NonZeroU32; use bitwarden_crypto::{ - BitwardenLegacyKeyContentFormat, Kdf, MasterKey, SerializedBytes, + BitwardenLegacyKeyContentFormat, Kdf, MasterKey, Bytes, SpkiPublicKeyDerContentFormat, }; @@ -136,7 +136,7 @@ mod tests { AsymmetricCryptoKey::from_der(&STANDARD.decode(&request.private_key).unwrap().into()) .unwrap(); - let secret = SerializedBytes::::from(secret); + let secret = Bytes::::from(secret); let encrypted = UnsignedSharedKey::encapsulate_key_unsigned( &SymmetricCryptoKey::try_from(&secret).unwrap(), &private_key.to_public_key(), @@ -172,7 +172,7 @@ mod tests { // Verify fingerprint let pubkey = STANDARD.decode(public_key).unwrap(); - let pubkey = SerializedBytes::::from(pubkey.clone()); + let pubkey = Bytes::::from(pubkey.clone()); let fingerprint = fingerprint("test@bitwarden.com", &pubkey).unwrap(); assert_eq!(fingerprint, "childless-unfair-prowler-dropbox-designate"); diff --git a/crates/bitwarden-core/src/auth/login/access_token.rs b/crates/bitwarden-core/src/auth/login/access_token.rs index 2888156a9..acd391791 100644 --- a/crates/bitwarden-core/src/auth/login/access_token.rs +++ b/crates/bitwarden-core/src/auth/login/access_token.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ - BitwardenLegacyKeyContentFormat, EncString, KeyDecryptable, SerializedBytes, SymmetricCryptoKey, + BitwardenLegacyKeyContentFormat, EncString, KeyDecryptable, Bytes, SymmetricCryptoKey, }; use chrono::Utc; use schemars::JsonSchema; @@ -70,7 +70,7 @@ pub(crate) async fn login_access_token( let payload: Payload = serde_json::from_slice(&decrypted_payload)?; let encryption_key = STANDARD.decode(&payload.encryption_key)?; let encryption_key = - SerializedBytes::::from(encryption_key); + Bytes::::from(encryption_key); let encryption_key = SymmetricCryptoKey::try_from(&encryption_key)?; let access_token_obj: JwtToken = r.access_token.parse()?; diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index 35a23d833..bb81fb544 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -58,10 +58,10 @@ impl EncryptionSettings { use crate::key_management::{AsymmetricKeyId, SigningKeyId, SymmetricKeyId}; let private_key = { - use bitwarden_crypto::{Pkcs8PrivateKeyDerContentFormat, SerializedBytes}; + use bitwarden_crypto::{Pkcs8PrivateKeyDerContentFormat, Bytes}; let dec: Vec = private_key.decrypt_with_key(&user_key)?; - let dec: SerializedBytes = SerializedBytes::from(dec); + let dec: Bytes = Bytes::from(dec); // FIXME: [PM-11690] - Temporarily ignore invalid private keys until we have a recovery // process in place. AsymmetricCryptoKey::from_der(&dec) @@ -82,12 +82,12 @@ impl EncryptionSettings { let mut ctx = store.context_mut(); if let Some(signing_key) = signing_key { - use bitwarden_crypto::SerializedBytes; + use bitwarden_crypto::Bytes; let dec: Vec = signing_key .decrypt_with_key(&user_key) .map_err(|_| EncryptionSettingsError::InvalidSigningKey)?; - let signing_key = SigningKey::from_cose(&SerializedBytes::from(dec)) + let signing_key = SigningKey::from_cose(&Bytes::from(dec)) .map_err(|_| EncryptionSettingsError::InvalidSigningKey)?; ctx.set_signing_key(SigningKeyId::UserSigningKey, signing_key)?; } diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index bfd9859e5..1cf147aba 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -10,7 +10,7 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ AsymmetricCryptoKey, CoseSerializable, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, Pkcs8PrivateKeyDerContentFormat, PrimitiveEncryptable, - SerializedBytes, SignatureAlgorithm, SignedPublicKey, SigningKey, SymmetricCryptoKey, + Bytes, SignatureAlgorithm, SignedPublicKey, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey, }; use bitwarden_error::bitwarden_error; @@ -537,8 +537,8 @@ pub(super) fn verify_asymmetric_keys( .decrypt_with_key(user_key) .map_err(VerifyError::DecryptFailed)?; - let decrypted_private_key: SerializedBytes = - SerializedBytes::from(decrypted_private_key); + let decrypted_private_key: Bytes = + Bytes::from(decrypted_private_key); let private_key = AsymmetricCryptoKey::from_der(&decrypted_private_key) .map_err(VerifyError::ParseFailed)?; @@ -844,7 +844,7 @@ mod tests { let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; let private_key = STANDARD.decode(private_key).unwrap(); - let private_key = SerializedBytes::::from(private_key); + let private_key = Bytes::::from(private_key); let private_key = AsymmetricCryptoKey::from_der(&private_key).unwrap(); let decrypted: SymmetricCryptoKey = encrypted.decapsulate_key_unsigned(&private_key).unwrap(); diff --git a/crates/bitwarden-core/src/platform/generate_fingerprint.rs b/crates/bitwarden-core/src/platform/generate_fingerprint.rs index a0cb5fc0c..7391cfa72 100644 --- a/crates/bitwarden-core/src/platform/generate_fingerprint.rs +++ b/crates/bitwarden-core/src/platform/generate_fingerprint.rs @@ -3,7 +3,7 @@ //! This module contains the logic for generating fingerprints. use base64::{engine::general_purpose::STANDARD, Engine}; -use bitwarden_crypto::{fingerprint, SerializedBytes, SpkiPublicKeyDerContentFormat}; +use bitwarden_crypto::{fingerprint, Bytes, SpkiPublicKeyDerContentFormat}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -42,7 +42,7 @@ pub enum FingerprintError { pub(crate) fn generate_fingerprint(input: &FingerprintRequest) -> Result { let key = STANDARD.decode(&input.public_key)?; - let key = SerializedBytes::::from(key); + let key = Bytes::::from(key); Ok(fingerprint(&input.fingerprint_material, &key)?) } diff --git a/crates/bitwarden-crypto/examples/signature.rs b/crates/bitwarden-crypto/examples/signature.rs index 73b44d924..4e2119256 100644 --- a/crates/bitwarden-crypto/examples/signature.rs +++ b/crates/bitwarden-crypto/examples/signature.rs @@ -2,7 +2,7 @@ //! to verify them. use bitwarden_crypto::{ - CoseSerializable, CoseSign1ContentFormat, SerializedBytes, SigningNamespace, + CoseSerializable, CoseSign1ContentFormat, Bytes, SigningNamespace, }; use serde::{Deserialize, Serialize}; @@ -45,7 +45,7 @@ fn main() { // Bob retrieves the signed object from the server let retrieved_signature = - bitwarden_crypto::Signature::from_cose(&SerializedBytes::::from( + bitwarden_crypto::Signature::from_cose(&Bytes::::from( mock_server .download("signature") .expect("Failed to download signature") @@ -93,7 +93,7 @@ fn main() { .expect("Failed to get content type from signature"), ); let retrieved_alice_signature = - bitwarden_crypto::Signature::from_cose(&SerializedBytes::::from( + bitwarden_crypto::Signature::from_cose(&Bytes::::from( mock_server .download("signature") .expect("Failed to download Alice's signature") @@ -101,7 +101,7 @@ fn main() { )) .expect("Failed to deserialize Alice's signature"); let retrieved_bobs_signature = - bitwarden_crypto::Signature::from_cose(&SerializedBytes::::from( + bitwarden_crypto::Signature::from_cose(&Bytes::::from( mock_server .download("bobs_signature") .expect("Failed to download Bob's signature") diff --git a/crates/bitwarden-crypto/examples/signed_object.rs b/crates/bitwarden-crypto/examples/signed_object.rs index 9b84ef84f..dd36665e9 100644 --- a/crates/bitwarden-crypto/examples/signed_object.rs +++ b/crates/bitwarden-crypto/examples/signed_object.rs @@ -1,7 +1,7 @@ //! This example demonstrates how to sign and verify structs. use bitwarden_crypto::{ - CoseSerializable, CoseSign1ContentFormat, SerializedBytes, SignedObject, SigningNamespace, + Bytes, CoseSerializable, CoseSign1ContentFormat, SignedObject, SigningNamespace, }; use serde::{Deserialize, Serialize}; @@ -39,14 +39,13 @@ fn main() { mock_server.upload("signed_object", signed_object.to_cose().as_ref().to_vec()); // Bob retrieves the signed object from the server - let retrieved_signed_object = - SignedObject::from_cose(&SerializedBytes::::from( - mock_server - .download("signed_object") - .expect("Failed to download signed object") - .clone(), - )) - .expect("Failed to deserialize signed object"); + let retrieved_signed_object = SignedObject::from_cose(&Bytes::::from( + mock_server + .download("signed_object") + .expect("Failed to download signed object") + .clone(), + )) + .expect("Failed to deserialize signed object"); // Bob verifies the signed object using Alice's verifying key let verified_message: MessageToBob = retrieved_signed_object .verify_and_unwrap(&alice_verifying_key, EXAMPLE_NAMESPACE) diff --git a/crates/bitwarden-crypto/src/content_format.rs b/crates/bitwarden-crypto/src/content_format.rs index c2f41258f..4d2df5ea8 100644 --- a/crates/bitwarden-crypto/src/content_format.rs +++ b/crates/bitwarden-crypto/src/content_format.rs @@ -53,12 +53,12 @@ pub trait ConstContentFormat { /// format is used to determine how the bytes should be interpreted when encrypting or decrypting /// the data. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct SerializedBytes { +pub struct Bytes { inner: Vec, _marker: std::marker::PhantomData, } -impl From> for SerializedBytes { +impl From> for Bytes { fn from(inner: Vec) -> Self { Self { inner, @@ -67,19 +67,19 @@ impl From> for SerializedBytes { } } -impl From<&[u8]> for SerializedBytes { +impl From<&[u8]> for Bytes { fn from(inner: &[u8]) -> Self { Self::from(inner.to_vec()) } } -impl AsRef<[u8]> for SerializedBytes { +impl AsRef<[u8]> for Bytes { fn as_ref(&self) -> &[u8] { &self.inner } } -impl SerializedBytes { +impl Bytes { /// Returns the serialized bytes as a `Vec`. pub fn to_vec(&self) -> Vec { self.inner.clone() @@ -156,7 +156,7 @@ impl CoseContentFormat for CoseSign1ContentFormat {} pub trait CoseContentFormat {} impl PrimitiveEncryptable - for SerializedBytes + for Bytes { fn encrypt( &self, @@ -167,20 +167,20 @@ impl PrimitiveEncryptable KeyEncryptable for &SerializedBytes { +impl KeyEncryptable for &Bytes { fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { self.as_ref().encrypt_with_key(key, T::content_format()) } } -impl From for SerializedBytes { +impl From for Bytes { fn from(val: String) -> Self { - SerializedBytes::from(val.into_bytes()) + Bytes::from(val.into_bytes()) } } -impl From<&str> for SerializedBytes { +impl From<&str> for Bytes { fn from(val: &str) -> Self { - SerializedBytes::from(val.as_bytes().to_vec()) + Bytes::from(val.as_bytes().to_vec()) } } diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 59d523242..e06ce4b34 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -11,7 +11,7 @@ use generic_array::GenericArray; use typenum::U32; use crate::{ - content_format::{ConstContentFormat, CoseContentFormat, SerializedBytes}, + content_format::{ConstContentFormat, CoseContentFormat, Bytes}, error::{EncStringParseError, EncodingError}, xchacha20, ContentFormat, CryptoError, SymmetricCryptoKey, XChaCha20Poly1305Key, }; @@ -216,9 +216,9 @@ fn should_pad_content(format: &ContentFormat) -> bool { /// Trait for structs that are serializable to COSE objects. pub trait CoseSerializable { /// Serializes the struct to COSE serialization - fn to_cose(&self) -> SerializedBytes; + fn to_cose(&self) -> Bytes; /// Deserializes a serialized COSE object to a struct - fn from_cose(bytes: &SerializedBytes) -> Result + fn from_cose(bytes: &Bytes) -> Result where Self: Sized; } diff --git a/crates/bitwarden-crypto/src/enc_string/asymmetric.rs b/crates/bitwarden-crypto/src/enc_string/asymmetric.rs index 457e41b42..7afa30ce9 100644 --- a/crates/bitwarden-crypto/src/enc_string/asymmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/asymmetric.rs @@ -11,7 +11,7 @@ use crate::{ rsa::encrypt_rsa2048_oaep_sha1, util::FromStrVisitor, AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyContentFormat, RawPrivateKey, - RawPublicKey, SerializedBytes, SymmetricCryptoKey, + RawPublicKey, Bytes, SymmetricCryptoKey, }; // This module is a workaround to avoid deprecated warnings that come from the ZeroizeOnDrop // macro expansion @@ -218,7 +218,7 @@ impl UnsignedSharedKey { } .map_err(|_| CryptoError::KeyDecrypt)?; SymmetricCryptoKey::try_from( - &SerializedBytes::::from(key_data), + &Bytes::::from(key_data), ) } } diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index 5874f8abf..e0e8ca17e 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -6,7 +6,7 @@ use serde::Deserialize; use super::{check_length, from_b64, from_b64_vec, split_enc_string}; use crate::{ - content_format::{SerializedBytes, Utf8ContentFormat}, + content_format::{Bytes, Utf8ContentFormat}, error::{CryptoError, EncStringParseError, Result, UnsupportedOperation}, util::FromStrVisitor, Aes256CbcHmacKey, ContentFormat, KeyDecryptable, KeyEncryptable, KeyEncryptableWithContentType, @@ -323,13 +323,13 @@ impl KeyDecryptable> for EncString { impl KeyEncryptable for String { fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { - Into::>::into(self).encrypt_with_key(key) + Into::>::into(self).encrypt_with_key(key) } } impl KeyEncryptable for &str { fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { - Into::>::into(self).encrypt_with_key(key) + Into::>::into(self).encrypt_with_key(key) } } diff --git a/crates/bitwarden-crypto/src/fingerprint.rs b/crates/bitwarden-crypto/src/fingerprint.rs index 06a3e2260..f292f2af0 100644 --- a/crates/bitwarden-crypto/src/fingerprint.rs +++ b/crates/bitwarden-crypto/src/fingerprint.rs @@ -10,7 +10,7 @@ use thiserror::Error; use crate::{ content_format::SpkiPublicKeyDerContentFormat, error::Result, wordlist::EFF_LONG_WORD_LIST, - CryptoError, SerializedBytes, + CryptoError, Bytes, }; /// Computes a fingerprint of the given `fingerprint_material` using the given `public_key`. @@ -20,7 +20,7 @@ use crate::{ /// - `public_key`: user's public key. pub fn fingerprint( fingerprint_material: &str, - public_key: &SerializedBytes, + public_key: &Bytes, ) -> Result { let hkdf = hkdf::Hkdf::::from_prk(public_key.as_ref()) .map_err(|_| CryptoError::InvalidKeyLen)?; @@ -70,7 +70,7 @@ pub enum FingerprintError { #[cfg(test)] mod tests { use super::fingerprint; - use crate::{SerializedBytes, SpkiPublicKeyDerContentFormat}; + use crate::{Bytes, SpkiPublicKeyDerContentFormat}; #[test] fn test_fingerprint() { @@ -93,8 +93,8 @@ mod tests { 197, 3, 219, 56, 77, 109, 47, 72, 251, 131, 36, 240, 96, 169, 31, 82, 93, 166, 242, 3, 33, 213, 2, 3, 1, 0, 1, ]; - let key: SerializedBytes = - SerializedBytes::from(key.to_vec()); + let key: Bytes = + Bytes::from(key.to_vec()); assert_eq!( "turban-deftly-anime-chatroom-unselfish", fingerprint(user_id, &key).unwrap() diff --git a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs index 572ead9f9..a3a579214 100644 --- a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs @@ -6,7 +6,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use super::key_encryptable::CryptoKey; use crate::{ content_format::{ - Pkcs8PrivateKeyDerContentFormat, SerializedBytes, SpkiPublicKeyDerContentFormat, + Pkcs8PrivateKeyDerContentFormat, Bytes, SpkiPublicKeyDerContentFormat, }, error::{CryptoError, Result}, }; @@ -46,7 +46,7 @@ impl AsymmetricPublicCryptoKey { } /// Makes a SubjectPublicKeyInfo DER serialized version of the public key. - pub fn to_der(&self) -> Result> { + pub fn to_der(&self) -> Result> { use rsa::pkcs8::EncodePublicKey; match &self.inner { RawPublicKey::RsaOaepSha1(public_key) => Ok(public_key @@ -116,7 +116,7 @@ impl AsymmetricCryptoKey { } #[allow(missing_docs)] - pub fn from_der(der: &SerializedBytes) -> Result { + pub fn from_der(der: &Bytes) -> Result { use rsa::pkcs8::DecodePrivateKey; Ok(Self { inner: RawPrivateKey::RsaOaepSha1(Box::pin( @@ -126,7 +126,7 @@ impl AsymmetricCryptoKey { } #[allow(missing_docs)] - pub fn to_der(&self) -> Result> { + pub fn to_der(&self) -> Result> { match &self.inner { RawPrivateKey::RsaOaepSha1(private_key) => { use rsa::pkcs8::EncodePrivateKey; @@ -167,7 +167,7 @@ mod tests { use base64::{engine::general_purpose::STANDARD, Engine}; use crate::{ - content_format::{Pkcs8PrivateKeyDerContentFormat, SerializedBytes}, + content_format::{Pkcs8PrivateKeyDerContentFormat, Bytes}, AsymmetricCryptoKey, AsymmetricPublicCryptoKey, SymmetricCryptoKey, UnsignedSharedKey, }; @@ -206,7 +206,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= // Load the two different formats and check they are the same key let pem_key = AsymmetricCryptoKey::from_pem(pem_key_str).unwrap(); - let der_key = AsymmetricCryptoKey::from_der(&SerializedBytes::< + let der_key = AsymmetricCryptoKey::from_der(&Bytes::< Pkcs8PrivateKeyDerContentFormat, >::from(der_key_vec.clone())) .unwrap(); @@ -262,7 +262,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= )) .unwrap(); - let private_key = SerializedBytes::::from(private_key); + let private_key = Bytes::::from(private_key); let private_key = AsymmetricCryptoKey::from_der(&private_key).unwrap(); let public_key = AsymmetricPublicCryptoKey::from_der(&public_key).unwrap(); diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index 37f6c1f69..1998dfa3a 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -1,6 +1,6 @@ use super::{AsymmetricCryptoKey, PublicKeyEncryptionAlgorithm}; use crate::{ - content_format::{Pkcs8PrivateKeyDerContentFormat, SerializedBytes}, + content_format::{Bytes, Pkcs8PrivateKeyDerContentFormat}, error::Result, CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, UnsignedSharedKey, }; @@ -66,8 +66,8 @@ impl DeviceKey { protected_user_key: UnsignedSharedKey, ) -> Result { let device_private_key: Vec = protected_device_private_key.decrypt_with_key(&self.0)?; - let device_private_key: SerializedBytes = - SerializedBytes::from(device_private_key); + let device_private_key: Bytes = + Bytes::from(device_private_key); let device_private_key = AsymmetricCryptoKey::from_der(&device_private_key)?; let user_key: SymmetricCryptoKey = @@ -120,10 +120,9 @@ mod tests { 128, 91, 226, 222, 165, 67, 251, 34, 83, 81, 77, 147, 225, 76, 13, 41, 102, 45, 183, 218, 106, 89, 254, 208, 251, 101, 130, 10, ]; - let user_key = SymmetricCryptoKey::try_from(&SerializedBytes::< - BitwardenLegacyKeyContentFormat, - >::from(user_key)) - .unwrap(); + let user_key = + SymmetricCryptoKey::try_from(&Bytes::::from(user_key)) + .unwrap(); let key_data: &[u8] = &[ 114, 235, 60, 115, 172, 156, 203, 145, 195, 130, 215, 250, 88, 146, 215, 230, 12, 109, @@ -132,10 +131,8 @@ mod tests { 8, 247, 7, 203, 201, 65, 147, 206, 247, ]; let device_key = DeviceKey( - SymmetricCryptoKey::try_from( - &SerializedBytes::::from(key_data), - ) - .unwrap(), + SymmetricCryptoKey::try_from(&Bytes::::from(key_data)) + .unwrap(), ); let protected_user_key: UnsignedSharedKey = "4.f+VbbacRhO2q4MOUSdt1AIjQ2FuLAvg4aDxJMXAh3VxvbmUADj8Ct/R7XEpPUqApmbRS566jS0eRVy8Sk08ogoCdj1IFN9VsIky2i2X1WHK1fUnr3UBmXE3tl2NPBbx56U+h73S2jNTSyet2W18Jg2q7/w8KIhR3J41QrG9aGoOTN93to3hb5W4z6rdrSI0e7GkizbwcIA0NH7Z1JyAhrjPm9+tjRjg060YbEbGaWTAOkZWfgbLjr8bY455DteO2xxG139cOx7EBo66N+YhjsLi0ozkeUyPQkoWBdKMcQllS7jCfB4fDyJA05ALTbk74syKkvqFxqwmQbg+aVn+dcw==".parse().unwrap(); diff --git a/crates/bitwarden-crypto/src/keys/master_key.rs b/crates/bitwarden-crypto/src/keys/master_key.rs index 37e8062e3..7e71806d1 100644 --- a/crates/bitwarden-crypto/src/keys/master_key.rs +++ b/crates/bitwarden-crypto/src/keys/master_key.rs @@ -13,7 +13,7 @@ use super::{ use crate::{ util::{self}, BitwardenLegacyKeyContentFormat, CryptoError, EncString, KeyDecryptable, Result, - SerializedBytes, SymmetricCryptoKey, UserKey, + Bytes, SymmetricCryptoKey, UserKey, }; #[allow(missing_docs)] @@ -160,7 +160,7 @@ pub(super) fn decrypt_user_key( } }; - SymmetricCryptoKey::try_from(&SerializedBytes::::from( + SymmetricCryptoKey::try_from(&Bytes::::from( dec, )) } diff --git a/crates/bitwarden-crypto/src/keys/signed_public_key.rs b/crates/bitwarden-crypto/src/keys/signed_public_key.rs index 7eb0c1992..a6f845392 100644 --- a/crates/bitwarden-crypto/src/keys/signed_public_key.rs +++ b/crates/bitwarden-crypto/src/keys/signed_public_key.rs @@ -11,7 +11,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use super::AsymmetricPublicCryptoKey; use crate::{ - content_format::{CoseSign1ContentFormat, SerializedBytes}, + content_format::{CoseSign1ContentFormat, Bytes}, cose::CoseSerializable, error::EncodingError, util::FromStrVisitor, @@ -90,7 +90,7 @@ impl TryFrom> for SignedPublicKey { type Error = EncodingError; fn try_from(bytes: Vec) -> Result { Ok(SignedPublicKey(SignedObject::from_cose( - &SerializedBytes::::from(bytes), + &Bytes::::from(bytes), )?)) } } diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index f33b0f094..6f19cf6c7 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -20,7 +20,7 @@ use super::{ }; use crate::{ cose, BitwardenLegacyKeyContentFormat, ContentFormat, CoseKeyContentFormat, CryptoError, - SerializedBytes, + Bytes, }; /// [Aes256CbcKey] is a symmetric encryption key, consisting of one 256-bit key, @@ -148,17 +148,17 @@ impl SymmetricCryptoKey { /// This can be used for storage and transmission in the old byte array format. /// When the wrapping key is a COSE key, and the wrapped key is a COSE key, then this should /// not use the byte representation but instead use the COSE key representation. - pub fn to_encoded(&self) -> SerializedBytes { + pub fn to_encoded(&self) -> Bytes { let encoded_key = self.to_encoded_raw(); match encoded_key { EncodedSymmetricKey::Aes256CbcKey(_) | EncodedSymmetricKey::Aes256CbcHmacKey(_) => { let encoded_key: Vec = encoded_key.into(); - SerializedBytes::from(encoded_key) + Bytes::from(encoded_key) } EncodedSymmetricKey::XChaCha20Poly1305Key(_) => { let mut encoded_key: Vec = encoded_key.into(); pad_key(&mut encoded_key, Self::AES256_CBC_HMAC_KEY_LEN + 1); - SerializedBytes::from(encoded_key) + Bytes::from(encoded_key) } } } @@ -267,17 +267,17 @@ impl TryFrom for SymmetricCryptoKey { let bytes = STANDARD .decode(value) .map_err(|_| CryptoError::InvalidKey)?; - Self::try_from(&SerializedBytes::::from( + Self::try_from(&Bytes::::from( bytes, )) } } -impl TryFrom<&SerializedBytes> for SymmetricCryptoKey { +impl TryFrom<&Bytes> for SymmetricCryptoKey { type Error = CryptoError; fn try_from( - value: &SerializedBytes, + value: &Bytes, ) -> Result { let slice = value.as_ref(); @@ -395,9 +395,9 @@ fn unpad_key(key_bytes: &[u8]) -> Result<&[u8], CryptoError> { /// An enum to represent the different encodings of symmetric crypto keys. pub enum EncodedSymmetricKey { - Aes256CbcKey(SerializedBytes), - Aes256CbcHmacKey(SerializedBytes), - XChaCha20Poly1305Key(SerializedBytes), + Aes256CbcKey(Bytes), + Aes256CbcHmacKey(Bytes), + XChaCha20Poly1305Key(Bytes), } impl Into> for EncodedSymmetricKey { fn into(self) -> Vec { @@ -439,7 +439,7 @@ mod tests { use super::{derive_symmetric_key, SymmetricCryptoKey}; use crate::{ keys::symmetric_crypto_key::{pad_key, unpad_key}, - Aes256CbcHmacKey, Aes256CbcKey, BitwardenLegacyKeyContentFormat, SerializedBytes, + Aes256CbcHmacKey, Aes256CbcKey, BitwardenLegacyKeyContentFormat, Bytes, XChaCha20Poly1305Key, }; @@ -466,7 +466,7 @@ mod tests { #[test] fn test_decode_new_symmetric_crypto_key() { let key = STANDARD.decode("pQEEAlDib+JxbqMBlcd3KTUesbufAzoAARFvBIQDBAUGIFggt79surJXmqhPhYuuqi9ZyPfieebmtw2OsmN5SDrb4yUB").unwrap(); - let key = SerializedBytes::::from(key); + let key = Bytes::::from(key); let key = SymmetricCryptoKey::try_from(&key).unwrap(); match key { SymmetricCryptoKey::XChaCha20Poly1305Key(_) => (), @@ -531,11 +531,11 @@ mod tests { #[test] fn test_eq_aes_cbc() { let key1 = SymmetricCryptoKey::try_from( - &SerializedBytes::::from(vec![1u8; 32]), + &Bytes::::from(vec![1u8; 32]), ) .unwrap(); let key2 = SymmetricCryptoKey::try_from( - &SerializedBytes::::from(vec![2u8; 32]), + &Bytes::::from(vec![2u8; 32]), ) .unwrap(); assert_ne!(key1, key2); diff --git a/crates/bitwarden-crypto/src/signing/signature.rs b/crates/bitwarden-crypto/src/signing/signature.rs index 62b9b763c..c42560491 100644 --- a/crates/bitwarden-crypto/src/signing/signature.rs +++ b/crates/bitwarden-crypto/src/signing/signature.rs @@ -7,7 +7,7 @@ use super::{ VerifyingKey, }; use crate::{ - content_format::{CoseSign1ContentFormat, SerializedBytes}, + content_format::{CoseSign1ContentFormat, Bytes}, cose::{CoseSerializable, SIGNING_NAMESPACE}, error::{EncodingError, SignatureError}, CryptoError, @@ -174,13 +174,13 @@ impl SigningKey { } impl CoseSerializable for Signature { - fn from_cose(bytes: &SerializedBytes) -> Result { + fn from_cose(bytes: &Bytes) -> Result { let cose_sign1 = CoseSign1::from_slice(bytes.as_ref()) .map_err(|_| EncodingError::InvalidCoseEncoding)?; Ok(Signature(cose_sign1)) } - fn to_cose(&self) -> SerializedBytes { + fn to_cose(&self) -> Bytes { self.0 .clone() .to_vec() @@ -193,7 +193,7 @@ impl CoseSerializable for Signature { mod tests { use super::*; use crate::{ - content_format::{CoseKeyContentFormat, SerializedBytes}, + content_format::{CoseKeyContentFormat, Bytes}, SignatureAlgorithm, }; @@ -218,7 +218,7 @@ mod tests { #[test] fn test_cose_roundtrip_encode_signature() { let signature = - Signature::from_cose(&SerializedBytes::::from(SIGNATURE)) + Signature::from_cose(&Bytes::::from(SIGNATURE)) .unwrap(); let cose_bytes = signature.to_cose(); let decoded_signature = Signature::from_cose(&cose_bytes).unwrap(); @@ -228,11 +228,11 @@ mod tests { #[test] fn test_verify_testvector() { let verifying_key = VerifyingKey::from_cose( - &SerializedBytes::::from(VERIFYING_KEY), + &Bytes::::from(VERIFYING_KEY), ) .unwrap(); let signature = - Signature::from_cose(&SerializedBytes::::from(SIGNATURE)) + Signature::from_cose(&Bytes::::from(SIGNATURE)) .unwrap(); let serialized_message = SerializedMessage::from_bytes(SERIALIZED_MESSAGE.to_vec(), CoapContentFormat::Cbor); diff --git a/crates/bitwarden-crypto/src/signing/signed_object.rs b/crates/bitwarden-crypto/src/signing/signed_object.rs index 8c4a364e3..581cada47 100644 --- a/crates/bitwarden-crypto/src/signing/signed_object.rs +++ b/crates/bitwarden-crypto/src/signing/signed_object.rs @@ -7,7 +7,7 @@ use super::{ verifying_key::VerifyingKey, SigningNamespace, }; use crate::{ - content_format::{CoseSign1ContentFormat, SerializedBytes}, + content_format::{CoseSign1ContentFormat, Bytes}, cose::{CoseSerializable, SIGNING_NAMESPACE}, error::{EncodingError, SignatureError}, CryptoError, @@ -151,14 +151,14 @@ impl SigningKey { } impl CoseSerializable for SignedObject { - fn from_cose(bytes: &SerializedBytes) -> Result { + fn from_cose(bytes: &Bytes) -> Result { Ok(SignedObject( CoseSign1::from_slice(bytes.as_ref()) .map_err(|_| EncodingError::InvalidCoseEncoding)?, )) } - fn to_cose(&self) -> SerializedBytes { + fn to_cose(&self) -> Bytes { self.0 .clone() .to_vec() @@ -172,7 +172,7 @@ mod tests { use serde::{Deserialize, Serialize}; use crate::{ - content_format::{CoseKeyContentFormat, CoseSign1ContentFormat, SerializedBytes}, + content_format::{CoseKeyContentFormat, CoseSign1ContentFormat, Bytes}, CoseSerializable, CryptoError, SignatureAlgorithm, SignedObject, SigningKey, SigningNamespace, VerifyingKey, }; @@ -201,7 +201,7 @@ mod tests { #[test] fn test_roundtrip_cose() { let signed_object = SignedObject::from_cose( - &Into::>::into(SIGNED_OBJECT), + &Into::>::into(SIGNED_OBJECT), ) .unwrap(); assert_eq!( @@ -211,7 +211,7 @@ mod tests { let cose_bytes = signed_object.to_cose(); assert_eq!( cose_bytes, - SerializedBytes::::from(SIGNED_OBJECT) + Bytes::::from(SIGNED_OBJECT) ); } @@ -221,11 +221,11 @@ mod tests { field1: "Test message".to_string(), }; let signed_object = SignedObject::from_cose( - &Into::>::into(SIGNED_OBJECT), + &Into::>::into(SIGNED_OBJECT), ) .unwrap(); let verifying_key = VerifyingKey::from_cose( - &Into::>::into(VERIFYING_KEY), + &Into::>::into(VERIFYING_KEY), ) .unwrap(); let namespace = SigningNamespace::ExampleNamespace; diff --git a/crates/bitwarden-crypto/src/signing/signing_key.rs b/crates/bitwarden-crypto/src/signing/signing_key.rs index 544a32e03..0a1033ffe 100644 --- a/crates/bitwarden-crypto/src/signing/signing_key.rs +++ b/crates/bitwarden-crypto/src/signing/signing_key.rs @@ -13,7 +13,7 @@ use super::{ SignatureAlgorithm, }; use crate::{ - content_format::{CoseKeyContentFormat, SerializedBytes}, + content_format::{CoseKeyContentFormat, Bytes}, cose::CoseSerializable, error::{EncodingError, Result}, keys::KeyId, @@ -89,7 +89,7 @@ impl SigningKey { impl CoseSerializable for SigningKey { /// Serializes the signing key to a COSE-formatted byte array. - fn to_cose(&self) -> SerializedBytes { + fn to_cose(&self) -> Bytes { match &self.inner { RawSigningKey::Ed25519(key) => { coset::CoseKeyBuilder::new_okp_key() @@ -114,7 +114,7 @@ impl CoseSerializable for SigningKey { } /// Deserializes a COSE-formatted byte array into a signing key. - fn from_cose(bytes: &SerializedBytes) -> Result { + fn from_cose(bytes: &Bytes) -> Result { let cose_key = CoseKey::from_slice(bytes.as_ref()).map_err(|_| EncodingError::InvalidCoseEncoding)?; diff --git a/crates/bitwarden-crypto/src/signing/verifying_key.rs b/crates/bitwarden-crypto/src/signing/verifying_key.rs index fef2da03d..007f43814 100644 --- a/crates/bitwarden-crypto/src/signing/verifying_key.rs +++ b/crates/bitwarden-crypto/src/signing/verifying_key.rs @@ -11,7 +11,7 @@ use coset::{ use super::{ed25519_verifying_key, key_id, SignatureAlgorithm}; use crate::{ - content_format::{CoseKeyContentFormat, SerializedBytes}, + content_format::{CoseKeyContentFormat, Bytes}, cose::CoseSerializable, error::{EncodingError, SignatureError}, keys::KeyId, @@ -59,7 +59,7 @@ impl VerifyingKey { } impl CoseSerializable for VerifyingKey { - fn to_cose(&self) -> SerializedBytes { + fn to_cose(&self) -> Bytes { match &self.inner { RawVerifyingKey::Ed25519(key) => coset::CoseKeyBuilder::new_okp_key() .key_id((&self.id).into()) @@ -84,7 +84,7 @@ impl CoseSerializable for VerifyingKey { } } - fn from_cose(bytes: &SerializedBytes) -> Result + fn from_cose(bytes: &Bytes) -> Result where Self: Sized, { @@ -130,7 +130,7 @@ mod tests { #[test] fn test_cose_roundtrip_encode_verifying() { let verifying_key = VerifyingKey::from_cose( - &SerializedBytes::::from(VERIFYING_KEY.to_vec()), + &Bytes::::from(VERIFYING_KEY.to_vec()), ) .unwrap(); let cose = verifying_key.to_cose(); @@ -142,7 +142,7 @@ mod tests { #[test] fn test_testvector() { let verifying_key = VerifyingKey::from_cose( - &SerializedBytes::::from(VERIFYING_KEY.to_vec()), + &Bytes::::from(VERIFYING_KEY.to_vec()), ) .unwrap(); assert_eq!(verifying_key.algorithm(), SignatureAlgorithm::Ed25519); @@ -155,7 +155,7 @@ mod tests { #[test] fn test_invalid_testvector() { let verifying_key = VerifyingKey::from_cose( - &SerializedBytes::::from(VERIFYING_KEY.to_vec()), + &Bytes::::from(VERIFYING_KEY.to_vec()), ) .unwrap(); assert_eq!(verifying_key.algorithm(), SignatureAlgorithm::Ed25519); diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 83e440ef7..5e644745a 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -10,7 +10,7 @@ use super::KeyStoreInner; use crate::{ derive_shareable_key, error::UnsupportedOperation, signing, store::backend::StoreBackend, AsymmetricCryptoKey, BitwardenLegacyKeyContentFormat, ContentFormat, CryptoError, EncString, - KeyId, KeyIds, Result, SerializedBytes, Signature, SignatureAlgorithm, SignedObject, + KeyId, KeyIds, Result, Bytes, Signature, SignatureAlgorithm, SignedObject, SignedPublicKey, SignedPublicKeyMessage, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, }; @@ -157,7 +157,7 @@ impl KeyStoreContext<'_, Ids> { let key = match (wrapped_key, wrapping_key) { (EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => { SymmetricCryptoKey::try_from( - &SerializedBytes::::from( + &Bytes::::from( crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)?, ), )? @@ -166,7 +166,7 @@ impl KeyStoreContext<'_, Ids> { EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data }, SymmetricCryptoKey::Aes256CbcHmacKey(key), ) => SymmetricCryptoKey::try_from( - &SerializedBytes::::from( + &Bytes::::from( crate::aes::decrypt_aes256_hmac( iv, mac, @@ -184,7 +184,7 @@ impl KeyStoreContext<'_, Ids> { crate::cose::decrypt_xchacha20_poly1305(data, key)?; match content_format { ContentFormat::BitwardenLegacyKey => { - SymmetricCryptoKey::try_from(&SerializedBytes::< + SymmetricCryptoKey::try_from(&Bytes::< BitwardenLegacyKeyContentFormat, >::from( content_bytes diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index 42c6c080f..303d3e473 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -9,7 +9,7 @@ use bitwarden_core::{ }; use bitwarden_crypto::{ generate_random_bytes, CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, - KeyStoreContext, OctetStreamContentFormat, PrimitiveEncryptable, SerializedBytes, + KeyStoreContext, OctetStreamContentFormat, PrimitiveEncryptable, Bytes, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -331,7 +331,7 @@ impl CompositeEncryptable for SendView { name: self.name.encrypt(ctx, send_key)?, notes: self.notes.encrypt(ctx, send_key)?, - key: SerializedBytes::::from(k.clone()).encrypt(ctx, key)?, + key: Bytes::::from(k.clone()).encrypt(ctx, key)?, password: self.new_password.as_ref().map(|password| { let password = bitwarden_crypto::pbkdf2(password.as_bytes(), &k, SEND_ITERATIONS); STANDARD.encode(password) diff --git a/crates/bitwarden-send/src/send_client.rs b/crates/bitwarden-send/src/send_client.rs index d5a5f8a27..cddf5f20c 100644 --- a/crates/bitwarden-send/src/send_client.rs +++ b/crates/bitwarden-send/src/send_client.rs @@ -3,7 +3,7 @@ use std::path::Path; use bitwarden_core::Client; use bitwarden_crypto::{ Decryptable, EncString, IdentifyKey, OctetStreamContentFormat, PrimitiveEncryptable, - SerializedBytes, + Bytes, }; use thiserror::Error; @@ -131,7 +131,7 @@ impl SendClient { let key = Send::get_key(&mut ctx, &send.key, send.key_identifier())?; let encrypted = - SerializedBytes::::from(buffer).encrypt(&mut ctx, key)?; + Bytes::::from(buffer).encrypt(&mut ctx, key)?; Ok(encrypted.to_buffer()?) } } diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index 4b165729a..e5b9f883e 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,7 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, - OctetStreamContentFormat, PrimitiveEncryptable, SerializedBytes, + OctetStreamContentFormat, PrimitiveEncryptable, Bytes, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -94,7 +94,7 @@ impl CompositeEncryptable // Because this is a new attachment, we have to generate a key for it, encrypt the contents // with it, and then encrypt the key with the cipher key let attachment_key = ctx.generate_symmetric_key(ATTACHMENT_KEY)?; - let encrypted_contents = SerializedBytes::::from(self.contents) + let encrypted_contents = Bytes::::from(self.contents) .encrypt(ctx, attachment_key)?; attachment.key = Some(ctx.wrap_symmetric_key(ciphers_key, attachment_key)?); diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 811cbf916..0da4cf85e 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -5,7 +5,7 @@ use bitwarden_crypto::{ AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyContentFormat, CoseKeyContentFormat, CoseSerializable, CryptoError, Decryptable, EncString, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, OctetStreamContentFormat, - Pkcs8PrivateKeyDerContentFormat, PrimitiveEncryptable, SerializedBytes, SignatureAlgorithm, + Pkcs8PrivateKeyDerContentFormat, PrimitiveEncryptable, Bytes, SignatureAlgorithm, SignedPublicKey, SigningKey, SpkiPublicKeyDerContentFormat, SymmetricCryptoKey, UnsignedSharedKey, VerifyingKey, }; @@ -31,7 +31,7 @@ impl PureCrypto { enc_string: String, key: Vec, ) -> Result { - let key = &SerializedBytes::::from(key); + let key = &Bytes::::from(key); EncString::from_str(&enc_string)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?) } @@ -39,7 +39,7 @@ impl PureCrypto { enc_string: String, key: Vec, ) -> Result, CryptoError> { - let key = &SerializedBytes::::from(key); + let key = &Bytes::::from(key); EncString::from_str(&enc_string)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?) } @@ -56,12 +56,12 @@ impl PureCrypto { enc_bytes: Vec, key: Vec, ) -> Result, CryptoError> { - let key = &SerializedBytes::::from(key); + let key = &Bytes::::from(key); EncString::from_buffer(&enc_bytes)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?) } pub fn symmetric_encrypt_string(plain: String, key: Vec) -> Result { - let key = &SerializedBytes::::from(key); + let key = &Bytes::::from(key); plain .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?) .map(|enc| enc.to_string()) @@ -69,8 +69,8 @@ impl PureCrypto { /// DEPRECATED: Only used by send keys pub fn symmetric_encrypt_bytes(plain: Vec, key: Vec) -> Result { - let key = &SerializedBytes::::from(key); - SerializedBytes::::from(plain) + let key = &Bytes::::from(key); + Bytes::::from(plain) .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?) .map(|enc| enc.to_string()) } @@ -79,8 +79,8 @@ impl PureCrypto { plain: Vec, key: Vec, ) -> Result, CryptoError> { - let key = &SerializedBytes::::from(key); - SerializedBytes::::from(plain) + let key = &Bytes::::from(key); + Bytes::::from(plain) .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)? .to_buffer() } @@ -106,7 +106,7 @@ impl PureCrypto { kdf: Kdf, ) -> Result { let master_key = MasterKey::derive(master_password.as_str(), email.as_str(), &kdf)?; - let user_key = &SerializedBytes::::from(user_key); + let user_key = &Bytes::::from(user_key); let user_key = SymmetricCryptoKey::try_from(user_key)?; let result = master_key.encrypt_user_key(&user_key)?; Ok(result.to_string()) @@ -132,12 +132,12 @@ impl PureCrypto { ) -> Result { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); - let wrapping_key = SymmetricCryptoKey::try_from(&SerializedBytes::< + let wrapping_key = SymmetricCryptoKey::try_from(&Bytes::< BitwardenLegacyKeyContentFormat, >::from(wrapping_key))?; #[allow(deprecated)] context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), wrapping_key)?; - let key_to_be_wrapped = SymmetricCryptoKey::try_from(&SerializedBytes::< + let key_to_be_wrapped = SymmetricCryptoKey::try_from(&Bytes::< BitwardenLegacyKeyContentFormat, >::from(key_to_be_wrapped))?; #[allow(deprecated)] @@ -159,7 +159,7 @@ impl PureCrypto { ) -> Result, CryptoError> { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); - let wrapping_key = SymmetricCryptoKey::try_from(&SerializedBytes::< + let wrapping_key = SymmetricCryptoKey::try_from(&Bytes::< BitwardenLegacyKeyContentFormat, >::from(wrapping_key))?; #[allow(deprecated)] @@ -190,11 +190,11 @@ impl PureCrypto { context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from( - &SerializedBytes::::from(wrapping_key), + &Bytes::::from(wrapping_key), )?, )?; Ok( - SerializedBytes::::from(encapsulation_key) + Bytes::::from(encapsulation_key) .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))? .to_string(), ) @@ -212,7 +212,7 @@ impl PureCrypto { context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from( - &SerializedBytes::::from(wrapping_key), + &Bytes::::from(wrapping_key), )?, )?; EncString::from_str(wrapped_key.as_str())? @@ -231,11 +231,11 @@ impl PureCrypto { context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from( - &SerializedBytes::::from(wrapping_key), + &Bytes::::from(wrapping_key), )?, )?; Ok( - SerializedBytes::::from(decapsulation_key) + Bytes::::from(decapsulation_key) .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))? .to_string(), ) @@ -253,7 +253,7 @@ impl PureCrypto { context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), SymmetricCryptoKey::try_from( - &SerializedBytes::::from(wrapping_key), + &Bytes::::from(wrapping_key), )?, )?; EncString::from_str(wrapped_key.as_str())? @@ -270,7 +270,7 @@ impl PureCrypto { let encapsulation_key = AsymmetricPublicCryptoKey::from_der(encapsulation_key.as_slice())?; Ok(UnsignedSharedKey::encapsulate_key_unsigned( &SymmetricCryptoKey::try_from( - &SerializedBytes::::from(shared_key), + &Bytes::::from(shared_key), )?, &encapsulation_key, )? @@ -285,7 +285,7 @@ impl PureCrypto { decapsulation_key: Vec, ) -> Result, CryptoError> { Ok(UnsignedSharedKey::from_str(encapsulated_key.as_str())? - .decapsulate_key_unsigned(&AsymmetricCryptoKey::from_der(&SerializedBytes::< + .decapsulate_key_unsigned(&AsymmetricCryptoKey::from_der(&Bytes::< Pkcs8PrivateKeyDerContentFormat, >::from( decapsulation_key @@ -302,7 +302,7 @@ impl PureCrypto { ) -> Result, CryptoError> { let bytes = Self::symmetric_decrypt_bytes(signing_key, wrapping_key)?; let signing_key = - SigningKey::from_cose(&SerializedBytes::::from(bytes))?; + SigningKey::from_cose(&Bytes::::from(bytes))?; let verifying_key = signing_key.to_verifying_key(); Ok(verifying_key.to_cose().as_ref().to_vec()) } @@ -312,7 +312,7 @@ impl PureCrypto { verifying_key: Vec, ) -> Result { let verifying_key = VerifyingKey::from_cose( - &SerializedBytes::::from(verifying_key), + &Bytes::::from(verifying_key), )?; let algorithm = verifying_key.algorithm(); Ok(algorithm) @@ -328,7 +328,7 @@ impl PureCrypto { ) -> Result, CryptoError> { let signed_public_key = SignedPublicKey::try_from(signed_public_key)?; let verifying_key = VerifyingKey::from_cose( - &SerializedBytes::::from(verifying_key), + &Bytes::::from(verifying_key), )?; signed_public_key .verify_and_unwrap(&verifying_key) @@ -612,7 +612,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= #[test] fn test_key_algorithm_for_verifying_key() { let verifying_key = VerifyingKey::from_cose( - &SerializedBytes::::from(VERIFYING_KEY.to_vec()), + &Bytes::::from(VERIFYING_KEY.to_vec()), ) .unwrap(); let algorithm = @@ -629,7 +629,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= ) .unwrap(); let verifying_key = VerifyingKey::from_cose( - &SerializedBytes::::from(VERIFYING_KEY.to_vec()), + &Bytes::::from(VERIFYING_KEY.to_vec()), ) .unwrap(); let verifying_key_derived = PureCrypto::verifying_key_for_signing_key( @@ -638,7 +638,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= ) .unwrap(); let verifying_key_derived = VerifyingKey::from_cose( - &SerializedBytes::::from(verifying_key_derived), + &Bytes::::from(verifying_key_derived), ) .unwrap(); assert_eq!(verifying_key.to_cose(), verifying_key_derived.to_cose()); From 625b830ea2bbd914904b4b22fc9300f9903310c8 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 19 Jun 2025 14:35:27 +0200 Subject: [PATCH 058/124] Cargo fmt --- .../bitwarden-core/src/auth/auth_request.rs | 3 +- .../src/auth/login/access_token.rs | 5 +- .../src/client/encryption_settings.rs | 2 +- .../src/key_management/crypto.rs | 6 +- crates/bitwarden-crypto/examples/signature.rs | 4 +- crates/bitwarden-crypto/src/cose.rs | 2 +- .../src/enc_string/asymmetric.rs | 10 +-- crates/bitwarden-crypto/src/fingerprint.rs | 5 +- .../src/keys/asymmetric_crypto_key.rs | 12 ++- .../bitwarden-crypto/src/keys/master_key.rs | 8 +- .../src/keys/signed_public_key.rs | 10 ++- .../src/keys/symmetric_crypto_key.rs | 31 ++++--- .../bitwarden-crypto/src/signing/signature.rs | 16 ++-- .../src/signing/signed_object.rs | 25 +++--- .../src/signing/signing_key.rs | 2 +- .../src/signing/verifying_key.rs | 23 +++-- crates/bitwarden-crypto/src/store/context.rs | 36 +++----- crates/bitwarden-send/src/send.rs | 4 +- crates/bitwarden-send/src/send_client.rs | 6 +- .../bitwarden-vault/src/cipher/attachment.rs | 8 +- .../src/pure_crypto.rs | 83 +++++++++---------- 21 files changed, 131 insertions(+), 170 deletions(-) diff --git a/crates/bitwarden-core/src/auth/auth_request.rs b/crates/bitwarden-core/src/auth/auth_request.rs index 976d8f8b9..ccc9ce3a0 100644 --- a/crates/bitwarden-core/src/auth/auth_request.rs +++ b/crates/bitwarden-core/src/auth/auth_request.rs @@ -111,8 +111,7 @@ mod tests { use std::num::NonZeroU32; use bitwarden_crypto::{ - BitwardenLegacyKeyContentFormat, Kdf, MasterKey, Bytes, - SpkiPublicKeyDerContentFormat, + BitwardenLegacyKeyContentFormat, Bytes, Kdf, MasterKey, SpkiPublicKeyDerContentFormat, }; use super::*; diff --git a/crates/bitwarden-core/src/auth/login/access_token.rs b/crates/bitwarden-core/src/auth/login/access_token.rs index acd391791..95a89463b 100644 --- a/crates/bitwarden-core/src/auth/login/access_token.rs +++ b/crates/bitwarden-core/src/auth/login/access_token.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ - BitwardenLegacyKeyContentFormat, EncString, KeyDecryptable, Bytes, SymmetricCryptoKey, + BitwardenLegacyKeyContentFormat, Bytes, EncString, KeyDecryptable, SymmetricCryptoKey, }; use chrono::Utc; use schemars::JsonSchema; @@ -69,8 +69,7 @@ pub(crate) async fn login_access_token( let payload: Payload = serde_json::from_slice(&decrypted_payload)?; let encryption_key = STANDARD.decode(&payload.encryption_key)?; - let encryption_key = - Bytes::::from(encryption_key); + let encryption_key = Bytes::::from(encryption_key); let encryption_key = SymmetricCryptoKey::try_from(&encryption_key)?; let access_token_obj: JwtToken = r.access_token.parse()?; diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index bb81fb544..eb5ce9c24 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -58,7 +58,7 @@ impl EncryptionSettings { use crate::key_management::{AsymmetricKeyId, SigningKeyId, SymmetricKeyId}; let private_key = { - use bitwarden_crypto::{Pkcs8PrivateKeyDerContentFormat, Bytes}; + use bitwarden_crypto::{Bytes, Pkcs8PrivateKeyDerContentFormat}; let dec: Vec = private_key.decrypt_with_key(&user_key)?; let dec: Bytes = Bytes::from(dec); diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index 1cf147aba..8a5e4d28a 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -8,10 +8,10 @@ use std::collections::HashMap; use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ - AsymmetricCryptoKey, CoseSerializable, CryptoError, EncString, Kdf, KeyDecryptable, + AsymmetricCryptoKey, Bytes, CoseSerializable, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, Pkcs8PrivateKeyDerContentFormat, PrimitiveEncryptable, - Bytes, SignatureAlgorithm, SignedPublicKey, SigningKey, SymmetricCryptoKey, - UnsignedSharedKey, UserKey, + SignatureAlgorithm, SignedPublicKey, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, + UserKey, }; use bitwarden_error::bitwarden_error; use schemars::JsonSchema; diff --git a/crates/bitwarden-crypto/examples/signature.rs b/crates/bitwarden-crypto/examples/signature.rs index 4e2119256..373fe620c 100644 --- a/crates/bitwarden-crypto/examples/signature.rs +++ b/crates/bitwarden-crypto/examples/signature.rs @@ -1,9 +1,7 @@ //! This example demonstrates how to create signatures and countersignatures for a message, and how //! to verify them. -use bitwarden_crypto::{ - CoseSerializable, CoseSign1ContentFormat, Bytes, SigningNamespace, -}; +use bitwarden_crypto::{Bytes, CoseSerializable, CoseSign1ContentFormat, SigningNamespace}; use serde::{Deserialize, Serialize}; const EXAMPLE_NAMESPACE: &SigningNamespace = &SigningNamespace::SignedPublicKey; diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index e06ce4b34..682146ab4 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -11,7 +11,7 @@ use generic_array::GenericArray; use typenum::U32; use crate::{ - content_format::{ConstContentFormat, CoseContentFormat, Bytes}, + content_format::{Bytes, ConstContentFormat, CoseContentFormat}, error::{EncStringParseError, EncodingError}, xchacha20, ContentFormat, CryptoError, SymmetricCryptoKey, XChaCha20Poly1305Key, }; diff --git a/crates/bitwarden-crypto/src/enc_string/asymmetric.rs b/crates/bitwarden-crypto/src/enc_string/asymmetric.rs index 7afa30ce9..88a8e4e3d 100644 --- a/crates/bitwarden-crypto/src/enc_string/asymmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/asymmetric.rs @@ -10,8 +10,8 @@ use crate::{ error::{CryptoError, EncStringParseError, Result}, rsa::encrypt_rsa2048_oaep_sha1, util::FromStrVisitor, - AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyContentFormat, RawPrivateKey, - RawPublicKey, Bytes, SymmetricCryptoKey, + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyContentFormat, Bytes, + RawPrivateKey, RawPublicKey, SymmetricCryptoKey, }; // This module is a workaround to avoid deprecated warnings that come from the ZeroizeOnDrop // macro expansion @@ -217,9 +217,9 @@ impl UnsignedSharedKey { } } .map_err(|_| CryptoError::KeyDecrypt)?; - SymmetricCryptoKey::try_from( - &Bytes::::from(key_data), - ) + SymmetricCryptoKey::try_from(&Bytes::::from( + key_data, + )) } } } diff --git a/crates/bitwarden-crypto/src/fingerprint.rs b/crates/bitwarden-crypto/src/fingerprint.rs index f292f2af0..0ac977f4a 100644 --- a/crates/bitwarden-crypto/src/fingerprint.rs +++ b/crates/bitwarden-crypto/src/fingerprint.rs @@ -10,7 +10,7 @@ use thiserror::Error; use crate::{ content_format::SpkiPublicKeyDerContentFormat, error::Result, wordlist::EFF_LONG_WORD_LIST, - CryptoError, Bytes, + Bytes, CryptoError, }; /// Computes a fingerprint of the given `fingerprint_material` using the given `public_key`. @@ -93,8 +93,7 @@ mod tests { 197, 3, 219, 56, 77, 109, 47, 72, 251, 131, 36, 240, 96, 169, 31, 82, 93, 166, 242, 3, 33, 213, 2, 3, 1, 0, 1, ]; - let key: Bytes = - Bytes::from(key.to_vec()); + let key: Bytes = Bytes::from(key.to_vec()); assert_eq!( "turban-deftly-anime-chatroom-unselfish", fingerprint(user_id, &key).unwrap() diff --git a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs index a3a579214..67461746e 100644 --- a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs @@ -5,9 +5,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use super::key_encryptable::CryptoKey; use crate::{ - content_format::{ - Pkcs8PrivateKeyDerContentFormat, Bytes, SpkiPublicKeyDerContentFormat, - }, + content_format::{Bytes, Pkcs8PrivateKeyDerContentFormat, SpkiPublicKeyDerContentFormat}, error::{CryptoError, Result}, }; @@ -167,7 +165,7 @@ mod tests { use base64::{engine::general_purpose::STANDARD, Engine}; use crate::{ - content_format::{Pkcs8PrivateKeyDerContentFormat, Bytes}, + content_format::{Bytes, Pkcs8PrivateKeyDerContentFormat}, AsymmetricCryptoKey, AsymmetricPublicCryptoKey, SymmetricCryptoKey, UnsignedSharedKey, }; @@ -206,9 +204,9 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= // Load the two different formats and check they are the same key let pem_key = AsymmetricCryptoKey::from_pem(pem_key_str).unwrap(); - let der_key = AsymmetricCryptoKey::from_der(&Bytes::< - Pkcs8PrivateKeyDerContentFormat, - >::from(der_key_vec.clone())) + let der_key = AsymmetricCryptoKey::from_der( + &Bytes::::from(der_key_vec.clone()), + ) .unwrap(); assert_eq!(pem_key.to_der().unwrap(), der_key.to_der().unwrap()); diff --git a/crates/bitwarden-crypto/src/keys/master_key.rs b/crates/bitwarden-crypto/src/keys/master_key.rs index 7e71806d1..3e380a9a2 100644 --- a/crates/bitwarden-crypto/src/keys/master_key.rs +++ b/crates/bitwarden-crypto/src/keys/master_key.rs @@ -12,8 +12,8 @@ use super::{ }; use crate::{ util::{self}, - BitwardenLegacyKeyContentFormat, CryptoError, EncString, KeyDecryptable, Result, - Bytes, SymmetricCryptoKey, UserKey, + BitwardenLegacyKeyContentFormat, Bytes, CryptoError, EncString, KeyDecryptable, Result, + SymmetricCryptoKey, UserKey, }; #[allow(missing_docs)] @@ -160,9 +160,7 @@ pub(super) fn decrypt_user_key( } }; - SymmetricCryptoKey::try_from(&Bytes::::from( - dec, - )) + SymmetricCryptoKey::try_from(&Bytes::::from(dec)) } /// Generate a new random user key and encrypt it with the master key. diff --git a/crates/bitwarden-crypto/src/keys/signed_public_key.rs b/crates/bitwarden-crypto/src/keys/signed_public_key.rs index a6f845392..758436b7b 100644 --- a/crates/bitwarden-crypto/src/keys/signed_public_key.rs +++ b/crates/bitwarden-crypto/src/keys/signed_public_key.rs @@ -11,7 +11,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use super::AsymmetricPublicCryptoKey; use crate::{ - content_format::{CoseSign1ContentFormat, Bytes}, + content_format::{Bytes, CoseSign1ContentFormat}, cose::CoseSerializable, error::EncodingError, util::FromStrVisitor, @@ -89,9 +89,11 @@ impl From for Vec { impl TryFrom> for SignedPublicKey { type Error = EncodingError; fn try_from(bytes: Vec) -> Result { - Ok(SignedPublicKey(SignedObject::from_cose( - &Bytes::::from(bytes), - )?)) + Ok(SignedPublicKey(SignedObject::from_cose(&Bytes::< + CoseSign1ContentFormat, + >::from( + bytes + ))?)) } } diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index 6f19cf6c7..ae1061bf0 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -19,8 +19,7 @@ use super::{ key_id::{KeyId, KEY_ID_SIZE}, }; use crate::{ - cose, BitwardenLegacyKeyContentFormat, ContentFormat, CoseKeyContentFormat, CryptoError, - Bytes, + cose, BitwardenLegacyKeyContentFormat, Bytes, ContentFormat, CoseKeyContentFormat, CryptoError, }; /// [Aes256CbcKey] is a symmetric encryption key, consisting of one 256-bit key, @@ -267,18 +266,14 @@ impl TryFrom for SymmetricCryptoKey { let bytes = STANDARD .decode(value) .map_err(|_| CryptoError::InvalidKey)?; - Self::try_from(&Bytes::::from( - bytes, - )) + Self::try_from(&Bytes::::from(bytes)) } } impl TryFrom<&Bytes> for SymmetricCryptoKey { type Error = CryptoError; - fn try_from( - value: &Bytes, - ) -> Result { + fn try_from(value: &Bytes) -> Result { let slice = value.as_ref(); // Raw byte serialized keys are either 32, 64, or more bytes long. If they are 32/64, they @@ -530,14 +525,18 @@ mod tests { #[test] fn test_eq_aes_cbc() { - let key1 = SymmetricCryptoKey::try_from( - &Bytes::::from(vec![1u8; 32]), - ) - .unwrap(); - let key2 = SymmetricCryptoKey::try_from( - &Bytes::::from(vec![2u8; 32]), - ) - .unwrap(); + let key1 = + SymmetricCryptoKey::try_from(&Bytes::::from(vec![ + 1u8; + 32 + ])) + .unwrap(); + let key2 = + SymmetricCryptoKey::try_from(&Bytes::::from(vec![ + 2u8; + 32 + ])) + .unwrap(); assert_ne!(key1, key2); let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap(); assert_eq!(key1, key3); diff --git a/crates/bitwarden-crypto/src/signing/signature.rs b/crates/bitwarden-crypto/src/signing/signature.rs index c42560491..ca2635805 100644 --- a/crates/bitwarden-crypto/src/signing/signature.rs +++ b/crates/bitwarden-crypto/src/signing/signature.rs @@ -7,7 +7,7 @@ use super::{ VerifyingKey, }; use crate::{ - content_format::{CoseSign1ContentFormat, Bytes}, + content_format::{Bytes, CoseSign1ContentFormat}, cose::{CoseSerializable, SIGNING_NAMESPACE}, error::{EncodingError, SignatureError}, CryptoError, @@ -193,7 +193,7 @@ impl CoseSerializable for Signature { mod tests { use super::*; use crate::{ - content_format::{CoseKeyContentFormat, Bytes}, + content_format::{Bytes, CoseKeyContentFormat}, SignatureAlgorithm, }; @@ -218,8 +218,7 @@ mod tests { #[test] fn test_cose_roundtrip_encode_signature() { let signature = - Signature::from_cose(&Bytes::::from(SIGNATURE)) - .unwrap(); + Signature::from_cose(&Bytes::::from(SIGNATURE)).unwrap(); let cose_bytes = signature.to_cose(); let decoded_signature = Signature::from_cose(&cose_bytes).unwrap(); assert_eq!(signature.inner(), decoded_signature.inner()); @@ -227,13 +226,10 @@ mod tests { #[test] fn test_verify_testvector() { - let verifying_key = VerifyingKey::from_cose( - &Bytes::::from(VERIFYING_KEY), - ) - .unwrap(); + let verifying_key = + VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY)).unwrap(); let signature = - Signature::from_cose(&Bytes::::from(SIGNATURE)) - .unwrap(); + Signature::from_cose(&Bytes::::from(SIGNATURE)).unwrap(); let serialized_message = SerializedMessage::from_bytes(SERIALIZED_MESSAGE.to_vec(), CoapContentFormat::Cbor); diff --git a/crates/bitwarden-crypto/src/signing/signed_object.rs b/crates/bitwarden-crypto/src/signing/signed_object.rs index 581cada47..b50366286 100644 --- a/crates/bitwarden-crypto/src/signing/signed_object.rs +++ b/crates/bitwarden-crypto/src/signing/signed_object.rs @@ -7,7 +7,7 @@ use super::{ verifying_key::VerifyingKey, SigningNamespace, }; use crate::{ - content_format::{CoseSign1ContentFormat, Bytes}, + content_format::{Bytes, CoseSign1ContentFormat}, cose::{CoseSerializable, SIGNING_NAMESPACE}, error::{EncodingError, SignatureError}, CryptoError, @@ -172,7 +172,7 @@ mod tests { use serde::{Deserialize, Serialize}; use crate::{ - content_format::{CoseKeyContentFormat, CoseSign1ContentFormat, Bytes}, + content_format::{Bytes, CoseKeyContentFormat, CoseSign1ContentFormat}, CoseSerializable, CryptoError, SignatureAlgorithm, SignedObject, SigningKey, SigningNamespace, VerifyingKey, }; @@ -200,10 +200,9 @@ mod tests { #[test] fn test_roundtrip_cose() { - let signed_object = SignedObject::from_cose( - &Into::>::into(SIGNED_OBJECT), - ) - .unwrap(); + let signed_object = + SignedObject::from_cose(&Into::>::into(SIGNED_OBJECT)) + .unwrap(); assert_eq!( signed_object.content_type().unwrap(), coset::iana::CoapContentFormat::Cbor @@ -220,14 +219,12 @@ mod tests { let test_message = TestMessage { field1: "Test message".to_string(), }; - let signed_object = SignedObject::from_cose( - &Into::>::into(SIGNED_OBJECT), - ) - .unwrap(); - let verifying_key = VerifyingKey::from_cose( - &Into::>::into(VERIFYING_KEY), - ) - .unwrap(); + let signed_object = + SignedObject::from_cose(&Into::>::into(SIGNED_OBJECT)) + .unwrap(); + let verifying_key = + VerifyingKey::from_cose(&Into::>::into(VERIFYING_KEY)) + .unwrap(); let namespace = SigningNamespace::ExampleNamespace; let payload: TestMessage = signed_object .verify_and_unwrap(&verifying_key, &namespace) diff --git a/crates/bitwarden-crypto/src/signing/signing_key.rs b/crates/bitwarden-crypto/src/signing/signing_key.rs index 0a1033ffe..12644b3f9 100644 --- a/crates/bitwarden-crypto/src/signing/signing_key.rs +++ b/crates/bitwarden-crypto/src/signing/signing_key.rs @@ -13,7 +13,7 @@ use super::{ SignatureAlgorithm, }; use crate::{ - content_format::{CoseKeyContentFormat, Bytes}, + content_format::{Bytes, CoseKeyContentFormat}, cose::CoseSerializable, error::{EncodingError, Result}, keys::KeyId, diff --git a/crates/bitwarden-crypto/src/signing/verifying_key.rs b/crates/bitwarden-crypto/src/signing/verifying_key.rs index 007f43814..507a1d0c7 100644 --- a/crates/bitwarden-crypto/src/signing/verifying_key.rs +++ b/crates/bitwarden-crypto/src/signing/verifying_key.rs @@ -11,7 +11,7 @@ use coset::{ use super::{ed25519_verifying_key, key_id, SignatureAlgorithm}; use crate::{ - content_format::{CoseKeyContentFormat, Bytes}, + content_format::{Bytes, CoseKeyContentFormat}, cose::CoseSerializable, error::{EncodingError, SignatureError}, keys::KeyId, @@ -129,10 +129,9 @@ mod tests { #[test] fn test_cose_roundtrip_encode_verifying() { - let verifying_key = VerifyingKey::from_cose( - &Bytes::::from(VERIFYING_KEY.to_vec()), - ) - .unwrap(); + let verifying_key = + VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY.to_vec())) + .unwrap(); let cose = verifying_key.to_cose(); let parsed_key = VerifyingKey::from_cose(&cose).unwrap(); @@ -141,10 +140,9 @@ mod tests { #[test] fn test_testvector() { - let verifying_key = VerifyingKey::from_cose( - &Bytes::::from(VERIFYING_KEY.to_vec()), - ) - .unwrap(); + let verifying_key = + VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY.to_vec())) + .unwrap(); assert_eq!(verifying_key.algorithm(), SignatureAlgorithm::Ed25519); verifying_key @@ -154,10 +152,9 @@ mod tests { #[test] fn test_invalid_testvector() { - let verifying_key = VerifyingKey::from_cose( - &Bytes::::from(VERIFYING_KEY.to_vec()), - ) - .unwrap(); + let verifying_key = + VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY.to_vec())) + .unwrap(); assert_eq!(verifying_key.algorithm(), SignatureAlgorithm::Ed25519); // This should fail, as the signed object is not valid for the given verifying key. diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 5e644745a..7cdb425c9 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -9,9 +9,9 @@ use zeroize::Zeroizing; use super::KeyStoreInner; use crate::{ derive_shareable_key, error::UnsupportedOperation, signing, store::backend::StoreBackend, - AsymmetricCryptoKey, BitwardenLegacyKeyContentFormat, ContentFormat, CryptoError, EncString, - KeyId, KeyIds, Result, Bytes, Signature, SignatureAlgorithm, SignedObject, - SignedPublicKey, SignedPublicKeyMessage, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, + AsymmetricCryptoKey, BitwardenLegacyKeyContentFormat, Bytes, ContentFormat, CryptoError, + EncString, KeyId, KeyIds, Result, Signature, SignatureAlgorithm, SignedObject, SignedPublicKey, + SignedPublicKeyMessage, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, }; /// The context of a crypto operation using [super::KeyStore] @@ -156,26 +156,16 @@ impl KeyStoreContext<'_, Ids> { let key = match (wrapped_key, wrapping_key) { (EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => { - SymmetricCryptoKey::try_from( - &Bytes::::from( - crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)?, - ), - )? + SymmetricCryptoKey::try_from(&Bytes::::from( + crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)?, + ))? } ( EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data }, SymmetricCryptoKey::Aes256CbcHmacKey(key), - ) => SymmetricCryptoKey::try_from( - &Bytes::::from( - crate::aes::decrypt_aes256_hmac( - iv, - mac, - data.clone(), - &key.mac_key, - &key.enc_key, - )?, - ), - )?, + ) => SymmetricCryptoKey::try_from(&Bytes::::from( + crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key)?, + ))?, ( EncString::Cose_Encrypt0_B64 { data }, SymmetricCryptoKey::XChaCha20Poly1305Key(key), @@ -184,11 +174,9 @@ impl KeyStoreContext<'_, Ids> { crate::cose::decrypt_xchacha20_poly1305(data, key)?; match content_format { ContentFormat::BitwardenLegacyKey => { - SymmetricCryptoKey::try_from(&Bytes::< - BitwardenLegacyKeyContentFormat, - >::from( - content_bytes - ))? + SymmetricCryptoKey::try_from( + &Bytes::::from(content_bytes), + )? } ContentFormat::CoseKey => SymmetricCryptoKey::try_from_cose(&content_bytes)?, _ => return Err(CryptoError::InvalidKey), diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index 303d3e473..ec28a46c4 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -8,8 +8,8 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - generate_random_bytes, CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, - KeyStoreContext, OctetStreamContentFormat, PrimitiveEncryptable, Bytes, + generate_random_bytes, Bytes, CompositeEncryptable, CryptoError, Decryptable, EncString, + IdentifyKey, KeyStoreContext, OctetStreamContentFormat, PrimitiveEncryptable, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/crates/bitwarden-send/src/send_client.rs b/crates/bitwarden-send/src/send_client.rs index cddf5f20c..d8a9a2936 100644 --- a/crates/bitwarden-send/src/send_client.rs +++ b/crates/bitwarden-send/src/send_client.rs @@ -2,8 +2,7 @@ use std::path::Path; use bitwarden_core::Client; use bitwarden_crypto::{ - Decryptable, EncString, IdentifyKey, OctetStreamContentFormat, PrimitiveEncryptable, - Bytes, + Bytes, Decryptable, EncString, IdentifyKey, OctetStreamContentFormat, PrimitiveEncryptable, }; use thiserror::Error; @@ -130,8 +129,7 @@ impl SendClient { let key = Send::get_key(&mut ctx, &send.key, send.key_identifier())?; - let encrypted = - Bytes::::from(buffer).encrypt(&mut ctx, key)?; + let encrypted = Bytes::::from(buffer).encrypt(&mut ctx, key)?; Ok(encrypted.to_buffer()?) } } diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index e5b9f883e..b37af2f6c 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,7 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, - OctetStreamContentFormat, PrimitiveEncryptable, Bytes, + Bytes, CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, + OctetStreamContentFormat, PrimitiveEncryptable, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -94,8 +94,8 @@ impl CompositeEncryptable // Because this is a new attachment, we have to generate a key for it, encrypt the contents // with it, and then encrypt the key with the cipher key let attachment_key = ctx.generate_symmetric_key(ATTACHMENT_KEY)?; - let encrypted_contents = Bytes::::from(self.contents) - .encrypt(ctx, attachment_key)?; + let encrypted_contents = + Bytes::::from(self.contents).encrypt(ctx, attachment_key)?; attachment.key = Some(ctx.wrap_symmetric_key(ciphers_key, attachment_key)?); let contents = encrypted_contents.to_buffer()?; diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 0da4cf85e..7bebaa85a 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -2,12 +2,11 @@ use std::str::FromStr; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyContentFormat, + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyContentFormat, Bytes, CoseKeyContentFormat, CoseSerializable, CryptoError, Decryptable, EncString, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, OctetStreamContentFormat, - Pkcs8PrivateKeyDerContentFormat, PrimitiveEncryptable, Bytes, SignatureAlgorithm, - SignedPublicKey, SigningKey, SpkiPublicKeyDerContentFormat, SymmetricCryptoKey, - UnsignedSharedKey, VerifyingKey, + Pkcs8PrivateKeyDerContentFormat, PrimitiveEncryptable, SignatureAlgorithm, SignedPublicKey, + SigningKey, SpkiPublicKeyDerContentFormat, SymmetricCryptoKey, UnsignedSharedKey, VerifyingKey, }; use wasm_bindgen::prelude::*; @@ -132,9 +131,9 @@ impl PureCrypto { ) -> Result { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); - let wrapping_key = SymmetricCryptoKey::try_from(&Bytes::< - BitwardenLegacyKeyContentFormat, - >::from(wrapping_key))?; + let wrapping_key = SymmetricCryptoKey::try_from( + &Bytes::::from(wrapping_key), + )?; #[allow(deprecated)] context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), wrapping_key)?; let key_to_be_wrapped = SymmetricCryptoKey::try_from(&Bytes::< @@ -159,9 +158,9 @@ impl PureCrypto { ) -> Result, CryptoError> { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); - let wrapping_key = SymmetricCryptoKey::try_from(&Bytes::< - BitwardenLegacyKeyContentFormat, - >::from(wrapping_key))?; + let wrapping_key = SymmetricCryptoKey::try_from( + &Bytes::::from(wrapping_key), + )?; #[allow(deprecated)] context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), wrapping_key)?; // Note: The order of arguments is different here, and should probably be refactored @@ -189,9 +188,9 @@ impl PureCrypto { #[allow(deprecated)] context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from( - &Bytes::::from(wrapping_key), - )?, + SymmetricCryptoKey::try_from(&Bytes::::from( + wrapping_key, + ))?, )?; Ok( Bytes::::from(encapsulation_key) @@ -211,9 +210,9 @@ impl PureCrypto { #[allow(deprecated)] context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from( - &Bytes::::from(wrapping_key), - )?, + SymmetricCryptoKey::try_from(&Bytes::::from( + wrapping_key, + ))?, )?; EncString::from_str(wrapped_key.as_str())? .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) @@ -230,9 +229,9 @@ impl PureCrypto { #[allow(deprecated)] context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from( - &Bytes::::from(wrapping_key), - )?, + SymmetricCryptoKey::try_from(&Bytes::::from( + wrapping_key, + ))?, )?; Ok( Bytes::::from(decapsulation_key) @@ -252,9 +251,9 @@ impl PureCrypto { #[allow(deprecated)] context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from( - &Bytes::::from(wrapping_key), - )?, + SymmetricCryptoKey::try_from(&Bytes::::from( + wrapping_key, + ))?, )?; EncString::from_str(wrapped_key.as_str())? .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) @@ -269,9 +268,9 @@ impl PureCrypto { ) -> Result { let encapsulation_key = AsymmetricPublicCryptoKey::from_der(encapsulation_key.as_slice())?; Ok(UnsignedSharedKey::encapsulate_key_unsigned( - &SymmetricCryptoKey::try_from( - &Bytes::::from(shared_key), - )?, + &SymmetricCryptoKey::try_from(&Bytes::::from( + shared_key, + ))?, &encapsulation_key, )? .to_string()) @@ -301,8 +300,7 @@ impl PureCrypto { wrapping_key: Vec, ) -> Result, CryptoError> { let bytes = Self::symmetric_decrypt_bytes(signing_key, wrapping_key)?; - let signing_key = - SigningKey::from_cose(&Bytes::::from(bytes))?; + let signing_key = SigningKey::from_cose(&Bytes::::from(bytes))?; let verifying_key = signing_key.to_verifying_key(); Ok(verifying_key.to_cose().as_ref().to_vec()) } @@ -311,9 +309,8 @@ impl PureCrypto { pub fn key_algorithm_for_verifying_key( verifying_key: Vec, ) -> Result { - let verifying_key = VerifyingKey::from_cose( - &Bytes::::from(verifying_key), - )?; + let verifying_key = + VerifyingKey::from_cose(&Bytes::::from(verifying_key))?; let algorithm = verifying_key.algorithm(); Ok(algorithm) } @@ -327,9 +324,8 @@ impl PureCrypto { verifying_key: Vec, ) -> Result, CryptoError> { let signed_public_key = SignedPublicKey::try_from(signed_public_key)?; - let verifying_key = VerifyingKey::from_cose( - &Bytes::::from(verifying_key), - )?; + let verifying_key = + VerifyingKey::from_cose(&Bytes::::from(verifying_key))?; signed_public_key .verify_and_unwrap(&verifying_key) .map(|public_key| public_key.to_der())? @@ -611,10 +607,9 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= #[test] fn test_key_algorithm_for_verifying_key() { - let verifying_key = VerifyingKey::from_cose( - &Bytes::::from(VERIFYING_KEY.to_vec()), - ) - .unwrap(); + let verifying_key = + VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY.to_vec())) + .unwrap(); let algorithm = PureCrypto::key_algorithm_for_verifying_key(verifying_key.to_cose().as_ref().to_vec()) .unwrap(); @@ -628,19 +623,17 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= SIGNING_KEY_WRAPPING_KEY.to_vec(), ) .unwrap(); - let verifying_key = VerifyingKey::from_cose( - &Bytes::::from(VERIFYING_KEY.to_vec()), - ) - .unwrap(); + let verifying_key = + VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY.to_vec())) + .unwrap(); let verifying_key_derived = PureCrypto::verifying_key_for_signing_key( wrapped_signing_key.to_string(), SIGNING_KEY_WRAPPING_KEY.to_vec(), ) .unwrap(); - let verifying_key_derived = VerifyingKey::from_cose( - &Bytes::::from(verifying_key_derived), - ) - .unwrap(); + let verifying_key_derived = + VerifyingKey::from_cose(&Bytes::::from(verifying_key_derived)) + .unwrap(); assert_eq!(verifying_key.to_cose(), verifying_key_derived.to_cose()); } From 24f14319d36cd979cf7ca58eff66a137bf0be366 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 19 Jun 2025 14:42:28 +0200 Subject: [PATCH 059/124] Simplify encoded symmetric key --- .../src/enc_string/asymmetric.rs | 2 +- .../src/keys/symmetric_crypto_key.rs | 53 ++++++++++--------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/crates/bitwarden-crypto/src/enc_string/asymmetric.rs b/crates/bitwarden-crypto/src/enc_string/asymmetric.rs index 88a8e4e3d..90f2880e0 100644 --- a/crates/bitwarden-crypto/src/enc_string/asymmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/asymmetric.rs @@ -169,7 +169,7 @@ impl UnsignedSharedKey { Ok(UnsignedSharedKey::Rsa2048_OaepSha1_B64 { data: encrypt_rsa2048_oaep_sha1( rsa_public_key, - &encapsulated_key.to_encoded().as_ref().to_vec(), + encapsulated_key.to_encoded().as_ref(), )?, }) } diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index ae1061bf0..bc60f3997 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -150,11 +150,11 @@ impl SymmetricCryptoKey { pub fn to_encoded(&self) -> Bytes { let encoded_key = self.to_encoded_raw(); match encoded_key { - EncodedSymmetricKey::Aes256CbcKey(_) | EncodedSymmetricKey::Aes256CbcHmacKey(_) => { + EncodedSymmetricKey::LegacyNonCoseKey(_) => { let encoded_key: Vec = encoded_key.into(); Bytes::from(encoded_key) } - EncodedSymmetricKey::XChaCha20Poly1305Key(_) => { + EncodedSymmetricKey::CoseKey(_) => { let mut encoded_key: Vec = encoded_key.into(); pad_key(&mut encoded_key, Self::AES256_CBC_HMAC_KEY_LEN + 1); Bytes::from(encoded_key) @@ -191,13 +191,13 @@ impl SymmetricCryptoKey { pub(crate) fn to_encoded_raw(&self) -> EncodedSymmetricKey { match self { Self::Aes256CbcKey(key) => { - EncodedSymmetricKey::Aes256CbcKey(key.enc_key.to_vec().into()) + EncodedSymmetricKey::LegacyNonCoseKey(key.enc_key.to_vec().into()) } Self::Aes256CbcHmacKey(key) => { let mut buf = Vec::with_capacity(64); buf.extend_from_slice(&key.enc_key); buf.extend_from_slice(&key.mac_key); - EncodedSymmetricKey::Aes256CbcHmacKey(buf.into()) + EncodedSymmetricKey::LegacyNonCoseKey(buf.into()) } Self::XChaCha20Poly1305Key(key) => { let builder = coset::CoseKeyBuilder::new_symmetric_key(key.enc_key.to_vec()); @@ -211,7 +211,7 @@ impl SymmetricCryptoKey { cose_key.alg = Some(RegisteredLabelWithPrivate::PrivateUse( cose::XCHACHA20_POLY1305, )); - EncodedSymmetricKey::XChaCha20Poly1305Key( + EncodedSymmetricKey::CoseKey( cose_key .to_vec() .expect("cose key serialization should not fail") @@ -280,10 +280,10 @@ impl TryFrom<&Bytes> for SymmetricCryptoKey { // are the raw serializations of the AES256-CBC, and AES256-CBC-HMAC keys. If they // are longer, they are COSE keys. The COSE keys are padded to the minimum length of // 65 bytes, when serialized to raw byte arrays. - let result = if slice.len() == Self::AES256_CBC_HMAC_KEY_LEN { - Self::try_from(EncodedSymmetricKey::Aes256CbcHmacKey(value.clone())) - } else if slice.len() == Self::AES256_CBC_KEY_LEN { - Self::try_from(EncodedSymmetricKey::Aes256CbcKey(value.clone())) + let result = if slice.len() == Self::AES256_CBC_HMAC_KEY_LEN + || slice.len() == Self::AES256_CBC_KEY_LEN + { + Self::try_from(EncodedSymmetricKey::LegacyNonCoseKey(value.clone())) } else if slice.len() > Self::AES256_CBC_HMAC_KEY_LEN { let unpadded_value = unpad_key(slice)?; Ok(Self::try_from_cose(unpadded_value)?) @@ -300,12 +300,16 @@ impl TryFrom for SymmetricCryptoKey { fn try_from(value: EncodedSymmetricKey) -> Result { match value { - EncodedSymmetricKey::Aes256CbcKey(key) => { + EncodedSymmetricKey::LegacyNonCoseKey(key) + if key.as_ref().len() == Self::AES256_CBC_KEY_LEN => + { let mut enc_key = Box::pin(GenericArray::::default()); enc_key.copy_from_slice(&key.as_ref()[..Self::AES256_CBC_KEY_LEN]); Ok(Self::Aes256CbcKey(Aes256CbcKey { enc_key })) } - EncodedSymmetricKey::Aes256CbcHmacKey(key) => { + EncodedSymmetricKey::LegacyNonCoseKey(key) + if key.as_ref().len() == Self::AES256_CBC_HMAC_KEY_LEN => + { let mut enc_key = Box::pin(GenericArray::::default()); enc_key.copy_from_slice(&key.as_ref()[..32]); @@ -317,7 +321,8 @@ impl TryFrom for SymmetricCryptoKey { mac_key, })) } - EncodedSymmetricKey::XChaCha20Poly1305Key(key) => Self::try_from_cose(key.as_ref()), + EncodedSymmetricKey::CoseKey(key) => Self::try_from_cose(key.as_ref()), + _ => Err(CryptoError::InvalidKey), } } } @@ -390,26 +395,24 @@ fn unpad_key(key_bytes: &[u8]) -> Result<&[u8], CryptoError> { /// An enum to represent the different encodings of symmetric crypto keys. pub enum EncodedSymmetricKey { - Aes256CbcKey(Bytes), - Aes256CbcHmacKey(Bytes), - XChaCha20Poly1305Key(Bytes), + /// An Aes256-CBC-HMAC key, or a Aes256-CBC key + LegacyNonCoseKey(Bytes), + /// A symmetric key encoded as a COSE key + CoseKey(Bytes), } -impl Into> for EncodedSymmetricKey { - fn into(self) -> Vec { - match self { - EncodedSymmetricKey::Aes256CbcKey(key) => key.as_ref().to_vec(), - EncodedSymmetricKey::Aes256CbcHmacKey(key) => key.as_ref().to_vec(), - EncodedSymmetricKey::XChaCha20Poly1305Key(key) => key.as_ref().to_vec(), +impl From for Vec { + fn from(val: EncodedSymmetricKey) -> Self { + match val { + EncodedSymmetricKey::LegacyNonCoseKey(key) => key.to_vec(), + EncodedSymmetricKey::CoseKey(key) => key.to_vec(), } } } impl EncodedSymmetricKey { pub fn content_format(&self) -> ContentFormat { match self { - EncodedSymmetricKey::Aes256CbcKey(_) | EncodedSymmetricKey::Aes256CbcHmacKey(_) => { - ContentFormat::BitwardenLegacyKey - } - EncodedSymmetricKey::XChaCha20Poly1305Key(_) => ContentFormat::CoseKey, + EncodedSymmetricKey::LegacyNonCoseKey(_) => ContentFormat::BitwardenLegacyKey, + EncodedSymmetricKey::CoseKey(_) => ContentFormat::CoseKey, } } } From 916a46e918d67edb9818390d3a89fc2bf4d25352 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 19 Jun 2025 15:01:33 +0200 Subject: [PATCH 060/124] Remove unwrap in example --- crates/bitwarden-crypto/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/README.md b/crates/bitwarden-crypto/README.md index 60ea5836f..5dc24ef2e 100644 --- a/crates/bitwarden-crypto/README.md +++ b/crates/bitwarden-crypto/README.md @@ -19,7 +19,7 @@ async fn example() -> Result<(), CryptoError> { let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let data = "Hello, World!".to_owned(); - let encrypted = data.clone().encrypt_with_key(&key).unwrap(); + let encrypted = data.clone().encrypt_with_key(&key)?; let decrypted: String = encrypted.decrypt_with_key(&key)?; assert_eq!(data, decrypted); From 670c6a78e9f7736d0088f73a3e690c35baae0490 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 19 Jun 2025 15:06:20 +0200 Subject: [PATCH 061/124] Cleanup --- crates/bitwarden-crypto/examples/signature.rs | 4 ++-- .../bitwarden-crypto/examples/signed_object.rs | 2 +- .../src/keys/asymmetric_crypto_key.rs | 4 ++-- .../src/keys/signed_public_key.rs | 2 +- .../bitwarden-wasm-internal/src/pure_crypto.rs | 18 +++++++----------- 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/crates/bitwarden-crypto/examples/signature.rs b/crates/bitwarden-crypto/examples/signature.rs index 373fe620c..71f6fb958 100644 --- a/crates/bitwarden-crypto/examples/signature.rs +++ b/crates/bitwarden-crypto/examples/signature.rs @@ -38,7 +38,7 @@ fn main() { .expect("Failed to sign message"); // Alice sends the signed object to Bob - mock_server.upload("signature", signature.to_cose().as_ref().to_vec()); + mock_server.upload("signature", signature.to_cose().to_vec()); mock_server.upload("serialized_message", serialized_message.as_bytes().to_vec()); // Bob retrieves the signed object from the server @@ -78,7 +78,7 @@ fn main() { ) .expect("Failed to counter sign message"); // Bob sends the counter signature to Charlie - mock_server.upload("bobs_signature", bobs_signature.to_cose().as_ref().to_vec()); + mock_server.upload("bobs_signature", bobs_signature.to_cose().to_vec()); // Charlie retrieves the signatures, and the message let retrieved_serialized_message = bitwarden_crypto::SerializedMessage::from_bytes( diff --git a/crates/bitwarden-crypto/examples/signed_object.rs b/crates/bitwarden-crypto/examples/signed_object.rs index dd36665e9..0223338b2 100644 --- a/crates/bitwarden-crypto/examples/signed_object.rs +++ b/crates/bitwarden-crypto/examples/signed_object.rs @@ -36,7 +36,7 @@ fn main() { .expect("Failed to sign message"); // Alice sends the signed object to Bob - mock_server.upload("signed_object", signed_object.to_cose().as_ref().to_vec()); + mock_server.upload("signed_object", signed_object.to_cose().to_vec()); // Bob retrieves the signed object from the server let retrieved_signed_object = SignedObject::from_cose(&Bytes::::from( diff --git a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs index 67461746e..666451489 100644 --- a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs @@ -211,8 +211,8 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= assert_eq!(pem_key.to_der().unwrap(), der_key.to_der().unwrap()); // Check that the keys can be converted back to DER - assert_eq!(der_key.to_der().unwrap().as_ref().to_vec(), der_key_vec); - assert_eq!(pem_key.to_der().unwrap().as_ref().to_vec(), der_key_vec); + assert_eq!(der_key.to_der().unwrap().to_vec(), der_key_vec); + assert_eq!(pem_key.to_der().unwrap().to_vec(), der_key_vec); } #[test] diff --git a/crates/bitwarden-crypto/src/keys/signed_public_key.rs b/crates/bitwarden-crypto/src/keys/signed_public_key.rs index 758436b7b..d46898996 100644 --- a/crates/bitwarden-crypto/src/keys/signed_public_key.rs +++ b/crates/bitwarden-crypto/src/keys/signed_public_key.rs @@ -82,7 +82,7 @@ pub struct SignedPublicKey(pub(crate) SignedObject); impl From for Vec { fn from(val: SignedPublicKey) -> Self { - val.0.to_cose().as_ref().to_vec() + val.0.to_cose().to_vec() } } diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 7bebaa85a..5e126163f 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -302,7 +302,7 @@ impl PureCrypto { let bytes = Self::symmetric_decrypt_bytes(signing_key, wrapping_key)?; let signing_key = SigningKey::from_cose(&Bytes::::from(bytes))?; let verifying_key = signing_key.to_verifying_key(); - Ok(verifying_key.to_cose().as_ref().to_vec()) + Ok(verifying_key.to_cose().to_vec()) } /// Returns the algorithm used for the given verifying key. @@ -329,7 +329,7 @@ impl PureCrypto { signed_public_key .verify_and_unwrap(&verifying_key) .map(|public_key| public_key.to_der())? - .map(|pk| pk.as_ref().to_vec()) + .map(|pk| pk.to_vec()) } } @@ -575,16 +575,13 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); let wrapping_key = PureCrypto::make_user_key_aes256_cbc_hmac(); let wrapped_key = PureCrypto::wrap_decapsulation_key( - decapsulation_key.to_der().unwrap().as_ref().to_vec(), + decapsulation_key.to_der().unwrap().to_vec(), wrapping_key.clone(), ) .unwrap(); let unwrapped_key = PureCrypto::unwrap_decapsulation_key(wrapped_key, wrapping_key).unwrap(); - assert_eq!( - decapsulation_key.to_der().unwrap().as_ref().to_vec(), - unwrapped_key - ); + assert_eq!(decapsulation_key.to_der().unwrap().to_vec(), unwrapped_key); } #[test] @@ -594,12 +591,12 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= let encapsulation_key = decapsulation_key.to_public_key().to_der().unwrap(); let encapsulated_key = PureCrypto::encapsulate_key_unsigned( shared_key.clone(), - encapsulation_key.clone().as_ref().to_vec(), + encapsulation_key.clone().to_vec(), ) .unwrap(); let unwrapped_key = PureCrypto::decapsulate_key_unsigned( encapsulated_key, - decapsulation_key.to_der().unwrap().as_ref().to_vec(), + decapsulation_key.to_der().unwrap().to_vec(), ) .unwrap(); assert_eq!(shared_key, unwrapped_key); @@ -611,8 +608,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY.to_vec())) .unwrap(); let algorithm = - PureCrypto::key_algorithm_for_verifying_key(verifying_key.to_cose().as_ref().to_vec()) - .unwrap(); + PureCrypto::key_algorithm_for_verifying_key(verifying_key.to_cose().to_vec()).unwrap(); assert_eq!(algorithm, SignatureAlgorithm::Ed25519); } From 6da4a0b9c5db7c169f5296f96cfb341a15b14030 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 19 Jun 2025 15:30:50 +0200 Subject: [PATCH 062/124] Fix docs --- crates/bitwarden-core/src/key_management/mod.rs | 1 - crates/bitwarden-crypto/src/store/context.rs | 2 +- crates/bitwarden-crypto/src/traits/encryptable.rs | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/bitwarden-core/src/key_management/mod.rs b/crates/bitwarden-core/src/key_management/mod.rs index 3e51f1106..82d2497b4 100644 --- a/crates/bitwarden-core/src/key_management/mod.rs +++ b/crates/bitwarden-core/src/key_management/mod.rs @@ -7,7 +7,6 @@ //! usually used in the type bounds of [KeyStore], //! [KeyStoreContext](bitwarden_crypto::KeyStoreContext), //! [PrimitiveEncryptable](bitwarden_crypto::PrimitiveEncryptable), -//! [PrimitiveEncryptableWithContentType](bitwarden_crypto::PrimitiveEncryptableWithContentType), //! [CompositeEncryptable](bitwarden_crypto::CompositeEncryptable), and //! [Decryptable](bitwarden_crypto::Decryptable). use bitwarden_crypto::{key_ids, KeyStore, SymmetricCryptoKey}; diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 7cdb425c9..ceca88898 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -18,7 +18,7 @@ use crate::{ /// /// This will usually be accessed from an implementation of [crate::Decryptable] or /// [crate::CompositeEncryptable], [crate::PrimitiveEncryptable], -/// [crate::PrimitiveEncryptableWithContentType], but can also be obtained +/// but can also be obtained /// through [super::KeyStore::context] /// /// This context contains access to the user keys stored in the [super::KeyStore] (sometimes diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index 6b5debafe..8a065af48 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -15,9 +15,9 @@ use crate::{store::KeyStoreContext, ContentFormat, CryptoError, EncString, KeyId, KeyIds}; /// An encryption operation that takes the input value and encrypts the fields on it recursively. -/// Implementations should generally consist of calling [PrimitiveEncryptable::encrypt] or -/// [PrimitiveEncryptableWithContentType::encrypt] for all the fields of the type. Sometimes, it is -/// necessary to call [CompositeEncryptable::encrypt_composite], if the object is not a flat struct. +/// Implementations should generally consist of calling [PrimitiveEncryptable::encrypt] for all the +/// fields of the type. Sometimes, it is necessary to call +/// [CompositeEncryptable::encrypt_composite], if the object is not a flat struct. pub trait CompositeEncryptable { /// For a struct made up of many small encstrings, such as a cipher, this takes the struct /// and recursively encrypts all the fields / sub-structs. From b00f48bd35ea14311dcf1c891973036f9a79eebe Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 24 Jun 2025 13:25:32 +0200 Subject: [PATCH 063/124] Make content format trait sealed and add type aliases --- .../bitwarden-core/src/auth/auth_request.rs | 4 +- .../src/auth/login/access_token.rs | 6 +- .../src/client/encryption_settings.rs | 5 +- .../src/key_management/crypto.rs | 12 +- crates/bitwarden-crypto/src/content_format.rs | 32 +++++- .../src/enc_string/asymmetric.rs | 8 +- .../src/enc_string/symmetric.rs | 7 +- .../src/keys/asymmetric_crypto_key.rs | 7 +- .../bitwarden-crypto/src/keys/device_key.rs | 16 +-- .../bitwarden-crypto/src/keys/master_key.rs | 6 +- .../src/keys/symmetric_crypto_key.rs | 33 ++---- .../bitwarden-crypto/src/signing/signature.rs | 22 ++-- .../src/signing/signed_object.rs | 28 ++--- .../src/signing/signing_key.rs | 8 +- .../src/signing/verifying_key.rs | 17 ++- crates/bitwarden-crypto/src/store/context.rs | 12 +- .../src/pure_crypto.rs | 104 +++++++----------- 17 files changed, 146 insertions(+), 181 deletions(-) diff --git a/crates/bitwarden-core/src/auth/auth_request.rs b/crates/bitwarden-core/src/auth/auth_request.rs index ccc9ce3a0..222a95809 100644 --- a/crates/bitwarden-core/src/auth/auth_request.rs +++ b/crates/bitwarden-core/src/auth/auth_request.rs @@ -111,7 +111,7 @@ mod tests { use std::num::NonZeroU32; use bitwarden_crypto::{ - BitwardenLegacyKeyContentFormat, Bytes, Kdf, MasterKey, SpkiPublicKeyDerContentFormat, + BitwardenLegacyKeyBytes, Bytes, Kdf, MasterKey, SpkiPublicKeyDerContentFormat, }; use super::*; @@ -135,7 +135,7 @@ mod tests { AsymmetricCryptoKey::from_der(&STANDARD.decode(&request.private_key).unwrap().into()) .unwrap(); - let secret = Bytes::::from(secret); + let secret = BitwardenLegacyKeyBytes::from(secret); let encrypted = UnsignedSharedKey::encapsulate_key_unsigned( &SymmetricCryptoKey::try_from(&secret).unwrap(), &private_key.to_public_key(), diff --git a/crates/bitwarden-core/src/auth/login/access_token.rs b/crates/bitwarden-core/src/auth/login/access_token.rs index 95a89463b..773e8ade8 100644 --- a/crates/bitwarden-core/src/auth/login/access_token.rs +++ b/crates/bitwarden-core/src/auth/login/access_token.rs @@ -1,9 +1,7 @@ use std::path::{Path, PathBuf}; use base64::{engine::general_purpose::STANDARD, Engine}; -use bitwarden_crypto::{ - BitwardenLegacyKeyContentFormat, Bytes, EncString, KeyDecryptable, SymmetricCryptoKey, -}; +use bitwarden_crypto::{BitwardenLegacyKeyBytes, EncString, KeyDecryptable, SymmetricCryptoKey}; use chrono::Utc; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -69,7 +67,7 @@ pub(crate) async fn login_access_token( let payload: Payload = serde_json::from_slice(&decrypted_payload)?; let encryption_key = STANDARD.decode(&payload.encryption_key)?; - let encryption_key = Bytes::::from(encryption_key); + let encryption_key = BitwardenLegacyKeyBytes::from(encryption_key); let encryption_key = SymmetricCryptoKey::try_from(&encryption_key)?; let access_token_obj: JwtToken = r.access_token.parse()?; diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index eb5ce9c24..b93fc01e3 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -58,13 +58,12 @@ impl EncryptionSettings { use crate::key_management::{AsymmetricKeyId, SigningKeyId, SymmetricKeyId}; let private_key = { - use bitwarden_crypto::{Bytes, Pkcs8PrivateKeyDerContentFormat}; + use bitwarden_crypto::Bytes; let dec: Vec = private_key.decrypt_with_key(&user_key)?; - let dec: Bytes = Bytes::from(dec); // FIXME: [PM-11690] - Temporarily ignore invalid private keys until we have a recovery // process in place. - AsymmetricCryptoKey::from_der(&dec) + AsymmetricCryptoKey::from_der(&Bytes::from(dec)) .map_err(|_| { warn!("Invalid private key"); }) diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index 8a5e4d28a..97e1f519a 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -9,9 +9,8 @@ use std::collections::HashMap; use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ AsymmetricCryptoKey, Bytes, CoseSerializable, CryptoError, EncString, Kdf, KeyDecryptable, - KeyEncryptable, MasterKey, Pkcs8PrivateKeyDerContentFormat, PrimitiveEncryptable, - SignatureAlgorithm, SignedPublicKey, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, - UserKey, + KeyEncryptable, MasterKey, Pkcs8PrivateKeyBytes, PrimitiveEncryptable, SignatureAlgorithm, + SignedPublicKey, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey, }; use bitwarden_error::bitwarden_error; use schemars::JsonSchema; @@ -537,8 +536,7 @@ pub(super) fn verify_asymmetric_keys( .decrypt_with_key(user_key) .map_err(VerifyError::DecryptFailed)?; - let decrypted_private_key: Bytes = - Bytes::from(decrypted_private_key); + let decrypted_private_key: Pkcs8PrivateKeyBytes = Bytes::from(decrypted_private_key); let private_key = AsymmetricCryptoKey::from_der(&decrypted_private_key) .map_err(VerifyError::ParseFailed)?; @@ -616,7 +614,7 @@ pub fn make_user_signing_keys_for_enrollment( mod tests { use std::num::NonZeroU32; - use bitwarden_crypto::RsaKeyPair; + use bitwarden_crypto::{Pkcs8PrivateKeyDerContentFormat, RsaKeyPair}; use super::*; use crate::Client; @@ -844,7 +842,7 @@ mod tests { let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; let private_key = STANDARD.decode(private_key).unwrap(); - let private_key = Bytes::::from(private_key); + let private_key = Pkcs8PrivateKeyBytes::from(private_key); let private_key = AsymmetricCryptoKey::from_der(&private_key).unwrap(); let decrypted: SymmetricCryptoKey = encrypted.decapsulate_key_unsigned(&private_key).unwrap(); diff --git a/crates/bitwarden-crypto/src/content_format.rs b/crates/bitwarden-crypto/src/content_format.rs index 4d2df5ea8..eaca2474a 100644 --- a/crates/bitwarden-crypto/src/content_format.rs +++ b/crates/bitwarden-crypto/src/content_format.rs @@ -39,11 +39,17 @@ pub enum ContentFormat { Cbor, } +mod private { + /// This trait is used to seal the `ConstContentFormat` trait, preventing external + /// implementations. + pub trait Sealed {} +} + /// This trait is used to instantiate different typed byte vectors with a specific content format, /// using `SerializedBytes`. This allows for compile-time guarantees about the content format /// of the serialized bytes. The exception here is the escape hatch using e.g. `from(Vec)`, /// which can still be mis-used, but has to be misused explicitly. -pub trait ConstContentFormat { +pub trait ConstContentFormat: private::Sealed { /// Returns the content format as a `ContentFormat` enum. fn content_format() -> ContentFormat; } @@ -89,68 +95,90 @@ impl Bytes { /// Content format for UTF-8 encoded text. Used for most text messages. #[derive(PartialEq, Eq, Clone, Debug)] pub(crate) struct Utf8ContentFormat; +impl private::Sealed for Utf8ContentFormat {} impl ConstContentFormat for Utf8ContentFormat { fn content_format() -> ContentFormat { ContentFormat::Utf8 } } +/// Utf8Bytes is a type alias for Bytes with `Utf8ContentFormat`, which is used for any textual data. +pub(crate) type Utf8Bytes = Bytes; /// Content format for raw bytes. Used for attachments and send seed keys. #[derive(PartialEq, Eq, Clone, Debug)] pub struct OctetStreamContentFormat; +impl private::Sealed for OctetStreamContentFormat {} impl ConstContentFormat for OctetStreamContentFormat { fn content_format() -> ContentFormat { ContentFormat::OctetStream } } +/// OctetStreamBytes is a type alias for Bytes with `OctetStreamContentFormat`. This should be used for e.g. attachments and other data without an +/// explicit content format. +pub type OctetStreamBytes = Bytes; /// Content format for PKCS8 private keys in DER format. #[derive(PartialEq, Eq, Clone, Debug)] pub struct Pkcs8PrivateKeyDerContentFormat; +impl private::Sealed for Pkcs8PrivateKeyDerContentFormat {} impl ConstContentFormat for Pkcs8PrivateKeyDerContentFormat { fn content_format() -> ContentFormat { ContentFormat::Pkcs8PrivateKey } } +/// Pkcs8PrivateKeyBytes is a type alias for Bytes with `Pkcs8PrivateKeyDerContentFormat`. This is used for PKCS8 private keys in DER format. +pub type Pkcs8PrivateKeyBytes = Bytes; /// Content format for SPKI public keys in DER format. #[derive(PartialEq, Eq, Clone, Debug)] pub struct SpkiPublicKeyDerContentFormat; +impl private::Sealed for SpkiPublicKeyDerContentFormat {} impl ConstContentFormat for SpkiPublicKeyDerContentFormat { fn content_format() -> ContentFormat { ContentFormat::SPKIPublicKeyDer } } +/// SpkiPublicKeyBytes is a type alias for Bytes with `SpkiPublicKeyDerContentFormat`. This is used for SPKI public keys in DER format. +pub type SpkiPublicKeyBytes = Bytes; /// Content format for COSE keys. #[derive(PartialEq, Eq, Clone, Debug)] pub struct CoseKeyContentFormat; +impl private::Sealed for CoseKeyContentFormat {} impl ConstContentFormat for CoseKeyContentFormat { fn content_format() -> ContentFormat { ContentFormat::CoseKey } } impl CoseContentFormat for CoseKeyContentFormat {} +/// CoseKeyBytes is a type alias for Bytes with `CoseKeyContentFormat`. This is used for serialized CoseKey objects. +pub type CoseKeyBytes = Bytes; /// A legacy content format for Bitwarden keys. See `ContentFormat::BitwardenLegacyKey` -#[allow(unused)] #[derive(PartialEq, Eq, Clone, Debug)] pub struct BitwardenLegacyKeyContentFormat; +impl private::Sealed for BitwardenLegacyKeyContentFormat {} impl ConstContentFormat for BitwardenLegacyKeyContentFormat { fn content_format() -> ContentFormat { ContentFormat::BitwardenLegacyKey } } +/// BitwardenLegacyKeyBytes is a type alias for Bytes with `BitwardenLegacyKeyContentFormat`. This is used for the legacy format for symmetric keys. +/// A description of the format is available in the `ContentFormat::BitwardenLegacyKey` documentation. +pub type BitwardenLegacyKeyBytes = Bytes; /// Content format for COSE Sign1 messages. #[derive(PartialEq, Eq, Clone, Debug)] pub struct CoseSign1ContentFormat; +impl private::Sealed for CoseSign1ContentFormat {} impl ConstContentFormat for CoseSign1ContentFormat { fn content_format() -> ContentFormat { ContentFormat::CoseSign1 } } impl CoseContentFormat for CoseSign1ContentFormat {} +/// CoseSign1Bytes is a type alias for Bytes with `CoseSign1ContentFormat`. This is used for serialized COSE Sign1 messages. +pub type CoseSign1Bytes = Bytes; /// A marker trait for COSE content formats. pub trait CoseContentFormat {} diff --git a/crates/bitwarden-crypto/src/enc_string/asymmetric.rs b/crates/bitwarden-crypto/src/enc_string/asymmetric.rs index 90f2880e0..1050bb3b0 100644 --- a/crates/bitwarden-crypto/src/enc_string/asymmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/asymmetric.rs @@ -10,8 +10,8 @@ use crate::{ error::{CryptoError, EncStringParseError, Result}, rsa::encrypt_rsa2048_oaep_sha1, util::FromStrVisitor, - AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyContentFormat, Bytes, - RawPrivateKey, RawPublicKey, SymmetricCryptoKey, + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyBytes, RawPrivateKey, + RawPublicKey, SymmetricCryptoKey, }; // This module is a workaround to avoid deprecated warnings that come from the ZeroizeOnDrop // macro expansion @@ -217,9 +217,7 @@ impl UnsignedSharedKey { } } .map_err(|_| CryptoError::KeyDecrypt)?; - SymmetricCryptoKey::try_from(&Bytes::::from( - key_data, - )) + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(key_data)) } } } diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index e0e8ca17e..0402444c6 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -6,11 +6,10 @@ use serde::Deserialize; use super::{check_length, from_b64, from_b64_vec, split_enc_string}; use crate::{ - content_format::{Bytes, Utf8ContentFormat}, error::{CryptoError, EncStringParseError, Result, UnsupportedOperation}, util::FromStrVisitor, Aes256CbcHmacKey, ContentFormat, KeyDecryptable, KeyEncryptable, KeyEncryptableWithContentType, - SymmetricCryptoKey, XChaCha20Poly1305Key, + SymmetricCryptoKey, Utf8Bytes, XChaCha20Poly1305Key, }; #[cfg(feature = "wasm")] @@ -323,13 +322,13 @@ impl KeyDecryptable> for EncString { impl KeyEncryptable for String { fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { - Into::>::into(self).encrypt_with_key(key) + Into::::into(self).encrypt_with_key(key) } } impl KeyEncryptable for &str { fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { - Into::>::into(self).encrypt_with_key(key) + Into::::into(self).encrypt_with_key(key) } } diff --git a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs index 666451489..0ed1bc12d 100644 --- a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs @@ -5,8 +5,9 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use super::key_encryptable::CryptoKey; use crate::{ - content_format::{Bytes, Pkcs8PrivateKeyDerContentFormat, SpkiPublicKeyDerContentFormat}, + content_format::{Bytes, SpkiPublicKeyDerContentFormat}, error::{CryptoError, Result}, + Pkcs8PrivateKeyBytes, }; /// Algorithm / public key encryption scheme used for encryption/decryption. @@ -114,7 +115,7 @@ impl AsymmetricCryptoKey { } #[allow(missing_docs)] - pub fn from_der(der: &Bytes) -> Result { + pub fn from_der(der: &Pkcs8PrivateKeyBytes) -> Result { use rsa::pkcs8::DecodePrivateKey; Ok(Self { inner: RawPrivateKey::RsaOaepSha1(Box::pin( @@ -124,7 +125,7 @@ impl AsymmetricCryptoKey { } #[allow(missing_docs)] - pub fn to_der(&self) -> Result> { + pub fn to_der(&self) -> Result { match &self.inner { RawPrivateKey::RsaOaepSha1(private_key) => { use rsa::pkcs8::EncodePrivateKey; diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index 1998dfa3a..2b71c878e 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -1,8 +1,7 @@ use super::{AsymmetricCryptoKey, PublicKeyEncryptionAlgorithm}; use crate::{ - content_format::{Bytes, Pkcs8PrivateKeyDerContentFormat}, - error::Result, - CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, UnsignedSharedKey, + content_format::Bytes, error::Result, CryptoError, EncString, KeyDecryptable, KeyEncryptable, + Pkcs8PrivateKeyBytes, SymmetricCryptoKey, UnsignedSharedKey, }; /// Device Key @@ -66,8 +65,7 @@ impl DeviceKey { protected_user_key: UnsignedSharedKey, ) -> Result { let device_private_key: Vec = protected_device_private_key.decrypt_with_key(&self.0)?; - let device_private_key: Bytes = - Bytes::from(device_private_key); + let device_private_key: Pkcs8PrivateKeyBytes = Bytes::from(device_private_key); let device_private_key = AsymmetricCryptoKey::from_der(&device_private_key)?; let user_key: SymmetricCryptoKey = @@ -91,7 +89,7 @@ impl TryFrom for DeviceKey { #[cfg(test)] mod tests { use super::*; - use crate::{derive_symmetric_key, BitwardenLegacyKeyContentFormat}; + use crate::{derive_symmetric_key, BitwardenLegacyKeyBytes, BitwardenLegacyKeyContentFormat}; #[test] fn test_trust_device() { @@ -121,8 +119,7 @@ mod tests { 218, 106, 89, 254, 208, 251, 101, 130, 10, ]; let user_key = - SymmetricCryptoKey::try_from(&Bytes::::from(user_key)) - .unwrap(); + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(user_key)).unwrap(); let key_data: &[u8] = &[ 114, 235, 60, 115, 172, 156, 203, 145, 195, 130, 215, 250, 88, 146, 215, 230, 12, 109, @@ -131,8 +128,7 @@ mod tests { 8, 247, 7, 203, 201, 65, 147, 206, 247, ]; let device_key = DeviceKey( - SymmetricCryptoKey::try_from(&Bytes::::from(key_data)) - .unwrap(), + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(key_data)).unwrap(), ); let protected_user_key: UnsignedSharedKey = "4.f+VbbacRhO2q4MOUSdt1AIjQ2FuLAvg4aDxJMXAh3VxvbmUADj8Ct/R7XEpPUqApmbRS566jS0eRVy8Sk08ogoCdj1IFN9VsIky2i2X1WHK1fUnr3UBmXE3tl2NPBbx56U+h73S2jNTSyet2W18Jg2q7/w8KIhR3J41QrG9aGoOTN93to3hb5W4z6rdrSI0e7GkizbwcIA0NH7Z1JyAhrjPm9+tjRjg060YbEbGaWTAOkZWfgbLjr8bY455DteO2xxG139cOx7EBo66N+YhjsLi0ozkeUyPQkoWBdKMcQllS7jCfB4fDyJA05ALTbk74syKkvqFxqwmQbg+aVn+dcw==".parse().unwrap(); diff --git a/crates/bitwarden-crypto/src/keys/master_key.rs b/crates/bitwarden-crypto/src/keys/master_key.rs index 3e380a9a2..653d34b7f 100644 --- a/crates/bitwarden-crypto/src/keys/master_key.rs +++ b/crates/bitwarden-crypto/src/keys/master_key.rs @@ -12,8 +12,8 @@ use super::{ }; use crate::{ util::{self}, - BitwardenLegacyKeyContentFormat, Bytes, CryptoError, EncString, KeyDecryptable, Result, - SymmetricCryptoKey, UserKey, + BitwardenLegacyKeyBytes, CryptoError, EncString, KeyDecryptable, Result, SymmetricCryptoKey, + UserKey, }; #[allow(missing_docs)] @@ -160,7 +160,7 @@ pub(super) fn decrypt_user_key( } }; - SymmetricCryptoKey::try_from(&Bytes::::from(dec)) + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(dec)) } /// Generate a new random user key and encrypt it with the master key. diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index bc60f3997..767cdd052 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -18,9 +18,7 @@ use super::{ key_encryptable::CryptoKey, key_id::{KeyId, KEY_ID_SIZE}, }; -use crate::{ - cose, BitwardenLegacyKeyContentFormat, Bytes, ContentFormat, CoseKeyContentFormat, CryptoError, -}; +use crate::{cose, BitwardenLegacyKeyBytes, Bytes, ContentFormat, CoseKeyBytes, CryptoError}; /// [Aes256CbcKey] is a symmetric encryption key, consisting of one 256-bit key, /// used to decrypt legacy type 0 enc strings. The data is not authenticated @@ -147,7 +145,7 @@ impl SymmetricCryptoKey { /// This can be used for storage and transmission in the old byte array format. /// When the wrapping key is a COSE key, and the wrapped key is a COSE key, then this should /// not use the byte representation but instead use the COSE key representation. - pub fn to_encoded(&self) -> Bytes { + pub fn to_encoded(&self) -> BitwardenLegacyKeyBytes { let encoded_key = self.to_encoded_raw(); match encoded_key { EncodedSymmetricKey::LegacyNonCoseKey(_) => { @@ -266,14 +264,14 @@ impl TryFrom for SymmetricCryptoKey { let bytes = STANDARD .decode(value) .map_err(|_| CryptoError::InvalidKey)?; - Self::try_from(&Bytes::::from(bytes)) + Self::try_from(&BitwardenLegacyKeyBytes::from(bytes)) } } -impl TryFrom<&Bytes> for SymmetricCryptoKey { +impl TryFrom<&BitwardenLegacyKeyBytes> for SymmetricCryptoKey { type Error = CryptoError; - fn try_from(value: &Bytes) -> Result { + fn try_from(value: &BitwardenLegacyKeyBytes) -> Result { let slice = value.as_ref(); // Raw byte serialized keys are either 32, 64, or more bytes long. If they are 32/64, they @@ -396,9 +394,9 @@ fn unpad_key(key_bytes: &[u8]) -> Result<&[u8], CryptoError> { /// An enum to represent the different encodings of symmetric crypto keys. pub enum EncodedSymmetricKey { /// An Aes256-CBC-HMAC key, or a Aes256-CBC key - LegacyNonCoseKey(Bytes), + LegacyNonCoseKey(BitwardenLegacyKeyBytes), /// A symmetric key encoded as a COSE key - CoseKey(Bytes), + CoseKey(CoseKeyBytes), } impl From for Vec { fn from(val: EncodedSymmetricKey) -> Self { @@ -437,8 +435,7 @@ mod tests { use super::{derive_symmetric_key, SymmetricCryptoKey}; use crate::{ keys::symmetric_crypto_key::{pad_key, unpad_key}, - Aes256CbcHmacKey, Aes256CbcKey, BitwardenLegacyKeyContentFormat, Bytes, - XChaCha20Poly1305Key, + Aes256CbcHmacKey, Aes256CbcKey, BitwardenLegacyKeyBytes, XChaCha20Poly1305Key, }; #[test] @@ -464,7 +461,7 @@ mod tests { #[test] fn test_decode_new_symmetric_crypto_key() { let key = STANDARD.decode("pQEEAlDib+JxbqMBlcd3KTUesbufAzoAARFvBIQDBAUGIFggt79surJXmqhPhYuuqi9ZyPfieebmtw2OsmN5SDrb4yUB").unwrap(); - let key = Bytes::::from(key); + let key = BitwardenLegacyKeyBytes::from(key); let key = SymmetricCryptoKey::try_from(&key).unwrap(); match key { SymmetricCryptoKey::XChaCha20Poly1305Key(_) => (), @@ -529,17 +526,9 @@ mod tests { #[test] fn test_eq_aes_cbc() { let key1 = - SymmetricCryptoKey::try_from(&Bytes::::from(vec![ - 1u8; - 32 - ])) - .unwrap(); + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(vec![1u8; 32])).unwrap(); let key2 = - SymmetricCryptoKey::try_from(&Bytes::::from(vec![ - 2u8; - 32 - ])) - .unwrap(); + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(vec![2u8; 32])).unwrap(); assert_ne!(key1, key2); let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap(); assert_eq!(key1, key3); diff --git a/crates/bitwarden-crypto/src/signing/signature.rs b/crates/bitwarden-crypto/src/signing/signature.rs index ca2635805..e044cfa0f 100644 --- a/crates/bitwarden-crypto/src/signing/signature.rs +++ b/crates/bitwarden-crypto/src/signing/signature.rs @@ -7,10 +7,10 @@ use super::{ VerifyingKey, }; use crate::{ - content_format::{Bytes, CoseSign1ContentFormat}, + content_format::CoseSign1ContentFormat, cose::{CoseSerializable, SIGNING_NAMESPACE}, error::{EncodingError, SignatureError}, - CryptoError, + CoseSign1Bytes, CryptoError, }; /// A signature cryptographically attests to a (namespace, data) pair. The namespace is included in @@ -174,13 +174,13 @@ impl SigningKey { } impl CoseSerializable for Signature { - fn from_cose(bytes: &Bytes) -> Result { + fn from_cose(bytes: &CoseSign1Bytes) -> Result { let cose_sign1 = CoseSign1::from_slice(bytes.as_ref()) .map_err(|_| EncodingError::InvalidCoseEncoding)?; Ok(Signature(cose_sign1)) } - fn to_cose(&self) -> Bytes { + fn to_cose(&self) -> CoseSign1Bytes { self.0 .clone() .to_vec() @@ -192,10 +192,7 @@ impl CoseSerializable for Signature { #[cfg(test)] mod tests { use super::*; - use crate::{ - content_format::{Bytes, CoseKeyContentFormat}, - SignatureAlgorithm, - }; + use crate::{content_format::Bytes, CoseKeyBytes, SignatureAlgorithm}; const VERIFYING_KEY: &[u8] = &[ 166, 1, 1, 2, 80, 55, 131, 40, 191, 230, 137, 76, 182, 184, 139, 94, 152, 45, 63, 13, 71, @@ -217,8 +214,7 @@ mod tests { #[test] fn test_cose_roundtrip_encode_signature() { - let signature = - Signature::from_cose(&Bytes::::from(SIGNATURE)).unwrap(); + let signature = Signature::from_cose(&CoseSign1Bytes::from(SIGNATURE)).unwrap(); let cose_bytes = signature.to_cose(); let decoded_signature = Signature::from_cose(&cose_bytes).unwrap(); assert_eq!(signature.inner(), decoded_signature.inner()); @@ -226,10 +222,8 @@ mod tests { #[test] fn test_verify_testvector() { - let verifying_key = - VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY)).unwrap(); - let signature = - Signature::from_cose(&Bytes::::from(SIGNATURE)).unwrap(); + let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY)).unwrap(); + let signature = Signature::from_cose(&CoseSign1Bytes::from(SIGNATURE)).unwrap(); let serialized_message = SerializedMessage::from_bytes(SERIALIZED_MESSAGE.to_vec(), CoapContentFormat::Cbor); diff --git a/crates/bitwarden-crypto/src/signing/signed_object.rs b/crates/bitwarden-crypto/src/signing/signed_object.rs index b50366286..100975919 100644 --- a/crates/bitwarden-crypto/src/signing/signed_object.rs +++ b/crates/bitwarden-crypto/src/signing/signed_object.rs @@ -7,10 +7,10 @@ use super::{ verifying_key::VerifyingKey, SigningNamespace, }; use crate::{ - content_format::{Bytes, CoseSign1ContentFormat}, + content_format::CoseSign1ContentFormat, cose::{CoseSerializable, SIGNING_NAMESPACE}, error::{EncodingError, SignatureError}, - CryptoError, + CoseSign1Bytes, CryptoError, }; /// A signed object is a message containing a payload and signature that attests the payload's @@ -151,14 +151,14 @@ impl SigningKey { } impl CoseSerializable for SignedObject { - fn from_cose(bytes: &Bytes) -> Result { + fn from_cose(bytes: &CoseSign1Bytes) -> Result { Ok(SignedObject( CoseSign1::from_slice(bytes.as_ref()) .map_err(|_| EncodingError::InvalidCoseEncoding)?, )) } - fn to_cose(&self) -> Bytes { + fn to_cose(&self) -> CoseSign1Bytes { self.0 .clone() .to_vec() @@ -172,9 +172,9 @@ mod tests { use serde::{Deserialize, Serialize}; use crate::{ - content_format::{Bytes, CoseKeyContentFormat, CoseSign1ContentFormat}, - CoseSerializable, CryptoError, SignatureAlgorithm, SignedObject, SigningKey, - SigningNamespace, VerifyingKey, + content_format::{Bytes, CoseSign1ContentFormat}, + CoseKeyBytes, CoseSerializable, CoseSign1Bytes, CryptoError, SignatureAlgorithm, + SignedObject, SigningKey, SigningNamespace, VerifyingKey, }; const VERIFYING_KEY: &[u8] = &[ @@ -201,17 +201,13 @@ mod tests { #[test] fn test_roundtrip_cose() { let signed_object = - SignedObject::from_cose(&Into::>::into(SIGNED_OBJECT)) - .unwrap(); + SignedObject::from_cose(&Into::::into(SIGNED_OBJECT)).unwrap(); assert_eq!( signed_object.content_type().unwrap(), coset::iana::CoapContentFormat::Cbor ); let cose_bytes = signed_object.to_cose(); - assert_eq!( - cose_bytes, - Bytes::::from(SIGNED_OBJECT) - ); + assert_eq!(cose_bytes, CoseSign1Bytes::from(SIGNED_OBJECT)); } #[test] @@ -220,11 +216,9 @@ mod tests { field1: "Test message".to_string(), }; let signed_object = - SignedObject::from_cose(&Into::>::into(SIGNED_OBJECT)) - .unwrap(); + SignedObject::from_cose(&Into::::into(SIGNED_OBJECT)).unwrap(); let verifying_key = - VerifyingKey::from_cose(&Into::>::into(VERIFYING_KEY)) - .unwrap(); + VerifyingKey::from_cose(&Into::::into(VERIFYING_KEY)).unwrap(); let namespace = SigningNamespace::ExampleNamespace; let payload: TestMessage = signed_object .verify_and_unwrap(&verifying_key, &namespace) diff --git a/crates/bitwarden-crypto/src/signing/signing_key.rs b/crates/bitwarden-crypto/src/signing/signing_key.rs index 12644b3f9..74658001b 100644 --- a/crates/bitwarden-crypto/src/signing/signing_key.rs +++ b/crates/bitwarden-crypto/src/signing/signing_key.rs @@ -13,11 +13,11 @@ use super::{ SignatureAlgorithm, }; use crate::{ - content_format::{Bytes, CoseKeyContentFormat}, + content_format::CoseKeyContentFormat, cose::CoseSerializable, error::{EncodingError, Result}, keys::KeyId, - CryptoKey, + CoseKeyBytes, CryptoKey, }; /// A `SigningKey` without the key id. This enum contains a variant for each supported signature @@ -89,7 +89,7 @@ impl SigningKey { impl CoseSerializable for SigningKey { /// Serializes the signing key to a COSE-formatted byte array. - fn to_cose(&self) -> Bytes { + fn to_cose(&self) -> CoseKeyBytes { match &self.inner { RawSigningKey::Ed25519(key) => { coset::CoseKeyBuilder::new_okp_key() @@ -114,7 +114,7 @@ impl CoseSerializable for SigningKey { } /// Deserializes a COSE-formatted byte array into a signing key. - fn from_cose(bytes: &Bytes) -> Result { + fn from_cose(bytes: &CoseKeyBytes) -> Result { let cose_key = CoseKey::from_slice(bytes.as_ref()).map_err(|_| EncodingError::InvalidCoseEncoding)?; diff --git a/crates/bitwarden-crypto/src/signing/verifying_key.rs b/crates/bitwarden-crypto/src/signing/verifying_key.rs index 507a1d0c7..8f1d031e6 100644 --- a/crates/bitwarden-crypto/src/signing/verifying_key.rs +++ b/crates/bitwarden-crypto/src/signing/verifying_key.rs @@ -11,11 +11,11 @@ use coset::{ use super::{ed25519_verifying_key, key_id, SignatureAlgorithm}; use crate::{ - content_format::{Bytes, CoseKeyContentFormat}, + content_format::CoseKeyContentFormat, cose::CoseSerializable, error::{EncodingError, SignatureError}, keys::KeyId, - CryptoError, + CoseKeyBytes, CryptoError, }; /// A `VerifyingKey` without the key id. This enum contains a variant for each supported signature @@ -59,7 +59,7 @@ impl VerifyingKey { } impl CoseSerializable for VerifyingKey { - fn to_cose(&self) -> Bytes { + fn to_cose(&self) -> CoseKeyBytes { match &self.inner { RawVerifyingKey::Ed25519(key) => coset::CoseKeyBuilder::new_okp_key() .key_id((&self.id).into()) @@ -84,7 +84,7 @@ impl CoseSerializable for VerifyingKey { } } - fn from_cose(bytes: &Bytes) -> Result + fn from_cose(bytes: &CoseKeyBytes) -> Result where Self: Sized, { @@ -130,8 +130,7 @@ mod tests { #[test] fn test_cose_roundtrip_encode_verifying() { let verifying_key = - VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY.to_vec())) - .unwrap(); + VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap(); let cose = verifying_key.to_cose(); let parsed_key = VerifyingKey::from_cose(&cose).unwrap(); @@ -141,8 +140,7 @@ mod tests { #[test] fn test_testvector() { let verifying_key = - VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY.to_vec())) - .unwrap(); + VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap(); assert_eq!(verifying_key.algorithm(), SignatureAlgorithm::Ed25519); verifying_key @@ -153,8 +151,7 @@ mod tests { #[test] fn test_invalid_testvector() { let verifying_key = - VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY.to_vec())) - .unwrap(); + VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap(); assert_eq!(verifying_key.algorithm(), SignatureAlgorithm::Ed25519); // This should fail, as the signed object is not valid for the given verifying key. diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index ceca88898..9675cfb5e 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -9,8 +9,8 @@ use zeroize::Zeroizing; use super::KeyStoreInner; use crate::{ derive_shareable_key, error::UnsupportedOperation, signing, store::backend::StoreBackend, - AsymmetricCryptoKey, BitwardenLegacyKeyContentFormat, Bytes, ContentFormat, CryptoError, - EncString, KeyId, KeyIds, Result, Signature, SignatureAlgorithm, SignedObject, SignedPublicKey, + AsymmetricCryptoKey, BitwardenLegacyKeyBytes, ContentFormat, CryptoError, EncString, KeyId, + KeyIds, Result, Signature, SignatureAlgorithm, SignedObject, SignedPublicKey, SignedPublicKeyMessage, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, }; @@ -156,14 +156,14 @@ impl KeyStoreContext<'_, Ids> { let key = match (wrapped_key, wrapping_key) { (EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => { - SymmetricCryptoKey::try_from(&Bytes::::from( + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from( crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)?, ))? } ( EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data }, SymmetricCryptoKey::Aes256CbcHmacKey(key), - ) => SymmetricCryptoKey::try_from(&Bytes::::from( + ) => SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from( crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key)?, ))?, ( @@ -174,9 +174,7 @@ impl KeyStoreContext<'_, Ids> { crate::cose::decrypt_xchacha20_poly1305(data, key)?; match content_format { ContentFormat::BitwardenLegacyKey => { - SymmetricCryptoKey::try_from( - &Bytes::::from(content_bytes), - )? + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(content_bytes))? } ContentFormat::CoseKey => SymmetricCryptoKey::try_from_cose(&content_bytes)?, _ => return Err(CryptoError::InvalidKey), diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 5e126163f..1de26aa17 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -2,11 +2,11 @@ use std::str::FromStr; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyContentFormat, Bytes, - CoseKeyContentFormat, CoseSerializable, CryptoError, Decryptable, EncString, Kdf, - KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, OctetStreamContentFormat, - Pkcs8PrivateKeyDerContentFormat, PrimitiveEncryptable, SignatureAlgorithm, SignedPublicKey, - SigningKey, SpkiPublicKeyDerContentFormat, SymmetricCryptoKey, UnsignedSharedKey, VerifyingKey, + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyBytes, CoseKeyBytes, + CoseSerializable, CryptoError, Decryptable, EncString, Kdf, KeyDecryptable, KeyEncryptable, + KeyStore, MasterKey, OctetStreamBytes, Pkcs8PrivateKeyBytes, PrimitiveEncryptable, + SignatureAlgorithm, SignedPublicKey, SigningKey, SpkiPublicKeyBytes, SymmetricCryptoKey, + UnsignedSharedKey, VerifyingKey, }; use wasm_bindgen::prelude::*; @@ -30,7 +30,7 @@ impl PureCrypto { enc_string: String, key: Vec, ) -> Result { - let key = &Bytes::::from(key); + let key = &BitwardenLegacyKeyBytes::from(key); EncString::from_str(&enc_string)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?) } @@ -38,7 +38,7 @@ impl PureCrypto { enc_string: String, key: Vec, ) -> Result, CryptoError> { - let key = &Bytes::::from(key); + let key = &BitwardenLegacyKeyBytes::from(key); EncString::from_str(&enc_string)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?) } @@ -55,12 +55,12 @@ impl PureCrypto { enc_bytes: Vec, key: Vec, ) -> Result, CryptoError> { - let key = &Bytes::::from(key); + let key = &BitwardenLegacyKeyBytes::from(key); EncString::from_buffer(&enc_bytes)?.decrypt_with_key(&SymmetricCryptoKey::try_from(key)?) } pub fn symmetric_encrypt_string(plain: String, key: Vec) -> Result { - let key = &Bytes::::from(key); + let key = &BitwardenLegacyKeyBytes::from(key); plain .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?) .map(|enc| enc.to_string()) @@ -68,8 +68,8 @@ impl PureCrypto { /// DEPRECATED: Only used by send keys pub fn symmetric_encrypt_bytes(plain: Vec, key: Vec) -> Result { - let key = &Bytes::::from(key); - Bytes::::from(plain) + let key = &BitwardenLegacyKeyBytes::from(key); + OctetStreamBytes::from(plain) .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?) .map(|enc| enc.to_string()) } @@ -78,8 +78,8 @@ impl PureCrypto { plain: Vec, key: Vec, ) -> Result, CryptoError> { - let key = &Bytes::::from(key); - Bytes::::from(plain) + let key = &BitwardenLegacyKeyBytes::from(key); + OctetStreamBytes::from(plain) .encrypt_with_key(&SymmetricCryptoKey::try_from(key)?)? .to_buffer() } @@ -105,7 +105,7 @@ impl PureCrypto { kdf: Kdf, ) -> Result { let master_key = MasterKey::derive(master_password.as_str(), email.as_str(), &kdf)?; - let user_key = &Bytes::::from(user_key); + let user_key = &BitwardenLegacyKeyBytes::from(user_key); let user_key = SymmetricCryptoKey::try_from(user_key)?; let result = master_key.encrypt_user_key(&user_key)?; Ok(result.to_string()) @@ -131,14 +131,12 @@ impl PureCrypto { ) -> Result { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); - let wrapping_key = SymmetricCryptoKey::try_from( - &Bytes::::from(wrapping_key), - )?; + let wrapping_key = + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(wrapping_key))?; #[allow(deprecated)] context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), wrapping_key)?; - let key_to_be_wrapped = SymmetricCryptoKey::try_from(&Bytes::< - BitwardenLegacyKeyContentFormat, - >::from(key_to_be_wrapped))?; + let key_to_be_wrapped = + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(key_to_be_wrapped))?; #[allow(deprecated)] context.set_symmetric_key(SymmetricKeyId::Local("key_to_wrap"), key_to_be_wrapped)?; // Note: The order of arguments is different here, and should probably be refactored @@ -158,9 +156,8 @@ impl PureCrypto { ) -> Result, CryptoError> { let tmp_store: KeyStore = KeyStore::default(); let mut context = tmp_store.context(); - let wrapping_key = SymmetricCryptoKey::try_from( - &Bytes::::from(wrapping_key), - )?; + let wrapping_key = + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(wrapping_key))?; #[allow(deprecated)] context.set_symmetric_key(SymmetricKeyId::Local("wrapping_key"), wrapping_key)?; // Note: The order of arguments is different here, and should probably be refactored @@ -188,15 +185,11 @@ impl PureCrypto { #[allow(deprecated)] context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from(&Bytes::::from( - wrapping_key, - ))?, + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(wrapping_key))?, )?; - Ok( - Bytes::::from(encapsulation_key) - .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))? - .to_string(), - ) + Ok(SpkiPublicKeyBytes::from(encapsulation_key) + .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))? + .to_string()) } /// Unwraps (decrypts) a wrapped SPKI DER encoded encapsulation (public) key using a symmetric @@ -210,9 +203,7 @@ impl PureCrypto { #[allow(deprecated)] context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from(&Bytes::::from( - wrapping_key, - ))?, + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(wrapping_key))?, )?; EncString::from_str(wrapped_key.as_str())? .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) @@ -229,15 +220,11 @@ impl PureCrypto { #[allow(deprecated)] context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from(&Bytes::::from( - wrapping_key, - ))?, + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(wrapping_key))?, )?; - Ok( - Bytes::::from(decapsulation_key) - .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))? - .to_string(), - ) + Ok(Pkcs8PrivateKeyBytes::from(decapsulation_key) + .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))? + .to_string()) } /// Unwraps (decrypts) a wrapped PKCS8 DER encoded decapsulation (private) key using a symmetric @@ -251,9 +238,7 @@ impl PureCrypto { #[allow(deprecated)] context.set_symmetric_key( SymmetricKeyId::Local("wrapping_key"), - SymmetricCryptoKey::try_from(&Bytes::::from( - wrapping_key, - ))?, + SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(wrapping_key))?, )?; EncString::from_str(wrapped_key.as_str())? .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) @@ -268,9 +253,7 @@ impl PureCrypto { ) -> Result { let encapsulation_key = AsymmetricPublicCryptoKey::from_der(encapsulation_key.as_slice())?; Ok(UnsignedSharedKey::encapsulate_key_unsigned( - &SymmetricCryptoKey::try_from(&Bytes::::from( - shared_key, - ))?, + &SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(shared_key))?, &encapsulation_key, )? .to_string()) @@ -284,11 +267,9 @@ impl PureCrypto { decapsulation_key: Vec, ) -> Result, CryptoError> { Ok(UnsignedSharedKey::from_str(encapsulated_key.as_str())? - .decapsulate_key_unsigned(&AsymmetricCryptoKey::from_der(&Bytes::< - Pkcs8PrivateKeyDerContentFormat, - >::from( - decapsulation_key - ))?)? + .decapsulate_key_unsigned(&AsymmetricCryptoKey::from_der( + &Pkcs8PrivateKeyBytes::from(decapsulation_key), + )?)? .to_encoded() .to_vec()) } @@ -300,7 +281,7 @@ impl PureCrypto { wrapping_key: Vec, ) -> Result, CryptoError> { let bytes = Self::symmetric_decrypt_bytes(signing_key, wrapping_key)?; - let signing_key = SigningKey::from_cose(&Bytes::::from(bytes))?; + let signing_key = SigningKey::from_cose(&CoseKeyBytes::from(bytes))?; let verifying_key = signing_key.to_verifying_key(); Ok(verifying_key.to_cose().to_vec()) } @@ -309,8 +290,7 @@ impl PureCrypto { pub fn key_algorithm_for_verifying_key( verifying_key: Vec, ) -> Result { - let verifying_key = - VerifyingKey::from_cose(&Bytes::::from(verifying_key))?; + let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(verifying_key))?; let algorithm = verifying_key.algorithm(); Ok(algorithm) } @@ -324,8 +304,7 @@ impl PureCrypto { verifying_key: Vec, ) -> Result, CryptoError> { let signed_public_key = SignedPublicKey::try_from(signed_public_key)?; - let verifying_key = - VerifyingKey::from_cose(&Bytes::::from(verifying_key))?; + let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(verifying_key))?; signed_public_key .verify_and_unwrap(&verifying_key) .map(|public_key| public_key.to_der())? @@ -605,8 +584,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= #[test] fn test_key_algorithm_for_verifying_key() { let verifying_key = - VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY.to_vec())) - .unwrap(); + VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap(); let algorithm = PureCrypto::key_algorithm_for_verifying_key(verifying_key.to_cose().to_vec()).unwrap(); assert_eq!(algorithm, SignatureAlgorithm::Ed25519); @@ -620,16 +598,14 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= ) .unwrap(); let verifying_key = - VerifyingKey::from_cose(&Bytes::::from(VERIFYING_KEY.to_vec())) - .unwrap(); + VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap(); let verifying_key_derived = PureCrypto::verifying_key_for_signing_key( wrapped_signing_key.to_string(), SIGNING_KEY_WRAPPING_KEY.to_vec(), ) .unwrap(); let verifying_key_derived = - VerifyingKey::from_cose(&Bytes::::from(verifying_key_derived)) - .unwrap(); + VerifyingKey::from_cose(&CoseKeyBytes::from(verifying_key_derived)).unwrap(); assert_eq!(verifying_key.to_cose(), verifying_key_derived.to_cose()); } From 235f3bce697c2798790aaa5e86ab9f8728b45356 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 24 Jun 2025 13:30:34 +0200 Subject: [PATCH 064/124] Apply cargo fmt --- crates/bitwarden-crypto/src/content_format.rs | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/bitwarden-crypto/src/content_format.rs b/crates/bitwarden-crypto/src/content_format.rs index eaca2474a..8896ec935 100644 --- a/crates/bitwarden-crypto/src/content_format.rs +++ b/crates/bitwarden-crypto/src/content_format.rs @@ -101,7 +101,8 @@ impl ConstContentFormat for Utf8ContentFormat { ContentFormat::Utf8 } } -/// Utf8Bytes is a type alias for Bytes with `Utf8ContentFormat`, which is used for any textual data. +/// Utf8Bytes is a type alias for Bytes with `Utf8ContentFormat`, which is used for any textual +/// data. pub(crate) type Utf8Bytes = Bytes; /// Content format for raw bytes. Used for attachments and send seed keys. @@ -113,8 +114,8 @@ impl ConstContentFormat for OctetStreamContentFormat { ContentFormat::OctetStream } } -/// OctetStreamBytes is a type alias for Bytes with `OctetStreamContentFormat`. This should be used for e.g. attachments and other data without an -/// explicit content format. +/// OctetStreamBytes is a type alias for Bytes with `OctetStreamContentFormat`. This should be used +/// for e.g. attachments and other data without an explicit content format. pub type OctetStreamBytes = Bytes; /// Content format for PKCS8 private keys in DER format. @@ -126,7 +127,8 @@ impl ConstContentFormat for Pkcs8PrivateKeyDerContentFormat { ContentFormat::Pkcs8PrivateKey } } -/// Pkcs8PrivateKeyBytes is a type alias for Bytes with `Pkcs8PrivateKeyDerContentFormat`. This is used for PKCS8 private keys in DER format. +/// Pkcs8PrivateKeyBytes is a type alias for Bytes with `Pkcs8PrivateKeyDerContentFormat`. This is +/// used for PKCS8 private keys in DER format. pub type Pkcs8PrivateKeyBytes = Bytes; /// Content format for SPKI public keys in DER format. @@ -138,7 +140,8 @@ impl ConstContentFormat for SpkiPublicKeyDerContentFormat { ContentFormat::SPKIPublicKeyDer } } -/// SpkiPublicKeyBytes is a type alias for Bytes with `SpkiPublicKeyDerContentFormat`. This is used for SPKI public keys in DER format. +/// SpkiPublicKeyBytes is a type alias for Bytes with `SpkiPublicKeyDerContentFormat`. This is used +/// for SPKI public keys in DER format. pub type SpkiPublicKeyBytes = Bytes; /// Content format for COSE keys. @@ -151,7 +154,8 @@ impl ConstContentFormat for CoseKeyContentFormat { } } impl CoseContentFormat for CoseKeyContentFormat {} -/// CoseKeyBytes is a type alias for Bytes with `CoseKeyContentFormat`. This is used for serialized CoseKey objects. +/// CoseKeyBytes is a type alias for Bytes with `CoseKeyContentFormat`. This is used for serialized +/// CoseKey objects. pub type CoseKeyBytes = Bytes; /// A legacy content format for Bitwarden keys. See `ContentFormat::BitwardenLegacyKey` @@ -163,8 +167,9 @@ impl ConstContentFormat for BitwardenLegacyKeyContentFormat { ContentFormat::BitwardenLegacyKey } } -/// BitwardenLegacyKeyBytes is a type alias for Bytes with `BitwardenLegacyKeyContentFormat`. This is used for the legacy format for symmetric keys. -/// A description of the format is available in the `ContentFormat::BitwardenLegacyKey` documentation. +/// BitwardenLegacyKeyBytes is a type alias for Bytes with `BitwardenLegacyKeyContentFormat`. This +/// is used for the legacy format for symmetric keys. A description of the format is available in +/// the `ContentFormat::BitwardenLegacyKey` documentation. pub type BitwardenLegacyKeyBytes = Bytes; /// Content format for COSE Sign1 messages. @@ -177,7 +182,8 @@ impl ConstContentFormat for CoseSign1ContentFormat { } } impl CoseContentFormat for CoseSign1ContentFormat {} -/// CoseSign1Bytes is a type alias for Bytes with `CoseSign1ContentFormat`. This is used for serialized COSE Sign1 messages. +/// CoseSign1Bytes is a type alias for Bytes with `CoseSign1ContentFormat`. This is used for +/// serialized COSE Sign1 messages. pub type CoseSign1Bytes = Bytes; /// A marker trait for COSE content formats. From 1953ba31138baac7341d03110a6405ff0eb80355 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 24 Jun 2025 13:36:03 +0200 Subject: [PATCH 065/124] Apply fixes --- crates/bitwarden-core/src/key_management/crypto.rs | 2 +- crates/bitwarden-crypto/src/keys/device_key.rs | 2 +- crates/bitwarden-crypto/src/signing/signature.rs | 2 +- crates/bitwarden-crypto/src/signing/signed_object.rs | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index ff35b0426..d4253a446 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -617,7 +617,7 @@ pub fn make_user_signing_keys_for_enrollment( mod tests { use std::num::NonZeroU32; - use bitwarden_crypto::{Pkcs8PrivateKeyDerContentFormat, RsaKeyPair}; + use bitwarden_crypto::RsaKeyPair; use super::*; use crate::Client; diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index 2b71c878e..3e09b2884 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -89,7 +89,7 @@ impl TryFrom for DeviceKey { #[cfg(test)] mod tests { use super::*; - use crate::{derive_symmetric_key, BitwardenLegacyKeyBytes, BitwardenLegacyKeyContentFormat}; + use crate::{derive_symmetric_key, BitwardenLegacyKeyBytes}; #[test] fn test_trust_device() { diff --git a/crates/bitwarden-crypto/src/signing/signature.rs b/crates/bitwarden-crypto/src/signing/signature.rs index e044cfa0f..1c2b95bc3 100644 --- a/crates/bitwarden-crypto/src/signing/signature.rs +++ b/crates/bitwarden-crypto/src/signing/signature.rs @@ -192,7 +192,7 @@ impl CoseSerializable for Signature { #[cfg(test)] mod tests { use super::*; - use crate::{content_format::Bytes, CoseKeyBytes, SignatureAlgorithm}; + use crate::{CoseKeyBytes, SignatureAlgorithm}; const VERIFYING_KEY: &[u8] = &[ 166, 1, 1, 2, 80, 55, 131, 40, 191, 230, 137, 76, 182, 184, 139, 94, 152, 45, 63, 13, 71, diff --git a/crates/bitwarden-crypto/src/signing/signed_object.rs b/crates/bitwarden-crypto/src/signing/signed_object.rs index 100975919..be35f7f40 100644 --- a/crates/bitwarden-crypto/src/signing/signed_object.rs +++ b/crates/bitwarden-crypto/src/signing/signed_object.rs @@ -172,7 +172,6 @@ mod tests { use serde::{Deserialize, Serialize}; use crate::{ - content_format::{Bytes, CoseSign1ContentFormat}, CoseKeyBytes, CoseSerializable, CoseSign1Bytes, CryptoError, SignatureAlgorithm, SignedObject, SigningKey, SigningNamespace, VerifyingKey, }; From 41bb1ad4e37564f582aaddcea3d39fa5da7ca8aa Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 24 Jun 2025 13:44:08 +0200 Subject: [PATCH 066/124] Replace non type aliased Bytes references with type aliases --- .../bitwarden-core/src/auth/auth_request.rs | 2 +- .../src/platform/generate_fingerprint.rs | 4 +- crates/bitwarden-crypto/examples/signature.rs | 45 +++++++++---------- .../examples/signed_object.rs | 2 +- crates/bitwarden-crypto/src/fingerprint.rs | 12 ++--- .../src/keys/asymmetric_crypto_key.rs | 10 ++--- crates/bitwarden-send/src/send.rs | 6 +-- crates/bitwarden-send/src/send_client.rs | 4 +- .../bitwarden-vault/src/cipher/attachment.rs | 6 +-- 9 files changed, 41 insertions(+), 50 deletions(-) diff --git a/crates/bitwarden-core/src/auth/auth_request.rs b/crates/bitwarden-core/src/auth/auth_request.rs index 222a95809..5eb1061ab 100644 --- a/crates/bitwarden-core/src/auth/auth_request.rs +++ b/crates/bitwarden-core/src/auth/auth_request.rs @@ -171,7 +171,7 @@ mod tests { // Verify fingerprint let pubkey = STANDARD.decode(public_key).unwrap(); - let pubkey = Bytes::::from(pubkey.clone()); + let pubkey = SpkiPublicKeyBytes::from(pubkey.clone()); let fingerprint = fingerprint("test@bitwarden.com", &pubkey).unwrap(); assert_eq!(fingerprint, "childless-unfair-prowler-dropbox-designate"); diff --git a/crates/bitwarden-core/src/platform/generate_fingerprint.rs b/crates/bitwarden-core/src/platform/generate_fingerprint.rs index 7391cfa72..2ec8673a3 100644 --- a/crates/bitwarden-core/src/platform/generate_fingerprint.rs +++ b/crates/bitwarden-core/src/platform/generate_fingerprint.rs @@ -3,7 +3,7 @@ //! This module contains the logic for generating fingerprints. use base64::{engine::general_purpose::STANDARD, Engine}; -use bitwarden_crypto::{fingerprint, Bytes, SpkiPublicKeyDerContentFormat}; +use bitwarden_crypto::{fingerprint, SpkiPublicKeyBytes}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -42,7 +42,7 @@ pub enum FingerprintError { pub(crate) fn generate_fingerprint(input: &FingerprintRequest) -> Result { let key = STANDARD.decode(&input.public_key)?; - let key = Bytes::::from(key); + let key = SpkiPublicKeyBytes::from(key); Ok(fingerprint(&input.fingerprint_material, &key)?) } diff --git a/crates/bitwarden-crypto/examples/signature.rs b/crates/bitwarden-crypto/examples/signature.rs index 71f6fb958..881b00de0 100644 --- a/crates/bitwarden-crypto/examples/signature.rs +++ b/crates/bitwarden-crypto/examples/signature.rs @@ -42,14 +42,13 @@ fn main() { mock_server.upload("serialized_message", serialized_message.as_bytes().to_vec()); // Bob retrieves the signed object from the server - let retrieved_signature = - bitwarden_crypto::Signature::from_cose(&Bytes::::from( - mock_server - .download("signature") - .expect("Failed to download signature") - .clone(), - )) - .expect("Failed to deserialize signature"); + let retrieved_signature = bitwarden_crypto::Signature::from_cose(&CoseSign1Bytes::from( + mock_server + .download("signature") + .expect("Failed to download signature") + .clone(), + )) + .expect("Failed to deserialize signature"); let retrieved_serialized_message = bitwarden_crypto::SerializedMessage::from_bytes( mock_server .download("serialized_message") @@ -90,22 +89,20 @@ fn main() { .content_type() .expect("Failed to get content type from signature"), ); - let retrieved_alice_signature = - bitwarden_crypto::Signature::from_cose(&Bytes::::from( - mock_server - .download("signature") - .expect("Failed to download Alice's signature") - .clone(), - )) - .expect("Failed to deserialize Alice's signature"); - let retrieved_bobs_signature = - bitwarden_crypto::Signature::from_cose(&Bytes::::from( - mock_server - .download("bobs_signature") - .expect("Failed to download Bob's signature") - .clone(), - )) - .expect("Failed to deserialize Bob's signature"); + let retrieved_alice_signature = bitwarden_crypto::Signature::from_cose(&CoseSign1Bytes::from( + mock_server + .download("signature") + .expect("Failed to download Alice's signature") + .clone(), + )) + .expect("Failed to deserialize Alice's signature"); + let retrieved_bobs_signature = bitwarden_crypto::Signature::from_cose(&CoseSign1Bytes::from( + mock_server + .download("bobs_signature") + .expect("Failed to download Bob's signature") + .clone(), + )) + .expect("Failed to deserialize Bob's signature"); // Charlie verifies Alice's signature if !retrieved_alice_signature.verify( diff --git a/crates/bitwarden-crypto/examples/signed_object.rs b/crates/bitwarden-crypto/examples/signed_object.rs index 0223338b2..5b692535b 100644 --- a/crates/bitwarden-crypto/examples/signed_object.rs +++ b/crates/bitwarden-crypto/examples/signed_object.rs @@ -39,7 +39,7 @@ fn main() { mock_server.upload("signed_object", signed_object.to_cose().to_vec()); // Bob retrieves the signed object from the server - let retrieved_signed_object = SignedObject::from_cose(&Bytes::::from( + let retrieved_signed_object = SignedObject::from_cose(&CoseSign1Bytes::from( mock_server .download("signed_object") .expect("Failed to download signed object") diff --git a/crates/bitwarden-crypto/src/fingerprint.rs b/crates/bitwarden-crypto/src/fingerprint.rs index 0ac977f4a..6e8d55772 100644 --- a/crates/bitwarden-crypto/src/fingerprint.rs +++ b/crates/bitwarden-crypto/src/fingerprint.rs @@ -8,20 +8,14 @@ use num_bigint::BigUint; use num_traits::cast::ToPrimitive; use thiserror::Error; -use crate::{ - content_format::SpkiPublicKeyDerContentFormat, error::Result, wordlist::EFF_LONG_WORD_LIST, - Bytes, CryptoError, -}; +use crate::{error::Result, wordlist::EFF_LONG_WORD_LIST, CryptoError, SpkiPublicKeyBytes}; /// Computes a fingerprint of the given `fingerprint_material` using the given `public_key`. /// /// This is commonly used for account fingerprints. With the following arguments: /// - `fingerprint_material`: user's id. /// - `public_key`: user's public key. -pub fn fingerprint( - fingerprint_material: &str, - public_key: &Bytes, -) -> Result { +pub fn fingerprint(fingerprint_material: &str, public_key: &SpkiPublicKeyBytes) -> Result { let hkdf = hkdf::Hkdf::::from_prk(public_key.as_ref()) .map_err(|_| CryptoError::InvalidKeyLen)?; @@ -93,7 +87,7 @@ mod tests { 197, 3, 219, 56, 77, 109, 47, 72, 251, 131, 36, 240, 96, 169, 31, 82, 93, 166, 242, 3, 33, 213, 2, 3, 1, 0, 1, ]; - let key: Bytes = Bytes::from(key.to_vec()); + let key: SpkiPublicKeyBytes = Bytes::from(key.to_vec()); assert_eq!( "turban-deftly-anime-chatroom-unselfish", fingerprint(user_id, &key).unwrap() diff --git a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs index 0ed1bc12d..14a400992 100644 --- a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs @@ -5,9 +5,8 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use super::key_encryptable::CryptoKey; use crate::{ - content_format::{Bytes, SpkiPublicKeyDerContentFormat}, error::{CryptoError, Result}, - Pkcs8PrivateKeyBytes, + Pkcs8PrivateKeyBytes, SpkiPublicKeyBytes, }; /// Algorithm / public key encryption scheme used for encryption/decryption. @@ -45,7 +44,7 @@ impl AsymmetricPublicCryptoKey { } /// Makes a SubjectPublicKeyInfo DER serialized version of the public key. - pub fn to_der(&self) -> Result> { + pub fn to_der(&self) -> Result { use rsa::pkcs8::EncodePublicKey; match &self.inner { RawPublicKey::RsaOaepSha1(public_key) => Ok(public_key @@ -167,7 +166,8 @@ mod tests { use crate::{ content_format::{Bytes, Pkcs8PrivateKeyDerContentFormat}, - AsymmetricCryptoKey, AsymmetricPublicCryptoKey, SymmetricCryptoKey, UnsignedSharedKey, + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, Pkcs8PrivateKeyBytes, SymmetricCryptoKey, + UnsignedSharedKey, }; #[test] @@ -261,7 +261,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= )) .unwrap(); - let private_key = Bytes::::from(private_key); + let private_key = Pkcs8PrivateKeyBytes::from(private_key); let private_key = AsymmetricCryptoKey::from_der(&private_key).unwrap(); let public_key = AsymmetricPublicCryptoKey::from_der(&public_key).unwrap(); diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index ec28a46c4..dc3af947d 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -8,8 +8,8 @@ use bitwarden_core::{ require, }; use bitwarden_crypto::{ - generate_random_bytes, Bytes, CompositeEncryptable, CryptoError, Decryptable, EncString, - IdentifyKey, KeyStoreContext, OctetStreamContentFormat, PrimitiveEncryptable, + generate_random_bytes, CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, + KeyStoreContext, OctetStreamBytes, PrimitiveEncryptable, }; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -331,7 +331,7 @@ impl CompositeEncryptable for SendView { name: self.name.encrypt(ctx, send_key)?, notes: self.notes.encrypt(ctx, send_key)?, - key: Bytes::::from(k.clone()).encrypt(ctx, key)?, + key: OctetStreamBytes::from(k.clone()).encrypt(ctx, key)?, password: self.new_password.as_ref().map(|password| { let password = bitwarden_crypto::pbkdf2(password.as_bytes(), &k, SEND_ITERATIONS); STANDARD.encode(password) diff --git a/crates/bitwarden-send/src/send_client.rs b/crates/bitwarden-send/src/send_client.rs index d8a9a2936..f703f47f4 100644 --- a/crates/bitwarden-send/src/send_client.rs +++ b/crates/bitwarden-send/src/send_client.rs @@ -2,7 +2,7 @@ use std::path::Path; use bitwarden_core::Client; use bitwarden_crypto::{ - Bytes, Decryptable, EncString, IdentifyKey, OctetStreamContentFormat, PrimitiveEncryptable, + Decryptable, EncString, IdentifyKey, OctetStreamBytes, PrimitiveEncryptable, }; use thiserror::Error; @@ -129,7 +129,7 @@ impl SendClient { let key = Send::get_key(&mut ctx, &send.key, send.key_identifier())?; - let encrypted = Bytes::::from(buffer).encrypt(&mut ctx, key)?; + let encrypted = OctetStreamBytes::from(buffer).encrypt(&mut ctx, key)?; Ok(encrypted.to_buffer()?) } } diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index b37af2f6c..213be72df 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,7 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ - Bytes, CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, - OctetStreamContentFormat, PrimitiveEncryptable, + CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, + OctetStreamBytes, PrimitiveEncryptable, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -95,7 +95,7 @@ impl CompositeEncryptable // with it, and then encrypt the key with the cipher key let attachment_key = ctx.generate_symmetric_key(ATTACHMENT_KEY)?; let encrypted_contents = - Bytes::::from(self.contents).encrypt(ctx, attachment_key)?; + OctetStreamBytes::from(self.contents).encrypt(ctx, attachment_key)?; attachment.key = Some(ctx.wrap_symmetric_key(ciphers_key, attachment_key)?); let contents = encrypted_contents.to_buffer()?; From c41e5138dd85f94267882751ec56b777c10c5bc5 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 24 Jun 2025 13:46:55 +0200 Subject: [PATCH 067/124] Apply clippy fixes --- crates/bitwarden-core/src/auth/auth_request.rs | 4 +--- crates/bitwarden-crypto/examples/signature.rs | 2 +- crates/bitwarden-crypto/examples/signed_object.rs | 4 +--- crates/bitwarden-crypto/src/fingerprint.rs | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/bitwarden-core/src/auth/auth_request.rs b/crates/bitwarden-core/src/auth/auth_request.rs index 5eb1061ab..521e60c85 100644 --- a/crates/bitwarden-core/src/auth/auth_request.rs +++ b/crates/bitwarden-core/src/auth/auth_request.rs @@ -110,9 +110,7 @@ pub(crate) fn approve_auth_request( mod tests { use std::num::NonZeroU32; - use bitwarden_crypto::{ - BitwardenLegacyKeyBytes, Bytes, Kdf, MasterKey, SpkiPublicKeyDerContentFormat, - }; + use bitwarden_crypto::{BitwardenLegacyKeyBytes, Kdf, MasterKey, SpkiPublicKeyBytes}; use super::*; use crate::key_management::{ diff --git a/crates/bitwarden-crypto/examples/signature.rs b/crates/bitwarden-crypto/examples/signature.rs index 881b00de0..23ab52b84 100644 --- a/crates/bitwarden-crypto/examples/signature.rs +++ b/crates/bitwarden-crypto/examples/signature.rs @@ -1,7 +1,7 @@ //! This example demonstrates how to create signatures and countersignatures for a message, and how //! to verify them. -use bitwarden_crypto::{Bytes, CoseSerializable, CoseSign1ContentFormat, SigningNamespace}; +use bitwarden_crypto::{CoseSerializable, CoseSign1Bytes, SigningNamespace}; use serde::{Deserialize, Serialize}; const EXAMPLE_NAMESPACE: &SigningNamespace = &SigningNamespace::SignedPublicKey; diff --git a/crates/bitwarden-crypto/examples/signed_object.rs b/crates/bitwarden-crypto/examples/signed_object.rs index 5b692535b..e34d06a8e 100644 --- a/crates/bitwarden-crypto/examples/signed_object.rs +++ b/crates/bitwarden-crypto/examples/signed_object.rs @@ -1,8 +1,6 @@ //! This example demonstrates how to sign and verify structs. -use bitwarden_crypto::{ - Bytes, CoseSerializable, CoseSign1ContentFormat, SignedObject, SigningNamespace, -}; +use bitwarden_crypto::{CoseSerializable, CoseSign1Bytes, SignedObject, SigningNamespace}; use serde::{Deserialize, Serialize}; const EXAMPLE_NAMESPACE: &SigningNamespace = &SigningNamespace::SignedPublicKey; diff --git a/crates/bitwarden-crypto/src/fingerprint.rs b/crates/bitwarden-crypto/src/fingerprint.rs index 6e8d55772..a520804ca 100644 --- a/crates/bitwarden-crypto/src/fingerprint.rs +++ b/crates/bitwarden-crypto/src/fingerprint.rs @@ -64,7 +64,7 @@ pub enum FingerprintError { #[cfg(test)] mod tests { use super::fingerprint; - use crate::{Bytes, SpkiPublicKeyDerContentFormat}; + use crate::{Bytes, SpkiPublicKeyBytes}; #[test] fn test_fingerprint() { From 90d2295e9a996d1417d323d881acdf56e36797ca Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:16:50 +0200 Subject: [PATCH 068/124] Update crates/bitwarden-crypto/src/enc_string/symmetric.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-crypto/src/enc_string/symmetric.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index 0402444c6..1a4a0955d 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -322,7 +322,7 @@ impl KeyDecryptable> for EncString { impl KeyEncryptable for String { fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { - Into::::into(self).encrypt_with_key(key) + Utf8Bytes::from(self).encrypt_with_key(key) } } From de8f9570e69a4487fc4b28d340fae5dedb2a8d19 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:17:01 +0200 Subject: [PATCH 069/124] Update crates/bitwarden-crypto/src/keys/signed_public_key.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-crypto/src/keys/signed_public_key.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/bitwarden-crypto/src/keys/signed_public_key.rs b/crates/bitwarden-crypto/src/keys/signed_public_key.rs index d46898996..bc2cc0c18 100644 --- a/crates/bitwarden-crypto/src/keys/signed_public_key.rs +++ b/crates/bitwarden-crypto/src/keys/signed_public_key.rs @@ -89,9 +89,7 @@ impl From for Vec { impl TryFrom> for SignedPublicKey { type Error = EncodingError; fn try_from(bytes: Vec) -> Result { - Ok(SignedPublicKey(SignedObject::from_cose(&Bytes::< - CoseSign1ContentFormat, - >::from( + Ok(SignedPublicKey(SignedObject::from_cose(&CoseSign1ContentFormat::from( bytes ))?)) } From f6ad5133ffa87c6c127ba2941cd6d67bcb33c79b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:17:32 +0200 Subject: [PATCH 070/124] Update crates/bitwarden-crypto/src/enc_string/symmetric.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-crypto/src/enc_string/symmetric.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index 1a4a0955d..c6b9ca13c 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -328,7 +328,7 @@ impl KeyEncryptable for String { impl KeyEncryptable for &str { fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { - Into::::into(self).encrypt_with_key(key) + Utf8Bytes::from(self).encrypt_with_key(key) } } From 3c2984bb5860d29d8e0a82dc9d049676541e9955 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:17:43 +0200 Subject: [PATCH 071/124] Update crates/bitwarden-crypto/src/fingerprint.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-crypto/src/fingerprint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/fingerprint.rs b/crates/bitwarden-crypto/src/fingerprint.rs index a520804ca..84d127612 100644 --- a/crates/bitwarden-crypto/src/fingerprint.rs +++ b/crates/bitwarden-crypto/src/fingerprint.rs @@ -87,7 +87,7 @@ mod tests { 197, 3, 219, 56, 77, 109, 47, 72, 251, 131, 36, 240, 96, 169, 31, 82, 93, 166, 242, 3, 33, 213, 2, 3, 1, 0, 1, ]; - let key: SpkiPublicKeyBytes = Bytes::from(key.to_vec()); + let key = SpkiPublicKeyBytes::from(key); assert_eq!( "turban-deftly-anime-chatroom-unselfish", fingerprint(user_id, &key).unwrap() From bdc90b33a882a66abe45291d060acb670e37e541 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:28:26 +0200 Subject: [PATCH 072/124] Update crates/bitwarden-crypto/src/signing/signed_object.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-crypto/src/signing/signed_object.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-crypto/src/signing/signed_object.rs b/crates/bitwarden-crypto/src/signing/signed_object.rs index be35f7f40..6ca744d86 100644 --- a/crates/bitwarden-crypto/src/signing/signed_object.rs +++ b/crates/bitwarden-crypto/src/signing/signed_object.rs @@ -215,9 +215,8 @@ mod tests { field1: "Test message".to_string(), }; let signed_object = - SignedObject::from_cose(&Into::::into(SIGNED_OBJECT)).unwrap(); - let verifying_key = - VerifyingKey::from_cose(&Into::::into(VERIFYING_KEY)).unwrap(); + SignedObject::from_cose(&::from(SIGNED_OBJECT)).unwrap(); + let verifying_key = VerifyingKey::from_cose(&::from(VERIFYING_KEY)).unwrap(); let namespace = SigningNamespace::ExampleNamespace; let payload: TestMessage = signed_object .verify_and_unwrap(&verifying_key, &namespace) From 80dde40f35e0f133028e4e8933609d435b7f718b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:28:37 +0200 Subject: [PATCH 073/124] Update crates/bitwarden-crypto/src/signing/signed_object.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-crypto/src/signing/signed_object.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/signing/signed_object.rs b/crates/bitwarden-crypto/src/signing/signed_object.rs index 6ca744d86..94134d290 100644 --- a/crates/bitwarden-crypto/src/signing/signed_object.rs +++ b/crates/bitwarden-crypto/src/signing/signed_object.rs @@ -200,7 +200,7 @@ mod tests { #[test] fn test_roundtrip_cose() { let signed_object = - SignedObject::from_cose(&Into::::into(SIGNED_OBJECT)).unwrap(); + SignedObject::from_cose(&::from(SIGNED_OBJECT)).unwrap(); assert_eq!( signed_object.content_type().unwrap(), coset::iana::CoapContentFormat::Cbor From 1f30896d9b0bd17e4e2d19c481ca4686633118da Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:29:27 +0200 Subject: [PATCH 074/124] Update crates/bitwarden-crypto/README.md Co-authored-by: Oscar Hinton --- crates/bitwarden-crypto/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/README.md b/crates/bitwarden-crypto/README.md index 5dc24ef2e..c568f475c 100644 --- a/crates/bitwarden-crypto/README.md +++ b/crates/bitwarden-crypto/README.md @@ -13,7 +13,7 @@ secure. ## Example: ```rust -use bitwarden_crypto::{SymmetricCryptoKey, KeyEncryptable, KeyDecryptable, CryptoError, ContentFormat}; +use bitwarden_crypto::{SymmetricCryptoKey, KeyEncryptable, KeyDecryptable, CryptoError}; async fn example() -> Result<(), CryptoError> { let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); From 1817ae09f1343bb73cea9c275f6de86f4d4edacc Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:29:42 +0200 Subject: [PATCH 075/124] Update crates/bitwarden-core/src/key_management/crypto.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-core/src/key_management/crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index d4253a446..47a8be49d 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -536,7 +536,7 @@ pub(super) fn verify_asymmetric_keys( .decrypt_with_key(user_key) .map_err(VerifyError::DecryptFailed)?; - let decrypted_private_key: Pkcs8PrivateKeyBytes = Bytes::from(decrypted_private_key); + let decrypted_private_key = Pkcs8PrivateKeyBytes::from(decrypted_private_key); let private_key = AsymmetricCryptoKey::from_der(&decrypted_private_key) .map_err(VerifyError::ParseFailed)?; From 416dbf5b85f1730863090b0c159a19c8d6b8d69f Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:30:05 +0200 Subject: [PATCH 076/124] Update crates/bitwarden-core/src/client/encryption_settings.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-core/src/client/encryption_settings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index b93fc01e3..a497f570e 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -63,7 +63,7 @@ impl EncryptionSettings { let dec: Vec = private_key.decrypt_with_key(&user_key)?; // FIXME: [PM-11690] - Temporarily ignore invalid private keys until we have a recovery // process in place. - AsymmetricCryptoKey::from_der(&Bytes::from(dec)) + AsymmetricCryptoKey::from_der(&Pkcs8PrivateKeyBytes::from(dec)) .map_err(|_| { warn!("Invalid private key"); }) From aaad50315641e4f3c66e10cc00bf3503687f3562 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:33:43 +0200 Subject: [PATCH 077/124] Fix build and cleanup --- .../bitwarden-core/src/client/encryption_settings.rs | 7 ++----- crates/bitwarden-core/src/key_management/crypto.rs | 2 +- crates/bitwarden-crypto/src/keys/signed_public_key.rs | 11 ++++------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index a497f570e..d3ef67e42 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -1,3 +1,4 @@ +use bitwarden_crypto::{CoseKeyBytes, Pkcs8PrivateKeyBytes}; #[cfg(feature = "internal")] use bitwarden_crypto::{EncString, UnsignedSharedKey}; #[cfg(any(feature = "internal", feature = "secrets"))] @@ -58,8 +59,6 @@ impl EncryptionSettings { use crate::key_management::{AsymmetricKeyId, SigningKeyId, SymmetricKeyId}; let private_key = { - use bitwarden_crypto::Bytes; - let dec: Vec = private_key.decrypt_with_key(&user_key)?; // FIXME: [PM-11690] - Temporarily ignore invalid private keys until we have a recovery // process in place. @@ -81,12 +80,10 @@ impl EncryptionSettings { let mut ctx = store.context_mut(); if let Some(signing_key) = signing_key { - use bitwarden_crypto::Bytes; - let dec: Vec = signing_key .decrypt_with_key(&user_key) .map_err(|_| EncryptionSettingsError::InvalidSigningKey)?; - let signing_key = SigningKey::from_cose(&Bytes::from(dec)) + let signing_key = SigningKey::from_cose(&CoseKeyBytes::from(dec)) .map_err(|_| EncryptionSettingsError::InvalidSigningKey)?; ctx.set_signing_key(SigningKeyId::UserSigningKey, signing_key)?; } diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index 47a8be49d..a76ea4294 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ - AsymmetricCryptoKey, Bytes, CoseSerializable, CryptoError, EncString, Kdf, KeyDecryptable, + AsymmetricCryptoKey, CoseSerializable, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, Pkcs8PrivateKeyBytes, PrimitiveEncryptable, SignatureAlgorithm, SignedPublicKey, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey, }; diff --git a/crates/bitwarden-crypto/src/keys/signed_public_key.rs b/crates/bitwarden-crypto/src/keys/signed_public_key.rs index bc2cc0c18..0d6c56e6e 100644 --- a/crates/bitwarden-crypto/src/keys/signed_public_key.rs +++ b/crates/bitwarden-crypto/src/keys/signed_public_key.rs @@ -11,10 +11,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use super::AsymmetricPublicCryptoKey; use crate::{ - content_format::{Bytes, CoseSign1ContentFormat}, - cose::CoseSerializable, - error::EncodingError, - util::FromStrVisitor, + cose::CoseSerializable, error::EncodingError, util::FromStrVisitor, CoseSign1Bytes, CryptoError, PublicKeyEncryptionAlgorithm, RawPublicKey, SignedObject, SigningKey, SigningNamespace, VerifyingKey, }; @@ -89,9 +86,9 @@ impl From for Vec { impl TryFrom> for SignedPublicKey { type Error = EncodingError; fn try_from(bytes: Vec) -> Result { - Ok(SignedPublicKey(SignedObject::from_cose(&CoseSign1ContentFormat::from( - bytes - ))?)) + Ok(SignedPublicKey(SignedObject::from_cose( + &CoseSign1Bytes::from(bytes), + )?)) } } From 2e21906a318c6d72b873ca493954b554a5bd436d Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:35:57 +0200 Subject: [PATCH 078/124] Remove to_vec from VerifyingKey usages --- crates/bitwarden-crypto/src/signing/verifying_key.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/bitwarden-crypto/src/signing/verifying_key.rs b/crates/bitwarden-crypto/src/signing/verifying_key.rs index 8f1d031e6..12fa0eebf 100644 --- a/crates/bitwarden-crypto/src/signing/verifying_key.rs +++ b/crates/bitwarden-crypto/src/signing/verifying_key.rs @@ -129,8 +129,7 @@ mod tests { #[test] fn test_cose_roundtrip_encode_verifying() { - let verifying_key = - VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap(); + let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY)).unwrap(); let cose = verifying_key.to_cose(); let parsed_key = VerifyingKey::from_cose(&cose).unwrap(); @@ -139,8 +138,7 @@ mod tests { #[test] fn test_testvector() { - let verifying_key = - VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap(); + let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY)).unwrap(); assert_eq!(verifying_key.algorithm(), SignatureAlgorithm::Ed25519); verifying_key @@ -150,8 +148,7 @@ mod tests { #[test] fn test_invalid_testvector() { - let verifying_key = - VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY.to_vec())).unwrap(); + let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY)).unwrap(); assert_eq!(verifying_key.algorithm(), SignatureAlgorithm::Ed25519); // This should fail, as the signed object is not valid for the given verifying key. From dbdee15ef4a04f1d9e0c066405f786ec69d1c0dc Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:37:18 +0200 Subject: [PATCH 079/124] Undo take --- crates/bitwarden-vault/src/cipher/cipher.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-vault/src/cipher/cipher.rs b/crates/bitwarden-vault/src/cipher/cipher.rs index 5492810cc..003079ae7 100644 --- a/crates/bitwarden-vault/src/cipher/cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher.rs @@ -432,7 +432,7 @@ impl CipherView { ctx: &mut KeyStoreContext, key: SymmetricKeyId, ) -> Result<(), CryptoError> { - let old_ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.key.take())?; + let old_ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.key)?; const NEW_KEY: SymmetricKeyId = SymmetricKeyId::Local("new_cipher_key"); From 375fd0d1324b2446a39d91a35d5d014fb358f4c0 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:37:55 +0200 Subject: [PATCH 080/124] Unapply allow missing docs --- crates/bitwarden-crypto/src/keys/key_encryptable.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/keys/key_encryptable.rs b/crates/bitwarden-crypto/src/keys/key_encryptable.rs index 190be2a9d..f0667125d 100644 --- a/crates/bitwarden-crypto/src/keys/key_encryptable.rs +++ b/crates/bitwarden-crypto/src/keys/key_encryptable.rs @@ -24,7 +24,6 @@ pub trait KeyEncryptable { fn encrypt_with_key(self, key: &Key) -> Result; } -#[allow(missing_docs)] pub(crate) trait KeyEncryptableWithContentType { fn encrypt_with_key(self, key: &Key, content_format: ContentFormat) -> Result; } From fd22ded6734a95904105b4269858313d5352e2cc Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:49:57 +0200 Subject: [PATCH 081/124] Clean up KeyEncryptable or pin key --- crates/bitwarden-crypto/src/keys/pin_key.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/crates/bitwarden-crypto/src/keys/pin_key.rs b/crates/bitwarden-crypto/src/keys/pin_key.rs index 247971f1e..0913144c8 100644 --- a/crates/bitwarden-crypto/src/keys/pin_key.rs +++ b/crates/bitwarden-crypto/src/keys/pin_key.rs @@ -1,11 +1,10 @@ use super::{ kdf::{Kdf, KdfDerivedKeyMaterial}, - master_key::{decrypt_user_key, encrypt_user_key}, + master_key::decrypt_user_key, utils::stretch_key, }; use crate::{ - keys::key_encryptable::CryptoKey, ContentFormat, EncString, KeyEncryptable, - KeyEncryptableWithContentType, Result, SymmetricCryptoKey, + keys::key_encryptable::CryptoKey, EncString, KeyEncryptable, Result, SymmetricCryptoKey, }; /// Pin Key. @@ -21,7 +20,7 @@ impl PinKey { /// Encrypt the users user key pub fn encrypt_user_key(&self, user_key: &SymmetricCryptoKey) -> Result { - encrypt_user_key(&self.0 .0, user_key) + user_key.encrypt_with_key(&self) } /// Decrypt the users user key @@ -32,15 +31,18 @@ impl PinKey { impl CryptoKey for PinKey {} -impl KeyEncryptableWithContentType for &[u8] { - fn encrypt_with_key(self, key: &PinKey, content_format: ContentFormat) -> Result { +impl KeyEncryptable for &SymmetricCryptoKey { + fn encrypt_with_key(self, key: &PinKey) -> Result { let stretched_key = SymmetricCryptoKey::Aes256CbcHmacKey(stretch_key(&key.0 .0)?); - self.encrypt_with_key(&stretched_key, content_format) + // The (stretched) pin key is currently always an AES-256-CBC-HMAC key, and wraps a bitwarden legacy encoded symmetric key + self.to_encoded().encrypt_with_key(&stretched_key) } } impl KeyEncryptable for String { fn encrypt_with_key(self, key: &PinKey) -> Result { - self.as_bytes().encrypt_with_key(key, ContentFormat::Utf8) + self.encrypt_with_key(&SymmetricCryptoKey::Aes256CbcHmacKey(stretch_key( + &key.0 .0, + )?)) } } From 7ee0278216a51fb5f90c0c296322acf17c60c5ad Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:58:52 +0200 Subject: [PATCH 082/124] Cleanup --- crates/bitwarden-crypto/src/content_format.rs | 9 ++++----- crates/bitwarden-crypto/src/cose.rs | 1 - crates/bitwarden-crypto/src/keys/pin_key.rs | 3 ++- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/bitwarden-crypto/src/content_format.rs b/crates/bitwarden-crypto/src/content_format.rs index 8896ec935..98d0cb8d2 100644 --- a/crates/bitwarden-crypto/src/content_format.rs +++ b/crates/bitwarden-crypto/src/content_format.rs @@ -14,9 +14,8 @@ use crate::{ /// a Pkcs8 private key, or a COSE key. Specifically, for COSE keys, this allows distinguishing /// between the old symmetric key format, represented as `ContentFormat::OctetStream`, and the new /// COSE key format, represented as `ContentFormat::CoseKey`. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] -pub enum ContentFormat { +#[derive(Clone, Copy, Debug, PartialEq)] +pub(crate) enum ContentFormat { /// UTF-8 encoded text Utf8, /// Pkcs8 private key DER @@ -35,8 +34,6 @@ pub enum ContentFormat { BitwardenLegacyKey, /// Stream of bytes OctetStream, - /// CBOR serialized data - Cbor, } mod private { @@ -51,6 +48,7 @@ mod private { /// which can still be mis-used, but has to be misused explicitly. pub trait ConstContentFormat: private::Sealed { /// Returns the content format as a `ContentFormat` enum. + #[allow(private_interfaces)] fn content_format() -> ContentFormat; } @@ -123,6 +121,7 @@ pub type OctetStreamBytes = Bytes; pub struct Pkcs8PrivateKeyDerContentFormat; impl private::Sealed for Pkcs8PrivateKeyDerContentFormat {} impl ConstContentFormat for Pkcs8PrivateKeyDerContentFormat { + #[allow(exported_private_dependencies)] fn content_format() -> ContentFormat { ContentFormat::Pkcs8PrivateKey } diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 682146ab4..b2a39ab56 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -176,7 +176,6 @@ impl From for coset::HeaderBuilder { ContentFormat::OctetStream => { header_builder.content_format(CoapContentFormat::OctetStream) } - ContentFormat::Cbor => header_builder.content_format(CoapContentFormat::Cbor), } } } diff --git a/crates/bitwarden-crypto/src/keys/pin_key.rs b/crates/bitwarden-crypto/src/keys/pin_key.rs index 0913144c8..e29c7cc3a 100644 --- a/crates/bitwarden-crypto/src/keys/pin_key.rs +++ b/crates/bitwarden-crypto/src/keys/pin_key.rs @@ -34,7 +34,8 @@ impl CryptoKey for PinKey {} impl KeyEncryptable for &SymmetricCryptoKey { fn encrypt_with_key(self, key: &PinKey) -> Result { let stretched_key = SymmetricCryptoKey::Aes256CbcHmacKey(stretch_key(&key.0 .0)?); - // The (stretched) pin key is currently always an AES-256-CBC-HMAC key, and wraps a bitwarden legacy encoded symmetric key + // The (stretched) pin key is currently always an AES-256-CBC-HMAC key, and wraps a + // bitwarden legacy encoded symmetric key self.to_encoded().encrypt_with_key(&stretched_key) } } From 9b3549a411243086019cf0589b694e032e5bd3a8 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 11:59:57 +0200 Subject: [PATCH 083/124] Apply cleanup --- crates/bitwarden-core/src/platform/generate_fingerprint.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bitwarden-core/src/platform/generate_fingerprint.rs b/crates/bitwarden-core/src/platform/generate_fingerprint.rs index 2ec8673a3..63654a907 100644 --- a/crates/bitwarden-core/src/platform/generate_fingerprint.rs +++ b/crates/bitwarden-core/src/platform/generate_fingerprint.rs @@ -42,8 +42,7 @@ pub enum FingerprintError { pub(crate) fn generate_fingerprint(input: &FingerprintRequest) -> Result { let key = STANDARD.decode(&input.public_key)?; - let key = SpkiPublicKeyBytes::from(key); - Ok(fingerprint(&input.fingerprint_material, &key)?) + Ok(fingerprint(&input.fingerprint_material, &key.into())?) } /// Errors that can occur when computing a fingerprint. From a5abaae54d522c4755a96773ccc3da040e589611 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 12:11:25 +0200 Subject: [PATCH 084/124] Undo changes to crypto init --- .../src/client/encryption_settings.rs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index d3ef67e42..4fc576083 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -73,26 +73,27 @@ impl EncryptionSettings { // .map_err(|_| EncryptionSettingsError::InvalidPrivateKey)?, // ) }; + let signing_key = signing_key + .map(|key| { + use bitwarden_crypto::CryptoError; + + let dec: Vec = key.decrypt_with_key(&user_key)?; + SigningKey::from_cose(&CoseKeyBytes::from(dec)).map_err(Into::::into) + }) + .transpose()?; // FIXME: [PM-18098] When this is part of crypto we won't need to use deprecated methods #[allow(deprecated)] { let mut ctx = store.context_mut(); - - if let Some(signing_key) = signing_key { - let dec: Vec = signing_key - .decrypt_with_key(&user_key) - .map_err(|_| EncryptionSettingsError::InvalidSigningKey)?; - let signing_key = SigningKey::from_cose(&CoseKeyBytes::from(dec)) - .map_err(|_| EncryptionSettingsError::InvalidSigningKey)?; - ctx.set_signing_key(SigningKeyId::UserSigningKey, signing_key)?; - } - + ctx.set_symmetric_key(SymmetricKeyId::User, user_key)?; if let Some(private_key) = private_key { ctx.set_asymmetric_key(AsymmetricKeyId::UserPrivateKey, private_key)?; } - ctx.set_symmetric_key(SymmetricKeyId::User, user_key)?; + if let Some(signing_key) = signing_key { + ctx.set_signing_key(SigningKeyId::UserSigningKey, signing_key)?; + } } Ok(()) From 81138ea6787b7f3210c22421d8f047bb038272e6 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 12:12:22 +0200 Subject: [PATCH 085/124] Apply allow private interfaces to content format --- crates/bitwarden-crypto/src/content_format.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/content_format.rs b/crates/bitwarden-crypto/src/content_format.rs index 98d0cb8d2..fa87b1759 100644 --- a/crates/bitwarden-crypto/src/content_format.rs +++ b/crates/bitwarden-crypto/src/content_format.rs @@ -108,6 +108,7 @@ pub(crate) type Utf8Bytes = Bytes; pub struct OctetStreamContentFormat; impl private::Sealed for OctetStreamContentFormat {} impl ConstContentFormat for OctetStreamContentFormat { + #[allow(private_interfaces)] fn content_format() -> ContentFormat { ContentFormat::OctetStream } @@ -121,7 +122,7 @@ pub type OctetStreamBytes = Bytes; pub struct Pkcs8PrivateKeyDerContentFormat; impl private::Sealed for Pkcs8PrivateKeyDerContentFormat {} impl ConstContentFormat for Pkcs8PrivateKeyDerContentFormat { - #[allow(exported_private_dependencies)] + #[allow(private_interfaces)] fn content_format() -> ContentFormat { ContentFormat::Pkcs8PrivateKey } @@ -135,6 +136,7 @@ pub type Pkcs8PrivateKeyBytes = Bytes; pub struct SpkiPublicKeyDerContentFormat; impl private::Sealed for SpkiPublicKeyDerContentFormat {} impl ConstContentFormat for SpkiPublicKeyDerContentFormat { + #[allow(private_interfaces)] fn content_format() -> ContentFormat { ContentFormat::SPKIPublicKeyDer } @@ -148,6 +150,7 @@ pub type SpkiPublicKeyBytes = Bytes; pub struct CoseKeyContentFormat; impl private::Sealed for CoseKeyContentFormat {} impl ConstContentFormat for CoseKeyContentFormat { + #[allow(private_interfaces)] fn content_format() -> ContentFormat { ContentFormat::CoseKey } @@ -162,6 +165,7 @@ pub type CoseKeyBytes = Bytes; pub struct BitwardenLegacyKeyContentFormat; impl private::Sealed for BitwardenLegacyKeyContentFormat {} impl ConstContentFormat for BitwardenLegacyKeyContentFormat { + #[allow(private_interfaces)] fn content_format() -> ContentFormat { ContentFormat::BitwardenLegacyKey } @@ -176,6 +180,7 @@ pub type BitwardenLegacyKeyBytes = Bytes; pub struct CoseSign1ContentFormat; impl private::Sealed for CoseSign1ContentFormat {} impl ConstContentFormat for CoseSign1ContentFormat { + #[allow(private_interfaces)] fn content_format() -> ContentFormat { ContentFormat::CoseSign1 } From 4445aec0f544f98b5147ed7526ff69cfdd80fbd7 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 12:17:10 +0200 Subject: [PATCH 086/124] Cleanup --- crates/bitwarden-core/src/platform/generate_fingerprint.rs | 2 +- crates/bitwarden-crypto/src/content_format.rs | 2 -- crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-core/src/platform/generate_fingerprint.rs b/crates/bitwarden-core/src/platform/generate_fingerprint.rs index 63654a907..e6ff4cbf8 100644 --- a/crates/bitwarden-core/src/platform/generate_fingerprint.rs +++ b/crates/bitwarden-core/src/platform/generate_fingerprint.rs @@ -3,7 +3,7 @@ //! This module contains the logic for generating fingerprints. use base64::{engine::general_purpose::STANDARD, Engine}; -use bitwarden_crypto::{fingerprint, SpkiPublicKeyBytes}; +use bitwarden_crypto::fingerprint; use serde::{Deserialize, Serialize}; use thiserror::Error; diff --git a/crates/bitwarden-crypto/src/content_format.rs b/crates/bitwarden-crypto/src/content_format.rs index fa87b1759..8c2c98ff1 100644 --- a/crates/bitwarden-crypto/src/content_format.rs +++ b/crates/bitwarden-crypto/src/content_format.rs @@ -1,6 +1,4 @@ use serde::{Deserialize, Serialize}; -#[cfg(feature = "wasm")] -use tsify_next::Tsify; use crate::{ traits::PrimitiveEncryptableWithContentType, CryptoError, EncString, KeyEncryptable, diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index 767cdd052..add3cfadf 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -407,6 +407,7 @@ impl From for Vec { } } impl EncodedSymmetricKey { + #[allow(private_interfaces)] pub fn content_format(&self) -> ContentFormat { match self { EncodedSymmetricKey::LegacyNonCoseKey(_) => ContentFormat::BitwardenLegacyKey, From 25d1907eb746f1c5c046a1243a314186628c0d7b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 13:07:26 +0200 Subject: [PATCH 087/124] Typesafe base64 handling --- crates/bitwarden-core/src/auth/auth_client.rs | 5 ++- .../bitwarden-core/src/auth/auth_request.rs | 38 +++++++++------- crates/bitwarden-core/src/auth/tde.rs | 7 ++- .../src/key_management/crypto.rs | 16 +++---- .../src/key_management/crypto_client.rs | 2 +- crates/bitwarden-core/src/lib.rs | 2 + crates/bitwarden-core/src/types.rs | 45 +++++++++++++++++++ crates/bitwarden-crypto/src/fingerprint.rs | 2 +- .../src/keys/asymmetric_crypto_key.rs | 7 +-- crates/bitwarden-crypto/src/keys/pin_key.rs | 2 +- .../src/keys/signed_public_key.rs | 8 ++-- crates/bitwarden-uniffi/src/auth/mod.rs | 4 +- .../src/pure_crypto.rs | 3 +- 13 files changed, 99 insertions(+), 42 deletions(-) create mode 100644 crates/bitwarden-core/src/types.rs diff --git a/crates/bitwarden-core/src/auth/auth_client.rs b/crates/bitwarden-core/src/auth/auth_client.rs index 6164a87a3..589df586d 100644 --- a/crates/bitwarden-core/src/auth/auth_client.rs +++ b/crates/bitwarden-core/src/auth/auth_client.rs @@ -26,6 +26,7 @@ use crate::{ RegisterRequest, }, client::encryption_settings::EncryptionSettingsError, + Base64String, }; use crate::{ auth::{login::LoginError, renew::renew_token}, @@ -89,7 +90,7 @@ impl AuthClient { pub fn make_register_tde_keys( &self, email: String, - org_public_key: String, + org_public_key: Base64String, remember_device: bool, ) -> Result { make_register_tde_keys(&self.client, email, org_public_key, remember_device) @@ -172,7 +173,7 @@ impl AuthClient { #[allow(missing_docs)] pub fn approve_auth_request( &self, - public_key: String, + public_key: Base64String, ) -> Result { approve_auth_request(&self.client, public_key) } diff --git a/crates/bitwarden-core/src/auth/auth_request.rs b/crates/bitwarden-core/src/auth/auth_request.rs index 521e60c85..3476dcd4e 100644 --- a/crates/bitwarden-core/src/auth/auth_request.rs +++ b/crates/bitwarden-core/src/auth/auth_request.rs @@ -8,7 +8,7 @@ use bitwarden_crypto::{EncString, SymmetricCryptoKey}; use thiserror::Error; #[cfg(feature = "internal")] -use crate::client::encryption_settings::EncryptionSettingsError; +use crate::{client::encryption_settings::EncryptionSettingsError, Base64String}; use crate::{key_management::SymmetricKeyId, Client, VaultLockedError}; /// Response for `new_auth_request`. @@ -49,25 +49,25 @@ pub(crate) fn new_auth_request(email: &str) -> Result Result { - let key = AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key)?.into())?; - let key: SymmetricCryptoKey = user_key.decapsulate_key_unsigned(&key)?; + let private_key = AsymmetricCryptoKey::from_der(&private_key.try_into()?)?; + let key: SymmetricCryptoKey = user_key.decapsulate_key_unsigned(&private_key)?; Ok(key) } /// Decrypt the user key using the private key generated previously. #[cfg(feature = "internal")] pub(crate) fn auth_request_decrypt_master_key( - private_key: String, + private_key: Base64String, master_key: UnsignedSharedKey, user_key: EncString, ) -> Result { use bitwarden_crypto::MasterKey; - let key = AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key)?.into())?; - let master_key: SymmetricCryptoKey = master_key.decapsulate_key_unsigned(&key)?; + let private_key = AsymmetricCryptoKey::from_der(&private_key.try_into()?)?; + let master_key: SymmetricCryptoKey = master_key.decapsulate_key_unsigned(&private_key)?; let master_key = MasterKey::try_from(&master_key)?; Ok(master_key.decrypt_user_key(user_key)?) @@ -89,9 +89,9 @@ pub enum ApproveAuthRequestError { /// Encrypts the user key with a public key. pub(crate) fn approve_auth_request( client: &Client, - public_key: String, + public_key: Base64String, ) -> Result { - let public_key = AsymmetricPublicCryptoKey::from_der(&STANDARD.decode(public_key)?)?; + let public_key = AsymmetricPublicCryptoKey::from_der(&public_key.try_into()?)?; let key_store = client.internal.get_key_store(); let ctx = key_store.context(); @@ -140,7 +140,8 @@ mod tests { ) .unwrap(); - let decrypted = auth_request_decrypt_user_key(request.private_key, encrypted).unwrap(); + let decrypted = + auth_request_decrypt_user_key(request.private_key.into(), encrypted).unwrap(); assert_eq!(decrypted.to_encoded().to_vec(), secret.to_vec()); } @@ -173,7 +174,7 @@ mod tests { let fingerprint = fingerprint("test@bitwarden.com", &pubkey).unwrap(); assert_eq!(fingerprint, "childless-unfair-prowler-dropbox-designate"); - approve_auth_request(&client, public_key.to_owned()).unwrap(); + approve_auth_request(&client, public_key.to_owned().into()).unwrap(); } #[tokio::test] @@ -181,7 +182,8 @@ mod tests { let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; let enc_user_key = "4.dxbd5OMwi/Avy7DQxvLV+Z7kDJgHBtg/jAbgYNO7QU0Zii4rLFNco2lS5aS9z42LTZHc2p5HYwn2ZwkZNfHsQ6//d5q40MDgGYJMKBXOZP62ZHhct1XsvYBmtcUtIOm5j2HSjt2pjEuGAc1LbyGIWRJJQ3Lp1ULbL2m71I+P23GF36JyOM8SUWvpvxE/3+qqVhRFPG2VqMCYa2kLLxwVfUmpV+KKjX1TXsrq6pfJIwHNwHw4h7MSfD8xTy2bx4MiBt638Z9Vt1pGsSQkh9RgPvCbnhuCpZQloUgJ8ByLVEcrlKx3yaaxiQXvte+ZhuOI7rGdjmoVoOzisooje4JgYw==".parse().unwrap(); - let dec = auth_request_decrypt_user_key(private_key.to_owned(), enc_user_key).unwrap(); + let dec = + auth_request_decrypt_user_key(private_key.to_owned().into(), enc_user_key).unwrap(); assert_eq!( &dec.to_encoded().to_vec(), @@ -198,9 +200,12 @@ mod tests { let enc_master_key = "4.dxbd5OMwi/Avy7DQxvLV+Z7kDJgHBtg/jAbgYNO7QU0Zii4rLFNco2lS5aS9z42LTZHc2p5HYwn2ZwkZNfHsQ6//d5q40MDgGYJMKBXOZP62ZHhct1XsvYBmtcUtIOm5j2HSjt2pjEuGAc1LbyGIWRJJQ3Lp1ULbL2m71I+P23GF36JyOM8SUWvpvxE/3+qqVhRFPG2VqMCYa2kLLxwVfUmpV+KKjX1TXsrq6pfJIwHNwHw4h7MSfD8xTy2bx4MiBt638Z9Vt1pGsSQkh9RgPvCbnhuCpZQloUgJ8ByLVEcrlKx3yaaxiQXvte+ZhuOI7rGdjmoVoOzisooje4JgYw==".parse().unwrap(); let enc_user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); - let dec = - auth_request_decrypt_master_key(private_key.to_owned(), enc_master_key, enc_user_key) - .unwrap(); + let dec = auth_request_decrypt_master_key( + private_key.to_owned().into(), + enc_master_key, + enc_user_key, + ) + .unwrap(); assert_eq!( &dec.to_encoded().to_vec(), @@ -238,7 +243,8 @@ mod tests { // Initialize an auth request, and approve it on the existing device let auth_req = new_auth_request(email).unwrap(); - let approved_req = approve_auth_request(&existing_device, auth_req.public_key).unwrap(); + let approved_req = + approve_auth_request(&existing_device, auth_req.public_key.into()).unwrap(); // Unlock the vault using the approved request new_device diff --git a/crates/bitwarden-core/src/auth/tde.rs b/crates/bitwarden-core/src/auth/tde.rs index 570a28fd2..1c1b9250d 100644 --- a/crates/bitwarden-core/src/auth/tde.rs +++ b/crates/bitwarden-core/src/auth/tde.rs @@ -1,10 +1,9 @@ -use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ AsymmetricPublicCryptoKey, DeviceKey, EncString, Kdf, SymmetricCryptoKey, TrustDeviceResponse, UnsignedSharedKey, UserKey, }; -use crate::{client::encryption_settings::EncryptionSettingsError, Client}; +use crate::{client::encryption_settings::EncryptionSettingsError, Base64String, Client}; /// This function generates a new user key and key pair, initializes the client's crypto with the /// generated user key, and encrypts the user key with the organization public key for admin @@ -12,10 +11,10 @@ use crate::{client::encryption_settings::EncryptionSettingsError, Client}; pub(super) fn make_register_tde_keys( client: &Client, email: String, - org_public_key: String, + org_public_key: Base64String, remember_device: bool, ) -> Result { - let public_key = AsymmetricPublicCryptoKey::from_der(&STANDARD.decode(org_public_key)?)?; + let public_key = AsymmetricPublicCryptoKey::from_der(&org_public_key.try_into()?)?; let user_key = UserKey::new(SymmetricCryptoKey::make_aes256_cbc_hmac_key()); let key_pair = user_key.make_key_pair()?; diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index a76ea4294..5c90c1c94 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -10,7 +10,8 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ AsymmetricCryptoKey, CoseSerializable, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, Pkcs8PrivateKeyBytes, PrimitiveEncryptable, SignatureAlgorithm, - SignedPublicKey, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey, + SignedPublicKey, SigningKey, SpkiPublicKeyBytes, SymmetricCryptoKey, UnsignedSharedKey, + UserKey, }; use bitwarden_error::bitwarden_error; use schemars::JsonSchema; @@ -21,7 +22,7 @@ use {tsify_next::Tsify, wasm_bindgen::prelude::*}; use crate::{ client::{encryption_settings::EncryptionSettingsError, LoginMethod, UserLoginMethod}, key_management::{AsymmetricKeyId, SigningKeyId, SymmetricKeyId}, - Client, NotAuthenticatedError, VaultLockedError, WrongPasswordError, + Base64String, Client, NotAuthenticatedError, VaultLockedError, WrongPasswordError, }; /// Catch all error for mobile crypto operations. @@ -177,13 +178,13 @@ pub(super) async fn initialize_user_crypto( } => { let user_key = match method { AuthRequestMethod::UserKey { protected_user_key } => { - auth_request_decrypt_user_key(request_private_key, protected_user_key)? + auth_request_decrypt_user_key(request_private_key.into(), protected_user_key)? } AuthRequestMethod::MasterKey { protected_master_key, auth_request_key, } => auth_request_decrypt_master_key( - request_private_key, + request_private_key.into(), protected_master_key, auth_request_key, )?, @@ -407,12 +408,11 @@ pub enum EnrollAdminPasswordResetError { pub(super) fn enroll_admin_password_reset( client: &Client, - public_key: String, + public_key: Base64String, ) -> Result { - use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::AsymmetricPublicCryptoKey; - let public_key = AsymmetricPublicCryptoKey::from_der(&STANDARD.decode(public_key)?)?; + let public_key = AsymmetricPublicCryptoKey::from_der(&public_key.try_into()?)?; let key_store = client.internal.get_key_store(); let ctx = key_store.context(); // FIXME: [PM-18110] This should be removed once the key store can handle public key encryption @@ -841,7 +841,7 @@ mod tests { let public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsy7RFHcX3C8Q4/OMmhhbFReYWfB45W9PDTEA8tUZwZmtOiN2RErIS2M1c+K/4HoDJ/TjpbX1f2MZcr4nWvKFuqnZXyewFc+jmvKVewYi+NAu2++vqKq2kKcmMNhwoQDQdQIVy/Uqlp4Cpi2cIwO6ogq5nHNJGR3jm+CpyrafYlbz1bPvL3hbyoGDuG2tgADhyhXUdFuef2oF3wMvn1lAJAvJnPYpMiXUFmj1ejmbwtlxZDrHgUJvUcp7nYdwUKaFoi+sOttHn3u7eZPtNvxMjhSS/X/1xBIzP/mKNLdywH5LoRxniokUk+fV3PYUxJsiU3lV0Trc/tH46jqd8ZGjmwIDAQAB"; - let encrypted = enroll_admin_password_reset(&client, public_key.to_owned()).unwrap(); + let encrypted = enroll_admin_password_reset(&client, public_key.to_owned().into()).unwrap(); let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; let private_key = STANDARD.decode(private_key).unwrap(); diff --git a/crates/bitwarden-core/src/key_management/crypto_client.rs b/crates/bitwarden-core/src/key_management/crypto_client.rs index 03390939a..d06169f8b 100644 --- a/crates/bitwarden-core/src/key_management/crypto_client.rs +++ b/crates/bitwarden-core/src/key_management/crypto_client.rs @@ -109,7 +109,7 @@ impl CryptoClient { &self, public_key: String, ) -> Result { - enroll_admin_password_reset(&self.client, public_key) + enroll_admin_password_reset(&self.client, public_key.into()) } /// Derive the master key for migrating to the key connector diff --git a/crates/bitwarden-core/src/lib.rs b/crates/bitwarden-core/src/lib.rs index 18885fa1f..f0a61e6ae 100644 --- a/crates/bitwarden-core/src/lib.rs +++ b/crates/bitwarden-core/src/lib.rs @@ -28,3 +28,5 @@ pub use client::{Client, ClientSettings, DeviceType}; mod ids; pub use ids::*; +mod types; +pub use types::*; diff --git a/crates/bitwarden-core/src/types.rs b/crates/bitwarden-core/src/types.rs new file mode 100644 index 000000000..3074a20e5 --- /dev/null +++ b/crates/bitwarden-core/src/types.rs @@ -0,0 +1,45 @@ +use base64::{engine::general_purpose::STANDARD, Engine}; +use bitwarden_crypto::{Pkcs8PrivateKeyBytes, SpkiPublicKeyBytes}; + +/// A wrapper around a Base64-encoded string that can be used to decode it into a byte vector. +/// This is useful for handling Base64-encoded strings in a type-safe manner, +/// ensuring that the string is always treated as Base64 data. +pub struct Base64String(String); + +impl From for Base64String { + fn from(val: String) -> Self { + Base64String(val) + } +} + +impl TryInto> for Base64String { + type Error = base64::DecodeError; + + fn try_into(self) -> Result, Self::Error> { + STANDARD.decode(&self.0) + } +} + +impl From> for Base64String { + fn from(val: Vec) -> Self { + Base64String(STANDARD.encode(val)) + } +} + +impl TryInto for Base64String { + type Error = base64::DecodeError; + + fn try_into(self) -> Result { + let bytes: Vec = self.try_into()?; + Ok(SpkiPublicKeyBytes::from(bytes)) + } +} + +impl TryInto for Base64String { + type Error = base64::DecodeError; + + fn try_into(self) -> Result { + let bytes: Vec = self.try_into()?; + Ok(Pkcs8PrivateKeyBytes::from(bytes)) + } +} diff --git a/crates/bitwarden-crypto/src/fingerprint.rs b/crates/bitwarden-crypto/src/fingerprint.rs index 84d127612..22307e977 100644 --- a/crates/bitwarden-crypto/src/fingerprint.rs +++ b/crates/bitwarden-crypto/src/fingerprint.rs @@ -64,7 +64,7 @@ pub enum FingerprintError { #[cfg(test)] mod tests { use super::fingerprint; - use crate::{Bytes, SpkiPublicKeyBytes}; + use crate::SpkiPublicKeyBytes; #[test] fn test_fingerprint() { diff --git a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs index 14a400992..f13f64bac 100644 --- a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs @@ -35,10 +35,11 @@ impl AsymmetricPublicCryptoKey { } /// Build a public key from the SubjectPublicKeyInfo DER. - pub fn from_der(der: &[u8]) -> Result { + pub fn from_der(der: &SpkiPublicKeyBytes) -> Result { Ok(AsymmetricPublicCryptoKey { inner: RawPublicKey::RsaOaepSha1( - RsaPublicKey::from_public_key_der(der).map_err(|_| CryptoError::InvalidKey)?, + RsaPublicKey::from_public_key_der(der.as_ref()) + .map_err(|_| CryptoError::InvalidKey)?, ), }) } @@ -263,7 +264,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= let private_key = Pkcs8PrivateKeyBytes::from(private_key); let private_key = AsymmetricCryptoKey::from_der(&private_key).unwrap(); - let public_key = AsymmetricPublicCryptoKey::from_der(&public_key).unwrap(); + let public_key = AsymmetricPublicCryptoKey::from_der(&public_key.into()).unwrap(); let raw_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let encrypted = UnsignedSharedKey::encapsulate_key_unsigned(&raw_key, &public_key).unwrap(); diff --git a/crates/bitwarden-crypto/src/keys/pin_key.rs b/crates/bitwarden-crypto/src/keys/pin_key.rs index e29c7cc3a..a1503a5ad 100644 --- a/crates/bitwarden-crypto/src/keys/pin_key.rs +++ b/crates/bitwarden-crypto/src/keys/pin_key.rs @@ -20,7 +20,7 @@ impl PinKey { /// Encrypt the users user key pub fn encrypt_user_key(&self, user_key: &SymmetricCryptoKey) -> Result { - user_key.encrypt_with_key(&self) + user_key.encrypt_with_key(self) } /// Decrypt the users user key diff --git a/crates/bitwarden-crypto/src/keys/signed_public_key.rs b/crates/bitwarden-crypto/src/keys/signed_public_key.rs index 0d6c56e6e..ff4fa3fb8 100644 --- a/crates/bitwarden-crypto/src/keys/signed_public_key.rs +++ b/crates/bitwarden-crypto/src/keys/signed_public_key.rs @@ -13,7 +13,7 @@ use super::AsymmetricPublicCryptoKey; use crate::{ cose::CoseSerializable, error::EncodingError, util::FromStrVisitor, CoseSign1Bytes, CryptoError, PublicKeyEncryptionAlgorithm, RawPublicKey, SignedObject, SigningKey, - SigningNamespace, VerifyingKey, + SigningNamespace, SpkiPublicKeyBytes, VerifyingKey, }; #[cfg(feature = "wasm")] @@ -114,8 +114,10 @@ impl SignedPublicKey { public_key_message.content_format, ) { (PublicKeyEncryptionAlgorithm::RsaOaepSha1, PublicKeyFormat::Spki) => Ok( - AsymmetricPublicCryptoKey::from_der(&public_key_message.public_key.into_vec()) - .map_err(|_| EncodingError::InvalidValue("public key"))?, + AsymmetricPublicCryptoKey::from_der(&SpkiPublicKeyBytes::from( + public_key_message.public_key.into_vec(), + )) + .map_err(|_| EncodingError::InvalidValue("public key"))?, ), } } diff --git a/crates/bitwarden-uniffi/src/auth/mod.rs b/crates/bitwarden-uniffi/src/auth/mod.rs index 5f90b1673..3d36612ea 100644 --- a/crates/bitwarden-uniffi/src/auth/mod.rs +++ b/crates/bitwarden-uniffi/src/auth/mod.rs @@ -73,7 +73,7 @@ impl AuthClient { Ok(self .0 .auth() - .make_register_tde_keys(email, org_public_key, remember_device) + .make_register_tde_keys(email, org_public_key.into(), remember_device) .map_err(Error::EncryptionSettings)?) } @@ -146,7 +146,7 @@ impl AuthClient { Ok(self .0 .auth() - .approve_auth_request(public_key) + .approve_auth_request(public_key.into()) .map_err(Error::ApproveAuthRequest)?) } diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 1de26aa17..196cca947 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -251,7 +251,8 @@ impl PureCrypto { shared_key: Vec, encapsulation_key: Vec, ) -> Result { - let encapsulation_key = AsymmetricPublicCryptoKey::from_der(encapsulation_key.as_slice())?; + let encapsulation_key = + AsymmetricPublicCryptoKey::from_der(&SpkiPublicKeyBytes::from(encapsulation_key))?; Ok(UnsignedSharedKey::encapsulate_key_unsigned( &SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(shared_key))?, &encapsulation_key, From 0d0f59b8dfd7c654ef7ef0f85656622d8711caa0 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 13:12:35 +0200 Subject: [PATCH 088/124] Cleanup --- crates/bitwarden-core/src/key_management/crypto.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index 5c90c1c94..bab793c35 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -10,8 +10,7 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ AsymmetricCryptoKey, CoseSerializable, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, Pkcs8PrivateKeyBytes, PrimitiveEncryptable, SignatureAlgorithm, - SignedPublicKey, SigningKey, SpkiPublicKeyBytes, SymmetricCryptoKey, UnsignedSharedKey, - UserKey, + SignedPublicKey, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey, }; use bitwarden_error::bitwarden_error; use schemars::JsonSchema; @@ -819,8 +818,6 @@ mod tests { #[test] fn test_enroll_admin_password_reset() { - use base64::{engine::general_purpose::STANDARD, Engine}; - let client = Client::new(None); let master_key = MasterKey::derive( @@ -844,9 +841,12 @@ mod tests { let encrypted = enroll_admin_password_reset(&client, public_key.to_owned().into()).unwrap(); let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; - let private_key = STANDARD.decode(private_key).unwrap(); - let private_key = Pkcs8PrivateKeyBytes::from(private_key); - let private_key = AsymmetricCryptoKey::from_der(&private_key).unwrap(); + let private_key = AsymmetricCryptoKey::from_der( + &Base64String::from(private_key.to_string()) + .try_into() + .unwrap(), + ) + .unwrap(); let decrypted: SymmetricCryptoKey = encrypted.decapsulate_key_unsigned(&private_key).unwrap(); From e3ee2795585c7634f8272239338f9ec73867e209 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 25 Jun 2025 14:15:02 +0200 Subject: [PATCH 089/124] Cleanup cose sign1 types --- .../bitwarden-crypto/src/keys/signed_public_key.rs | 12 ++++++------ crates/bitwarden-wasm-internal/src/pure_crypto.rs | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/bitwarden-crypto/src/keys/signed_public_key.rs b/crates/bitwarden-crypto/src/keys/signed_public_key.rs index ff4fa3fb8..f6fe2128a 100644 --- a/crates/bitwarden-crypto/src/keys/signed_public_key.rs +++ b/crates/bitwarden-crypto/src/keys/signed_public_key.rs @@ -77,15 +77,15 @@ impl SignedPublicKeyMessage { #[derive(Clone, Debug)] pub struct SignedPublicKey(pub(crate) SignedObject); -impl From for Vec { +impl From for CoseSign1Bytes { fn from(val: SignedPublicKey) -> Self { - val.0.to_cose().to_vec() + val.0.to_cose() } } -impl TryFrom> for SignedPublicKey { +impl TryFrom for SignedPublicKey { type Error = EncodingError; - fn try_from(bytes: Vec) -> Result { + fn try_from(bytes: CoseSign1Bytes) -> Result { Ok(SignedPublicKey(SignedObject::from_cose( &CoseSign1Bytes::from(bytes), )?)) @@ -94,7 +94,7 @@ impl TryFrom> for SignedPublicKey { impl From for String { fn from(val: SignedPublicKey) -> Self { - let bytes: Vec = val.into(); + let bytes: CoseSign1Bytes = val.into(); STANDARD.encode(&bytes) } } @@ -130,7 +130,7 @@ impl FromStr for SignedPublicKey { let bytes = STANDARD .decode(s) .map_err(|_| EncodingError::InvalidCborSerialization)?; - Self::try_from(bytes) + Self::try_from(CoseSign1Bytes::from(bytes)) } } diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 196cca947..66c6cfd8b 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -3,10 +3,10 @@ use std::str::FromStr; use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ AsymmetricCryptoKey, AsymmetricPublicCryptoKey, BitwardenLegacyKeyBytes, CoseKeyBytes, - CoseSerializable, CryptoError, Decryptable, EncString, Kdf, KeyDecryptable, KeyEncryptable, - KeyStore, MasterKey, OctetStreamBytes, Pkcs8PrivateKeyBytes, PrimitiveEncryptable, - SignatureAlgorithm, SignedPublicKey, SigningKey, SpkiPublicKeyBytes, SymmetricCryptoKey, - UnsignedSharedKey, VerifyingKey, + CoseSerializable, CoseSign1Bytes, CryptoError, Decryptable, EncString, Kdf, KeyDecryptable, + KeyEncryptable, KeyStore, MasterKey, OctetStreamBytes, Pkcs8PrivateKeyBytes, + PrimitiveEncryptable, SignatureAlgorithm, SignedPublicKey, SigningKey, SpkiPublicKeyBytes, + SymmetricCryptoKey, UnsignedSharedKey, VerifyingKey, }; use wasm_bindgen::prelude::*; @@ -304,7 +304,7 @@ impl PureCrypto { signed_public_key: Vec, verifying_key: Vec, ) -> Result, CryptoError> { - let signed_public_key = SignedPublicKey::try_from(signed_public_key)?; + let signed_public_key = SignedPublicKey::try_from(CoseSign1Bytes::from(signed_public_key))?; let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(verifying_key))?; signed_public_key .verify_and_unwrap(&verifying_key) From bf7bc829e183862ec0a4e48ff39ce875aec7eadb Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 12:27:05 +0200 Subject: [PATCH 090/124] Revert "Typesafe base64 handling" This reverts commit 25d1907eb746f1c5c046a1243a314186628c0d7b. --- crates/bitwarden-core/src/auth/auth_client.rs | 5 +-- .../bitwarden-core/src/auth/auth_request.rs | 38 +++++++--------- crates/bitwarden-core/src/auth/tde.rs | 7 +-- .../src/key_management/crypto.rs | 13 +++--- .../src/key_management/crypto_client.rs | 2 +- crates/bitwarden-core/src/lib.rs | 2 - crates/bitwarden-core/src/types.rs | 45 ------------------- crates/bitwarden-crypto/src/fingerprint.rs | 2 +- .../src/keys/asymmetric_crypto_key.rs | 7 ++- crates/bitwarden-crypto/src/keys/pin_key.rs | 2 +- .../src/keys/signed_public_key.rs | 8 ++-- crates/bitwarden-uniffi/src/auth/mod.rs | 4 +- .../src/pure_crypto.rs | 3 +- 13 files changed, 41 insertions(+), 97 deletions(-) delete mode 100644 crates/bitwarden-core/src/types.rs diff --git a/crates/bitwarden-core/src/auth/auth_client.rs b/crates/bitwarden-core/src/auth/auth_client.rs index 589df586d..6164a87a3 100644 --- a/crates/bitwarden-core/src/auth/auth_client.rs +++ b/crates/bitwarden-core/src/auth/auth_client.rs @@ -26,7 +26,6 @@ use crate::{ RegisterRequest, }, client::encryption_settings::EncryptionSettingsError, - Base64String, }; use crate::{ auth::{login::LoginError, renew::renew_token}, @@ -90,7 +89,7 @@ impl AuthClient { pub fn make_register_tde_keys( &self, email: String, - org_public_key: Base64String, + org_public_key: String, remember_device: bool, ) -> Result { make_register_tde_keys(&self.client, email, org_public_key, remember_device) @@ -173,7 +172,7 @@ impl AuthClient { #[allow(missing_docs)] pub fn approve_auth_request( &self, - public_key: Base64String, + public_key: String, ) -> Result { approve_auth_request(&self.client, public_key) } diff --git a/crates/bitwarden-core/src/auth/auth_request.rs b/crates/bitwarden-core/src/auth/auth_request.rs index 3476dcd4e..521e60c85 100644 --- a/crates/bitwarden-core/src/auth/auth_request.rs +++ b/crates/bitwarden-core/src/auth/auth_request.rs @@ -8,7 +8,7 @@ use bitwarden_crypto::{EncString, SymmetricCryptoKey}; use thiserror::Error; #[cfg(feature = "internal")] -use crate::{client::encryption_settings::EncryptionSettingsError, Base64String}; +use crate::client::encryption_settings::EncryptionSettingsError; use crate::{key_management::SymmetricKeyId, Client, VaultLockedError}; /// Response for `new_auth_request`. @@ -49,25 +49,25 @@ pub(crate) fn new_auth_request(email: &str) -> Result Result { - let private_key = AsymmetricCryptoKey::from_der(&private_key.try_into()?)?; - let key: SymmetricCryptoKey = user_key.decapsulate_key_unsigned(&private_key)?; + let key = AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key)?.into())?; + let key: SymmetricCryptoKey = user_key.decapsulate_key_unsigned(&key)?; Ok(key) } /// Decrypt the user key using the private key generated previously. #[cfg(feature = "internal")] pub(crate) fn auth_request_decrypt_master_key( - private_key: Base64String, + private_key: String, master_key: UnsignedSharedKey, user_key: EncString, ) -> Result { use bitwarden_crypto::MasterKey; - let private_key = AsymmetricCryptoKey::from_der(&private_key.try_into()?)?; - let master_key: SymmetricCryptoKey = master_key.decapsulate_key_unsigned(&private_key)?; + let key = AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key)?.into())?; + let master_key: SymmetricCryptoKey = master_key.decapsulate_key_unsigned(&key)?; let master_key = MasterKey::try_from(&master_key)?; Ok(master_key.decrypt_user_key(user_key)?) @@ -89,9 +89,9 @@ pub enum ApproveAuthRequestError { /// Encrypts the user key with a public key. pub(crate) fn approve_auth_request( client: &Client, - public_key: Base64String, + public_key: String, ) -> Result { - let public_key = AsymmetricPublicCryptoKey::from_der(&public_key.try_into()?)?; + let public_key = AsymmetricPublicCryptoKey::from_der(&STANDARD.decode(public_key)?)?; let key_store = client.internal.get_key_store(); let ctx = key_store.context(); @@ -140,8 +140,7 @@ mod tests { ) .unwrap(); - let decrypted = - auth_request_decrypt_user_key(request.private_key.into(), encrypted).unwrap(); + let decrypted = auth_request_decrypt_user_key(request.private_key, encrypted).unwrap(); assert_eq!(decrypted.to_encoded().to_vec(), secret.to_vec()); } @@ -174,7 +173,7 @@ mod tests { let fingerprint = fingerprint("test@bitwarden.com", &pubkey).unwrap(); assert_eq!(fingerprint, "childless-unfair-prowler-dropbox-designate"); - approve_auth_request(&client, public_key.to_owned().into()).unwrap(); + approve_auth_request(&client, public_key.to_owned()).unwrap(); } #[tokio::test] @@ -182,8 +181,7 @@ mod tests { let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; let enc_user_key = "4.dxbd5OMwi/Avy7DQxvLV+Z7kDJgHBtg/jAbgYNO7QU0Zii4rLFNco2lS5aS9z42LTZHc2p5HYwn2ZwkZNfHsQ6//d5q40MDgGYJMKBXOZP62ZHhct1XsvYBmtcUtIOm5j2HSjt2pjEuGAc1LbyGIWRJJQ3Lp1ULbL2m71I+P23GF36JyOM8SUWvpvxE/3+qqVhRFPG2VqMCYa2kLLxwVfUmpV+KKjX1TXsrq6pfJIwHNwHw4h7MSfD8xTy2bx4MiBt638Z9Vt1pGsSQkh9RgPvCbnhuCpZQloUgJ8ByLVEcrlKx3yaaxiQXvte+ZhuOI7rGdjmoVoOzisooje4JgYw==".parse().unwrap(); - let dec = - auth_request_decrypt_user_key(private_key.to_owned().into(), enc_user_key).unwrap(); + let dec = auth_request_decrypt_user_key(private_key.to_owned(), enc_user_key).unwrap(); assert_eq!( &dec.to_encoded().to_vec(), @@ -200,12 +198,9 @@ mod tests { let enc_master_key = "4.dxbd5OMwi/Avy7DQxvLV+Z7kDJgHBtg/jAbgYNO7QU0Zii4rLFNco2lS5aS9z42LTZHc2p5HYwn2ZwkZNfHsQ6//d5q40MDgGYJMKBXOZP62ZHhct1XsvYBmtcUtIOm5j2HSjt2pjEuGAc1LbyGIWRJJQ3Lp1ULbL2m71I+P23GF36JyOM8SUWvpvxE/3+qqVhRFPG2VqMCYa2kLLxwVfUmpV+KKjX1TXsrq6pfJIwHNwHw4h7MSfD8xTy2bx4MiBt638Z9Vt1pGsSQkh9RgPvCbnhuCpZQloUgJ8ByLVEcrlKx3yaaxiQXvte+ZhuOI7rGdjmoVoOzisooje4JgYw==".parse().unwrap(); let enc_user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); - let dec = auth_request_decrypt_master_key( - private_key.to_owned().into(), - enc_master_key, - enc_user_key, - ) - .unwrap(); + let dec = + auth_request_decrypt_master_key(private_key.to_owned(), enc_master_key, enc_user_key) + .unwrap(); assert_eq!( &dec.to_encoded().to_vec(), @@ -243,8 +238,7 @@ mod tests { // Initialize an auth request, and approve it on the existing device let auth_req = new_auth_request(email).unwrap(); - let approved_req = - approve_auth_request(&existing_device, auth_req.public_key.into()).unwrap(); + let approved_req = approve_auth_request(&existing_device, auth_req.public_key).unwrap(); // Unlock the vault using the approved request new_device diff --git a/crates/bitwarden-core/src/auth/tde.rs b/crates/bitwarden-core/src/auth/tde.rs index 1c1b9250d..570a28fd2 100644 --- a/crates/bitwarden-core/src/auth/tde.rs +++ b/crates/bitwarden-core/src/auth/tde.rs @@ -1,9 +1,10 @@ +use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ AsymmetricPublicCryptoKey, DeviceKey, EncString, Kdf, SymmetricCryptoKey, TrustDeviceResponse, UnsignedSharedKey, UserKey, }; -use crate::{client::encryption_settings::EncryptionSettingsError, Base64String, Client}; +use crate::{client::encryption_settings::EncryptionSettingsError, Client}; /// This function generates a new user key and key pair, initializes the client's crypto with the /// generated user key, and encrypts the user key with the organization public key for admin @@ -11,10 +12,10 @@ use crate::{client::encryption_settings::EncryptionSettingsError, Base64String, pub(super) fn make_register_tde_keys( client: &Client, email: String, - org_public_key: Base64String, + org_public_key: String, remember_device: bool, ) -> Result { - let public_key = AsymmetricPublicCryptoKey::from_der(&org_public_key.try_into()?)?; + let public_key = AsymmetricPublicCryptoKey::from_der(&STANDARD.decode(org_public_key)?)?; let user_key = UserKey::new(SymmetricCryptoKey::make_aes256_cbc_hmac_key()); let key_pair = user_key.make_key_pair()?; diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index bab793c35..b002b799c 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -21,7 +21,7 @@ use {tsify_next::Tsify, wasm_bindgen::prelude::*}; use crate::{ client::{encryption_settings::EncryptionSettingsError, LoginMethod, UserLoginMethod}, key_management::{AsymmetricKeyId, SigningKeyId, SymmetricKeyId}, - Base64String, Client, NotAuthenticatedError, VaultLockedError, WrongPasswordError, + Client, NotAuthenticatedError, VaultLockedError, WrongPasswordError, }; /// Catch all error for mobile crypto operations. @@ -177,13 +177,13 @@ pub(super) async fn initialize_user_crypto( } => { let user_key = match method { AuthRequestMethod::UserKey { protected_user_key } => { - auth_request_decrypt_user_key(request_private_key.into(), protected_user_key)? + auth_request_decrypt_user_key(request_private_key, protected_user_key)? } AuthRequestMethod::MasterKey { protected_master_key, auth_request_key, } => auth_request_decrypt_master_key( - request_private_key.into(), + request_private_key, protected_master_key, auth_request_key, )?, @@ -407,11 +407,12 @@ pub enum EnrollAdminPasswordResetError { pub(super) fn enroll_admin_password_reset( client: &Client, - public_key: Base64String, + public_key: String, ) -> Result { + use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::AsymmetricPublicCryptoKey; - let public_key = AsymmetricPublicCryptoKey::from_der(&public_key.try_into()?)?; + let public_key = AsymmetricPublicCryptoKey::from_der(&STANDARD.decode(public_key)?)?; let key_store = client.internal.get_key_store(); let ctx = key_store.context(); // FIXME: [PM-18110] This should be removed once the key store can handle public key encryption @@ -838,7 +839,7 @@ mod tests { let public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsy7RFHcX3C8Q4/OMmhhbFReYWfB45W9PDTEA8tUZwZmtOiN2RErIS2M1c+K/4HoDJ/TjpbX1f2MZcr4nWvKFuqnZXyewFc+jmvKVewYi+NAu2++vqKq2kKcmMNhwoQDQdQIVy/Uqlp4Cpi2cIwO6ogq5nHNJGR3jm+CpyrafYlbz1bPvL3hbyoGDuG2tgADhyhXUdFuef2oF3wMvn1lAJAvJnPYpMiXUFmj1ejmbwtlxZDrHgUJvUcp7nYdwUKaFoi+sOttHn3u7eZPtNvxMjhSS/X/1xBIzP/mKNLdywH5LoRxniokUk+fV3PYUxJsiU3lV0Trc/tH46jqd8ZGjmwIDAQAB"; - let encrypted = enroll_admin_password_reset(&client, public_key.to_owned().into()).unwrap(); + let encrypted = enroll_admin_password_reset(&client, public_key.to_owned()).unwrap(); let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; let private_key = AsymmetricCryptoKey::from_der( diff --git a/crates/bitwarden-core/src/key_management/crypto_client.rs b/crates/bitwarden-core/src/key_management/crypto_client.rs index d06169f8b..03390939a 100644 --- a/crates/bitwarden-core/src/key_management/crypto_client.rs +++ b/crates/bitwarden-core/src/key_management/crypto_client.rs @@ -109,7 +109,7 @@ impl CryptoClient { &self, public_key: String, ) -> Result { - enroll_admin_password_reset(&self.client, public_key.into()) + enroll_admin_password_reset(&self.client, public_key) } /// Derive the master key for migrating to the key connector diff --git a/crates/bitwarden-core/src/lib.rs b/crates/bitwarden-core/src/lib.rs index f0a61e6ae..18885fa1f 100644 --- a/crates/bitwarden-core/src/lib.rs +++ b/crates/bitwarden-core/src/lib.rs @@ -28,5 +28,3 @@ pub use client::{Client, ClientSettings, DeviceType}; mod ids; pub use ids::*; -mod types; -pub use types::*; diff --git a/crates/bitwarden-core/src/types.rs b/crates/bitwarden-core/src/types.rs deleted file mode 100644 index 3074a20e5..000000000 --- a/crates/bitwarden-core/src/types.rs +++ /dev/null @@ -1,45 +0,0 @@ -use base64::{engine::general_purpose::STANDARD, Engine}; -use bitwarden_crypto::{Pkcs8PrivateKeyBytes, SpkiPublicKeyBytes}; - -/// A wrapper around a Base64-encoded string that can be used to decode it into a byte vector. -/// This is useful for handling Base64-encoded strings in a type-safe manner, -/// ensuring that the string is always treated as Base64 data. -pub struct Base64String(String); - -impl From for Base64String { - fn from(val: String) -> Self { - Base64String(val) - } -} - -impl TryInto> for Base64String { - type Error = base64::DecodeError; - - fn try_into(self) -> Result, Self::Error> { - STANDARD.decode(&self.0) - } -} - -impl From> for Base64String { - fn from(val: Vec) -> Self { - Base64String(STANDARD.encode(val)) - } -} - -impl TryInto for Base64String { - type Error = base64::DecodeError; - - fn try_into(self) -> Result { - let bytes: Vec = self.try_into()?; - Ok(SpkiPublicKeyBytes::from(bytes)) - } -} - -impl TryInto for Base64String { - type Error = base64::DecodeError; - - fn try_into(self) -> Result { - let bytes: Vec = self.try_into()?; - Ok(Pkcs8PrivateKeyBytes::from(bytes)) - } -} diff --git a/crates/bitwarden-crypto/src/fingerprint.rs b/crates/bitwarden-crypto/src/fingerprint.rs index 22307e977..84d127612 100644 --- a/crates/bitwarden-crypto/src/fingerprint.rs +++ b/crates/bitwarden-crypto/src/fingerprint.rs @@ -64,7 +64,7 @@ pub enum FingerprintError { #[cfg(test)] mod tests { use super::fingerprint; - use crate::SpkiPublicKeyBytes; + use crate::{Bytes, SpkiPublicKeyBytes}; #[test] fn test_fingerprint() { diff --git a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs index f13f64bac..14a400992 100644 --- a/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/asymmetric_crypto_key.rs @@ -35,11 +35,10 @@ impl AsymmetricPublicCryptoKey { } /// Build a public key from the SubjectPublicKeyInfo DER. - pub fn from_der(der: &SpkiPublicKeyBytes) -> Result { + pub fn from_der(der: &[u8]) -> Result { Ok(AsymmetricPublicCryptoKey { inner: RawPublicKey::RsaOaepSha1( - RsaPublicKey::from_public_key_der(der.as_ref()) - .map_err(|_| CryptoError::InvalidKey)?, + RsaPublicKey::from_public_key_der(der).map_err(|_| CryptoError::InvalidKey)?, ), }) } @@ -264,7 +263,7 @@ DnqOsltgPomWZ7xVfMkm9niL2OA= let private_key = Pkcs8PrivateKeyBytes::from(private_key); let private_key = AsymmetricCryptoKey::from_der(&private_key).unwrap(); - let public_key = AsymmetricPublicCryptoKey::from_der(&public_key.into()).unwrap(); + let public_key = AsymmetricPublicCryptoKey::from_der(&public_key).unwrap(); let raw_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let encrypted = UnsignedSharedKey::encapsulate_key_unsigned(&raw_key, &public_key).unwrap(); diff --git a/crates/bitwarden-crypto/src/keys/pin_key.rs b/crates/bitwarden-crypto/src/keys/pin_key.rs index a1503a5ad..e29c7cc3a 100644 --- a/crates/bitwarden-crypto/src/keys/pin_key.rs +++ b/crates/bitwarden-crypto/src/keys/pin_key.rs @@ -20,7 +20,7 @@ impl PinKey { /// Encrypt the users user key pub fn encrypt_user_key(&self, user_key: &SymmetricCryptoKey) -> Result { - user_key.encrypt_with_key(self) + user_key.encrypt_with_key(&self) } /// Decrypt the users user key diff --git a/crates/bitwarden-crypto/src/keys/signed_public_key.rs b/crates/bitwarden-crypto/src/keys/signed_public_key.rs index f6fe2128a..25407e219 100644 --- a/crates/bitwarden-crypto/src/keys/signed_public_key.rs +++ b/crates/bitwarden-crypto/src/keys/signed_public_key.rs @@ -13,7 +13,7 @@ use super::AsymmetricPublicCryptoKey; use crate::{ cose::CoseSerializable, error::EncodingError, util::FromStrVisitor, CoseSign1Bytes, CryptoError, PublicKeyEncryptionAlgorithm, RawPublicKey, SignedObject, SigningKey, - SigningNamespace, SpkiPublicKeyBytes, VerifyingKey, + SigningNamespace, VerifyingKey, }; #[cfg(feature = "wasm")] @@ -114,10 +114,8 @@ impl SignedPublicKey { public_key_message.content_format, ) { (PublicKeyEncryptionAlgorithm::RsaOaepSha1, PublicKeyFormat::Spki) => Ok( - AsymmetricPublicCryptoKey::from_der(&SpkiPublicKeyBytes::from( - public_key_message.public_key.into_vec(), - )) - .map_err(|_| EncodingError::InvalidValue("public key"))?, + AsymmetricPublicCryptoKey::from_der(&public_key_message.public_key.into_vec()) + .map_err(|_| EncodingError::InvalidValue("public key"))?, ), } } diff --git a/crates/bitwarden-uniffi/src/auth/mod.rs b/crates/bitwarden-uniffi/src/auth/mod.rs index 3d36612ea..5f90b1673 100644 --- a/crates/bitwarden-uniffi/src/auth/mod.rs +++ b/crates/bitwarden-uniffi/src/auth/mod.rs @@ -73,7 +73,7 @@ impl AuthClient { Ok(self .0 .auth() - .make_register_tde_keys(email, org_public_key.into(), remember_device) + .make_register_tde_keys(email, org_public_key, remember_device) .map_err(Error::EncryptionSettings)?) } @@ -146,7 +146,7 @@ impl AuthClient { Ok(self .0 .auth() - .approve_auth_request(public_key.into()) + .approve_auth_request(public_key) .map_err(Error::ApproveAuthRequest)?) } diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 66c6cfd8b..13ab4da05 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -251,8 +251,7 @@ impl PureCrypto { shared_key: Vec, encapsulation_key: Vec, ) -> Result { - let encapsulation_key = - AsymmetricPublicCryptoKey::from_der(&SpkiPublicKeyBytes::from(encapsulation_key))?; + let encapsulation_key = AsymmetricPublicCryptoKey::from_der(encapsulation_key.as_slice())?; Ok(UnsignedSharedKey::encapsulate_key_unsigned( &SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(shared_key))?, &encapsulation_key, From 1da017372450f116677df2243a39f756bcd3051e Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 12:27:43 +0200 Subject: [PATCH 091/124] Revert "Cleanup" This reverts commit 0d0f59b8dfd7c654ef7ef0f85656622d8711caa0. --- crates/bitwarden-core/src/key_management/crypto.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index b002b799c..33477761f 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -10,7 +10,8 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ AsymmetricCryptoKey, CoseSerializable, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, Pkcs8PrivateKeyBytes, PrimitiveEncryptable, SignatureAlgorithm, - SignedPublicKey, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey, + SignedPublicKey, SigningKey, SpkiPublicKeyBytes, SymmetricCryptoKey, UnsignedSharedKey, + UserKey, }; use bitwarden_error::bitwarden_error; use schemars::JsonSchema; @@ -819,6 +820,8 @@ mod tests { #[test] fn test_enroll_admin_password_reset() { + use base64::{engine::general_purpose::STANDARD, Engine}; + let client = Client::new(None); let master_key = MasterKey::derive( @@ -842,12 +845,9 @@ mod tests { let encrypted = enroll_admin_password_reset(&client, public_key.to_owned()).unwrap(); let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; - let private_key = AsymmetricCryptoKey::from_der( - &Base64String::from(private_key.to_string()) - .try_into() - .unwrap(), - ) - .unwrap(); + let private_key = STANDARD.decode(private_key).unwrap(); + let private_key = Pkcs8PrivateKeyBytes::from(private_key); + let private_key = AsymmetricCryptoKey::from_der(&private_key).unwrap(); let decrypted: SymmetricCryptoKey = encrypted.decapsulate_key_unsigned(&private_key).unwrap(); From 87f87a63ea59933b254c922fd769cc3d54f66a22 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 12:29:13 +0200 Subject: [PATCH 092/124] Cleanup --- crates/bitwarden-core/src/key_management/crypto.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index 33477761f..a76ea4294 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -10,8 +10,7 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ AsymmetricCryptoKey, CoseSerializable, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, Pkcs8PrivateKeyBytes, PrimitiveEncryptable, SignatureAlgorithm, - SignedPublicKey, SigningKey, SpkiPublicKeyBytes, SymmetricCryptoKey, UnsignedSharedKey, - UserKey, + SignedPublicKey, SigningKey, SymmetricCryptoKey, UnsignedSharedKey, UserKey, }; use bitwarden_error::bitwarden_error; use schemars::JsonSchema; From 15ffca922a6af1c9cb4be679fcd540cbec67baf6 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 12:31:06 +0200 Subject: [PATCH 093/124] Clippy fix --- crates/bitwarden-crypto/src/fingerprint.rs | 2 +- crates/bitwarden-crypto/src/keys/pin_key.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bitwarden-crypto/src/fingerprint.rs b/crates/bitwarden-crypto/src/fingerprint.rs index 84d127612..22307e977 100644 --- a/crates/bitwarden-crypto/src/fingerprint.rs +++ b/crates/bitwarden-crypto/src/fingerprint.rs @@ -64,7 +64,7 @@ pub enum FingerprintError { #[cfg(test)] mod tests { use super::fingerprint; - use crate::{Bytes, SpkiPublicKeyBytes}; + use crate::SpkiPublicKeyBytes; #[test] fn test_fingerprint() { diff --git a/crates/bitwarden-crypto/src/keys/pin_key.rs b/crates/bitwarden-crypto/src/keys/pin_key.rs index e29c7cc3a..a1503a5ad 100644 --- a/crates/bitwarden-crypto/src/keys/pin_key.rs +++ b/crates/bitwarden-crypto/src/keys/pin_key.rs @@ -20,7 +20,7 @@ impl PinKey { /// Encrypt the users user key pub fn encrypt_user_key(&self, user_key: &SymmetricCryptoKey) -> Result { - user_key.encrypt_with_key(&self) + user_key.encrypt_with_key(self) } /// Decrypt the users user key From 144620fa92bdf9f7e699b7645d4b368d672bf848 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 13:02:41 +0200 Subject: [PATCH 094/124] Move use under internal flag --- crates/bitwarden-core/src/client/encryption_settings.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index 4fc576083..f02d95ee6 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -1,6 +1,5 @@ -use bitwarden_crypto::{CoseKeyBytes, Pkcs8PrivateKeyBytes}; #[cfg(feature = "internal")] -use bitwarden_crypto::{EncString, UnsignedSharedKey}; +use bitwarden_crypto::{CoseKeyBytes, EncString, Pkcs8PrivateKeyBytes, UnsignedSharedKey}; #[cfg(any(feature = "internal", feature = "secrets"))] use bitwarden_crypto::{KeyStore, SymmetricCryptoKey}; use bitwarden_error::bitwarden_error; From 81b5a15678da1953c86f9f2d1b8dc2c6c8dc240b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 17:14:46 +0200 Subject: [PATCH 095/124] Update crates/bitwarden-core/src/client/encryption_settings.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-core/src/client/encryption_settings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index f02d95ee6..343c707ac 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -61,7 +61,7 @@ impl EncryptionSettings { let dec: Vec = private_key.decrypt_with_key(&user_key)?; // FIXME: [PM-11690] - Temporarily ignore invalid private keys until we have a recovery // process in place. - AsymmetricCryptoKey::from_der(&Pkcs8PrivateKeyBytes::from(dec)) + AsymmetricCryptoKey::from_der(&dec.into()) .map_err(|_| { warn!("Invalid private key"); }) From 94276344594d113293ac0331c9eea05245ebe58e Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 17:14:54 +0200 Subject: [PATCH 096/124] Update crates/bitwarden-core/src/client/encryption_settings.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-core/src/client/encryption_settings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index 343c707ac..3eea43e91 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -77,7 +77,7 @@ impl EncryptionSettings { use bitwarden_crypto::CryptoError; let dec: Vec = key.decrypt_with_key(&user_key)?; - SigningKey::from_cose(&CoseKeyBytes::from(dec)).map_err(Into::::into) + SigningKey::from_cose(&dec.into()).map_err(Into::::into) }) .transpose()?; From d1f8029d24eed872256be9bfd6b1af46d3e453ab Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 18:20:38 +0200 Subject: [PATCH 097/124] Move cose content format trait higher --- crates/bitwarden-crypto/src/content_format.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-crypto/src/content_format.rs b/crates/bitwarden-crypto/src/content_format.rs index 8c2c98ff1..1f44dc65c 100644 --- a/crates/bitwarden-crypto/src/content_format.rs +++ b/crates/bitwarden-crypto/src/content_format.rs @@ -143,6 +143,9 @@ impl ConstContentFormat for SpkiPublicKeyDerContentFormat { /// for SPKI public keys in DER format. pub type SpkiPublicKeyBytes = Bytes; +/// A marker trait for COSE content formats. +pub trait CoseContentFormat {} + /// Content format for COSE keys. #[derive(PartialEq, Eq, Clone, Debug)] pub struct CoseKeyContentFormat; @@ -188,9 +191,6 @@ impl CoseContentFormat for CoseSign1ContentFormat {} /// serialized COSE Sign1 messages. pub type CoseSign1Bytes = Bytes; -/// A marker trait for COSE content formats. -pub trait CoseContentFormat {} - impl PrimitiveEncryptable for Bytes { From 7f52fb0464478ce4abfaaec9a7cea8ce71ba4d8d Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 18:39:41 +0200 Subject: [PATCH 098/124] Add docs --- crates/bitwarden-crypto/src/keys/key_encryptable.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/keys/key_encryptable.rs b/crates/bitwarden-crypto/src/keys/key_encryptable.rs index f0667125d..c3516f656 100644 --- a/crates/bitwarden-crypto/src/keys/key_encryptable.rs +++ b/crates/bitwarden-crypto/src/keys/key_encryptable.rs @@ -19,11 +19,22 @@ impl KeyContainer for Arc { #[allow(missing_docs)] pub trait CryptoKey {} -#[allow(missing_docs)] +/// An encryption operation that takes the input value and encrypts it into the output value +/// using a key reference. Implementing this requires a content type to be specified in +/// the implementation. pub trait KeyEncryptable { + /// Encrypts a value using the provided key reference. fn encrypt_with_key(self, key: &Key) -> Result; } +/// An encryption operation that takes the input value and encrypts it into the output value +/// using a key reference, with an externally provided content type. +/// +/// In contrast to `KeyEncryptable`, this trait allows the caller to specify the content format. +/// Because of this, it is not exposed outside of the crate, because outside callers should +/// not make a choice about the content format. Where possible, the content format is +/// ensured at compile time by the type system, not at runtime by the caller passing +/// in a parameter. pub(crate) trait KeyEncryptableWithContentType { fn encrypt_with_key(self, key: &Key, content_format: ContentFormat) -> Result; } From 53c528b74eb6bcf960925febc7810891456aa1e4 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 18:41:04 +0200 Subject: [PATCH 099/124] Update crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index add3cfadf..893b96d4e 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -150,7 +150,7 @@ impl SymmetricCryptoKey { match encoded_key { EncodedSymmetricKey::LegacyNonCoseKey(_) => { let encoded_key: Vec = encoded_key.into(); - Bytes::from(encoded_key) + BitwardenLegacyKeyBytes::from(encoded_key) } EncodedSymmetricKey::CoseKey(_) => { let mut encoded_key: Vec = encoded_key.into(); From c51e779e53268b56f90a4c02bdd995a82e518784 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 18:41:14 +0200 Subject: [PATCH 100/124] Update crates/bitwarden-crypto/src/keys/device_key.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-crypto/src/keys/device_key.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index 3e09b2884..7b5319a4b 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -65,7 +65,7 @@ impl DeviceKey { protected_user_key: UnsignedSharedKey, ) -> Result { let device_private_key: Vec = protected_device_private_key.decrypt_with_key(&self.0)?; - let device_private_key: Pkcs8PrivateKeyBytes = Bytes::from(device_private_key); + let device_private_key = Pkcs8PrivateKeyBytes::from(device_private_key); let device_private_key = AsymmetricCryptoKey::from_der(&device_private_key)?; let user_key: SymmetricCryptoKey = From 6c8092ab336ac74ddd83804f048ce59e57888449 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 18:41:27 +0200 Subject: [PATCH 101/124] Update crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs Co-authored-by: Oscar Hinton --- crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index 893b96d4e..8268c5dd5 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -155,7 +155,7 @@ impl SymmetricCryptoKey { EncodedSymmetricKey::CoseKey(_) => { let mut encoded_key: Vec = encoded_key.into(); pad_key(&mut encoded_key, Self::AES256_CBC_HMAC_KEY_LEN + 1); - Bytes::from(encoded_key) + BitwardenLegacyKeyBytes::from(encoded_key) } } } From 954a6a510f90d50fe03269a635f691f46edb04c5 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 18:45:05 +0200 Subject: [PATCH 102/124] Clean up symmetric crypto key --- .../src/keys/symmetric_crypto_key.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index add3cfadf..ac9dfb245 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -148,7 +148,7 @@ impl SymmetricCryptoKey { pub fn to_encoded(&self) -> BitwardenLegacyKeyBytes { let encoded_key = self.to_encoded_raw(); match encoded_key { - EncodedSymmetricKey::LegacyNonCoseKey(_) => { + EncodedSymmetricKey::BitwardenLegacyKey(_) => { let encoded_key: Vec = encoded_key.into(); Bytes::from(encoded_key) } @@ -189,13 +189,13 @@ impl SymmetricCryptoKey { pub(crate) fn to_encoded_raw(&self) -> EncodedSymmetricKey { match self { Self::Aes256CbcKey(key) => { - EncodedSymmetricKey::LegacyNonCoseKey(key.enc_key.to_vec().into()) + EncodedSymmetricKey::BitwardenLegacyKey(key.enc_key.to_vec().into()) } Self::Aes256CbcHmacKey(key) => { let mut buf = Vec::with_capacity(64); buf.extend_from_slice(&key.enc_key); buf.extend_from_slice(&key.mac_key); - EncodedSymmetricKey::LegacyNonCoseKey(buf.into()) + EncodedSymmetricKey::BitwardenLegacyKey(buf.into()) } Self::XChaCha20Poly1305Key(key) => { let builder = coset::CoseKeyBuilder::new_symmetric_key(key.enc_key.to_vec()); @@ -281,7 +281,7 @@ impl TryFrom<&BitwardenLegacyKeyBytes> for SymmetricCryptoKey { let result = if slice.len() == Self::AES256_CBC_HMAC_KEY_LEN || slice.len() == Self::AES256_CBC_KEY_LEN { - Self::try_from(EncodedSymmetricKey::LegacyNonCoseKey(value.clone())) + Self::try_from(EncodedSymmetricKey::BitwardenLegacyKey(value.clone())) } else if slice.len() > Self::AES256_CBC_HMAC_KEY_LEN { let unpadded_value = unpad_key(slice)?; Ok(Self::try_from_cose(unpadded_value)?) @@ -298,14 +298,14 @@ impl TryFrom for SymmetricCryptoKey { fn try_from(value: EncodedSymmetricKey) -> Result { match value { - EncodedSymmetricKey::LegacyNonCoseKey(key) + EncodedSymmetricKey::BitwardenLegacyKey(key) if key.as_ref().len() == Self::AES256_CBC_KEY_LEN => { let mut enc_key = Box::pin(GenericArray::::default()); enc_key.copy_from_slice(&key.as_ref()[..Self::AES256_CBC_KEY_LEN]); Ok(Self::Aes256CbcKey(Aes256CbcKey { enc_key })) } - EncodedSymmetricKey::LegacyNonCoseKey(key) + EncodedSymmetricKey::BitwardenLegacyKey(key) if key.as_ref().len() == Self::AES256_CBC_HMAC_KEY_LEN => { let mut enc_key = Box::pin(GenericArray::::default()); @@ -391,17 +391,17 @@ fn unpad_key(key_bytes: &[u8]) -> Result<&[u8], CryptoError> { crate::keys::utils::unpad_bytes(key_bytes).map_err(|_| CryptoError::InvalidKey) } -/// An enum to represent the different encodings of symmetric crypto keys. +/// Encoded representation of [SymmetricCryptoKey] pub enum EncodedSymmetricKey { /// An Aes256-CBC-HMAC key, or a Aes256-CBC key - LegacyNonCoseKey(BitwardenLegacyKeyBytes), + BitwardenLegacyKey(BitwardenLegacyKeyBytes), /// A symmetric key encoded as a COSE key CoseKey(CoseKeyBytes), } impl From for Vec { fn from(val: EncodedSymmetricKey) -> Self { match val { - EncodedSymmetricKey::LegacyNonCoseKey(key) => key.to_vec(), + EncodedSymmetricKey::BitwardenLegacyKey(key) => key.to_vec(), EncodedSymmetricKey::CoseKey(key) => key.to_vec(), } } @@ -410,7 +410,7 @@ impl EncodedSymmetricKey { #[allow(private_interfaces)] pub fn content_format(&self) -> ContentFormat { match self { - EncodedSymmetricKey::LegacyNonCoseKey(_) => ContentFormat::BitwardenLegacyKey, + EncodedSymmetricKey::BitwardenLegacyKey(_) => ContentFormat::BitwardenLegacyKey, EncodedSymmetricKey::CoseKey(_) => ContentFormat::CoseKey, } } From b2f521105108cc1df65e6fed991ed912e44b14e1 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 19:03:34 +0200 Subject: [PATCH 103/124] Fix encryptable docs --- crates/bitwarden-crypto/src/traits/encryptable.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index 8a065af48..bdbe53392 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -8,9 +8,14 @@ //! Encrypting data directly, a content type must be provided, since an encrypted byte array alone //! is not enough to tell the decryption code how to interpret the decrypted bytes. For this, there //! are two traits, `PrimitiveEncryptable` and `PrimitiveEncryptableWithContentType`. The former -//! assumes that the caller provides the content format when encrypting. The latter is a convenience -//! trait that pre-defines the content format based on the type of data that is encrypted. This is -//! currently done exactly for `String` and `&str`, which are encrypted as UTF-8, +//! assumes that the implementation provides content format when encrypting, based on the type +//! of struct that is being encrypted. The latter allows the caller to specify the content format +//! at runtime, which is only allowed within the crypto crate. +//! +//! `PrimitiveEncryptable` is implemented for `crate::content_format::Bytes` types, where `C` is +//! a type that implements the `ConstContentFormat` trait. This allows for compile-time type +//! checking of the content format, and the risk of using the wrong content format is limited to +//! converting untyped bytes into a `Bytes` type use crate::{store::KeyStoreContext, ContentFormat, CryptoError, EncString, KeyId, KeyIds}; From f0b6ec5a455baafab70000c39f8d60d71c38ac40 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 19:03:38 +0200 Subject: [PATCH 104/124] Cleanup --- crates/bitwarden-core/src/client/encryption_settings.rs | 2 +- crates/bitwarden-crypto/src/keys/device_key.rs | 4 ++-- crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index 3eea43e91..3610f0054 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -1,5 +1,5 @@ #[cfg(feature = "internal")] -use bitwarden_crypto::{CoseKeyBytes, EncString, Pkcs8PrivateKeyBytes, UnsignedSharedKey}; +use bitwarden_crypto::{EncString, UnsignedSharedKey}; #[cfg(any(feature = "internal", feature = "secrets"))] use bitwarden_crypto::{KeyStore, SymmetricCryptoKey}; use bitwarden_error::bitwarden_error; diff --git a/crates/bitwarden-crypto/src/keys/device_key.rs b/crates/bitwarden-crypto/src/keys/device_key.rs index 7b5319a4b..b7ba7a9c0 100644 --- a/crates/bitwarden-crypto/src/keys/device_key.rs +++ b/crates/bitwarden-crypto/src/keys/device_key.rs @@ -1,7 +1,7 @@ use super::{AsymmetricCryptoKey, PublicKeyEncryptionAlgorithm}; use crate::{ - content_format::Bytes, error::Result, CryptoError, EncString, KeyDecryptable, KeyEncryptable, - Pkcs8PrivateKeyBytes, SymmetricCryptoKey, UnsignedSharedKey, + error::Result, CryptoError, EncString, KeyDecryptable, KeyEncryptable, Pkcs8PrivateKeyBytes, + SymmetricCryptoKey, UnsignedSharedKey, }; /// Device Key diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index 1c5b87597..de2b54a8a 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -18,7 +18,7 @@ use super::{ key_encryptable::CryptoKey, key_id::{KeyId, KEY_ID_SIZE}, }; -use crate::{cose, BitwardenLegacyKeyBytes, Bytes, ContentFormat, CoseKeyBytes, CryptoError}; +use crate::{cose, BitwardenLegacyKeyBytes, ContentFormat, CoseKeyBytes, CryptoError}; /// [Aes256CbcKey] is a symmetric encryption key, consisting of one 256-bit key, /// used to decrypt legacy type 0 enc strings. The data is not authenticated From 390463cc5b95062559e60a6e51999695c6b24413 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 26 Jun 2025 19:04:28 +0200 Subject: [PATCH 105/124] Small cleanup --- crates/bitwarden-crypto/src/traits/encryptable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/traits/encryptable.rs b/crates/bitwarden-crypto/src/traits/encryptable.rs index bdbe53392..654a1fa90 100644 --- a/crates/bitwarden-crypto/src/traits/encryptable.rs +++ b/crates/bitwarden-crypto/src/traits/encryptable.rs @@ -15,7 +15,7 @@ //! `PrimitiveEncryptable` is implemented for `crate::content_format::Bytes` types, where `C` is //! a type that implements the `ConstContentFormat` trait. This allows for compile-time type //! checking of the content format, and the risk of using the wrong content format is limited to -//! converting untyped bytes into a `Bytes` type +//! converting untyped bytes into a `Bytes` use crate::{store::KeyStoreContext, ContentFormat, CryptoError, EncString, KeyId, KeyIds}; From a2e243e99007f6ebcdd13a495d795105a6aaa3ff Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 27 Jun 2025 16:48:01 +0200 Subject: [PATCH 106/124] Implement password-protected key envelope --- .../examples/protect_key_with_password.rs | 102 ++++ crates/bitwarden-crypto/src/cose.rs | 8 +- crates/bitwarden-crypto/src/keys/mod.rs | 2 +- .../src/keys/symmetric_crypto_key.rs | 1 + crates/bitwarden-crypto/src/lib.rs | 2 + crates/bitwarden-crypto/src/safe/mod.rs | 2 + .../safe/password_protected_key_envelope.rs | 446 ++++++++++++++++++ crates/bitwarden-crypto/src/store/context.rs | 11 + 8 files changed, 572 insertions(+), 2 deletions(-) create mode 100644 crates/bitwarden-crypto/examples/protect_key_with_password.rs create mode 100644 crates/bitwarden-crypto/src/safe/mod.rs create mode 100644 crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs diff --git a/crates/bitwarden-crypto/examples/protect_key_with_password.rs b/crates/bitwarden-crypto/examples/protect_key_with_password.rs new file mode 100644 index 000000000..d8e95f486 --- /dev/null +++ b/crates/bitwarden-crypto/examples/protect_key_with_password.rs @@ -0,0 +1,102 @@ +//! This example demonstrates how to securely protect keys with a password using the [PasswordProtectedKeyEnvelope]. + +use bitwarden_crypto::{ + key_ids, KeyStore, KeyStoreContext, PasswordProtectedKeyEnvelope, + PasswordProtectedKeyEnvelopeError, +}; + +fn main() { + let key_story = KeyStore::::default(); + let mut ctx: KeyStoreContext<'_, ExampleIds> = key_story.context_mut(); + let mut disk = MockDisk::new(); + + // Alice wants to protect a key with a password. + // For example to: + // - Protect her vault with a pin + // - Protect her exported vault with a password + // - Protect a send with a URL fragment secret + // For this, the `PasswordProtectedKeyEnvelope` is used. + + // Alice has a vault protected with a symmetric key. She wants this protected with a PIN. + let vault_key = ctx + .generate_symmetric_key(ExampleSymmetricKey::VaultKey) + .unwrap(); + + // Seal the key with the PIN + // The KDF settings are chosen for you, and do not need to be separately tracked or synced + // Next, story this protected key envelope on disk. + let pin = "1234"; + let envelope = + PasswordProtectedKeyEnvelope::seal(vault_key, pin, &ctx).expect("Sealing should work"); + disk.save("vault_key_envelope", (&envelope).try_into().unwrap()); + + // Wipe the context to simulate new session + ctx.clear_local(); + + // Load the envelope from disk and unseal it with the PIN, and store it in the context. + let deserialized: PasswordProtectedKeyEnvelope = + PasswordProtectedKeyEnvelope::try_from(disk.load("vault_key_envelope").unwrap()).unwrap(); + deserialized + .unseal(ExampleSymmetricKey::VaultKey, pin, &mut ctx) + .unwrap(); + + // Alice wants to change her password; also her KDF settings are below the minimums. + // Re-sealing will update the password, and KDF settings. + let envelope = envelope + .reseal(pin, "0000") + .expect("The password should be valid"); + disk.save("vault_key_envelope", (&envelope).try_into().unwrap()); + + // Alice wants to change the protected key. This requires creating a new envelope + ctx.generate_symmetric_key(ExampleSymmetricKey::VaultKey) + .unwrap(); + let envelope = PasswordProtectedKeyEnvelope::seal(ExampleSymmetricKey::VaultKey, "0000", &ctx) + .expect("Sealing should work"); + disk.save("vault_key_envelope", (&envelope).try_into().unwrap()); + + // Alice tries the password but it is wrong + assert!(matches!( + envelope.unseal(ExampleSymmetricKey::VaultKey, "9999", &mut ctx), + Err(PasswordProtectedKeyEnvelopeError::WrongPassword) + )); +} + +pub(crate) struct MockDisk { + map: std::collections::HashMap>, +} + +impl MockDisk { + pub(crate) fn new() -> Self { + MockDisk { + map: std::collections::HashMap::new(), + } + } + + pub(crate) fn save(&mut self, key: &str, value: Vec) { + self.map.insert(key.to_string(), value); + } + + pub(crate) fn load(&self, key: &str) -> Option<&Vec> { + self.map.get(key) + } +} + +key_ids! { + #[symmetric] + pub enum ExampleSymmetricKey { + #[local] + VaultKey + } + + #[asymmetric] + pub enum ExampleAsymmetricKey { + Key(u8), + } + + #[signing] + pub enum ExampleSigningKey { + Key(u8), + } + + pub ExampleIds => ExampleSymmetricKey, ExampleAsymmetricKey, ExampleSigningKey; +} diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index b2a39ab56..67f4990b3 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -22,10 +22,16 @@ use crate::{ pub(crate) const XCHACHA20_POLY1305: i64 = -70000; const XCHACHA20_TEXT_PAD_BLOCK_SIZE: usize = 32; +pub(crate) const ALG_ARGON2ID13: i64 = -71000; +pub(crate) const ARGON2_SALT: i64 = -71001; +pub(crate) const ARGON2_ITERATIONS: i64 = -71002; +pub(crate) const ARGON2_MEMORY: i64 = -71003; +pub(crate) const ARGON2_PARALLELISM: i64 = -71004; + // Note: These are in the "unregistered" tree: https://datatracker.ietf.org/doc/html/rfc6838#section-3.4 // These are only used within Bitwarden, and not meant for exchange with other systems. const CONTENT_TYPE_PADDED_UTF8: &str = "application/x.bitwarden.utf8-padded"; -const CONTENT_TYPE_BITWARDEN_LEGACY_KEY: &str = "application/x.bitwarden.legacy-key"; +pub(crate) const CONTENT_TYPE_BITWARDEN_LEGACY_KEY: &str = "application/x.bitwarden.legacy-key"; const CONTENT_TYPE_SPKI_PUBLIC_KEY: &str = "application/x.bitwarden.spki-public-key"; // Labels diff --git a/crates/bitwarden-crypto/src/keys/mod.rs b/crates/bitwarden-crypto/src/keys/mod.rs index c5dd18bb6..7d63dd31e 100644 --- a/crates/bitwarden-crypto/src/keys/mod.rs +++ b/crates/bitwarden-crypto/src/keys/mod.rs @@ -9,7 +9,7 @@ mod symmetric_crypto_key; #[cfg(test)] pub use symmetric_crypto_key::derive_symmetric_key; pub use symmetric_crypto_key::{ - Aes256CbcHmacKey, Aes256CbcKey, SymmetricCryptoKey, XChaCha20Poly1305Key, + Aes256CbcHmacKey, Aes256CbcKey, EncodedSymmetricKey, SymmetricCryptoKey, XChaCha20Poly1305Key, }; mod asymmetric_crypto_key; pub use asymmetric_crypto_key::{ diff --git a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs index de2b54a8a..4fcdeac09 100644 --- a/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs +++ b/crates/bitwarden-crypto/src/keys/symmetric_crypto_key.rs @@ -407,6 +407,7 @@ impl From for Vec { } } impl EncodedSymmetricKey { + /// Returns the content format of the encoded symmetric key. #[allow(private_interfaces)] pub fn content_format(&self) -> ContentFormat { match self { diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index 60428d8a4..a60ab7ace 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -34,6 +34,8 @@ mod store; pub use store::{KeyStore, KeyStoreContext}; mod cose; pub use cose::CoseSerializable; +mod safe; +pub use safe::*; mod signing; pub use signing::*; mod traits; diff --git a/crates/bitwarden-crypto/src/safe/mod.rs b/crates/bitwarden-crypto/src/safe/mod.rs new file mode 100644 index 000000000..fa863bc19 --- /dev/null +++ b/crates/bitwarden-crypto/src/safe/mod.rs @@ -0,0 +1,2 @@ +mod password_protected_key_envelope; +pub use password_protected_key_envelope::*; diff --git a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs new file mode 100644 index 000000000..272f7e4be --- /dev/null +++ b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs @@ -0,0 +1,446 @@ +use std::marker::PhantomData; + +use ciborium::{value::Integer, Value}; +use coset::{ + iana::CoapContentFormat, CborSerializable, ContentType, CoseError, Header, HeaderBuilder, Label, +}; +use rand::RngCore; +use thiserror::Error; + +use crate::{ + cose::{ + ALG_ARGON2ID13, ARGON2_ITERATIONS, ARGON2_MEMORY, ARGON2_PARALLELISM, ARGON2_SALT, + CONTENT_TYPE_BITWARDEN_LEGACY_KEY, + }, + xchacha20, BitwardenLegacyKeyBytes, ContentFormat, CoseKeyBytes, EncodedSymmetricKey, KeyIds, + KeyStoreContext, SymmetricCryptoKey, +}; + +/// A password-protected key envelope can seal a symmetric key, and protect it with a password. It does so +/// by using a Key Derivation Function (KDF), to increase the difficulty of brute-forcing the password. +/// +/// The KDF parameters such as iterations and salt are stored in the key-envelope and do not have to be provided. +pub struct PasswordProtectedKeyEnvelope { + _phantom: PhantomData, + cose_encrypt: coset::CoseEncrypt, +} + +impl PasswordProtectedKeyEnvelope { + /// Seals a symmetric key with a password, using the current default KDF parameters and a random salt. + /// + /// This should never fail, except for memory allocation error, when running the KDF. + pub fn seal( + key_to_seal: Ids::Symmetric, + password: &str, + ctx: &KeyStoreContext, + ) -> Result { + #[allow(deprecated)] + let key_ref = ctx + .dangerous_get_symmetric_key(key_to_seal) + .expect("Key should exist in the key store"); + Self::seal_ref(&key_ref, password) + } + + fn seal_ref( + key_to_seal: &SymmetricCryptoKey, + password: &str, + ) -> Result { + let kdf = Argon2RawSettings { + iterations: 3, + memory: 64 * 1024, // 64 MiB + parallelism: 4, + salt: make_salt(), + }; + Self::seal_ref_with_settings(key_to_seal, password, &kdf) + } + + fn seal_ref_with_settings( + key_to_seal: &SymmetricCryptoKey, + password: &str, + kdf_settings: &Argon2RawSettings, + ) -> Result { + // Cose does not yet have a standardized way to protect a key using a password. + // This implements content encryption using direct encryption with a KDF derived key, similar to + // "Direct Key with KDF". The KDF settings are placed in a single recipient struct. + + // The envelope key is directly derived from the KDF and used as the key to encrypt the key that should + // be sealed. + let envelope_key = derive_key(kdf_settings, password) + .map_err(|_| PasswordProtectedKeyEnvelopeError::KdfError)?; + + #[allow(deprecated)] + let (content_format, key_to_seal_bytes) = match key_to_seal.to_encoded_raw() { + EncodedSymmetricKey::BitwardenLegacyKey(key_bytes) => { + (ContentFormat::BitwardenLegacyKey, key_bytes.to_vec()) + } + EncodedSymmetricKey::CoseKey(key_bytes) => (ContentFormat::CoseKey, key_bytes.to_vec()), + }; + + let mut nonce = [0u8; crate::xchacha20::NONCE_SIZE]; + let mut cose_encrypt = coset::CoseEncryptBuilder::new() + .add_recipient({ + let mut recipient = coset::CoseRecipientBuilder::new() + .unprotected(kdf_settings.into()) + .build(); + recipient.protected.header.alg = Some(coset::Algorithm::PrivateUse(ALG_ARGON2ID13)); + recipient + }) + .protected(HeaderBuilder::from(content_format).build()) + .create_ciphertext(&key_to_seal_bytes, &[], |data, aad| { + let ciphertext = xchacha20::encrypt_xchacha20_poly1305(&envelope_key, data, aad); + nonce.copy_from_slice(&ciphertext.nonce()); + ciphertext.encrypted_bytes().to_vec() + }) + .build(); + cose_encrypt.unprotected.iv = nonce.into(); + + Ok(PasswordProtectedKeyEnvelope { + _phantom: PhantomData, + cose_encrypt, + }) + } + + /// Unseals a symmetric key from the password-protected envelope, and stores it in the key store context. + pub fn unseal( + &self, + target_keyslot: Ids::Symmetric, + password: &str, + ctx: &mut KeyStoreContext, + ) -> Result { + let key = self.unseal_ref(password)?; + #[allow(deprecated)] + ctx.set_symmetric_key(target_keyslot, key).unwrap(); + Ok(target_keyslot) + } + + fn unseal_ref( + &self, + password: &str, + ) -> Result { + // There must be exactly one recipient in the COSE Encrypt object, which contains the KDF parameters. + if self.cose_encrypt.recipients.len() != 1 { + return Err(PasswordProtectedKeyEnvelopeError::ParsingError( + "Invalid number of recipients".to_string(), + )); + } + + let recipient = self + .cose_encrypt + .recipients + .get(0) + .expect("Recipient should exist"); + if recipient.protected.header.alg != Some(coset::Algorithm::PrivateUse(ALG_ARGON2ID13)) { + return Err(PasswordProtectedKeyEnvelopeError::ParsingError( + "Unknown or unsupported KDF algorithm".to_string(), + )); + } + + let kdf_settings: Argon2RawSettings = + recipient.unprotected.clone().try_into().map_err(|_| { + PasswordProtectedKeyEnvelopeError::ParsingError( + "Invalid or missing KDF parameters".to_string(), + ) + })?; + let envelope_key = derive_key(&kdf_settings, password) + .map_err(|_| PasswordProtectedKeyEnvelopeError::KdfError)?; + + let key_bytes = self + .cose_encrypt + .decrypt(&[], |data, aad| { + xchacha20::decrypt_xchacha20_poly1305( + &self.cose_encrypt.unprotected.iv.clone().try_into().unwrap(), + &envelope_key, + data, + aad, + ) + }) + .map_err(|_| PasswordProtectedKeyEnvelopeError::WrongPassword)?; + + let key = SymmetricCryptoKey::try_from( + match self.cose_encrypt.protected.header.content_type.as_ref() { + Some(ContentType::Text(format)) if format == CONTENT_TYPE_BITWARDEN_LEGACY_KEY => { + EncodedSymmetricKey::BitwardenLegacyKey(BitwardenLegacyKeyBytes::from( + key_bytes, + )) + } + Some(ContentType::Assigned(CoapContentFormat::CoseKey)) => { + EncodedSymmetricKey::CoseKey(CoseKeyBytes::from(key_bytes)) + } + _ => { + return Err(PasswordProtectedKeyEnvelopeError::ParsingError( + "Unknown or unsupported content format".to_string(), + )); + } + }, + ) + .unwrap(); + Ok(key) + } + + /// Re-seals the key with new KDF parameters (updated settings, salt), and a new password + pub fn reseal( + &self, + password: &str, + new_password: &str, + ) -> Result { + let unsealed = self.unseal_ref(password)?; + Self::seal_ref(&unsealed, new_password) + } +} + +impl TryInto> for &PasswordProtectedKeyEnvelope { + type Error = CoseError; + + fn try_into(self) -> Result, Self::Error> { + self.cose_encrypt.clone().to_vec() + } +} + +impl TryFrom<&Vec> for PasswordProtectedKeyEnvelope { + type Error = CoseError; + + fn try_from(value: &Vec) -> Result { + let cose_encrypt = coset::CoseEncrypt::from_slice(&value)?; + Ok(PasswordProtectedKeyEnvelope { + _phantom: PhantomData, + cose_encrypt, + }) + } +} + +/// Raw argon2 settings differ from the KDF struct defined for existing master-password unlock. +/// The memory is represented in kibibytes (KiB) instead of mebibytes (MiB), and the salt is a fixed size of 32 bytes, +/// and randomly generated, instead of being derived from the email. +struct Argon2RawSettings { + iterations: u32, + memory: u32, + parallelism: u32, + salt: [u8; 32], +} + +impl Into
for &Argon2RawSettings { + fn into(self) -> Header { + let builder = HeaderBuilder::new() + .value(ARGON2_ITERATIONS, Integer::from(self.iterations).into()) + .value(ARGON2_MEMORY, Integer::from(self.memory).into()) + .value(ARGON2_PARALLELISM, Integer::from(self.parallelism).into()) + .value(ARGON2_SALT, Value::from(self.salt.to_vec())); + + let mut header = builder.build(); + header.alg = Some(coset::Algorithm::PrivateUse(ALG_ARGON2ID13)); + header + } +} + +impl TryInto for Header { + type Error = PasswordProtectedKeyEnvelopeError; + + fn try_into(self) -> Result { + let iterations = self + .rest + .iter() + .find_map(|(label, value)| match (label, value) { + (Label::Int(ARGON2_ITERATIONS), ciborium::Value::Integer(value)) => Some(value), + _ => None, + }) + .ok_or(PasswordProtectedKeyEnvelopeError::ParsingError( + "Missing Argon2 iterations".to_string(), + ))?; + let memory = self + .rest + .iter() + .find_map(|(label, value)| match (label, value) { + (Label::Int(ARGON2_MEMORY), ciborium::Value::Integer(value)) => Some(value), + _ => None, + }) + .ok_or(PasswordProtectedKeyEnvelopeError::ParsingError( + "Missing Argon2 memory".to_string(), + ))?; + let parallelism = self + .rest + .iter() + .find_map(|(label, value)| match (label, value) { + (Label::Int(ARGON2_PARALLELISM), ciborium::Value::Integer(value)) => Some(value), + _ => None, + }) + .ok_or(PasswordProtectedKeyEnvelopeError::ParsingError( + "Missing Argon2 parallelism".to_string(), + ))?; + let salt: [u8; 32] = self + .rest + .iter() + .find_map(|(label, value)| match (label, value) { + (Label::Int(ARGON2_SALT), ciborium::Value::Bytes(value)) if value.len() == 32 => { + Some(value.as_slice().try_into().unwrap()) + } + _ => None, + }) + .ok_or(PasswordProtectedKeyEnvelopeError::ParsingError( + "Missing or invalid Argon2 salt".to_string(), + ))?; + + Ok(Argon2RawSettings { + iterations: i128::from(*iterations).try_into().unwrap(), + memory: i128::from(*memory).try_into().unwrap(), + parallelism: i128::from(*parallelism).try_into().unwrap(), + salt, + }) + } +} + +fn make_salt() -> [u8; 32] { + let mut salt = [0u8; 32]; + rand::thread_rng().fill_bytes(&mut salt); + salt +} + +fn derive_key( + argon2_settings: &Argon2RawSettings, + password: &str, +) -> Result<[u8; 32], crate::CryptoError> { + use argon2::*; + + let params = Params::new( + argon2_settings.memory, + argon2_settings.iterations, + argon2_settings.parallelism, + Some(32), + )?; + let argon = Argon2::new(Algorithm::Argon2id, Version::V0x13, params); + + let mut hash = [0u8; 32]; + argon.hash_password_into(password.as_bytes(), &argon2_settings.salt, &mut hash)?; + Ok(hash) +} + +/// Errors that can occur when sealing or unsealing a key with the `PasswordProtectedKeyEnvelope`. +#[derive(Debug, Error)] +pub enum PasswordProtectedKeyEnvelopeError { + /// The password provided is incorrect or the envelope was tampered with + #[error("Wrong password")] + WrongPassword, + /// The envelope could not be parsed correctly, or the KDF parameters are invalid + #[error("Parsing error {0}")] + ParsingError(String), + /// The KDF failed to derive a key, possibly due to invalid parameters or memory allocation issues + #[error("Kdf error")] + KdfError, +} + +#[cfg(test)] +mod tests { + use crate::{ + traits::tests::{TestIds, TestSymmKey}, + KeyStore, + }; + + use super::*; + + #[test] + fn test_make_envelope() { + let key_store = KeyStore::::default(); + let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut(); + let test_key = ctx.make_cose_symmetric_key(TestSymmKey::A(0)).unwrap(); + + let password = "test_password"; + + // Seal the key with a password + let envelope = PasswordProtectedKeyEnvelope::seal(test_key, password, &ctx).unwrap(); + let serialized: Vec = (&envelope).try_into().unwrap(); + + // Unseal the key from the envelope + let deserialized: PasswordProtectedKeyEnvelope = + PasswordProtectedKeyEnvelope::try_from(&serialized).unwrap(); + deserialized + .unseal(TestSymmKey::A(1), password, &mut ctx) + .unwrap(); + + // Verify that the unsealed key matches the original key + #[allow(deprecated)] + let unsealed_key = ctx + .dangerous_get_symmetric_key(TestSymmKey::A(1)) + .expect("Key should exist in the key store"); + + #[allow(deprecated)] + let key_before_sealing = ctx + .dangerous_get_symmetric_key(test_key) + .expect("Key should exist in the key store"); + + assert_eq!(unsealed_key, key_before_sealing); + } + + #[test] + fn test_make_envelope_legacy_key() { + let key_store = KeyStore::::default(); + let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut(); + let test_key = ctx.generate_symmetric_key(TestSymmKey::A(0)).unwrap(); + + let password = "test_password"; + + // Seal the key with a password + let envelope = PasswordProtectedKeyEnvelope::seal(test_key, password, &ctx).unwrap(); + let serialized: Vec = (&envelope).try_into().unwrap(); + + // Unseal the key from the envelope + let deserialized: PasswordProtectedKeyEnvelope = + PasswordProtectedKeyEnvelope::try_from(&serialized).unwrap(); + deserialized + .unseal(TestSymmKey::A(1), password, &mut ctx) + .unwrap(); + + // Verify that the unsealed key matches the original key + #[allow(deprecated)] + let unsealed_key = ctx + .dangerous_get_symmetric_key(TestSymmKey::A(1)) + .expect("Key should exist in the key store"); + + #[allow(deprecated)] + let key_before_sealing = ctx + .dangerous_get_symmetric_key(test_key) + .expect("Key should exist in the key store"); + + assert_eq!(unsealed_key, key_before_sealing); + } + + #[test] + fn test_reseal_envelope() { + let key = SymmetricCryptoKey::make_xchacha20_poly1305_key(); + let password = "test_password"; + let new_password = "new_test_password"; + + // Seal the key with a password + let envelope: PasswordProtectedKeyEnvelope = + PasswordProtectedKeyEnvelope::seal_ref(&key, password).expect("Sealing should work"); + // Reseal + let envelope = envelope + .reseal(password, new_password) + .expect("Resealing should work"); + let unsealed = envelope + .unseal_ref(new_password) + .expect("Unsealing should work"); + + // Verify that the unsealed key matches the original key + assert_eq!(unsealed, key); + } + + #[test] + fn test_wrong_password() { + let key_store = KeyStore::::default(); + let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut(); + let test_key = ctx.make_cose_symmetric_key(TestSymmKey::A(0)).unwrap(); + + let password = "test_password"; + let wrong_password = "wrong_password"; + + // Seal the key with a password + let envelope = PasswordProtectedKeyEnvelope::seal(test_key, password, &ctx).unwrap(); + + // Attempt to unseal with the wrong password + let deserialized: PasswordProtectedKeyEnvelope = + PasswordProtectedKeyEnvelope::try_from(&(&envelope).try_into().unwrap()).unwrap(); + assert!(matches!( + deserialized.unseal(TestSymmKey::A(1), wrong_password, &mut ctx), + Err(PasswordProtectedKeyEnvelopeError::WrongPassword) + )); + } +} diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 9675cfb5e..84e2dc47a 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -308,6 +308,17 @@ impl KeyStoreContext<'_, Ids> { Ok(key_id) } + /// Generate a new random xchacha20-poly1305 symmetric key and store it in the context + pub(crate) fn make_cose_symmetric_key( + &mut self, + key_id: Ids::Symmetric, + ) -> Result { + let key = SymmetricCryptoKey::make_xchacha20_poly1305_key(); + #[allow(deprecated)] + self.set_symmetric_key(key_id, key)?; + Ok(key_id) + } + /// Generate a new signature key using the current default algorithm, and store it in the /// context pub fn make_signing_key(&mut self, key_id: Ids::Signing) -> Result { From 616786e9845b2c2be8b6f374f7b568b4259cff7b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 27 Jun 2025 18:23:42 +0200 Subject: [PATCH 107/124] Add cfg(test) to function --- crates/bitwarden-crypto/src/store/context.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 84e2dc47a..2f6b4dfa9 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -309,6 +309,7 @@ impl KeyStoreContext<'_, Ids> { } /// Generate a new random xchacha20-poly1305 symmetric key and store it in the context + #[cfg(test)] pub(crate) fn make_cose_symmetric_key( &mut self, key_id: Ids::Symmetric, From b9137626747128b4281549472e43bb12a44e922b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 27 Jun 2025 18:31:27 +0200 Subject: [PATCH 108/124] Cargo format --- .../examples/protect_key_with_password.rs | 3 +- .../safe/password_protected_key_envelope.rs | 36 +++++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/crates/bitwarden-crypto/examples/protect_key_with_password.rs b/crates/bitwarden-crypto/examples/protect_key_with_password.rs index d8e95f486..b3fd4f5a2 100644 --- a/crates/bitwarden-crypto/examples/protect_key_with_password.rs +++ b/crates/bitwarden-crypto/examples/protect_key_with_password.rs @@ -1,4 +1,5 @@ -//! This example demonstrates how to securely protect keys with a password using the [PasswordProtectedKeyEnvelope]. +//! This example demonstrates how to securely protect keys with a password using the +//! [PasswordProtectedKeyEnvelope]. use bitwarden_crypto::{ key_ids, KeyStore, KeyStoreContext, PasswordProtectedKeyEnvelope, diff --git a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs index 272f7e4be..6a9fde860 100644 --- a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs +++ b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs @@ -16,17 +16,20 @@ use crate::{ KeyStoreContext, SymmetricCryptoKey, }; -/// A password-protected key envelope can seal a symmetric key, and protect it with a password. It does so -/// by using a Key Derivation Function (KDF), to increase the difficulty of brute-forcing the password. +/// A password-protected key envelope can seal a symmetric key, and protect it with a password. It +/// does so by using a Key Derivation Function (KDF), to increase the difficulty of brute-forcing +/// the password. /// -/// The KDF parameters such as iterations and salt are stored in the key-envelope and do not have to be provided. +/// The KDF parameters such as iterations and salt are stored in the key-envelope and do not have to +/// be provided. pub struct PasswordProtectedKeyEnvelope { _phantom: PhantomData, cose_encrypt: coset::CoseEncrypt, } impl PasswordProtectedKeyEnvelope { - /// Seals a symmetric key with a password, using the current default KDF parameters and a random salt. + /// Seals a symmetric key with a password, using the current default KDF parameters and a random + /// salt. /// /// This should never fail, except for memory allocation error, when running the KDF. pub fn seal( @@ -60,11 +63,12 @@ impl PasswordProtectedKeyEnvelope { kdf_settings: &Argon2RawSettings, ) -> Result { // Cose does not yet have a standardized way to protect a key using a password. - // This implements content encryption using direct encryption with a KDF derived key, similar to - // "Direct Key with KDF". The KDF settings are placed in a single recipient struct. + // This implements content encryption using direct encryption with a KDF derived key, + // similar to "Direct Key with KDF". The KDF settings are placed in a single + // recipient struct. - // The envelope key is directly derived from the KDF and used as the key to encrypt the key that should - // be sealed. + // The envelope key is directly derived from the KDF and used as the key to encrypt the key + // that should be sealed. let envelope_key = derive_key(kdf_settings, password) .map_err(|_| PasswordProtectedKeyEnvelopeError::KdfError)?; @@ -100,7 +104,8 @@ impl PasswordProtectedKeyEnvelope { }) } - /// Unseals a symmetric key from the password-protected envelope, and stores it in the key store context. + /// Unseals a symmetric key from the password-protected envelope, and stores it in the key store + /// context. pub fn unseal( &self, target_keyslot: Ids::Symmetric, @@ -117,7 +122,8 @@ impl PasswordProtectedKeyEnvelope { &self, password: &str, ) -> Result { - // There must be exactly one recipient in the COSE Encrypt object, which contains the KDF parameters. + // There must be exactly one recipient in the COSE Encrypt object, which contains the KDF + // parameters. if self.cose_encrypt.recipients.len() != 1 { return Err(PasswordProtectedKeyEnvelopeError::ParsingError( "Invalid number of recipients".to_string(), @@ -209,8 +215,8 @@ impl TryFrom<&Vec> for PasswordProtectedKeyEnvelope { } /// Raw argon2 settings differ from the KDF struct defined for existing master-password unlock. -/// The memory is represented in kibibytes (KiB) instead of mebibytes (MiB), and the salt is a fixed size of 32 bytes, -/// and randomly generated, instead of being derived from the email. +/// The memory is represented in kibibytes (KiB) instead of mebibytes (MiB), and the salt is a fixed +/// size of 32 bytes, and randomly generated, instead of being derived from the email. struct Argon2RawSettings { iterations: u32, memory: u32, @@ -322,20 +328,20 @@ pub enum PasswordProtectedKeyEnvelopeError { /// The envelope could not be parsed correctly, or the KDF parameters are invalid #[error("Parsing error {0}")] ParsingError(String), - /// The KDF failed to derive a key, possibly due to invalid parameters or memory allocation issues + /// The KDF failed to derive a key, possibly due to invalid parameters or memory allocation + /// issues #[error("Kdf error")] KdfError, } #[cfg(test)] mod tests { + use super::*; use crate::{ traits::tests::{TestIds, TestSymmKey}, KeyStore, }; - use super::*; - #[test] fn test_make_envelope() { let key_store = KeyStore::::default(); From 120f8c695e41013fe953c0a3fc759ab57aa06f9b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 27 Jun 2025 18:36:46 +0200 Subject: [PATCH 109/124] Fix clippy errors --- .../src/safe/password_protected_key_envelope.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs index 6a9fde860..bb5162d55 100644 --- a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs +++ b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs @@ -286,9 +286,19 @@ impl TryInto for Header { ))?; Ok(Argon2RawSettings { - iterations: i128::from(*iterations).try_into().unwrap(), - memory: i128::from(*memory).try_into().unwrap(), - parallelism: i128::from(*parallelism).try_into().unwrap(), + iterations: i128::from(*iterations).try_into().map_err(|_| { + PasswordProtectedKeyEnvelopeError::ParsingError( + "Invalid Argon2 iterations".to_string(), + ) + })?, + memory: i128::from(*memory).try_into().map_err(|_| { + PasswordProtectedKeyEnvelopeError::ParsingError("Invalid Argon2 memory".to_string()) + })?, + parallelism: i128::from(*parallelism).try_into().map_err(|_| { + PasswordProtectedKeyEnvelopeError::ParsingError( + "Invalid Argon2 parallelism".to_string(), + ) + })?, salt, }) } From 25700ef7c9f42a3d72c4b01a63f37228363ffd84 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 7 Jul 2025 13:38:52 +0200 Subject: [PATCH 110/124] Fix private const --- crates/bitwarden-crypto/src/cose.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 01076328f..67f4990b3 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -31,7 +31,7 @@ pub(crate) const ARGON2_PARALLELISM: i64 = -71004; // Note: These are in the "unregistered" tree: https://datatracker.ietf.org/doc/html/rfc6838#section-3.4 // These are only used within Bitwarden, and not meant for exchange with other systems. const CONTENT_TYPE_PADDED_UTF8: &str = "application/x.bitwarden.utf8-padded"; -const CONTENT_TYPE_BITWARDEN_LEGACY_KEY: &str = "application/x.bitwarden.legacy-key"; +pub(crate) const CONTENT_TYPE_BITWARDEN_LEGACY_KEY: &str = "application/x.bitwarden.legacy-key"; const CONTENT_TYPE_SPKI_PUBLIC_KEY: &str = "application/x.bitwarden.spki-public-key"; // Labels From 690c6dfa6bf7de9dbe7952ee3af9dd258f3c5f54 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 29 Jul 2025 14:12:19 +0200 Subject: [PATCH 111/124] Cleanup and add tests --- .vscode/settings.json | 1 + .../examples/protect_key_with_password.rs | 5 +- crates/bitwarden-crypto/src/cose.rs | 49 ++- crates/bitwarden-crypto/src/lib.rs | 3 +- crates/bitwarden-crypto/src/safe/README.md | 12 + crates/bitwarden-crypto/src/safe/mod.rs | 2 + .../safe/password_protected_key_envelope.rs | 287 ++++++++++++------ 7 files changed, 260 insertions(+), 99 deletions(-) create mode 100644 crates/bitwarden-crypto/src/safe/README.md diff --git a/.vscode/settings.json b/.vscode/settings.json index 0df252c66..dd8928521 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,6 +15,7 @@ "encryptable", "Hkdf", "Hmac", + "keyslot", "Maybeable", "Oaep", "Pbkdf", diff --git a/crates/bitwarden-crypto/examples/protect_key_with_password.rs b/crates/bitwarden-crypto/examples/protect_key_with_password.rs index b3fd4f5a2..216d91b39 100644 --- a/crates/bitwarden-crypto/examples/protect_key_with_password.rs +++ b/crates/bitwarden-crypto/examples/protect_key_with_password.rs @@ -2,8 +2,9 @@ //! [PasswordProtectedKeyEnvelope]. use bitwarden_crypto::{ - key_ids, KeyStore, KeyStoreContext, PasswordProtectedKeyEnvelope, - PasswordProtectedKeyEnvelopeError, + key_ids, + safe::{PasswordProtectedKeyEnvelope, PasswordProtectedKeyEnvelopeError}, + KeyStore, KeyStoreContext, }; fn main() { diff --git a/crates/bitwarden-crypto/src/cose.rs b/crates/bitwarden-crypto/src/cose.rs index 67f4990b3..b3cfa23c5 100644 --- a/crates/bitwarden-crypto/src/cose.rs +++ b/crates/bitwarden-crypto/src/cose.rs @@ -5,9 +5,10 @@ use coset::{ iana::{self, CoapContentFormat}, - CborSerializable, ContentType, Label, + CborSerializable, ContentType, Header, Label, }; use generic_array::GenericArray; +use thiserror::Error; use typenum::U32; use crate::{ @@ -227,6 +228,52 @@ pub trait CoseSerializable { where Self: Sized; } + +pub(crate) fn extract_integer( + header: &Header, + target_label: i64, + value_name: &str, +) -> Result { + Ok(header + .rest + .iter() + .find_map(|(label, value)| match (label, value) { + (Label::Int(label_value), ciborium::Value::Integer(int_value)) + if *label_value == target_label => + { + Some(*int_value) + } + _ => None, + }) + .ok_or(CoseExtractError::MissingValue(value_name.to_string()))? + .into()) +} + +pub(crate) fn extract_bytes( + header: &Header, + target_label: i64, + value_name: &str, +) -> Result, CoseExtractError> { + header + .rest + .iter() + .find_map(|(label, value)| match (label, value) { + (Label::Int(label_value), ciborium::Value::Bytes(byte_value)) + if *label_value == target_label => + { + Some(byte_value.clone()) + } + _ => None, + }) + .ok_or(CoseExtractError::MissingValue(value_name.to_string())) +} + +#[derive(Debug, Error)] +pub(crate) enum CoseExtractError { + #[error("Missing value {0}")] + MissingValue(String), +} + #[cfg(test)] mod test { use super::*; diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index e9dcbfaa4..80766b41e 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -36,8 +36,7 @@ pub use store::{ }; mod cose; pub use cose::CoseSerializable; -mod safe; -pub use safe::*; +pub mod safe; mod signing; pub use signing::*; mod traits; diff --git a/crates/bitwarden-crypto/src/safe/README.md b/crates/bitwarden-crypto/src/safe/README.md new file mode 100644 index 000000000..08e8ad2c5 --- /dev/null +++ b/crates/bitwarden-crypto/src/safe/README.md @@ -0,0 +1,12 @@ +# Bitwarden-crypto safe module + +The safe module contains a high-level set of tools used in building protocols and features involving +cryptography. Whenever possible, a feature should be build with features from this module, before +opting to build with any other, more lower-level primitives in the `bitwarden-crypto` crate. + +## Password-protected key envelope + +The password protected key envelope should be used, when the goal is to protect a symmetric key with +a password, for example for locking a vault with a PIN/Password, for protecting exports with a +password, etc. Internally, a KDF is used to protect against brute-forcing, but this is not exposed +to the consumer. The consumer only provides a password and key. diff --git a/crates/bitwarden-crypto/src/safe/mod.rs b/crates/bitwarden-crypto/src/safe/mod.rs index fa863bc19..04b88ca58 100644 --- a/crates/bitwarden-crypto/src/safe/mod.rs +++ b/crates/bitwarden-crypto/src/safe/mod.rs @@ -1,2 +1,4 @@ +#![doc = include_str!("./README.md")] + mod password_protected_key_envelope; pub use password_protected_key_envelope::*; diff --git a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs index bb5162d55..639e510f2 100644 --- a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs +++ b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs @@ -1,16 +1,17 @@ -use std::marker::PhantomData; +use std::{marker::PhantomData, num::TryFromIntError}; +use argon2::Params; use ciborium::{value::Integer, Value}; use coset::{ - iana::CoapContentFormat, CborSerializable, ContentType, CoseError, Header, HeaderBuilder, Label, + iana::CoapContentFormat, CborSerializable, ContentType, CoseError, Header, HeaderBuilder, }; use rand::RngCore; use thiserror::Error; use crate::{ cose::{ - ALG_ARGON2ID13, ARGON2_ITERATIONS, ARGON2_MEMORY, ARGON2_PARALLELISM, ARGON2_SALT, - CONTENT_TYPE_BITWARDEN_LEGACY_KEY, + extract_bytes, extract_integer, CoseExtractError, ALG_ARGON2ID13, ARGON2_ITERATIONS, + ARGON2_MEMORY, ARGON2_PARALLELISM, ARGON2_SALT, CONTENT_TYPE_BITWARDEN_LEGACY_KEY, }, xchacha20, BitwardenLegacyKeyBytes, ContentFormat, CoseKeyBytes, EncodedSymmetricKey, KeyIds, KeyStoreContext, SymmetricCryptoKey, @@ -22,6 +23,8 @@ use crate::{ /// /// The KDF parameters such as iterations and salt are stored in the key-envelope and do not have to /// be provided. +/// +/// Internally, Argon2 as the KDF, and XChaCha20-Poly1305 are used to encrypt the message. pub struct PasswordProtectedKeyEnvelope { _phantom: PhantomData, cose_encrypt: coset::CoseEncrypt, @@ -40,23 +43,26 @@ impl PasswordProtectedKeyEnvelope { #[allow(deprecated)] let key_ref = ctx .dangerous_get_symmetric_key(key_to_seal) - .expect("Key should exist in the key store"); - Self::seal_ref(&key_ref, password) + .map_err(|_| PasswordProtectedKeyEnvelopeError::KeyMissingError)?; + Self::seal_ref(key_ref, password) } + /// Seals a key reference with a password. This function is not public since callers are + /// expected to only work with key store references. fn seal_ref( key_to_seal: &SymmetricCryptoKey, password: &str, ) -> Result { - let kdf = Argon2RawSettings { - iterations: 3, - memory: 64 * 1024, // 64 MiB - parallelism: 4, - salt: make_salt(), - }; - Self::seal_ref_with_settings(key_to_seal, password, &kdf) + Self::seal_ref_with_settings( + key_to_seal, + password, + &Argon2RawSettings::default_for_platform(), + ) } + /// Seals a key reference with a password and custom provided settings. This function is not + /// public since callers are expected to only work with key store references, and to not + /// control the KDF difficulty where possible. fn seal_ref_with_settings( key_to_seal: &SymmetricCryptoKey, password: &str, @@ -64,15 +70,14 @@ impl PasswordProtectedKeyEnvelope { ) -> Result { // Cose does not yet have a standardized way to protect a key using a password. // This implements content encryption using direct encryption with a KDF derived key, - // similar to "Direct Key with KDF". The KDF settings are placed in a single - // recipient struct. + // similar to "Direct Key with KDF" mentioned in the COSE spec. The KDF settings are + // placed in a single recipient struct. // The envelope key is directly derived from the KDF and used as the key to encrypt the key // that should be sealed. let envelope_key = derive_key(kdf_settings, password) .map_err(|_| PasswordProtectedKeyEnvelopeError::KdfError)?; - #[allow(deprecated)] let (content_format, key_to_seal_bytes) = match key_to_seal.to_encoded_raw() { EncodedSymmetricKey::BitwardenLegacyKey(key_bytes) => { (ContentFormat::BitwardenLegacyKey, key_bytes.to_vec()) @@ -81,6 +86,10 @@ impl PasswordProtectedKeyEnvelope { }; let mut nonce = [0u8; crate::xchacha20::NONCE_SIZE]; + + // The message is constructed by placing the KDF settings in a recipient struct's + // unprotected headers. They do not need to live in the protected header, since to + // authenticate the protected header, the settings must be correct. let mut cose_encrypt = coset::CoseEncryptBuilder::new() .add_recipient({ let mut recipient = coset::CoseRecipientBuilder::new() @@ -130,11 +139,9 @@ impl PasswordProtectedKeyEnvelope { )); } - let recipient = self - .cose_encrypt - .recipients - .get(0) - .expect("Recipient should exist"); + let recipient = self.cose_encrypt.recipients.first().ok_or( + PasswordProtectedKeyEnvelopeError::ParsingError("Missing recipient".to_string()), + )?; if recipient.protected.header.alg != Some(coset::Algorithm::PrivateUse(ALG_ARGON2ID13)) { return Err(PasswordProtectedKeyEnvelopeError::ParsingError( "Unknown or unsupported KDF algorithm".to_string(), @@ -142,7 +149,7 @@ impl PasswordProtectedKeyEnvelope { } let kdf_settings: Argon2RawSettings = - recipient.unprotected.clone().try_into().map_err(|_| { + (&recipient.unprotected).try_into().map_err(|_| { PasswordProtectedKeyEnvelopeError::ParsingError( "Invalid or missing KDF parameters".to_string(), ) @@ -160,9 +167,11 @@ impl PasswordProtectedKeyEnvelope { aad, ) }) + // If decryption fails, the envelope-key is incorrect and thus the password is incorrect + // since the KDF parameters & salt are guaranteed to be correct .map_err(|_| PasswordProtectedKeyEnvelopeError::WrongPassword)?; - let key = SymmetricCryptoKey::try_from( + SymmetricCryptoKey::try_from( match self.cose_encrypt.protected.header.content_type.as_ref() { Some(ContentType::Text(format)) if format == CONTENT_TYPE_BITWARDEN_LEGACY_KEY => { EncodedSymmetricKey::BitwardenLegacyKey(BitwardenLegacyKeyBytes::from( @@ -179,8 +188,9 @@ impl PasswordProtectedKeyEnvelope { } }, ) - .unwrap(); - Ok(key) + .map_err(|_| { + PasswordProtectedKeyEnvelopeError::ParsingError("Failed to decode key".to_string()) + }) } /// Re-seals the key with new KDF parameters (updated settings, salt), and a new password @@ -206,7 +216,7 @@ impl TryFrom<&Vec> for PasswordProtectedKeyEnvelope { type Error = CoseError; fn try_from(value: &Vec) -> Result { - let cose_encrypt = coset::CoseEncrypt::from_slice(&value)?; + let cose_encrypt = coset::CoseEncrypt::from_slice(value)?; Ok(PasswordProtectedKeyEnvelope { _phantom: PhantomData, cose_encrypt, @@ -219,11 +229,40 @@ impl TryFrom<&Vec> for PasswordProtectedKeyEnvelope { /// size of 32 bytes, and randomly generated, instead of being derived from the email. struct Argon2RawSettings { iterations: u32, + /// Memory in KiB memory: u32, parallelism: u32, salt: [u8; 32], } +impl Argon2RawSettings { + /// Creates default Argon2 settings based on the platform. This currently is a static preset + /// based on the target os + fn default_for_platform() -> Self { + // iOS has memory limitations in the auto-fill context. So, the memory is halved + // but the iterations are doubled + if cfg!(target_os = "ios") { + // The SECOND RECOMMENDED option from: https://datatracker.ietf.org/doc/rfc9106/, with halved memory and doubled iteration count + Self { + iterations: 6, + memory: 32 * 1024, // 32 MiB + parallelism: 4, + salt: make_salt(), + } + } else { + // The SECOND RECOMMENDED option from: https://datatracker.ietf.org/doc/rfc9106/ + // The FIRST RECOMMENDED option currently still has too much memory consumption for most + // clients except desktop. + Self { + iterations: 3, + memory: 64 * 1024, // 64 MiB + parallelism: 4, + salt: make_salt(), + } + } + } +} + impl Into
for &Argon2RawSettings { fn into(self) -> Header { let builder = HeaderBuilder::new() @@ -238,68 +277,30 @@ impl Into
for &Argon2RawSettings { } } -impl TryInto for Header { +impl TryInto for &Argon2RawSettings { type Error = PasswordProtectedKeyEnvelopeError; - fn try_into(self) -> Result { - let iterations = self - .rest - .iter() - .find_map(|(label, value)| match (label, value) { - (Label::Int(ARGON2_ITERATIONS), ciborium::Value::Integer(value)) => Some(value), - _ => None, - }) - .ok_or(PasswordProtectedKeyEnvelopeError::ParsingError( - "Missing Argon2 iterations".to_string(), - ))?; - let memory = self - .rest - .iter() - .find_map(|(label, value)| match (label, value) { - (Label::Int(ARGON2_MEMORY), ciborium::Value::Integer(value)) => Some(value), - _ => None, - }) - .ok_or(PasswordProtectedKeyEnvelopeError::ParsingError( - "Missing Argon2 memory".to_string(), - ))?; - let parallelism = self - .rest - .iter() - .find_map(|(label, value)| match (label, value) { - (Label::Int(ARGON2_PARALLELISM), ciborium::Value::Integer(value)) => Some(value), - _ => None, - }) - .ok_or(PasswordProtectedKeyEnvelopeError::ParsingError( - "Missing Argon2 parallelism".to_string(), - ))?; - let salt: [u8; 32] = self - .rest - .iter() - .find_map(|(label, value)| match (label, value) { - (Label::Int(ARGON2_SALT), ciborium::Value::Bytes(value)) if value.len() == 32 => { - Some(value.as_slice().try_into().unwrap()) - } - _ => None, - }) - .ok_or(PasswordProtectedKeyEnvelopeError::ParsingError( - "Missing or invalid Argon2 salt".to_string(), - ))?; + fn try_into(self) -> Result { + Params::new(self.memory, self.iterations, self.parallelism, Some(32)) + .map_err(|_| PasswordProtectedKeyEnvelopeError::KdfError) + } +} + +impl TryInto for &Header { + type Error = PasswordProtectedKeyEnvelopeError; + fn try_into(self) -> Result { Ok(Argon2RawSettings { - iterations: i128::from(*iterations).try_into().map_err(|_| { - PasswordProtectedKeyEnvelopeError::ParsingError( - "Invalid Argon2 iterations".to_string(), - ) - })?, - memory: i128::from(*memory).try_into().map_err(|_| { - PasswordProtectedKeyEnvelopeError::ParsingError("Invalid Argon2 memory".to_string()) - })?, - parallelism: i128::from(*parallelism).try_into().map_err(|_| { - PasswordProtectedKeyEnvelopeError::ParsingError( - "Invalid Argon2 parallelism".to_string(), - ) - })?, - salt, + iterations: extract_integer(self, ARGON2_ITERATIONS, "iterations")?.try_into()?, + memory: extract_integer(self, ARGON2_MEMORY, "memory")?.try_into()?, + parallelism: extract_integer(self, ARGON2_PARALLELISM, "parallelism")?.try_into()?, + salt: extract_bytes(self, ARGON2_SALT, "salt")? + .try_into() + .map_err(|_| { + PasswordProtectedKeyEnvelopeError::ParsingError( + "Invalid Argon2 salt".to_string(), + ) + })?, }) } } @@ -313,19 +314,18 @@ fn make_salt() -> [u8; 32] { fn derive_key( argon2_settings: &Argon2RawSettings, password: &str, -) -> Result<[u8; 32], crate::CryptoError> { +) -> Result<[u8; 32], PasswordProtectedKeyEnvelopeError> { use argon2::*; - let params = Params::new( - argon2_settings.memory, - argon2_settings.iterations, - argon2_settings.parallelism, - Some(32), - )?; - let argon = Argon2::new(Algorithm::Argon2id, Version::V0x13, params); - let mut hash = [0u8; 32]; - argon.hash_password_into(password.as_bytes(), &argon2_settings.salt, &mut hash)?; + Argon2::new( + Algorithm::Argon2id, + Version::V0x13, + argon2_settings.try_into()?, + ) + .hash_password_into(password.as_bytes(), &argon2_settings.salt, &mut hash) + .map_err(|_| PasswordProtectedKeyEnvelopeError::KdfError)?; + Ok(hash) } @@ -342,6 +342,22 @@ pub enum PasswordProtectedKeyEnvelopeError { /// issues #[error("Kdf error")] KdfError, + /// There is no key for the provided key id in the key store + #[error("Key missing error")] + KeyMissingError, +} + +impl From for PasswordProtectedKeyEnvelopeError { + fn from(err: CoseExtractError) -> Self { + let CoseExtractError::MissingValue(label) = err; + PasswordProtectedKeyEnvelopeError::ParsingError(format!("Missing value for {}", label)) + } +} + +impl From for PasswordProtectedKeyEnvelopeError { + fn from(err: TryFromIntError) -> Self { + PasswordProtectedKeyEnvelopeError::ParsingError(format!("Invalid integer: {}", err)) + } } #[cfg(test)] @@ -352,6 +368,88 @@ mod tests { KeyStore, }; + const TESTVECTOR_COSEKEY_ENVELOPE: &[u8] = &[ + 132, 68, 161, 3, 24, 101, 161, 5, 88, 24, 173, 142, 229, 217, 156, 211, 58, 187, 48, 229, + 94, 63, 201, 106, 223, 123, 129, 149, 111, 108, 216, 234, 114, 242, 88, 84, 7, 21, 43, 61, + 136, 100, 166, 73, 66, 77, 244, 30, 110, 208, 228, 170, 69, 37, 144, 43, 124, 28, 63, 202, + 233, 27, 49, 217, 144, 182, 88, 129, 128, 233, 209, 11, 89, 15, 138, 146, 163, 147, 198, + 182, 151, 227, 147, 183, 28, 124, 183, 83, 47, 84, 223, 129, 131, 217, 203, 128, 180, 109, + 45, 247, 181, 136, 8, 23, 30, 113, 229, 90, 121, 182, 162, 209, 249, 55, 17, 189, 200, 69, + 4, 254, 129, 131, 71, 161, 1, 58, 0, 1, 21, 87, 165, 1, 58, 0, 1, 21, 87, 58, 0, 1, 21, 89, + 3, 58, 0, 1, 21, 90, 26, 0, 1, 0, 0, 58, 0, 1, 21, 91, 4, 58, 0, 1, 21, 88, 88, 32, 168, + 162, 100, 184, 10, 1, 169, 18, 176, 1, 201, 181, 212, 40, 154, 8, 81, 194, 251, 57, 226, + 182, 247, 242, 237, 175, 189, 254, 89, 218, 226, 158, 246, + ]; + const TEST_UNSEALED_COSEKEY_ENCODED: &[u8] = &[ + 165, 1, 4, 2, 80, 80, 63, 72, 147, 13, 151, 69, 121, 184, 220, 160, 176, 227, 247, 83, 112, + 3, 58, 0, 1, 17, 111, 4, 132, 3, 4, 5, 6, 32, 88, 32, 95, 169, 162, 129, 95, 51, 121, 95, + 226, 3, 25, 67, 120, 143, 6, 169, 235, 157, 217, 6, 224, 25, 126, 237, 82, 169, 60, 245, + 122, 3, 35, 250, 1, + ]; + + const TESTVECTOR_LEGACYKEY_ENVELOPE: &[u8] = &[ + 132, 88, 38, 161, 3, 120, 34, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 120, + 46, 98, 105, 116, 119, 97, 114, 100, 101, 110, 46, 108, 101, 103, 97, 99, 121, 45, 107, + 101, 121, 161, 5, 88, 24, 64, 18, 232, 33, 184, 69, 105, 229, 203, 52, 40, 19, 228, 121, + 232, 82, 6, 253, 145, 215, 99, 4, 88, 149, 88, 80, 16, 4, 72, 82, 162, 71, 130, 214, 222, + 19, 97, 28, 23, 82, 10, 222, 115, 60, 208, 71, 178, 128, 132, 129, 173, 19, 148, 119, 91, + 72, 155, 49, 172, 139, 4, 71, 209, 90, 110, 239, 180, 150, 23, 213, 134, 34, 52, 59, 27, + 40, 86, 86, 225, 49, 63, 39, 219, 197, 163, 90, 146, 204, 205, 93, 166, 199, 73, 72, 118, + 36, 11, 35, 124, 96, 209, 157, 75, 69, 24, 90, 129, 131, 71, 161, 1, 58, 0, 1, 21, 87, 165, + 1, 58, 0, 1, 21, 87, 58, 0, 1, 21, 89, 3, 58, 0, 1, 21, 90, 26, 0, 1, 0, 0, 58, 0, 1, 21, + 91, 4, 58, 0, 1, 21, 88, 88, 32, 89, 248, 223, 6, 137, 20, 160, 157, 139, 147, 235, 241, + 162, 143, 82, 84, 221, 133, 13, 15, 207, 253, 7, 17, 96, 75, 80, 31, 241, 241, 191, 97, + 246, + ]; + const TEST_UNSEALED_LEGACYKEY_ENCODED: &[u8] = &[ + 231, 34, 128, 103, 132, 210, 72, 65, 163, 123, 158, 12, 87, 153, 92, 230, 220, 186, 114, + 185, 42, 83, 62, 49, 190, 95, 188, 14, 111, 233, 136, 210, 202, 127, 163, 160, 70, 45, 135, + 210, 236, 237, 180, 212, 215, 151, 220, 250, 32, 184, 100, 154, 226, 23, 204, 106, 64, 85, + 205, 152, 118, 138, 199, 129, + ]; + + const TESTVECTOR_PASSWORD: &str = "test_password"; + + #[test] + fn test_testvector_cosekey() { + let key_store = KeyStore::::default(); + let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut(); + let envelope = + PasswordProtectedKeyEnvelope::try_from(&TESTVECTOR_COSEKEY_ENVELOPE.to_vec()) + .expect("Key envelope should be valid"); + envelope + .unseal(TestSymmKey::A(0), TESTVECTOR_PASSWORD, &mut ctx) + .expect("Unsealing should succeed"); + #[allow(deprecated)] + let unsealed_key = ctx + .dangerous_get_symmetric_key(TestSymmKey::A(0)) + .expect("Key should exist in the key store"); + assert_eq!( + unsealed_key.to_encoded().to_vec(), + TEST_UNSEALED_COSEKEY_ENCODED + ); + } + + #[test] + fn test_testvector_legacykey() { + let key_store = KeyStore::::default(); + let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut(); + let envelope = + PasswordProtectedKeyEnvelope::try_from(&TESTVECTOR_LEGACYKEY_ENVELOPE.to_vec()) + .expect("Key envelope should be valid"); + envelope + .unseal(TestSymmKey::A(0), TESTVECTOR_PASSWORD, &mut ctx) + .expect("Unsealing should succeed"); + #[allow(deprecated)] + let unsealed_key = ctx + .dangerous_get_symmetric_key(TestSymmKey::A(0)) + .expect("Key should exist in the key store"); + assert_eq!( + unsealed_key.to_encoded().to_vec(), + TEST_UNSEALED_LEGACYKEY_ENCODED + ); + } + #[test] fn test_make_envelope() { let key_store = KeyStore::::default(); @@ -427,6 +525,7 @@ mod tests { // Seal the key with a password let envelope: PasswordProtectedKeyEnvelope = PasswordProtectedKeyEnvelope::seal_ref(&key, password).expect("Sealing should work"); + // Reseal let envelope = envelope .reseal(password, new_password) From fabee1672883fb83dcf3aaacddf0d1fb205c9ab5 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 29 Jul 2025 14:23:56 +0200 Subject: [PATCH 112/124] Fix clippy errors --- .../examples/protect_key_with_password.rs | 27 ++++++++++---- .../safe/password_protected_key_envelope.rs | 37 ++++++++++++------- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/crates/bitwarden-crypto/examples/protect_key_with_password.rs b/crates/bitwarden-crypto/examples/protect_key_with_password.rs index 216d91b39..7cb0a5cee 100644 --- a/crates/bitwarden-crypto/examples/protect_key_with_password.rs +++ b/crates/bitwarden-crypto/examples/protect_key_with_password.rs @@ -22,7 +22,7 @@ fn main() { // Alice has a vault protected with a symmetric key. She wants this protected with a PIN. let vault_key = ctx .generate_symmetric_key(ExampleSymmetricKey::VaultKey) - .unwrap(); + .expect("Generating vault key should work"); // Seal the key with the PIN // The KDF settings are chosen for you, and do not need to be separately tracked or synced @@ -30,31 +30,44 @@ fn main() { let pin = "1234"; let envelope = PasswordProtectedKeyEnvelope::seal(vault_key, pin, &ctx).expect("Sealing should work"); - disk.save("vault_key_envelope", (&envelope).try_into().unwrap()); + disk.save( + "vault_key_envelope", + (&envelope).try_into().expect("Saving envelope should work"), + ); // Wipe the context to simulate new session ctx.clear_local(); // Load the envelope from disk and unseal it with the PIN, and store it in the context. let deserialized: PasswordProtectedKeyEnvelope = - PasswordProtectedKeyEnvelope::try_from(disk.load("vault_key_envelope").unwrap()).unwrap(); + PasswordProtectedKeyEnvelope::try_from( + disk.load("vault_key_envelope") + .expect("Loading from disk should work"), + ) + .expect("Deserializing envelope should work"); deserialized .unseal(ExampleSymmetricKey::VaultKey, pin, &mut ctx) - .unwrap(); + .expect("Unsealing should work"); // Alice wants to change her password; also her KDF settings are below the minimums. // Re-sealing will update the password, and KDF settings. let envelope = envelope .reseal(pin, "0000") .expect("The password should be valid"); - disk.save("vault_key_envelope", (&envelope).try_into().unwrap()); + disk.save( + "vault_key_envelope", + (&envelope).try_into().expect("Saving envelope should work"), + ); // Alice wants to change the protected key. This requires creating a new envelope ctx.generate_symmetric_key(ExampleSymmetricKey::VaultKey) - .unwrap(); + .expect("Generating vault key should work"); let envelope = PasswordProtectedKeyEnvelope::seal(ExampleSymmetricKey::VaultKey, "0000", &ctx) .expect("Sealing should work"); - disk.save("vault_key_envelope", (&envelope).try_into().unwrap()); + disk.save( + "vault_key_envelope", + (&envelope).try_into().expect("Saving envelope should work"), + ); // Alice tries the password but it is wrong assert!(matches!( diff --git a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs index 639e510f2..1aeea7f2b 100644 --- a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs +++ b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs @@ -123,7 +123,8 @@ impl PasswordProtectedKeyEnvelope { ) -> Result { let key = self.unseal_ref(password)?; #[allow(deprecated)] - ctx.set_symmetric_key(target_keyslot, key).unwrap(); + ctx.set_symmetric_key(target_keyslot, key) + .map_err(|_| PasswordProtectedKeyEnvelopeError::KeyStoreError)?; Ok(target_keyslot) } @@ -156,16 +157,20 @@ impl PasswordProtectedKeyEnvelope { })?; let envelope_key = derive_key(&kdf_settings, password) .map_err(|_| PasswordProtectedKeyEnvelopeError::KdfError)?; + let nonce: [u8; 24] = self + .cose_encrypt + .unprotected + .iv + .clone() + .try_into() + .map_err(|_| { + PasswordProtectedKeyEnvelopeError::ParsingError("Invalid IV".to_string()) + })?; let key_bytes = self .cose_encrypt .decrypt(&[], |data, aad| { - xchacha20::decrypt_xchacha20_poly1305( - &self.cose_encrypt.unprotected.iv.clone().try_into().unwrap(), - &envelope_key, - data, - aad, - ) + xchacha20::decrypt_xchacha20_poly1305(&nonce, &envelope_key, data, aad) }) // If decryption fails, the envelope-key is incorrect and thus the password is incorrect // since the KDF parameters & salt are guaranteed to be correct @@ -263,13 +268,16 @@ impl Argon2RawSettings { } } -impl Into
for &Argon2RawSettings { - fn into(self) -> Header { +impl From<&Argon2RawSettings> for Header { + fn from(settings: &Argon2RawSettings) -> Header { let builder = HeaderBuilder::new() - .value(ARGON2_ITERATIONS, Integer::from(self.iterations).into()) - .value(ARGON2_MEMORY, Integer::from(self.memory).into()) - .value(ARGON2_PARALLELISM, Integer::from(self.parallelism).into()) - .value(ARGON2_SALT, Value::from(self.salt.to_vec())); + .value(ARGON2_ITERATIONS, Integer::from(settings.iterations).into()) + .value(ARGON2_MEMORY, Integer::from(settings.memory).into()) + .value( + ARGON2_PARALLELISM, + Integer::from(settings.parallelism).into(), + ) + .value(ARGON2_SALT, Value::from(settings.salt.to_vec())); let mut header = builder.build(); header.alg = Some(coset::Algorithm::PrivateUse(ALG_ARGON2ID13)); @@ -345,6 +353,9 @@ pub enum PasswordProtectedKeyEnvelopeError { /// There is no key for the provided key id in the key store #[error("Key missing error")] KeyMissingError, + /// The key store could not be written to, for example due to being read-only + #[error("Could not write to key store")] + KeyStoreError, } impl From for PasswordProtectedKeyEnvelopeError { From 1fef06f06b3ff17652d12e3476e908400215b3af Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 11 Aug 2025 10:30:35 +0200 Subject: [PATCH 113/124] Update crates/bitwarden-crypto/examples/protect_key_with_password.rs Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> --- crates/bitwarden-crypto/examples/protect_key_with_password.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/examples/protect_key_with_password.rs b/crates/bitwarden-crypto/examples/protect_key_with_password.rs index 7cb0a5cee..304298fa0 100644 --- a/crates/bitwarden-crypto/examples/protect_key_with_password.rs +++ b/crates/bitwarden-crypto/examples/protect_key_with_password.rs @@ -26,7 +26,7 @@ fn main() { // Seal the key with the PIN // The KDF settings are chosen for you, and do not need to be separately tracked or synced - // Next, story this protected key envelope on disk. + // Next, store this protected key envelope on disk. let pin = "1234"; let envelope = PasswordProtectedKeyEnvelope::seal(vault_key, pin, &ctx).expect("Sealing should work"); From b85806a80bf1b11818aa80bd7b17223171afb96c Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 11 Aug 2025 10:33:38 +0200 Subject: [PATCH 114/124] Update crates/bitwarden-crypto/examples/protect_key_with_password.rs Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> --- crates/bitwarden-crypto/examples/protect_key_with_password.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/examples/protect_key_with_password.rs b/crates/bitwarden-crypto/examples/protect_key_with_password.rs index 304298fa0..1f8ded0da 100644 --- a/crates/bitwarden-crypto/examples/protect_key_with_password.rs +++ b/crates/bitwarden-crypto/examples/protect_key_with_password.rs @@ -19,7 +19,7 @@ fn main() { // - Protect a send with a URL fragment secret // For this, the `PasswordProtectedKeyEnvelope` is used. - // Alice has a vault protected with a symmetric key. She wants this protected with a PIN. + // Alice has a vault protected with a symmetric key. She wants the symmetric key protected with a PIN. let vault_key = ctx .generate_symmetric_key(ExampleSymmetricKey::VaultKey) .expect("Generating vault key should work"); From 7848fa6cf2a024474ccc88c462b3365fc03e6f60 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 11 Aug 2025 10:34:16 +0200 Subject: [PATCH 115/124] Update crates/bitwarden-crypto/examples/protect_key_with_password.rs Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> --- crates/bitwarden-crypto/examples/protect_key_with_password.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/examples/protect_key_with_password.rs b/crates/bitwarden-crypto/examples/protect_key_with_password.rs index 1f8ded0da..b82e3cca3 100644 --- a/crates/bitwarden-crypto/examples/protect_key_with_password.rs +++ b/crates/bitwarden-crypto/examples/protect_key_with_password.rs @@ -8,7 +8,7 @@ use bitwarden_crypto::{ }; fn main() { - let key_story = KeyStore::::default(); + let key_store = KeyStore::::default(); let mut ctx: KeyStoreContext<'_, ExampleIds> = key_story.context_mut(); let mut disk = MockDisk::new(); From 32aacd7935ff2aaf1fc6c1df73c9a7a18350c90f Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 11 Aug 2025 10:36:07 +0200 Subject: [PATCH 116/124] Update crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> --- .../src/safe/password_protected_key_envelope.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs index 1aeea7f2b..ace1b9b97 100644 --- a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs +++ b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs @@ -21,10 +21,10 @@ use crate::{ /// does so by using a Key Derivation Function (KDF), to increase the difficulty of brute-forcing /// the password. /// -/// The KDF parameters such as iterations and salt are stored in the key-envelope and do not have to +/// The KDF parameters such as iterations and salt are stored in the envelope and do not have to /// be provided. /// -/// Internally, Argon2 as the KDF, and XChaCha20-Poly1305 are used to encrypt the message. +/// Internally, Argon2 is used as the KDF and XChaCha20-Poly1305 is used to encrypt the key. pub struct PasswordProtectedKeyEnvelope { _phantom: PhantomData, cose_encrypt: coset::CoseEncrypt, From 6fd28c223c76835773f55f460caff134a1854332 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 11 Aug 2025 10:37:14 +0200 Subject: [PATCH 117/124] Update crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> --- .../src/safe/password_protected_key_envelope.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs index ace1b9b97..96ade47d0 100644 --- a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs +++ b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs @@ -157,7 +157,7 @@ impl PasswordProtectedKeyEnvelope { })?; let envelope_key = derive_key(&kdf_settings, password) .map_err(|_| PasswordProtectedKeyEnvelopeError::KdfError)?; - let nonce: [u8; 24] = self + let nonce: [u8; crate::xchacha20::NONCE_SIZE] = self .cose_encrypt .unprotected .iv From d601b0c22a1eef6bd47690445e47c4207fca31c0 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 11 Aug 2025 10:40:15 +0200 Subject: [PATCH 118/124] Update crates/bitwarden-crypto/src/safe/README.md Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> --- crates/bitwarden-crypto/src/safe/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/bitwarden-crypto/src/safe/README.md b/crates/bitwarden-crypto/src/safe/README.md index 08e8ad2c5..d6cb866bd 100644 --- a/crates/bitwarden-crypto/src/safe/README.md +++ b/crates/bitwarden-crypto/src/safe/README.md @@ -1,8 +1,6 @@ # Bitwarden-crypto safe module -The safe module contains a high-level set of tools used in building protocols and features involving -cryptography. Whenever possible, a feature should be build with features from this module, before -opting to build with any other, more lower-level primitives in the `bitwarden-crypto` crate. +The safe module provides high-level cryptographic tools for building secure protocols and features. When developing new features, use this module first before considering lower-level primitives from other parts of `bitwarden-crypto`. ## Password-protected key envelope From 6b114723e0f268fd3e4644bc8bb72ceab18fb17a Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 11 Aug 2025 10:40:32 +0200 Subject: [PATCH 119/124] Update crates/bitwarden-crypto/src/safe/README.md Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> --- crates/bitwarden-crypto/src/safe/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/bitwarden-crypto/src/safe/README.md b/crates/bitwarden-crypto/src/safe/README.md index d6cb866bd..d1d22d8c3 100644 --- a/crates/bitwarden-crypto/src/safe/README.md +++ b/crates/bitwarden-crypto/src/safe/README.md @@ -4,7 +4,9 @@ The safe module provides high-level cryptographic tools for building secure prot ## Password-protected key envelope -The password protected key envelope should be used, when the goal is to protect a symmetric key with -a password, for example for locking a vault with a PIN/Password, for protecting exports with a -password, etc. Internally, a KDF is used to protect against brute-forcing, but this is not exposed -to the consumer. The consumer only provides a password and key. +Use the password protected key envelope to protect a symmetric key with a password. Examples include: + +- locking a vault with a PIN/Password +- protecting exports with a password + +Internally, the module uses a KDF to protect against brute-forcing, but it does not expose this to the consumer. The consumer only provides a password and key. From b8198bf4e294401e15f6141be39e39d46411bc9f Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 11 Aug 2025 11:22:37 +0200 Subject: [PATCH 120/124] Fix typo --- crates/bitwarden-crypto/examples/protect_key_with_password.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/examples/protect_key_with_password.rs b/crates/bitwarden-crypto/examples/protect_key_with_password.rs index b82e3cca3..86fd3d3bc 100644 --- a/crates/bitwarden-crypto/examples/protect_key_with_password.rs +++ b/crates/bitwarden-crypto/examples/protect_key_with_password.rs @@ -9,7 +9,7 @@ use bitwarden_crypto::{ fn main() { let key_store = KeyStore::::default(); - let mut ctx: KeyStoreContext<'_, ExampleIds> = key_story.context_mut(); + let mut ctx: KeyStoreContext<'_, ExampleIds> = key_store.context_mut(); let mut disk = MockDisk::new(); // Alice wants to protect a key with a password. From 08926872e6f27950793939ec2e5889a85d6729ad Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 11 Aug 2025 11:48:49 +0200 Subject: [PATCH 121/124] Re-generate test vectors --- .../safe/password_protected_key_envelope.rs | 90 +++++++++++-------- 1 file changed, 53 insertions(+), 37 deletions(-) diff --git a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs index 96ade47d0..711571d2a 100644 --- a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs +++ b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs @@ -1,3 +1,16 @@ +//! Password protected key envelope is a cryptographic building block that allows sealing a symmetric key +//! with a low entropy secret (password, PIN, etc.). +//! +//! It is implemented by using a KDF (Argon2ID) combined with secret key encryption (XChaCha20-Poly1305). +//! The KDF prevents brute-force by requiring work to be done to derive the key from the password. +//! +//! For the consumer, the output is an opaque blob that can be later unsealed with the same password. The +//! KDF parameters and salt are contained in the envelope, and don't need to be provided for unsealing. +//! +//! Internally, the envelope is a CoseEncrypt object. The KDF parameters / salt are placed in the single +//! recipient's unprotected headers. The output from the KDF - "envelope key", is used to wrap the +//! symmetric key, that is sealed by the envelope. + use std::{marker::PhantomData, num::TryFromIntError}; use argon2::Params; @@ -17,6 +30,12 @@ use crate::{ KeyStoreContext, SymmetricCryptoKey, }; +/// 16 is the RECOMMENDED salt size for all applications: +/// https://datatracker.ietf.org/doc/rfc9106/ +const ENVELOPE_ARGON2_SALT_SIZE: usize = 16; +/// 32 is chosen to match the size of an XChaCha20-Poly1305 key +const ENVELOPE_ARGON2_OUTPUT_KEY_SIZE: usize = 32; + /// A password-protected key envelope can seal a symmetric key, and protect it with a password. It /// does so by using a Key Derivation Function (KDF), to increase the difficulty of brute-forcing /// the password. @@ -237,7 +256,7 @@ struct Argon2RawSettings { /// Memory in KiB memory: u32, parallelism: u32, - salt: [u8; 32], + salt: [u8; ENVELOPE_ARGON2_SALT_SIZE], } impl Argon2RawSettings { @@ -313,8 +332,8 @@ impl TryInto for &Header { } } -fn make_salt() -> [u8; 32] { - let mut salt = [0u8; 32]; +fn make_salt() -> [u8; ENVELOPE_ARGON2_SALT_SIZE] { + let mut salt = [0u8; ENVELOPE_ARGON2_SALT_SIZE]; rand::thread_rng().fill_bytes(&mut salt); salt } @@ -322,10 +341,10 @@ fn make_salt() -> [u8; 32] { fn derive_key( argon2_settings: &Argon2RawSettings, password: &str, -) -> Result<[u8; 32], PasswordProtectedKeyEnvelopeError> { +) -> Result<[u8; ENVELOPE_ARGON2_OUTPUT_KEY_SIZE], PasswordProtectedKeyEnvelopeError> { use argon2::*; - let mut hash = [0u8; 32]; + let mut hash = [0u8; ENVELOPE_ARGON2_OUTPUT_KEY_SIZE]; Argon2::new( Algorithm::Argon2id, Version::V0x13, @@ -379,44 +398,41 @@ mod tests { KeyStore, }; + const TEST_UNSEALED_COSEKEY_ENCODED: &[u8] = &[ + 165, 1, 4, 2, 80, 63, 208, 189, 183, 204, 37, 72, 170, 179, 236, 190, 208, 22, 65, 227, + 183, 3, 58, 0, 1, 17, 111, 4, 132, 3, 4, 5, 6, 32, 88, 32, 88, 25, 68, 85, 205, 28, 133, + 28, 90, 147, 160, 145, 48, 3, 178, 184, 30, 11, 122, 132, 64, 59, 51, 233, 191, 117, 159, + 117, 23, 168, 248, 36, 1, + ]; const TESTVECTOR_COSEKEY_ENVELOPE: &[u8] = &[ - 132, 68, 161, 3, 24, 101, 161, 5, 88, 24, 173, 142, 229, 217, 156, 211, 58, 187, 48, 229, - 94, 63, 201, 106, 223, 123, 129, 149, 111, 108, 216, 234, 114, 242, 88, 84, 7, 21, 43, 61, - 136, 100, 166, 73, 66, 77, 244, 30, 110, 208, 228, 170, 69, 37, 144, 43, 124, 28, 63, 202, - 233, 27, 49, 217, 144, 182, 88, 129, 128, 233, 209, 11, 89, 15, 138, 146, 163, 147, 198, - 182, 151, 227, 147, 183, 28, 124, 183, 83, 47, 84, 223, 129, 131, 217, 203, 128, 180, 109, - 45, 247, 181, 136, 8, 23, 30, 113, 229, 90, 121, 182, 162, 209, 249, 55, 17, 189, 200, 69, - 4, 254, 129, 131, 71, 161, 1, 58, 0, 1, 21, 87, 165, 1, 58, 0, 1, 21, 87, 58, 0, 1, 21, 89, - 3, 58, 0, 1, 21, 90, 26, 0, 1, 0, 0, 58, 0, 1, 21, 91, 4, 58, 0, 1, 21, 88, 88, 32, 168, - 162, 100, 184, 10, 1, 169, 18, 176, 1, 201, 181, 212, 40, 154, 8, 81, 194, 251, 57, 226, - 182, 247, 242, 237, 175, 189, 254, 89, 218, 226, 158, 246, + 132, 68, 161, 3, 24, 101, 161, 5, 88, 24, 1, 31, 58, 230, 10, 92, 195, 233, 212, 7, 166, + 252, 67, 115, 221, 58, 3, 191, 218, 188, 181, 192, 28, 11, 88, 84, 141, 183, 137, 167, 166, + 161, 33, 82, 30, 255, 23, 10, 179, 149, 88, 24, 39, 60, 74, 232, 133, 44, 90, 98, 117, 31, + 41, 69, 251, 76, 250, 141, 229, 83, 191, 6, 237, 107, 127, 93, 238, 110, 49, 125, 201, 37, + 162, 120, 157, 32, 116, 195, 208, 143, 83, 254, 223, 93, 97, 158, 0, 24, 95, 197, 249, 35, + 240, 3, 20, 71, 164, 97, 180, 29, 203, 69, 31, 151, 249, 244, 197, 91, 101, 174, 129, 131, + 71, 161, 1, 58, 0, 1, 21, 87, 165, 1, 58, 0, 1, 21, 87, 58, 0, 1, 21, 89, 3, 58, 0, 1, 21, + 90, 26, 0, 1, 0, 0, 58, 0, 1, 21, 91, 4, 58, 0, 1, 21, 88, 80, 165, 253, 56, 243, 255, 54, + 246, 252, 231, 230, 33, 252, 49, 175, 1, 111, 246, ]; - const TEST_UNSEALED_COSEKEY_ENCODED: &[u8] = &[ - 165, 1, 4, 2, 80, 80, 63, 72, 147, 13, 151, 69, 121, 184, 220, 160, 176, 227, 247, 83, 112, - 3, 58, 0, 1, 17, 111, 4, 132, 3, 4, 5, 6, 32, 88, 32, 95, 169, 162, 129, 95, 51, 121, 95, - 226, 3, 25, 67, 120, 143, 6, 169, 235, 157, 217, 6, 224, 25, 126, 237, 82, 169, 60, 245, - 122, 3, 35, 250, 1, + const TEST_UNSEALED_LEGACYKEY_ENCODED: &[u8] = &[ + 135, 114, 97, 155, 115, 209, 215, 224, 175, 159, 231, 208, 15, 244, 40, 171, 239, 137, 57, + 98, 207, 167, 231, 138, 145, 254, 28, 136, 236, 60, 23, 163, 4, 246, 219, 117, 104, 246, + 86, 10, 152, 52, 90, 85, 58, 6, 70, 39, 111, 128, 93, 145, 143, 180, 77, 129, 178, 242, 82, + 72, 57, 61, 192, 64, ]; - const TESTVECTOR_LEGACYKEY_ENVELOPE: &[u8] = &[ 132, 88, 38, 161, 3, 120, 34, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 120, 46, 98, 105, 116, 119, 97, 114, 100, 101, 110, 46, 108, 101, 103, 97, 99, 121, 45, 107, - 101, 121, 161, 5, 88, 24, 64, 18, 232, 33, 184, 69, 105, 229, 203, 52, 40, 19, 228, 121, - 232, 82, 6, 253, 145, 215, 99, 4, 88, 149, 88, 80, 16, 4, 72, 82, 162, 71, 130, 214, 222, - 19, 97, 28, 23, 82, 10, 222, 115, 60, 208, 71, 178, 128, 132, 129, 173, 19, 148, 119, 91, - 72, 155, 49, 172, 139, 4, 71, 209, 90, 110, 239, 180, 150, 23, 213, 134, 34, 52, 59, 27, - 40, 86, 86, 225, 49, 63, 39, 219, 197, 163, 90, 146, 204, 205, 93, 166, 199, 73, 72, 118, - 36, 11, 35, 124, 96, 209, 157, 75, 69, 24, 90, 129, 131, 71, 161, 1, 58, 0, 1, 21, 87, 165, - 1, 58, 0, 1, 21, 87, 58, 0, 1, 21, 89, 3, 58, 0, 1, 21, 90, 26, 0, 1, 0, 0, 58, 0, 1, 21, - 91, 4, 58, 0, 1, 21, 88, 88, 32, 89, 248, 223, 6, 137, 20, 160, 157, 139, 147, 235, 241, - 162, 143, 82, 84, 221, 133, 13, 15, 207, 253, 7, 17, 96, 75, 80, 31, 241, 241, 191, 97, - 246, - ]; - const TEST_UNSEALED_LEGACYKEY_ENCODED: &[u8] = &[ - 231, 34, 128, 103, 132, 210, 72, 65, 163, 123, 158, 12, 87, 153, 92, 230, 220, 186, 114, - 185, 42, 83, 62, 49, 190, 95, 188, 14, 111, 233, 136, 210, 202, 127, 163, 160, 70, 45, 135, - 210, 236, 237, 180, 212, 215, 151, 220, 250, 32, 184, 100, 154, 226, 23, 204, 106, 64, 85, - 205, 152, 118, 138, 199, 129, + 101, 121, 161, 5, 88, 24, 218, 72, 22, 79, 149, 30, 12, 36, 180, 212, 44, 21, 167, 208, + 214, 221, 7, 91, 178, 12, 104, 17, 45, 219, 88, 80, 114, 38, 14, 165, 85, 229, 103, 108, + 17, 175, 41, 43, 203, 175, 119, 125, 227, 127, 163, 214, 213, 138, 12, 216, 163, 204, 38, + 222, 47, 11, 44, 231, 239, 170, 63, 8, 249, 56, 102, 18, 134, 34, 232, 193, 44, 19, 228, + 17, 187, 199, 238, 187, 2, 13, 30, 112, 103, 110, 5, 31, 238, 58, 4, 24, 19, 239, 135, 57, + 206, 190, 144, 83, 128, 204, 59, 155, 21, 80, 180, 34, 129, 131, 71, 161, 1, 58, 0, 1, 21, + 87, 165, 1, 58, 0, 1, 21, 87, 58, 0, 1, 21, 89, 3, 58, 0, 1, 21, 90, 26, 0, 1, 0, 0, 58, 0, + 1, 21, 91, 4, 58, 0, 1, 21, 88, 80, 212, 91, 185, 112, 92, 177, 108, 33, 182, 202, 26, 141, + 11, 133, 95, 235, 246, ]; const TESTVECTOR_PASSWORD: &str = "test_password"; From 3e03ec396b4a1ffd7bafbf340a8795acc7dcc792 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 11 Aug 2025 11:49:10 +0200 Subject: [PATCH 122/124] Cargo fmt --- .../examples/protect_key_with_password.rs | 3 ++- .../safe/password_protected_key_envelope.rs | 20 ++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/crates/bitwarden-crypto/examples/protect_key_with_password.rs b/crates/bitwarden-crypto/examples/protect_key_with_password.rs index 86fd3d3bc..e5243398a 100644 --- a/crates/bitwarden-crypto/examples/protect_key_with_password.rs +++ b/crates/bitwarden-crypto/examples/protect_key_with_password.rs @@ -19,7 +19,8 @@ fn main() { // - Protect a send with a URL fragment secret // For this, the `PasswordProtectedKeyEnvelope` is used. - // Alice has a vault protected with a symmetric key. She wants the symmetric key protected with a PIN. + // Alice has a vault protected with a symmetric key. She wants the symmetric key protected with + // a PIN. let vault_key = ctx .generate_symmetric_key(ExampleSymmetricKey::VaultKey) .expect("Generating vault key should work"); diff --git a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs index 711571d2a..99cff39a6 100644 --- a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs +++ b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs @@ -1,15 +1,17 @@ -//! Password protected key envelope is a cryptographic building block that allows sealing a symmetric key -//! with a low entropy secret (password, PIN, etc.). +//! Password protected key envelope is a cryptographic building block that allows sealing a +//! symmetric key with a low entropy secret (password, PIN, etc.). //! -//! It is implemented by using a KDF (Argon2ID) combined with secret key encryption (XChaCha20-Poly1305). -//! The KDF prevents brute-force by requiring work to be done to derive the key from the password. +//! It is implemented by using a KDF (Argon2ID) combined with secret key encryption +//! (XChaCha20-Poly1305). The KDF prevents brute-force by requiring work to be done to derive the +//! key from the password. //! -//! For the consumer, the output is an opaque blob that can be later unsealed with the same password. The -//! KDF parameters and salt are contained in the envelope, and don't need to be provided for unsealing. +//! For the consumer, the output is an opaque blob that can be later unsealed with the same +//! password. The KDF parameters and salt are contained in the envelope, and don't need to be +//! provided for unsealing. //! -//! Internally, the envelope is a CoseEncrypt object. The KDF parameters / salt are placed in the single -//! recipient's unprotected headers. The output from the KDF - "envelope key", is used to wrap the -//! symmetric key, that is sealed by the envelope. +//! Internally, the envelope is a CoseEncrypt object. The KDF parameters / salt are placed in the +//! single recipient's unprotected headers. The output from the KDF - "envelope key", is used to +//! wrap the symmetric key, that is sealed by the envelope. use std::{marker::PhantomData, num::TryFromIntError}; From 93454080b58d559f9971bd03754bb4b71ea5d68d Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 11 Aug 2025 11:58:53 +0200 Subject: [PATCH 123/124] Prettier fix formatting --- crates/bitwarden-crypto/src/safe/README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-crypto/src/safe/README.md b/crates/bitwarden-crypto/src/safe/README.md index d1d22d8c3..f098c7c9d 100644 --- a/crates/bitwarden-crypto/src/safe/README.md +++ b/crates/bitwarden-crypto/src/safe/README.md @@ -1,12 +1,16 @@ # Bitwarden-crypto safe module -The safe module provides high-level cryptographic tools for building secure protocols and features. When developing new features, use this module first before considering lower-level primitives from other parts of `bitwarden-crypto`. +The safe module provides high-level cryptographic tools for building secure protocols and features. +When developing new features, use this module first before considering lower-level primitives from +other parts of `bitwarden-crypto`. ## Password-protected key envelope -Use the password protected key envelope to protect a symmetric key with a password. Examples include: +Use the password protected key envelope to protect a symmetric key with a password. Examples +include: - locking a vault with a PIN/Password - protecting exports with a password -Internally, the module uses a KDF to protect against brute-forcing, but it does not expose this to the consumer. The consumer only provides a password and key. +Internally, the module uses a KDF to protect against brute-forcing, but it does not expose this to +the consumer. The consumer only provides a password and key. From 6fb4e9754a5f6ae018ece15dff96ddab22ce215e Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 11 Aug 2025 12:05:41 +0200 Subject: [PATCH 124/124] Fix link formatting --- .../src/safe/password_protected_key_envelope.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs index 99cff39a6..c25a57743 100644 --- a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs +++ b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs @@ -33,7 +33,7 @@ use crate::{ }; /// 16 is the RECOMMENDED salt size for all applications: -/// https://datatracker.ietf.org/doc/rfc9106/ +/// const ENVELOPE_ARGON2_SALT_SIZE: usize = 16; /// 32 is chosen to match the size of an XChaCha20-Poly1305 key const ENVELOPE_ARGON2_OUTPUT_KEY_SIZE: usize = 32;