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.
1.7 KiB
1.7 KiB
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
AddressRowandAddressInfohold no key.AddressRowkeepsHasPrivKey(true when the account is not watch-only);AddressInfocarries the derivation coordinates (IsChange,Index).- The ViewModel derives the WIF in the reveal step, after the password check (
RevealPrivKeyInternal), intoRevealedPrivKey, which the overlay binds to. RevealedPrivKeyis 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 (
HasPrivKeyis false and the derivation returns nothing).
References
src/App/ViewModels/MainWindowViewModel.cssrc/App/ViewModels/MainWindowViewModel.Receive.cstests/PalladiumWallet.Tests/App/MainWindowViewModelSecurityTests.cs