1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-06-12 01:53:07 +03:00

Add cryptographically signed update support (#5213)

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).

An example using automatic signing is included.

Update the docs to show the signing steps and how to use it in the
automatic and manual modes.

Also remove one debugging line from the signing tool.

Saves ~600 bytes when in debug mode by moving strings to PMEM

Windows can't run the signing script, nor does it normally have OpenSSL
installed.  When trying to build an automatically signed binary, warn
and don't run the python.
This commit is contained in:
Earle F. Philhower, III
2018-12-02 19:57:47 -08:00
committed by GitHub
parent e5d50a6e4b
commit d8acfffdb0
11 changed files with 532 additions and 27 deletions

View File

@ -826,6 +826,62 @@ bool X509List::append(const uint8_t *derCert, size_t derLen) {
return true;
}
// SHA256 hash for updater
void HashSHA256::begin() {
br_sha256_init( &_cc );
memset( _sha256, 0, sizeof(_sha256) );
}
void HashSHA256::add(const void *data, uint32_t len) {
br_sha256_update( &_cc, data, len );
}
void HashSHA256::end() {
br_sha256_out( &_cc, _sha256 );
}
int HashSHA256::len() {
return sizeof(_sha256);
}
const void *HashSHA256::hash() {
return (const void*) _sha256;
}
// SHA256 verifier
uint32_t SigningVerifier::length()
{
if (!_pubKey) {
return 0;
} else if (_pubKey->isRSA()) {
return _pubKey->getRSA()->nlen;
} else if (_pubKey->isEC()) {
return _pubKey->getEC()->qlen;
} else {
return 0;
}
}
bool SigningVerifier::verify(UpdaterHashClass *hash, const void *signature, uint32_t signatureLen) {
if (!_pubKey || !hash || !signature || signatureLen != length()) return false;
if (_pubKey->isRSA()) {
bool ret;
unsigned char vrf[hash->len()];
br_rsa_pkcs1_vrfy vrfy = br_rsa_pkcs1_vrfy_get_default();
ret = vrfy((const unsigned char *)signature, signatureLen, NULL, sizeof(vrf), _pubKey->getRSA(), vrf);
if (!ret || memcmp(vrf, hash->hash(), sizeof(vrf)) ) {
return false;
} else {
return true;
}
} else {
br_ecdsa_vrfy vrfy = br_ecdsa_vrfy_raw_get_default();
// The EC verifier actually does the compare, unlike the RSA one
return vrfy(br_ec_get_default(), hash->hash(), hash->len(), _pubKey->getEC(), (const unsigned char *)signature, signatureLen);
}
};
#if !CORE_MOCK
// Second stack thunked helpers

View File

