Skip to content

Commit

Permalink
Move SHA256 code into OTA files
Browse files Browse the repository at this point in the history
  • Loading branch information
pennam committed Jan 19, 2023
1 parent 30e0f5c commit 1ab8b3f
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 129 deletions.
145 changes: 21 additions & 124 deletions src/ArduinoIoTCloudTCP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#ifdef HAS_TCP
#include <ArduinoIoTCloudTCP.h>

#ifdef BOARD_HAS_ECCX08
#include "tls/BearSSLTrustAnchors.h"
#include "tls/utility/CryptoUtil.h"
Expand All @@ -34,45 +35,18 @@
#endif

#ifdef BOARD_HAS_OFFLOADED_ECCX08
#include <ArduinoECCX08.h>
#include "tls/utility/CryptoUtil.h"
#endif

#ifdef BOARD_STM32H7
# include "tls/utility/SHA256.h"
# include <stm32h7xx_hal_rtc_ex.h>
# include <WiFi.h>
#endif

#if defined (ARDUINO_ARCH_ESP32) && OTA_ENABLED
# include "tls/utility/SHA256.h"
#include "esp_spi_flash.h"
#include "esp_ota_ops.h"
#include "esp_image_format.h"
#endif

#if defined (ARDUINO_NANO_RP2040_CONNECT) || \
(defined (ARDUINO_ARCH_SAMD) && OTA_ENABLED)
#include "utility/ota/FlashSHA256.h"
#include <ArduinoECCX08.h>
#include "tls/utility/CryptoUtil.h"
#endif

#if OTA_ENABLED
#include "utility/ota/OTA.h"
#include "utility/ota/OTA.h"
#endif

#include <algorithm>
#include "cbor/CBOREncoder.h"

#include "utility/watchdog/Watchdog.h"

/******************************************************************************
* EXTERN
******************************************************************************/

#ifdef BOARD_STM32H7
extern RTC_HandleTypeDef RTCHandle;
#endif

/******************************************************************************
LOCAL MODULE FUNCTIONS
******************************************************************************/
Expand Down Expand Up @@ -164,100 +138,8 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress,
#endif /* AVR */

#if OTA_ENABLED && !defined(__AVR__)
#if defined(BOARD_STM32H7)
/* The length of the application can be retrieved the same way it was
* communicated to the bootloader, that is by writing to the non-volatile
* storage registers of the RTC.
*/
SHA256 sha256;
uint32_t const app_start = 0x8040000;
uint32_t const app_size = HAL_RTCEx_BKUPRead(&RTCHandle, RTC_BKP_DR3);

sha256.begin();
uint32_t b = 0;
uint32_t bytes_read = 0; for(uint32_t a = app_start;
bytes_read < app_size;
bytes_read += sizeof(b), a += sizeof(b))
{
/* Read the next chunk of memory. */
memcpy(&b, reinterpret_cast<const void *>(a), sizeof(b));
/* Feed it to SHA256. */
sha256.update(reinterpret_cast<uint8_t *>(&b), sizeof(b));
}
/* Retrieve the final hash string. */
uint8_t sha256_hash[SHA256::HASH_SIZE] = {0};
sha256.finalize(sha256_hash);
String sha256_str;
std::for_each(sha256_hash,
sha256_hash + SHA256::HASH_SIZE,
[&sha256_str](uint8_t const elem)
{
char buf[4];
snprintf(buf, 4, "%02X", elem);
sha256_str += buf;
});
DEBUG_VERBOSE("SHA256: %d bytes (of %d) read", bytes_read, app_size);
#elif defined(ARDUINO_ARCH_SAMD)
/* Calculate the SHA256 checksum over the firmware stored in the flash of the
* MCU. Note: As we don't know the length per-se we read chunks of the flash
* until we detect one containing only 0xFF (= flash erased). This only works
* for firmware updated via OTA and second stage bootloaders (SxU family)
* because only those erase the complete flash before performing an update.
* Since the SHA256 firmware image is only required for the cloud servers to
* perform a version check after the OTA update this is a acceptable trade off.
* The bootloader is excluded from the calculation and occupies flash address
* range 0 to 0x2000, total flash size of 0x40000 bytes (256 kByte).
*/
String const sha256_str = FlashSHA256::calc(0x2000, 0x40000 - 0x2000);
#elif defined(ARDUINO_NANO_RP2040_CONNECT)
/* The maximum size of a RP2040 OTA update image is 1 MByte (that is 1024 *
* 1024 bytes or 0x100'000 bytes).
*/
String const sha256_str = FlashSHA256::calc(XIP_BASE, 0x100000);
#elif defined(ARDUINO_ARCH_ESP32)
SHA256 sha256;

uint32_t lengthLeft = ESP.getSketchSize();

