crypto_test.go raw
1 package nwc_test
2
3 import (
4 "encoding/json"
5 "testing"
6 "time"
7
8 "next.orly.dev/pkg/nostr/crypto/encryption"
9 "next.orly.dev/pkg/nostr/encoders/event"
10 "next.orly.dev/pkg/nostr/encoders/hex"
11 "next.orly.dev/pkg/nostr/encoders/tag"
12 "next.orly.dev/pkg/nostr/interfaces/signer/p8k"
13 "next.orly.dev/pkg/protocol/nwc"
14 "next.orly.dev/pkg/utils"
15 )
16
17 func TestNWCConversationKey(t *testing.T) {
18 secret := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
19 walletPubkey := "816fd7f1d000ae81a3da251c91866fc47f4bcd6ce36921e6d46773c32f1d548b"
20
21 uri := "nostr+walletconnect://" + walletPubkey + "?relay=wss://relay.getalby.com/v1&secret=" + secret
22
23 parts, err := nwc.ParseConnectionURI(uri)
24 if err != nil {
25 t.Fatal(err)
26 }
27
28 // Validate conversation key was generated
29 convKey := parts.GetConversationKey()
30 if len(convKey) == 0 {
31 t.Fatal("conversation key should not be empty")
32 }
33
34 // Validate wallet public key
35 walletKey := parts.GetWalletPublicKey()
36 if len(walletKey) == 0 {
37 t.Fatal("wallet public key should not be empty")
38 }
39
40 expected, err := hex.Dec(walletPubkey)
41 if err != nil {
42 t.Fatal(err)
43 }
44
45 if len(walletKey) != len(expected) {
46 t.Fatal("wallet public key length mismatch")
47 }
48
49 for i := range walletKey {
50 if walletKey[i] != expected[i] {
51 t.Fatal("wallet public key mismatch")
52 }
53 }
54
55 // Test passed
56 }
57
58 func TestNWCEncryptionDecryption(t *testing.T) {
59 secret := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
60 walletPubkey := "816fd7f1d000ae81a3da251c91866fc47f4bcd6ce36921e6d46773c32f1d548b"
61
62 uri := "nostr+walletconnect://" + walletPubkey + "?relay=wss://relay.getalby.com/v1&secret=" + secret
63
64 parts, err := nwc.ParseConnectionURI(uri)
65 if err != nil {
66 t.Fatal(err)
67 }
68
69 convKey := parts.GetConversationKey()
70 testMessage := `{"method":"get_info","params":null}`
71
72 // Test encryption
73 encrypted, err := encryption.Encrypt(convKey, []byte(testMessage), nil)
74 if err != nil {
75 t.Fatalf("encryption failed: %v", err)
76 }
77
78 if len(encrypted) == 0 {
79 t.Fatal("encrypted message should not be empty")
80 }
81
82 // Test decryption
83 decrypted, err := encryption.Decrypt(convKey, encrypted)
84 if err != nil {
85 t.Fatalf("decryption failed: %v", err)
86 }
87
88 if decrypted != testMessage {
89 t.Fatalf(
90 "decrypted message mismatch: got %s, want %s", decrypted,
91 testMessage,
92 )
93 }
94
95 // Test passed
96 }
97
98 func TestNWCEventCreation(t *testing.T) {
99 secretBytes, err := hex.Dec("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
100 if err != nil {
101 t.Fatal(err)
102 }
103
104 clientKey := p8k.MustNew()
105 if err := clientKey.InitSec(secretBytes); err != nil {
106 t.Fatal(err)
107 }
108
109 walletPubkey, err := hex.Dec("816fd7f1d000ae81a3da251c91866fc47f4bcd6ce36921e6d46773c32f1d548b")
110 if err != nil {
111 t.Fatal(err)
112 }
113
114 convKey, err := encryption.GenerateConversationKey(
115 clientKey.Sec(), walletPubkey,
116 )
117 if err != nil {
118 t.Fatal(err)
119 }
120
121 request := map[string]any{"method": "get_info"}
122 reqBytes, err := json.Marshal(request)
123 if err != nil {
124 t.Fatal(err)
125 }
126
127 encrypted, err := encryption.Encrypt(convKey, reqBytes, nil)
128 if err != nil {
129 t.Fatal(err)
130 }
131
132 // Create NWC event
133 ev := &event.E{
134 Content: []byte(encrypted),
135 CreatedAt: time.Now().Unix(),
136 Kind: 23194,
137 Tags: tag.NewS(
138 tag.NewFromAny("encryption", "nip44_v2"),
139 tag.NewFromAny("p", hex.Enc(walletPubkey)),
140 ),
141 }
142
143 if err := ev.Sign(clientKey); err != nil {
144 t.Fatalf("event signing failed: %v", err)
145 }
146
147 // Validate event structure
148 if len(ev.Content) == 0 {
149 t.Fatal("event content should not be empty")
150 }
151
152 if len(ev.ID) == 0 {
153 t.Fatal("event should have ID after signing")
154 }
155
156 if len(ev.Sig) == 0 {
157 t.Fatal("event should have signature after signing")
158 }
159
160 // Validate tags
161 hasEncryption := false
162 hasP := false
163 for i := 0; i < ev.Tags.Len(); i++ {
164 tag := ev.Tags.GetTagElement(i)
165 if tag.Len() >= 2 {
166 if utils.FastEqual(
167 tag.T[0], "encryption",
168 ) && utils.FastEqual(tag.T[1], "nip44_v2") {
169 hasEncryption = true
170 }
171 if utils.FastEqual(
172 tag.T[0], "p",
173 ) && utils.FastEqual(tag.T[1], hex.Enc(walletPubkey)) {
174 hasP = true
175 }
176 }
177 }
178
179 if !hasEncryption {
180 t.Fatal("event missing encryption tag")
181 }
182
183 if !hasP {
184 t.Fatal("event missing p tag")
185 }
186
187 // Test passed
188 }
189