Example #1
0
/**
 * \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;
}
Example #2
0
/**
 * \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();
}