aboutsummaryrefslogtreecommitdiffstats
path: root/bech32_test.go
blob: 276d998f9885b5a9f1733235813df1932f786f2b (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
package secp256k1

import (
	"strings"
	"testing"
)

func TestBech32EncodeBasic(t *testing.T) {
	data := []byte{0x00, 0x01, 0x02}
	encoded, err := Bech32Encode("test", data)
	if err != nil {
		t.Fatalf("encoding failed: %v", err)
	}

	// Should start with hrp + "1"
	if !strings.HasPrefix(encoded, "test1") {
		t.Errorf("encoded should start with 'test1', got %s", encoded)
	}
}

func TestBech32RoundTrip(t *testing.T) {
	data := []byte{0xde, 0xad, 0xbe, 0xef}
	encoded, err := Bech32Encode("test", data)
	if err != nil {
		t.Fatalf("encoding failed: %v", err)
	}

	hrp, decoded, err := Bech32Decode(encoded)
	if err != nil {
		t.Fatalf("decoding failed: %v", err)
	}

	if hrp != "test" {
		t.Errorf("hrp mismatch: got %s, want test", hrp)
	}

	if len(decoded) != len(data) {
		t.Fatalf("length mismatch: got %d, want %d", len(decoded), len(data))
	}

	for i := range data {
		if decoded[i] != data[i] {
			t.Errorf("byte %d mismatch: got %x, want %x", i, decoded[i], data[i])
		}
	}
}

func TestBech32DecodeInvalidChecksum(t *testing.T) {
	// Valid encoding, then corrupt it
	data := []byte{0x01, 0x02, 0x03}
	encoded, _ := Bech32Encode("test", data)

	// Corrupt last character
	corrupted := encoded[:len(encoded)-1] + "q"

	_, _, err := Bech32Decode(corrupted)
	if err == nil {
		t.Error("should reject invalid checksum")
	}
}

func TestBech32DecodeInvalidCharacter(t *testing.T) {
	_, _, err := Bech32Decode("test1invalid!")
	if err == nil {
		t.Error("should reject invalid character")
	}
}

func TestNsecEncode(t *testing.T) {
	priv, _ := NewPrivateKeyFromHex("0000000000000000000000000000000000000000000000000000000000000001")
	nsec := priv.Nsec()

	if !strings.HasPrefix(nsec, "nsec1") {
		t.Errorf("nsec should start with 'nsec1', got %s", nsec)
	}
}

func TestNpubEncode(t *testing.T) {
	priv, _ := NewPrivateKeyFromHex("0000000000000000000000000000000000000000000000000000000000000001")
	pub := priv.PublicKey()
	npub := pub.Npub()

	if !strings.HasPrefix(npub, "npub1") {
		t.Errorf("npub should start with 'npub1', got %s", npub)
	}
}

func TestNsecRoundTrip(t *testing.T) {
	priv1, _ := GeneratePrivateKey()
	nsec := priv1.Nsec()

	priv2, err := PrivateKeyFromNsec(nsec)
	if err != nil {
		t.Fatalf("failed to parse nsec: %v", err)
	}

	if priv1.D.Cmp(priv2.D) != 0 {
		t.Error("private key should survive nsec round-trip")
	}
}

func TestNpubRoundTrip(t *testing.T) {
	priv, _ := GeneratePrivateKey()
	pub1 := priv.PublicKey()
	npub := pub1.Npub()

	pub2, err := PublicKeyFromNpub(npub)
	if err != nil {
		t.Fatalf("failed to parse npub: %v", err)
	}

	// X coordinates should match (y might differ in sign)
	if pub1.Point.x.value.Cmp(pub2.Point.x.value) != 0 {
		t.Error("public key x should survive npub round-trip")
	}
}

func TestPrivateKeyFromNsecInvalid(t *testing.T) {
	// Wrong prefix
	_, err := PrivateKeyFromNsec("npub1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xjaeh")
	if err == nil {
		t.Error("should reject npub as nsec")
	}
}

func TestPublicKeyFromNpubInvalid(t *testing.T) {
	// Wrong prefix
	_, err := PublicKeyFromNpub("nsec1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq0dcpx3")
	if err == nil {
		t.Error("should reject nsec as npub")
	}
}

// Test with known private key
func TestKnownNostrKeyPair(t *testing.T) {
	// Private key = 1, should give G as public key
	priv, _ := NewPrivateKeyFromHex("0000000000000000000000000000000000000000000000000000000000000001")

	nsec := priv.Nsec()
	// Verify it starts with nsec and round-trips
	if nsec[:5] != "nsec1" {
		t.Errorf("nsec should start with nsec1, got %s", nsec)
	}

	// Parse it back
	priv2, err := PrivateKeyFromNsec(nsec)
	if err != nil {
		t.Fatalf("failed to parse nsec: %v", err)
	}
	if priv.D.Cmp(priv2.D) != 0 {
		t.Error("nsec round-trip failed")
	}

	// Public key should be G
	pub := priv.PublicKey()
	npub := pub.Npub()

	if npub[:5] != "npub1" {
		t.Errorf("npub should start with npub1, got %s", npub)
	}

	// Parse it back and verify x matches G
	pub2, err := PublicKeyFromNpub(npub)
	if err != nil {
		t.Fatalf("failed to parse npub: %v", err)
	}
	if pub2.Point.x.value.Cmp(Gx) != 0 {
		t.Error("npub should decode to G.x")
	}
}

func TestSignAndVerifyWithNostrKeys(t *testing.T) {
	// Create keys
	priv, _ := GeneratePrivateKey()
	nsec := priv.Nsec()
	npub := priv.PublicKey().Npub()

	// Parse them back
	priv2, _ := PrivateKeyFromNsec(nsec)
	pub2, _ := PublicKeyFromNpub(npub)

	// Sign with parsed private key
	message := []byte("hello nostr")
	sig, err := Sign(priv2, message)
	if err != nil {
		t.Fatalf("signing failed: %v", err)
	}

	// Verify with parsed public key
	if !Verify(pub2, message, sig) {
		t.Error("signature should verify with parsed keys")
	}
}