Skip to content

[PM-22034] (fix): incomplete zeroization after conversion to ordinary buffers#1379

Merged
gilescope merged 6 commits into
mainfrom
fix/53-incomplete-zeroization-buffers
Apr 29, 2026
Merged

[PM-22034] (fix): incomplete zeroization after conversion to ordinary buffers#1379
gilescope merged 6 commits into
mainfrom
fix/53-incomplete-zeroization-buffers

Conversation

@m2ux

@m2ux m2ux commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix incomplete zeroization of intermediate secret buffers in the midnight-node toolkit, ensuring wallet seeds and signing keys are erased from heap memory after use. This addresses audit finding A2 (Low severity) from the Least Authority Node DIFF Audit.

🎫 PM-22034 📐 Engineering


Motivation

The toolkit's signing key type zeroizes on drop, but secret values are converted into ordinary String and Vec<u8> buffers for CLI argument passing. These containers lack wipe-on-drop guarantees, leaving sensitive material in process memory until the OS reclaims the pages. An attacker with memory read access could recover these secrets long after they should have been erased.

This change explicitly zeroizes the intermediate buffers after they are no longer needed, closing the gap identified in the security audit.


Changes

Implementation:

  • Add zeroize import and a zeroize_string helper to util/toolkit/src/toolkit_js/mod.rs
  • Zeroize intermediate Vec<u8> serialization buffer in execute_deploy after hex encoding
  • Zeroize hex-encoded signing key string in execute_deploy after execute_js returns
  • Zeroize hex-encoded seed string in execute_maintain after execute_js returns
  • Verify build (cargo check), tests (cargo test), and lint (cargo clippy) pass

🧪 QA Note — Zeroization Testing

Zeroization correctness (ensuring heap buffers are actually overwritten with zeros before drop) cannot be validated by standard Rust unit tests because the memory is freed immediately afterward and the borrow checker prevents inspecting it. The zeroize crate provides this guarantee via volatile writes and compiler fences, and its own upstream tests cover the correctness of the zeroization primitives.

For this PR, validation is limited to:

  • Successful compilation (cargo check / cargo clippy)
  • Existing tests continue to pass (cargo test)
  • Code review confirming zeroize is called on the correct buffers at the correct points

If QA requires dedicated zeroization verification (e.g., Miri runs, custom allocator tests, or manual memory inspection), please flag this before merge and we can add the appropriate test infrastructure.


📌 Submission Checklist

  • Changes are backward-compatible (or flagged if breaking)
  • Pull request description explains why the change is needed
  • Self-reviewed the diff
  • I have included a change file, or skipped for this reason: [reason]
  • If the changes introduce a new feature, I have bumped the node minor version
  • Update documentation (if relevant)
  • No new todos introduced

🔱 Fork Strategy

  • Node Runtime Update
  • Node Client Update
  • Other
  • N/A

🗹 TODO before merging

  • Ready for review

Issue: midnightntwrk/midnight-security#53
Signed-off-by: Mike Clay <mike.clay@shielded.io>
Made-with: Cursor
@m2ux m2ux force-pushed the fix/53-incomplete-zeroization-buffers branch from 2cb178e to bc38557 Compare April 22, 2026 10:19
m2ux added 2 commits April 22, 2026 12:17
Add zeroize dependency and use it to clear intermediate buffers
and hex-encoded signing key strings in execute_deploy and
execute_maintain to prevent sensitive material from lingering
in memory.

- Add zeroize = "1" to util/toolkit/Cargo.toml
- Import zeroize::Zeroize in toolkit_js/mod.rs
- Add zeroize_string helper for heap-allocated Strings
- Zeroize raw signing key bytes after hex encoding in deploy
- Zeroize hex signing key after toolkit-js execution in deploy
- Zeroize new_authority hex after toolkit-js execution in maintain

Refs: #53
Signed-off-by: Mike Clay <mike.clay@shielded.io>
Made-with: Cursor
…oization

- Replace custom zeroize_string helper with direct String::zeroize() from the zeroize crate, eliminating local unsafe.

- Fix error-path zeroization gap by zeroizing strings before propagating execute_js errors via ? operator.

Signed-off-by: Mike Clay <mike.clay@shielded.io>
Made-with: Cursor
@m2ux m2ux marked this pull request as ready for review April 22, 2026 14:23
@m2ux m2ux requested a review from a team as a code owner April 22, 2026 14:23
@m2ux m2ux marked this pull request as draft April 22, 2026 14:26
@m2ux m2ux marked this pull request as ready for review April 22, 2026 14:54
m2ux added 2 commits April 22, 2026 15:56
Reordered imports to satisfy cargo fmt

Signed-off-by: Mike Clay <mike.clay@shielded.io>
Made-with: Cursor
Signed-off-by: Mike Clay <mike.clay@shielded.io>
Made-with: Cursor
@m2ux m2ux self-assigned this Apr 22, 2026
Comment thread util/toolkit/Cargo.toml Outdated
@m2ux m2ux changed the title fix: incomplete zeroization after conversion to ordinary buffers fix(PM22034): incomplete zeroization after conversion to ordinary buffers Apr 29, 2026
@m2ux m2ux changed the title fix(PM22034): incomplete zeroization after conversion to ordinary buffers fix(PM-22034): incomplete zeroization after conversion to ordinary buffers Apr 29, 2026
@m2ux m2ux changed the title fix(PM-22034): incomplete zeroization after conversion to ordinary buffers [PM-22034] (fix): incomplete zeroization after conversion to ordinary buffers Apr 29, 2026
@gilescope gilescope added this pull request to the merge queue Apr 29, 2026
Merged via the queue into main with commit f78562f Apr 29, 2026
35 of 36 checks passed
@gilescope gilescope deleted the fix/53-incomplete-zeroization-buffers branch April 29, 2026 15:00
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