Update signer to use get_public_key instead of assuming bunker key == user key
This commit is contained in:
@@ -49,7 +49,12 @@ export const getSigner = memoize((session: Session) => {
|
||||
case "nip01":
|
||||
return new Nip01Signer(session.secret!)
|
||||
case "nip46":
|
||||
return new Nip46Signer(Nip46Broker.get(session.pubkey, session.secret!, session.handler!))
|
||||
return new Nip46Signer(
|
||||
Nip46Broker.get({
|
||||
secret: session.secret!,
|
||||
handler: session.handler!,
|
||||
})
|
||||
)
|
||||
case "nip55":
|
||||
return new Nip55Signer(session.signer!)
|
||||
default:
|
||||
|
||||
@@ -20,6 +20,7 @@ import {Connection} from './Connection'
|
||||
|
||||
export enum SubscriptionEvent {
|
||||
Eose = "eose",
|
||||
Send = "send",
|
||||
Close = "close",
|
||||
Event = "event",
|
||||
Complete = "complete",
|
||||
@@ -120,6 +121,7 @@ export const mergeSubscriptions = (subs: Subscription[]) => {
|
||||
propagateEvent(SubscriptionEvent.FailedFilter)
|
||||
propagateEvent(SubscriptionEvent.Invalid)
|
||||
propagateEvent(SubscriptionEvent.Eose)
|
||||
propagateEvent(SubscriptionEvent.Send)
|
||||
propagateEvent(SubscriptionEvent.Close)
|
||||
}
|
||||
|
||||
@@ -136,6 +138,7 @@ export const optimizeSubscriptions = (subs: Subscription[]) => {
|
||||
const abortedSubs = new Set<string>()
|
||||
const closedSubs = new Set<string>()
|
||||
const eosedSubs = new Set<string>()
|
||||
const sentSubs = new Set<string>()
|
||||
const mergedSubs = []
|
||||
|
||||
for (const {relays, filters} of ctx.net.optimizeSubscriptions(group)) {
|
||||
@@ -201,6 +204,7 @@ export const optimizeSubscriptions = (subs: Subscription[]) => {
|
||||
}
|
||||
})
|
||||
|
||||
propagateFinality(SubscriptionEvent.Send, sentSubs)
|
||||
propagateFinality(SubscriptionEvent.Eose, eosedSubs)
|
||||
propagateFinality(SubscriptionEvent.Close, closedSubs)
|
||||
propagateFinality(SubscriptionEvent.Complete, completedSubs)
|
||||
@@ -300,6 +304,8 @@ const _executeSubscription = (sub: Subscription) => {
|
||||
for (const filtersChunk of chunk(8, filters)) {
|
||||
subs.push(executor.subscribe(filtersChunk, {onEvent, onEose}))
|
||||
}
|
||||
|
||||
emitter.emit(SubscriptionEvent.Send)
|
||||
})
|
||||
} else {
|
||||
onComplete()
|
||||
|
||||
@@ -2,19 +2,19 @@ import {StampedEvent} from '@welshman/util'
|
||||
import {nip04, nip44, own, hash, sign, getPubkey, ISigner, makeSecret} from "../util"
|
||||
|
||||
export class Nip01Signer implements ISigner {
|
||||
private pubkey: string
|
||||
#pubkey: string
|
||||
|
||||
constructor(private secret: string) {
|
||||
this.pubkey = getPubkey(this.secret)
|
||||
this.#pubkey = getPubkey(this.secret)
|
||||
}
|
||||
|
||||
static fromSecret = (secret: string) => new Nip01Signer(secret)
|
||||
|
||||
static ephemeral = () => new Nip01Signer(makeSecret())
|
||||
|
||||
getPubkey = async () => this.pubkey
|
||||
getPubkey = async () => this.#pubkey
|
||||
|
||||
sign = async (event: StampedEvent) => sign(hash(own(event, this.pubkey)), this.secret)
|
||||
sign = async (event: StampedEvent) => sign(hash(own(event, this.#pubkey)), this.secret)
|
||||
|
||||
nip04 = {
|
||||
encrypt: async (pubkey: string, message: string) =>
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import {finalizeEvent, getPublicKey} from "nostr-tools"
|
||||
import {hexToBytes} from '@noble/hashes/utils'
|
||||
import {Emitter, tryCatch, randomId, sleep, equals, now} from "@welshman/lib"
|
||||
import {Emitter, tryCatch, randomId, equals, now} from "@welshman/lib"
|
||||
import {createEvent, TrustedEvent, StampedEvent, NOSTR_CONNECT} from "@welshman/util"
|
||||
import {subscribe, publish, Subscription} from "@welshman/net"
|
||||
import {nip04, nip44, ISigner, decrypt, hash, own} from '../util'
|
||||
import {ISigner, decrypt, hash, own} from '../util'
|
||||
import {Nip01Signer} from './nip01'
|
||||
|
||||
export type Algorithm = "nip04" | "nip44"
|
||||
export type Nip46Algorithm = "nip04" | "nip44"
|
||||
|
||||
export type Nip46Handler = {
|
||||
relays: string[]
|
||||
pubkey?: string
|
||||
pubkey: string
|
||||
domain?: string
|
||||
}
|
||||
|
||||
export type Nip46BrokerParams = {
|
||||
secret: string
|
||||
handler: Nip46Handler
|
||||
algorithm?: Nip46Algorithm
|
||||
}
|
||||
|
||||
export type Nip46Response = {
|
||||
id: string
|
||||
error?: string
|
||||
@@ -23,87 +27,89 @@ export type Nip46Response = {
|
||||
let singleton: Nip46Broker
|
||||
|
||||
export class Nip46Broker extends Emitter {
|
||||
#sub: Subscription
|
||||
#signer: ISigner
|
||||
#ready = sleep(500)
|
||||
#handler: Nip46Handler
|
||||
#algorithm: Nip46Algorithm
|
||||
#closed = false
|
||||
#connectResult: any
|
||||
#connectResult?: string
|
||||
#sub?: Subscription
|
||||
|
||||
static get(pubkey: string, secret: string, handler: Nip46Handler, algorithm: Algorithm = "nip04") {
|
||||
if (
|
||||
singleton?.pubkey !== pubkey ||
|
||||
singleton?.secret !== secret ||
|
||||
!equals(singleton?.handler, handler) ||
|
||||
singleton?.algorithm !== algorithm
|
||||
) {
|
||||
static get(params: Nip46BrokerParams) {
|
||||
if (!singleton?.hasParams(params)) {
|
||||
singleton?.teardown()
|
||||
singleton = new Nip46Broker(pubkey, secret, handler, algorithm)
|
||||
singleton = new Nip46Broker(params)
|
||||
}
|
||||
|
||||
return singleton
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly pubkey: string,
|
||||
readonly secret: string,
|
||||
readonly handler: Nip46Handler,
|
||||
readonly algorithm: Algorithm
|
||||
) {
|
||||
constructor(private params: Nip46BrokerParams) {
|
||||
super()
|
||||
|
||||
this.#signer = new Nip01Signer(secret)
|
||||
this.#sub = this.subscribe()
|
||||
this.#handler = params.handler
|
||||
this.#algorithm = params.algorithm || 'nip04'
|
||||
this.#signer = new Nip01Signer(params.secret)
|
||||
}
|
||||
|
||||
subscribe = () => {
|
||||
const sub = subscribe({
|
||||
relays: this.handler.relays,
|
||||
filters: [
|
||||
{
|
||||
since: now() - 30,
|
||||
kinds: [NOSTR_CONNECT],
|
||||
"#p": [getPublicKey(hexToBytes(this.secret))],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
sub.emitter.on("event", async (url: string, e: TrustedEvent) => {
|
||||
const json = await decrypt(this.#signer, e.pubkey, e.content)
|
||||
const res = await tryCatch(() => JSON.parse(json))
|
||||
|
||||
if (!res.id) {
|
||||
console.error(`Invalid nostr-connect response: ${json}`)
|
||||
}
|
||||
|
||||
if (res.result === "auth_url") {
|
||||
this.emit(`auth-${res.id}`, res)
|
||||
} else {
|
||||
this.emit(`res-${res.id}`, res)
|
||||
}
|
||||
})
|
||||
|
||||
sub.emitter.on("complete", () => {
|
||||
if (!this.#closed) {
|
||||
this.#sub = this.subscribe()
|
||||
}
|
||||
})
|
||||
|
||||
return sub
|
||||
hasParams(params: Nip46BrokerParams) {
|
||||
return equals(this.params, params)
|
||||
}
|
||||
|
||||
request = async (method: string, params: string[], admin = false) => {
|
||||
// nsecbunker has a race condition
|
||||
await this.#ready
|
||||
#subscribe = async () => {
|
||||
const pubkey = await this.#signer.getPubkey()
|
||||
|
||||
return new Promise<void>(resolve => {
|
||||
if (this.#sub) {
|
||||
this.#sub.close()
|
||||
}
|
||||
|
||||
this.#sub = subscribe({
|
||||
relays: this.#handler.relays,
|
||||
filters: [{since: now() - 30, kinds: [NOSTR_CONNECT], "#p": [pubkey]}],
|
||||
})
|
||||
|
||||
this.#sub.emitter.on('send', resolve)
|
||||
|
||||
this.#sub.emitter.on("event", async (url: string, e: TrustedEvent) => {
|
||||
const json = await decrypt(this.#signer, e.pubkey, e.content)
|
||||
const res = await tryCatch(() => JSON.parse(json))
|
||||
|
||||
if (!res.id) {
|
||||
console.error(`Invalid nostr-connect response: ${json}`)
|
||||
}
|
||||
|
||||
if (res.result === "auth_url") {
|
||||
this.emit(`auth-${res.id}`, res)
|
||||
} else {
|
||||
this.emit(`res-${res.id}`, res)
|
||||
}
|
||||
})
|
||||
|
||||
this.#sub.emitter.on("complete", () => {
|
||||
this.#sub = undefined
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
request = async (method: string, params: string[]) => {
|
||||
if (this.#closed) {
|
||||
throw new Error("Attempted to make a nip46 request with a closed broker")
|
||||
}
|
||||
|
||||
if (!this.#sub) {
|
||||
await this.#subscribe()
|
||||
}
|
||||
|
||||
const id = randomId()
|
||||
const pubkey = admin ? this.handler.pubkey! : this.pubkey
|
||||
const recipient = this.#handler.pubkey
|
||||
const payload = JSON.stringify({id, method, params})
|
||||
const crypt = this.algorithm === "nip04" ? nip04 : nip44
|
||||
const content = await crypt.encrypt(pubkey, this.secret, payload)
|
||||
const template = createEvent(NOSTR_CONNECT, {content, tags: [["p", pubkey]]})
|
||||
const event = finalizeEvent(template, this.secret as any)
|
||||
const content = await this.#signer[this.#algorithm].encrypt(recipient, payload)
|
||||
const template = createEvent(NOSTR_CONNECT, {content, tags: [["p", recipient]]})
|
||||
|
||||
publish({event, relays: this.handler.relays})
|
||||
publish({
|
||||
relays: this.#handler.relays,
|
||||
event: await this.#signer.sign(template),
|
||||
})
|
||||
|
||||
this.once(`auth-${id}`, res => {
|
||||
window.open(res.error, "Coracle", "width=600,height=800,popup=yes")
|
||||
@@ -121,16 +127,16 @@ export class Nip46Broker extends Emitter {
|
||||
}
|
||||
|
||||
createAccount = (username: string, perms = "") => {
|
||||
if (!this.handler.domain) {
|
||||
if (!this.#handler.domain) {
|
||||
throw new Error("Unable to create an account without a handler domain")
|
||||
}
|
||||
|
||||
return this.request("create_account", [username, this.handler.domain, "", perms], true)
|
||||
return this.request("create_account", [username, this.#handler.domain, "", perms])
|
||||
}
|
||||
|
||||
connect = async (token = "", perms = "") => {
|
||||
if (!this.#connectResult) {
|
||||
const params = [this.pubkey, token, perms]
|
||||
const params = ["", token, perms]
|
||||
|
||||
this.#connectResult = await this.request("connect", params)
|
||||
}
|
||||
@@ -138,6 +144,8 @@ export class Nip46Broker extends Emitter {
|
||||
return this.#connectResult === "ack"
|
||||
}
|
||||
|
||||
getPublicKey = () => this.request("get_public_key", [])
|
||||
|
||||
signEvent = async (event: StampedEvent) => {
|
||||
return JSON.parse(await this.request("sign_event", [JSON.stringify(event)]) as string)
|
||||
}
|
||||
@@ -165,12 +173,20 @@ export class Nip46Broker extends Emitter {
|
||||
}
|
||||
|
||||
export class Nip46Signer implements ISigner {
|
||||
#pubkey?: string
|
||||
|
||||
constructor(private broker: Nip46Broker) {}
|
||||
|
||||
getPubkey = async () => this.broker.pubkey
|
||||
getPubkey = async () => {
|
||||
if (!this.#pubkey) {
|
||||
this.#pubkey = await this.broker.getPublicKey()
|
||||
}
|
||||
|
||||
sign = (template: StampedEvent) =>
|
||||
this.broker.signEvent(hash(own(template, this.broker.pubkey)))
|
||||
return this.#pubkey
|
||||
}
|
||||
|
||||
sign = async (template: StampedEvent) =>
|
||||
this.broker.signEvent(hash(own(template, await this.getPubkey())))
|
||||
|
||||
nip04 = {
|
||||
encrypt: this.broker.nip04Encrypt,
|
||||
|
||||
Reference in New Issue
Block a user