aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-02-16 20:56:39 -0800
committerAdam Soltys <[email protected]>2026-02-16 20:56:39 -0800
commitb22c813f9fb3b4d69dade75d22e90e6779919f07 (patch)
tree173fe377e83ad65ac6721dd567d8e90d9d204be8 /src/lib
parent8bb150db1f73164030f9e8085c13765120363644 (diff)
downloadcoinos-ui-b22c813f9fb3b4d69dade75d22e90e6779919f07.tar.gz
coinos-ui-b22c813f9fb3b4d69dade75d22e90e6779919f07.zip
nostr key derivation
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/passwordCache.ts12
-rw-r--r--src/lib/walletEntropy.ts67
2 files changed, 73 insertions, 6 deletions
diff --git a/src/lib/passwordCache.ts b/src/lib/passwordCache.ts
index 98272e0f..0ed36af1 100644
--- a/src/lib/passwordCache.ts
+++ b/src/lib/passwordCache.ts
@@ -5,12 +5,12 @@ const PASSWORD_KEY = "walletPassword";
const EXPIRES_KEY = "walletPasswordExpiresAt";
export const rememberForOptions = [
- { label: "Don't remember", ms: 0 },
- { label: "8 hours", ms: 8 * 60 * 60 * 1000 },
- { label: "1 day", ms: 24 * 60 * 60 * 1000 },
- { label: "1 week", ms: 7 * 24 * 60 * 60 * 1000 },
- { label: "1 month", ms: 30 * 24 * 60 * 60 * 1000 },
- { label: "1 year", ms: 365 * 24 * 60 * 60 * 1000 },
+ { label: "user.settings.dontRemember", ms: 0 },
+ { label: "user.settings.8hours", ms: 8 * 60 * 60 * 1000 },
+ { label: "user.settings.1day", ms: 24 * 60 * 60 * 1000 },
+ { label: "user.settings.1week", ms: 7 * 24 * 60 * 60 * 1000 },
+ { label: "user.settings.1month", ms: 30 * 24 * 60 * 60 * 1000 },
+ { label: "user.settings.1year", ms: 365 * 24 * 60 * 60 * 1000 },
];
export const defaultRememberForMs = 30 * 24 * 60 * 60 * 1000;
diff --git a/src/lib/walletEntropy.ts b/src/lib/walletEntropy.ts
new file mode 100644
index 00000000..f022a2cc
--- /dev/null
+++ b/src/lib/walletEntropy.ts
@@ -0,0 +1,67 @@
+import { browser } from "$app/environment";
+import { getCachedPrfKey, rememberPrfKey, defaultRememberForMs } from "$lib/passwordCache";
+
+const DERIVATION_EVENT = {
+ kind: 1,
+ created_at: 0,
+ content: "coinos-wallet-key-derivation",
+ tags: [],
+};
+
+async function hashSignature(sig: string): Promise<ArrayBuffer> {
+ const sigBytes = new Uint8Array(sig.length / 2);
+ for (let i = 0; i < sig.length; i += 2)
+ sigBytes[i / 2] = parseInt(sig.slice(i, i + 2), 16);
+ return crypto.subtle.digest("SHA-256", sigBytes);
+}
+
+/**
+ * Silent helper. Checks localStorage for nsec, signs fixed event locally,
+ * caches and returns entropy. Returns null if no nsec stored.
+ */
+export async function tryDeriveFromNsec(): Promise<ArrayBuffer | null> {
+ if (!browser) return null;
+ const nsec = localStorage.getItem("nsec");
+ if (!nsec) return null;
+
+ try {
+ const { nip19, finalizeEvent } = await import("nostr-tools");
+ const sk = nip19.decode(nsec).data as Uint8Array;
+ const signed = finalizeEvent({ ...DERIVATION_EVENT } as any, sk);
+ const entropy = await hashSignature(signed.sig);
+ rememberPrfKey(entropy, defaultRememberForMs);
+ return entropy;
+ } catch (e) {
+ console.log("nsec entropy derivation failed", e);
+ return null;
+ }
+}
+
+/**
+ * Non-interactive. Checks cached prfKey, then tries silent nsec derivation.
+ * Returns null if neither available. Use in onMount and auto-provisioning.
+ */
+export async function getWalletEntropy(): Promise<ArrayBuffer | null> {
+ const cached = getCachedPrfKey();
+ if (cached) return cached;
+ return tryDeriveFromNsec();
+}
+
+/**
+ * Interactive. Tries silent nsec first, then calls sign() from $lib/nostr
+ * which shows Nostr.svelte modal if signer isn't ready. Caches result.
+ * Use for "Unlock" buttons.
+ */
+export async function deriveNostrEntropy(): Promise<ArrayBuffer> {
+ const cached = getCachedPrfKey();
+ if (cached) return cached;
+
+ const fromNsec = await tryDeriveFromNsec();
+ if (fromNsec) return fromNsec;
+
+ const { sign } = await import("$lib/nostr");
+ const signed = await sign({ ...DERIVATION_EVENT });
+ const entropy = await hashSignature(signed.sig);
+ rememberPrfKey(entropy, defaultRememberForMs);
+ return entropy;
+}