initial addition of essential crypto, encoders, workflows and LLM instructions
This commit is contained in:
68
pkg/crypto/p256k/README.md
Normal file
68
pkg/crypto/p256k/README.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# p256k1
|
||||
|
||||
This is a library that uses the `bitcoin-core` optimized secp256k1 elliptic
|
||||
curve signatures library for `nostr` schnorr signatures.
|
||||
|
||||
If you need to build it without `libsecp256k1` C library, you must disable cgo:
|
||||
|
||||
export CGO_ENABLED='0'
|
||||
|
||||
This enables the fallback `btcec` pure Go library to be used in its place. This
|
||||
CGO setting is not default for Go, so it must be set in order to disable this.
|
||||
|
||||
The standard `libsecp256k1-0` and `libsecp256k1-dev` available through the
|
||||
ubuntu dpkg repositories do not include support for the BIP-340 schnorr
|
||||
signatures or the ECDH X-only shared secret generation algorithm, so you must
|
||||
follow the following instructions to get the benefits of using this library. It
|
||||
is 4x faster at signing and generating shared secrets so it is a must if your
|
||||
intention is to use it for high throughput systems like a network transport.
|
||||
|
||||
The easy way to install it, if you have ubuntu/debian, is the script
|
||||
[../ubuntu_install_libsecp256k1.sh](../../../scripts/ubuntu_install_libsecp256k1.sh),
|
||||
it
|
||||
handles the dependencies and runs the build all in one step for you. Note that
|
||||
it
|
||||
|
||||
For ubuntu, you need these:
|
||||
|
||||
sudo apt -y install build-essential autoconf libtool
|
||||
|
||||
For other linux distributions, the process is the same but the dependencies are
|
||||
likely different. The main thing is it requires make, gcc/++, autoconf and
|
||||
libtool to run. The most important thing to point out is that you must enable
|
||||
the schnorr signatures feature, and ECDH.
|
||||
|
||||
The directory `p256k/secp256k1` needs to be initialized, built and installed,
|
||||
like so:
|
||||
|
||||
```bash
|
||||
cd secp256k1
|
||||
git submodule init
|
||||
git submodule update
|
||||
```
|
||||
|
||||
Then to build, you can refer to the [instructions](./secp256k1/README.md) or
|
||||
just use the default autotools:
|
||||
|
||||
```bash
|
||||
./autogen.sh
|
||||
./configure --enable-module-schnorrsig --enable-module-ecdh --prefix=/usr
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
On WSL2 you may have to attend to various things to make this work, setting up
|
||||
your basic locale (uncomment one or more in `/etc/locale.gen`, and run
|
||||
`locale-gen`), installing the basic build tools (build-essential or base-devel)
|
||||
and of course git, curl, wget, libtool and
|
||||
autoconf.
|
||||
|
||||
## ECDH
|
||||
|
||||
TODO: Currently the use of the libsecp256k1 library for ECDH, used in nip-04 and
|
||||
nip-44 encryption is not enabled, because the default version uses the Y
|
||||
coordinate and this is incorrect for nostr. It will be enabled soon... for now
|
||||
it is done with the `btcec` fallback version. This is slower, however previous
|
||||
tests have shown that this ECDH library is fast enough to enable 8mb/s
|
||||
throughput per CPU thread when used to generate a distinct secret for TCP
|
||||
packets. The C library will likely raise this to 20mb/s or more.
|
||||
25
pkg/crypto/p256k/btcec.go
Normal file
25
pkg/crypto/p256k/btcec.go
Normal file
@@ -0,0 +1,25 @@
|
||||
//go:build !cgo
|
||||
|
||||
package p256k
|
||||
|
||||
import (
|
||||
"lol.mleku.dev/log"
|
||||
"next.orly.dev/pkg/crypto/p256k/btcec"
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.T.Ln("using btcec signature library")
|
||||
}
|
||||
|
||||
// BTCECSigner is always available but enabling it disables the use of
|
||||
// github.com/bitcoin-core/secp256k1 CGO signature implementation and points it at the btec
|
||||
// version.
|
||||
|
||||
type Signer = btcec.Signer
|
||||
type Keygen = btcec.Keygen
|
||||
|
||||
func NewKeygen() (k *Keygen) { return new(Keygen) }
|
||||
|
||||
var NewSecFromHex = btcec.NewSecFromHex[string]
|
||||
var NewPubFromHex = btcec.NewPubFromHex[string]
|
||||
var HexToBin = btcec.HexToBin
|
||||
170
pkg/crypto/p256k/btcec/btcec.go
Normal file
170
pkg/crypto/p256k/btcec/btcec.go
Normal file
@@ -0,0 +1,170 @@
|
||||
//go:build !cgo
|
||||
|
||||
// Package btcec implements the signer.I interface for signatures and ECDH with nostr.
|
||||
package btcec
|
||||
|
||||
import (
|
||||
"lol.mleku.dev/chk"
|
||||
"lol.mleku.dev/errorf"
|
||||
btcec3 "next.orly.dev/pkg/crypto/ec"
|
||||
"next.orly.dev/pkg/crypto/ec/schnorr"
|
||||
"next.orly.dev/pkg/crypto/ec/secp256k1"
|
||||
"next.orly.dev/pkg/interfaces/signer"
|
||||
)
|
||||
|
||||
// Signer is an implementation of signer.I that uses the btcec library.
|
||||
type Signer struct {
|
||||
SecretKey *secp256k1.SecretKey
|
||||
PublicKey *secp256k1.PublicKey
|
||||
BTCECSec *btcec3.SecretKey
|
||||
pkb, skb []byte
|
||||
}
|
||||
|
||||
var _ signer.I = &Signer{}
|
||||
|
||||
// Generate creates a new Signer.
|
||||
func (s *Signer) Generate() (err error) {
|
||||
if s.SecretKey, err = btcec3.NewSecretKey(); chk.E(err) {
|
||||
return
|
||||
}
|
||||
s.skb = s.SecretKey.Serialize()
|
||||
s.BTCECSec, _ = btcec3.PrivKeyFromBytes(s.skb)
|
||||
s.PublicKey = s.SecretKey.PubKey()
|
||||
s.pkb = schnorr.SerializePubKey(s.PublicKey)
|
||||
return
|
||||
}
|
||||
|
||||
// InitSec initialises a Signer using raw secret key bytes.
|
||||
func (s *Signer) InitSec(sec []byte) (err error) {
|
||||
if len(sec) != secp256k1.SecKeyBytesLen {
|
||||
err = errorf.E("sec key must be %d bytes", secp256k1.SecKeyBytesLen)
|
||||
return
|
||||
}
|
||||
s.skb = sec
|
||||
s.SecretKey = secp256k1.SecKeyFromBytes(sec)
|
||||
s.PublicKey = s.SecretKey.PubKey()
|
||||
s.pkb = schnorr.SerializePubKey(s.PublicKey)
|
||||
s.BTCECSec, _ = btcec3.PrivKeyFromBytes(s.skb)
|
||||
return
|
||||
}
|
||||
|
||||
// InitPub initializes a signature verifier Signer from raw public key bytes.
|
||||
func (s *Signer) InitPub(pub []byte) (err error) {
|
||||
if s.PublicKey, err = schnorr.ParsePubKey(pub); chk.E(err) {
|
||||
return
|
||||
}
|
||||
s.pkb = pub
|
||||
return
|
||||
}
|
||||
|
||||
// Sec returns the raw secret key bytes.
|
||||
func (s *Signer) Sec() (b []byte) {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return s.skb
|
||||
}
|
||||
|
||||
// Pub returns the raw BIP-340 schnorr public key bytes.
|
||||
func (s *Signer) Pub() (b []byte) {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return s.pkb
|
||||
}
|
||||
|
||||
// Sign a message with the Signer. Requires an initialised secret key.
|
||||
func (s *Signer) Sign(msg []byte) (sig []byte, err error) {
|
||||
if s.SecretKey == nil {
|
||||
err = errorf.E("btcec: Signer not initialized")
|
||||
return
|
||||
}
|
||||
var si *schnorr.Signature
|
||||
if si, err = schnorr.Sign(s.SecretKey, msg); chk.E(err) {
|
||||
return
|
||||
}
|
||||
sig = si.Serialize()
|
||||
return
|
||||
}
|
||||
|
||||
// Verify a message signature, only requires the public key is initialised.
|
||||
func (s *Signer) Verify(msg, sig []byte) (valid bool, err error) {
|
||||
if s.PublicKey == nil {
|
||||
err = errorf.E("btcec: Pubkey not initialized")
|
||||
return
|
||||
}
|
||||
|
||||
// First try to verify using the schnorr package
|
||||
var si *schnorr.Signature
|
||||
if si, err = schnorr.ParseSignature(sig); err == nil {
|
||||
valid = si.Verify(msg, s.PublicKey)
|
||||
return
|
||||
}
|
||||
|
||||
// If parsing the signature failed, log it at debug level
|
||||
chk.D(err)
|
||||
|
||||
// If the signature is exactly 64 bytes, try to verify it directly
|
||||
// This is to handle signatures created by p256k.Signer which uses libsecp256k1
|
||||
if len(sig) == schnorr.SignatureSize {
|
||||
// Create a new signature with the raw bytes
|
||||
var r secp256k1.FieldVal
|
||||
var sScalar secp256k1.ModNScalar
|
||||
|
||||
// Split the signature into r and s components
|
||||
if overflow := r.SetByteSlice(sig[0:32]); !overflow {
|
||||
sScalar.SetByteSlice(sig[32:64])
|
||||
|
||||
// Create a new signature and verify it
|
||||
newSig := schnorr.NewSignature(&r, &sScalar)
|
||||
valid = newSig.Verify(msg, s.PublicKey)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If all verification methods failed, return an error
|
||||
err = errorf.E(
|
||||
"failed to verify signature:\n%d %s", len(sig), sig,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Zero wipes the bytes of the secret key.
|
||||
func (s *Signer) Zero() { s.SecretKey.Key.Zero() }
|
||||
|
||||
// ECDH creates a shared secret from a secret key and a provided public key bytes. It is advised
|
||||
// to hash this result for security reasons.
|
||||
func (s *Signer) ECDH(pubkeyBytes []byte) (secret []byte, err error) {
|
||||
var pub *secp256k1.PublicKey
|
||||
if pub, err = secp256k1.ParsePubKey(
|
||||
append(
|
||||
[]byte{0x02}, pubkeyBytes...,
|
||||
),
|
||||
); chk.E(err) {
|
||||
return
|
||||
}
|
||||
secret = btcec3.GenerateSharedSecret(s.BTCECSec, pub)
|
||||
return
|
||||
}
|
||||
|
||||
// Keygen implements a key generator. Used for such things as vanity npub mining.
|
||||
type Keygen struct {
|
||||
Signer
|
||||
}
|
||||
|
||||
// Generate a new key pair. If the result is suitable, the embedded Signer can have its contents
|
||||
// extracted.
|
||||
func (k *Keygen) Generate() (pubBytes []byte, err error) {
|
||||
if k.Signer.SecretKey, err = btcec3.NewSecretKey(); chk.E(err) {
|
||||
return
|
||||
}
|
||||
k.Signer.PublicKey = k.SecretKey.PubKey()
|
||||
k.Signer.pkb = schnorr.SerializePubKey(k.Signer.PublicKey)
|
||||
pubBytes = k.Signer.pkb
|
||||
return
|
||||
}
|
||||
|
||||
// KeyPairBytes returns the raw bytes of the embedded Signer.
|
||||
func (k *Keygen) KeyPairBytes() (secBytes, cmprPubBytes []byte) {
|
||||
return k.Signer.SecretKey.Serialize(), k.Signer.PublicKey.SerializeCompressed()
|
||||
}
|
||||
195
pkg/crypto/p256k/btcec/btcec_test.go
Normal file
195
pkg/crypto/p256k/btcec/btcec_test.go
Normal file
@@ -0,0 +1,195 @@
|
||||
//go:build !cgo
|
||||
|
||||
package btcec_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"next.orly.dev/pkg/utils"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
"lol.mleku.dev/log"
|
||||
"next.orly.dev/pkg/crypto/p256k/btcec"
|
||||
)
|
||||
|
||||
func TestSigner_Generate(t *testing.T) {
|
||||
for _ = range 100 {
|
||||
var err error
|
||||
signer := &btcec.Signer{}
|
||||
var skb []byte
|
||||
if err = signer.Generate(); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
skb = signer.Sec()
|
||||
if err = signer.InitSec(skb); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// func TestBTCECSignerVerify(t *testing.T) {
|
||||
// evs := make([]*event.E, 0, 10000)
|
||||
// scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
// buf := make([]byte, 1_000_000)
|
||||
// scanner.Buffer(buf, len(buf))
|
||||
// var err error
|
||||
//
|
||||
// // Create both btcec and p256k signers
|
||||
// btcecSigner := &btcec.Signer{}
|
||||
// p256kSigner := &p256k.Signer{}
|
||||
//
|
||||
// for scanner.Scan() {
|
||||
// var valid bool
|
||||
// b := scanner.Bytes()
|
||||
// ev := event.New()
|
||||
// if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
// t.Errorf("failed to marshal\n%s", b)
|
||||
// } else {
|
||||
// // We know ev.Verify() works, so we'll use it as a reference
|
||||
// if valid, err = ev.Verify(); chk.E(err) || !valid {
|
||||
// t.Errorf("invalid signature\n%s", b)
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Get the ID from the event
|
||||
// storedID := ev.ID
|
||||
// calculatedID := ev.GetIDBytes()
|
||||
//
|
||||
// // Check if the stored ID matches the calculated ID
|
||||
// if !utils.FastEqual(storedID, calculatedID) {
|
||||
// log.D.Ln("Event ID mismatch: stored ID doesn't match calculated ID")
|
||||
// // Use the calculated ID for verification as ev.Verify() would do
|
||||
// ev.ID = calculatedID
|
||||
// }
|
||||
//
|
||||
// if len(ev.ID) != sha256.Size {
|
||||
// t.Errorf("id should be 32 bytes, got %d", len(ev.ID))
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// // Initialize both signers with the same public key
|
||||
// if err = btcecSigner.InitPub(ev.Pubkey); chk.E(err) {
|
||||
// t.Errorf("failed to init btcec pub key: %s\n%0x", err, b)
|
||||
// }
|
||||
// if err = p256kSigner.InitPub(ev.Pubkey); chk.E(err) {
|
||||
// t.Errorf("failed to init p256k pub key: %s\n%0x", err, b)
|
||||
// }
|
||||
//
|
||||
// // First try to verify with btcec.Signer
|
||||
// if valid, err = btcecSigner.Verify(ev.ID, ev.Sig); err == nil && valid {
|
||||
// // If btcec.Signer verification succeeds, great!
|
||||
// log.D.Ln("btcec.Signer verification succeeded")
|
||||
// } else {
|
||||
// // If btcec.Signer verification fails, try with p256k.Signer
|
||||
// // Use chk.T(err) like ev.Verify() does
|
||||
// if valid, err = p256kSigner.Verify(ev.ID, ev.Sig); chk.T(err) {
|
||||
// // If there's an error, log it but don't fail the test
|
||||
// log.D.Ln("p256k.Signer verification error:", err)
|
||||
// } else if !valid {
|
||||
// // Only fail the test if both verifications fail
|
||||
// t.Errorf(
|
||||
// "invalid signature for pub %0x %0x %0x", ev.Pubkey, ev.ID,
|
||||
// ev.Sig,
|
||||
// )
|
||||
// } else {
|
||||
// log.D.Ln("p256k.Signer verification succeeded where btcec.Signer failed")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// evs = append(evs, ev)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestBTCECSignerSign(t *testing.T) {
|
||||
// evs := make([]*event.E, 0, 10000)
|
||||
// scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
// buf := make([]byte, 1_000_000)
|
||||
// scanner.Buffer(buf, len(buf))
|
||||
// var err error
|
||||
// signer := &btcec.Signer{}
|
||||
// var skb []byte
|
||||
// if err = signer.Generate(); chk.E(err) {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// skb = signer.Sec()
|
||||
// if err = signer.InitSec(skb); chk.E(err) {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// verifier := &btcec.Signer{}
|
||||
// pkb := signer.Pub()
|
||||
// if err = verifier.InitPub(pkb); chk.E(err) {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// counter := 0
|
||||
// for scanner.Scan() {
|
||||
// counter++
|
||||
// if counter > 1000 {
|
||||
// break
|
||||
// }
|
||||
// b := scanner.Bytes()
|
||||
// ev := event.New()
|
||||
// if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
// t.Errorf("failed to marshal\n%s", b)
|
||||
// }
|
||||
// evs = append(evs, ev)
|
||||
// }
|
||||
// var valid bool
|
||||
// sig := make([]byte, schnorr.SignatureSize)
|
||||
// for _, ev := range evs {
|
||||
// ev.Pubkey = pkb
|
||||
// id := ev.GetIDBytes()
|
||||
// if sig, err = signer.Sign(id); chk.E(err) {
|
||||
// t.Errorf("failed to sign: %s\n%0x", err, id)
|
||||
// }
|
||||
// if valid, err = verifier.Verify(id, sig); chk.E(err) {
|
||||
// t.Errorf("failed to verify: %s\n%0x", err, id)
|
||||
// }
|
||||
// if !valid {
|
||||
// t.Errorf("invalid signature")
|
||||
// }
|
||||
// }
|
||||
// signer.Zero()
|
||||
// }
|
||||
|
||||
func TestBTCECECDH(t *testing.T) {
|
||||
n := time.Now()
|
||||
var err error
|
||||
var counter int
|
||||
const total = 50
|
||||
for _ = range total {
|
||||
s1 := new(btcec.Signer)
|
||||
if err = s1.Generate(); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s2 := new(btcec.Signer)
|
||||
if err = s2.Generate(); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _ = range total {
|
||||
var secret1, secret2 []byte
|
||||
if secret1, err = s1.ECDH(s2.Pub()); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret2, err = s2.ECDH(s1.Pub()); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !utils.FastEqual(secret1, secret2) {
|
||||
counter++
|
||||
t.Errorf(
|
||||
"ECDH generation failed to work in both directions, %x %x",
|
||||
secret1,
|
||||
secret2,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
a := time.Now()
|
||||
duration := a.Sub(n)
|
||||
log.I.Ln(
|
||||
"errors", counter, "total", total, "time", duration, "time/op",
|
||||
int(duration/total),
|
||||
"ops/sec", int(time.Second)/int(duration/total),
|
||||
)
|
||||
}
|
||||
41
pkg/crypto/p256k/btcec/helpers-btcec.go
Normal file
41
pkg/crypto/p256k/btcec/helpers-btcec.go
Normal file
@@ -0,0 +1,41 @@
|
||||
//go:build !cgo
|
||||
|
||||
package btcec
|
||||
|
||||
import (
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/encoders/hex"
|
||||
"next.orly.dev/pkg/interfaces/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 = &Signer{}
|
||||
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 = &Signer{}
|
||||
if err = sign.InitPub(pk); chk.E(err) {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func HexToBin(hexStr string) (b []byte, err error) {
|
||||
b = make([]byte, len(hexStr)/2)
|
||||
if _, err = hex.DecBytes(b, []byte(hexStr)); chk.E(err) {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
6
pkg/crypto/p256k/doc.go
Normal file
6
pkg/crypto/p256k/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Package p256k is a signer interface that (by default) uses the
|
||||
// bitcoin/libsecp256k1 library for fast signature creation and verification of
|
||||
// the BIP-340 nostr X-only signatures and public keys, and ECDH.
|
||||
//
|
||||
// Currently the ECDH is only implemented with the btcec library.
|
||||
package p256k
|
||||
40
pkg/crypto/p256k/helpers.go
Normal file
40
pkg/crypto/p256k/helpers.go
Normal file
@@ -0,0 +1,40 @@
|
||||
//go:build cgo
|
||||
|
||||
package p256k
|
||||
|
||||
import (
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/encoders/hex"
|
||||
"next.orly.dev/pkg/interfaces/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 = &Signer{}
|
||||
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 = &Signer{}
|
||||
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
|
||||
}
|
||||
140
pkg/crypto/p256k/p256k.go
Normal file
140
pkg/crypto/p256k/p256k.go
Normal file
@@ -0,0 +1,140 @@
|
||||
//go:build cgo
|
||||
|
||||
package p256k
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"lol.mleku.dev/chk"
|
||||
"lol.mleku.dev/errorf"
|
||||
"lol.mleku.dev/log"
|
||||
"next.orly.dev/pkg/crypto/ec"
|
||||
"next.orly.dev/pkg/crypto/ec/secp256k1"
|
||||
realy "next.orly.dev/pkg/interfaces/signer"
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.T.Ln("using bitcoin/secp256k1 signature library")
|
||||
}
|
||||
|
||||
// Signer implements the signer.I interface.
|
||||
//
|
||||
// Either the Sec or Pub must be populated, the former is for generating
|
||||
// signatures, the latter is for verifying them.
|
||||
//
|
||||
// When using this library only for verification, a constructor that converts
|
||||
// from bytes to PubKey is needed prior to calling Verify.
|
||||
type Signer struct {
|
||||
// SecretKey is the secret key.
|
||||
SecretKey *SecKey
|
||||
// PublicKey is the public key.
|
||||
PublicKey *PubKey
|
||||
// BTCECSec is needed for ECDH as currently the CGO bindings don't include it
|
||||
BTCECSec *btcec.SecretKey
|
||||
skb, pkb []byte
|
||||
}
|
||||
|
||||
var _ realy.I = &Signer{}
|
||||
|
||||
// Generate a new Signer key pair using the CGO bindings to libsecp256k1
|
||||
func (s *Signer) Generate() (err error) {
|
||||
var cs *Sec
|
||||
var cx *XPublicKey
|
||||
if s.skb, s.pkb, cs, cx, err = Generate(); chk.E(err) {
|
||||
return
|
||||
}
|
||||
s.SecretKey = &cs.Key
|
||||
s.PublicKey = cx.Key
|
||||
s.BTCECSec, _ = btcec.PrivKeyFromBytes(s.skb)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Signer) InitSec(skb []byte) (err error) {
|
||||
var cs *Sec
|
||||
var cx *XPublicKey
|
||||
// var cp *PublicKey
|
||||
if s.pkb, cs, cx, err = FromSecretBytes(skb); chk.E(err) {
|
||||
if err.Error() != "provided secret generates a public key with odd Y coordinate, fixed version returned" {
|
||||
log.E.Ln(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
s.skb = skb
|
||||
s.SecretKey = &cs.Key
|
||||
s.PublicKey = cx.Key
|
||||
// s.ECPublicKey = cp.Key
|
||||
// needed for ecdh
|
||||
s.BTCECSec, _ = btcec.PrivKeyFromBytes(s.skb)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Signer) InitPub(pub []byte) (err error) {
|
||||
var up *Pub
|
||||
if up, err = PubFromBytes(pub); chk.E(err) {
|
||||
return
|
||||
}
|
||||
s.PublicKey = &up.Key
|
||||
s.pkb = up.PubB()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Signer) Sec() (b []byte) {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return s.skb
|
||||
}
|
||||
func (s *Signer) Pub() (b []byte) {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return s.pkb
|
||||
}
|
||||
|
||||
// func (s *Signer) ECPub() (b []byte) { return s.pkb }
|
||||
|
||||
func (s *Signer) Sign(msg []byte) (sig []byte, err error) {
|
||||
if s.SecretKey == nil {
|
||||
err = errorf.E("p256k: I secret not initialized")
|
||||
return
|
||||
}
|
||||
u := ToUchar(msg)
|
||||
if sig, err = Sign(u, s.SecretKey); chk.E(err) {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Signer) Verify(msg, sig []byte) (valid bool, err error) {
|
||||
if s.PublicKey == nil {
|
||||
err = errorf.E("p256k: Pubkey not initialized")
|
||||
return
|
||||
}
|
||||
var uMsg, uSig *Uchar
|
||||
if uMsg, err = Msg(msg); chk.E(err) {
|
||||
return
|
||||
}
|
||||
if uSig, err = Sig(sig); chk.E(err) {
|
||||
return
|
||||
}
|
||||
valid = Verify(uMsg, uSig, s.PublicKey)
|
||||
if !valid {
|
||||
err = errorf.E("p256k: invalid signature")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Signer) ECDH(pubkeyBytes []byte) (secret []byte, err error) {
|
||||
var pub *secp256k1.PublicKey
|
||||
if pub, err = secp256k1.ParsePubKey(
|
||||
append(
|
||||
[]byte{0x02},
|
||||
pubkeyBytes...,
|
||||
),
|
||||
); chk.E(err) {
|
||||
return
|
||||
}
|
||||
secret = btcec.GenerateSharedSecret(s.BTCECSec, pub)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Signer) Zero() { Zero(s.SecretKey) }
|
||||
162
pkg/crypto/p256k/p256k_test.go
Normal file
162
pkg/crypto/p256k/p256k_test.go
Normal file
@@ -0,0 +1,162 @@
|
||||
//go:build cgo
|
||||
|
||||
package p256k_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"next.orly.dev/pkg/utils"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
"lol.mleku.dev/log"
|
||||
"next.orly.dev/pkg/crypto/p256k"
|
||||
realy "next.orly.dev/pkg/interfaces/signer"
|
||||
)
|
||||
|
||||
func TestSigner_Generate(t *testing.T) {
|
||||
for _ = range 10000 {
|
||||
var err error
|
||||
signer := &p256k.Signer{}
|
||||
var skb []byte
|
||||
if err = signer.Generate(); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
skb = signer.Sec()
|
||||
if err = signer.InitSec(skb); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// func TestSignerVerify(t *testing.T) {
|
||||
// // evs := make([]*event.E, 0, 10000)
|
||||
// scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
// buf := make([]byte, 1_000_000)
|
||||
// scanner.Buffer(buf, len(buf))
|
||||
// var err error
|
||||
// signer := &p256k.Signer{}
|
||||
// for scanner.Scan() {
|
||||
// var valid bool
|
||||
// b := scanner.Bytes()
|
||||
// bc := make([]byte, 0, len(b))
|
||||
// bc = append(bc, b...)
|
||||
// ev := event.New()
|
||||
// if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
// t.Errorf("failed to marshal\n%s", b)
|
||||
// } else {
|
||||
// if valid, err = ev.Verify(); chk.T(err) || !valid {
|
||||
// t.Errorf("invalid signature\n%s", bc)
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// id := ev.GetIDBytes()
|
||||
// if len(id) != sha256.Size {
|
||||
// t.Errorf("id should be 32 bytes, got %d", len(id))
|
||||
// continue
|
||||
// }
|
||||
// if err = signer.InitPub(ev.Pubkey); chk.T(err) {
|
||||
// t.Errorf("failed to init pub key: %s\n%0x", err, ev.Pubkey)
|
||||
// continue
|
||||
// }
|
||||
// if valid, err = signer.Verify(id, ev.Sig); chk.E(err) {
|
||||
// t.Errorf("failed to verify: %s\n%0x", err, ev.ID)
|
||||
// continue
|
||||
// }
|
||||
// if !valid {
|
||||
// t.Errorf(
|
||||
// "invalid signature for\npub %0x\neid %0x\nsig %0x\n%s",
|
||||
// ev.Pubkey, id, ev.Sig, bc,
|
||||
// )
|
||||
// continue
|
||||
// }
|
||||
// // fmt.Printf("%s\n", bc)
|
||||
// // evs = append(evs, ev)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestSignerSign(t *testing.T) {
|
||||
// evs := make([]*event.E, 0, 10000)
|
||||
// scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
// buf := make([]byte, 1_000_000)
|
||||
// scanner.Buffer(buf, len(buf))
|
||||
// var err error
|
||||
// signer := &p256k.Signer{}
|
||||
// var skb, pkb []byte
|
||||
// if skb, pkb, _, _, err = p256k.Generate(); chk.E(err) {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// log.I.S(skb, pkb)
|
||||
// if err = signer.InitSec(skb); chk.E(err) {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// verifier := &p256k.Signer{}
|
||||
// if err = verifier.InitPub(pkb); chk.E(err) {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// for scanner.Scan() {
|
||||
// b := scanner.Bytes()
|
||||
// ev := event.New()
|
||||
// if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
// t.Errorf("failed to marshal\n%s", b)
|
||||
// }
|
||||
// evs = append(evs, ev)
|
||||
// }
|
||||
// var valid bool
|
||||
// sig := make([]byte, schnorr.SignatureSize)
|
||||
// for _, ev := range evs {
|
||||
// ev.Pubkey = pkb
|
||||
// id := ev.GetIDBytes()
|
||||
// if sig, err = signer.Sign(id); chk.E(err) {
|
||||
// t.Errorf("failed to sign: %s\n%0x", err, id)
|
||||
// }
|
||||
// if valid, err = verifier.Verify(id, sig); chk.E(err) {
|
||||
// t.Errorf("failed to verify: %s\n%0x", err, id)
|
||||
// }
|
||||
// if !valid {
|
||||
// t.Errorf("invalid signature")
|
||||
// }
|
||||
// }
|
||||
// signer.Zero()
|
||||
// }
|
||||
|
||||
func TestECDH(t *testing.T) {
|
||||
n := time.Now()
|
||||
var err error
|
||||
var s1, s2 realy.I
|
||||
var counter int
|
||||
const total = 100
|
||||
for _ = range total {
|
||||
s1, s2 = &p256k.Signer{}, &p256k.Signer{}
|
||||
if err = s1.Generate(); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _ = range total {
|
||||
if err = s2.Generate(); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var secret1, secret2 []byte
|
||||
if secret1, err = s1.ECDH(s2.Pub()); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret2, err = s2.ECDH(s1.Pub()); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !utils.FastEqual(secret1, secret2) {
|
||||
counter++
|
||||
t.Errorf(
|
||||
"ECDH generation failed to work in both directions, %x %x",
|
||||
secret1,
|
||||
secret2,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
a := time.Now()
|
||||
duration := a.Sub(n)
|
||||
log.I.Ln(
|
||||
"errors", counter, "total", total*total, "time", duration, "time/op",
|
||||
duration/total/total, "ops/sec",
|
||||
float64(time.Second)/float64(duration/total/total),
|
||||
)
|
||||
}
|
||||
426
pkg/crypto/p256k/secp256k1.go
Normal file
426
pkg/crypto/p256k/secp256k1.go
Normal file
@@ -0,0 +1,426 @@
|
||||
//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
|
||||
}
|
||||
76
pkg/crypto/p256k/secp256k1_test.go
Normal file
76
pkg/crypto/p256k/secp256k1_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
//go:build cgo
|
||||
|
||||
package p256k_test
|
||||
|
||||
// func TestVerify(t *testing.T) {
|
||||
// evs := make([]*event.E, 0, 10000)
|
||||
// scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
// buf := make([]byte, 1_000_000)
|
||||
// scanner.Buffer(buf, len(buf))
|
||||
// var err error
|
||||
// for scanner.Scan() {
|
||||
// var valid bool
|
||||
// b := scanner.Bytes()
|
||||
// ev := event.New()
|
||||
// if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
// t.Errorf("failed to marshal\n%s", b)
|
||||
// } else {
|
||||
// if valid, err = ev.Verify(); chk.E(err) || !valid {
|
||||
// t.Errorf("btcec: invalid signature\n%s", b)
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// id := ev.GetIDBytes()
|
||||
// if len(id) != sha256.Size {
|
||||
// t.Errorf("id should be 32 bytes, got %d", len(id))
|
||||
// continue
|
||||
// }
|
||||
// if err = p256k.VerifyFromBytes(id, ev.Sig, ev.Pubkey); chk.E(err) {
|
||||
// t.Error(err)
|
||||
// continue
|
||||
// }
|
||||
// evs = append(evs, ev)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestSign(t *testing.T) {
|
||||
// evs := make([]*event.E, 0, 10000)
|
||||
// scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
|
||||
// buf := make([]byte, 1_000_000)
|
||||
// scanner.Buffer(buf, len(buf))
|
||||
// var err error
|
||||
// var sec1 *p256k.Sec
|
||||
// var pub1 *p256k.XPublicKey
|
||||
// var pb []byte
|
||||
// if _, pb, sec1, pub1, err = p256k.Generate(); chk.E(err) {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// for scanner.Scan() {
|
||||
// b := scanner.Bytes()
|
||||
// ev := event.New()
|
||||
// if _, err = ev.Unmarshal(b); chk.E(err) {
|
||||
// t.Errorf("failed to marshal\n%s", b)
|
||||
// }
|
||||
// evs = append(evs, ev)
|
||||
// }
|
||||
// sig := make([]byte, schnorr.SignatureSize)
|
||||
// for _, ev := range evs {
|
||||
// ev.Pubkey = pb
|
||||
// var uid *p256k.Uchar
|
||||
// if uid, err = p256k.Msg(ev.GetIDBytes()); chk.E(err) {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if sig, err = p256k.Sign(uid, sec1.Sec()); chk.E(err) {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// ev.Sig = sig
|
||||
// var usig *p256k.Uchar
|
||||
// if usig, err = p256k.Sig(sig); chk.E(err) {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if !p256k.Verify(uid, usig, pub1.Key) {
|
||||
// t.Errorf("invalid signature")
|
||||
// }
|
||||
// }
|
||||
// p256k.Zero(&sec1.Key)
|
||||
// }
|
||||
Reference in New Issue
Block a user