Arquitectura de cifrado Kalista (.kal v3pin)
Resumen
Kalista persiste historial financiero en blobs .kal cifrados con AES-256-GCM; el vault (KALV) requiere PIN derivado con Argon2id en el browser (v3pin). El staging (KALS) usa HKDF(zolski_token) y es descifrable server-side — diseño débil documentado.
Contexto y motivación
Kalista es local-first: la fuente de verdad vive en el Drive/OneDrive del usuario. El servidor es procesador efímero entre Gmail/Outlook y la nube del usuario. v3pin separa credenciales Folks del PIN de bóveda para que reset de password no exponga el vault.
Componentes
| Archivo | Magic | Clave | Quién descifra |
|---|---|---|---|
kalista-profile.vault.kal |
KALV | Argon2id(PIN, user_id) → AES-256-GCM | Solo browser |
kalista-profile.staging.kal |
KALS | HKDF(zolski_token, user_code) | Servidor (merge pipeline) |
Legacy kalista-profile.kal |
KAL2 | Migración one-shot | Deprecado |
Derivación v3pin
| Parámetro | Valor |
|---|---|
| KDF | Argon2id, 64 MiB, t=3, p=1 |
| Salt | user_id (UUID Folks) |
| PIN charset | 34 chars (A–Z, 1–9, sin O/0) |
| Espacio | 34⁶ ≈ 1.5×10⁹ (~30.6 bits) |
| KALV header | version 0x02, kdf_id=0x03 |
El PIN nunca llega al servidor (zero-knowledge unlock).
Flujo ingest (KAL_V3_CLIENT_RENDER=1)
- Backend parsea emails/PDFs en memoria.
- Worker cifra staging (KALS) y sube a Drive.
- Browser:
mergeStagingFromServer()descifra staging, mergea vault en RAM, re-cifra KALV, sube víavault-upload. - Dashboard renderiza desde
ProfileStore— sin APIs de datos en servidor.
Threat model
Protege contra: lectura no autorizada del blob en Drive sin PIN; dump DB sin tokens de descifrado del vault; cross-user contamination (HKDF per-sub, AAD=sub).
No protege contra: RCE con MASTER_KEY (descifra OAuth/PII, no PIN); cookies robadas (actúa como usuario); XSS en dashboard (perfil en RAM); staging legible con zolski_token.
Script de verificación: scripts/verify_v3_threat_model.py — vault con zolski_token debe dar rejected_gcm.
Invariantes
- Montos y descripciones nunca en plaintext persistente en Railway con client-render ON.
- Rotación MASTER_KEY re-cifra OAuth/PII, no sustituye PIN.
- Migración v3 → v3pin obligatoria al login para cuentas legacy.
Ver también
- Bóveda y PIN
- [SECURITY.md en repo Kalista]
- Arquitectura del ecosistema