Primitivas criptográficas
| Función | Algoritmo | Parámetros |
|---|---|---|
| KDF | Argon2id | OPSLIMIT_MODERATE, 256 MiB |
| AEAD | XChaCha20-Poly1305 | nonce 24B, tag 16B |
| HKDF | BLAKE2b-keyed | output 32B |
| Firmas | Ed25519 | RFC 8032 |
| Key Exchange | X25519 | RFC 7748 |
| Sealed Boxes | X25519 + XSalsa20-Poly1305 | nonce derivado |
| Hash | BLAKE2b-256 | output 32B |
Jerarquía de claves
// Derivación completa desde la contraseña
password
│ Argon2id(salt, opsLimit, memLimit)
▼
Master Key (MK)
├── BLAKE2b("vault.wrap") → K_vault_wrap
│ └── Unwrap → vault_key → file_key → chunks
├── BLAKE2b("login.sign") → seed → Ed25519 keypair
└── Unwrap → sk_exchange (X25519)
// Rama paralela: la frase de recuperación (12 palabras BIP39)
mnemónica
│ BLAKE2b(key="recovery.v1")
▼
recovery seed
├── Ed25519 → firma el challenge de recuperación
└── BLAKE2b("recovery.box.v1") → X25519 → abre los seals del kit
Recuperación de cuenta (kit v2)
Tu frase de 12 palabras (BIP39, 128 bits de entropía) no solo recupera el acceso: recupera también tus archivos. De ella se deriva un par X25519 cuya clave pública queda registrada en el servidor; con esa pública, tu navegador sella (crypto_box_seal) la clave de cada bóveda y tu clave de intercambio. Sellar solo necesita la pública — abrir los seals, solo la privada, que únicamente existe si tienes la frase.
Olvidaste la contraseña → nada se pierde
La frase firma un reto, el servidor entrega los seals, tu navegador los abre y re-cifra las claves de bóveda con tu nueva contraseña. Los archivos y lo que te han compartido siguen contigo.
El servidor solo guarda sobres cerrados
Los seals son ciphertext: ni Noctcom ni un atacante con la base de datos pueden abrirlos. La privada que los abre se deriva de tu frase y nunca viaja.
Pierdes frase Y contraseña → irrecuperable
No hay puerta trasera, ni 'contacta con soporte'. Es el precio del zero-knowledge real, y lo decimos sin letra pequeña.
Cifrado de archivos
Chunks de 4 MiB
Cada archivo se divide en chunks de 4 MiB, cifrados independientemente con XChaCha20-Poly1305. Cada chunk usa un nonce aleatorio de 24 bytes.
AAD anti-reorder
Cada chunk incluye su índice como Additional Authenticated Data (AAD = 'chunk:N'). Impide que un atacante reordene chunks.
Content hash
BLAKE2b-256 sobre todos los chunks cifrados. Verifica integridad en cada descarga.
Zero-knowledge email
// El servidor nunca ve tu email
email_hash = BLAKE2b(
message = normalize(email),
key = "noctcom.email.v1",
output = 32 bytes
)
Auditorías externas
Aún no se han realizado auditorías externas. Cuando se completen, los reportes completos se publicarán aquí y en el repositorio.
Mientras tanto, el código es 100% auditable en GitHub.