1234567891011121314151617const KAT_PLAINTEXT = "peach self-test vector v1";18const KAT_PASSWORD = "kat-fixed-password-do-not-use";1920const KAT_SALT_HEX = "1719d355fb0e9b33cc95bca2298a00c9";2122const KAT_NONCE_HEX = "6b65792d6e6f6e63652d3031";2324function hexToBytes(hex: string): Uint8Array<ArrayBuffer> {25 const buf = new ArrayBuffer(hex.length / 2);26 const out = new Uint8Array(buf);27 for (let i = 0; i < out.length; i++) {28 out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);29 }30 return out;31}3233async function deriveKatKey(password: string, salt: Uint8Array<ArrayBuffer>): Promise<CryptoKey> {34 const enc = new TextEncoder();35 const keyMaterial = await crypto.subtle.importKey(36 "raw",37 enc.encode(password),38 "PBKDF2",39 false,40 ["deriveKey"]41 );42 return crypto.subtle.deriveKey(43 { name: "PBKDF2", salt, iterations: 1000, hash: "SHA-256" },44 keyMaterial,45 { name: "AES-GCM", length: 256 },46 false,47 ["encrypt", "decrypt"]48 );49}5051export async function runSelfTest(): Promise<{ ok: boolean; error?: string }> {52 try {53 const salt = hexToBytes(KAT_SALT_HEX);54 const nonce = hexToBytes(KAT_NONCE_HEX);55 const enc = new TextEncoder();56 const plaintext = enc.encode(KAT_PLAINTEXT);5758 const key = await deriveKatKey(KAT_PASSWORD, salt);5960 const ciphertext = await crypto.subtle.encrypt(61 { name: "AES-GCM", iv: nonce },62 key,63 plaintext64 );6566 const decrypted = await crypto.subtle.decrypt(67 { name: "AES-GCM", iv: nonce },68 key,69 ciphertext70 );7172 const restored = new TextDecoder().decode(decrypted);73 if (restored !== KAT_PLAINTEXT) {74 return {75 ok: false,76 error: `KAT FAIL: expected "${KAT_PLAINTEXT}", got "${restored}"`,77 };78 }7980 return { ok: true };81 } catch (err) {82 return { ok: false, error: `KAT ERROR: ${String(err)}` };83 }84}