2 pontos por GN⁺ 2026-01-01 | 1 comentários | Compartilhar no WhatsApp
  • 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

 
GN⁺ 2026-01-01
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

    • Descobri que em algumas bibliotecas a lógica de validação simplesmente não existe
      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
    • Quero agradecer por contribuir com o open source
  • 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

    • Fico curioso para saber se existe um repositório público desse projeto
  • 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

    • Observação interessante. Mas, por outro lado, o que você acha que os usuários querem pode ser diferente do que eles realmente precisam
      É 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”
    • Se todas as funções internas forem tratadas como parte do contrato da API, quase toda mudança vira uma quebra de compatibilidade
      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
    • No passado eu criei um framework de testes em C++ chamado CeeFIT, e tinha orgulho da forma como ele registrava fixtures em tempo de compilação
      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

    • Mas X25519 e Ed25519 foram originalmente projetados para não exigir esse tipo de validação
      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() ou crypto_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

    • Eu trabalho na Apple, mas não sei quem é o Frank, nem conheço o processo de patrocínio
      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