- Foi descoberta uma falha na validação inadequada de pontos na curva Edwards25519 na função de baixo nível
crypto_core_ed25519_is_valid_point() do libsodium
- Essa função deveria verificar se um ponto pertence ao grupo criptográfico principal, mas acaba aceitando incorretamente alguns pontos de ordem mista (subgrupo)
- A causa é um erro no código de validação interna de coordenadas: ele verificava apenas X=0 e omitia a validação de Y=Z, permitindo que pontos inválidos fossem tratados como válidos
- Na versão corrigida, a checagem foi alterada para verificar ambas as condições (X=0 e Y=Z), e as versões 1.0.20 ou anteriores ou releases anteriores a 30 de dezembro de 2025 são afetadas
- A API de alto nível (
crypto_sign_*) não é afetada, e o uso do grupo Ristretto255 é recomendado por segurança e desempenho
Visão geral do projeto libsodium
- O libsodium é um projeto iniciado há 13 anos, com o objetivo de oferecer uma API simples para facilitar o uso de criptografia
- Ele foi projetado para permitir apenas operações de alto nível, sem exigir que o usuário conheça os algoritmos internos
- O projeto prioriza a manutenção da compatibilidade da API e mantém consistência até hoje com base na API do NaCl
- Como alguns usuários passaram a utilizar diretamente funções de baixo nível além das limitações descritas na documentação, a biblioteca vem sendo usada cada vez mais como um toolkit criptográfico
Causa do bug encontrado
- Função problemática:
crypto_core_ed25519_is_valid_point()
- Ela deveria rejeitar pontos que não pertencem ao grupo principal (ordem L) na curva Edwards25519
- No entanto, alguns pontos de ordem mista (2L, 4L, 8L etc.) passam pela validação
- Internamente, para verificar a ordem de um ponto, a função o multiplica por L e então checa se o resultado é o elemento identidade (identity)
- O elemento identidade é representado na forma X=0, Y=Z, mas o código anterior verificava apenas X=0
- Por isso, pontos incorretos com Y≠Z podiam ser tratados como válidos
- Exemplo: para um ponto Q do grupo principal, somar o ponto de ordem 2 (0, -1) e obter Q+(0, -1) gera um ponto inválido, mas ele passava antes da correção
Conteúdo da correção
- O commit do patch foi alterado da seguinte forma
- Código anterior:
return fe25519_iszero(pl.X);
- Código corrigido:
fe25519_sub(t, pl.Y, pl.Z); return fe25519_iszero(pl.X) & fe25519_iszero(t);
- Agora as duas condições, X=0 e Y=Z, são verificadas para realizar a validação correta
Escopo do impacto
- Pode haver impacto nos seguintes casos
- Uso da versão 1.0.20 ou anterior ou de um release anterior a 30 de dezembro de 2025
- Validação de pontos de entrada não confiáveis com
crypto_core_ed25519_is_valid_point()
- Usuários que implementam diretamente operações na curva Edwards25519
- No entanto, a maioria dos usuários não é afetada
- A API de alto nível (
crypto_sign_*) não usa essa função
crypto_scalarmult_ed25519 não vaza informações mesmo com chaves públicas inválidas
- Chaves geradas por
crypto_sign_keypair e crypto_sign_seed_keypair pertencem ao grupo correto
Medidas recomendadas
- Recomenda-se o uso do grupo Ristretto255
- Ele está incluído no libsodium desde 2019 e resolve problemas relacionados ao cofactor
- Pontos decodificados são automaticamente seguros, sem necessidade de validação adicional
- Oferece desempenho de operação superior ao Edwards25519
- Se não for possível atualizar, é possível validar usando a função alternativa em nível de aplicação (
is_on_main_subgroup) apresentada
Distribuição corrigida e suporte
- O problema foi corrigido imediatamente após a descoberta e está incluído em todas as versões estáveis distribuídas após 30 de dezembro de 2025
- Inclui tarballs oficiais, binários para Visual Studio/MingW, pacote NuGet, builds para Android, xcframework do
swift-sodium, Rust libsodium-sys-stable e libsodium.js
- Um novo point release também está previsto
- O projeto é mantido por uma única pessoa, e melhorias contínuas podem ser apoiadas por meio de patrocínio no OpenCollective
1 comentários
Comentários no Hacker News
A biblioteca PHP sodium_compat também foi afetada por este problema
É possível conferir os detalhes no security-advisories PR #756
Hoje à noite pretendo verificar todas as outras implementações de Ed25519 no ecossistema open source para confirmar se existe a mesma ausência de validação
Mas não encontrei nenhum caso implementado de forma incorreta como a vulnerabilidade mencionada acima
Se eu não enviei um e-mail, então provavelmente não está na lista de implantações de Ed25519 do ianix, ou eu deixei passar, ou é uma implementação segura
Nos últimos 4 meses estou desenvolvendo bindings de sodium para Lean4
Agora cheguei à etapa de Ristretto255 e passei a entender por que o autor é tão entusiasmado com essa tecnologia
O Ristretto é uma API sofisticada para construir polinômios arbitrários sobre Curve25519, e tem sido muito divertido experimentar com isso
Caso o autor veja este texto, gostaria de deixar um agradecimento sincero
O objetivo do Libsodium era oferecer APIs de alto nível, não funções de baixo nível
Foi projetado para que os usuários não precisassem conhecer os algoritmos internos, mas com o tempo surgiram cada vez mais casos de uso direto das funções de baixo nível
No fim, o Libsodium acabou sendo usado como uma espécie de kit de ferramentas de algoritmos
O importante é perceber a direção desejada pelos usuários e não forçar o projeto a funcionar de apenas uma maneira específica
Alguns projetos fracassam por se tornarem dogmáticos nesse aspecto
É perigoso que não especialistas usem primitivas criptográficas diretamente
O Libsodium foi projetado para impedir que o usuário coloque a si mesmo em risco
O ideal é que a biblioteca impossibilite o uso incorreto
Recomendo este texto relacionado: “If You're Typing The Letters A-E-S Into Your Code, You're Doing It Wrong”
Por isso, muitas vezes faz sentido restringir certos recursos como private ou internal
Não tenho certeza se o Libsodium definiu bem essa fronteira, mas o equilíbrio é importante
Mas alguns usuários passaram a usá-lo como um executor em lote
Corrigi alguns bugs para atender às necessidades deles
No fim, eu só fiquei feliz por haver usuários
Este bug é um erro de validação criptográfica sutil, mas importante
Uma checagem aparentemente simples como “confirmar se é válido” na prática é muito complexa
Permitir pontos fora do subgrupo de ordem prima pode não parecer uma vulnerabilidade imediata, mas pode quebrar premissas nas camadas superiores
Além disso, primitivas de baixo nível acabam sendo reutilizadas muito mais amplamente do que o pretendido, então uma pequena falha de validação pode ter grande impacto
O problema de subgrupos só aparece quando se constroem protocolos mais complexos sobre a Curve25519
Por isso eu remapeio, sempre que possível, todos os pontos para o subgrupo de ordem prima
O Monocypher tem essas funções avançadas
Por exemplo, funções como
crypto_x25519_dirty_fast()oucrypto_elligator_map()Essas funções “dirty” geram chaves públicas que cobrem a curva inteira, de forma indistinguível de aleatoriedade
Depois, mesmo em uma troca de chaves X25519, ainda é possível obter o mesmo segredo compartilhado
Isso é possível graças ao design do DJB, em que mesmo uma chave pública anômala faz o segredo compartilhado ser mapeado para o subgrupo de ordem prima
No fim, o Ristretto só é necessário quando esse tipo de remapeamento não é possível
Claro, a abstração de grupo de ordem prima é útil, mas alguém capaz de projetar esse tipo de protocolo também deveria conseguir lidar com um cofactor não trivial
Se você trabalha em uma grande empresa, eu recomendaria considerar patrocinar o Frank em nível corporativo
E, mesmo que soubesse, provavelmente teria de apoiar com meu próprio dinheiro, não com a conta da empresa
Gostaria de saber se o libnacl também foi afetado
Eu uso diariamente software compilado com libnacl, mas nenhum compilado com “libsodium”
É realmente uma biblioteca excelente
Deixo aqui meu agradecimento ao Frank Denis