Infosec Scribbles

July 19, 2020

How I Picked and Set Up a SmartCard

Since I had to migrate my work machine to Windows, it became impossible to use the TPM of my Precision 5520 as a secure key store. The open source tpm2-software stack does not work on Windows, and there is no alternative software stack provided by Microsoft. So I figured, might as well switch to a crypto stick. This post contains my notes from that process.

PKCS#11 or OpenPGP?

With the built-in TPM, using PKCS#11 was the simplest way to use it for practical applications, such as to authenticate against OpenSSH servers.

With the cryptographic tokens, that is not always the case. I haven’t researched deep into this, but from what is on the surface, it seems that there are two “competing” standards for accessing cryptographic tokens: PKCS#11 and OpenPGP. This is probably a gross oversimplification, but it appears that PKCS#11 is an interoperability standard favoured by proprietary vendors, and OpenPGP is an open standard with an open implementation favoured by crypto geeks. The two groups don’t get along very well, but as long as my end goal can be achieved, do politics matter?


If you look closely, you will notice that most crypto available out there seems to have some arguments against it.

  • RSA 2048-bit: below ECRYPT-CSA and NSA CNSA recommendations
  • RSA 4096-bit: too slow to be practical (8 seconds per operation on NitroKeys)
  • NIST P-whatever: “I no longer trust the constants. I believe the NSA has manipulated them through their relationships with industry.” - Bruce Schneier after seeing Snowden documents
  • Brainpool: “I admit that this is unfortunate.” - Johannes Merkle on BADA55 findings
  • EdDSA: as much as it is loved by crypto geeks, it is relatively new and still hard to come by in crypto hardware in 2020

I settled on EdDSA and Ed25519 for my keys, which left me with NitroKey Start as pretty much the only hardware token that currently supports it and doesn’t cost $650. For anyone wondering, NitroKey have no plans of releasing an HSM with Ed25519 support in the near future.

Yes, the “Start” stick is not tamper-resistant and doesn’t do OTP. Since my use case is merely “don’t expose my crypto keys to devices I work with” and I do not require to “leave my crypto stick unattended in hostile environments”, or OTPs, I can live with that. In fact, if I had more free time on my hands, putting together a stick with a microcontroller like SC4-HSM for key storage would have been the writeup on this page. I don’t (and can’t) hold government clearance, my cash wallet and flat keys are not tamper-resistant either, so I don’t leave them lying around and it works out fine.


NitroKey Start Setup

Instructions for setting up NitroKey are all over the place, some outdated, some linking to GPG documentation where it would make more sense to link to NitroKey’s own documentation.

On Ubuntu, install scdaemon from universe repositories:

$ sudo add-apt-repository universe
$ sudo apt install scdaemon

GPG comes already installed. If you are using a live image, it may not have udev rules. Those can be installed automatically with OpenSC:

$ sudo apt install opensc

The PINs can only be set up after generating or importing the keys. If you start by setting a user PIN, you will activate Gnuk admin-less mode which is a vendor-specific solution that is not compliant with the OpenPGP standard.

Admin PIN exists for cases when companies hand out crypto sticks to their employees. IT sets the admin PIN and the employee only knows the user PIN.

Both PINs must be 14+ characters. Shorter or digit-only PINs are not allowed, they will trigger Conditions of use not satisfied.

Prior to firmware 1.2.5 entering the admin PIN wrong 3 times would hard brick the stick. The documentation implies that this is no longer the case and that factory reset is still possible in newer firmwares; however that is a €29.00 experiment I did not attempt.

To use Ed25519, the stick needs to be reconfigured first:

$ gpg-connect-agent "SCD SETATTR KEY-ATTR --force 1 22 ed25519" /bye
$ gpg-connect-agent "SCD SETATTR KEY-ATTR --force 2 18 cv25519" /bye
$ gpg-connect-agent "SCD SETATTR KEY-ATTR --force 3 22 ed25519" /bye

This should change the output of gpg --card-status to:

Key attributes ...: ed25519 cv25519 ed25519



The seemingly correct way to go about this is to generate your master and subkeys on a secure machine, make a backup, import the subkeys onto the crypto stick, export the public key for the master key, and lock the backup away in a safe.

