Table of Contents (Jump to section)

For over four decades, internet authentication has rested upon a fundamentally flawed premise: the shared secret. Whether a user memorizes an alphanumeric password, receives a 6-digit SMS one-time password (OTP), or copies a Time-based One-Time Password (TOTP) from an authenticator app, the underlying model requires transmitting confidential data across network boundaries to a remote server for verification.

This paradigm is structurally vulnerable to exploitation. Reverse-proxy phishing kits (such as Evilginx) intercept session cookies and TOTP tokens in real time; credential stuffing bots test billions of leaked database dumps against login endpoints daily; and server-side password database breaches expose millions of accounts despite high-iteration Argon2 or bcrypt hashing.

The industry's definitive answer is WebAuthn (Web Authentication, standardized by the W3C and FIDO Alliance under the FIDO2 umbrella) and its consumer deployment form: Passkeys. By replacing shared secrets with asymmetric public-key cryptography locked inside hardware secure enclaves, WebAuthn makes phishing mathematically impossible. In this comprehensive technical guide, we will examine the byte-level cryptography of WebAuthn, dissect CBOR-encoded attestation objects, extract COSE public keys, verify ECDSA assertion signatures, and build a production-grade passkey authentication engine.

WebAuthn Registration and Attestation Ceremony Diagram Figure 1: The WebAuthn Registration & Attestation ceremony. The Relying Party issues an unpredictable cryptographic challenge; the authenticator generates an asymmetric keypair in silicon and returns a CBOR-packed attestation statement.

1. The Cryptographic Architecture of WebAuthn

WebAuthn fundamentally reframes authentication. Instead of storing a secret on the server, the server acts as a Relying Party (RP) that stores only a public key. The corresponding private key is generated directly inside an Authenticator—such as an Apple Secure Enclave (Touch ID / Face ID), a Windows Hello TPM chip, an Android Keystore, or an external hardware security key (YubiKey).

The private key never leaves the hardware boundary. It cannot be exported, read by the operating system, or transmitted over the wire. When a user authenticates, the server presents a cryptographic challenge, and the authenticator signs that challenge using its hardware-isolated private key.

Core Actors in the FIDO2 / WebAuthn Ecosystem

  • The Relying Party (RP): The web application backend (e.g., mangobaz.com) that initiates authentication challenges, validates cryptographic assertions, and establishes user sessions.
  • The Client (User Agent / Browser): The web browser (Chrome, Safari, Firefox) implementing the navigator.credentials JavaScript API. The browser acts as an untrusted intermediary that strictly constructs the origin context and proxies messages.
  • The Authenticator: The cryptographic engine that holds credentials and performs cryptographic operations. Authenticators are classified into:
    • Platform Authenticators: Built into the device hardware (Touch ID, Face ID, Windows Hello).
    • Roaming Authenticators: Removable hardware connected via USB, NFC, or BLE (YubiKey, Nitrokey).

2. The Registration Ceremony: Attestation and Key Generation

Registration is the cryptographic ceremony where an authenticator generates a new asymmetric keypair specifically bound to your web application's origin domain.

Step 1: The Server Generates PublicKeyCredentialCreationOptions

The server creates an options payload containing an unpredictable 32-byte cryptographic challenge, relying party metadata, user identity, and acceptable cryptographic algorithms:

{
  "challenge": "rGq9pY3qW9u_6qK5xY8zT1vB7nM4lK2jP0oI8uY6tE4",
  "rp": {
    "name": "Mangobaz IT Solutions",
    "id": "mangobaz.com"
  },
  "user": {
    "id": "dXNlcl85MDRyX2hhZGk",
    "name": "hadi@mangobaz.com",
    "displayName": "Hadi Zulfiqar"
  },
  "pubKeyCredParams": [
    { "type": "public-key", "alg": -7 },   // ES256 (ECDSA over P-256 with SHA-256)
    { "type": "public-key", "alg": -257 }, // RS256 (RSASSA-PKCS1-v1_5 with SHA-256)
    { "type": "public-key", "alg": -8 }    // EdDSA (Ed25519)
  ],
  "authenticatorSelection": {
    "residentKey": "required",            // Creates a Discoverable Credential (Passkey)
    "userVerification": "preferred"       // Biometric or PIN prompt
  },
  "timeout": 60000
}

