[PATCH v4 6/6] test: binman: Add test for pkcs11 signed capsule

Quentin Schulz quentin.schulz at cherry.de
Tue Jan 20 16:53:04 CET 2026


Hi Wojciech,

On 1/20/26 9:12 AM, Wojciech Dubowik wrote:
> Test pkcs11 URI support for UEFI capsule generation. For
> simplicity only private key is defined in binman section
> as softhsm tool doesn't support certificate import (yet).
> 
> Signed-off-by: Wojciech Dubowik <Wojciech.Dubowik at mt.com>
> Reviewed-by: Simon Glass <simon.glass at canonical.com>
> ---
>   tools/binman/ftest.py                         | 53 +++++++++++++++++++
>   .../binman/test/351_capsule_signed_pkcs11.dts | 22 ++++++++
>   2 files changed, 75 insertions(+)
>   create mode 100644 tools/binman/test/351_capsule_signed_pkcs11.dts
> 
> diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
> index 21ec48d86fd1..a005a167e414 100644
> --- a/tools/binman/ftest.py
> +++ b/tools/binman/ftest.py
> @@ -7,6 +7,7 @@
>   #    python -m unittest func_test.TestFunctional.testHelp
>   
>   import collections
> +import configparser
>   import glob
>   import gzip
>   import hashlib
> @@ -7532,6 +7533,58 @@ fdt         fdtmap                Extract the devicetree blob from the fdtmap
>   
>           self._CheckCapsule(data, signed_capsule=True)
>   
> +    def testPkcs11SignedCapsuleGen(self):
> +        """Test generation of EFI capsule (with PKCS11)"""
> +        data = tools.read_file(self.TestFile("key.key"))
> +        private_key = self._MakeInputFile("key.key", data)
> +        data = tools.read_file(self.TestFile("key.pem"))
> +        cert_file = self._MakeInputFile("key.crt", data)
> +
> +        softhsm2_util = bintool.Bintool.create('softhsm2_util')
> +        self._CheckBintool(softhsm2_util)
> +
> +        prefix = "testPkcs11SignedCapsuleGen."
> +        # Configure SoftHSMv2
> +        data = tools.read_file(self.TestFile('340_softhsm2.conf'))
> +        softhsm2_conf = self._MakeInputFile(f'{prefix}softhsm2.conf', data)
> +        softhsm2_tokens_dir = self._MakeInputDir(f'{prefix}softhsm2.tokens')
> +        tools.write_file(softhsm2_conf, data +
> +                         f'\ndirectories.tokendir = \
> +                         {softhsm2_tokens_dir}\n'.encode("utf-8"))
> +

data is already in softhsm2_conf due to calling
self._MakeInputFile(f'{prefix}softhsm2.conf', data)
you can simply do the same I did in testFitSignPKCS11Simple and append 
to the file. Or you can append to data before you call _MakeInputFile().

> +        p11_kit_config = configparser.ConfigParser()
> +        out = tools.run('p11-kit', 'print-config')

Please create a bintool and call CheckBintool so we can skip the test if 
p11-kit isn't installed. It'll be a bit awkward because there's no 
--version, --help, to print the help text but not have a non-zero exit code.

> +        p11_kit_config.read_string(out)
> +        softhsm2_lib = p11_kit_config['softhsm2']['module']
> +

We probably want to have some try..except here, or a more forgiving 
.get('softhsm2', {}).get('module')
and fail the test if we don't get a path?

> +        os.environ['SOFTHSM2_CONF'] = softhsm2_conf

This is wrong, you'll be messing up with the environment of all tests 
being run in the same thread. You must use the "with 
unittest.mock.patch.dict('os.environ'," implementation I used in 
testFitSignPKCS11Simple.

> +        tools.run('softhsm2-util', '--init-token', '--free', '--label',
> +                  'U-Boot token', '--pin', '1111', '--so-pin',
> +                  '222222')
> +        tools.run('softhsm2-util', '--import', private_key, '--token',
> +                  'U-Boot token', '--label', 'test_key', '--id', '999999',
> +                  '--pin', '1111')
> +
> +        os.environ['PKCS11_MODULE_PATH'] = softhsm2_lib

Same issue as with SOFTHSM2_CONF, you must mock the environment and not 
write to it.

> +        data = self._DoReadFile('351_capsule_signed_pkcs11.dts')
> +
> +        self._CheckCapsule(data, signed_capsule=True)
> +
> +        # Verify signed capsule
> +        hdr = self._GetCapsuleHeaders(data)
> +        monotonic_count = hdr['EFI_FIRMWARE_IMAGE_AUTH.MONOTONIC_COUNT']
> +
> +        with open(self._indir + '/capsule_input.bin', 'ab') as f:

Please use self._MakeInputFile() and prefix it with the name of the 
method so there's no possible name clash.

> +            f.write(struct.pack('<Q', int(monotonic_count, 16)))
> +
> +        try:
> +            tools.run('openssl', 'smime', '-verify', '-inform', 'DER',

This means you depend on openssl being present. Should we maybe do 
something like

openssl = bintool.Bintool.create('openssl')
self._CheckBintool(openssl)
[...]
openssl.run_cmd(['smime', ...])

to skip the test if it isn't installed?

> +                      '-in', tools.get_output_dir() + '/capsule.efi-capsule.p7',

tools.get_output_filename('capsule.efi-capsule.p7')

> +                      '-content', self._indir + '/capsule_input.bin',

Reuse the path created by self._MakeInputFile().

> +                      '-CAfile', cert_file, '-no_check_time')
> +        except ValueError:
> +            self.assertIn('UEFI Capsule verification failed')
> +

I don't think this is valid. 
https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertIn 
states you need 2 (or three) arguments, only one's provided here.

I'm assuming we don't need the try..except since the raised Exception 
will just stop (and fail) the execution of the test but continue with 
the other ones? Can you check (locally) by purposefully using the wrong 
key for example?

Cheers,
Quentin


More information about the U-Boot mailing list