X25519 explicado para desarrolladores: intercambio de claves sin magia
Si alguna vez has visto "X25519" en el package.json de una librería de criptografía y has seguido adelante sin preguntarte qué hace exactamente, este post es para ti. No hace falta un doctorado en matemáticas para entenderlo — solo entender el problema que resuelve y por qué se eligió esta curva en concreto.
El problema: dos partes, un secreto compartido, sin canal seguro
Imagina que Alice y Bob quieren compartir una clave secreta, pero solo pueden comunicarse por un canal que un atacante puede espiar (internet, básicamente). Esto es el problema clásico que resuelve Diffie-Hellman (DH): permite que dos partes generen un secreto compartido sin haberlo transmitido nunca directamente.
El DH clásico usa aritmética modular sobre números grandes. Funciona, pero es lento y las claves son enormes. La versión con curvas elípticas (ECDH) consigue la misma propiedad matemática — conmutatividad del intercambio — con claves mucho más pequeñas y operaciones mucho más rápidas.
Curve25519: la curva, no el algoritmo
Curve25519 es una curva elíptica concreta, diseñada por Daniel J. Bernstein, pensada desde el principio para ser rápida y difícil de implementar mal. Sus propiedades clave:
- Aritmética en tiempo constante más sencilla de conseguir (menos superficie para ataques de canal lateral).
- Resistente a ataques de "curva inválida", un problema histórico de otras curvas donde un atacante podía enviar puntos fuera de la curva esperada.
- Claves de 32 bytes, rápidas de generar y de operar incluso en hardware modesto.
X25519: la función ECDH sobre Curve25519
Aquí está el matiz que mucha gente se salta: Curve25519 es la curva; X25519 es la función de intercambio de claves (ECDH) definida sobre esa curva, especificada en el RFC 7748. Es lo que realmente llamas desde tu código.
El esquema, simplificado:
- Cada parte genera un par de claves: una privada (un escalar aleatorio de 32 bytes) y una pública (el resultado de multiplicar ese escalar por un punto base fijo de la curva).
- Alice envía su clave pública a Bob, y viceversa — por un canal que puede ser público.
- Alice calcula
secreto = privadaAlice * públicaBob. Bob calculasecreto = privadaBob * públicaAlice. Por las propiedades de la curva, ambos llegan al mismo valor.
Un observador que solo ve las dos claves públicas no puede reconstruir ese secreto sin resolver el problema del logaritmo discreto sobre curvas elípticas, que es computacionalmente inviable con los tamaños de clave actuales.
En código, usando los primitivos abiertos de @koove/crypto:
import { generateIdentityKeyPair } from '@koove/crypto';
const alice = generateIdentityKeyPair();
const bob = generateIdentityKeyPair();
// alice.publicKey y bob.publicKey pueden compartirse abiertamente
// alice.privateKey y bob.privateKey nunca salen del dispositivo
Lo que X25519 NO te da: cifrado
Esto es importante y a menudo se ignora: X25519 solo produce un secreto compartido, no cifra nada por sí mismo. Ese secreto crudo tampoco debería usarse directamente como clave simétrica — hay que pasarlo por una función de derivación de claves (en el caso de Koove, HKDF-SHA256) para obtener una clave con las propiedades adecuadas, y luego usar esa clave con un cifrado autenticado como AES-256-GCM.
Este patrón — X25519 para acordar/envolver una clave, AES-256-GCM para cifrar los datos reales — se llama cifrado de sobre (envelope encryption), y es exactamente lo que usa Koove para proteger secretos:
import { generateIdentityKeyPair, encryptSecret, sealKey, openKey } from '@koove/crypto';
const bob = generateIdentityKeyPair();
// La clave de datos (AES-256-GCM) cifra el secreto una sola vez
const { envelope, dataKey } = encryptSecret('sk_live_...');
// Esa clave de datos se "sella" (wrap) con la clave pública X25519 de Bob
const sealedForBob = sealKey(dataKey, bob.publicKey);
// Solo el dispositivo de Bob, con su clave privada, puede abrirla
const recoveredKey = openKey(sealedForBob, bob.privateKey);
El servidor de Koove nunca ve dataKey en claro ni las claves privadas — solo ciphertext y sobres sellados. Esa es la diferencia entre "cifrado en tránsito" y cifrado de extremo a extremo de verdad.
Lo que X25519 tampoco te da: forward secrecy automático
Si las claves X25519 que usas son estáticas (una identidad fija por dispositivo o usuario, que es el modelo habitual en un gestor de secretos), no tienes forward secrecy por defecto: si esa clave privada se compromete en el futuro, un atacante podría descifrar sobres antiguos que sellaste con esa clave pública.
Protocolos como Signal resuelven esto con un ratchet (rotación continua de claves efímeras). Koove no implementa un ratchet — usa X25519 + AES-256-GCM con identidades por dispositivo, que es el diseño correcto para "recuperar un secreto cuando lo necesito", pero no es equivalente en garantías a un protocolo de mensajería como Signal. Merece la pena decirlo con claridad: son propiedades distintas para casos de uso distintos.
Por qué esto importa en la era del código generado por IA
Cuando un asistente de IA escribe una integración con Stripe o con una API de terceros, tiende a pegar la clave directamente en el código o en un .env sin cifrar. Entender X25519 no es un ejercicio académico: es la base de por qué un secreto puede vivir cifrado hasta el último momento, descifrándose solo en el dispositivo o backend autorizado, verificado con App Attest / Play Integrity y biometría.
En la práctica, para tu flujo de trabajo diario esto se traduce en:
koove set STRIPE_SECRET_KEY sk_live_xxx --env prod
Tu código solo referencia STRIPE_SECRET_KEY por nombre. El valor real nunca pasa por el asistente de IA ni por el repositorio.
Conclusiones prácticas
- No implementes ECDH desde cero. Usa librerías auditadas (
libsodium,noble-curves, o primitivos ya empaquetados como@koove/crypto, que son de código abierto y auditables). - X25519 resuelve el acuerdo de claves, no el cifrado completo — necesitas KDF + AEAD alrededor.
- Si tu amenaza principal es "secretos filtrados en código generado por IA", el diseño correcto es cifrado de extremo a extremo con identidades verificadas por dispositivo, no forward secrecy tipo mensajería.
Puedes ver la arquitectura completa, incluidas las decisiones de diseño y sus límites, en el centro de confianza y seguridad, o revisar la documentación técnica del SDK y la CLI en /docs/sdk. Si tienes dudas concretas sobre el modelo de amenazas, el FAQ cubre las preguntas más comunes.
Prueba Koove
Si quieres dejar de pegar claves en texto plano en tu código y empezar a usar cifrado de extremo a extremo real desde el primer commit, crea tu cuenta gratuita y prueba koove set en tu proyecto en menos de cinco minutos.