From a0ff35ad09d6a09cbc2c1c5d29761c627d458421 Mon Sep 17 00:00:00 2001 From: Jonathan Chappelow Date: Mon, 18 Apr 2022 16:57:41 -0500 Subject: [PATCH] btcec: remove import of chainhash The chainhash import is challenging for consumers of the latest tagged btcd module (v0.22.0-beta) because that module revision has its own chainhash package. This creates an ambiguous import when trying to import both the older btcd module and btcec/v2 which requires the standalone module version of chainhash. This is the brute solution. An alternative would be to create a chaincfg/chainhash/v2 module and have btcec/v2 require that instead. --- btcec/ecdsa/example_test.go | 12 +++-- btcec/field_test.go | 3 +- btcec/go.mod | 1 - btcec/go.sum | 2 - btcec/schnorr/signature.go | 105 +++++++++++++++++++++++++++++------- 5 files changed, 95 insertions(+), 28 deletions(-) diff --git a/btcec/ecdsa/example_test.go b/btcec/ecdsa/example_test.go index 409a95bd23..2e302969a3 100644 --- a/btcec/ecdsa/example_test.go +++ b/btcec/ecdsa/example_test.go @@ -5,14 +5,20 @@ package ecdsa_test import ( + "crypto/sha256" "encoding/hex" "fmt" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/ecdsa" - "github.com/btcsuite/btcd/chaincfg/chainhash" ) +func doubleHash(b []byte) []byte { + first := sha256.Sum256(b) + second := sha256.Sum256(first[:]) + return second[:] +} + // This example demonstrates signing a message with a secp256k1 private key that // is first parsed form raw bytes and serializing the generated signature. func Example_signMessage() { @@ -27,7 +33,7 @@ func Example_signMessage() { // Sign a message using the private key. message := "test message" - messageHash := chainhash.DoubleHashB([]byte(message)) + messageHash := doubleHash([]byte(message)) signature := ecdsa.Sign(privKey, messageHash) // Serialize and display the signature. @@ -76,7 +82,7 @@ func Example_verifySignature() { // Verify the signature for the message using the public key. message := "test message" - messageHash := chainhash.DoubleHashB([]byte(message)) + messageHash := doubleHash([]byte(message)) verified := signature.Verify(messageHash, pubKey) fmt.Println("Signature Verified?", verified) diff --git a/btcec/field_test.go b/btcec/field_test.go index 6ade97a1eb..11b38c6c78 100644 --- a/btcec/field_test.go +++ b/btcec/field_test.go @@ -6,9 +6,8 @@ package btcec import ( - "math/rand" - "encoding/hex" + "math/rand" "testing" ) diff --git a/btcec/go.mod b/btcec/go.mod index bc8cf721e3..7767a9183e 100644 --- a/btcec/go.mod +++ b/btcec/go.mod @@ -3,7 +3,6 @@ module github.com/btcsuite/btcd/btcec/v2 go 1.17 require ( - github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0 github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 ) diff --git a/btcec/go.sum b/btcec/go.sum index 00af17624d..67aa2a25c6 100644 --- a/btcec/go.sum +++ b/btcec/go.sum @@ -1,5 +1,3 @@ -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0 h1:MSskdM4/xJYcFzy0altH/C/xHopifpWzHUi1JeVI34Q= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= diff --git a/btcec/schnorr/signature.go b/btcec/schnorr/signature.go index ed4b9cb5bf..7c33b9b07f 100644 --- a/btcec/schnorr/signature.go +++ b/btcec/schnorr/signature.go @@ -6,10 +6,10 @@ package schnorr import ( + "crypto/sha256" "fmt" "github.com/btcsuite/btcd/btcec/v2" - "github.com/btcsuite/btcd/chaincfg/chainhash" secp "github.com/decred/dcrd/dcrec/secp256k1/v4" ecdsa_schnorr "github.com/decred/dcrd/dcrec/secp256k1/v4/schnorr" ) @@ -35,8 +35,73 @@ var ( 0x68, 0x6d, 0x71, 0xe8, 0x7f, 0x39, 0x4f, 0x79, 0x9c, 0x00, 0xa5, 0x21, 0x03, 0xcb, 0x4e, 0x17, } + + // tagBIP0340Challenge is the BIP-0340 tag for challenges. + tagBIP0340Challenge = []byte("BIP0340/challenge") + + // tagBIP0340Aux is the BIP-0340 tag for aux data. + tagBIP0340Aux = []byte("BIP0340/aux") + + // tagBIP0340Nonce is the BIP-0340 tag for nonces. + tagBIP0340Nonce = []byte("BIP0340/nonce") + + // tagTapSighash is the tag used by BIP 341 to generate the sighash + // flags. + tagTapSighash = []byte("TapSighash") + + // tagTagTapLeaf is the message tag prefix used to compute the hash + // digest of a tapscript leaf. + tagTapLeaf = []byte("TapLeaf") + + // tagTapBranch is the message tag prefix used to compute the + // hash digest of two tap leaves into a taproot branch node. + tagTapBranch = []byte("TapBranch") + + // tagTapTweak is the message tag prefix used to compute the hash tweak + // used to enable a public key to commit to the taproot branch root + // for the witness program. + tagTapTweak = []byte("TapTweak") + + // precomputedTags is a map containing the SHA-256 hash of the BIP-0340 + // tags. + precomputedTags = map[string][32]byte{ + string(tagBIP0340Challenge): sha256.Sum256(tagBIP0340Challenge), + string(tagBIP0340Aux): sha256.Sum256(tagBIP0340Aux), + string(tagBIP0340Nonce): sha256.Sum256(tagBIP0340Nonce), + string(tagTapSighash): sha256.Sum256(tagTapSighash), + string(tagTapLeaf): sha256.Sum256(tagTapLeaf), + string(tagTapBranch): sha256.Sum256(tagTapBranch), + string(tagTapTweak): sha256.Sum256(tagTapTweak), + } ) +// taggedHash implements the tagged hash scheme described in BIP-340. We use +// sha-256 to bind a message hash to a specific context using a tag: +// sha256(sha256(tag) || sha256(tag) || msg). +func taggedHash(tag []byte, msgs ...[]byte) [32]byte { + // Check to see if we've already pre-computed the hash of the tag. If + // so then this'll save us an extra sha256 hash. + shaTag, ok := precomputedTags[string(tag)] + if !ok { + shaTag = sha256.Sum256(tag) + } + + // h = sha256(sha256(tag) || sha256(tag) || msg) + h := sha256.New() + h.Write(shaTag[:]) + h.Write(shaTag[:]) + + for _, msg := range msgs { + h.Write(msg) + } + + taggedHash := h.Sum(nil) + var hash [32]byte + copy(hash[:], taggedHash) + + return hash +} + // Signature is a type representing a Schnorr signature. type Signature struct { r btcec.FieldVal @@ -129,7 +194,7 @@ func schnorrVerify(sig *Signature, hash []byte, pubKeyBytes []byte) error { // 7. Fail if is_infinite(R) // 8. Fail if not hash_even_y(R) // 9. Fail is x(R) != r. - // 10. Return success iff not failure occured before reachign this + // 10. Return success iff not failure occurred before reaching this // point. // Step 1. @@ -174,12 +239,12 @@ func schnorrVerify(sig *Signature, hash []byte, pubKeyBytes []byte) error { sig.r.PutBytesUnchecked(rBytes[:]) pBytes := SerializePubKey(pubKey) - commitment := chainhash.TaggedHash( - chainhash.TagBIP0340Challenge, rBytes[:], pBytes, hash, + commitment := taggedHash( + tagBIP0340Challenge, rBytes[:], pBytes, hash, ) var e btcec.ModNScalar - if overflow := e.SetBytes((*[32]byte)(commitment)); overflow != 0 { + if overflow := e.SetBytes(&commitment); overflow != 0 { str := "hash of (r || P || m) too big" return signatureError(ecdsa_schnorr.ErrSchnorrHashValue, str) } @@ -228,7 +293,7 @@ func schnorrVerify(sig *Signature, hash []byte, pubKeyBytes []byte) error { // Step 10. // - // Return success iff not failure occured before reachign this + // Return success iff not failure occurred before reaching this return nil } @@ -264,7 +329,7 @@ func schnorrSign(privKey, nonce *btcec.ModNScalar, pubKey *btcec.PublicKey, hash // n = curve order // d = private key // m = message - // a = input randmoness + // a = input randomness // r, s = signature // // 1. d' = int(d) @@ -316,12 +381,12 @@ func schnorrSign(privKey, nonce *btcec.ModNScalar, pubKey *btcec.PublicKey, hash r.PutBytesUnchecked(rBytes[:]) pBytes := SerializePubKey(pubKey) - commitment := chainhash.TaggedHash( - chainhash.TagBIP0340Challenge, rBytes[:], pBytes, hash, + commitment := taggedHash( + tagBIP0340Challenge, rBytes[:], pBytes, hash, ) var e btcec.ModNScalar - if overflow := e.SetBytes((*[32]byte)(commitment)); overflow != 0 { + if overflow := e.SetBytes(&commitment); overflow != 0 { k.Zero() str := "hash of (r || P || m) too big" return nil, signatureError(ecdsa_schnorr.ErrSchnorrHashValue, str) @@ -350,8 +415,8 @@ func schnorrSign(privKey, nonce *btcec.ModNScalar, pubKey *btcec.PublicKey, hash return sig, nil } -// SignOption is a functional option arguemnt that allows callers to modify the -// way we generate BIP-340 schnorr signatues. +// SignOption is a functional option argument that allows callers to modify the +// way we generate BIP-340 schnorr signatures. type SignOption func(*signOptions) // signOptions houses the set of functional options that can be used to modify @@ -372,7 +437,7 @@ func defaultSignOptions() *signOptions { } // FastSign forces signing to skip the extra verification step at the end. -// Peformance sensitive applications may opt to use this option to speed up the +// Performance sensitive applications may opt to use this option to speed up the // signing operation. func FastSign() SignOption { return func(o *signOptions) { @@ -417,7 +482,7 @@ func Sign(privKey *btcec.PrivateKey, hash []byte, // n = curve order // d = private key // m = message - // a = input randmoness + // a = input randomness // r, s = signature // // 1. d' = int(d) @@ -478,14 +543,14 @@ func Sign(privKey *btcec.PrivateKey, hash []byte, // At this point, we check to see if a CustomNonce has been passed in, // and if so, then we'll deviate from the main routine here by - // generating the nonce value as specifid by BIP-0340. + // generating the nonce value as specified by BIP-0340. if opts.authNonce != nil { // Step 6. // // t = bytes(d) xor tagged_hash("BIP0340/aux", a) privBytes := privKeyScalar.Bytes() - t := chainhash.TaggedHash( - chainhash.TagBIP0340Aux, (*opts.authNonce)[:], + t := taggedHash( + tagBIP0340Aux, (*opts.authNonce)[:], ) for i := 0; i < len(t); i++ { t[i] ^= privBytes[i] @@ -497,15 +562,15 @@ func Sign(privKey *btcec.PrivateKey, hash []byte, // // We snip off the first byte of the serialized pubkey, as we // only need the x coordinate and not the market byte. - rand := chainhash.TaggedHash( - chainhash.TagBIP0340Nonce, t[:], pubKeyBytes[1:], hash, + rand := taggedHash( + tagBIP0340Nonce, t[:], pubKeyBytes[1:], hash, ) // Step 8. // // k'= int(rand) mod n var kPrime btcec.ModNScalar - kPrime.SetBytes((*[32]byte)(rand)) + kPrime.SetBytes(&rand) // Step 9. //