Получение действительно случайного числа в Arduino

Каков наилучший способ получить истинное (в отличие от псевдо) случайное число в Arduino или, по крайней мере, наилучшее приближение? По моему мнению, функция randomSeed (analogRead (x)) не достаточно случайна.

Если возможно, метод должен использовать базовую настройку Arduino самостоятельно (без дополнительных датчиков). Решения с внешними датчиками приветствуются, если они значительно улучшают случайность по сравнению с базовой настройкой.

9 голосов | спросил Rexcirus 12 MarpmMon, 12 Mar 2018 21:02:16 +03002018-03-12T21:02:16+03:0009 2018, 21:02:16

3 ответа


8

библиотека Entropy использует:

  

естественный джиттер сторожевого таймера для создания надежного потока истинных случайных чисел

Мне нравится это решение, потому что оно не использует никаких контактов вашего микроконтроллера и не требует каких-либо внешних схем. Это также делает его менее подверженным внешним отказам.

В дополнение к библиотеке они также предоставляют эскиз, демонстрирующий использование того же метода, который используется для генерации случайного семени для PRNG микроконтроллера без библиотеки: https: //sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library/arduino-random-seed

ответил per1234 12 MarpmMon, 12 Mar 2018 23:47:24 +03002018-03-12T23:47:24+03:0011 2018, 23:47:24
5

randomSeed(analogRead(x)) будет генерировать только 255 последовательностей чисел, что делает тривиальным попробовать все комбо и создать оракул, который может соединяться с вашим потоком вывода, прогнозируя весь выход 100 %. Вы на правильном пути, но это всего лишь игра с цифрами, и вам нужно много больше. Например, принимать 100 аналоговых чтений из 4 АЦП, суммируя их все и подавать, чтобы randomSeed было бы намного лучше. Для максимальной безопасности вам необходимы как непредсказуемое входное, так и не детерминированное перемешивание.

Я не криптограф, но я потратил тысячи часов на исследования и создание случайных генераторов аппаратного и программного обеспечения, поэтому позвольте мне поделиться некоторыми из того, что я узнал:

Непредсказуемый ввод:

  • analogRead () (на плавающих контактах)
  • GetTemp () литий>

Потенциально непредсказуемый ввод:

  • micros () (без недетерминированного периода выборки)
  • джиттер часов (с низкой пропускной способностью, но применимый)
  • readVCC () (если не работает от батареи)

Внешний непредсказуемый ввод:

  • датчики температуры, влажности и давления
  • микрофоны
  • делители напряжения LDR
  • обратный транзисторный шум
  • дрожание компаса /ускорения
  • esp8266 wifi hotspot scan (ssid, db и т. д.)
  • esp8266 timing (фоновые wifi задачи делают запланированные выборки micros () неопределенными)
  • esp8266 HWRNG - RANDOM_REG32 - очень быстрый и непредсказуемый, 1-stop

собирая Последнее, что вы хотите сделать, это выплюнуть энтропию по мере ее появления. Легче угадать монетный флип, чем ведро монет. Подведение итогов - это хорошо. unsigned long bank;, а затем bank+= thisSample; - это хорошо; он перевернется. bank[32] еще лучше, читайте дальше. Вы хотите собрать не менее 8 выборок ввода для каждого фрагмента вывода, в идеале намного больше.

Защита от отравления Если нагрев платы вызывает определенное максимальное дрожание часов, это вектор атаки. То же самое с взрывным RFI входы analogRead (). Другая распространенная атака просто отключает блок, тем самым сбрасывая всю накопленную энтропию. Вы не должны выводить номера, пока не знаете, что это безопасно, даже за счет скорости.

Вот почему вы хотите сохранить некоторую энтропию в долгосрочной перспективе, используя EEPROM, SD и т. д. Посмотрите на Fortuna PRNG , который использует 32 банка, каждый из которых обновляется вдвое чаще, чем тот, который был до него. Это затрудняет атаку на все 32 банка за разумное время.

