Arduino में वास्तव में यादृच्छिक संख्या प्राप्त करना


13

Arduino में यादृच्छिक संख्या (या छद्म के विपरीत) यादृच्छिक संख्या या कम से कम सर्वोत्तम संभव सन्निकटन प्राप्त करने के लिए सबसे अच्छी विधि क्या है? मेरी समझ से, फंक्शन randomSeed (analogRead (x)) यह रैंडम पर्याप्त नहीं है।

यदि संभव हो तो विधि को अकेले मूल Arduino सेटअप का लाभ उठाना चाहिए (कोई अतिरिक्त सेंसर नहीं)। बाहरी सेंसरों के साथ समाधान का स्वागत किया जाता है यदि वे मूल सेटअप पर यादृच्छिकता में काफी सुधार करते हैं।


आवेदन क्या है? क्या यह क्रिप्टोग्राफिक रूप से सुरक्षित होना चाहिए? फिर बेतरतीबी से क्या कर रहे हो? फिर एक बाहरी चिप के बिना एक भौतिक एन्ट्रापी स्रोत से TRNG को लागू करना, आप भाग्य से बाहर हैं। आप HMAC DRBG की तरह एक नियतांक RNG को भी लागू कर सकते हैं और इसे कुछ स्थैतिक प्लस से कम गुणवत्ता वाले एंट्रॉपी स्रोत से निकाल सकते हैं, लेकिन फिर भी यह क्रिप्टोग्राफिक रूप से सुरक्षित नहीं होगा।
मैक्सिमिलियन गेरहार्ट

हां, मुझे क्रिप्टोग्राफिक रूप से सुरक्षित अनुप्रयोगों के लिए यादृच्छिक संख्याओं की आवश्यकता है।
रिंकेरस वायरस

जवाबों:


10

Entropy पुस्तकालय का उपयोग करता है:

प्रहरी टिमर का प्राकृतिक घबराना सच यादृच्छिक संख्याओं की एक विश्वसनीय धारा उत्पन्न करने के लिए है

मुझे यह समाधान पसंद है क्योंकि यह आपके माइक्रोकंट्रोलर के किसी भी पिन का उपयोग नहीं करता है और किसी भी बाहरी सर्किटरी की आवश्यकता नहीं है। यह भी बाहरी विफलताओं के लिए इसे कम विषय बनाता है।

एक पुस्तकालय के अलावा, वे एक स्केच भी प्रदान करते हैं जो उसी तकनीक का उपयोग करता है जो पुस्तकालय के बिना माइक्रोकंट्रोलर के PRNG के लिए एक यादृच्छिक बीज उत्पन्न करने के लिए उपयोग किया जाता है: https://sites.google.com/site/astudyofentropy-project-definition / टाइमर-घबराना एंट्रोपी-स्रोतों / एन्ट्रापी-पुस्तकालय / arduino यादृच्छिक बीज


8

randomSeed(analogRead(x))केवल संख्याओं के 255 अनुक्रमों का उत्पादन करेगा, जो सभी कॉम्बो की कोशिश करने के लिए तुच्छ बनाता है और एक आउटपुट पैदा करता है जो आपके आउटपुट स्ट्रीम में जोड़े जा सकता है, सभी आउटपुट 100% की भविष्यवाणी करता है। आप हालांकि सही रास्ते पर हैं, यह सिर्फ एक नंबर का खेल है, और आपको उनमें से बहुत कुछ चाहिए। उदाहरण के लिए, 4 एडीसी से 100 एनालॉग रीडिंग लेना, उन सभी को समेटना, और खिलाना जो randomSeedबहुत बेहतर होगा। अधिकतम सुरक्षा के लिए, आपको अप्रत्याशित इनपुट और गैर-नियतात्मक मिश्रण दोनों की आवश्यकता होती है।

मैं एक क्रिप्टोग्राफर नहीं हूं, लेकिन मैंने हार्डवेयर और सॉफ़्टवेयर यादृच्छिक जनरेटर पर शोध और निर्माण करने में हजारों घंटे बिताए हैं, इसलिए मुझे जो कुछ मैंने सीखा है उसे साझा करने दें:

