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

178 lines
4.8 KiB
Go

package p256k1
import (
"sync"
)
const (
// Number of bytes in a 256-bit scalar
numBytes = 32
// Number of possible byte values
numByteValues = 256
)
// bytePointTable stores precomputed byte points for each byte position
// bytePoints[byteNum][byteVal] = byteVal * 2^(8*(31-byteNum)) * G
// where byteNum is 0-31 (MSB to LSB) and byteVal is 0-255
// Each entry stores [X, Y] coordinates as 32-byte arrays
type bytePointTable [numBytes][numByteValues][2][32]byte
// EcmultGenContext holds precomputed data for generator multiplication
type EcmultGenContext struct {
// Precomputed byte points: bytePoints[byteNum][byteVal] = [X, Y] coordinates
// in affine form for byteVal * 2^(8*(31-byteNum)) * G
bytePoints bytePointTable
initialized bool
}
var (
// Global context for generator multiplication (initialized once)
globalGenContext *EcmultGenContext
genContextOnce sync.Once
)
// initGenContext initializes the precomputed byte points table
func (ctx *EcmultGenContext) initGenContext() {
// Start with G (generator point)
var gJac GroupElementJacobian
gJac.setGE(&Generator)
// Compute base points for each byte position
// For byteNum i, we need: byteVal * 2^(8*(31-i)) * G
// We'll compute each byte position's base multiplier first
// Compute 2^8 * G, 2^16 * G, ..., 2^248 * G
var byteBases [numBytes]GroupElementJacobian
// Base for byte 31 (LSB): 2^0 * G = G
byteBases[31] = gJac
// Compute bases for bytes 30 down to 0 (MSB)
// byteBases[i] = 2^(8*(31-i)) * G
for i := numBytes - 2; i >= 0; i-- {
// byteBases[i] = byteBases[i+1] * 2^8
byteBases[i] = byteBases[i+1]
for j := 0; j < 8; j++ {
byteBases[i].double(&byteBases[i])
}
}
// Now compute all byte points for each byte position
for byteNum := 0; byteNum < numBytes; byteNum++ {
base := byteBases[byteNum]
// Convert base to affine for efficiency
var baseAff GroupElementAffine
baseAff.setGEJ(&base)
// bytePoints[byteNum][0] = infinity (point at infinity)
// We'll skip this and handle it in the lookup
// bytePoints[byteNum][1] = base
var ptJac GroupElementJacobian
ptJac.setGE(&baseAff)
var ptAff GroupElementAffine
ptAff.setGEJ(&ptJac)
ptAff.x.normalize()
ptAff.y.normalize()
ptAff.x.getB32(ctx.bytePoints[byteNum][1][0][:])
ptAff.y.getB32(ctx.bytePoints[byteNum][1][1][:])
// Compute bytePoints[byteNum][byteVal] = byteVal * base
// We'll use addition to build up multiples
var accJac GroupElementJacobian = ptJac
var accAff GroupElementAffine
for byteVal := 2; byteVal < numByteValues; byteVal++ {
// acc = acc + base
accJac.addVar(&accJac, &ptJac)
accAff.setGEJ(&accJac)
accAff.x.normalize()
accAff.y.normalize()
accAff.x.getB32(ctx.bytePoints[byteNum][byteVal][0][:])
accAff.y.getB32(ctx.bytePoints[byteNum][byteVal][1][:])
}
}
ctx.initialized = true
}
// getGlobalGenContext returns the global precomputed context
func getGlobalGenContext() *EcmultGenContext {
genContextOnce.Do(func() {
globalGenContext = &EcmultGenContext{}
globalGenContext.initGenContext()
})
return globalGenContext
}
// NewEcmultGenContext creates a new generator multiplication context
func NewEcmultGenContext() *EcmultGenContext {
ctx := &EcmultGenContext{}
ctx.initGenContext()
return ctx
}
// ecmultGen computes r = n * G where G is the generator point
// Uses 8-bit byte-based lookup table (like btcec) for maximum efficiency
func (ctx *EcmultGenContext) ecmultGen(r *GroupElementJacobian, n *Scalar) {
if !ctx.initialized {
panic("ecmult_gen context not initialized")
}
// Handle zero scalar
if n.isZero() {
r.setInfinity()
return
}
// Handle scalar = 1
if n.isOne() {
r.setGE(&Generator)
return
}
// Byte-based method: process one byte at a time (MSB to LSB)
// For each byte, lookup the precomputed point and add it
r.setInfinity()
// Get scalar bytes (MSB to LSB) - optimize by getting bytes directly
var scalarBytes [32]byte
n.getB32(scalarBytes[:])
// Pre-allocate group elements to avoid repeated allocations
var ptAff GroupElementAffine
var ptJac GroupElementJacobian
var xFe, yFe FieldElement
for byteNum := 0; byteNum < numBytes; byteNum++ {
byteVal := scalarBytes[byteNum]
// Skip zero bytes
if byteVal == 0 {
continue
}
// Lookup precomputed point for this byte - optimized: reuse field elements
xFe.setB32(ctx.bytePoints[byteNum][byteVal][0][:])
yFe.setB32(ctx.bytePoints[byteNum][byteVal][1][:])
ptAff.setXY(&xFe, &yFe)
// Convert to Jacobian and add - optimized: reuse Jacobian element
ptJac.setGE(&ptAff)
if r.isInfinity() {
*r = ptJac
} else {
r.addVar(r, &ptJac)
}
}
}
// EcmultGen is the public interface for generator multiplication
func EcmultGen(r *GroupElementJacobian, n *Scalar) {
// Use global precomputed context for efficiency
ctx := getGlobalGenContext()
ctx.ecmultGen(r, n)
}