From 1ad3caaa2fd47d3e4a913c8ee081dc25cce1fe39 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Wed, 11 Sep 2024 11:43:49 -0300 Subject: [PATCH] keyring -> keyer, fix misunderstanding with NIP-59 and adjust api. --- {keyring => keyer}/bunker.go | 2 +- {keyring => keyer}/encrypted.go | 2 +- {keyring => keyer}/lib.go | 6 +-- {keyring => keyer}/manual.go | 2 +- {keyring => keyer}/plain.go | 2 +- nip17/nip17.go | 39 ++++++++----------- nip59/nip59.go | 69 +++++++++++++++++++-------------- 7 files changed, 62 insertions(+), 60 deletions(-) rename {keyring => keyer}/bunker.go (98%) rename {keyring => keyer}/encrypted.go (99%) rename {keyring => keyer}/lib.go (96%) rename {keyring => keyer}/manual.go (98%) rename {keyring => keyer}/plain.go (98%) diff --git a/keyring/bunker.go b/keyer/bunker.go similarity index 98% rename from keyring/bunker.go rename to keyer/bunker.go index b6426f2..f62c719 100644 --- a/keyring/bunker.go +++ b/keyer/bunker.go @@ -1,4 +1,4 @@ -package keyring +package keyer import ( "context" diff --git a/keyring/encrypted.go b/keyer/encrypted.go similarity index 99% rename from keyring/encrypted.go rename to keyer/encrypted.go index cd9febc..3ce5ec7 100644 --- a/keyring/encrypted.go +++ b/keyer/encrypted.go @@ -1,4 +1,4 @@ -package keyring +package keyer import ( "context" diff --git a/keyring/lib.go b/keyer/lib.go similarity index 96% rename from keyring/lib.go rename to keyer/lib.go index 3d1801f..ec7eb42 100644 --- a/keyring/lib.go +++ b/keyer/lib.go @@ -1,4 +1,4 @@ -package keyring +package keyer import ( "context" @@ -13,7 +13,7 @@ import ( "github.com/nbd-wtf/go-nostr/nip49" ) -type Keyring interface { +type Keyer interface { Signer Cipher } @@ -43,7 +43,7 @@ type SignerOptions struct { 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 { opts = &SignerOptions{} } diff --git a/keyring/manual.go b/keyer/manual.go similarity index 98% rename from keyring/manual.go rename to keyer/manual.go index 39b5365..c518006 100644 --- a/keyring/manual.go +++ b/keyer/manual.go @@ -1,4 +1,4 @@ -package keyring +package keyer import ( "context" diff --git a/keyring/plain.go b/keyer/plain.go similarity index 98% rename from keyring/plain.go rename to keyer/plain.go index 9f6dea9..b056031 100644 --- a/keyring/plain.go +++ b/keyer/plain.go @@ -1,4 +1,4 @@ -package keyring +package keyer import ( "context" diff --git a/nip17/nip17.go b/nip17/nip17.go index 62a2454..57042ee 100644 --- a/nip17/nip17.go +++ b/nip17/nip17.go @@ -2,9 +2,9 @@ package nip17 import ( "context" - "fmt" "github.com/nbd-wtf/go-nostr" + "github.com/nbd-wtf/go-nostr/keyer" "github.com/nbd-wtf/go-nostr/nip59" ) @@ -31,11 +31,10 @@ func GetDMRelays(ctx context.Context, pubkey string, pool *nostr.SimplePool, rel } func PrepareMessage( + ctx context.Context, content string, tags nostr.Tags, - ourPubkey string, - encrypt func(string) (string, error), - finalizeAndSign func(*nostr.Event) error, + kr keyer.Keyer, recipientPubKey string, modify func(*nostr.Event), ) (nostr.Event, error) { @@ -44,20 +43,17 @@ func PrepareMessage( Content: content, Tags: tags, CreatedAt: nostr.Now(), - PubKey: ourPubkey, + PubKey: kr.GetPublicKey(ctx), } rumor.ID = rumor.GetID() - seal, err := nip59.Seal(rumor, encrypt) - if err != nil { - return nostr.Event{}, fmt.Errorf("failed to seal: %w", err) - } - - if err := finalizeAndSign(&seal); err != nil { - return nostr.Event{}, fmt.Errorf("finalizeAndSign failed: %w", err) - } - - return nip59.GiftWrap(seal, recipientPubKey, modify) + return nip59.GiftWrap( + rumor, + recipientPubKey, + func(s string) (string, error) { return kr.Encrypt(ctx, s, recipientPubKey) }, + func(e *nostr.Event) error { return kr.SignEvent(ctx, e) }, + modify, + ) } // ListenForMessages returns a channel with the rumors already decrypted and checked @@ -67,7 +63,7 @@ func ListenForMessages( relays []string, ourPubkey string, since nostr.Timestamp, - decrypt func(string) (string, error), + kr keyer.Cipher, ) chan nostr.Event { ch := make(chan nostr.Event) @@ -81,18 +77,15 @@ func ListenForMessages( 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 { nostr.InfoLogger.Printf("[nip17] failed to unwrap received message: %s\n", err) 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 } }() diff --git a/nip59/nip59.go b/nip59/nip59.go index 935a023..1640d1f 100644 --- a/nip59/nip59.go +++ b/nip59/nip59.go @@ -9,83 +9,92 @@ import ( "github.com/nbd-wtf/go-nostr/nip44" ) -// Seal takes a rumor, encrypts it and returns an unsigned 'seal' event, the 'seal' must be signed -// afterwards. -func Seal(rumor nostr.Event, encrypt func(string) (string, error)) (nostr.Event, error) { +// Seal takes a 'rumor', encrypts it with our own key, making a 'seal', then encrypts that with a nonce key and +// signs that (after potentially applying a modify function, which can be nil otherwise), yielding a 'gift-wrap'. +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 = "" - 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, - Content: ciphertext, + Content: rumorCiphertext, CreatedAt: nostr.Now() - nostr.Timestamp(60*rand.Int63n(600) /* up to 6 hours in the past */), 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() temporaryConversationKey, err := nip44.GenerateConversationKey(recipientPublicKey, nonceKey) if err != nil { return nostr.Event{}, err } - - ciphertext, err := nip44.Encrypt(seal.String(), temporaryConversationKey, nil) + sealCiphertext, err := nip44.Encrypt(seal.String(), temporaryConversationKey, nil) if err != nil { return nostr.Event{}, err } gw := nostr.Event{ Kind: 1059, - Content: ciphertext, + Content: sealCiphertext, CreatedAt: nostr.Now() - nostr.Timestamp(60*rand.Int63n(600) /* up to 6 hours in the past */), Tags: nostr.Tags{ nostr.Tag{"p", recipientPublicKey}, }, } - // apply POW if necessary if modify != nil { modify(&gw) } - err = gw.Sign(nonceKey) + if err := seal.Sign(nonceKey); err != nil { + return seal, err + } + return gw, nil } -func GiftUnwrap(gw nostr.Event, decrypt func(string) (string, error)) (seal nostr.Event, err error) { - jevt, err := decrypt(gw.Content) +func GiftUnwrap( + gw nostr.Event, + decrypt func(otherpubkey, ciphertext string) (string, error), +) (rumor nostr.Event, err error) { + jseal, err := decrypt(gw.PubKey, gw.Content) if err != nil { - return seal, err + return rumor, err } - err = easyjson.Unmarshal([]byte(jevt), &seal) + var seal nostr.Event + err = easyjson.Unmarshal([]byte(jseal), &seal) if err != nil { - return seal, err + return rumor, err } if ok, _ := seal.CheckSignature(); !ok { - return seal, fmt.Errorf("seal signature is invalid") + return rumor, 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) + jrumor, err := decrypt(seal.PubKey, seal.Content) if err != nil { return rumor, err } - err = easyjson.Unmarshal([]byte(jevt), &rumor) + err = easyjson.Unmarshal([]byte(jrumor), &rumor) if err != nil { return rumor, err } rumor.PubKey = seal.PubKey + rumor.ID = rumor.GetID() + return rumor, nil }