Browse Source
- Bumped version of lol.mleku.dev from v1.0.4 to v1.0.5. - Added new dependencies: p256k1.mleku.dev and several indirect dependencies for improved cryptographic functionality. - Refactored p256k package to utilize p256k1.mleku.dev/signer for signature operations, replacing the previous btcec implementation. - Removed the secp256k1.go file, consolidating the crypto logic under the new p256k1 library. - Updated documentation to reflect changes in the signer interface and usage.main
8 changed files with 87 additions and 574 deletions
@ -1,6 +1,9 @@ |
|||||||
// Package p256k is a signer interface that (by default) uses the
|
// Package p256k provides a signer interface that uses p256k1.mleku.dev library for
|
||||||
// bitcoin/libsecp256k1 library for fast signature creation and verification of
|
// fast signature creation and verification of BIP-340 nostr X-only signatures and
|
||||||
// the BIP-340 nostr X-only signatures and public keys, and ECDH.
|
// public keys, and ECDH.
|
||||||
//
|
//
|
||||||
// Currently the ECDH is only implemented with the btcec library.
|
// The package provides type aliases to p256k1.mleku.dev/signer:
|
||||||
|
// - cgo: Uses the CGO-optimized version from p256k1.mleku.dev
|
||||||
|
// - btcec: Uses the btcec version from p256k1.mleku.dev
|
||||||
|
// - default: Uses the pure Go version from p256k1.mleku.dev
|
||||||
package p256k |
package p256k |
||||||
|
|||||||
@ -0,0 +1,41 @@ |
|||||||
|
//go:build !cgo
|
||||||
|
|
||||||
|
package p256k |
||||||
|
|
||||||
|
import ( |
||||||
|
"lol.mleku.dev/chk" |
||||||
|
"next.orly.dev/pkg/encoders/hex" |
||||||
|
"next.orly.dev/pkg/interfaces/signer" |
||||||
|
p256k1signer "p256k1.mleku.dev/signer" |
||||||
|
) |
||||||
|
|
||||||
|
func NewSecFromHex[V []byte | string](skh V) (sign signer.I, err error) { |
||||||
|
sk := make([]byte, len(skh)/2) |
||||||
|
if _, err = hex.DecBytes(sk, []byte(skh)); chk.E(err) { |
||||||
|
return |
||||||
|
} |
||||||
|
sign = p256k1signer.NewBtcecSigner() |
||||||
|
if err = sign.InitSec(sk); chk.E(err) { |
||||||
|
return |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func NewPubFromHex[V []byte | string](pkh V) (sign signer.I, err error) { |
||||||
|
pk := make([]byte, len(pkh)/2) |
||||||
|
if _, err = hex.DecBytes(pk, []byte(pkh)); chk.E(err) { |
||||||
|
return |
||||||
|
} |
||||||
|
sign = p256k1signer.NewBtcecSigner() |
||||||
|
if err = sign.InitPub(pk); chk.E(err) { |
||||||
|
return |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func HexToBin(hexStr string) (b []byte, err error) { |
||||||
|
if b, err = hex.DecAppend(b, []byte(hexStr)); chk.E(err) { |
||||||
|
return |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
@ -1,426 +0,0 @@ |
|||||||
//go:build cgo
|
|
||||||
|
|
||||||
package p256k |
|
||||||
|
|
||||||
import ( |
|
||||||
"crypto/rand" |
|
||||||
"unsafe" |
|
||||||
|
|
||||||
"lol.mleku.dev/chk" |
|
||||||
"lol.mleku.dev/errorf" |
|
||||||
"lol.mleku.dev/log" |
|
||||||
"next.orly.dev/pkg/crypto/ec/schnorr" |
|
||||||
"next.orly.dev/pkg/crypto/ec/secp256k1" |
|
||||||
"next.orly.dev/pkg/crypto/sha256" |
|
||||||
) |
|
||||||
|
|
||||||
/* |
|
||||||
#cgo LDFLAGS: -lsecp256k1 |
|
||||||
#include <secp256k1.h> |
|
||||||
#include <secp256k1_schnorrsig.h> |
|
||||||
#include <secp256k1_extrakeys.h> |
|
||||||
*/ |
|
||||||
import "C" |
|
||||||
|
|
||||||
type ( |
|
||||||
Context = C.secp256k1_context |
|
||||||
Uchar = C.uchar |
|
||||||
Cint = C.int |
|
||||||
SecKey = C.secp256k1_keypair |
|
||||||
PubKey = C.secp256k1_xonly_pubkey |
|
||||||
ECPubKey = C.secp256k1_pubkey |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
ctx *Context |
|
||||||
) |
|
||||||
|
|
||||||
func CreateContext() *Context { |
|
||||||
return C.secp256k1_context_create( |
|
||||||
C.SECP256K1_CONTEXT_SIGN | |
|
||||||
C.SECP256K1_CONTEXT_VERIFY, |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
func GetRandom() (u *Uchar) { |
|
||||||
rnd := make([]byte, 32) |
|
||||||
_, _ = rand.Read(rnd) |
|
||||||
return ToUchar(rnd) |
|
||||||
} |
|
||||||
|
|
||||||
func AssertLen(b []byte, length int, name string) (err error) { |
|
||||||
if len(b) != length { |
|
||||||
err = errorf.E("%s should be %d bytes, got %d", name, length, len(b)) |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func RandomizeContext(ctx *C.secp256k1_context) { |
|
||||||
C.secp256k1_context_randomize(ctx, GetRandom()) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func CreateRandomContext() (c *Context) { |
|
||||||
c = CreateContext() |
|
||||||
RandomizeContext(c) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func init() { |
|
||||||
if ctx = CreateContext(); ctx == nil { |
|
||||||
panic("failed to create secp256k1 context") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func ToUchar(b []byte) (u *Uchar) { return (*Uchar)(unsafe.Pointer(&b[0])) } |
|
||||||
|
|
||||||
type Sec struct { |
|
||||||
Key SecKey |
|
||||||
} |
|
||||||
|
|
||||||
func GenSec() (sec *Sec, err error) { |
|
||||||
if _, _, sec, _, err = Generate(); chk.E(err) { |
|
||||||
return |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func SecFromBytes(sk []byte) (sec *Sec, err error) { |
|
||||||
sec = new(Sec) |
|
||||||
if C.secp256k1_keypair_create(ctx, &sec.Key, ToUchar(sk)) != 1 { |
|
||||||
err = errorf.E("failed to parse private key") |
|
||||||
return |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func (s *Sec) Sec() *SecKey { return &s.Key } |
|
||||||
|
|
||||||
func (s *Sec) Pub() (p *Pub, err error) { |
|
||||||
p = new(Pub) |
|
||||||
if C.secp256k1_keypair_xonly_pub(ctx, &p.Key, nil, s.Sec()) != 1 { |
|
||||||
err = errorf.E("pubkey derivation failed") |
|
||||||
return |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// type PublicKey struct {
|
|
||||||
// Key *C.secp256k1_pubkey
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func NewPublicKey() *PublicKey {
|
|
||||||
// return &PublicKey{
|
|
||||||
// Key: &C.secp256k1_pubkey{},
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
type XPublicKey struct { |
|
||||||
Key *C.secp256k1_xonly_pubkey |
|
||||||
} |
|
||||||
|
|
||||||
func NewXPublicKey() *XPublicKey { |
|
||||||
return &XPublicKey{ |
|
||||||
Key: &C.secp256k1_xonly_pubkey{}, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// FromSecretBytes parses and processes what should be a secret key. If it is a correct key within the curve order, but
|
|
||||||
// with a public key having an odd Y coordinate, it returns an error with the fixed key.
|
|
||||||
func FromSecretBytes(skb []byte) ( |
|
||||||
pkb []byte, |
|
||||||
sec *Sec, |
|
||||||
pub *XPublicKey, |
|
||||||
// ecPub *PublicKey,
|
|
||||||
err error, |
|
||||||
) { |
|
||||||
xpkb := make([]byte, schnorr.PubKeyBytesLen) |
|
||||||
// clen := C.size_t(secp256k1.PubKeyBytesLenCompressed - 1)
|
|
||||||
pkb = make([]byte, schnorr.PubKeyBytesLen) |
|
||||||
var parity Cint |
|
||||||
// ecPub = NewPublicKey()
|
|
||||||
pub = NewXPublicKey() |
|
||||||
sec = &Sec{} |
|
||||||
uskb := ToUchar(skb) |
|
||||||
res := C.secp256k1_keypair_create(ctx, &sec.Key, uskb) |
|
||||||
if res != 1 { |
|
||||||
err = errorf.E("failed to create secp256k1 keypair") |
|
||||||
return |
|
||||||
} |
|
||||||
// C.secp256k1_keypair_pub(ctx, ecPub.Key, &sec.Key)
|
|
||||||
// C.secp256k1_ec_pubkey_serialize(ctx, ToUchar(ecpkb), &clen, ecPub.Key,
|
|
||||||
// C.SECP256K1_EC_COMPRESSED)
|
|
||||||
// if ecpkb[0] != 2 {
|
|
||||||
// log.W.ToSliceOfBytes("odd pubkey from %0x -> %0x", skb, ecpkb)
|
|
||||||
// Negate(skb)
|
|
||||||
// uskb = ToUchar(skb)
|
|
||||||
// res = C.secp256k1_keypair_create(ctx, &sec.Key, uskb)
|
|
||||||
// if res != 1 {
|
|
||||||
// err = errorf.E("failed to create secp256k1 keypair")
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// C.secp256k1_keypair_pub(ctx, ecPub.Key, &sec.Key)
|
|
||||||
// C.secp256k1_ec_pubkey_serialize(ctx, ToUchar(ecpkb), &clen, ecPub.Key, C.SECP256K1_EC_COMPRESSED)
|
|
||||||
// C.secp256k1_keypair_xonly_pub(ctx, pub.Key, &parity, &sec.Key)
|
|
||||||
// err = errors.New("provided secret generates a public key with odd Y coordinate, fixed version returned")
|
|
||||||
// }
|
|
||||||
C.secp256k1_keypair_xonly_pub(ctx, pub.Key, &parity, &sec.Key) |
|
||||||
C.secp256k1_xonly_pubkey_serialize(ctx, ToUchar(xpkb), pub.Key) |
|
||||||
pkb = xpkb |
|
||||||
// log.I.S(sec, pub, skb, pkb)
|
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Generate gathers entropy to generate a full set of bytes and CGO values of it and derived from it to perform
|
|
||||||
// signature and ECDH operations.
|
|
||||||
func Generate() ( |
|
||||||
skb, pkb []byte, |
|
||||||
sec *Sec, |
|
||||||
pub *XPublicKey, |
|
||||||
err error, |
|
||||||
) { |
|
||||||
skb = make([]byte, secp256k1.SecKeyBytesLen) |
|
||||||
pkb = make([]byte, schnorr.PubKeyBytesLen) |
|
||||||
upkb := ToUchar(pkb) |
|
||||||
var parity Cint |
|
||||||
pub = NewXPublicKey() |
|
||||||
sec = &Sec{} |
|
||||||
for { |
|
||||||
if _, err = rand.Read(skb); chk.E(err) { |
|
||||||
return |
|
||||||
} |
|
||||||
uskb := ToUchar(skb) |
|
||||||
if res := C.secp256k1_keypair_create(ctx, &sec.Key, uskb); res != 1 { |
|
||||||
err = errorf.E("failed to create secp256k1 keypair") |
|
||||||
continue |
|
||||||
} |
|
||||||
C.secp256k1_keypair_xonly_pub(ctx, pub.Key, &parity, &sec.Key) |
|
||||||
C.secp256k1_xonly_pubkey_serialize(ctx, upkb, pub.Key) |
|
||||||
break |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Negate inverts a secret key so an odd prefix bit becomes even and vice versa.
|
|
||||||
func Negate(uskb []byte) { C.secp256k1_ec_seckey_negate(ctx, ToUchar(uskb)) } |
|
||||||
|
|
||||||
type ECPub struct { |
|
||||||
Key ECPubKey |
|
||||||
} |
|
||||||
|
|
||||||
// ECPubFromSchnorrBytes converts a BIP-340 public key to its even standard 33 byte encoding.
|
|
||||||
//
|
|
||||||
// This function is for the purpose of getting a key to do ECDH from an x-only key.
|
|
||||||
func ECPubFromSchnorrBytes(xkb []byte) (pub *ECPub, err error) { |
|
||||||
if err = AssertLen(xkb, schnorr.PubKeyBytesLen, "pubkey"); chk.E(err) { |
|
||||||
return |
|
||||||
} |
|
||||||
pub = &ECPub{} |
|
||||||
p := append([]byte{0}, xkb...) |
|
||||||
if C.secp256k1_ec_pubkey_parse( |
|
||||||
ctx, &pub.Key, ToUchar(p), |
|
||||||
secp256k1.PubKeyBytesLenCompressed, |
|
||||||
) != 1 { |
|
||||||
err = errorf.E("failed to parse pubkey from %0x", p) |
|
||||||
log.I.S(pub) |
|
||||||
return |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// // ECPubFromBytes parses a pubkey from 33 bytes to the bitcoin-core/secp256k1 struct.
|
|
||||||
// func ECPubFromBytes(pkb []byte) (pub *ECPub, err error) {
|
|
||||||
// if err = AssertLen(pkb, secp256k1.PubKeyBytesLenCompressed, "pubkey"); chk.E(err) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// pub = &ECPub{}
|
|
||||||
// if C.secp256k1_ec_pubkey_parse(ctx, &pub.Key, ToUchar(pkb),
|
|
||||||
// secp256k1.PubKeyBytesLenCompressed) != 1 {
|
|
||||||
// err = errorf.E("failed to parse pubkey from %0x", pkb)
|
|
||||||
// log.I.S(pub)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Pub is a schnorr BIP-340 public key.
|
|
||||||
type Pub struct { |
|
||||||
Key PubKey |
|
||||||
} |
|
||||||
|
|
||||||
// PubFromBytes creates a public key from raw bytes.
|
|
||||||
func PubFromBytes(pk []byte) (pub *Pub, err error) { |
|
||||||
if err = AssertLen(pk, schnorr.PubKeyBytesLen, "pubkey"); chk.E(err) { |
|
||||||
return |
|
||||||
} |
|
||||||
pub = new(Pub) |
|
||||||
if C.secp256k1_xonly_pubkey_parse(ctx, &pub.Key, ToUchar(pk)) != 1 { |
|
||||||
err = errorf.E("failed to parse pubkey from %0x", pk) |
|
||||||
return |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// PubB returns the contained public key as bytes.
|
|
||||||
func (p *Pub) PubB() (b []byte) { |
|
||||||
b = make([]byte, schnorr.PubKeyBytesLen) |
|
||||||
C.secp256k1_xonly_pubkey_serialize(ctx, ToUchar(b), &p.Key) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Pub returns the public key as a PubKey.
|
|
||||||
func (p *Pub) Pub() *PubKey { return &p.Key } |
|
||||||
|
|
||||||
// ToBytes returns the contained public key as bytes.
|
|
||||||
func (p *Pub) ToBytes() (b []byte, err error) { |
|
||||||
b = make([]byte, schnorr.PubKeyBytesLen) |
|
||||||
if C.secp256k1_xonly_pubkey_serialize(ctx, ToUchar(b), p.Pub()) != 1 { |
|
||||||
err = errorf.E("pubkey serialize failed") |
|
||||||
return |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Sign a message and return a schnorr BIP-340 64 byte signature.
|
|
||||||
func Sign(msg *Uchar, sk *SecKey) (sig []byte, err error) { |
|
||||||
sig = make([]byte, schnorr.SignatureSize) |
|
||||||
c := CreateRandomContext() |
|
||||||
if C.secp256k1_schnorrsig_sign32( |
|
||||||
c, ToUchar(sig), msg, sk, |
|
||||||
GetRandom(), |
|
||||||
) != 1 { |
|
||||||
err = errorf.E("failed to sign message") |
|
||||||
return |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// SignFromBytes Signs a message using a provided secret key and message as raw bytes.
|
|
||||||
func SignFromBytes(msg, sk []byte) (sig []byte, err error) { |
|
||||||
var umsg *Uchar |
|
||||||
if umsg, err = Msg(msg); chk.E(err) { |
|
||||||
return |
|
||||||
} |
|
||||||
var sec *Sec |
|
||||||
if sec, err = SecFromBytes(sk); chk.E(err) { |
|
||||||
return |
|
||||||
} |
|
||||||
return Sign(umsg, sec.Sec()) |
|
||||||
} |
|
||||||
|
|
||||||
// Msg checks that a message hash is correct, and converts it for use with a Signer.
|
|
||||||
func Msg(b []byte) (id *Uchar, err error) { |
|
||||||
if err = AssertLen(b, sha256.Size, "id"); chk.E(err) { |
|
||||||
return |
|
||||||
} |
|
||||||
id = ToUchar(b) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Sig checks that a signature bytes is correct, and converts it for use with a Signer.
|
|
||||||
func Sig(b []byte) (sig *Uchar, err error) { |
|
||||||
if err = AssertLen(b, schnorr.SignatureSize, "sig"); chk.E(err) { |
|
||||||
return |
|
||||||
} |
|
||||||
sig = ToUchar(b) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Verify a message signature matches the provided PubKey.
|
|
||||||
func Verify(msg, sig *Uchar, pk *PubKey) (valid bool) { |
|
||||||
return C.secp256k1_schnorrsig_verify(ctx, sig, msg, 32, pk) == 1 |
|
||||||
} |
|
||||||
|
|
||||||
// VerifyFromBytes a signature from the raw bytes of the message hash, signature and public key
|
|
||||||
func VerifyFromBytes(msg, sig, pk []byte) (err error) { |
|
||||||
var umsg, usig *Uchar |
|
||||||
if umsg, err = Msg(msg); chk.E(err) { |
|
||||||
return |
|
||||||
} |
|
||||||
if usig, err = Sig(sig); chk.E(err) { |
|
||||||
return |
|
||||||
} |
|
||||||
var pub *Pub |
|
||||||
if pub, err = PubFromBytes(pk); chk.E(err) { |
|
||||||
return |
|
||||||
} |
|
||||||
valid := Verify(umsg, usig, pub.Pub()) |
|
||||||
if !valid { |
|
||||||
err = errorf.E("failed to verify signature") |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Zero wipes the memory of a SecKey by overwriting it three times with random data and then
|
|
||||||
// zeroing it.
|
|
||||||
func Zero(sk *SecKey) { |
|
||||||
b := (*[96]byte)(unsafe.Pointer(sk))[:96] |
|
||||||
for range 3 { |
|
||||||
rand.Read(b) |
|
||||||
// reverse the order and negate
|
|
||||||
lb := len(b) |
|
||||||
l := lb / 2 |
|
||||||
for j := range l { |
|
||||||
b[j] = ^b[lb-1-j] |
|
||||||
} |
|
||||||
} |
|
||||||
for i := range b { |
|
||||||
b[i] = 0 |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Keygen is an implementation of a key miner designed to be used for vanity key generation with X-only BIP-340 keys.
|
|
||||||
type Keygen struct { |
|
||||||
secBytes, comprPubBytes []byte |
|
||||||
secUchar, cmprPubUchar *Uchar |
|
||||||
sec *Sec |
|
||||||
// ecpub *PublicKey
|
|
||||||
cmprLen C.size_t |
|
||||||
} |
|
||||||
|
|
||||||
// NewKeygen allocates the required buffers for deriving a key. This should only be done once to avoid garbage and make
|
|
||||||
// the key mining as fast as possible.
|
|
||||||
//
|
|
||||||
// This allocates everything and creates proper CGO variables needed for the generate function so they only need to be
|
|
||||||
// allocated once per thread.
|
|
||||||
func NewKeygen() (k *Keygen) { |
|
||||||
k = new(Keygen) |
|
||||||
k.cmprLen = C.size_t(secp256k1.PubKeyBytesLenCompressed) |
|
||||||
k.secBytes = make([]byte, secp256k1.SecKeyBytesLen) |
|
||||||
k.comprPubBytes = make([]byte, secp256k1.PubKeyBytesLenCompressed) |
|
||||||
k.secUchar = ToUchar(k.secBytes) |
|
||||||
k.cmprPubUchar = ToUchar(k.comprPubBytes) |
|
||||||
k.sec = &Sec{} |
|
||||||
// k.ecpub = NewPublicKey()
|
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Generate takes a pair of buffers for the secret and ec pubkey bytes and gathers new entropy and returns a valid
|
|
||||||
// secret key and the compressed pubkey bytes for the partial collision search.
|
|
||||||
//
|
|
||||||
// The first byte of pubBytes must be sliced off before deriving the hex/Bech32 forms of the nostr public key.
|
|
||||||
func (k *Keygen) Generate() ( |
|
||||||
sec *Sec, |
|
||||||
pub *XPublicKey, |
|
||||||
pubBytes []byte, |
|
||||||
err error, |
|
||||||
) { |
|
||||||
if _, err = rand.Read(k.secBytes); chk.E(err) { |
|
||||||
return |
|
||||||
} |
|
||||||
if res := C.secp256k1_keypair_create( |
|
||||||
ctx, &k.sec.Key, k.secUchar, |
|
||||||
); res != 1 { |
|
||||||
err = errorf.E("failed to create secp256k1 keypair") |
|
||||||
return |
|
||||||
} |
|
||||||
var parity Cint |
|
||||||
C.secp256k1_keypair_xonly_pub(ctx, pub.Key, &parity, &sec.Key) |
|
||||||
// C.secp256k1_keypair_pub(ctx, k.ecpub.Key, &k.sec.Key)
|
|
||||||
// C.secp256k1_ec_pubkey_serialize(ctx, k.cmprPubUchar, &k.cmprLen, k.ecpub.Key,
|
|
||||||
// C.SECP256K1_EC_COMPRESSED)
|
|
||||||
// pubBytes = k.comprPubBytes
|
|
||||||
C.secp256k1_xonly_pubkey_serialize(ctx, ToUchar(pubBytes), pub.Key) |
|
||||||
// pubBytes =
|
|
||||||
return |
|
||||||
} |
|
||||||
Loading…
Reference in new issue