Configure an HSM for use with debusine

After setting up a signing worker, you may want to attach a hardware security module (HSM) to provide additional protection for some keys. Debusine supports any HSM that you can interact with using PKCS #11, but we have currently only tested it with YubiHSM 2 devices. The remainder of this document describes how to set up a YubiHSM 2 for use with Debusine; it may be possible to adapt the same ideas for other devices.

Install packages needed to use the YubiHSM 2

These packages can be installed from bookworm-backports or newer (including trixie):

$ sudo apt install \
    libengine-pkcs11-openssl p11-kit-modules \
    yubihsm-connector yubihsm-pkcs11 yubihsm-shell

Decide on a key policy

The YubiHSM 2 user guide has some advice on initial provisioning and deployment, which you should consider.

You may wish to have a “management” authentication key with a strong password held offline which you can use to change the key policy later, and as an escape hatch in case you make a mistake. If so, note that the default authentication key shipped with the device has a weak password; either that key must be removed or its password must be changed.

Debusine currently only supports using “static” PKCS #11 signing keys: that is, keys that have been pre-generated on the HSM and that can then be registered for use by Debusine. (In future, we may support dynamic key generation as well.) Debusine-specific keys should be kept within their own domain, to allow using the HSM for other purposes.

Debusine additionally needs an authentication key that it can use to connect to the HSM and make signatures. This should have a strong password, but the password will need to be made available to debusine-signing.service. This key must have enough capabilities and delegated capabilities to allow making use of signing keys. A useful set of capabilities is:

  • delete-asymmetric-key

  • generate-asymmetric-key

  • put-asymmetric-key

  • sign-attestation-certificate

  • sign-ecdsa

  • sign-eddsa

  • sign-pkcs

  • sign-pss

  • export-wrapped

  • import-wrapped

And a useful set of delegated capabilities is:

  • sign-ecdsa

  • sign-eddsa

  • sign-pkcs

  • sign-pss

  • exportable-under-wrap

For backup purposes, it is wise to use “wrap keys” which allow exporting keys “under wrap” in such a way that they can be imported into devices possessing the same wrap key. You should have a management wrap key that allows backing up and restoring all exportable keys on the device. There should also be a wrap key that debusine-signing can use to export keys in its own domain: this is part of our plan for supporting more keys than the HSM supports, so it will eventually become a requirement.

Although the YubiHSM itself supports passwords from 8 to 64 bytes long, in practice the userspace stack currently imposes a maximum length of 27 bytes.

You can use yubihsm-shell to create the various keys involved. The put authkey, put wrapkey, generate wrapkey, and put asymmetric commands are especially useful here. You should keep a record of the script you followed and the resulting key IDs for later reference.

For each key, you will need its corresponding certificate (public key). If you imported an externally-generated key into the HSM, then you should already have its certificate. Otherwise, you can use the attest asymmetric command in yubihsm-shell to generate one.

Configure debusine-signing to use the HSM

  1. The yubihsm-connector package provides an HTTP daemon that passes through requests to the USB device. Some tools need to be pointed to the connector’s URL. Create a /etc/yubihsm_pkcs11.conf file (the exact path is not important as long as debusine-signing can read it) with the following contents:

    connector = http://127.0.0.1:12345
    
  2. Create the /etc/systemd/system/debusine-signing.service.d directory and create /etc/systemd/system/debusine-signing.service.d/override.conf with the following contents, where the path is the same as that of the file you created in the previous step:

    [Service]
    Environment=YUBIHSM_PKCS11_CONF=/etc/yubihsm_pkcs11.conf
    
  3. Assemble the PIN to be used by Debusine. This consists of its authentication key ID as a four-character hexadecimal string, followed by the actual password: for example, key ID 5 with password secretpw would become 0005secretpw.

  4. Encrypt this PIN for use by systemd (this must be run as root). Note the use of the systemd-ask-password -n option to ensure that the PIN does not end with a newline character:

    # systemd-ask-password -n | systemd-creds encrypt --name=yubihsm-pin -p - - \
        >>/etc/systemd/system/debusine-signing.service.d/override.conf
    

Register a UEFI signing key stored in the HSM

  1. Use YUBIHSM_PKCS11_CONF=/etc/yubihsm_pkcs11.conf p11-kit list-modules to find the base PKCS #11 URI for the HSM. This will be in the uri: line under module: yubihsm_pkcs11. You may drop the manufacturer and token fields; model and serial should be sufficient to uniquely identify the device.

  2. Append ;pin-source=/run/credentials/debusine-signing.service/yubihsm-pin so that debusine-signing uses the PIN for its authentication key.

  3. Append ;id=KEY-ID, where KEY-ID is the ID of the relevant signing key as a four-character hexadecimal string.

  4. Ensure that the /etc/debusine/signing/certificates directory exists and write the key’s certificate to /etc/debusine/signing/certificates/KEY-NAME.crt (for some appropriate identifier KEY-NAME),

  5. Register the key with debusine-signing as follows, where KEY-URI is the URI assembled in the previous steps:

    $ sudo -u debusine-signing debusine-signing register_pkcs11_static_key \
        uefi KEY-URI /etc/debusine/signing/certificates/KEY-NAME.crt \
        'some description of the new key'
    
  6. Create a debusine:signing-key artifact corresponding to the new key:

    $ debusine create-artifact --workspace System --data - <<END
    purpose: uefi
    fingerprint: KEY-FINGERPRINT
    public_key: BASE64-ENCODED-CERTIFICATE
    END