randomSeed(analogRead(x))
erzeugt nur 255 Folgen von Zahlen, was es trivial macht, alle Combos auszuprobieren und ein Orakel zu erzeugen, das mit Ihrem Ausgabestream gekoppelt werden kann und die gesamte Ausgabe zu 100% vorhersagt. Sie sind jedoch auf dem richtigen Weg, es ist nur ein Zahlenspiel, und Sie brauchen VIEL mehr davon. Beispiel: Nehmen Sie 100 analoge Lesevorgänge von 4 ADCs, addieren Sie diese und geben Sie sie an. Das randomSeed
wäre viel besser. Für maximale Sicherheit benötigen Sie sowohl unvorhersehbare Eingaben als auch nicht deterministisches Mischen.
Ich bin kein Kryptograf, aber ich habe Tausende von Stunden damit verbracht, nach zufälligen Generatoren für Hardware und Software zu suchen und diese zu erstellen.
Unvorhersehbare Eingabe:
- analogRead () (auf schwebenden Pins)
- GetTemp ()
Möglicherweise unvorhersehbarer Eingang:
- micros () (mit nicht deterministischer Abtastperiode)
- Taktjitter (niedrige Bandbreite, aber verwendbar)
- readVCC () (falls nicht batteriebetrieben)
Externer unvorhersehbarer Eingang:
- Temperatur-, Feuchtigkeits- und Drucksensoren
- Mikrofone
- LDR Spannungsteiler
- Rauschen des Transistors in Sperrrichtung
- Kompass / Beschleunigungsjitter
- esp8266 wifi hotspot scan (ssid, db, etc)
- esp8266 timing (die Hintergrund-WLAN-Aufgaben machen geplante micros () -Aufrufe unbestimmt)
- esp8266 HWRNG -
RANDOM_REG32
Extrem schnell und unvorhersehbar
sammeln
Das letzte, was Sie tun möchten, ist Entropie auszuspucken, wie es kommt. Es ist einfacher, einen Münzwurf zu erraten als einen Eimer mit Münzen. Summieren ist gut. unsigned long bank;
dann ist später bank+= thisSample;
gut; es wird sich überschlagen. bank[32]
ist noch besser, lesen Sie weiter. Sie möchten für jeden Output-Block mindestens 8 Input-Samples sammeln, im Idealfall viel mehr.
Schutz vor Vergiftungen
Wenn das Erhitzen der Platine einen gewissen maximalen Clock-Jitter verursacht, ist dies ein Angriffsvektor. Dasselbe gilt für das Abstrahlen von RFI zu den analogen Read () - Eingängen. Ein weiterer häufiger Angriff, bei dem die Einheit einfach vom Stromnetz getrennt wird und die gesamte angesammelte Entropie verloren geht. Sie sollten erst dann Zahlen ausgeben, wenn Sie wissen, dass dies auch auf Kosten der Geschwindigkeit sicher ist.
Aus diesem Grund möchten Sie mit EEPROM, SD usw. eine gewisse Entropie auf lange Sicht erhalten. Schauen Sie sich den Fortuna PRNG an , der 32 Bänke verwendet, von denen jede halb so oft aktualisiert wird wie die vorherige. Das macht es schwierig, alle 32 Banken in angemessener Zeit anzugreifen.
Verarbeitung
Sobald Sie "Entropie" gesammelt haben, müssen Sie diese bereinigen und auf eine schwer umkehrbare Weise von der Eingabe trennen. SHA / 1/256 ist dafür gut. Sie können SHA1 (oder sogar MD5) für die Geschwindigkeit verwenden, da Sie keine Sicherheitsanfälligkeit in Klartext haben. Zur Ernte, nie die volle entopy Bank verwenden und immer immer ein „Salz“ mit dem Ausgang hinzufügen , die jedes Mal anders ist , um identische Ausgänge zu verhindern gegeben keine Entropie Bankwechsel: output = sha1( String(micros()) + String(bank[0]) + [...] );
Die sha Funktion beide kaschiert Eingänge und bleicht Ausgang, Schutz vor schwachen Samen, niedrige akkumulierte ent und andere häufige Probleme.
Um Timer-Eingänge zu verwenden, müssen Sie sie unbestimmt machen. Dies ist eine einfache als delayMicroseconds(lastSample % 255)
; Dadurch wird eine unvorhersehbare Zeitspanne angehalten, und die "aufeinanderfolgende" Uhr liest den Unterschied ungleichmäßig. Tun Sie dies regelmäßig, if(analogRead(A1)>200){...}
vorausgesetzt, A1 ist verrauscht oder mit einem dynamischen Eingang verbunden. Wenn es schwierig ist, jede Gabelung Ihres Flusses zu bestimmen, wird die Kryptoanalyse für dekompilierte / gerippte Ausgaben verhindert.
Wirkliche Sicherheit besteht darin, dass der Angreifer Ihr gesamtes System kennt und es dennoch nicht überwinden kann.
Zuletzt überprüfen Sie Ihre Arbeit. Führen Sie Ihre Ausgabe über ENT.EXE aus (auch für nix / mac verfügbar) und prüfen Sie, ob sie funktioniert . Am wichtigsten ist die Chi-Quadrat-Verteilung, die normalerweise zwischen 33% und 66% liegen sollte. Wenn Sie 1,43% oder 99,999% oder so etwas bekommen, mehr als einen Test hintereinander, ist Ihr Zufall Mist. Sie möchten auch, dass die Entropie-HNO-Berichte so nahe wie möglich bei 8 Bit pro Byte liegen,> 7,9 sicher.
TLDR: Der einfachste und narrensicherste Weg ist das HWRNG des ESP8266. Es ist schnell, gleichmäßig und unvorhersehbar. Führen Sie so etwas auf einem ESP8266 mit Ardunio-Core aus und verwenden Sie die serielle Schnittstelle, um mit dem AVR zu kommunizieren:
// 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"
}
** bearbeiten
Hier ist eine HWRNG-Skizze, die ich vor einiger Zeit geschrieben habe und die nicht nur als Sammler, sondern als ganzer CSPRNG fungiert, der aus der seriellen Schnittstelle ausspuckt. Es ist für einen Pro-Mini gebaut, sollte aber leicht an andere Boards anpassbar sein. Sie können nur schwebende analoge Pins verwenden, aber es ist besser, ihnen etwas hinzuzufügen, vorzugsweise verschiedene Dinge. Wie Mikrofone, LDRs, Thermistoren (auf maximale Temperatur eingestellt) und sogar lange Drähte. Bei HNO funktioniert es ziemlich gut, wenn Sie sogar mäßiges Rauschen haben.
Die Skizze integriert mehrere Begriffe, die ich in meiner Antwort und den nachfolgenden Kommentaren erwähnt habe: Akkumulieren der Entropie, Dehnen durch Überabtasten der weniger als idealen Entropie (von Neumann sagte, es sei cool) und Hasching zur Gleichförmigkeit. Es verzichtet auf die Schätzung der Entropiequalität zugunsten von "irgendetwas Mögliches Dynamisches" und Vermischen unter Verwendung eines kryptografischen Primitivs.
// 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()