Update 2020-02-14: As pointed out by a reader (thank you!), attestations do not protect against man-in-the-middle attacks where an attacker owns a genuine authenticator of the same model as the victim’s. The attestation section below was updated to explain a possible attack scenario.

This article is an advanced blog post about FIDO2. It is recommended to first read the introduction about what FIDO2 is. In this blog post we will describe FIDO2’s security model and discuss advanced topics at the core of the protocol such as attestations.

WebAuthn defines two operations: registration and authentication. Let us see how these operations work and discuss their security.

Registration flow

Figure: Registration flow, from the official WebAuthn specification

During registration, the user who registers a new account on a relying party (RP), with their authenticator, will use their web browser, which implements the WebAuthn specification. The web browser’s WebAuthn implementation will make calls to authenticators using the CTAP2 protocol, which uses a binary format named CBOR to encode data.

The relying party’s web app serves a registration page which contains client-side Javascript code that calls navigator.credentials.create() with some parameters generated by the RP, such as a challenge, randomly generated server-side, to prevent replay attacks, and a list of public key algorithms that the RP supports. The client-side code typically executes when the user clicks a “Register” button on the page. This WebAuthn call is used to tell the web browser to ask an attached authenticator (embedded or via USB, NFC, etc.) to generate a new credential (private/public key pair) using the authenticatorMakeCredential() operation, store it locally (or encrypt it so that only this authenticator can decrypt it and return it so that it can be sent to the RP for server-side storage there).

Before generating a new credential, the authenticator verifies some of the received parameters and performs user verification if the authenticator supports it (for example, ask for a biometric or PIN). Otherwise, it performs a simple user presence test. For example, the authenticator may ask the user to tap on the authenticator to confirm user presence.

The authenticatorMakeCredential operation returns an attestation object which contains an attestation signature and the public key, to be sent to the relying party. The created credential can later be used by the authenticator to sign-in/login during authentication.

The authenticator generates that attestation signature over the concatenation of the authenticator data (the generated public key and the authenticator’s properties) and the hash of the client data (operation type, token binding ID, challenge, origin).

Namely, the authenticator data includes the authenticator’s AAGUID, a 128-bit identifier indicating the make and model of the authenticator. AAGUID stands for “Authenticator Attestation Globally Unique ID”. For example, a Yubikey 5 should have a different AAGUID ( 2fc0579f-8113-47ea-b116-bb5a8db9202a ) than the one of a Solo Key ( 0076631b-d4a0-427f-5773-0ec71c9e0279 ).

The attestation signature and a certificate chain are sent back to the relying party. If the RP cares about attestation, it verifies the attestation signature to make sure that the authenticator actually is the model it pretends to be, and that, for example, user verification was actually performed by the authenticator, as the authenticator claims. It also validates the certificate chain up to a common root of trust obtained through the FIDO Metadata Service (MDS). The RP also verifies whether the authenticator’s security properties are acceptable according to its policy and whether the challenge received is the same as the one generated in the first place.

If all checks are successful, the relying party stores the public key with the newly created user and displays a success message. The user can now use their authenticator to authenticate with this credential on that relying party.

See WebAuthn section 5.1.3 for more details.

Authentication flow

Figure: Authentication flow, from the official WebAuthn specification

The authentication process is largely similar to the registration flow described above but there are some differences which we will discuss here.

During authentication, the user’s web browser renders the relying party’s login page, and typically executes the embedded client-side Javascript code once the “Login” button is clicked. This code will contain a call to navigator.credentials.get() . This WebAuthn call is used to tell the web browser to ask an attached authenticator to generate an assertion signature using existing credentials.

The difference with the registration flow here, is that the RP only generates a challenge and lets the client web browser select the credential (or “account”) that should be used for authentication, instead of initiating the generation of a new credential.

The web browser prepares the client data (type, origin, challenge, tokenBinding) but, this time, the type is set to “webauthn.get” instead of “webauthn.create” since this is an authentication and not a registration operation. It also verifies that the RP ID corresponds to the effective domain of the RP to avoid phishing attacks.

