more conversions.

This commit is contained in:
fiatjaf
2025-04-15 00:00:03 -03:00
parent f9e4a5efa3
commit 376834cbf9
117 changed files with 450 additions and 1019 deletions

View File

@@ -3,15 +3,13 @@ package nip46
import (
"fmt"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip04"
"fiatjaf.com/nostrlib/nip44"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip44"
)
type Session struct {
PublicKey string
SharedKey []byte // nip04
ConversationKey [32]byte // nip44
PublicKey nostr.PubKey
ConversationKey [32]byte
}
type RelayReadWrite struct {
@@ -22,22 +20,18 @@ type RelayReadWrite struct {
func (s Session) ParseRequest(event *nostr.Event) (Request, error) {
var req Request
plain, err1 := nip44.Decrypt(event.Content, s.ConversationKey)
if err1 != nil {
var err2 error
plain, err2 = nip04.Decrypt(event.Content, s.SharedKey)
if err2 != nil {
return req, fmt.Errorf("failed to decrypt event from %s: (nip44: %w, nip04: %w)", event.PubKey, err1, err2)
}
plain, err := nip44.Decrypt(event.Content, s.ConversationKey)
if err != nil {
return req, fmt.Errorf("failed to decrypt event from %s: (nip44: %w)", event.PubKey, err)
}
err := json.Unmarshal([]byte(plain), &req)
err = json.Unmarshal([]byte(plain), &req)
return req, err
}
func (s Session) MakeResponse(
id string,
requester string,
requester nostr.PubKey,
result string,
err error,
) (resp Response, evt nostr.Event, error error) {

View File

@@ -8,10 +8,10 @@ import (
"strconv"
"sync/atomic"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip04"
"fiatjaf.com/nostr/nip44"
"github.com/mailru/easyjson"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip04"
"fiatjaf.com/nostrlib/nip44"
"github.com/puzpuzpuz/xsync/v3"
)
@@ -38,7 +38,7 @@ type BunkerClient struct {
// pool can be passed to reuse an existing pool, otherwise a new pool will be created.
func ConnectBunker(
ctx context.Context,
clientSecretKey string,
clientSecretKey nostr.PubKey,
bunkerURLOrNIP05 string,
pool *nostr.SimplePool,
onAuth func(string),
@@ -51,7 +51,7 @@ func ConnectBunker(
// assume it's a bunker url (will fail later if not)
secret := parsed.Query().Get("secret")
relays := parsed.Query()["relay"]
targetPublicKey := parsed.Host
targetPublicKey, _ := nostr.PubKeyFromHex(parsed.Host)
if parsed.Scheme == "" {
// could be a NIP-05
@@ -85,8 +85,8 @@ func ConnectBunker(
func NewBunker(
ctx context.Context,
clientSecretKey string,
targetPublicKey string,
clientSecretKey [32]byte,
targetPublicKey nostr.PubKey,
relays []string,
pool *nostr.SimplePool,
onAuth func(string),
@@ -95,8 +95,7 @@ func NewBunker(
pool = nostr.NewSimplePool(ctx)
}
clientPublicKey, _ := nostr.GetPublicKey(clientSecretKey)
sharedSecret, _ := nip04.ComputeSharedSecret(targetPublicKey, clientSecretKey)
clientPublicKey := nostr.GetPublicKey(clientSecretKey)
conversationKey, _ := nip44.GenerateConversationKey(targetPublicKey, clientSecretKey)
bunker := &BunkerClient{

View File

@@ -2,44 +2,41 @@ package nip46
import (
"context"
"encoding/hex"
"fmt"
"slices"
"sync"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip44"
"github.com/mailru/easyjson"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip04"
"fiatjaf.com/nostrlib/nip44"
)
var _ Signer = (*DynamicSigner)(nil)
type DynamicSigner struct {
sessionKeys []string
sessions []Session
sessions map[nostr.PubKey]Session
sync.Mutex
getHandlerSecretKey func(handlerPubkey string) (string, error)
getUserKeyer func(handlerPubkey string) (nostr.Keyer, error)
authorizeSigning func(event nostr.Event, from string, secret string) bool
authorizeEncryption func(from string, secret string) bool
getHandlerSecretKey func(handlerPubkey nostr.PubKey) ([32]byte, error)
getUserKeyer func(handlerPubkey nostr.PubKey) (nostr.Keyer, error)
authorizeSigning func(event nostr.Event, from nostr.PubKey, secret string) bool
authorizeEncryption func(from nostr.PubKey, secret string) bool
onEventSigned func(event nostr.Event)
getRelays func(pubkey string) map[string]RelayReadWrite
}
func NewDynamicSigner(
// the handler is the keypair we use to communicate with the NIP-46 client, decrypt requests, encrypt responses etc
getHandlerSecretKey func(handlerPubkey string) (string, error),
getHandlerSecretKey func(handlerPubkey nostr.PubKey) ([32]byte, error),
// this should correspond to the actual user on behalf of which we will respond to requests
getUserKeyer func(handlerPubkey string) (nostr.Keyer, error),
getUserKeyer func(handlerPubkey nostr.PubKey) (nostr.Keyer, error),
// this is called on every sign_event call, if it is nil it will be assumed that everything is authorized
authorizeSigning func(event nostr.Event, from string, secret string) bool,
authorizeSigning func(event nostr.Event, from nostr.PubKey, secret string) bool,
// this is called on every encrypt or decrypt calls, if it is nil it will be assumed that everything is authorized
authorizeEncryption func(from string, secret string) bool,
authorizeEncryption func(from nostr.PubKey, secret string) bool,
// unless it is nil, this is called after every event is signed
onEventSigned func(event nostr.Event),
@@ -53,34 +50,28 @@ func NewDynamicSigner(
authorizeSigning: authorizeSigning,
authorizeEncryption: authorizeEncryption,
onEventSigned: onEventSigned,
getRelays: getRelays,
}
}
func (p *DynamicSigner) GetSession(clientPubkey string) (Session, bool) {
idx, exists := slices.BinarySearch(p.sessionKeys, clientPubkey)
func (p *DynamicSigner) GetSession(clientPubkey nostr.PubKey) (Session, bool) {
session, exists := p.sessions[clientPubkey]
if exists {
return p.sessions[idx], true
return session, true
}
return Session{}, false
}
func (p *DynamicSigner) setSession(clientPubkey string, session Session) {
func (p *DynamicSigner) setSession(clientPubkey nostr.PubKey, session Session) {
p.Lock()
defer p.Unlock()
idx, exists := slices.BinarySearch(p.sessionKeys, clientPubkey)
_, exists := p.sessions[clientPubkey]
if exists {
return
}
// add to pool
p.sessionKeys = append(p.sessionKeys, "") // bogus append just to increase the capacity
p.sessions = append(p.sessions, Session{})
copy(p.sessionKeys[idx+1:], p.sessionKeys[idx:])
copy(p.sessions[idx+1:], p.sessions[idx:])
p.sessionKeys[idx] = clientPubkey
p.sessions[idx] = session
p.sessions[clientPubkey] = session
}
func (p *DynamicSigner) HandleRequest(ctx context.Context, event *nostr.Event) (
@@ -99,7 +90,10 @@ func (p *DynamicSigner) HandleRequest(ctx context.Context, event *nostr.Event) (
return req, resp, eventResponse, fmt.Errorf("invalid \"p\" tag")
}
handlerPubkey := handler[1]
handlerPubkey, err := nostr.PubKeyFromHex(handler[1])
if err != nil {
return req, resp, eventResponse, fmt.Errorf("%x is invalid pubkey: %w", handler[1], err)
}
handlerSecret, err := p.getHandlerSecretKey(handlerPubkey)
if err != nil {
return req, resp, eventResponse, fmt.Errorf("no private key for %s: %w", handlerPubkey, err)
@@ -109,18 +103,10 @@ func (p *DynamicSigner) HandleRequest(ctx context.Context, event *nostr.Event) (
return req, resp, eventResponse, fmt.Errorf("failed to get user keyer for %s: %w", handlerPubkey, err)
}
var session Session
idx, exists := slices.BinarySearch(p.sessionKeys, event.PubKey)
if exists {
session = p.sessions[idx]
} else {
session, exists := p.sessions[event.PubKey]
if !exists {
session = Session{}
session.SharedKey, err = nip04.ComputeSharedSecret(event.PubKey, handlerSecret)
if err != nil {
return req, resp, eventResponse, fmt.Errorf("failed to compute shared secret: %w", err)
}
session.ConversationKey, err = nip44.GenerateConversationKey(event.PubKey, handlerSecret)
if err != nil {
return req, resp, eventResponse, fmt.Errorf("failed to compute shared secret: %w", err)
@@ -150,7 +136,7 @@ func (p *DynamicSigner) HandleRequest(ctx context.Context, event *nostr.Event) (
}
result = "ack"
case "get_public_key":
result = session.PublicKey
result = hex.EncodeToString(session.PublicKey[:])
case "sign_event":
if len(req.Params) != 1 {
resultErr = fmt.Errorf("wrong number of arguments to 'sign_event'")
@@ -174,21 +160,14 @@ func (p *DynamicSigner) HandleRequest(ctx context.Context, event *nostr.Event) (
}
jrevt, _ := easyjson.Marshal(evt)
result = string(jrevt)
case "get_relays":
if p.getRelays == nil {
jrelays, _ := json.Marshal(p.getRelays(session.PublicKey))
result = string(jrelays)
} else {
result = "{}"
}
case "nip44_encrypt":
if len(req.Params) != 2 {
resultErr = fmt.Errorf("wrong number of arguments to 'nip04_encrypt'")
resultErr = fmt.Errorf("wrong number of arguments to 'nip44_encrypt'")
break
}
thirdPartyPubkey := req.Params[0]
if !nostr.IsValidPublicKey(thirdPartyPubkey) {
resultErr = fmt.Errorf("first argument to 'nip04_encrypt' is not a pubkey string")
thirdPartyPubkey, err := nostr.PubKeyFromHex(req.Params[0])
if err != nil {
resultErr = fmt.Errorf("first argument to 'nip44_encrypt' is not a valid pubkey string")
break
}
if p.authorizeEncryption != nil && !p.authorizeEncryption(event.PubKey, secret) {
@@ -208,9 +187,9 @@ func (p *DynamicSigner) HandleRequest(ctx context.Context, event *nostr.Event) (
resultErr = fmt.Errorf("wrong number of arguments to 'nip04_decrypt'")
break
}
thirdPartyPubkey := req.Params[0]
if !nostr.IsValidPublicKey(thirdPartyPubkey) {
resultErr = fmt.Errorf("first argument to 'nip04_decrypt' is not a pubkey string")
thirdPartyPubkey, err := nostr.PubKeyFromHex(req.Params[0])
if err != nil {
resultErr = fmt.Errorf("first argument to 'nip04_decrypt' is not a valid pubkey string")
break
}
if p.authorizeEncryption != nil && !p.authorizeEncryption(event.PubKey, secret) {

View File

@@ -5,8 +5,8 @@ import (
"net/url"
"strings"
"fiatjaf.com/nostr"
jsoniter "github.com/json-iterator/go"
"fiatjaf.com/nostrlib"
)
var json = jsoniter.ConfigFastest
@@ -34,7 +34,7 @@ func (r Response) String() string {
}
type Signer interface {
GetSession(clientPubkey string) (Session, bool)
GetSession(client nostr.PubKey) (Session, bool)
HandleRequest(context.Context, *nostr.Event) (req Request, resp Response, eventResponse nostr.Event, err error)
}
@@ -46,7 +46,7 @@ func IsValidBunkerURL(input string) bool {
if p.Scheme != "bunker" {
return false
}
if !nostr.IsValidPublicKey(p.Host) {
if _, err := nostr.PubKeyFromHex(p.Host); err != nil {
return false
}
if !strings.Contains(p.RawQuery, "relay=") {

View File

@@ -2,57 +2,45 @@ package nip46
import (
"context"
"encoding/hex"
"fmt"
"slices"
"sync"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip04"
"fiatjaf.com/nostr/nip44"
"github.com/mailru/easyjson"
"fiatjaf.com/nostrlib"
"fiatjaf.com/nostrlib/nip04"
"fiatjaf.com/nostrlib/nip44"
)
var _ Signer = (*StaticKeySigner)(nil)
type StaticKeySigner struct {
secretKey string
sessionKeys []string
sessions []Session
secretKey [32]byte
sessions map[nostr.PubKey]Session
sync.Mutex
RelaysToAdvertise map[string]RelayReadWrite
AuthorizeRequest func(harmless bool, from string, secret string) bool
AuthorizeRequest func(harmless bool, from nostr.PubKey, secret string) bool
}
func NewStaticKeySigner(secretKey string) StaticKeySigner {
func NewStaticKeySigner(secretKey [32]byte) StaticKeySigner {
return StaticKeySigner{
secretKey: secretKey,
RelaysToAdvertise: make(map[string]RelayReadWrite),
secretKey: secretKey,
}
}
func (p *StaticKeySigner) GetSession(clientPubkey string) (Session, bool) {
idx, exists := slices.BinarySearch(p.sessionKeys, clientPubkey)
if exists {
return p.sessions[idx], true
}
return Session{}, false
func (p *StaticKeySigner) GetSession(clientPubkey nostr.PubKey) (Session, bool) {
session, ok := p.sessions[clientPubkey]
return session, ok
}
func (p *StaticKeySigner) getOrCreateSession(clientPubkey string) (Session, error) {
func (p *StaticKeySigner) getOrCreateSession(clientPubkey nostr.PubKey) (Session, error) {
p.Lock()
defer p.Unlock()
idx, exists := slices.BinarySearch(p.sessionKeys, clientPubkey)
session, exists := p.sessions[clientPubkey]
if exists {
return p.sessions[idx], nil
}
shared, err := nip04.ComputeSharedSecret(clientPubkey, p.secretKey)
if err != nil {
return Session{}, fmt.Errorf("failed to compute shared secret: %w", err)
return session, nil
}
ck, err := nip44.GenerateConversationKey(clientPubkey, p.secretKey)
@@ -60,24 +48,14 @@ func (p *StaticKeySigner) getOrCreateSession(clientPubkey string) (Session, erro
return Session{}, fmt.Errorf("failed to compute shared secret: %w", err)
}
pubkey, err := nostr.GetPublicKey(p.secretKey)
if err != nil {
return Session{}, fmt.Errorf("failed to derive public key: %w", err)
}
session := Session{
pubkey := nostr.GetPublicKey(p.secretKey)
session = Session{
PublicKey: pubkey,
SharedKey: shared,
ConversationKey: ck,
}
// add to pool
p.sessionKeys = append(p.sessionKeys, "") // bogus append just to increase the capacity
p.sessions = append(p.sessions, Session{})
copy(p.sessionKeys[idx+1:], p.sessionKeys[idx:])
copy(p.sessions[idx+1:], p.sessions[idx:])
p.sessionKeys[idx] = clientPubkey
p.sessions[idx] = session
p.sessions[pubkey] = session
return session, nil
}
@@ -116,7 +94,7 @@ func (p *StaticKeySigner) HandleRequest(_ context.Context, event *nostr.Event) (
result = "ack"
harmless = true
case "get_public_key":
result = session.PublicKey
result = hex.EncodeToString(session.PublicKey[:])
harmless = true
case "sign_event":
if len(req.Params) != 1 {
@@ -136,18 +114,14 @@ func (p *StaticKeySigner) HandleRequest(_ context.Context, event *nostr.Event) (
}
jrevt, _ := easyjson.Marshal(evt)
result = string(jrevt)
case "get_relays":
jrelays, _ := json.Marshal(p.RelaysToAdvertise)
result = string(jrelays)
harmless = true
case "nip44_encrypt":
if len(req.Params) != 2 {
resultErr = fmt.Errorf("wrong number of arguments to 'nip04_encrypt'")
resultErr = fmt.Errorf("wrong number of arguments to 'nip44_encrypt'")
break
}
thirdPartyPubkey := req.Params[0]
if !nostr.IsValidPublicKey(thirdPartyPubkey) {
resultErr = fmt.Errorf("first argument to 'nip04_encrypt' is not a pubkey string")
thirdPartyPubkey, err := nostr.PubKeyFromHex(req.Params[0])
if err != nil {
resultErr = fmt.Errorf("first argument to 'nip04_encrypt' is not a valid pubkey string")
break
}
plaintext := req.Params[1]
@@ -168,9 +142,9 @@ func (p *StaticKeySigner) HandleRequest(_ context.Context, event *nostr.Event) (
resultErr = fmt.Errorf("wrong number of arguments to 'nip04_decrypt'")
break
}
thirdPartyPubkey := req.Params[0]
if !nostr.IsValidPublicKey(thirdPartyPubkey) {
resultErr = fmt.Errorf("first argument to 'nip04_decrypt' is not a pubkey string")
thirdPartyPubkey, err := nostr.PubKeyFromHex(req.Params[0])
if err != nil {
resultErr = fmt.Errorf("first argument to 'nip04_decrypt' is not a valid pubkey string")
break
}
ciphertext := req.Params[1]
@@ -191,9 +165,9 @@ func (p *StaticKeySigner) HandleRequest(_ context.Context, event *nostr.Event) (
resultErr = fmt.Errorf("wrong number of arguments to 'nip04_encrypt'")
break
}
thirdPartyPubkey := req.Params[0]
if !nostr.IsValidPublicKey(thirdPartyPubkey) {
resultErr = fmt.Errorf("first argument to 'nip04_encrypt' is not a pubkey string")
thirdPartyPubkey, err := nostr.PubKeyFromHex(req.Params[0])
if err != nil {
resultErr = fmt.Errorf("first argument to 'nip04_encrypt' is not a valid pubkey string")
break
}
plaintext := req.Params[1]
@@ -214,9 +188,9 @@ func (p *StaticKeySigner) HandleRequest(_ context.Context, event *nostr.Event) (
resultErr = fmt.Errorf("wrong number of arguments to 'nip04_decrypt'")
break
}
thirdPartyPubkey := req.Params[0]
if !nostr.IsValidPublicKey(thirdPartyPubkey) {
resultErr = fmt.Errorf("first argument to 'nip04_decrypt' is not a pubkey string")
thirdPartyPubkey, err := nostr.PubKeyFromHex(req.Params[0])
if err != nil {
resultErr = fmt.Errorf("first argument to 'nip04_decrypt' is not a valid pubkey string")
break
}
ciphertext := req.Params[1]

View File

@@ -4,22 +4,23 @@ import (
"context"
"fmt"
"fiatjaf.com/nostrlib/nip05"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip05"
)
func queryWellKnownNostrJson(ctx context.Context, fullname string) (pubkey string, relays []string, err error) {
func queryWellKnownNostrJson(ctx context.Context, fullname string) (pubkey nostr.PubKey, relays []string, err error) {
result, name, err := nip05.Fetch(ctx, fullname)
if err != nil {
return "", nil, err
return nostr.ZeroPK, nil, err
}
pubkey, ok := result.Names[name]
pubkeyh, ok := result.Names[name]
if !ok {
return "", nil, fmt.Errorf("no entry found for the '%s' name", name)
return nostr.ZeroPK, nil, fmt.Errorf("no entry found for the '%s' name", name)
}
relays, _ = result.NIP46[pubkey]
relays, _ = result.NIP46[pubkeyh]
if !ok {
return "", nil, fmt.Errorf("no bunker relays found for the '%s' name", name)
return nostr.ZeroPK, nil, fmt.Errorf("no bunker relays found for the '%s' name", name)
}
return pubkey, relays, nil