diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index e2198e65d6..d47c613330 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -38,6 +38,7 @@ class UpdaterHashClass { virtual void end() = 0; virtual int len() = 0; virtual const void *hash() = 0; + virtual const unsigned char *oid() = 0; }; // Abstract class to implement a signature verifier diff --git a/doc/ota_updates/readme.rst b/doc/ota_updates/readme.rst index 9c47a88309..bec4fba2e9 100644 --- a/doc/ota_updates/readme.rst +++ b/doc/ota_updates/readme.rst @@ -125,6 +125,18 @@ Compile the sketch normally and, once a `.bin` file is available, sign it using /tools/signing.py --mode sign --privatekey --bin --out +Old And New Signature Formats +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Up to version 2.5.2 of the core, the format of signatures was a little different. An additional signed binary with the extension legacy_sig is created. This file contains a signature in the old format and can be uploaded OTA to a device that checks for the old signature format. + +To create a legacy signature, call the signing script with --legacy: + +.. code:: bash + + /tools/signing.py --mode sign --privatekey --bin --out --legacy + + Safety ~~~~~~ diff --git a/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp b/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp index 8698df5972..6dcf0b384d 100644 --- a/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp +++ b/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp @@ -848,6 +848,10 @@ const void *HashSHA256::hash() { return (const void*) _sha256; } +const unsigned char *HashSHA256::oid() { + return BR_HASH_OID_SHA256; +} + // SHA256 verifier uint32_t SigningVerifier::length() { @@ -869,7 +873,7 @@ bool SigningVerifier::verify(UpdaterHashClass *hash, const void *signature, uint 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); + ret = vrfy((const unsigned char *)signature, signatureLen, hash->oid(), sizeof(vrf), _pubKey->getRSA(), vrf); if (!ret || memcmp(vrf, hash->hash(), sizeof(vrf)) ) { return false; } else { @@ -896,4 +900,4 @@ make_stack_thunk(br_ssl_engine_sendrec_buf); #endif -}; \ No newline at end of file +}; diff --git a/libraries/ESP8266WiFi/src/BearSSLHelpers.h b/libraries/ESP8266WiFi/src/BearSSLHelpers.h index b7c16b03eb..2f6eb8e84e 100644 --- a/libraries/ESP8266WiFi/src/BearSSLHelpers.h +++ b/libraries/ESP8266WiFi/src/BearSSLHelpers.h @@ -146,6 +146,7 @@ class HashSHA256 : public UpdaterHashClass { virtual void end() override; virtual int len() override; virtual const void *hash() override; + virtual const unsigned char *oid() override; private: br_sha256_context _cc; unsigned char _sha256[32]; diff --git a/platform.txt b/platform.txt index c983055be6..0b588d5eb4 100644 --- a/platform.txt +++ b/platform.txt @@ -109,7 +109,7 @@ recipe.objcopy.eep.pattern= ## Create hex recipe.objcopy.hex.1.pattern="{runtime.tools.python.path}/python" "{runtime.tools.elf2bin}" --eboot "{runtime.tools.eboot}" --app "{build.path}/{build.project_name}.elf" --flash_mode {build.flash_mode} --flash_freq {build.flash_freq} --flash_size {build.flash_size} --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" --out "{build.path}/{build.project_name}.bin" -recipe.objcopy.hex.2.pattern="{runtime.tools.python.path}/python" "{runtime.tools.signing}" --mode sign --privatekey "{build.source.path}/private.key" --bin "{build.path}/{build.project_name}.bin" --out "{build.path}/{build.project_name}.bin.signed" +recipe.objcopy.hex.2.pattern="{runtime.tools.python.path}/python" "{runtime.tools.signing}" --mode sign --privatekey "{build.source.path}/private.key" --bin "{build.path}/{build.project_name}.bin" --out "{build.path}/{build.project_name}.bin.signed" --legacy "{build.path}/{build.project_name}.bin.legacy_sig" ## Save hex recipe.output.tmp_file={build.project_name}.bin diff --git a/tools/signing.py b/tools/signing.py index 0f266fd8f4..d3dcc7cc18 100755 --- a/tools/signing.py +++ b/tools/signing.py @@ -12,10 +12,43 @@ def parse_args(): parser.add_argument('-m', '--mode', help='Mode (header, sign)') parser.add_argument('-b', '--bin', help='Unsigned binary') parser.add_argument('-o', '--out', help='Output file'); + parser.add_argument('-l', '--legacy', help='Legacy output file'); parser.add_argument('-p', '--publickey', help='Public key file'); parser.add_argument('-s', '--privatekey', help='Private(secret) key file'); return parser.parse_args() +def sign_and_write(data, priv_key, out_file): + """Signs the data (bytes) with the private key (file path).""" + """Save the signed firmware to out_file (file path).""" + + signcmd = [ 'openssl', 'dgst', '-sha256', '-sign', priv_key ] + proc = subprocess.Popen(signcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) + signout, signerr = proc.communicate(input=data) + if proc.returncode: + sys.stderr.write("OpenSSL returned an error signing the binary: " + str(proc.returncode) + "\nSTDERR: " + str(signerr)) + else: + with open(out_file, "wb") as out: + out.write(data) + out.write(signout) + out.write(b'\x00\x01\x00\x00') + sys.stderr.write("Signed binary: " + out_file + "\n") + +def sign_and_write_legacy(data, priv_key, out_file): + """Signs the data (bytes) with the private key (file path).""" + """Save the signed firmware to out_file (file path).""" + + sha256 = hashlib.sha256(data) + signcmd = [ 'openssl', 'rsautl', '-sign', '-inkey', priv_key ] + proc = subprocess.Popen(signcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) + signout, signerr = proc.communicate(input=sha256.digest()) + if proc.returncode: + sys.stderr.write("OpenSSL returned an error legacy signing the binary: " + str(proc.returncode) + "\nSTDERR: " + str(signerr)) + else: + with open(out_file, "wb") as out: + out.write(data) + out.write(signout) + out.write(b'\x00\x01\x00\x00') + sys.stderr.write("Legacy signed binary: " + out_file + "\n") def main(): args = parse_args() @@ -51,18 +84,12 @@ def main(): try: with open(args.bin, "rb") as b: bin = b.read() - sha256 = hashlib.sha256(bin) - signcmd = [ 'openssl', 'rsautl', '-sign', '-inkey', args.privatekey ] - proc = subprocess.Popen(signcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) - signout, signerr = proc.communicate(input=sha256.digest()) - if proc.returncode: - sys.stderr.write("OpenSSL returned an error signing the binary: " + str(proc.returncode) + "\nSTDERR: " + str(signerr)) - else: - with open(args.out, "wb") as out: - out.write(bin) - out.write(signout) - out.write(b'\x00\x01\x00\x00') - sys.stderr.write("Signed binary: " + args.out + "\n") + + sign_and_write(bin, args.privatekey, args.out) + + if args.legacy: + sign_and_write_legacy(bin, args.privatekey, args.legacy) + except Exception as e: sys.stderr.write(str(e)) sys.stderr.write("Not signing the generated binary\n")