This commit introduces a new benchmark function, `BenchmarkSchnorrVerify`, in `schnorr_test.go` to evaluate the performance of the Schnorr signature verification process. Additionally, it optimizes the `SchnorrVerify` function in `schnorr.go` by implementing a global precomputed context, reducing overhead during verification calls. The changes aim to enhance performance and provide insights into the efficiency of the verification process.
275 lines
5.9 KiB
Go
275 lines
5.9 KiB
Go
package p256k1
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func TestSchnorrSignVerify(t *testing.T) {
|
|
// Generate keypair
|
|
kp, err := KeyPairGenerate()
|
|
if err != nil {
|
|
t.Fatalf("failed to generate keypair: %v", err)
|
|
}
|
|
defer kp.Clear()
|
|
|
|
// Get x-only pubkey
|
|
xonly, err := kp.XOnlyPubkey()
|
|
if err != nil {
|
|
t.Fatalf("failed to get x-only pubkey: %v", err)
|
|
}
|
|
|
|
// Create message
|
|
msg := make([]byte, 32)
|
|
for i := range msg {
|
|
msg[i] = byte(i)
|
|
}
|
|
|
|
// Sign
|
|
var sig [64]byte
|
|
if err := SchnorrSign(sig[:], msg, kp, nil); err != nil {
|
|
t.Fatalf("failed to sign: %v", err)
|
|
}
|
|
|
|
// Verify
|
|
if !SchnorrVerify(sig[:], msg, xonly) {
|
|
t.Error("signature verification failed")
|
|
}
|
|
|
|
// Test with wrong message
|
|
wrongMsg := make([]byte, 32)
|
|
copy(wrongMsg, msg)
|
|
wrongMsg[0] ^= 1
|
|
if SchnorrVerify(sig[:], wrongMsg, xonly) {
|
|
t.Error("signature verification should fail with wrong message")
|
|
}
|
|
}
|
|
|
|
func TestSchnorrSignWithAuxRand(t *testing.T) {
|
|
// Generate keypair
|
|
kp, err := KeyPairGenerate()
|
|
if err != nil {
|
|
t.Fatalf("failed to generate keypair: %v", err)
|
|
}
|
|
defer kp.Clear()
|
|
|
|
// Get x-only pubkey
|
|
xonly, err := kp.XOnlyPubkey()
|
|
if err != nil {
|
|
t.Fatalf("failed to get x-only pubkey: %v", err)
|
|
}
|
|
|
|
// Create message
|
|
msg := make([]byte, 32)
|
|
for i := range msg {
|
|
msg[i] = byte(i)
|
|
}
|
|
|
|
// Auxiliary randomness
|
|
auxRand := make([]byte, 32)
|
|
for i := range auxRand {
|
|
auxRand[i] = byte(i + 100)
|
|
}
|
|
|
|
// Sign
|
|
var sig [64]byte
|
|
if err := SchnorrSign(sig[:], msg, kp, auxRand); err != nil {
|
|
t.Fatalf("failed to sign: %v", err)
|
|
}
|
|
|
|
// Verify
|
|
if !SchnorrVerify(sig[:], msg, xonly) {
|
|
t.Error("signature verification failed")
|
|
}
|
|
}
|
|
|
|
func TestSchnorrVerifyInvalid(t *testing.T) {
|
|
// Generate keypair
|
|
kp, err := KeyPairGenerate()
|
|
if err != nil {
|
|
t.Fatalf("failed to generate keypair: %v", err)
|
|
}
|
|
defer kp.Clear()
|
|
|
|
// Get x-only pubkey
|
|
xonly, err := kp.XOnlyPubkey()
|
|
if err != nil {
|
|
t.Fatalf("failed to get x-only pubkey: %v", err)
|
|
}
|
|
|
|
msg := make([]byte, 32)
|
|
|
|
// Test with invalid signature length
|
|
if SchnorrVerify([]byte{1}, msg, xonly) {
|
|
t.Error("should fail with invalid signature length")
|
|
}
|
|
|
|
// Test with invalid message length
|
|
var sig [64]byte
|
|
if SchnorrVerify(sig[:], []byte{1}, xonly) {
|
|
t.Error("should fail with invalid message length")
|
|
}
|
|
|
|
// Test with nil pubkey
|
|
if SchnorrVerify(sig[:], msg, nil) {
|
|
t.Error("should fail with nil pubkey")
|
|
}
|
|
}
|
|
|
|
func TestNonceFunctionBIP340(t *testing.T) {
|
|
key32 := make([]byte, 32)
|
|
xonlyPk32 := make([]byte, 32)
|
|
msg := []byte("test message")
|
|
auxRand32 := make([]byte, 32)
|
|
|
|
// Initialize test data
|
|
for i := range key32 {
|
|
key32[i] = byte(i)
|
|
}
|
|
for i := range xonlyPk32 {
|
|
xonlyPk32[i] = byte(i + 10)
|
|
}
|
|
for i := range auxRand32 {
|
|
auxRand32[i] = byte(i + 20)
|
|
}
|
|
|
|
// Test with aux random
|
|
var nonce1 [32]byte
|
|
if err := NonceFunctionBIP340(nonce1[:], msg, key32, xonlyPk32, auxRand32); err != nil {
|
|
t.Fatalf("nonce generation failed: %v", err)
|
|
}
|
|
|
|
// Test without aux random
|
|
var nonce2 [32]byte
|
|
if err := NonceFunctionBIP340(nonce2[:], msg, key32, xonlyPk32, nil); err != nil {
|
|
t.Fatalf("nonce generation failed: %v", err)
|
|
}
|
|
|
|
// Nonces should be different
|
|
allSame := true
|
|
for i := 0; i < 32; i++ {
|
|
if nonce1[i] != nonce2[i] {
|
|
allSame = false
|
|
break
|
|
}
|
|
}
|
|
if allSame {
|
|
t.Error("nonces should differ with different aux random")
|
|
}
|
|
}
|
|
|
|
func TestSchnorrMultipleSignatures(t *testing.T) {
|
|
// Test that multiple signatures with same keypair are different when using different aux_rand
|
|
kp, err := KeyPairGenerate()
|
|
if err != nil {
|
|
t.Fatalf("failed to generate keypair: %v", err)
|
|
}
|
|
defer kp.Clear()
|
|
|
|
xonly, err := kp.XOnlyPubkey()
|
|
if err != nil {
|
|
t.Fatalf("failed to get x-only pubkey: %v", err)
|
|
}
|
|
|
|
msg := make([]byte, 32)
|
|
|
|
// Sign without aux_rand (deterministic - should be same)
|
|
var sig1, sig2 [64]byte
|
|
if err := SchnorrSign(sig1[:], msg, kp, nil); err != nil {
|
|
t.Fatalf("failed to sign: %v", err)
|
|
}
|
|
if err := SchnorrSign(sig2[:], msg, kp, nil); err != nil {
|
|
t.Fatalf("failed to sign: %v", err)
|
|
}
|
|
|
|
// Both should verify
|
|
if !SchnorrVerify(sig1[:], msg, xonly) {
|
|
t.Error("signature 1 verification failed")
|
|
}
|
|
if !SchnorrVerify(sig2[:], msg, xonly) {
|
|
t.Error("signature 2 verification failed")
|
|
}
|
|
|
|
// Without aux_rand, signatures should be deterministic (same)
|
|
allSame := true
|
|
for i := 0; i < 64; i++ {
|
|
if sig1[i] != sig2[i] {
|
|
allSame = false
|
|
break
|
|
}
|
|
}
|
|
if !allSame {
|
|
t.Error("without aux_rand, signatures should be deterministic (same)")
|
|
}
|
|
|
|
// Sign with different aux_rand (should be different)
|
|
auxRand1 := make([]byte, 32)
|
|
auxRand2 := make([]byte, 32)
|
|
for i := range auxRand1 {
|
|
auxRand1[i] = byte(i)
|
|
auxRand2[i] = byte(i + 1)
|
|
}
|
|
|
|
if err := SchnorrSign(sig1[:], msg, kp, auxRand1); err != nil {
|
|
t.Fatalf("failed to sign: %v", err)
|
|
}
|
|
if err := SchnorrSign(sig2[:], msg, kp, auxRand2); err != nil {
|
|
t.Fatalf("failed to sign: %v", err)
|
|
}
|
|
|
|
// Both should verify
|
|
if !SchnorrVerify(sig1[:], msg, xonly) {
|
|
t.Error("signature 1 verification failed")
|
|
}
|
|
if !SchnorrVerify(sig2[:], msg, xonly) {
|
|
t.Error("signature 2 verification failed")
|
|
}
|
|
|
|
// With different aux_rand, signatures should differ
|
|
allSame = true
|
|
for i := 0; i < 64; i++ {
|
|
if sig1[i] != sig2[i] {
|
|
allSame = false
|
|
break
|
|
}
|
|
}
|
|
if allSame {
|
|
t.Error("with different aux_rand, signatures should differ")
|
|
}
|
|
}
|
|
|
|
func BenchmarkSchnorrVerify(b *testing.B) {
|
|
// Generate keypair
|
|
kp, err := KeyPairGenerate()
|
|
if err != nil {
|
|
b.Fatalf("failed to generate keypair: %v", err)
|
|
}
|
|
defer kp.Clear()
|
|
|
|
// Get x-only pubkey
|
|
xonly, err := kp.XOnlyPubkey()
|
|
if err != nil {
|
|
b.Fatalf("failed to get x-only pubkey: %v", err)
|
|
}
|
|
|
|
// Create message
|
|
msg := make([]byte, 32)
|
|
for i := range msg {
|
|
msg[i] = byte(i)
|
|
}
|
|
|
|
// Sign
|
|
var sig [64]byte
|
|
if err := SchnorrSign(sig[:], msg, kp, nil); err != nil {
|
|
b.Fatalf("failed to sign: %v", err)
|
|
}
|
|
|
|
// Benchmark verification
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
if !SchnorrVerify(sig[:], msg, xonly) {
|
|
b.Fatal("verification failed")
|
|
}
|
|
}
|
|
}
|