package nostr import ( "encoding/hex" "strings" "testing" ) func TestGenerateKey(t *testing.T) { key1, err := GenerateKey() if err != nil { t.Fatalf("GenerateKey() error = %v", err) } if !key1.CanSign() { t.Error("Generated key should be able to sign") } // Private key should be 64 hex characters if len(key1.Private()) != 64 { t.Errorf("Private() length = %d, want 64", len(key1.Private())) } // Public key should be 64 hex characters if len(key1.Public()) != 64 { t.Errorf("Public() length = %d, want 64", len(key1.Public())) } // Should be valid hex if _, err := hex.DecodeString(key1.Private()); err != nil { t.Errorf("Private() is not valid hex: %v", err) } if _, err := hex.DecodeString(key1.Public()); err != nil { t.Errorf("Public() is not valid hex: %v", err) } // Keys should be unique key2, err := GenerateKey() if err != nil { t.Fatalf("GenerateKey() second call error = %v", err) } if key1.Private() == key2.Private() { t.Error("GenerateKey() returned same private key twice") } } func TestKeyNpubNsec(t *testing.T) { key, err := GenerateKey() if err != nil { t.Fatalf("GenerateKey() error = %v", err) } npub := key.Npub() nsec := key.Nsec() // Check prefixes if !strings.HasPrefix(npub, "npub1") { t.Errorf("Npub() = %s, want prefix 'npub1'", npub) } if !strings.HasPrefix(nsec, "nsec1") { t.Errorf("Nsec() = %s, want prefix 'nsec1'", nsec) } // Should be able to parse them back keyFromNsec, err := ParseKey(nsec) if err != nil { t.Fatalf("ParseKey(nsec) error = %v", err) } if keyFromNsec.Private() != key.Private() { t.Error("ParseKey(nsec) did not restore original private key") } keyFromNpub, err := ParsePublicKey(npub) if err != nil { t.Fatalf("ParsePublicKey(npub) error = %v", err) } if keyFromNpub.Public() != key.Public() { t.Error("ParsePublicKey(npub) did not restore original public key") } } func TestParseKey(t *testing.T) { // Known test vector hexKey := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" key, err := ParseKey(hexKey) if err != nil { t.Fatalf("ParseKey(hex) error = %v", err) } if !key.CanSign() { t.Error("ParseKey should return key that can sign") } if key.Private() != hexKey { t.Errorf("Private() = %s, want %s", key.Private(), hexKey) } // Parse the nsec back nsec := key.Nsec() key2, err := ParseKey(nsec) if err != nil { t.Fatalf("ParseKey(nsec) error = %v", err) } if key2.Private() != hexKey { t.Error("Round-trip through nsec failed") } } func TestParseKeyErrors(t *testing.T) { tests := []struct { name string key string }{ {"invalid hex", "not-hex"}, {"too short", "0123456789abcdef"}, {"too long", "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef00"}, {"invalid nsec", "nsec1invalid"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := ParseKey(tt.key) if err == nil { t.Error("ParseKey() expected error, got nil") } }) } } func TestParsePublicKey(t *testing.T) { // Generate a key and extract public fullKey, _ := GenerateKey() pubHex := fullKey.Public() // Parse public key from hex key, err := ParsePublicKey(pubHex) if err != nil { t.Fatalf("ParsePublicKey(hex) error = %v", err) } if key.CanSign() { t.Error("ParsePublicKey should return key that cannot sign") } if key.Public() != pubHex { t.Errorf("Public() = %s, want %s", key.Public(), pubHex) } if key.Private() != "" { t.Error("Private() should return empty string for public-only key") } if key.Nsec() != "" { t.Error("Nsec() should return empty string for public-only key") } // Parse from npub npub := fullKey.Npub() key2, err := ParsePublicKey(npub) if err != nil { t.Fatalf("ParsePublicKey(npub) error = %v", err) } if key2.Public() != pubHex { t.Error("ParsePublicKey(npub) did not restore correct public key") } } func TestParsePublicKeyErrors(t *testing.T) { tests := []struct { name string key string }{ {"invalid hex", "not-hex"}, {"too short", "0123456789abcdef"}, {"invalid npub", "npub1invalid"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := ParsePublicKey(tt.key) if err == nil { t.Error("ParsePublicKey() expected error, got nil") } }) } } func TestKeySign(t *testing.T) { key, err := GenerateKey() if err != nil { t.Fatalf("GenerateKey() error = %v", err) } event := &Event{ CreatedAt: 1704067200, Kind: 1, Tags: Tags{}, Content: "Test message", } if err := key.Sign(event); err != nil { t.Fatalf("Sign() error = %v", err) } // Check that all fields are set if event.PubKey == "" { t.Error("Sign() did not set PubKey") } if event.ID == "" { t.Error("Sign() did not set ID") } if event.Sig == "" { t.Error("Sign() did not set Sig") } // PubKey should match if event.PubKey != key.Public() { t.Errorf("PubKey = %s, want %s", event.PubKey, key.Public()) } // Signature should be 128 hex characters (64 bytes) if len(event.Sig) != 128 { t.Errorf("Signature length = %d, want 128", len(event.Sig)) } } func TestKeySignPublicOnlyError(t *testing.T) { fullKey, _ := GenerateKey() pubOnlyKey, _ := ParsePublicKey(fullKey.Public()) event := &Event{ CreatedAt: 1704067200, Kind: 1, Tags: Tags{}, Content: "Test", } err := pubOnlyKey.Sign(event) if err == nil { t.Error("Sign() with public-only key should return error") } } func TestEventVerify(t *testing.T) { key, err := GenerateKey() if err != nil { t.Fatalf("GenerateKey() error = %v", err) } event := &Event{ CreatedAt: 1704067200, Kind: 1, Tags: Tags{{"test", "value"}}, Content: "Test message for verification", } if err := key.Sign(event); err != nil { t.Fatalf("Sign() error = %v", err) } if !event.Verify() { t.Error("Verify() returned false for valid signature") } } func TestEventVerifyInvalid(t *testing.T) { key, err := GenerateKey() if err != nil { t.Fatalf("GenerateKey() error = %v", err) } event := &Event{ CreatedAt: 1704067200, Kind: 1, Tags: Tags{}, Content: "Test message", } if err := key.Sign(event); err != nil { t.Fatalf("Sign() error = %v", err) } // Corrupt the content (ID becomes invalid) event.Content = "Modified content" if event.Verify() { t.Error("Verify() returned true for modified content") } // Restore content but corrupt signature event.Content = "Test message" event.SetID() event.Sig = "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" if event.Verify() { t.Error("Verify() returned true for invalid signature") } } func TestSignAndVerifyRoundTrip(t *testing.T) { // Generate key key, err := GenerateKey() if err != nil { t.Fatalf("GenerateKey() error = %v", err) } // Create and sign event event := &Event{ CreatedAt: 1704067200, Kind: KindTextNote, Tags: Tags{{"t", "test"}}, Content: "Integration test message", } if err := key.Sign(event); err != nil { t.Fatalf("Sign() error = %v", err) } // Verify public key matches if event.PubKey != key.Public() { t.Errorf("Signed event PubKey = %s, want %s", event.PubKey, key.Public()) } // Verify the signature if !event.Verify() { t.Error("Verify() failed for freshly signed event") } // Check ID is correct if !event.CheckID() { t.Error("CheckID() failed for freshly signed event") } }