Skip to content

Commit 18a5629

Browse files
committed
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). *Note* Python signing scripts need `python3`, therefore mbed-cli has to be installed using `pip3` or else building an image for Armv8-M based SoCs will fail. Signed-off-by: Devaraj Ranganna <[email protected]>
1 parent dc63202 commit 18a5629

File tree

11 files changed

+461
-131
lines changed

11 files changed

+461
-131
lines changed
Binary file not shown.

targets/targets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5757,7 +5757,7 @@
57575757
"inherits": ["Target"],
57585758
"default_toolchain": "ARMC6",
57595759
"extra_labels": ["ARM_SSG", "MUSCA_A1"],
5760-
"forced_reset_timeout": 7,
5760+
"forced_reset_timeout": 13,
57615761
"release_versions": ["5"]
57625762
},
57635763
"ARM_MUSCA_A1_NS": {

tools/psa/tfm/bin_utils/assemble.py

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#! /usr/bin/env python3
22
#
33
# Copyright 2017 Linaro Limited
4-
# Copyright (c) 2017-2018, Arm Limited.
4+
# Copyright (c) 2017-2019, Arm Limited.
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import re
2626
import os
2727
import shutil
28+
from . import macro_parser
2829

2930
offset_re = re.compile(r"^#define ([0-9A-Z_]+)_IMAGE_OFFSET\s+((0x)?[0-9a-fA-F]+)")
3031
size_re = re.compile(r"^#define ([0-9A-Z_]+)_IMAGE_MAX_SIZE\s+((0x)?[0-9a-fA-F]+)")
@@ -44,20 +45,8 @@ def find_slots(self):
4445
offsets = {}
4546
sizes = {}
4647

47-
if os.path.isabs(self.layout_path):
48-
configFile = self.layout_path
49-
else:
50-
scriptsDir = os.path.dirname(os.path.abspath(__file__))
51-
configFile = os.path.join(scriptsDir, self.layout_path)
52-
53-
with open(configFile, 'r') as fd:
54-
for line in fd:
55-
m = offset_re.match(line)
56-
if m is not None:
57-
offsets[m.group(1)] = int(m.group(2), 0)
58-
m = size_re.match(line)
59-
if m is not None:
60-
sizes[m.group(1)] = int(m.group(2), 0)
48+
offsets = macro_parser.evaluate_macro(self.layout_path, offset_re, 1, 2)
49+
sizes = macro_parser.evaluate_macro(self.layout_path, size_re, 1, 2)
6150

6251
if 'SECURE' not in offsets:
6352
raise Exception("Image config does not have secure partition")
@@ -86,7 +75,7 @@ def main():
8675
parser = argparse.ArgumentParser()
8776

8877
parser.add_argument('-l', '--layout', required=True,
89-
help='Location of the memory layout file')
78+
help='Location of the file that contains preprocessed macros')
9079
parser.add_argument('-s', '--secure', required=True,
9180
help='Unsigned secure image')
9281
parser.add_argument('-n', '--non_secure',
@@ -97,7 +86,6 @@ def main():
9786
args = parser.parse_args()
9887
output = Assembly(args.layout, args.output)
9988

100-
10189
output.add_image(args.secure, "SECURE")
10290
output.add_image(args.non_secure, "NON_SECURE")
10391

tools/psa/tfm/bin_utils/imgtool.py

Lines changed: 81 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#! /usr/bin/env python3
22
#
33
# Copyright 2017 Linaro Limited
4-
# Copyright (c) 2018, Arm Limited.
4+
# Copyright (c) 2018-2019, Arm Limited.
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
@@ -24,6 +24,9 @@
2424
from .imgtool_lib import version
2525
import sys
2626

27+
sign_bin_size_re = re.compile(r"^\s*RE_SIGN_BIN_SIZE\s*=\s*(.*)")
28+
image_load_address_re = re.compile(r"^\s*RE_IMAGE_LOAD_ADDRESS\s*=\s*(.*)")
29+
2730
def find_load_address(args):
2831
load_address_re = re.compile(r"^#define\sIMAGE_LOAD_ADDRESS\s+(0x[0-9a-fA-F]+)")
2932

@@ -60,30 +63,37 @@ def get_last_version(path):
6063

6164
def next_version_number(args, defaultVersion, path):
6265
newVersion = None
66+
versionProvided = False
6367
if (version.compare(args.version, defaultVersion) == 0): # Default version
6468
lastVersion = get_last_version(path)
6569
if (lastVersion is not None):
6670
newVersion = version.increment_build_num(lastVersion)
6771
else:
6872
newVersion = version.increment_build_num(defaultVersion)
6973
else: # Version number has been explicitly provided (not using the default)
74+
versionProvided = True
7075
newVersion = args.version
7176
versionString = "{a}.{b}.{c}+{d}".format(
7277
a=str(newVersion.major),
7378
b=str(newVersion.minor),
7479
c=str(newVersion.revision),
7580
d=str(newVersion.build)
7681
)
77-
with open(path, "w") as newFile:
78-
newFile.write(versionString)
82+
if not versionProvided:
83+
with open(path, "w") as newFile:
84+
newFile.write(versionString)
7985
print("**[INFO]** Image version number set to " + versionString)
8086
return newVersion
8187

8288
def gen_rsa2048(args):
83-
keys.RSA2048.generate().export_private(args.key)
89+
keys.RSAutil.generate().export_private(args.key)
90+
91+
def gen_rsa3072(args):
92+
keys.RSAutil.generate(key_size=3072).export_private(args.key)
8493

8594
keygens = {
86-
'rsa-2048': gen_rsa2048, }
95+
'rsa-2048': gen_rsa2048,
96+
'rsa-3072': gen_rsa3072, }
8797

