[PATCH v2 3/4] tools: binman: fit: add support for OpenSSL engines
Wolfgang Wallner
wolfgang.wallner at br-automation.com
Wed Nov 19 13:19:12 CET 2025
Hi Quentin,
> From: Quentin Schulz <quentin.schulz at cherry.de>
>
> This adds support for using an OpenSSL engine for signing a FIT image.
> To use it, one should set the fit,engine property at the FIT node level
> with the engine to use. This will in turn call mkimage with the -N
> option.
>
> The -k argument to mkimage can be specified via fit,engine-keydir. This
> is especially useful for pkcs11 engine to specify slots, token label,
> etc...
Thanks a lot, v2 of the patch series is really an improvement for the
PKCS#11 use case. I was not aware that there was already code that
could automatically prefix 'pkcs11:object=' to the key-name-hint.
(Yes, I know, it is part of the existing documentation, and I have read
that, but that just shows how much I miss even when the reading docs ...)
> The -k argument to mkimage can be specified by setting fit,engine-keydir
> property, if missing no -k argument will be passed
Small nitpick: the above two paragraphs seem repetitive, how about the following?
The -k argument to mkimage can be specified via fit,engine-keydir. If
fit,engine-keydir is not specified, no -k argument will be passed to mkimage.
The attribute fit,engine-keydir is especially useful for a pkcs11 engine to
specify slots, token label, etc...
> As far as I could tell, mkimage encrypts and signs a FIT in one go, thus
> the -k argument applies to both signing and encrypting. Considering we
> reuse the -k argument for two different meanings (info to pass to the
> engine when using an engine otherwise the directory where keys are
> stored), we cannot reasonably encrypt using local keys and signing with
> an engine, hence the enforced check. I believe it should be possible to
> support encrypting and signing with the same engine (using different
> key pairs of course, via different key-name-hint likely), but this is
> left for the next person to implement.
> This is why the property is named fit,engine and not fit,sign-engine.
> Ditto for fit,engine-keydir.
>
> The public key (with .crt extension) is still required if it needs to be
> embedded in the SPL DTB for example. We could probably support
> retrieving the public key from an engine, but this is a change to make
> to fdt_add_pubkey.c.
>
> Signed-off-by: Quentin Schulz <quentin.schulz at cherry.de>
> ---
> tools/binman/entries.rst | 45 +++++++++++++++++++++++++++++++---
> tools/binman/etype/fit.py | 61 +++++++++++++++++++++++++++++++++++++++++++----
> 2 files changed, 99 insertions(+), 7 deletions(-)
>
> diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
> index 8922d6cd070..fb17157cd79 100644
> --- a/tools/binman/entries.rst
> +++ b/tools/binman/entries.rst
> @@ -885,9 +885,10 @@ The top-level 'fit' node supports the following special properties:
>
> fit,sign
> Enable signing FIT images via mkimage as described in
> - verified-boot.rst. If the property is found, the private keys path
> - is detected among binman include directories and passed to mkimage
> - via -k flag. All the keys required for signing FIT must be
> + verified-boot.rst.
> + If the property is found and fit,engine is not set, the private
> + keys path is detected among binman include directories and passed to
> + mkimage via -k flag. All the keys required for signing FIT must be
> available at time of signing and must be located in single include
> directory.
>
> @@ -898,6 +899,44 @@ The top-level 'fit' node supports the following special properties:
> required for encrypting the FIT must be available at the time of
> encrypting and must be located in a single include directory.
>
> + Incompatible with fit,engine.
> +
> + fit,engine
> + Indicates the OpenSSL engine to use for signing the FIT image. This
> + is passed to mkimage via the `-N` flag. Example::
> +
> + fit,engine = "my-engine";
> +
> + A `-k` argument for mkimage may be passed via `fit,engine-keydir`.
> +
> + When `fit,engine` is set to `pkcs11`, the value of
> + `fit,engine-keydir` (if present) is concatenated with `;object=` and
> + the value of `key-name-hint`, and passed as-is to the OpenSSL engine
> + API. If the value of `fit,engine-keydir` contains `object=` or
> + `id=`, only the value of `fit,engine-keydir` is passed as-is to the
> + OpenSSL engine API. If no `fit,engine-keydir` is present,
> + `pkcs11:object=` is concatenated with the value of `key-name-hint`,
> + and passed as-is to the OpenSSL engine API.
This description is quite convoluted. I would propose to describe it as a list,
something like the following:
When `fit,engine` is set to `pkcs11`, the following cases are distinguished
regarding the value of `fit,engine-keydir`:
- If `fit,engine-keydir` is not present, value of `key-name-hint` is prefixed
with `pkcs11:object=`, and then passed as-is to the OpenSSL engine API.
PKCS#11 id: `pkcs11:object=<key-name-hint>`
- If `fit,engine-keydir` is present, and its value contains either `object=` or
`id=`, then the value of `fit,engine-keydir` is passed as-is to theOpenSSL
engine API. The value of `fit,engine-keydir` has to start with `pkcs11:`.
PKCS#11 id: `<fit,engine-keydir>`
- If `fit,engine-keydir` is present, but its value does not contain either
`object=` or `id=`, then the value of `fit,engine-keydir` is concatenated
with `;object=` and the value of `key-name-hint`, and passed as-is to the
OpenSSL engine API. The value of `fit,engine-keydir` has to start with
`pkcs11:`.
PKCS#11 id: `<fit,engine-keydir>; object=<key-name-hint>`
In all of the above cases, the value of `<key-name-hint>` is used for
verification to find the matching public key.
> + For all other values of `fit,engine`, the value of
> + `fit,engine-keydir` (if present) is concatenated with the value of
> + `key-name-hint` and passed as-is to the OpenSSL engine API.
> +
> + Depends on fit,sign.
> +
> + Incompatible with fit,encrypt.
> +
> + fit,engine-keydir
> + Indicates the `-k` argument to pass to mkimage if an OpenSSL engine
> + is to be used for signing the FIT image. Example::
> +
> + fit,engine-keydir = "pkcs11:model=xxx;manufacturer=xxx";
> +
> + Read `fit,engine` documentation for more info on special cases when
> + using `pkcs11` as engine.
> +
> + Depends on fit,engine.
> +
> Substitutions
> ~~~~~~~~~~~~~
>
> diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py
> index db40479d30e..6547d0314b9 100644
> --- a/tools/binman/etype/fit.py
> +++ b/tools/binman/etype/fit.py
> @@ -104,9 +104,10 @@ class Entry_fit(Entry_section):
>
> fit,sign
> Enable signing FIT images via mkimage as described in
> - verified-boot.rst. If the property is found, the private keys path
> - is detected among binman include directories and passed to mkimage
> - via -k flag. All the keys required for signing FIT must be
> + verified-boot.rst.
> + If the property is found and fit,engine is not set, the private
> + keys path is detected among binman include directories and passed to
> + mkimage via -k flag. All the keys required for signing FIT must be
> available at time of signing and must be located in single include
> directory.
>
> @@ -117,6 +118,44 @@ class Entry_fit(Entry_section):
> required for encrypting the FIT must be available at the time of
> encrypting and must be located in a single include directory.
>
> + Incompatible with fit,engine.
> +
> + fit,engine
> + Indicates the OpenSSL engine to use for signing the FIT image. This
> + is passed to mkimage via the `-N` flag. Example::
> +
> + fit,engine = "my-engine";
> +
> + A `-k` argument for mkimage may be passed via `fit,engine-keydir`.
> +
> + When `fit,engine` is set to `pkcs11`, the value of
> + `fit,engine-keydir` (if present) is concatenated with `;object=` and
> + the value of `key-name-hint`, and passed as-is to the OpenSSL engine
> + API. If the value of `fit,engine-keydir` contains `object=` or
> + `id=`, only the value of `fit,engine-keydir` is passed as-is to the
> + OpenSSL engine API. If no `fit,engine-keydir` is present,
> + `pkcs11:object=` is concatenated with the value of `key-name-hint`,
> + and passed as-is to the OpenSSL engine API.
Same as above.
> +
> + For all other values of `fit,engine`, the value of
> + `fit,engine-keydir` (if present) is concatenated with the value of
> + `key-name-hint` and passed as-is to the OpenSSL engine API.
> +
> + Depends on fit,sign.
> +
> + Incompatible with fit,encrypt.
> +
> + fit,engine-keydir
> + Indicates the `-k` argument to pass to mkimage if an OpenSSL engine
> + is to be used for signing the FIT image. Example::
> +
> + fit,engine-keydir = "pkcs11:model=xxx;manufacturer=xxx";
> +
> + Read `fit,engine` documentation for more info on special cases when
> + using `pkcs11` as engine.
> +
> + Depends on fit,engine.
> +
> Substitutions
> ~~~~~~~~~~~~~
>
> @@ -620,7 +659,21 @@ class Entry_fit(Entry_section):
> args.update({'align': fdt_util.fdt32_to_cpu(align.value)})
> if (self._fit_props.get('fit,sign') is not None or
> self._fit_props.get('fit,encrypt') is not None):
> - args.update({'keys_dir': self._get_keys_dir(data)})
> + engine = None
> + if self._fit_props.get('fit,sign') is not None:
> + engine_prop = self._fit_props.get('fit,engine')
> + if engine_prop is not None:
> + engine = engine_prop.value
> + args.update({'engine': engine})
> + engine_keydir_prop = self._fit_props.get('fit,engine-keydir')
> + if engine_keydir_prop is not None:
> + engine_keydir = engine_keydir_prop.value
> + if engine_keydir:
> + args.update({'keys_dir': engine_keydir})
> + if engine is None:
> + args.update({'keys_dir': self._get_keys_dir(data)})
> + elif self._fit_props.get('fit,encrypt') is not None:
> + self.Raise('fit,engine currently does not support encryption')
> if self.mkimage.run(reset_timestamp=True, output_fname=output_fname,
> **args) is None:
> if not self.GetAllowMissing():
Tested-by: Wolfgang Wallner <wolfgang.wallner at br-automation.com>
I did test the 3 cases listed in the description of fit,engine:
Test 1: No fit,engine-keydir:
fit,sign;
fit,engine = "pkcs11";
key-name-hint = "<object-id-of-my-key>";
Test 2: fit,engine-keydir present, and contains the string 'object=':
fit,sign;
fit,engine = "pkcs11";
fit,engine-keydir = "pkcs11:object=<object-id-of-my-key>";
key-name-hint = "MyTestKey";
Test 3: fit,engine-keydir present, but does not contain'object=' of 'id=':
fit,sign;
fit,engine = "pkcs11";
fit,engine-keydir = "pkcs11:serial=<serial-of-my-key>";
key-name-hint = "<object-id-of-my-key>";
I think the suggestions above would improve the clarity, but even if you
choose to keep the text as is would be fine for me.
Both code + documentation:
Reviewed-by: Wolfgang Wallner <wolfgang.wallner at br-automation.com>
regards, Wolfgang
More information about the U-Boot
mailing list