hash.go raw
1 package p256k1
2
3 import (
4 "crypto/sha256"
5 "errors"
6 "hash"
7 "sync"
8 "unsafe"
9
10 sha256simd "github.com/minio/sha256-simd"
11 )
12
13 // Precomputed TaggedHash prefixes for common BIP-340 tags
14 // These are computed once at init time to avoid repeated hash operations
15 var (
16 bip340AuxTagHash [32]byte
17 bip340NonceTagHash [32]byte
18 bip340ChallengeTagHash [32]byte
19 taggedHashInitOnce sync.Once
20 )
21
22 // sha256Pool provides a pool of SHA256 hash contexts to reduce allocations
23 var sha256Pool = sync.Pool{
24 New: func() interface{} {
25 return sha256.New()
26 },
27 }
28
29 // getSHA256 gets a SHA256 hash context from the pool
30 func getSHA256() hash.Hash {
31 h := sha256Pool.Get().(hash.Hash)
32 h.Reset()
33 return h
34 }
35
36 // putSHA256 returns a SHA256 hash context to the pool
37 func putSHA256(h hash.Hash) {
38 sha256Pool.Put(h)
39 }
40
41 // hmacPool provides a pool of HMACSHA256 structs to reduce allocations
42 var hmacPool = sync.Pool{
43 New: func() interface{} {
44 return &HMACSHA256{}
45 },
46 }
47
48 // getHMAC gets an HMACSHA256 from the pool
49 func getHMAC() *HMACSHA256 {
50 return hmacPool.Get().(*HMACSHA256)
51 }
52
53 // putHMAC returns an HMACSHA256 to the pool
54 func putHMAC(h *HMACSHA256) {
55 if h.inner != nil {
56 putSHA256(h.inner)
57 h.inner = nil
58 }
59 if h.outer != nil {
60 putSHA256(h.outer)
61 h.outer = nil
62 }
63 hmacPool.Put(h)
64 }
65
66 func initTaggedHashPrefixes() {
67 bip340AuxTagHash = sha256.Sum256([]byte("BIP0340/aux"))
68 bip340NonceTagHash = sha256.Sum256([]byte("BIP0340/nonce"))
69 bip340ChallengeTagHash = sha256.Sum256([]byte("BIP0340/challenge"))
70 }
71
72 // getTaggedHashPrefix returns the precomputed SHA256(tag) for common tags
73 func getTaggedHashPrefix(tag []byte) [32]byte {
74 taggedHashInitOnce.Do(initTaggedHashPrefixes)
75
76 // Fast path for common BIP-340 tags
77 if len(tag) == 13 {
78 switch string(tag) {
79 case "BIP0340/aux":
80 return bip340AuxTagHash
81 case "BIP0340/nonce":
82 return bip340NonceTagHash
83 case "BIP0340/challenge":
84 return bip340ChallengeTagHash
85 }
86 }
87
88 // Fallback for unknown tags
89 return sha256.Sum256(tag)
90 }
91
92 // SHA256 represents a SHA-256 hash context
93 type SHA256 struct {
94 hasher hash.Hash
95 }
96
97 // NewSHA256 creates a new SHA-256 hash context
98 func NewSHA256() *SHA256 {
99 h := &SHA256{}
100 h.hasher = sha256simd.New()
101 return h
102 }
103
104 // Write writes data to the hash
105 func (h *SHA256) Write(data []byte) {
106 h.hasher.Write(data)
107 }
108
109 // Sum finalizes the hash and returns the 32-byte result
110 func (h *SHA256) Sum(out []byte) []byte {
111 if out == nil {
112 out = make([]byte, 32)
113 }
114 copy(out, h.hasher.Sum(nil))
115 return out
116 }
117
118 // Finalize finalizes the hash and writes the result to out32 (must be 32 bytes)
119 func (h *SHA256) Finalize(out32 []byte) {
120 if len(out32) != 32 {
121 panic("output buffer must be 32 bytes")
122 }
123 sum := h.hasher.Sum(nil)
124 copy(out32, sum)
125 }
126
127 // Clear clears the hash context to prevent leaking sensitive information
128 func (h *SHA256) Clear() {
129 memclear(unsafe.Pointer(h), unsafe.Sizeof(*h))
130 }
131
132 // HMACSHA256 represents an HMAC-SHA256 context
133 // Uses pooled hash contexts to minimize allocations
134 type HMACSHA256 struct {
135 inner, outer hash.Hash
136 }
137
138 // NewHMACSHA256 creates a new HMAC-SHA256 context with the given key
139 // Uses pooled HMACSHA256 and SHA256 contexts to minimize allocations
140 func NewHMACSHA256(key []byte) *HMACSHA256 {
141 h := getHMAC()
142
143 // Prepare key: if keylen > 64, hash it first
144 var rkey [64]byte
145 if len(key) <= 64 {
146 copy(rkey[:], key)
147 // Zero pad the rest
148 for i := len(key); i < 64; i++ {
149 rkey[i] = 0
150 }
151 } else {
152 // Hash the key if it's too long
153 hasher := getSHA256()
154 hasher.Write(key)
155 sum := hasher.Sum(nil)
156 copy(rkey[:32], sum)
157 putSHA256(hasher)
158 // Zero pad the rest
159 for i := 32; i < 64; i++ {
160 rkey[i] = 0
161 }
162 }
163
164 // Initialize outer hash with key XOR 0x5c
165 h.outer = getSHA256()
166 for i := 0; i < 64; i++ {
167 rkey[i] ^= 0x5c
168 }
169 h.outer.Write(rkey[:])
170
171 // Initialize inner hash with key XOR 0x36
172 h.inner = getSHA256()
173 for i := 0; i < 64; i++ {
174 rkey[i] ^= 0x5c ^ 0x36
175 }
176 h.inner.Write(rkey[:])
177
178 // Clear sensitive key material
179 memclear(unsafe.Pointer(&rkey), unsafe.Sizeof(rkey))
180 return h
181 }
182
183 // Write writes data to the inner hash
184 func (h *HMACSHA256) Write(data []byte) {
185 h.inner.Write(data)
186 }
187
188 // Finalize finalizes the HMAC and writes the result to out32 (must be 32 bytes)
189 func (h *HMACSHA256) Finalize(out32 []byte) {
190 if len(out32) != 32 {
191 panic("output buffer must be 32 bytes")
192 }
193
194 // Finalize inner hash - use temp[:0] to avoid allocation
195 var temp [32]byte
196 h.inner.Sum(temp[:0])
197
198 // Feed inner hash result to outer hash
199 h.outer.Write(temp[:])
200
201 // Finalize outer hash - append to out32[:0] to avoid allocation
202 h.outer.Sum(out32[:0])
203
204 // Clear temp
205 memclear(unsafe.Pointer(&temp), unsafe.Sizeof(temp))
206 }
207
208 // Clear clears the HMAC context and returns it to the pool
209 func (h *HMACSHA256) Clear() {
210 putHMAC(h)
211 }
212
213 // RFC6979HMACSHA256 implements RFC 6979 deterministic nonce generation
214 type RFC6979HMACSHA256 struct {
215 v [32]byte
216 k [32]byte
217 retry int
218 }
219
220 // Preallocated single-byte slices to avoid allocation in hot paths
221 var (
222 byte0x00 = []byte{0x00}
223 byte0x01 = []byte{0x01}
224 )
225
226 // rfc6979Pool provides a pool of RFC6979HMACSHA256 structs
227 var rfc6979Pool = sync.Pool{
228 New: func() interface{} {
229 return &RFC6979HMACSHA256{}
230 },
231 }
232
233 // NewRFC6979HMACSHA256 initializes a new RFC6979 HMAC-SHA256 context
234 func NewRFC6979HMACSHA256(key []byte) *RFC6979HMACSHA256 {
235 rng := rfc6979Pool.Get().(*RFC6979HMACSHA256)
236
237 // RFC6979 3.2.b: V = 0x01 0x01 0x01 ... 0x01 (32 bytes)
238 for i := 0; i < 32; i++ {
239 rng.v[i] = 0x01
240 }
241
242 // RFC6979 3.2.c: K = 0x00 0x00 0x00 ... 0x00 (32 bytes)
243 for i := 0; i < 32; i++ {
244 rng.k[i] = 0x00
245 }
246
247 // RFC6979 3.2.d: K = HMAC_K(V || 0x00 || key)
248 hmac := NewHMACSHA256(rng.k[:])
249 hmac.Write(rng.v[:])
250 hmac.Write(byte0x00)
251 hmac.Write(key)
252 hmac.Finalize(rng.k[:])
253 hmac.Clear()
254
255 // V = HMAC_K(V)
256 hmac = NewHMACSHA256(rng.k[:])
257 hmac.Write(rng.v[:])
258 hmac.Finalize(rng.v[:])
259 hmac.Clear()
260
261 // RFC6979 3.2.f: K = HMAC_K(V || 0x01 || key)
262 hmac = NewHMACSHA256(rng.k[:])
263 hmac.Write(rng.v[:])
264 hmac.Write(byte0x01)
265 hmac.Write(key)
266 hmac.Finalize(rng.k[:])
267 hmac.Clear()
268
269 // V = HMAC_K(V)
270 hmac = NewHMACSHA256(rng.k[:])
271 hmac.Write(rng.v[:])
272 hmac.Finalize(rng.v[:])
273 hmac.Clear()
274
275 rng.retry = 0
276 return rng
277 }
278
279 // Generate generates output bytes using RFC6979
280 func (rng *RFC6979HMACSHA256) Generate(out []byte) {
281 // RFC6979 3.2.h: If retry, update K and V
282 if rng.retry != 0 {
283 hmac := NewHMACSHA256(rng.k[:])
284 hmac.Write(rng.v[:])
285 hmac.Write(byte0x00)
286 hmac.Finalize(rng.k[:])
287 hmac.Clear()
288
289 hmac = NewHMACSHA256(rng.k[:])
290 hmac.Write(rng.v[:])
291 hmac.Finalize(rng.v[:])
292 hmac.Clear()
293 }
294
295 // Generate output bytes
296 outlen := len(out)
297 for outlen > 0 {
298 hmac := NewHMACSHA256(rng.k[:])
299 hmac.Write(rng.v[:])
300 hmac.Finalize(rng.v[:])
301 hmac.Clear()
302
303 now := outlen
304 if now > 32 {
305 now = 32
306 }
307 copy(out, rng.v[:now])
308 out = out[now:]
309 outlen -= now
310 }
311
312 rng.retry = 1
313 }
314
315 // Finalize finalizes the RFC6979 context
316 func (rng *RFC6979HMACSHA256) Finalize() {
317 // Nothing to do, but matches C API
318 }
319
320 // Clear clears the RFC6979 context and returns it to the pool
321 func (rng *RFC6979HMACSHA256) Clear() {
322 memclear(unsafe.Pointer(rng), unsafe.Sizeof(*rng))
323 rfc6979Pool.Put(rng)
324 }
325
326 // TaggedHash computes SHA256(SHA256(tag) || SHA256(tag) || data)
327 // This is used in BIP-340 for Schnorr signatures
328 // Optimized to use precomputed tag hashes for common BIP-340 tags
329 // Pool of SHA-256 hash contexts for TaggedHash — avoids allocation in steady
330 // state while remaining safe for concurrent use across goroutines.
331 var taggedHashPool = sync.Pool{
332 New: func() any { return sha256.New() },
333 }
334
335 func TaggedHash(tag []byte, data []byte) [32]byte {
336 var result [32]byte
337
338 // Get precomputed SHA256(tag) prefix (or compute if not cached)
339 tagHash := getTaggedHashPrefix(tag)
340
341 // Second hash: SHA256(SHA256(tag) || SHA256(tag) || data)
342 // Each goroutine gets its own hasher from the pool — safe for concurrent use.
343 h := taggedHashPool.Get().(hash.Hash)
344 h.Reset()
345 h.Write(tagHash[:]) // SHA256(tag)
346 h.Write(tagHash[:]) // SHA256(tag) again
347 h.Write(data) // data
348 h.Sum(result[:0]) // Sum directly into result without allocation
349 taggedHashPool.Put(h)
350
351 return result
352 }
353
354 // HashToScalar converts a 32-byte hash to a scalar value
355 func HashToScalar(hash []byte) (*Scalar, error) {
356 if len(hash) != 32 {
357 return nil, errors.New("hash must be 32 bytes")
358 }
359
360 var scalar Scalar
361 scalar.setB32(hash)
362 return &scalar, nil
363 }
364
365 // HashToField converts a 32-byte hash to a field element
366 func HashToField(hash []byte) (*FieldElement, error) {
367 if len(hash) != 32 {
368 return nil, errors.New("hash must be 32 bytes")
369 }
370
371 var field FieldElement
372 if err := field.setB32(hash); err != nil {
373 return nil, err
374 }
375 return &field, nil
376 }
377