mirror of
https://github.com/taigrr/arduinolibs
synced 2025-01-18 04:33:12 -08:00
Watchdog-based noise source
This commit is contained in:
parent
738d86cf2b
commit
25e9f6f3d4
@ -85,6 +85,27 @@ TransistorNoiseSource as Rob's design has had more review. Another
|
||||
approach is to mix multiple noise sources together to get the best
|
||||
of both worlds.
|
||||
|
||||
\section crypto_rng_builtin Built-in entropy sources
|
||||
|
||||
Some entropy sources are built in and do not need to be provided via a
|
||||
NoiseSource object.
|
||||
|
||||
On the Arduino Due, the built-in True Random Number Generator (TRNG) is used
|
||||
to seed the random number generator in addition to any configured noise
|
||||
sources.
|
||||
|
||||
On AVR-based Arduino platforms (Uno, Nano, Mega, etc), jitter between the
|
||||
watchdog timer and the main CPU clock is used to harvest some entropy
|
||||
using a technique similar to that described
|
||||
<a href="https://sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library">here</a>.
|
||||
This is not a high quality source of entropy but it is "better than nothing"
|
||||
if an external noise source is not available or practical. Entropy
|
||||
accumulates very slowly and it could take several minutes before the state
|
||||
is sufficiently random for safe use.
|
||||
|
||||
For security-critical applications it is very important to combine the
|
||||
built-in entropy sources with an external noise source.
|
||||
|
||||
\section crypto_rng_init Initializing the random number generator
|
||||
|
||||
To use the random number generator, both \link RNGClass RNG\endlink and a
|
||||
|
@ -30,9 +30,11 @@
|
||||
// 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
|
||||
#elif defined(__AVR__)
|
||||
#define RNG_EEPROM 1 // Use EEPROM to save the seed.
|
||||
#define RNG_WATCHDOG 1 // Harvest entropy from watchdog jitter.
|
||||
#include <avr/eeprom.h>
|
||||
#include <avr/wdt.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
@ -169,6 +171,52 @@ static const uint8_t initRNG[48] PROGMEM = {
|
||||
0x0B, 0xFB, 0x18, 0x84, 0x60, 0xA5, 0x77, 0x72
|
||||
};
|
||||
|
||||
#if defined(RNG_WATCHDOG)
|
||||
|
||||
// Use jitter between the watchdog timer and the main CPU clock to
|
||||
// harvest some entropy on AVR-based systems. This technique comes from:
|
||||
//
|
||||
// https://sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library
|
||||
//
|
||||
// The watchdog generates entropy very slowly - it can take around 32 seconds
|
||||
// to generate 256 bits of entropy credit. This is a "better than nothing"
|
||||
// entropy source but a real noise source is definitely recommended.
|
||||
|
||||
// Helper macros for specific 32-bit shift counts.
|
||||
#define leftShift3(value) ((value) << 3)
|
||||
#define leftShift10(value) ((value) << 10)
|
||||
#define leftShift15(value) ((value) << 15)
|
||||
#define rightShift6(value) ((value) >> 6)
|
||||
#define rightShift11(value) ((value) >> 11)
|
||||
|
||||
static uint32_t volatile hash = 0;
|
||||
static uint8_t volatile outBits = 0;
|
||||
|
||||
// Watchdog interrupt handler. This fires off every 16ms. We collect
|
||||
// 32 bits and then pass them off onto RNGClass::loop().
|
||||
ISR(WDT_vect)
|
||||
{
|
||||
// Read the low byte of Timer 1. We assume that the timer was
|
||||
// initialized by the Arduino startup code for PWM use or that the
|
||||
// application is free-running Timer 1 for its own purposes.
|
||||
// Timer 0 is used on systems that don't have a Timer 1.
|
||||
#if defined(TCNT1L)
|
||||
unsigned char value = TCNT1L;
|
||||
#elif defined(TCNT0L)
|
||||
unsigned char value = TCNT0L;
|
||||
#else
|
||||
unsigned char value = TCNT0;
|
||||
#endif
|
||||
// Use Jenkin's one-at-a-time hash function to scatter the entropy a bit.
|
||||
// https://en.wikipedia.org/wiki/Jenkins_hash_function
|
||||
hash += value;
|
||||
hash += leftShift10(hash);
|
||||
hash ^= rightShift6(hash);
|
||||
++outBits;
|
||||
}
|
||||
|
||||
#endif // RNG_WATCHDOG
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
@ -198,6 +246,21 @@ RNGClass::~RNGClass()
|
||||
#if defined(RNG_DUE_TRNG)
|
||||
// Disable the TRNG in the Arduino Due.
|
||||
REG_TRNG_CR = TRNG_CR_KEY(0x524E47);
|
||||
#endif
|
||||
#if defined(RNG_WATCHDOG)
|
||||
// Disable interrupts and reset the watchdog.
|
||||
cli();
|
||||
wdt_reset();
|
||||
|
||||
// Clear the "reset due to watchdog" flag.
|
||||
MCUSR &= ~(1 << WDRF);
|
||||
|
||||
// Disable the watchdog.
|
||||
_WD_CONTROL_REG |= (1 << _WD_CHANGE_BIT) | (1 << WDE);
|
||||
_WD_CONTROL_REG = 0;
|
||||
|
||||
// Re-enable interrupts. The watchdog should be stopped.
|
||||
sei();
|
||||
#endif
|
||||
clean(block);
|
||||
clean(stream);
|
||||
@ -358,6 +421,23 @@ void RNGClass::begin(const char *tag, int eepromAddress)
|
||||
stirUniqueIdentifier();
|
||||
#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.
|
||||
@ -538,7 +618,7 @@ bool RNGClass::available(size_t len) const
|
||||
void RNGClass::stir(const uint8_t *data, size_t len, unsigned int credit)
|
||||
{
|
||||
// Increase the entropy credit.
|
||||
if ((credit / 8) >= len)
|
||||
if ((credit / 8) >= len && len)
|
||||
credit = len * 8;
|
||||
if ((RNG_MAX_CREDITS - credits) > credit)
|
||||
credits += credit;
|
||||
@ -669,6 +749,34 @@ void RNGClass::loop()
|
||||
++credits;
|
||||
}
|
||||
}
|
||||
#elif defined(RNG_WATCHDOG)
|
||||
// Read the 32 bit buffer from the WDT interrupt.
|
||||
cli();
|
||||
if (outBits >= 32) {
|
||||
uint32_t value = hash;
|
||||
hash = 0;
|
||||
outBits = 0;
|
||||
sei();
|
||||
|
||||
// Final steps of the Jenkin's one-at-a-time hash function.
|
||||
// https://en.wikipedia.org/wiki/Jenkins_hash_function
|
||||
value += leftShift3(value);
|
||||
value ^= rightShift11(value);
|
||||
value += leftShift15(value);
|
||||
|
||||
// XOR the word with the state. Stir once we accumulate 48 bytes,
|
||||
// which happens about once every 6.4 seconds.
|
||||
block[4 + trngPosn] ^= value;
|
||||
if (++trngPosn >= 12) {
|
||||
trngPosn = 0;
|
||||
|
||||
// Credit 1 bit of entropy for each byte of input. It can take
|
||||
// between 30 and 40 seconds to accumulate 256 bits of credit.
|
||||
stir(0, 0, 48);
|
||||
}
|
||||
} else {
|
||||
sei();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Save the seed if the auto-save timer has expired.
|
||||
|
Loading…
x
Reference in New Issue
Block a user