blob: cc6c0c4dc4239817b4ba6f5f779be715d18ef645 [file] [log] [blame]
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -06001# SPDX-License-Identifier: GPL-2.0+
2#
3# Copyright (c) 2020,2021 Alexandru Gagniuc <mr.nuke.me@gmail.com>
4
5"""
6Test ECDSA signing of FIT images
7
8This test uses mkimage to sign an existing FIT image with an ECDSA key. The
9signature is then extracted, and verified against pyCryptodome.
10This test doesn't run the sandbox. It only checks the host tool 'mkimage'
11"""
12
Simon Glass998a2f22022-08-06 17:51:49 -060013import os
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060014import pytest
15import u_boot_utils as util
16from Cryptodome.Hash import SHA256
17from Cryptodome.PublicKey import ECC
18from Cryptodome.Signature import DSS
19
20class SignableFitImage(object):
21 """ Helper to manipulate a FIT image on disk """
22 def __init__(self, cons, file_name):
23 self.fit = file_name
24 self.cons = cons
25 self.signable_nodes = set()
26
27 def __fdt_list(self, path):
28 return util.run_and_log(self.cons, f'fdtget -l {self.fit} {path}')
29
30 def __fdt_set(self, node, **prop_value):
31 for prop, value in prop_value.items():
32 util.run_and_log(self.cons, f'fdtput -ts {self.fit} {node} {prop} {value}')
33
34 def __fdt_get_binary(self, node, prop):
35 numbers = util.run_and_log(self.cons, f'fdtget -tbi {self.fit} {node} {prop}')
36
37 bignum = bytearray()
38 for little_num in numbers.split():
39 bignum.append(int(little_num))
40
41 return bignum
42
43 def find_signable_image_nodes(self):
44 for node in self.__fdt_list('/images').split():
45 image = f'/images/{node}'
46 if 'signature' in self.__fdt_list(image):
47 self.signable_nodes.add(image)
48
49 return self.signable_nodes
50
51 def change_signature_algo_to_ecdsa(self):
52 for image in self.signable_nodes:
53 self.__fdt_set(f'{image}/signature', algo='sha256,ecdsa256')
54
55 def sign(self, mkimage, key_file):
Alexandru Gagniucbc8aa282021-02-19 12:45:20 -060056 util.run_and_log(self.cons, [mkimage, '-F', self.fit, f'-G{key_file}'])
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060057
58 def check_signatures(self, key):
59 for image in self.signable_nodes:
60 raw_sig = self.__fdt_get_binary(f'{image}/signature', 'value')
61 raw_bin = self.__fdt_get_binary(image, 'data')
62
63 sha = SHA256.new(raw_bin)
64 verifier = DSS.new(key, 'fips-186-3')
65 verifier.verify(sha, bytes(raw_sig))
66
67
68@pytest.mark.buildconfigspec('fit_signature')
69@pytest.mark.requiredtool('dtc')
70@pytest.mark.requiredtool('fdtget')
71@pytest.mark.requiredtool('fdtput')
72def test_fit_ecdsa(u_boot_console):
73 """ Test that signatures generated by mkimage are legible. """
74 def generate_ecdsa_key():
75 return ECC.generate(curve='prime256v1')
76
77 def assemble_fit_image(dest_fit, its, destdir):
78 dtc_args = f'-I dts -O dtb -i {destdir}'
79 util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f', its, dest_fit])
80
81 def dtc(dts):
82 dtb = dts.replace('.dts', '.dtb')
83 util.run_and_log(cons, f'dtc {datadir}/{dts} -O dtb -o {tempdir}/{dtb}')
84
85 cons = u_boot_console
86 mkimage = cons.config.build_dir + '/tools/mkimage'
87 datadir = cons.config.source_dir + '/test/py/tests/vboot/'
Simon Glass998a2f22022-08-06 17:51:49 -060088 tempdir = os.path.join(cons.config.result_dir, 'ecdsa')
89 os.makedirs(tempdir, exist_ok=True)
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060090 key_file = f'{tempdir}/ecdsa-test-key.pem'
91 fit_file = f'{tempdir}/test.fit'
92 dtc('sandbox-kernel.dts')
93
94 key = generate_ecdsa_key()
95
96 # Create a fake kernel image -- zeroes will do just fine
97 with open(f'{tempdir}/test-kernel.bin', 'w') as fd:
98 fd.write(500 * chr(0))
99
100 # invocations of mkimage expect to read the key from disk
101 with open(key_file, 'w') as f:
102 f.write(key.export_key(format='PEM'))
103
104 assemble_fit_image(fit_file, f'{datadir}/sign-images-sha256.its', tempdir)
105
106 fit = SignableFitImage(cons, fit_file)
107 nodes = fit.find_signable_image_nodes()
108 if len(nodes) == 0:
109 raise ValueError('FIT image has no "/image" nodes with "signature"')
110
111 fit.change_signature_algo_to_ecdsa()
112 fit.sign(mkimage, key_file)
113 fit.check_signatures(key)