2 pontos por GN⁺ 2024-05-08 | 1 comentários | Compartilhar no WhatsApp
  • O Go 1.22 troca a fonte padrão de números aleatórios em math/rand e math/rand/v2 por um gerador criptograficamente forte, reduzindo bastante o impacto de erros em casos em que deveria ter sido usado crypto/rand
  • O gerador do Go 1 usava um registrador de deslocamento com realimentação linear com estado de 607 uint64, de modo que, ao observar 607 saídas, já era possível reconstruir valores passados e futuros
  • O PCG-DXSM de math/rand/v2 melhorou a qualidade estatística dos números aleatórios e o tamanho do estado, mas ainda não garante a imprevisibilidade exigida para valores secretos
  • O novo ChaCha8Rand usa uma semente de 32 bytes, faz rekeying a cada 16 blocos e mantém 300 bytes de estado por núcleo, sendo aplicado em math/rand/v2, em parte de math/rand e nas seeds de hash de maps
  • O custo de desempenho é limitado: o ChaCha8Rand é mais lento que o gerador do Go 1, mas nunca passa de 2x, e em servidores comuns a diferença não supera 3 ns, então o ganho de segurança compensa na maioria dos programas

Os novos padrões de aleatoriedade no Go 1.22

  • O Go 1.22 alterou o padrão de math/rand e math/rand/v2 para usar um gerador pseudoaleatório criptograficamente forte
  • O objetivo é reduzir o dano quando desenvolvedores usam math/rand por engano em lugares onde crypto/rand seria necessário
  • As APIs de aleatoriedade do Go tradicionalmente se dividem em duas categorias
    • math/rand: números aleatórios estatísticos usados em simulação, amostragem, análise numérica, algoritmos aleatórios não criptográficos, fuzzing, embaralhamento e exponential backoff
    • crypto/rand: números aleatórios criptográficos para casos que exigem imprevisibilidade, como chaves e tokens

Por que números aleatórios estatísticos não bastam

  • Geradores estatísticos podem ser suficientes para muitos usos não criptográficos se passarem em testes estatísticos básicos
  • Mas, em muitos casos, um observador que conhece o algoritmo consegue prever a sequência seguinte ao ver saídas suficientes
  • O srand e o rand do Unix V3 são formas iniciais que influenciaram depois a API de aleatoriedade do C e de várias outras linguagens
    • Definem o estado a partir de uma única seed inteira
    • Calculam o próximo valor com um gerador congruencial linear (LCG)
    • Como o estado interno é simples, uma única saída já pode bastar para calcular facilmente valores futuros
  • É possível escolher constantes de um LCG para que ele produza cada valor possível uma vez antes de repetir, mas há a fraqueza de que os bits menos significativos se repetem em ciclos curtos

Estrutura e vulnerabilidades do gerador do Go 1

  • O gerador math/rand do Go 1 pertence à família dos registradores de deslocamento com realimentação linear
  • O estado interno é um slice vec com 607 uint64
    • vec[606] é o “tap”
    • vec[334] é o “feed”
    • Para gerar o próximo valor, ele soma tap e feed para formar x, grava x na posição feed e então o retorna
  • A implementação real não move o slice inteiro; ela apenas desloca para trás as posições de tap e feed para reduzir custo
    • Gerar o próximo valor exige duas subtrações, duas somas condicionais, dois loads, uma soma e um store
  • Como o valor retornado é um elemento do vetor de estado interno, ler 607 saídas expõe todo o estado
    • Preenchendo o mesmo vec e executando o algoritmo, é possível prever valores futuros
    • Executando o algoritmo ao contrário, também é possível reconstruir valores passados
  • O gerador do Go 1 não foi feito para segurança, e a qualidade dos números gerados também depende da inicialização do vec

