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