अप्रत्याशित इनपुट:

  • analogRead () (फ्लोटिंग पिन पर)
  • GetTemp ()

संभावित अप्रत्याशित इनपुट:

  • माइक्रो () (डब्ल्यू / एक गैर-नियतात्मक नमूना अवधि)
  • घड़ी घबराना (कम बैंडविड्थ, लेकिन प्रयोग करने योग्य)
  • readVCC () (यदि बैटरी चालित नहीं है)

बाहरी अप्रत्याशित इनपुट:

  • अस्थायी, आर्द्रता और दबाव सेंसर
  • माइक्रोफोन
  • LDR वोल्टेज डिवाइडर
  • रिवर्स-बायस ट्रांजिस्टर शोर
  • कम्पास / त्वरण घबराना
  • esp8266 वाईफाई हॉटस्पॉट स्कैन (ssid, db, आदि)
  • एस्प 8266 समय (पृष्ठभूमि वाईफ़ाई कार्य शेड्यूल किए गए माइक्रोक्रेट्स बनाते हैं)
  • esp8266 HWRNG - RANDOM_REG32पूरी तरह से तेज और अप्रत्याशित, 1-स्टॉप

एकत्रित आखिरी बात आप क्या करना चाहते है साथ आता है बाहर एन्ट्रापी थूक है। सिक्कों की एक बाल्टी की तुलना में एक सिक्का फ्लिप करना आसान है। सममिंग अच्छी है। unsigned long bank;फिर बाद bank+= thisSample;में अच्छा है; यह लुढ़क जाएगा। bank[32]और भी बेहतर है, पर पढ़ें। आप आउटपुट के प्रत्येक भाग के लिए इनपुट के कम से कम 8 नमूने एकत्र करना चाहते हैं, आदर्श रूप से बहुत अधिक।

विषाक्तता से बचाव अगर बोर्ड को गर्म करने से एक निश्चित अधिकतम घड़ी घबराहट होती है, तो यह एक हमला वेक्टर है। RFR को analogRead () इनपुट की ओर नष्ट करने के साथ ही। एक और आम हमला बस यूनिट को अनप्लग करना है ताकि सभी संचित एन्ट्रॉपी को डंप किया जा सके। आपको संख्याओं का उत्पादन तब तक नहीं करना चाहिए जब तक कि आपको पता न हो कि ऐसा करना सुरक्षित है, यहां तक ​​कि गति की कीमत पर भी।

यही कारण है कि आप EEPROM, SD, आदि का उपयोग करते हुए दीर्घावधि के आस-पास कुछ एन्ट्रापी रखना चाहते हैं, Fortuna PRNG में देखें , जो कि 32 बैंकों का उपयोग करता है, प्रत्येक एक आधे अपडेट किया गया है, जो कि पहले एक था। जिससे उचित समय में सभी 32 बैंकों पर हमला करना मुश्किल हो जाता है।

प्रसंस्करण एक बार जब आप "एन्ट्रॉपी" एकत्र करते हैं, तो आपको इसे साफ करना होगा और इसे इनपुट से हार्ड-टू-रिवर्स तरीके से तलाक देना होगा। SHA / 1/256 इसके लिए अच्छा है। जब से आपके पास एक प्लेटेक्स्ट भेद्यता नहीं है, आप गति के लिए SHA1 (या यहां तक ​​कि एमडी 5 वास्तव में) का उपयोग कर सकते हैं। कटाई करने के लिए, कभी भी पूर्ण एन्ट्रापी बैंक का उपयोग न करें, और ALWAYS ALWAYS आउटपुट में एक "नमक" मिलाते हैं जो कि हर बार अलग-अलग समान आउटपुट को रोकने के लिए दिया जाता है ताकि कोई एंट्रॉपी बैंक परिवर्तन न हो: output = sha1( String(micros()) + String(bank[0]) + [...] );शेक फ़ंक्शन दोनों इनपुट और वाइटन्स आउटपुट को छुपाता है, कमजोर बीज की रक्षा करता है। कम संचित प्रवेश, और अन्य सामान्य मुद्दे।

