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":
|
case "nip01":
|
||||||
return new Nip01Signer(session.secret!)
|
return new Nip01Signer(session.secret!)
|
||||||
case "nip46":
|
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":
|
case "nip55":
|
||||||
return new Nip55Signer(session.signer!)
|
return new Nip55Signer(session.signer!)
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {Connection} from './Connection'
|
|||||||
|
|
||||||
export enum SubscriptionEvent {
|
export enum SubscriptionEvent {
|
||||||
Eose = "eose",
|
Eose = "eose",
|
||||||
|
Send = "send",
|
||||||
Close = "close",
|
Close = "close",
|
||||||
Event = "event",
|
Event = "event",
|
||||||
Complete = "complete",
|
Complete = "complete",
|
||||||
@@ -120,6 +121,7 @@ export const mergeSubscriptions = (subs: Subscription[]) => {
|
|||||||
propagateEvent(SubscriptionEvent.FailedFilter)
|
propagateEvent(SubscriptionEvent.FailedFilter)
|
||||||
propagateEvent(SubscriptionEvent.Invalid)
|
propagateEvent(SubscriptionEvent.Invalid)
|
||||||
propagateEvent(SubscriptionEvent.Eose)
|
propagateEvent(SubscriptionEvent.Eose)
|
||||||
|
propagateEvent(SubscriptionEvent.Send)
|
||||||
propagateEvent(SubscriptionEvent.Close)
|
propagateEvent(SubscriptionEvent.Close)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +138,7 @@ export const optimizeSubscriptions = (subs: Subscription[]) => {
|
|||||||
const abortedSubs = new Set<string>()
|
const abortedSubs = new Set<string>()
|
||||||
const closedSubs = new Set<string>()
|
const closedSubs = new Set<string>()
|
||||||
const eosedSubs = new Set<string>()
|
const eosedSubs = new Set<string>()
|
||||||
|
const sentSubs = new Set<string>()
|
||||||
const mergedSubs = []
|
const mergedSubs = []
|
||||||
|
|
||||||
for (const {relays, filters} of ctx.net.optimizeSubscriptions(group)) {
|
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.Eose, eosedSubs)
|
||||||
propagateFinality(SubscriptionEvent.Close, closedSubs)
|
propagateFinality(SubscriptionEvent.Close, closedSubs)
|
||||||
propagateFinality(SubscriptionEvent.Complete, completedSubs)
|
propagateFinality(SubscriptionEvent.Complete, completedSubs)
|
||||||
@@ -300,6 +304,8 @@ const _executeSubscription = (sub: Subscription) => {
|
|||||||
for (const filtersChunk of chunk(8, filters)) {
|
for (const filtersChunk of chunk(8, filters)) {
|
||||||
subs.push(executor.subscribe(filtersChunk, {onEvent, onEose}))
|
subs.push(executor.subscribe(filtersChunk, {onEvent, onEose}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitter.emit(SubscriptionEvent.Send)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
onComplete()
|
onComplete()
|
||||||
|
|||||||
@@ -2,19 +2,19 @@ import {StampedEvent} from '@welshman/util'
|
|||||||
import {nip04, nip44, own, hash, sign, getPubkey, ISigner, makeSecret} from "../util"
|
import {nip04, nip44, own, hash, sign, getPubkey, ISigner, makeSecret} from "../util"
|
||||||
|
|
||||||
export class Nip01Signer implements ISigner {
|
export class Nip01Signer implements ISigner {
|
||||||
private pubkey: string
|
#pubkey: string
|
||||||
|
|
||||||
constructor(private secret: string) {
|
constructor(private secret: string) {
|
||||||
this.pubkey = getPubkey(this.secret)
|
this.#pubkey = getPubkey(this.secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromSecret = (secret: string) => new Nip01Signer(secret)
|
static fromSecret = (secret: string) => new Nip01Signer(secret)
|
||||||
|
|
||||||
static ephemeral = () => new Nip01Signer(makeSecret())
|
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 = {
|
nip04 = {
|
||||||
encrypt: async (pubkey: string, message: string) =>
|
encrypt: async (pubkey: string, message: string) =>
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
import {finalizeEvent, getPublicKey} from "nostr-tools"
|
import {Emitter, tryCatch, randomId, equals, now} from "@welshman/lib"
|
||||||
import {hexToBytes} from '@noble/hashes/utils'
|
|
||||||
import {Emitter, tryCatch, randomId, sleep, equals, now} from "@welshman/lib"
|
|
||||||
import {createEvent, TrustedEvent, StampedEvent, NOSTR_CONNECT} from "@welshman/util"
|
import {createEvent, TrustedEvent, StampedEvent, NOSTR_CONNECT} from "@welshman/util"
|
||||||
import {subscribe, publish, Subscription} from "@welshman/net"
|
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'
|
import {Nip01Signer} from './nip01'
|
||||||
|
|
||||||
export type Algorithm = "nip04" | "nip44"
|
export type Nip46Algorithm = "nip04" | "nip44"
|
||||||
|
|
||||||
export type Nip46Handler = {
|
export type Nip46Handler = {
|
||||||
relays: string[]
|
relays: string[]
|
||||||
pubkey?: string
|
pubkey: string
|
||||||
domain?: string
|
domain?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Nip46BrokerParams = {
|
||||||
|
secret: string
|
||||||
|
handler: Nip46Handler
|
||||||
|
algorithm?: Nip46Algorithm
|
||||||
|
}
|
||||||
|
|
||||||
export type Nip46Response = {
|
export type Nip46Response = {
|
||||||
id: string
|
id: string
|
||||||
error?: string
|
error?: string
|
||||||
@@ -23,87 +27,89 @@ export type Nip46Response = {
|
|||||||
let singleton: Nip46Broker
|
let singleton: Nip46Broker
|
||||||
|
|
||||||
export class Nip46Broker extends Emitter {
|
export class Nip46Broker extends Emitter {
|
||||||
#sub: Subscription
|
|
||||||
#signer: ISigner
|
#signer: ISigner
|
||||||
#ready = sleep(500)
|
#handler: Nip46Handler
|
||||||
|
#algorithm: Nip46Algorithm
|
||||||
#closed = false
|
#closed = false
|
||||||
#connectResult: any
|
#connectResult?: string
|
||||||
|
#sub?: Subscription
|
||||||
|
|
||||||
static get(pubkey: string, secret: string, handler: Nip46Handler, algorithm: Algorithm = "nip04") {
|
static get(params: Nip46BrokerParams) {
|
||||||
if (
|
if (!singleton?.hasParams(params)) {
|
||||||
singleton?.pubkey !== pubkey ||
|
|
||||||
singleton?.secret !== secret ||
|
|
||||||
!equals(singleton?.handler, handler) ||
|
|
||||||
singleton?.algorithm !== algorithm
|
|
||||||
) {
|
|
||||||
singleton?.teardown()
|
singleton?.teardown()
|
||||||
singleton = new Nip46Broker(pubkey, secret, handler, algorithm)
|
singleton = new Nip46Broker(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
return singleton
|
return singleton
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(private params: Nip46BrokerParams) {
|
||||||
readonly pubkey: string,
|
|
||||||
readonly secret: string,
|
|
||||||
readonly handler: Nip46Handler,
|
|
||||||
readonly algorithm: Algorithm
|
|
||||||
) {
|
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.#signer = new Nip01Signer(secret)
|
this.#handler = params.handler
|
||||||
this.#sub = this.subscribe()
|
this.#algorithm = params.algorithm || 'nip04'
|
||||||
|
this.#signer = new Nip01Signer(params.secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe = () => {
|
hasParams(params: Nip46BrokerParams) {
|
||||||
const sub = subscribe({
|
return equals(this.params, params)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
request = async (method: string, params: string[], admin = false) => {
|
#subscribe = async () => {
|
||||||
// nsecbunker has a race condition
|
const pubkey = await this.#signer.getPubkey()
|
||||||
await this.#ready
|
|
||||||
|
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 id = randomId()
|
||||||
const pubkey = admin ? this.handler.pubkey! : this.pubkey
|
const recipient = this.#handler.pubkey
|
||||||
const payload = JSON.stringify({id, method, params})
|
const payload = JSON.stringify({id, method, params})
|
||||||
const crypt = this.algorithm === "nip04" ? nip04 : nip44
|
const content = await this.#signer[this.#algorithm].encrypt(recipient, payload)
|
||||||
const content = await crypt.encrypt(pubkey, this.secret, payload)
|
const template = createEvent(NOSTR_CONNECT, {content, tags: [["p", recipient]]})
|
||||||
const template = createEvent(NOSTR_CONNECT, {content, tags: [["p", pubkey]]})
|
|
||||||
const event = finalizeEvent(template, this.secret as any)
|
|
||||||
|
|
||||||
publish({event, relays: this.handler.relays})
|
publish({
|
||||||
|
relays: this.#handler.relays,
|
||||||
|
event: await this.#signer.sign(template),
|
||||||
|
})
|
||||||
|
|
||||||
this.once(`auth-${id}`, res => {
|
this.once(`auth-${id}`, res => {
|
||||||
window.open(res.error, "Coracle", "width=600,height=800,popup=yes")
|
window.open(res.error, "Coracle", "width=600,height=800,popup=yes")
|
||||||
@@ -121,16 +127,16 @@ export class Nip46Broker extends Emitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createAccount = (username: string, perms = "") => {
|
createAccount = (username: string, perms = "") => {
|
||||||
if (!this.handler.domain) {
|
if (!this.#handler.domain) {
|
||||||
throw new Error("Unable to create an account without a 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 = "") => {
|
connect = async (token = "", perms = "") => {
|
||||||
if (!this.#connectResult) {
|
if (!this.#connectResult) {
|
||||||
const params = [this.pubkey, token, perms]
|
const params = ["", token, perms]
|
||||||
|
|
||||||
this.#connectResult = await this.request("connect", params)
|
this.#connectResult = await this.request("connect", params)
|
||||||
}
|
}
|
||||||
@@ -138,6 +144,8 @@ export class Nip46Broker extends Emitter {
|
|||||||
return this.#connectResult === "ack"
|
return this.#connectResult === "ack"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPublicKey = () => this.request("get_public_key", [])
|
||||||
|
|
||||||
signEvent = async (event: StampedEvent) => {
|
signEvent = async (event: StampedEvent) => {
|
||||||
return JSON.parse(await this.request("sign_event", [JSON.stringify(event)]) as string)
|
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 {
|
export class Nip46Signer implements ISigner {
|
||||||
|
#pubkey?: string
|
||||||
|
|
||||||
constructor(private broker: Nip46Broker) {}
|
constructor(private broker: Nip46Broker) {}
|
||||||
|
|
||||||
getPubkey = async () => this.broker.pubkey
|
getPubkey = async () => {
|
||||||
|
if (!this.#pubkey) {
|
||||||
|
this.#pubkey = await this.broker.getPublicKey()
|
||||||
|
}
|
||||||
|
|
||||||
sign = (template: StampedEvent) =>
|
return this.#pubkey
|
||||||
this.broker.signEvent(hash(own(template, this.broker.pubkey)))
|
}
|
||||||
|
|
||||||
|
sign = async (template: StampedEvent) =>
|
||||||
|
this.broker.signEvent(hash(own(template, await this.getPubkey())))
|
||||||
|
|
||||||
nip04 = {
|
nip04 = {
|
||||||
encrypt: this.broker.nip04Encrypt,
|
encrypt: this.broker.nip04Encrypt,
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
./build.sh
|
|
||||||
|
|
||||||
for package in $(./get_packages.py); do
|
for package in $(./get_packages.py); do
|
||||||
npx onchange packages/$package -e '**/build/**' -k -- ./build_and_link.sh $package &
|
npx onchange packages/$package -e '**/build/**' -k -- ./build_and_link.sh $package &
|
||||||
done
|
done
|
||||||
|
|||||||
Reference in New Issue
Block a user