fix(app): derive an address private key only when revealed

Address rows and the address detail sheet no longer carry the WIF of
every listed address for the whole session. The key is derived after the
password gate and dropped when hidden, when the sheet closes or when the
wallet closes. See ADR 0037.
This commit is contained in:
davide committed 2026-11-03 14:10:58 +01:00
1 parent 8bcee27c23
commit 06d1f59c2b
8 files changed
+119 -23

No files matched your search

@@ -0,0 +1,27 @@
# 0037. Derive an address private key only when it is revealed
- **Status:** Accepted
- **Area:** Security
- **Recorded:** 2026-11-03
## Context
`AddressRow` carried the WIF of its address, computed for every listed address (ten rows before the first sync, every scanned address after it). The reveal gate (password prompt) controlled only display, not presence: for the whole session the WIF of every address sat in managed strings, which cannot be zeroed, inside objects bound to the UI. That does not leak by itself, and `SECURITY.md` already states that keys live in process memory after unlock, but it widened the exposure to a memory dump or a binding bug for no benefit.
## Decision
- `AddressRow` and `AddressInfo` hold no key. `AddressRow` keeps `HasPrivKey` (true when the account is not watch-only); `AddressInfo` carries the derivation coordinates (`IsChange`, `Index`).
- The ViewModel derives the WIF in the reveal step, after the password check (`RevealPrivKeyInternal`), into `RevealedPrivKey`, which the overlay binds to.
- `RevealedPrivKey` is cleared when the key is hidden, when the sheet changes or closes, and when the wallet closes.
## Consequences
- The exposure window of an address key shrinks from the whole session to the time it is shown.
- The WIF string still cannot be zeroed; it lives until the garbage collector reclaims it. This is documented in `SECURITY.md`.
- Watch-only wallets still never reveal a key (`HasPrivKey` is false and the derivation returns nothing).
## References
- `src/App/ViewModels/MainWindowViewModel.cs`
- `src/App/ViewModels/MainWindowViewModel.Receive.cs`
- `tests/PalladiumWallet.Tests/App/MainWindowViewModelSecurityTests.cs`
+1
View File
@@ -10,6 +10,7 @@ One ADR per feature or significant design decision. Format: `template.md`. Workf
| 0004 | [Centralize all chain constants in a network profile](0004-centralized-network-profile.md) | Chain | Accepted |
| 0005 | [Use NBitcoin for protocol primitives, hand-write only the rest](0005-nbitcoin-for-protocol-primitives.md) | Crypto | Accepted |
| 0006 | [Anchor headers to hardcoded checkpoints and skip PoW validation](0006-spv-checkpoint-anchoring-without-pow.md) | SPV | Accepted |
| 0037 | [Derive an address private key only when it is revealed](0037-derive-address-private-keys-on-reveal.md) | Security | Accepted |
| 0007 | [Verify a Merkle proof for every confirmed transaction](0007-mandatory-merkle-verification.md) | SPV | Accepted |
| 0008 | [Progressive verification: show early, spend only after proof](0008-progressive-verification-sync.md) | SPV | Accepted |
| 0009 | [Hand-written Electrum JSON-RPC client with pipelining and an in-flight cap](0009-custom-electrum-client.md) | Network | Accepted |