keyring -> keyer, fix misunderstanding with NIP-59 and adjust api.

This commit is contained in:
fiatjaf
2024-09-11 11:43:49 -03:00
parent 9addd57db7
commit 5e2e0bf458
7 changed files with 74 additions and 73 deletions

View File

@@ -1,4 +1,4 @@
package keyring package keyer
import ( import (
"context" "context"

View File

@@ -1,4 +1,4 @@
package keyring package keyer
import ( import (
"context" "context"

View File

@@ -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{}
} }

View File

@@ -1,4 +1,4 @@
package keyring package keyer
import ( import (
"context" "context"

View File

@@ -1,4 +1,4 @@
package keyring package keyer
import ( import (
"context" "context"

View File

@@ -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
} }
}() }()

View File

@@ -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
} }