The cryptography behind passkeys

When most people think of cryptography, the first thing they typically think of is encryption: keeping information confidential. But just as important (if not more) is authenticity: ensuring that information is really coming from an authentic source. When you visit a website, the server typically proves its identity through a Transport Layer Security (TLS) certificate authenticated by the Web Public Key Infrastructure (PKI). Passwords are the traditional solution for user authentication, but they suffer from phishing attacks and data breaches. This is where passkeys come in.

Instead of explaining what passkeys are and why they are better than passwords—something many other resources have already covered—this post will examine the cryptography behind passkeys, the guarantees they do or do not give, and interesting cryptographic things you can do with them, such as generating cryptographic keys and storing certificates. You need to understand the cryptography behind passkeys to implement secure authentication correctly. We’ll also discuss the main passkey specification, WebAuthn, and show you how to use extensions of passkey mechanisms to build a more intricate system with different capabilities.

Passkey cryptography basics

At their core, passkeys are just key pairs used to produce digital signatures. When registering a passkey, the website saves the public key and an identifier. When authenticating a user via a passkey, the website provides a challenge and waits for a signed response including this challenge (and some other metadata, such as the identifier). The identifier is used to look up the public key, which is used to verify the signature.

From a cryptographic perspective, this is quite straightforward. The private key authenticates the user, but no sensitive information useful to an attacker is communicated to the server. If the server challenge is properly generated—e.g., as a uniformly random sequence of 32 bytes—then it will prevent replay attacks. Since the server holds only a public key and the user does not send it sensitive information, there is nothing to be leaked in case of a hack.

But digital signatures alone aren’t enough to solve the phishing problem. If we stopped here with just the cryptographic primitives, users would still be vulnerable. For instance, without additional safeguards, an attacker might trick users into signing challenges for the wrong website or reusing the same key pair across multiple sites.

This is why passkeys are built on the W3C’s WebAuthn specification, which adds crucial security properties beyond the basic cryptography. Let’s look at how WebAuthn transforms these simple cryptographic primitives into a phishing-resistant authentication system.

WebAuthn

WebAuthn is the main specification behind passkeys. In simple terms, users access a website (relying party) through their browser (WebAuthn user agent) on a device such as a laptop, phone, or PC (client device). The browser interacts with an authenticator, a piece of hardware or software that generates the passkey key pair, and creates digital signatures using this key pair.

Simplified view of a passkey authentication flow
Figure 1: Simplified view of a passkey authentication flow.

In the diagram above, you can see how a passkey authentication works:

  1. The website requests authentication through the browser.
  2. The browser communicates with the authenticator.
  3. The authenticator checks credentials and user presence.
  4. The authenticator returns a signed response.
  5. The browser forwards this response to the website for verification.

(This interaction between browser and authenticator is described in more detail in another specification: the FIDO Alliance’s Client to Authenticator Protocol (CTAP).) This is a simplified description; the WebAuthn specification allows for a larger variety of use cases (e.g., everything could work via a mobile application instead of a website/browser). However, those specifics are not relevant to understanding how passkeys work with cryptography.

Anti-phishing protections

WebAuthn solves the phishing problem through origin binding. The specification requires browsers to provide the origin of the request (i.e., the website domain) to the authenticator. The authenticator in turn uses passkeys only when the website making the request matches the website that created the passkey.

This means that if you create a passkey for bank.com, a phishing site at fake-bank.com simply cannot use it—your authenticator will refuse the request. Each website also gets its own unique key pair, eliminating the password reuse problem entirely.

Additionally, the specification allows only origins that use HTTPS, which means that the request comes from a server that has a valid certificate for the corresponding origin.

Types of authenticators

Generally, authenticators are “something you have.” All authenticators can check whether a user is actually present when authenticating. Some authenticators can additionally verify the user according to “something they know,” such as a PIN, or “something they are,” such as their biometrics.

There are two main types of authenticators you’ll encounter:

  • Platform authenticators: These live inside the user device itself.
    • Examples: iCloud Keychain, Google Password Manager, Windows Hello, 1Password
    • Pros: Convenient, often include cloud backup capabilities
    • Cons: Vulnerable if the device itself is compromised
  • Roaming authenticators: These are separate dedicated hardware devices
    • Examples: YubiKeys, Titan Security Keys, Feitian keys
    • Pros: Higher security isolation, not affected by device compromise
    • Cons: Can be lost or damaged, typically no backup mechanism

If a platform can do cross-platform communication (such as Bluetooth), its platform authenticators can also be used as roaming authenticators by communicating with another device (e.g., a smartphone1). For maximum security in high-value applications, we recommend using dedicated hardware security keys as your authenticators.