O que o PCG melhorou e quais limites permanecem

  • O math/rand/v2 adotou o PCG de Melissa O’Neill como gerador estatístico mais moderno
  • O PCG do Go se baseia em um LCG de 128 bits e usa uma função scramble para reduzir o estado de 128 bits a uma saída de 64 bits
  • Durante a discussão da proposta, o Go adotou um scramble baseado em multiplicação a partir de uma sugestão de O’Neill
    • Essa forma é chamada de PCG-DXSM
    • O Numpy também usa essa forma de PCG
  • O PCG tem um estado muito menor que o gerador do Go 1
    • Gerador do Go 1: 607 uint64
    • PCG: dois uint64
  • O PCG é menos sensível ao estado inicial e passa em vários testes estatísticos, mas não garante imprevisibilidade
    • O PCG-XSL-RR pode ser revertido
    • Não seria surpreendente que o PCG-DXSM também pudesse ser revertido
  • Para gerar valores secretos, é preciso usar outro gerador, não o PCG

Números aleatórios criptográficos e o papel do sistema operacional

  • Números aleatórios criptográficos precisam permanecer efetivamente imprevisíveis mesmo para um observador que conhece o método de geração e já viu muitas saídas anteriores
  • Protocolos criptográficos, chaves secretas, o comércio moderno e a privacidade online dependem de números aleatórios criptográficos
  • Na prática, o sistema operacional é quem fornece a aleatoriedade
    • Ele coleta aleatoriedade de dispositivos físicos, como mouse, teclado, disco e temporização de rede
    • Mais recentemente, também usa ruído elétrico medido diretamente pela CPU
  • Quando o sistema operacional reúne aleatoriedade suficiente, por exemplo ao menos 256 bits, ele gera sequências longas com hash ou algoritmos criptográficos
  • Antigamente, o uso de arquivos de dispositivo como /dev/random era comum, mas hoje os sistemas operacionais oferecem syscalls diretas
  • O crypto/rand do Go oculta as diferenças entre sistemas operacionais e oferece a mesma interface, rand.Read

Projeto do ChaCha8Rand

  • O novo ChaCha8Rand do Go 1.22 é uma forma levemente modificada da cifra de fluxo ChaCha, de Daniel J. Bernstein
  • A variante mais conhecida do ChaCha é o ChaCha20, usado também em TLS e SSH
  • O artigo Too Much Crypto, de Jean-Philippe Aumasson, considera segura também a variante de 8 rodadas, ChaCha8, que é cerca de 2,5 vezes mais rápida
  • O ChaCha8Rand usa o ChaCha8 como rand.Source, empregando o bloco gerado diretamente como fluxo aleatório em vez de fazer XOR com a entrada
    • Isso equivale a cifrar ou decifrar dados inteiramente zerados

Mudanças no ChaCha8Rand

  • O ChaCha8Rand usa uma seed de 32 bytes como chave do ChaCha8
  • O ChaCha8 gera blocos de 64 bytes, tratando o cálculo como 16 uint32
  • Implementações comuns conseguem calcular 4 blocos ao mesmo tempo com instruções SIMD, mas para usá-los como entrada de XOR é preciso desfazer os blocos intercalados
    • O ChaCha8Rand define esse próprio bloco intercalado como fluxo aleatório, eliminando o custo do unshuffle
  • Na etapa final de um bloco do ChaCha8, certos valores são somados a cada uint32
    • Metade deles é material de chave e a outra metade são constantes conhecidas
    • O ChaCha8Rand deixa de somar novamente as constantes conhecidas, removendo metade da soma final
  • A cada 16º bloco gerado, os 32 bytes finais passam a ser usados como chave dos 16 blocos seguintes
    • Esse rekeying fornece uma forma de segurança futura
    • Mesmo que todo o estado do gerador em memória seja exposto, só é possível recuperar valores desde o último rekeying, não os anteriores
  • O Go publicou a especificação C2SP do ChaCha8Rand e casos de teste, permitindo que outras implementações compartilhem a mesma repetibilidade da implementação do Go com a mesma seed

