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