The web browser invokes the authenticatorGetAssertion operation on an attached authenticator with some parameters. The authenticator receives the parameters and makes a few checks. If the parameters contain a non-empty list of allowed credentials, the authenticator tries to decrypt the credential IDs as if they were encrypted (non-resident) keys. If successful, then it adds those keys to the list of selectable keys.

The authenticator prompts the user to select a credential, among the ones scoped to this RP ID to prevent phishing attacks, and performs user presence or user verification tests. It increments the signature counter (if present). This counter is used by the RP to detect cloned authenticators.

The authenticator generates the assertion signature using the private key of the selected credential over the concatenation of the authenticator data and the client data hash received in parameter. The authenticator prepares the authenticator data structure in the same way as during the registration flow and returns a structure containing, namely, the selected credential ID, the authenticator data, and the generated assertion signature.

The web browser sends the credential ID, the client data, the authenticator data, the assertion signature and the user handle back to the Relying Party.

The relying party verifies the challenge, the operation type, the origin, and that user presence or user verification was performed. Finally the RP verifies the assertion signature using the public key associated with the credential ID received. If all checks are successful, then the user is successfully authenticated.

For more details, see WebAuthn section 5.1.4.

What is Token binding?

The client data produced during registration may contain a token binding ID, but what is it used for? The token binding protocol (RFC 8471) allows applications to create uniquely identifiable TLS bindings spanning multiple TLS sessions, preventing token export and replay attacks because the server can then detect if it is replayed over another TLS connection.

Currently, only Edge supports token binding. Other major browsers do not support it. Chrome had an early implementation, which was disabled by default, but Chrome developers decided to remove the feature after weighing the security benefits against maintenance costs, industry adoption, web compatibility risks, etc. Therefore, in practice, this will not be used very often at the moment due to the lack of support by major web browsers.

What is the purpose of attestations?

All authenticators must provide an attestation when a new key pair is generated. Typically, the value that is signed to produce the attestation signature is the concatenation of the authenticator data and the client data hash, but that may differ depending on the attestation format that is used.

The first purpose of an attestation in FIDO2 is to attest to the provenance of an authenticator and the data it creates, such as private/public key pairs. In other words, it allows RPs to verify that the authenticator that generated this public key is really the specific model of authenticator it claims to be. This is important because it allows RPs to trust that the authenticator being used has specific characteristics, such as the method used for storing private keys, the user verification methods used or the authenticator’s claimed overall cryptographic strength. It allows RPs to build an acceptance policy based on authenticator characteristics, and the attestation statement proves to the RP that these characteristics are real.

Update: The second purpose of attestation statements is to prevent an attacker from replacing the public key that was generated with its own and forward the replaced key to the RP. The signature over the public key contained in the attestation statement would not match. However, this does not prevent an attacker from replacing the signature as well.

Indeed, an attacker may own a variety of authenticator models, inspect the intercepted attestation certificate and determine the authenticator model used, request an attestation from their own authenticator of the same model, and forward that attestation to the RP, which is unable to tell if the attestation signature was generated by the victim’s authenticator or the attacker’s.

This attack can be fully automated, including the user presence test on the attacker’s authenticator using GPIO to fake touching the authenticator. This attack is, however, easily detectable since the RP registered the attacker’s authenticator and not the victim’s. If the attacker cannot perform its man-in-the-middle-attack on all of the victim’s computers, then future authentication attempts will fail and the victim will know that something is happening. The attacker can, however, work around this and let the registration ceremony complete unaltered, then silently compromise the session by stealing the cookie and registrer their own authenticator in the background.

Attestation statements are embedded in an attestation object which is returned by the authenticatorMakeCredential operation and conveyed to the relying party during registration. As seen above, this attestation statement contains an attestation signature. This signature is generated differently depending on the attestation type that is used. The attestation statement also usually contains a certificate chain that the RP can use to verify the signature up to a common root of trust. Each attestation type provides a different trust model. Let us see the various attestation types that are available.

Basic attestation

