clone

tiny dependency-free nostr libraries for plain html, each under 5kb and named for an element. public domain

main
nobel / test / fluorine.test.js
fluorine.test.js 4.0K
1import { assert, assertEquals, assertRejects } from "jsr:@std/assert@1";2import { v2 } from "npm:nostr-tools@2.17.0/nip44";3import "../helium/helium.js";4import "../fluorine/fluorine.js";56const { helium, fluorine } = globalThis;7const V = JSON.parse(await Deno.readTextFile(new URL("./nip44.vectors.json", import.meta.url))).v2;8const hex = (b) => [...b].map((x) => x.toString(16).padStart(2, "0")).join("");9const bytes = (h) => Uint8Array.from(h.match(/../g), (x) => parseInt(x, 16));10const sha = async (x) => hex(new Uint8Array(await crypto.subtle.digest("SHA-256", typeof x == "string" ? new TextEncoder().encode(x) : x)));1112Deno.test("conversation keys match vectors", async () => {13 for (const v of V.valid.get_conversation_key) assertEquals(hex(await fluorine.key(v.sec1, v.pub2)), v.conversation_key);14});1516Deno.test("invalid conversation keys throw", async () => {17 for (const v of V.invalid.get_conversation_key) await assertRejects(() => fluorine.key(v.sec1, v.pub2), Error, undefined, v.note);18});1920Deno.test("message key nonces encrypt like nostr-tools", async () => {21 // message keys are internal; check they drive encryption identically to nostr-tools for each nonce22 const ck = bytes(V.valid.get_message_keys.conversation_key);23 for (const k of V.valid.get_message_keys.keys) {24 const n = bytes(k.nonce);25 assertEquals(await fluorine.encrypt("message keys", ck, n), v2.encrypt("message keys", ck, n));26 }27});2829Deno.test("padded lengths match vectors", () => {30 for (const [n, p] of V.valid.calc_padded_len) assertEquals(fluorine.pad(n), p);31});3233Deno.test("encrypt/decrypt vectors", async () => {34 for (const v of V.valid.encrypt_decrypt) {35 const ck = await fluorine.key(v.sec1, helium.pub(v.sec2));36 assertEquals(hex(ck), v.conversation_key);37 assertEquals(hex(await fluorine.key(v.sec2, helium.pub(v.sec1))), v.conversation_key);38 assertEquals(await fluorine.encrypt(v.plaintext, ck, bytes(v.nonce)), v.payload);39 assertEquals(await fluorine.decrypt(v.payload, ck), v.plaintext);40 }41});4243Deno.test("long message vectors", async () => {44 for (const v of V.valid.encrypt_decrypt_long_msg) {45 const text = v.pattern.repeat(v.repeat), ck = bytes(v.conversation_key);46 assertEquals(await sha(text), v.plaintext_sha256);47 const payload = await fluorine.encrypt(text, ck, bytes(v.nonce));48 assertEquals(await sha(payload), v.payload_sha256);49 assertEquals(await fluorine.decrypt(payload, ck), text);50 }51});5253Deno.test("invalid payloads are rejected", async () => {54 for (const v of V.invalid.decrypt) await assertRejects(() => fluorine.decrypt(v.payload, bytes(v.conversation_key)), Error, undefined, v.note);55});5657Deno.test("invalid message lengths are rejected", async () => {58 const ck = crypto.getRandomValues(new Uint8Array(32));59 for (const n of V.invalid.encrypt_msg_lengths) await assertRejects(() => fluorine.encrypt("a".repeat(n), ck));60});6162Deno.test("round trips with nostr-tools", async () => {63 for (let i = 0; i < 20; i++) {64 const a = helium.generate(), b = helium.generate();65 const ck = await fluorine.key(a, helium.pub(b));66 assertEquals(hex(ck), hex(v2.utils.getConversationKey(bytes(b), helium.pub(a))));67 const text = "héllo 🦄 ".repeat(1 + i * 37);68 assertEquals(v2.decrypt(await fluorine.encrypt(text, ck), ck), text);69 assertEquals(await fluorine.decrypt(v2.encrypt(text, ck), ck), text);70 }71});7273Deno.test("nip44 signer shape", async () => {74 const a = helium.generate(), b = helium.generate();75 const A = fluorine.nip44(a), B = fluorine.nip44(b);76 const c = await A.encrypt(helium.pub(b), "secret");77 assertEquals(await B.decrypt(helium.pub(a), c), "secret");78 assertEquals(await A.decrypt(helium.pub(b), c), "secret");79 assert(helium.signer.nip44);80 const local = helium.key();81 const d = await helium.signer.nip44.encrypt(helium.pub(b), "to b");82 assertEquals(await B.decrypt(helium.pub(local), d), "to b");83 assertEquals(await helium.signer.nip44.decrypt(helium.pub(b), await B.encrypt(helium.pub(local), "to local")), "to local");84});