mirror of
https://github.com/taigrr/arduinolibs
synced 2025-01-18 04:33:12 -08:00
Port RNG to the Arduino Due and add TRNG support
This commit is contained in:
parent
786e52f923
commit
b5d6c8de33
@ -40,6 +40,8 @@ global random number pool. It has the following features:
|
|||||||
\li Provision for plug-in environmental noise sources and entropy estimation.
|
\li Provision for plug-in environmental noise sources and entropy estimation.
|
||||||
\li Whitening of noise values to scatter the input noise across the entire
|
\li Whitening of noise values to scatter the input noise across the entire
|
||||||
random number pool.
|
random number pool.
|
||||||
|
\li Built-in support for the True Random Number Generator (TRNG) in the
|
||||||
|
Arduino Due's CPU.
|
||||||
\li Support for mixing in static values like serial numbers and MAC
|
\li Support for mixing in static values like serial numbers and MAC
|
||||||
addresses so that otherwise identical devices do not generate the
|
addresses so that otherwise identical devices do not generate the
|
||||||
same sequence of random numbers upon first boot.
|
same sequence of random numbers upon first boot.
|
||||||
@ -151,6 +153,14 @@ 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
|
||||||
|
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.
|
||||||
|
|
||||||
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().
|
||||||
Newly accumulated noise must be mixed in and auto-saves must be performed
|
Newly accumulated noise must be mixed in and auto-saves must be performed
|
||||||
|
@ -26,7 +26,14 @@
|
|||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "utility/ProgMemUtil.h"
|
#include "utility/ProgMemUtil.h"
|
||||||
#include <Arduino.h>
|
#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
|
||||||
|
#else
|
||||||
#include <avr/eeprom.h>
|
#include <avr/eeprom.h>
|
||||||
|
#endif
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,6 +113,14 @@
|
|||||||
* 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
|
||||||
|
* number seed across system restarts. Instead the RNG 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.
|
||||||
|
*
|
||||||
* \sa NoiseSource
|
* \sa NoiseSource
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -171,6 +186,7 @@ RNGClass::RNGClass()
|
|||||||
, timer(0)
|
, timer(0)
|
||||||
, timeout(3600000UL) // 1 hour in milliseconds
|
, timeout(3600000UL) // 1 hour in milliseconds
|
||||||
, count(0)
|
, count(0)
|
||||||
|
, trngPosn(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,10 +195,47 @@ RNGClass::RNGClass()
|
|||||||
*/
|
*/
|
||||||
RNGClass::~RNGClass()
|
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(block);
|
||||||
clean(stream);
|
clean(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(RNG_DUE_TRNG)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
EFC1->EEFC_FCR = (0x5A << 24) | EFC_FCMD_STUI;
|
||||||
|
while ((EFC1->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));
|
||||||
|
|
||||||
|
// Stop Read Unique Identifier.
|
||||||
|
EFC1->EEFC_FCR = (0x5A << 24) | EFC_FCMD_SPUI;
|
||||||
|
while ((EFC1->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));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Initializes the random number generator.
|
* \brief Initializes the random number generator.
|
||||||
*
|
*
|
||||||
@ -207,6 +260,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 (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) {
|
||||||
@ -214,6 +268,27 @@ void RNGClass::begin(const char *tag, int eepromAddress)
|
|||||||
eeprom_read_dword((const uint32_t *)(address + posn * 4 + 1));
|
eeprom_read_dword((const uint32_t *)(address + posn * 4 + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#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.
|
||||||
|
int posn, counter;
|
||||||
|
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.
|
// No entropy credits for the saved seed.
|
||||||
credits = 0;
|
credits = 0;
|
||||||
@ -228,6 +303,12 @@ void RNGClass::begin(const char *tag, int eepromAddress)
|
|||||||
if (tag)
|
if (tag)
|
||||||
stir((const uint8_t *)tag, strlen(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
|
// 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
|
// that if the system is reset without a call to save() that we won't
|
||||||
// accidentally generate the same sequence of random data again.
|
// accidentally generate the same sequence of random data again.
|
||||||
@ -478,10 +559,12 @@ 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);
|
||||||
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');
|
||||||
|
#endif
|
||||||
rekey();
|
rekey();
|
||||||
timer = millis();
|
timer = millis();
|
||||||
}
|
}
|
||||||
@ -498,6 +581,39 @@ void RNGClass::loop()
|
|||||||
for (uint8_t posn = 0; posn < count; ++posn)
|
for (uint8_t posn = 0; posn < count; ++posn)
|
||||||
noiseSources[posn]->stir();
|
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.
|
// Save the seed if the auto-save timer has expired.
|
||||||
if ((millis() - timer) >= timeout)
|
if ((millis() - timer) >= timeout)
|
||||||
save();
|
save();
|
||||||
@ -526,8 +642,10 @@ void RNGClass::destroy()
|
|||||||
{
|
{
|
||||||
clean(block);
|
clean(block);
|
||||||
clean(stream);
|
clean(stream);
|
||||||
|
#if !defined(RNG_NO_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);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,6 +62,7 @@ private:
|
|||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
NoiseSource *noiseSources[4];
|
NoiseSource *noiseSources[4];
|
||||||
uint8_t count;
|
uint8_t count;
|
||||||
|
uint8_t trngPosn;
|
||||||
|
|
||||||
void rekey();
|
void rekey();
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <Crypto.h>
|
#include <Crypto.h>
|
||||||
#include <RNG.h>
|
#include <RNG.h>
|
||||||
#include <TransistorNoiseSource.h>
|
#include <TransistorNoiseSource.h>
|
||||||
#include <RingOscillatorNoiseSource.h>
|
//#include <RingOscillatorNoiseSource.h>
|
||||||
|
|
||||||
// Change "MyApp 1.0" to some other tag for your application
|
// Change "MyApp 1.0" to some other tag for your application
|
||||||
// so that different applications will generate different results
|
// so that different applications will generate different results
|
||||||
|
Loading…
x
Reference in New Issue
Block a user