const esp_partition_t *running = esp_ota_get_running_partition();
if (!running) {
DEBUG_ERROR("Partition could not be found");
}
const size_t bufSize = SPI_FLASH_SEC_SIZE;
std::unique_ptr<uint8_t[]> buf(new uint8_t[bufSize]);
uint32_t offset = 0;
if(!buf.get()) {
DEBUG_ERROR("Not enough memory to allocate buffer");
}

sha256.begin();
while( lengthLeft > 0) {
size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize;
if (!ESP.flashRead(running->address + offset, reinterpret_cast<uint32_t*>(buf.get()), (readBytes + 3) & ~3)) {
DEBUG_ERROR("Could not read buffer from flash");
}
sha256.update(buf.get(), readBytes);
lengthLeft -= readBytes;
offset += readBytes;
}
/* Retrieve the final hash string. */
uint8_t sha256_hash[SHA256::HASH_SIZE] = {0};
sha256.finalize(sha256_hash);
String sha256_str;
std::for_each(sha256_hash,
sha256_hash + SHA256::HASH_SIZE,
[&sha256_str](uint8_t const elem)
{
char buf[4];
snprintf(buf, 4, "%02X", elem);
sha256_str += buf;
});
DEBUG_VERBOSE("SHA256: %d bytes (of %d) read", ESP.getSketchSize() - lengthLeft, ESP.getSketchSize());
#else
# error "No method for SHA256 checksum calculation over application image defined for this architecture."
#endif
DEBUG_VERBOSE("SHA256: HASH(%d) = %s", strlen(sha256_str.c_str()), sha256_str.c_str());
_ota_img_sha256 = sha256_str;
_ota_img_sha256 = getOTAImageSHA256();
DEBUG_VERBOSE("SHA256: HASH(%d) = %s", strlen(_ota_img_sha256.c_str()), _ota_img_sha256.c_str());
#endif /* OTA_ENABLED */

#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050)
Expand Down Expand Up @@ -902,6 +784,21 @@ void ArduinoIoTCloudTCP::onOTARequest()
_ota_error = esp32_onOTARequest(_ota_url.c_str());
#endif
}

String ArduinoIoTCloudTCP::getOTAImageSHA256()
{
#if defined (ARDUINO_ARCH_SAMD)
return samd_getOTAImageSHA256();
#elif defined (ARDUINO_NANO_RP2040_CONNECT)
return rp2040_connect_getOTAImageSHA256();
#elif defined (BOARD_STM32H7)
return portenta_h7_getOTAImageSHA256();
#elif defined (ARDUINO_ARCH_ESP32)
return esp32_getOTAImageSHA256();
#else
# error "No method for SHA256 checksum calculation over application image defined for this architecture."
#endif
}
#endif

void ArduinoIoTCloudTCP::updateThingTopics()
Expand Down
1 change: 1 addition & 0 deletions src/ArduinoIoTCloudTCP.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass

#if OTA_ENABLED
void onOTARequest();
String getOTAImageSHA256();
void sendDevicePropertyToCloud(String const name);
#endif

Expand Down
49 changes: 48 additions & 1 deletion src/utility/ota/OTA-esp32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@
#include <AIoTC_Config.h>

#if defined ARDUINO_ARCH_ESP32 && OTA_ENABLED

#include "OTA.h"
#include <Arduino_DebugUtils.h>
#include <Arduino_ESP_OTA.h>
#include <Arduino_ESP32_OTA.h>
#include "tls/utility/SHA256.h"

#include <esp_ota_ops.h>

/******************************************************************************
* FUNCTION DEFINITION
Expand Down Expand Up @@ -64,4 +68,47 @@ int esp32_onOTARequest(char const * ota_url)
return static_cast<int>(OTAError::None);
}

String esp32_getOTAImageSHA256()
{
SHA256 sha256;

uint32_t lengthLeft = ESP.getSketchSize();

const esp_partition_t *running = esp_ota_get_running_partition();
if (!running) {
DEBUG_ERROR("Partition could not be found");
}
const size_t bufSize = SPI_FLASH_SEC_SIZE;
std::unique_ptr<uint8_t[]> buf(new uint8_t[bufSize]);
uint32_t offset = 0;
if(!buf.get()) {
DEBUG_ERROR("Not enough memory to allocate buffer");
}

sha256.begin();
while( lengthLeft > 0) {
size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize;
if (!ESP.flashRead(running->address + offset, reinterpret_cast<uint32_t*>(buf.get()), (readBytes + 3) & ~3)) {
DEBUG_ERROR("Could not read buffer from flash");
}
sha256.update(buf.get(), readBytes);
lengthLeft -= readBytes;
offset += readBytes;
}
/* Retrieve the final hash string. */
uint8_t sha256_hash[SHA256::HASH_SIZE] = {0};
sha256.finalize(sha256_hash);
String sha256_str;
std::for_each(sha256_hash,
sha256_hash + SHA256::HASH_SIZE,
[&sha256_str](uint8_t const elem)
{
char buf[4];
snprintf(buf, 4, "%02X", elem);
sha256_str += buf;
});
DEBUG_VERBOSE("SHA256: %d bytes (of %d) read", ESP.getSketchSize() - lengthLeft, ESP.getSketchSize());
return sha256_str;
}

