Refactor signer implementation to use p8k package
- Replaced all instances of p256k1signer with the new p8k.Signer across various modules, including event creation, policy handling, and database interactions. - Updated related test cases and benchmarks to ensure compatibility with the new signer interface. - Bumped version to v0.25.0 to reflect these significant changes and improvements in cryptographic operations.
This commit is contained in:
451
pkg/crypto/p8k/secp.go
Normal file
451
pkg/crypto/p8k/secp.go
Normal file
@@ -0,0 +1,451 @@
|
||||
// Package secp provides Go bindings to libsecp256k1 without CGO.
|
||||
// It uses dynamic library loading via purego to call C functions directly.
|
||||
package secp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
// Constants for context flags
|
||||
const (
|
||||
ContextNone = 1
|
||||
ContextVerify = 257 // 1 | (1 << 8)
|
||||
ContextSign = 513 // 1 | (1 << 9)
|
||||
ContextDeclassify = 1025 // 1 | (1 << 10)
|
||||
)
|
||||
|
||||
// EC flags
|
||||
const (
|
||||
ECCompressed = 258 // SECP256K1_EC_COMPRESSED
|
||||
ECUncompressed = 2 // SECP256K1_EC_UNCOMPRESSED
|
||||
)
|
||||
|
||||
// Size constants
|
||||
const (
|
||||
PublicKeySize = 64
|
||||
CompressedPublicKeySize = 33
|
||||
UncompressedPublicKeySize = 65
|
||||
SignatureSize = 64
|
||||
CompactSignatureSize = 64
|
||||
PrivateKeySize = 32
|
||||
SharedSecretSize = 32
|
||||
SchnorrSignatureSize = 64
|
||||
RecoverableSignatureSize = 65
|
||||
)
|
||||
|
||||
var (
|
||||
libHandle uintptr
|
||||
loadLibOnce sync.Once
|
||||
loadLibErr error
|
||||
)
|
||||
|
||||
// Function pointers
|
||||
var (
|
||||
contextCreate func(flags uint32) uintptr
|
||||
contextDestroy func(ctx uintptr)
|
||||
contextRandomize func(ctx uintptr, seed32 *byte) int32
|
||||
ecPubkeyCreate func(ctx uintptr, pubkey *byte, seckey *byte) int32
|
||||
ecPubkeySerialize func(ctx uintptr, output *byte, outputlen *uint64, pubkey *byte, flags uint32) int32
|
||||
ecPubkeyParse func(ctx uintptr, pubkey *byte, input *byte, inputlen uint64) int32
|
||||
ecdsaSign func(ctx uintptr, sig *byte, msg32 *byte, seckey *byte, noncefp uintptr, ndata uintptr) int32
|
||||
ecdsaVerify func(ctx uintptr, sig *byte, msg32 *byte, pubkey *byte) int32
|
||||
ecdsaSignatureSerializeDer func(ctx uintptr, output *byte, outputlen *uint64, sig *byte) int32
|
||||
ecdsaSignatureParseDer func(ctx uintptr, sig *byte, input *byte, inputlen uint64) int32
|
||||
ecdsaSignatureSerializeCompact func(ctx uintptr, output64 *byte, sig *byte) int32
|
||||
ecdsaSignatureParseCompact func(ctx uintptr, sig *byte, input64 *byte) int32
|
||||
ecdsaSignatureNormalize func(ctx uintptr, sigout *byte, sigin *byte) int32
|
||||
|
||||
// Schnorr functions
|
||||
schnorrsigSign32 func(ctx uintptr, sig64 *byte, msg32 *byte, keypair *byte, auxrand32 *byte) int32
|
||||
schnorrsigVerify func(ctx uintptr, sig64 *byte, msg32 *byte, msglen uint64, pubkey *byte) int32
|
||||
keypairCreate func(ctx uintptr, keypair *byte, seckey *byte) int32
|
||||
xonlyPubkeyParse func(ctx uintptr, pubkey *byte, input32 *byte) int32
|
||||
xonlyPubkeySerialize func(ctx uintptr, output32 *byte, pubkey *byte) int32
|
||||
keypairXonlyPub func(ctx uintptr, pubkey *byte, pkParity *int32, keypair *byte) int32
|
||||
|
||||
// ECDH functions
|
||||
ecdh func(ctx uintptr, output *byte, pubkey *byte, seckey *byte, hashfp uintptr, data uintptr) int32
|
||||
|
||||
// Recovery functions
|
||||
ecdsaRecoverableSignatureSerializeCompact func(ctx uintptr, output64 *byte, recid *int32, sig *byte) int32
|
||||
ecdsaRecoverableSignatureParseCompact func(ctx uintptr, sig *byte, input64 *byte, recid int32) int32
|
||||
ecdsaSignRecoverable func(ctx uintptr, sig *byte, msg32 *byte, seckey *byte, noncefp uintptr, ndata uintptr) int32
|
||||
ecdsaRecover func(ctx uintptr, pubkey *byte, sig *byte, msg32 *byte) int32
|
||||
|
||||
// Extrakeys
|
||||
xonlyPubkeyFromPubkey func(ctx uintptr, xonlyPubkey *byte, pkParity *int32, pubkey *byte) int32
|
||||
)
|
||||
|
||||
// LoadLibrary loads the libsecp256k1 shared library
|
||||
func LoadLibrary() (err error) {
|
||||
loadLibOnce.Do(func() {
|
||||
var libPath string
|
||||
|
||||
// Try to find the library
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
// Try common library paths
|
||||
// For linux/amd64, try the bundled library first
|
||||
paths := []string{
|
||||
"./libsecp256k1.so", // Bundled in repo for linux amd64
|
||||
"libsecp256k1.so.5",
|
||||
"libsecp256k1.so.2",
|
||||
"libsecp256k1.so.1",
|
||||
"libsecp256k1.so.0",
|
||||
"libsecp256k1.so",
|
||||
"/usr/lib/libsecp256k1.so",
|
||||
"/usr/local/lib/libsecp256k1.so",
|
||||
"/usr/lib/x86_64-linux-gnu/libsecp256k1.so",
|
||||
}
|
||||
for _, p := range paths {
|
||||
libHandle, err = purego.Dlopen(p, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
||||
if err == nil {
|
||||
libPath = p
|
||||
break
|
||||
}
|
||||
}
|
||||
case "darwin":
|
||||
paths := []string{
|
||||
"libsecp256k1.2.dylib",
|
||||
"libsecp256k1.1.dylib",
|
||||
"libsecp256k1.0.dylib",
|
||||
"libsecp256k1.dylib",
|
||||
"/usr/local/lib/libsecp256k1.dylib",
|
||||
"/opt/homebrew/lib/libsecp256k1.dylib",
|
||||
}
|
||||
for _, p := range paths {
|
||||
libHandle, err = purego.Dlopen(p, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
||||
if err == nil {
|
||||
libPath = p
|
||||
break
|
||||
}
|
||||
}
|
||||
case "windows":
|
||||
paths := []string{
|
||||
"libsecp256k1-2.dll",
|
||||
"libsecp256k1-1.dll",
|
||||
"libsecp256k1-0.dll",
|
||||
"libsecp256k1.dll",
|
||||
"secp256k1.dll",
|
||||
}
|
||||
for _, p := range paths {
|
||||
libHandle, err = purego.Dlopen(p, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
||||
if err == nil {
|
||||
libPath = p
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("unsupported platform: %s", runtime.GOOS)
|
||||
loadLibErr = err
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
loadLibErr = fmt.Errorf("failed to load libsecp256k1: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Register symbols
|
||||
if err = registerSymbols(); err != nil {
|
||||
loadLibErr = fmt.Errorf("failed to register symbols from %s: %w", libPath, err)
|
||||
return
|
||||
}
|
||||
|
||||
loadLibErr = nil
|
||||
})
|
||||
|
||||
return loadLibErr
|
||||
}
|
||||
|
||||
// registerSymbols registers all C function symbols
|
||||
func registerSymbols() (err error) {
|
||||
// Core context functions
|
||||
purego.RegisterLibFunc(&contextCreate, libHandle, "secp256k1_context_create")
|
||||
purego.RegisterLibFunc(&contextDestroy, libHandle, "secp256k1_context_destroy")
|
||||
purego.RegisterLibFunc(&contextRandomize, libHandle, "secp256k1_context_randomize")
|
||||
|
||||
// Public key functions
|
||||
purego.RegisterLibFunc(&ecPubkeyCreate, libHandle, "secp256k1_ec_pubkey_create")
|
||||
purego.RegisterLibFunc(&ecPubkeySerialize, libHandle, "secp256k1_ec_pubkey_serialize")
|
||||
purego.RegisterLibFunc(&ecPubkeyParse, libHandle, "secp256k1_ec_pubkey_parse")
|
||||
|
||||
// ECDSA functions
|
||||
purego.RegisterLibFunc(&ecdsaSign, libHandle, "secp256k1_ecdsa_sign")
|
||||
purego.RegisterLibFunc(&ecdsaVerify, libHandle, "secp256k1_ecdsa_verify")
|
||||
purego.RegisterLibFunc(&ecdsaSignatureSerializeDer, libHandle, "secp256k1_ecdsa_signature_serialize_der")
|
||||
purego.RegisterLibFunc(&ecdsaSignatureParseDer, libHandle, "secp256k1_ecdsa_signature_parse_der")
|
||||
purego.RegisterLibFunc(&ecdsaSignatureSerializeCompact, libHandle, "secp256k1_ecdsa_signature_serialize_compact")
|
||||
purego.RegisterLibFunc(&ecdsaSignatureParseCompact, libHandle, "secp256k1_ecdsa_signature_parse_compact")
|
||||
purego.RegisterLibFunc(&ecdsaSignatureNormalize, libHandle, "secp256k1_ecdsa_signature_normalize")
|
||||
|
||||
// Try to load optional modules - don't fail if they're not available
|
||||
|
||||
// Schnorr module
|
||||
tryRegister(&schnorrsigSign32, "secp256k1_schnorrsig_sign32")
|
||||
tryRegister(&schnorrsigVerify, "secp256k1_schnorrsig_verify")
|
||||
tryRegister(&keypairCreate, "secp256k1_keypair_create")
|
||||
tryRegister(&xonlyPubkeyParse, "secp256k1_xonly_pubkey_parse")
|
||||
tryRegister(&xonlyPubkeySerialize, "secp256k1_xonly_pubkey_serialize")
|
||||
tryRegister(&keypairXonlyPub, "secp256k1_keypair_xonly_pub")
|
||||
tryRegister(&xonlyPubkeyFromPubkey, "secp256k1_xonly_pubkey_from_pubkey")
|
||||
|
||||
// ECDH module
|
||||
tryRegister(&ecdh, "secp256k1_ecdh")
|
||||
|
||||
// Recovery module
|
||||
tryRegister(&ecdsaRecoverableSignatureSerializeCompact, "secp256k1_ecdsa_recoverable_signature_serialize_compact")
|
||||
tryRegister(&ecdsaRecoverableSignatureParseCompact, "secp256k1_ecdsa_recoverable_signature_parse_compact")
|
||||
tryRegister(&ecdsaSignRecoverable, "secp256k1_ecdsa_sign_recoverable")
|
||||
tryRegister(&ecdsaRecover, "secp256k1_ecdsa_recover")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// tryRegister attempts to register a symbol without failing if it doesn't exist
|
||||
func tryRegister(fptr interface{}, symbol string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// Symbol not found, ignore
|
||||
}
|
||||
}()
|
||||
purego.RegisterLibFunc(fptr, libHandle, symbol)
|
||||
}
|
||||
|
||||
// Context represents a secp256k1 context
|
||||
type Context struct {
|
||||
ctx uintptr
|
||||
}
|
||||
|
||||
// NewContext creates a new secp256k1 context
|
||||
func NewContext(flags uint32) (c *Context, err error) {
|
||||
if err = LoadLibrary(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctx := contextCreate(flags)
|
||||
if ctx == 0 {
|
||||
err = fmt.Errorf("failed to create context")
|
||||
return
|
||||
}
|
||||
|
||||
c = &Context{ctx: ctx}
|
||||
runtime.SetFinalizer(c, (*Context).Destroy)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Destroy destroys the context
|
||||
func (c *Context) Destroy() {
|
||||
if c.ctx != 0 {
|
||||
contextDestroy(c.ctx)
|
||||
c.ctx = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Randomize randomizes the context with entropy
|
||||
func (c *Context) Randomize(seed32 []byte) (err error) {
|
||||
if len(seed32) != 32 {
|
||||
err = fmt.Errorf("seed must be 32 bytes")
|
||||
return
|
||||
}
|
||||
|
||||
ret := contextRandomize(c.ctx, &seed32[0])
|
||||
if ret != 1 {
|
||||
err = fmt.Errorf("failed to randomize context")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CreatePublicKey creates a public key from a private key
|
||||
func (c *Context) CreatePublicKey(seckey []byte) (pubkey []byte, err error) {
|
||||
if len(seckey) != PrivateKeySize {
|
||||
err = fmt.Errorf("private key must be %d bytes", PrivateKeySize)
|
||||
return
|
||||
}
|
||||
|
||||
pubkey = make([]byte, PublicKeySize)
|
||||
ret := ecPubkeyCreate(c.ctx, &pubkey[0], &seckey[0])
|
||||
if ret != 1 {
|
||||
err = fmt.Errorf("failed to create public key")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SerializePublicKey serializes a public key
|
||||
func (c *Context) SerializePublicKey(pubkey []byte, compressed bool) (output []byte, err error) {
|
||||
if len(pubkey) != PublicKeySize {
|
||||
err = fmt.Errorf("public key must be %d bytes", PublicKeySize)
|
||||
return
|
||||
}
|
||||
|
||||
var flags uint32
|
||||
if compressed {
|
||||
output = make([]byte, CompressedPublicKeySize)
|
||||
flags = ECCompressed
|
||||
} else {
|
||||
output = make([]byte, UncompressedPublicKeySize)
|
||||
flags = ECUncompressed
|
||||
}
|
||||
|
||||
outputLen := uint64(len(output))
|
||||
ret := ecPubkeySerialize(c.ctx, &output[0], &outputLen, &pubkey[0], flags)
|
||||
if ret != 1 {
|
||||
err = fmt.Errorf("failed to serialize public key")
|
||||
return
|
||||
}
|
||||
|
||||
output = output[:outputLen]
|
||||
return
|
||||
}
|
||||
|
||||
// ParsePublicKey parses a serialized public key
|
||||
func (c *Context) ParsePublicKey(input []byte) (pubkey []byte, err error) {
|
||||
pubkey = make([]byte, PublicKeySize)
|
||||
ret := ecPubkeyParse(c.ctx, &pubkey[0], &input[0], uint64(len(input)))
|
||||
if ret != 1 {
|
||||
err = fmt.Errorf("failed to parse public key")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Sign creates an ECDSA signature
|
||||
func (c *Context) Sign(msg32 []byte, seckey []byte) (sig []byte, err error) {
|
||||
if len(msg32) != 32 {
|
||||
err = fmt.Errorf("message must be 32 bytes")
|
||||
return
|
||||
}
|
||||
if len(seckey) != PrivateKeySize {
|
||||
err = fmt.Errorf("private key must be %d bytes", PrivateKeySize)
|
||||
return
|
||||
}
|
||||
|
||||
sig = make([]byte, SignatureSize)
|
||||
ret := ecdsaSign(c.ctx, &sig[0], &msg32[0], &seckey[0], 0, 0)
|
||||
if ret != 1 {
|
||||
err = fmt.Errorf("failed to sign message")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Verify verifies an ECDSA signature
|
||||
func (c *Context) Verify(msg32 []byte, sig []byte, pubkey []byte) (valid bool, err error) {
|
||||
if len(msg32) != 32 {
|
||||
err = fmt.Errorf("message must be 32 bytes")
|
||||
return
|
||||
}
|
||||
if len(sig) != SignatureSize {
|
||||
err = fmt.Errorf("signature must be %d bytes", SignatureSize)
|
||||
return
|
||||
}
|
||||
if len(pubkey) != PublicKeySize {
|
||||
err = fmt.Errorf("public key must be %d bytes", PublicKeySize)
|
||||
return
|
||||
}
|
||||
|
||||
ret := ecdsaVerify(c.ctx, &sig[0], &msg32[0], &pubkey[0])
|
||||
valid = ret == 1
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SerializeSignatureDER serializes a signature in DER format
|
||||
func (c *Context) SerializeSignatureDER(sig []byte) (output []byte, err error) {
|
||||
if len(sig) != SignatureSize {
|
||||
err = fmt.Errorf("signature must be %d bytes", SignatureSize)
|
||||
return
|
||||
}
|
||||
|
||||
output = make([]byte, 72) // Max DER signature size
|
||||
outputLen := uint64(len(output))
|
||||
|
||||
ret := ecdsaSignatureSerializeDer(c.ctx, &output[0], &outputLen, &sig[0])
|
||||
if ret != 1 {
|
||||
err = fmt.Errorf("failed to serialize signature")
|
||||
return
|
||||
}
|
||||
|
||||
output = output[:outputLen]
|
||||
return
|
||||
}
|
||||
|
||||
// ParseSignatureDER parses a DER-encoded signature
|
||||
func (c *Context) ParseSignatureDER(input []byte) (sig []byte, err error) {
|
||||
sig = make([]byte, SignatureSize)
|
||||
ret := ecdsaSignatureParseDer(c.ctx, &sig[0], &input[0], uint64(len(input)))
|
||||
if ret != 1 {
|
||||
err = fmt.Errorf("failed to parse DER signature")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SerializeSignatureCompact serializes a signature in compact format (64 bytes)
|
||||
func (c *Context) SerializeSignatureCompact(sig []byte) (output []byte, err error) {
|
||||
if len(sig) != SignatureSize {
|
||||
err = fmt.Errorf("signature must be %d bytes", SignatureSize)
|
||||
return
|
||||
}
|
||||
|
||||
output = make([]byte, CompactSignatureSize)
|
||||
ret := ecdsaSignatureSerializeCompact(c.ctx, &output[0], &sig[0])
|
||||
if ret != 1 {
|
||||
err = fmt.Errorf("failed to serialize signature")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ParseSignatureCompact parses a compact (64-byte) signature
|
||||
func (c *Context) ParseSignatureCompact(input64 []byte) (sig []byte, err error) {
|
||||
if len(input64) != CompactSignatureSize {
|
||||
err = fmt.Errorf("compact signature must be %d bytes", CompactSignatureSize)
|
||||
return
|
||||
}
|
||||
|
||||
sig = make([]byte, SignatureSize)
|
||||
ret := ecdsaSignatureParseCompact(c.ctx, &sig[0], &input64[0])
|
||||
if ret != 1 {
|
||||
err = fmt.Errorf("failed to parse compact signature")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NormalizeSignature normalizes a signature to lower-S form
|
||||
func (c *Context) NormalizeSignature(sig []byte) (normalized []byte, wasNormalized bool, err error) {
|
||||
if len(sig) != SignatureSize {
|
||||
err = fmt.Errorf("signature must be %d bytes", SignatureSize)
|
||||
return
|
||||
}
|
||||
|
||||
normalized = make([]byte, SignatureSize)
|
||||
ret := ecdsaSignatureNormalize(c.ctx, &normalized[0], &sig[0])
|
||||
wasNormalized = ret == 1
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Utility function to convert *byte to unsafe.Pointer
|
||||
func bytesToPtr(b []byte) unsafe.Pointer {
|
||||
if len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
return unsafe.Pointer(&b[0])
|
||||
}
|
||||
Reference in New Issue
Block a user