replaced nip04 with nip44 (#43)

* replaced nip04 with nip44

* Refactor key decoding logic to use common function

* Refactor key decoding logic to use common function

* fix nostr connection read test
This commit is contained in:
asmogo 2024-11-08 14:21:43 +01:00 committed by GitHub
parent 646495de4c
commit a77add0859
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 82 additions and 45 deletions

View File

@ -4,6 +4,7 @@ import (
"encoding/base32"
"encoding/hex"
"fmt"
"github.com/ekzyis/nip44"
"log/slog"
"net"
"strings"
@ -15,7 +16,6 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip04"
"github.com/nbd-wtf/go-nostr/nip19"
"github.com/puzpuzpuz/xsync/v3"
"golang.org/x/net/context"
@ -227,12 +227,18 @@ func (e *Exit) ListenAndServe(ctx context.Context) {
// processMessage decrypts and unmarshals the incoming event message, and then
// routes the message to the appropriate handler based on its protocol type.
func (e *Exit) processMessage(ctx context.Context, msg nostr.IncomingEvent) {
sharedKey, err := nip04.ComputeSharedSecret(msg.PubKey, e.config.NostrPrivateKey)
// hex decode the target public key
privateKeyBytes, targetPublicKeyBytes, err := protocol.GetEncryptionKeys(e.config.NostrPrivateKey, msg.PubKey)
if err != nil {
slog.Error("could not get encryption keys", "error", err)
return
}
sharedKey, err := nip44.GenerateConversationKey(privateKeyBytes, targetPublicKeyBytes)
if err != nil {
slog.Error("could not compute shared key", "error", err)
return
}
decodedMessage, err := nip04.Decrypt(msg.Content, sharedKey)
decodedMessage, err := nip44.Decrypt(sharedKey, msg.Content)
if err != nil {
slog.Error("could not decrypt message", "error", err)
return

View File

@ -19,8 +19,8 @@ import (
"time"
"github.com/asmogo/nws/protocol"
"github.com/ekzyis/nip44"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip04"
)
const (
@ -63,7 +63,11 @@ func (e *Exit) StartReverseProxy(ctx context.Context, httpTarget string, port in
}
func (e *Exit) handleCertificateEvent(incomingEvent *nostr.IncomingEvent, ctx context.Context, cert tls.Certificate) (tls.Certificate, error) {
func (e *Exit) handleCertificateEvent(
incomingEvent *nostr.IncomingEvent,
ctx context.Context,
cert tls.Certificate,
) (tls.Certificate, error) {
slog.Info("found certificate event", "certificate", incomingEvent.Content)
// load private key from file
privateKeyEvent := e.pool.QuerySingle(ctx, e.config.NostrRelays, nostr.Filter{
@ -74,11 +78,15 @@ func (e *Exit) handleCertificateEvent(incomingEvent *nostr.IncomingEvent, ctx co
if privateKeyEvent == nil {
return tls.Certificate{}, errNoCertificateEvent
}
sharedKey, err := nip04.ComputeSharedSecret(privateKeyEvent.PubKey, e.config.NostrPrivateKey)
privateKeyBytes, targetPublicKeyBytes, err := protocol.GetEncryptionKeys(e.config.NostrPrivateKey, privateKeyEvent.PubKey)
if err != nil {
return tls.Certificate{}, err
}
sharedKey, err := nip44.GenerateConversationKey(privateKeyBytes, targetPublicKeyBytes)
if err != nil {
return tls.Certificate{}, fmt.Errorf("failed to compute shared key: %w", err)
}
decodedMessage, err := nip04.Decrypt(privateKeyEvent.Content, sharedKey)
decodedMessage, err := nip44.Decrypt(sharedKey, privateKeyEvent.Content)
if err != nil {
return tls.Certificate{}, fmt.Errorf("failed to decrypt private key: %w", err)
}

6
go.mod
View File

@ -1,9 +1,11 @@
module github.com/asmogo/nws
go 1.21
go 1.21.0
require (
github.com/btcsuite/btcd/btcec/v2 v2.3.2
github.com/caarlos0/env/v11 v11.0.0
github.com/ekzyis/nip44 v0.0.0-20240425094820-6a3d864c8f08
github.com/google/uuid v1.6.0
github.com/joho/godotenv v1.5.1
github.com/nbd-wtf/go-nostr v0.30.2
@ -15,7 +17,6 @@ require (
)
require (
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/btcsuite/btcd/btcutil v1.1.3 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@ -34,6 +35,7 @@ require (
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect

4
go.sum
View File

@ -38,6 +38,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeC
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/ekzyis/nip44 v0.0.0-20240425094820-6a3d864c8f08 h1:4/W0TU+JXOxZhWYQKqGEnEHigOym+ilZUiWwtD/KQNo=
github.com/ekzyis/nip44 v0.0.0-20240425094820-6a3d864c8f08/go.mod h1:SWpq7RYr0raQqGwJaSaCCWzKIuPHB+SmALuhCZd1dWI=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
@ -114,6 +116,8 @@ github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=

View File

@ -15,9 +15,9 @@ import (
"github.com/asmogo/nws/protocol"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/ekzyis/nip44"
"github.com/google/uuid"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip04"
"github.com/nbd-wtf/go-nostr/nip19"
"github.com/samber/lo"
)
@ -128,11 +128,16 @@ func (nc *NostrConnection) handleNostrRead(buffer []byte) (int, error) {
continue
}
nc.readIDs = append(nc.readIDs, event.ID)
sharedKey, err := nip04.ComputeSharedSecret(event.PubKey, nc.privateKey)
// hex decode the target public key
privateKeyBytes, targetPublicKeyBytes, err := protocol.GetEncryptionKeys(nc.privateKey, event.PubKey)
if err != nil {
return 0, fmt.Errorf("could not get encryption keys: %w", err)
}
sharedKey, err := nip44.GenerateConversationKey(privateKeyBytes, targetPublicKeyBytes)
if err != nil {
return 0, fmt.Errorf("could not compute shared key: %w", err)
}
decodedMessage, err := nip04.Decrypt(event.Content, sharedKey)
decodedMessage, err := nip44.Decrypt(sharedKey, event.Content)
if err != nil {
return 0, fmt.Errorf("could not decrypt message: %w", err)
}
@ -387,7 +392,7 @@ func WithTargetPublicKey(pubKey string) NostrConnOption {
func WithSub(...bool) NostrConnOption {
return func(connection *NostrConnection) {
connection.sub = true
go connection.handleSubscription()
//go connection.handleSubscription()
}
}
@ -406,30 +411,3 @@ func WithUUID(uuid uuid.UUID) NostrConnOption {
connection.uuid = uuid
}
}
// handleSubscription handles the subscription channel for incoming events.
// It continuously listens for events on the subscription channel and performs necessary operations.
// If the event has a valid relay, it computes the shared key and decrypts the event content.
// The decrypted message is then written to the read buffer.
// If the context is canceled, the method returns.
func (nc *NostrConnection) handleSubscription() {
for {
select {
case event := <-nc.subscriptionChan:
if event.Relay == nil {
continue
}
sharedKey, err := nip04.ComputeSharedSecret(event.PubKey, nc.privateKey)
if err != nil {
continue
}
decodedMessage, err := nip04.Decrypt(event.Content, sharedKey)
if err != nil {
continue
}
nc.readBuffer.Write([]byte(decodedMessage))
case <-nc.ctx.Done():
return
}
}
}

View File

@ -2,6 +2,9 @@ package netstr
import (
"context"
"fmt"
"github.com/asmogo/nws/protocol"
"github.com/ekzyis/nip44"
"github.com/nbd-wtf/go-nostr"
"runtime"
"testing"
@ -41,7 +44,7 @@ func TestNostrConnection_Read(t *testing.T) {
Event: &nostr.Event{
ID: "eventID",
PubKey: "8f97a664471f0b6d599a1e4a781c9a25f39902d96fb462c08df48697bb851611",
Content: "BnHzzyrUhKjDcDPOGfXJDYijUsgxw0hUZq2m+bX5QFI=?iv=NrEqv/jL+SASB2YTjo9i9Q=="}},
Content: `AuaBj8mXZ9n9IfdonNra0lpaed6Alc+H0xjUdyN9h6mCSuy7ZrEjWUZQj4HWNd4P1RCme1pda0z8hyItT4nVzESByRiQT5+hf+ij0aJw9+DW/ggJIWGbpm4wp7bk4loYKdERr+nzorqEjWNzpxsJXhXJ0nKtIxu61To5XY4SjuMqpUuOtznuHiPJJhKNWSSRPV92L/iVoOnjKJhfR5jOWBK3vA==`}},
nc: func() *NostrConnection {
ctx, cancelFunc := context.WithCancel(context.Background())
return &NostrConnection{
@ -52,7 +55,7 @@ func TestNostrConnection_Read(t *testing.T) {
privateKey: "788de536151854213cc28dff9c3042e7897f0a1d59b391ddbbc1619d7e716e78",
}
},
wantN: 11, // hello world
wantN: 5, // hello world
wantErr: false,
},
// Add more cases here to cover more corner situations
@ -62,6 +65,17 @@ func TestNostrConnection_Read(t *testing.T) {
nc := tt.nc()
defer nc.Close()
b := make([]byte, 1024)
if tt.event.Event != nil {
private, public, err := protocol.GetEncryptionKeys(nc.privateKey, tt.event.PubKey)
if err != nil {
panic(err)
}
sharedKey, err := nip44.GenerateConversationKey(private, public)
if err != nil {
panic(err)
}
fmt.Println(nip44.Encrypt(sharedKey, tt.event.Content, &nip44.EncryptOptions{}))
}
nc.subscriptionChan <- tt.event
gotN, err := nc.Read(b)
if (err != nil) != tt.wantErr {

17
protocol/nip44.go Normal file
View File

@ -0,0 +1,17 @@
package protocol
import (
"encoding/hex"
)
func GetEncryptionKeys(privateKey, publicKey string) ([]byte, []byte, error) {
targetPublicKeyBytes, err := hex.DecodeString("02" + publicKey)
if err != nil {
return nil, nil, err
}
privateKeyBytes, err := hex.DecodeString(privateKey)
if err != nil {
return nil, nil, err
}
return privateKeyBytes, targetPublicKeyBytes, nil
}

View File

@ -2,8 +2,9 @@ package protocol
import (
"fmt"
"github.com/ekzyis/nip44"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip04"
)
// KindEphemeralEvent represents the unique identifier for ephemeral events.
@ -64,7 +65,11 @@ func (s *EventSigner) CreateSignedEvent(
tags nostr.Tags,
opts ...MessageOption,
) (nostr.Event, error) {
sharedKey, err := nip04.ComputeSharedSecret(targetPublicKey, s.privateKey)
privateKeyBytes, targetPublicKeyBytes, err := GetEncryptionKeys(s.privateKey, targetPublicKey)
if err != nil {
return nostr.Event{}, fmt.Errorf("could not get encryption keys: %w", err)
}
sharedKey, err := nip44.GenerateConversationKey(privateKeyBytes, targetPublicKeyBytes)
if err != nil {
return nostr.Event{}, fmt.Errorf("could not compute shared key: %w", err)
}
@ -75,7 +80,10 @@ func (s *EventSigner) CreateSignedEvent(
if err != nil {
return nostr.Event{}, fmt.Errorf("could not marshal message: %w", err)
}
encryptedMessage, err := nip04.Encrypt(string(messageJSON), sharedKey)
encryptedMessage, err := nip44.Encrypt(sharedKey, string(messageJSON), &nip44.EncryptOptions{
Salt: nil,
Version: 0,
})
if err != nil {
return nostr.Event{}, fmt.Errorf("could not encrypt message: %w", err)
}