This commit introduces the implementation of Schnorr signatures following BIP-340, including the `SchnorrSign` and `SchnorrVerify` functions. It also adds comprehensive tests to validate the signing and verification processes, ensuring correct functionality with both standard and auxiliary randomness. The tests cover various scenarios, including signature generation, verification, and edge cases, enhancing the robustness of the secp256k1 implementation. Additionally, new files for Schnorr signature operations and tests have been created, contributing to the overall cryptographic capabilities of the library.
267 lines
5.6 KiB
Go
267 lines
5.6 KiB
Go
package signer
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"p256k1.mleku.dev"
|
|
)
|
|
|
|
func TestP256K1Signer_Generate(t *testing.T) {
|
|
s := NewP256K1Signer()
|
|
if err := s.Generate(); err != nil {
|
|
t.Fatalf("Generate failed: %v", err)
|
|
}
|
|
|
|
// Check that we have a secret key
|
|
sec := s.Sec()
|
|
if sec == nil || len(sec) != 32 {
|
|
t.Error("secret key should be 32 bytes")
|
|
}
|
|
|
|
// Check that we have a public key
|
|
pub := s.Pub()
|
|
if pub == nil || len(pub) != 32 {
|
|
t.Error("public key should be 32 bytes")
|
|
}
|
|
|
|
// Check that we can sign
|
|
msg := make([]byte, 32)
|
|
sig, err := s.Sign(msg)
|
|
if err != nil {
|
|
t.Fatalf("Sign failed: %v", err)
|
|
}
|
|
if len(sig) != 64 {
|
|
t.Error("signature should be 64 bytes")
|
|
}
|
|
|
|
// Check that we can verify
|
|
valid, err := s.Verify(msg, sig)
|
|
if err != nil {
|
|
t.Fatalf("Verify failed: %v", err)
|
|
}
|
|
if !valid {
|
|
t.Error("signature should be valid")
|
|
}
|
|
|
|
// Test with wrong message
|
|
wrongMsg := make([]byte, 32)
|
|
wrongMsg[0] = 1
|
|
valid, err = s.Verify(wrongMsg, sig)
|
|
if err != nil {
|
|
t.Fatalf("Verify failed: %v", err)
|
|
}
|
|
if valid {
|
|
t.Error("signature should be invalid for wrong message")
|
|
}
|
|
|
|
s.Zero()
|
|
}
|
|
|
|
func TestP256K1Signer_InitSec(t *testing.T) {
|
|
// Generate a secret key
|
|
seckey := make([]byte, 32)
|
|
for i := range seckey {
|
|
seckey[i] = byte(i + 1)
|
|
}
|
|
|
|
s := NewP256K1Signer()
|
|
if err := s.InitSec(seckey); err != nil {
|
|
t.Fatalf("InitSec failed: %v", err)
|
|
}
|
|
|
|
// Check secret key matches
|
|
sec := s.Sec()
|
|
for i := 0; i < 32; i++ {
|
|
if sec[i] != seckey[i] {
|
|
t.Errorf("secret key mismatch at byte %d", i)
|
|
}
|
|
}
|
|
|
|
// Check we can sign
|
|
msg := make([]byte, 32)
|
|
sig, err := s.Sign(msg)
|
|
if err != nil {
|
|
t.Fatalf("Sign failed: %v", err)
|
|
}
|
|
if len(sig) != 64 {
|
|
t.Error("signature should be 64 bytes")
|
|
}
|
|
|
|
s.Zero()
|
|
}
|
|
|
|
func TestP256K1Signer_InitPub(t *testing.T) {
|
|
// Generate a keypair first to get a valid x-only pubkey
|
|
kp, err := p256k1.KeyPairGenerate()
|
|
if err != nil {
|
|
t.Fatalf("KeyPairGenerate failed: %v", err)
|
|
}
|
|
|
|
xonly, err := kp.XOnlyPubkey()
|
|
if err != nil {
|
|
t.Fatalf("XOnlyPubkey failed: %v", err)
|
|
}
|
|
|
|
pubBytes := xonly.Serialize()
|
|
|
|
// Create signer with only public key
|
|
s := NewP256K1Signer()
|
|
if err := s.InitPub(pubBytes[:]); err != nil {
|
|
t.Fatalf("InitPub failed: %v", err)
|
|
}
|
|
|
|
// Check public key matches
|
|
pub := s.Pub()
|
|
for i := 0; i < 32; i++ {
|
|
if pub[i] != pubBytes[i] {
|
|
t.Errorf("public key mismatch at byte %d", i)
|
|
}
|
|
}
|
|
|
|
// Should not be able to sign
|
|
msg := make([]byte, 32)
|
|
_, err = s.Sign(msg)
|
|
if err == nil {
|
|
t.Error("should not be able to sign with only public key")
|
|
}
|
|
|
|
// Should be able to verify (create a signature with the original keypair)
|
|
var sig [64]byte
|
|
if err := p256k1.SchnorrSign(sig[:], msg, kp, nil); err != nil {
|
|
t.Fatalf("SchnorrSign failed: %v", err)
|
|
}
|
|
|
|
valid, err := s.Verify(msg, sig[:])
|
|
if err != nil {
|
|
t.Fatalf("Verify failed: %v", err)
|
|
}
|
|
if !valid {
|
|
t.Error("signature should be valid")
|
|
}
|
|
|
|
s.Zero()
|
|
}
|
|
|
|
func TestP256K1Signer_ECDH(t *testing.T) {
|
|
// Generate two keypairs
|
|
s1 := NewP256K1Signer()
|
|
if err := s1.Generate(); err != nil {
|
|
t.Fatalf("Generate failed: %v", err)
|
|
}
|
|
defer s1.Zero()
|
|
|
|
s2 := NewP256K1Signer()
|
|
if err := s2.Generate(); err != nil {
|
|
t.Fatalf("Generate failed: %v", err)
|
|
}
|
|
defer s2.Zero()
|
|
|
|
// Compute shared secrets
|
|
pub1 := s1.Pub()
|
|
pub2 := s2.Pub()
|
|
|
|
secret1, err := s1.ECDH(pub2)
|
|
if err != nil {
|
|
t.Fatalf("ECDH failed: %v", err)
|
|
}
|
|
|
|
secret2, err := s2.ECDH(pub1)
|
|
if err != nil {
|
|
t.Fatalf("ECDH failed: %v", err)
|
|
}
|
|
|
|
// Shared secrets should match
|
|
if len(secret1) != 32 || len(secret2) != 32 {
|
|
t.Error("shared secrets should be 32 bytes")
|
|
}
|
|
|
|
for i := 0; i < 32; i++ {
|
|
if secret1[i] != secret2[i] {
|
|
t.Errorf("shared secrets mismatch at byte %d", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestP256K1Gen_Generate(t *testing.T) {
|
|
g := NewP256K1Gen()
|
|
|
|
pubBytes, err := g.Generate()
|
|
if err != nil {
|
|
t.Fatalf("Generate failed: %v", err)
|
|
}
|
|
|
|
if len(pubBytes) != 33 {
|
|
t.Errorf("compressed pubkey should be 33 bytes, got %d", len(pubBytes))
|
|
}
|
|
|
|
// Check prefix is 0x02 or 0x03
|
|
if pubBytes[0] != 0x02 && pubBytes[0] != 0x03 {
|
|
t.Errorf("invalid compressed pubkey prefix: 0x%02x", pubBytes[0])
|
|
}
|
|
}
|
|
|
|
func TestP256K1Gen_Negate(t *testing.T) {
|
|
g := NewP256K1Gen()
|
|
|
|
pubBytes1, err := g.Generate()
|
|
if err != nil {
|
|
t.Fatalf("Generate failed: %v", err)
|
|
}
|
|
|
|
// Store the original prefix
|
|
originalPrefix := pubBytes1[0]
|
|
|
|
// Negate and check prefix changes
|
|
g.Negate()
|
|
|
|
// Get compressed pubkey from the keypair (don't generate new one)
|
|
if g.compressedPub == nil {
|
|
t.Fatal("compressedPub should not be nil after Generate")
|
|
}
|
|
|
|
var compressedPub [33]byte
|
|
n := p256k1.ECPubkeySerialize(compressedPub[:], g.compressedPub, p256k1.ECCompressed)
|
|
if n != 33 {
|
|
t.Fatal("failed to serialize compressed pubkey")
|
|
}
|
|
|
|
// Prefixes should be different (02 vs 03)
|
|
if originalPrefix == compressedPub[0] {
|
|
t.Error("Negate should flip the Y coordinate parity")
|
|
}
|
|
|
|
// X coordinates should be the same
|
|
for i := 1; i < 33; i++ {
|
|
if pubBytes1[i] != compressedPub[i] {
|
|
t.Errorf("X coordinate should not change, mismatch at byte %d", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestP256K1Gen_KeyPairBytes(t *testing.T) {
|
|
g := NewP256K1Gen()
|
|
|
|
compressedPub, err := g.Generate()
|
|
if err != nil {
|
|
t.Fatalf("Generate failed: %v", err)
|
|
}
|
|
|
|
secBytes, pubBytes := g.KeyPairBytes()
|
|
|
|
if len(secBytes) != 32 {
|
|
t.Errorf("secret key should be 32 bytes, got %d", len(secBytes))
|
|
}
|
|
|
|
if len(pubBytes) != 32 {
|
|
t.Errorf("x-only pubkey should be 32 bytes, got %d", len(pubBytes))
|
|
}
|
|
|
|
// Verify the pubkey matches the compressed pubkey X coordinate
|
|
// (compressedPub[1:] is the X coordinate)
|
|
for i := 0; i < 32; i++ {
|
|
if pubBytes[i] != compressedPub[i+1] {
|
|
t.Errorf("x-only pubkey mismatch at byte %d", i)
|
|
}
|
|
}
|
|
}
|