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:
parent
c0470980de
commit
2e64f4bec9
@ -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
|
is a special case in the code: the first time that the entropy pool
|
||||||
fills up, a save will be automatically forced.
|
fills up, a save will be automatically forced.
|
||||||
|
|
||||||
\note The Arduino Due does not have EEPROM so it cannot save the random
|
The Arduino Due does not have EEPROM so RNG saves the seed into
|
||||||
number seed across system restarts. Instead the \link RNGClass RNG\endlink
|
the last page of system flash memory instead. The RNG class will also
|
||||||
class will mix in data from the CPU's built-in True Random Number
|
mix in data from the CPU's built-in True Random Number Generator (TRNG).
|
||||||
Generator (TRNG). Assuming that the CPU's TRNG is trustworthy,
|
Assuming that the CPU's TRNG is trustworthy, this should be sufficient
|
||||||
this should be sufficient to properly seed the random number generator.
|
to properly seed the random number generator. It is recommended to
|
||||||
It is recommended to also mix in data from other noise sources just in
|
also mix in data from other noise sources just in case the CPU's TRNG
|
||||||
case the CPU's TRNG is not trustworthy.
|
is not trustworthy.
|
||||||
|
|
||||||
To use the random number generator properly, there are some regular tasks
|
To use the random number generator properly, there are some regular tasks
|
||||||
that must be performed every time around the application's main loop().
|
that must be performed every time around the application's main loop().
|
||||||
|
@ -28,10 +28,10 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#if defined (__arm__) && defined (__SAM3X8E__)
|
#if defined (__arm__) && defined (__SAM3X8E__)
|
||||||
// The Arduino Due does not have any EEPROM natively on the main chip.
|
// 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.
|
// However, it does have a TRNG and flash memory.
|
||||||
#define RNG_NO_EEPROM 1
|
#define RNG_DUE_TRNG 1
|
||||||
#define RNG_DUE_TRNG 1
|
|
||||||
#else
|
#else
|
||||||
|
#define RNG_EEPROM 1
|
||||||
#include <avr/eeprom.h>
|
#include <avr/eeprom.h>
|
||||||
#endif
|
#endif
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -113,9 +113,9 @@
|
|||||||
* conditions programmatically, then it may make sense to force a save()
|
* conditions programmatically, then it may make sense to force a save()
|
||||||
* of the seed upon shutdown.
|
* of the seed upon shutdown.
|
||||||
*
|
*
|
||||||
* \note The Arduino Due does not have EEPROM so it cannot save the random
|
* The Arduino Due does not have EEPROM so RNG saves the seed into
|
||||||
* number seed across system restarts. Instead the RNG class will mix
|
* the last page of system flash memory instead. The RNG class will also
|
||||||
* in data from the CPU's built-in True Random Number Generator (TRNG).
|
* 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
|
* Assuming that the CPU's TRNG is trustworthy, this should be sufficient
|
||||||
* to properly seed the random number generator. It is recommended to
|
* 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
|
* 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)
|
#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.
|
// Stir in the unique identifier for the Arduino Due's CPU.
|
||||||
// This function must be in RAM because programs running out of
|
// This function must be in RAM because programs running out of
|
||||||
// flash memory are not allowed to access the unique identifier.
|
// flash memory are not allowed to access the unique identifier.
|
||||||
@ -215,25 +240,38 @@ static void stirUniqueIdentifier(void)
|
|||||||
uint32_t id[4];
|
uint32_t id[4];
|
||||||
|
|
||||||
// Start Read Unique Identifier.
|
// Start Read Unique Identifier.
|
||||||
EFC1->EEFC_FCR = (0x5A << 24) | EFC_FCMD_STUI;
|
RNG_EFC->EEFC_FCR = (0x5A << 24) | EFC_FCMD_STUI;
|
||||||
while ((EFC1->EEFC_FSR & EEFC_FSR_FRDY) != 0)
|
while ((RNG_EFC->EEFC_FSR & EEFC_FSR_FRDY) != 0)
|
||||||
; // do nothing until FRDY falls.
|
; // do nothing until FRDY falls.
|
||||||
|
|
||||||
// Read the identifier.
|
// Read the identifier.
|
||||||
id[0] = *((const uint32_t *)IFLASH1_ADDR);
|
id[0] = *((const uint32_t *)RNG_FLASH_ADDR);
|
||||||
id[1] = *((const uint32_t *)(IFLASH1_ADDR + 4));
|
id[1] = *((const uint32_t *)(RNG_FLASH_ADDR + 4));
|
||||||
id[2] = *((const uint32_t *)(IFLASH1_ADDR + 8));
|
id[2] = *((const uint32_t *)(RNG_FLASH_ADDR + 8));
|
||||||
id[3] = *((const uint32_t *)(IFLASH1_ADDR + 12));
|
id[3] = *((const uint32_t *)(RNG_FLASH_ADDR + 12));
|
||||||
|
|
||||||
// Stop Read Unique Identifier.
|
// Stop Read Unique Identifier.
|
||||||
EFC1->EEFC_FCR = (0x5A << 24) | EFC_FCMD_SPUI;
|
RNG_EFC->EEFC_FCR = (0x5A << 24) | EFC_FCMD_SPUI;
|
||||||
while ((EFC1->EEFC_FSR & EEFC_FSR_FRDY) == 0)
|
while ((RNG_EFC->EEFC_FSR & EEFC_FSR_FRDY) == 0)
|
||||||
; // do nothing until FRDY rises.
|
; // do nothing until FRDY rises.
|
||||||
|
|
||||||
// Stir the unique identifier into the entropy pool.
|
// Stir the unique identifier into the entropy pool.
|
||||||
RNG.stir((uint8_t *)id, sizeof(id));
|
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
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -250,6 +288,9 @@ static void stirUniqueIdentifier(void)
|
|||||||
* This function should be followed by calls to addNoiseSource() to
|
* This function should be followed by calls to addNoiseSource() to
|
||||||
* register the application's noise sources.
|
* 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()
|
* \sa addNoiseSource(), stir(), save()
|
||||||
*/
|
*/
|
||||||
void RNGClass::begin(const char *tag, int eepromAddress)
|
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.
|
// Initialize the ChaCha20 input block from the saved seed.
|
||||||
memcpy_P(block, tagRNG, sizeof(tagRNG));
|
memcpy_P(block, tagRNG, sizeof(tagRNG));
|
||||||
memcpy_P(block + 4, initRNG, sizeof(initRNG));
|
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') {
|
if (eeprom_read_byte((const uint8_t *)address) == 'S') {
|
||||||
// We have a saved seed: XOR it with the initialization block.
|
// We have a saved seed: XOR it with the initialization block.
|
||||||
for (int posn = 0; posn < 12; ++posn) {
|
for (int posn = 0; posn < 12; ++posn) {
|
||||||
@ -269,9 +310,17 @@ void RNGClass::begin(const char *tag, int eepromAddress)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#elif defined(RNG_DUE_TRNG)
|
#elif defined(RNG_DUE_TRNG)
|
||||||
// The Arduino Due does not have EEPROM so we instead XOR the
|
// Do we have a seed saved in the last page of flash memory on the Due?
|
||||||
// initialization block with some output from the CPU's TRNG.
|
|
||||||
int posn, counter;
|
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);
|
pmc_enable_periph_clk(ID_TRNG);
|
||||||
REG_TRNG_CR = TRNG_CR_KEY(0x524E47) | TRNG_CR_ENABLE;
|
REG_TRNG_CR = TRNG_CR_KEY(0x524E47) | TRNG_CR_ENABLE;
|
||||||
REG_TRNG_IDR = TRNG_IDR_DATRDY; // Disable interrupts - we will poll.
|
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
|
// Generate random data from the current state and save
|
||||||
// that as the seed. Then force a rekey.
|
// that as the seed. Then force a rekey.
|
||||||
#if !defined(RNG_NO_EEPROM)
|
|
||||||
++(block[12]);
|
++(block[12]);
|
||||||
ChaCha::hashCore(stream, block, RNG_ROUNDS);
|
ChaCha::hashCore(stream, block, RNG_ROUNDS);
|
||||||
|
#if defined(RNG_EEPROM)
|
||||||
eeprom_write_block(stream, (void *)(address + 1), 48);
|
eeprom_write_block(stream, (void *)(address + 1), 48);
|
||||||
eeprom_update_byte((uint8_t *)address, 'S');
|
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
|
#endif
|
||||||
rekey();
|
rekey();
|
||||||
timer = millis();
|
timer = millis();
|
||||||
@ -642,9 +699,13 @@ void RNGClass::destroy()
|
|||||||
{
|
{
|
||||||
clean(block);
|
clean(block);
|
||||||
clean(stream);
|
clean(stream);
|
||||||
#if !defined(RNG_NO_EEPROM)
|
#if defined(RNG_EEPROM)
|
||||||
for (int posn = 0; posn < SEED_SIZE; ++posn)
|
for (int posn = 0; posn < SEED_SIZE; ++posn)
|
||||||
eeprom_write_byte((uint8_t *)(address + posn), 0xFF);
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user