Отримання дійсно випадкового числа в Ардуїно


13

Який найкращий метод отримати справді (на відміну від псевдо) випадкового числа в Ардуїно чи принаймні найкращого можливого наближення? З мого розуміння, функція randomSeed (analogRead (x)) недостатньо випадкова.

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


Що таке додаток? Повинно бути криптографічно захищеним? Що ти робиш із випадковістю тоді? Тоді без зовнішнього чіпа, що реалізує TRNG від фізичного джерела ентропії, вам не пощастить. Ви також можете застосувати детерміністичний RNG, як HMAC DRBG, і вивести його з чогось статичного плюс джерело ентропії низької якості, але це все ще не буде захищено криптографічно.
Максиміліан Герхардт

Так, мені потрібні випадкові числа для криптографічно захищених програм.
Рекскірус

Відповіді:


10

Бібліотека ентропій використовує:

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

Мені подобається це рішення, оскільки воно не використовує жодних штифтів вашого мікроконтролера і не потребує зовнішньої схеми. Це також робить його менш схильним до зовнішніх збоїв.

Окрім бібліотеки, вони також надають ескіз, який демонструє використання тієї ж методики, яка використовується для генерування випадкового насіння для PRNG мікроконтролера без бібліотеки: https://sites.google.com/site/astudyofentropy/project-definition / timer-jitter-entropy-source / entropy-library / arduino-random-seed


8

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

Я не криптограф, але витратив тисячі годин на дослідження та створення апаратних і програмних генераторів випадкових генераторів, тому дозвольте мені поділитися деяким з того, що я дізнався:

Непередбачувана інформація:

  • analogRead () (на плаваючих штифтах)
  • GetTemp ()

Потенційно непередбачувана інформація:

  • micros () (без періоду вибірки, що не визначається)
  • тактовий тремтіння (низька пропускна здатність, але придатний для використання)
  • readVCC () (якщо не працює від акумулятора)

Зовнішній непередбачуваний вклад:

  • датчики температури, вологості та тиску
  • мікрофони
  • LDR-подільники напруги
  • зворотний зміщення транзисторного шуму
  • компакт / тремтіння прискорення
  • сканування точки доступу до Wi-Fi у форматі esp8266 (ssid, db тощо)
  • esp8266 синхронізація (фонові завдання Wi-Fi роблять заплановані мікро-файли)
  • esp8266 HWRNG - надзвичайно RANDOM_REG32швидкий і непередбачуваний, 1-стоп

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

Захист від отруєння Якщо нагрівання дошки викликає певний максимум тремтіння годинника, це вектор атаки. Те ж саме з вибуховим радіочастотним сигналом щодо входів analogRead (). Ще одна поширена атака - просто відключення підключення пристрою, тим самим скидання всієї накопиченої ентропії. Вам не слід виводити цифри, поки ви не знаєте, що це безпечно, навіть ціною швидкості.

Ось чому ви хочете тримати деяку ентропію навколо довгострокових, використовуючи EEPROM, SD тощо. Подивіться на PRNG Fortuna , який використовує 32 банки, кожен з яких оновлюється вдвічі частіше, ніж той, що був до нього. Це ускладнює атаку на всі 32 банки за розумну кількість часу.

Обробка Після того, як ви збираєте "ентропію", вам доведеться її очистити та відірвати від введення важко зворотним способом. SHA / 1/256 добре для цього. Ви можете використовувати SHA1 (або навіть MD5 дійсно) для швидкості, оскільки у вас немає вразливості простого тексту. Щоб збирати урожай, ніколи не використовуйте повний банк entopy, і ЗАВЖДИ додайте "сіль" у вихід, який кожен раз відрізняється, щоб запобігти однакові виходи, не даючи змін ентропії в банку: output = sha1( String(micros()) + String(bank[0]) + [...] );Функція "sha" приховує входи та відбілює вихід, захищаючи від слабких насінин, низький рівень накопиченості та інші поширені проблеми.

Щоб використовувати таймерні введення, потрібно зробити їх неефективними. Це простий як delayMicroseconds(lastSample % 255); який призупиняє непередбачувану кількість часу, роблячи "послідовні" годинник, що читає нерівномірно у різниці. Робіть це напів регулярно, наприклад if(analogRead(A1)>200){...}, за умови, що А1 шумно чи підключено до динамічного вводу. Зробити кожну вилку вашого потоку досить важко, це запобіжить криптоаналізу на декомпільованому / видобутому виході.

