Skip navigation

Effective October 28, 2019 Duo Security will be transitioning to Cisco's Privacy Statement. View the Duo Privacy Data Sheet.

Nick Mooney

Nick Mooney

Senior R&D Engineer

Nick Mooney is a Senior R&D Engineer at Duo Security as part of the Duo Labs team. Prior to arriving at Duo, Nick studied Computer Science at the University of Washington in Seattle. Outside of security research, Nick is passionate about boats, islands, and youth outdoor/wilderness education.

How Security Keys Store Credentials

Nick Mooney July 16th, 2019 (Last Updated: July 16th, 2019)

01. Introduction

When you use a security key such as a YubiKey, you need to create a credential for a specific website before you can use it. You can see this process at work when you register with our WebAuthn demonstration site, webauthn.io. The demo site deletes credentials server-side after 24 hours, but what happens to the credential stored on the security key? Does it just sit there taking up storage? Can your security key run out of credential storage?

To answer these questions, we will take a look at a specific concept in the WebAuthn specification: resident and non-resident keys. We will then explore how Yubico has implemented these concepts, and figure out whether or not generated credentials will accumulate in your security keys. As a side note, while we discuss WebAuthn in this post, most of these concepts apply to U2F tokens as well, except the concept of resident keys is not defined in the U2F specification.

02. The Registration Process

Before talking about how keys are stored, it’s important to talk a little about how the WebAuthn registration flow works. At the end of the WebAuthn credential creation process, the authenticator returns an attestation object. The attestation object consists of authenticator data and an attestation statement. The attestation statement is generally only needed to verify the provenance of the credential, so we will focus on the authenticator data object. Specifically, we will be looking at the Credential ID and Credential Public Key, since they are responsible for uniquely identifying a credential.

attestation object diagram

After a credential has been created in the registration flow, a standard 2-factor login flow with WebAuthn looks like this:

  1. The user provides their username to the relying party
  2. The relying party provides a list of registered credential IDs and a challenge value to the authenticator
  3. The authenticator picks a credential ID and hands back a signed assertion containing the challenge provided by the relying party
  4. The relying party uses the credential public key to validate the signature

The private key is never disclosed to the relying party, so the security key must store it for future use… right?

03. “Credential Storage Modality”

The WebAuthn specification has this to say (emphasis mine):

An authenticator can store a public key credential source in one of two ways:

"In persistent storage embedded in the authenticator, client or client device, e.g., in a secure element. A public key credential source stored in this way is a resident credential. By encrypting (i.e., wrapping) the credential private key such that only this authenticator can decrypt (i.e., unwrap) it and letting the resulting ciphertext be the credential ID for the public key credential source. The credential ID is stored by the Relying Party and returned to the authenticator via the allowCredentials option of get(), which allows the authenticator to decrypt and use the credential private key. "

"This enables the authenticator to have unlimited storage capacity for credential private keys, since the encrypted credential private keys are stored by the Relying Party instead of by the authenticator - but it means that a credential stored in this way must be retrieved from the Relying Party before the authenticator can use it."

From this excerpt, we learn that authenticators are permitted to wrap the credential private keys in the credential ID. Key wrapping is a technique allowing cryptographic systems to store private key material with an untrusted host. In the case of WebAuthn, this means an authenticator is permitted to store a single wrapping key that it uses to wrap all the credential private keys. The wrapped credential private key is encoded in the credential ID and handed back to the relying party. Without the wrapping key, the credential ID is useless except as a unique identifier for the credential -- but when the relying party hands the credential ID back to the security key, the security key can unwrap the credential private key and use it to sign assertions.

In case you’re curious, Adam Langley tested various U2F security keys back in 2017 to try to figure out how they were wrapping keys in credential IDs and determine if the encoded data was malleable at all.

04. Yubico’s Take

Yubico published an article in 2014 about their take on U2F key wrapping. Their approach may have changed since then (any Yubifriends reading please let us know if you’re doing anything new!), but it’s an interesting method that takes into account possible cryptographic pitfalls. Here’s what they have to say:

"[Key wrapping] is a sound approach, and is secure when done correctly. It does have its drawbacks, however. One is that it feels less secure, as even though the private key is encrypted, it does leave the device. In practice as long as the encryption used for the wrapping is strong, this isn’t a problem. Another issue is that it introduces additional complexity to the protocol, as we now have a new cryptographic primitive (encryption), with possible pitfalls.

Rather than dealing with these issues, we at Yubico chose to use the following approach (still fully compliant with the U2F specs): instead of randomly generating the key-pair and then encrypting the private key, we deterministically generate a key-pair based on several inputs, so that we can re-create the same key later on when it’s needed, without needing to store it anywhere."

Rather than using key wrapping, Yubico decided to use an HMAC construction (often used to validate message integrity and authenticity) keyed with a device-specific secret. An HMAC functions like a standard hash function with a secret input: the non-secret parameters can be shared publicly, and the secret parameter (the device-specific secret in the YubiKey case) is required to derive the same output from the HMAC. From the AppID (now called “Relying Party ID” in WebAuthn parlance) and a randomly generated nonce value, a specific YubiKey can deterministically derive a credential private key. This has the added value of reusing the SHA256 implementation used elsewhere in U2F and WebAuthn, minimizing the amount of cryptographic primitives used overall and decreasing total attack surface.

The nonce and the output of the HMAC are encoded in the credential ID returned to the Relying Party, and can be used to re-derive the same private key during login.

hmac key construction diagram

(source: Yubico blog)

You can learn more in the article posted by Yubico.

05. Resident Key Storage

The Web Authentication specification allows relying parties to require that a generated credential be a “resident credential,” or in other words prohibit the authenticator from providing a wrapped key to the relying party. In this case, a generated credential will either be stored entirely on the authenticator, or a wrapped key can be stored by the client device (e.g. the web browser on your laptop).

Resident keys provide two unique properties that are not generally provided by non-resident keys:

The private key material is never shared, even in wrapped form, with the Relying Party. This provides some protection in the case of weaknesses discovered in a security key’s key wrapping algorithm now or in the future. The security key (or the client) maintains a mapping from Relying Party ID to credential ID, eliminating the need for the user to provide a username before using the security key to authenticate.

The second property is particularly interesting. In the case of non-resident keys, the security key has no “memory” of which credentials it has generated, so it is unable to provide a credential for, say, webauthn.io without receiving a list of allowed credential IDs from the Relying Party. In the case of resident keys, this mapping is stored. This allows for the following flow:

  1. Navigate to a website
  2. Click “log in” without typing a username
  3. Perform an authorization gesture with your security key (a test of user presence and an optional verification of user identity -- the latter is much more important when the security key is being used as a single factor)

Boom! That’s it. This, combined with user verification, makes WebAuthn a secure and easy-to-use solution for strong single factor authentication. The downside is that resident keys require non-volatile storage, and storage is harder to come by in the small form factors of security keys. The YubiKey 5, for example, can store up to 25 resident credentials (with a maximum of 3 of those being RSA keys).

06. Looking Forward

Today, most WebAuthn credentials generated are not required to be resident keys. This is a great solution that allows inexpensive security keys to generate a practically infinite number of keys. As the world moves toward using WebAuthn as a strong single factor, we expect to see the use of resident keys rise, mostly due to the ease of use provided by the stored mapping from relying party ID to credential ID. This enables a “user-name-less” authentication flow that captures many of the benefits of existing single factor solutions such as smart cards while maintaining the user-friendly security properties we like in existing WebAuthn flows.