Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(prp): zeroize Aes128Prng keystream on drop (ZA-0001)#84

Merged
coderdan merged 3 commits into
mainfrom
fix/za-0001-prng-zeroize
Jun 16, 2026
Merged

fix(prp): zeroize Aes128Prng keystream on drop (ZA-0001)#84
coderdan merged 3 commits into
mainfrom
fix/za-0001-prng-zeroize

Conversation

@coderdan

@coderdan coderdan commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Closes #85

Summary

Aes128Prng (the AES-CTR keystream PRNG behind the legacy Knuth-shuffle PRP)
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.

Fix

  • Add a manual impl Drop that calls zeroize(), and assert the
    ZeroizeOnDrop marker — matching the existing KnuthShufflePRP convention,
    since the derive does not apply cleanly to these types.
  • Extend zeroize() to also clear ptr/ctr, not just data.
  • cipher's AES key schedule is intentionally left to the aes crate's own
    ZeroizeOnDrop (the zeroize feature), which wipes it when it drops after
    this — confirmed by LLVM-IR analysis (the wipe survives -O2 on both the
    normal and unwind paths).

Provenance

Found by the Trail of Bits zeroize-audit skill (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

  • New regression tests: zeroize clears all state, and the type is
    ZeroizeOnDrop.
  • cargo fmt --check, clippy -D warnings, full lib suite — all green.
  • No wire-format change (compat vectors unchanged).

Note for the ORE v2 stack

This targets main (shipped 0.8.x code). The v2 stack's branch feat/ore-v2-chained
independently refactored prng.rs (ptr: (usize,usize) → flat usize) but did
not fix ZA-0001 — the finding is still live there. On rebasing the stack onto
a main containing this fix, expect a small mechanical conflict in prng.rs:
resolve self.ptr.0.zeroize(); self.ptr.1.zeroize();self.ptr.zeroize();.
The Drop impl, marker, ctr wipe, and tests carry over cleanly.

coderdan added 2 commits June 16, 2026 09:59
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).

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.

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 Drop implementation that calls zeroize(), and implement the ZeroizeOnDrop marker trait for Aes128Prng.
  • Extend Aes128Prng::zeroize() to wipe ptr and ctr in addition to the data keystream.
  • Add regression tests validating both explicit zeroize() wiping and the runtime Drop -> zeroize() behavior (plus compile-time marker assertions).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/ore-rs/src/primitives/prp/prng.rs Outdated

@freshtonic freshtonic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.
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.

Aes128Prng keystream is not zeroized on drop (ZA-0001)

3 participants