From 06987988becf6f8a3ee06d0b39b20886d02a6a50 Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Sun, 26 Nov 2017 10:06:29 +1000 Subject: [PATCH] Use CRC-8 to validate the random seed in EEPROM/Flash --- doc/crypto-rng.dox | 2 +- libraries/Crypto/Crypto.cpp | 35 +++++++++++++++++++++++++++++++++++ libraries/Crypto/RNG.cpp | 27 ++++++++++++++++++--------- libraries/Crypto/RNG.h | 2 +- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/doc/crypto-rng.dox b/doc/crypto-rng.dox index 1138705d..fc5c5305 100644 --- a/doc/crypto-rng.dox +++ b/doc/crypto-rng.dox @@ -160,7 +160,7 @@ void setup() { } \endcode -The random number generator uses 49 bytes of space at the end of +The random number generator uses 48 bytes of space at the end of EEPROM memory to store the previous seed. When the system is started next time, the previous saved seed is loaded and then deliberately overwritten with a new seed. This ensures that the device will not diff --git a/libraries/Crypto/Crypto.cpp b/libraries/Crypto/Crypto.cpp index 8088db8a..6c537bac 100644 --- a/libraries/Crypto/Crypto.cpp +++ b/libraries/Crypto/Crypto.cpp @@ -77,3 +77,38 @@ bool secure_compare(const void *data1, const void *data2, size_t len) } return (bool)((((uint16_t)0x0100) - result) >> 8); } + +/** + * \brief Calculates the CRC-8 value over an array in memory. + * + * \param tag Starting tag to distinguish this calculation. + * \param data The data to checksum. + * \param size The number of bytes to checksum. + * \return The CRC-8 value over the data. + * + * This function does not provide any real security. It is a simple + * check that seed values have been initialized within EEPROM or Flash. + * If the CRC-8 check fails, then it is assumed that the EEPROM/Flash + * contents are invalid and should be re-initialized. + * + * Reference: http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html#ch4 + */ +uint8_t crypto_crc8(uint8_t tag, const void *data, unsigned size) +{ + const uint8_t *d = (const uint8_t *)data; + uint8_t crc = 0xFF ^ tag; + uint8_t bit; + while (size > 0) { + crc ^= *d++; + for (bit = 0; bit < 8; ++bit) { + // if (crc & 0x80) + // crc = (crc << 1) ^ 0x1D; + // else + // crc = (crc << 1); + uint8_t generator = (uint8_t)((((int8_t)crc) >> 7) & 0x1D); + crc = (crc << 1) ^ generator; + } + --size; + } + return crc; +} diff --git a/libraries/Crypto/RNG.cpp b/libraries/Crypto/RNG.cpp index f0647db2..56c156a0 100644 --- a/libraries/Crypto/RNG.cpp +++ b/libraries/Crypto/RNG.cpp @@ -154,6 +154,9 @@ RNGClass RNG; /** @cond */ +// Imported from Crypto.cpp. +extern uint8_t crypto_crc8(uint8_t tag, const void *data, unsigned size); + // Tag for 256-bit ChaCha20 keys. This will always appear in the // first 16 bytes of the block. The remaining 48 bytes are the seed. static const char tagRNG[16] PROGMEM = { @@ -361,17 +364,19 @@ void RNGClass::begin(const char *tag) memcpy_P(block + 4, initRNG, sizeof(initRNG)); #if defined(RNG_EEPROM) int address = RNG_EEPROM_ADDRESS; - if (eeprom_read_byte((const uint8_t *)address) == 'S') { + eeprom_read_block(stream, (const void *)address, SEED_SIZE); + if (crypto_crc8('S', stream, SEED_SIZE - 1) == + ((const uint8_t *)stream)[SEED_SIZE - 1]) { // We have a saved seed: XOR it with the initialization block. - for (int posn = 0; posn < 12; ++posn) { - block[posn + 4] ^= - eeprom_read_dword((const uint32_t *)(address + posn * 4 + 1)); - } + // Note: the CRC-8 value is included. No point throwing it away. + for (int posn = 0; posn < 12; ++posn) + block[posn + 4] ^= stream[posn]; } #elif defined(RNG_DUE_TRNG) // Do we have a seed saved in the last page of flash memory on the Due? int posn, counter; - if (((const uint32_t *)RNG_SEED_ADDR)[0] == 'S') { + if (crypto_crc8('S', ((const uint32_t *)RNG_SEED_ADDR) + 1, SEED_SIZE) + == ((const uint32_t *)RNG_SEED_ADDR)[0]) { // XOR the saved seed with the initialization block. for (posn = 0; posn < 12; ++posn) block[posn + 4] ^= ((const uint32_t *)RNG_SEED_ADDR)[posn + 1]; @@ -694,12 +699,16 @@ void RNGClass::save() ++(block[12]); ChaCha::hashCore(stream, block, RNG_ROUNDS); #if defined(RNG_EEPROM) + // We shorten the seed from 48 bytes to 47 to leave room for + // the CRC-8 value. We do this to align the data on an 8-byte + // boundary in EERPOM. int address = RNG_EEPROM_ADDRESS; - eeprom_write_block(stream, (void *)(address + 1), 48); - eeprom_update_byte((uint8_t *)address, 'S'); + eeprom_write_block(stream, (void *)address, SEED_SIZE - 1); + eeprom_write_byte((uint8_t *)(address + SEED_SIZE - 1), + crypto_crc8('S', stream, SEED_SIZE - 1)); #elif defined(RNG_DUE_TRNG) unsigned posn; - ((uint32_t *)(RNG_SEED_ADDR))[0] = 'S'; + ((uint32_t *)(RNG_SEED_ADDR))[0] = crypto_crc8('S', stream, SEED_SIZE); for (posn = 0; posn < 12; ++posn) ((uint32_t *)(RNG_SEED_ADDR))[posn + 1] = stream[posn]; for (posn = 13; posn < (RNG_FLASH_PAGE_SIZE / 4); ++posn) diff --git a/libraries/Crypto/RNG.h b/libraries/Crypto/RNG.h index 426d6afe..d9e3b776 100644 --- a/libraries/Crypto/RNG.h +++ b/libraries/Crypto/RNG.h @@ -50,7 +50,7 @@ public: void destroy(); - static const int SEED_SIZE = 49; + static const int SEED_SIZE = 48; private: uint32_t block[16];