2 pontos por GN⁺ 2024-05-08 | 1 comentários | Compartilhar no WhatsApp
  • No Go 1.22, foi feita uma mudança para que tanto o pacote math/rand existente quanto o novo pacote math/rand/v2 passem a usar um gerador de números aleatórios criptograficamente seguro. Com isso, passa a haver melhor aleatoriedade e também é possível reduzir bastante os danos que podem ocorrer quando um desenvolvedor usa math/rand por engano no lugar de crypto/rand.

Diferença entre aleatoriedade estatística e aleatoriedade criptográfica

  • A aleatoriedade estatística é adequada para simulação, amostragem, análise numérica, algoritmos aleatórios não criptográficos, testes aleatórios, embaralhamento de entradas, recuo exponencial aleatório e afins.
  • Mesmo fórmulas matemáticas muito básicas e fáceis de calcular funcionam bem o suficiente para esses usos. Porém, um observador que conheça o algoritmo usado pode prever a sequência futura depois de ver uma certa quantidade de valores.
  • A aleatoriedade criptográfica deve ser, na prática, totalmente imprevisível, mesmo que alguém observe quaisquer valores gerados anteriormente.
  • Protocolos criptográficos seguros, chaves secretas, o comércio moderno e a privacidade online dependem fortemente da aleatoriedade criptográfica.

O gerador math/rand do Go 1

  • Usa o método Linear-feedback shift register (LFSR).
  • Há o problema de o estado interno ser totalmente exposto como um vetor composto por 607 valores uint64.
  • Se 607 valores forem lidos do gerador, todo o estado é exposto e os valores seguintes podem ser previstos.

O gerador PCG de math/rand/v2

  • Usa o algoritmo PCG de Melissa O'Neill. Trata-se de um LCG de 128 bits com pós-processamento.
  • Todo o estado é um único número de 128 bits, e a atualização é feita com multiplicação e soma de 128 bits.
  • No Go, seguindo a proposta de O'Neill, é usada uma função de embaralhamento baseada em multiplicação em vez de uma baseada em XOR, misturando os bits de forma mais agressiva.
  • Exige mais computação do que o gerador do Go 1, mas precisa de muito menos memória para armazenar o estado, é menos sensível ao valor inicial do estado e também passa em testes estatísticos nos quais outros geradores falham.
  • Ainda assim, o PCG continua não sendo imprevisível.

Aleatoriedade criptográfica

  • Em última instância, o sistema operacional precisa coletar aleatoriedade real a partir do ruído de dispositivos físicos.
  • Quando aleatoriedade suficiente (256 bits ou mais) é coletada, ela pode ser expandida com hash criptográfico ou algoritmos de criptografia para criar uma sequência aleatória de comprimento arbitrário.
  • O pacote crypto/rand do Go abstrai essas diferenças entre interfaces de sistemas operacionais e fornece a mesma interface, rand.Read.

O gerador ChaCha8Rand

  • Um novo gerador criado a partir de uma adaptação da cifra de fluxo ChaCha, de DJB.
  • Usa o ChaCha8, a versão de 8 rodadas. É 2,5 vezes mais rápido que o ChaCha20 e ainda assim seguro.
  • Usa uma seed de 32 bytes como chave do ChaCha8. A cada 16 blocos, os 32 bytes finais do bloco gerado passam a ser usados como chave dos próximos 16 blocos, fornecendo sigilo futuro.
  • rand.Float64, rand.N etc. de math/rand/v2 sempre usam esse gerador.
  • math/rand também usa esse gerador. Porém, se rand.Seed for chamado, ele usa o gerador do Go 1.
  • O runtime também usa o ChaCha8Rand ao escolher a seed de hash de novos mapas.

Correção de erros de segurança

  • O Go 1.22 torna os programas mais seguros sem exigir mudanças no código, ao reforçar o math/rand.
  • Por exemplo, se Read de math/rand for usado por engano para geração de chaves e afins, no Go 1.20 isso é um problema grave de segurança, mas no Go 1.22 passa a ser apenas um erro.
  • Mesmo em usos que não parecem “criptográficos”, como geração de UUID ou balanceamento de carga de servidores de frontend, usar ChaCha8Rand torna tudo muito mais robusto do que com o gerador do Go 1.