#endif /* ARDUINO_ARCH_ESP32 */
9 changes: 9 additions & 0 deletions src/utility/ota/OTA-nano-rp2040.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "mbed.h"
#include "FATFileSystem.h"
#include "FlashIAPBlockDevice.h"
#include "utility/ota/FlashSHA256.h"

/******************************************************************************
* FUNCTION DEFINITION
Expand Down Expand Up @@ -249,4 +250,12 @@ int rp2040_connect_onOTARequest(char const * ota_url)
return static_cast<int>(OTAError::None);
}

String rp2040_connect_getOTAImageSHA256()
{
/* The maximum size of a RP2040 OTA update image is 1 MByte (that is 1024 *
* 1024 bytes or 0x100'000 bytes).
*/
return FlashSHA256::calc(XIP_BASE, 0x100000);
}

#endif /* ARDUINO_NANO_RP2040_CONNECT */
47 changes: 47 additions & 0 deletions src/utility/ota/OTA-portenta-h7.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,18 @@
#include <Arduino_Portenta_OTA.h>
#include <Arduino_ConnectionHandler.h>

#include <stm32h7xx_hal_rtc_ex.h>

#include "tls/utility/SHA256.h"

#include "../watchdog/Watchdog.h"

/******************************************************************************
* EXTERN
******************************************************************************/

extern RTC_HandleTypeDef RTCHandle;

/******************************************************************************
* FUNCTION DEFINITION
******************************************************************************/
Expand Down Expand Up @@ -96,4 +106,41 @@ int portenta_h7_onOTARequest(char const * ota_url, const bool use_ethernet)
NVIC_SystemReset();
}

String portenta_h7_getOTAImageSHA256()
{
/* The length of the application can be retrieved the same way it was
* communicated to the bootloader, that is by writing to the non-volatile
* storage registers of the RTC.
*/
SHA256 sha256;
uint32_t const app_start = 0x8040000;
uint32_t const app_size = HAL_RTCEx_BKUPRead(&RTCHandle, RTC_BKP_DR3);

sha256.begin();
uint32_t b = 0;
uint32_t bytes_read = 0; for(uint32_t a = app_start;
bytes_read < app_size;
bytes_read += sizeof(b), a += sizeof(b))
{
/* Read the next chunk of memory. */
memcpy(&b, reinterpret_cast<const void *>(a), sizeof(b));
/* Feed it to SHA256. */
sha256.update(reinterpret_cast<uint8_t *>(&b), sizeof(b));
}
/* Retrieve the final hash string. */
uint8_t sha256_hash[SHA256::HASH_SIZE] = {0};
sha256.finalize(sha256_hash);
String sha256_str;
std::for_each(sha256_hash,
sha256_hash + SHA256::HASH_SIZE,
[&sha256_str](uint8_t const elem)
{
char buf[4];
snprintf(buf, 4, "%02X", elem);
sha256_str += buf;
});
DEBUG_VERBOSE("SHA256: %d bytes (of %d) read", bytes_read, app_size);
return sha256_str;
}

#endif /* BOARD_STM32H7 */
24 changes: 20 additions & 4 deletions src/utility/ota/OTA-samd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@
a commercial license, send an email to [email protected].
*/

#ifdef ARDUINO_ARCH_SAMD

/******************************************************************************
* INCLUDE
******************************************************************************/

#include "OTA.h"
#include <AIoTC_Config.h>

#include <Arduino_DebugUtils.h>
#if defined (ARDUINO_ARCH_SAMD) && OTA_ENABLED

#include "OTA.h"
#include <Arduino_DebugUtils.h>
#include "../watchdog/Watchdog.h"
#include "utility/ota/FlashSHA256.h"

#if OTA_STORAGE_SNU
# include <SNU.h>
Expand Down Expand Up @@ -65,4 +66,19 @@ int samd_onOTARequest(char const * ota_url)
return static_cast<int>(OTAError::DownloadFailed);
}

String samd_getOTAImageSHA256()
{
/* Calculate the SHA256 checksum over the firmware stored in the flash of the
* MCU. Note: As we don't know the length per-se we read chunks of the flash
* until we detect one containing only 0xFF (= flash erased). This only works
* for firmware updated via OTA and second stage bootloaders (SxU family)
* because only those erase the complete flash before performing an update.
* Since the SHA256 firmware image is only required for the cloud servers to
* perform a version check after the OTA update this is a acceptable trade off.
* The bootloader is excluded from the calculation and occupies flash address
* range 0 to 0x2000, total flash size of 0x40000 bytes (256 kByte).
*/
return FlashSHA256::calc(0x2000, 0x40000 - 0x2000);
}

#endif /* ARDUINO_ARCH_SAMD */
Loading

0 comments on commit 1ab8b3f

Please sign in to comment.