8898
def do_keygen(args):
8999
if args.type not in keygens:
@@ -102,18 +112,38 @@ def do_getpub(args):
102112
def do_sign(args):
103113
if args.rsa_pkcs1_15:
104114
keys.sign_rsa_pss = False
115+
116+
version_num = next_version_number(args,
117+
version.decode_version("0"),
118+
"lastVerNum.txt")
119+
120+
if args.security_counter is None:
121+
# Security counter has not been explicitly provided,
122+
# generate it from the version number
123+
args.security_counter = ((version_num.major << 24)
124+
+ (version_num.minor << 16)
125+
+ version_num.revision)
126+
127+
if "_s.c" in args.layout:
128+
sw_type = "SPE"
129+
elif "_ns.c" in args.layout:
130+
sw_type = "NSPE"
131+
else:
132+
sw_type = "NSPE_SPE"
133+
134+
pad_size = args.pad
105135
img = image.Image.load(args.infile,
106-
version=next_version_number(args,
107-
version.decode_version("0"),
108-
"lastVerNum.txt"),
109-
header_size=args.header_size,
110-
included_header=args.included_header,
111-
pad=args.pad)
112-
key = keys.load(args.key) if args.key else None
113-
img.sign(key, find_load_address(args))
114-
115-
if args.pad:
116-
img.pad_to(args.pad, args.align)
136+
version=version_num,
137+
header_size=args.header_size,
138+
security_cnt=args.security_counter,
139+
included_header=args.included_header,
140+
pad=pad_size)
141+
key = keys.load(args.key, args.public_key_format) if args.key else None
142+
ram_load_address = find_load_address(args)
143+
img.sign(sw_type, key, ram_load_address, args.dependencies)
144+
145+
if pad_size:
146+
img.pad_to(pad_size, args.align)
117147

118148
img.save(args.outfile)
119149

@@ -122,6 +152,30 @@ def do_sign(args):
122152
'getpub': do_getpub,
123153
'sign': do_sign, }
124154

155+
156+
def get_dependencies(text):
157+
if text is not None:
158+
versions = []
159+
images = re.findall(r"\((\d+)", text)
160+
if len(images) == 0:
161+
msg = "Image dependency format is invalid: {}".format(text)
162+
raise argparse.ArgumentTypeError(msg)
163+
raw_versions = re.findall(r",\s*([0-9.+]+)\)", text)
164+
if len(images) != len(raw_versions):
165+
msg = '''There's a mismatch between the number of dependency images
166+
and versions in: {}'''.format(text)
167+
raise argparse.ArgumentTypeError(msg)
168+
for raw_version in raw_versions:
169+
try:
170+
versions.append(version.decode_version(raw_version))
171+
except ValueError as e:
172+
print(e)
173+
dependencies = dict()
174+
dependencies[image.DEP_IMAGES_KEY] = images
175+
dependencies[image.DEP_VERSIONS_KEY] = versions
176+
return dependencies
177+
178+
125179
def alignment_value(text):
126180
value = int(text)
127181
if value not in [1, 2, 4, 8]:
@@ -149,17 +203,23 @@ def args():
149203
getpub.add_argument('-l', '--lang', metavar='lang', default='c')
150204

