aboutsummaryrefslogtreecommitdiff
path: root/crates/chat-crypto/tests/browser.mjs
diff options
context:
space:
mode:
authorDoan Luu Duc Tai <doan@luuduct.ai>2026-10-30 17:24:45 -0500
committerDoan Luu Duc Tai <doan@luuduct.ai>2026-10-30 17:24:45 -0500
commit7a0ce2805258e8ac061202904379692d53d5dc95 (patch)
treea495aee0cbdc111cd9773f7d2d02753cd1d778d5 /crates/chat-crypto/tests/browser.mjs
parent436e217c7858888702d2b6d9e655597ccadb7281 (diff)
Prove OpenSSL PQ crypto in browser WASI and document the migrationfeature/private-chat
Diffstat (limited to 'crates/chat-crypto/tests/browser.mjs')
-rw-r--r--crates/chat-crypto/tests/browser.mjs36
1 files changed, 28 insertions, 8 deletions
diff --git a/crates/chat-crypto/tests/browser.mjs b/crates/chat-crypto/tests/browser.mjs
index c6aae93..8d2a9bb 100644
--- a/crates/chat-crypto/tests/browser.mjs
+++ b/crates/chat-crypto/tests/browser.mjs
@@ -20,8 +20,8 @@ import assert from 'node:assert/strict';
import { chromium } from '../../../.tools/browser/node_modules/playwright/index.mjs';
const assets = new Map([
- ['/crypto.js', ['stack_chat_crypto.js', 'text/javascript']],
- ['/stack_chat_crypto_bg.wasm', ['stack_chat_crypto_bg.wasm', 'application/wasm']],
+ ['/wasi.mjs', [resolve('crates/chat-crypto/browser/wasi.mjs'), 'text/javascript']],
+ ['/probe.wasm', [resolve('target/wasm32-wasip1/release/stack-chat-crypto.wasm'), 'application/wasm']],
]);
const server = createServer(async (request, response) => {
response.setHeader('Cache-Control', 'no-store');
@@ -33,7 +33,7 @@ const server = createServer(async (request, response) => {
const asset = assets.get(request.url);
if (!asset) { response.writeHead(404); response.end(); return; }
response.setHeader('Content-Type', asset[1]);
- response.end(await readFile(resolve('.tools/chat-proof', asset[0])));
+ response.end(await readFile(asset[0]));
});
await new Promise(resolveReady => server.listen(0, '127.0.0.1', resolveReady));
const origin = `http://127.0.0.1:${server.address().port}`;
@@ -47,17 +47,37 @@ try {
});
await page.goto(origin);
const result = await page.evaluate(async () => {
- const module = await import('/crypto.js');
- await module.default();
- return module.compatibility_probe();
+ const { runProbe } = await import('/wasi.mjs');
+ const bytes = await (await fetch('/probe.wasm', { cache: 'no-store' })).arrayBuffer();
+ const valid = await runProbe(bytes);
+ const again = await runProbe(bytes);
+ const getRandomValues = crypto.getRandomValues;
+ let rejected = false;
+ crypto.getRandomValues = () => { throw new Error('Entropy unavailable'); };
+ try { await runProbe(bytes); } catch (error) { rejected = error.message.includes("Entropy unavailable"); }
+ finally { crypto.getRandomValues = getRandomValues; }
+ const modified = new Uint8Array(bytes.slice(0));
+ const pattern = new TextEncoder().encode('random_get');
+ const offset = modified.findIndex((value, index) => pattern.every((byte, position) => modified[index + position] === byte));
+ if (offset < 0) throw new Error('Expected entropy import was not found');
+ modified.set(new TextEncoder().encode('reject_get'), offset);
+ let unknownImportRejected = false;
+ try { await runProbe(modified); }
+ catch (error) { unknownImportRejected = error.message.includes('Unsupported WASI import: wasi_snapshot_preview1.reject_get'); }
+ return { valid, again, rejected, unknownImportRejected };
});
- assert.match(result, /^PASS:/);
+ assert.match(result.valid.output, /^PASS: backend=OpenSSL 3\.6\.5;/);
+ assert.match(result.again.output, /^PASS: backend=OpenSSL 3\.6\.5;/);
+ assert.ok(result.valid.randomCalls > 0 && result.valid.randomBytes > 0);
+ assert.ok(result.rejected, 'Unavailable browser entropy must fail closed');
+ assert.ok(result.unknownImportRejected, 'Unknown host imports must fail closed');
assert.deepEqual(external, []);
assert.deepEqual(await page.context().cookies(), []);
assert.equal(await page.evaluate(() => localStorage.length + sessionStorage.length), 0);
assert.deepEqual(await page.evaluate(async () => indexedDB.databases()), []);
assert.equal(await page.evaluate(async () => (await navigator.serviceWorker.getRegistrations()).length), 0);
- console.log(result);
+ console.log(result.valid.output.trim());
+ console.log(`Browser CSPRNG: ${result.valid.randomCalls} calls, ${result.valid.randomBytes} bytes; missing entropy and unknown imports rejected.`);
console.log('PASS: actual Chromium WASM execution; no external requests, cookies, web storage, or IndexedDB.');
} finally {
clearTimeout(deadline);