[libs] Implement Update MD5
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-03. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
unsigned long total[2]; /*!< number of bytes processed */
|
||||
unsigned long state[4]; /*!< intermediate digest state */
|
||||
unsigned char buffer[64]; /*!< data block being processed */
|
||||
} md5_context;
|
||||
|
||||
#define LT_MD5_CTX_T md5_context
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -61,6 +61,10 @@ bool UpdateClass::begin(
|
||||
lt_ota_begin(this->ctx, size);
|
||||
this->ctx->callback = reinterpret_cast<void (*)(void *)>(progressHandler);
|
||||
this->ctx->callback_param = this;
|
||||
|
||||
this->md5Ctx = static_cast<LT_MD5_CTX_T *>(malloc(sizeof(LT_MD5_CTX_T)));
|
||||
MD5Init(this->md5Ctx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -79,6 +83,9 @@ bool UpdateClass::end(bool evenIfRemaining) {
|
||||
// abort if not finished
|
||||
this->errArd = UPDATE_ERROR_ABORT;
|
||||
|
||||
this->md5Digest = static_cast<uint8_t *>(malloc(16));
|
||||
MD5Final(this->md5Digest, this->md5Ctx);
|
||||
|
||||
this->cleanup(/* clearError= */ evenIfRemaining);
|
||||
return !this->hasError();
|
||||
}
|
||||
@@ -97,6 +104,10 @@ void UpdateClass::cleanup(bool clearError) {
|
||||
// activating firmware failed
|
||||
this->errArd = UPDATE_ERROR_ACTIVATE;
|
||||
this->errUf2 = UF2_ERR_OK;
|
||||
} else if (this->md5Digest && this->md5Expected && memcmp(this->md5Digest, this->md5Expected, 16) != 0) {
|
||||
// MD5 doesn't match
|
||||
this->errArd = UPDATE_ERROR_MD5;
|
||||
this->errUf2 = UF2_ERR_OK;
|
||||
} else if (clearError) {
|
||||
// successful finish and activation, clear error codes
|
||||
this->clearError();
|
||||
@@ -116,6 +127,12 @@ void UpdateClass::cleanup(bool clearError) {
|
||||
|
||||
free(this->ctx);
|
||||
this->ctx = nullptr;
|
||||
free(this->md5Ctx);
|
||||
this->md5Ctx = nullptr;
|
||||
free(this->md5Digest);
|
||||
this->md5Digest = nullptr;
|
||||
free(this->md5Expected);
|
||||
this->md5Expected = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,6 +149,7 @@ size_t UpdateClass::write(const uint8_t *data, size_t len) {
|
||||
return 0;
|
||||
|
||||
size_t written = lt_ota_write(ctx, data, len);
|
||||
MD5Update(this->md5Ctx, data, len);
|
||||
if (written != len)
|
||||
this->cleanup(/* clearError= */ false);
|
||||
return written;
|
||||
@@ -171,6 +189,8 @@ size_t UpdateClass::writeStream(Stream &data) {
|
||||
// read data to fit in the remaining buffer space
|
||||
auto bufSize = this->ctx->buf_pos - this->ctx->buf;
|
||||
auto read = data.readBytes(this->ctx->buf_pos, UF2_BLOCK_SIZE - bufSize);
|
||||
// update MD5
|
||||
MD5Update(this->md5Ctx, this->ctx->buf_pos, read);
|
||||
// increment buffer writing head
|
||||
this->ctx->buf_pos += read;
|
||||
// process the block if complete
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <MD5.h>
|
||||
#include <functional>
|
||||
#include <uf2ota/uf2ota.h>
|
||||
|
||||
@@ -56,6 +57,9 @@ class UpdateClass {
|
||||
UpdateClass &onProgress(THandlerFunction_Progress handler);
|
||||
static bool canRollBack();
|
||||
static bool rollBack();
|
||||
bool setMD5(const char *md5);
|
||||
String md5String();
|
||||
void md5(uint8_t *result);
|
||||
uint16_t getErrorCode() const;
|
||||
bool hasError() const;
|
||||
void clearError();
|
||||
@@ -71,6 +75,9 @@ class UpdateClass {
|
||||
uf2_err_t errUf2{UF2_ERR_OK};
|
||||
UpdateError errArd{UPDATE_ERROR_OK};
|
||||
THandlerFunction_Progress callback{nullptr};
|
||||
LT_MD5_CTX_T *md5Ctx{nullptr};
|
||||
uint8_t *md5Digest{nullptr};
|
||||
uint8_t *md5Expected{nullptr};
|
||||
|
||||
public:
|
||||
/**
|
||||
|
||||
@@ -71,6 +71,41 @@ bool UpdateClass::rollBack() {
|
||||
return lt_ota_switch(/* revert= */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the expected MD5 of the firmware (hexadecimal string).
|
||||
*/
|
||||
bool UpdateClass::setMD5(const char *md5) {
|
||||
if (strlen(md5) != 32)
|
||||
return false;
|
||||
this->md5Expected = static_cast<uint8_t *>(malloc(16));
|
||||
if (!this->md5Expected)
|
||||
return false;
|
||||
lt_xtob(md5, 32, this->md5Expected);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return a hexadecimal string of calculated firmware MD5 sum.
|
||||
*/
|
||||
String UpdateClass::md5String() {
|
||||
if (!this->md5Digest)
|
||||
return "";
|
||||
char out[32 + 1];
|
||||
lt_btox(this->md5Digest, 16, out);
|
||||
return String(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get calculated MD5 digest of the firmware.
|
||||
*/
|
||||
void UpdateClass::md5(uint8_t *result) {
|
||||
if (!this->md5Digest) {
|
||||
memset(result, '\0', 16);
|
||||
return;
|
||||
}
|
||||
memcpy(result, this->md5Digest, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get combined error code of the update.
|
||||
*/
|
||||
|
||||
@@ -39,3 +39,36 @@ void hexdump(const uint8_t *buf, size_t len, uint32_t offset, uint8_t width) {
|
||||
pos += lineWidth;
|
||||
}
|
||||
}
|
||||
|
||||
char *lt_btox(const uint8_t *src, int len, char *dest) {
|
||||
// https://stackoverflow.com/a/53966346
|
||||
const char hex[] = "0123456789abcdef";
|
||||
len *= 2;
|
||||
dest[len] = '\0';
|
||||
while (--len >= 0)
|
||||
dest[len] = hex[(src[len >> 1] >> ((1 - (len & 1)) << 2)) & 0xF];
|
||||
return dest;
|
||||
}
|
||||
|
||||
uint8_t *lt_xtob(const char *src, int len, uint8_t *dest) {
|
||||
// https://gist.github.com/vi/dd3b5569af8a26b97c8e20ae06e804cb
|
||||
|
||||
// mapping of ASCII characters to hex values
|
||||
// (16-byte swapped to reduce XOR 0x10 operation)
|
||||
const uint8_t mapping[] = {
|
||||
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // @ABCDEFG
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // HIJKLMNO
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
|
||||
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
|
||||
};
|
||||
|
||||
int j = 0;
|
||||
uint8_t idx0;
|
||||
uint8_t idx1;
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
idx0 = ((uint8_t)src[i + 0] & 0x1F);
|
||||
idx1 = ((uint8_t)src[i + 1] & 0x1F);
|
||||
dest[j++] = (mapping[idx0] << 4) | (mapping[idx1] << 0);
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
@@ -45,3 +45,23 @@ void hexdump(
|
||||
uint8_t width
|
||||
#endif
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Convert a byte array to hexadecimal string.
|
||||
*
|
||||
* @param src source byte array
|
||||
* @param len source length (bytes)
|
||||
* @param dest destination string
|
||||
* @return destination string
|
||||
*/
|
||||
char *lt_btox(const uint8_t *src, int len, char *dest);
|
||||
|
||||
/**
|
||||
* @brief Convert a hexadecimal string to byte array.
|
||||
*
|
||||
* @param src source string
|
||||
* @param len source length (chars)
|
||||
* @param dest destination byte array
|
||||
* @return destination byte array
|
||||
*/
|
||||
uint8_t *lt_xtob(const char *src, int len, uint8_t *dest);
|
||||
|
||||
Reference in New Issue
Block a user