396 lines
10 KiB
Go
396 lines
10 KiB
Go
package p256k1
|
|
|
|
import "math/bits"
|
|
|
|
// mul multiplies two field elements: r = a * b
|
|
func (r *FieldElement) mul(a, b *FieldElement) {
|
|
// Normalize inputs if magnitude is too high
|
|
var aNorm, bNorm FieldElement
|
|
aNorm = *a
|
|
bNorm = *b
|
|
|
|
if aNorm.magnitude > 8 {
|
|
aNorm.normalizeWeak()
|
|
}
|
|
if bNorm.magnitude > 8 {
|
|
bNorm.normalizeWeak()
|
|
}
|
|
|
|
// Full 5x52 multiplication implementation
|
|
// Compute all cross products: sum(i,j) a[i] * b[j] * 2^(52*(i+j))
|
|
|
|
var t [10]uint64 // Temporary array for intermediate results
|
|
|
|
// Compute all cross products
|
|
for i := 0; i < 5; i++ {
|
|
for j := 0; j < 5; j++ {
|
|
hi, lo := bits.Mul64(aNorm.n[i], bNorm.n[j])
|
|
k := i + j
|
|
|
|
// Add lo to t[k]
|
|
var carry uint64
|
|
t[k], carry = bits.Add64(t[k], lo, 0)
|
|
|
|
// Propagate carry and add hi
|
|
if k+1 < 10 {
|
|
t[k+1], carry = bits.Add64(t[k+1], hi, carry)
|
|
// Propagate any remaining carry
|
|
for l := k + 2; l < 10 && carry != 0; l++ {
|
|
t[l], carry = bits.Add64(t[l], 0, carry)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reduce modulo field prime using the fact that 2^256 ≡ 2^32 + 977 (mod p)
|
|
// The field prime is p = 2^256 - 2^32 - 977
|
|
r.reduceFromWide(t)
|
|
}
|
|
|
|
// mulSimple is a simplified multiplication that may not be constant-time
|
|
func (r *FieldElement) mulSimple(a, b *FieldElement) {
|
|
// Convert to big integers for multiplication
|
|
var aVal, bVal, pVal [5]uint64
|
|
copy(aVal[:], a.n[:])
|
|
copy(bVal[:], b.n[:])
|
|
|
|
// Field modulus as limbs
|
|
pVal[0] = fieldModulusLimb0
|
|
pVal[1] = fieldModulusLimb1
|
|
pVal[2] = fieldModulusLimb2
|
|
pVal[3] = fieldModulusLimb3
|
|
pVal[4] = fieldModulusLimb4
|
|
|
|
// Perform multiplication and reduction
|
|
// This is a placeholder - real implementation needs proper big integer arithmetic
|
|
result := r.mulAndReduce(aVal, bVal, pVal)
|
|
copy(r.n[:], result[:])
|
|
|
|
r.magnitude = 1
|
|
r.normalized = false
|
|
}
|
|
|
|
// reduceFromWide reduces a 520-bit (10 limb) value modulo the field prime
|
|
func (r *FieldElement) reduceFromWide(t [10]uint64) {
|
|
// The field prime is p = 2^256 - 2^32 - 977 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
|
|
// We use the fact that 2^256 ≡ 2^32 + 977 (mod p)
|
|
|
|
// First, handle the upper limbs (t[5] through t[9])
|
|
// Each represents a multiple of 2^(52*i) where i >= 5
|
|
|
|
// Reduction constant for secp256k1: 2^32 + 977 = 0x1000003D1
|
|
const M = uint64(0x1000003D1)
|
|
|
|
// Start from the highest limb and work down
|
|
for i := 9; i >= 5; i-- {
|
|
if t[i] == 0 {
|
|
continue
|
|
}
|
|
|
|
// t[i] * 2^(52*i) ≡ t[i] * 2^(52*(i-5)) * 2^(52*5) ≡ t[i] * 2^(52*(i-5)) * 2^260
|
|
// Since 2^256 ≡ M (mod p), we have 2^260 ≡ 2^4 * M ≡ 16 * M (mod p)
|
|
|
|
// For i=5: 2^260 ≡ 16*M (mod p)
|
|
// For i=6: 2^312 ≡ 2^52 * 16*M ≡ 2^56 * M (mod p)
|
|
// etc.
|
|
|
|
shift := uint(52 * (i - 5) + 4) // Additional 4 bits for the 16 factor
|
|
|
|
// Multiply t[i] by the appropriate power of M
|
|
var carry uint64
|
|
if shift < 64 {
|
|
// Simple case: can multiply directly
|
|
factor := M << shift
|
|
hi, lo := bits.Mul64(t[i], factor)
|
|
|
|
// Add to appropriate position
|
|
pos := 0
|
|
t[pos], carry = bits.Add64(t[pos], lo, 0)
|
|
if pos+1 < 10 {
|
|
t[pos+1], carry = bits.Add64(t[pos+1], hi, carry)
|
|
}
|
|
|
|
// Propagate carry
|
|
for j := pos + 2; j < 10 && carry != 0; j++ {
|
|
t[j], carry = bits.Add64(t[j], 0, carry)
|
|
}
|
|
} else {
|
|
// Need to handle larger shifts by distributing across limbs
|
|
hi, lo := bits.Mul64(t[i], M)
|
|
limbShift := shift / 52
|
|
bitShift := shift % 52
|
|
|
|
if bitShift == 0 {
|
|
// Aligned to limb boundary
|
|
if limbShift < 10 {
|
|
t[limbShift], carry = bits.Add64(t[limbShift], lo, 0)
|
|
if limbShift+1 < 10 {
|
|
t[limbShift+1], carry = bits.Add64(t[limbShift+1], hi, carry)
|
|
}
|
|
}
|
|
} else {
|
|
// Need to split across limbs
|
|
loShifted := lo << bitShift
|
|
hiShifted := (lo >> (64 - bitShift)) | (hi << bitShift)
|
|
|
|
if limbShift < 10 {
|
|
t[limbShift], carry = bits.Add64(t[limbShift], loShifted, 0)
|
|
if limbShift+1 < 10 {
|
|
t[limbShift+1], carry = bits.Add64(t[limbShift+1], hiShifted, carry)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Propagate any remaining carry
|
|
for j := int(limbShift) + 2; j < 10 && carry != 0; j++ {
|
|
t[j], carry = bits.Add64(t[j], 0, carry)
|
|
}
|
|
}
|
|
|
|
t[i] = 0 // Clear the processed limb
|
|
}
|
|
|
|
// Now we have a value in t[0..4] that may still be >= p
|
|
// Convert to 5x52 format and normalize
|
|
r.n[0] = t[0] & limb0Max
|
|
r.n[1] = ((t[0] >> 52) | (t[1] << 12)) & limb0Max
|
|
r.n[2] = ((t[1] >> 40) | (t[2] << 24)) & limb0Max
|
|
r.n[3] = ((t[2] >> 28) | (t[3] << 36)) & limb0Max
|
|
r.n[4] = ((t[3] >> 16) | (t[4] << 48)) & limb4Max
|
|
|
|
r.magnitude = 1
|
|
r.normalized = false
|
|
|
|
// Final reduction if needed
|
|
if r.n[4] == limb4Max && r.n[3] == limb0Max && r.n[2] == limb0Max &&
|
|
r.n[1] == limb0Max && r.n[0] >= fieldModulusLimb0 {
|
|
r.reduce()
|
|
}
|
|
}
|
|
|
|
// mulAndReduce performs multiplication and modular reduction
|
|
func (r *FieldElement) mulAndReduce(a, b, p [5]uint64) [5]uint64 {
|
|
// This function is deprecated - use mul() instead
|
|
var fa, fb FieldElement
|
|
copy(fa.n[:], a[:])
|
|
copy(fb.n[:], b[:])
|
|
fa.magnitude = 1
|
|
fb.magnitude = 1
|
|
fa.normalized = false
|
|
fb.normalized = false
|
|
|
|
r.mul(&fa, &fb)
|
|
|
|
var result [5]uint64
|
|
copy(result[:], r.n[:])
|
|
return result
|
|
}
|
|
|
|
// sqr squares a field element: r = a^2
|
|
func (r *FieldElement) sqr(a *FieldElement) {
|
|
// Squaring can be optimized compared to general multiplication
|
|
// For now, use multiplication
|
|
r.mul(a, a)
|
|
}
|
|
|
|
// inv computes the modular inverse of a field element using Fermat's little theorem
|
|
func (r *FieldElement) inv(a *FieldElement) {
|
|
// For field F_p, a^(-1) = a^(p-2) mod p
|
|
// The secp256k1 field prime is p = 2^256 - 2^32 - 977
|
|
// So p-2 = 2^256 - 2^32 - 979
|
|
|
|
// Use binary exponentiation with the exponent p-2
|
|
// p-2 in binary (from LSB): 1111...1111 0000...0000 1111...1111 0110...1101
|
|
|
|
var x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223 FieldElement
|
|
|
|
// Build powers using addition chains (optimized sequence)
|
|
x2.sqr(a) // a^2
|
|
x3.mul(&x2, a) // a^3
|
|
|
|
// Build x6 = a^6 by squaring x3
|
|
x6.sqr(&x3) // a^6
|
|
|
|
// Build x9 = a^9 = a^6 * a^3
|
|
x9.mul(&x6, &x3) // a^9
|
|
|
|
// Build x11 = a^11 = a^9 * a^2
|
|
x11.mul(&x9, &x2) // a^11
|
|
|
|
// Build x22 = a^22 by squaring x11
|
|
x22.sqr(&x11) // a^22
|
|
|
|
// Build x44 = a^44 by squaring x22
|
|
x44.sqr(&x22) // a^44
|
|
|
|
// Build x88 = a^88 by squaring x44
|
|
x88.sqr(&x44) // a^88
|
|
|
|
// Build x176 = a^176 by squaring x88
|
|
x176.sqr(&x88) // a^176
|
|
|
|
// Build x220 = a^220 = a^176 * a^44
|
|
x220.mul(&x176, &x44) // a^220
|
|
|
|
// Build x223 = a^223 = a^220 * a^3
|
|
x223.mul(&x220, &x3) // a^223
|
|
|
|
// Now compute the full exponent using addition chains
|
|
// This is a simplified version - the full implementation would use
|
|
// the optimal addition chain for p-2
|
|
|
|
*r = x223
|
|
|
|
// Square 23 times to get a^(223 * 2^23)
|
|
for i := 0; i < 23; i++ {
|
|
r.sqr(r)
|
|
}
|
|
|
|
// Multiply by x22 to get a^(223 * 2^23 + 22)
|
|
r.mul(r, &x22)
|
|
|
|
// Continue with remaining bits...
|
|
// This is a simplified implementation
|
|
// The full version would implement the complete addition chain
|
|
|
|
// Final squaring and multiplication steps
|
|
for i := 0; i < 6; i++ {
|
|
r.sqr(r)
|
|
}
|
|
r.mul(r, &x2)
|
|
|
|
for i := 0; i < 2; i++ {
|
|
r.sqr(r)
|
|
}
|
|
|
|
r.normalize()
|
|
}
|
|
|
|
// sqrt computes the square root of a field element if it exists
|
|
func (r *FieldElement) sqrt(a *FieldElement) bool {
|
|
// For secp256k1, p ≡ 3 (mod 4), so we can use a^((p+1)/4) if a is a quadratic residue
|
|
// The secp256k1 field prime is p = 2^256 - 2^32 - 977
|
|
// So (p+1)/4 = (2^256 - 2^32 - 977 + 1)/4 = (2^256 - 2^32 - 976)/4 = 2^254 - 2^30 - 244
|
|
|
|
// First check if a is zero
|
|
var aNorm FieldElement
|
|
aNorm = *a
|
|
aNorm.normalize()
|
|
|
|
if aNorm.isZero() {
|
|
r.setInt(0)
|
|
return true
|
|
}
|
|
|
|
// Compute a^((p+1)/4) using addition chains
|
|
// This is similar to inversion but with exponent (p+1)/4
|
|
|
|
var x2, x3, x6, x12, x15, x30, x60, x120, x240 FieldElement
|
|
|
|
// Build powers
|
|
x2.sqr(&aNorm) // a^2
|
|
x3.mul(&x2, &aNorm) // a^3
|
|
|
|
x6.sqr(&x3) // a^6
|
|
|
|
x12.sqr(&x6) // a^12
|
|
|
|
x15.mul(&x12, &x3) // a^15
|
|
|
|
x30.sqr(&x15) // a^30
|
|
|
|
x60.sqr(&x30) // a^60
|
|
|
|
x120.sqr(&x60) // a^120
|
|
|
|
x240.sqr(&x120) // a^240
|
|
|
|
// Now build the full exponent
|
|
// This is a simplified version - the complete implementation would
|
|
// use the optimal addition chain for (p+1)/4
|
|
|
|
*r = x240
|
|
|
|
// Continue with squaring and multiplication to reach (p+1)/4
|
|
// Simplified implementation
|
|
for i := 0; i < 14; i++ {
|
|
r.sqr(r)
|
|
}
|
|
|
|
r.mul(r, &x15)
|
|
|
|
// Verify the result by squaring
|
|
var check FieldElement
|
|
check.sqr(r)
|
|
check.normalize()
|
|
aNorm.normalize()
|
|
|
|
if check.equal(&aNorm) {
|
|
return true
|
|
}
|
|
|
|
// If the first candidate doesn't work, try the negative
|
|
r.negate(r, 1)
|
|
r.normalize()
|
|
|
|
check.sqr(r)
|
|
check.normalize()
|
|
|
|
return check.equal(&aNorm)
|
|
}
|
|
|
|
// isSquare checks if a field element is a quadratic residue
|
|
func (a *FieldElement) isSquare() bool {
|
|
// Use Legendre symbol: a^((p-1)/2) mod p
|
|
// If result is 1, then a is a quadratic residue
|
|
|
|
var result FieldElement
|
|
result = *a
|
|
|
|
// Compute a^((p-1)/2) - simplified implementation
|
|
for i := 0; i < 127; i++ { // Approximate (p-1)/2 bit length
|
|
result.sqr(&result)
|
|
}
|
|
|
|
result.normalize()
|
|
return result.equal(&FieldElementOne)
|
|
}
|
|
|
|
// half computes r = a/2 mod p
|
|
func (r *FieldElement) half(a *FieldElement) {
|
|
// If a is even, divide by 2
|
|
// If a is odd, compute (a + p) / 2
|
|
|
|
*r = *a
|
|
r.normalize()
|
|
|
|
if r.n[0]&1 == 0 {
|
|
// Even case: simple right shift
|
|
r.n[0] = (r.n[0] >> 1) | ((r.n[1] & 1) << 51)
|
|
r.n[1] = (r.n[1] >> 1) | ((r.n[2] & 1) << 51)
|
|
r.n[2] = (r.n[2] >> 1) | ((r.n[3] & 1) << 51)
|
|
r.n[3] = (r.n[3] >> 1) | ((r.n[4] & 1) << 51)
|
|
r.n[4] = r.n[4] >> 1
|
|
} else {
|
|
// Odd case: add p then divide by 2
|
|
// p = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
|
|
// (a + p) / 2 for odd a
|
|
|
|
carry := uint64(1) // Since a is odd, adding p makes it even
|
|
r.n[0] = (r.n[0] + fieldModulusLimb0) >> 1
|
|
if r.n[0] >= (1 << 51) {
|
|
carry = 1
|
|
r.n[0] &= limb0Max
|
|
} else {
|
|
carry = 0
|
|
}
|
|
|
|
r.n[1] = (r.n[1] + fieldModulusLimb1 + carry) >> 1
|
|
// Continue for other limbs...
|
|
// Simplified implementation
|
|
}
|
|
|
|
r.magnitude = 1
|
|
r.normalized = true
|
|
}
|