1
0
mirror of https://github.com/taigrr/arduinolibs synced 2025-01-18 04:33:12 -08:00

Save the RNG seed to flash memory on the Due

This commit is contained in:
Rhys Weatherley 2015-05-02 09:40:13 +10:00
parent c0470980de
commit 2e64f4bec9
2 changed files with 87 additions and 26 deletions

View File

@ -153,13 +153,13 @@ Because the device may be restarted before the first hour expires, there
is a special case in the code: the first time that the entropy pool
fills up, a save will be automatically forced.
\note The Arduino Due does not have EEPROM so it cannot save the random
number seed across system restarts. Instead the \link RNGClass RNG\endlink
class will 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.
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.
To use the random number generator properly, there are some regular tasks
that must be performed every time around the application's main loop().

View File

@ -28,10 +28,10 @@
#include <Arduino.h>
#if defined (__arm__) && defined (__SAM3X8E__)
// The Arduino Due does not have any EEPROM natively on the main chip.
// However, it does have a TRNG so seed saving is not as critical.
#define RNG_NO_EEPROM 1
#define RNG_DUE_TRNG 1
// However, it does have a TRNG and flash memory.
#define RNG_DUE_TRNG 1
#else
#define RNG_EEPROM 1
#include <avr/eeprom.h>
#endif
#include <string.h>
@ -113,9 +113,9 @@
* conditions programmatically, then it may make sense to force a save()
* of the seed upon shutdown.
*
* \note The Arduino Due does not have EEPROM so it cannot save the random
* number seed across system restarts. Instead the RNG class will mix
* in data from the CPU's built-in True Random Number Generator (TRNG).
* 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
@ -205,6 +205,31 @@ RNGClass::~RNGClass()
#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.
@ -215,25 +240,38 @@ static void stirUniqueIdentifier(void)
uint32_t id[4];
// Start Read Unique Identifier.
EFC1->EEFC_FCR = (0x5A << 24) | EFC_FCMD_STUI;
while ((EFC1->EEFC_FSR & EEFC_FSR_FRDY) != 0)
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 *)IFLASH1_ADDR);
id[1] = *((const uint32_t *)(IFLASH1_ADDR + 4));
id[2] = *((const uint32_t *)(IFLASH1_ADDR + 8));
id[3] = *((const uint32_t *)(IFLASH1_ADDR + 12));
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.
EFC1->EEFC_FCR = (0x5A << 24) | EFC_FCMD_SPUI;
while ((EFC1->EEFC_FSR & EEFC_FSR_FRDY) == 0)
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
/**
@ -250,6 +288,9 @@ static void stirUniqueIdentifier(void)
* 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)
@ -260,7 +301,7 @@ void RNGClass::begin(const char *tag, int 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_NO_EEPROM)
#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) {
@ -269,9 +310,17 @@ void RNGClass::begin(const char *tag, int eepromAddress)
}
}
#elif defined(RNG_DUE_TRNG)
// The Arduino Due does not have EEPROM so we instead XOR the
// initialization block with some output from the CPU's 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.
@ -559,11 +608,19 @@ void RNGClass::save()
{
// Generate random data from the current state and save
// that as the seed. Then force a rekey.
#if !defined(RNG_NO_EEPROM)
++(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();
@ -642,9 +699,13 @@ void RNGClass::destroy()
{
clean(block);
clean(stream);
#if !defined(RNG_NO_EEPROM)
#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
}