Merge pull request #2545 from Lrifton92/fix/wif-private-key-range-validation
Some checks are pending
Build and Test / Build (push) Waiting to run
Build and Test / Unit coverage (push) Waiting to run
Build and Test / Unit race (push) Waiting to run
Build and Test / Unit rpctest (push) Waiting to run

btcutil: reject out-of-range private keys in DecodeWIF
This commit is contained in:
Olaoluwa Osuntokun 2026-07-20 18:52:27 -05:00 committed by GitHub
commit e454fa7762
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 40 additions and 0 deletions

View file

@ -118,6 +118,22 @@ func DecodeWIF(wif string) (*WIF, error) {
netID := decoded[0]
privKeyBytes := decoded[1 : 1+btcec.PrivKeyBytesLen]
// Ensure the private key is within the valid range for a secp256k1
// private key, that is [1, N-1]. Without this check, a WIF encoding a
// key of zero or one greater than or equal to the group order N is
// silently accepted: btcec.PrivKeyFromBytes reduces the scalar modulo
// N, so DecodeWIF would otherwise return a private key that differs from
// the one actually encoded in the WIF (or the all-zero key) without
// reporting an error.
var keyScalar btcec.ModNScalar
defer keyScalar.Zero()
if overflow := keyScalar.SetByteSlice(privKeyBytes); overflow ||
keyScalar.IsZero() {
return nil, ErrMalformedPrivateKey
}
privKey, _ := btcec.PrivKeyFromBytes(privKeyBytes)
return &WIF{privKey, compress, netID}, nil
}

View file

@ -122,6 +122,30 @@ func TestEncodeDecodeWIF(t *testing.T) {
wif: "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTj",
err: address.ErrChecksumMismatch,
},
{
// A WIF encoding a private key of zero, which is
// outside the valid range [1, N-1] for a secp256k1
// private key.
name: "decodeZeroPrivKeyWif",
wif: "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAbuatmU",
err: ErrMalformedPrivateKey,
},
{
// A WIF encoding a private key equal to the group order
// N, which is outside the valid range [1, N-1].
name: "decodeOrderNPrivKeyWif",
wif: "5Km2kuu7vtFDPpxywn4u3NLpbr5jKpTB3jsuDU2KYEqetwr388P",
err: ErrMalformedPrivateKey,
},
{
// A WIF encoding a private key of N+5, which is outside
// the valid range [1, N-1]. Before validation was
// added, this was silently reduced modulo N and decoded
// to a different private key (5) without any error.
name: "decodeAboveOrderNPrivKeyWif",
wif: "5Km2kuu7vtFDPpxywn4u3NLpbr5jKpTB3jsuDU2KYEqeuVhzTbv",
err: ErrMalformedPrivateKey,
},
}
for _, invalidCase := range invalidDecodeCases {