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