diff options
| author | Adam Soltys <[email protected]> | 2024-03-26 08:22:36 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2024-03-26 08:22:36 -0700 |
| commit | fb20dfb656903dfa32774b130692fae9c6df947a (patch) | |
| tree | 6f017b6de45bf29ddacdb0dff22d30263c6d63ee | |
| parent | 2746800495d3572ac354c843040b2fcdd603b882 (diff) | |
| download | coinos-ui-fb20dfb656903dfa32774b130692fae9c6df947a.tar.gz coinos-ui-fb20dfb656903dfa32774b130692fae9c6df947a.zip | |
deps
| -rwxr-xr-x | bun.lockb | bin | 121205 -> 127209 bytes | |||
| -rw-r--r-- | package.json | 4 | ||||
| -rw-r--r-- | src/lib/nostr.js | 95 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/messages/+page.svelte | 2 |
4 files changed, 59 insertions, 42 deletions
| Binary files differ diff --git a/package.json b/package.json index 1bb77abd..560875e1 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,11 @@ "bech32": "^2.0.0", "bip21": "^2.0.3", "bitcoin-address-validation": "^2.2.1", + "bits-ui": "^0.21.0", "buffer": "^6.0.3", + "clsx": "^2.1.0", "date-fns": "^2.29.3", + "gsap": "^3.12.5", "html5-qrcode": "^2.3.8", "leaflet": "^1.9.3", "leaflet.markercluster": "^1.5.3", @@ -37,6 +40,7 @@ "svelte-virtual-scroll-list": "^1.1.0", "sveltekit-i18n": "^2.2.2", "tailwind-scrollbar": "^2.1.0", + "tailwind-variants": "^0.2.1", "tailwindcss": "^3.2.4", "uint8array-extras": "^1.0.0", "unique-names-generator": "^4.7.1", diff --git a/src/lib/nostr.js b/src/lib/nostr.js index 5ab87e51..5209418a 100644 --- a/src/lib/nostr.js +++ b/src/lib/nostr.js @@ -1,3 +1,5 @@ +import { browser } from "$app/environment"; +import { hexToUint8Array } from "uint8array-extras"; import { get } from "svelte/store"; import { Buffer } from "buffer"; import { fail, wait, post, stretch } from "$lib/utils"; @@ -11,22 +13,26 @@ import { import { goto, invalidate } from "$app/navigation"; import { getPublicKey, - signEvent, + finalizeEvent, getEventHash, nip04, - nip06, nip19, } from "nostr-tools"; + +import { + encrypt as nip49encrypt, + decrypt as nip49decrypt, +} from "nostr-tools/nip49"; + import { mnemonicToEntropy, entropyToMnemonic } from "@scure/bip39"; import wordlist from "$lib/english"; import { bech32m } from "@scure/base"; -const { generateSeedWords, privateKeyFromSeedWords } = nip06; +import { generateSeedWords, privateKeyFromSeedWords } from "nostr-tools/nip06"; const { encode, decode, toWords, fromWords } = bech32m; export let generate = async (user) => { if (!get(pin)?.length === 6) return; - let password = get(pw); let salt = crypto.getRandomValues(new Uint8Array(16)); let mnemonic = generateSeedWords(); @@ -35,7 +41,7 @@ export let generate = async (user) => { let bytes = new Uint8Array( await crypto.subtle.encrypt( { name: "AES-GCM", iv: new Uint8Array(16) }, - await stretch(password, salt), + await stretch(await getPassword(), salt), Uint8Array.from(Buffer.from(mnemonicToEntropy(mnemonic, wordlist), "hex")) ) ); @@ -46,7 +52,8 @@ export let generate = async (user) => { }; export let encrypt = async ({ message, recipient, user }) => { - return nip04.encrypt(await getPrivateKey(user), recipient, message); + let sk = await getPrivateKey(user); + return nip04.encrypt(sk, recipient, message); }; export let decrypt = async ({ event, user }) => { @@ -78,29 +85,33 @@ function typedArrayToBuffer(array) { ); } -export let getMnemonic = async (user) => { - let { cipher, username, salt } = user; - let password = get(pw); - - if (!password) { - passwordPrompt.set(true); +export let getPrivateKey = async (user) => { + let k; + if (browser) { + k = localStorage.getItem("nsec"); + if (k) { + return nip19.decode(k).data; + } } - try { - await post("/password", { password }); - } catch (e) { - passwordPrompt.set(true); - } + let { nsec, username, salt } = user; - await wait(() => !!get(pw)); - password = get(pw); + if (nsec) { + k = nip49decrypt(nsec, await getPassword()); + } else k = hexToUint8Array(privateKeyFromSeedWords(await getMnemonic(user))); + + localStorage.setItem("nsec", nip19.nsecEncode(k)); + return k; +}; +export let getMnemonic = async (user) => { + let { cipher, username, salt } = user; let mnemonic, key, seed, entropy, child, privkey; entropy = new Uint8Array( await crypto.subtle.decrypt( { name: "AES-GCM", iv: new Uint8Array(16) }, - await stretch(password, Buffer.from(salt, "hex")), + await stretch(await getPassword(), Buffer.from(salt, "hex")), Uint8Array.from(fromWords(decode(cipher, 180).words)) ) ); @@ -108,45 +119,47 @@ export let getMnemonic = async (user) => { return entropyToMnemonic(entropy, wordlist); }; -let getPrivateKey = async (user) => { - return privateKeyFromSeedWords(await getMnemonic(user)); -}; - export let getNsec = async (user) => { return nip19.nsecEncode(await getPrivateKey(user)); }; +export let setNsec = async (user, nsec) => { + let { data: sk } = nip19.decode(nsec); + user.pubkey = getPublicKey(sk); + user.nsec = await encryptNsec(nsec); +}; + +export let encryptNsec = async (nsec) => { + let { type, data } = nip19.decode(nsec); + if (type === "nsec" && data.length === 32) { + return nip49encrypt(data, await getPassword()); + } else { + fail("invalid nsec"); + } +}; + export let sign = async ({ event, user }) => { - event.id = getEventHash(event); - event.sig = signEvent(event, await getPrivateKey(user)); + event = finalizeEvent(event, await getPrivateKey(user)); }; export let send = (event) => { return post("/events", { event }); }; +let getPassword = async () => { + if (!get(pw)) passwordPrompt.set(true); + await wait(() => !!get(pw)); + return get(pw); +}; + export let reEncryptEntropy = async (user, newPassword) => { let { cipher, username, salt } = user; - let password = get(pw); - - if (!password) { - passwordPrompt.set(true); - } - - try { - await post("/password", { password }); - } catch (e) { - passwordPrompt.set(true); - } - - await wait(() => !!get(pw)); - password = get(pw); let mnemonic, key, seed, entropy, child, privkey; entropy = Buffer.from( await crypto.subtle.decrypt( { name: "AES-GCM", iv: new Uint8Array(16) }, - await stretch(password, Buffer.from(salt, "hex")), + await stretch(await getPassword(), Buffer.from(salt, "hex")), Uint8Array.from(fromWords(decode(cipher, 180).words)) ), "hex" diff --git a/src/routes/(app)/[username]/messages/+page.svelte b/src/routes/(app)/[username]/messages/+page.svelte index 263f7294..b36eab2a 100644 --- a/src/routes/(app)/[username]/messages/+page.svelte +++ b/src/routes/(app)/[username]/messages/+page.svelte @@ -11,7 +11,7 @@ import { sign, send, encrypt, decrypt } from "$lib/nostr"; import { event as e, password } from "$lib/store"; import { tick, onMount } from "svelte"; - import { getEventHash } from "nostr-tools"; + import { getEventHash } from "@asoltys/nostr-tools"; export let data; |
