Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build(deps): bump btcd in v0.45 (backport #14846) #15127

Merged
merged 5 commits into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

## Improvements

* (deps) [#15127](https://github.com/cosmos/cosmos-sdk/pull/15127) Bump btcd.
* (store) [#14410](https://github.com/cosmos/cosmos-sdk/pull/14410) `rootmulti.Store.loadVersion` has validation to check if all the module stores' height is correct, it will error if any module store has incorrect height.

## [v0.45.14](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.45.14) - 2023-02-16
Expand Down
4 changes: 2 additions & 2 deletions crypto/hd/hdpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"strconv"
"strings"

"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/btcec/v2"
)

// BIP44Params wraps BIP 44 params (5 level BIP 32 path).
Expand Down Expand Up @@ -237,7 +237,7 @@ func derivePrivateKey(privKeyBytes [32]byte, chainCode [32]byte, index uint32, h
data = append([]byte{byte(0)}, privKeyBytes[:]...)
} else {
// this can't return an error:
_, ecPub := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes[:])
_, ecPub := btcec.PrivKeyFromBytes(privKeyBytes[:])
pubkeyBytes := ecPub.SerializeCompressed()
data = pubkeyBytes

Expand Down
4 changes: 2 additions & 2 deletions crypto/keys/secp256k1/secp256k1.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"io"
"math/big"

secp256k1 "github.com/btcsuite/btcd/btcec"
secp256k1 "github.com/btcsuite/btcd/btcec/v2"
"github.com/tendermint/tendermint/crypto"
"golang.org/x/crypto/ripemd160" //nolint: staticcheck // necessary for Bitcoin address format

Expand Down Expand Up @@ -37,7 +37,7 @@ func (privKey *PrivKey) Bytes() []byte {
// PubKey performs the point-scalar multiplication from the privKey on the
// generator point to get the pubkey.
func (privKey *PrivKey) PubKey() cryptotypes.PubKey {
_, pubkeyObject := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey.Key)
_, pubkeyObject := secp256k1.PrivKeyFromBytes(privKey.Key)
pk := pubkeyObject.SerializeCompressed()
return &PubKey{Key: pk}
}
Expand Down
2 changes: 1 addition & 1 deletion crypto/keys/secp256k1/secp256k1_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"math/big"
"testing"

btcSecp256k1 "github.com/btcsuite/btcd/btcec"
btcSecp256k1 "github.com/btcsuite/btcd/btcec/v2"
"github.com/stretchr/testify/require"
)

Expand Down
52 changes: 20 additions & 32 deletions crypto/keys/secp256k1/secp256k1_nocgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,22 @@
package secp256k1

import (
"math/big"

secp256k1 "github.com/btcsuite/btcd/btcec"
secp256k1 "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"

"github.com/tendermint/tendermint/crypto"
)

// used to reject malleable signatures
// see:
// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93
// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/crypto.go#L39
var secp256k1halfN = new(big.Int).Rsh(secp256k1.S256().N, 1)

// Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg.
// The returned signature will be of the form R || S (in lower-S form).
func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) {
priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey.Key)
sig, err := priv.Sign(crypto.Sha256(msg))
priv, _ := secp256k1.PrivKeyFromBytes(privKey.Key)
sig, err := ecdsa.SignCompact(priv, crypto.Sha256(msg), false)
if err != nil {
return nil, err
}
sigBytes := serializeSig(sig)
return sigBytes, nil
// remove the first byte which is compactSigRecoveryCode
return sig[1:], nil
}

