From ab173fb46b87de8427edc789ad9835a728f83b24 Mon Sep 17 00:00:00 2001 From: Hinton Date: Thu, 28 Aug 2025 11:23:01 +0200 Subject: [PATCH 1/3] Migrate more b64 --- Cargo.lock | 7 ++-- crates/bitwarden-exporters/Cargo.toml | 2 +- crates/bitwarden-exporters/src/cxf/import.rs | 32 ++++++------------- crates/bitwarden-exporters/src/cxf/login.rs | 19 ++++++----- .../bitwarden-exporters/src/encrypted_json.rs | 6 ++-- crates/bitwarden-fido/Cargo.toml | 2 +- crates/bitwarden-fido/src/lib.rs | 31 ++++++++---------- crates/bitwarden-fido/src/types.rs | 12 +++---- crates/bitwarden-send/Cargo.toml | 2 +- crates/bitwarden-send/src/send.rs | 16 ++++------ crates/bitwarden-vault/Cargo.toml | 3 +- .../bitwarden-vault/src/cipher/attachment.rs | 18 +++++------ crates/bitwarden-vault/src/cipher/login.rs | 8 ++--- 13 files changed, 68 insertions(+), 90 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1b92ed84..63b0b0612 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -518,10 +518,10 @@ dependencies = [ name = "bitwarden-exporters" version = "1.0.0" dependencies = [ - "base64", "bitwarden-collections", "bitwarden-core", "bitwarden-crypto", + "bitwarden-encoding", "bitwarden-error", "bitwarden-fido", "bitwarden-ssh", @@ -544,9 +544,9 @@ name = "bitwarden-fido" version = "1.0.0" dependencies = [ "async-trait", - "base64", "bitwarden-core", "bitwarden-crypto", + "bitwarden-encoding", "bitwarden-vault", "chrono", "coset", @@ -608,10 +608,10 @@ dependencies = [ name = "bitwarden-send" version = "1.0.0" dependencies = [ - "base64", "bitwarden-api-api", "bitwarden-core", "bitwarden-crypto", + "bitwarden-encoding", "chrono", "serde", "serde_repr", @@ -763,7 +763,6 @@ dependencies = [ name = "bitwarden-vault" version = "1.0.0" dependencies = [ - "base64", "bitwarden-api-api", "bitwarden-collections", "bitwarden-core", diff --git a/crates/bitwarden-exporters/Cargo.toml b/crates/bitwarden-exporters/Cargo.toml index 4f39acfb8..677b91678 100644 --- a/crates/bitwarden-exporters/Cargo.toml +++ b/crates/bitwarden-exporters/Cargo.toml @@ -25,10 +25,10 @@ wasm = [ ] # WebAssembly bindings [dependencies] -base64 = ">=0.22.1, <0.23" bitwarden-collections = { workspace = true } bitwarden-core = { workspace = true } bitwarden-crypto = { workspace = true } +bitwarden-encoding = { workspace = true } bitwarden-error = { workspace = true } bitwarden-fido = { workspace = true } bitwarden-ssh = { workspace = true } diff --git a/crates/bitwarden-exporters/src/cxf/import.rs b/crates/bitwarden-exporters/src/cxf/import.rs index c80eabff5..0123bac26 100644 --- a/crates/bitwarden-exporters/src/cxf/import.rs +++ b/crates/bitwarden-exporters/src/cxf/import.rs @@ -257,9 +257,8 @@ struct GroupedCredentials { #[cfg(test)] mod tests { - use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use chrono::{Duration, Month}; - use credential_exchange_format::{CreditCardCredential, EditableFieldYearMonth}; + use credential_exchange_format::{B64Url, CreditCardCredential, EditableFieldYearMonth}; use super::*; @@ -302,35 +301,24 @@ mod tests { #[test] fn test_parse_passkey() { let item = Item { - id: URL_SAFE_NO_PAD - .decode("Njk1RERENTItNkQ0Ny00NERBLTlFN0EtNDM1MjNEQjYzNjVF") - .unwrap() - .as_slice() - .into(), + id: B64Url::try_from("Njk1RERENTItNkQ0Ny00NERBLTlFN0EtNDM1MjNEQjYzNjVF") + .unwrap(), creation_at: Some(1732181986), modified_at: Some(1732182026), title: "opotonniee.github.io".to_string(), subtitle: None, favorite: None, credentials: vec![Credential::Passkey(Box::new(PasskeyCredential { - credential_id: URL_SAFE_NO_PAD - .decode("6NiHiekW4ZY8vYHa-ucbvA") - .unwrap() - .as_slice() - .into(), + credential_id: B64Url::try_from("6NiHiekW4ZY8vYHa-ucbvA") + .unwrap(), rp_id: "opotonniee.github.io".to_string(), username: "alex muller".to_string(), user_display_name: "alex muller".to_string(), - user_handle: URL_SAFE_NO_PAD - .decode("YWxleCBtdWxsZXI") - .unwrap() - .as_slice() - .into(), - key: URL_SAFE_NO_PAD - .decode("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPzvtWYWmIsvqqr3LsZB0K-cbjuhJSGTGziL1LksHAPShRANCAAT-vqHTyEDS9QBNNi2BNLyu6TunubJT_L3G3i7KLpEDhMD15hi24IjGBH0QylJIrvlT4JN2tdRGF436XGc-VoAl") - .unwrap() - .as_slice() - .into(), + user_handle: B64Url::try_from("YWxleCBtdWxsZXI") + .unwrap(), + key: B64Url::try_from( + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPzvtWYWmIsvqqr3LsZB0K-cbjuhJSGTGziL1LksHAPShRANCAAT-vqHTyEDS9QBNNi2BNLyu6TunubJT_L3G3i7KLpEDhMD15hi24IjGBH0QylJIrvlT4JN2tdRGF436XGc-VoAl") + .unwrap(), fido2_extensions: None, }))], tags: None, diff --git a/crates/bitwarden-exporters/src/cxf/login.rs b/crates/bitwarden-exporters/src/cxf/login.rs index fa8106c74..a74f470e6 100644 --- a/crates/bitwarden-exporters/src/cxf/login.rs +++ b/crates/bitwarden-exporters/src/cxf/login.rs @@ -3,14 +3,13 @@ //! Handles conversion between internal [Login] and credential exchange [BasicAuthCredential] and //! [PasskeyCredential]. -use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use bitwarden_core::MissingFieldError; use bitwarden_fido::{string_to_guid_bytes, InvalidGuid}; use bitwarden_vault::{FieldType, Totp, TotpAlgorithm}; use chrono::{DateTime, Utc}; use credential_exchange_format::{ - AndroidAppIdCredential, BasicAuthCredential, CredentialScope, OTPHashAlgorithm, - PasskeyCredential, TotpCredential, + AndroidAppIdCredential, B64Url, BasicAuthCredential, CredentialScope, NotB64UrlEncoded, + OTPHashAlgorithm, PasskeyCredential, TotpCredential, }; use thiserror::Error; @@ -67,7 +66,7 @@ pub(super) fn to_login( key_type: "public-key".to_string(), key_algorithm: "ECDSA".to_string(), key_curve: "P-256".to_string(), - key_value: URL_SAFE_NO_PAD.encode(&p.key), + key_value: p.key.to_string(), rp_id: p.rp_id.clone(), user_handle: Some(p.user_handle.to_string()), user_name: Some(p.username.clone()), @@ -161,8 +160,8 @@ pub enum PasskeyError { InvalidGuid(InvalidGuid), #[error(transparent)] MissingField(MissingFieldError), - #[error(transparent)] - InvalidBase64(#[from] base64::DecodeError), + #[error("Data isn't base64url encoded")] + InvalidBase64(NotB64UrlEncoded), } impl TryFrom for PasskeyCredential { @@ -182,11 +181,11 @@ impl TryFrom for PasskeyCredential { user_display_name: value.user_display_name.unwrap_or_default(), user_handle: value .user_handle - .map(|v| URL_SAFE_NO_PAD.decode(v)) - .transpose()? - .map(|v| v.into()) + .map(|v| B64Url::try_from(v.as_str())) + .transpose() + .map_err(PasskeyError::InvalidBase64)? .ok_or(PasskeyError::MissingField(MissingFieldError("user_handle")))?, - key: URL_SAFE_NO_PAD.decode(value.key_value)?.into(), + key: B64Url::try_from(value.key_value.as_str()).map_err(PasskeyError::InvalidBase64)?, fido2_extensions: None, }) } diff --git a/crates/bitwarden-exporters/src/encrypted_json.rs b/crates/bitwarden-exporters/src/encrypted_json.rs index 32f1c9307..d0a855738 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_encoding::B64; use serde::Serialize; use thiserror::Error; use uuid::Uuid; @@ -44,7 +44,7 @@ pub(crate) fn export_encrypted_json( }; let salt = generate_random_bytes::<[u8; 16]>(); - let salt = STANDARD.encode(salt); + let salt = B64::from(salt.as_slice()); let key = PinKey::derive(password.as_bytes(), salt.as_bytes(), &kdf)?; let enc_key_validation = Uuid::new_v4().to_string(); @@ -52,7 +52,7 @@ pub(crate) fn export_encrypted_json( let encrypted_export = EncryptedJsonExport { encrypted: true, password_protected: true, - salt, + salt: salt.to_string(), kdf_type, kdf_iterations, kdf_memory, diff --git a/crates/bitwarden-fido/Cargo.toml b/crates/bitwarden-fido/Cargo.toml index 0f880a945..9d8637b96 100644 --- a/crates/bitwarden-fido/Cargo.toml +++ b/crates/bitwarden-fido/Cargo.toml @@ -19,9 +19,9 @@ uniffi = ["dep:uniffi", "bitwarden-core/uniffi", "bitwarden-vault/uniffi"] [dependencies] async-trait = { workspace = true } -base64 = ">=0.22.1, <0.23" bitwarden-core = { workspace = true } bitwarden-crypto = { workspace = true } +bitwarden-encoding = { workspace = true } bitwarden-vault = { workspace = true } chrono = { workspace = true } coset = ">=0.3.7, <0.4" diff --git a/crates/bitwarden-fido/src/lib.rs b/crates/bitwarden-fido/src/lib.rs index 79a431daa..4bf5c91bd 100644 --- a/crates/bitwarden-fido/src/lib.rs +++ b/crates/bitwarden-fido/src/lib.rs @@ -1,8 +1,8 @@ #![doc = include_str!("../README.md")] -use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use bitwarden_core::key_management::KeyIds; use bitwarden_crypto::KeyStoreContext; +use bitwarden_encoding::{B64Url, NotB64UrlEncoded}; use bitwarden_vault::{ CipherError, CipherView, Fido2CredentialFullView, Fido2CredentialNewView, Fido2CredentialView, }; @@ -78,7 +78,7 @@ impl CipherViewContainer { #[derive(Debug, Error)] pub enum Fido2Error { #[error(transparent)] - DecodeError(#[from] base64::DecodeError), + DecodeError(#[from] NotB64UrlEncoded), #[error(transparent)] UnknownEnum(#[from] UnknownEnum), @@ -115,19 +115,16 @@ fn try_from_credential_full_view(value: Fido2CredentialFullView) -> Result = value.credential_id.into(); let user_handle = value .user_handle - .map(|u| URL_SAFE_NO_PAD.encode(u.to_vec())); - let key_value = URL_SAFE_NO_PAD.encode(cose_key_to_pkcs8(&value.key)?); + .map(|u| B64Url::from(u.to_vec()).to_string()); + let key_value = B64Url::from(cose_key_to_pkcs8(&value.key)?).to_string(); Ok(Fido2CredentialFullView { credential_id: guid_bytes_to_string(&cred_id)?, @@ -175,7 +172,7 @@ pub(crate) fn try_from_credential_new_view( rp: &passkey::types::ctap2::make_credential::PublicKeyCredentialRpEntity, ) -> Result { let cred_id: Vec = vec![0; 16]; - let user_handle = URL_SAFE_NO_PAD.encode(user.id.to_vec()); + let user_handle = B64Url::from(user.id.to_vec()).to_string(); Ok(Fido2CredentialNewView { // TODO: Why do we have a credential id here? @@ -201,8 +198,8 @@ pub(crate) fn try_from_credential_full( options: passkey::types::ctap2::get_assertion::Options, ) -> Result { let cred_id: Vec = value.credential_id.into(); - let key_value = URL_SAFE_NO_PAD.encode(cose_key_to_pkcs8(&value.key)?); - let user_handle = URL_SAFE_NO_PAD.encode(user.id.to_vec()); + let key_value = B64Url::from(cose_key_to_pkcs8(&value.key)?).to_string(); + let user_handle = B64Url::from(user.id.to_vec()).to_string(); Ok(Fido2CredentialFullView { credential_id: guid_bytes_to_string(&cred_id)?, @@ -243,10 +240,8 @@ pub struct InvalidGuid; #[allow(missing_docs)] pub fn string_to_guid_bytes(source: &str) -> Result, InvalidGuid> { if source.starts_with("b64.") { - let bytes = URL_SAFE_NO_PAD - .decode(source.trim_start_matches("b64.")) - .map_err(|_| InvalidGuid)?; - Ok(bytes) + let bytes = B64Url::try_from(source.trim_start_matches("b64.")).map_err(|_| InvalidGuid)?; + Ok(bytes.as_bytes().to_vec()) } else { let Ok(uuid) = uuid::Uuid::try_parse(source) else { return Err(InvalidGuid); diff --git a/crates/bitwarden-fido/src/types.rs b/crates/bitwarden-fido/src/types.rs index 1a7a80c61..57e2744a9 100644 --- a/crates/bitwarden-fido/src/types.rs +++ b/crates/bitwarden-fido/src/types.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; -use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use bitwarden_core::key_management::KeyIds; use bitwarden_crypto::{CryptoError, KeyStoreContext}; +use bitwarden_encoding::{B64Url, NotB64UrlEncoded}; use bitwarden_vault::{CipherListView, CipherListViewType, CipherView, LoginListView}; use passkey::types::webauthn::UserVerificationRequirement; use reqwest::Url; @@ -61,7 +61,7 @@ pub enum Fido2CredentialAutofillViewError { CryptoError(#[from] CryptoError), #[error(transparent)] - Base64DecodeError(#[from] base64::DecodeError), + Base64DecodeError(#[from] NotB64UrlEncoded), } impl Fido2CredentialAutofillView { @@ -77,7 +77,7 @@ impl Fido2CredentialAutofillView { .filter_map(|c| -> Option> { c.user_handle .as_ref() - .map(|u| URL_SAFE_NO_PAD.decode(u)) + .map(|u| B64Url::try_from(u.as_str())) .map(|user_handle| { Ok(Fido2CredentialAutofillView { credential_id: string_to_guid_bytes(&c.credential_id)?, @@ -86,7 +86,7 @@ impl Fido2CredentialAutofillView { .ok_or(Fido2CredentialAutofillViewError::MissingCipherId)? .into(), rp_id: c.rp_id.clone(), - user_handle: user_handle?, + user_handle: user_handle?.as_bytes().to_vec(), user_name_for_ui: c .user_name .none_whitespace() @@ -116,7 +116,7 @@ impl Fido2CredentialAutofillView { .filter_map(|c| -> Option> { c.user_handle .as_ref() - .map(|u| URL_SAFE_NO_PAD.decode(u)) + .map(|u| B64Url::try_from(u.as_str())) .map(|user_handle| { Ok(Fido2CredentialAutofillView { credential_id: string_to_guid_bytes(&c.credential_id)?, @@ -125,7 +125,7 @@ impl Fido2CredentialAutofillView { .ok_or(Fido2CredentialAutofillViewError::MissingCipherId)? .into(), rp_id: c.rp_id.clone(), - user_handle: user_handle?, + user_handle: user_handle?.as_bytes().to_vec(), user_name_for_ui: c .user_name .none_whitespace() diff --git a/crates/bitwarden-send/Cargo.toml b/crates/bitwarden-send/Cargo.toml index 161be4b93..ee6522f25 100644 --- a/crates/bitwarden-send/Cargo.toml +++ b/crates/bitwarden-send/Cargo.toml @@ -22,10 +22,10 @@ uniffi = [ ] # Uniffi bindings [dependencies] -base64 = ">=0.22.1, <0.23" bitwarden-api-api = { workspace = true } bitwarden-core = { workspace = true } bitwarden-crypto = { workspace = true } +bitwarden-encoding = { workspace = true } chrono = { workspace = true } serde = { workspace = true } serde_repr = { workspace = true } diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index f00632136..6c2bfa946 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -1,7 +1,3 @@ -use base64::{ - engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD}, - Engine, -}; use bitwarden_api_api::models::{SendFileModel, SendResponseModel, SendTextModel}; use bitwarden_core::{ key_management::{KeyIds, SymmetricKeyId}, @@ -11,6 +7,7 @@ use bitwarden_crypto::{ generate_random_bytes, CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext, OctetStreamBytes, PrimitiveEncryptable, }; +use bitwarden_encoding::{B64Url, B64}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; @@ -254,7 +251,7 @@ impl Decryptable for Send { name: self.name.decrypt(ctx, key).ok().unwrap_or_default(), notes: self.notes.decrypt(ctx, key).ok().flatten(), - key: Some(URL_SAFE_NO_PAD.encode(k)), + key: Some(B64Url::from(k).to_string()), new_password: None, has_password: self.password.is_some(), @@ -312,9 +309,10 @@ impl CompositeEncryptable for SendView { // the stretched key let k = match (&self.key, &self.id) { // Existing send, decrypt key - (Some(k), _) => URL_SAFE_NO_PAD - .decode(k) - .map_err(|_| CryptoError::InvalidKey)?, + (Some(k), _) => B64Url::try_from(k.as_str()) + .map_err(|_| CryptoError::InvalidKey)? + .as_bytes() + .to_vec(), // New send, generate random key (None, None) => { let key = generate_random_bytes::<[u8; 16]>(); @@ -334,7 +332,7 @@ impl CompositeEncryptable for SendView { 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) + B64::from(password.as_slice()).to_string() }), r#type: self.r#type, diff --git a/crates/bitwarden-vault/Cargo.toml b/crates/bitwarden-vault/Cargo.toml index fd5e40540..2e837c859 100644 --- a/crates/bitwarden-vault/Cargo.toml +++ b/crates/bitwarden-vault/Cargo.toml @@ -30,12 +30,11 @@ wasm = [ ] # WASM support [dependencies] -base64 = ">=0.22.1, <0.23" bitwarden-api-api = { workspace = true } bitwarden-collections = { workspace = true } bitwarden-core = { workspace = true, features = ["internal"] } bitwarden-crypto = { workspace = true } -bitwarden-encoding.workspace = true +bitwarden-encoding = { workspace = true } bitwarden-error = { workspace = true } bitwarden-state = { workspace = true } bitwarden-uuid = { workspace = true } diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index af63efc5e..0af8e01ab 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -220,9 +220,9 @@ impl TryFrom for Attachment #[cfg(test)] mod tests { - use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_core::key_management::create_test_crypto_with_user_key; use bitwarden_crypto::{EncString, SymmetricCryptoKey}; + use bitwarden_encoding::B64; use crate::{ cipher::cipher::{CipherRepromptType, CipherType}, @@ -342,18 +342,18 @@ mod tests { revision_date: "2023-07-27T19:28:05.240Z".parse().unwrap(), }; - let enc_file = STANDARD.decode(b"Ao00qr1xLsV+ZNQpYZ/UwEwOWo3hheKwCYcOGIbsorZ6JIG2vLWfWEXCVqP0hDuzRvmx8otApNZr8pJYLNwCe1aQ+ySHQYGkdubFjoMojulMbQ959Y4SJ6Its/EnVvpbDnxpXTDpbutDxyhxfq1P3lstL2G9rObJRrxiwdGlRGu1h94UA1fCCkIUQux5LcqUee6W4MyQmRnsUziH8gGzmtI=").unwrap(); - let original = STANDARD.decode(b"rMweTemxOL9D0iWWfRxiY3enxiZ5IrwWD6ef2apGO6MvgdGhy2fpwmATmn7BpSj9lRumddLLXm7u8zSp6hnXt1hS71YDNh78LjGKGhGL4sbg8uNnpa/I6GK/83jzqGYN7+ESbg==").unwrap(); + let enc_file = B64::try_from("Ao00qr1xLsV+ZNQpYZ/UwEwOWo3hheKwCYcOGIbsorZ6JIG2vLWfWEXCVqP0hDuzRvmx8otApNZr8pJYLNwCe1aQ+ySHQYGkdubFjoMojulMbQ959Y4SJ6Its/EnVvpbDnxpXTDpbutDxyhxfq1P3lstL2G9rObJRrxiwdGlRGu1h94UA1fCCkIUQux5LcqUee6W4MyQmRnsUziH8gGzmtI=").unwrap(); + let original = B64::try_from("rMweTemxOL9D0iWWfRxiY3enxiZ5IrwWD6ef2apGO6MvgdGhy2fpwmATmn7BpSj9lRumddLLXm7u8zSp6hnXt1hS71YDNh78LjGKGhGL4sbg8uNnpa/I6GK/83jzqGYN7+ESbg==").unwrap(); let dec = key_store .decrypt(&AttachmentFile { cipher, attachment, - contents: EncString::from_buffer(&enc_file).unwrap(), + contents: EncString::from_buffer(enc_file.as_bytes()).unwrap(), }) .unwrap(); - assert_eq!(dec, original); + assert_eq!(dec, original.as_bytes()); } #[test] @@ -400,17 +400,17 @@ mod tests { revision_date: "2023-07-27T19:28:05.240Z".parse().unwrap(), }; - let enc_file = STANDARD.decode(b"AsQLXOBHrJ8porroTUlPxeJOm9XID7LL9D2+KwYATXEpR1EFjLBpcCvMmnqcnYLXIEefe9TCeY4Us50ux43kRSpvdB7YkjxDKV0O1/y6tB7qC4vvv9J9+O/uDEnMx/9yXuEhAW/LA/TsU/WAgxkOM0uTvm8JdD9LUR1z9Ql7zOWycMVzkvGsk2KBNcqAdrotS5FlDftZOXyU8pWecNeyA/w=").unwrap(); - let original = STANDARD.decode(b"rMweTemxOL9D0iWWfRxiY3enxiZ5IrwWD6ef2apGO6MvgdGhy2fpwmATmn7BpSj9lRumddLLXm7u8zSp6hnXt1hS71YDNh78LjGKGhGL4sbg8uNnpa/I6GK/83jzqGYN7+ESbg==").unwrap(); + let enc_file = B64::try_from("AsQLXOBHrJ8porroTUlPxeJOm9XID7LL9D2+KwYATXEpR1EFjLBpcCvMmnqcnYLXIEefe9TCeY4Us50ux43kRSpvdB7YkjxDKV0O1/y6tB7qC4vvv9J9+O/uDEnMx/9yXuEhAW/LA/TsU/WAgxkOM0uTvm8JdD9LUR1z9Ql7zOWycMVzkvGsk2KBNcqAdrotS5FlDftZOXyU8pWecNeyA/w=").unwrap(); + let original = B64::try_from("rMweTemxOL9D0iWWfRxiY3enxiZ5IrwWD6ef2apGO6MvgdGhy2fpwmATmn7BpSj9lRumddLLXm7u8zSp6hnXt1hS71YDNh78LjGKGhGL4sbg8uNnpa/I6GK/83jzqGYN7+ESbg==").unwrap(); let dec = key_store .decrypt(&AttachmentFile { cipher, attachment, - contents: EncString::from_buffer(&enc_file).unwrap(), + contents: EncString::from_buffer(enc_file.as_bytes()).unwrap(), }) .unwrap(); - assert_eq!(dec, original); + assert_eq!(dec, original.as_bytes()); } } diff --git a/crates/bitwarden-vault/src/cipher/login.rs b/crates/bitwarden-vault/src/cipher/login.rs index 5e6d952ea..c61c500e7 100644 --- a/crates/bitwarden-vault/src/cipher/login.rs +++ b/crates/bitwarden-vault/src/cipher/login.rs @@ -1,4 +1,3 @@ -use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_api_api::models::{CipherLoginModel, CipherLoginUriModel}; use bitwarden_core::{ key_management::{KeyIds, SymmetricKeyId}, @@ -8,6 +7,7 @@ use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, PrimitiveEncryptable, }; +use bitwarden_encoding::B64; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; @@ -63,21 +63,21 @@ impl LoginUriView { let Some(cs) = &self.uri_checksum else { return false; }; - let Ok(cs) = STANDARD.decode(cs) else { + let Ok(cs) = B64::try_from(cs.as_str()) else { return false; }; use sha2::Digest; let uri_hash = sha2::Sha256::new().chain_update(uri.as_bytes()).finalize(); - uri_hash.as_slice() == cs + uri_hash.as_slice() == cs.as_bytes() } pub(crate) fn generate_checksum(&mut self) { if let Some(uri) = &self.uri { use sha2::Digest; let uri_hash = sha2::Sha256::new().chain_update(uri.as_bytes()).finalize(); - let uri_hash = STANDARD.encode(uri_hash.as_slice()); + let uri_hash = B64::from(uri_hash.as_slice()).to_string(); self.uri_checksum = Some(uri_hash); } } From 5fc8206bdce111f6b896b7f5cafa69d8c43c3f27 Mon Sep 17 00:00:00 2001 From: Hinton Date: Thu, 28 Aug 2025 12:11:41 +0200 Subject: [PATCH 2/3] lint --- crates/bitwarden-fido/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-fido/src/lib.rs b/crates/bitwarden-fido/src/lib.rs index 4bf5c91bd..51f46eee7 100644 --- a/crates/bitwarden-fido/src/lib.rs +++ b/crates/bitwarden-fido/src/lib.rs @@ -116,7 +116,7 @@ fn try_from_credential_full_view(value: Fido2CredentialFullView) -> Result Date: Thu, 11 Sep 2025 20:10:42 +0200 Subject: [PATCH 3/3] Add into_bytes --- crates/bitwarden-core/src/key_management/crypto.rs | 2 +- .../src/safe/password_protected_key_envelope.rs | 2 +- crates/bitwarden-encoding/src/b64.rs | 5 +++++ crates/bitwarden-encoding/src/b64url.rs | 5 +++++ crates/bitwarden-fido/src/lib.rs | 2 +- crates/bitwarden-fido/src/types.rs | 4 ++-- 6 files changed, 15 insertions(+), 5 deletions(-) diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index f5253a9a7..4c61279e9 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -233,7 +233,7 @@ pub(super) async fn initialize_user_crypto( master_key, user_key, } => { - let mut bytes = master_key.as_bytes().to_vec(); + let mut bytes = master_key.into_bytes(); let master_key = MasterKey::try_from(bytes.as_mut_slice())?; client 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 f41fe07b2..233efeb51 100644 --- a/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs +++ b/crates/bitwarden-crypto/src/safe/password_protected_key_envelope.rs @@ -272,7 +272,7 @@ impl FromStr for PasswordProtectedKeyEnvelope { "Invalid PasswordProtectedKeyEnvelope Base64 encoding".to_string(), ) })?; - Self::try_from(&data.as_bytes().to_vec()).map_err(|_| { + Self::try_from(&data.into_bytes()).map_err(|_| { PasswordProtectedKeyEnvelopeError::ParsingError( "Failed to parse PasswordProtectedKeyEnvelope".to_string(), ) diff --git a/crates/bitwarden-encoding/src/b64.rs b/crates/bitwarden-encoding/src/b64.rs index be73913a4..322f820e5 100644 --- a/crates/bitwarden-encoding/src/b64.rs +++ b/crates/bitwarden-encoding/src/b64.rs @@ -31,6 +31,11 @@ impl B64 { pub fn as_bytes(&self) -> &[u8] { &self.0 } + + /// Returns the inner byte vector. + pub fn into_bytes(self) -> Vec { + self.0 + } } // We manually implement this to handle both `String` and `&str` diff --git a/crates/bitwarden-encoding/src/b64url.rs b/crates/bitwarden-encoding/src/b64url.rs index b006b1fc5..e9981f417 100644 --- a/crates/bitwarden-encoding/src/b64url.rs +++ b/crates/bitwarden-encoding/src/b64url.rs @@ -16,6 +16,11 @@ impl B64Url { pub fn as_bytes(&self) -> &[u8] { &self.0 } + + /// Returns the inner byte vector. + pub fn into_bytes(self) -> Vec { + self.0 + } } impl From> for B64Url { diff --git a/crates/bitwarden-fido/src/lib.rs b/crates/bitwarden-fido/src/lib.rs index 51f46eee7..52d11b4b2 100644 --- a/crates/bitwarden-fido/src/lib.rs +++ b/crates/bitwarden-fido/src/lib.rs @@ -124,7 +124,7 @@ fn try_from_credential_full_view(value: Fido2CredentialFullView) -> Result