mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-03-26 17:52:20 +01:00
nip17 and nip59.
This commit is contained in:
parent
cc038de0dd
commit
7514629906
99
nip17/nip17.go
Normal file
99
nip17/nip17.go
Normal file
@ -0,0 +1,99 @@
|
||||
package nip17
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/nbd-wtf/go-nostr/nip59"
|
||||
)
|
||||
|
||||
func GetDMRelays(ctx context.Context, pubkey string, pool *nostr.SimplePool, relaysToQuery []string) []string {
|
||||
ie := pool.QuerySingle(ctx, relaysToQuery, nostr.Filter{
|
||||
Authors: []string{pubkey},
|
||||
Kinds: []int{10050},
|
||||
})
|
||||
if ie == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
res := make([]string, 0, 3)
|
||||
for _, tag := range ie.Tags {
|
||||
if len(tag) >= 2 && tag[0] == "relay" {
|
||||
res = append(res, tag[1])
|
||||
if len(res) == 3 {
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func PrepareMessage(
|
||||
content string,
|
||||
tags nostr.Tags,
|
||||
ourPubkey string,
|
||||
encrypt func(string) (string, error),
|
||||
finalizeAndSign func(*nostr.Event) error,
|
||||
recipientPubKey string,
|
||||
modify func(*nostr.Event),
|
||||
) (nostr.Event, error) {
|
||||
rumor := nostr.Event{
|
||||
Kind: 14,
|
||||
Content: content,
|
||||
Tags: tags,
|
||||
CreatedAt: nostr.Now(),
|
||||
PubKey: ourPubkey,
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// ListenForMessages returns a channel with the rumors already decrypted and checked
|
||||
func ListenForMessages(
|
||||
ctx context.Context,
|
||||
pool *nostr.SimplePool,
|
||||
relays []string,
|
||||
ourPubkey string,
|
||||
since nostr.Timestamp,
|
||||
decrypt func(string) (string, error),
|
||||
) chan nostr.Event {
|
||||
ch := make(chan nostr.Event)
|
||||
|
||||
go func() {
|
||||
for ie := range pool.SubMany(ctx, relays, nostr.Filters{
|
||||
{
|
||||
Kinds: []int{1059},
|
||||
Tags: nostr.TagMap{"p": []string{ourPubkey}},
|
||||
Since: &since,
|
||||
},
|
||||
}) {
|
||||
seal, err := nip59.GiftUnwrap(*ie.Event, decrypt)
|
||||
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
|
||||
}
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
91
nip59/nip59.go
Normal file
91
nip59/nip59.go
Normal file
@ -0,0 +1,91 @@
|
||||
package nip59
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/mailru/easyjson"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"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) {
|
||||
rumor.Sig = ""
|
||||
ciphertext, err := encrypt(rumor.String())
|
||||
|
||||
return nostr.Event{
|
||||
Kind: 13,
|
||||
Content: ciphertext,
|
||||
CreatedAt: nostr.Now() - nostr.Timestamp(60*rand.Int63n(600) /* up to 6 hours in the past */),
|
||||
Tags: make(nostr.Tags, 0),
|
||||
}, 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)
|
||||
if err != nil {
|
||||
return nostr.Event{}, err
|
||||
}
|
||||
|
||||
gw := nostr.Event{
|
||||
Kind: 1059,
|
||||
Content: ciphertext,
|
||||
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)
|
||||
return gw, nil
|
||||
}
|
||||
|
||||
func GiftUnwrap(gw nostr.Event, decrypt func(string) (string, error)) (seal nostr.Event, err error) {
|
||||
jevt, err := decrypt(gw.Content)
|
||||
if err != nil {
|
||||
return seal, err
|
||||
}
|
||||
|
||||
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 {
|
||||
return rumor, err
|
||||
}
|
||||
|
||||
err = easyjson.Unmarshal([]byte(jevt), &rumor)
|
||||
if err != nil {
|
||||
return rumor, err
|
||||
}
|
||||
|
||||
rumor.PubKey = seal.PubKey
|
||||
return rumor, nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user