When Basic attestation is used, the signature is obtained by signing the public key that was generated for this registration procedure, using an attestation private key securely contained in the authenticator (burned in at the factory). Note that this attestation private key is the same that is used for signing all the credential key pairs that are generated by this authenticator. Furthermore, the attestation statement also contains an attestation certificate, which contains the corresponding attestation public key, so that the relying party can verify the attestation signature. Note that the RP will also receive a full certificate chain up to a root certificate that is trusted by the RP. The RP can verify that the attestation certificate is valid.

If each authenticator had a different attestation private key, then users could be tracked across relying parties and it would represent a privacy concern. Therefore, it is suggested that authenticator vendors provide the same attestation private key for a large number of authenticators (such as 100’000) to ensure users cannot be tracked. The downside of this approach is that if the attestation private key is compromised, it cannot be revoked and all authenticators sharing the same key are at risk. The risk is that once the attestation private key is compromised, every time a new registration is performed by an authenticator using that attestation key, the relying party cannot distinguish original authenticators and fake ones using the leaked key. Therefore the properties of the authenticator cannot be trusted for future registrations. Keys that were generated during a registration that happened before the compromise are still safe.

Self attestation

Some authenticators do not have an attestation private key, and in that case they must perform self attestation by signing the generated public key using the corresponding private key.

Note that when self attestation is used, the signature does not prove that the authenticator is what it claims to be, since there is no certificate chain or root of trust that the RP can use to verify the claim. It only proves the ownership of the public key.

This attestation type makes sense only when the authenticator does not have sufficient hardware protections to securely hold an attestation private key. Therefore it uses self attestation instead. Note that this attestation type is not secure against man-in-the-middle attacks during registration since the attacker can generate its own key pair, replace the public key by its own and also replace the signature with a valid one since it has the private key required to produce the signature in case of self attestation.

Attestation CA (AttCA)

AttCA attestations are produced by authenticators that have a Trusted Platform Module (TPM) onboard. TPMs have an Endorsement Key (EK) private/public pair burned in during manufacturing. The TPM also contains the certificate, and therefore the public key, of an Attestation CA (ACA). The authenticator uses the TPM’s features to obtain a TPM-backed privacy-friendly private/public Attestation Identity Key (AIK) pair for producing an attestation signature. The authenticator returns this AIK to the platform. The platform communicates with a trusted third-party Attestation CA to obtain an AIK certificate, proving that the Attestation CA trusts this TPM. The platform sends the AIK and the AIK certificate to the RP. This attestation type guarantees that the authenticator uses a TPM and preserves the user’s privacy by generating a new AIK each time instead of using the EK directly.

For complete details about the AIK enrollment process , see chapter 2 of “A CMC Profile for AIK Certificate Enrollment“.

In summary, this attestation type differs from the Basic type because it uses a TPM and a trusted third-party Attestation CA to achieve the same goal. Also, this method creates a new private AIK for every new registration, whereas the Basic method uses the same attestation private key every time. This attestation type is arguably more secure because the AIK certificate issued by the Attestation CA proves that the key is actually stored in a TPM, which guarantees a certain security level. Also, the privacy of users is guaranteed because of the use of the Attestation CA. Finally, the compromise of an AIK private key would affect a single user, while the compromise of an attestation private key in the Basic method would affect all authenticators of that model batch (about 100’000 authenticators).

However, from a privacy perspective, the Attestation CA receives data from multiple RPs and may use it to track user behavior. Also, the trust is shifted to the Attestation CA. All of that makes the Attestation CA an interesting target for attackers.

Elliptic Curve based Direct Anonymous Attestation (ECDAA)

This attestation type uses the FIDO ECDAA algorithm to generate an attestation signature. ECDAA is based on the 2004 paper Direct Anonymous Attestation and offers a good compromise of security, privacy and availability. In ECDAA, there is an ECDAA issuer who is represented by the authenticator vendor.

ECDAA has two steps:

The authenticator performs an ECDAA-Join with the issuer. The issuer securely sends a credential to the authenticator. The authenticator stores the credential and will use it for the rest of its lifetime to generate attestation signatures. This ECDAA-Join needs to be performed only once in the lifetime of the authenticator, and therefore, the issuer must be contacted only once unlike the Attestation CA in the AttCA method. Note that this operation can be performed in-factory , before the authenticator is sold to consumers, meaning that the authenticator does not need to contact any third party once it’s out of factory and the authenticator manufacturer does not need to have an always-online web service to perform ECDAA-Joins, in that case.

