/* * Copyright (C) 2015 Southern Storm Software, Pty Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "RNG.h" #include "NoiseSource.h" #include "ChaCha.h" #include "Crypto.h" #include "utility/ProgMemUtil.h" #include #if defined (__arm__) && defined (__SAM3X8E__) // The Arduino Due does not have any EEPROM natively on the main chip. // However, it does have a TRNG and flash memory. #define RNG_DUE_TRNG 1 #else #define RNG_EEPROM 1 #include #endif #include /** * \class RNGClass RNG.h * \brief Pseudo random number generator suitable for cryptography. * * Random number generators must be seeded properly before they can * be used or an adversary may be able to predict the random output. * Seed data may be: * * \li Device-specific, for example serial numbers or MAC addresses. * \li Application-specific, unique to the application. The tag that is * passed to begin() is an example of an application-specific value. * \li Noise-based, generated by a hardware random number generator * that provides unpredictable values from a noise source. * * The following example demonstrates how to initialise the random * number generator: * * \code * #include * #include * #include * #include * #include * * // Noise source to seed the random number generator. * TransistorNoiseSource noise(A1); * * // MAC address for Ethernet communication. * byte mac_address[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; * * void setup() { * // Initialize the Ethernet shield. * Ethernet.begin(mac_address); * * // Initialize the random number generator with the application tag * // "MyApp 1.0" and load the previous seed from EEPROM address 950. * RNG.begin("MyApp 1.0", 950); * * // Stir in the Ethernet MAC address. * RNG.stir(mac_address, sizeof(mac_address)); * * // Add the noise source to the list of sources known to RNG. * RNG.addNoiseSource(noise); * * // ... * } * \endcode * * The application should regularly call loop() to stir in new data * from the registered noise sources and to periodically save the seed: * * \code * void loop() { * // ... * * // Perform regular housekeeping on the random number generator. * RNG.loop(); * * // ... * } * \endcode * * The loop() function will automatically save the random number seed on a * regular basis. By default the seed is saved every hour but this can be * changed using setAutoSaveTime(). * * Keep in mind that saving too often may cause the EEPROM to wear out quicker. * It is wise to limit saving to once an hour or once a day depending * upon how long you intend to field the device before replacing it. * For example, an EEPROM rated for 100k erase/write cycles will last about * 69 days saving once a minute or 11 years saving once an hour. * * The application can still elect to call save() at any time if wants. * For example, if the application can detect power loss or shutdown * conditions programmatically, then it may make sense to force a save() * of the seed upon shutdown. * * The Arduino Due does not have EEPROM so RNG saves the seed into * the last page of system flash memory instead. The RNG class will also * mix in data from the CPU's built-in True Random Number Generator (TRNG). * Assuming that the CPU's TRNG is trustworthy, this should be sufficient * to properly seed the random number generator. It is recommended to * also mix in data from other noise sources just in case the CPU's TRNG * is not trustworthy. * * \sa NoiseSource */ /** * \brief Global random number generator instance. * * \sa RNGClass */ RNGClass RNG; /** * \var RNGClass::SEED_SIZE * \brief Size of a saved random number seed in EEPROM space. */ // Number of ChaCha hash rounds to use for random number generation. #define RNG_ROUNDS 20 // Force a rekey after this many blocks of random data. #define RNG_REKEY_BLOCKS 16 // Maximum entropy credit that can be contained in the pool. #define RNG_MAX_CREDITS 384 /** @cond */ // Tag for 256-bit ChaCha20 keys. This will always appear in the // first 16 bytes of the block. The remaining 48 bytes are the seed. static const char tagRNG[16] PROGMEM = { 'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k' }; // Initialization seed. This is the ChaCha20 output of hashing // "expand 32-byte k" followed by 48 bytes set to the numbers 1 to 48. // The ChaCha20 output block is then truncated to the first 48 bytes. // // This value is intended to start the RNG in a semi-chaotic state if // we don't have a previously saved seed in EEPROM. static const uint8_t initRNG[48] PROGMEM = { 0xB0, 0x2A, 0xAE, 0x7D, 0xEE, 0xCB, 0xBB, 0xB1, 0xFC, 0x03, 0x6F, 0xDD, 0xDC, 0x7D, 0x76, 0x67, 0x0C, 0xE8, 0x1F, 0x0D, 0xA3, 0xA0, 0xAA, 0x1E, 0xB0, 0xBD, 0x72, 0x6B, 0x2B, 0x4C, 0x8A, 0x7E, 0x34, 0xFC, 0x37, 0x60, 0xF4, 0x1E, 0x22, 0xA0, 0x0B, 0xFB, 0x18, 0x84, 0x60, 0xA5, 0x77, 0x72 }; /** @endcond */ /** * \brief Constructs a new random number generator instance. * * This constructor must be followed by a call to begin() to * properly initialize the random number generator. * * \sa begin() */ RNGClass::RNGClass() : address(0) , credits(0) , firstSave(1) , timer(0) , timeout(3600000UL) // 1 hour in milliseconds , count(0) , trngPosn(0) { } /** * \brief Destroys this random number generator instance. */ RNGClass::~RNGClass() { #if defined(RNG_DUE_TRNG) // Disable the TRNG in the Arduino Due. REG_TRNG_CR = TRNG_CR_KEY(0x524E47); #endif clean(block); clean(stream); } #if defined(RNG_DUE_TRNG) // Find the flash memory of interest. Allow for the possibility // of other SAM-based Arduino variants in the future. #if defined(IFLASH1_ADDR) #define RNG_FLASH_ADDR IFLASH1_ADDR #define RNG_FLASH_SIZE IFLASH1_SIZE #define RNG_FLASH_PAGE_SIZE IFLASH1_PAGE_SIZE #define RNG_EFC EFC1 #elif defined(IFLASH0_ADDR) #define RNG_FLASH_ADDR IFLASH0_ADDR #define RNG_FLASH_SIZE IFLASH0_SIZE #define RNG_FLASH_PAGE_SIZE IFLASH0_PAGE_SIZE #define RNG_EFC EFC0 #else #define RNG_FLASH_ADDR IFLASH_ADDR #define RNG_FLASH_SIZE IFLASH_SIZE #define RNG_FLASH_PAGE_SIZE IFLASH_PAGE_SIZE #define RNG_EFC EFC #endif // Address of the flash page to use for saving the seed on the Due. // All SAM variants have a page size of 256 bytes or greater so there is // plenty of room for the 48 byte seed in the last page of flash memory. #define RNG_SEED_ADDR (RNG_FLASH_ADDR + RNG_FLASH_SIZE - RNG_FLASH_PAGE_SIZE) #define RNG_SEED_PAGE ((RNG_FLASH_SIZE / RNG_FLASH_PAGE_SIZE) - 1) // Stir in the unique identifier for the Arduino Due's CPU. // This function must be in RAM because programs running out of // flash memory are not allowed to access the unique identifier. // Info from: http://forum.arduino.cc/index.php?topic=289190.0 __attribute__((section(".ramfunc"))) static void stirUniqueIdentifier(void) { uint32_t id[4]; // Start Read Unique Identifier. RNG_EFC->EEFC_FCR = (0x5A << 24) | EFC_FCMD_STUI; while ((RNG_EFC->EEFC_FSR & EEFC_FSR_FRDY) != 0) ; // do nothing until FRDY falls. // Read the identifier. id[0] = *((const uint32_t *)RNG_FLASH_ADDR); id[1] = *((const uint32_t *)(RNG_FLASH_ADDR + 4)); id[2] = *((const uint32_t *)(RNG_FLASH_ADDR + 8)); id[3] = *((const uint32_t *)(RNG_FLASH_ADDR + 12)); // Stop Read Unique Identifier. RNG_EFC->EEFC_FCR = (0x5A << 24) | EFC_FCMD_SPUI; while ((RNG_EFC->EEFC_FSR & EEFC_FSR_FRDY) == 0) ; // do nothing until FRDY rises. // Stir the unique identifier into the entropy pool. RNG.stir((uint8_t *)id, sizeof(id)); } // Erases the flash page containing the seed and then writes the new seed. // It is assumed the seed has already been loaded into the latch registers. __attribute__((section(".ramfunc"))) static void eraseAndWriteSeed() { // Execute the "Erase and Write Page" command. RNG_EFC->EEFC_FCR = (0x5A << 24) | (RNG_SEED_PAGE << 8) | EFC_FCMD_EWP; // Wait for the FRDY bit to be raised. while ((RNG_EFC->EEFC_FSR & EEFC_FSR_FRDY) == 0) ; // do nothing until FRDY rises. } #endif /** * \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(); } /** * \brief Adds a noise source to the random number generator. * * \param source The noise source to add, which will be polled regularly * by loop() to accumulate noise-based entropy from the source. * * RNG supports a maximum of four noise sources. If the application needs * more than that then the application must poll the noise sources itself by * calling NoiseSource::stir() directly. * * \sa loop(), begin() */ void RNGClass::addNoiseSource(NoiseSource &source) { #define MAX_NOISE_SOURCES (sizeof(noiseSources) / sizeof(noiseSources[0])) if (count < MAX_NOISE_SOURCES) { noiseSources[count++] = &source; source.added(); } } /** * \brief Sets the amount of time between automatic seed saves. * * \param minutes The number of minutes between automatic seed saves. * * The default time between automatic seed saves is 1 hour. * * This function is intended to help with EEPROM wear by slowing down how * often seed data is saved as noise is stirred into the random pool. * The exact period to use depends upon how long you intend to field * the device before replacing it. For example, an EEPROM rated for * 100k erase/write cycles will last about 69 days saving once a minute * or 11 years saving once an hour. * * \sa save(), stir() */ void RNGClass::setAutoSaveTime(uint16_t minutes) { if (!minutes) minutes = 1; // Just in case. timeout = ((uint32_t)minutes) * 60000U; } /** * \brief Generates random bytes into a caller-supplied buffer. * * \param data Points to the buffer to fill with random bytes. * \param len Number of bytes to generate. * * Calling this function will decrease the amount of entropy in the * random number pool by \a len * 8 bits. If there isn't enough * entropy, then this function will still return \a len bytes of * random data generated from what entropy it does have. * * If the application requires a specific amount of entropy before * generating important values, the available() function can be * polled to determine when sufficient entropy is available. * * \sa available(), stir() */ void RNGClass::rand(uint8_t *data, size_t len) { // Decrease the amount of entropy in the pool. if (len > (credits / 8)) credits = 0; else credits -= len * 8; // Generate the random data. uint8_t count = 0; while (len > 0) { // Force a rekey if we have generated too many blocks in this request. if (count >= RNG_REKEY_BLOCKS) { rekey(); count = 1; } else { ++count; } // Increment the low counter word and generate a new keystream block. ++(block[12]); ChaCha::hashCore(stream, block, RNG_ROUNDS); // Copy the data to the return buffer. if (len < 64) { memcpy(data, stream, len); break; } else { memcpy(data, stream, 64); data += 64; len -= 64; } } // Force a rekey after every request. rekey(); } /** * \brief Determine if there is sufficient entropy available for a * specific request size. * * \param len The number of bytes of random data that will be requested * via a call to rand(). * \return Returns true if there is at least \a len * 8 bits of entropy * in the random number pool, or false if not. * * This function can be used by the application to wait for sufficient * entropy to become available from the system's noise sources before * generating important values. For example: * * \code * bool haveKey = false; * byte key[32]; * * void loop() { * ... * * if (!haveKey && RNG.available(sizeof(key))) { * RNG.rand(key, sizeof(key)); * haveKey = true; * } * * ... * } * \endcode * * If \a len is larger than the maximum number of entropy credits supported * by the random number pool (384 bits, 48 bytes), then the maximum will be * used instead. For example, asking if 512 bits (64 bytes) are available * will return true if in reality only 384 bits are available. If this is a * problem for the application's security requirements, then large requests * for random data should be broken up into smaller chunks with the * application waiting for the entropy pool to refill between chunks. * * \sa rand() */ bool RNGClass::available(size_t len) const { if (len >= (RNG_MAX_CREDITS / 8)) return credits >= RNG_MAX_CREDITS; else return len <= (credits / 8); } /** * \brief Stirs additional entropy data into the random pool. * * \param data Points to the additional data to be stirred in. * \param len Number of bytes to be stirred in. * \param credit The number of bits of entropy to credit for the * data that is stirred in. Note that this is bits, not bytes. * * The maximum credit allowed is \a len * 8 bits, indicating that * every bit in the input \a data is good and random. Practical noise * sources are rarely that good, so \a credit will usually be smaller. * For example, to credit 2 bits of entropy per byte, the function * would be called as follows: * * \code * RNG.stir(noise_data, noise_bytes, noise_bytes * 2); * \endcode * * If \a credit is zero, then the \a data will be stirred in but no * entropy credit is given. This is useful for static values like * serial numbers and MAC addresses that are different between * devices but highly predictable. * * \sa loop() */ void RNGClass::stir(const uint8_t *data, size_t len, unsigned int credit) { // Increase the entropy credit. if ((credit / 8) >= len) credit = len * 8; if ((RNG_MAX_CREDITS - credits) > credit) credits += credit; else credits = RNG_MAX_CREDITS; // Process the supplied input data. if (len > 0) { // XOR the data with the ChaCha input block in 48 byte // chunks and rekey the ChaCha cipher for each chunk to mix // the data in. This should scatter any "true entropy" in // the input across the entire block. while (len > 0) { size_t templen = len; if (templen > 48) templen = 48; uint8_t *output = ((uint8_t *)block) + 16; len -= templen; while (templen > 0) { *output++ ^= *data++; --templen; } rekey(); } } else { // There was no input data, so just force a rekey so we // get some mixing of the state even without new data. rekey(); } // Save if this is the first time we have reached max entropy. // This provides some protection if the system is powered off before // the first auto-save timeout occurs. if (firstSave && credits >= RNG_MAX_CREDITS) { firstSave = 0; save(); } } /** * \brief Saves the random seed to EEPROM. * * During system startup, noise sources typically won't have accumulated * much entropy. But startup is usually the time when the system most * needs to generate random data for session keys, IV's, and the like. * * The purpose of this function is to pass some of the accumulated entropy * from one session to the next after a loss of power. Thus, once the system * has been running for a while it will get progressively better at generating * random values and the accumulated entropy will not be completely lost. * * Normally it isn't necessary to call save() directly. The loop() function * will automatically save the seed on a periodic basis (default of 1 hour). * * The seed that is saved is generated in such a way that it cannot be used * to predict random values that were generated previously or subsequently * in the current session. So a compromise of the EEPROM contents of a * captured device should not result in compromise of random values * that have already been generated. However, if power is lost and the * system restarted, then there will be a short period of time where the * random state will be predictable from the seed. For this reason it is * very important to stir() in new noise data at startup. * * \sa loop(), stir() */ void RNGClass::save() { // Generate random data from the current state and save // that as the seed. Then force a rekey. ++(block[12]); ChaCha::hashCore(stream, block, RNG_ROUNDS); #if defined(RNG_EEPROM) eeprom_write_block(stream, (void *)(address + 1), 48); eeprom_update_byte((uint8_t *)address, 'S'); #elif defined(RNG_DUE_TRNG) unsigned posn; ((uint32_t *)(RNG_SEED_ADDR))[0] = 'S'; for (posn = 0; posn < 12; ++posn) ((uint32_t *)(RNG_SEED_ADDR))[posn + 1] = stream[posn]; for (posn = 13; posn < (RNG_FLASH_PAGE_SIZE / 4); ++posn) ((uint32_t *)(RNG_SEED_ADDR))[posn + 13] = 0xFFFFFFFF; eraseAndWriteSeed(); #endif rekey(); timer = millis(); } /** * \brief Run periodic housekeeping tasks on the random number generator. * * This function must be called on a regular basis from the application's * main "loop()" function. */ void RNGClass::loop() { // Stir in the entropy from all registered noise sources. for (uint8_t posn = 0; posn < count; ++posn) noiseSources[posn]->stir(); #if defined(RNG_DUE_TRNG) // If there is data available from the Arudino Due's TRNG, then XOR // it with the state block and increase the entropy credit. We don't // call stir() yet because that will seriously slow down the system // given how fast the TRNG is. Instead we save up the XOR'ed TRNG // data until the next rand() call and then hash it to generate the // desired output. // // The CPU documentation claims that the TRNG output is very good so // this should only make the pool more and more random as time goes on. // However there is a risk that the CPU manufacturer was pressured by // government or intelligence agencies to insert a back door that // generates predictable output. Or the manufacturer was overly // optimistic about their TRNG design and it is actually flawed in a // way they don't realise. // // If you are concerned about such threats, then make sure to mix in // data from other noise sources. By hashing together the TRNG with // the other noise data, rand() should produce unpredictable data even // if one of the sources is actually predictable. if ((REG_TRNG_ISR & TRNG_ISR_DATRDY) != 0) { block[4 + trngPosn] ^= REG_TRNG_ODATA; if (++trngPosn >= 12) trngPosn = 0; if (credits < RNG_MAX_CREDITS) { // Credit 1 bit of entropy for the word. The TRNG should be // better than this but it is so fast that we want to collect // up more data before passing it to the application. ++credits; } } #endif // Save the seed if the auto-save timer has expired. if ((millis() - timer) >= timeout) save(); } /** * \brief Destroys the data in the random number pool and the saved seed * in EEPROM. * * This function attempts to throw away any data that could theoretically be * used to predict previous and future outputs of the random number generator * if the device is captured, sold, or otherwise compromised. * * After this function is called, begin() must be called again to * re-initialize the random number generator. * * \note The rand() and save() functions take some care to manage the * random number pool in a way that makes prediction of past outputs from a * captured state very difficult. Future outputs may be predictable if * noise or other high-entropy data is not mixed in with stir() on a * regular basis. * * \sa begin() */ void RNGClass::destroy() { clean(block); clean(stream); #if defined(RNG_EEPROM) for (int posn = 0; posn < SEED_SIZE; ++posn) eeprom_write_byte((uint8_t *)(address + posn), 0xFF); #elif defined(RNG_DUE_TRNG) for (unsigned posn = 0; posn < (RNG_FLASH_PAGE_SIZE / 4); ++posn) ((uint32_t *)(RNG_SEED_ADDR))[posn] = 0xFFFFFFFF; eraseAndWriteSeed(); #endif } /** * \brief Rekeys the random number generator. */ void RNGClass::rekey() { // Rekey the cipher for the next request by generating a new block. // This is intended to make it difficult to wind the random number // backwards if the state is captured later. The first 16 bytes of // "block" remain set to "tagRNG". ++(block[12]); ChaCha::hashCore(stream, block, RNG_ROUNDS); memcpy(block + 4, stream, 48); // Permute the high word of the counter using the system microsecond // counter to introduce a little bit of non-stir randomness for each // request. Note: If random data is requested on a predictable schedule // then this may not help very much. It is still necessary to stir in // high quality entropy data on a regular basis using stir(). block[13] ^= micros(); }