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