Справжня безпека - це коли зловмисник знає всю вашу систему і все ще безпорадний її подолати.

І нарешті, перевірте свою роботу. Запустіть свій висновок через ENT.EXE (також доступний для nix / mac) і подивіться, чи добре це. Найважливішим є розподіл квадратних чи, який зазвичай повинен становити від 33% до 66%. Якщо ви отримуєте 1,43% або 99,999% або щось подібне, більш ніж один тест поспіль, ваш випадковий випадок. Ви також хочете, щоб звіти про ентропію ENT були максимально близькими до 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, що виплюнув із серійного порту. Він створений для pro-mini, але повинен легко адаптуватися до інших плат. Можна використовувати просто плаваючі аналогові штифти, але краще додати до них речі, бажано різні речі. Як мікрофони, ЛДР, термістори (оброблені для максимального розповсюдження навколо температури приміщення) і навіть довгі дроти. Це досить добре в ЛОР, якщо у вас навіть помірний шум.

Ескіз об'єднує декілька понять, які я згадував у своїй відповіді та коментарях, що стосуються: накопичення ентропії, розтягнення шляхом надмірного відбору ентропії, меншої від ідеалу (фон Нейманн сказав, що це круто), і стискання до однорідності. Він відмовляється від оцінки якості ентропії на користь "дайте все, що можливо, динамічне" та змішання з використанням криптографічного примітиву.

// 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()

(Мені тут не вистачає персонажів, вибачте.) Гарний огляд! Я б запропонував використовувати лічильник солі; micros () - це марність бітів, оскільки вона може стрибати через кілька кроків між дзвінками. Уникайте високих бітів аналогових входів, обмежте найнижчий один або два біти. Навіть при цілеспрямованій атаці їх важко підключити (якщо тільки ви не можете покласти провід на вхід). "Недетерміновані змішування" - це не те, що ви можете зробити в програмному забезпеченні. Змішування SHA-1 стандартизовано: crypto.stackexchange.com/a/6232 . Індет. Таймер, який ви пропонуєте, є настільки ж випадковим, як і джерело, яке ви вже маєте. Тут не так багато виграшів.
Йонас Шефер

sha спрощує та захищає, так що вам не доведеться турбуватися про те, скільки біт, наприклад, схопити з аналогового входу. кілька дюймів дроту, підключеного до аналога (або сліду змієвої плати), розгойдують його більше ніж на кілька біт. змішування є недетермінованим внаслідок несохраненной і невідомої солі, поданої в хеш з підпробором накопичених значень. micros () важче відтворити, ніж лічильник, esp при запуску через недетерміновані інтервали.
dandavis

1
Я маю питання. Ви сказали, що краще вжити 100 заходів. Але чи не вживати багато заходів, якась "середня", яка обмежує ефективність отримання цих "випадкових" даних? Я маю на увазі, як правило, ви в середньому отримуєте менш галасливі (тим менш "випадкові") вимірювання ...
frarugi87

добре, я рекомендую постійний вибірки, я просто сказав, що 100 краще, ніж 1, оскільки він пропонує більше комбінацій. Модель накопичення, як Yarrow / Fortuna, все ще значно краща. Подумайте про об'єднання (не підсумовування) цих 100 аналогічних зразків перед перемішуванням; сильніший, оскільки він робить важливим порядок вибірок, а те, що мимоволі дістаєшся, дає зовсім інший хеш. Тож, хоч можна було б середньо оцінити вибірки, щоб отримати менше шуму, зловмиснику доведеться дослівно переказувати всі значення або не відповідати ... Моя головна суть - це "накопичувати, перемішувати та перевіряти" більше, ніж виступати за певне джерело шуму.
dandavis

4

З мого досвіду, analogRead()на плаваючому штирі є дуже низька ентропія. Можливо один або два біти випадковості за виклик. Ви точно хочете чогось кращого. Тремтіння сторожового таймера, як це запропоновано у відповіді per1234, є хорошою альтернативою. Однак він створює ентропію з досить повільною швидкістю, що може бути проблемою, якщо вона потрібна вам правильно, коли програма запускається. dandavis має досить багато хороших пропозицій, але вони, як правило, вимагають або ESP8266, або зовнішнього обладнання.

