Add NWC protocol handling and NIP-44 encryption and decryption functions.
This commit is contained in:
188
pkg/protocol/nwc/crypto_test.go
Normal file
188
pkg/protocol/nwc/crypto_test.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package nwc_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"next.orly.dev/pkg/crypto/encryption"
|
||||
"next.orly.dev/pkg/crypto/p256k"
|
||||
"next.orly.dev/pkg/encoders/event"
|
||||
"next.orly.dev/pkg/encoders/hex"
|
||||
"next.orly.dev/pkg/encoders/tag"
|
||||
"next.orly.dev/pkg/protocol/nwc"
|
||||
"next.orly.dev/pkg/utils"
|
||||
)
|
||||
|
||||
func TestNWCConversationKey(t *testing.T) {
|
||||
secret := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
walletPubkey := "816fd7f1d000ae81a3da251c91866fc47f4bcd6ce36921e6d46773c32f1d548b"
|
||||
|
||||
uri := "nostr+walletconnect://" + walletPubkey + "?relay=wss://relay.getalby.com/v1&secret=" + secret
|
||||
|
||||
parts, err := nwc.ParseConnectionURI(uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Validate conversation key was generated
|
||||
convKey := parts.GetConversationKey()
|
||||
if len(convKey) == 0 {
|
||||
t.Fatal("conversation key should not be empty")
|
||||
}
|
||||
|
||||
// Validate wallet public key
|
||||
walletKey := parts.GetWalletPublicKey()
|
||||
if len(walletKey) == 0 {
|
||||
t.Fatal("wallet public key should not be empty")
|
||||
}
|
||||
|
||||
expected, err := hex.Dec(walletPubkey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(walletKey) != len(expected) {
|
||||
t.Fatal("wallet public key length mismatch")
|
||||
}
|
||||
|
||||
for i := range walletKey {
|
||||
if walletKey[i] != expected[i] {
|
||||
t.Fatal("wallet public key mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
// Test passed
|
||||
}
|
||||
|
||||
func TestNWCEncryptionDecryption(t *testing.T) {
|
||||
secret := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
walletPubkey := "816fd7f1d000ae81a3da251c91866fc47f4bcd6ce36921e6d46773c32f1d548b"
|
||||
|
||||
uri := "nostr+walletconnect://" + walletPubkey + "?relay=wss://relay.getalby.com/v1&secret=" + secret
|
||||
|
||||
parts, err := nwc.ParseConnectionURI(uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
convKey := parts.GetConversationKey()
|
||||
testMessage := `{"method":"get_info","params":null}`
|
||||
|
||||
// Test encryption
|
||||
encrypted, err := encryption.Encrypt([]byte(testMessage), convKey)
|
||||
if err != nil {
|
||||
t.Fatalf("encryption failed: %v", err)
|
||||
}
|
||||
|
||||
if len(encrypted) == 0 {
|
||||
t.Fatal("encrypted message should not be empty")
|
||||
}
|
||||
|
||||
// Test decryption
|
||||
decrypted, err := encryption.Decrypt(encrypted, convKey)
|
||||
if err != nil {
|
||||
t.Fatalf("decryption failed: %v", err)
|
||||
}
|
||||
|
||||
if string(decrypted) != testMessage {
|
||||
t.Fatalf(
|
||||
"decrypted message mismatch: got %s, want %s", string(decrypted),
|
||||
testMessage,
|
||||
)
|
||||
}
|
||||
|
||||
// Test passed
|
||||
}
|
||||
|
||||
func TestNWCEventCreation(t *testing.T) {
|
||||
secretBytes, err := hex.Dec("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
clientKey := &p256k.Signer{}
|
||||
if err := clientKey.InitSec(secretBytes); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
walletPubkey, err := hex.Dec("816fd7f1d000ae81a3da251c91866fc47f4bcd6ce36921e6d46773c32f1d548b")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
convKey, err := encryption.GenerateConversationKeyWithSigner(
|
||||
clientKey, walletPubkey,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
request := map[string]any{"method": "get_info"}
|
||||
reqBytes, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
encrypted, err := encryption.Encrypt(reqBytes, convKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create NWC event
|
||||
ev := &event.E{
|
||||
Content: encrypted,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
Kind: 23194,
|
||||
Tags: tag.NewS(
|
||||
tag.NewFromAny("encryption", "nip44_v2"),
|
||||
tag.NewFromAny("p", hex.Enc(walletPubkey)),
|
||||
),
|
||||
}
|
||||
|
||||
if err := ev.Sign(clientKey); err != nil {
|
||||
t.Fatalf("event signing failed: %v", err)
|
||||
}
|
||||
|
||||
// Validate event structure
|
||||
if len(ev.Content) == 0 {
|
||||
t.Fatal("event content should not be empty")
|
||||
}
|
||||
|
||||
if len(ev.ID) == 0 {
|
||||
t.Fatal("event should have ID after signing")
|
||||
}
|
||||
|
||||
if len(ev.Sig) == 0 {
|
||||
t.Fatal("event should have signature after signing")
|
||||
}
|
||||
|
||||
// Validate tags
|
||||
hasEncryption := false
|
||||
hasP := false
|
||||
for i := 0; i < ev.Tags.Len(); i++ {
|
||||
tag := ev.Tags.GetTagElement(i)
|
||||
if tag.Len() >= 2 {
|
||||
if utils.FastEqual(
|
||||
tag.T[0], "encryption",
|
||||
) && utils.FastEqual(tag.T[1], "nip44_v2") {
|
||||
hasEncryption = true
|
||||
}
|
||||
if utils.FastEqual(
|
||||
tag.T[0], "p",
|
||||
) && utils.FastEqual(tag.T[1], hex.Enc(walletPubkey)) {
|
||||
hasP = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !hasEncryption {
|
||||
t.Fatal("event missing encryption tag")
|
||||
}
|
||||
|
||||
if !hasP {
|
||||
t.Fatal("event missing p tag")
|
||||
}
|
||||
|
||||
// Test passed
|
||||
}
|
||||
Reference in New Issue
Block a user