, before the authenticator is sold to consumers, meaning that the authenticator does not need to contact any third party once it’s out of factory and the authenticator manufacturer does not need to have an always-online web service to perform ECDAA-Joins, in that case. The authenticator performs an ECDAA-Sign operation to create a signature, which the RP can later verify with an ECDAA-Verify operation.

The properties of this method are:

Unlike the Basic method, ECDAA does not use a shared private attestation key for large batches of a same authenticator model to guarantee privacy at the cost of increased impact in case of a key compromise. Indeed, each authenticator receives a unique credential from the issuer during ECDAA-Join and the algorithm preserves privacy (see ECDAA-Join for details).

No need for an always online service such as an Attestation CA, as long as the ECDAA-Join is performed in-factory.

No attestation (None)

When the RP does not care about attestation, an attestation of type “none” should be used. In this case, the attestation statement is simply empty.

Choosing the best attestation type

In summary, the ECDAA attestation type provides the best of both worlds and is a good candidate for applications that have strict security policies (banking, government) with respect to the authenticators that their relying party should accept. However, not all relying parties have such policies and may just use the Basic attestation type or not care about attestations at all. Moreover, it does not make sense to use the more complex attestation types with authenticators that do not provide sufficient security against physical attacks.

Attestation formats

The attestation format can be one of the defined attestation formats, namely “packed”, “tpm”, “android-key”, “android-safetynet”, “fido-u2f” or “none”. Each attestation format supports different attestation types (which are not the same as attestation formats). For a given attestation type, the same information can be expressed in multiple attestation formats. The most used attestation format should be the “packed” format since it supports all attestation types (Basic, Self, AttCA and ECDAA) except the None type, which has its own dedicated “none” format.

Each attestation type has a different signing procedure for generating a signature given the authenticator data and the client data hash. Remember that the public key is contained in the authenticator data. Therefore, signing over these two values includes signing the public key. The attestation statement therefore contains statements about the public key that was generated and the authenticator that created it. The details of how this signature is generated depends on the attestation type.

What could happen if the RP does not care about attestations?

If the RP does not care about attestations, then it does not care about the characteristics of the authenticator used. This could mean a few things:

If a physically stolen authenticator uses a weak user verification technique or no user verification at all, then that user verification may be bypassed by the thief and the RP must accept that risk.

If an authenticator model’s attestation private key is compromised, an RP that performs proper attestation verification will see that the key was added to a revocation list and should not accept new registrations with that key. If the RP does not care about attestations, it may accept a registration with a fake authenticator that claims to be the original one but is actually a malicious one that may compromise its user’s security.

If attestations are discarded or self attestation is accepted, then man-in-the-middle attacks can happen during registration.

In the end, when proper attestation types are used, the security of users is improved by verifying that the authenticator’s security meets a certain level and blocking man-in-the-middle attacks.

Are authenticators really limited by the number of credentials they can store?

There are two ways to store generated private keys. Either locally on the authenticator (resident keys) or encrypted (non-resident) and sent to the RP for storage. In the case of resident keys, the authenticator is limited by the available storage space and will have a maximum number of keys that it can store. If that happens, the authenticator may offload the storage of the private key to the client platform, but not to server-side entities, such as the RP.

If the keys are encrypted and sent to the RP, then there is no limit to how many keys can be generated with an authenticator. However, note that non-resident keys are not compatible with the usernameless scenario. Typically, the RP should support both resident and non-resident keys but that may vary if the RP has a strict policy.

How does usernameless login work?

To perform a usernameless registration and authentication, the RP must simply serve a registration page where no username is asked and require the use of resident keys by the authenticator. This is required because that is the only way the credential private key to be used for authentication can be selected given only an RP ID.

If the key was encrypted and stored by the RP, then the RP would be unable to determine which encrypted non-resident key should be sent to the client because it doesn’t know any associated username.

