aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/walletEntropy.ts
blob: e203ed69a574b3a5ac357aa119c36d201b42f16a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import { browser } from "$app/environment";
import { getCachedPrfKey } 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 Promise.all([
      import("nostr-tools/nip19"),
      import("nostr-tools/pure"),
    ]);
    const sk = nip19.decode(nsec).data as Uint8Array;
    const signed = finalizeEvent({ ...DERIVATION_EVENT } as any, sk);
    return await hashSignature(signed.sig);
  } 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 });
  return hashSignature(signed.sig);
}