aboutsummaryrefslogtreecommitdiff
path: root/CryptoGzipModule-draft.js
diff options
context:
space:
mode:
Diffstat (limited to 'CryptoGzipModule-draft.js')
-rw-r--r--CryptoGzipModule-draft.js315
1 files changed, 0 insertions, 315 deletions
diff --git a/CryptoGzipModule-draft.js b/CryptoGzipModule-draft.js
deleted file mode 100644
index 2c1e45f..0000000
--- a/CryptoGzipModule-draft.js
+++ /dev/null
@@ -1,315 +0,0 @@
-/**
- * CryptoGzipModule — Draft for Dev Agent
- *
- * Commands:
- * gzip <path> → creates <path>.gz
- * gunzip <path.gz> → restores <path>
- * encrypt <path> → prompts passphrase → creates <path>.enc
- * decrypt <path.enc> → prompts passphrase → restores <path>
- *
- * Wires into WebX11 module system. Copy into modules/ext/ or modules/tools/.
- */
-
-class CryptoGzipModule {
- constructor() {
- this.commands = {};
- this.terminal = null;
- this._passphraseCache = new Map(); // session-only, never persisted
- }
-
- requirements() {
- const hasCrypto = !!(window.crypto && crypto.subtle);
- const hasCompression = !!(window.CompressionStream);
- const missing = [];
- if (!hasCrypto) missing.push('Web Crypto API not available (requires secure context)');
- if (!hasCompression) missing.push('CompressionStream not available (requires Chromium-based browser or recent Firefox)');
- return { met: missing.length === 0, missing };
- }
-
- getHelpInfo() {
- return {
- 'gzip': 'gzip <path> — Compress a VFS file to .gz',
- 'gunzip': 'gunzip <path.gz> — Decompress a .gz file in VFS',
- 'encrypt':'encrypt <path> — AES-256-GCM encrypt a VFS file (interactive passphrase)',
- 'decrypt':'decrypt <path.enc> — Decrypt a .enc file (interactive passphrase)',
- };
- }
-
- init(terminal) {
- this.terminal = terminal;
- this.commands = {
- 'gzip': (args) => this._cmdGzip(args),
- 'gunzip': (args) => this._cmdGunzip(args),
- 'encrypt':(args) => this._cmdEncrypt(args),
- 'decrypt':(args) => this._cmdDecrypt(args),
- };
- }
-
- // ============================================================
- // VFS WIRE POINTS — Dev Agent: replace with real VFS calls
- // ============================================================
-
- async _vfsRead(path) {
- // WIRE: return await this.terminal.vfs.readFile(path, 'string');
- // OR: return window.webx11_vfs.read(path);
- throw new Error('_vfsRead not wired');
- }
-
- async _vfsWrite(path, content) {
- // WIRE: await this.terminal.vfs.writeFile(path, content);
- // content is a string (base64 for binary-ish data)
- throw new Error('_vfsWrite not wired');
- }
-
- async _vfsExists(path) {
- // WIRE: return this.terminal.vfs.exists(path);
- throw new Error('_vfsExists not wired');
- }
-
- async _vfsDelete(path) {
- // WIRE: await this.terminal.vfs.delete(path);
- throw new Error('_vfsDelete not wired');
- }
-
- // ============================================================
- // Prompt Utility
- // ============================================================
-
- _promptPassphrase(message = 'Enter passphrase: ') {
- return new Promise((resolve) => {
- // WIRE: adapt to WebX11 terminal prompt
- // Option A: if terminal has hidden prompt:
- // this.terminal.prompt(message, { hidden: true }).then(resolve);
- // Option B: temporary override:
- const savedHandler = this.terminal.onCommand;
- this.terminal.print(message);
- this.terminal.onCommand = (line) => {
- this.terminal.onCommand = savedHandler;
- resolve(line);
- };
- });
- }
-
- _confirmPrompt(message) {
- return new Promise((resolve) => {
- this.terminal.print(message + ' (yes/no): ');
- const savedHandler = this.terminal.onCommand;
- this.terminal.onCommand = (line) => {
- this.terminal.onCommand = savedHandler;
- resolve(line.trim().toLowerCase() === 'yes');
- };
- });
- }
-
- // ============================================================
- // GZIP
- // ============================================================
-
- async _cmdGzip(args) {
- const path = args[0];
- if (!path) throw new Error('Usage: gzip <path>');
-
- const exists = await this._vfsExists(path);
- if (!exists) throw new Error(`File not found: ${path}`);
-
- const outPath = path + '.gz';
- if (await this._vfsExists(outPath)) {
- const ok = await this._confirmPrompt(`Overwrite ${outPath}?`);
- if (!ok) return 'Cancelled.';
- }
-
- const content = await this._vfsRead(path);
- const compressed = await this._gzipString(content);
- await this._vfsWrite(outPath, compressed);
- await this._vfsDelete(path);
-
- const ratio = (compressed.length / Math.max(content.length, 1)).toFixed(2);
- return `gzipped: ${path} → ${outPath} (${content.length} → ${compressed.length} chars, ratio ${ratio})`;
- }
-
- async _cmdGunzip(args) {
- const path = args[0];
- if (!path || !path.endsWith('.gz')) throw new Error('Usage: gunzip <path.gz>');
-
- const exists = await this._vfsExists(path);
- if (!exists) throw new Error(`File not found: ${path}`);
-
- const outPath = path.replace(/\.gz$/, '');
- if (await this._vfsExists(outPath)) {
- const ok = await this._confirmPrompt(`Overwrite ${outPath}?`);
- if (!ok) return 'Cancelled.';
- }
-
- const compressed = await this._vfsRead(path);
- const content = await this._gunzipString(compressed);
- await this._vfsWrite(outPath, content);
- await this._vfsDelete(path);
-
- return `gunzipped: ${path} → ${outPath} (${compressed.length} chars)`;
- }
-
- async _gzipString(str) {
- const encoder = new TextEncoder();
- const bytes = encoder.encode(str);
- const cs = new CompressionStream('gzip');
- const writer = cs.writable.getWriter();
- writer.write(bytes);
- writer.close();
- const compressed = await new Response(cs.readable).arrayBuffer();
- return this._bytesToBase64(new Uint8Array(compressed));
- }
-
- async _gunzipString(b64) {
- const compressed = this._base64ToBytes(b64);
- const ds = new DecompressionStream('gzip');
- const writer = ds.writable.getWriter();
- writer.write(compressed);
- writer.close();
- const decompressed = await new Response(ds.readable).arrayBuffer();
- return new TextDecoder().decode(decompressed);
- }
-
- // ============================================================
- // ENCRYPT / DECRYPT (AES-256-GCM)
- // ============================================================
-
- async _cmdEncrypt(args) {
- const path = args[0];
- if (!path) throw new Error('Usage: encrypt <path>');
-
- const exists = await this._vfsExists(path);
- if (!exists) throw new Error(`File not found: ${path}`);
-
- const outPath = path + '.enc';
- if (await this._vfsExists(outPath)) {
- const ok = await this._confirmPrompt(`Overwrite ${outPath}?`);
- if (!ok) return 'Cancelled.';
- }
-
- const passphrase = await this._promptPassphrase('Enter passphrase: ');
- if (!passphrase || passphrase.length < 4) throw new Error('Passphrase too short (min 4 chars)');
-
- const plaintext = await this._vfsRead(path);
- const envelope = await this._encryptString(plaintext, passphrase);
- await this._vfsWrite(outPath, JSON.stringify(envelope, null, 0));
- await this._vfsDelete(path);
-
- return `encrypted: ${path} → ${outPath}`;
- }
-
- async _cmdDecrypt(args) {
- const path = args[0];
- if (!path || !path.endsWith('.enc')) throw new Error('Usage: decrypt <path.enc>');
-
- const exists = await this._vfsExists(path);
- if (!exists) throw new Error(`File not found: ${path}`);
-
- const outPath = path.replace(/\.enc$/, '');
- if (await this._vfsExists(outPath)) {
- const ok = await this._confirmPrompt(`Overwrite ${outPath}?`);
- if (!ok) return 'Cancelled.';
- }
-
- const passphrase = await this._promptPassphrase('Enter passphrase: ');
- if (!passphrase) throw new Error('Passphrase required');
-
- const envelopeJson = await this._vfsRead(path);
- let envelope;
- try {
- envelope = JSON.parse(envelopeJson);
- } catch (e) {
- throw new Error('Invalid .enc file format (expected JSON envelope)');
- }
-
- const plaintext = await this._decryptEnvelope(envelope, passphrase);
- await this._vfsWrite(outPath, plaintext);
- await this._vfsDelete(path);
-
- return `decrypted: ${path} → ${outPath}`;
- }
-
- async _encryptString(plaintext, passphrase) {
- const salt = crypto.getRandomValues(new Uint8Array(16));
- const iv = crypto.getRandomValues(new Uint8Array(12));
- const key = await this._deriveKey(passphrase, salt);
-
- const encoder = new TextEncoder();
- const ciphertext = await crypto.subtle.encrypt(
- { name: 'AES-GCM', iv },
- key,
- encoder.encode(plaintext)
- );
-
- return {
- v: 1, // format version
- alg: 'AES-256-GCM+PBKDF2',
- salt: this._bytesToBase64(salt),
- iv: this._bytesToBase64(iv),
- ct: this._bytesToBase64(new Uint8Array(ciphertext)),
- };
- }
-
- async _decryptEnvelope(envelope, passphrase) {
- if (!envelope || envelope.v !== 1) throw new Error('Unsupported envelope version');
-
- const salt = this._base64ToBytes(envelope.salt);
- const iv = this._base64ToBytes(envelope.iv);
- const ct = this._base64ToBytes(envelope.ct);
- const key = await this._deriveKey(passphrase, salt);
-
- try {
- const decrypted = await crypto.subtle.decrypt(
- { name: 'AES-GCM', iv },
- key,
- ct
- );
- return new TextDecoder().decode(decrypted);
- } catch (e) {
- throw new Error('Decryption failed — wrong passphrase or corrupted file');
- }
- }
-
- async _deriveKey(passphrase, salt) {
- const encoder = new TextEncoder();
- const baseKey = await crypto.subtle.importKey(
- 'raw',
- encoder.encode(passphrase),
- { name: 'PBKDF2' },
- false,
- ['deriveKey']
- );
- return crypto.subtle.deriveKey(
- {
- name: 'PBKDF2',
- salt: salt,
- iterations: 100000,
- hash: 'SHA-256',
- },
- baseKey,
- { name: 'AES-GCM', length: 256 },
- false,
- ['encrypt', 'decrypt']
- );
- }
-
- // ============================================================
- // Base64 Helpers (binary-safe, no deprecated btoa/unescape)
- // ============================================================
-
- _bytesToBase64(bytes) {
- // Works in all modern browsers + WebX11
- const bin = Array.from(bytes, (b) => String.fromCharCode(b)).join('');
- return btoa(bin);
- }
-
- _base64ToBytes(b64) {
- const bin = atob(b64);
- return Uint8Array.from(bin, (c) => c.charCodeAt(0));
- }
-}
-
-// ================================================================
-// EXPORTS — uncomment the appropriate line for your loader:
-// ================================================================
-// window.CryptoGzipModule = CryptoGzipModule; // global registration
-// export { CryptoGzipModule }; // ES module