How can the Relying Party trust authenticators?

Authenticators peform certain actions client-side, such as user verification. One may wonder how the Relying Party can trust the authenticator when it claims that user verification was performed.

This is one of the purposes of attestations. The attestation signature is computed over the concatenation of the authenticator data and the client data. Since the user verification (UV) bit is part of the authenticator data, the RP can verify the attestation signature. If the signature is verified, then it knows that user verification was performed by an authenticator of the claimed model.

To verify the attestation signature, the Relying Party uses an X.509 certificate chain (returned by the authenticator) that establishes a chain of trust up to a root certificate, which the relying party trusts. If the trust cannot be established, then the attestation signature cannot be verified. Each authenticator model may use a different root certificate. To obtain these root certificates, the relying party may use the FIDO Metadata service (MDS). Indeed, the RP should periodically download metadata statements for all known authenticator models from the MDS. This way, it can trust these models and keep an updated trust store. To access the MDS, an API access token must be requested, and then, the token must be manually renewed yearly. RPs that download all metadata statements from the MDS can be sure to have updated information about all known authenticator models, including their certification status. This is especially useful for RPs to be aware of authenticator models that have been compromised. Authenticator vendors are recommended to publish their authenticator metadata to the MDS.

Relying party responsibilities regarding cloned authenticators

Some authenticators may be cloned. Cloning a device means being able to read its embedded memory, including the attestation private key and any private key credentials that it previously generated, and duplicating all of it to another authenticator (the clone).

Authenticators should support signature counters. A signature counter should be increased every time a credential is used for authentication. The RP should store this counter and verify that for a given user account and authenticator, the counter is always increasing after each successful authentication operation. If that does not happen, then the authenticator may have been cloned and the RP can take an action, such as notifying the user or blocking access to this authenticator if the risk is considered too high by the RP. Note that there is no way for the RP to know whether the original authenticator or the cloned authenticator was used. Alternatively, a non-increasing signature counter may be a sign that the authenticator is malfunctioning/not respecting the specification.

Ideally, the authenticator should support per credential signature counters to allow RPs to detect cloned authenticators while preserving the user’s privacy. Alternatively, authenticators may implement a global signature counter, but that is less privacy-friendly since RPs that share their data between each other may correlate counter values to identify a user. Finally, the authenticator may not support a signature counter at all and provide no way for RPs to detect cloned authenticators. Here is how the signature counter value should be set by the authenticator for each case:

If a per credential counter is supported, then create a new counter initialized to zero for that new credential and use that value in the authenticator data.

If a global counter is supported, then use that global signature counter’s current value in the authenticator data.

If there is no counter support, then use zero as value in the authenticator data.

Relying parties should really check the signature counter and authenticators should implement a per-credential signature counter. Not doing so increases the risk of not detecting a malicious login in case an attacker temporarily steals the victim’s authenticator to clone it and returns the original before the victim notices it, while keeping the clone. If the victim does not notice that their authenticator was cloned and does not revoke it on all services where it was used for registration, the attacker can use the clone to authenticate, provided that they can bypass user verification such as PIN or biometric authentication or that the authenticator does not perform user verification at all.

The signature counter will let the RP detect, at some point, that either a clone was used or that the authenticator is malfunctioning and is not properly increasing the signature counter (less likely). At that point, we can assume with a certain probability that the attacker has already had access to unauthorized information, but at least we now know that it happened and necessary measures can be taken, such as revoking that authenticator and forcibly signing out all active sessions on that account. If the RP does that, it is important to check that the user has registered another authenticator on that account or that they have another method to sign in so that they are not locked out of their account. This is an example of a possible action that could be taken when a clone is detected.

Conclusions

We have described FIDO2 registration and authentication flows, and have seen that the system is based on public-key cryptography. It allows for strong authenticator trust using attestation signatures and a chain of trust that can be updated using the FIDO Metadata Service. Relying parties verifying attestations improve the security of their users. We have seen the various attestation types and their characteristics. The system is complex and offers many options. Hopefully this blog post gives an overview of these options and the security considerations for each. For more details, refer to the Webauthn and CTAP2 specifications.

References