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