I've been using Yubikeys for 2FA for quite a long time, but never took the chance to look at how they operate at the protocol level. This is what we will discuss in this article.
If you are not familiar with FIDO U2F, this is an authentification standard that enables physical security keys "to act as a second factor to strengthen existing username/password-based login flows." Simply put, it requires you to have a security key plugged into your computer, and optionally to touch it to allow a login after entering your password (NFC versions also exist.)
Note that for the sake of brevity and clarity, I simplified a few things (for example, I am not explaining the role of the browser in the various interactions.) If you want to learn more about the details, Yubico has a very thorough documentation page.
The first step to being able to use your key as 2FA with a website is to register it. The following figure shows how this is done.
Basically, the U2F token generates a pair of (ECC) keys. It then uses its master key - which never leaves the device - to encrypt the private key + App ID, and send it along with the public key to the website.
While sending a private key (even encrypted) can seem a little weird, it enables the security key to not rely on local storage, and it can therefore be used for an unlimited number of websites.
The schema is just showing the App ID as a simplification, but you should understand this App ID as something like the URL of the website/source of the requests to the U2F key.
Now that we have our key setup, we can use it as a 2FA authentication device when logging into the website. What happens when doing so is shown in the following figure.
Long story short the workflow is as follows when the security key is used:
- The U2F key takes the key handle (generated during the registration phase) and decrypts it with its master key to obtain the private key and app ID. This process also enables checking that this website was indeed registered
- The U2F key has a local counter that is incremented each time it authenticates with a website. This allows preventing replay attacks: the website will store the current counter each time an authentification is done and will discard any response with a lower counter
- During the authentification, the client will also check that the App ID matches the address of the website it is currently connected to. This is done to avoid phishing: if a third party tries to intercept the messages, the origin of the authentification request will be different, and therefore the authentication will not move forward
- As you remember from the previous part, the website is storing the ECC public key generated by the U2F token during the registration phase.
The U2F Key will use the ECC private key it retrieved from the handle to sign the challenge (random) data and a few other fields, and the website will be able to validate using the public key that the data was indeed signed by the correct device
You should now have a good comprehension of how U2F Security Keys work. Note that the implementation of such keys can be different from one manufacturer to another. For example:
- There could be one counter for each service the key is used to authenticate with rather than a global one
- The handle does not have to contain the ECC private key. The token could very well send a unique ID to the service, and locally store which ECC private key is associated with which service