Nitrokey HSM Key Attestation Questions

I’m playing around with the key-attestation.js sample, and I have the following questions:

  1. Is it possible to get the CVC-Req some time after the key has been generated?
    • I already generated a key with pkcs11-tool. Can I get a CVC-Req (and hence an attestation) for that key? Or do I need to regenerate the key with scsh3 and extract the CVC-Req at generation time?
  2. The CVC-Req does not attest to usage and access attributes like extractable or neverExtractable, does it?
    • I would like to prove to my users not just that I generated my release signing key in an HSM (which the CVC-Req → DevAut → DICA → SRCA chain does), but also that it has certain attributes (like that it has extractable=false).
  3. How can I explicitly set the usage and access flags when generating the key via scsh3?
  1. The CV-REQ is stored instead of the certificate. So unless you store a certificate, you can read the CV-REQ from that file.
  2. The CV-REQ does not contain key attributes. If you need to assure, that certain attributes are used during key generation, then you need to establish secure messaging and send the GENERATE ASYMMETRIC KEY PAIR command with protection. This is how the PKI-as-a-Service Portal does that.
  3. You can define the algorithm list when you generate keys in the SCSH. With scripting you can set the attributes using setAlgorithms().
1 Like

Btw. the PKCS#11 attributes (like CKA_EXTRACTABLE) are not mapped to attributes in the HSM. They are provided by the PKCS#15 metadata for information purpose only.

Relevant and enforced are only the internal attribute list and other constraints.

Thanks for the reply and information!

Stored where? On the smartcard or on the host? “[…] from that file” sounds like it’s stored locally on my computer.

So if I used pkcs11-tool to generate the key, there is no way to get a CVC-Req at a later point?
Is there maybe a way to get a new CVC-Req for an existing key?

Background thoughts

Maybe my mental model of “attestation” is biased from what I know from Android, and doesn’t fit the smartcard world. On Android, you can 1) obtain an attestation at any point in time and 2) the attestation includes a bunch of security-relevant attributes.

Only being able to get attestations during keygen is useful and sufficient for establishing session keys. But it’s a bit constrained for long-term keys that I cannot easily re-generate (in my case, APK signing keys).

The CV-REQ is on the SmartCard-HSM. The card stores key objects and files. You can use the explore/explore.js script from the sc-hsm-workspace in the Starterkit to inspect key and file objects.

The PKCS#11 layer translates the PKCS#11 model into a file layout and APDUs exchanged with the card.

An asymmetric key always has a private key object and a file containing the public key (FID=‘CE’|). The public key file is generated during key generation and contains the CV-REQ, signed by the device authentication key. Typically you pass this CV-REQ to a certificate issuing application, retrieve a certificate (X.509 or CVC or other) and store this in place of the initial CV-REQ. Usually you don’t need the key attestation, once a CA has issued a certificate for the key.

In addition to the private key object and public key file, there is another file containing the PKCS#15 meta-data, describing the private key (FID=‘C4’|). The PKCS#15 meta-data contains details like the key identifier, key label and PKCS#11 attributes. But these information are just informative for the PKCS#11 layer, they have no relevance for the internal processing in the secure element.

If you want to keep the attestation information beyond certificate issuance and storing, you need to extract the CV-REQ and keep that on file externally (or write it to a separate file on the SE).

The CV-REQ format basically serves two purposes:

  1. It is a public key encoding format that provides proof-of-correspondence (the inner signature is self-signed by the private key) and proof-of-origin (the outer signature is generated by the device authentication key, which is certified during production in the SmartCard-HSM Scheme PKI). As all SmartCard-HSMs produced under the same SRCA-Policy have the SRCA certificate stored as trust anchor, a device can authenticate a public key from another SmartCard-HSM. This is the basis for Public Key Authentication and XKEK Key Domains, as well as the authentication mechanism in the PKIaaS portal.
  2. The CV-REQ allows a certificate issuing application to determine that the public key is authentic and was generated on an approved device. In a PKI based on Card Verifiable Certificates (like the CVC-PKI used for Passports or eID cards) this is important as certificate issuing happens frequently and automatically. In the X.509 world key attestation is still pretty much in the doing and there are competing standards and vendor implementations. The PKI Consortion is doing some work in that area.

PKIX based structures are typically too complex to be processed in the very small computing environment of a smart card. This is typically a 16-bit microcontroller with 2Kb RAM, so nothing to process or create complex ASN.1 encoded PKIX structures. CVCs were invented specifically for such devices.

Thanks for excellent explanations. Is there any way to restore the CV-REQ once the CE smartcard filesystem file got overwritten with, say, X.509 certificate? (Say the X.509 certificate expired or something).

Not unless the certificate issuing application saved the CV-REQ before overwriting the EF with the issued certificate.

Thanks for the explanation and background details!

So far, I was using scriptrunner to play around with the Nitrokey HSM.

I’ve just tried scsh3gui. The first thing I noticed is that the UI is super tiny on my 2880x1800 laptop display. It does not scale well under Wayland. Other Java GUI applications (such as Android Studio or IntelliJ) scale properly and automatically.

