test_fit_ecdsa.py 3.8 KB

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