diff options
author | David Vincze <david.vincze@arm.com> | 2019-09-27 17:40:29 +0200 |
---|---|---|
committer | Tamas Ban <tamas.ban@arm.com> | 2019-10-17 10:29:19 +0000 |
commit | 4b84de59de0b883b5e8c3e97b9ed3f7f33618322 (patch) | |
tree | bbd4f6484f9a9f3a54b26781d0b4eacbc25964c0 | |
parent | 21f5bf6127bee8ae8f1976ca186dbc7f8dcb5082 (diff) | |
download | trusted-firmware-m-4b84de59de0b883b5e8c3e97b9ed3f7f33618322.tar.gz |
Boot: Add measured boot record TLV to image manifest
Generate CBOR encoded boot record structure at build time and add as a
new type of TLV to the image manifest for device attestation. This new
TLV can represent the image to which it belongs as one item of the
SW_COMPONENTS array in an IAT token.
Change-Id: I84adf0faa57c40428c3e48cce4398e346d9192b7
Signed-off-by: David Vincze <david.vincze@arm.com>
-rw-r--r-- | bl2/ext/mcuboot/scripts/imgtool.py | 9 | ||||
-rw-r--r-- | bl2/ext/mcuboot/scripts/imgtool_lib/boot_record.py | 77 | ||||
-rw-r--r-- | bl2/ext/mcuboot/scripts/imgtool_lib/image.py | 50 | ||||
-rw-r--r-- | docs/user_guides/tfm_sw_requirement.rst | 5 |
4 files changed, 129 insertions, 12 deletions
diff --git a/bl2/ext/mcuboot/scripts/imgtool.py b/bl2/ext/mcuboot/scripts/imgtool.py index 1b539c2192..2d1d97bbb9 100644 --- a/bl2/ext/mcuboot/scripts/imgtool.py +++ b/bl2/ext/mcuboot/scripts/imgtool.py @@ -103,6 +103,13 @@ def do_sign(args): + (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 = macro_parser.evaluate_macro(args.layout, sign_bin_size_re, 0, 1) img = image.Image.load(args.infile, version=version_num, @@ -112,7 +119,7 @@ def do_sign(args): pad=pad_size) key = keys.load(args.key, args.public_key_format) if args.key else None ram_load_address = macro_parser.evaluate_macro(args.layout, image_load_address_re, 0, 1) - img.sign(key, ram_load_address, args.dependencies) + img.sign(sw_type, key, ram_load_address, args.dependencies) if pad_size: img.pad_to(pad_size, args.align) diff --git a/bl2/ext/mcuboot/scripts/imgtool_lib/boot_record.py b/bl2/ext/mcuboot/scripts/imgtool_lib/boot_record.py new file mode 100644 index 0000000000..41887bb5f0 --- /dev/null +++ b/bl2/ext/mcuboot/scripts/imgtool_lib/boot_record.py @@ -0,0 +1,77 @@ + +# Copyright (c) 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. +# 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. + +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/bl2/ext/mcuboot/scripts/imgtool_lib/image.py b/bl2/ext/mcuboot/scripts/imgtool_lib/image.py index cb97a350b8..d89ec990f1 100644 --- a/bl2/ext/mcuboot/scripts/imgtool_lib/image.py +++ b/bl2/ext/mcuboot/scripts/imgtool_lib/image.py @@ -18,6 +18,7 @@ Image signing and management. """ from . import version as versmod +from . import boot_record as br import hashlib import struct @@ -41,7 +42,8 @@ TLV_VALUES = { 'RSA2048': 0x20, 'RSA3072': 0x23, 'DEPENDENCY': 0x40, - 'SEC_CNT': 0x50, } + 'SEC_CNT': 0x50, + 'BOOT_RECORD': 0x60, } TLV_INFO_SIZE = 4 TLV_INFO_MAGIC = 0x6907 @@ -116,10 +118,39 @@ 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, dependencies=None): - # Size of the security counter TLV: - # header ('BBH') + payload ('I') = 8 Bytes - protected_tlv_size = TLV_INFO_SIZE + 8 + 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() + else: + pubbytes = bytes(KEYHASH_SIZE) + + # 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 @@ -135,6 +166,7 @@ class Image(): payload = struct.pack('I', self.security_cnt) tlv.add('SEC_CNT', payload) + tlv.add('BOOT_RECORD', boot_record) if dependencies_num != 0: for i in range(dependencies_num): @@ -153,7 +185,6 @@ class Image(): full_size = (TLV_INFO_SIZE + len(tlv.buf) + TLV_HEADER_SIZE + PAYLOAD_DIGEST_SIZE) if key is not None: - pub = key.get_public_bytes() if key.get_public_key_format() == 'hash': tlv_key_data_size = KEYHASH_SIZE else: @@ -166,15 +197,12 @@ class Image(): sha = hashlib.sha256() sha.update(self.payload) - digest = sha.digest() + image_hash = sha.digest() - tlv.add('SHA256', digest) + tlv.add('SHA256', image_hash) if key is not None: if key.get_public_key_format() == 'hash': - sha = hashlib.sha256() - sha.update(pub) - pubbytes = sha.digest() tlv.add('KEYHASH', pubbytes) else: tlv.add('KEY', pub) diff --git a/docs/user_guides/tfm_sw_requirement.rst b/docs/user_guides/tfm_sw_requirement.rst index 432b2375e7..fb0184659e 100644 --- a/docs/user_guides/tfm_sw_requirement.rst +++ b/docs/user_guides/tfm_sw_requirement.rst @@ -19,6 +19,7 @@ To build the TF-M firmware the following tools are needed: "pyasn1",,"Firmware" "jinja2",,"Firmware" "cryptography",,"Firmware" + "cbor",,"Firmware" "Doxygen",">1.8","Reference manual" "Sphinx",">1.4","User Guide" "sphinxcontrib-plantuml",,"User Guide" @@ -68,10 +69,12 @@ Dependency chain: fw --> pyasn1 fw --> yaml fw --> jinja2 + fw --> cbor cryptography --> Python3 pyasn1 --> Python3 yaml --> Python3 jinja2 --> Python3 + cbor --> Python3 [*] --> u_guide u_guide --> Sphinx @@ -217,6 +220,7 @@ Ubuntu setup - cryptography - pyasn1 - yaml +- cbor - jinja2 v2.10 - sudo apt-get install python3-crypto python3-pyasn1 python3-yaml python3-jinja2 @@ -270,6 +274,7 @@ Windows + Cygwin setup - cryptography (pip3 install --user cryptography) - pyasn1 (pip3 install --user pyasn1) - pyyaml (pip3 install --user pyyaml) +- cbor (pip3 install --user cbor) - jinja2 (pip3 install --user jinja2) - Python3 pip - `SRecord v1.63 <https://sourceforge.net/projects/srecord/>`__ (for Musca test |