You can use the explore/explore.js script

When I run this via File -> Run Script, it fails with:

>load("/home/thore/Downloads/sc-hsm-starterkit/sc-hsm-workspace/sc-hsm-sdk-scripts/explorer/explore.js");
Exception selecting EF_DIR. Assuming no EF_DIR...
Function (CARD_COMM_ERROR/27904) - "SELECT failed with SW1/SW2 = 6D00 "Checking error: Invalid instruction (0)"" in /home/thore/Downloads/scsh-3.18.61/tools/p15classes.js#1827
    at /home/thore/Downloads/scsh-3.18.61/tools/p15classes.js#1827
    at /home/thore/Downloads/scsh-3.18.61/tools/CardOutlineFactory2.0.js#106
    at /home/thore/Downloads/sc-hsm-starterkit/sc-hsm-workspace/sc-hsm-sdk-scripts/explorer/explore.js#270

I get a similar error when accessing File -> Key Manager:

>load("keymanager/keymanager.js");
GPError: Card (CARD_INVALID_SW/27904) - "Unexpected SW1/SW2=6D00 (Checking error: Invalid instruction (0)) received" in /home/thore/Downloads/scsh-3.18.61/scsh/sc-hsm/SmartCardHSM.js#1642
    at /home/thore/Downloads/scsh-3.18.61/scsh/sc-hsm/SmartCardHSM.js#1642
    at /home/thore/Downloads/scsh-3.18.61/scsh/sc-hsm/SmartCardHSM.js#100
    at /home/thore/Downloads/scsh-3.18.61/keymanager/keymanager.js#233
    at /home/thore/Downloads/scsh-3.18.61/keymanager/keymanager.js#41
    at /home/thore/Downloads/scsh-3.18.61/keymanager/keymanager.js#3347

When I eject and reinsert the Nitrokey HSM, the Automatically access card with Key Manager? dialog appears. When I confirm this with Yes, scsh3gui loads the tree structure view. :tada:

In the tree view, I can see the keys that I generated with pkcs11-tool, and their CVC-REQs. I can also right click and Dump Certificate to the the pretty-print of the CVC-REQ ASN.1.

Getting the CVC-REQ programmatically

The fact that the CVC-REQ is stored in place of the cert was the info that I was missing. Previously, I was searching for ks.getCertificateRequest-like functions.

I managed to get the CVC-REQ from a script and serialise it for external storage:

var bin = ks.getCertificate(label);
var req = new CVC(bin);

print(req.isCertificateRequest());

print(JSON.stringify(req.toJSON()));

CVC-REQ not stored by default

If I run ./scriptrunner key-attestation.js and then inspect the HSM with the scsh3gui, the generated key does NOT have any CVC-REQ:

image

In this screenshot, attest-example was generated via ./scriptrunner key-attestation.js and aaaa-test was generated via pkcs11-tool.

It’s surprising to see that the CVC-REQ is not there, given that the script printed it. I would suggest that you add a call to

ks.storeEndEntityCertificate(label, req);

to the key-attestation.js sample, or otherwise mention this fact. To make it clear that the CVC-REQ needs to be explicitly stored. This seems important, since otherwise the CVC-REQ is forever lost, no?

Also, this apparently means that the OpenSC PKCS#11 provider stores the CVC-REQ automatically? Or how did that CVC-REQ get stored for the key generated by pkcs11-tool?

In the scsh3gui shell script there is a remark:

# If you are using a HDPI Display and the Smart Card Shell appears to small, then try adding
#  -Dsun.java2d.uiScale=2
# to scale the display by 2. The value must be an integer.

Unfortunately Java / Swing does not support HPDI very well and there is no auto-detection. And I doubt that is will change, as Swing is no longer actively developed. Porting the shell UI to a new framework is on the ToDo list, but with very low priority.

For storing the CV-REQ there are actually two options at the APDU level. You can either return the CV-REQ as part of the GENERATE ASYMMETRIC KEY PAIR or have the SmartCard-HSM store the CV-REQ in the associated file. The former is typically used when a remote system has established secure messaging and will immediately process the CV-REQ to issue the certificate. The later is typically used, if the CV-REQ must be stored (at least for a certain period of time) or if the APDU communication has a length limitation and obtaining the CV-REQ must be done in smaller reads.

OpenSC uses the later variant, as one would typically use individual invocations of the pksc11-tool to do the individual steps of key generation and certificate issuance.

The key-attestation.js just wants to demonstrate the principle, but adding storeEndEntityCertificate() make of course sense. I’ve pushed the change in the sc-hsm-sdk-scripts repo in the CDN.

Ah I missed that. :see_no_evil_monkey: Thanks!

Btw. a good example where public key attestation is useful are XKEK Key Domains.

In a XKEK Key Domain the Key Encryption Key is always the result of performing an ECDH with a public key that carries the attestation of the peer. This prevents man-in-the-middle attacks, as only authentic ECDH public keys are accepted.

The same scheme can be used to pairwise establish a shared secret, as shown in the agreeKeys.js example. This is useful for setting up a secure communication channel, e.g. for voice encryption or VPN.