zero-knowledge file sharing
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

use worker to encrypt/decrypt

Juliet f48d18b0 961767c7

+81 -11
+23
web/src/lib/crypto.worker.ts
··· 1 + import { importKey, encrypt, decrypt } from "./crypto"; 2 + 3 + self.onmessage = async (e: MessageEvent) => { 4 + const { type, keyEncoded } = e.data; 5 + try { 6 + const key = await importKey(keyEncoded); 7 + 8 + if (type === "encrypt") { 9 + const { fileName, fileBuffer } = e.data; 10 + const ciphertext = await encrypt(fileName, fileBuffer, key); 11 + self.postMessage({ ciphertext }, [ciphertext.buffer]); 12 + } else if (type === "decrypt") { 13 + const { ciphertext } = e.data; 14 + const { fileName, fileData } = await decrypt( 15 + new Uint8Array(ciphertext), 16 + key, 17 + ); 18 + self.postMessage({ fileName, fileData }, [fileData.buffer]); 19 + } 20 + } catch (err: any) { 21 + self.postMessage({ error: err.message }); 22 + } 23 + };
+24 -3
web/src/pages/Upload.tsx
··· 7 7 createEffect, 8 8 } from "solid-js"; 9 9 10 - import { generateKey, encrypt } from "../lib/crypto"; 10 + import { generateKey } from "../lib/crypto"; 11 11 import { formatBytes } from "../lib/utils"; 12 12 13 13 const viewClass = "flex flex-col items-center gap-2 sm:gap-3"; ··· 51 51 52 52 let fileInput!: HTMLInputElement; 53 53 let activeXhr: XMLHttpRequest | null = null; 54 + let worker: Worker | null = null; 54 55 55 56 const RADIUS = 130; 56 57 const CENTER = RADIUS + 10; ··· 135 136 }; 136 137 137 138 onMount(async () => { 139 + worker = new Worker(new URL("../lib/crypto.worker.ts", import.meta.url), { 140 + type: "module", 141 + }); 138 142 document.addEventListener("dragover", handleDragOver); 139 143 document.addEventListener("dragleave", handleDragLeave); 140 144 document.addEventListener("drop", handleDrop); ··· 157 161 document.removeEventListener("dragover", handleDragOver); 158 162 document.removeEventListener("dragleave", handleDragLeave); 159 163 document.removeEventListener("drop", handleDrop); 164 + worker?.terminate(); 160 165 }); 161 166 162 167 const removeFile = () => { ··· 185 190 setProgress(0); 186 191 187 192 try { 188 - const { key, encoded } = await generateKey(); 193 + const { encoded } = await generateKey(); 189 194 const buffer = await f.arrayBuffer(); 190 - const ciphertext = await encrypt(f.name || "file", buffer, key); 195 + const ciphertext = await new Promise<Uint8Array<ArrayBuffer>>( 196 + (resolve, reject) => { 197 + worker!.onmessage = (e) => { 198 + if (e.data.error) reject(new Error(e.data.error)); 199 + else resolve(e.data.ciphertext); 200 + }; 201 + worker!.postMessage( 202 + { 203 + type: "encrypt", 204 + fileName: f.name || "file", 205 + fileBuffer: buffer, 206 + keyEncoded: encoded, 207 + }, 208 + [buffer], 209 + ); 210 + }, 211 + ); 191 212 192 213 const formData = new FormData(); 193 214 formData.append("file", new Blob([ciphertext]));
+34 -8
web/src/pages/View.tsx
··· 1 1 import { useParams } from "@solidjs/router"; 2 - import { createSignal, Show, Switch, Match, onMount } from "solid-js"; 2 + import { 3 + createSignal, 4 + Show, 5 + Switch, 6 + Match, 7 + onMount, 8 + onCleanup, 9 + } from "solid-js"; 3 10 4 - import { importKey, decrypt } from "../lib/crypto"; 11 + import { importKey } from "../lib/crypto"; 5 12 import { 6 13 formatBytes, 7 14 formatExpiry, ··· 34 41 const [fileName, setFileName] = createSignal(""); 35 42 36 43 let decryptedBlob: Blob | null = null; 37 - let cryptoKey: CryptoKey; 44 + const worker = new Worker( 45 + new URL("../lib/crypto.worker.ts", import.meta.url), 46 + { 47 + type: "module", 48 + }, 49 + ); 50 + onCleanup(() => worker.terminate()); 38 51 39 52 onMount(async () => { 40 53 const id = params.id; ··· 47 60 } 48 61 49 62 try { 50 - cryptoKey = await importKey(keyEncoded); 63 + await importKey(keyEncoded); 51 64 } catch { 52 65 setError("Invalid key."); 53 66 setStage("error"); ··· 81 94 } 82 95 83 96 const buf = await res.arrayBuffer(); 84 - const { fileName: name, fileData } = await decrypt( 85 - new Uint8Array(buf), 86 - cryptoKey, 87 - ); 97 + const { fileName: name, fileData } = await new Promise<{ 98 + fileName: string; 99 + fileData: Uint8Array<ArrayBuffer>; 100 + }>((resolve, reject) => { 101 + worker.onmessage = (e) => { 102 + if (e.data.error) reject(new Error(e.data.error)); 103 + else resolve(e.data); 104 + }; 105 + worker.postMessage( 106 + { 107 + type: "decrypt", 108 + ciphertext: buf, 109 + keyEncoded: location.hash.slice(1), 110 + }, 111 + [buf], 112 + ); 113 + }); 88 114 const ext = getExt(name); 89 115 setFileName(name); 90 116