Vamos jogar poker no computador, apenas você, eu e um servidor em que ambos confiamos. O servidor usa um gerador de números pseudo-aleatórios que é inicializado com uma semente de 32 bits antes de jogar. Portanto, existem cerca de quatro bilhões de decks possíveis.
Recebo cinco cartas na minha mão - aparentemente não estamos jogando Texas Hold 'Em. Suponha que as cartas sejam distribuídas uma para mim, uma para você, uma para mim, uma para você e assim por diante. Então, eu tenho as primeiras, terceira, quinta, sétima e nona cartas do baralho.
Anteriormente, executei o gerador de números pseudo-aleatórios quatro bilhões de vezes, uma vez com cada semente, e escrevi o primeiro cartão gerado para cada um em um banco de dados. Suponha que minha primeira carta seja a dama de espadas. Isso mostra apenas uma como a primeira carta em uma em cada 52 desses decks possíveis, então reduzimos os decks possíveis de quatro bilhões para cerca de 80 milhões.
Suponha que minha segunda carta seja a de três corações. Agora eu corro meu RNG 80 milhões mais vezes usando as 80 milhões de sementes que produzem a rainha de espadas como o primeiro número. Isso leva alguns segundos. Eu escrevo todos os baralhos que produzem os três corações como a terceira carta - a segunda carta na minha mão. Novamente, isso representa apenas cerca de 2% dos decks, então agora estamos com 2 milhões de decks.
Suponha que a terceira carta na minha mão seja a 7 dos clubes. Eu tenho um banco de dados de 2 milhões de sementes que distribuem meus dois cartões; Eu corro meu RNG mais 2 milhões de vezes para encontrar os 2% desses decks que produzem os 7 dos clubes como a terceira carta, e estamos com apenas 40 mil decks.
Você vê como isso acontece. Eu corro meu RNG 40000 mais vezes para encontrar todas as sementes que produzem minha quarta carta e isso nos leva a 800 decks, e depois corro 800 mais vezes para obter as ~ 20 sementes que produzem minha quinta carta, e agora apenas gere esses vinte baralhos de cartas e eu sei que você tem uma das vinte mãos possíveis. Além disso, tenho uma ideia muito boa do que vou desenhar a seguir.
Agora você vê por que a verdadeira aleatoriedade é importante? Da maneira como a descreve, você acha que a distribuição é importante, mas a distribuição não é o que torna um processo aleatório. Imprevisibilidade é o que torna um processo aleatório.
ATUALIZAR
Com base nos comentários (agora excluídos devido à sua natureza pouco construtiva), pelo menos 0,3% das pessoas que leram isso estão confusas quanto ao meu argumento. Quando as pessoas argumentam contra pontos que eu não fiz, ou pior, argumentam por pontos que eu assumi na suposição de que não os fiz, então eu sei que preciso explicar de maneira mais clara e cuidadosa.
Parece haver uma confusão particular em torno da distribuição de palavras, por isso quero chamar os usos com cuidado.
As perguntas em questão são:
- Como os números pseudo-aleatórios e números verdadeiramente aleatórios diferem?
- Por que a diferença é importante?
- As diferenças têm algo a ver com a distribuição da saída do PRNG?
Vamos começar considerando a maneira perfeita de gerar um baralho aleatório com o qual jogar poker. Depois, veremos como outras técnicas para gerar decks são diferentes e se é possível tirar proveito dessa diferença.
Vamos começar supondo que temos uma caixa mágica rotulada TRNG
. Como entrada, damos a ele um número inteiro n maior ou igual a um e, como saída, nos fornece um número verdadeiramente aleatório entre um e n, inclusive. A saída da caixa é totalmente imprevisível (quando é fornecido um número diferente de um) e qualquer número entre um e n é tão provável quanto outro; isto é, a distribuição é uniforme . (Existem outras verificações estatísticas mais avançadas da aleatoriedade que poderíamos executar; estou ignorando esse ponto, pois não é pertinente ao meu argumento. O TRNG é perfeitamente estatisticamente aleatório por suposição.)
Começamos com um baralho de cartas não embaralhadas. Pedimos à caixa um número entre um e 52 - ou seja TRNG(52)
,. Qualquer que seja o número que devolvemos, contamos muitas cartas do nosso baralho classificado e as removemos. Torna-se a primeira carta no baralho embaralhado. Em seguida, solicitamos TRNG(51)
e fazemos o mesmo para selecionar o segundo cartão, e assim por diante.
Outra maneira de ver é: são 52! = 52 x 51 x 50 ... x 2 x 1 decks possíveis, que são aproximadamente 2 226 . Nós escolhemos um deles verdadeiramente ao acaso.
Agora nós negociamos as cartas. Quando olho para minhas cartas, não tenho idéia de quais cartas você tem. (Além do fato óbvio de que você não possui nenhuma das cartas que tenho.) Elas podem ser quaisquer cartas, com igual probabilidade.
Então deixe-me explicar isso claramente. Temos distribuição uniforme de cada saída individual de TRNG(n)
; cada um escolhe um número entre 1 e n com probabilidade 1 / n. Além disso, o resultado desse processo é que escolhemos um dos 52! decks possíveis com uma probabilidade de 1/52 !, portanto, a distribuição no conjunto de decks possíveis também é uniforme.
Tudo certo.
Agora vamos supor que temos uma caixa menos mágica, rotulada PRNG
. Antes de poder usá-lo, ele deve ser semeado com um número não assinado de 32 bits.
LADO: Por que 32 ? Não foi possível semear com um número de 64, 256 ou 10000 bits? Certo. Mas (1) na prática, a maioria dos PRNGs disponíveis no mercado é semeada com um número de 32 bits e (2) se você possui 10000 bits de aleatoriedade para fazer a semente, por que está usando um PRNG? Você já tem uma fonte de 10000 bits de aleatoriedade!
De qualquer forma, voltando ao modo como o PRNG funciona: depois de semeado, você pode usá-lo da mesma maneira que usa TRNG
. Ou seja, você passa um número, n, e retorna um número entre 1 e n, inclusive. Além disso, a distribuição dessa produção é mais ou menos uniforme . Ou seja, quando pedimos PRNG
um número entre 1 e 6, obtemos 1, 2, 3, 4, 5 ou 6 aproximadamente um sexto das vezes, independentemente da semente.
Quero enfatizar esse ponto várias vezes, porque parece ser o que está confundindo certos comentaristas. A distribuição do PRNG é uniforme de pelo menos duas maneiras. Primeiro, suponha que escolhemos qualquer semente em particular. Esperamos que a sequência PRNG(6), PRNG(6), PRNG(6)...
um milhão de vezes produza uma distribuição uniforme de números entre 1 e 6. E segundo, se escolhermos um milhão de sementes diferentes e chamarmos PRNG(6)
uma vez para cada semente, novamente esperaremos uma distribuição uniforme de números de 1 a 6. 6. A uniformidade do PRNG em qualquer uma dessas operações não é relevante para o ataque que estou descrevendo .
Diz-se que esse processo é pseudo-aleatório porque o comportamento da caixa é realmente totalmente determinístico; ele escolhe entre um dos 2 32 comportamentos possíveis com base na semente. Ou seja, uma vez semeada, PRNG(6), PRNG(6), PRNG(6), ...
produz uma sequência de números com uma distribuição uniforme, mas essa sequência é inteiramente determinada pela semente. Para uma determinada sequência de chamadas, digamos, PRNG (52), PRNG (51) ... e assim por diante, existem apenas 2 32 sequências possíveis. A semente escolhe essencialmente qual deles obteremos.
Para gerar um baralho, o servidor agora gera uma semente. (Como? Voltaremos a esse ponto.) Em seguida, eles chamam PRNG(52)
, PRNG(51)
e assim por diante para gerar o convés, semelhante ao anterior.
Este sistema é suscetível ao ataque que descrevi. Para atacar o servidor, primeiro, antecipadamente, semeamos nossa própria cópia da caixa com 0 e pedimos PRNG(52)
e anotamos isso. Em seguida, reintroduzimos 1, pedimos PRNG(52)
e escrevemos isso até 2 32 -1.
Agora, o servidor de poker que está usando o PRNG para gerar decks deve gerar uma semente de alguma forma. Não importa como eles fazem isso. Eles poderiam ligar TRNG(2^32)
para obter uma semente verdadeiramente aleatória. Ou eles poderiam tomar o tempo atual como uma semente, o que dificilmente é aleatório; Eu sei que horas são tanto quanto você. O ponto do meu ataque é que isso não importa, porque eu tenho meu banco de dados . Quando vejo meu primeiro cartão, posso eliminar 98% das sementes possíveis. Quando vejo minha segunda carta, posso eliminar 98% a mais, e assim por diante, até que finalmente chegue a um punhado de sementes possíveis e saiba com alta probabilidade o que está em sua mão.
Agora, novamente, quero enfatizar que a suposição aqui é que, se ligássemos PRNG(6)
um milhão de vezes, obteríamos cada número aproximadamente um sexto das vezes . Essa distribuição é (mais ou menos) uniforme , e se a uniformidade dessa distribuição é tudo o que importa , tudo bem. O ponto principal da pergunta era : existem outras coisas além dessa distribuição com a PRNG(6)
qual nos preocupamos? e a resposta é sim . Também nos preocupamos com a imprevisibilidade .
Outra maneira de analisar o problema é que, embora a distribuição de um milhão de chamadas PRNG(6)
seja boa, porque o PRNG está escolhendo entre apenas 32 comportamentos possíveis, ele não pode gerar todos os baralhos possíveis. Só pode gerar 2 32 dos 2 226 decks possíveis; uma pequena fração. Portanto, a distribuição no conjunto de todos os decks é muito ruim. Mas, novamente, o ataque fundamental aqui se baseia em sermos capazes de prever com sucesso o comportamento passado e futuro de PRNG
uma pequena amostra de sua produção.
Deixe-me dizer isso uma terceira ou quatro vezes para garantir que isso aconteça. Existem três distribuições aqui. Primeiro, a distribuição do processo que produz a semente aleatória de 32 bits. Isso pode ser perfeitamente aleatório, imprevisível e uniforme, e o ataque ainda funcionará . Segundo, a distribuição de um milhão de chamadas para PRNG(6)
. Isso pode ser perfeitamente uniforme e o ataque ainda funcionará. Terceiro, a distribuição dos decks escolhidos pelo processo pseudo-aleatório que descrevi. Essa distribuição é extremamente ruim; apenas uma pequena fração dos decks possíveis da IRL pode ser escolhida. O ataque depende da previsibilidade do comportamento do PRNG com base no conhecimento parcial de sua saída .
À parte: Esse ataque exige que o invasor saiba ou seja capaz de adivinhar qual é o algoritmo exato usado pelo PRNG. Se isso é realista ou não, é uma questão em aberto. No entanto, ao projetar um sistema de segurança, você deve projetá-lo para ser seguro contra ataques, mesmo que o invasor conheça todos os algoritmos do programa . Em outras palavras: a parte de um sistema de segurança que deve permanecer em segredo para que o sistema seja seguro é chamada de "chave". Se o seu sistema depende, para sua segurança, dos algoritmos usados como secretos, sua chave contém esses algoritmos . Essa é uma posição extremamente fraca para se estar!
Se movendo.
Agora vamos supor que tenhamos uma terceira caixa mágica rotulada CPRNG
. É uma versão com força de criptografia PRNG
. É preciso uma semente de 256 bits em vez de uma semente de 32 bits. Ele compartilha com PRNG
a propriedade que a semente escolhe entre um dos 2 256 comportamentos possíveis. E, como nossas outras máquinas, possui a propriedade de que um grande número de chamadas CPRNG(n)
produz uma distribuição uniforme de resultados entre 1 e n: cada uma acontece 1 / n do tempo. Podemos executar nosso ataque contra isso?
Nosso ataque original exige que armazenemos 2 32 mapeamentos de sementes para PRNG(52)
. Mas 2 256 é um número muito maior; é completamente inviável executar CPRNG(52)
isso muitas vezes e armazenar os resultados.
Mas suponha que exista outra maneira de extrair o valor CPRNG(52)
e deduzir um fato sobre a semente. Até agora, fomos muito burros, forçando brutalmente todas as combinações possíveis. Podemos olhar dentro da caixa mágica, descobrir como ela funciona e deduzir fatos sobre a semente com base na produção?
Não. Os detalhes são muito complicados para explicar, mas os CPRNGs são projetados de maneira inteligente, de modo que é inviável deduzir qualquer fato útil sobre a semente da primeira saída de CPRNG(52)
ou de qualquer subconjunto da saída, não importa o tamanho .
OK, então agora vamos supor que o servidor esteja usando CPRNG
para gerar decks. Ele precisa de uma semente de 256 bits. Como ele escolhe essa semente? Se escolher qualquer valor que um invasor possa prever , de repente o ataque se tornará viável novamente . Se pudermos determinar que das 2 256 sementes possíveis, apenas quatro bilhões delas provavelmente serão escolhidas pelo servidor, então estaremos de volta aos negócios . Podemos montar esse ataque novamente, prestando atenção apenas no pequeno número de sementes que podem ser geradas.
Portanto, o servidor deve trabalhar para garantir que o número de 256 bits seja distribuído uniformemente - ou seja, cada semente possível é escolhida com probabilidade de 1/2 256 . Basicamente, o servidor deve estar chamando TRNG(2^256)-1
para gerar a semente CPRNG
.
E se eu puder invadir o servidor e examiná-lo para ver qual semente foi escolhida? Nesse caso, o atacante conhece o passado e o futuro completos do CPRNG . O autor do servidor precisa se proteger contra esse ataque! (É claro que se eu conseguir montar esse ataque com êxito, provavelmente também posso transferir o dinheiro diretamente para minha conta bancária, então talvez isso não seja tão interessante. O ponto é: a semente deve ser um segredo difícil de adivinhar, e um um número verdadeiramente aleatório de 256 bits é bastante difícil de adivinhar.)
Voltando ao meu argumento anterior sobre defesa em profundidade: a semente de 256 bits é a chave desse sistema de segurança. A idéia de um CPRNG é que o sistema esteja seguro enquanto a chave estiver segura ; mesmo que todos os outros fatos sobre o algoritmo sejam conhecidos, desde que você mantenha a chave em segredo, as cartas do oponente são imprevisíveis.
OK, então a semente deve ser secreta e distribuída uniformemente, porque, se não for, podemos montar um ataque. Assumimos que a distribuição dos resultados de CPRNG(n)
é uniforme. E a distribuição sobre o conjunto de todos os decks possíveis?
Você pode dizer: existem 2 256 sequências possíveis produzidas pelo CPRNG, mas existem apenas 2 226 decks possíveis. Portanto, existem mais sequências possíveis do que os decks, então estamos bem; agora todos os baralhos de IRL possíveis (com alta probabilidade) são possíveis neste sistema. E esse é um bom argumento, exceto ...
2 226 é apenas uma aproximação de 52 !. Divida isso. 2 256/52 ! não pode ser um número inteiro porque, por um lado, 52! é divisível por 3, mas não há potência de dois! Como esse número não é todo agora, temos a situação em que todos os decks são possíveis , mas alguns são mais prováveis que outros .
Se isso não estiver claro, considere a situação com números menores. Suponha que tenhamos três cartões, A, B e C. Suponha que usemos um PRNG com uma semente de 8 bits, portanto, existem 256 sementes possíveis. Existem 256 saídas possíveis PRNG(3)
dependendo da semente; não há como ter um terço deles como A, um terço deles com B e um terço com C porque 256 não é igualmente divisível por 3. Deve haver um pequeno viés em relação a um deles.
Da mesma forma, 52 não se divide uniformemente em 2 256 , então deve haver algum viés em relação a algumas cartas, como a primeira carta escolhida e uma diferença em relação a outras.
Em nosso sistema original com uma semente de 32 bits, houve um grande viés e a grande maioria dos decks possíveis nunca foi produzida. Neste sistema, todos os decks podem ser produzidos, mas a distribuição dos decks ainda é falha . Alguns decks são um pouco mais prováveis que outros.
Agora, a pergunta é: nós temos um ataque baseado nessa falha? e a resposta está na prática, provavelmente não . CPRNGs são projetados de modo que se a semente é verdadeiramente aleatório , em seguida, é computacionalmente inviável para dizer a diferença entre CPRNG
e TRNG
.
OK, então vamos resumir.
Como os números pseudo-aleatórios e números verdadeiramente aleatórios diferem?
Eles diferem no nível de previsibilidade que exibem.
- Números verdadeiramente aleatórios não são previsíveis.
- Todos os números pseudo-aleatórios são previsíveis se a semente puder ser determinada ou adivinhada.
Por que a diferença é importante?
Porque existem aplicativos em que a segurança do sistema depende de imprevisibilidade .
- Se um TRNG for usado para escolher cada cartão, o sistema estará indisponível.
- Se um CPRNG for usado para escolher cada cartão, o sistema estará seguro se a semente for imprevisível e desconhecida.
- Se um PRNG comum com um pequeno espaço de semente é usado, o sistema não é seguro, independentemente de a semente ser imprevisível ou desconhecida; um espaço de semente pequeno o suficiente é suscetível a ataques de força bruta do tipo que descrevi.
A diferença tem algo a ver com a distribuição da saída do PRNG?
A uniformidade de distribuição ou a falta dela para chamadas individuais para RNG(n)
não é relevante para os ataques que eu descrevi.
Como vimos, tanto um PRNG
e CPRNG
produzir distribuições pobres da probabilidade de escolher qualquer plataforma individual de todos os possíveis decks. O PRNG
é consideravelmente pior, mas ambos têm problemas.
Mais uma pergunta:
Se o TRNG é muito melhor que o CPRNG, que por sua vez é muito melhor que o PRNG, por que alguém usa o CPRNG ou o PRNG?
Duas razões.
Primeiro: despesa. TRNG é caro . Gerar números verdadeiramente aleatórios é difícil. Os CPRNGs fornecem bons resultados para muitas chamadas arbitrariamente, com apenas uma chamada para o TRNG para a semente. O lado ruim é, obviamente, que você deve manter essa semente em segredo .
Segundo: às vezes queremos previsibilidade e tudo o que importa é uma boa distribuição. Se você estiver gerando dados "aleatórios" como entradas do programa para uma suíte de testes e aparecer um erro, seria bom que a execução da suíte de testes produza o bug novamente!
Espero que agora seja muito mais claro.
Por fim, se você gostou disso, poderá ler mais sobre o assunto aleatoriedade e permutações: