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

Reply via email to