Some authenticators show the user details of the request that it is producing a digital signature for. For authenticators that cannot do this, the browser will display these details instead. Always verify these details before approving an authentication request.

When a user registers a passkey on a website, the authenticator generates a passkey and an identifier (credential ID). The website stores the public key and the identifier and ties them to the user account. The website can then use this identifier to tell authenticators which passkey they want to access. Some authenticators have a lot of storage, and they store all user passkeys themselves. Other authenticators do not, so they instead encrypt the passkey and provide the encrypted passkey to the website as the identifier during registration. When the website wants to authenticate a user, it provides the identifier to the browser, which in turn provides it to the authenticator, which decrypts it and uses the passkey. Essentially, the website is storing the passkey, but since it is encrypted it is of limited value if the website gets hacked.

In theory, you can just store a cryptographic key pair in a file, write some software around it that uses this key pair for cryptographic operations, and pretend that it’s an authenticator. But how can websites know whether its users are using secure authenticators? Authenticators can cryptographically prove certain facts about their origins, like who manufactured it, by generating an attestation statement when the user creates a passkey; this statement is backed by a certificate chain signed by the manufacturer. This is especially useful for enterprise users because it allows the enterprise to ensure that all users have specific authenticators that meet some security requirements. However, attestation is optional: the WebAuthn specification does not require authenticators to support it.

Finally, as with any authentication factor that is “something you have,” an important question is, what happens when you lose it or it breaks? Generally speaking, losing an authenticator means losing all passkeys controlled by it. Since passkeys are essentially randomly generated cryptographic key pairs, there is really no hope of recovery. Most platform authenticators, such as iCloud Keychain, Google Password Manager, and 1Password, allow passkeys to be backed up by synchronizing them to the cloud. However, this is always a trade-off: passkeys that are recoverable have a larger attack surface, in that attackers could try to obtain the passkey through the recovery mechanism. In general, it is important that websites have a recovery mechanism for when users lose access to their passkeys, while keeping in mind that attackers could target this recovery mechanism instead.

While using platform authenticators with backup capabilities reduces the risk of losing passkeys, it does not eliminate it. Users that get banned from the platform would lose access to their passkeys, and the platform could accidentally delete the passkeys. Furthermore, platforms can also support passkey sharing or family accounts, where multiple users can access the same passkeys. The website should warn users of these risks, depending on what access the passkey provides.

Threat model

Despite the marketing claims you might have heard, passkeys aren’t a security silver bullet. Let’s look at what they actually protect against.

The threat model of passkeys shows they protect against threats that passwords typically protect against, while also eliminating the risk of phishing and password reuse. That’s a significant improvement! The Conformance section of the WebAuthn specification makes a very strong statement implying that websites, browsers, and authenticators that conform to the specification are “secure” against malicious behavior.

This claim oversimplifies the security reality. Here are real attack scenarios that can still occur:

  • Browser-based attacks: Some authenticators (like a YubiKey 5C) have no built-in display and rely entirely on the browser to show users what site they’re authenticating to. If your browser is compromised by malware or a malicious extension, it could display “attacker.com” to you while actually sending your authenticator a request to sign for “google.com.”
  • Compromised authenticators: The security of passkeys depends on the authenticator protecting private keys. A counterfeit hardware key, backdoored authenticator software, or malware that impersonates your OS’s built-in authenticator could secretly extract your private keys. Think of buying what appears to be a YubiKey from an untrustworthy source—it might be sending copies of your keys to someone else.

Passkeys do not fully protect against most compromises of user devices, such as malicious browsers or malware. However, they do serve as effective rate limiters for attacks, as each signature requires a separate user interaction with the authenticator. Additionally, passkeys do not protect against attackers that can control the domain of the website, either through a direct takeover or through subdomain hijacking.

Another thing websites need to account for is credential ID collisions. The specification requires only that they are probabilistically unique—meaning they’re generated randomly with an extremely low (but non-zero) chance of duplication, similar to UUIDs.

Why does this matter? When a user registers a passkey, the website stores the credential ID as an identifier for that user’s passkey. If an attacker could somehow register a passkey with the same credential ID as their target victim, they might create authentication confusion.

This might seem far-fetched, but consider these scenarios:

  • An attacker who knows a victim’s credential ID (perhaps captured from network traffic) might try to register their own passkey with that same ID.
  • A malicious authenticator app could deliberately generate duplicate credential IDs rather than follow the protocol’s randomness requirements.
  • Implementation bugs could reduce the effective randomness of credential ID generation.

The fix is straightforward: websites should always reject registration attempts when a new passkey’s credential ID matches one already in the database. This creates a simple “first-come, first-served” protection against credential ID conflicts.

