YubiKey, for multi-factor authentication on Linux.

YubiKey Authentication


A YubiKey is a small hardware authentication device that supports PKCS #11, OTP or One-Time Password, and U2F or Universal 2nd Factor with the FIDO/FIDO2 protocols.

Several models are available, for US$ 20–70. They plug into USB ports, either the classic USB-A like the one shown above or the newer USB-C, and some can communicate with NFC or Near-Field Communications over low-power HF radio.

A YubiKey can provide a second mechanism for multi-factor authentication. A password is something you know, while a device like this is something you have.

YubiKey with
YubiKey with
YubiKey with
A YubiKey can function in various ways, depending on which authentication libraries we use. A YubiKey contains a challenge-response application which operates with the pam_yubico.so authentication module, a PIV application which operates with the pam_pkcs11.so authentication module, and a FIDO U2F application which operates with the pam_u2f.so authentication module.

This page explains what a YubiKey does and how to test and configure it with a command-line tool. Other pages explain the specific PAM configuration details.

YubiKey authentication device.

Public Key Cryptography Standards

RSA Security published a series of Public-Key Cryptography Standard or PKCS documents, starting in the early 1990s. PKCS #1 through #15 are mentioned, although #13 and #14 were apparently abandoned before any documents were written, #2 and #4 were merged into #1, and #6 is obsolete as it defined extensions to the old X.509 certification format (from 1988) that were included in X.509v3. The most interesting ones include:

Interacting with a YubiKey

EPEL is Extra Packages for Enterprise Linux, a large collection of software included in the Fedora project but not included with the Enterprise distributions. Linux Package Management

The yubikey-manager package contains only one file, /usr/bin/ykman, an 11-line Python script, which needs some Python packages:
python3-fido2 *
python3-pyscard *
python3-yubikey-manager *
* = in the EPEL repository
Debian and Ubuntu software repositories include it, with Red Hat and Oracle you need to add the EPEL repository.

No documentation is included with the packages, from either the EPEL repositories used with Red Hat and Oracle Linux, or the Debian repositories. Yubico has the missing ykman manual, although it doesn't completely agree with the behavior of the latest available packages available in the EPEL and Debian repositories.

YubiKey Applications

I can ask the device what it is and which applications it supports:

$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 1050:0407 Yubico.com Yubikey 4/5 OTP+U2F+CCID
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
$ ykman list
YubiKey 5 NFC [OTP+FIDO+CCID] Serial: 23981622
$ ykman info
Device type: YubiKey 5 NFC
Serial number: 23981622
Firmware version: 5.4.3
Form factor: Keychain (USB-A)
Enabled USB interfaces: OTP+FIDO+CCID
NFC interface is enabled.

Applications	USB    	NFC    
OTP     	Enabled	Enabled	
FIDO U2F	Enabled	Enabled	
OpenPGP 	Enabled	Enabled	
PIV     	Enabled	Enabled	
OATH    	Enabled	Enabled	
FIDO2   	Enabled	Enabled	
YubiKey authentication device plugged into a laptop.

OTP or One-Time Password

The OTP or One-Time Password application outputs a 44-character string. The device acts as though it were a keyboard. The first 12 characters of the output remain constant, the Public ID of the YubiKey device.

The remaining 32 characters contain an encrypted one-time passcode. That passcode is generated by first concatenating five fields:

Bytes Value
6 Private ID field
2 Number of times plugged in
3 Timestamp: set to a random value at power-up, incremented at 8 Hz.
1 Number of button presses since last plugged in
2 Random number

That 32-byte string is encrypted with AES using a 128-bit key stored in the device. Here is the result of plugging in a device and pressing the button 10 times, cccccbhtuueh is its OTP YubiKey ID.


Yubico uses its own "Modhex" or "Modified Hexadecimal" encoding designed to be portable across a wide range of keyboard layouts and encodings. The YubiKey acts like a keyboard plugged into a USB port. USB keyboards send scan codes, not the actual characters, and it's up to the terminal emulator and the locale setting to turn scan codes into characters. A US keyboard with a QWERTY layout, a French one with AZERTY, or a German one with QWERTZ would agree on the Modhex coding.

Hex 0 1 2 3 4 5 6 7 8 9 a b c d e f
Modhex c b d e f g h i j k l n r t u v

And so the Yubico Modhex cccccbhtuueh corresponds to 0x0000008b7833 0x0000016dee36 or 23981622 in base 10. Yes, that's the serial number.

$ ykman list
YubiKey 5 NFC [OTP+FIDO+CCID] Serial: 23981622 

A good cipher like AES has a diffusion property that spreads the first 6 bytes of the cleartext throughout the ciphertext, and the first 6 bytes of the ciphertext contain substituted bits from throughout the cleartext. The first 6 bytes of ciphertext do not correspond to the first 6 bytes of cleartext.

