diff --git a/exit/exit.go b/exit/exit.go index 77e95ed..2d72703 100644 --- a/exit/exit.go +++ b/exit/exit.go @@ -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 diff --git a/exit/https.go b/exit/https.go index d030668..ddaf27a 100644 --- a/exit/https.go +++ b/exit/https.go @@ -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) } diff --git a/go.mod b/go.mod index 7896e92..1ed0c7b 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index a1f34df..3192b7e 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/netstr/conn.go b/netstr/conn.go index 9245b92..d248f71 100644 --- a/netstr/conn.go +++ b/netstr/conn.go @@ -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 - } - } -} diff --git a/netstr/conn_test.go b/netstr/conn_test.go index 7c3481f..6e98346 100644 --- a/netstr/conn_test.go +++ b/netstr/conn_test.go @@ -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 { diff --git a/protocol/nip44.go b/protocol/nip44.go new file mode 100644 index 0000000..207883d --- /dev/null +++ b/protocol/nip44.go @@ -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 +} diff --git a/protocol/signer.go b/protocol/signer.go index 67bb729..db2e45b 100644 --- a/protocol/signer.go +++ b/protocol/signer.go @@ -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) }