Skip to content

Commit

Permalink
Merge pull request #1777 from Roasbeef/bip340
Browse files Browse the repository at this point in the history
btcec/v2: create new schnorr package for BIP-340, move existing ecdsa implementation into new ecdsa package
  • Loading branch information
Roasbeef committed Feb 1, 2022
2 parents a277387 + b7a4622 commit 81fbd9b
Show file tree
Hide file tree
Showing 23 changed files with 1,451 additions and 201 deletions.
29 changes: 0 additions & 29 deletions btcec/bench_test.go
Expand Up @@ -164,35 +164,6 @@ func hexToModNScalar(s string) *ModNScalar {
return &scalar
}

// BenchmarkSigVerify benchmarks how long it takes the secp256k1 curve to
// verify signatures.
func BenchmarkSigVerify(b *testing.B) {
b.StopTimer()
// Randomly generated keypair.
// Private key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d
pubKey := NewPublicKey(
hexToFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"),
hexToFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"),
)

// Double sha256 of []byte{0x01, 0x02, 0x03, 0x04}
msgHash := fromHex("8de472e2399610baaa7f84840547cd409434e31f5d3bd71e4d947f283874f9c0")
sig := NewSignature(
hexToModNScalar("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"),
hexToModNScalar("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"),
)

if !sig.Verify(msgHash.Bytes(), pubKey) {
b.Errorf("Signature failed to verify")
return
}
b.StartTimer()

for i := 0; i < b.N; i++ {
sig.Verify(msgHash.Bytes(), pubKey)
}
}

// BenchmarkFieldNormalize benchmarks how long it takes the internal field
// to perform normalization (which includes modular reduction).
func BenchmarkFieldNormalize(b *testing.B) {
Expand Down
21 changes: 0 additions & 21 deletions btcec/btcec_test.go
Expand Up @@ -851,27 +851,6 @@ func TestKeyGeneration(t *testing.T) {
testKeyGeneration(t, S256(), "S256")
}

func testSignAndVerify(t *testing.T, c *KoblitzCurve, tag string) {
priv, _ := NewPrivateKey()
pub := priv.PubKey()

hashed := []byte("testing")
sig := Sign(priv, hashed)

if !sig.Verify(hashed, pub) {
t.Errorf("%s: Verify failed", tag)
}

hashed[0] ^= 0xff
if sig.Verify(hashed, pub) {
t.Errorf("%s: Verify always works!", tag)
}
}

func TestSignAndVerify(t *testing.T) {
testSignAndVerify(t, S256(), "S256")
}

// checkNAFEncoding returns an error if the provided positive and negative
// portions of an overall NAF encoding do not adhere to the requirements or they
// do not sum back to the provided original value.
Expand Down
210 changes: 210 additions & 0 deletions btcec/ecdsa/bench_test.go
@@ -0,0 +1,210 @@
// Copyright 2013-2016 The btcsuite developers
// Copyright (c) 2015-2021 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package ecdsa

import (
"encoding/hex"
"math/big"
"testing"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
)

// hexToBytes converts the passed hex string into bytes and will panic if there
// is an error. This is only provided for the hard-coded constants so errors in
// the source code can be detected. It will only (and must only) be called with
// hard-coded values.
func hexToBytes(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
return b
}

// hexToModNScalar converts the passed hex string into a ModNScalar and will
// panic if there is an error. This is only provided for the hard-coded
// constants so errors in the source code can be detected. It will only (and
// must only) be called with hard-coded values.
func hexToModNScalar(s string) *btcec.ModNScalar {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
var scalar btcec.ModNScalar
if overflow := scalar.SetByteSlice(b); overflow {
panic("hex in source file overflows mod N scalar: " + s)
}
return &scalar
}

// hexToFieldVal converts the passed hex string into a FieldVal and will panic
// if there is an error. This is only provided for the hard-coded constants so
// errors in the source code can be detected. It will only (and must only) be
// called with hard-coded values.
func hexToFieldVal(s string) *btcec.FieldVal {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
var f btcec.FieldVal
if overflow := f.SetByteSlice(b); overflow {
panic("hex in source file overflows mod P: " + s)
}
return &f
}

// fromHex converts the passed hex string into a big integer pointer and will
// panic is there is an error. This is only provided for the hard-coded
// constants so errors in the source code can bet detected. It will only (and
// must only) be called for initialization purposes.
func fromHex(s string) *big.Int {
if s == "" {
return big.NewInt(0)
}
r, ok := new(big.Int).SetString(s, 16)
if !ok {
panic("invalid hex in source file: " + s)
}
return r
}

// BenchmarkSigVerify benchmarks how long it takes the secp256k1 curve to
// verify signatures.
func BenchmarkSigVerify(b *testing.B) {
b.StopTimer()
// Randomly generated keypair.
// Private key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d
pubKey := btcec.NewPublicKey(
hexToFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"),
hexToFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"),
)

// Double sha256 of []byte{0x01, 0x02, 0x03, 0x04}
msgHash := fromHex("8de472e2399610baaa7f84840547cd409434e31f5d3bd71e4d947f283874f9c0")
sig := NewSignature(
hexToModNScalar("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"),
hexToModNScalar("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"),
)

if !sig.Verify(msgHash.Bytes(), pubKey) {
b.Errorf("Signature failed to verify")
return
}
b.StartTimer()

for i := 0; i < b.N; i++ {
sig.Verify(msgHash.Bytes(), pubKey)
}
}

// BenchmarkSign benchmarks how long it takes to sign a message.
func BenchmarkSign(b *testing.B) {
// Randomly generated keypair.
d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d")
privKey := secp256k1.NewPrivateKey(d)

// blake256 of []byte{0x01, 0x02, 0x03, 0x04}.
msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7")

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
Sign(privKey, msgHash)
}
}

