Files
p256k1/hash.go
2025-11-02 02:45:59 +00:00

309 lines
6.8 KiB
Go

package p256k1
import (
"crypto/sha256"
"errors"
"hash"
"sync"
"unsafe"
sha256simd "github.com/minio/sha256-simd"
)
// Precomputed TaggedHash prefixes for common BIP-340 tags
// These are computed once at init time to avoid repeated hash operations
var (
bip340AuxTagHash [32]byte
bip340NonceTagHash [32]byte
bip340ChallengeTagHash [32]byte
taggedHashInitOnce sync.Once
)
func initTaggedHashPrefixes() {
bip340AuxTagHash = sha256.Sum256([]byte("BIP0340/aux"))
bip340NonceTagHash = sha256.Sum256([]byte("BIP0340/nonce"))
bip340ChallengeTagHash = sha256.Sum256([]byte("BIP0340/challenge"))
}
// getTaggedHashPrefix returns the precomputed SHA256(tag) for common tags
func getTaggedHashPrefix(tag []byte) [32]byte {
taggedHashInitOnce.Do(initTaggedHashPrefixes)
// Fast path for common BIP-340 tags
if len(tag) == 13 {
switch string(tag) {
case "BIP0340/aux":
return bip340AuxTagHash
case "BIP0340/nonce":
return bip340NonceTagHash
case "BIP0340/challenge":
return bip340ChallengeTagHash
}
}
// Fallback for unknown tags
return sha256.Sum256(tag)
}
// SHA256 represents a SHA-256 hash context
type SHA256 struct {
hasher hash.Hash
}
// NewSHA256 creates a new SHA-256 hash context
func NewSHA256() *SHA256 {
h := &SHA256{}
h.hasher = sha256simd.New()
return h
}
// Write writes data to the hash
func (h *SHA256) Write(data []byte) {
h.hasher.Write(data)
}
// Sum finalizes the hash and returns the 32-byte result
func (h *SHA256) Sum(out []byte) []byte {
if out == nil {
out = make([]byte, 32)
}
copy(out, h.hasher.Sum(nil))
return out
}
// Finalize finalizes the hash and writes the result to out32 (must be 32 bytes)
func (h *SHA256) Finalize(out32 []byte) {
if len(out32) != 32 {
panic("output buffer must be 32 bytes")
}
sum := h.hasher.Sum(nil)
copy(out32, sum)
}
// Clear clears the hash context to prevent leaking sensitive information
func (h *SHA256) Clear() {
memclear(unsafe.Pointer(h), unsafe.Sizeof(*h))
}
// HMACSHA256 represents an HMAC-SHA256 context
type HMACSHA256 struct {
inner, outer SHA256
}
// NewHMACSHA256 creates a new HMAC-SHA256 context with the given key
func NewHMACSHA256(key []byte) *HMACSHA256 {
h := &HMACSHA256{}
// Prepare key: if keylen > 64, hash it first
var rkey [64]byte
if len(key) <= 64 {
copy(rkey[:], key)
// Zero pad the rest
for i := len(key); i < 64; i++ {
rkey[i] = 0
}
} else {
// Hash the key if it's too long
hasher := sha256.New()
hasher.Write(key)
sum := hasher.Sum(nil)
copy(rkey[:32], sum)
// Zero pad the rest
for i := 32; i < 64; i++ {
rkey[i] = 0
}
}
// Initialize outer hash with key XOR 0x5c
h.outer = SHA256{hasher: sha256.New()}
for i := 0; i < 64; i++ {
rkey[i] ^= 0x5c
}
h.outer.hasher.Write(rkey[:])
// Initialize inner hash with key XOR 0x36
h.inner = SHA256{hasher: sha256.New()}
for i := 0; i < 64; i++ {
rkey[i] ^= 0x5c ^ 0x36
}
h.inner.hasher.Write(rkey[:])
// Clear sensitive key material
memclear(unsafe.Pointer(&rkey), unsafe.Sizeof(rkey))
return h
}
// Write writes data to the inner hash
func (h *HMACSHA256) Write(data []byte) {
h.inner.Write(data)
}
// Finalize finalizes the HMAC and writes the result to out32 (must be 32 bytes)
func (h *HMACSHA256) Finalize(out32 []byte) {
if len(out32) != 32 {
panic("output buffer must be 32 bytes")
}
// Finalize inner hash
var temp [32]byte
h.inner.Finalize(temp[:])
// Feed inner hash result to outer hash
h.outer.Write(temp[:])
// Finalize outer hash
h.outer.Finalize(out32)
// Clear temp
memclear(unsafe.Pointer(&temp), unsafe.Sizeof(temp))
}
// Clear clears the HMAC context
func (h *HMACSHA256) Clear() {
h.inner.Clear()
h.outer.Clear()
memclear(unsafe.Pointer(h), unsafe.Sizeof(*h))
}
// RFC6979HMACSHA256 implements RFC 6979 deterministic nonce generation
type RFC6979HMACSHA256 struct {
v [32]byte
k [32]byte
retry int
}
// NewRFC6979HMACSHA256 initializes a new RFC6979 HMAC-SHA256 context
func NewRFC6979HMACSHA256(key []byte) *RFC6979HMACSHA256 {
rng := &RFC6979HMACSHA256{}
// RFC6979 3.2.b: V = 0x01 0x01 0x01 ... 0x01 (32 bytes)
for i := 0; i < 32; i++ {
rng.v[i] = 0x01
}
// RFC6979 3.2.c: K = 0x00 0x00 0x00 ... 0x00 (32 bytes)
for i := 0; i < 32; i++ {
rng.k[i] = 0x00
}
// RFC6979 3.2.d: K = HMAC_K(V || 0x00 || key)
hmac := NewHMACSHA256(rng.k[:])
hmac.Write(rng.v[:])
hmac.Write([]byte{0x00})
hmac.Write(key)
hmac.Finalize(rng.k[:])
hmac.Clear()
// V = HMAC_K(V)
hmac = NewHMACSHA256(rng.k[:])
hmac.Write(rng.v[:])
hmac.Finalize(rng.v[:])
hmac.Clear()
// RFC6979 3.2.f: K = HMAC_K(V || 0x01 || key)
hmac = NewHMACSHA256(rng.k[:])
hmac.Write(rng.v[:])
hmac.Write([]byte{0x01})
hmac.Write(key)
hmac.Finalize(rng.k[:])
hmac.Clear()
// V = HMAC_K(V)
hmac = NewHMACSHA256(rng.k[:])
hmac.Write(rng.v[:])
hmac.Finalize(rng.v[:])
hmac.Clear()
rng.retry = 0
return rng
}
// Generate generates output bytes using RFC6979
func (rng *RFC6979HMACSHA256) Generate(out []byte) {
// RFC6979 3.2.h: If retry, update K and V
if rng.retry != 0 {
hmac := NewHMACSHA256(rng.k[:])
hmac.Write(rng.v[:])
hmac.Write([]byte{0x00})
hmac.Finalize(rng.k[:])
hmac.Clear()
hmac = NewHMACSHA256(rng.k[:])
hmac.Write(rng.v[:])
hmac.Finalize(rng.v[:])
hmac.Clear()
}
// Generate output bytes
outlen := len(out)
for outlen > 0 {
hmac := NewHMACSHA256(rng.k[:])
hmac.Write(rng.v[:])
hmac.Finalize(rng.v[:])
hmac.Clear()
now := outlen
if now > 32 {
now = 32
}
copy(out, rng.v[:now])
out = out[now:]
outlen -= now
}
rng.retry = 1
}
// Finalize finalizes the RFC6979 context
func (rng *RFC6979HMACSHA256) Finalize() {
// Nothing to do, but matches C API
}
// Clear clears the RFC6979 context
func (rng *RFC6979HMACSHA256) Clear() {
memclear(unsafe.Pointer(rng), unsafe.Sizeof(*rng))
}
// TaggedHash computes SHA256(SHA256(tag) || SHA256(tag) || data)
// This is used in BIP-340 for Schnorr signatures
// Optimized to use precomputed tag hashes for common BIP-340 tags
func TaggedHash(tag []byte, data []byte) [32]byte {
var result [32]byte
// Get precomputed SHA256(tag) prefix (or compute if not cached)
tagHash := getTaggedHashPrefix(tag)
// Second hash: SHA256(SHA256(tag) || SHA256(tag) || data)
h := sha256.New()
h.Write(tagHash[:]) // SHA256(tag)
h.Write(tagHash[:]) // SHA256(tag) again
h.Write(data) // data
copy(result[:], h.Sum(nil))
return result
}
// HashToScalar converts a 32-byte hash to a scalar value
func HashToScalar(hash []byte) (*Scalar, error) {
if len(hash) != 32 {
return nil, errors.New("hash must be 32 bytes")
}
var scalar Scalar
scalar.setB32(hash)
return &scalar, nil
}
// HashToField converts a 32-byte hash to a field element
func HashToField(hash []byte) (*FieldElement, error) {
if len(hash) != 32 {
return nil, errors.New("hash must be 32 bytes")
}
var field FieldElement
if err := field.setB32(hash); err != nil {
return nil, err
}
return &field, nil
}