diff --git a/libraries/Update/src/Update.h b/libraries/Update/src/Update.h index d34efe73196..cb99508238b 100644 --- a/libraries/Update/src/Update.h +++ b/libraries/Update/src/Update.h @@ -5,6 +5,7 @@ #include #include #include "esp_partition.h" +#include "aes/esp_aes.h" #define UPDATE_ERROR_OK (0) #define UPDATE_ERROR_WRITE (1) @@ -19,6 +20,7 @@ #define UPDATE_ERROR_NO_PARTITION (10) #define UPDATE_ERROR_BAD_ARGUMENT (11) #define UPDATE_ERROR_ABORT (12) +#define UPDATE_ERROR_DECRYPT (13) #define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF @@ -26,7 +28,15 @@ #define U_SPIFFS 100 #define U_AUTH 200 -#define ENCRYPTED_BLOCK_SIZE 16 +#define ENCRYPTED_BLOCK_SIZE 16 +#define ENCRYPTED_TWEAK_BLOCK_SIZE 32 +#define ENCRYPTED_KEY_SIZE 32 + +#define U_AES_DECRYPT_NONE 0 +#define U_AES_DECRYPT_AUTO 1 +#define U_AES_DECRYPT_ON 2 +#define U_AES_DECRYPT_MODE_MASK 3 +#define U_AES_IMAGE_DECRYPTING_BIT 4 #define SPI_SECTORS_PER_BLOCK 16 // usually large erase block is 32k/64k #define SPI_FLASH_BLOCK_SIZE (SPI_SECTORS_PER_BLOCK*SPI_FLASH_SEC_SIZE) @@ -48,6 +58,15 @@ class UpdateClass { */ bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW, const char *label = NULL); + /* + Setup decryption configuration + Crypt Key is 32bytes(256bits) block of data, use the same key as used to encrypt image file + Crypt Address, use the same value as used to encrypt image file + Crypt Config, use the same value as used to encrypt image file + Crypt Mode, used to select if image files should be decrypted or not + */ + bool setupCrypt(const uint8_t *cryptKey=0, size_t cryptAddress=0, uint8_t cryptConfig=0xf, int cryptMode=U_AES_DECRYPT_AUTO); + /* Writes a buffer to the flash and increments the address Returns the amount written @@ -75,6 +94,26 @@ class UpdateClass { */ bool end(bool evenIfRemaining = false); + /* + sets AES256 key(32 bytes) used for decrypting image file + */ + bool setCryptKey(const uint8_t *cryptKey); + + /* + sets crypt mode used on image files + */ + bool setCryptMode(const int cryptMode); + + /* + sets address used for decrypting image file + */ + void setCryptAddress(const size_t cryptAddress){ _cryptAddress = cryptAddress & 0x00fffff0; } + + /* + sets crypt config used for decrypting image file + */ + void setCryptConfig(const uint8_t cryptConfig){ _cryptCfg = cryptConfig & 0x0f; } + /* Aborts the running update */ @@ -165,6 +204,8 @@ class UpdateClass { private: void _reset(); void _abort(uint8_t err); + void _cryptKeyTweak(size_t cryptAddress, uint8_t *tweaked_key); + bool _decryptBuffer(); bool _writeBuffer(); bool _verifyHeader(uint8_t data); bool _verifyEnd(); @@ -173,6 +214,8 @@ class UpdateClass { uint8_t _error; + uint8_t *_cryptKey; + uint8_t *_cryptBuffer; uint8_t *_buffer; uint8_t *_skipBuffer; size_t _bufferLen; @@ -188,6 +231,10 @@ class UpdateClass { int _ledPin; uint8_t _ledOn; + + uint8_t _cryptMode; + size_t _cryptAddress; + uint8_t _cryptCfg; }; #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_UPDATE) diff --git a/libraries/Update/src/Updater.cpp b/libraries/Update/src/Updater.cpp index 9980558dbba..bd22419d37a 100644 --- a/libraries/Update/src/Updater.cpp +++ b/libraries/Update/src/Updater.cpp @@ -31,6 +31,8 @@ static const char * _err2str(uint8_t _error){ return ("Bad Argument"); } else if(_error == UPDATE_ERROR_ABORT){ return ("Aborted"); + } else if(_error == UPDATE_ERROR_DECRYPT){ + return ("Decryption error"); } return ("UNKNOWN"); } @@ -59,7 +61,10 @@ bool UpdateClass::_enablePartition(const esp_partition_t* partition){ UpdateClass::UpdateClass() : _error(0) +, _cryptKey(0) +, _cryptBuffer(0) , _buffer(0) +, _skipBuffer(0) , _bufferLen(0) , _size(0) , _progress_callback(NULL) @@ -67,6 +72,9 @@ UpdateClass::UpdateClass() , _paroffset(0) , _command(U_FLASH) , _partition(NULL) +, _cryptMode(U_AES_DECRYPT_AUTO) +, _cryptAddress(0) +, _cryptCfg(0xf) { } @@ -83,6 +91,7 @@ void UpdateClass::_reset() { delete[] _skipBuffer; } + _cryptBuffer = nullptr; _buffer = nullptr; _skipBuffer = nullptr; _bufferLen = 0; @@ -176,6 +185,48 @@ bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn, con return true; } +bool UpdateClass::setupCrypt(const uint8_t *cryptKey, size_t cryptAddress, uint8_t cryptConfig, int cryptMode){ + if(setCryptKey(cryptKey)){ + if(setCryptMode(cryptMode)){ + setCryptAddress(cryptAddress); + setCryptConfig(cryptConfig); + return true; + } + } + return false; +} + +bool UpdateClass::setCryptKey(const uint8_t *cryptKey){ + if(!cryptKey){ + if (_cryptKey){ + delete[] _cryptKey; + _cryptKey = 0; + log_d("AES key unset"); + } + return false; //key cleared, no key to decrypt with + } + //initialize + if(!_cryptKey){ + _cryptKey = new (std::nothrow) uint8_t[ENCRYPTED_KEY_SIZE]; + } + if(!_cryptKey){ + log_e("new failed"); + return false; + } + memcpy(_cryptKey, cryptKey, ENCRYPTED_KEY_SIZE); + return true; +} + +bool UpdateClass::setCryptMode(const int cryptMode){ + if(cryptMode >= U_AES_DECRYPT_NONE && cryptMode <= U_AES_DECRYPT_ON){ + _cryptMode = cryptMode; + }else{ + log_e("bad crypt mode arguement %i", cryptMode); + return false; + } + return true; +} + void UpdateClass::_abort(uint8_t err){ _reset(); _error = err; @@ -185,7 +236,119 @@ void UpdateClass::abort(){ _abort(UPDATE_ERROR_ABORT); } +void UpdateClass::_cryptKeyTweak(size_t cryptAddress, uint8_t *tweaked_key){ + memcpy(tweaked_key, _cryptKey, ENCRYPTED_KEY_SIZE ); + if(_cryptCfg == 0) return; //no tweaking needed, use crypt key as-is + + const uint8_t pattern[] = { 23, 23, 23, 14, 23, 23, 23, 12, 23, 23, 23, 10, 23, 23, 23, 8 }; + int pattern_idx = 0; + int key_idx = 0; + int bit_len = 0; + uint32_t tweak = 0; + cryptAddress &= 0x00ffffe0; //bit 23-5 + cryptAddress <<= 8; //bit23 shifted to bit31(MSB) + while(pattern_idx < sizeof(pattern)){ + tweak = cryptAddress<<(23 - pattern[pattern_idx]); //bit shift for small patterns + // alternative to: tweak = rotl32(tweak,8 - bit_len); + tweak = (tweak<<(8 - bit_len)) | (tweak>>(24 + bit_len)); //rotate to line up with end of previous tweak bits + bit_len += pattern[pattern_idx++] - 4; //add number of bits in next pattern(23-4 = 19bits = 23bit to 5bit) + while(bit_len > 7){ + tweaked_key[key_idx++] ^= tweak; //XOR byte + // alternative to: tweak = rotl32(tweak, 8); + tweak = (tweak<<8) | (tweak>>24); //compiler should optimize to use rotate(fast) + bit_len -=8; + } + tweaked_key[key_idx] ^= tweak; //XOR remaining bits, will XOR zeros if no remaining bits + } + if(_cryptCfg == 0xf) return; //return with fully tweaked key + + //some of tweaked key bits need to be restore back to crypt key bits + const uint8_t cfg_bits[] = { 67, 65, 63, 61 }; + key_idx = 0; + pattern_idx = 0; + while(key_idx < ENCRYPTED_KEY_SIZE){ + bit_len += cfg_bits[pattern_idx]; + if( (_cryptCfg & (1< 0){ + if( bit_len > 7 || ((_cryptCfg & (2<>bit_len); + tweaked_key[key_idx] |= (_cryptKey[key_idx] & (~(0xff>>bit_len)) ); + } + key_idx++; + bit_len -= 8; + } + }else{ //keep tweaked key bits + while(bit_len > 0){ + if( bit_len <8 && ((_cryptCfg & (2<>bit_len)); + tweaked_key[key_idx] |= (_cryptKey[key_idx] & (0xff>>bit_len)); + } + key_idx++; + bit_len -= 8; + } + } + pattern_idx++; + } +} + +bool UpdateClass::_decryptBuffer(){ + if(!_cryptKey){ + log_w("AES key not set"); + return false; + } + if(_bufferLen%ENCRYPTED_BLOCK_SIZE !=0 ){ + log_e("buffer size error"); + return false; + } + if(!_cryptBuffer){ + _cryptBuffer = new (std::nothrow) uint8_t[ENCRYPTED_BLOCK_SIZE]; + } + if(!_cryptBuffer){ + log_e("new failed"); + return false; + } + uint8_t tweaked_key[ENCRYPTED_KEY_SIZE]; //tweaked crypt key + int done = 0; + + esp_aes_context ctx; //initialize AES + esp_aes_init( &ctx ); + while((_bufferLen - done) >= ENCRYPTED_BLOCK_SIZE){ + for(int i=0; i < ENCRYPTED_BLOCK_SIZE; i++) _cryptBuffer[(ENCRYPTED_BLOCK_SIZE - 1) - i] = _buffer[i + done]; //reverse order 16 bytes to decrypt + if( ((_cryptAddress + _progress + done) % ENCRYPTED_TWEAK_BLOCK_SIZE) == 0 || done == 0 ){ + _cryptKeyTweak(_cryptAddress + _progress + done, tweaked_key); //update tweaked crypt key + if( esp_aes_setkey( &ctx, tweaked_key, 256 ) ){ + return false; + } + } + if( esp_aes_crypt_ecb( &ctx, ESP_AES_ENCRYPT, _cryptBuffer, _cryptBuffer ) ){ //use ESP_AES_ENCRYPT to decrypt flash code + return false; + } + for(int i=0; i < ENCRYPTED_BLOCK_SIZE; i++) _buffer[i + done] = _cryptBuffer[(ENCRYPTED_BLOCK_SIZE - 1) - i]; //reverse order 16 bytes from decrypt + done += ENCRYPTED_BLOCK_SIZE; + } + return true; +} + bool UpdateClass::_writeBuffer(){ + //first bytes of loading image, check to see if loading image needs decrypting + if(!_progress){ + _cryptMode &= U_AES_DECRYPT_MODE_MASK; + if( ( _cryptMode == U_AES_DECRYPT_ON ) + || ((_command == U_FLASH) && (_cryptMode & U_AES_DECRYPT_AUTO) && (_buffer[0] != ESP_IMAGE_HEADER_MAGIC)) + ){ + _cryptMode |= U_AES_IMAGE_DECRYPTING_BIT; //set to decrypt the loading image + log_d("Decrypting OTA Image"); + } + } + //check if data in buffer needs decrypting + if( _cryptMode & U_AES_IMAGE_DECRYPTING_BIT ){ + if( !_decryptBuffer() ){ + _abort(UPDATE_ERROR_DECRYPT); + return false; + } + } //first bytes of new firmware uint8_t skip = 0; if(!_progress && _command == U_FLASH){