espsecure.py: Add support for 192 bit key operations on the host:

* encrypt_flash_data
* decrypt_flash_data
* digest_private_key
This commit is contained in:
Angus Gratton
2018-10-10 14:20:52 +11:00
committed by Angus Gratton
parent 8dee0921d7
commit 8305193b44

View File

@@ -47,6 +47,25 @@ def swap_word_order(source):
return struct.pack(words, *reversed(struct.unpack(words, source))) return struct.pack(words, *reversed(struct.unpack(words, source)))
def _load_hardware_key(keyfile):
""" Load a 256-bit key, similar to stored in efuse, from a file
192-bit keys will be extended to 256-bit using the same algorithm used
by hardware if 3/4 Coding Scheme is set.
"""
key = keyfile.read()
if len(key) not in [24, 32]:
raise esptool.FatalError("Key file contains wrong length (%d bytes), 24 or 32 expected." % len(key))
if len(key) == 24:
key = key + key[8:16]
print("Using 192-bit key (extended)")
else:
print("Using 256-bit key")
assert len(key) == 32
return key
def digest_secure_bootloader(args): def digest_secure_bootloader(args):
""" Calculate the digest of a bootloader image, in the same way the hardware """ Calculate the digest of a bootloader image, in the same way the hardware
secure boot engine would do so. Can be used with a pre-loaded key to update a secure boot engine would do so. Can be used with a pre-loaded key to update a
@@ -81,9 +100,7 @@ def digest_secure_bootloader(args):
# produce the digest. Each block in/out of ECB is reordered # produce the digest. Each block in/out of ECB is reordered
# (due to hardware quirks not for security.) # (due to hardware quirks not for security.)
key = args.keyfile.read() key = _load_hardware_key(args.keyfile)
if len(key) != 32:
raise esptool.FatalError("Key file contains wrong length (%d bytes), 32 expected." % len(key))
aes = pyaes.AESModeOfOperationECB(key) aes = pyaes.AESModeOfOperationECB(key)
digest = hashlib.sha512() digest = hashlib.sha512()
@@ -119,7 +136,7 @@ def generate_signing_key(args):
print("ECDSA NIST256p private key in PEM format written to %s" % args.keyfile) print("ECDSA NIST256p private key in PEM format written to %s" % args.keyfile)
def _load_key(args): def _load_signing_key(args):
sk = ecdsa.SigningKey.from_pem(args.keyfile.read()) sk = ecdsa.SigningKey.from_pem(args.keyfile.read())
if sk.curve != ecdsa.NIST256p: if sk.curve != ecdsa.NIST256p:
raise esptool.FatalError("Signing key uses incorrect curve. ESP32 Secure Boot only supports NIST256p (openssl calls this curve 'prime256v1") raise esptool.FatalError("Signing key uses incorrect curve. ESP32 Secure Boot only supports NIST256p (openssl calls this curve 'prime256v1")
@@ -128,7 +145,7 @@ def _load_key(args):
def sign_data(args): def sign_data(args):
""" Sign a data file with a ECDSA private key, append binary signature to file contents """ """ Sign a data file with a ECDSA private key, append binary signature to file contents """
sk = _load_key(args) sk = _load_signing_key(args)
# calculate signature of binary data # calculate signature of binary data
binary_content = args.datafile.read() binary_content = args.datafile.read()
@@ -153,7 +170,7 @@ def sign_data(args):
def verify_signature(args): def verify_signature(args):
""" Verify a previously signed binary image, using the ECDSA public key """ """ Verify a previously signed binary image, using the ECDSA public key """
try: try:
sk = _load_key(args) # try to load as private key first sk = _load_hardware_key(args) # try to load as private key first
vk = sk.get_verifying_key() vk = sk.get_verifying_key()
except Exception: # this is a catchall because ecdsa can throw private Exceptions except Exception: # this is a catchall because ecdsa can throw private Exceptions
args.keyfile.seek(0) args.keyfile.seek(0)
@@ -178,19 +195,25 @@ def verify_signature(args):
def extract_public_key(args): def extract_public_key(args):
""" Load an ECDSA private key and extract the embedded public key as raw binary data. """ """ Load an ECDSA private key and extract the embedded public key as raw binary data. """
sk = _load_key(args) sk = _load_signing_key(args)
vk = sk.get_verifying_key() vk = sk.get_verifying_key()
args.public_keyfile.write(vk.to_string()) args.public_keyfile.write(vk.to_string())
print("%s public key extracted to %s" % (args.keyfile.name, args.public_keyfile.name)) print("%s public key extracted to %s" % (args.keyfile.name, args.public_keyfile.name))
def digest_private_key(args): def digest_private_key(args):
sk = _load_key(args) sk = _load_signing_key(args)
repr(sk.to_string()) repr(sk.to_string())
digest = hashlib.sha256() digest = hashlib.sha256()
digest.update(sk.to_string()) digest.update(sk.to_string())
args.digest_file.write(digest.digest()) result = digest.digest()
print("SHA-256 digest of private key %s written to %s" % (args.keyfile.name, args.digest_file.name)) if args.keylen == '192':
result = result[0:24]
args.digest_file.write(result)
print("SHA-256 digest of private key %s%s written to %s" % (args.keyfile.name,
"" if args.keylen == '256'
else " (truncated to 192 bits)",
args.digest_file.name))
# flash encryption key tweaking pattern: the nth bit of the key is # flash encryption key tweaking pattern: the nth bit of the key is
@@ -260,9 +283,7 @@ def generate_flash_encryption_key(args):
def _flash_encryption_operation(output_file, input_file, flash_address, keyfile, flash_crypt_conf, do_decrypt): def _flash_encryption_operation(output_file, input_file, flash_address, keyfile, flash_crypt_conf, do_decrypt):
key = keyfile.read() key = _load_hardware_key(keyfile)
if len(key) != 32:
raise esptool.FatalError("Key file contains wrong length (%d bytes), 32 expected." % len(key))
if flash_address % 16 != 0: if flash_address % 16 != 0:
raise esptool.FatalError("Starting flash address 0x%x must be a multiple of 16" % flash_address) raise esptool.FatalError("Starting flash address 0x%x must be a multiple of 16" % flash_address)
@@ -355,6 +376,8 @@ def main():
'This can be used as a reproducible secure bootloader or flash encryption key.') 'This can be used as a reproducible secure bootloader or flash encryption key.')
p.add_argument('--keyfile', '-k', help="Private key file to generate a digest from.", type=argparse.FileType('rb'), p.add_argument('--keyfile', '-k', help="Private key file to generate a digest from.", type=argparse.FileType('rb'),
required=True) required=True)
p.add_argument('--keylen', '-l', help="Length of private key digest file to generate (in bits).",
choices=['192','256'], default='256')
p.add_argument('digest_file', help="File to write 32 byte digest into", type=argparse.FileType('wb')) p.add_argument('digest_file', help="File to write 32 byte digest into", type=argparse.FileType('wb'))
p = subparsers.add_parser('generate_flash_encryption_key', help='Generate a development-use 32 byte flash encryption key with random data.') p = subparsers.add_parser('generate_flash_encryption_key', help='Generate a development-use 32 byte flash encryption key with random data.')