hash.go raw

   1  // Copyright (c) 2013-2016 The btcsuite developers
   2  // Copyright (c) 2015 The Decred developers
   3  // Use of this source code is governed by an ISC
   4  // license that can be found in the LICENSE file.
   5  
   6  package chainhash
   7  
   8  import (
   9  	"encoding/json"
  10  	"fmt"
  11  
  12  	"next.orly.dev/pkg/nostr/encoders/hex"
  13  	"github.com/minio/sha256-simd"
  14  )
  15  
  16  const (
  17  	// HashSize of array used to store hashes.  See Hash.
  18  	HashSize = 32
  19  	// MaxHashStringSize is the maximum length of a Hash hash string.
  20  	MaxHashStringSize = HashSize * 2
  21  )
  22  
  23  var (
  24  	// TagBIP0340Challenge is the BIP-0340 tag for challenges.
  25  	TagBIP0340Challenge = []byte("BIP0340/challenge")
  26  	// TagBIP0340Aux is the BIP-0340 tag for aux data.
  27  	TagBIP0340Aux = []byte("BIP0340/aux")
  28  	// TagBIP0340Nonce is the BIP-0340 tag for nonces.
  29  	TagBIP0340Nonce = []byte("BIP0340/nonce")
  30  	// TagTapSighash is the tag used by BIP 341 to generate the sighash
  31  	// flags.
  32  	TagTapSighash = []byte("TapSighash")
  33  	// TagTapLeaf is the message tag prefix used to compute the hash
  34  	// digest of a tapscript leaf.
  35  	TagTapLeaf = []byte("TapLeaf")
  36  	// TagTapBranch is the message tag prefix used to compute the
  37  	// hash digest of two tap leaves into a taproot branch node.
  38  	TagTapBranch = []byte("TapBranch")
  39  	// TagTapTweak is the message tag prefix used to compute the hash tweak
  40  	// used to enable a public key to commit to the taproot branch root
  41  	// for the witness program.
  42  	TagTapTweak = []byte("TapTweak")
  43  	// precomputedTags is a map containing the SHA-256 hash of the BIP-0340
  44  	// tags.
  45  	precomputedTags = map[string]Hash{
  46  		string(TagBIP0340Challenge): sha256.Sum256(TagBIP0340Challenge),
  47  		string(TagBIP0340Aux):       sha256.Sum256(TagBIP0340Aux),
  48  		string(TagBIP0340Nonce):     sha256.Sum256(TagBIP0340Nonce),
  49  		string(TagTapSighash):       sha256.Sum256(TagTapSighash),
  50  		string(TagTapLeaf):          sha256.Sum256(TagTapLeaf),
  51  		string(TagTapBranch):        sha256.Sum256(TagTapBranch),
  52  		string(TagTapTweak):         sha256.Sum256(TagTapTweak),
  53  	}
  54  )
  55  
  56  // ErrHashStrSize describes an error that indicates the caller specified a hash
  57  // string that has too many characters.
  58  var ErrHashStrSize = fmt.Errorf(
  59  	"max hash string length is %v bytes",
  60  	MaxHashStringSize,
  61  )
  62  
  63  // Hash is used in several of the bitcoin messages and common structures.  It
  64  // typically represents the double sha256 of data.
  65  type Hash [HashSize]byte
  66  
  67  // String returns the Hash as the hexadecimal string of the byte-reversed
  68  // hash.
  69  func (hash Hash) String() string {
  70  	for i := 0; i < HashSize/2; i++ {
  71  		hash[i], hash[HashSize-1-i] = hash[HashSize-1-i], hash[i]
  72  	}
  73  	return hex.Enc(hash[:])
  74  }
  75  
  76  // CloneBytes returns a copy of the bytes which represent the hash as a byte
  77  // slice.
  78  //
  79  // NOTE: It is generally cheaper to just slice the hash directly thereby reusing
  80  // the same bytes rather than calling this method.
  81  func (hash *Hash) CloneBytes() []byte {
  82  	newHash := make([]byte, HashSize)
  83  	copy(newHash, hash[:])
  84  	return newHash
  85  }
  86  
  87  // SetBytes sets the bytes which represent the hash.  An error is returned if
  88  // the number of bytes passed in is not HashSize.
  89  func (hash *Hash) SetBytes(newHash []byte) error {
  90  	nhlen := len(newHash)
  91  	if nhlen != HashSize {
  92  		return fmt.Errorf(
  93  			"invalid hash length of %v, want %v", nhlen,
  94  			HashSize,
  95  		)
  96  	}
  97  	copy(hash[:], newHash)
  98  	return nil
  99  }
 100  
 101  // IsEqual returns true if target is the same as hash.
 102  func (hash *Hash) IsEqual(target *Hash) bool {
 103  	if hash == nil && target == nil {
 104  		return true
 105  	}
 106  	if hash == nil || target == nil {
 107  		return false
 108  	}
 109  	return *hash == *target
 110  }
 111  
 112  // MarshalJSON serialises the hash as a JSON appropriate string value.
 113  func (hash Hash) MarshalJSON() ([]byte, error) {
 114  	return json.Marshal(hash.String())
 115  }
 116  
 117  // UnmarshalJSON parses the hash with JSON appropriate string value.
 118  func (hash *Hash) UnmarshalJSON(input []byte) error {
 119  	// If the first byte indicates an array, the hash could have been marshalled
 120  	// using the legacy method and e.g. persisted.
 121  	if len(input) > 0 && input[0] == '[' {
 122  		return decodeLegacy(hash, input)
 123  	}
 124  	var sh string
 125  	err := json.Unmarshal(input, &sh)
 126  	if err != nil {
 127  		return err
 128  	}
 129  	newHash, err := NewHashFromStr(sh)
 130  	if err != nil {
 131  		return err
 132  	}
 133  	return hash.SetBytes(newHash[:])
 134  }
 135  
 136  // NewHash returns a new Hash from a byte slice.  An error is returned if
 137  // the number of bytes passed in is not HashSize.
 138  func NewHash(newHash []byte) (*Hash, error) {
 139  	var sh Hash
 140  	err := sh.SetBytes(newHash)
 141  	if err != nil {
 142  		return nil, err
 143  	}
 144  	return &sh, err
 145  }
 146  
 147  // TaggedHash implements the tagged hash scheme described in BIP-340. We use
 148  // sha-256 to bind a message hash to a specific context using a tag:
 149  // sha256(sha256(tag) || sha256(tag) || msg).
 150  func TaggedHash(tag []byte, msgs ...[]byte) *Hash {
 151  	// Check to see if we've already pre-computed the hash of the tag. If
 152  	// so then this'll save us an extra sha256 hash.
 153  	shaTag, ok := precomputedTags[string(tag)]
 154  	if !ok {
 155  		shaTag = sha256.Sum256(tag)
 156  	}
 157  	// h = sha256(sha256(tag) || sha256(tag) || msg)
 158  	h := sha256.New()
 159  	h.Write(shaTag[:])
 160  	h.Write(shaTag[:])
 161  	for _, msg := range msgs {
 162  		h.Write(msg)
 163  	}
 164  	taggedHash := h.Sum(nil)
 165  	// The function can't error out since the above hash is guaranteed to
 166  	// be 32 bytes.
 167  	hash, _ := NewHash(taggedHash)
 168  	return hash
 169  }
 170  
 171  // NewHashFromStr creates a Hash from a hash string.  The string should be
 172  // the hexadecimal string of a byte-reversed hash, but any missing characters
 173  // result in zero padding at the end of the Hash.
 174  func NewHashFromStr(hash string) (*Hash, error) {
 175  	ret := new(Hash)
 176  	err := Decode(ret, hash)
 177  	if err != nil {
 178  		return nil, err
 179  	}
 180  	return ret, nil
 181  }
 182  
 183  // Decode decodes the byte-reversed hexadecimal string encoding of a Hash to a
 184  // destination.
 185  func Decode(dst *Hash, src string) error {
 186  	// Return error if hash string is too long.
 187  	if len(src) > MaxHashStringSize {
 188  		return ErrHashStrSize
 189  	}
 190  	// Hex decoder expects the hash to be a multiple of two.  When not, pad
 191  	// with a leading zero.
 192  	var srcBytes []byte
 193  	if len(src)%2 == 0 {
 194  		srcBytes = []byte(src)
 195  	} else {
 196  		srcBytes = make([]byte, 1+len(src))
 197  		srcBytes[0] = '0'
 198  		copy(srcBytes[1:], src)
 199  	}
 200  	// Hex decode the source bytes to a temporary destination.
 201  	var reversedHash Hash
 202  	_, err := hex.DecAppend(
 203  		reversedHash[HashSize-hex.DecLen(len(srcBytes)):],
 204  		srcBytes,
 205  	)
 206  	if err != nil {
 207  		return err
 208  	}
 209  	// Reverse copy from the temporary hash to destination.  Because the
 210  	// temporary was zeroed, the written result will be correctly padded.
 211  	for i, b := range reversedHash[:HashSize/2] {
 212  		dst[i], dst[HashSize-1-i] = reversedHash[HashSize-1-i], b
 213  	}
 214  	return nil
 215  }
 216  
 217  // decodeLegacy decodes an Hash that has been encoded with the legacy method
 218  // (i.e. represented as a bytes array) to a destination.
 219  func decodeLegacy(dst *Hash, src []byte) error {
 220  	var hashBytes []byte
 221  	err := json.Unmarshal(src, &hashBytes)
 222  	if err != nil {
 223  		return err
 224  	}
 225  	if len(hashBytes) != HashSize {
 226  		return ErrHashStrSize
 227  	}
 228  	return dst.SetBytes(hashBytes)
 229  }
 230