178 lines
4.8 KiB
Go
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)
|
|
}
|