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
|
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
|
||||||
|
@ -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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user