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

Simon Glass sjg at chromium.org
Fri May 1 04:12:14 CEST 2026


Hi Sergio,

On 2026-04-23T15:45:36, Sergio Prado <sergio.prado at e-labworks.com> wrote:
> binman: x509_cert: add PKCS#11/HSM signing support
>
> Allow X509 certificates used for TI K3 secure boot to be signed via an
> HSM using the PKCS#11 standard, so that the private key never leaves
> the hardware token.
>
> Two new make variables are introduced:
>
>   BINMAN_PKCS11_URI    PKCS#11 URI identifying the signing key, e.g.
>                        pkcs11:token=mytoken;object=mykey;type=private
>   BINMAN_PKCS11_MODULE Path to the PKCS#11 shared library (.so), e.g.
>                        /usr/lib/softhsm/libsofthsm2.so
>
> When BINMAN_PKCS11_URI is set, it is forwarded to binman as the
> pkcs11-uri entry argument, overriding the keyfile property at signing
> time. BINMAN_PKCS11_MODULE is optional and only needed when the PKCS#11
> library is not already configured via openssl.cnf or the
> PKCS11_MODULE_PATH environment variable.
>
> The openssl bintool gains three helper methods:
> [...]
>
> Makefile                        |   2 +
>  tools/binman/binman.rst         |  18 +++++++
>  tools/binman/btool/openssl.py   | 117 ++++++++++++++++++++++++++++++++++------
>  tools/binman/etype/x509_cert.py |  47 ++++++++++++++--
>  tools/binman/ftest.py           | 110 +++++++++++++++++++++++++++++++++++++
>  5 files changed, 274 insertions(+), 20 deletions(-)

> diff --git a/tools/binman/etype/x509_cert.py b/tools/binman/etype/x509_cert.py
> @@ -61,6 +85,10 @@ class Entry_x509_cert(Entry_collection):
>          self.key_fname = self.GetEntryArgsOrProps([
>              EntryArg('keyfile', str)], required=True)[0]
>          self.sw_rev = fdt_util.GetInt(self._node, 'sw-rev', 1)
> +        self.pkcs11_uri = self.GetEntryArgsOrProps([
> +            EntryArg('pkcs11-uri', str)], required=False)[0]
> +        self.pkcs11_module = self.GetEntryArgsOrProps([
> +            EntryArg('pkcs11-module', str)], required=False)[0]

keyfile is still required=True, so a user driving this from
BINMAN_PKCS11_URI must also supply a dummy keyfile= on the binman
command line, otherwise ReadNode() raises before the override in
GetCertificate() runs. If pkcs11-uri is to be a real alternative to
keyfile, one of them should be required, not both.

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

Splicing pin-value=<pin> into the URI puts the cleartext PIN on
openssl's argv, where any local user can read it from
/proc/<pid>/cmdline or ps. Maybe that isn't critical depending on what
machine it is running in. Both pkcs11-provider and the libp11 engine
accept the PIN via pin-source=env:PKCS11_PIN (or OPENSSL_PIN for the
provider), which keeps it out of argv - could you use one of those
instead?

> diff --git a/tools/binman/etype/x509_cert.py b/tools/binman/etype/x509_cert.py
> @@ -80,6 +108,13 @@ class Entry_x509_cert(Entry_collection):
> +        # Override keyfile with the PKCS#11 URI if provided. This must be
> +        # done here rather than in ReadNode(), because subclasses (e.g.
> +        # ti_secure_rom, ti_secure) call super().ReadNode() and then
> +        # overwrite self.key_fname with the DTS 'keyfile' property.
> +        if self.pkcs11_uri:
> +            self.key_fname = self.pkcs11_uri

Doing the override here, in the middle of GetCertificate(), is a sign
the data flow is wrong...it runs on every call and quietly changes
state read by ReadNode(). I'd prefer keeping ReadNode() the single
source of truth: store pkcs11_uri separately and have the openssl
helper choose between key_fname and pkcs11_uri at the call site.

> diff --git a/tools/binman/btool/openssl.py b/tools/binman/btool/openssl.py
> @@ -35,18 +41,89 @@ class Bintoolopenssl(bintool.Bintool):
> +    def _run_cmd_pkcs11(self, pkcs11_module, *args):

Threading pkcs11_module through every x509_cert_* variant (and every
caller in etype/x509_cert.py) is a lot of plumbing for one piece of
state that never varies within a build. Since Bintoolopenssl is a
singleton per binman run, storing the module path (and uri/pin policy)
on the instance once and consulting it inside the existing run_cmd
path would avoid touching all four signatures and their callers.

Regards,
Simon


More information about the U-Boot mailing list