randomSeed(analogRead(x))
sẽ chỉ tạo ra 255 chuỗi số, điều này khiến cho việc thử tất cả các combo trở nên tầm thường và tạo ra một lời tiên tri có thể kết hợp với luồng đầu ra của bạn, dự đoán tất cả 100% đầu ra. Tuy nhiên, bạn đang đi đúng hướng, đây chỉ là một trò chơi số và bạn cần RẤT NHIỀU trò chơi này. Ví dụ, lấy 100 lần đọc tương tự từ 4 ADC, tổng hợp tất cả chúng và cho ăn điều đó randomSeed
sẽ tốt hơn nhiều. Để bảo mật tối đa, bạn cần cả đầu vào không thể đoán trước và trộn không xác định.
Tôi không phải là một nhà mật mã học, nhưng tôi đã dành hàng ngàn giờ để nghiên cứu và xây dựng các trình tạo ngẫu nhiên phần cứng và phần mềm, vì vậy hãy để tôi chia sẻ một số điều tôi đã học được:
Đầu vào không thể đoán trước:
- analogRead () (trên chân nổi)
- GetTemp ()
Đầu vào tiềm năng không thể đoán trước:
- micros () (w / một khoảng thời gian mẫu không xác định)
- jitter đồng hồ (băng thông thấp, nhưng có thể sử dụng)
- readVCC () (nếu không chạy bằng pin)
Đầu vào bên ngoài không thể đoán trước:
- cảm biến nhiệt độ, độ ẩm và áp suất
- micro
- Bộ chia điện áp LDR
- đảo ngược bóng bán dẫn tiếng ồn
- la bàn / gia tốc jitter
- Quét điểm truy cập wifi Esp8266 (ssid, db, v.v.)
- thời gian đặc biệt (các tác vụ wifi nền làm cho micros () được lập lịch tìm nạp không xác định)
- Esp8266 HWRNG -
RANDOM_REG32
cực kỳ nhanh và không thể đoán trước, 1 điểm dừng
thu thập
Điều cuối cùng bạn muốn làm là nhổ entropy như đi cùng. Thật dễ dàng để đoán một đồng xu lật hơn một xô tiền xu. Tổng kết là tốt. unsigned long bank;
sau đó bank+= thisSample;
là tốt; nó sẽ lăn qua bank[32]
thậm chí còn tốt hơn, đọc tiếp Bạn muốn thu thập ít nhất 8 mẫu đầu vào cho mỗi khối đầu ra, lý tưởng hơn nhiều.
Bảo vệ chống nhiễm độc
Nếu làm nóng bảng gây ra hiện tượng giật đồng hồ tối đa nhất định, đó là một vectơ tấn công. Tương tự với nổ RFI đối với các đầu vào analogRead (). Một cuộc tấn công phổ biến khác chỉ đơn giản là rút phích cắm của thiết bị do đó vứt bỏ toàn bộ entropy tích lũy. Bạn không nên xuất số cho đến khi bạn biết an toàn để làm như vậy, ngay cả với chi phí tốc độ.
Đây là lý do tại sao bạn muốn giữ một số entropy trong thời gian dài, sử dụng EEPROM, SD, v.v. Hãy nhìn vào Fortuna PRNG , sử dụng 32 ngân hàng, mỗi ngân hàng được cập nhật một nửa như trước đây. Điều đó gây khó khăn cho việc tấn công tất cả 32 ngân hàng trong một khoảng thời gian hợp lý.
Xử lý
Một khi bạn thu thập "entropy", bạn phải dọn sạch nó và tách nó khỏi đầu vào theo cách khó đảo ngược. SHA / 1/256 là tốt cho việc này. Bạn có thể sử dụng SHA1 (hoặc thậm chí MD5 thực sự) cho tốc độ vì bạn không có lỗ hổng văn bản gốc. Để thu hoạch, không bao giờ sử dụng ngân hàng entopy đầy đủ và LUÔN LUÔN LUÔN thêm "muối" vào đầu ra khác nhau mỗi lần để ngăn chặn đầu ra giống hệt nhau khi không có thay đổi ngân hàng entropy: output = sha1( String(micros()) + String(bank[0]) + [...] );
Chức năng sha vừa che giấu đầu vào vừa làm trắng đầu ra, bảo vệ chống lại hạt giống yếu, ent tích lũy thấp, và các vấn đề phổ biến khác.
Để sử dụng đầu vào hẹn giờ, bạn cần làm cho chúng không xác định. Đây là một đơn giản như delayMicroseconds(lastSample % 255)
; trong đó tạm dừng một lượng thời gian không thể đoán trước, làm cho đồng hồ "kế tiếp" đọc không đồng nhất khác nhau. Làm điều đó một cách thường xuyên, như if(analogRead(A1)>200){...}
, với điều kiện A1 là ồn ào hoặc được nối với đầu vào động. Làm cho mỗi ngã ba của luồng của bạn khá khó xác định sẽ ngăn phân tích tiền điện tử trên đầu ra được dịch ngược / tách.
An ninh thực sự là khi kẻ tấn công biết toàn bộ hệ thống của bạn và vẫn bất lực để vượt qua nó.
Cuối cùng, kiểm tra công việc của bạn. Chạy đầu ra của bạn thông qua ENT.EXE (cũng có sẵn cho nix / mac) và xem nó có tốt không. Quan trọng nhất là phân phối chi vuông, thường nên nằm trong khoảng từ 33% đến 66%. Nếu bạn nhận được 1,43% hoặc 99,999% hoặc một cái gì đó sắc nét như thế, nhiều hơn một bài kiểm tra liên tiếp, ngẫu nhiên của bạn là tào lao. Bạn cũng muốn các báo cáo ENT entropy càng gần 8 bit trên mỗi byte càng tốt,> 7.9 chắc chắn.
TLDR: Cách chống lừa đơn giản nhất là sử dụng HWRNG của ESP8266. Nó nhanh, đồng đều và không thể đoán trước. Chạy một cái gì đó như thế này trên một chiếc ESP8266 chạy lõi Ardunio và sử dụng nối tiếp để nói chuyện với 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"
}
** biên tập
Đây là một bản phác thảo HWRNG trần trụi mà tôi đã viết cách đây một thời gian, hoạt động như một người sưu tầm, mà không chỉ là một CSPRNG phun ra khỏi cổng nối tiếp. Nó được chế tạo cho một chiếc pro-mini nhưng có thể dễ dàng thích ứng với các bo mạch khác. Bạn chỉ có thể sử dụng các chân tương tự nổi, nhưng tốt hơn là thêm công cụ vào chúng, tốt nhất là những thứ khác nhau. Giống như micro, LDR, thermistors (được cắt xén tối đa xung quanh nhiệt độ phòng) và thậm chí cả dây dài. Nó hoạt động khá tốt trong ENT nếu bạn có độ ồn vừa phải.
Bản phác thảo tích hợp một số khái niệm mà tôi đã đề cập trong câu trả lời và nhận xét tiếp theo của mình: tích lũy entropy, kéo dài bằng cách lấy mẫu quá mức entropy kém lý tưởng (von neumann nói rằng nó rất tuyệt) và băm cho đồng đều. Nó bỏ qua ước tính chất lượng entropy để ủng hộ "gimme bất cứ điều gì có thể năng động" và trộn bằng cách sử dụng một nguyên thủy mã hóa.
// 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()