@ -24,6 +24,8 @@
#define _BEARSSLHELPERS_H
#include <bearssl/bearssl.h>
#include <Updater.h>
// Internal opaque structures, not needed by user applications
namespace brssl {
@ -136,6 +138,31 @@ class Session {
br_ssl_session_parameters _session;
};
// Updater SHA256 hash and signature verification
class HashSHA256 : public UpdaterHashClass {
public:
virtual void begin() override;
virtual void add(const void *data, uint32_t len) override;
virtual void end() override;
virtual int len() override;
virtual const void *hash() override;
private:
br_sha256_context _cc;
unsigned char _sha256[32];
};
class SigningVerifier : public UpdaterVerifyClass {
public:
virtual uint32_t length() override;
virtual bool verify(UpdaterHashClass *hash, const void *signature, uint32_t signatureLen) override;
public:
SigningVerifier(PublicKey *pubKey) { _pubKey = pubKey; }
private:
PublicKey *_pubKey;
};
// Stack thunked versions of calls
extern "C" {
extern unsigned char *thunk_br_ssl_engine_recvapp_buf( const br_ssl_engine_context *cc, size_t *len);

View File

@ -0,0 +1,112 @@
/*
httpUpdateSigned.ino - Earle F. Philhower, III
Released into the Public Domain
For use while building under Linux or Mac.
Automatic code signing is not supported on Windows, so this example
DOES NOT WORK UNDER WINDOWS.
Shows how to use a public key extracted from your private certificate to
only allow updates that you have signed to be applied over HTTP. Remote
updates will require your private key to sign them, but of course
**ANYONE WITH PHYSICAL ACCESS CAN UPDATE THE 8266 VIA THE SERIAL PORT**.
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
ESP8266WiFiMulti WiFiMulti;
#define MANUAL_SIGNING 0
// This example is now configured to use the automated signing support
// present in the Arduino IDE by having a "private.key" and "public.key"
// in the sketch folder. You can also programmatically enable signing
// using the method shown here.
// This key is taken from the server public certificate in BearSSL examples
// You should make your own private/public key pair and guard the private
// key (never upload it to the 8266).
const char pubkey[] PROGMEM = R"EOF(
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyW5a4OO7xd6pRDTETO7h
vEMBOr/wCqcTi/gi2/99rPnVvT7IH/qGSiYMxpGFKCXVqS4rU5k2XspALEquyGse
Uav5hqsgHO6CQFFALqXzUVNCsJA9V6raUFhBaIqqCKmWzmeAkV+avM/zDQR9Wj1Q
TCmi997sJJ5ICQc8cGSdvrhisUSbfPpKI9Ql4FApOZRABBBuZKhN9ujIzTv3OIAa
rpQVfACKKuv7a2N2qU0uxRDojeO6odT1c6AZv6BlcF76GQGTo+/oBhqPdbAQuaBy
cuWNgTnDQd6KUzV0E4it2fNG+cHN4kEvofN6gHx8IbOrXwFttlpAH/o7bcfCnUVh
TQIDAQAB
-----END PUBLIC KEY-----
)EOF";
#if MANUAL_SIGNING
BearSSL::PublicKey *signPubKey = nullptr;
BearSSL::HashSHA256 *hash;
BearSSL::SigningVerifier *sign;
#endif
void setup() {
Serial.begin(115200);
// Serial.setDebugOutput(true);
Serial.println();
Serial.println();
Serial.println();
for (uint8_t t = 4; t > 0; t--) {
Serial.printf("[SETUP] WAIT %d...\n", t);
Serial.flush();
delay(1000);
}
WiFi.mode(WIFI_STA);
WiFiMulti.addAP("SSID", "PASS");
#if MANUAL_SIGNING
signPubKey = new BearSSL::PublicKey(pubkey);
hash = new BearSSL::HashSHA256();
sign = new BearSSL::SigningVerifier(signPubKey);
#endif
}
void loop() {
// wait for WiFi connection
if ((WiFiMulti.run() == WL_CONNECTED)) {
WiFiClient client;
#if MANUAL_SIGNING
// Ensure all updates are signed appropriately. W/o this call, all will be accepted.
Update.installSignature(hash, sign);
#endif
// If the key files are present in the build directory, signing will be
// enabled using them automatically
ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW);
t_httpUpdate_return ret = ESPhttpUpdate.update(client, "http://192.168.1.8/esp8266.bin");
switch (ret) {
case HTTP_UPDATE_FAILED:
Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
Serial.println("HTTP_UPDATE_OK");
break;
}
}
delay(10000);
}

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAu1Pt7yEk/xI+6cozLj5Bu4xV8gXDXcHS0rSJFfl4wBTk4UXp
aJRaLfR1k0juEEa5LBRZaoA0iLj2e6kfCibONx0VVoWmeqN2HBc3zkA1eqCksI0Q
Uudzto4KhKHp0odiZ2zo6c/2Tn1zqD/m3OLoSjVTbsJmGuwx8RGMBXozpg/uL0hH
flihX+HND4Xfw92QXv7SaPBhgvM9xyRxn0/w3J2nNjtuPuVN5vcQkd8ncMexVfy9
AWp+HSA5AT5N8CJ/EeIsdDMY1US28bUePzj1WIo75bZHKZNFw/iXe2xoPpm74qri
MNSlW2craFP2K3KYnI28vJeUU6t9I6LS9zt2zQIDAQABAoIBAE5GpuDKb8Qp4qIc
fMBxAVSWMn+cSuONj0O+bp4BDaTt1ioP5ZVukDQtt0ehLOEePFgf9LEc+1a6Ozy3
EaJTTs4W2Ai8djE+xqa8SPRlPjOMluSzPUP3NRHuTpTXd3YiXksrZjP1U02+/Cos
8ZIROtFvcPqSPso3MjMyitjrFFPqEtf1P+UiamjDrMSM72YX4W55kOkiCWCnAOmw
mGTlXOIqDSTBb1lloKWJfpB3RdnNo2izkU1HMBn7hVi433NUBA22o+RZhDFSZdD4
3kbkUqXd4p+vc/sh6muJtWS/COSIPFkLzdEYpBdt3XQ4FhlsRtILJaPWXa4OPjR6
ZoOwMB0CgYEA6OHfIofQiu4+HlTDN5YdyTmtYEYcrtbaQUxuQSEa2mshBphHP8uT
mYRVl2BzuprFmXZPz+FcjnPnfxqEehljvA3wMjA/PE+nQo9yyOC0N4ulXpkkqHdR
f+4KZVR7D+hesGe+57OQmvTqYZSHEt/ubjC9wZ90UFonLjsa4zibbrsCgYEAzexn
XDnThb3ffyBgvprP0IJjgMAEY0pXD++PKPQqPu9JMz68t7roYzkKFCFVOsaWpKxC
vX9mvYjTBjLpWh+ltIAN+EFz6seIbeSJ0RNybsAXYwT/mFWGHx2tMtlW6DgBu3UD
J2Yf76n0JaddBkfNMQI00Dl41+MU+AwwTB9fTBcCgYB2+f6Pm6d1cyYVROS/X1g0
V9011FwPDwFOXwftCka31Ad5YQ71jsIHqk44GjTF3xCYyJMZ917cAGcCzr9jydjk
WJKgcXm9DEy9ep//9Jzdy+BepgrObrcajriM8E424FaP9VDY+yojoICl/cXMZM9h
SFGJvDcmXgiqW9PuxhrSxQKBgAMN2oqXoPd+1W3BQS4ShbqF9IvYTThbxebKmsj0
thuw2NkVuR7Qetnd4rRhui3g/CL9GxBMb22oNdkFsEhR59dBfvOLpPh6dR+MIC8l
prDV0IL7c/8CZbbYbdUvPAa9rejl12IiNZ8MWj6kuNB7CCQN8FKWR6CMEaeMJrs6
S+OJAoGAbehNOUwEzmUKkfxf+279kBkgabcQ3NTaeSx0QOnI9KWHFGLYLQk9cMSu
maQJ1TYpbIoP1njzJ4bI2tynhwEuSMEhh4afP6U5H10NJX4PqSd0Rqc1vSJYcszr
5mUWil8FfbCBZ8jod2NQ55KYMVY5CphCqaK/s2bw2pvIR3uqJGg=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1Pt7yEk/xI+6cozLj5B
u4xV8gXDXcHS0rSJFfl4wBTk4UXpaJRaLfR1k0juEEa5LBRZaoA0iLj2e6kfCibO
Nx0VVoWmeqN2HBc3zkA1eqCksI0QUudzto4KhKHp0odiZ2zo6c/2Tn1zqD/m3OLo
SjVTbsJmGuwx8RGMBXozpg/uL0hHflihX+HND4Xfw92QXv7SaPBhgvM9xyRxn0/w
3J2nNjtuPuVN5vcQkd8ncMexVfy9AWp+HSA5AT5N8CJ/EeIsdDMY1US28bUePzj1
WIo75bZHKZNFw/iXe2xoPpm74qriMNSlW2craFP2K3KYnI28vJeUU6t9I6LS9zt2
zQIDAQAB
-----END PUBLIC KEY-----