[PATCH v2] binman: x509_cert: add PKCS#11/HSM signing support

Simon Glass sjg at chromium.org
Wed Apr 22 02:14:48 CEST 2026


Hi Sergio,

On 2026-04-21T13:36:46, Sergio Prado <sergio.prado at e-labworks.com> wrote:
> binman: x509_cert: add PKCS#11/HSM signing support
>
> Allow X509 certificates used for K3/TI secure boot to be signed via an
> HSM using the PKCS#11 standard.
>
> Two new make variables are introduced:
>
> - BINMAN_PKCS11_URI    PKCS#11 URI identifying the signing key on the HSM
> - BINMAN_PKCS11_MODULE Path to the PKCS#11 shared library (.so)
>
> When BINMAN_PKCS11_URI is set, it is passed to binman as the pkcs11-uri
> entry argument, which overrides the keyfile property at signing time.
>
> The openssl bintool gains three helper methods:
>
> - _pkcs11_use_provider() detects whether the pkcs11 provider (OpenSSL
>   >= 3.1) or the legacy pkcs11 engine (libp11) is available.
>
> - _build_key_args() builds the appropriate -key/-provider/-engine
>   arguments for the openssl command line, appending ?pin-value=<pin>
> [...]
>
> Makefile                        |   2 +
>  tools/binman/binman.rst         |  18 +++++++
>  tools/binman/btool/openssl.py   | 106 ++++++++++++++++++++++++++++++++++------
>  tools/binman/etype/x509_cert.py |  47 ++++++++++++++++--
>  tools/binman/ftest.py           |  96 ++++++++++++++++++++++++++++++++++++
>  5 files changed, 249 insertions(+), 20 deletions(-)

> diff --git a/tools/binman/btool/openssl.py b/tools/binman/btool/openssl.py
> @@ -35,18 +36,83 @@ class Bintoolopenssl(bintool.Bintool):
> +        if key_fname.startswith('pkcs11:'):
> +            pin = os.environ.get('PKCS11_PIN')
> +            if pin:
> +                key_fname = f'{key_fname}?pin-value={pin}'

This assumes the URI has no existing query parameters. It might be
worth using '&' instead of '?' if the URI already contains '?'

> diff --git a/tools/binman/btool/openssl.py b/tools/binman/btool/openssl.py
> @@ -35,18 +36,83 @@ class Bintoolopenssl(bintool.Bintool):
> +    def _run_cmd_pkcs11(self, pkcs11_module, *args):
> +        if pkcs11_module:
> +            os.environ['PKCS11_MODULE_PATH'] = pkcs11_module
> +            os.environ['PKCS11_PROVIDER_MODULE'] = pkcs11_module
> +        return self.run_cmd(*args)

Setting os.environ directly pollutes the global environment. This
could cause issues if binman signs multiple images with different
module paths in a single run. It would be cleaner to pass the env to
run_cmd() explicitly. What do you think?

Also, the environment variables persist even if pkcs11_module is None
on a subsequent call.

> diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
> @@ -6884,6 +6884,102 @@ fdt         fdtmap                Extract the devicetree blob from the fdtmap
> +    def testX509CertPkcs11(self):
> +        """Test X509 certificate creation using a PKCS#11 URI instead of a key file"""
> +        PKCS11_URI = 'pkcs11:token=test;object=mykey;type=private'
> +        PKCS11_MODULE = '/usr/lib/pkcs11/libsofthsm2.so'
> +        original = bintool.Bintool.run_cmd
> +
> +        def fake_openssl(self_tool, *args, binary=False):
> +            if self_tool.name != 'openssl':
> +                return original(self_tool, *args, binary=binary)
> +            arg_list = list(args)
> +            if arg_list == ['list', '-providers']:
> +                return 'pkcs11 provider'
> +            if '-out' in arg_list:
> +                tools.write_file(arg_list[arg_list.index('-out') + 1],
> +                                 b'\x00' * 32)
> +            return ''

The test only confirms the code path runs without error. You could
also add assertions that check the args contain '-provider pkcs11' and
that PKCS11_URI is passed as the key argument.

Reviewed-by: Simon Glass <sjg at chromium.org>

Regards,
Simon


More information about the U-Boot mailing list