fix: isValidXprv accepts a mistyped extended key and silently imports a different wallet #210
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
isValidXprvaccepts an extended private key with a one-character typo.importXprvKey(gated on it atsrc/popup/views/addWallet.js:215) then creates a different wallet with no error shown.Root cause: ethers'
HDNodeWallet.fromExtendedKeyskips base58 checksum verification whenever the decoded payload is the usual 82 bytes — exactly the case the checksum exists to catch.Measured against the BIP-32 test vector 1 key: changing any one of the last 14 characters passes validation, and 9 of those 14 positions produce a different wallet. Example — correct key gives
0x022b971dFF0C43305e691DEd7a14367AF19D6407; a typo at position -6 gives0x3F334f0a356d6B46B1d70B590E7437D77100d28D.Consequence: a user importing an xprv and mistyping one character is shown a successful import of an empty wallet that is not theirs. There is no error and nothing indicates the key was wrong. They will reasonably conclude their funds are gone. The base58 checksum exists precisely to make this impossible, and it is not being enforced.
Found while adding crypto-core coverage in #159, which left a skipped test naming this defect.
Implementation requirements
isValidXprvbefore accepting an extended key — e.g. requireencodeBase58Check(bytes.slice(0, 78)) === key— rather than relying on ethers to do it.fromExtendedKeythrowing; it demonstrably does not for this input class.isValidXpuband any other extended-key entry point have the same hole, and fix them the same way if so.Definition of done
isValidXprv.rejects an extended key with a one-character typointests/wallet.test.jsis unskipped and passes.TODO.mdupdated in the same commit.make checkpasses.Scope extension — a second xprv import defect in the same code, found by the independent review of #209. Fix both in this unit, since they touch the same functions.
hdWalletFromXprv/getSignerForAddressderive using a RELATIVE path (44'/60'/0'/0). For a depth-0 master xprv that is correct. But importing a NON-master extended key — an account-level xprv at depth 3, which is a normal thing for a user to have — derives44'/60'/0'/0BENEATH it rather than rejecting it. The result is a wallet whose addresses correspond to nothing the user holds, created silently, with no error.Same user-visible failure as the checksum hole already described here: an apparently successful import of a wallet that is not theirs. Nothing currently tests it.
Additional definition of done: