Repository: libp2p/go-libp2p
Pull request #1689 (2022-08-18 16:26:18)

crypto/secp256k1: Remove btcsuite intermediary.
This updates the `core/crypto/secp256k1` code to make use of the `dcrec/secp256k1/v4` module directly instead of using `btcec/v2` which itself is now just a shim around `dcrec/secp256k1/v4` anyway.

This has the benefit of removing the additional transitive `github.com/btcsuite/btcd/chaincfg/chainhash` dependency since `dcrec/secp256k1/v4` is its own module and does not rely on it.

It also updates to the latest `v4.1.0` release which implements direct key generation and has some other nice optimizations that speed up signature verification as compared to the `v4.0.1` release.

---

This was originally opened as libp2p/go-libp2p-core#280 and is being reopened here as requested.

Also, per discussion on the referenced PR, I confirmed that this is actually not a breaking change to the API despite changing the type aliases because the types in `btcec/v2` are themselves just type aliases to the same underlying type this changes them to.  For example, see the [btcec/v2.PrivateKey](https://github.com/btcsuite/btcd/blob/0f49e1000633b0af6d7bbef97d69671f4c99e37e/btcec/privkey.go#L14) type declaration which is an alias for `dcrec/secp256k1/v4.PrivateKey`.  The same is true for [btcec/v2.PublicKey](https://github.com/btcsuite/btcd/blob/0f49e1000633b0af6d7bbef97d69671f4c99e37e/btcec/pubkey.go#L40). 

In order to check that is indeed the case, I intentionally kept the type assertion in `KeyPairFromStdKey` as `*btcec.PrivateKey` and ran the `TestKeyPairFromKey` test which generates the key with `secp256k1.GeneratePrivateKey` (which is actually a `*secp256k1.PrivateKey`) and then calls `KeyPairFromStdKey` with it to ensure they are considered identical by the compiler.

Here is the diff relative to this PR to be more concrete about what I just explained:

```diff
diff --git a/core/crypto/key_openssl.go b/core/crypto/key_openssl.go
index 7a13ff69..b88ea5a7 100644
--- a/core/crypto/key_openssl.go
+++ b/core/crypto/key_openssl.go
@@ -12,7 +12,7 @@ import (
 
        "github.com/libp2p/go-libp2p/core/internal/catch"
 
-       "github.com/decred/dcrd/dcrec/secp256k1/v4"
+       "github.com/btcsuite/btcd/btcec/v2"
        "github.com/libp2p/go-openssl"
 )
 
@@ -40,7 +40,7 @@ func KeyPairFromStdKey(priv crypto.PrivateKey) (_priv PrivKey, _pub PubKey, err
                pub, _ := pubIfc.(ed25519.PublicKey)
                return &Ed25519PrivateKey{*p}, &Ed25519PublicKey{pub}, nil
 
-       case *secp256k1.PrivateKey:
+       case *btcec.PrivateKey:
                sPriv := Secp256k1PrivateKey(*p)
                sPub := Secp256k1PublicKey(*p.PubKey())
                return &sPriv, &sPub, nil
diff --git a/go.mod b/go.mod
index 15f490a9..bc7f4e5e 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.18
 
 require (
        github.com/benbjohnson/clock v1.3.0
+       github.com/btcsuite/btcd/btcec/v2 v2.2.0
        github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c
        github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0
        github.com/flynn/noise v1.0.0
```

```bash
$ go test -run=TestKeyPairFromKey
PASS
ok      github.com/libp2p/go-libp2p/core/crypto 0.474s
```

---

Closes libp2p/go-libp2p-core#11.


Patch:
diff --git a/core/crypto/key_not_openssl.go b/core/crypto/key_not_openssl.go
index 0f446c0f..00324675 100644
--- a/core/crypto/key_not_openssl.go
+++ b/core/crypto/key_not_openssl.go
@@ -9,7 +9,7 @@ import (
 	"crypto/ed25519"
 	"crypto/rsa"
 
-	"github.com/btcsuite/btcd/btcec/v2"
+	"github.com/decred/dcrd/dcrec/secp256k1/v4"
 )
 
 // KeyPairFromStdKey wraps standard library (and secp256k1) private keys in libp2p/go-libp2p/core/crypto keys
@@ -30,7 +30,7 @@ func KeyPairFromStdKey(priv crypto.PrivateKey) (PrivKey, PubKey, error) {
 		pub, _ := pubIfc.(ed25519.PublicKey)
 		return &Ed25519PrivateKey{*p}, &Ed25519PublicKey{pub}, nil
 
-	case *btcec.PrivateKey:
+	case *secp256k1.PrivateKey:
 		sPriv := Secp256k1PrivateKey(*p)
 		sPub := Secp256k1PublicKey(*p.PubKey())
 		return &sPriv, &sPub, nil
diff --git a/core/crypto/key_openssl.go b/core/crypto/key_openssl.go
index b88ea5a7..7a13ff69 100644
--- a/core/crypto/key_openssl.go
+++ b/core/crypto/key_openssl.go
@@ -12,7 +12,7 @@ import (
 
 	"github.com/libp2p/go-libp2p/core/internal/catch"
 
-	"github.com/btcsuite/btcd/btcec/v2"
+	"github.com/decred/dcrd/dcrec/secp256k1/v4"
 	"github.com/libp2p/go-openssl"
 )
 
@@ -40,7 +40,7 @@ func KeyPairFromStdKey(priv crypto.PrivateKey) (_priv PrivKey, _pub PubKey, err
 		pub, _ := pubIfc.(ed25519.PublicKey)
 		return &Ed25519PrivateKey{*p}, &Ed25519PublicKey{pub}, nil
 
-	case *btcec.PrivateKey:
+	case *secp256k1.PrivateKey:
 		sPriv := Secp256k1PrivateKey(*p)
 		sPub := Secp256k1PublicKey(*p.PubKey())
 		return &sPriv, &sPub, nil
diff --git a/core/crypto/secp256k1.go b/core/crypto/secp256k1.go
index fbae3096..3b3aa6fe 100644
--- a/core/crypto/secp256k1.go
+++ b/core/crypto/secp256k1.go
@@ -7,20 +7,20 @@ import (
 	pb "github.com/libp2p/go-libp2p/core/crypto/pb"
 	"github.com/libp2p/go-libp2p/core/internal/catch"
 
-	btcec "github.com/btcsuite/btcd/btcec/v2"
-	btcececdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
+	"github.com/decred/dcrd/dcrec/secp256k1/v4"
+	"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
 	"github.com/minio/sha256-simd"
 )
 
 // Secp256k1PrivateKey is an Secp256k1 private key
-type Secp256k1PrivateKey btcec.PrivateKey
+type Secp256k1PrivateKey secp256k1.PrivateKey
 
 // Secp256k1PublicKey is an Secp256k1 public key
-type Secp256k1PublicKey btcec.PublicKey
+type Secp256k1PublicKey secp256k1.PublicKey
 
 // GenerateSecp256k1Key generates a new Secp256k1 private and public key pair
 func GenerateSecp256k1Key(src io.Reader) (PrivKey, PubKey, error) {
-	privk, err := btcec.NewPrivateKey()
+	privk, err := secp256k1.GeneratePrivateKey()
 	if err != nil {
 		return nil, nil, err
 	}
@@ -31,19 +31,19 @@ func GenerateSecp256k1Key(src io.Reader) (PrivKey, PubKey, error) {
 
 // UnmarshalSecp256k1PrivateKey returns a private key from bytes
 func UnmarshalSecp256k1PrivateKey(data []byte) (k PrivKey, err error) {
-	if len(data) != btcec.PrivKeyBytesLen {
-		return nil, fmt.Errorf("expected secp256k1 data size to be %d", btcec.PrivKeyBytesLen)
+	if len(data) != secp256k1.PrivKeyBytesLen {
+		return nil, fmt.Errorf("expected secp256k1 data size to be %d", secp256k1.PrivKeyBytesLen)
 	}
 	defer func() { catch.HandlePanic(recover(), &err, "secp256k1 private-key unmarshal") }()
 
-	privk, _ := btcec.PrivKeyFromBytes(data)
+	privk := secp256k1.PrivKeyFromBytes(data)
 	return (*Secp256k1PrivateKey)(privk), nil
 }
 
 // UnmarshalSecp256k1PublicKey returns a public key from bytes
 func UnmarshalSecp256k1PublicKey(data []byte) (_k PubKey, err error) {
 	defer func() { catch.HandlePanic(recover(), &err, "secp256k1 public-key unmarshal") }()
-	k, err := btcec.ParsePubKey(data)
+	k, err := secp256k1.ParsePubKey(data)
 	if err != nil {
 		return nil, err
 	}
@@ -58,7 +58,7 @@ func (k *Secp256k1PrivateKey) Type() pb.KeyType {
 
 // Raw returns the bytes of the key
 func (k *Secp256k1PrivateKey) Raw() ([]byte, error) {
-	return (*btcec.PrivateKey)(k).Serialize(), nil
+	return (*secp256k1.PrivateKey)(k).Serialize(), nil
 }
 
 // Equals compares two private keys
@@ -74,16 +74,16 @@ func (k *Secp256k1PrivateKey) Equals(o Key) bool {
 // Sign returns a signature from input data
 func (k *Secp256k1PrivateKey) Sign(data []byte) (_sig []byte, err error) {
 	defer func() { catch.HandlePanic(recover(), &err, "secp256k1 signing") }()
-	key := (*btcec.PrivateKey)(k)
+	key := (*secp256k1.PrivateKey)(k)
 	hash := sha256.Sum256(data)
-	sig := btcececdsa.Sign(key, hash[:])
+	sig := ecdsa.Sign(key, hash[:])
 
 	return sig.Serialize(), nil
 }
 
 // GetPublic returns a public key
 func (k *Secp256k1PrivateKey) GetPublic() PubKey {
-	return (*Secp256k1PublicKey)((*btcec.PrivateKey)(k).PubKey())
+	return (*Secp256k1PublicKey)((*secp256k1.PrivateKey)(k).PubKey())
 }
 
 // Type returns the public key type
@@ -94,7 +94,7 @@ func (k *Secp256k1PublicKey) Type() pb.KeyType {
 // Raw returns the bytes of the key
 func (k *Secp256k1PublicKey) Raw() (res []byte, err error) {
 	defer func() { catch.HandlePanic(recover(), &err, "secp256k1 public key marshaling") }()
-	return (*btcec.PublicKey)(k).SerializeCompressed(), nil
+	return (*secp256k1.PublicKey)(k).SerializeCompressed(), nil
 }
 
 // Equals compares two public keys
@@ -104,7 +104,7 @@ func (k *Secp256k1PublicKey) Equals(o Key) bool {
 		return basicEquals(k, o)
 	}
 
-	return (*btcec.PublicKey)(k).IsEqual((*btcec.PublicKey)(sk))
+	return (*secp256k1.PublicKey)(k).IsEqual((*secp256k1.PublicKey)(sk))
 }
 
 // Verify compares a signature against the input data
@@ -117,11 +117,11 @@ func (k *Secp256k1PublicKey) Verify(data []byte, sigStr []byte) (success bool, e
 			success = false
 		}
 	}()
-	sig, err := btcececdsa.ParseDERSignature(sigStr)
+	sig, err := ecdsa.ParseDERSignature(sigStr)
 	if err != nil {
 		return false, err
 	}
 
 	hash := sha256.Sum256(data)
-	return sig.Verify(hash[:], (*btcec.PublicKey)(k)), nil
+	return sig.Verify(hash[:], (*secp256k1.PublicKey)(k)), nil
 }
diff --git a/go.mod b/go.mod
index 0f704416..15f490a9 100644
--- a/go.mod
+++ b/go.mod
@@ -4,8 +4,8 @@ go 1.18
 
 require (
 	github.com/benbjohnson/clock v1.3.0
-	github.com/btcsuite/btcd/btcec/v2 v2.2.0
 	github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c
+	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0
 	github.com/flynn/noise v1.0.0
 	github.com/gogo/protobuf v1.3.2
 	github.com/golang/mock v1.6.0
@@ -64,7 +64,6 @@ require (
 	github.com/containerd/cgroups v1.0.4 // indirect
 	github.com/coreos/go-systemd/v22 v22.3.2 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
-	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
 	github.com/dgraph-io/badger v1.6.2 // indirect
 	github.com/dgraph-io/ristretto v0.0.2 // indirect
 	github.com/docker/go-units v0.4.0 // indirect
diff --git a/go.sum b/go.sum
index f3c26dee..37e6b43d 100644
--- a/go.sum
+++ b/go.sum
@@ -59,10 +59,6 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
-github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c=
-github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
-github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
-github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
 github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
@@ -97,9 +93,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU=
 github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U=
 github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
-github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
-github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
 github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
 github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
 github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po=


Test patch:
diff --git a/core/crypto/key_test.go b/core/crypto/key_test.go
index 1d6e2bdb..23e96246 100644
--- a/core/crypto/key_test.go
+++ b/core/crypto/key_test.go
@@ -17,8 +17,8 @@ import (
 	pb "github.com/libp2p/go-libp2p/core/crypto/pb"
 	"github.com/libp2p/go-libp2p/core/test"
 
-	"github.com/btcsuite/btcd/btcec/v2"
-	btcececdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
+	"github.com/decred/dcrd/dcrec/secp256k1/v4"
+	secp256k1ecdsa "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
 	"github.com/minio/sha256-simd"
 )
 
@@ -34,11 +34,11 @@ func TestKeyPairFromKey(t *testing.T) {
 		hashed = sha256.Sum256(data)
 	)
 
-	privk, err := btcec.NewPrivateKey()
+	privk, err := secp256k1.GeneratePrivateKey()
 	if err != nil {
 		t.Fatalf("err generating btcec priv key:\n%v", err)
 	}
-	sigK := btcececdsa.Sign(privk, hashed[:])
+	sigK := secp256k1ecdsa.Sign(privk, hashed[:])
 
 	eKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 	if err != nil {