From 9640a7fa570ad4a36f8cc88e3cb080a906ad0939 Mon Sep 17 00:00:00 2001 From: Kieran Date: Tue, 4 Jul 2023 18:18:52 +0100 Subject: [PATCH] Refactor signers --- packages/app/src/Element/SendSats.tsx | 2 +- packages/app/src/Feed/EventPublisher.ts | 7 +- packages/app/src/Login/Functions.ts | 2 +- packages/app/src/index.tsx | 8 +- packages/system/src/event-publisher.ts | 158 +++++++++++++++--------- 5 files changed, 112 insertions(+), 65 deletions(-) diff --git a/packages/app/src/Element/SendSats.tsx b/packages/app/src/Element/SendSats.tsx index 9963fd8b9..bc8d9be43 100644 --- a/packages/app/src/Element/SendSats.tsx +++ b/packages/app/src/Element/SendSats.tsx @@ -134,7 +134,7 @@ export default function SendSats(props: SendSatsProps) { const randomKey = generateRandomKey(); console.debug("Generated new key for zap: ", randomKey); - const publisher = new EventPublisher(randomKey.publicKey, randomKey.privateKey); + const publisher = EventPublisher.privateKey(randomKey.privateKey); zap = await publisher.zap(amount * 1000, author, relays, note, comment, eb => eb.tag(["anon", ""])); } else { zap = await publisher.zap(amount * 1000, author, relays, note, comment); diff --git a/packages/app/src/Feed/EventPublisher.ts b/packages/app/src/Feed/EventPublisher.ts index d8ed51f97..16d30d831 100644 --- a/packages/app/src/Feed/EventPublisher.ts +++ b/packages/app/src/Feed/EventPublisher.ts @@ -1,12 +1,15 @@ import { useMemo } from "react"; import useLogin from "Hooks/useLogin"; -import { EventPublisher } from "@snort/system"; +import { EventPublisher, Nip7Signer } from "@snort/system"; export default function useEventPublisher() { const { publicKey, privateKey } = useLogin(); return useMemo(() => { + if (privateKey) { + return EventPublisher.privateKey(privateKey); + } if (publicKey) { - return new EventPublisher(publicKey, privateKey); + return new EventPublisher(new Nip7Signer(), publicKey); } }, [publicKey, privateKey]); } diff --git a/packages/app/src/Login/Functions.ts b/packages/app/src/Login/Functions.ts index 08e4a3be4..42ce7152f 100644 --- a/packages/app/src/Login/Functions.ts +++ b/packages/app/src/Login/Functions.ts @@ -78,7 +78,7 @@ export async function generateNewLogin() { } const publicKey = utils.bytesToHex(secp.schnorr.getPublicKey(privateKey)); - const publisher = new EventPublisher(publicKey, privateKey); + const publisher = EventPublisher.privateKey(privateKey); const ev = await publisher.contactList([bech32ToHex(SnortPubKey), publicKey], newRelays); System.BroadcastEvent(ev); diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index 105766f18..b40ba3131 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -6,7 +6,7 @@ import { StrictMode } from "react"; import * as ReactDOM from "react-dom/client"; import { Provider } from "react-redux"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; -import { EventPublisher, NostrSystem, ProfileLoaderService } from "@snort/system"; +import { EventPublisher, NostrSystem, ProfileLoaderService, Nip7Signer } from "@snort/system"; import * as serviceWorkerRegistration from "serviceWorkerRegistration"; import { IntlProvider } from "IntlProvider"; @@ -46,8 +46,12 @@ export const System = new NostrSystem({ relayMetrics: RelayMetrics, authHandler: async (c, r) => { const { publicKey, privateKey } = LoginStore.snapshot(); + if (privateKey) { + const pub = EventPublisher.privateKey(privateKey); + return await pub.nip42Auth(c, r); + } if (publicKey) { - const pub = new EventPublisher(publicKey, privateKey); + const pub = new EventPublisher(new Nip7Signer(), publicKey); return await pub.nip42Auth(c, r); } }, diff --git a/packages/system/src/event-publisher.ts b/packages/system/src/event-publisher.ts index 1445b8e92..4b46e2e52 100644 --- a/packages/system/src/event-publisher.ts +++ b/packages/system/src/event-publisher.ts @@ -39,85 +39,125 @@ declare global { } } +export interface EventSigner { + getPubKey(): Promise | string; + nip4Encrypt(content: string, key: string): Promise; + nip4Decrypt(content: string, otherKey: string): Promise; + sign(ev: NostrEvent): Promise; +} + +export class Nip7Signer implements EventSigner { + async getPubKey(): Promise { + if (!window.nostr) { + throw new Error("Cannot use NIP-07 signer, not found!"); + } + return await window.nostr.getPublicKey(); + } + + async nip4Encrypt(content: string, key: string): Promise { + if (!window.nostr) { + throw new Error("Cannot use NIP-07 signer, not found!"); + } + return await barrierQueue(Nip7Queue, () => + unwrap(window.nostr?.nip04?.encrypt).call(window.nostr?.nip04, key, content) + ); + } + + async nip4Decrypt(content: string, otherKey: string): Promise { + if (!window.nostr) { + throw new Error("Cannot use NIP-07 signer, not found!"); + } + return await barrierQueue(Nip7Queue, () => + unwrap(window.nostr?.nip04?.decrypt).call(window.nostr?.nip04, otherKey, content) + ); + } + + async sign(ev: NostrEvent): Promise { + if (!window.nostr) { + throw new Error("Cannot use NIP-07 signer, not found!"); + } + return await barrierQueue(Nip7Queue, () => unwrap(window.nostr).signEvent(ev)); + } + +} + +export class PrivateKeySigner implements EventSigner { + #publicKey: string; + #privateKey: string; + + constructor(privateKey: string) { + this.#privateKey = privateKey; + this.#publicKey = getPublicKey(privateKey); + } + + getPubKey(): string { + return this.#publicKey; + } + + async nip4Encrypt(content: string, key: string): Promise { + return await EventExt.encryptDm(content, this.#privateKey, key); + } + + async nip4Decrypt(content: string, otherKey: string): Promise { + return await EventExt.decryptDm(content, this.#privateKey, otherKey); + } + + sign(ev: NostrEvent): Promise { + EventExt.sign(ev, this.#privateKey); + return Promise.resolve(ev); + } +} + export class EventPublisher { #pubKey: string; - #privateKey?: string; + #signer: EventSigner; - constructor(pubKey: string, privKey?: string) { - if (privKey) { - this.#privateKey = privKey; - this.#pubKey = utils.bytesToHex(secp.schnorr.getPublicKey(privKey)); - } else { - this.#pubKey = pubKey; + constructor(signer: EventSigner, pubKey: string) { + this.#signer = signer; + this.#pubKey = pubKey; + } + + /** + * Create a NIP-07 EventPublisher + */ + static async nip7() { + if ("nostr" in window) { + const signer = new Nip7Signer(); + const pubkey = await signer.getPubKey(); + if (pubkey) { + return new EventPublisher(signer, pubkey); + } } } + /** + * Create an EventPublisher for a private key + */ + static privateKey(privateKey: string) { + const signer = new PrivateKeySigner(privateKey) + return new EventPublisher(signer, signer.getPubKey()); + } + get pubKey() { return this.#pubKey; } - get #hasNip07() { - return "nostr" in window; - } - - /** - * Get a NIP-07 EventPublisher - */ - static async nip7() { - if ("nostr" in window) { - const pubkey = await window.nostr?.getPublicKey(); - if (pubkey) { - return new EventPublisher(pubkey); - } - } - } - #eb(k: EventKind) { const eb = new EventBuilder(); return eb.pubKey(this.#pubKey).kind(k); } async #sign(eb: EventBuilder) { - if (this.#hasNip07 && !this.#privateKey) { - const nip7PubKey = await barrierQueue(Nip7Queue, () => unwrap(window.nostr).getPublicKey()); - if (nip7PubKey !== this.#pubKey) { - throw new Error("Can't sign event, NIP-07 pubkey does not match"); - } - const ev = eb.build(); - return await barrierQueue(Nip7Queue, () => unwrap(window.nostr).signEvent(ev)); - } else if (this.#privateKey) { - return await eb.buildAndSign(this.#privateKey); - } else { - throw new Error("Can't sign event, no private keys available"); - } + const ev = eb.build(); + return await this.#signer.sign(ev); } - async nip4Encrypt(content: string, key: HexKey) { - if (this.#hasNip07 && !this.#privateKey) { - const nip7PubKey = await barrierQueue(Nip7Queue, () => unwrap(window.nostr).getPublicKey()); - if (nip7PubKey !== this.#pubKey) { - throw new Error("Can't encrypt content, NIP-07 pubkey does not match"); - } - return await barrierQueue(Nip7Queue, () => - unwrap(window.nostr?.nip04?.encrypt).call(window.nostr?.nip04, key, content) - ); - } else if (this.#privateKey) { - return await EventExt.encryptDm(content, this.#privateKey, key); - } else { - throw new Error("Can't encrypt content, no private keys available"); - } + async nip4Encrypt(content: string, otherKey: string) { + return await this.#signer.nip4Encrypt(content, otherKey); } - async nip4Decrypt(content: string, otherKey: HexKey) { - if (this.#hasNip07 && !this.#privateKey && window.nostr?.nip04?.decrypt) { - return await barrierQueue(Nip7Queue, () => - unwrap(window.nostr?.nip04?.decrypt).call(window.nostr?.nip04, otherKey, content) - ); - } else if (this.#privateKey) { - return await EventExt.decryptDm(content, this.#privateKey, otherKey); - } else { - throw new Error("Can't decrypt content, no private keys available"); - } + async nip4Decrypt(content: string, otherKey: string) { + return await this.#signer.nip4Decrypt(content, otherKey); } async nip42Auth(challenge: string, relay: string) {