1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
|
# Copyright (c) 2013, Google Inc.
#
# SPDX-License-Identifier: GPL-2.0+
#
# U-Boot Verified Boot Test
"""
This tests verified boot in the following ways:
For image verification:
- Create FIT (unsigned) with mkimage
- Check that verification shows that no keys are verified
- Sign image
- Check that verification shows that a key is now verified
For configuration verification:
- Corrupt signature and check for failure
- Create FIT (with unsigned configuration) with mkimage
- Check that image veriication works
- Sign the FIT and mark the key as 'required' for verification
- Check that image verification works
- Corrupt the signature
- Check that image verification no-longer works
Tests run with both SHA1 and SHA256 hashing.
"""
import pytest
import sys
import u_boot_utils as util
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('fit_signature')
def test_vboot(u_boot_console):
"""Test verified boot signing with mkimage and verification with 'bootm'.
This works using sandbox only as it needs to update the device tree used
by U-Boot to hold public keys from the signing process.
The SHA1 and SHA256 tests are combined into a single test since the
key-generation process is quite slow and we want to avoid doing it twice.
"""
def dtc(dts):
"""Run the device-tree compiler to compile a .dts file
The output file will be the same as the input file but with a .dtb
extension.
Args:
dts: Device tree file to compile.
"""
dtb = dts.replace('.dts', '.dtb')
util.cmd(cons, 'dtc %s %s%s -O dtb '
'-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb))
def run_bootm(test_type, expect_string):
"""Run a 'bootm' command U-Boot.
This always starts a fresh U-Boot instance since the device tree may
contain a new public key.
Args:
test_type: A string identifying the test type
expect_string: A string which is expected in the output
"""
cons.cleanup_spawn()
cons.ensure_spawned()
cons.log.action('%s: Test Verified Boot Run: %s' % (algo, test_type))
output = cons.run_command_list(
['sb load hostfs - 100 %stest.fit' % tmpdir,
'fdt addr 100',
'bootm 100'])
assert(expect_string in output)
def make_fit(its):
"""Make a new FIT from the .its source file
This runs 'mkimage -f' to create a new FIT.
Args:
its: Filename containing .its source
"""
util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f',
'%s%s' % (datadir, its), fit])
def sign_fit():
"""Sign the FIT
Signs the FIT and writes the signature into it. It also writes the
public key into the dtb.
"""
cons.log.action('%s: Sign images' % algo)
util.run_and_log(cons, [mkimage, '-F', '-k', tmpdir, '-K', dtb,
'-r', fit])
def test_with_algo(sha):
"""Test verified boot with the given hash algorithm
This is the main part of the test code. The same procedure is followed
for both hashing algorithms.
Args:
sha: Either 'sha1' or 'sha256', to select the algorithm to use
"""
global algo
algo = sha
# Compile our device tree files for kernel and U-Boot. These are
# regenerated here since mkimage will modify them (by adding a
# public key) below.
dtc('sandbox-kernel.dts')
dtc('sandbox-u-boot.dts')
# Build the FIT, but don't sign anything yet
cons.log.action('%s: Test FIT with signed images' % algo)
make_fit('sign-images-%s.its' % algo)
run_bootm('unsigned images', 'dev-')
# Sign images with our dev keys
sign_fit()
run_bootm('signed images', 'dev+')
# Create a fresh .dtb without the public keys
dtc('sandbox-u-boot.dts')
cons.log.action('%s: Test FIT with signed configuration' % algo)
make_fit('sign-configs-%s.its' % algo)
run_bootm('unsigned config', '%s+ OK' % algo)
# Sign images with our dev keys
sign_fit()
run_bootm('signed config', 'dev+')
cons.log.action('%s: Check signed config on the host' % algo)
util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', tmpdir,
'-k', dtb])
# Increment the first byte of the signature, which should cause failure
sig = util.cmd(cons, 'fdtget -t bx %s %s value' % (fit, sig_node))
byte_list = sig.split()
byte = int(byte_list[0], 16)
byte_list[0] = '%x' % (byte + 1)
sig = ' '.join(byte_list)
util.cmd(cons, 'fdtput -t bx %s %s value %s' % (fit, sig_node, sig))
run_bootm('Signed config with bad hash', 'Bad Data Hash')
cons.log.action('%s: Check bad config on the host' % algo)
util.run_and_log_expect_exception(cons, [fit_check_sign, '-f', fit,
'-k', dtb], 1, 'Failed to verify required signature')
cons = u_boot_console
tmpdir = cons.config.result_dir + '/'
tmp = tmpdir + 'vboot.tmp'
datadir = cons.config.source_dir + '/test/py/tests/vboot/'
fit = '%stest.fit' % tmpdir
mkimage = cons.config.build_dir + '/tools/mkimage'
fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign'
dtc_args = '-I dts -O dtb -i %s' % tmpdir
dtb = '%ssandbox-u-boot.dtb' % tmpdir
sig_node = '/configurations/conf@1/signature@1'
# Create an RSA key pair
public_exponent = 65537
util.cmd(cons, 'openssl genpkey -algorithm RSA -out %sdev.key '
'-pkeyopt rsa_keygen_bits:2048 '
'-pkeyopt rsa_keygen_pubexp:%d '
'2>/dev/null' % (tmpdir, public_exponent))
# Create a certificate containing the public key
util.cmd(cons, 'openssl req -batch -new -x509 -key %sdev.key -out '
'%sdev.crt' % (tmpdir, tmpdir))
# Create a number kernel image with zeroes
with open('%stest-kernel.bin' % tmpdir, 'w') as fd:
fd.write(5000 * chr(0))
try:
# We need to use our own device tree file. Remember to restore it
# afterwards.
old_dtb = cons.config.dtb
cons.config.dtb = dtb
test_with_algo('sha1')
test_with_algo('sha256')
finally:
cons.config.dtb = old_dtb
|