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