Skip to content

Commit 71bf243

Browse files
Add cryptographically signed update support
Using a pluggable architecture, allow updates delivered via the Update class to be verified as signed by a certificate. By using plugins, avoid pulling either axTLS or BearSSL into normal builds. A signature is appended to a binary image, followed by the size of the signature as a 32-bit int. The updater takes a verification function and checks this signature using whatever method it chooses, and if it fails the update is not applied. A SHA256 hash class is presently implemented for the signing hash (since MD5 is a busted algorithm). A BearSSLPublicKey based verifier is implemented for RSA keys. The application only needs the Public Key, while to sign you can use OpenSSL and your private key (which should never leave your control or be deployed on any endpoints).
1 parent b21619c commit 71bf243

File tree

4 files changed

+151
-0
lines changed

4 files changed

+151
-0
lines changed

cores/esp8266/Updater.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ UpdaterClass::UpdaterClass()
2323
, _startAddress(0)
2424
, _currentAddress(0)
2525
, _command(U_FLASH)
26+
, _hash(nullptr)
27+
, _verify(nullptr)
2628
{
2729
}
2830

@@ -140,6 +142,9 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
140142
#endif
141143

142144
_md5.begin();
145+
if (_hash) {
146+
_hash->begin();
147+
}
143148
return true;
144149
}
145150

@@ -176,6 +181,42 @@ bool UpdaterClass::end(bool evenIfRemaining){
176181
_size = progress();
177182
}
178183