Onde ele é aplicado na biblioteca padrão

  • O runtime do Go mantém um estado ChaCha8Rand por núcleo, semeado com aleatoriedade criptográfica fornecida pelo sistema operacional
    • O tamanho do estado é de 300 bytes por núcleo
    • Em um sistema com 16 núcleos, isso fica em nível parecido ao único estado compartilhado de 4.872 bytes do gerador do Go 1
    • O estado por núcleo permite gerar números aleatórios rapidamente sem contenção por locks
  • As funções de pacote de math/rand/v2 sempre usam ChaCha8Rand
    • Ex.: rand.N, rand.Float64
  • As funções de pacote de math/rand usam ChaCha8Rand quando rand.Seed não foi chamado
    • Ex.: rand.Intn, rand.Float64
    • Se rand.Seed for chamado, é preciso voltar ao gerador do Go 1 por compatibilidade
  • O runtime passou a escolher a seed de hash de novos maps com ChaCha8Rand em vez do gerador anterior baseado em wyrand
    • Se um atacante souber a função de hash específica usada pela implementação de map, ele pode preparar entradas para forçar comportamento quadrático
    • Usar uma seed por map, e não uma única seed global, também evita outros comportamentos degenerados
    • Não está claro se a seed de map realmente exige aleatoriedade criptográfica, mas a troca foi simples e prudente
  • Código que precisa de uma instância separada de ChaCha8Rand pode criar diretamente rand.ChaCha8

Redução do impacto de erros de segurança

  • O Go busca reduzir ou eliminar erros comuns com impacto de segurança, ajudando a escrever código seguro por padrão
  • Quando o Read de math/rand foi marcado como deprecated no Go 1.20, descobriu-se que alguns desenvolvedores estavam usando math/rand em situações que exigiam crypto/rand, como geração de material de chave
  • No Go 1.20, esse tipo de erro era um problema grave de segurança
    • Era preciso investigar onde a chave foi usada
    • Como essa chave pode ter sido exposta
    • E se outras saídas aleatórias deram pistas ao atacante para derivá-la
  • No Go 1.22, o mesmo erro continua sendo um erro, mas fica menos provável que ele vire uma catástrofe de segurança
  • Mesmo assim, ainda é melhor usar crypto/rand para valores secretos
    • O kernel do sistema operacional consegue proteger melhor os valores aleatórios
    • O kernel continua adicionando nova entropia ao gerador
    • A implementação do kernel recebeu mais revisão

Casos que não parecem criptográficos

  • Gerar UUIDs aleatórios pode parecer um caso em que math/rand basta, já que UUID não é um valor secreto
  • Mas, ao semear math/rand com a hora atual, computadores diferentes executados no mesmo instante podem produzir o mesmo valor
    • Em sistemas em que a hora atual só tem precisão de milissegundos, essa possibilidade aumenta
  • Mesmo com a semeadura automática baseada em entropia do sistema operacional no Go 1.20, a seed do gerador do Go 1 continua sendo apenas um inteiro de 63 bits
    • Um programa que gera um UUID na inicialização fica limitado a 2⁶³ UUIDs iniciais possíveis
    • Depois de cerca de 2³¹ UUIDs, colisões passam a ser uma possibilidade
  • O ChaCha8Rand do Go 1.22 é semeado com 256 bits de entropia
    • O número de primeiros UUIDs possíveis passa a ser 2²⁵⁶
    • Não há motivo para se preocupar com colisões
  • Balanceamento de carga em que um servidor frontend distribui requisições aleatoriamente entre backends também pode exigir números aleatórios imprevisíveis
    • Se um atacante observar a distribuição e conhecer um algoritmo previsível, ele pode concentrar requisições caras em um backend específico
    • No gerador do Go 1, isso era raro, mas possível
    • No Go 1.22, isso deixa de ser problema