A Yubico Server could use the ID to look up the AES key programmed into the device at the factory, and then decrypt the remaining 32 bytes to verify the private ID field, the first 6 bytes. It could then use the counters of times plugged in and button presses since then to verify that it's a new event, foiling a replay attack.

And so, you could use the OTP functionality in two ways. First, a weak or naive way which only looks at the cleartext 12-byte initial output. "Does the device output begin with ccccccjnijee?" The second way would decrypt the remaining 32 bytes of the output and check the Private ID. That would require an attacker to extract the Private ID and embedded 128-bit AES key, in order to generate properly encrypted 32-byte passcodes containing the Private ID and an incrementing counter value. Of course, the stronger second method would also require the server to know how to decrypt the passcode! You could pay Yubico for the use of a server with that capability. Or, you can generate your own OTP identity and add it to the device, and then use suitable software to communicate with the device and then decrypt and analyze its output.

There are two "slots", two storage locations, for OTP identities. The first is programmed at the factory, with a private device ID and 128-bit AES key known only to the factory. The second slot is available for your use.

$ ykman otp info
Slot 1: programmed
Slot 2: empty 

To program your own OTP credentials into slot 2 using the device serial number as its public ID, 0x0123456789ab as its 12-byte private ID, and 0xfeedfacedeadbeeffeedfacedeadbeef as the 128-bit AES key:

$ ykman otp yubiotp --serial-public-id \
	--private-id 0123456789ab \
	--key feedfacedeadbeeffeedfacedeadbeef 2
Using YubiKey serial as public ID: vvcccbhtuueh
Program an OTP credential in slot 2? [y/N]: y
$ ykman otp info
Slot 1: programmed
Slot 2: programmed 

While it reported "Using YubiKey serial as public ID", it used vvcccbhtuueh instead of cccccbhtuueh, so 0xff00016dee36 instead of 0x0000016dee36.

As we'll see, that's extremely helpful, essential even, for distinguishing between your locally created identity and the factory-installed one. The one starting 0x00 was installed at the factory, the one starting 0xFF or with your selected value is one you created.

Good to know. Next time I can explicitly set a public ID I will recognize.

You could instead use --public-id followed by an arbitrary 12-character Modhex string. There are at least 26 choices in the form of words or names, these are known to Debian Linux:

$ egrep -i '^[cbdefghijklnrtuv]{12}$' /usr/share/dict/words

That seems arbitrary, jitterbugged but not jitterbugger, the act but not the actor. Mint Linux adds the names Breckenridge and Ferlinghetti to that list. Red Hat offers an additional 197 supposed words, including dubious ones like becudgelling, eleventeenth, digitinerved, fiddledeedee, hedenbergite, killeekillee, tenthredinid, and unthundering, plus downright Turkish looking strings like beglerbeglik.

Now we can swap the contents of the two slots, so our new identity will be used to generate the output:

$ ykman otp swap
Swap the two slots of the YubiKey? [y/N]: y
Swapping slots...
$ cat | tee /tmp/otp-output
vvccccjnijeeeetvrdcdttvrljkltttitdlvjfkdfhcn   # pressing the button 10 times...

That was an interesting experiment, but let's get the OTP functionality back to the original so we don't lose track of the factory identity. To be safe, I'll press the button and make sure we're back to the original ccccccjnijee identity in slot 1. Then I'll delete the test identity in slot 2.

$ ykman otp swap
Swap the two slots of the YubiKey? [y/N]: y
Swapping slots...
$ cccccbhtuuehrdulibltfekbigunvikcfenkrltbicht
cccccbhtuuehrdulibltfekbigunvikcfenkrltbicht: command not found
$ ykman otp delete 2
Do you really want to delete the configuration of slot 2? [y/N]: y
Deleting the configuration of slot 2...
$ ykman otp info
Slot 1: programmed
Slot 2: empty 


FIDO U2F is enabled on the device:

$ ykman list
YubiKey 5 NFC [OTP+FIDO+CCID] Serial: 23981622
$ ykman info
Device type: YubiKey 5 NFC
Serial number: 23981622
Firmware version: 5.4.3
Form factor: Keychain (USB-A)
Enabled USB interfaces: OTP+FIDO+CCID
NFC interface is enabled.

Applications	USB    	NFC    
OTP     	Enabled	Enabled	
FIDO U2F	Enabled	Enabled	
OpenPGP 	Enabled	Enabled	
PIV     	Enabled	Enabled	
OATH    	Enabled	Enabled	
FIDO2   	Enabled	Enabled