Step 2: Browser Invokes navigator.credentials.create()

The client browser parses these options and invokes the WebAuthn API:

// Client-side registration invocation
const credential = await navigator.credentials.create({
  publicKey: creationOptions
}) as PublicKeyCredential;

Before prompting the hardware, the browser constructs the clientDataJSON structure:

{
  "type": "webauthn.create",
  "challenge": "rGq9pY3qW9u_6qK5xY8zT1vB7nM4lK2jP0oI8uY6tE4",
  "origin": "https://mangobaz.com",
  "crossOrigin": false
}

The Phishing Immunity Anchor: Notice the origin field. The browser determines this value programmatically from the current window context. A compromised web page or phishing proxy (e.g., https://mangobaz-login-fake.com) cannot forge this field. If the user is on a phishing site, the browser inserts the phishing URL into clientDataJSON, and the signature will fail server-side verification against the genuine Relying Party ID.

Step 3: Authenticator Generates Keypair and authData

The user satisfies the biometric prompt (Touch ID, Windows Hello). The authenticator generates an asymmetric keypair, binds the private key to rp.id, and returns an attestationObject encoded in CBOR (Concise Binary Object Representation, RFC 8949).

3. Deep-Dive: Decoding authenticatorData and COSE Keys

The core binary artifact generated by any FIDO2 authenticator is authenticatorData (often abbreviated as authData). It is a tightly packed byte array with a fixed structure:

Byte Offset Field Name Length Description
0 .. 31 rpIdHash 32 bytes SHA-256 hash of the Relying Party ID (e.g., SHA256("mangobaz.com")).
32 flags 1 byte Bit flags: UP (User Present, bit 0), UV (User Verified, bit 2), AT (Attested Credential Data, bit 6), ED (Extension Data, bit 7).
33 .. 36 signCount 4 bytes Big-endian 32-bit unsigned integer counter tracking authentications.
37 .. end attestedCredentialData Variable AAGUID (16 bytes) + CredentialIdLength (2 bytes) + CredentialId + COSE Public Key.

The COSE (CBOR Object Signing and Encryption) Key Format

Unlike traditional RSA or PEM keys, WebAuthn encodes public keys using RFC 8152 COSE Key structures. For a standard ES256 (NIST P-256) elliptic curve key, the CBOR map contains integer keys representing cryptographic coordinates:

// Decoded COSE Key Map (RFC 8152)
{
   1: 2,           // Key Type: 2 = EC2 (Elliptic Curve Keys)
   3: -7,          // Algorithm: -7 = ES256 (ECDSA with SHA-256)
  -1: 1,           // Curve: 1 = P-256 (secp256r1)
  -2: ,  // X Coordinate on curve
  -3:    // Y Coordinate on curve
}

The server decodes these CBOR bytes, reconstructs the public point $(X, Y)$, and converts it into standard IEEE P1363 or ASN.1 DER format for persistent database storage.

WebAuthn Authentication and Assertion Verification Diagram Figure 2: The WebAuthn Authentication & Assertion verification pipeline. Zero shared secrets are transmitted; the server verifies the mathematical signature computed over authData + clientDataHash.

4. The Authentication Ceremony: Assertion and Verification

When an existing user logs in, the Relying Party initiates an Assertion Ceremony.

The Cryptographic Signature Calculation

During authentication, the authenticator signs an exact concatenated binary payload using its private key:

$$\text{Payload to Sign} = \text{authenticatorData} \mathbin{\Vert} \text{SHA-256}(\text{clientDataJSON})$$

The authenticator outputs an ECDSA signature $(r, s)$ formatted in ASN.1 DER encoding. The client browser returns this signature along with the raw authenticatorData and clientDataJSON to the server.

The Server-Side Verification Pipeline

To accept the authentication, the server must execute the following non-negotiable verification sequence:

  1. Verify Challenge: Base64url-decode clientDataJSON, parse the JSON, and confirm that clientData.challenge exactly matches the challenge issued in the user's active session.
  2. Verify Origin: Ensure clientData.origin matches your exact expected scheme and domain (e.g., https://mangobaz.com). If an attacker proxies the request through a malicious domain, this check instantly halts the ceremony.
  3. Verify Relying Party ID Hash: Calculate $\text{SHA-256}(\text{"mangobaz.com"})$ and compare it against the first 32 bytes of authenticatorData (authData[0..31]).
  4. Verify User Presence & Verification Flags: Inspect byte 32 of authData:
    • Bit 0 (UP) must equal 1 (confirming physical human interaction).
    • Bit 2 (UV) must equal 1 if your security policy mandates biometric verification.
  5. Verify Signature Counter (Clone Detection): If the authenticator implements a monotonic counter, compare authData.signCount against the stored counter in your database. If signCount <= storedSignCount and signCount != 0, a cloned authenticator attack has occurred; reject the session immediately.
  6. Verify Cryptographic Signature: Hash clientDataJSON with SHA-256, concatenate authData with the hash, and verify the signature using the stored public key.

5. Production Implementation in C# / .NET

Below is a production-grade C# implementation of the assertion verification engine using .NET 8's System.Security.Cryptography.ECDsa and System.Formats.Cbor:

using System;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

public sealed class WebAuthnAssertionVerifier
{
    private readonly string _expectedRpId;
    private readonly string _expectedOrigin;

    public WebAuthnAssertionVerifier(string expectedRpId, string expectedOrigin)
    {
        _expectedRpId = expectedRpId;
        _expectedOrigin = expectedOrigin;
    }

    public bool VerifyAssertion(
        byte[] authData,
        byte[] clientDataJson,
        byte[] signatureDer,
        byte[] expectedChallenge,
        ECParameters storedPublicKey,
        uint storedSignCount,
        out uint newSignCount)
    {
        newSignCount = 0;

        // 1. Parse and verify clientDataJSON
        using var jsonDoc = JsonDocument.Parse(clientDataJson);
        var root = jsonDoc.RootElement;

        var type = root.GetProperty("type").GetString();
        if (type != "webauthn.get") return false;

        var origin = root.GetProperty("origin").GetString();
        if (origin != _expectedOrigin) return false;

        var challengeBase64 = root.GetProperty("challenge").GetString();
        var receivedChallenge = Base64UrlDecode(challengeBase64!);
        if (!CryptographicOperations.FixedTimeEquals(receivedChallenge, expectedChallenge))
            return false;

        // 2. Verify rpIdHash in authData (Bytes 0..31)
        if (authData.Length  actualRpHash = authData.AsSpan(0, 32);
        if (!CryptographicOperations.FixedTimeEquals(actualRpHash, expectedRpHash))
            return false;

        // 3. Verify Flags (Byte 32)
        byte flags = authData[32];
        bool userPresent = (flags & 0x01) != 0;
        bool userVerified = (flags & 0x04) != 0;
        if (!userPresent) return false;

        // 4. Extract and check SignCount (Bytes 33..36, Big Endian)
        uint currentSignCount = (uint)(
            (authData[33] 

6. Syncable Passkeys vs. Hardware Security Keys

The FIDO Alliance introduced Multi-Device Credentials (Passkeys) to solve consumer backup and synchronization without compromising cryptographic strength:

Security Characteristic Hardware Security Keys (YubiKey) Synced Passkeys (Apple / Google / Bitwarden)
Private Key Portability Device-bound (Cannot leave physical token) End-to-End Encrypted Cloud Sync across user devices
Account Recovery Requires registering secondary physical backup key Automated via iCloud / Google Account recovery
Phishing Resistance 100% Mathematical Immunity 100% Mathematical Immunity
Attestation Support Enterprise Attestation (Batch / CA verifiable) Anonymized (Attestation format none)
Primary Use Case High-value infrastructure, SREs, finance admins B2C Consumer SaaS, high-conversion web auth

✓
Verified Engineering Architecture: Formally verified against W3C Web Authentication Level 3 recommendations and tested on iOS 18 (Safari), macOS Sequoia, Android 15, and Windows 11 TPM 2.0. Authored and maintained by the Mangobaz Web Security Desk.
Share this engineering guide Found this walkthrough useful? Help other developers discover it.