Є одне цікаве джерело ентропії, про яке ще не було сказано: вміст неініціалізованої ОЗУ. Коли живиться MCU, деякі його біти оперативної пам’яті (ті, у яких трапляються найбільш симетричні транзистори) запускаються у випадковому стані. Як обговорюється в цій статті про хакадей , це може використовуватися як джерело ентропії. Він доступний лише в холодному завантаженні, тому ви можете використовувати його для заповнення початкового пулу ентропії, який ви потім періодично поповнюєте з іншого, потенційно повільного джерела. Таким чином ваша програма може розпочати свою роботу, не чекаючи, коли пул повільно поповниться.

Ось приклад того, як це могло бути зібрано на Arduino на базі AVR. Фрагмент коду нижче XOR містить всю оперативну пам’ять, щоб створити насіння, яке згодом подається srandom(). Найскладніша частина полягає в тому, що збирання має бути виконано до того, як час виконання C ініціалізує розділи пам’яті .data та .bss, а потім насіння потрібно зберегти на місці, коли час виконання C не перезапишеться. Це робиться за допомогою конкретних розділів пам'яті .

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

void __attribute__((naked, section(".init3"))) seed_from_ram()
{
    const uint32_t * const ramstart = (uint32_t *) RAMSTART;
    const uint32_t * const ramend   = (uint32_t *) RAMEND;
    uint32_t seed = 0;
    for (const uint32_t *p = ramstart; p <= ramend; p++)
        seed ^= *p;
    random_seed = seed;
}

void setup()
{
    srandom(random_seed);
}

Зауважте, що при теплому скиді SRAM зберігається, тому він все ще містить весь вміст вашого пулу ентропії. Цей самий код може бути використаний для збереження зібраної ентропії через скидання.

Редагувати : виправлена ​​проблема в моїй початковій версії, seed_from_ram()що працювала в глобальному, random_seedзамість використання локальної seed. Це може призвести до того, що насіння буде XORed із самим собою, знищивши всю ентропію, зібрану дотепер.


Хороша робота! я можу вкрасти? re: штифти: одного або двох бітів невідомого достатньо, якщо їх правильно використовувати; це обмежило б лише вихідну швидкість ідеальної таємниці (юк), але не обчислювальну таємницю, яка нам потрібна ...
dandavis

1
@dandavis: Так, ви можете використовувати повторно. Ви маєте рацію в тому, щоб analogRead()бути корисним, якщо знаєте, що робите. Ви просто повинні бути обережними, щоб не переоцінити її випадковість при оновленні оцінки ентропії вашого пулу. Моя точка зору про те analogRead(), в основному , означає , як критика бідних ще часто неодноразового «рецепт» : randomSeed(analogRead(0)) тільки один раз в setup()і припустимо , що це досить.
Едгар Боне

Якщо analogRead(0)в 1 виклику ентропії на виклик, то повторний виклик приводить до 10000/8 = 1,25 Кбайт / сек ентропії, що в 150 разів більше, ніж бібліотека ентропії.
Дмитро Григор'єв

0

Якщо вам не потрібна ентропія і просто хочете отримати різну послідовність псевдовипадкових чисел при кожному запуску, ви можете використовувати EEPROM для повторення послідовних насінин. Технічно процес буде повністю детермінованим, але в практичному плані він набагато кращий, ніж randomSeed(analogRead(0))на нез'єднаному штирі, що часто змушує вас почати з того ж насіння або 0, або 1023. Збереження наступного насіння в EEPROM гарантує, що ви почнете з іншого насіння кожен раз.

#include <EEPROM.h>

const int seed_addr = 0;
unsigned long seed;

void setup() {
    seed = EEPROM.read(seed_addr);
    EEPROM.write(seed_addr, seed+1);
    randomSeed(seed);
}

Якщо вам потрібна реальна ентропія, ви можете збирати її або з переміщення годин, або підсилюючи зовнішній шум. І якщо вам потрібно багато ентропії, зовнішній шум - єдиний життєздатний варіант. Ценеровий діод є популярним вибором, особливо якщо у вас джерело напруги понад 5-6 В (він також буде працювати з 5 В із відповідним діодом Зенера, але матиме меншу ентропію):

введіть тут опис зображення

( джерело ).

Вихід підсилювача повинен бути підключений до аналогового штифта, який буде виробляти кілька біт ентропії з кожним analogRead()до десятків МГц (швидше, ніж Ардуіно може вибірку).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.