- Se descubrió un error de validación incorrecta de puntos en la curva Edwards25519 en la función de bajo nivel
crypto_core_ed25519_is_valid_point() de libsodium
- Esta función debe comprobar que un punto pertenezca al grupo criptográfico principal, pero deja pasar por error algunos puntos que forman parte de subgrupos de orden mixto (subgroup)
- La causa fue un error de código en la validación interna de coordenadas: solo verificaba X=0 y omitía la comprobación de Y=Z, por lo que algunos puntos inválidos podían tratarse como válidos
- En la versión corregida, se cambió para comprobar ambas condiciones (X=0 y Y=Z), y están afectadas las versiones 1.0.20 o anteriores o los lanzamientos previos al 30 de diciembre de 2025
- La API de alto nivel (
crypto_sign_*) no se ve afectada, y se recomienda usar el grupo Ristretto255 por seguridad y rendimiento
Resumen del proyecto libsodium
- libsodium es un proyecto iniciado hace 13 años cuyo objetivo es ofrecer una API simple para facilitar el uso de criptografía
- Está diseñado para que el usuario pueda realizar operaciones de alto nivel sin necesidad de conocer los algoritmos internos
- Da mucha importancia a mantener la compatibilidad de la API, y ha conservado esa consistencia hasta hoy basándose en la API de NaCl
- Como algunos usuarios empezaron a usar directamente funciones de bajo nivel más allá de las limitaciones indicadas en la documentación, aumentaron los casos en los que la librería se usa como si fuera un toolkit criptográfico
Causa del bug encontrado
- Función afectada:
crypto_core_ed25519_is_valid_point()
- En la curva Edwards25519, debería rechazar los puntos que no pertenecen al grupo principal (de orden L)
- Sin embargo, algunos puntos de orden mixto (2L, 4L, 8L, etc.) pasaban la validación
- Internamente, para verificar el orden del punto, multiplica por L y luego comprueba si el resultado es el elemento identidad (identity)
- El elemento identidad se representa como X=0, Y=Z, pero el código anterior solo comprobaba X=0
- Por eso, algunos puntos inválidos con Y≠Z se trataban como válidos
- Ejemplo: si al punto Q del grupo principal se le suma el punto de orden 2 (0, -1), entonces Q+(0, -1) es un punto inválido, pero antes de la corrección pasaba la validación
Contenido de la corrección
- El commit del parche cambió lo siguiente
- Código anterior:
return fe25519_iszero(pl.X);
- Código corregido:
fe25519_sub(t, pl.Y, pl.Z); return fe25519_iszero(pl.X) & fe25519_iszero(t);
- Ahora se comprueban ambas condiciones, X=0 y Y=Z, para realizar la validación correctamente
Alcance del impacto
- Puede haber impacto si se cumple alguna de estas condiciones
- Se usa la versión 1.0.20 o anterior o un lanzamiento previo al 30 de diciembre de 2025
- Se valida un punto de entrada no confiable con
crypto_core_ed25519_is_valid_point()
- El usuario implementa directamente operaciones sobre la curva Edwards25519
- Sin embargo, la mayoría de los usuarios no se ven afectados
- La API de alto nivel (
crypto_sign_*) no usa esa función
crypto_scalarmult_ed25519 no filtra información incluso con una clave pública inválida
- Las claves generadas con
crypto_sign_keypair y crypto_sign_seed_keypair pertenecen al grupo correcto
Medidas recomendadas
- Se recomienda usar el grupo Ristretto255
- Está incluido en libsodium desde 2019 y resuelve los problemas relacionados con el cofactor
- Los puntos decodificados son seguros automáticamente, sin necesidad de validación adicional
- Ofrece mejor rendimiento en las operaciones que Edwards25519
- Si no es posible actualizar, se puede validar usando la función alternativa a nivel de aplicación (
is_on_main_subgroup) propuesta
Distribuciones corregidas y soporte
- El problema se corrigió de inmediato tras ser descubierto y está incluido en todas las versiones estables distribuidas después del 30 de diciembre de 2025
- Incluye el tarball oficial, binarios para Visual Studio/MingW, paquete NuGet, compilaciones para Android, el xcframework de
swift-sodium, Rust libsodium-sys-stable y libsodium.js
- También está previsto un nuevo point release
- El proyecto se mantiene por una sola persona, y se puede apoyar su mejora continua mediante patrocinios en OpenCollective
1 comentarios
Comentarios en Hacker News
La biblioteca de PHP sodium_compat también se vio afectada por este problema
Se puede revisar información relacionada en security-advisories PR #756
Esta noche planeo revisar todas las demás implementaciones de Ed25519 dentro del ecosistema open source para confirmar si tienen la misma omisión en la validación
Pero no hubo casos implementados de forma incorrecta como la vulnerabilidad mencionada arriba
Si no les envié un correo, lo más probable es que no estén en la lista de despliegues de Ed25519 de ianix, que se me hayan pasado por alto, o que sean implementaciones seguras
Llevo los últimos 4 meses desarrollando bindings de sodium para Lean4
Ya llegué a la etapa de Ristretto255, y ahora entiendo por qué al autor le entusiasma tanto esta tecnología
Ristretto es una API sofisticada para construir polinomios arbitrarios sobre Curve25519, y ha sido realmente divertido experimentar con ella
Si el autor llega a ver esta publicación, quiero darle las gracias sinceramente
El objetivo de Libsodium era ofrecer una API de alto nivel, no funciones de bajo nivel
Fue diseñado para que los usuarios no tuvieran que conocer los algoritmos internos, pero con el tiempo fueron aumentando los casos en que se usan directamente funciones de bajo nivel
Al final, Libsodium terminó usándose como una especie de toolkit de algoritmos
Lo importante es reconocer hacia dónde quieren ir los usuarios y no forzar el proyecto a una sola forma de uso
Algunos proyectos se vuelven dogmáticos en este punto y fracasan
Es peligroso que personas no expertas usen directamente primitivas criptográficas
Libsodium fue diseñado para evitar que los usuarios se pusieran en riesgo por sí mismos
Lo ideal es que la biblioteca haga imposible el uso incorrecto
Recomiendo este texto relacionado: “If You're Typing The Letters A-E-S Into Your Code, You're Doing It Wrong”
Por eso muchas veces es correcto limitar ciertas funciones como private o internal
No estoy seguro de si Libsodium definió bien esa frontera, pero el equilibrio es importante
Pero algunos usuarios lo usaban como si fuera un ejecutor por lotes
Corregí algunos bugs para soportar sus necesidades
Al final, me alegró simplemente que hubiera usuarios
Este bug es un error de validación criptográfica sutil pero importante
La verificación aparentemente simple de “comprobar si es válido” en realidad es muy compleja
Permitir puntos fuera del subgrupo de orden primo puede no parecer una vulnerabilidad inmediata, pero puede romper supuestos en capas superiores
Además, las primitivas de bajo nivel se reutilizan mucho más ampliamente de lo previsto, así que una pequeña omisión de validación puede tener un impacto enorme
Los problemas de subgrupos solo aparecen cuando se construyen protocolos más complejos sobre Curve25519
Por eso yo remapeo, siempre que puedo, todos los puntos al subgrupo de orden primo
Monocypher tiene este tipo de funciones avanzadas
Por ejemplo, funciones como
crypto_x25519_dirty_fast()ocrypto_elligator_map()Estas funciones “dirty” generan claves públicas que cubren toda la curva, haciéndolas indistinguibles de la aleatoriedad
Después, incluso en un intercambio de claves X25519, se puede obtener el mismo secreto compartido
Esto es gracias al diseño de DJB, donde incluso si la clave pública es anómala, el secreto compartido se remapea al subgrupo de orden primo
En última instancia, Ristretto solo es necesario cuando ese remapeo no es posible
Claro, la abstracción de grupo de orden primo es útil, pero alguien capaz de diseñar ese tipo de protocolos también debería poder manejar un cofactor no trivial
Si trabajas en una gran empresa, te recomendaría considerar apoyar a Frank a nivel corporativo
Y aunque lo supiera, probablemente tendría que apoyarlo con mi propio dinero, no con fondos de la empresa
Me pregunto si libnacl también está afectado
Uso software compilado con libnacl todos los días, pero ninguno compilado con “libsodium”
Es una biblioteca realmente excelente
Quiero expresar mi agradecimiento a Frank Denis