Ir al contenido

Criptografía

kovra no implementa criptografía propia. Cada primitiva de abajo es una implementación de Rust verificada y ampliamente usada — la familia RustCrypto, age y BLAKE3. El trabajo de kovra es componerlas correctamente: la primitiva adecuada para cada tarea, los parámetros adecuados y ningún texto plano dejado donde no debería estar.

Esta página documenta exactamente qué corre, con los parámetros reales del código fuente.

PropósitoAlgoritmoClave / parámetrosBiblioteca
Cifrado en reposoChaCha20-Poly1305 (AEAD)clave de 256 bits, nonce aleatorio de 96 bits por escriturachacha20poly1305
Custodia de la master keyKeyring del SO— (Keychain / Credential Manager / Secret Service)keyring
Derivación de clave headlessArgon2id19 MiB de memoria, 2 pasadas, 1 lane → clave de 256 bitsargon2
Fingerprint de valorBLAKE3 (truncado)primeros 4 bytes → 8 caracteres hexblake3
Direccionamiento de coordenadasBLAKE3digest completo de la ruta canónicablake3
Backup de la master keyage scrypt (passphrase)ASCII-armoredage
Keypairs de firmaed25519 / RSA-3072 (PKCS#1 v1.5 + SHA-2)formato OpenSSHssh-key, rsa
Cifrado asimétricoX25519 (vía age, solo claves ed25519)age, ssh-key
Códigos TOTPRFC-6238 HMAC-SHA1 (SHA-256/512 opcional)6 dígitos, período de 30 segundoshmac, sha1/sha2
AleatoriedadCSPRNG del SOgetrandom / OsRng
Higiene en memoriazeroize + secrecyzeroize, secrecy

Cada registro del vault — y el índice de metadatos — se sella de forma independiente con ChaCha20-Poly1305, un cifrador AEAD (authenticated encryption with associated data), bajo la master key de 256 bits del vault. Cada escritura genera un nonce aleatorio de 96 bits nuevo, de modo que dos sellados del mismo registro siempre difieren y un nonce nunca se reutiliza. Los metadatos y el valor se sellan juntos, de modo que ni el secreto ni siquiera su coordenada aparecen como texto plano en disco. El tag de autenticación (Poly1305) significa que un registro manipulado falla al abrir en vez de devolver basura, y los fallos de descifrado son opacos — una clave equivocada, un ciphertext corrupto y un nonce malformado son indistinguibles, de modo que el error no puede actuar como oráculo. El buffer transitorio de texto plano se zeroiza tras su uso.

Por qué ChaCha20-Poly1305. Es un AEAD moderno de tiempo constante que es rápido en software puro sin instrucciones especiales de CPU (a diferencia de AES, que se apoya en AES-NI tanto para velocidad como para resistencia a canales laterales) — el predeterminado adecuado para una herramienta que corre en cualquier laptop. El AEAD da confidencialidad e integridad en una sola pasada, y el nonce aleatorio por registro esquiva el modo de fallo catastrófico de reutilización de nonce.

La master key de 256 bits nunca se teclea, se muestra ni se escribe en un archivo de proyecto. Por defecto vive en el keyring del SO — el Keychain de macOS, el Credential Manager de Windows o el Secret Service de Linux — y kovra la carga solo para sellar y abrir registros.

Para uso headless (CI, contenedores, sin keyring), kovra deriva la clave en su lugar con Argon2id, el KDF de contraseñas memory-hard, a partir de una passphrase más una sal estable por vault. Corre con los valores predeterminados de la biblioteca — 19 MiB de memoria, 2 pasadas, 1 lane — produciendo la clave de 256 bits de forma determinista, de modo que el mismo vault se desbloquea entre corridas sin nada secreto almacenado en disco (solo la sal no secreta).

Por qué Argon2id. El memory-hardness hace que forzar por fuerza bruta una passphrase robada sea caro en GPUs y hardware a medida; Argon2id es el estándar actual (y el ganador de la Password Hashing Competition). El keyring del SO se prefiere cuando está presente porque liga la clave a la sesión de login del usuario y a las protecciones propias de la plataforma; Argon2id es el fallback portable que no necesita nada más que una passphrase.

kovra usa BLAKE3 en dos lugares:

  • Fingerprints de valor. kovra list y doctor muestran un fingerprint truncado — los primeros 4 bytes del digest BLAKE3, como 8 caracteres hex en minúscula. Es determinista (sin sal), de modo que se puede responder “¿cambió este valor?” o “¿es este el mismo secreto que antes?” sin ver nunca el valor. Es deliberadamente demasiado corto para ayudar a forzar el valor por fuerza bruta, y nunca es el hash completo.
  • Direccionamiento de coordenadas. El identificador en disco de un registro es el digest BLAKE3 de su ruta canónica env/component/key, de modo que las coordenadas no se filtran como nombres de archivo en texto plano.

Por qué BLAKE3. Es rápida, moderna y tiene una API limpia y difícil de usar mal. La seguridad del fingerprint viene de truncamiento más determinismo: lo bastante largo para detectar un cambio, lo bastante corto como para que esencialmente no revele nada sobre el valor.

kovra key export escribe un backup de recuperación ante desastres de la master key como un blob age scrypt ASCII-armored — cifrado bajo una passphrase de recuperación que se elige, descifrable por cualquier implementación de age en una emergencia. El texto plano transitorio se borra tras la llamada; solo el blob cifrado se devuelve.

Los keypairs custodiados se almacenan en formato OpenSSH y se usan solo a través de kovra:

  • ed25519 — firma (curva de Edwards) y cifrado asimétrico (X25519, vía el camino de age de arriba).
  • RSA-3072 — solo firma y SSH, usando PKCS#1 v1.5 con SHA-2. Deliberadamente sin cifrado RSA — cuando se necesita cifrar hacia una clave, usar ed25519.

Las firmas se hacen bajo un namespace de firma SSH fijo, de modo que una firma que kovra produce se verifica con el estándar ssh-keygen -Y verify. La mitad privada se genera dentro del vault, sellada bajo la master key con el mismo camino ChaCha20-Poly1305 que todo otro secreto, y nunca se escribe en disco ni se imprime.

Por qué ed25519 primero. Claves pequeñas, firmas rápidas, sin foot-guns de parámetros, y un puente limpio hacia el cifrado a través de X25519. RSA-3072 (≈128 bits de seguridad) se mantiene por interoperabilidad con sistemas que todavía requieren RSA.

Una inscripción TOTP custodia la semilla compartida y computa códigos según RFC-6238: HMAC-SHA1 por defecto (el predeterminado del RFC), con SHA-256 y SHA-512 disponibles, 6 dígitos, período de 30 segundos. El HMAC se construye sobre los crates hmac + sha1/sha2. La semilla se sella como cualquier otro secreto y nunca se revela — solo el código derivado y de tiempo limitado se produce.

Todos los nonces, los secretos generados (kovra generate) y los keypairs recién creados toman del CSPRNG del sistema operativo (getrandom / OsRng) — nunca de un PRNG de userspace sembrado desde una fuente adivinable.

Los tipos portadores de secretos se envuelven en secrecy e implementan zeroize: sus Debug/Display están redactados (un valor no puede filtrarse a una línea de log o un mensaje de panic), y los bytes subyacentes se borran de memoria al descartarse. Los buffers transitorios de texto plano — el registro descifrado, el payload serializado antes del sellado — se zeroizan explícitamente tan pronto como ya no se necesitan.

  • Sin criptografía casera. Cada primitiva es una biblioteca verificada y ampliamente revisada; kovra solo las compone.
  • Sin reutilización de nonce. Cada sellado AEAD usa un nonce aleatorio nuevo.
  • Sin oráculo de valor. Los fingerprints son truncados y los errores son opacos.
  • Sin protección más allá de la entrega. Una vez que un valor se entrega al proceso que lo necesita, vive en la memoria de ese proceso bajo las reglas de ese programa — la criptografía de kovra asegura la custodia y la entrega, no lo que un programa hace con un valor después de recibirlo.