// BenchmarkSigSerialize benchmarks how long it takes to serialize a typical
// signature with the strict DER encoding.
func BenchmarkSigSerialize(b *testing.B) {
// Randomly generated keypair.
// Private key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d
// Signature for double sha256 of []byte{0x01, 0x02, 0x03, 0x04}.
sig := NewSignature(
hexToModNScalar("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"),
hexToModNScalar("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"),
)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
sig.Serialize()
}
}

// BenchmarkNonceRFC6979 benchmarks how long it takes to generate a
// deterministic nonce according to RFC6979.
func BenchmarkNonceRFC6979(b *testing.B) {
// Randomly generated keypair.
// Private key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d
// X: d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab
// Y: ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52
privKeyStr := "9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d"
privKey := hexToBytes(privKeyStr)

// BLAKE-256 of []byte{0x01, 0x02, 0x03, 0x04}.
msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7")

b.ReportAllocs()
b.ResetTimer()
var noElideNonce *secp256k1.ModNScalar
for i := 0; i < b.N; i++ {
noElideNonce = secp256k1.NonceRFC6979(privKey, msgHash, nil, nil, 0)
}
_ = noElideNonce
}

// BenchmarkSignCompact benchmarks how long it takes to produce a compact
// signature for a message.
func BenchmarkSignCompact(b *testing.B) {
d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d")
privKey := secp256k1.NewPrivateKey(d)

// blake256 of []byte{0x01, 0x02, 0x03, 0x04}.
msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7")

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = SignCompact(privKey, msgHash, true)
}
}

// BenchmarkSignCompact benchmarks how long it takes to recover a public key
// given a compact signature and message.
func BenchmarkRecoverCompact(b *testing.B) {
// Private key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d
wantPubKey := secp256k1.NewPublicKey(
hexToFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"),
hexToFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"),
)

compactSig := hexToBytes("205978b7896bc71676ba2e459882a8f52e1299449596c4f" +
"93c59bf1fbfa2f9d3b76ecd0c99406f61a6de2bb5a8937c061c176ecf381d0231e0d" +
"af73b922c8952c7")

// blake256 of []byte{0x01, 0x02, 0x03, 0x04}.
msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7")

// Ensure a valid compact signature is being benchmarked.
pubKey, wasCompressed, err := RecoverCompact(compactSig, msgHash)
if err != nil {
b.Fatalf("unexpected err: %v", err)
}
if !wasCompressed {
b.Fatal("recover claims uncompressed pubkey")
}
if !pubKey.IsEqual(wantPubKey) {
b.Fatal("recover returned unexpected pubkey")
}

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _ = RecoverCompact(compactSig, msgHash)
}
}
18 changes: 18 additions & 0 deletions btcec/ecdsa/error.go
@@ -0,0 +1,18 @@
// Copyright (c) 2013-2021 The btcsuite developers
// Copyright (c) 2015-2021 The Decred developers

package ecdsa

import (
secp_ecdsa "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
)

// ErrorKind identifies a kind of error. It has full support for
// errors.Is and errors.As, so the caller can directly check against
// an error kind when determining the reason for an error.
type ErrorKind = secp_ecdsa.ErrorKind

