Add context tests and implement generator multiplication context

This commit introduces a new test file for context management, covering various scenarios for context creation, destruction, and capabilities. Additionally, it implements the generator multiplication context, enhancing the secp256k1 elliptic curve operations. The changes ensure comprehensive testing and improved functionality for context handling, contributing to the overall robustness of the implementation.
This commit is contained in:
2025-11-01 20:01:52 +00:00
parent 715bdff306
commit 5416381478
37 changed files with 2213 additions and 7833 deletions

185
pubkey.go Normal file
View File

@@ -0,0 +1,185 @@
package p256k1
import (
"errors"
)
// PublicKey represents a secp256k1 public key
type PublicKey struct {
data [64]byte // Internal representation
}
// Compression flags for public key serialization
const (
ECCompressed = 0x02
ECUncompressed = 0x04
)
// ECPubkeyParse parses a public key from bytes
func ECPubkeyParse(pubkey *PublicKey, input []byte) error {
if len(input) == 0 {
return errors.New("input cannot be empty")
}
var point GroupElementAffine
switch len(input) {
case 33:
// Compressed format
if input[0] != 0x02 && input[0] != 0x03 {
return errors.New("invalid compressed public key prefix")
}
// Extract X coordinate
var x FieldElement
if err := x.setB32(input[1:33]); err != nil {
return err
}
// Determine Y coordinate from X and parity
odd := input[0] == 0x03
if !point.setXOVar(&x, odd) {
return errors.New("invalid public key")
}
case 65:
// Uncompressed format
if input[0] != 0x04 {
return errors.New("invalid uncompressed public key prefix")
}
// Extract X and Y coordinates
var x, y FieldElement
if err := x.setB32(input[1:33]); err != nil {
return err
}
if err := y.setB32(input[33:65]); err != nil {
return err
}
point.setXY(&x, &y)
default:
return errors.New("invalid public key length")
}
// Validate the point is on the curve
if !point.isValid() {
return errors.New("public key not on curve")
}
// Store in internal format
point.toBytes(pubkey.data[:])
return nil
}
// ECPubkeySerialize serializes a public key to bytes
func ECPubkeySerialize(output []byte, pubkey *PublicKey, flags uint) int {
// Load the public key
var point GroupElementAffine
point.fromBytes(pubkey.data[:])
if point.isInfinity() {
return 0 // Invalid public key
}
// Normalize coordinates
point.x.normalize()
point.y.normalize()
if flags == ECCompressed {
if len(output) < 33 {
return 0 // Buffer too small
}
// Compressed format: 0x02/0x03 + X coordinate
if point.y.isOdd() {
output[0] = 0x03
} else {
output[0] = 0x02
}
point.x.getB32(output[1:33])
return 33
} else if flags == ECUncompressed {
if len(output) < 65 {
return 0 // Buffer too small
}
// Uncompressed format: 0x04 + X + Y coordinates
output[0] = 0x04
point.x.getB32(output[1:33])
point.y.getB32(output[33:65])
return 65
} else {
return 0 // Invalid flags
}
}
// ECPubkeyCmp compares two public keys
func ECPubkeyCmp(pubkey1, pubkey2 *PublicKey) int {
// Load both public keys
var point1, point2 GroupElementAffine
point1.fromBytes(pubkey1.data[:])
point2.fromBytes(pubkey2.data[:])
if point1.equal(&point2) {
return 0
}
// For ordering, compare the serialized forms
var buf1, buf2 [33]byte
ECPubkeySerialize(buf1[:], pubkey1, ECCompressed)
ECPubkeySerialize(buf2[:], pubkey2, ECCompressed)
for i := 0; i < 33; i++ {
if buf1[i] < buf2[i] {
return -1
}
if buf1[i] > buf2[i] {
return 1
}
}
return 0
}
// ECPubkeyCreate creates a public key from a private key
func ECPubkeyCreate(pubkey *PublicKey, seckey []byte) error {
if len(seckey) != 32 {
return errors.New("private key must be 32 bytes")
}
// Parse the private key as a scalar
var scalar Scalar
if !scalar.setB32Seckey(seckey) {
return errors.New("invalid private key")
}
// Compute pubkey = scalar * G
var point GroupElementJacobian
EcmultGen(&point, &scalar)
// Convert to affine and store
var affine GroupElementAffine
affine.setGEJ(&point)
affine.toBytes(pubkey.data[:])
// Clear sensitive data
scalar.clear()
point.clear()
return nil
}
// pubkeyLoad loads a public key from internal format (helper function)
func pubkeyLoad(point *GroupElementAffine, pubkey *PublicKey) {
point.fromBytes(pubkey.data[:])
}
// pubkeySave saves a public key to internal format (helper function)
func pubkeySave(pubkey *PublicKey, point *GroupElementAffine) {
point.toBytes(pubkey.data[:])
}