//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 #include #include */ 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 }