Обработка Когда вы собираете «энтропию», вы должны очистить ее и развести ее со входа с помощью жесткого обращения. SHA /1/256 подходит для этого. Вы можете использовать SHA1 (или даже MD5 действительно) для скорости, так как у вас нет уязвимости. Чтобы собрать урожай, никогда не используйте полный банк энтопии, и ВСЕГДА ВСЕГДА добавьте «соль» к выходу, каждый раз отличающийся друг от друга, чтобы исключить одинаковые выходы, если нет изменений в банке энтропии: output = sha1( String(micros()) + String(bank[0]) + [...] ); Функция sha скрывает входы и отбеливает выход, защищая от слабых семян, низкую накопленную и другие распространенные проблемы.

Чтобы использовать входы таймера, вы должны сделать их неопределенными. Это просто, как delayMicroseconds(lastSample % 255); который приостанавливает непредсказуемое количество времени, делая «последовательные» часы, неравномерными в разнице. Сделайте это полу-регулярно, например if(analogRead(A1)>200){...}, если A1 шумно или подключен к динамическому вводу. Создание каждой вилки вашего потока довольно сложно определить, предотвратит криптоанализ на декомпилированном /разорванном выходе.

Реальная безопасность - это когда злоумышленник знает всю вашу систему и все еще беспомощно ее преодолевает.

Наконец, проверьте свою работу. Запустите свой вывод через ENT.EXE (также доступен для nix /mac) и посмотрите, хорошо ли это , Наиболее важным является распределение хи-квадратов, которое обычно составляет от 33 до 66%. Если вы получаете 1,43% или 99,999% или что-то вроде этого, более одного теста подряд, ваш случайный дерьмо. Вы также хотите, чтобы энтропийные ЛОР-отчеты были как можно ближе к 8 бит на каждый байт,> 7.9.

TLDR: Простейшим способом защиты от несанкционированного доступа является HWRNG ESP8266. Это быстро, равномерно и непредсказуемо. Запустите что-то подобное на ESP8266, работающем с ядром Ardunio, и используйте серийный номер для разговора с AVR:

// ESP8266 Arduino core code:
void setup(){
 Serial.begin(9600); // or whatever
}

void loop() {
  // Serial.write((char)(RANDOM_REG32 % 256)); // "bin"
  Serial.print( String(RANDOM_REG32, HEX).substring(1)); // "hex"
}

** изменить

вот эскиз HWRNG с открытым бортом, который я написал некоторое время назад, работая как не только коллекционер,но целая CSPRNG выплевывает из последовательного порта. Он построен для про-мини, но должен быть легко адаптирован к другим платам. Вы можете использовать только плавающие аналоговые контакты, но лучше добавлять к ним материал, предпочтительнее разные вещи. Как микрофоны, LDR, термисторы (обрезанные до максимального разброса по комнатной температуре) и даже длинные провода. Это довольно хорошо в ENT, если у вас даже умеренный шум.

Эскиз объединяет несколько понятий, о которых я упомянул в своем ответе и последующих комментариях: накопление энтропии, растяжение с помощью чрезмерной выборки, отличной от идеальной энтропии (фон Нейманн сказал, что это круто) и хеширование до однородности. Он отказывается от оценки качества энтропии в пользу «gimme anything possible» и смешивания с использованием криптографического примитива.

// AVR (ardunio) HWRNG by dandavis. released to public domain by author.
#include <Hash.h> 

unsigned long read[8] = {0, 0, 0, 0, 0, 0, 0, 0};
const int pincount = 9; // adjust down for non pro-mini boards
int pins[9] = {A0, A1, A2, A3, A4, A5, A6, A7, A0}; // adjust for board, name analog inputs to be sampled
unsigned int ticks = 0;
String buff = ""; // holds one round of derivation tokens to be hashed.
String cache; // the last read hash



void harvest() { // String() slows down the processing, making micros() calls harder to recreate
  unsigned long tot = 0; // the total of all analog reads
  buff = String(random(2147483647)) + String(millis() % 999);
  int seed =  random(256) + (micros() % 32);
  int offset =  random(2147483647) % 256;

  for (int i = 0; i < 8; i++) {
    buff += String( seed + read[i] + i + (ticks % 65), HEX );
    buff += String(random(2147483647), HEX);
    tot += read[i];
  }//next i

  buff += String( (micros() + ticks + offset) % 99999, HEX);
  if (random(10) < 3) randomSeed(tot + random(2147483647) + micros()); 
  buff = sha1( String(random(2147483647)) + buff + (micros()%64) + cache); // used hash to uniform output and waste time
  Serial.print( buff ); // output the hash
  cache = buff;
  spin();
}//end harvest()


