1
0
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:
Rhys Weatherley 2015-12-30 14:08:01 +10:00
parent 738d86cf2b
commit 25e9f6f3d4
2 changed files with 132 additions and 3 deletions

View File

@ -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 approach is to mix multiple noise sources together to get the best
of both worlds. 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 \section crypto_rng_init Initializing the random number generator
To use the random number generator, both \link RNGClass RNG\endlink and a To use the random number generator, both \link RNGClass RNG\endlink and a

View File

@ -30,9 +30,11 @@
// 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 and flash memory. // However, it does have a TRNG and flash memory.
#define RNG_DUE_TRNG 1 #define RNG_DUE_TRNG 1
#else #elif defined(__AVR__)
#define RNG_EEPROM 1 #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/eeprom.h>
#include <avr/wdt.h>
#endif #endif
#include <string.h> #include <string.h>
@ -169,6 +171,52 @@ static const uint8_t initRNG[48] PROGMEM = {
0x0B, 0xFB, 0x18, 0x84, 0x60, 0xA5, 0x77, 0x72 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 */ /** @endcond */
/** /**
@ -198,6 +246,21 @@ RNGClass::~RNGClass()
#if defined(RNG_DUE_TRNG) #if defined(RNG_DUE_TRNG)
// Disable the TRNG in the Arduino Due. // Disable the TRNG in the Arduino Due.
REG_TRNG_CR = TRNG_CR_KEY(0x524E47); 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 #endif
clean(block); clean(block);
clean(stream); clean(stream);
@ -358,6 +421,23 @@ void RNGClass::begin(const char *tag, int eepromAddress)
stirUniqueIdentifier(); stirUniqueIdentifier();
#endif #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 // 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.
@ -538,7 +618,7 @@ bool RNGClass::available(size_t len) const
void RNGClass::stir(const uint8_t *data, size_t len, unsigned int credit) void RNGClass::stir(const uint8_t *data, size_t len, unsigned int credit)
{ {
// Increase the entropy credit. // Increase the entropy credit.
if ((credit / 8) >= len) if ((credit / 8) >= len && len)
credit = len * 8; credit = len * 8;
if ((RNG_MAX_CREDITS - credits) > credit) if ((RNG_MAX_CREDITS - credits) > credit)
credits += credit; credits += credit;
@ -669,6 +749,34 @@ void RNGClass::loop()
++credits; ++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 #endif
// Save the seed if the auto-save timer has expired. // Save the seed if the auto-save timer has expired.