Jaka jest najlepsza metoda na uzyskanie prawdziwie (w przeciwieństwie do pseudo) losowej liczby w Arduino, lub przynajmniej najlepsze możliwe przybliżenie? Z mojego zrozumienia, funkcja randomSeed (analogRead (x)) nie jest wystarczająco losowa.
Jeśli to możliwe, metoda powinna wykorzystywać samą podstawową konfigurację Arduino (bez dodatkowych czujników). Rozwiązania z czujnikami zewnętrznymi są mile widziane, jeśli znacznie poprawią losowość w stosunku do podstawowej konfiguracji.
programming
Rexcirus
źródło
źródło
Odpowiedzi:
W Entropy bibliotecznych zastosowania:
Podoba mi się to rozwiązanie, ponieważ nie zużywa żadnych styków twojego mikrokontrolera i nie wymaga żadnych zewnętrznych obwodów. Dzięki temu jest mniej podatny na awarie zewnętrzne.
Oprócz biblioteki udostępniają również szkic ilustrujący użycie tej samej techniki, która została użyta do wygenerowania losowego materiału siewnego dla PRNG mikrokontrolera bez biblioteki: https://sites.google.com/site/astudyofentropy/project-definition / timer-jitter-entropy-sources / entropy-library / arduino-random-seed
źródło
randomSeed(analogRead(x))
wygeneruje tylko 255 ciągów liczb, co sprawia, że wypróbowanie wszystkich kombinacji jest proste, a wyrocznia może łączyć się ze strumieniem wyjściowym, przewidując 100% wyniku. Jesteś na dobrej drodze, ale to tylko gra liczbowa i potrzebujesz dużo więcej. Na przykład pobranie 100 odczytów analogowych z 4 ADC, zsumowanie ich wszystkich i karmienie tegorandomSeed
byłoby znacznie lepsze. Aby uzyskać maksymalne bezpieczeństwo, potrzebujesz zarówno nieprzewidywalnych danych wejściowych, jak i niedeterministycznego miksowania.Nie jestem kryptografem, ale spędziłem tysiące godzin na badaniu i budowaniu losowych generatorów sprzętu i oprogramowania, więc pozwólcie, że podzielę się częścią mojej wiedzy:
Nieprzewidywalne dane wejściowe:
Potencjalnie nieprzewidywalne dane wejściowe:
Zewnętrzne nieprzewidywalne dane wejściowe:
RANDOM_REG32
wyjątkowo szybki i nieprzewidywalny, 1 przystanekzbieranie Ostatnią rzeczą, którą chcesz zrobić, jest wyplucie entropii, jak tylko się pojawi. Łatwiej zgadnąć rzut monetą niż wiadro monet. Podsumowanie jest dobre.
unsigned long bank;
potembank+= thisSample;
jest dobrze; przewróci się.bank[32]
jest jeszcze lepszy, czytaj dalej. Chcesz zebrać co najmniej 8 próbek danych wejściowych dla każdego kawałka danych wyjściowych, najlepiej znacznie więcej.Ochrona przed zatruciem Jeśli ogrzewanie planszy powoduje pewne maksymalne drgania zegara, jest to wektor ataku. To samo z odpalaniem RFI w kierunku wejść analogRead (). Kolejnym częstym atakiem jest po prostu odłączenie jednostki od zasilania, a więc zrzucenie całej nagromadzonej entropii. Nie powinieneś wypisywać liczb, dopóki nie będziesz wiedział, że jest to bezpieczne, nawet kosztem szybkości.
Dlatego chcesz zachować trochę entropii przez długi czas, używając EEPROM, SD itp. Spójrz na Fortuna PRNG , który wykorzystuje 32 banki, z których każdy aktualizuje się o połowę tak często, jak przed nim. Utrudnia to atakowanie wszystkich 32 banków w rozsądnym czasie.
Przetwarzanie Po zebraniu „entropii” musisz ją wyczyścić i oddzielić od danych wejściowych w trudny do odwrócenia sposób. SHA / 1/256 jest do tego dobry. Możesz użyć SHA1 (a nawet MD5 naprawdę) do prędkości, ponieważ nie masz luki w postaci zwykłego tekstu. Do zbioru nigdy nie używaj pełnego banku entopy, a ZAWSZE ZAWSZE dodawaj „sól” do wyjścia, które jest za każdym razem inne, aby zapobiec identycznym wyjściom, biorąc pod uwagę brak zmian banku entropii:
output = sha1( String(micros()) + String(bank[0]) + [...] );
funkcja sha zarówno ukrywa nakłady, jak i wybiela wyjście, chroniąc przed słabymi nasionami, niski poziom akumulacji entów i inne typowe problemy.Aby korzystać z wejść timera, musisz uczynić je nieokreślonymi. To proste jak
delayMicroseconds(lastSample % 255)
; która wstrzymuje nieprzewidywalny czas, powodując, że „kolejny” zegar odczytuje różnicę nierównomierną. Rób to co pół, regularnie,if(analogRead(A1)>200){...}
pod warunkiem, że A1 jest głośny lub podłączony do wejścia dynamicznego. Sprawienie, że każdy rozwidlenie przepływu będzie trudny do ustalenia, zapobiegnie kryptoanalizie dekompilowanej / zgranej produkcji.Prawdziwe bezpieczeństwo ma miejsce, gdy osoba atakująca zna cały system i nadal nie jest w stanie go pokonać.
Na koniec sprawdź swoją pracę. Uruchom swoje wyjście za pomocą ENT.EXE (dostępnego również dla nix / mac) i sprawdź, czy jest w porządku. Najważniejszy jest rozkład chi-kwadrat, który zwykle powinien wynosić od 33% do 66%. Jeśli dostaniesz 1,43% lub 99,999% lub coś w tym stylu, więcej niż jeden test z rzędu, twój los to bzdury. Chcesz także, aby raporty ENT z entropii były jak najbliższe 8 bitom na bajt, na pewno> 7,9.
TLDR: Najprostszym niezawodnym sposobem jest użycie HWRNG w ESP8266. Jest szybki, jednolity i nieprzewidywalny. Uruchom coś takiego na ESP8266 z rdzeniem Ardunio i użyj serial do rozmowy z AVR:
** edytować
oto szkic HWRNG bez systemu operacyjnego, który napisałem jakiś czas temu, działając nie tylko jako kolektor, ale cały CSPRNG wypluwający z portu szeregowego. Jest zbudowany na pro-mini, ale powinien być łatwy do dostosowania do innych płyt. Możesz użyć tylko pływających pinów analogowych, ale lepiej jest do nich dodawać różne rzeczy, najlepiej różne. Jak mikrofony, LDR, termistory (przycięte do maks. Rozpiętości wokół temperatury pokojowej), a nawet długie przewody. W ENT robi to całkiem dobrze, jeśli masz nawet umiarkowany hałas.
Szkic obejmuje kilka pojęć, o których wspomniałem w mojej odpowiedzi i komentarzach uzupełniających: akumulację entropii, rozciąganie poprzez nadmierne próbkowanie entropii mniej niż idealnej (von neumann powiedział, że jest fajna) i mieszanie się do jednolitości. Rezygnuje z oceny jakości entropii na rzecz „daj mi wszystko, co możliwe, dynamiczne” i miksowania przy użyciu kryptograficznego elementu pierwotnego.
źródło
Z mojego doświadczenia
analogRead()
wynika , że na pływającej szpilce ma bardzo niską entropię. Może jeden lub dwa bity losowości na połączenie. Na pewno chcesz czegoś lepszego. Jitter timera watchdoga, jak zaproponowano w odpowiedzi per1234, jest dobrą alternatywą. Jednak generuje entropię z dość małą szybkością, co może być problemem, jeśli potrzebujesz go w momencie uruchamiania programu. dandavis ma kilka dobrych sugestii, ale na ogół wymagają ESP8266 lub zewnętrznego sprzętu.Jest jedno interesujące źródło entropii, o którym jeszcze nie wspomniano: zawartość niezainicjowanej pamięci RAM. Po włączeniu MCU niektóre bity RAM (te, które mają najbardziej symetryczne tranzystory) uruchamiają się w losowym stanie. Jak omówiono w tym artykule o hackaday , można go użyć jako źródła entropii. Jest dostępny tylko przy zimnym rozruchu, więc możesz go użyć do wypełnienia początkowej puli entropii, którą następnie okresowo uzupełniasz z innego, potencjalnie wolnego źródła. W ten sposób Twój program może rozpocząć pracę bez czekania, aż pula powoli się zapełni.
Oto przykład, w jaki sposób można to zrobić na Arduino opartym na AVR. Fragment kodu poniżej XOR zapełnia całą pamięć RAM, aby zbudować ziarno, do którego później będzie się karmił
srandom()
. Problem polega na tym, że zbieranie musi zostać wykonane, zanim środowisko wykonawcze C zainicjuje sekcje pamięci .data i .bss, a następnie nasiona muszą zostać zapisane w miejscu, w którym środowisko wykonawcze C nie nadpisze. Odbywa się to za pomocą określonych sekcji pamięci .Zauważ, że po ciepłym resecie pamięć SRAM zostaje zachowana, więc nadal ma całą zawartość twojej puli entropii. Ten sam kod można następnie wykorzystać do zachowania zgromadzonej entropii podczas resetowania.
Edycja : naprawiono problem w mojej początkowej wersji,
seed_from_ram()
który działał na globalnymrandom_seed
zamiast używać lokalnegoseed
. Może to doprowadzić do tego, że ziarno zostanie XOR z samym sobą, niszcząc całą dotychczasową entropię.źródło
analogRead()
do użyteczności, jeśli wiesz, co robisz. Musisz tylko uważać, aby nie przecenić jego losowości podczas aktualizacji oszacowania entropii puli. Chodzi mi oanalogRead()
to głównie pomyślany jako krytykę ubogiej jeszcze często powtarzane „przepis” :randomSeed(analogRead(0))
tylko raz wsetup()
i zakładamy, że to wystarczy.analogRead(0)
ma 1 bit entropii na wywołanie, to wywołanie go wielokrotnie da 10000/8 = 1,25 KB / s entropii, 150 razy więcej niż biblioteka Entropy.Jeśli tak naprawdę nie potrzebujesz entropii i po prostu chcesz uzyskać inną sekwencję liczb pseudolosowych przy każdym uruchomieniu, możesz użyć EEPROM do iteracji przez kolejne nasiona. Technicznie proces będzie całkowicie deterministyczny, ale pod względem praktycznym jest znacznie lepszy niż
randomSeed(analogRead(0))
na niepodłączonym pinie, co często powoduje, że zaczynasz z tym samym początkiem 0 lub 1023. Zapisanie następnego początku w EEPROM zagwarantuje, że zaczniesz od innego ziarno za każdym razem.Jeśli potrzebujesz prawdziwej entropii, możesz ją zebrać z dryftu zegara lub przez wzmocnienie zewnętrznego szumu. A jeśli potrzebujesz dużo entropii, hałas zewnętrzny jest jedyną realną opcją. Dioda Zenera jest popularnym wyborem, szczególnie jeśli masz źródło napięcia powyżej 5-6 V (będzie również działać z 5 V z odpowiednią diodą Zenera, ale spowoduje mniej entropii):
( źródło ).
Wyjście wzmacniacza musi być podłączone do pinu analogowego, który wytworzy kilka bitów entropii z każdym
analogRead()
do dziesiątek MHz (szybciej niż Arduino może próbkować).źródło