Last edited one month ago

How to sign a software image using an external HSM

Applicable for STM32MP13x lines, STM32MP15x lines, STM32MP21x lines, STM32MP23x lines, STM32MP25x lines

1. Article purpose[edit | edit source]

The main purpose of this article is to explain how to use secrets stored in an external Hardware Security Module (HSM) to sign a software image such as TF-A BL2 or even a FIP. This allows to perform a Trusted boot by establishing a Chain of Trust (CoT).

2. OSTL implementation[edit | edit source]

In the OSTL ecosystem, the Signing tool is used to sign the platform Trusted Firmware-A BL2 software that is loaded by the ROM code. The Trusted Firmware-A FIP contains the firmware images loaded by the Trusted Firmware-A BL2 and their configuration files. The cert_create tool [1] handles the signature and the certificate chain generation for the binaries contained in the FIP. Both tools support the use of an external HSM.

3. Use Case[edit | edit source]

This article will be useful for people wanting to sign firmware without having to manipulate a plain private key at some point. One may not want such secrets to be accessible at all. That can be done by interfacing an HSM where the secrets are securely stored to a signing tool.

4. Prerequisites[edit | edit source]

Following this article, it is mandatory that:

  • That the HSM you are using can be accessed using PKCS#11 APIs
  • You are running an OpenSSL version supporting engines. OpenSSL engines are deprecated [2] since OpenSSL version 3.0 but are still implemented. Therefore most of versions of OpenSSL above 0.9.6 should fit to perform the operation.
  • You have generated a key pair and stored the private key in the HSM
Info white.png Information
Some packages like OpenSSL are already included in the OSTL SDK.

5. Perform a binary signature[edit | edit source]

First, check openSSL version:

openssl version
OpenSSL 3.2.2 4 Jun 2024 (Library: OpenSSL 3.2.2 4 Jun 2024)

Check that PKCS#11 engine is correctly set:

openssl engine pkcs11 -t
(pkcs11) pkcs11 engine
    [ available ]

Create a custom openSSL configuration file containing:

openssl_conf = openssl_conf

[openssl_conf]
engines = engine_section

[engine_section]
pkcs11 = pkcs11_section

[pkcs11_section]
engine_id = pkcs11
dynamic_path = <path_to_libpkcs11.so> #Usually /usr/lib/x86_64-linux-gnu/engines-xxx/libpkcs11.so
MODULE_PATH = <path_to_HSM_module_library>

and use it as openSSL configuration file:

export OPENSSL_CONF=<path_to_your_custom_config_file>

5.1. Perform a signature using only openSSL[edit | edit source]

Simply use the PKCS#11 URL of the private key that you wish to use to perform the signature:

openssl dgst -engine pkcs11 -sign "<PKCS11_URL_OF_THE_PRIVATE_KEY>" -keyform engine -out test_signed.bin test.txt
engine "pkcs11" set.

Note that this will not generate the certificates necessary to implement the CoT.

5.2. Perform Trusted Firmware-A FIP signature with PKCS#11[edit | edit source]

At this stage, the process is exactly the same as the one described in the TF-A BL2 Trusted Board Boot article. Simply specify the PKCS#11 URLs in the key arguments instead of paths to *.pem files.

Info white.png Information
The PKCS#11 URLs must be enclosed with the double quote (") character. E.g: ROT_KEY="\"<PKCS11_URL_OF_THE_PRIVATE_KEY>\""

6. Example with a software HSM[edit | edit source]

6.1. Extra prerequisites[edit | edit source]

In this example, the HSM will be software. We will use SoftHSMv2 [3] that is a software implementation of a generic cryptographic device with a PKCS#11 interface. To set up the environment, we need to:

  • Make sure the following packages are installed:
    • pkcs11-tools that is provided in the opensc package
    • p11tool that is provided in the gnutls-bin package
    • libengine-pkcs11-openssl
  • Install SoftHSMV2

6.2. Setup[edit | edit source]

Fields enclosed <this_way> are supposed to be replaced by your absolute paths.

  • First, we need to create a working directory where we will be storing configuration files and tokens.
cd $HOME
mkdir -p softhsm/tokens
cd softhsm
  • Copy SoftHSMv2 default configuration file
cp /etc/softhsm2.conf .
  • Custom the token directory path in this configuration file

Insert "directories.tokendir = <path_to>/softhsm/tokens" in softhsm2.conf

  • Export SOFTHSM2_CONF environment variable
export SOFTHSM2_CONF=<path_to>/softhsm/softhsm2.conf
  • Show slots
softhsm2-util --show-slots
Available slots:
Slot 0
   Slot info:
       Description:      SoftHSM slot ID 0x0
       Manufacturer ID:  SoftHSM project
       Hardware version: 2.6
       Firmware version: 2.6
       Token present:    yes
   Token info:
       Manufacturer ID:  SoftHSM project
       Model:            SoftHSM v2
       Hardware version: 2.6
       Firmware version: 2.6
       Serial number:
       Initialized:      no
       User PIN init.:   no
       Label:
  • Initialize token with a given label. SO PIN is the administrator PIN, User PIN will be used to access the slot.
softhsm2-util --init-token --slot 0 --label "TFASignFIP"
=== SO PIN (4-255 characters) ===
Please enter SO PIN: **** #0000
Please reenter SO PIN: ****
=== User PIN (4-255 characters) ===
Please enter user PIN: **** #1234
Please reenter user PIN: ****
The token has been initialized and is reassigned to slot 1861710895
  • Check module