151205
sign = subs.add_parser('sign', help='Sign an image with a private key')
152-
sign.add_argument('--layout', required=True,
153-
help='Location of the memory layout file')
206+
sign.add_argument('-l', '--layout', required=True,
207+
help='Location of the file that contains preprocessed macros')
154208
sign.add_argument('-k', '--key', metavar='filename')
209+
sign.add_argument("-K", "--public-key-format",
210+
help='In what format to add the public key to the image manifest: full or hash',
211+
metavar='pub_key_format', choices=['full', 'hash'], default='hash')
155212
sign.add_argument("--align", type=alignment_value, required=True)
156213
sign.add_argument("-v", "--version", type=version.decode_version,
157214
default="0.0.0+0")
215+
sign.add_argument("-d", "--dependencies", type=get_dependencies,
216+
required=False, help='''Add dependence on another image,
217+
format: "(<image_ID>,<image_version>), ... "''')
218+
sign.add_argument("-s", "--security-counter", type=intparse,
219+
help='Specify explicitly the security counter value')
158220
sign.add_argument("-H", "--header-size", type=intparse, required=True)
159221
sign.add_argument("--included-header", default=False, action='store_true',
160222
help='Image has gap for header')
161-
sign.add_argument("--pad", type=intparse,
162-
help='Pad image to this many bytes, adding trailer magic')
163223
sign.add_argument("--rsa-pkcs1-15",
164224
help='Use old PKCS#1 v1.5 signature algorithm',
165225
default=False, action='store_true')
@@ -174,4 +234,4 @@ def args():
174234
subcmds[args.subcmd](args)
175235

176236
if __name__ == '__main__':
177-
args()
237+
args()
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
2+
# Copyright (c) 2019, Arm Limited.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import os
17+
import sys
18+
import cbor
19+
20+
21+
# SW component IDs
22+
SW_COMPONENT_RANGE = 0
23+
SW_COMPONENT_TYPE = SW_COMPONENT_RANGE + 1
24+
MEASUREMENT_VALUE = SW_COMPONENT_RANGE + 2
25+
SW_COMPONENT_VERSION = SW_COMPONENT_RANGE + 4
26+
SIGNER_ID = SW_COMPONENT_RANGE + 5
27+
MEASUREMENT_DESCRIPTION = SW_COMPONENT_RANGE + 6
28+
29+
30+
def create_sw_component_data(sw_type, sw_version, sw_measurement_type,
31+
sw_measurement_value, sw_signer_id):
32+
33+
# List of SW component claims (key ID + value)
34+
key_value_list = [
35+
SW_COMPONENT_TYPE, sw_type,
36+
SW_COMPONENT_VERSION, sw_version,
37+
SIGNER_ID, sw_signer_id,
38+
MEASUREMENT_DESCRIPTION, sw_measurement_type,
39+
MEASUREMENT_VALUE, sw_measurement_value
40+
]
41+
# The measurement value should be the last item (key + value) in the list
42+
# to make it easier to modify its value later in the bootloader.
43+
# A dictionary would be the best suited data structure to store these
44+
# key-value pairs (claims), however dictionaries are not sorted, but for
45+
# example the lists do keep to order of items which we care about now.
46+
# An ordered dictionary could be used instead, but it would be converted
47+
# to a dict before the encoding and this conversion may not keep the order
48+
# of the items.
49+
50+
if (len(key_value_list) % 2) != 0:
51+
print('Error: The length of the sw component claim list must '
52+
'be even (key + value).', file=sys.stderr)
53+
sys.exit(1)
54+
else:
55+
claim_number = (int)(len(key_value_list) / 2)
56+
57+
# The output of this function must be a CBOR encoded map (dictionary) of
58+
# the SW component claims. The CBOR representation of an array and a map
59+
# (dictionary) is quite similar. To convert the encoded list to a map, it
60+
# is enough to modify the first byte (CBOR data item header) of the
61+
# data. This applies up to 23 items (11 claims in this case) - until the 5
62+
# lower bits of the item header are used as an item count specifier.
63+
64+
if claim_number > 11:
65+
print('Error: There are more than 11 claims in the '
66+
'list of sw component claims.', file=sys.stderr)
67+
sys.exit(1)
68+
69+
record_array = bytearray(cbor.dumps(key_value_list))
70+
# Modify the CBOR data item header (from array to map)
71+
# 7..5 bits : Major type
72+
# Array - 0x80
73+
# Map - 0xA0
74+
# 4..0 bits : Number of items
75+
record_array[0] = 0xA0 + claim_number
76+
77+
return bytes(record_array)

0 commit comments

Comments
 (0)