From b7a4622a6b75b412be1e0acf72f2081eb4b19957 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 31 Jan 2022 14:54:12 -0800 Subject: [PATCH] btcec+chaincfg: use pre-computed tag hash values In this commit, we optimize our signature implementation slightly, by defining pre-computed sha256(tag) variables for the commonly used values. If a tag matches this, then we'll use that hash value to avoid an extra round of hashing. --- btcec/schnorr/signature.go | 18 ++++++------------ chaincfg/chainhash/hash.go | 28 +++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/btcec/schnorr/signature.go b/btcec/schnorr/signature.go index d6be341a8e..ed4b9cb5bf 100644 --- a/btcec/schnorr/signature.go +++ b/btcec/schnorr/signature.go @@ -22,14 +22,6 @@ const ( scalarSize = 32 ) -var ( - tagHashAux = []byte("BIP0340/aux") - - tagHashNonce = []byte("BIP0340/nonce") - - tagHashChallenge = []byte("BIP0340/challenge") -) - var ( // rfc6979ExtraDataV0 is the extra data to feed to RFC6979 when // generating the deterministic nonce for the BIP-340 scheme. This @@ -183,7 +175,7 @@ func schnorrVerify(sig *Signature, hash []byte, pubKeyBytes []byte) error { pBytes := SerializePubKey(pubKey) commitment := chainhash.TaggedHash( - tagHashChallenge, rBytes[:], pBytes, hash, + chainhash.TagBIP0340Challenge, rBytes[:], pBytes, hash, ) var e btcec.ModNScalar @@ -325,7 +317,7 @@ func schnorrSign(privKey, nonce *btcec.ModNScalar, pubKey *btcec.PublicKey, hash pBytes := SerializePubKey(pubKey) commitment := chainhash.TaggedHash( - tagHashChallenge, rBytes[:], pBytes, hash, + chainhash.TagBIP0340Challenge, rBytes[:], pBytes, hash, ) var e btcec.ModNScalar @@ -492,7 +484,9 @@ func Sign(privKey *btcec.PrivateKey, hash []byte, // // t = bytes(d) xor tagged_hash("BIP0340/aux", a) privBytes := privKeyScalar.Bytes() - t := chainhash.TaggedHash(tagHashAux, (*opts.authNonce)[:]) + t := chainhash.TaggedHash( + chainhash.TagBIP0340Aux, (*opts.authNonce)[:], + ) for i := 0; i < len(t); i++ { t[i] ^= privBytes[i] } @@ -504,7 +498,7 @@ 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( - tagHashNonce, t[:], pubKeyBytes[1:], hash, + chainhash.TagBIP0340Nonce, t[:], pubKeyBytes[1:], hash, ) // Step 8. diff --git a/chaincfg/chainhash/hash.go b/chaincfg/chainhash/hash.go index 2859680fe8..b7a2d8a7f8 100644 --- a/chaincfg/chainhash/hash.go +++ b/chaincfg/chainhash/hash.go @@ -17,6 +17,25 @@ const HashSize = 32 // MaxHashStringSize is the maximum length of a Hash hash string. const MaxHashStringSize = HashSize * 2 +var ( + // 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") + + // precomputedTags is a map containing the SHA-256 hash of the BIP-0340 + // tags. + precomputedTags = map[string]Hash{ + string(TagBIP0340Challenge): sha256.Sum256(TagBIP0340Challenge), + string(TagBIP0340Aux): sha256.Sum256(TagBIP0340Aux), + string(TagBIP0340Nonce): sha256.Sum256(TagBIP0340Nonce), + } +) + // ErrHashStrSize describes an error that indicates the caller specified a hash // string that has too many characters. var ErrHashStrSize = fmt.Errorf("max hash string length is %v bytes", MaxHashStringSize) @@ -84,10 +103,13 @@ func NewHash(newHash []byte) (*Hash, error) { // 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). -// -// TODO(roasbeef): add fast paths for common known tags func TaggedHash(tag []byte, msgs ...[]byte) *Hash { - shaTag := sha256.Sum256(tag) + // 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()