Desempenho

  • O ChaCha8Rand apresenta desempenho em nível semelhante ao do gerador do Go 1 e ao PCG.
  • Em código de 32 bits, o ChaCha8Rand é mais rápido que o PCG, que exige multiplicação de 128 bits.
  • Graças ao algoritmo de math/rand/v2, que evita divisão de 64 bits, em operações N(1000) o ChaCha8Rand ou o PCG às vezes podem ser mais rápidos que o gerador do Go 1.
  • No geral, o ChaCha8Rand é mais lento que o gerador do Go 1, mas nunca mais de 2 vezes mais lento, e em servidores comuns a diferença não passa de 3 ns.

Opinião do GN⁺

  • A adoção do ChaCha8Rand no Go 1.22 pode ser considerada um caso exemplar de melhoria no nível da linguagem, elevando bastante a segurança com impacto mínimo no desempenho. É impressionante ver erros frequentes de desenvolvedores sendo bloqueados na origem pelo próprio design da linguagem.
  • Como o próprio texto menciona, esse tipo de erro não se limita ao Go e também é comum em outras linguagens. A segurança do sistema não deveria depender de erro humano, então outras linguagens também deveriam avançar na direção do Go e usar geradores pseudoaleatórios criptograficamente fortes até mesmo para aleatoriedade “matemática”.
  • Ainda assim, o ChaCha8Rand é inadequado para uso em primitivas criptográficas como crypto_box ou xchacha20poly1305. Para esses casos, ainda é necessário usar crypto/rand diretamente.
  • Foi um pouco surpreendente que o runtime do Go também tenha passado a usar ChaCha8Rand para escolher a seed de hash de mapas. Não está claro se uma seed de hash realmente precisa de aleatoriedade criptográfica, mas isso destaca a preocupação da equipe de desenvolvimento em eliminar de saída possíveis vetores de ataque problemáticos.
  • Agora que a qualidade do math/rand, um pacote padrão fornecido pela linguagem, melhorou, parece provável que ele também passe a ser usado com mais frequência nas aplicações. Projetos que vinham usando bibliotecas separadas de geração aleatória por causa da previsibilidade do math/rand podem se beneficiar dessa mudança.

1 comentários

 
GN⁺ 2024-05-08
Opiniões do Hacker News

Resumindo, é o seguinte:

  • No Go 1.20, a função Read do pacote math/rand foi descontinuada, e foram encontrados casos de uso incorreto dela no lugar de crypto/rand. Isso leva ao erro de usar um gerador determinístico de números aleatórios vulnerável do ponto de vista de segurança.
  • Trocar o gerador padrão de números aleatórios do Go por um CSPRNG (gerador pseudoaleatório criptograficamente seguro) é uma abordagem melhor para segurança. O ideal é que um PRNG seja escolhido explicitamente apenas quando realmente for necessário.
  • Ferramentas de análise estática como gosec e golangci-lint emitem avisos sobre o uso de math/rand.
  • O pacote math/rand/v2 usa a cifra ChaCha8 e é inicializado com entropia do sistema, passando uma impressão de ser "seguro", mas ainda assim não é apropriado para tarefas sensíveis à segurança. Nesses casos, deve-se usar crypto/rand.
  • O math/rand do Go 1 pode ser visto, mais precisamente, como um additive lagged Fibonacci generator.
  • O novo math/rand, mesmo no pior caso, apresenta cerca de metade da velocidade do gerador aleatório não seguro anterior, e na maioria dos benchmarks quase não houve diferença. O Go está encontrando um bom equilíbrio entre segurança e desempenho na biblioteca padrão.
  • É visto como uma abordagem amigável para desenvolvedores, que evita erros como os de java.util.Random no Java.
  • Foi levantada a dúvida sobre por que usar ChaCha8 e não uma cifra de bloco com suporte a aceleração por hardware, como AES-GCM.