Skip to content

Commit bc7331b

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). Signed-off-by: Devaraj Ranganna <[email protected]>
1 parent a81f016 commit bc7331b

File tree

12 files changed

+466
-133
lines changed

12 files changed

+466
-133
lines changed

targets/TARGET_ARM_SSG/TARGET_MUSCA_A1/TARGET_MUSCA_A1_NS/prebuilt/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ These images were compiled by the following command:
1010
### Repository
1111
https://git.trustedfirmware.org/trusted-firmware-m.git
1212
### Commit SHA
13-
8da7f102a6a6a1a99462f7f32edbd1565096c2f3
13+
6c5be4a98e4d7055ee49076ca4e515fb4b172e66
1414
```sh
1515
cmake ../ -G"Unix Makefiles" -DTARGET_PLATFORM=MUSCA_A -DCOMPILER=ARMCLANG -DCMAKE_BUILD_TYPE=Debug
1616
make
Binary file not shown.

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

0 commit comments

Comments
 (0)