टाइमर इनपुट का उपयोग करने के लिए, आपको उन्हें अनिश्चितकालीन बनाने की आवश्यकता है। यह एक सरल है delayMicroseconds(lastSample % 255); जो अप्रत्याशित समय को रोक देता है, जिससे "क्रमिक" घड़ी अंतर में गैर-समान हो जाती है। ऐसा करें कि अर्ध-नियमित रूप से, जैसे कि if(analogRead(A1)>200){...}A1 एक शोर है या एक गतिशील इनपुट से जुड़ा हुआ है। आपके प्रवाह के प्रत्येक कांटे को निर्धारित करना मुश्किल है, यह विघटित / चीरने वाले आउटपुट पर क्रिप्टोकरंसी को रोक देगा।

वास्तविक सुरक्षा तब है जब हमलावर आपके पूरे सिस्टम को जानता है और इसे दूर करने के लिए अभी भी असहाय है।

अंत में, अपने काम की जाँच करें। ENT.EXE (nix / mac के लिए भी उपलब्ध) के माध्यम से अपना आउटपुट चलाएँ और देखें कि क्या यह कोई अच्छा है। सबसे महत्वपूर्ण ची वर्ग वितरण है, जो आमतौर पर 33% और 66% के बीच होना चाहिए। अगर आपको 1.43% या 99.999% या ऐसा कुछ नुकीला मिलता है, जो एक पंक्ति में एक से अधिक परीक्षण करता है, तो आपका रैंडम बकवास है। आप यह भी चाहते हैं कि एंट्रॉपी ईएनटी रिपोर्ट 8 बिट प्रति बाइट के करीब हो,> 7.9 निश्चित रूप से।

TLDR: सबसे सरल मूर्खतापूर्ण तरीका है ESP8266 का HWRNG। यह तेज़, एकसमान और अप्रत्याशित है। ESP8266 पर इस तरह से कुछ चलाएं कि अर्दीनियो कोर चल रहा है, और 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 सीरियल पोर्ट से बाहर थूक रहा था। यह एक मिनी के लिए बनाया गया है, लेकिन आसानी से अन्य बोर्डों के लिए अनुकूल होना चाहिए। आप सिर्फ फ्लोटिंग एनालॉग पिन का उपयोग कर सकते हैं, लेकिन उनमें सामान जोड़ना बेहतर है, अधिमानतः अलग चीजें। माइक्रोफोन की तरह, LDRs, थर्मिस्टर्स (कमरे के अस्थायी के आसपास अधिकतम फैलाने के लिए छंटनी), और यहां तक ​​कि लंबे तार भी। यह ईएनटी में बहुत अच्छा करता है यदि आपके पास मध्यम शोर है।

स्केच ने अपने उत्तर और अनुवर्ती टिप्पणियों में मेरे द्वारा उल्लिखित कई धारणाओं को एकीकृत किया है: एंट्रॉपी को जमा करना, कम से कम-आदर्श एंट्रोपी (वॉन न्यूमैन ने कहा कि यह अच्छा है) द्वारा खींचकर, और एकरूपता के लिए हैशिंग। यह क्रिप्टोग्राफिक आदिम का उपयोग करके "जिमी कुछ भी गतिशील" के पक्ष में एन्ट्रापी गुणवत्ता के अनुमान को भूल जाता है।

// 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 () गैर-नियतात्मक अंतराल पर निकाल दिए जाने पर काउंटर, एस्प की तुलना में फिर से खेलना कठिन होता है।
डंडविस

1
मेरा एक सवाल है। आपने कहा कि 100 उपाय करना बेहतर है। लेकिन बहुत सारे उपाय "औसत" नहीं कर रहे हैं जो इन "यादृच्छिक" डेटा को लेने की प्रभावशीलता को सीमित करता है? मेरा मतलब है, आमतौर पर आप कम शोर (औसत से कम "यादृच्छिक") माप प्राप्त करने के लिए औसत करते हैं ...
frarugi87

अच्छी तरह से मैं निरंतर नमूना लेने की सलाह देता हूं, मैं सिर्फ यह कह रहा था कि 100 अधिक बेहतर है क्योंकि यह अधिक संयोजन प्रदान करता है। यारो / फोर्टुना जैसी एक संचय मॉडल अभी भी काफी बेहतर है। हैशिंग से पहले उन 100 एनालॉग नमूनों को समेटना (समेटना नहीं) पर विचार करें; यह मजबूत है क्योंकि यह नमूना आदेश को महत्वपूर्ण बनाता है, और एक चार होने से एक अलग हैश प्राप्त होता है। इसलिए, भले ही कोई कम शोर पाने के लिए नमूनों को औसत कर सकता है, एक हमलावर को सभी मानों या किसी भी मैच को सुनाने के लिए मौखिक रूप से सुनाना होगा ... मेरा मुख्य बिंदु एक विशिष्ट शोर स्रोत की वकालत करने से अधिक "जमा, मिश्रण और सत्यापित" है।
dandavis

4

मेरे अनुभव से, analogRead()एक फ्लोटिंग पिन पर बहुत कम एन्ट्रापी है। शायद प्रति कॉल यादृच्छिकता के एक या दो बिट्स। आप निश्चित रूप से कुछ बेहतर करना चाहते हैं। प्रहरी टिमर का घबराना, जैसा कि प्रति 1234 के उत्तर में प्रस्तावित है, एक अच्छा विकल्प है। हालाँकि, यह एक बहुत ही धीमी दर से एन्ट्रॉपी उत्पन्न करता है, जो कि कार्यक्रम शुरू होने पर आपको सही जरूरत पड़ने पर एक मुद्दा हो सकता है। dandavis के पास कुछ अच्छे सुझाव हैं, लेकिन उन्हें आमतौर पर ESP8266 या बाहरी हार्डवेयर की आवश्यकता होती है।

एक दिलचस्प एन्ट्रापी स्रोत है जिसका अभी तक उल्लेख नहीं किया गया है: असिंचित रैम की सामग्री। जब MCU को संचालित किया जाता है, तो इसके कुछ रैम बिट्स (जो कि सबसे अधिक सममित ट्रांजिस्टर होते हैं) एक यादृच्छिक अवस्था में शुरू होते हैं। जैसा कि इस हैकडाय लेख में चर्चा की गई है , इसका उपयोग एक एन्ट्रोपी स्रोत के रूप में किया जा सकता है। यह केवल एक ठंडे बूट पर उपलब्ध है, इसलिए आप इसका उपयोग प्रारंभिक एन्ट्रापी पूल को भरने के लिए कर सकते हैं, जिसे आप समय-समय पर दूसरे, संभावित धीमी स्रोत से फिर से भर देंगे। इस तरह आपका कार्यक्रम पूल के लिए धीरे-धीरे भरने के लिए इंतजार किए बिना अपना काम शुरू कर सकता है।

यहाँ एक उदाहरण दिया गया है कि यह एक AVR- आधारित Arduino पर कैसे काटा जा सकता है। XORs के नीचे का कोड स्निपेट पूरे राम को एक बीज बनाने के लिए बनाता है जिसे वह बाद में खिलाता है srandom()। मुश्किल हिस्सा यह है कि सी रनटाइम से पहले कटाई करनी पड़ती है। यह विशिष्ट मेमोरी सेक्शन का उपयोग करके किया जाता है ।

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 हो सकता है, जिससे अब तक काटे गए सभी एन्ट्रापी नष्ट हो जाएंगे।


अच्छा काम! क्या मैं चोरी कर सकता हूँ? पुन: पिन: अज्ञात का एक या दो बिट पर्याप्त है अगर सही उपयोग किया जाता है; यह केवल सही गोपनीयता (yuck) के उत्पादन की गति को सीमित करेगा, लेकिन हमारे लिए आवश्यक कम्प्यूटेशनल गोपनीयता नहीं ...
dandavis

1
@ डांडवीस: हां, आप पुन: उपयोग कर सकते हैं, सुनिश्चित करें। 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 Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.