Skip to content

refactor(crypto): replace legacy aes.rs with hazmat primitives - #1282

Merged
quexten merged 1 commit into
mainfrom
km/aes-replace-legacy
Jul 28, 2026
Merged

refactor(crypto): replace legacy aes.rs with hazmat primitives#1282
quexten merged 1 commit into
mainfrom
km/aes-replace-legacy

Conversation

@quexten

@quexten quexten commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

https://bitwarden.atlassian.net/browse/PM-40756

Replace the legacy aes implementation with the hazmat module based implementations.

impl Aes256CbcHmacKey {
/// Returns the 64-byte composite key (`enc_key || mac_key`) in the layout expected by the
/// [`Aes256CbcHmacSha256`](crate::hazmat::symmetric_encryption::Aes256CbcHmacSha256) cipher.
pub(crate) fn to_composite_key(&self) -> Zeroizing<[u8; 64]> {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future PR will probably replace the internal representation to not carry two distinct subkeys to eliminate copying here.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

🔍 SDK Breaking Change Detection

SDK Version: km/aes-replace-legacy (c2ed0f5)

⚠️ If breaking changes are detected, a corresponding pull request addressing them must be ready for merge in the affected client repository.

Client Status Details
typescript ✅ No breaking changes detected Compilation passed with new SDK version - View Details
android ✅ No breaking changes detected Compilation passed with new SDK version - View Details

Breaking change detection uses the build of the SDK from this branch, including any incompatibities pre-existing on or merged into this branch. Check the workflow logs to confirm.
Results update as workflows complete.

@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.78%. Comparing base (6ae3f66) to head (18c4f36).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1282      +/-   ##
==========================================
- Coverage   85.79%   85.78%   -0.02%     
==========================================
  Files         487      489       +2     
  Lines       69999    70054      +55     
==========================================
+ Hits        60059    60096      +37     
- Misses       9940     9958      +18     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@quexten
quexten requested a review from eligrubb July 22, 2026 13:25
@quexten
quexten marked this pull request as ready for review July 22, 2026 13:25
Copilot AI review requested due to automatic review settings July 22, 2026 13:25
@quexten
quexten requested a review from a team as a code owner July 22, 2026 13:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

This PR removes the legacy aes.rs module and rewires all call sites (EncString encrypt/decrypt, KeyStoreContext, key_connector_key, master_key) to the hazmat Aes256Cbc and Aes256CbcHmacSha256 primitives introduced in the base branch. The refactor preserves cryptographic behavior: the MAC uses the same hmac::Hmac<sha2::Sha256> type, PKCS7/CBC handling is unchanged, IV generation still uses the SDK CRNG, and the new to_composite_key() helper matches the composite-key layout the cipher expects. All touched functions are pub(crate), so there is no external API break from this change.

Code Review Details
  • 🎨 : Legacy-CBC decrypt paths materialize an unzeroized [u8; 32] stack copy of key material via .into(), unlike the sibling to_composite_key() which wraps its output in Zeroizing.
    • crates/bitwarden-crypto/src/keys/key_connector_key.rs:119
    • crates/bitwarden-crypto/src/keys/master_key.rs:176

EncString::Aes256Cbc_B64 { iv, ref data } => {
let legacy_key = self.0.clone();
crate::aes::decrypt_aes256(&iv, data.clone(), &legacy_key)
Aes256Cbc::decrypt(&iv, data, &(*self.0).into())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎨 SUGGESTED: (*self.0).into() materializes an unzeroized [u8; 32] copy of the key on the stack.

Details and fix

(*self.0).into() (and the equivalent (**key).into() in master_key.rs:176) copies the 32-byte key onto the stack as a bare [u8; 32] that is not zeroized on drop. The previous aes.rs path cloned the heap-allocated Pin<Box<Array<u8, U32>>>, which the ZeroizingAllocator wiped on drop.

The to_composite_key() helper added in this same PR deliberately returns Zeroizing<[u8; 64]> for exactly this reason, so wrapping these legacy-CBC copies for consistency would keep the zeroization guarantee uniform:

Aes256Cbc::decrypt(&iv, data, &Zeroizing::new((*self.0).into()))

Low severity — the exposure is transient and decrypt-only — but it restores the zeroization behavior the old code had and matches the pattern used elsewhere in this PR.

eligrubb
eligrubb previously approved these changes Jul 28, 2026
@quexten
quexten force-pushed the km/aes-replace-legacy branch from c2ed0f5 to c4f76af Compare July 28, 2026 03:56
Base automatically changed from km/aes-hazmat to main July 28, 2026 04:20
Route all AES-256-CBC and AES-256-CBC-HMAC-SHA256 operations through the
hazmat::symmetric_encryption primitives and delete the private aes module;
the on-wire format is unchanged. IV generation for the HMAC path moves into
EncString::encrypt_aes256_hmac, and the 64-byte composite key is assembled
via a new Aes256CbcHmacKey::to_composite_key helper.
@quexten
quexten force-pushed the km/aes-replace-legacy branch from c4f76af to 18c4f36 Compare July 28, 2026 04:20
@quexten
quexten merged commit 7eb08fb into main Jul 28, 2026
69 checks passed
@quexten
quexten deleted the km/aes-replace-legacy branch July 28, 2026 13:39
@quexten

quexten commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Given we have good coverage no AES primitives; merging.

bw-ghapp Bot added a commit to bitwarden/sdk-swift that referenced this pull request Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants