1 pontos por GN⁺ 2024-06-30 | 1 comentários | Compartilhar no WhatsApp
  • XAES-256-GCM é uma nova especificação de AEAD que usa chave de 256 bits e nonce de 192 bits para tornar o gerenciamento de nonces menos arriscado em APIs de criptografia de alto nível
  • O nonce maior permite uma abordagem em que um novo valor é gerado automaticamente pelo CSPRNG do sistema operacional para cada mensagem, visando um risco de colisão na ordem de 2⁻³² em 2⁸⁰ mensagens
  • Internamente, é uma construção de nonce estendido que deriva uma chave e um nonce de 96 bits a partir da chave de entrada e do nonce grande, e então usa AES-256-GCM padrão sem modificações
  • A implementação de referência em Go cabe em menos de 100 linhas usando apenas crypto/cipher e crypto/aes, e parte das 3 chamadas de AES-256 por mensagem pode ser pré-computada
  • Com foco em conformidade com FIPS 140 e compatibilidade com bibliotecas, pode ser usada como candidata para uma API AEAD sem nonce, junto com XChaCha20Poly1305 e AES-GCM-SIV

AEAD com nonce grande para APIs de alto nível

  • XAES-256-GCM é um algoritmo de criptografia autenticada com dados adicionais (AEAD) que usa chave de 256 bits e nonce de 192 bits
  • O objetivo do projeto se resume a três pontos
    • suporte a nonce grande em que a geração aleatória é segura mesmo com uma quantidade de mensagens praticamente ilimitada
    • conformidade total e direta com FIPS 140
    • implementação fácil sobre bibliotecas criptográficas comuns
  • Graças ao nonce grande, é possível criar APIs que leem um novo nonce do CSPRNG do sistema operacional para cada mensagem, sem que o usuário precise calcular diretamente o birthday bound
  • Ao priorizar conformidade e compatibilidade, pode ser aplicado onde for necessário AEAD mesmo em ambientes em que outros AEADs com nonce grande sejam difíceis de usar

Uma construção de nonce estendido que reaproveita AES-256-GCM como está

  • XAES-256-GCM é uma construção de nonce estendido construída sobre um AEAD existente, como o XChaCha20Poly1305
  • A partir da chave de entrada e do nonce de 192 bits, calcula uma chave derivada e um nonce derivado para uso interno com AES-256-GCM
    • a chave de entrada e o nonce são K e N, respectivamente
    • a chave e o nonce derivados para AES-256-GCM são Kₓ e Nₓ
  • Kₓ é gerado com chamadas a AES-256ₖ e a parte inicial do nonce, e os 96 bits finais do nonce de entrada são usados como Nₓ
  • São necessárias 3 chamadas a AES-256ₖ por mensagem
    • uma delas pode ser pré-computada para uma determinada chave
    • as outras duas podem reutilizar o mesmo key schedule

A implementação é curta e pode ser descrita com componentes padrão

  • A implementação de referência em Go tem menos de 100 linhas, incluindo a otimização de pré-computação e a maior parte do boilerplate
  • A implementação em Go usa apenas crypto/cipher e crypto/aes da biblioteca padrão
  • XAES-256-GCM também pode ser descrito com o KDF padrão NIST SP 800-108r1 e o AEAD NIST AES-256-GCM padrão
    • o KDF é counter-based KDF
    • a PRF é CMAC-AES256
    • a chave de entrada é Kin
    • o rótulo é o caractere ASCII X, ou seja, 0x58
    • o contexto são os primeiros 96 bits do nonce de entrada
    • o tamanho do contador é de 16 bits
    • o campo opcional L é omitido
    • a saída é uma chave derivada de 256 bits
  • A chave derivada e os últimos 96 bits do nonce de entrada são passados para o AES-256-GCM
  • Graças à escolha dos parâmetros, ao remover as abstrações de KDF e CMAC, o resultado é apenas um pouco mais lento e mais complexo do que simplesmente chamar AES-256 sobre o contador
  • Os mesmos parâmetros também são compatíveis com a API OpenSSL de alto nível

Implementações de terceiros e vetores de teste

O abandono de /11 e alternativas

  • A ideia anterior usava o nome XAES-256-GCM/11, mas a especificação final abandonou o /11
  • O /11 era uma otimização de desempenho e, como um dos motivos para usar AES-GCM é a conformidade com FIPS 140, alterar o número de rodadas eliminaria essa conformidade
  • Se a conformidade com FIPS 140 não for um objetivo, há várias alternativas
    • AES-GCM-SIV
    • construções modernas de AEAD baseadas no core do AES
  • A seção Alternatives da especificação compara cada alternativa com o XAES-256-GCM

Seu lugar no Go e em APIs AEAD sem nonce

  • XAES-256-GCM busca ser um AEAD seguro, sem firulas, compatível com conformidade e interoperável
  • Seu principal caso de uso é o tipo de API de alto nível que se gostaria de adicionar ao Go
  • XAES-256-GCM complementa XChaCha20Poly1305 e AES-GCM-SIV, e foi projetado como candidato de implementação para uma hipotética API AEAD sem nonce
  • Como não há preferência por adicionar à biblioteca padrão do Go uma construção específica do Go, são necessárias opiniões de mantenedores de outras bibliotecas criptográficas

1 comentários

 
GN⁺ 2024-06-30
Opiniões no Hacker News
  • O design é muito inteligente: por ser baseado em CMAC, consegue derivar a chave com AES-CBC mesmo sem primitivas de nível mais baixo
    Do ponto de vista do AES-CBC, dá para ver como começar com L = AES-CBC-256ₖ(iv = 0¹²⁸, plaintext = 0¹²⁸)[:16], criar K1, criptografar M1 e M2 para obter Kₓ e então usar Nₓ = N[12:]
    Dá para pensar no AES-CBC-256 como retornando apenas o primeiro bloco de 128 bits do texto cifrado e descartando o bloco de padding; mesmo que não seja possível desativar o padding, isso só custa mais 3 chamadas AES com a mesma chave do que uma implementação de nível mais baixo, então não é ruim
    Uma implementação em JS baseada na WebCrypto API que usa essa característica está em https://github.com/dchest/xaes e também oferece suporte a características de CryptoKey, como aceitar diretamente uma CryptoKey para AES-CBC e armazená-la no IndexedDB com extractable=false

    • Nesta área, isso parece uma notação padrão, mas, sendo sincero, eu detesto notação criptográfica
      Naquele pseudocódigo, metade dos números parece contar bytes e a outra metade bits, e, se você já não conhece o algoritmo, é quase impossível saber qual é qual
      Por exemplo, N[:12] parece 12 bytes, mas 0¹²⁸ são 16 bytes, e X é o caractere literal 'X', ou seja, a sequência de bits 01011000, enquanto L é uma variável, não a sequência de bits 01001100
      Parece claro que matemáticos não gostam de notação sem ambiguidades tanto quanto o pessoal de ciência da computação
    • Constantes com aparência sinistra como 0¹²⁰10000111 são sequências de bits que representam os coeficientes do primeiro polinômio em ordem lexicográfica, entre os polinômios irredutíveis de grau b com o menor número de termos não nulos, para o tamanho de bloco b da cifra de bloco subjacente usada pelo CMAC
      Não há nenhuma intenção escondida
    • Não sou especialista em criptografia, mas, em resumo, entendo que o AES-GCM AEAD padrão quebra de forma catastrófica se o mesmo nonce for usado duas vezes em mensagens diferentes[1], e que o tamanho do nonce também é pequeno demais, em muitos casos, para usar nonces aleatórios com segurança
      Este trabalho permite evitar esse problema facilmente mudando não só o nonce, mas também a chave a cada chamada do AES-GCM
      Além disso, se você tem AES-GCM, ele usa apenas AES “comum”, geralmente disponível, e evita novas construções complexas que ainda podem ter pontos fracos
      O overhead por mensagem é de dois buffers pequenos que precisam ser criptografados/descriptografados com AES “comum” e um nonce mais longo de 192 bits
      [1]: https://frereit.de/aes_gcm/
  • Parece eliminar a armadilha do AES-GCM puro, em que, ao usar nonces aleatórios, é preciso trocar a chave a cada cerca de 2^32 mensagens
    No AES-GCM, uma colisão de nonce é catastrófica e, no mínimo, permite que um atacante assine mensagens arbitrárias
    Não é obrigatório usar nonces aleatórios, mas isso costuma ser recomendado, e é bastante inteligente torná-lo compatível com FIPS usando duas primitivas: uma função de derivação de chaves baseada em contador e GCM puro

    • Mais precisamente, esse método torna nonces aleatórios seguros desde o início
      No AES-GCM padrão, 96 bits não são suficientes para evitar colisões aleatórias, então é preciso usar geração determinística de nonces
      Além disso, independentemente de como o nonce foi criado, depois de 2^32 blocos é preciso trocar o nonce ou a chave, porque o contador dá rollover e o próximo bloco passa a usar o mesmo nonce+contador do primeiro bloco
  • Realmente impressionante. Eu gostaria que isso existisse alguns anos atrás, quando criei meu último sistema de arquivos criptografado
    Em implantações de sistemas de arquivos em grande escala, colisões de nonce são uma grande preocupação
    2^32 parece grande, mas, em um array na escala de PB gravando a 100k IOPS por segundo e contando com a aleatoriedade de um gerador pseudoaleatório, a chance de colisão é praticamente garantida

    • A competição CAESAR[1] terminou em 2019 e resultou em vários AEADs com espaço de nonce suficiente
      [1] https://en.m.wikipedia.org/wiki/CAESAR_Competition
    • Mas fico me perguntando por que colisão de nonce é um problema
      Isso não significa apenas que dois blocos compartilham a mesma chave de criptografia?
      Se eu não conheço o texto claro de nenhum dos blocos, não vejo como isso enfraquece a segurança do sistema
  • Eu gostaria que isso fosse usado em uma variante do age compatível com FIPS para criptografia de arquivos de arquivamento
    Em auditorias do setor bancário, o age foi rejeitado para esse uso por usar ChaCha, enquanto a parte de chave pública X25519 do age foi considerada aceitável. Eu achava que X25519 tinha sido aprovado pelo NIST relativamente recentemente
    Não tenho experiência com Go, mas, olhando a especificação do age, parece que isso poderia ser encaixado diretamente, e talvez eu tente quando tiver tempo
    O nome poderia ser “cage”, significando “compliant actually good encryption”
    1: https://github.com/FiloSottile/age

    • “compliant actually good encryption” talvez seja um oxímoro
    • Conferi, e parece que Ed25519 foi aprovado (FIPS 186-5), mas X25519 ainda não
    • Como observação, a biblioteca padrão do Go ainda não tem implementação de XAES, apenas a implementação de referência do C2SP
    • bge, bureaucratic good encryption
  • Como não criptógrafo, fico curioso: por que usar um nonce de 192 bits e não de 256 bits?
    Não imagino que esses bits extras sejam considerados um custo em aplicações práticas

    • Não há espaço para colocar 256 bits. Os 192 bits são compostos por 96 bits vindos do espaço do nonce inferior e por 96 bits que entram em um bloco CMAC de 128 bits junto com o prefixo necessário
      Seria possível tornar a entrada do CMAC mais longa, mas isso exigiria executar a função de bloco AES-256 mais vezes e também traria problemas incômodos de controle de chave na função de derivação de chaves do CMAC
      É parecido com o motivo pelo qual o XChaCha20Poly1305 usa nonces de 192 bits, e o fato de ser consistente com outros AEADs importantes de nonce estendido também é uma vantagem menor
  • Dizem “risco de colisão de 2⁻³² em 2⁸⁰ mensagens”, mas o fato de o tamanho de bloco do AES ser de apenas 128 bits não causa problemas antes disso?

    • Se você está falando do limite de aniversário para blocos (https://sweet32.info), isso é um limite sobre o número de blocos criptografados com uma única chave
      Como o XAES deriva uma chave grande por mensagem, ele alcança o que muitas vezes é chamado de garantia melhor que o limite de aniversário
    • Não
      Sem mais contexto sobre por que você acha que isso seria um problema, é difícil responder com mais detalhes
  • “Seguro, entediante, compatível e interoperável”
    É o meu tipo favorito de tecnologia

  • Legal. É bom ver que existe de fato uma construção baseada no NIST
    Ainda assim, é uma pena abrir mão de vários recursos que são vantagens da função de derivação de chaves do NIST, como labels e contexto
    Entendo que o sacrifício foi feito para minimizar o número de chamadas AES, mas, especialmente para mensagens maiores que algumas centenas de bytes, eu teria priorizado uma separação criptográfica mais forte em vez de economizar algumas chamadas AES
    Por fim, nonces GCM aleatórios com mais de 96 bits são definitivamente muito mal compreendidos e oferecem garantias melhores do que nonces de 96 bits[1]
    Claro que, se for possível derivar uma nova chave por mensagem, isso é claramente melhor
    [1] https://neilmadden.blog/2024/05/23/galois-counter-mode-and-r...