kairos icon indicating copy to clipboard operation
kairos copied to clipboard

feat: Add PKCS#11 support for platform key(s)

Open bencorrado opened this issue 9 months ago • 1 comments

Is your feature request related to a problem? Please describe.

When using auroraboot to build a UKI we should be able to pass a URI for a PKCS#11 device that is holding the RSA private key.

Describe the solution you'd like

Add support to go-ukify and auroraboot to pass in a URI, passcode and/or module link to allow for the private key (primarily db.key) to be stored on a hardware key instead of plaintext on the filesystem.

Describe alternatives you've considered

Keep using a more insecure option of keys in plaintext on the filesystem.

Additional context

For auroraboot something like changing/adding:

		&cli.StringFlag{
			Name:  "sb-key-uri",
			Value: "",
			Usage: "Override SBKey with a PKCS#11 URI (e.g. 'pkcs11:manufacturer=piv_II;id=%02'). If empty, the default file-based key will be used.",
		},
		&cli.StringFlag{
			Name:  "pkcs11-pin",
			Value: "",
			Usage: "PIN for the PKCS#11 token (YubiKey); required when using a pkcs11 URI",
		},
		&cli.StringFlag{
			Name:  "pkcs11-path",
			Value: "/usr/lib/libykcs11.so",
			Usage: "Path to the PKCS#11 module (default: /usr/lib/libykcs11.so)",
		},

...

		// Check if the keys directory contains the required files
		requiredFiles := []string{"db.der", "db.auth", "KEK.der", "KEK.auth", "PK.der", "PK.auth", "tpm2-pcr-private.pem"}
		// If no SBKey override is provided, then require "db.key".
		if ctx.String("sb-key-uri") == "" {
			requiredFiles = append(requiredFiles, "db.key")
		}

...


			var sbKey string
			if ctx.String("sb-key-uri") != "" {
				// Use the provided PKCS#11 URI.
				sbKey = ctx.String("sb-key-uri")
			} else {
				// Otherwise use the default file-based SBKey.
				sbKey = filepath.Join(ctx.String("keys"), "db.key")
			}

			builder := &uki.Builder{
				Arch:          config.Arch,
				Version:       kairosVersion,
				SdStubPath:    stub,
				KernelPath:    filepath.Join(artifactsTempDir, "vmlinuz"),
				InitrdPath:    filepath.Join(artifactsTempDir, "initrd"),
				Cmdline:       entry.Cmdline,
				OsRelease:     filepath.Join(sourceDir, "etc/os-release"),
				OutUKIPath:    entry.FileName + ".efi",
				PCRKey:        filepath.Join(ctx.String("keys"), "tpm2-pcr-private.pem"),
				SBKey:         sbKey,
				SBCert:        filepath.Join(ctx.String("keys"), "db.pem"),
				SdBootPath:    systemdBoot,
				OutSdBootPath: outputSystemdBootEfi,
				Splash:        ctx.String("splash"),
			        PKCS11Pin:    ctx.String("pkcs11-pin"),
			        PKCS11Path:   ctx.String("pkcs11-path"),
			}

and adding support for github.com/ThalesIgnite/crypto11 to go-ukifiy and the handlers to support the auroraboot parts above.

bencorrado avatar Feb 25 '25 22:02 bencorrado

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar May 29 '25 02:05 github-actions[bot]

nitrokeys seems to cover this and they are pretty cheap for testing https://www.nitrokey.com/products/nitrokeys

I got one for myself to substitute my 2 yubikeys so I migth be able to test this when it arrives.

Itxaka avatar Jun 18 '25 09:06 Itxaka

I just bought some https://www.smartcard-hsm.com/ from the US disti last week, they arrive tomorrow.

You can probably get them from: https://www.cardomatic.de/en/c/smartcard-hsm

The banner on the Nitrokey HSM 2 site said wait till August.

The regular NitroKey can only hold a few keys, the HSM version can hold 10's of them. It also lets you setup security zones and have a few HSMs all hold they same copies of keys without they plaintext ever being exposed. This is what I plan to use for production. I can write a blog on it when we get things moving here.

bencorrado avatar Jun 19 '25 22:06 bencorrado

a thing that its clear is that most of the docs and libs out there are for the ?HSM versions of all this keys, so make sure that we get one. The one mentioned by @bencorrado seems cheap enough that we can get one to add the support in there.

Also this: have a few HSMs all hold they same copies of keys its very interesting becuase it means the whole team could have a a hardware key that contains the "kairos official key" or whatever so we dont have to rely on a single person for this. Still the more people that has it the more chances to lose it soooo...

Itxaka avatar Jun 26 '25 07:06 Itxaka

also having a look at the smartcard-hsm, they seem to cover also our use cases

Itxaka avatar Jun 26 '25 08:06 Itxaka

Ok, I was able to sign an efi file with just the nitrokey start which is very cheap (27 EUR + shipping)

Remember that default PINs for the card are 123456 for User and 12345678 for Admin

Here are the steps

Configure GPG to use PC/SC

Create or edit ~/.gnupg/scdaemon.conf to prevent conflicts between GPG and PKCS#11 tools:

disable-ccid

And then restart GPG:

