Hi Yan,

On 2026-10-23T09:06:06, yan wang <[email protected]> wrote:
> binman: pre_load: Add HSM signing support to pre-load entry type
>
> Extend the pre-load entry type to support signing via hardware
> security modules (HSM) using PKCS#11. The implementation integrates
> with the pkcs11_tool btool to perform signing operations.

The commit message is the same as in v1. Please can you describe the
hsm-key property, the pre-load-hsm-library / pre-load-hsm-pin entry
arguments and PKCS11_SIGN_PIN, and give the reason for the change,
i.e. so the private key never has to leave the token? Your cover
letter already says this well.

> diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py
> @@ -53,6 +82,37 @@ class Entry_pre_load(Entry_collection):
> +    This entry creates a pre-load header that contains a global image
> +    signature. The signature can be created using either a traditional RSA
> +    key file (key-name) or a Hardware Security Module (HSM) via PKCS#11
> +    (hsm-key), enabling production keys to remain on the HSM and never touch
> +    the build machine's filesystem.
> +
>
>      This entry creates a pre-load header that contains a global
>      image signature.

The old paragraph is still there, so the docs now say this twice, with
two blank lines in between. Please merge them. Also, a key file can be
ECDSA as well, so 'RSA' is not right here.

> diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py
> @@ -87,6 +169,18 @@ class Entry_pre_load(Entry_collection):
> +        self.hsm_library = fdt_util.GetString(self._node, 'hsm-library')

The cover letter says the library is not stored in the devicetree, but
the DT can provide it in three ways: this hsm-library property, a
'pre-load-hsm-library' property (see below) and module-path= in the
URI. Please can you drop the hsm-library property? The entry argument
and module-path= cover it.

> diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py
> @@ -94,6 +188,50 @@ class Entry_pre_load(Entry_collection):
> +        # PIN from entry arg or environment variable, NOT from the DT.
> +        pin_arg, = self.GetEntryArgsOrProps([EntryArg('pre-load-hsm-pin', 
> str)])
> +        self.hsm_pin = pin_arg or os.environ.get('PKCS11_SIGN_PIN')

One thing I'd like to check here: GetEntryArgsOrProps() calls
GetArg(), which falls back to fdt_util.GetDatatype() on the node when
no -a argument is given. So 'pre-load-hsm-pin = '1234';' in the DT is
still used, despite the comment. Please can you use
state.GetEntryArg('pre-load-hsm-pin') directly so the DT is never read
for the PIN? A test that puts the PIN in the DT and checks it is
ignored would be good.

> diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py
> @@ -94,6 +188,50 @@ class Entry_pre_load(Entry_collection):
> +            hash_name, sign_name = self.algo_name.split(',')
> +            if hash_name not in SHAS:
> +                self.Raise(f'{hash_name} is not supported')
> +            if sign_name not in RSAS and sign_name not in ECDSAS:
> +                self.Raise(f'{sign_name} is not supported')
> +            padding_name = self.padding_name or 'pkcs-1.5'
> +            if padding_name not in ('pss', 'pkcs-1.5'):
> +                self.Raise(f'Unsupported padding scheme: {padding_name}')

_SignHsmData() repeats these checks. Since ReadNode() always runs
first, please keep them here and work out the values once, e.g. in
instance variables.

> diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py
> @@ -197,8 +335,90 @@ class Entry_pre_load(Entry_collection):
> +        When HSM signing is active the pkcs11-tool btool is called twice per
> +        _CreateHeader() invocation (once for the image signature and once for
> +        the header signature). Binman may call ProcessContents() several 
> times
> +        as entries settle, so consider caching the slot and mechanism results
> +        in a real deployment against a rate-limited HSM.

Someone running binman can't act on this. Please either do the caching
or drop the paragraph (perhaps leave a short TODO instead).

> diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py
> @@ -197,8 +335,90 @@ class Entry_pre_load(Entry_collection):
> +            hash_sig = SHA256.new(img_sig)
> +
> +            version = self.version
> +            header_size = self.header_size
> +            image_size = len(self.image)
> +            ofs_img_sig = 64 + len(img_sig)
> +            flags = 0
> +            reserved0 = 0
> +            reserved1 = 0
> +
> +            first_header = struct.pack('>4sIIIIIII32s', PRE_LOAD_MAGIC,
> +                                       version, header_size, image_size,
> +                                       ofs_img_sig, flags, reserved0,
> +                                       reserved1, hash_sig.digest())

This is the third copy of the header-building code, after
_CreateHeaderRsa() and _CreateHeaderEcdsa(). It would be worth a
preparatory patch with a helper that takes a signing function and
builds the header, so the HSM path only has to provide _SignHsmData().
What do you think?

>
> Signed-off-by: yan wang <[email protected]>
>
>  tools/binman/etype/pre_load.py | 222 
> ++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 221 insertions(+), 1 deletion(-)

Regards,
Simon

Reply via email to