12345678910111213import { describe, expect, it } from "vitest";14import {15 exportEntriesToPeachJson,16 importEntriesFromPeachJson,17 derivePeachExportKey,18} from "./backup";19import { encrypt } from "../background/aes";20import type { VaultEntry } from "./types";212223242526const NFD_PASSWORD = "café";2728const NFC_PASSWORD = "café";293031if ((NFD_PASSWORD as string) === NFC_PASSWORD) {32 throw new Error("Test assumption broken: NFD and NFC passwords are identical on this platform");33}34if (NFD_PASSWORD.normalize("NFC") !== NFC_PASSWORD) {35 throw new Error("Test assumption broken: NFD normalize(NFC) !== NFC");36}3738function sampleEntries(): VaultEntry[] {39 return [40 {41 id: "test-entry-1",42 type: "login",43 title: "Test Site",44 username: "[email protected]",45 password: "correct-horse-battery",46 url: "https://example.com",47 createdAt: 1700000000000,48 modifiedAt: 1700000000000,49 },50 ];51}5253describe("T-006: backup/import NFC raw-fallback decrypt", () => {54 55 56 it("T-006a: export sealed with NFC password → import with NFC password succeeds", async () => {57 const envelope = await exportEntriesToPeachJson(sampleEntries(), NFC_PASSWORD);58 const result = await importEntriesFromPeachJson(JSON.stringify(envelope), NFC_PASSWORD);59 expect(result.entries).toHaveLength(1);60 expect(result.entries[0].title).toBe("Test Site");61 });6263 64 65 66 67 68 69 it("T-006b: export sealed with NFD password (raw) → import with NFD password succeeds via fallback", async () => {70 71 const salt = crypto.getRandomValues(new Uint8Array(16));72 const keyBytes = await derivePeachExportKey(NFD_PASSWORD, salt, { raw: true });73 const inner = { entries: sampleEntries() };74 const { nonce, ciphertext, tag } = await encrypt(keyBytes, JSON.stringify(inner));7576 function toHex(buf: Uint8Array): string {77 return Array.from(buf).map(b => b.toString(16).padStart(2, "0")).join("");78 }7980 const envelope = JSON.stringify({81 version: 2,82 type: "peach-export",83 kdf: "PBKDF2-SHA256",84 salt: toHex(salt),85 nonce,86 ciphertext,87 tag,88 });8990 91 92 const result = await importEntriesFromPeachJson(envelope, NFD_PASSWORD);93 expect(result.entries).toHaveLength(1);94 expect(result.entries[0].title).toBe("Test Site");95 });9697 98 99 100 it("T-006c: export sealed with NFC password → import with NFD password succeeds", async () => {101 const envelope = await exportEntriesToPeachJson(sampleEntries(), NFC_PASSWORD);102 103 104 const result = await importEntriesFromPeachJson(JSON.stringify(envelope), NFD_PASSWORD);105 expect(result.entries).toHaveLength(1);106 expect(result.entries[0].title).toBe("Test Site");107 });108109 110 111 it("T-006d: completely wrong password → fails (neither NFC nor raw match)", async () => {112 const envelope = await exportEntriesToPeachJson(sampleEntries(), NFC_PASSWORD);113 await expect(114 importEntriesFromPeachJson(JSON.stringify(envelope), "wrong-password-entirely"),115 ).rejects.toThrow(/INVALID_EXPORT_PASSWORD/);116 });117118 119 120 121 it("T-006e: NFD and NFC forms of the same password both open the same vault", async () => {122 const envelope = await exportEntriesToPeachJson(sampleEntries(), NFC_PASSWORD);123 const envelopeStr = JSON.stringify(envelope);124125 126 const resultNfc = await importEntriesFromPeachJson(envelopeStr, NFC_PASSWORD);127 const resultNfd = await importEntriesFromPeachJson(envelopeStr, NFD_PASSWORD);128129 expect(resultNfc.entries[0].title).toBe("Test Site");130 expect(resultNfd.entries[0].title).toBe("Test Site");131 expect(resultNfc.entries[0].password).toBe(resultNfd.entries[0].password);132 });133});