Update signer to use get_public_key instead of assuming bunker key == user key

This commit is contained in:
Jon Staab
2024-10-28 15:41:23 -07:00
parent dcdf42723c
commit dc62248b7c
5 changed files with 106 additions and 81 deletions
+6 -1
View File
@@ -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:
+6
View File
@@ -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()
+4 -4
View File
@@ -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) =>
+90 -74
View File
@@ -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,