Attestation
This guide explains how to use key attestation to provide a verifiable hardware root of trust for a project's signing keys.
- As a maintainer: prove that your signing keys were generated inside a Nitrokey HSM.
- As a user: verify that a project's signing keys were generated inside a Nitrokey HSM.
Motivation
The elaborate key management using hardware token that the previous sections described: that is all well and good. But how do you know that FMD is really using a Nitrokey HSM?
This is where attestation comes in. Attestation allows you to verify that a signing key was generated inside a genuine Nitrokey HSM, by following a certificate chain that leads up to the chip manufacturer's root CA.
This allows users to verify that FMD's signing keys are really generated and stored in a Nitrokey HSM. You don't have to trust the FMD maintainers. It's sufficient to trust the hardware vendor.
Key attestation is an important feature of modern cryptographic hardware. It's not unique to the Nitrokey HSM. For example, Android also supports key attestation. Android goes even a step further: its attestations include a lot of metadata about the state of the device (such as the OS patch level and the boot loader lock state).
Attestation with the Nitrokey HSM
CVCs
The Nitrokey HSM is based on the SmartCard-HSM, which supports attestation. Being a smart card, it uses the Card Verifiable Certificate (CVC) format (instead of traditional X.509 certificates). This is because CVCs are much smaller, and thus more amenable to low power, low memory environments such as smart cards.
Certificate Hierarchy
The SmartCard-HSM uses the following certificate hierarchy:
- SRCA: Scheme Root CA. Operated by CardContact (the SmartCard-HSM designer).
- DICA: Device Issuer CA. Operated by "every licensed producer of SmartCard-HSMs".
- Device Cert: Created during production, unique to each SmartCard-HSM.
- CVC-REQ: Created at key generation time by the device, to attest the key's provenance.
The first three are normal CVCs.
The CVC-REQ is (as the name implies) not a full certificate, but a certificate request (like an X.509 Certificate Signing Request (CSR)). It is designed so that a remote CA can verify the CVC-REQ's attestation chain, and then issue a normal CVC, signed by that remote CA. For our purpose, we don't need a remote CA.
Tooling
The SmartCard-HSM comes with custom tooling, grouped under the term "Smart Card Shell".
It can be downloaded here.
The Smart Card Shell allows writing scripts in JavaScript to automate interactions with the SmartCard-HSM.
It includes a scriptrunner tool to execute these JavaScript files.
We can use the Smart Card Shell to write scripts that:
- As a maintainer: Extract the attestation certificate chain and write it to a JSON file. This requires a physical SmartCard-HSM to be present.
- As a user: Read the JSON-encoded attestation certificate chain and verify it. The Smart Card Shell provides the CVC parser and the root certificate to compare against. This does not require a SmartCard-HSM.
Storage slots
For a given label (such as fmd-android-ec), you can think of the SmartCard-HSM as having multiple "slots":
one for the private key, one for the public key, and one for the certificate.
These "slots" share the same label, but differ in their type.
When you generate a key, the SmartCard-HSM automatically issues a CVC-REQ
signed by the private key corresponding to the Device Cert.
The OpenSC PKCS#11 module (that we were using with pkcs11-tool) automatically
writes this CVC-REQ to the certificate slot in the SmartCard-HSM.
Note: if you are writing a Smart Card Shell script, you need to explicitly write
the returned CVC-REQ to the HSM.
If you later write a "normal" X.509 certificate to the certificate slot, this will overwrite the CVC-REQ stored there. Therefore, you must export the CVC-REQ first, otherwise it is lost. This is why the key generation guide has warnings around this.
Export an attestation
Start by generating a new key with pkcs11-tool.
Then, to export the CVC-REQ for this key, proceed as follows:
-
Make sure that your Nitrokey HSM is inserted.
-
Install the Smart Card Shell.
-
Make sure that the
scriptrunnertool is in your PATH:export PATH=$PATH:/path/to/scsh-3.18.61/
which scriptrunner -
Download the
attestation_extract.jsscript. -
Execute the script:
LABEL=my-key-label scriptrunner attestation_extract.js
The script extracts the CVC attestation chain, serializes it, and writes it to an attestation.json file.
Share this file with your users.
Because the CVC-REQ is considered public and non-sensitive, it can be read without requiring the User PIN of the HSM.
Verifying an attestation
To verify that a given public-private key pair was generated inside a SmartCard-HSM (such as a Nitrokey HSM), you can proceed as follows:
-
Install the Smart Card Shell.
-
Make sure that the
scriptrunnertool is in your PATH:export PATH=$PATH:/path/to/scsh-3.18.61/
which scriptrunner -
Download the
attestation_verify.jsscript. -
Download the
attestation.json. For FMD, you can find these in the Verify Signatures section. -
Execute the script:
ATT_FILE=path/to/attestation.json scriptrunner attestation_verify.js
This script:
- Verifies that CVC attestation chain against the SRCA hardcoded in the Smart Card Shell.
If successful, it prints
Validated CVC cert chain: OK. - Prints the public key SHA-256 fingerprint of the CVC-REQ.
Together, this proves that a key pair with the printed SHA-256 fingerprint has been generated in a genuine SmartCard-HSM.
Next, compare this fingerprint against the published fingerprints. Through the public key fingerprint you are linking the physical HSM to the APK signatures (which should verify using the same public key fingerprint).