import { useState, FormEvent } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { Shield } from 'lucide-react'; import api from '../lib/api'; import { useAuthStore } from '../lib/store'; import { generateKdfSalt, deriveKeyFromPassword, generateMasterKey, encryptMasterKey, generateX25519Keypair, encryptPrivateKey, } from '../crypto'; import { socket } from '../lib/ws'; export default function RegisterPage() { const navigate = useNavigate(); const { setAuth, setMasterKey, setKeyPair, setUsername } = useAuthStore(); const [username, setUsernameField] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirm, setConfirm] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); async function submit(e: FormEvent) { e.preventDefault(); setError(''); if (password !== confirm) { setError('Passwords do not match'); return; } if (password.length < 12) { setError('Password must be at least 12 characters'); return; } setLoading(true); try { // All crypto material generated client-side — nothing sensitive leaves the browser in plaintext const kdfSalt = await generateKdfSalt(); const derivedKey = await deriveKeyFromPassword(password, kdfSalt); const masterKey = await generateMasterKey(); const encryptedMasterKey = await encryptMasterKey(masterKey, derivedKey); // X25519 keypair for ECDH conversation key exchange const { publicKeyB64, privateKey, pkcs8Bytes } = await generateX25519Keypair(); const encryptedPrivKey = await encryptPrivateKey(pkcs8Bytes, masterKey); const res = await api.post('/auth/register', { username, email, password, kdfSalt, encryptedMasterKey, publicKey: publicKeyB64, encryptedPrivateKey: encryptedPrivKey, }); const { accessToken, refreshToken, userId } = res.data; setAuth({ accessToken, refreshToken, userId, email }); setMasterKey(masterKey); setKeyPair(publicKeyB64, privateKey); setUsername(username); socket.connect(accessToken); navigate('/messages'); } catch (err: any) { setError(err.response?.data?.error || err.message || 'Registration failed'); } finally { setLoading(false); } } return (

CloudLink

Create your encrypted account

Create account

{error &&

{error}

}

Your password derives the encryption key. If you forget it, your data cannot be recovered.

Already have an account?{' '} Sign in

); } function Field({ label, type, value, onChange }: { label: string; type: string; value: string; onChange: (v: string) => void; }) { return (
onChange(e.target.value)} required />
); }