p256k1_signer_test.go raw

   1  //go:build !js && !wasm && !tinygo && !wasm32
   2  
   3  package signer
   4  
   5  import (
   6  	"testing"
   7  
   8  	"next.orly.dev/pkg/p256k1"
   9  )
  10  
  11  func TestP256K1Signer_Generate(t *testing.T) {
  12  	s := NewP256K1Signer()
  13  	if err := s.Generate(); err != nil {
  14  		t.Fatalf("Generate failed: %v", err)
  15  	}
  16  
  17  	// Check that we have a secret key
  18  	sec := s.Sec()
  19  	if sec == nil || len(sec) != 32 {
  20  		t.Error("secret key should be 32 bytes")
  21  	}
  22  
  23  	// Check that we have a public key
  24  	pub := s.Pub()
  25  	if pub == nil || len(pub) != 32 {
  26  		t.Error("public key should be 32 bytes")
  27  	}
  28  
  29  	// Check that we can sign
  30  	msg := make([]byte, 32)
  31  	sig, err := s.Sign(msg)
  32  	if err != nil {
  33  		t.Fatalf("Sign failed: %v", err)
  34  	}
  35  	if len(sig) != 64 {
  36  		t.Error("signature should be 64 bytes")
  37  	}
  38  
  39  	// Check that we can verify
  40  	valid, err := s.Verify(msg, sig)
  41  	if err != nil {
  42  		t.Fatalf("Verify failed: %v", err)
  43  	}
  44  	if !valid {
  45  		t.Error("signature should be valid")
  46  	}
  47  
  48  	// Test with wrong message
  49  	wrongMsg := make([]byte, 32)
  50  	wrongMsg[0] = 1
  51  	valid, err = s.Verify(wrongMsg, sig)
  52  	if err != nil {
  53  		t.Fatalf("Verify failed: %v", err)
  54  	}
  55  	if valid {
  56  		t.Error("signature should be invalid for wrong message")
  57  	}
  58  
  59  	s.Zero()
  60  }
  61  
  62  func TestP256K1Signer_InitSec(t *testing.T) {
  63  	// Generate a secret key
  64  	seckey := make([]byte, 32)
  65  	for i := range seckey {
  66  		seckey[i] = byte(i + 1)
  67  	}
  68  
  69  	s := NewP256K1Signer()
  70  	if err := s.InitSec(seckey); err != nil {
  71  		t.Fatalf("InitSec failed: %v", err)
  72  	}
  73  
  74  	// Check secret key matches
  75  	sec := s.Sec()
  76  	for i := 0; i < 32; i++ {
  77  		if sec[i] != seckey[i] {
  78  			t.Errorf("secret key mismatch at byte %d", i)
  79  		}
  80  	}
  81  
  82  	// Check we can sign
  83  	msg := make([]byte, 32)
  84  	sig, err := s.Sign(msg)
  85  	if err != nil {
  86  		t.Fatalf("Sign failed: %v", err)
  87  	}
  88  	if len(sig) != 64 {
  89  		t.Error("signature should be 64 bytes")
  90  	}
  91  
  92  	s.Zero()
  93  }
  94  
  95  func TestP256K1Signer_InitPub(t *testing.T) {
  96  	// Generate a keypair first to get a valid x-only pubkey
  97  	kp, err := p256k1.KeyPairGenerate()
  98  	if err != nil {
  99  		t.Fatalf("KeyPairGenerate failed: %v", err)
 100  	}
 101  
 102  	xonly, err := kp.XOnlyPubkey()
 103  	if err != nil {
 104  		t.Fatalf("XOnlyPubkey failed: %v", err)
 105  	}
 106  
 107  	pubBytes := xonly.Serialize()
 108  
 109  	// Create signer with only public key
 110  	s := NewP256K1Signer()
 111  	if err := s.InitPub(pubBytes[:]); err != nil {
 112  		t.Fatalf("InitPub failed: %v", err)
 113  	}
 114  
 115  	// Check public key matches
 116  	pub := s.Pub()
 117  	for i := 0; i < 32; i++ {
 118  		if pub[i] != pubBytes[i] {
 119  			t.Errorf("public key mismatch at byte %d", i)
 120  		}
 121  	}
 122  
 123  	// Should not be able to sign
 124  	msg := make([]byte, 32)
 125  	_, err = s.Sign(msg)
 126  	if err == nil {
 127  		t.Error("should not be able to sign with only public key")
 128  	}
 129  
 130  	// Should be able to verify (create a signature with the original keypair)
 131  	var sig [64]byte
 132  	if err := p256k1.SchnorrSign(sig[:], msg, kp, nil); err != nil {
 133  		t.Fatalf("SchnorrSign failed: %v", err)
 134  	}
 135  
 136  	valid, err := s.Verify(msg, sig[:])
 137  	if err != nil {
 138  		t.Fatalf("Verify failed: %v", err)
 139  	}
 140  	if !valid {
 141  		t.Error("signature should be valid")
 142  	}
 143  
 144  	s.Zero()
 145  }
 146  
 147  func TestP256K1Signer_ECDH(t *testing.T) {
 148  	// Generate two keypairs
 149  	s1 := NewP256K1Signer()
 150  	if err := s1.Generate(); err != nil {
 151  		t.Fatalf("Generate failed: %v", err)
 152  	}
 153  	defer s1.Zero()
 154  
 155  	s2 := NewP256K1Signer()
 156  	if err := s2.Generate(); err != nil {
 157  		t.Fatalf("Generate failed: %v", err)
 158  	}
 159  	defer s2.Zero()
 160  
 161  	// Compute shared secrets
 162  	pub1 := s1.Pub()
 163  	pub2 := s2.Pub()
 164  
 165  	secret1, err := s1.ECDH(pub2)
 166  	if err != nil {
 167  		t.Fatalf("ECDH failed: %v", err)
 168  	}
 169  
 170  	secret2, err := s2.ECDH(pub1)
 171  	if err != nil {
 172  		t.Fatalf("ECDH failed: %v", err)
 173  	}
 174  
 175  	// Shared secrets should match
 176  	if len(secret1) != 32 || len(secret2) != 32 {
 177  		t.Error("shared secrets should be 32 bytes")
 178  	}
 179  
 180  	for i := 0; i < 32; i++ {
 181  		if secret1[i] != secret2[i] {
 182  			t.Errorf("shared secrets mismatch at byte %d", i)
 183  		}
 184  	}
 185  }
 186  
 187  func TestP256K1Gen_Generate(t *testing.T) {
 188  	g := NewP256K1Gen()
 189  
 190  	pubBytes, err := g.Generate()
 191  	if err != nil {
 192  		t.Fatalf("Generate failed: %v", err)
 193  	}
 194  
 195  	if len(pubBytes) != 33 {
 196  		t.Errorf("compressed pubkey should be 33 bytes, got %d", len(pubBytes))
 197  	}
 198  
 199  	// Check prefix is 0x02 or 0x03
 200  	if pubBytes[0] != 0x02 && pubBytes[0] != 0x03 {
 201  		t.Errorf("invalid compressed pubkey prefix: 0x%02x", pubBytes[0])
 202  	}
 203  }
 204  
 205  func TestP256K1Gen_Negate(t *testing.T) {
 206  	g := NewP256K1Gen()
 207  
 208  	pubBytes1, err := g.Generate()
 209  	if err != nil {
 210  		t.Fatalf("Generate failed: %v", err)
 211  	}
 212  
 213  	// Store the original prefix
 214  	originalPrefix := pubBytes1[0]
 215  
 216  	// Negate and check prefix changes
 217  	g.Negate()
 218  
 219  	// Get compressed pubkey from the keypair (don't generate new one)
 220  	if g.compressedPub == nil {
 221  		t.Fatal("compressedPub should not be nil after Generate")
 222  	}
 223  
 224  	var compressedPub [33]byte
 225  	n := p256k1.ECPubkeySerialize(compressedPub[:], g.compressedPub, p256k1.ECCompressed)
 226  	if n != 33 {
 227  		t.Fatal("failed to serialize compressed pubkey")
 228  	}
 229  
 230  	// Prefixes should be different (02 vs 03)
 231  	if originalPrefix == compressedPub[0] {
 232  		t.Error("Negate should flip the Y coordinate parity")
 233  	}
 234  
 235  	// X coordinates should be the same
 236  	for i := 1; i < 33; i++ {
 237  		if pubBytes1[i] != compressedPub[i] {
 238  			t.Errorf("X coordinate should not change, mismatch at byte %d", i)
 239  		}
 240  	}
 241  }
 242  
 243  func TestP256K1Gen_KeyPairBytes(t *testing.T) {
 244  	g := NewP256K1Gen()
 245  
 246  	compressedPub, err := g.Generate()
 247  	if err != nil {
 248  		t.Fatalf("Generate failed: %v", err)
 249  	}
 250  
 251  	secBytes, pubBytes := g.KeyPairBytes()
 252  
 253  	if len(secBytes) != 32 {
 254  		t.Errorf("secret key should be 32 bytes, got %d", len(secBytes))
 255  	}
 256  
 257  	if len(pubBytes) != 32 {
 258  		t.Errorf("x-only pubkey should be 32 bytes, got %d", len(pubBytes))
 259  	}
 260  
 261  	// Verify the pubkey matches the compressed pubkey X coordinate
 262  	// (compressedPub[1:] is the X coordinate)
 263  	for i := 0; i < 32; i++ {
 264  		if pubBytes[i] != compressedPub[i+1] {
 265  			t.Errorf("x-only pubkey mismatch at byte %d", i)
 266  		}
 267  	}
 268  }
 269