Extensions

WebAuthn also supports defining extensions for mechanisms used to generate credentials and perform authentication. Basically, a website can request the use of one or more extensions through the WebAuthn API. The browser and authenticator will process these extensions if they support them and ignore unsupported extensions.

The WebAuthn specification lists some defined extensions, and links to the Internet Assigned Numbers Authority (IANA) registry for definitions of more extensions. These extensions range from enabling backward compatibility with older APIs to supporting completely different cryptographic functionalities. Since this blog post is about cryptography, those latter extensions are the most interesting.

One such extension is one that the WebAuthn specification calls prf (for pseudorandom function family), which is built on top of the hmac-secret extension defined in the FIDO CTAP v2.1 specification. With the prf extension, the authenticator can calculate HMAC-SHA-256 using a fixed randomly generated 32-byte HMAC key. The input to the HMAC calculation is the SHA-256 digest of a fixed WebAuthn prefix followed by the input provided by the website. While this extension is not flexible enough to implement something like HKDF, it is possible to use it to implement HKDF Extract2.

Another such extension is called largeBlob and prompts supporting authenticators to store a “large blob” of opaque data that the website can read or write during authentication assertions. The website can use this to store any (sensitive) data such as certificates or cryptographic keys.

So using these extensions, it’s possible to derive or store static cryptographic keys. As suggested in the largeBlob example, you might even use this for end-to-end encryption. However, as with all applications of cryptography in the browser setting, it is extremely difficult, if not impossible, to achieve true end-to-end security. Typically, this requires the system to be resistant against a malicious server. Web cryptography runs on JavaScript served by a server, which means that a malicious server can just serve malicious JavaScript that extracts keys, sends decrypted messages back to the server, and so on. Even worse, a malicious server can do this in a highly targeted manner, serving correct JavaScript to most users but malicious JavaScript to a specific target user. Implementing subresource integrity for code on the web (e.g., storing the hash of all published versions with a trusted third party) and binary transparency techniques (e.g., a publicly verifiable, tamper-evident log) are two promising solutions to this kind of problem.

Additionally, it is important to note that the specification considers all extensions optional, which means that there is no guarantee that browsers and authenticators support them. Websites need to check whether extensions are available when requiring specific extensions or else users will have problems accessing their services. In the future, all major browsers and authenticators will hopefully support them, which could improve key management for cryptography on the web.

In general the specification is in active development, and there is room for many more interesting extensions. Possible extensions include additional cryptographic primitives (such as more advanced signature schemes and zero-knowledge proofs), but monotonic counters would be an interesting extension. While this is not directly a cryptographic feature, monotic counters could be used to protect external storage—such as end-to-end encrypted cloud storage—from rollback attacks.

The path forward for passkeys

The time to adopt passkeys is now. The cryptographic foundations of passkeys provide strong security guarantees that make them the clear default choice for modern authentication systems when properly implemented with WebAuthn. While not a perfect security solution, passkeys eliminate many critical vulnerabilities that have plagued passwords for decades: passkeys never transmit sensitive information to servers, cannot be reused across sites, and resist phishing through origin binding.

Here’s our advice for users and developers:

  • Users should adopt passkeys and developers should support them wherever possible. Hardware security keys offer the strongest protection for high-value applications, whereas platform authenticators typically provide better user experience and backup capabilities. When authenticating on untrusted devices, use passkeys from a separate device with its own display to verify the authentication requests.

  • Developers should implement account recovery mechanisms, as passkeys are cryptographic key pairs that cannot be reconstructed if lost. Even platform authenticators with backup capabilities carry risks users should understand.

Passkeys can serve as the first authentication factor, a second authentication factor, or even multiple authentication factors. However, developers need to consider passkeys within their broader threat model. For protection from a malicious server—such as in E2EE applications—implement subresource integrity and binary transparency techniques. As WebAuthn evolves, new extensions will enable more cryptographic applications, though support varies across browsers and authenticators.

If you’re implementing passkeys or exploring novel uses of WebAuthn extensions, contact us to evaluate your design and implementation and help protect your users.


  1. Smartphones often also support something called ‘hybrid transport’, where the phone talks to the authentication server directly, while separately proving its physical proximity to the browser (e.g., through Bluetooth Low Energy). ↩︎

  2. The salt parameter of HKDF Extract would be the randomly generated 32-byte key of the credential, and the input key material would be the SHA-256 digest. The resulting value can be used as the pseudorandom key for HKDF Expand. It is not recommended to generate more than one pseudorandom key per passkey in this way. Instead, it is possible to derive multiple keys from a single pseudorandom key by varying the info parameter of HKDF Expand. ↩︎