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