pkcs12: zeroize KDF output and intermediates#2283
Open
MarkAtwood wants to merge 1 commit intoRustCrypto:masterfrom
Open
pkcs12: zeroize KDF output and intermediates#2283MarkAtwood wants to merge 1 commit intoRustCrypto:masterfrom
MarkAtwood wants to merge 1 commit intoRustCrypto:masterfrom
Conversation
Return `Zeroizing<Vec<u8>>` from `derive_key`, `derive_key_bmp`, and `derive_key_utf8` so all key material is erased from memory on drop. The returned key being wrapped in `Zeroizing` is documented on each public function. Three buffers are now zeroized: - `out` (the derived key) — wrapped in `Zeroizing`, erased when the caller drops the return value. - `result` (per-round intermediate hash) — wrapped in `Zeroizing`; each reassignment in the inner loop drops and erases the previous value before the next hash is computed. - `init_key` (the S||P buffer holding stretched password bytes) — wrapped in `Zeroizing` for panic-safe erasure on unwind, plus an explicit `.zeroize()` call on the normal return path for eager erasure. Enable `digest/zeroize` (zeroes the digest's internal block buffer, which holds input fragments of id||salt||password between Update calls) and add `hybrid-array` as a direct optional dep with `features = ["zeroize"]`. The latter is required because `digest/zeroize` does not propagate `hybrid-array/zeroize`, leaving `Array<u8, OutputSize>: Zeroize` unsatisfied without the direct dep. Both are gated behind the existing `kdf` feature. `(*result)[..]` explicit dereferences are necessary because `Zeroizing<T>` implements `Deref` but not `Index`. New test functions verified against `openssl kdf PKCS12KDF`: - `pkcs12_key_derive_empty_password`: empty UTF-8 password (BMP null-terminates to [0x00,0x00]); covers all three key types plus a key_len=1 prefix-consistency check. - `pkcs12_key_derive_long_salt`: 72-byte salt spanning two 64-byte SHA-256 blocks (slen=128), exercising the S-padding loop beyond one diversifier block. Breaking change: `derive_key`, `derive_key_bmp`, and `derive_key_utf8` now return `Zeroizing<Vec<u8>>` instead of `Vec<u8>`. Callers that stored the result as `Vec<u8>` or compared it directly with `[u8; N]` will need to call `.as_slice()` or deref-coerce. Most read-only callers are unaffected due to `Deref<Target = Vec<u8>>`. Pre-existing issue (not introduced here): `rounds: i32` — passing 0 or a negative value silently behaves as `rounds = 1` (one hash iteration).
MarkAtwood
added a commit
to MarkAtwood/formats
that referenced
this pull request
Apr 11, 2026
Add two explanatory comments in decrypt_rc2: - Zeroizing::new() wrappers on derive_key_utf8 return values are intentional on this branch (kdf returns Vec<u8>); note they will be removed once kdf PR RustCrypto#2283 (Zeroizing<Vec<u8>> return) lands. - Salt length is bounded by DER input size; no separate allocation cap is needed.
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Zeroizing<Vec<u8>>fromderive_key,derive_key_bmp, andderive_key_utf8so all derived key material is erased on drop.out), each per-round intermediate hash (result), and the stretched S||P password buffer (init_key).init_keyusesZeroizingfor panic-safe erasure on unwind in addition to an explicit.zeroize()on the normal path.digest/zeroize(zeroes the digest block buffer holding input fragments of id||salt||password) and addhybrid-arrayas a direct optional dep withfeatures = ["zeroize"]. The direct dep is required becausedigest/zeroizedoes not propagatehybrid-array/zeroize, leavingArray<u8, OutputSize>: Zeroizeunsatisfied. Ifdigestever addshybrid-array/zeroizeto its ownzeroizefeature, the direct dep can be removed. Both are gated behind thekdffeature.openssl kdf PKCS12KDF: empty UTF-8 password (BMP null-terminates to[0x00, 0x00]) and a 72-byte salt spanning two SHA-256 blocks.Breaking change
derive_key,derive_key_bmp, andderive_key_utf8now returnZeroizing<Vec<u8>>instead ofVec<u8>. This is intentional and is the point of the PR. BecauseZeroizing<Vec<u8>>implementsDeref<Target = Vec<u8>>, most callers that use&result[..]or iterate are unaffected. Callers that stored the result asVec<u8>or compared directly with[u8; N]will need to call.as_slice()or deref-coerce. The crate is0.2.0-pre.0so this break is within the pre-release versioning policy.Dependency notes
cms = "=0.3.0-pre.2"pin is intentional coordinated monorepo pre-release.hybrid-arraydep isversion = "0.4", matching the version already pulled in transitively bydigest.Pre-existing issue (out of scope)
rounds: i32— passing0or a negative value silently behaves asrounds = 1(one hash iteration). This is pre-existing behavior and is not introduced or changed by this PR.Test plan
cargo test --features kdf -p pkcs12— 6 KDF tests pass, including new empty-password and long-salt casescargo clippy --all-features -p pkcs12 -- -D warnings— cleancargo hack check --feature-powerset --no-dev-deps -p pkcs12— all 7 feature combinations compileRUSTDOCFLAGS="--cfg docsrs -D warnings" cargo +nightly doc --no-deps --all-features -p pkcs12— cleancargo +1.85 test --features kdf -p pkcs12— passes at MSRV