gpgconf --kill scdaemon
gpgconf --kill gpg-agent

Otherwise you might get a forbidden access to the card

Generate the keys inside the card

$ gpg --card-edit

inside gpg:

 $ admin
 $ generate

That should generate the key in the hardware key

Check the card status

$ gpg --card-status
Reader ...........: Nitrokey Nitrokey Start (FSIJ-1.2.19-C5B562D9) 00 00
Application ID ...: D276000124010200FFFEC5B562D90000
Application type .: OpenPGP
Version ..........: 2.0
Manufacturer .....: unmanaged S/N range
Serial number ....: C5B562D9
Name of cardholder: [not set]
Language prefs ...: [not set]
Salutation .......: 
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: forced
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 3 3
Signature counter : 3
KDF setting ......: off
UIF setting ......: Sign=off Decrypt=off Auth=off
Signature key ....: 843D 24A5 C992 3E97 6E6E  080E B479 5DD1 D89D 7EDE
      created ....: 2025-06-26 10:36:55
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

Check slot 1 (slot 1 is where Nitrokey stores the sign keys)

$ pkcs11-tool --module opensc-pkcs11.so --slot 1 -l --list-objects
Logging in to "OpenPGP card (User PIN (sig))".
Please enter User PIN: 
Private Key Object; RSA 
  label:      Signature key
  ID:         01
  Usage:      sign, signRecover, non-repudiation
  Access:     sensitive, always sensitive, never extractable, local
  uri:        pkcs11:model=PKCS%2315%20emulated;manufacturer=OpenPGP%20project;serial=fffec5b562d9;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29;id=%01;object=Signature%20key;type=private
Public Key Object; RSA 2048 bits
  label:      Signature key
  ID:         01
  Usage:      verify, verifyRecover
  Access:     none
  uri:        pkcs11:model=PKCS%2315%20emulated;manufacturer=OpenPGP%20project;serial=fffec5b562d9;token=OpenPGP%20card%20%28User%20PIN%20%28sig%29%29;id=%01;object=Signature%20key;type=public
Profile object 3983474672
  profile_id:          CKP_PUBLIC_CERTIFICATES_TOKEN (4)

The 01 ID KEY is the one we will use for signing

Create openssl-pkcs11-provider.cnf file

openssl_conf = openssl_init

[openssl_init]
providers = provider_sect

[provider_sect]
default = default_sect
base = base_sect
pkcs11 = pkcs11_sect

[default_sect]
activate = 1

[base_sect]
activate = 1

[pkcs11_sect]
activate = 1
pkcs11-module-path = /usr/lib/pkcs11/opensc-pkcs11.so # Change to the path of yours, can differ by OS

Note that the pkcs11-module-path is specific to the distro and even to the arch. For example on Fedora ARM64 the correct path is /usr/lib64/pkcs11/opensc-pkcs11.so

Verify provider activation

$ OPENSSL_CONF=openssl-pkcs11-provider.cnf openssl list -providers
Providers:
  base
    name: OpenSSL Base Provider
    version: 3.5.0
    status: active
  default
    name: OpenSSL Default Provider
    version: 3.5.0
    status: active
  pkcs11
    name: PKCS#11 Provider
    version: 3.4.1
    status: active

You should see the pkcs11 provider active

Generate a CSR

$ OPENSSL_CONF=openssl-pkcs11-provider.cnf openssl req \
    -new \
    -key "pkcs11:slot-id=1;id=%01" \       
    -sha256 -out nitrokey.csr.pem \
    -subj "/CN=SecureBoot Key/"

HINT: You can append ;pin-value=<YOUR USER PIN> to the pkcs11 url to not have to write it. If you dont add it, openssl will ask you for it

Self-sign certificate

$ OPENSSL_CONF=openssl-pkcs11-provider.cnf openssl x509 \
    -req -days 3650 \
    -in nitrokey.csr.pem \
    -signkey "pkcs11:slot-id=1;id=%01" \
    -out nitrokey.crt.pem

HINT: You can append ;pin-value=<YOUR USER PIN> to the pkcs11 url to not have to write it. If you dont add it, openssl will ask you for it

Sign your EFI binary

OPENSSL_CONF=openssl-pkcs11-provider.cnf /usr/lib/systemd/systemd-sbsign sign \
  --private-key="pkcs11:slot-id=1;id=%01" \
  --private-key-source=provider:pkcs11 \
  --certificate=nitrokey.crt.pem \
  --output=OUTPUT.efi \
  INPUT.efi

Troubleshooting

If card is blocked or device errors (CKR_DEVICE_ERROR):

Restart GPG and PC/SC services explicitly:

gpgconf --kill scdaemon
gpgconf --kill gpg-agent
sudo systemctl restart pcscd

Physically unplug and reinsert your Nitrokey. Retry explicitly checking the key availability:

pkcs11-tool --slot 1 -l --list-objects 

Itxaka avatar Jun 26 '25 14:06 Itxaka

This is now only missing surfacing on AuroraBoot with a new release and documentation

mudler avatar Jul 07 '25 08:07 mudler

This just needs QA, but it's not part of a release yet, however AuroraBoot 0.9.0 contains the necessary changes to test this.

mudler avatar Jul 15 '25 08:07 mudler