Skip to content

Import latest python scripts and MCUBoot image #12231

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Binary file not shown.
22 changes: 5 additions & 17 deletions tools/psa/tfm/bin_utils/assemble.py
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -25,6 +25,7 @@
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]+)")
Expand All @@ -44,20 +45,8 @@ def find_slots(self):
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")
Expand Down Expand Up @@ -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',
Expand All @@ -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")

Expand Down
102 changes: 81 additions & 21 deletions tools/psa/tfm/bin_utils/imgtool.py
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -24,6 +24,9 @@
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]+)")

Expand Down Expand Up @@ -60,30 +63,37 @@ 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):
newVersion = version.increment_build_num(lastVersion)
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),
b=str(newVersion.minor),
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:
Expand All @@ -102,18 +112,38 @@ def do_getpub(args):
def do_sign(args):
if args.rsa_pkcs1_15:
keys.sign_rsa_pss = False

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=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=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)

Expand All @@ -122,6 +152,30 @@ def do_sign(args):
'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]:
Expand Down Expand Up @@ -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: "(<image_ID>,<image_version>), ... "''')
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')
Expand All @@ -174,4 +234,4 @@ def args():
subcmds[args.subcmd](args)

if __name__ == '__main__':
args()
args()
80 changes: 80 additions & 0 deletions tools/psa/tfm/bin_utils/imgtool_lib/boot_record.py
Original file line number Diff line number Diff line change
@@ -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)
Loading