Test pre-load signing through the pkcs11 engine with a key imported into SoftHSM.
Add tests for: - PKCS#11 signing with PKCS1.5 padding. - PKCS#11 signing with PSS padding. - Reporting a missing OpenSSL bintool. - Rejecting a missing engine key name before signing. - Rejecting an engine signature with an incorrect size. Signed-off-by: Lorenz Kofler <[email protected]> --- Changes in v2: - Use the separate SoftHSM setup refactor introduced in patch 3. - Use one device-tree for both paddings. - Add tests for a missing engine key name and an incorrect signature size. tools/binman/ftest.py | 88 +++++++++++++++++++ .../test/security/pre_load_engine_pkcs11.dts | 24 +++++ 2 files changed, 112 insertions(+) create mode 100644 tools/binman/test/security/pre_load_engine_pkcs11.dts diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index f5eb895475f..c498e4c4959 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -5969,6 +5969,94 @@ def _SetupSoftHsm(self, softhsm2_util, prefix, private_key, return softhsm2_conf + def _CheckPreLoadEngine(self, padding): + """Sign an image through the pkcs11 engine and check the signature + + Args: + padding (str): Padding used by the device tree + """ + softhsm2_util = bintool.Bintool.create('softhsm2_util') + self._CheckBintool(softhsm2_util) + + try: + tools.run('openssl', 'engine', 'dynamic', '-c', 'pkcs11') + except ValueError: + self.skipTest('PKCS11 engine setup not functional, ' + 'did you install libengine-pkcs11-openssl?') + + softhsm2_conf = self._SetupSoftHsm( + softhsm2_util, f'pre_load_engine_{padding}.', + self.TestFile('dev.key')) + + data = tools.read_file( + self.TestFile('security/pre_load_engine_pkcs11.dts')) + data = data.replace(b'padding-name = "pkcs-1.5";', + f'padding-name = "{padding}";'.encode()) + dts = self._MakeInputFile(f'pre_load_engine_{padding}.dts', data) + + ossl_conf = self.TestFile('fit/openssl.conf') + with unittest.mock.patch.dict('os.environ', + {'OPENSSL_CONF': ossl_conf, + 'SOFTHSM2_CONF': softhsm2_conf}): + data = self._DoReadFile(dts) + + image_fname = tools.get_output_filename('image.bin') + is_signed = self._CheckPreload(image_fname, self.TestFile('dev.key'), + padding=padding) + + self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)]) + self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)]) + self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)]) + self.assertEqual(is_signed, True) + + def testPreLoadEnginePkcs11(self): + """Test a pre-load header signed through the pkcs11 OpenSSL engine""" + self._CheckPreLoadEngine('pkcs-1.5') + + def testPreLoadEnginePkcs11Pss(self): + """Test a pre-load header signed through the engine with pss padding""" + self._CheckPreLoadEngine('pss') + + def testPreLoadEngineMissingOpenssl(self): + """Test that a missing openssl is reported when using an engine""" + with terminal.capture() as (_, stderr): + self._DoTestFile('security/pre_load_engine_pkcs11.dts', + force_missing_bintools='openssl') + err = stderr.getvalue() + self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl") + + def testPreLoadEngineMissingKey(self): + """Test that an engine requires a key name before signing""" + from binman.etype import pre_load + + data = tools.read_file( + self.TestFile('security/pre_load_engine_pkcs11.dts')) + data = data.replace( + b'key-name = "pkcs11:object=test_key;type=private";', b'') + dts = self._MakeInputFile('pre_load_engine_missing_key.dts', data) + + with unittest.mock.patch.object( + pre_load.Entry_pre_load, '_sign', + side_effect=AssertionError('signing should not be attempted')): + with self.assertRaisesRegex( + ValueError, 'key-name must be provided when using an engine'): + self._DoReadFile(dts) + + def testPreLoadEngineInvalidKeySize(self): + """Test that an engine signature with the wrong size is rejected""" + from binman.btool import openssl + + def sign(input_fname, output_fname, key, hash_name, padding_name, + engine=None): + tools.write_file(output_fname, tools.get_bytes(0, 128)) + return '' + + with unittest.mock.patch.object(openssl.Bintoolopenssl, 'sign', + side_effect=sign): + with self.assertRaisesRegex(ValueError, + "don't have the expected size"): + self._DoReadFile('security/pre_load_engine_pkcs11.dts') + def _CheckSafeUniqueNames(self, *images): """Check all entries of given images for unsafe unique names""" for image in images: diff --git a/tools/binman/test/security/pre_load_engine_pkcs11.dts b/tools/binman/test/security/pre_load_engine_pkcs11.dts new file mode 100644 index 00000000000..eb4957c35b4 --- /dev/null +++ b/tools/binman/test/security/pre_load_engine_pkcs11.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha256,rsa2048"; + padding-name = "pkcs-1.5"; + key-name = "pkcs11:object=test_key;type=private"; + engine = "pkcs11"; + header-size = <4096>; + version = <0x11223344>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; -- 2.55.0
