-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Signed updates #3105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Signed updates #3105
Changes from 12 commits
991197c
1546732
effbb3f
0b69fa4
e5e6747
d4e81ed
60c1b5b
b819b6c
9b880f9
024ef36
68da99c
74a9b90
659c291
84b0005
6616f03
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,9 @@ UpdaterClass::UpdaterClass() | |
, _currentAddress(0) | ||
, _command(U_FLASH) | ||
{ | ||
#ifdef VERIFY_SIGNATURE | ||
_ca_ctx = (CA_CERT_CTX *)malloc(sizeof(CA_CERT_CTX)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest allocating this only when the feature gets actually used. |
||
#endif | ||
} | ||
|
||
void UpdaterClass::_reset() { | ||
|
@@ -158,8 +161,52 @@ bool UpdaterClass::end(bool evenIfRemaining){ | |
_size = progress(); | ||
} | ||
|
||
_md5.calculate(); | ||
#ifdef VERIFY_SIGNATURE | ||
// If this package has been signed correctly, the last uint32 is the size of the signature | ||
// the second-last uint32 is the size of the certificate | ||
ESP.flashRead(_startAddress + _size - sizeof(uint32_t), &_signatureLen, sizeof(uint32_t)); | ||
ESP.flashRead(_startAddress + _size - (2 * sizeof(uint32_t)), &_certificateLen, sizeof(uint32_t)); | ||
_signatureStartAddress = _startAddress + _size - (2 * sizeof(uint32_t)) - _signatureLen; | ||
_certificateStartAddress = _signatureStartAddress - _certificateLen; | ||
|
||
#ifdef DEBUG_UPDATER | ||
DEBUG_UPDATER.printf("\n"); | ||
DEBUG_UPDATER.printf("[begin] _signatureLen: 0x%08X (%d)\n", _signatureLen, _signatureLen); | ||
DEBUG_UPDATER.printf("[begin] _certificateLen: 0x%08X (%d)\n", _certificateLen, _certificateLen); | ||
DEBUG_UPDATER.printf("[begin] _signatureStartAddress: 0x%08X (%d)\n", _signatureStartAddress, _signatureStartAddress); | ||
DEBUG_UPDATER.printf("[begin] _certificateStartAddress: 0x%08X (%d)\n", _certificateStartAddress, _certificateStartAddress); | ||
#endif | ||
|
||
if(!_decryptMD5()) { | ||
#ifdef DEBUG_UPDATER | ||
DEBUG_UPDATER.printf("MD5 Decryption Failed.\n"); | ||
#endif | ||
_reset(); | ||
return false; | ||
} | ||
#endif | ||
|
||
if(_target_md5.length()) { | ||
// If there is a target MD5 hash set, we now take the md5 hash of the binary | ||
int bin_size = (int)_size; | ||
#ifdef VERIFY_SIGNATURE | ||
bin_size -= (int)(_signatureLen + _certificateLen + (2 * sizeof(uint32_t))); | ||
#endif | ||
|
||
uint8_t *bin_buffer = (uint8_t *)malloc(sizeof(uint8_t) * 32); | ||
for(int i = 0; i < bin_size; i += 32) { | ||
ESP.flashRead(_startAddress + i, (uint32_t *)bin_buffer, 32); | ||
|
||
int read = bin_size - i; | ||
if(read > 32) { | ||
read = 32; | ||
} | ||
|
||
_md5.add(bin_buffer, read); | ||
} | ||
_md5.calculate(); | ||
free(bin_buffer); | ||
|
||
if(_target_md5 != _md5.toString()){ | ||
_error = UPDATE_ERROR_MD5; | ||
#ifdef DEBUG_UPDATER | ||
|
@@ -219,7 +266,6 @@ bool UpdaterClass::_writeBuffer(){ | |
#endif | ||
return false; | ||
} | ||
_md5.add(_buffer, _bufferLen); | ||
_currentAddress += _bufferLen; | ||
_bufferLen = 0; | ||
return true; | ||
|
@@ -348,6 +394,120 @@ size_t UpdaterClass::writeStream(Stream &data) { | |
return written; | ||
} | ||
|
||
#ifdef VERIFY_SIGNATURE | ||
int UpdaterClass::addCA(const uint8_t *cert, int *len) { | ||
// TODO: Allow more than one CA | ||
int res = x509_new(cert, len, &(_ca_ctx->cert[0])); | ||
|
||
#ifdef DEBUG_UPDATER | ||
if(res == X509_OK) { | ||
DEBUG_UPDATER.printf("Loaded CA certificate. Common Name: %s\n", _ca_ctx->cert[0]->cert_dn[X509_COMMON_NAME]); | ||
} else { | ||
DEBUG_UPDATER.printf("Unable to load CA certificate: %i\n", res); | ||
} | ||
#endif | ||
|
||
return res; | ||
} | ||
|
||
bool UpdaterClass::_loadCertificate(X509_CTX **ctx) { | ||
size_t num_of_bits = sizeof(uint8_t) * _certificateLen; | ||
uint8_t *cert = (uint8_t *)malloc(num_of_bits + (num_of_bits % 32)); // Round up to the next uint32_t boundary | ||
ESP.flashRead(_certificateStartAddress, (uint32_t *)cert, num_of_bits); | ||
|
||
int res = x509_new(cert, (int *)&_certificateLen, ctx); | ||
free(cert); | ||
|
||
if(res != X509_OK) { | ||
#ifdef DEBUG_UPDATER | ||
DEBUG_UPDATER.printf("Unable to load developer certificate: %i\n", res); | ||
#endif | ||
return false; | ||
} | ||
#ifdef DEBUG_UPDATER | ||
DEBUG_UPDATER.printf("Loaded developer certificate. Common Name: %s\n", (*ctx)->cert_dn[X509_COMMON_NAME]); | ||
#endif | ||
return true; | ||
} | ||
|
||
bool UpdaterClass::_verifyCertificate(X509_CTX **ctx) { | ||
int res = x509_verify(_ca_ctx, *ctx); | ||
|
||
#ifdef DEBUG_UPDATER | ||
if(res == X509_OK) { | ||
DEBUG_UPDATER.printf("Developer certificate verified\n"); | ||
} else { | ||
DEBUG_UPDATER.printf("Developer certificate not verified: %i\n", res); | ||
} | ||
#endif | ||
|
||
return res == X509_OK; | ||
} | ||
|
||
bool UpdaterClass::_decryptSignature(X509_CTX **ctx, char **hash) { | ||
size_t num_of_bits = sizeof(uint8_t) * _signatureLen; | ||
uint8_t *sig = (uint8_t *)malloc(num_of_bits + (num_of_bits % 32)); // Round up to the next uint32_t boundary | ||
ESP.flashRead(_signatureStartAddress, (uint32_t *)sig, num_of_bits); | ||
|
||
//const int signature_size = (*ctx)->rsa_ctx->num_octets; | ||
const int signature_size = _signatureLen; // In this version of the library, the num_octects is 0. Not sure why... | ||
#ifdef DEBUG_UPDATER | ||
DEBUG_UPDATER.printf("Size of output buffer: %i\n", signature_size); | ||
#endif | ||
uint8_t sig_data[signature_size]; | ||
int len = RSA_decrypt((*ctx)->rsa_ctx, (const uint8_t *)sig, sig_data, signature_size, 0); | ||
free(sig); | ||
|
||
if(len == -1) { | ||
#ifdef DEBUG_UPDATER | ||
DEBUG_UPDATER.printf("Decryption failed\n"); | ||
#endif | ||
return false; | ||
} | ||
|
||
if(len < MD5_SIZE) { | ||
#ifdef DEBUG_UPDATER | ||
DEBUG_UPDATER.printf("Decryption failed: Signature is too short. Expected %i, got %i\n", MD5_SIZE, len); | ||
#endif | ||
return false; | ||
} | ||
|
||
#ifdef DEBUG_UPDATER | ||
DEBUG_UPDATER.printf("Decryption successful.\n"); | ||
#endif | ||
|
||
// Fetch the last part of the encrypted string - that is the MD5 hash, and then save the string | ||
// version of it in to the hash pointer. | ||
(*hash) = (char *)calloc((MD5_SIZE * 2) + 1, sizeof(char)); | ||
for(int i = 0; i < MD5_SIZE; i++) { | ||
sprintf(*hash + (i * 2), "%02x", sig_data[len - MD5_SIZE + i]); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
bool UpdaterClass::_decryptMD5() { | ||
X509_CTX *ctx; | ||
|
||
if(!_loadCertificate(&ctx)) { | ||
return false; | ||
} | ||
|
||
if(!_verifyCertificate(&ctx)) { | ||
return false; | ||
} | ||
|
||
char *hash; | ||
if(!_decryptSignature(&ctx, &hash)) { | ||
return false; | ||
} | ||
|
||
setMD5((const char *)hash); | ||
|
||
return true; | ||
} | ||
#endif | ||
|
||
void UpdaterClass::printError(Stream &out){ | ||
out.printf("ERROR[%u]: ", _error); | ||
if(_error == UPDATE_ERROR_OK){ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,17 @@ | ||
#ifndef ESP8266UPDATER_H | ||
#define ESP8266UPDATER_H | ||
|
||
#define VERIFY_SIGNATURE 1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Posted a note about using preprocessor macro as a feature switch above. |
||
|
||
#include <Arduino.h> | ||
#include <flash_utils.h> | ||
#include <MD5Builder.h> | ||
|
||
#ifdef VERIFY_SIGNATURE | ||
#include <ssl/crypto_misc.h> | ||
#include <crypto/crypto.h> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need these headers in user-facing header file? |
||
#endif | ||
|
||
#define UPDATE_ERROR_OK (0) | ||
#define UPDATE_ERROR_WRITE (1) | ||
#define UPDATE_ERROR_ERASE (2) | ||
|
@@ -141,18 +148,35 @@ class UpdaterClass { | |
return written; | ||
} | ||
|
||
#ifdef VERIFY_SIGNATURE | ||
int addCA(const uint8_t *cert, int *len); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to |
||
#endif | ||
|
||
private: | ||
void _reset(); | ||
bool _writeBuffer(); | ||
|
||
bool _verifyHeader(uint8_t data); | ||
bool _verifyEnd(); | ||
|
||
#ifdef VERIFY_SIGNATURE | ||
CA_CERT_CTX *_ca_ctx; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, i see now why the headers were needed. If you go for the approach with |
||
bool _loadCertificate(X509_CTX **ctx); | ||
bool _verifyCertificate(X509_CTX **ctx); | ||
bool _decryptSignature(X509_CTX **ctx, char **hash); | ||
bool _decryptMD5(); | ||
|
||
uint32_t _certificateStartAddress; | ||
uint32_t _certificateLen; | ||
uint32_t _signatureStartAddress; | ||
uint32_t _signatureLen; | ||
#endif | ||
|
||
bool _async; | ||
uint8_t _error; | ||
uint8_t *_buffer; | ||
size_t _bufferLen; | ||
size_t _size; | ||
uint32_t _bufferLen; | ||
uint32_t _size; | ||
uint32_t _startAddress; | ||
uint32_t _currentAddress; | ||
uint32_t _command; | ||
|
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise, suggest keeping ROM functions for performance reasons.