fix(prp): zeroize Aes128Prng keystream on drop (ZA-0001)#84
Merged
Conversation
Aes128Prng implemented Zeroize but had no Drop/ZeroizeOnDrop, so its manual zeroize() was never triggered — the PRP builds one per block and drops it without an explicit wipe. The compiler-synthesised drop glue only delegated to the `cipher` field, leaving the key-derived AES-CTR keystream (`data`) plus the position/counter state in the reclaimed stack frame. Add a manual Drop that calls zeroize() and assert the ZeroizeOnDrop marker (matching the KnuthShufflePRP convention, since the derive does not apply cleanly to these types), and extend zeroize() to also clear ptr/ctr. The aes crate's own ZeroizeOnDrop (zeroize feature) already wipes the cipher key schedule when it drops after this — confirmed by LLVM-IR analysis; the wipe survives O2 on normal and unwind paths. Found by the Trail of Bits zeroize-audit skill (ZA-0001), PoC-verified (253/256 keystream bytes survived drop before this change, 0/256 after). Adds two regression tests: zeroize clears all state, and the type is ZeroizeOnDrop. No wire-format change (compat vectors unchanged).
Follow-ups from a code-review pass on the fix:
1. The drop-time wipe of the AES key schedule relies on aes::Aes128's
feature-gated ZeroizeOnDrop (aes = { features = ["zeroize"] }), which
Aes128Prng::zeroize() cannot wipe itself. Add a compile-time assertion
that aes::Aes128: ZeroizeOnDrop so the build fails fast if that feature
is ever lost, instead of silently dropping the key-schedule wipe while
this type still claims ZeroizeOnDrop.
2. Add drop_zeroizes_keystream: a runtime test that exercises the real
Drop -> zeroize() path (ManuallyDrop + drop_in_place + volatile read),
so a removed/emptied `impl Drop` is caught even if the ZeroizeOnDrop
marker is kept. Verified to fail (253/256 bytes survive) when Drop is
neutered, where the prior marker/explicit-zeroize tests still pass.
Reworded the impls_zeroize_on_drop comment (it checks the marker, not
that the wipe runs).
There was a problem hiding this comment.
Pull request overview
This PR addresses ZA-0001 by ensuring Aes128Prng (the AES-CTR keystream PRNG used by the legacy Knuth-shuffle PRP) reliably zeroizes key-derived keystream material and associated state when dropped, rather than relying on callers to invoke zeroize() manually.
Changes:
- Add a manual
Dropimplementation that callszeroize(), and implement theZeroizeOnDropmarker trait forAes128Prng. - Extend
Aes128Prng::zeroize()to wipeptrandctrin addition to thedatakeystream. - Add regression tests validating both explicit
zeroize()wiping and the runtimeDrop -> zeroize()behavior (plus compile-time marker assertions).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
freshtonic
reviewed
Jun 16, 2026
freshtonic
approved these changes
Jun 16, 2026
freshtonic
left a comment
Contributor
There was a problem hiding this comment.
This makes sense and the fix is clean & sufficiently well tested.
Just one minor nit (see comment).
Address review nit: convert impls_zeroize_on_drop from a runtime #[test] to a const _ compile-time assertion, matching the convention already used for the aes::Aes128 marker check.
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.
Closes #85
Summary
Aes128Prng(the AES-CTR keystream PRNG behind the legacy Knuth-shuffle PRP)implemented
Zeroizebut had noDrop/ZeroizeOnDrop, so its manualzeroize()was never triggered — the PRP builds one per block and drops itwithout an explicit wipe. The compiler-synthesised drop glue only delegated to
the
cipherfield, leaving the key-derived AES-CTR keystream (data) plusthe position/counter state in the reclaimed stack frame.
Fix
impl Dropthat callszeroize(), and assert theZeroizeOnDropmarker — matching the existingKnuthShufflePRPconvention,since the derive does not apply cleanly to these types.
zeroize()to also clearptr/ctr, not justdata.cipher's AES key schedule is intentionally left to theaescrate's ownZeroizeOnDrop(thezeroizefeature), which wipes it when it drops afterthis — confirmed by LLVM-IR analysis (the wipe survives
-O2on both thenormal and unwind paths).
Provenance
Found by the Trail of Bits
zeroize-auditskill (finding ZA-0001,medium/confirmed) and PoC-verified: 253/256 keystream bytes survived drop
before this change, 0/256 after. The audit also confirmed nothing is
optimized away and the AES key schedule itself was already wiped.
Verification
zeroizeclears all state, and the type isZeroizeOnDrop.cargo fmt --check,clippy -D warnings, full lib suite — all green.Note for the ORE v2 stack
This targets
main(shipped 0.8.x code). The v2 stack's branchfeat/ore-v2-chainedindependently refactored
prng.rs(ptr: (usize,usize)→ flatusize) but didnot fix ZA-0001 — the finding is still live there. On rebasing the stack onto
a
maincontaining this fix, expect a small mechanical conflict inprng.rs:resolve
self.ptr.0.zeroize(); self.ptr.1.zeroize();→self.ptr.zeroize();.The
Dropimpl, marker,ctrwipe, and tests carry over cleanly.