Skip to content

Commit 6272b49

Browse files
qistophd-a-v
authored andcommitted
Updater signature validation - format incompatible w/RFC8017 (#6250)
* Add hash OID to signature verification (#6201) * Add legacy signing option * Describe and use the legacy option of signing.py
1 parent 7036297 commit 6272b49

File tree

6 files changed

+60
-15
lines changed

6 files changed

+60
-15
lines changed

cores/esp8266/Updater.h

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class UpdaterHashClass {
3838
virtual void end() = 0;
3939
virtual int len() = 0;
4040
virtual const void *hash() = 0;
41+
virtual const unsigned char *oid() = 0;
4142
};
4243

4344
// Abstract class to implement a signature verifier

doc/ota_updates/readme.rst

+12
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,18 @@ Compile the sketch normally and, once a `.bin` file is available, sign it using
125125
126126
<ESP8266ArduioPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin <path-to-unsigned-bin> --out <path-to-signed-binary>
127127
128+
Old And New Signature Formats
129+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
130+
131+
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.
132+
133+
To create a legacy signature, call the signing script with --legacy:
134+
135+
.. code:: bash
136+
137+
<ESP8266ArduioPath>/tools/signing.py --mode sign --privatekey <path-to-private.key> --bin <path-to-unsigned-bin> --out <path-to-signed-binary> --legacy <path-to-legacy-file>
138+
139+
128140
Safety
129141
~~~~~~
130142

libraries/ESP8266WiFi/src/BearSSLHelpers.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,10 @@ const void *HashSHA256::hash() {
848848
return (const void*) _sha256;
849849
}
850850

851+
const unsigned char *HashSHA256::oid() {
852+
return BR_HASH_OID_SHA256;
853+
}
854+
851855
// SHA256 verifier
852856
uint32_t SigningVerifier::length()
853857
{
@@ -869,7 +873,7 @@ bool SigningVerifier::verify(UpdaterHashClass *hash, const void *signature, uint
869873
bool ret;
870874
unsigned char vrf[hash->len()];
871875
br_rsa_pkcs1_vrfy vrfy = br_rsa_pkcs1_vrfy_get_default();
872-
ret = vrfy((const unsigned char *)signature, signatureLen, NULL, sizeof(vrf), _pubKey->getRSA(), vrf);
876+
ret = vrfy((const unsigned char *)signature, signatureLen, hash->oid(), sizeof(vrf), _pubKey->getRSA(), vrf);
873877
if (!ret || memcmp(vrf, hash->hash(), sizeof(vrf)) ) {
874878
return false;
875879
} else {
@@ -896,4 +900,4 @@ make_stack_thunk(br_ssl_engine_sendrec_buf);
896900

897901
#endif
898902

899-
};
903+
};

libraries/ESP8266WiFi/src/BearSSLHelpers.h

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ class HashSHA256 : public UpdaterHashClass {
146146
virtual void end() override;
147147
virtual int len() override;
148148
virtual const void *hash() override;
149+
virtual const unsigned char *oid() override;
149150
private:
150151
br_sha256_context _cc;
151152
unsigned char _sha256[32];

platform.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ recipe.objcopy.eep.pattern=
109109

110110
## Create hex
111111
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"
112-
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"
112+
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"
113113

114114
## Save hex
115115
recipe.output.tmp_file={build.project_name}.bin

tools/signing.py

+39-12
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,43 @@ def parse_args():
1212
parser.add_argument('-m', '--mode', help='Mode (header, sign)')
1313
parser.add_argument('-b', '--bin', help='Unsigned binary')
1414
parser.add_argument('-o', '--out', help='Output file');
15+
parser.add_argument('-l', '--legacy', help='Legacy output file');
1516
parser.add_argument('-p', '--publickey', help='Public key file');
1617
parser.add_argument('-s', '--privatekey', help='Private(secret) key file');
1718
return parser.parse_args()
1819

20+
def sign_and_write(data, priv_key, out_file):
21+
"""Signs the data (bytes) with the private key (file path)."""
22+
"""Save the signed firmware to out_file (file path)."""
23+
24+
signcmd = [ 'openssl', 'dgst', '-sha256', '-sign', priv_key ]
25+
proc = subprocess.Popen(signcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
26+
signout, signerr = proc.communicate(input=data)
27+
if proc.returncode:
28+
sys.stderr.write("OpenSSL returned an error signing the binary: " + str(proc.returncode) + "\nSTDERR: " + str(signerr))
29+
else:
30+
with open(out_file, "wb") as out:
31+
out.write(data)
32+
out.write(signout)
33+
out.write(b'\x00\x01\x00\x00')
34+
sys.stderr.write("Signed binary: " + out_file + "\n")
35+
36+
def sign_and_write_legacy(data, priv_key, out_file):
37+
"""Signs the data (bytes) with the private key (file path)."""
38+
"""Save the signed firmware to out_file (file path)."""
39+
40+
sha256 = hashlib.sha256(data)
41+
signcmd = [ 'openssl', 'rsautl', '-sign', '-inkey', priv_key ]
42+
proc = subprocess.Popen(signcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
43+
signout, signerr = proc.communicate(input=sha256.digest())
44+
if proc.returncode:
45+
sys.stderr.write("OpenSSL returned an error legacy signing the binary: " + str(proc.returncode) + "\nSTDERR: " + str(signerr))
46+
else:
47+
with open(out_file, "wb") as out:
48+
out.write(data)
49+
out.write(signout)
50+
out.write(b'\x00\x01\x00\x00')
51+
sys.stderr.write("Legacy signed binary: " + out_file + "\n")
1952

2053
def main():
2154
args = parse_args()
@@ -51,18 +84,12 @@ def main():
5184
try:
5285
with open(args.bin, "rb") as b:
5386
bin = b.read()
54-
sha256 = hashlib.sha256(bin)
55-
signcmd = [ 'openssl', 'rsautl', '-sign', '-inkey', args.privatekey ]
56-
proc = subprocess.Popen(signcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
57-
signout, signerr = proc.communicate(input=sha256.digest())
58-
if proc.returncode:
59-
sys.stderr.write("OpenSSL returned an error signing the binary: " + str(proc.returncode) + "\nSTDERR: " + str(signerr))
60-
else:
61-
with open(args.out, "wb") as out:
62-
out.write(bin)
63-
out.write(signout)
64-
out.write(b'\x00\x01\x00\x00')
65-
sys.stderr.write("Signed binary: " + args.out + "\n")
87+
88+
sign_and_write(bin, args.privatekey, args.out)
89+
90+
if args.legacy:
91+
sign_and_write_legacy(bin, args.privatekey, args.legacy)
92+
6693
except Exception as e:
6794
sys.stderr.write(str(e))
6895
sys.stderr.write("Not signing the generated binary\n")

0 commit comments

Comments
 (0)