summaryrefslogtreecommitdiffstats
path: root/keys_test.go
blob: 6c3dd3d8ac920ef81e37bf7095e3b43d59da4f80 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
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")
	}
}