void spin() { // add entropy and mix
  ticks++;
  int sample = 128;
  for (int i = 0; i < 8; i++) { // update ~6/8 banks 8 times
    read[ read[i] % 8] += (micros() % 128);
    sample = analogRead(  pins[i] ); // a read from each analog pin
    read[ micros() % 8] += ( read[i] % 64 ); // mix timing and 6LSBs from read
    read[i] += sample; // mix whole raw sample
    read[(i + 1) % 8] += random(2147483647) % 1024; // mix prng
    read[ticks % 8] += sample % 16; // mix the best nibble of the read
    read[sample % 8] += read[ticks % 8] % 2147483647; // intra-mix banks
  }

}//end spin()



void setup() {
  Serial.begin(9600);
  delay(222);
  int mx = 2028 + ((analogRead(A0)  + analogRead(A1) + analogRead(A2)  + analogRead(A3)) % 256);  
  while (ticks < mx) {
    spin();
    delay(1);
    randomSeed(read[2] + read[1] + read[0] + micros() + random(4096) + ticks);
  }// wend
}// end setup()



void loop() {
  spin();
  delayMicroseconds((read[ micros() % 8] %  2048) + 333  );
  delay(random(10));
  //if (millis() < 500) return;
  if ((ticks % 16) == (millis() % 16) ) harvest();
}// end loop()
ответил dandavis 13 MaramTue, 13 Mar 2018 03:01:57 +03002018-03-13T03:01:57+03:0003 2018, 03:01:57
3

По моему опыту, analogRead() на плавающем штифте имеет очень низкий уровень энтропия. Возможно, один или два бита случайности за звонок. Вы определенно хочу что-то лучше. Тревога сторожевого таймера, как предлагается в per1234 ответ, является хорошей альтернативой. Однако он генерирует энтропию с довольно медленной скоростью, что может быть проблемой, если вам это нужно, когда программа запускается. у dandavis есть немало хороших предложений, но они обычно требуется либо ESP8266, либо внешнее оборудование.

Есть один интересный источник энтропии, о котором еще не упоминалось: содержимое неинициализированной ОЗУ. Когда MCU включен, некоторые его битов ОЗУ (те, которые имеют самые симметричные транзисторы) запускаются в случайном состоянии. Как обсуждалось в этом статья hackaday , это может быть использовано как источник энтропии. Это только доступный на холодном ботинке, поэтому вы можете использовать его для заполнения начальной энтропии пул, который вы затем периодически пополняете из другого, потенциально медленный источник. Таким образом, ваша программа может начать свою работу без необходимости ждать, пока пул будет медленно заполняться.

Вот пример того, как это можно было собрать на основе AVR Arduino. Фрагмент кода ниже XOR для всей ОЗУ, чтобы построить семя, которое позже передает в srandom(). Сложная часть состоит в том, что необходимо выполнить сборку до , среда выполнения C инициализирует .data и .bss разделы памяти, а затем семя должно быть сохранено в месте время выполнения C не будет перезаписано. Это делается с помощью конкретных разделы памяти .

 uint32_t __attribute__((section(".noinit"))) random_seed;

void __attribute__((naked, section(".init3"))) seed_from_ram()
{
    extern const uint32_t __data_start;
    const uint32_t * const ramend = (uint32_t *) RAMEND;
    for (const uint32_t *p = &__data_start; p <= ramend; p++)
        random_seed ^= *p;
}

void setup()
{
    srandom(random_seed);
}

Обратите внимание, что при сбросе warm SRAM сохраняется, поэтому он все еще имеет все содержимое вашего энтропийного пула. Этот же код можно затем использовать для сохранить собранную энтропию через сброс.

ответил Edgar Bonet 14 MarpmWed, 14 Mar 2018 17:49:53 +03002018-03-14T17:49:53+03:0005 2018, 17:49:53

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132