The alternative is to generate the keys on the crypto stick itself, but I’ve lost unrecoverable things before and have learned to keep backups. To get started:

$ gpg --full-generate-key --expert

You will want to select (9) ECC and ECC to generate the master key using Ed25519. Fill out the prompts and you should have 2 keys: master key with SC (Sign+Certify) capabilities and a subkey with E (Encrypt). Type in save.

Depending on your planned usage, you may also need to generate S (Sign) and A (Authenticate) subkeys. To do that, run:

$ gpg --edit-key --expert keyID

Where keyID is the fingerprint of your generated master key. Then, type in addkey. Select (11) ECC (set your own capabilities) and toggle the necessary capabilities to generate the keys you want. Hit q followed by save when done.

Before proceeding further, make a backup of your keys. There are many different ways to achieve this; the simplest seems to be to make a copy of ~/.gnupg directory.



Run the same key editing command as before:

$ gpg --edit-key --expert keyID

Where keyID is the fingerprint of your generated master key. Type in toggle to switch to working with private keys. Select the keys one by one using key <number>, for example, key 1. Import them in the crypto stick by running keytocard. It will ask you which of the 3 slots to use, pick those according to the purposes of your subkeys.

Repeat this process until you have transferred all your subkeys to the stick and hit save.

If you run gpg --card-status now, you will see all your keys that are now stored in the crypto stick.



Before you can use this stick on another machine, the public key for the master key needs to imported there. Export it with:

$ gpg --armor --export keyID > pubkey.asc

You can go with you master key’s fingerprint as the keyID, but I found that even if you provide one of a subkey, the master public key will get exported.

Now keep your backup safe, and put this pubkey.asc somewhere you can easily access. Your website, your laptop, a USB stick are all fine. It’s not a secret file, it’s safe to share it.

Import it on the computer where you intend to use your crypto stick with:

$ gpg --import pubkey.asc

This will make the keyring on that computer aware of keys in your crypto stick. You will also have to mark it as trusted. Open it for editing:

$ gpg --edit-key keyID

And mark it as trusted by typing in trust. For your own master key, you probably want option 5 = I trust ultimately.


Use Cases

For all use cases, your machine needs to be aware of the keys. The public key must be imported and trusted as described in the previous section.

File Encryption and Signing

To encrypt a file with the public key of the intended recipient:

$ gpg -e -r recipientID -o encrypted_file_path.pgp file_path

As far as I can tell, recipientID is a very flexible field. As long as it can match some recipient on your keyring, it will work. For my key Georgi Boiko <> with ID ABCDEFABC, all of the following are valid recipients:

  • georgi
  • boiko
  • pandasauce
  • ABCDEFABC - as long as it is at least 8 bytes off the tail of the full keyID

To decrypt it with your private (sub)key:

$ gpg -d encrypted_file_path.pgp

To sign it with your private (sub)key:

$ gpg -s -o signed_file_path.sig file_path

To verify a signature with a public key:

$ gpg --verify signed_file_path.sig



Requires creation or modification of two files:

  • ~/.gnupg/gpg.conf to contain use-agent
  • ~/.gnupg/gpg-agent.conf to contain enable-ssh-support

The shell configuration script (.bashrc, .zshrc etc) needs to pick up this:

if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then
export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"

The agent can be restarted with pkill gpg-agent.

To get an OpenSSH-compatible public key:

$ gpg2 --export-ssh-key keyID >> ~/authorized_keys

Where keyID is the subkey id being used for authentication.


Known Issues

This can come up if attempts were made to use the SmartCard with PKCS#11 after inserting it:

$ gpg --card-status
gpg: selecting card failed: No such device
gpg: OpenPGP card not available: No such device

Re-insert the SmartCard and run gpg --card-status for GPG to initialize.

What About Expirations and Other Stuff?

You got me there. I intentionally left out the bits that everyone should decide for themselves. Do you want to use your real name? Do you want your master key to expire? Do you want the subkeys to? How to set up a secure key generation machine? What PINs to use? Decide for yourself.


I'm Georgi (Russian: Георгий). Although I do various software security things for work, I particularly enjoy reverse engineering and breaking native code on Android and embedded systems. Check out more about me.