blob: 3e816d68eb672ee1bbdaf83e635d8dceb1689094 [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
Simon Glassdb0e4532025-02-09 09:07:16 -070015import utils
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060016from 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 """
Simon Glass32701112025-02-09 09:07:17 -070022 def __init__(self, ubman, file_name):
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060023 self.fit = file_name
Simon Glass32701112025-02-09 09:07:17 -070024 self.ubman = ubman
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060025 self.signable_nodes = set()
26
27 def __fdt_list(self, path):
Simon Glass32701112025-02-09 09:07:17 -070028 return utils.run_and_log(self.ubman, f'fdtget -l {self.fit} {path}')
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060029
30 def __fdt_set(self, node, **prop_value):
31 for prop, value in prop_value.items():
Simon Glass32701112025-02-09 09:07:17 -070032 utils.run_and_log(self.ubman,
Simon Glassdb0e4532025-02-09 09:07:16 -070033 f'fdtput -ts {self.fit} {node} {prop} {value}')
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060034
35 def __fdt_get_binary(self, node, prop):
Simon Glass32701112025-02-09 09:07:17 -070036 numbers = utils.run_and_log(self.ubman,
Simon Glassdb0e4532025-02-09 09:07:16 -070037 f'fdtget -tbi {self.fit} {node} {prop}')
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060038
39 bignum = bytearray()
40 for little_num in numbers.split():
41 bignum.append(int(little_num))
42
43 return bignum
44
45 def find_signable_image_nodes(self):
46 for node in self.__fdt_list('/images').split():
47 image = f'/images/{node}'
48 if 'signature' in self.__fdt_list(image):
49 self.signable_nodes.add(image)
50
51 return self.signable_nodes
52
53 def change_signature_algo_to_ecdsa(self):
54 for image in self.signable_nodes:
55 self.__fdt_set(f'{image}/signature', algo='sha256,ecdsa256')
56
57 def sign(self, mkimage, key_file):
Simon Glass32701112025-02-09 09:07:17 -070058 utils.run_and_log(self.ubman, [mkimage, '-F', self.fit, f'-G{key_file}'])
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060059
60 def check_signatures(self, key):
61 for image in self.signable_nodes:
62 raw_sig = self.__fdt_get_binary(f'{image}/signature', 'value')
63 raw_bin = self.__fdt_get_binary(image, 'data')
64
65 sha = SHA256.new(raw_bin)
66 verifier = DSS.new(key, 'fips-186-3')
67 verifier.verify(sha, bytes(raw_sig))
68
69
70@pytest.mark.buildconfigspec('fit_signature')
71@pytest.mark.requiredtool('dtc')
72@pytest.mark.requiredtool('fdtget')
73@pytest.mark.requiredtool('fdtput')
Simon Glassddba5202025-02-09 09:07:14 -070074def test_fit_ecdsa(ubman):
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060075 """ Test that signatures generated by mkimage are legible. """
76 def generate_ecdsa_key():
77 return ECC.generate(curve='prime256v1')
78
79 def assemble_fit_image(dest_fit, its, destdir):
80 dtc_args = f'-I dts -O dtb -i {destdir}'
Simon Glass32701112025-02-09 09:07:17 -070081 utils.run_and_log(ubman, [mkimage, '-D', dtc_args, '-f', its, dest_fit])
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060082
83 def dtc(dts):
84 dtb = dts.replace('.dts', '.dtb')
Simon Glass32701112025-02-09 09:07:17 -070085 utils.run_and_log(ubman, f'dtc {datadir}/{dts} -O dtb -o {tempdir}/{dtb}')
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060086
Simon Glass32701112025-02-09 09:07:17 -070087 mkimage = ubman.config.build_dir + '/tools/mkimage'
88 datadir = ubman.config.source_dir + '/test/py/tests/vboot/'
89 tempdir = os.path.join(ubman.config.result_dir, 'ecdsa')
Simon Glass998a2f22022-08-06 17:51:49 -060090 os.makedirs(tempdir, exist_ok=True)
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -060091 key_file = f'{tempdir}/ecdsa-test-key.pem'
92 fit_file = f'{tempdir}/test.fit'
93 dtc('sandbox-kernel.dts')
94
95 key = generate_ecdsa_key()
96
97 # Create a fake kernel image -- zeroes will do just fine
98 with open(f'{tempdir}/test-kernel.bin', 'w') as fd:
99 fd.write(500 * chr(0))
100
101 # invocations of mkimage expect to read the key from disk
102 with open(key_file, 'w') as f:
103 f.write(key.export_key(format='PEM'))
104
105 assemble_fit_image(fit_file, f'{datadir}/sign-images-sha256.its', tempdir)
106
Simon Glass32701112025-02-09 09:07:17 -0700107 fit = SignableFitImage(ubman, fit_file)
Alexandru Gagniuc9b6fc0c2021-02-19 12:45:15 -0600108 nodes = fit.find_signable_image_nodes()
109 if len(nodes) == 0:
110 raise ValueError('FIT image has no "/image" nodes with "signature"')
111
112 fit.change_signature_algo_to_ecdsa()
113 fit.sign(mkimage, key_file)
114 fit.check_signatures(key)