Skip to content

PM-22038: Improve Wallet Seed, Key Pair, and Address Code Quality#1217

Open
m2ux wants to merge 3 commits intomainfrom
task/PM-22038-improve-wallet-seed-keypair-code-quality
Open

PM-22038: Improve Wallet Seed, Key Pair, and Address Code Quality#1217
m2ux wants to merge 3 commits intomainfrom
task/PM-22038-improve-wallet-seed-keypair-code-quality

Conversation

@m2ux
Copy link
Copy Markdown
Contributor

@m2ux m2ux commented Apr 1, 2026

Summary

Harden WalletSeed, KeyPair, and address-related types in the Midnight node ledger by implementing redacted Debug, removing Copy (required for ZeroizeOnDrop), adding Zeroize/ZeroizeOnDrop for automatic secret material cleanup, removing unused Clone from Keypair, replacing index-based panics in add_addresses with zip iteration, and enforcing input-size validation in try_from_lazy_hex — addressing Least Authority audit Suggestion 3 (A2).

🎫 Ticket 📐 Engineering


Motivation

The Least Authority Node DIFF audit identified informational findings in ledger/helpers/src/versions/common/types.rs where cryptographic types derive traits that expose secret material. WalletSeed derives Debug, Clone, Copy, Hash, PartialEq, and Eq, enabling accidental logging of seed bytes, invisible copies via Copy, and non-constant-time comparisons. KeyPair derives Clone, encouraging copies of secret key material. Address generation code uses unchecked indexing that can panic, and try_from_lazy_hex allocates memory from untrusted input before validating size.

Key technical insight: ZeroizeOnDrop generates a Drop impl, and Rust prohibits Copy + Drop. Therefore Copy removal is a technical requirement for zeroize support, not merely a style choice. Hash/PartialEq/Eq must remain because WalletSeed serves as a HashMap key in LedgerContext.


Changes

  • WalletSeed type — Removed Copy; added Zeroize + ZeroizeOnDrop via the zeroize crate; implemented redacted fmt::Debug (prints WalletSeed(Medium(***)) instead of raw bytes)
  • Keypair type — Removed unused Clone derivation (zero callers — pure parsing wrapper)
  • Address generation — Rewrote add_addresses to use zip with &[MaintenanceCounter], eliminating unchecked indexing panics
  • Hex decoding — Added pre-validation of hex string length in try_from_lazy_hex before hex::decode allocation
  • Copy→Clone fixups — 3 call sites in util/toolkit updated to use explicit .clone() where WalletSeed previously relied on implicit Copy
  • Tests — Added tests for redacted Debug output and hex length validation; all existing tests pass

📌 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: code-quality improvement, no functional behavior change
  • 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

m2ux added 3 commits April 1, 2026 18:02
Improve Wallet Seed, Key Pair, and Address Code Quality

Made-with: Cursor
Address Least Authority audit Suggestion 3 (A2):

- WalletSeed: remove Copy (required for ZeroizeOnDrop), add
  Zeroize+ZeroizeOnDrop, implement redacted Debug that prints
  WalletSeed::<variant>(***) instead of raw bytes. Keep Clone
  (needed for HashMap dual-ownership pattern) and Hash/PartialEq/Eq
  (required for HashMap<WalletSeed, Wallet<D>> key).

- Keypair: remove Clone (zero callers — pure parsing wrapper).

- MaintenanceUpdateBuilder::add_addresses: accept &[MaintenanceCounter]
  and use zip iterator instead of unchecked index loop, eliminating
  potential index out-of-bounds panic.

- WalletSeed::try_from_lazy_hex: pre-validate hex string length
  (max 128 hex chars) before calling hex::decode, preventing memory
  allocation from oversized untrusted input.

- Fix all Copy-removal compilation errors across ledger/helpers and
  util/toolkit by adding explicit .clone() at ownership transfer points.

- Add unit tests for redacted Debug output, hex length validation,
  zip truncation safety, and HashMap key compatibility.

Made-with: Cursor
WalletSeed no longer derives Copy (removed for security hardening).
Two test helpers and one genesis utility need explicit .clone() to
construct owned values from borrows.

Made-with: Cursor
@m2ux m2ux marked this pull request as ready for review April 1, 2026 22:48
@m2ux m2ux requested a review from a team as a code owner April 1, 2026 22:48
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.

1 participant