// VerifyBytes verifies a signature of the form R || S.
Expand All @@ -35,37 +28,32 @@ func (pubKey *PubKey) VerifySignature(msg []byte, sigStr []byte) bool {
if len(sigStr) != 64 {
return false
}
pub, err := secp256k1.ParsePubKey(pubKey.Key, secp256k1.S256())
pub, err := secp256k1.ParsePubKey(pubKey.Key)
if err != nil {
return false
}
// parse the signature:
signature := signatureFromBytes(sigStr)
// Reject malleable signatures. libsecp256k1 does this check but btcec doesn't.
// see: https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93
if signature.S.Cmp(secp256k1halfN) > 0 {
// Serialize() would negate S value if it is over half order.
// Hence, if the signature is different after Serialize() if should be rejected.
modifiedSignature, parseErr := ecdsa.ParseDERSignature(signature.Serialize())
if parseErr != nil {
return false
}
if !signature.IsEqual(modifiedSignature) {
return false
}
return signature.Verify(crypto.Sha256(msg), pub)
}

// Read Signature struct from R || S. Caller needs to ensure
// that len(sigStr) == 64.
func signatureFromBytes(sigStr []byte) *secp256k1.Signature {
return &secp256k1.Signature{
R: new(big.Int).SetBytes(sigStr[:32]),
S: new(big.Int).SetBytes(sigStr[32:64]),
}
}

// Serialize signature to R || S.
// R, S are padded to 32 bytes respectively.
func serializeSig(sig *secp256k1.Signature) []byte {
rBytes := sig.R.Bytes()
sBytes := sig.S.Bytes()
sigBytes := make([]byte, 64)
// 0 pad the byte arrays from the left if they aren't big enough.
copy(sigBytes[32-len(rBytes):32], rBytes)
copy(sigBytes[64-len(sBytes):64], sBytes)
return sigBytes
func signatureFromBytes(sigStr []byte) *ecdsa.Signature {
var r secp256k1.ModNScalar
r.SetByteSlice(sigStr[:32])
var s secp256k1.ModNScalar
s.SetByteSlice(sigStr[32:64])
return ecdsa.NewSignature(&r, &s)
}
23 changes: 16 additions & 7 deletions crypto/keys/secp256k1/secp256k1_nocgo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package secp256k1
import (
"testing"

secp256k1 "github.com/btcsuite/btcd/btcec"
secp256k1 "github.com/btcsuite/btcd/btcec/v2"
"github.com/stretchr/testify/require"
)

Expand All @@ -19,20 +19,29 @@ func TestSignatureVerificationAndRejectUpperS(t *testing.T) {
priv := GenPrivKey()
sigStr, err := priv.Sign(msg)
require.NoError(t, err)
sig := signatureFromBytes(sigStr)
require.False(t, sig.S.Cmp(secp256k1halfN) > 0)
var r secp256k1.ModNScalar
r.SetByteSlice(sigStr[:32])
var s secp256k1.ModNScalar
s.SetByteSlice(sigStr[32:64])
require.False(t, s.IsOverHalfOrder())

pub := priv.PubKey()
require.True(t, pub.VerifySignature(msg, sigStr))

// malleate:
sig.S.Sub(secp256k1.S256().CurveParams.N, sig.S)
require.True(t, sig.S.Cmp(secp256k1halfN) > 0)
malSigStr := serializeSig(sig)
var S256 secp256k1.ModNScalar
S256.SetByteSlice(secp256k1.S256().N.Bytes())
s.Negate().Add(&S256)
require.True(t, s.IsOverHalfOrder())

rBytes := r.Bytes()
sBytes := s.Bytes()
malSigStr := make([]byte, 64)
copy(malSigStr[32-len(rBytes):32], rBytes[:])
copy(malSigStr[64-len(sBytes):64], sBytes[:])
require.False(t, pub.VerifySignature(msg, malSigStr),
"VerifyBytes incorrect with malleated & invalid S. sig=%v, key=%v",
sig,
malSigStr,
priv,
)
}
Expand Down
13 changes: 8 additions & 5 deletions crypto/keys/secp256k1/secp256k1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (
"math/big"
"testing"

btcSecp256k1 "github.com/btcsuite/btcd/btcec"
btcSecp256k1 "github.com/btcsuite/btcd/btcec/v2"
btcecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/cosmos/btcutil/base58"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -64,7 +65,7 @@ func TestSignAndValidateSecp256k1(t *testing.T) {
// ----
// Test cross packages verification
msgHash := crypto.Sha256(msg)
btcPrivKey, btcPubKey := btcSecp256k1.PrivKeyFromBytes(btcSecp256k1.S256(), privKey.Key)
btcPrivKey, btcPubKey := btcSecp256k1.PrivKeyFromBytes(privKey.Key)
// This fails: malformed signature: no header magic
// btcSig, err := secp256k1.ParseSignature(sig, secp256k1.S256())
// require.NoError(t, err)
Expand All @@ -77,9 +78,11 @@ func TestSignAndValidateSecp256k1(t *testing.T) {
ok := ecdsa.Verify(btcPubKey.ToECDSA(), msgHash, r, s)
require.True(t, ok)

sig2, err := btcPrivKey.Sign(msgHash)
sig2, err := btcecdsa.SignCompact(btcPrivKey, msgHash, false)
// Chop off compactSigRecoveryCode.
sig2 = sig2[1:]
require.NoError(t, err)
pubKey.VerifySignature(msg, sig2.Serialize())
pubKey.VerifySignature(msg, sig2)

// ----
// Mutate the signature, just one bit.
Expand All @@ -98,7 +101,7 @@ func TestSecp256k1LoadPrivkeyAndSerializeIsIdentity(t *testing.T) {

// This function creates a private and public key in the underlying libraries format.
// The private key is basically calling new(big.Int).SetBytes(pk), which removes leading zero bytes
priv, _ := btcSecp256k1.PrivKeyFromBytes(btcSecp256k1.S256(), privKeyBytes[:])
priv, _ := btcSecp256k1.PrivKeyFromBytes(privKeyBytes[:])
// this takes the bytes returned by `(big int).Bytes()`, and if the length is less than 32 bytes,
// pads the bytes from the left with zero bytes. Therefore these two functions composed
// result in the identity function on privKeyBytes, hence the following equality check
Expand Down
13 changes: 0 additions & 13 deletions crypto/keys/utils.go

This file was deleted.

17 changes: 7 additions & 10 deletions crypto/ledger/ledger_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
package ledger

import (
"errors"
"fmt"

"github.com/btcsuite/btcd/btcec"
"github.com/pkg/errors"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/cosmos/go-bip39"
"github.com/tendermint/tendermint/crypto"

Expand Down Expand Up @@ -56,7 +56,7 @@ func (mock LedgerSECP256K1Mock) GetPublicKeySECP256K1(derivationPath []uint32) (
return nil, err
}

_, pubkeyObject := btcec.PrivKeyFromBytes(btcec.S256(), derivedPriv)
_, pubkeyObject := btcec.PrivKeyFromBytes(derivedPriv)

return pubkeyObject.SerializeUncompressed(), nil
}
Expand All @@ -70,7 +70,7 @@ func (mock LedgerSECP256K1Mock) GetAddressPubKeySECP256K1(derivationPath []uint3
}

// re-serialize in the 33-byte compressed format
cmp, err := btcec.ParsePubKey(pk, btcec.S256())
cmp, err := btcec.ParsePubKey(pk)
if err != nil {
return nil, "", fmt.Errorf("error parsing public key: %v", err)
}
Expand All @@ -97,11 +97,8 @@ func (mock LedgerSECP256K1Mock) SignSECP256K1(derivationPath []uint32, message [
return nil, err
}

priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), derivedPriv)
sig, err := priv.Sign(crypto.Sha256(message))
if err != nil {
return nil, err
}
priv, _ := btcec.PrivKeyFromBytes(derivedPriv)
sig := ecdsa.Sign(priv, crypto.Sha256(message))

return sig.Serialize(), nil
}
Expand Down
2 changes: 1 addition & 1 deletion crypto/ledger/ledger_notavail.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
package ledger

import (
"github.com/pkg/errors"
"errors"
)

// If ledger support (build tag) has been enabled, which implies a CGO dependency,
Expand Down
35 changes: 20 additions & 15 deletions crypto/ledger/ledger_secp256k1.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package ledger

import (
"errors"
"fmt"
"math/big"
"os"

"github.com/btcsuite/btcd/btcec"
"github.com/pkg/errors"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"

"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/types"
)
Expand Down Expand Up @@ -171,24 +171,29 @@ func warnIfErrors(f func() error) {
}

func convertDERtoBER(signatureDER []byte) ([]byte, error) {
sigDER, err := btcec.ParseDERSignature(signatureDER, btcec.S256())
sigDER, err := ecdsa.ParseDERSignature(signatureDER)
if err != nil {
return nil, err
}

sigStr := sigDER.Serialize()
// The format of a DER encoded signature is as follows:
// 0x30 <total length> 0x02 <length of R> <R> 0x02 <length of S> <S>
r, s := new(big.Int), new(big.Int)
r.SetBytes(sigStr[4 : 4+sigStr[3]])
s.SetBytes(sigStr[4+sigStr[3]+2:])

sModNScalar := new(btcec.ModNScalar)
sModNScalar.SetByteSlice(s.Bytes())
// based on https://github.com/tendermint/btcd/blob/ec996c5/btcec/signature.go#L33-L50
// low 'S' malleability breaker
sigS := sigDER.S
if keys.IsOverHalfOrder(sigS) {
sigS = new(big.Int).Sub(btcec.S256().N, sigS)
if sModNScalar.IsOverHalfOrder() {
s = new(big.Int).Sub(btcec.S256().N, s)
}

rBytes := sigDER.R.Bytes()
sBytes := sigS.Bytes()
sigBytes := make([]byte, 64)
// 0 pad the byte arrays from the left if they aren't big enough.
copy(sigBytes[32-len(rBytes):32], rBytes)
copy(sigBytes[64-len(sBytes):64], sBytes)
copy(sigBytes[32-len(r.Bytes()):32], r.Bytes())
copy(sigBytes[64-len(s.Bytes()):64], s.Bytes())

return sigBytes, nil
}
Expand All @@ -200,7 +205,7 @@ func getDevice() (SECP256K1, error) {

device, err := discoverLedger()
if err != nil {
return nil, errors.Wrap(err, "ledger nano S")
return nil, fmt.Errorf("ledger nano S: %w", err)
}

return device, nil
Expand Down Expand Up @@ -254,7 +259,7 @@ func getPubKeyUnsafe(device SECP256K1, path hd.BIP44Params) (types.PubKey, error
}

// re-serialize in the 33-byte compressed format
cmp, err := btcec.ParsePubKey(publicKey, btcec.S256())
cmp, err := btcec.ParsePubKey(publicKey)
if err != nil {
return nil, fmt.Errorf("error parsing public key: %v", err)
}
Expand All @@ -278,7 +283,7 @@ func getPubKeyAddrSafe(device SECP256K1, path hd.BIP44Params, hrp string) (types
}

// re-serialize in the 33-byte compressed format
cmp, err := btcec.ParsePubKey(publicKey, btcec.S256())
cmp, err := btcec.ParsePubKey(publicKey)
if err != nil {
return nil, "", fmt.Errorf("error parsing public key: %v", err)
}
Expand Down
Loading