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