mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-08-27 14:22:20 +02:00
keyring -> keyer, fix misunderstanding with NIP-59 and adjust api.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
package keyring
|
package keyer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
@@ -1,4 +1,4 @@
|
|||||||
package keyring
|
package keyer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
@@ -1,4 +1,4 @@
|
|||||||
package keyring
|
package keyer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/nbd-wtf/go-nostr/nip49"
|
"github.com/nbd-wtf/go-nostr/nip49"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Keyring interface {
|
type Keyer interface {
|
||||||
Signer
|
Signer
|
||||||
Cipher
|
Cipher
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ type SignerOptions struct {
|
|||||||
Password string
|
Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, pool *nostr.SimplePool, input string, opts *SignerOptions) (Keyring, error) {
|
func New(ctx context.Context, pool *nostr.SimplePool, input string, opts *SignerOptions) (Keyer, error) {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &SignerOptions{}
|
opts = &SignerOptions{}
|
||||||
}
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
package keyring
|
package keyer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
@@ -1,4 +1,4 @@
|
|||||||
package keyring
|
package keyer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
@@ -2,9 +2,9 @@ package nip17
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/nbd-wtf/go-nostr"
|
"github.com/nbd-wtf/go-nostr"
|
||||||
|
"github.com/nbd-wtf/go-nostr/keyer"
|
||||||
"github.com/nbd-wtf/go-nostr/nip59"
|
"github.com/nbd-wtf/go-nostr/nip59"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,11 +31,10 @@ func GetDMRelays(ctx context.Context, pubkey string, pool *nostr.SimplePool, rel
|
|||||||
}
|
}
|
||||||
|
|
||||||
func PrepareMessage(
|
func PrepareMessage(
|
||||||
|
ctx context.Context,
|
||||||
content string,
|
content string,
|
||||||
tags nostr.Tags,
|
tags nostr.Tags,
|
||||||
ourPubkey string,
|
kr keyer.Keyer,
|
||||||
encrypt func(string) (string, error),
|
|
||||||
finalizeAndSign func(*nostr.Event) error,
|
|
||||||
recipientPubKey string,
|
recipientPubKey string,
|
||||||
modify func(*nostr.Event),
|
modify func(*nostr.Event),
|
||||||
) (nostr.Event, error) {
|
) (nostr.Event, error) {
|
||||||
@@ -44,55 +43,48 @@ func PrepareMessage(
|
|||||||
Content: content,
|
Content: content,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
CreatedAt: nostr.Now(),
|
CreatedAt: nostr.Now(),
|
||||||
PubKey: ourPubkey,
|
PubKey: kr.GetPublicKey(ctx),
|
||||||
}
|
}
|
||||||
rumor.ID = rumor.GetID()
|
rumor.ID = rumor.GetID()
|
||||||
|
|
||||||
seal, err := nip59.Seal(rumor, encrypt)
|
return nip59.GiftWrap(
|
||||||
if err != nil {
|
rumor,
|
||||||
return nostr.Event{}, fmt.Errorf("failed to seal: %w", err)
|
recipientPubKey,
|
||||||
}
|
func(s string) (string, error) { return kr.Encrypt(ctx, s, recipientPubKey) },
|
||||||
|
func(e *nostr.Event) error { return kr.SignEvent(ctx, e) },
|
||||||
if err := finalizeAndSign(&seal); err != nil {
|
modify,
|
||||||
return nostr.Event{}, fmt.Errorf("finalizeAndSign failed: %w", err)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
return nip59.GiftWrap(seal, recipientPubKey, modify)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenForMessages returns a channel with the rumors already decrypted and checked
|
// ListenForMessages returns a channel with the rumors already decrypted and checked
|
||||||
func ListenForMessages(
|
func ListenForMessages(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
pool *nostr.SimplePool,
|
pool *nostr.SimplePool,
|
||||||
relays []string,
|
kr keyer.Keyer,
|
||||||
ourPubkey string,
|
ourRelays []string,
|
||||||
since nostr.Timestamp,
|
since nostr.Timestamp,
|
||||||
decrypt func(string) (string, error),
|
|
||||||
) chan nostr.Event {
|
) chan nostr.Event {
|
||||||
ch := make(chan nostr.Event)
|
ch := make(chan nostr.Event)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(ch)
|
defer close(ch)
|
||||||
|
|
||||||
for ie := range pool.SubMany(ctx, relays, nostr.Filters{
|
for ie := range pool.SubMany(ctx, ourRelays, nostr.Filters{
|
||||||
{
|
{
|
||||||
Kinds: []int{1059},
|
Kinds: []int{1059},
|
||||||
Tags: nostr.TagMap{"p": []string{ourPubkey}},
|
Tags: nostr.TagMap{"p": []string{kr.GetPublicKey(ctx)}},
|
||||||
Since: &since,
|
Since: &since,
|
||||||
},
|
},
|
||||||
}) {
|
}) {
|
||||||
seal, err := nip59.GiftUnwrap(*ie.Event, decrypt)
|
rumor, err := nip59.GiftUnwrap(
|
||||||
|
*ie.Event,
|
||||||
|
func(otherpubkey, ciphertext string) (string, error) { return kr.Decrypt(ctx, ciphertext, otherpubkey) },
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
nostr.InfoLogger.Printf("[nip17] failed to unwrap received message: %s\n", err)
|
nostr.InfoLogger.Printf("[nip17] failed to unwrap received message: %s\n", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rumor, err := nip59.Unseal(seal, decrypt)
|
|
||||||
if err != nil {
|
|
||||||
nostr.InfoLogger.Printf("[nip17] failed to unseal received message: %s\n", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ch <- rumor
|
ch <- rumor
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@@ -9,83 +9,92 @@ import (
|
|||||||
"github.com/nbd-wtf/go-nostr/nip44"
|
"github.com/nbd-wtf/go-nostr/nip44"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Seal takes a rumor, encrypts it and returns an unsigned 'seal' event, the 'seal' must be signed
|
// Seal takes a 'rumor', encrypts it with our own key, making a 'seal', then encrypts that with a nonce key and
|
||||||
// afterwards.
|
// signs that (after potentially applying a modify function, which can be nil otherwise), yielding a 'gift-wrap'.
|
||||||
func Seal(rumor nostr.Event, encrypt func(string) (string, error)) (nostr.Event, error) {
|
func GiftWrap(
|
||||||
|
rumor nostr.Event,
|
||||||
|
recipientPublicKey string,
|
||||||
|
encrypt func(plaintext string) (string, error),
|
||||||
|
sign func(*nostr.Event) error,
|
||||||
|
modify func(*nostr.Event),
|
||||||
|
) (nostr.Event, error) {
|
||||||
rumor.Sig = ""
|
rumor.Sig = ""
|
||||||
ciphertext, err := encrypt(rumor.String())
|
rumorCiphertext, err := encrypt(rumor.String())
|
||||||
|
if err != nil {
|
||||||
|
return nostr.Event{}, err
|
||||||
|
}
|
||||||
|
|
||||||
return nostr.Event{
|
seal := nostr.Event{
|
||||||
Kind: 13,
|
Kind: 13,
|
||||||
Content: ciphertext,
|
Content: rumorCiphertext,
|
||||||
CreatedAt: nostr.Now() - nostr.Timestamp(60*rand.Int63n(600) /* up to 6 hours in the past */),
|
CreatedAt: nostr.Now() - nostr.Timestamp(60*rand.Int63n(600) /* up to 6 hours in the past */),
|
||||||
Tags: make(nostr.Tags, 0),
|
Tags: make(nostr.Tags, 0),
|
||||||
}, err
|
}
|
||||||
}
|
if err := sign(&seal); err != nil {
|
||||||
|
return nostr.Event{}, err
|
||||||
|
}
|
||||||
|
|
||||||
// Takes a signed 'seal' and gift-wraps it using a random key, returns it signed.
|
|
||||||
//
|
|
||||||
// modify is a function that takes the gift-wrap before signing, can be used to apply
|
|
||||||
// NIP-13 PoW or other things, otherwise can be nil.
|
|
||||||
func GiftWrap(seal nostr.Event, recipientPublicKey string, modify func(*nostr.Event)) (nostr.Event, error) {
|
|
||||||
nonceKey := nostr.GeneratePrivateKey()
|
nonceKey := nostr.GeneratePrivateKey()
|
||||||
temporaryConversationKey, err := nip44.GenerateConversationKey(recipientPublicKey, nonceKey)
|
temporaryConversationKey, err := nip44.GenerateConversationKey(recipientPublicKey, nonceKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nostr.Event{}, err
|
return nostr.Event{}, err
|
||||||
}
|
}
|
||||||
|
sealCiphertext, err := nip44.Encrypt(seal.String(), temporaryConversationKey, nil)
|
||||||
ciphertext, err := nip44.Encrypt(seal.String(), temporaryConversationKey, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nostr.Event{}, err
|
return nostr.Event{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
gw := nostr.Event{
|
gw := nostr.Event{
|
||||||
Kind: 1059,
|
Kind: 1059,
|
||||||
Content: ciphertext,
|
Content: sealCiphertext,
|
||||||
CreatedAt: nostr.Now() - nostr.Timestamp(60*rand.Int63n(600) /* up to 6 hours in the past */),
|
CreatedAt: nostr.Now() - nostr.Timestamp(60*rand.Int63n(600) /* up to 6 hours in the past */),
|
||||||
Tags: nostr.Tags{
|
Tags: nostr.Tags{
|
||||||
nostr.Tag{"p", recipientPublicKey},
|
nostr.Tag{"p", recipientPublicKey},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply POW if necessary
|
|
||||||
if modify != nil {
|
if modify != nil {
|
||||||
modify(&gw)
|
modify(&gw)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = gw.Sign(nonceKey)
|
if err := seal.Sign(nonceKey); err != nil {
|
||||||
|
return seal, err
|
||||||
|
}
|
||||||
|
|
||||||
return gw, nil
|
return gw, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GiftUnwrap(gw nostr.Event, decrypt func(string) (string, error)) (seal nostr.Event, err error) {
|
func GiftUnwrap(
|
||||||
jevt, err := decrypt(gw.Content)
|
gw nostr.Event,
|
||||||
if err != nil {
|
decrypt func(otherpubkey, ciphertext string) (string, error),
|
||||||
return seal, err
|
) (rumor nostr.Event, err error) {
|
||||||
}
|
jseal, err := decrypt(gw.PubKey, gw.Content)
|
||||||
|
|
||||||
err = easyjson.Unmarshal([]byte(jevt), &seal)
|
|
||||||
if err != nil {
|
|
||||||
return seal, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok, _ := seal.CheckSignature(); !ok {
|
|
||||||
return seal, fmt.Errorf("seal signature is invalid")
|
|
||||||
}
|
|
||||||
|
|
||||||
return seal, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Unseal(seal nostr.Event, decrypt func(string) (string, error)) (rumor nostr.Event, err error) {
|
|
||||||
jevt, err := decrypt(seal.Content)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rumor, err
|
return rumor, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = easyjson.Unmarshal([]byte(jevt), &rumor)
|
var seal nostr.Event
|
||||||
|
err = easyjson.Unmarshal([]byte(jseal), &seal)
|
||||||
|
if err != nil {
|
||||||
|
return rumor, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok, _ := seal.CheckSignature(); !ok {
|
||||||
|
return rumor, fmt.Errorf("seal signature is invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
jrumor, err := decrypt(seal.PubKey, seal.Content)
|
||||||
|
if err != nil {
|
||||||
|
return rumor, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = easyjson.Unmarshal([]byte(jrumor), &rumor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rumor, err
|
return rumor, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rumor.PubKey = seal.PubKey
|
rumor.PubKey = seal.PubKey
|
||||||
|
rumor.ID = rumor.GetID()
|
||||||
|
|
||||||
return rumor, nil
|
return rumor, nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user