Qual é o melhor método para obter um número aleatório verdadeiramente (em oposição ao pseudo) no Arduino, ou pelo menos a melhor aproximação possível? Pelo meu entendimento, a função randomSeed (analogRead (x)) não é aleatória o suficiente.
Se possível, o método deve aproveitar sozinho a configuração básica do Arduino (sem sensores adicionais). Soluções com sensores externos são bem-vindas se melhorarem significativamente a aleatoriedade em relação à configuração básica.
programming
Rexcirus
fonte
fonte
Respostas:
A biblioteca do Entropy usa:
Eu gosto dessa solução porque ela não usa pinos do seu microcontrolador e não requer circuitos externos. Isso também o torna menos sujeito a falhas externas.
Além de uma biblioteca, eles também fornecem um esboço que demonstra o uso da mesma técnica usada para gerar uma semente aleatória para o PRNG do microcontrolador sem a biblioteca: https://sites.google.com/site/astudyofentropy/project-definition / timer-jitter-entropy-sources / biblioteca de entropia / arduino-random-seed
fonte
randomSeed(analogRead(x))
produzirá apenas 255 seqüências de números, o que torna trivial tentar todos os combos e produzir um oráculo que possa acoplar ao seu fluxo de saída, prevendo 100% da saída. Você está no caminho certo, porém, é apenas um jogo de números e precisa de muito mais deles. Por exemplo, fazer 100 leituras analógicas de 4 ADCs, resumindo todas elas e alimentando issorandomSeed
seria muito melhor. Para segurança máxima, você precisa de entradas imprevisíveis e mixagens não determinísticas.Não sou um criptógrafo, mas passei milhares de horas pesquisando e construindo geradores aleatórios de hardware e software, então deixe-me compartilhar um pouco do que aprendi:
Entrada imprevisível:
Entrada potencialmente imprevisível:
Entrada imprevisível externa:
RANDOM_REG32
extremamente rápido e imprevisível, uma paradacoletando A última coisa que você quer fazer é cuspir a entropia como ela aparece. É mais fácil adivinhar um lançamento de moeda do que um balde de moedas. A soma é boa.
unsigned long bank;
depoisbank+= thisSample;
é bom; vai rolar.bank[32]
é ainda melhor, continue a ler. Você deseja coletar pelo menos 8 amostras de entrada para cada parte da saída, idealmente muito mais.Protegendo contra envenenamento Se o aquecimento da prancha causar um certo jitter máximo de clock, esse é um vetor de ataque. O mesmo ocorre com o jateamento de RFI nas entradas analogRead (). Outro ataque comum simplesmente desconectou a unidade, despejando toda a entropia acumulada. Você não deve produzir números até saber que é seguro fazê-lo, mesmo ao custo da velocidade.
É por isso que você quer manter alguma entropia em torno de longo prazo, utilizando EEPROM, SD, etc Olhe para a Fortuna PRNG , que usa 32 bancos, cada um atualizados metade tão frequentemente quanto o anterior. Isso dificulta o ataque a todos os 32 bancos em um período de tempo razoável.
Processamento Depois de coletar a "entropia", é necessário limpá-la e separá-la da entrada de uma maneira difícil de reverter. SHA / 1/256 é bom para isso. Você pode usar o SHA1 (ou até mesmo o MD5) para obter velocidade, pois não possui uma vulnerabilidade em texto sem formatação. Para colher, nunca use o banco de entopia completo e SEMPRE adicione um "sal" à saída que é diferente a cada vez para evitar saídas idênticas, sem alterações no banco de entropia:
output = sha1( String(micros()) + String(bank[0]) + [...] );
a função sha oculta as entradas e clareia a saída, protegendo contra sementes fracas, baixo nível de acúmulo acumulado e outros problemas comuns.Para usar as entradas do timer, você precisa torná-las indeterministas. Este é um simples como
delayMicroseconds(lastSample % 255)
; que pausa uma quantidade imprevisível de tempo, tornando o relógio "sucessivo" com uma diferença não uniforme. Faça isso de forma semi-regular,if(analogRead(A1)>200){...}
desde que A1 seja barulhento ou conectado a uma entrada dinâmica. Tornar cada bifurcação do seu fluxo bastante difícil de determinar impedirá a criptoanálise na saída descompilada / rasgada.A verdadeira segurança é quando o invasor conhece todo o sistema e ainda não consegue superá-lo.
Por fim, verifique seu trabalho. Execute sua saída através do ENT.EXE (também disponível para nix / mac) e veja se é bom. O mais importante é a distribuição do qui quadrado, que geralmente deve estar entre 33% e 66%. Se você receber 1,43% ou 99,999% ou algo do tipo, mais de um teste consecutivo, sua aleatória é uma porcaria. Você também deseja que os relatórios ENT da entropia sejam o mais próximo possível de 8 bits por byte,> 7,9, com certeza.
TLDR: A maneira mais simples e segura de enganar é o HWRNG do ESP8266. É rápido, uniforme e imprevisível. Execute algo assim em um ESP8266 executando o núcleo do Ardunio e use o serial para conversar com o AVR:
** editar
aqui está um esboço de HWRNG que escrevi há algum tempo, operando não apenas como um coletor, mas como um CSPRNG inteiro saindo da porta serial. Ele foi desenvolvido para um profissional, mas deve ser facilmente adaptável a outras placas. Você pode usar apenas pinos analógicos flutuantes, mas é melhor adicionar itens a eles, de preferência coisas diferentes. Como microfones, LDRs, termistores (aparados ao máximo na temperatura ambiente) e até fios longos. Faz muito bem em ENT se você tiver um ruído moderado.
O esboço integra várias noções que mencionei na minha resposta e nos comentários seguintes: acumular entropia, esticar por sobre-amostrar uma entropia abaixo do ideal (von neumann disse que é legal) e buscar uniformidade. Ele renuncia à estimativa da qualidade da entropia em favor de "me dê qualquer coisa possivelmente dinâmica" e à mistura usando um primitivo criptográfico.
fonte
Da minha experiência,
analogRead()
em um pino flutuante tem entropia muito baixa. Talvez um ou dois bits de aleatoriedade por chamada. Você definitivamente quer algo melhor. A instabilidade do cronômetro de vigilância, como proposto na resposta de per1234, é uma boa alternativa. No entanto, gera entropia a uma taxa bastante lenta, o que pode ser um problema se você precisar exatamente quando o programa for iniciado. O dandavis tem algumas boas sugestões, mas geralmente requerem um ESP8266 ou hardware externo.Há uma fonte interessante de entropia que ainda não foi mencionada: o conteúdo da RAM não inicializada. Quando o MCU é ligado, alguns de seus bits de RAM (aqueles que possuem os transistores mais simétricos) são iniciados em um estado aleatório. Conforme discutido neste artigo , este pode ser usado como uma fonte de entropia. Ele está disponível apenas em uma inicialização a frio, portanto, você pode usá-lo para preencher um pool de entropia inicial, que seria reabastecido periodicamente de outra fonte potencialmente lenta. Dessa forma, seu programa pode iniciar seu trabalho sem precisar esperar que a piscina se encha lentamente.
Aqui está um exemplo de como isso pode ser obtido em um Arduino baseado em AVR. O trecho de código abaixo de XORs toda a RAM, a fim de construir uma semente que alimenta mais tarde
srandom()
. A parte complicada é que a coleta precisa ser feita antes que o tempo de execução C inicialize as seções de memória .data e .bss e, em seguida, a semente precisa ser salva em um local que o tempo de execução C não substitua. Isso é feito usando seções específicas da memória .Observe que, em uma redefinição a quente , a SRAM é preservada, portanto ainda possui todo o conteúdo do seu conjunto de entropia. Esse mesmo código pode ser usado para preservar a entropia coletada em uma redefinição.
Edit : corrigido um problema na minha versão inicial do
seed_from_ram()
que funcionava no global emrandom_seed
vez de usar um localseed
. Isso poderia levar a semente a ser XORed com ela mesma, destruindo toda a entropia colhida até agora.fonte
analogRead()
ser utilizável se souber o que está fazendo. Você só precisa ter cuidado para não superestimar sua aleatoriedade ao atualizar uma estimativa da entropia da sua piscina. Meu ponto sobreanalogRead()
é principalmente significava como uma crítica de um pobre, mas muitas vezes repetida “receita” :randomSeed(analogRead(0))
apenas uma vez emsetup()
e assumir que é o suficiente.analogRead(0)
houver 1 bit de entropia por chamada, a chamada repetida produzirá 10000/8 = 1,25 KBytes / s de entropia, 150 vezes mais que a biblioteca de entropia.Se você realmente não precisa de entropia e simplesmente deseja obter uma sequência diferente de números pseudo-aleatórios em cada inicialização, você pode usar a EEPROM para iterar através de sementes consecutivas. Tecnicamente, o processo será completamente determinístico, mas em termos práticos é muito melhor do que
randomSeed(analogRead(0))
em um pino desconectado, o que geralmente fará com que você comece com a mesma semente de 0 ou 1023. Salvar a próxima semente na EEPROM garantirá que você comece com uma diferente semeie cada vez.Se você precisar de entropia real, poderá coletá-la usando o desvio do relógio ou amplificando o ruído externo. E se você precisar de muita entropia, o ruído externo é a única opção viável. O diodo Zener é uma escolha popular, especialmente se você tiver uma fonte de tensão acima de 5-6V (ele funcionará com 5V também com um diodo Zener apropriado, mas produzirá menos entropia):
( fonte ).
A saída do amplificador deve ser conectada a um pino analógico, que produzirá vários bits de entropia com cada
analogRead()
um até dezenas de MHz (mais rápido do que o Arduino pode amostrar).fonte