Created
November 18, 2025 01:43
-
-
Save williamzujkowski/a69980ca2a6261aaabb00b84d3b8d853 to your computer and use it in GitHub Desktop.
Zero-Knowledge Authentication Client - Browser JavaScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Zero-Knowledge Authentication Client (Browser) | |
| // Generates ZK-SNARK proofs without transmitting password | |
| async function register(username, password) { | |
| // Generate ZK circuit keys | |
| const { publicKey, privateKey } = await generateZKKeys(); | |
| // Hash password locally | |
| const passwordHash = await sha256(password); | |
| // Encrypt private key with password (stored in browser) | |
| const encryptedKey = await encryptKey(privateKey, password); | |
| localStorage.setItem('zk_private_key', encryptedKey); | |
| // Send only public key to server | |
| const response = await fetch('/api/register', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| username: username, | |
| public_key: publicKey, | |
| password_hash: passwordHash | |
| }) | |
| }); | |
| return await response.json(); | |
| } | |
| async function login(username, password) { | |
| // Get user's public key from server | |
| const publicKeyResp = await fetch(`/api/public-key/${username}`); | |
| const { publicKey, expectedHash } = await publicKeyResp.json(); | |
| // Retrieve encrypted private key from browser storage | |
| const encryptedKey = localStorage.getItem('zk_private_key'); | |
| const privateKey = await decryptKey(encryptedKey, password); | |
| // Generate ZK-SNARK proof locally (password never sent) | |
| const proof = await generateZKProof({ | |
| public: { | |
| username_hash: await sha256(username), | |
| expected_hash: expectedHash | |
| }, | |
| private: { | |
| password: password // Stays in browser, never transmitted | |
| } | |
| }, privateKey); | |
| // Send only proof to server | |
| const authResp = await fetch('/api/auth/verify', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| username: username, | |
| proof: proof // ~400 bytes, no password info | |
| }) | |
| }); | |
| const { access_token } = await authResp.json(); | |
| localStorage.setItem('jwt_token', access_token); | |
| return access_token; | |
| } | |
| async function sha256(message) { | |
| const msgBuffer = new TextEncoder().encode(message); | |
| const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); | |
| return Array.from(new Uint8Array(hashBuffer)) | |
| .map(b => b.toString(16).padStart(2, '0')) | |
| .join(''); | |
| } | |
| async function generateZKKeys() { | |
| // Actual implementation would use zksnark.js library | |
| // Simplified for demonstration | |
| return { | |
| publicKey: crypto.randomUUID(), | |
| privateKey: crypto.randomUUID() | |
| }; | |
| } | |
| async function generateZKProof(inputs, privateKey) { | |
| // Actual implementation would use zksnark.js prove() | |
| // Returns ~400 byte proof | |
| return new Uint8Array(400); | |
| } | |
| async function encryptKey(key, password) { | |
| // Encrypt private key with password using AES-GCM | |
| const passwordKey = await crypto.subtle.importKey( | |
| 'raw', | |
| new TextEncoder().encode(password), | |
| { name: 'PBKDF2' }, | |
| false, | |
| ['deriveKey'] | |
| ); | |
| const aesKey = await crypto.subtle.deriveKey( | |
| { name: 'PBKDF2', salt: crypto.getRandomValues(new Uint8Array(16)), iterations: 100000, hash: 'SHA-256' }, | |
| passwordKey, | |
| { name: 'AES-GCM', length: 256 }, | |
| false, | |
| ['encrypt'] | |
| ); | |
| const iv = crypto.getRandomValues(new Uint8Array(12)); | |
| const encrypted = await crypto.subtle.encrypt( | |
| { name: 'AES-GCM', iv: iv }, | |
| aesKey, | |
| new TextEncoder().encode(key) | |
| ); | |
| return btoa(String.fromCharCode(...new Uint8Array(encrypted))); | |
| } | |
| async function decryptKey(encryptedKey, password) { | |
| // Decrypt private key with password | |
| // Implementation mirrors encryptKey | |
| return "decrypted_key"; // Simplified | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment