/** * \brief Initializes the random number generator. * * \param tag A string that is stirred into the random pool at startup; * usually this should be a value that is unique to the application and * version such as "MyApp 1.0" so that different applications do not * generate the same sequence of values upon first boot. * * This function should be followed by calls to addNoiseSource() to * register the application's noise sources. * * \sa addNoiseSource(), stir(), save() */ void RNGClass::begin(const char *tag) { // Bail out if we have already done this. if (initialized) return; // Initialize the ChaCha20 input block from the saved seed. memcpy_P(block, tagRNG, sizeof(tagRNG)); memcpy_P(block + 4, initRNG, sizeof(initRNG)); #if defined(RNG_EEPROM) int address = RNG_EEPROM_ADDRESS; eeprom_read_block(stream, (const void *)address, SEED_SIZE); if (crypto_crc8('S', stream, SEED_SIZE - 1) == ((const uint8_t *)stream)[SEED_SIZE - 1]) { // We have a saved seed: XOR it with the initialization block. // Note: the CRC-8 value is included. No point throwing it away. for (int posn = 0; posn < 12; ++posn) block[posn + 4] ^= stream[posn]; } #elif defined(RNG_DUE_TRNG) // Do we have a seed saved in the last page of flash memory on the Due? if (crypto_crc8('S', ((const uint32_t *)RNG_SEED_ADDR) + 1, SEED_SIZE) == ((const uint32_t *)RNG_SEED_ADDR)[0]) { // XOR the saved seed with the initialization block. for (int posn = 0; posn < 12; ++posn) block[posn + 4] ^= ((const uint32_t *)RNG_SEED_ADDR)[posn + 1]; } // If the device has just been reprogrammed, there will be no saved seed. // XOR the initialization block with some output from the CPU's TRNG // to permute the state in a first boot situation after reprogramming. pmc_enable_periph_clk(ID_TRNG); REG_TRNG_CR = TRNG_CR_KEY(0x524E47) | TRNG_CR_ENABLE; REG_TRNG_IDR = TRNG_IDR_DATRDY; // Disable interrupts - we will poll. mixTRNG(); #endif #if defined(RNG_ESP_NVS) // Do we have a seed saved in ESP non-volatile storage (NVS)? nvs_handle handle = 0; if (nvs_open("rng", NVS_READONLY, &handle) == 0) { size_t len = 0; if (nvs_get_blob(handle, "seed", NULL, &len) == 0 && len == SEED_SIZE) { uint32_t seed[12]; if (nvs_get_blob(handle, "seed", seed, &len) == 0) { for (int posn = 0; posn < 12; ++posn) block[posn + 4] ^= seed[posn]; } clean(seed); } nvs_close(handle); } #endif #if defined(RNG_WORD_TRNG) // Mix in some output from a word-based TRNG to initialize the state. mixTRNG(); #endif // No entropy credits for the saved seed. credits = 0; // Trigger an automatic save once the entropy credits max out. firstSave = 1; // Rekey the random number generator immediately. rekey(); // Stir in the supplied tag data but don't credit any entropy to it. if (tag) stir((const uint8_t *)tag, strlen(tag)); #if defined(RNG_DUE_TRNG) // Stir in the unique identifier for the CPU so that different // devices will give different outputs even without seeding. stirUniqueIdentifier(); #elif defined(ESP8266) // ESP8266's have a 32-bit CPU chip ID and 32-bit flash chip ID // that we can use as a device unique identifier. uint32_t ids[2]; ids[0] = ESP.getChipId(); ids[1] = ESP.getFlashChipId(); stir((const uint8_t *)ids, sizeof(ids)); #elif defined(ESP32) // ESP32's have a MAC address that can be used as a device identifier. uint64_t mac = ESP.getEfuseMac(); stir((const uint8_t *)&mac, sizeof(mac)); #else // AVR devices don't have anything like a serial number so it is // difficult to make every device unique. Use the compilation // time and date to provide a little randomness across applications // if not across devices running the same pre-compiled application. tag = __TIME__ __DATE__; stir((const uint8_t *)tag, strlen(tag)); #endif #if defined(RNG_WATCHDOG) // Disable interrupts and reset the watchdog. cli(); wdt_reset(); // Clear the "reset due to watchdog" flag. MCUSR &= ~(1 << WDRF); // Enable the watchdog with the smallest duration (16ms) // and interrupt-only mode. _WD_CONTROL_REG |= (1 << _WD_CHANGE_BIT) | (1 << WDE); _WD_CONTROL_REG = (1 << WDIE); // Re-enable interrupts. The watchdog should be running. sei(); #endif // Re-save the seed to obliterate the previous value and to ensure // that if the system is reset without a call to save() that we won't // accidentally generate the same sequence of random data again. save(); // The RNG has now been initialized. initialized = 1; }
/** * \brief Initializes the random number generator. * * \param tag A string that is stirred into the random pool at startup; * usually this should be a value that is unique to the application and * version such as "MyApp 1.0" so that different applications do not * generate the same sequence of values upon first boot. * \param eepromAddress The EEPROM address to load the previously saved * seed from and to save new seeds when save() is called. There must be * at least SEED_SIZE (49) bytes of EEPROM space available at the address. * * This function should be followed by calls to addNoiseSource() to * register the application's noise sources. * * The \a eepromAddress is ignored on the Arduino Due. The seed is instead * stored in the last page of system flash memory. * * \sa addNoiseSource(), stir(), save() */ void RNGClass::begin(const char *tag, int eepromAddress) { // Save the EEPROM address for use by save(). address = eepromAddress; // Initialize the ChaCha20 input block from the saved seed. memcpy_P(block, tagRNG, sizeof(tagRNG)); memcpy_P(block + 4, initRNG, sizeof(initRNG)); #if defined(RNG_EEPROM) if (eeprom_read_byte((const uint8_t *)address) == 'S') { // We have a saved seed: XOR it with the initialization block. for (int posn = 0; posn < 12; ++posn) { block[posn + 4] ^= eeprom_read_dword((const uint32_t *)(address + posn * 4 + 1)); } } #elif defined(RNG_DUE_TRNG) // Do we have a seed saved in the last page of flash memory on the Due? int posn, counter; if (((const uint32_t *)RNG_SEED_ADDR)[0] == 'S') { // XOR the saved seed with the initialization block. for (posn = 0; posn < 12; ++posn) block[posn + 4] ^= ((const uint32_t *)RNG_SEED_ADDR)[posn + 1]; } // If the device has just been reprogrammed, there will be no saved seed. // XOR the initialization block with some output from the CPU's TRNG // to permute the state in a first boot situation after reprogramming. pmc_enable_periph_clk(ID_TRNG); REG_TRNG_CR = TRNG_CR_KEY(0x524E47) | TRNG_CR_ENABLE; REG_TRNG_IDR = TRNG_IDR_DATRDY; // Disable interrupts - we will poll. for (posn = 0; posn < 12; ++posn) { // According to the documentation the TRNG should produce a new // 32-bit random value every 84 clock cycles. If it still hasn't // produced a value after 200 iterations, then assume that the // TRNG is not producing output and stop. for (counter = 0; counter < 200; ++counter) { if ((REG_TRNG_ISR & TRNG_ISR_DATRDY) != 0) break; } if (counter >= 200) break; block[posn + 4] ^= REG_TRNG_ODATA; } #endif // No entropy credits for the saved seed. credits = 0; // Trigger an automatic save once the entropy credits max out. firstSave = 1; // Rekey the random number generator immediately. rekey(); // Stir in the supplied tag data but don't credit any entropy to it. if (tag) stir((const uint8_t *)tag, strlen(tag)); #if defined(RNG_DUE_TRNG) // Stir in the unique identifier for the CPU so that different // devices will give different outputs even without seeding. stirUniqueIdentifier(); #endif // Re-save the seed to obliterate the previous value and to ensure // that if the system is reset without a call to save() that we won't // accidentally generate the same sequence of random data again. save(); }