pkcs11-tool --show-info --module /usr/local/lib/softhsm/libsofthsm2.so
Cryptoki version 2.40
Manufacturer     SoftHSM
Library          Implementation of PKCS11 (ver 2.6)
Using slot 0 with a present token (0x6ef7742f)  #1861710895
Info white.png Information
To ease manipulations, you can create an alias to: pkcs11-tool --module /usr/local/lib/softhsm/libsofthsm2.so
alias p11-softhsm="pkcs11-tool --module /usr/local/lib/softhsm/libsofthsm2.so"
  • List available slots
p11-softhsm --list-slots
Available slots:
Slot 0 (0x6ef7742f): SoftHSM slot ID 0x6ef7742f
 token label        : TFASignFIP
 token manufacturer : SoftHSM project
 token model        : SoftHSM v2
 token flags        : login required, rng, token initialized, PIN initialized, other flags=0x20
 hardware version   : 2.6
 firmware version   : 2.6
 serial num         : 1eb49a0ceef7742f
 pin min/max        : 4/255
Slot 1 (0x1): SoftHSM slot ID 0x1
 token state:   uninitialized
  • Generate a keypair using pkcs11-tools
p11-softhsm --login --login-type user --keypairgen --id 0 --key-type EC:secp256r1 --slot 1861710895
Logging in to "TFASignFIP".
Please enter User PIN: #Enter User PIN
Key pair generated:
Private Key Object; EC
 label:
 ID:         00
 Usage:      decrypt, sign, unwrap, derive
 Access:     sensitive, always sensitive, never extractable, local
Public Key Object; EC  EC_POINT 256 bits
 EC_POINT:   044104510cedc7dabaa4b3a7f9953069065d3f9b3978d772e3c04069c34f4198a5bda64e16ec685c9dadbe91c5f35a76362d8c955a4167e78e3f1365e98ab05f7884a4
 EC_PARAMS:  06082a8648ce3d030107
 label:
 ID:         00
 Usage:      encrypt, verify, wrap, derive
 Access:     local
  • List and get the private key URL with p11tool
p11tool --provider=/usr/local/lib/softhsm/libsofthsm2.so --login --set-pin=1234 --list-all
Object 0:
	URL: pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=1eb49a0ceef7742f;token=TFASignFIP;id=%00;type=private #This is the URI
	Type: Private key (EC/ECDSA-SECP256R1)
	Label:
	Flags: CKA_WRAP/UNWRAP; CKA_PRIVATE; CKA_NEVER_EXTRACTABLE; CKA_SENSITIVE;
	ID: 00

Object 1:
	URL: pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=1eb49a0ceef7742f;token=TFASignFIP;id=%00;type=public
 	Type: Public key (EC/ECDSA-SECP256R1)
	Label:
	Flags: CKA_WRAP/UNWRAP;
	ID: 00
  • Create a custom openSSL configuration file (e.g: openssl-pkcs11.conf) with the content described here and add:
MODULE_PATH = /usr/local/lib/softhsm/libsofthsm2.so
  • Export the OPENSSL_CONF environment variable
export OPENSSL_CONF=<path_to>/softhsm/openssl-pkcs11.conf
  • Sign a binary using the PKCS11 URL
openssl dgst -engine pkcs11 -sign "pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=1eb49a0ceef7742f;token=TFASignFIP;id=%00;object=tfasign;type=private" -keyform engine -out test_signed.bin test.txt
engine "pkcs11" set.
Info white.png Information
At this stage, and similarly to the procedure above, the PKCS11 URLs can be used to generate the CoT certificates

7. Troubleshooting[edit | edit source]

  • If you can't enumerate slots like:
openssl dgst -engine pkcs11 -sign "pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=1eb49a0ceef7742f;token=TFASignFIP;id=%00;object=tfasign;type=private" -keyform engine -out signature.bin test.txt
engine "pkcs11" set.
Failed to enumerate slots
Failed to enumerate slots
PKCS11_get_private_key returned NULL
cannot load key file from engine
140310533596480:error:80067065:pkcs11 engine:ctx_load_privkey:object not found:eng_back.c:858:
140310533596480:error:26096080:engine routines:ENGINE_load_private_key:failed loading private key:../crypto/engine/eng_pkey.c:77:
unable to load key file

That means the openSSL configuration file is incorrect or that the OPENSSL_CONF variable hasn't been defined, please check it.

  • If you can't find the key:
openssl dgst -engine pkcs11 -sign "pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=1eb49a0ceef7742f;token=TFASignFIP;id=%00;object=tfasign;type=private" -keyform engine -out signature.bin test.txt
engine "pkcs11" set.
Specified object not found
Specified object not found
PKCS11_get_private_key returned NULL
cannot load key file from engine
140107714262336:error:80067065:pkcs11 engine:ctx_load_privkey:object not found:eng_back.c:858:
140107714262336:error:26096080:engine routines:ENGINE_load_private_key:failed loading private key:../crypto/engine/eng_pkey.c:77:
unable to load key file
Segmentation fault

This is likely that your SOFTHSM2_CONF variable hasn't been set or that you configuration file isn't correct.

8. References[edit | edit source]