From bc7331b96e4af5fb2293e04893e1511787403261 Mon Sep 17 00:00:00 2001 From: Devaraj Ranganna Date: Tue, 21 Jan 2020 10:25:56 +0000 Subject: [PATCH 1/2] Import latest python scripts and MCUBoot image To help with the integration of Musca B1 into Mbed OS, python signing scripts and MCUBoot image and RSA private key for Musca A has been updated from latest TF-M (https://git.trustedfirmware.org/trusted-firmware-m.git/commit/?id=6c5be4a98e4d7055ee49076ca4e515fb4b172e66). Signed-off-by: Devaraj Ranganna --- .../TARGET_MUSCA_A1_NS/prebuilt/README.md | 2 +- .../TARGET_MUSCA_A1_NS/prebuilt/mcuboot.bin | Bin 42576 -> 37312 bytes tools/psa/tfm/bin_utils/assemble.py | 22 +-- tools/psa/tfm/bin_utils/imgtool.py | 102 +++++++++--- .../tfm/bin_utils/imgtool_lib/boot_record.py | 80 +++++++++ tools/psa/tfm/bin_utils/imgtool_lib/image.py | 154 ++++++++++++++---- tools/psa/tfm/bin_utils/imgtool_lib/keys.py | 98 +++++++---- tools/psa/tfm/bin_utils/macro_parser.py | 70 ++++++++ tools/targets/ARM_MUSCA_A1.py | 5 +- tools/targets/musca_a1-root-rsa-2048.pem | 27 --- ...-rsa-2048.md => musca_a1-root-rsa-3072.md} | 2 +- tools/targets/musca_a1-root-rsa-3072.pem | 39 +++++ 12 files changed, 467 insertions(+), 134 deletions(-) create mode 100644 tools/psa/tfm/bin_utils/imgtool_lib/boot_record.py create mode 100644 tools/psa/tfm/bin_utils/macro_parser.py delete mode 100644 tools/targets/musca_a1-root-rsa-2048.pem rename tools/targets/{musca_a1-root-rsa-2048.md => musca_a1-root-rsa-3072.md} (92%) create mode 100644 tools/targets/musca_a1-root-rsa-3072.pem diff --git a/targets/TARGET_ARM_SSG/TARGET_MUSCA_A1/TARGET_MUSCA_A1_NS/prebuilt/README.md b/targets/TARGET_ARM_SSG/TARGET_MUSCA_A1/TARGET_MUSCA_A1_NS/prebuilt/README.md index 4e58e703e8..ca2c87049a 100644 --- a/targets/TARGET_ARM_SSG/TARGET_MUSCA_A1/TARGET_MUSCA_A1_NS/prebuilt/README.md +++ b/targets/TARGET_ARM_SSG/TARGET_MUSCA_A1/TARGET_MUSCA_A1_NS/prebuilt/README.md @@ -10,7 +10,7 @@ These images were compiled by the following command: ### Repository https://git.trustedfirmware.org/trusted-firmware-m.git ### Commit SHA -8da7f102a6a6a1a99462f7f32edbd1565096c2f3 +6c5be4a98e4d7055ee49076ca4e515fb4b172e66 ```sh cmake ../ -G"Unix Makefiles" -DTARGET_PLATFORM=MUSCA_A -DCOMPILER=ARMCLANG -DCMAKE_BUILD_TYPE=Debug make diff --git a/targets/TARGET_ARM_SSG/TARGET_MUSCA_A1/TARGET_MUSCA_A1_NS/prebuilt/mcuboot.bin b/targets/TARGET_ARM_SSG/TARGET_MUSCA_A1/TARGET_MUSCA_A1_NS/prebuilt/mcuboot.bin index 718c0217e25328dc627b76907fe95883d2ce09b7..977cc2cbe2e57823e0086863cfaf575259e6b7bf 100644 GIT binary patch literal 37312 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 literal 42576 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 diff --git a/tools/psa/tfm/bin_utils/assemble.py b/tools/psa/tfm/bin_utils/assemble.py index 0bb41f122e..d12fd96250 100644 --- a/tools/psa/tfm/bin_utils/assemble.py +++ b/tools/psa/tfm/bin_utils/assemble.py @@ -1,7 +1,7 @@ #! /usr/bin/env python3 # # Copyright 2017 Linaro Limited -# Copyright (c) 2017-2018, Arm Limited. +# Copyright (c) 2017-2019, Arm Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import io import re import os import shutil +from . import macro_parser offset_re = re.compile(r"^#define ([0-9A-Z_]+)_IMAGE_OFFSET\s+((0x)?[0-9a-fA-F]+)") size_re = re.compile(r"^#define ([0-9A-Z_]+)_IMAGE_MAX_SIZE\s+((0x)?[0-9a-fA-F]+)") @@ -44,20 +45,8 @@ class Assembly(): offsets = {} sizes = {} - if os.path.isabs(self.layout_path): - configFile = self.layout_path - else: - scriptsDir = os.path.dirname(os.path.abspath(__file__)) - configFile = os.path.join(scriptsDir, self.layout_path) - - with open(configFile, 'r') as fd: - for line in fd: - m = offset_re.match(line) - if m is not None: - offsets[m.group(1)] = int(m.group(2), 0) - m = size_re.match(line) - if m is not None: - sizes[m.group(1)] = int(m.group(2), 0) + offsets = macro_parser.evaluate_macro(self.layout_path, offset_re, 1, 2) + sizes = macro_parser.evaluate_macro(self.layout_path, size_re, 1, 2) if 'SECURE' not in offsets: raise Exception("Image config does not have secure partition") @@ -86,7 +75,7 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument('-l', '--layout', required=True, - help='Location of the memory layout file') + help='Location of the file that contains preprocessed macros') parser.add_argument('-s', '--secure', required=True, help='Unsigned secure image') parser.add_argument('-n', '--non_secure', @@ -97,7 +86,6 @@ def main(): args = parser.parse_args() output = Assembly(args.layout, args.output) - output.add_image(args.secure, "SECURE") output.add_image(args.non_secure, "NON_SECURE") diff --git a/tools/psa/tfm/bin_utils/imgtool.py b/tools/psa/tfm/bin_utils/imgtool.py index 0440f88fd7..cfc372743f 100644 --- a/tools/psa/tfm/bin_utils/imgtool.py +++ b/tools/psa/tfm/bin_utils/imgtool.py @@ -1,7 +1,7 @@ #! /usr/bin/env python3 # # Copyright 2017 Linaro Limited -# Copyright (c) 2018, Arm Limited. +# Copyright (c) 2018-2019, Arm Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,6 +24,9 @@ from .imgtool_lib import image from .imgtool_lib import version import sys +sign_bin_size_re = re.compile(r"^\s*RE_SIGN_BIN_SIZE\s*=\s*(.*)") +image_load_address_re = re.compile(r"^\s*RE_IMAGE_LOAD_ADDRESS\s*=\s*(.*)") + def find_load_address(args): load_address_re = re.compile(r"^#define\sIMAGE_LOAD_ADDRESS\s+(0x[0-9a-fA-F]+)") @@ -60,6 +63,7 @@ def get_last_version(path): def next_version_number(args, defaultVersion, path): newVersion = None + versionProvided = False if (version.compare(args.version, defaultVersion) == 0): # Default version lastVersion = get_last_version(path) if (lastVersion is not None): @@ -67,6 +71,7 @@ def next_version_number(args, defaultVersion, path): else: newVersion = version.increment_build_num(defaultVersion) else: # Version number has been explicitly provided (not using the default) + versionProvided = True newVersion = args.version versionString = "{a}.{b}.{c}+{d}".format( a=str(newVersion.major), @@ -74,16 +79,21 @@ def next_version_number(args, defaultVersion, path): c=str(newVersion.revision), d=str(newVersion.build) ) - with open(path, "w") as newFile: - newFile.write(versionString) + if not versionProvided: + with open(path, "w") as newFile: + newFile.write(versionString) print("**[INFO]** Image version number set to " + versionString) return newVersion def gen_rsa2048(args): - keys.RSA2048.generate().export_private(args.key) + keys.RSAutil.generate().export_private(args.key) + +def gen_rsa3072(args): + keys.RSAutil.generate(key_size=3072).export_private(args.key) keygens = { - 'rsa-2048': gen_rsa2048, } + 'rsa-2048': gen_rsa2048, + 'rsa-3072': gen_rsa3072, } def do_keygen(args): if args.type not in keygens: @@ -102,18 +112,38 @@ def do_getpub(args): def do_sign(args): if args.rsa_pkcs1_15: keys.sign_rsa_pss = False - img = image.Image.load(args.infile, - version=next_version_number(args, - version.decode_version("0"), - "lastVerNum.txt"), - header_size=args.header_size, - included_header=args.included_header, - pad=args.pad) - key = keys.load(args.key) if args.key else None - img.sign(key, find_load_address(args)) - if args.pad: - img.pad_to(args.pad, args.align) + version_num = next_version_number(args, + version.decode_version("0"), + "lastVerNum.txt") + + if args.security_counter is None: + # Security counter has not been explicitly provided, + # generate it from the version number + args.security_counter = ((version_num.major << 24) + + (version_num.minor << 16) + + version_num.revision) + + if "_s.c" in args.layout: + sw_type = "SPE" + elif "_ns.c" in args.layout: + sw_type = "NSPE" + else: + sw_type = "NSPE_SPE" + + pad_size = args.pad + img = image.Image.load(args.infile, + version=version_num, + header_size=args.header_size, + security_cnt=args.security_counter, + included_header=args.included_header, + pad=pad_size) + key = keys.load(args.key, args.public_key_format) if args.key else None + ram_load_address = find_load_address(args) + img.sign(sw_type, key, ram_load_address, args.dependencies) + + if pad_size: + img.pad_to(pad_size, args.align) img.save(args.outfile) @@ -122,6 +152,30 @@ subcmds = { 'getpub': do_getpub, 'sign': do_sign, } + +def get_dependencies(text): + if text is not None: + versions = [] + images = re.findall(r"\((\d+)", text) + if len(images) == 0: + msg = "Image dependency format is invalid: {}".format(text) + raise argparse.ArgumentTypeError(msg) + raw_versions = re.findall(r",\s*([0-9.+]+)\)", text) + if len(images) != len(raw_versions): + msg = '''There's a mismatch between the number of dependency images + and versions in: {}'''.format(text) + raise argparse.ArgumentTypeError(msg) + for raw_version in raw_versions: + try: + versions.append(version.decode_version(raw_version)) + except ValueError as e: + print(e) + dependencies = dict() + dependencies[image.DEP_IMAGES_KEY] = images + dependencies[image.DEP_VERSIONS_KEY] = versions + return dependencies + + def alignment_value(text): value = int(text) if value not in [1, 2, 4, 8]: @@ -149,17 +203,23 @@ def args(): getpub.add_argument('-l', '--lang', metavar='lang', default='c') sign = subs.add_parser('sign', help='Sign an image with a private key') - sign.add_argument('--layout', required=True, - help='Location of the memory layout file') + sign.add_argument('-l', '--layout', required=True, + help='Location of the file that contains preprocessed macros') sign.add_argument('-k', '--key', metavar='filename') + sign.add_argument("-K", "--public-key-format", + help='In what format to add the public key to the image manifest: full or hash', + metavar='pub_key_format', choices=['full', 'hash'], default='hash') sign.add_argument("--align", type=alignment_value, required=True) sign.add_argument("-v", "--version", type=version.decode_version, default="0.0.0+0") + sign.add_argument("-d", "--dependencies", type=get_dependencies, + required=False, help='''Add dependence on another image, + format: "(,), ... "''') + sign.add_argument("-s", "--security-counter", type=intparse, + help='Specify explicitly the security counter value') sign.add_argument("-H", "--header-size", type=intparse, required=True) sign.add_argument("--included-header", default=False, action='store_true', help='Image has gap for header') - sign.add_argument("--pad", type=intparse, - help='Pad image to this many bytes, adding trailer magic') sign.add_argument("--rsa-pkcs1-15", help='Use old PKCS#1 v1.5 signature algorithm', default=False, action='store_true') @@ -174,4 +234,4 @@ def args(): subcmds[args.subcmd](args) if __name__ == '__main__': - args() \ No newline at end of file + args() diff --git a/tools/psa/tfm/bin_utils/imgtool_lib/boot_record.py b/tools/psa/tfm/bin_utils/imgtool_lib/boot_record.py new file mode 100644 index 0000000000..97212fd716 --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool_lib/boot_record.py @@ -0,0 +1,80 @@ + +# Copyright (c) 2019, Arm Limited. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import print_function +import os +import sys +import cbor + + +# SW component IDs +SW_COMPONENT_RANGE = 0 +SW_COMPONENT_TYPE = SW_COMPONENT_RANGE + 1 +MEASUREMENT_VALUE = SW_COMPONENT_RANGE + 2 +SW_COMPONENT_VERSION = SW_COMPONENT_RANGE + 4 +SIGNER_ID = SW_COMPONENT_RANGE + 5 +MEASUREMENT_DESCRIPTION = SW_COMPONENT_RANGE + 6 + + +def create_sw_component_data(sw_type, sw_version, sw_measurement_type, + sw_measurement_value, sw_signer_id): + + # List of SW component claims (key ID + value) + key_value_list = [ + SW_COMPONENT_TYPE, sw_type, + SW_COMPONENT_VERSION, sw_version, + SIGNER_ID, sw_signer_id, + MEASUREMENT_DESCRIPTION, sw_measurement_type, + MEASUREMENT_VALUE, sw_measurement_value + ] + # The measurement value should be the last item (key + value) in the list + # to make it easier to modify its value later in the bootloader. + # A dictionary would be the best suited data structure to store these + # key-value pairs (claims), however dictionaries are not sorted, but for + # example the lists do keep to order of items which we care about now. + # An ordered dictionary could be used instead, but it would be converted + # to a dict before the encoding and this conversion may not keep the order + # of the items. + + if (len(key_value_list) % 2) != 0: + print('Error: The length of the sw component claim list must ' + 'be even (key + value).', file=sys.stderr) + sys.exit(1) + else: + claim_number = (int)(len(key_value_list) / 2) + + # The output of this function must be a CBOR encoded map (dictionary) of + # the SW component claims. The CBOR representation of an array and a map + # (dictionary) is quite similar. To convert the encoded list to a map, it + # is enough to modify the first byte (CBOR data item header) of the + # data. This applies up to 23 items (11 claims in this case) - until the 5 + # lower bits of the item header are used as an item count specifier. + + if claim_number > 11: + print('Error: There are more than 11 claims in the ' + 'list of sw component claims.', file=sys.stderr) + sys.exit(1) + + record_array = bytearray(cbor.dumps(key_value_list)) + # Modify the CBOR data item header (from array to map) + # 7..5 bits : Major type + # Array - 0x80 + # Map - 0xA0 + # 4..0 bits : Number of items + record_array[0] = 0xA0 + claim_number + + return bytes(record_array) diff --git a/tools/psa/tfm/bin_utils/imgtool_lib/image.py b/tools/psa/tfm/bin_utils/imgtool_lib/image.py index 67425d4ba3..93a454b313 100644 --- a/tools/psa/tfm/bin_utils/imgtool_lib/image.py +++ b/tools/psa/tfm/bin_utils/imgtool_lib/image.py @@ -1,5 +1,5 @@ # Copyright 2017 Linaro Limited -# Copyright (c) 2018, Arm Limited. +# Copyright (c) 2018-2019, Arm Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,11 +18,17 @@ Image signing and management. """ from . import version as versmod +from . import boot_record as br import hashlib import struct IMAGE_MAGIC = 0x96f3b83d IMAGE_HEADER_SIZE = 32 +TLV_HEADER_SIZE = 4 +PAYLOAD_DIGEST_SIZE = 32 # SHA256 hash +KEYHASH_SIZE = 32 +DEP_IMAGES_KEY = "images" +DEP_VERSIONS_KEY = "versions" # Image header flags. IMAGE_F = { @@ -31,11 +37,17 @@ IMAGE_F = { 'RAM_LOAD': 0x0000020, } TLV_VALUES = { 'KEYHASH': 0x01, + 'KEY' : 0x02, 'SHA256' : 0x10, - 'RSA2048': 0x20, } + 'RSA2048': 0x20, + 'RSA3072': 0x23, + 'DEPENDENCY': 0x40, + 'SEC_CNT': 0x50, + 'BOOT_RECORD': 0x60, } TLV_INFO_SIZE = 4 TLV_INFO_MAGIC = 0x6907 +TLV_PROT_INFO_MAGIC = 0x6908 # Sizes of the image trailer, depending on flash write size. trailer_sizes = { @@ -50,17 +62,25 @@ boot_magic = bytearray([ 0x2c, 0xb6, 0x79, 0x80, ]) class TLV(): - def __init__(self): + def __init__(self, magic=TLV_INFO_MAGIC): + self.magic = magic self.buf = bytearray() + def __len__(self): + return TLV_INFO_SIZE + len(self.buf) + def add(self, kind, payload): - """Add a TLV record. Kind should be a string found in TLV_VALUES above.""" + """ + Add a TLV record. Kind should be a string found in TLV_VALUES above. + """ buf = struct.pack('".format( + return "".format( self.version, self.header_size, + self.security_cnt, self.pad, len(self.payload)) @@ -103,30 +127,94 @@ class Image(): if any(v != 0 and v != b'\000' for v in self.payload[0:self.header_size]): raise Exception("Padding requested, but image does not start with zeros") - def sign(self, key, ramLoadAddress): - self.add_header(key, ramLoadAddress) - - tlv = TLV() - - sha = hashlib.sha256() - sha.update(self.payload) - digest = sha.digest() - - tlv.add('SHA256', digest) + def sign(self, sw_type, key, ramLoadAddress, dependencies=None): + image_version = (str(self.version.major) + '.' + + str(self.version.minor) + '.' + + str(self.version.revision)) + # Calculate the hash of the public key if key is not None: pub = key.get_public_bytes() sha = hashlib.sha256() sha.update(pub) pubbytes = sha.digest() - tlv.add('KEYHASH', pubbytes) + else: + pubbytes = bytes(KEYHASH_SIZE) - sig = key.sign(self.payload) + # The image hash is computed over the image header, the image itself + # and the protected TLV area. However, the boot record TLV (which is + # part of the protected area) should contain this hash before it is + # even calculated. For this reason the script fills this field with + # zeros and the bootloader will insert the right value later. + image_hash = bytes(PAYLOAD_DIGEST_SIZE) + + # Create CBOR encoded boot record + boot_record = br.create_sw_component_data(sw_type, image_version, + "SHA256", image_hash, + pubbytes) + + # Mandatory protected TLV area: TLV info header + # + security counter TLV + # + boot record TLV + # Size of the security counter TLV: header ('BBH') + payload ('I') + # = 8 Bytes + protected_tlv_size = TLV_INFO_SIZE + 8 + TLV_HEADER_SIZE \ + + len(boot_record) + + if dependencies is None: + dependencies_num = 0 + else: + # Size of a dependency TLV: + # header ('BBH') + payload('IBBHI') = 16 Bytes + dependencies_num = len(dependencies[DEP_IMAGES_KEY]) + protected_tlv_size += (dependencies_num * 16) + + # At this point the image is already on the payload, this adds + # the header to the payload as well + self.add_header(key, protected_tlv_size, ramLoadAddress) + + prot_tlv = TLV(TLV_PROT_INFO_MAGIC) + + # Protected TLVs must be added first, because they are also included + # in the hash calculation + payload = struct.pack('I', self.security_cnt) + prot_tlv.add('SEC_CNT', payload) + prot_tlv.add('BOOT_RECORD', boot_record) + + if dependencies_num != 0: + for i in range(dependencies_num): + payload = struct.pack( + '<'+'B3x'+'BBHI', + int(dependencies[DEP_IMAGES_KEY][i]), + dependencies[DEP_VERSIONS_KEY][i].major, + dependencies[DEP_VERSIONS_KEY][i].minor, + dependencies[DEP_VERSIONS_KEY][i].revision, + dependencies[DEP_VERSIONS_KEY][i].build + ) + prot_tlv.add('DEPENDENCY', payload) + + self.payload += prot_tlv.get() + + sha = hashlib.sha256() + sha.update(self.payload) + image_hash = sha.digest() + + tlv = TLV() + + tlv.add('SHA256', image_hash) + + if key is not None: + if key.get_public_key_format() == 'hash': + tlv.add('KEYHASH', pubbytes) + else: + tlv.add('KEY', pub) + + sig = key.sign(bytes(self.payload)) tlv.add(key.sig_tlv(), sig) self.payload += tlv.get() - def add_header(self, key, ramLoadAddress): + def add_header(self, key, protected_tlv_size, ramLoadAddress): """Install the image header. The key is needed to know the type of signature, and @@ -140,28 +228,28 @@ class Image(): fmt = ('<' + # type ImageHdr struct { - 'I' + # Magic uint32 - 'I' + # LoadAddr uint32 - 'H' + # HdrSz uint16 - 'H' + # Pad1 uint16 - 'I' + # ImgSz uint32 - 'I' + # Flags uint32 - 'BBHI' + # Vers ImageVersion - 'I' # Pad2 uint32 + 'I' + # Magic uint32 + 'I' + # LoadAddr uint32 + 'H' + # HdrSz uint16 + 'H' + # PTLVSz uint16 + 'I' + # ImgSz uint32 + 'I' + # Flags uint32 + 'BBHI' + # Vers ImageVersion + 'I' # Pad1 uint32 ) # } assert struct.calcsize(fmt) == IMAGE_HEADER_SIZE header = struct.pack(fmt, IMAGE_MAGIC, 0 if (ramLoadAddress is None) else ramLoadAddress, # LoadAddr self.header_size, - 0, # Pad1 - len(self.payload) - self.header_size, # ImageSz - flags, # Flags + protected_tlv_size, # TLV info header + Protected TLVs + len(self.payload) - self.header_size, # ImageSz + flags, self.version.major, self.version.minor or 0, self.version.revision or 0, self.version.build or 0, - 0) # Pad2 + 0) # Pad1 self.payload = bytearray(self.payload) self.payload[:len(header)] = header @@ -176,4 +264,4 @@ class Image(): pbytes = b'\xff' * padding pbytes += b'\xff' * (tsize - len(boot_magic)) pbytes += boot_magic - self.payload += pbytes \ No newline at end of file + self.payload += pbytes diff --git a/tools/psa/tfm/bin_utils/imgtool_lib/keys.py b/tools/psa/tfm/bin_utils/imgtool_lib/keys.py index fda3ed6710..7ee9671bd5 100644 --- a/tools/psa/tfm/bin_utils/imgtool_lib/keys.py +++ b/tools/psa/tfm/bin_utils/imgtool_lib/keys.py @@ -1,5 +1,5 @@ -# Copyright 2017 Linaro Limited -# Copyright (c) 2017-2018, Arm Limited. +# Copyright (c) 2017,2019 Linaro Limited. +# Copyright (c) 2017-2019, Arm Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,42 +18,64 @@ Cryptographic key management for imgtool. """ from __future__ import print_function -from Crypto.Hash import SHA256 -from Crypto.PublicKey import RSA -from Crypto.Signature import PKCS1_v1_5, PKCS1_PSS +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.hashes import SHA256 +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric.padding import PSS, PKCS1v15 +from cryptography.hazmat.primitives.asymmetric.padding import MGF1 import hashlib from pyasn1.type import namedtype, univ from pyasn1.codec.der.encoder import encode +# Sizes that bootutil will recognize +RSA_KEY_SIZES = [2048, 3072] + +# Public exponent +PUBLIC_EXPONENT = 65537 + # By default, we use RSA-PSS (PKCS 2.1). That can be overridden on # the command line to support the older (less secure) PKCS1.5 sign_rsa_pss = True AUTOGEN_MESSAGE = "/* Autogenerated by imgtool.py, do not edit. */" -class RSAPublicKey(univ.Sequence): - componentType = namedtype.NamedTypes( - namedtype.NamedType('modulus', univ.Integer()), - namedtype.NamedType('publicExponent', univ.Integer())) +class RSAUsageError(Exception): + pass -class RSA2048(): - def __init__(self, key): - """Construct an RSA2048 key with the given key data""" +class RSAutil(): + def __init__(self, key, public_key_format='hash'): + """Construct an RSA key with the given key data""" self.key = key + self.public_key_format = public_key_format + + def key_size(self): + return self.key.key_size + + def get_public_key_format(self): + return self.public_key_format @staticmethod - def generate(): - return RSA2048(RSA.generate(2048)) + def generate(key_size=2048): + if key_size not in RSA_KEY_SIZES: + raise RSAUsageError("Key size {} is not supported by MCUboot" + .format(key_size)) + return RSAutil(rsa.generate_private_key( + public_exponent=PUBLIC_EXPONENT, + key_size=key_size, + backend=default_backend())) def export_private(self, path): with open(path, 'wb') as f: - f.write(self.key.exportKey('PEM')) + f.write(self.key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption())) def get_public_bytes(self): - node = RSAPublicKey() - node['modulus'] = self.key.n - node['publicExponent'] = self.key.e - return bytearray(encode(node)) + return self.key.public_key().public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.PKCS1) def emit_c(self): print(AUTOGEN_MESSAGE) @@ -71,34 +93,44 @@ class RSA2048(): def sig_type(self): """Return the type of this signature (as a string)""" if sign_rsa_pss: - return "PKCS1_PSS_RSA2048_SHA256" + return "PKCS1_PSS_RSA{}_SHA256".format(self.key_size()) else: - return "PKCS15_RSA2048_SHA256" + return "PKCS15_RSA{}_SHA256".format(self.key_size()) def sig_len(self): - return 256 + return 256 if self.key_size() == 2048 else 384 def sig_tlv(self): - return "RSA2048" + return "RSA2048" if self.key_size() == 2048 else "RSA3072" def sign(self, payload): - converted_payload = bytes(payload) - sha = SHA256.new(converted_payload) if sign_rsa_pss: - signer = PKCS1_PSS.new(self.key) + signature = self.key.sign( + data=payload, + padding=PSS( + mgf=MGF1(SHA256()), + salt_length=32 + ), + algorithm=SHA256() + ) else: - signer = PKCS1_v1_5.new(self.key) - signature = signer.sign(sha) + signature = self.key.sign( + data=payload, + padding=PKCS1v15(), + algorithm=SHA256() + ) assert len(signature) == self.sig_len() return signature -def load(path): +def load(path, public_key_format='hash'): with open(path, 'rb') as f: pem = f.read() try: - key = RSA.importKey(pem) - if key.n.bit_length() != 2048: - raise Exception("Unsupported RSA bit length, only 2048 supported") - return RSA2048(key) + key = serialization.load_pem_private_key( + pem, + password=None, + backend=default_backend() + ) + return RSAutil(key, public_key_format) except ValueError: raise Exception("Unsupported RSA key file") diff --git a/tools/psa/tfm/bin_utils/macro_parser.py b/tools/psa/tfm/bin_utils/macro_parser.py new file mode 100644 index 0000000000..5e489a9c57 --- /dev/null +++ b/tools/psa/tfm/bin_utils/macro_parser.py @@ -0,0 +1,70 @@ +#! /usr/bin/env python3 +# +# ----------------------------------------------------------------------------- +# Copyright (c) 2019, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# ----------------------------------------------------------------------------- + + +import re +import os + +expression_re = re.compile(r"[(]?(([(]?(((0x)[0-9a-fA-F]+)|([0-9]+))[)]?)\s*([\+\-]\s*([(]?(((0x)[0-9a-fA-F]+)|([0-9]+))[)]?)\s*)*)[)]?") + +# Simple parser that takes a string and evaluates an expression from it. +# The expression might contain additions and subtractions amongst numbers that +# are written in decimal or hexadecimal form. +# The parses can process expressions in which the parentheses does not change +# the sign of the following number or numbers in an expression. +# Thus the parser can process the following expression: (x + y) +# However it will not calculate the correct sum for the expression below: +# (x - (y + z)) +def parse_and_sum(text): + m = expression_re.match(text) + if m is None: + msg = "The script was probably invoked manually" + msg += " with having certain macros nested in flash_layouts.h.\n" + msg += "Please revisit the flash_layout.h file and hardcode values" + msg += " for the (NON-)SECURE_IMAGE_OFFSET and" + msg += " (NON-)SECURE_IMAGE_MAX_SIZE macros" + raise Exception(msg) + + nums = re.findall(r'(0x[A-Fa-f0-9]+)|[\d]+', m.group(0)) + for i in range(len(nums)): + nums[i] = int(nums[i], 0) + ops = re.findall(r'\+|\-', m.group(0)) + sum = nums[0] + for i in range(len(ops)): + if ops[i] == '+': + sum += nums[i+1] + else: + sum -= nums[i+1] + return sum + + +# Opens a file that contains the macro of interest, then finds the macro with +# a regular expression, parses the expression that is defined for the given +# macro. Lastly it evaluates the expression with the parse_and_sum function +def evaluate_macro(file, regexp, matchGroupKey, matchGroupData): + regexp_compiled = re.compile(regexp) + + if os.path.isabs(file): + configFile = file + else: + scriptsDir = os.path.dirname(os.path.abspath(__file__)) + configFile = os.path.join(scriptsDir, file) + + macroValue = {} + with open(configFile, 'r') as macros_preprocessed_file: + for line in macros_preprocessed_file: + m = regexp_compiled.match(line) + if m is not None: + macroValue[m.group(matchGroupKey)] = \ + parse_and_sum(m.group(matchGroupData)) + + if (matchGroupKey == 0 and not macroValue): + macroValue["None"] = None + + return list(macroValue.values())[0] if (matchGroupKey == 0) else macroValue diff --git a/tools/targets/ARM_MUSCA_A1.py b/tools/targets/ARM_MUSCA_A1.py index 6143fad32f..4729e43b70 100644 --- a/tools/targets/ARM_MUSCA_A1.py +++ b/tools/targets/ARM_MUSCA_A1.py @@ -54,11 +54,14 @@ def musca_tfm_bin(t_self, non_secure_bin, secure_bin): #2. Run imgtool to sign the concatenated binary sign_args = Namespace( layout=flash_layout, - key=path_join(SCRIPT_DIR, 'musca_a1-root-rsa-2048.pem'), + key=path_join(SCRIPT_DIR, 'musca_a1-root-rsa-3072.pem'), + public_key_format=None, align=1, + dependencies=None, version=version.decode_version('1.0'), header_size=0x400, pad=0x100000, + security_counter=None, rsa_pkcs1_15=False, included_header=False, infile=concatenated_bin, diff --git a/tools/targets/musca_a1-root-rsa-2048.pem b/tools/targets/musca_a1-root-rsa-2048.pem deleted file mode 100644 index 78c0c34194..0000000000 --- a/tools/targets/musca_a1-root-rsa-2048.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA0QYIGhhELBjo+/33DaNPH7vuXvmq0ksY01rpbRiAGfnwnDQb -y/O8dNtC54x/EFN+Q14NVyxE0WcIDw27XO7ss5nf4E2EC6p3QWDtFShJpwG0PBDm -aYwvX6xBTZ5cFN/y+M89Hm/nW7q0qciIfkc8lMN3Z1RLqo04NcpiYX634RXbd3PU -vntyIYlpJPv4ZW5kPsgO14XVXErkUw0v/7f98xM5gz+jrtIPp2qd+f64zvoqvq+4 -4PqCN1T0PuEr0NMIWBj2XkzIiIExrV+wghfyimknI/Orhz6TGh3+6PgaJGZZ+Byr -3M5oG2ZkNez6DRGdr1w6p9FnxkfvsUssYuHRyQIDAQABAoIBAEahFCHFK1v/OtLT -eSSZl0Xw2dYr5QXULFpWsOOVUMv2QdB2ZyIehQKziEL3nYPlwpd+82EOa16awwVb -LYF0lnUFvLltV/4dJtjnqJTqnSCamc1mJIVrwiJA8XwJ07GWDuL2G//p7jJ3v05T -nZOV/KmD9xfqSvshZun+LgolqHqcrAa1f4cmuP9C9oqenZryljyfj7piaIZGI0JR -PrJJ5kImYJqRcMgKTyHP4L8nwQ4moMJr6zbfbWxxb5TC7KVZSQ9UKZZ+ZLuy/pkU -Qe4G8XSE0r+R9u4JCg87I1vgHhn8WJSxVX027OVUq5HfOzg2skQBTcExph5V9B2b -onNxd8UCgYEA/32PW+ZwRcdKXMj+QVkxXUd6xkXy7mTXPEaQuOLWZQgrSqAFH1l4 -5/6d099KAJrjM6kR8pKXtz72IIyMHTPm332ghymjKvaEl2XP9sF+f6FmYURar4y6 -8Zh3eivP86+Q/YzOGKwtRSziBMzrAfoIXgtwH8pwIPYLP3zBV4449ZsCgYEA0XC/ -gu2ub5M6EXBnjq9K2d4LlTyAPsIbAcMSwkhOUH4YJFS22qXLYQUA9zM+DUyLCrl/ -PKN2G0HQVgMb4DIbeHv8kXB5oGm5zfbWorWqOomXB3AsI7X8YDMtf/PsZV2amBei -qVskmPJQV21qFyeOcHlT+dHuRb0O0un3dK8RHmsCgYEApDCH4dJ80osZoflVVJ/C -VqTqJOOtFEFgBQ+AUCEPEQyn7aRaxmPUjJsXyKJVx3/ChV+g9hf5Qj1HJXHNVbMW -KwhsEpDSmHimizlV5clBxzntNpMcCHdTaJHILo5bbMqmThugE0ELMsp+UgFzAeky -WWXWX8fUOYqFff5prh/rQQMCgYBQQ8FhT+113Rp37HgDerJY5HvT6afMZV8sQbJC -uqsotepSohShnsBeoihIlF7HgfoXVhepCYwNzh8ll3NrbEiS2BFnO4+hJmOKx3pi -SPTAElLLCvYfiXL6+yII01ZZUpIYj5ZLCR7xbovTtZ7e2M4B1L2WFBoYp+eydO/c -y+rnmQKBgCh0gfyBT/OKPkfKv+Bbt8HcoxgEj+TyH+vYdeTbP9ZSJ6y5utMbPg7z -iLLbJ+9IcSwPCwJSmI+I0Om4xEp4ZblCrzAG7hWvG2NNzxQjmoOOrAANyTvJR/ap -N+UkQA4WrMSKEYyBlRS/hR9Unz31vMc2k9Re0ukWhWh/QksQGDfJ ------END RSA PRIVATE KEY----- diff --git a/tools/targets/musca_a1-root-rsa-2048.md b/tools/targets/musca_a1-root-rsa-3072.md similarity index 92% rename from tools/targets/musca_a1-root-rsa-2048.md rename to tools/targets/musca_a1-root-rsa-3072.md index ba112361f1..59100bc8d1 100644 --- a/tools/targets/musca_a1-root-rsa-2048.md +++ b/tools/targets/musca_a1-root-rsa-3072.md @@ -2,7 +2,7 @@ A default RSA key pair is given to the Musca-A1 target. -Public key was pre-compiled to `targets/TARGET_ARM_SSG/TARGET_MUSCA_A1/TARGET_MUSCA_A1_NS/device/mcuboot.bin` and private key is in `musca_a1-root-rsa-2048.pem`. +Public key was pre-compiled to `targets/TARGET_ARM_SSG/TARGET_MUSCA_A1/TARGET_MUSCA_A1_NS/device/mcuboot.bin` and private key is in `root-rsa-3072.pem`. DO NOT use them in production code, they are exclusively for testing! diff --git a/tools/targets/musca_a1-root-rsa-3072.pem b/tools/targets/musca_a1-root-rsa-3072.pem new file mode 100644 index 0000000000..23288bc101 --- /dev/null +++ b/tools/targets/musca_a1-root-rsa-3072.pem @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- From 6d6d1e695bd5ab7ac712624dfb635c7a1712c132 Mon Sep 17 00:00:00 2001 From: Devaraj Ranganna Date: Tue, 21 Jan 2020 10:44:38 +0000 Subject: [PATCH 2/2] Bump `cryptography` python module version The requirements.txt mandates 2.4.x version of cryptography python module which has an issue as it checks if the payload is an instance of bytes which is actually a bytearray. This issue is fixed in 2.5 version, hence bump up minimum version to 2.5. Signed-off-by: Devaraj Ranganna --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index df51dfb828..bc59271729 100644 --- a/requirements.txt +++ b/requirements.txt @@ -26,5 +26,5 @@ cmsis-pack-manager>=0.2.3,<0.3.0 pywin32==224;platform_system=='Windows' wmi==1.4.9;platform_system=='Windows' psutil==5.6.2 -cryptography>=2.4.x,<2.5 +cryptography>=2.5,<3 Click>=7.0,<7.1