Características de desempenho

  • Os ganhos de segurança do ChaCha8Rand têm um custo pequeno, mas o desempenho continua na mesma faixa do gerador do Go 1 e do PCG
  • Duas operações foram usadas na comparação
    • Uint64: retorna o próximo uint64 do fluxo aleatório
    • N(1000): retorna um número aleatório no intervalo [0, 1000)
  • Em chips x86 de 64 bits, ao compilar com GOARCH=386 e executar em modo 32 bits, o PCG fica mais lento que o ChaCha8Rand por causa da multiplicação de 128 bits
    • O ChaCha8Rand usa aritmética SIMD de 32 bits
  • Em alguns sistemas, Go 1: Uint64 é mais rápido que PCG: Uint64, mas Go 1: N(1000) é mais lento que PCG: N(1000)
    • O N(1000) do Go 1 usa duas divisões inteiras de 64 bits para reduzir o intervalo
    • O N(1000) do PCG e do ChaCha8 usa o algoritmo mais rápido de math/rand/v2, que evita divisões na maioria dos casos
  • No geral, o ChaCha8Rand é mais lento que o gerador do Go 1, mas não chega a ser mais de 2 vezes mais lento
  • Em servidores comuns, a diferença não passa de 3 ns, e são muito poucos os programas em que isso vira gargalo

Conclusão

  • O Go 1.22 aumenta a segurança dos programas sem exigir mudanças no código
  • A estratégia central foi fortalecer o próprio math/rand para reduzir um erro comum: usar math/rand por engano no lugar de crypto/rand
  • Há casos parecidos no ecossistema, como o pacote npm keypair, que tenta gerar pares de chaves RSA com Math.random do JavaScript quando a Web Crypto API não está disponível
  • A segurança de sistemas não pode depender da suposição de que desenvolvedores nunca erram
  • O ChaCha8Rand do Go 1.22 mostra que usar um gerador pseudoaleatório criptograficamente forte até para aleatoriedade “matemática” pode oferecer desempenho competitivo em relação a outros geradores

