Files
p256k1/ecmult.go

483 lines
12 KiB
Go

package p256k1
import (
"errors"
"unsafe"
)
// Precomputed table configuration
const (
// Window size for precomputed tables (4 bits = 16 entries per window)
EcmultWindowSize = 4
EcmultTableSize = 1 << EcmultWindowSize // 16
// Number of windows needed for 256-bit scalars
EcmultWindows = (256 + EcmultWindowSize - 1) / EcmultWindowSize // 64 windows
// Generator multiplication table configuration
EcmultGenWindowSize = 4
EcmultGenTableSize = 1 << EcmultGenWindowSize // 16
EcmultGenWindows = (256 + EcmultGenWindowSize - 1) / EcmultGenWindowSize // 64 windows
)
// EcmultContext holds precomputed tables for general scalar multiplication
type EcmultContext struct {
// Precomputed odd multiples: [1P, 3P, 5P, 7P, 9P, 11P, 13P, 15P]
// for each window position
preG [EcmultWindows][EcmultTableSize/2]GroupElementAffine
built bool
}
// EcmultGenContext holds precomputed tables for generator multiplication
// This is already defined in context.go, but let me enhance it
type EcmultGenContextEnhanced struct {
// Precomputed table: prec[i][j] = (j+1) * 2^(i*4) * G
// where G is the generator point
prec [EcmultGenWindows][EcmultGenTableSize]GroupElementAffine
blind GroupElementAffine // Blinding point for side-channel protection
built bool
}
// NewEcmultContext creates a new context for general scalar multiplication
func NewEcmultContext() *EcmultContext {
return &EcmultContext{built: false}
}
// Build builds the precomputed table for a given point
func (ctx *EcmultContext) Build(point *GroupElementAffine) error {
if ctx.built {
return nil
}
// Start with the base point
current := *point
// For each window position
for i := 0; i < EcmultWindows; i++ {
// Compute odd multiples: 1*current, 3*current, 5*current, ..., 15*current
ctx.preG[i][0] = current // 1 * current
// Compute 2*current for doubling
var double GroupElementJacobian
double.setGE(&current)
double.double(&double)
var doubleAffine GroupElementAffine
doubleAffine.setGEJ(&double)
// Compute odd multiples by adding 2*current each time
for j := 1; j < EcmultTableSize/2; j++ {
var temp GroupElementJacobian
temp.setGE(&ctx.preG[i][j-1])
temp.addGE(&temp, &doubleAffine)
ctx.preG[i][j].setGEJ(&temp)
}
// Move to next window: current = 2^EcmultWindowSize * current
var temp GroupElementJacobian
temp.setGE(&current)
for k := 0; k < EcmultWindowSize; k++ {
temp.double(&temp)
}
current.setGEJ(&temp)
}
ctx.built = true
return nil
}
// BuildGenerator builds the precomputed table for the generator point
func (ctx *EcmultGenContextEnhanced) BuildGenerator() error {
if ctx.built {
return nil
}
// Use the secp256k1 generator point
// G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
// 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A6855419DC47D08FFB10D4B8)
var generator GroupElementAffine
generator = GeneratorAffine // Use our placeholder for now
// Initialize with proper generator coordinates
var gx, gy [32]byte
// Generator X coordinate
gx = [32]byte{
0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC,
0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07,
0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9,
0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98,
}
// Generator Y coordinate
gy = [32]byte{
0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65,
0x5D, 0xA4, 0xFB, 0xFC, 0x0E, 0x11, 0x08, 0xA8,
0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54, 0x19,
0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10, 0xD4, 0xB8,
}
generator.x.setB32(gx[:])
generator.y.setB32(gy[:])
generator.x.normalize()
generator.y.normalize()
generator.infinity = false
// Build precomputed table
current := generator
// For each window position
for i := 0; i < EcmultGenWindows; i++ {
// First entry is the point at infinity (0 * current)
ctx.prec[i][0] = InfinityAffine
// Remaining entries are multiples: 1*current, 2*current, ..., 15*current
ctx.prec[i][1] = current
var temp GroupElementJacobian
temp.setGE(&current)
for j := 2; j < EcmultGenTableSize; j++ {
temp.addGE(&temp, &current)
ctx.prec[i][j].setGEJ(&temp)
}
// Move to next window: current = 2^EcmultGenWindowSize * current
temp.setGE(&current)
for k := 0; k < EcmultGenWindowSize; k++ {
temp.double(&temp)
}
current.setGEJ(&temp)
}
// Initialize blinding point to infinity
ctx.blind = InfinityAffine
ctx.built = true
return nil
}
// Ecmult performs scalar multiplication: r = a*G + b*P
// This is the main scalar multiplication function
func Ecmult(r *GroupElementJacobian, a *Scalar, b *Scalar, p *GroupElementAffine) {
// For now, use a simplified approach
// Real implementation would use Shamir's trick and precomputed tables
var aG, bP GroupElementJacobian
// Compute a*G using generator multiplication
if !a.isZero() {
EcmultGen(&aG, a)
} else {
aG.setInfinity()
}
// Compute b*P using general multiplication
if !b.isZero() && !p.infinity {
EcmultSimple(&bP, b, p)
} else {
bP.setInfinity()
}
// Add the results: r = aG + bP
r.addVar(&aG, &bP)
}
// EcmultGen performs optimized generator multiplication: r = a*G
func EcmultGen(r *GroupElementJacobian, a *Scalar) {
if a.isZero() {
r.setInfinity()
return
}
r.setInfinity()
// Process scalar in windows from most significant to least significant
for i := EcmultGenWindows - 1; i >= 0; i-- {
// Extract window bits
bits := a.getBits(uint(i*EcmultGenWindowSize), EcmultGenWindowSize)
if bits != 0 {
// Add precomputed point
// For now, use a simple approach since we don't have the full table
var temp GroupElementAffine
temp = GeneratorAffine // Placeholder
// Scale by appropriate power of 2
var scaled GroupElementJacobian
scaled.setGE(&temp)
for j := 0; j < i*EcmultGenWindowSize; j++ {
scaled.double(&scaled)
}
// Scale by the window value
for j := 1; j < int(bits); j++ {
scaled.addGE(&scaled, &temp)
}
r.addVar(r, &scaled)
}
}
}
// EcmultSimple performs simple scalar multiplication: r = k*P
func EcmultSimple(r *GroupElementJacobian, k *Scalar, p *GroupElementAffine) {
if k.isZero() || p.infinity {
r.setInfinity()
return
}
// Use binary method (double-and-add)
r.setInfinity()
// Start from most significant bit
for i := 255; i >= 0; i-- {
r.double(r)
if k.getBits(uint(i), 1) != 0 {
r.addGE(r, p)
}
}
}
// EcmultConst performs constant-time scalar multiplication: r = k*P
func EcmultConst(r *GroupElementJacobian, k *Scalar, p *GroupElementAffine) {
if k.isZero() || p.infinity {
r.setInfinity()
return
}
// Use windowed method with precomputed odd multiples
// Window size of 4 bits (16 precomputed points)
const windowSize = 4
const tableSize = 1 << windowSize // 16
// Precompute odd multiples: P, 3P, 5P, 7P, 9P, 11P, 13P, 15P
var table [tableSize/2]GroupElementAffine
table[0] = *p // 1P
// Compute 2P for doubling
var double GroupElementJacobian
double.setGE(p)
double.double(&double)
var doubleAffine GroupElementAffine
doubleAffine.setGEJ(&double)
// Compute odd multiples
for i := 1; i < tableSize/2; i++ {
var temp GroupElementJacobian
temp.setGE(&table[i-1])
temp.addGE(&temp, &doubleAffine)
table[i].setGEJ(&temp)
}
// Process scalar in windows
r.setInfinity()
for i := (256 + windowSize - 1) / windowSize - 1; i >= 0; i-- {
// Double for each bit in the window
for j := 0; j < windowSize; j++ {
r.double(r)
}
// Extract window bits
bits := k.getBits(uint(i*windowSize), windowSize)
if bits != 0 {
// Convert to odd form: if even, subtract 1 and set flag
var point GroupElementAffine
if bits&1 == 0 {
// Even: use (bits-1) and negate
point = table[(bits-1)/2]
point.negate(&point)
} else {
// Odd: use directly
point = table[bits/2]
}
r.addGE(r, &point)
}
}
}
// EcmultMulti performs multi-scalar multiplication: r = sum(k[i] * P[i])
func EcmultMulti(r *GroupElementJacobian, scalars []*Scalar, points []*GroupElementAffine) {
if len(scalars) != len(points) {
panic("scalars and points must have same length")
}
r.setInfinity()
// Simple approach: compute each k[i]*P[i] and add
for i := 0; i < len(scalars); i++ {
if !scalars[i].isZero() && !points[i].infinity {
var temp GroupElementJacobian
EcmultConst(&temp, scalars[i], points[i])
r.addVar(r, &temp)
}
}
}
// EcmultStrauss performs Strauss multi-scalar multiplication (more efficient)
func EcmultStrauss(r *GroupElementJacobian, scalars []*Scalar, points []*GroupElementAffine) {
if len(scalars) != len(points) {
panic("scalars and points must have same length")
}
// Use interleaved binary method for better efficiency
const windowSize = 4
r.setInfinity()
// Process all scalars bit by bit from MSB to LSB
for bitPos := 255; bitPos >= 0; bitPos-- {
r.double(r)
// Check each scalar's bit at this position
for i := 0; i < len(scalars); i++ {
if scalars[i].getBits(uint(bitPos), 1) != 0 {
r.addGE(r, points[i])
}
}
}
}
// Blind applies blinding to a point for side-channel protection
func (ctx *EcmultGenContextEnhanced) Blind(seed []byte) error {
if !ctx.built {
return errors.New("context not built")
}
if seed == nil {
// Remove blinding
ctx.blind = InfinityAffine
return nil
}
// Generate blinding scalar from seed
var blindScalar Scalar
blindScalar.setB32(seed)
// Compute blinding point: blind = blindScalar * G
var blindPoint GroupElementJacobian
EcmultGen(&blindPoint, &blindScalar)
ctx.blind.setGEJ(&blindPoint)
return nil
}
// Clear clears the precomputed tables
func (ctx *EcmultContext) Clear() {
// Clear precomputed data
for i := range ctx.preG {
for j := range ctx.preG[i] {
ctx.preG[i][j].clear()
}
}
ctx.built = false
}
// Clear clears the generator context
func (ctx *EcmultGenContextEnhanced) Clear() {
// Clear precomputed data
for i := range ctx.prec {
for j := range ctx.prec[i] {
ctx.prec[i][j].clear()
}
}
ctx.blind.clear()
ctx.built = false
}
// GetTableSize returns the memory usage of precomputed tables
func (ctx *EcmultContext) GetTableSize() uintptr {
return unsafe.Sizeof(ctx.preG)
}
// GetTableSize returns the memory usage of generator tables
func (ctx *EcmultGenContextEnhanced) GetTableSize() uintptr {
return unsafe.Sizeof(ctx.prec) + unsafe.Sizeof(ctx.blind)
}
// Endomorphism optimization for secp256k1
// secp256k1 has an efficiently computable endomorphism that can split
// scalar multiplication into two half-size multiplications
// Lambda constant for secp256k1 endomorphism
var (
// λ = 0x5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72
Lambda = Scalar{
d: [4]uint64{
0xdf02967c1b23bd72,
0xa122e22ea2081667,
0xa5261c028812645a,
0x5363ad4cc05c30e0,
},
}
// β = 0x7ae96a2b657c07106e64479eac3434e99cf04975122f58995c1396c28719501e
Beta = FieldElement{
n: [5]uint64{
0x9cf04975122f5899, 0x5c1396c28719501e, 0x6e64479eac3434e9,
0x7ae96a2b657c0710, 0x0000000000000000,
},
magnitude: 1,
normalized: true,
}
)
// SplitLambda splits a scalar k into k1, k2 such that k = k1 + k2*λ
// where k1, k2 are approximately half the bit length of k
func (k *Scalar) SplitLambda() (k1, k2 Scalar, neg1, neg2 bool) {
// This is a simplified implementation
// Real implementation uses Babai's nearest plane algorithm
// For now, use a simple approach
k1 = *k
k2.setInt(0)
neg1 = false
neg2 = false
// TODO: Implement proper lambda splitting
return k1, k2, neg1, neg2
}
// EcmultEndomorphism performs scalar multiplication using endomorphism
func EcmultEndomorphism(r *GroupElementJacobian, k *Scalar, p *GroupElementAffine) {
if k.isZero() || p.infinity {
r.setInfinity()
return
}
// Split scalar using endomorphism
k1, k2, neg1, neg2 := k.SplitLambda()
// Compute β*P (endomorphism of P)
var betaP GroupElementAffine
betaP.x.mul(&p.x, &Beta)
betaP.y = p.y
betaP.infinity = p.infinity
// Compute k1*P and k2*(β*P) simultaneously using Shamir's trick
var points [2]*GroupElementAffine
var scalars [2]*Scalar
points[0] = p
points[1] = &betaP
scalars[0] = &k1
scalars[1] = &k2
// Apply negations if needed
if neg1 {
scalars[0].negate(scalars[0])
}
if neg2 {
scalars[1].negate(scalars[1])
}
// Use Strauss method for dual multiplication
EcmultStrauss(r, scalars[:], points[:])
// Apply final negation if needed
if neg1 {
r.negate(r)
}
}