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:
185
pubkey.go
Normal file
185
pubkey.go
Normal 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[:])
|
||||
}
|
||||
Reference in New Issue
Block a user