$ ykman fido -h
Usage: ykman fido [OPTIONS] COMMAND [ARGS]...

  Manage FIDO applications.


    Reset the FIDO (FIDO2 and U2F) applications:
    $ ykman fido reset

    Change the FIDO2 PIN from 123456 to 654321:
    $ ykman fido set-pin --pin 123456 --new-pin 654321

  -h, --help  Show this message and exit.

  delete   Delete a resident credential.
  info     Display status of FIDO2 application.
  reset    Reset all FIDO applications.
  set-pin  Set or change the PIN code.
  unlock   Verify U2F PIN for YubiKey FIPS.
$ ykman fido info
PIN is not set.
$ ykman fido set-pin --new-pin 1234
$ ykman fido info
PIN is set, with 8 tries left.
$ ykman fido2 info
Usage: ykman [OPTIONS] COMMAND [ARGS]...

Error: No such command "fido2".
$ ykman u2f info
Usage: ykman [OPTIONS] COMMAND [ARGS]...

Error: No such command "u2f".

There has been a yubikey-personalization-gui tool, as shown below.

YubiKey Linux software yubikey-personalization-gui.

However, it offers no FIDO or FIDO2 controls. And, I notice that while I installed this package from the EPEL repository in the spring of 2021, the package reports a Build Date of Jan 17 2018, and the panel displays "Copyright © 2011-2016 Yubico". The company says that the project is no longer maintained, and we should install the graphical yubikey-manager-qt or the command-line yubikey-manager. They recommend some steps for installing those from their on-line repository, but their repository contains no data.

But the FIDO U2F function does work. See my pam_u2f.so page for the details.

YubiKey cryptographic device

PIV Standards and Ciphers and Slots

YubiKey devices support the PIV or Personal Identity Verification interface specified in NIST SP 800-73. This supports RSA or ECC (Elliptic-Curve Cryptography) sign and decrypt operations using a private key stored on the device, through common interfaces like PKCS #11.

The PIV application has its own slots, individual key storage locations with very arbitrary names or addresses. Key slots 9A, 9C, 9D, and 9E can hold RSA or ECC asymmetric keys. Key slot 9B can hold a Triple-DES key used for PIV management.

Access to the keys can be controlled by user knowledge of a PIN. Depending on the slot, the PIN might be needed for every single operation, once to begin a series of operations, or not at all.

Key slots 9A, 9C, 9D, and 9E can hold RSA 1024-bit, RSA 2048-bit, or ECC secp256r1 (also known as NIST P-256) keys. According to Yubico, with firmware v5.2 and later you can store RSA plus ECC with curves secp256r1, secp256k1, secp384r1, secp521r1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1, and curve25519 (x25519 for decipher only, ed25519 for sign/auth only). In 2020, NSA described the CNSA or Commercial National Security Algorithm Suite as protecting US Government data up to TOP SECRET using:

For PIV authentication using slot 9A, you store a PFX (or PKCS #12) file. That file combines the user's private key and their public key in the form of a certificate signed by a trusted CA or Certificate Authority. You create and operate that CA. The YubiKey will require the device PIN to begin any sequence of operations using this slot, as the PFX file contains their private key.

The details of the user key pair don't really matter. It could be RSA of any length, or ECC with the prime256v1 (also called NIST P-256), secp256k1, secp384r1 (P-384), or secp521r1 (P-521) curves. Or, with a more capable version of OpenSSL, any elliptic curve listed in the output of:

$ openssl ecparam -list_curves

What matters is that the certificate must name the user as the CN or Canonical Name, and it must be signed by the CA using either RSA (with a 1024 or 2048-bit key) or ECC secp256r1 (that is, P-256).

Here is an example of a YubiKey on which PIV authentication has not yet been configured.

$ ykman piv info
PIV version: 5.4.3
PIN tries remaining: 3
CHUID:  No data available.
CCC:    No data available.

Here is an example of a YubiKey set up for PIV authentication from today through the following year. The certificate was signed with an ECC P-256 key.

$ ykman piv info
PIV version: 4.3.7
PIN tries remaining: 3
CHUID:	3019d4e739da739ced39ce739d836858210842108421c84210c3eb3410cfd6d8b0604959b1871b5ab6b2ffc34a350832303330303130313e00fe00
CCC: 	f015a000000116ff0217b5e9fa5cc2252ff4070b8ac930f10121f20121f300f40100f50110f600f700fa00fb00fc00fd00fe00
Slot 9a:
	Algorithm:	ECCP256
	Subject CN:	cromwell
	Issuer CN:	pki.cromwell-intl.com
	Serial:		16865202850031608285
	Fingerprint:	6f27531482794e9f2dc79025046e401ee330bffd43fbf579ccc6bcb2f03c2198
	Not before:	2024-06-17 03:17:51
	Not after:	2025-06-17 03:17:51

Now you can configure YubiKey authentication with PAM modules pam_yubico.so or pam_u2f.so or pam_pkcs11.so.