// Error identifies an error related to an ECDSA signature. It has full
// support for errors.Is and errors.As, so the caller can ascertain the
// specific reason for the error by checking the underlying error.
type Error = secp_ecdsa.ErrorKind
7 changes: 4 additions & 3 deletions btcec/example_test.go → btcec/ecdsa/example_test.go
Expand Up @@ -2,13 +2,14 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcec_test
package ecdsa_test

import (
"encoding/hex"
"fmt"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)

Expand All @@ -27,7 +28,7 @@ func Example_signMessage() {
// Sign a message using the private key.
message := "test message"
messageHash := chainhash.DoubleHashB([]byte(message))
signature := btcec.Sign(privKey, messageHash)
signature := ecdsa.Sign(privKey, messageHash)

// Serialize and display the signature.
fmt.Printf("Serialized Signature: %x\n", signature.Serialize())
Expand Down Expand Up @@ -67,7 +68,7 @@ func Example_verifySignature() {
fmt.Println(err)
return
}
signature, err := btcec.ParseSignature(sigBytes)
signature, err := ecdsa.ParseSignature(sigBytes)
if err != nil {
fmt.Println(err)
return
Expand Down
16 changes: 9 additions & 7 deletions btcec/signature.go → btcec/ecdsa/signature.go
@@ -1,14 +1,16 @@
// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2021 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcec
package ecdsa

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

"github.com/btcsuite/btcd/btcec/v2"
secp_ecdsa "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
)

Expand All @@ -22,7 +24,7 @@ var (
type Signature = secp_ecdsa.Signature

// NewSignature instantiates a new signature given some r and s values.
func NewSignature(r, s *ModNScalar) *Signature {
func NewSignature(r, s *btcec.ModNScalar) *Signature {
return secp_ecdsa.NewSignature(r, s)
}

Expand Down Expand Up @@ -122,7 +124,7 @@ func parseSig(sigStr []byte, der bool) (*Signature, error) {
// R must be in the range [1, N-1]. Notice the check for the maximum number
// of bytes is required because SetByteSlice truncates as noted in its
// comment so it could otherwise fail to detect the overflow.
var r ModNScalar
var r btcec.ModNScalar
if len(rBytes) > 32 {
str := "invalid signature: R is larger than 256 bits"
return nil, errors.New(str)
Expand Down Expand Up @@ -169,7 +171,7 @@ func parseSig(sigStr []byte, der bool) (*Signature, error) {
// S must be in the range [1, N-1]. Notice the check for the maximum number
// of bytes is required because SetByteSlice truncates as noted in its
// comment so it could otherwise fail to detect the overflow.
var s ModNScalar
var s btcec.ModNScalar
if len(sBytes) > 32 {
str := "invalid signature: S is larger than 256 bits"
return nil, errors.New(str)
Expand Down Expand Up @@ -214,7 +216,7 @@ func ParseDERSignature(sigStr []byte) (*Signature, error) {
// returned in the format:
// <(byte of 27+public key solution)+4 if compressed >< padded bytes for signature R><padded bytes for signature S>
// where the R and S parameters are padde up to the bitlengh of the curve.
func SignCompact(key *PrivateKey, hash []byte,
func SignCompact(key *btcec.PrivateKey, hash []byte,
isCompressedKey bool) ([]byte, error) {

return secp_ecdsa.SignCompact(key, hash, isCompressedKey), nil
Expand All @@ -224,7 +226,7 @@ func SignCompact(key *PrivateKey, hash []byte,
// Koblitz curve in "curve". If the signature matches then the recovered public
// key will be returned as well as a boolean if the original key was compressed
// or not, else an error will be returned.
func RecoverCompact(signature, hash []byte) (*PublicKey, bool, error) {
func RecoverCompact(signature, hash []byte) (*btcec.PublicKey, bool, error) {
return secp_ecdsa.RecoverCompact(signature, hash)
}

Expand All @@ -233,6 +235,6 @@ func RecoverCompact(signature, hash []byte) (*PublicKey, bool, error) {
// given private key. The produced signature is deterministic (same message and
// same key yield the same signature) and canonical in accordance with RFC6979
// and BIP0062.
func Sign(key *PrivateKey, hash []byte) *Signature {
func Sign(key *btcec.PrivateKey, hash []byte) *Signature {
return secp_ecdsa.Sign(key, hash)
}

0 comments on commit 81fbd9b

Please sign in to comment.