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

btcec/v2: create new schnorr package for BIP-340, move existing ecdsa implementation into new ecdsa package #1777

Merged
merged 6 commits into from
Feb 1, 2022
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
29 changes: 0 additions & 29 deletions btcec/bench_test.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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 {
Roasbeef marked this conversation as resolved.
Show resolved Hide resolved
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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)
}
Loading