aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-02-24 12:17:11 -0800
committerAdam Soltys <[email protected]>2026-02-24 12:17:11 -0800
commitac550ea1bbe0b29a363ee668d7b8c21dd3b35f6e (patch)
tree1cb87458dcb4448677da89675abb52d50f474ad7 /src/lib
parentf576478465f4192928d387d3408de54b1f369bf8 (diff)
parent8ab48973812289c19f48d15b7a9771a89d35797a (diff)
downloadcoinos-ui-ac550ea1bbe0b29a363ee668d7b8c21dd3b35f6e.tar.gz
coinos-ui-ac550ea1bbe0b29a363ee668d7b8c21dd3b35f6e.zip
Merge remote-tracking branch 'origin/nip17dm' into ark2
# Conflicts: # src/lib/nip07.ts # src/lib/nip17.ts # src/locales/en.json # src/locales/fr.json # src/routes/(app)/dm/+page.svelte
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/nip07.ts123
-rw-r--r--src/lib/nip17.ts80
2 files changed, 99 insertions, 104 deletions
diff --git a/src/lib/nip07.ts b/src/lib/nip07.ts
index c26226d9..a0af0861 100644
--- a/src/lib/nip07.ts
+++ b/src/lib/nip07.ts
@@ -1,39 +1,102 @@
-import { bytesToHex } from "@noble/hashes/utils.js";
-import { finalizeEvent } from "nostr-tools/pure";
-import { getPrivateKey } from "$lib/nostr";
-import * as libnip44 from "$lib/nip44";
-import { u } from "$lib/nip44";
+import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
+import { finalizeEvent } from 'nostr-tools/pure';
+import * as libnip44 from '$lib/nip44';
+import { u } from '$lib/nip44';
+
+import { eventToSign, signer } from '$lib/store';
+import { get } from 'svelte/store';
+
+// Ensures we have access to a secret key, triggering the signer modal if needed.
+// Returns the sk as Uint8Array, or null if using NIP-07 extension.
+export const ensureSigner = async (userPubkey: string): Promise<Uint8Array | null> => {
+ // Check if NIP-07 extension is available and matches user
+ if (window.nostr) {
+ try {
+ const extPubkey = await window.nostr.getPublicKey();
+ if (extPubkey === userPubkey) {
+ return null; // Use NIP-07
+ }
+ } catch (e) {
+ // Extension didn't work, fall through to signer
+ }
+ }
+
+ // Check if signer already has a valid sk (64 hex chars = 32 bytes)
+ const currentSigner = get(signer);
+ if (currentSigner?.ready && currentSigner?.params?.sk && currentSigner.params.sk.length === 64) {
+ return hexToBytes(currentSigner.params.sk);
+ }
+
+ // Clear invalid signer state before triggering modal
+ if (currentSigner?.ready && (!currentSigner?.params?.sk || currentSigner.params.sk.length !== 64)) {
+ signer.set(undefined);
+ }
+
+ // Trigger the signer modal with a dummy event
+ const dummyEvent = { kind: 14, content: 'DM encryption', tags: [], created_at: Math.floor(Date.now() / 1000) };
+ eventToSign.set(dummyEvent);
+
+ // Wait for signer to be ready
+ const signerValue = await new Promise<any>((resolve, reject) => {
+ let unsubscribe: () => void;
+ unsubscribe = signer.subscribe((v) => {
+ if (v === 'cancel') {
+ setTimeout(() => unsubscribe?.());
+ eventToSign.set(null);
+ reject(new Error('cancelled'));
+ }
+ if (v?.ready) {
+ setTimeout(() => unsubscribe?.());
+ eventToSign.set(null);
+ resolve(v);
+ }
+ });
+ });
+
+ if (signerValue?.params?.sk) {
+ return hexToBytes(signerValue.params.sk);
+ }
+
+ // If extension method was selected
+ if (signerValue?.method === 'extension') {
+ return null; // Use NIP-07
+ }
+
+ throw new Error('No signing method available');
+}
// Signs an event using NIP-07 if available, otherwise getPrivateKey()
export const signEvent = async (event: any, user: any): Promise<any> => {
- if ((await (window as any).nostr.getPublicKey()) === user.pubkey) {
- return (window as any).nostr.signEvent(event);
- } else {
- const sk = await getPrivateKey(user);
- return finalizeEvent(event, sk);
- }
-};
+ const sk = await ensureSigner(user.pubkey);
+ if (sk === null) {
+ return (window as any).nostr.signEvent(event);
+ } else {
+ return finalizeEvent(event, sk);
+ }
+}
// Encrypts an event in NIP-44 using NIP-07 if available, otherwise getPrivateKey()
export const encrypt = async (json: any, user: any, receiverPK: string): Promise<string> => {
- if ((await (window as any).nostr.getPublicKey()) === user.pubkey) {
- return (window as any).nostr.nip44.encrypt(receiverPK, JSON.stringify(json));
- } else {
- const senderSK = await getPrivateKey(user);
- const senderSKString = bytesToHex(senderSK);
- const ckey = u.getConversationKey(senderSKString, receiverPK);
- return libnip44.encrypt(JSON.stringify(json), ckey);
- }
-};
+ const senderSK = await ensureSigner(user.pubkey);
+ if (senderSK === null) {
+ return (window as any).nostr.nip44.encrypt(
+ receiverPK, JSON.stringify(json));
+ } else {
+ const senderSKString = bytesToHex(senderSK);
+ const ckey = u.getConversationKey(senderSKString, receiverPK);
+ return libnip44.encrypt(JSON.stringify(json), ckey);
+ }
+}
// Decrypts an event in NIP-44 using NIP-07 if available, otherwise getPrivateKey()
export const decrypt = async (encrypted: string, user: any, receiverPK: string): Promise<any> => {
- if ((await (window as any).nostr.getPublicKey()) === user.pubkey) {
- return JSON.parse(await (window as any).nostr.nip44.decrypt(receiverPK, encrypted));
- } else {
- const senderSK = await getPrivateKey(user);
- const senderSKString = bytesToHex(senderSK);
- const ckey = u.getConversationKey(senderSKString, receiverPK);
- return JSON.parse(libnip44.decrypt(encrypted, ckey));
- }
-};
+ const senderSK = await ensureSigner(user.pubkey);
+ if (senderSK === null) {
+ return JSON.parse(await (window as any).nostr.nip44.decrypt(
+ receiverPK, encrypted));
+ } else {
+ const senderSKString = bytesToHex(senderSK);
+ const ckey = u.getConversationKey(senderSKString, receiverPK);
+ return JSON.parse(libnip44.decrypt(encrypted, ckey));
+ }
+}
diff --git a/src/lib/nip17.ts b/src/lib/nip17.ts
index d0b32d78..0398c426 100644
--- a/src/lib/nip17.ts
+++ b/src/lib/nip17.ts
@@ -5,88 +5,20 @@ import { SimplePool } from "nostr-tools/pool";
import { Relay } from "nostr-tools/relay";
import { unwrapEvent } from "nostr-tools/nip17";
-import { relaysSupporting } from "$lib/nip11";
-import { expired, expiration } from "$lib/nip40";
-import { encrypt, u } from "$lib/nip44";
-import { eventToSign, signer } from "$lib/store";
-import { get } from "svelte/store";
+import { ensureSigner } from '$lib/nip07';
+import { relaysSupporting } from '$lib/nip11';
+import { expired, expiration } from '$lib/nip40';
+import { encrypt, u } from '$lib/nip44';
const TWO_DAYS = 2 * 24 * 60 * 60;
const randomTimestamp = () => Math.floor(Date.now() / 1000 - Math.random() * TWO_DAYS);
const pool = new SimplePool();
let relayListCache = new Map();
-import { PUBLIC_DM_RELAYS } from "$env/static/public";
-const DM_RELAYS_LIST = PUBLIC_DM_RELAYS.split(",");
+import { PUBLIC_DM_RELAYS } from '$env/static/public';
+const DM_RELAYS_LIST = PUBLIC_DM_RELAYS.split(',');
const DM_FETCH_LIMIT = 65536;
-// Ensures we have access to a secret key, triggering the signer modal if needed.
-// Returns the sk as Uint8Array, or null if using NIP-07 extension.
-const ensureSigner = async (userPubkey: string): Promise<Uint8Array | null> => {
- // Check if NIP-07 extension is available and matches user
- if ((window as any).nostr) {
- try {
- const extPubkey = await (window as any).nostr.getPublicKey();
- if (extPubkey === userPubkey) {
- return null; // Use NIP-07
- }
- } catch {
- // Extension didn't work, fall through to signer
- }
- }
-
- // Check if signer already has a valid sk (64 hex chars = 32 bytes)
- const currentSigner: any = get(signer);
- if (currentSigner?.ready && currentSigner?.params?.sk && currentSigner.params.sk.length === 64) {
- return hexToBytes(currentSigner.params.sk);
- }
-
- // Clear invalid signer state before triggering modal
- if (
- currentSigner?.ready &&
- (!currentSigner?.params?.sk || currentSigner.params.sk.length !== 64)
- ) {
- signer.set(undefined);
- }
-
- // Trigger the signer modal with a dummy event
- const dummyEvent = {
- kind: 14,
- content: "DM encryption",
- tags: [],
- created_at: Math.floor(Date.now() / 1000),
- };
- eventToSign.set(dummyEvent);
-
- // Wait for signer to be ready
- const signerValue = await new Promise<any>((resolve, reject) => {
- let unsubscribe: () => void;
- unsubscribe = signer.subscribe((v: any) => {
- if (v === "cancel") {
- setTimeout(() => unsubscribe?.());
- eventToSign.set(null);
- reject(new Error("cancelled"));
- }
- if (v?.ready) {
- setTimeout(() => unsubscribe?.());
- eventToSign.set(null);
- resolve(v);
- }
- });
- });
-
- if (signerValue?.params?.sk) {
- return hexToBytes(signerValue.params.sk);
- }
-
- // If extension method was selected
- if (signerValue?.method === "extension") {
- return null; // Use NIP-07
- }
-
- throw new Error("No signing method available");
-};
-
// Create a NIP-17 message using a provided sender secret key.
// If wrapPK is specified and different than receiverPK,
// the message will be gift-wrapped to wrapPK