1 comentários

 
GN⁺ 2024-05-08
Opiniões do Hacker News
  • Como mencionado no artigo, o rclone cometeu exatamente esse erro
    Ao refatorar um código que usava Read de crypto/rand, o import foi alterado automaticamente e, provavelmente ao se misturar com código que usava math/rand, o goimports parece ter mudado para math/rand
    Como resultado, em vez de um gerador de números aleatórios seguro, passamos a usar um gerador determinístico que o rclone semeava com o horário, e isso não foi percebido no diff :-(
    https://www.cvedetails.com/cve/CVE-2020-28924/
    Por isso sou muito a favor dessa mudança

    • Isso dói, desculpe. Em 2016, o goimports foi alterado para preferir crypto/rand, então não está claro o que aconteceu durante a refatoração
      Talvez tenha entrado no mesmo arquivo algum código que usava uma API específica de math/rand
      https://go-review.googlesource.com/24847
      De todo modo, é bom ver essa parte sendo arrumada
    • Eu também já recebi um relatório de vulnerabilidade por alguém achar erroneamente que math/rand estava sendo usado. Na prática não estava; era só uma confusão entre vários arquivos, então não foi grave, mas isso mostra como toda essa questão é confusa
      text/template e html/template são parecidos. Olhando em retrospecto, esse tipo de ocultação por nome de pacote foi uma má ideia
    • Também vi que, ao pesquisar por "secure password generation golang", quase todos os exemplos usam math/rand
      Pior ainda: todos reinicializam a seed com o horário atual logo antes de gerar a senha
      Descobri isso depois de encontrar alguém usando math/rand no nosso código e ficar curioso para saber de onde aquilo tinha sido copiado
    • O goimports tratou math/rand.Read e crypto/rand.Read como casos especiais praticamente desde o início
      Mas, olhando para https://github.com/golang/tools/commit/0835c735343e0d8e375f0... de 2016, há menção a um período em que "rand.Read" podia ser interpretado como "math/rand"
      Talvez tenha sido nessa época
    • Não parece tão difícil oferecer uma chamada de API com um nome como "PredictableRand"
  • Na semana passada, spacey também publicou em https://news.ycombinator.com/item?id=40237491, mas aquele post parece ter sido enterrado por engano como duplicata de https://news.ycombinator.com/item?id=40224864
    Os dois posts do blog go.dev são duas partes da mesma série, mas são bem diferentes. Este texto é sobre algoritmos eficientes de geração de números aleatórios seguros, enquanto o anterior é sobre design de APIs em Go

  • Russell Cox produz de forma consistente ótimos blogs técnicos, propostas e trabalhos
    Se você quer melhorar a clareza da escrita e do pensamento, começar por Russell Cox é um bom ponto de partida

    • A série dele sobre autômatos finitos e expressões regulares me fez mergulhar de cabeça nessa área
      Na época eu nem sabia quem era Russ Cox, mas aquela série de textos era realmente incrível
      É provavelmente o material gratuito de maior qualidade sobre implementação de expressões regulares; depois disso vêm vários livros focados em compiladores, mas eles não são gratuitos nem fáceis de encontrar na web
    • Ele também faz boas demonstrações em vídeo https://research.swtch.com/acme
  • Eu já usei math/rand em um lugar onde crypto/rand era absolutamente necessário
    Como resultado, as primeiras versões do dnscrypt-proxy2 usavam chaves estáticas
    A causa foi uma extensão do VSCode que adicionava imports automaticamente. Em todos os arquivos-fonte que precisavam de números aleatórios seguros, eu importava crypto/rand manualmente com cuidado, mas esqueci em um arquivo; tudo compilou e funcionou bem, e não percebi que a extensão tinha adicionado silenciosamente um import de math/rand naquele arquivo específico
    Desde então, importo crypto/rand com o alias cryptorand para evitar que o rand errado seja importado automaticamente
    A propósito, Zig também usa um gerador de números aleatórios baseado em ChaCha8 e, em operações criptográficas, o usuário não pode fornecer seu próprio gerador: um gerador seguro é sempre usado. Para testes, algumas funções recebem uma seed explícita
    Para ambientes restritos, a biblioteca padrão também inclui um gerador menor baseado na permutação Ascon e na construção Reverie

    • Não sei exatamente o que aconteceu no seu caso, mas é bem provável que tenha sido diferente do que você descreveu
      Em 2016, o goimports foi alterado para preferir crypto/rand em vez de math/rand (https://go-review.googlesource.com/24847), e isso foi antes de existir suporte a VSCode para Go
    • Outra pessoa disse a mesma coisa. Sinceramente, a prática de adicionar imports automaticamente parece completamente estranha e acaba minando o próprio objetivo de separar nomes em namespaces
  • Mesmo nos anos 2020, eu pensava com frequência por que as implementações padrão de números aleatórios em várias linguagens de programação ainda usam geradores rápidos como LFSR e MT.
    Parece melhor adotar uma postura conservadora, assumindo que as pessoas não sabem se precisam de um gerador de números pseudoaleatórios ou de um gerador de números pseudoaleatórios criptograficamente seguro, mudar o padrão para o segundo e fazer com que apenas quem precisa do primeiro o escolha explicitamente.

    • Na nova API orientada a objetos de números aleatórios do PHP 8.2, foi feito exatamente isso.
      Se o desenvolvedor não escolher explicitamente o mecanismo de números aleatórios a ser usado, recebe um gerador criptograficamente seguro.
      Agora a parte difícil é convencer as pessoas a migrarem para a nova API. Indo além, nem mesmo é fácil migrar de mt_rand(), que usa uma instância global de Mt19937, para random_int(), baseado em CSPRNG e já disponível desde o PHP 7.0.
      [1] https://www.php.net/releases/8.2/en.php#random_extension
    • Recentemente comecei a usar uma nova biblioteca Go que gera IDs aleatórios para vários componentes de uma estrutura de dados complexa.
      No meu caso de uso havia dezenas de milhares de componentes e, ao fazer profiling, vi que uma parte considerável do tempo de inicialização da estrutura de dados era gasto no Read() de crypto/rand, que no meu MacBook executava chamadas de sistema.
      Ao corrigir a biblioteca para usar o Read() de math/rand, o desempenho melhorou bastante.
      Além de math/rand ser mais rápido, eu também estava preocupado em esgotar sem motivo o pool de entropia do sistema. Nesse caso, a única razão para os IDs precisarem ser aleatórios era permitir adicionar mais componentes depois de serializar/desserializar a estrutura de dados, algo que eu não pretendia fazer.
      Não sei exatamente como o momento da mudança descrita neste blog se encaixa com a minha experiência. Talvez eu tenha usado uma versão antiga da biblioteca e, se agora crypto/rand é praticamente indistinguível de math/rand, então ótimo :-)
    • Um dos melhores argumentos para usar um CSPRNG, neste caso ChaCha8, é que nos benchmarks ele fica dentro de 2x em relação ao PCG.
      O tamanho do estado ainda é relativamente grande (64 bytes contra 16 bytes), mas é muito melhor do que mt19937 ou o PRNG antigo do Go.
      Se um CSPRNG fosse muito mais lento, como costuma acontecer com CSPRNGs comuns que não são variantes de ChaCha com rodadas reduzidas, ele perderia atratividade como padrão.
    • Em que outros casos alguém precisaria do primeiro? Só consigo pensar em uma semente fixa para obter resultados reproduzíveis, como em testes ou validação.
      Há mais um pequeno fator que empurra as pessoas para PRNGs mesmo quando elas não precisam de seed. As APIs de CSPRNG incluem erros que sempre precisam ser tratados, para o caso de falha em chamada de sistema ou falta de entropia.
      Com que frequência uma leitura de crypto/rand realmente falha? Quanto seria preciso ler em um sistema moderno para esgotar a entropia? Nunca vi falha mesmo em bilhões de requisições, e dd também funciona bem.
      Fico me perguntando se, para a maioria dos casos de uso, uma API no estilo Must/panic talvez fosse o padrão correto.
      Além disso, olhei o pacote secrets do Python (https://docs.python.org/3/library/secrets.html) e não há nenhuma menção de que ele possa lançar exceções. Na prática, isso simplesmente não acontece?
    • Gosto da abordagem de que “todos os números aleatórios do sistema devem vir de um CSPRNG, a menos que se opte explicitamente por sair disso”.
      Você perde um pouco de desempenho, mas ganha uma garantia muito mais forte de não causar uma catástrofe por usar o gerador de números aleatórios errado.
      É uma pena que, em quase todas as linguagens, os desenvolvedores ainda tenham que se preocupar com essa aresta cortante.
  • Para quem não sabe, gosec e sua extensão golangci-lint avisam sobre o uso de math/rand.
    https://github.com/securego/gosec/blob/d3b2359ae29fe344f4df5...

    • Uma das coisas de que mais gosto em math/rand/v2 é poder usá-lo na empresa sem diretivas nolint e sem a discussão de PR que vem depois.
  • Ainda estou interpretando as recomendações sobre segurança e as novas opções da v2.
    O post do blog usa frases como “valores secretos exigem outra coisa” e, em seguida, entra em detalhes sobre aleatoriedade criptográfica, ChaCha8 e o fato de ser semeado com números aleatórios do sistema, passando uma impressão muito “segura”.
    Mas a documentação do pacote diz isto:
    ... but it should not be used for security-sensitive work ... This package's outputs might be easily predictable regardless of how it's seeded. For random numbers suitable for security-sensitive work, see the crypto/rand package.
    Então por que o post do blog parece insinuar o uso de math/rand/v2 para “valores secretos”?
    Em resumo, significa que tudo que for sensível ainda deve usar crypto/rand, e que a melhoria descrita aqui é uma rede de segurança para quando alguém usar math/rand/v2 indevidamente?

    • Sim. math/rand/v2 não é o ideal, mas usá-lo por engano onde se deveria usar crypto/rand já não vira imediatamente uma falha de segurança fatal.
      O texto também diz isto:
      Ainda é melhor usar crypto/rand. O kernel do sistema operacional consegue fazer um trabalho melhor ao manter os valores aleatórios em segredo contra vários ataques de espionagem, continua adicionando nova entropia ao gerador e passou por mais revisões. Mas usar math/rand por engano já não é mais um desastre de segurança.
  • Mesmo no pior benchmark, a nova estratégia é apenas cerca de metade mais lenta do que o gerador de números aleatórios não seguro, e a maioria dos benchmarks ficou muito mais próxima disso
    Go consegue equilibrar bem segurança e desempenho para a biblioteca padrão e os apps construídos sobre ela. Seria bom se outros ecossistemas acompanhassem
    Se uma aplicação precisar de números aleatórios rápidos e não seguros, deve implementar seu próprio gerador interno
    Deixar números aleatórios não seguros ao alcance fácil é uma ferramenta para dar um tiro no próprio pé que pode ser guardada

    • Sinceramente, isso parece pior
      Incentivar as pessoas a presumirem que uma primitiva "random" é criptograficamente segura é promover más práticas
      Tornar math/rand/v2 criptograficamente seguro pode resolver um problema, mas agora algo que não parece prometer segurança passa a estar em um estado “ok”
      Em geral, não existe a convenção de que as funções de math/rand sejam criptograficamente seguras. Se mudarmos isso para fazer código ruim funcionar corretamente por acaso, podemos acabar mascarando quais outros erros estamos cometendo se já estamos cometendo um erro tão óbvio
  • O math/rand do Go 1 seria mais precisamente chamado de gerador Fibonacci com atraso aditivo
    A publicação original é o artigo de Green, Smith e Klem
    [1] https://doi.org/10.1145/320998.321006

    • Acho que esse artigo não menciona a parte do “atraso”. Ou talvez eu tenha deixado passar
      Também conheço https://www.leviathansecurity.com/blog/attacking-gos-lagged-..., e ali também o chamam de gerador Fibonacci com atraso
      Rob Pike e eu trocamos e-mails alguns meses atrás com Don Mitchell, que escreveu a versão original em C do gerador do Go 1, perguntando como ele descreveria esse algoritmo, e ele respondeu: “pelo que me lembro, Jim e eu implementamos um gerador parecido com LFSR, do Marsaglia”
      Acho que as duas descrições, gerador Fibonacci com atraso e gerador semelhante a LFSR, são corretas a partir de perspectivas diferentes. Qualquer uma serve, mas no texto decidi usar a descrição do autor original
  • Se eu fosse apontar uma pequena falha, parece que aqui aleatoriedade estatística e gerador de números pseudoaleatórios foram misturados
    A definição da Wikipédia para aleatoriedade estatística é: “diz-se que uma sequência de números é estatisticamente aleatória quando não contém padrões ou regularidades reconhecíveis”
    Essa definição se aplica a um gerador de números aleatórios verdadeiro (TRNG)? É melhor que sim. Pelo menos no longo prazo ou no limite, deveria. Caso contrário, não é um TRNG
    Um TRNG deve gerar, no longo prazo, uma “sequência de números sem padrões ou regularidades reconhecíveis”
    Portanto, aleatoriedade estatística não significa PRNG, mas pode-se dizer que ela também se aplica a TRNGs
    O problema parece vir do fato de existirem muitos testes de aleatoriedade estatística para verificar se um PRNG tem uma forma limitada de aleatoriedade estatística
    Por isso, para identificar um PRNG, a expressão “gerador de números pseudoaleatórios” teria sido mais adequada do que “aleatoriedade estatística”. Ainda assim, é uma falha bem pequena