184+
185+
uint32_t sigLen = 0;
186+
if (_verify) {
187+
ESP.flashRead(_startAddress + _size - sizeof(uint32_t), &sigLen, sizeof(uint32_t));
188+
#ifdef DEBUG_UPDATER
189+
DEBUG_UPDATER.printf("[Updater] sigLen: %d\n", sigLen);
190+
#endif
191+
if (!sigLen || sigLen > 256) {
192+
_setError(UPDATE_ERROR_SIGN);
193+
_reset();
194+
return false;
195+
}
196+
}
197+
198+
int binSize = _size;
199+
if (_hash) {
200+
_hash->begin();
201+
binSize -= sigLen + sizeof(uint32_t);
202+
}
203+
#ifdef DEBUG_UPDATER
204+
DEBUG_UPDATER.printf("[Updater] Adjusted binsize: %d\n", binSize);
205+
#endif
206+
// Calculate the MD5 and hash using proper size
207+
uint8_t buff[32];
208+
for(int i = 0; i < binSize; i += 32) {
209+
ESP.flashRead(_startAddress + i, (uint32_t *)buff, 32);
210+
211+
int read = binSize - i;
212+
if(read > 32) {
213+
read = 32;
214+
}
215+
_md5.add(buff, read);
216+
if (_hash) {
217+
_hash->add(buff, read);
218+
}
219+
}
179220
_md5.calculate();
180221
if(_target_md5.length()) {
181222
if(_target_md5 != _md5.toString()){
@@ -188,6 +229,29 @@ bool UpdaterClass::end(bool evenIfRemaining){
188229
#endif
189230
}
190231

232+
if (_verify && _hash) {
233+
_hash->end();
234+
#ifdef DEBUG_UPDATER
235+
unsigned char *ret = (unsigned char *)_hash->hash();
236+
DEBUG_UPDATER.printf("[Updater] Computed Hash:");
237+
for (int i=0; i<_hash->len(); i++) DEBUG_UPDATER.printf(" %02x", ret[i]);
238+
DEBUG_UPDATER.printf("\n");
239+
#endif
240+
uint8_t sig[256];
241+
ESP.flashRead(_startAddress + binSize, (uint32_t *)sig, sigLen);
242+
#ifdef DEBUG_UPDATER
243+
DEBUG_UPDATER.printf("[Updater] sig[]:");
244+
for (size_t i=0; i<sigLen; i++) {
245+
DEBUG_UPDATER.printf(" %02x", sig[i]);
246+
}
247+
DEBUG_UPDATER.printf("\n");
248+
#endif
249+
if (!_verify->verify(_hash, (void *)sig, sigLen)) {
250+
_setError(UPDATE_ERROR_SIGN);
251+
return false;
252+
}
253+
}
254+
191255
if(!_verifyEnd()) {
192256
_reset();
193257
return false;
@@ -266,6 +330,9 @@ bool UpdaterClass::_writeBuffer(){
266330
return false;
267331
}
268332
_md5.add(_buffer, _bufferLen);
333+
if (_hash) {
334+
_hash->add(_buffer, _bufferLen);
335+
}
269336
_currentAddress += _bufferLen;
270337
_bufferLen = 0;
271338
return true;
@@ -431,6 +498,8 @@ void UpdaterClass::printError(Print &out){
431498
} else if(_error == UPDATE_ERROR_MD5){
432499
//out.println(F("MD5 Check Failed"));
433500
out.printf("MD5 Failed: expected:%s, calculated:%s\n", _target_md5.c_str(), _md5.toString().c_str());
501+
} else if(_error == UPDATE_ERROR_SIGN){
502+
out.printf("Signature verification failed\n");
434503
} else if(_error == UPDATE_ERROR_FLASH_CONFIG){
435504
out.printf_P(PSTR("Flash config wrong real: %d IDE: %d\n"), ESP.getFlashChipRealSize(), ESP.getFlashChipSize());
436505
} else if(_error == UPDATE_ERROR_NEW_FLASH_CONFIG){

cores/esp8266/Updater.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define UPDATE_ERROR_NEW_FLASH_CONFIG (9)
1818
#define UPDATE_ERROR_MAGIC_BYTE (10)
1919
#define UPDATE_ERROR_BOOTSTRAP (11)
20+
#define UPDATE_ERROR_SIGN (12)
2021

2122
#define U_FLASH 0
2223
#define U_SPIFFS 100
@@ -28,9 +29,27 @@
2829
#endif
2930
#endif
3031

32+
class UpdaterHashClass {
33+
public:
34+
virtual void begin() = 0;
35+
virtual void add(const void *data, uint32_t len) = 0;
36+
virtual void end() = 0;
37+
virtual int len() = 0;
38+
virtual void *hash() = 0;
39+
};
40+
41+
class UpdaterVerifyClass {
42+
public:
43+
virtual bool verify(UpdaterHashClass *hash, void *signature, uint32_t signatureLen) = 0;
44+
};
45+
3146
class UpdaterClass {
3247
public:
3348
UpdaterClass();
49+
50+
/* Optionally add a cryptographic signature verification hash and method */
51+
void installSignature(UpdaterHashClass *hash, UpdaterVerifyClass *verify) { _hash = hash; _verify = verify; }
52+
3453
/*
3554
Call this to check the space needed for the update
3655
Will return false if there is not enough space
@@ -166,6 +185,9 @@ class UpdaterClass {
166185
int _ledPin;
167186
uint8_t _ledOn;
168187
int _ledStateRestore;
188+
189+
UpdaterHashClass *_hash;
190+
UpdaterVerifyClass *_verify;
169191
};
170192

171193
extern UpdaterClass Update;

libraries/ESP8266WiFi/src/BearSSLHelpers.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,3 +819,36 @@ bool BearSSLX509List::append(const uint8_t *derCert, size_t derLen) {
819819

820820
return true;
821821
}
822+
823+
824+
// SHA256 hash for updater
825+
void BearSSLHashSHA256::begin() {
826+
br_sha256_init( &_cc );
827+
memset( _sha256, 0, sizeof(_sha256) );
828+
}
829+
830+
void BearSSLHashSHA256::add(const void *data, uint32_t len) {
831+
br_sha256_update( &_cc, data, len );
832+
}
833+
834+
void BearSSLHashSHA256::end() {
835+
br_sha256_out( &_cc, _sha256 );
836+
}
837+
838+
int BearSSLHashSHA256::len() {
839+
return sizeof(_sha256);
840+
}
841+
842+
void *BearSSLHashSHA256::hash() {
843+
return (void*) _sha256;
844+
}
845+
846+
// SHA256 verifier
847+
bool BearSSLVerifier::verify(UpdaterHashClass *hash, void *signature, uint32_t signatureLen) {
848+
if (!_pubKey || !hash || !signature || signatureLen != 256) return false;
849+
br_rsa_pkcs1_vrfy vrfy = br_rsa_pkcs1_vrfy_get_default();
850+
unsigned char vrf[32];
851+
bool ret = vrfy((const unsigned char *)signature, signatureLen, NULL, sizeof(vrf), _pubKey->getRSA(), vrf);
852+
if (!ret) return false;
853+
return !memcmp(vrf, hash->hash(), sizeof(vrf));
854+
};

libraries/ESP8266WiFi/src/BearSSLHelpers.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#define _BEARSSLHELPERS_H
2525

2626
#include <bearssl/bearssl.h>
27+
#include <Updater.h>
28+
2729

2830
// Internal opaque structures, not needed by user applications
2931
namespace brssl {
@@ -136,4 +138,29 @@ class BearSSLSession {
136138
br_ssl_session_parameters _session;
137139
};
138140

141+
142+
// Updater SHA256 hash and signature verification
143+
class BearSSLHashSHA256 : public UpdaterHashClass {
144+
public:
145+
virtual void begin() override;
146+
virtual void add(const void *data, uint32_t len) override;
147+
virtual void end() override;
148+
virtual int len() override;
149+
virtual void *hash() override;
150+
private:
151+
br_sha256_context _cc;
152+
unsigned char _sha256[32];
153+
};
154+
155+
class BearSSLVerifier : public UpdaterVerifyClass {
156+
public:
157+
virtual bool verify(UpdaterHashClass *hash, void *signature, uint32_t signatureLen) override;
158+
159+
public:
160+
BearSSLVerifier(BearSSLPublicKey *pubKey) { _pubKey = pubKey; }
161+
162+
private:
163+
BearSSLPublicKey *_pubKey;
164+
};
165+
139166
#endif

0 commit comments

Comments
 (0)