From e6b350a1d48ca0490b7a3447bb8d0a4235514b76 Mon Sep 17 00:00:00 2001 From: Doan Luu Duc Tai Date: Fri, 30 Oct 2026 21:39:57 -0500 Subject: Implement experimental file-based encrypted private chat Add fixed-suite Rust/WASI identity files, signed challenge authentication, ciphertext-only SQLite storage, a minimal LibreJS-compatible interface, and consistent backup snapshots. Keep production unchanged and document the local workflow, limits, source distribution, and browser recovery tests. --- crates/chat-crypto/browser/wasi.mjs | 50 +++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 7 deletions(-) (limited to 'crates/chat-crypto/browser/wasi.mjs') diff --git a/crates/chat-crypto/browser/wasi.mjs b/crates/chat-crypto/browser/wasi.mjs index d27fe4b..80d1299 100644 --- a/crates/chat-crypto/browser/wasi.mjs +++ b/crates/chat-crypto/browser/wasi.mjs @@ -15,10 +15,14 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 -export async function runProbe(bytes) { +async function execute(bytes, input) { let memory; let output = ''; let errors = ''; + const stdoutDecoder = new TextDecoder('utf-8', { fatal: true }); + const stderrDecoder = new TextDecoder('utf-8', { fatal: true }); + const stdin = new TextEncoder().encode(input); + let position = 0; let randomCalls = 0; let randomBytes = 0; const data = () => new DataView(memory.buffer); @@ -54,15 +58,28 @@ export async function runProbe(bytes) { for (let index = 0; index < count; index += 1) { const address = data().getUint32(vectors + index * 8, true); const length = data().getUint32(vectors + index * 8 + 4, true); - const text = new TextDecoder().decode(new Uint8Array(memory.buffer, address, length)); + const text = (fd === 1 ? stdoutDecoder : stderrDecoder).decode(new Uint8Array(memory.buffer, address, length), { stream: true }); if (fd === 1) output += text; else errors += text; total += length; - if (output.length + errors.length > 65536) throw new Error('Probe output exceeded its limit'); + if (output.length + errors.length > 1048576) throw new Error('Probe output exceeded its limit'); } put32(written, total); return 0; }, - fd_read(fd, vectors, count, read) { if (fd !== 0) return 8; put32(read, 0); return 0; }, + fd_read(fd, vectors, count, read) { + if (fd !== 0) return 8; + let total = 0; + for (let index = 0; index < count && position < stdin.length; index += 1) { + const address = data().getUint32(vectors + index * 8, true); + const capacity = data().getUint32(vectors + index * 8 + 4, true); + const length = Math.min(capacity, stdin.length - position); + new Uint8Array(memory.buffer, address, length).set(stdin.subarray(position, position + length)); + position += length; + total += length; + } + put32(read, total); + return 0; + }, fd_fdstat_get(fd, address) { if (fd > 2) return 8; new Uint8Array(memory.buffer, address, 24).fill(0); @@ -97,10 +114,29 @@ export async function runProbe(bytes) { memory = instance.exports.memory; try { instance.exports._start(); } catch (error) { if (!(error instanceof Exit) || error.code !== 0) throw new Error(`${error.message}: ${errors}`); } - if (!output.startsWith('PASS: backend=OpenSSL ') || randomCalls === 0) { - throw new Error(`OpenSSL probe did not pass: ${errors || output}`); - } + output += stdoutDecoder.decode(); + errors += stderrDecoder.decode(); + stdin.fill(0); return { output, randomCalls, randomBytes }; } +async function runProbe(bytes) { + const result = await execute(bytes, ''); + if (!result.output.startsWith('PASS: backend=OpenSSL ') || result.randomCalls === 0) { + throw new Error('OpenSSL probe did not pass'); + } + return result; +} + +async function runCommand(bytes, request) { + const input = JSON.stringify(request); + if (input.length > 8388608) throw new Error('Crypto request exceeds its limit'); + const result = await execute(bytes, input); + const response = JSON.parse(result.output); + if (response.error) throw new Error(response.error); + return response; +} + +globalThis.stackChatWasi = Object.freeze({ runCommand, runProbe }); + // @license-end -- cgit v1.2.3