[PM-22038]: Improve Wallet Seed, Key Pair, and Address Code Quality#1217
Merged
m2ux merged 9 commits intoMay 1, 2026
Merged
Conversation
ozgb
approved these changes
Apr 2, 2026
m2ux
added a commit
that referenced
this pull request
Apr 17, 2026
- Remove extra blank line causing cargo fmt check failure - Fix test assertions to match actual Debug output (REDACTED vs ***) Signed-off-by: Mike <mike@midnight.network>
m2ux
added a commit
that referenced
this pull request
Apr 17, 2026
- Remove extra blank line causing cargo fmt check failure - Fix test assertions to match actual Debug output (REDACTED vs ***) Signed-off-by: Mike <mike@midnight.network> Signed-off-by: Mike Clay <mike.clay@shielded.io>
bf1540b to
5e4ef3e
Compare
m2ux
added a commit
that referenced
this pull request
Apr 23, 2026
Made-with: Cursor Signed-off-by: Mike Clay <mike.clay@shielded.io>
m2ux
added a commit
that referenced
this pull request
Apr 23, 2026
- Remove extra blank line causing cargo fmt check failure - Fix test assertions to match actual Debug output (REDACTED vs ***) Signed-off-by: Mike Clay <mike.clay@shielded.io>
5e4ef3e to
403c5d6
Compare
m2ux
added a commit
that referenced
this pull request
Apr 23, 2026
Made-with: Cursor Signed-off-by: Mike Clay <mike.clay@shielded.io>
m2ux
added a commit
that referenced
this pull request
Apr 23, 2026
- Remove extra blank line causing cargo fmt check failure - Fix test assertions to match actual Debug output (REDACTED vs ***) Signed-off-by: Mike Clay <mike.clay@shielded.io>
m2ux
added a commit
that referenced
this pull request
Apr 29, 2026
Made-with: Cursor Signed-off-by: Mike Clay <mike.clay@shielded.io>
d5705a1 to
beb49f7
Compare
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 Signed-off-by: Mike Clay <mike.clay@shielded.io>
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 Signed-off-by: Mike Clay <mike.clay@shielded.io>
Add explicit .clone() on WalletSeed in e2e tests and format merge-resolved files to satisfy CI cargo fmt check. Made-with: Cursor Signed-off-by: Mike Clay <mike.clay@shielded.io>
Add .clone() for WalletSeed in transaction, utxo_spend, dust_balance, generate_intent, show_wallet, and contract_maintenance. Apply cargo fmt. Made-with: Cursor Signed-off-by: Mike Clay <mike.clay@shielded.io>
Made-with: Cursor Signed-off-by: Mike Clay <mike.clay@shielded.io>
Signed-off-by: Mike Clay <mike.clay@shielded.io>
6677bec to
be21f44
Compare
Made-with: Cursor Signed-off-by: Mike Clay <mike.clay@shielded.io>
be21f44 to
92e78d1
Compare
…eploy/maintain PM-22038 inadvertently reverted the PM-22034 zeroization fix when this file was re-touched. The Vec<u8> from serialize_untagged(...signing_key()) and the hex String it's encoded into are not covered by WalletSeed's ZeroizeOnDrop, so they need explicit zeroization on both success and error paths. Refs: PM-22034, #53 Signed-off-by: Mike Clay <mike.clay@shielded.io>
Signed-off-by: Mike Clay <mike.clay@shielded.io>
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
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 inadd_addresseswith zip iteration, and enforcing input-size validation intry_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.rswhere cryptographic types derive traits that expose secret material.WalletSeedderivesDebug,Clone,Copy,Hash,PartialEq, andEq, enabling accidental logging of seed bytes, invisible copies via Copy, and non-constant-time comparisons.KeyPairderivesClone, encouraging copies of secret key material. Address generation code uses unchecked indexing that can panic, andtry_from_lazy_hexallocates memory from untrusted input before validating size.Key technical insight:
ZeroizeOnDropgenerates aDropimpl, and Rust prohibitsCopy + Drop. Therefore Copy removal is a technical requirement for zeroize support, not merely a style choice.Hash/PartialEq/Eqmust remain becauseWalletSeedserves as aHashMapkey inLedgerContext.Changes
Copy; addedZeroize+ZeroizeOnDropvia thezeroizecrate; implemented redactedfmt::Debug(printsWalletSeed(Medium(***))instead of raw bytes)Clonederivation (zero callers — pure parsing wrapper)add_addressesto usezipwith&[MaintenanceCounter], eliminating unchecked indexing panicstry_from_lazy_hexbeforehex::decodeallocationledger/helpersandutil/toolkitupdated to use explicit.clone()whereWalletSeedpreviously relied on implicitCopy(see scope note below)Note
The Least Authority finding has two related intents: (1) zeroize secret material on drop, and (2) minimize the number of plaintext copies of
WalletSeedthat coexist in memory. This PR fully addresses (1) — every clone is now independently zeroized viaZeroizeOnDrop— but only partially addresses (2). RemovingCopymakes each duplication explicit (~100.clone()insertions acrossledger/helpersandutil/toolkit), but the resolution preserves the original duplication count rather than reducing it; the implicit bitwise copies are now explicit owned buffers, all of which get zeroized.A fuller fix would convert
BuildInput/BuildOutput/UtxoOutputInfo/wallet_from_seedand similar APIs to borrow&WalletSeedinstead of owning it, so the seed lives in one canonical location for the duration of tx construction. That refactor requires threading lifetimes through trait objects (Box<dyn BuildInput<DefaultDB>>, etc.) and is deferred as follow-up scope.📌 Submission Checklist
🔱 Fork Strategy
🗹 TODO before merging