- 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
Opiniões do Hacker News
Resumindo, é o seguinte:
Readdo pacotemath/randfoi descontinuada, e foram encontrados casos de uso incorreto dela no lugar decrypto/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.gosecegolangci-lintemitem avisos sobre o uso demath/rand.math/rand/v2usa 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 usarcrypto/rand.math/randdo Go 1 pode ser visto, mais precisamente, como um additive lagged Fibonacci generator.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.java.util.Randomno Java.