Section 3/161 menit

3. Teori: FIDO2 dan WebAuthn Protocol Internals

3. Teori: FIDO2 dan WebAuthn Protocol Internals

Komponen Kriptografi

Passkey menggunakan asymmetric cryptography (public-key). Saat registrasi, authenticator (Secure Enclave di iPhone) menghasilkan key pair:

swift
Registrasi:
┌─────────────────────────────────────┐
│  Secure Enclave (iPhone)            │
│  ┌─────────────┐  ┌──────────────┐  │
│  │ Private Key │  │  Public Key  │  │
│  │ (TIDAK      │  │  (dikirim ke │  │
│  │  keluar)    │  │   server)    │  │
│  └─────────────┘  └──────────────┘  │
└─────────────────────────────────────┘
         │ public key
         ↓
   Server menyimpan: (credentialId, publicKey, rpId, userId, signCount)
swift
Authentication:
  Server kirim: challenge (random bytes, sekali pakai)
         ↓
  Secure Enclave: sign(challenge + authenticatorData) dengan private key
         ↓
  Server verifikasi signature dengan stored public key
  ✓ Jika valid → user terautentikasi

Anatomi Authenticator Data

authenticatorData adalah binary blob yang berisi informasi penting:

swift
Byte offset | Isi
------------|----------------------------------------------
0-31        | rpIdHash (SHA-256 dari RP ID)
32          | flags (bit field):
            |   bit 0: UP  User Present (touch/button)
            |   bit 2: UV  User Verified (biometric/PIN)
            |   bit 6: AT  Attested credential data present
            |   bit 7: ED  Extension data present
33-36       | signCount (big-endian uint32, increment tiap auth)
37+         | attestedCredentialData (hanya saat registrasi):
            |   aaguid (16 bytes)  authenticator model identifier
            |   credentialIdLength (2 bytes)
            |   credentialId (variable)
            |   credentialPublicKey (CBOR-encoded COSE key)

Sign Count dan Anti-Replay

signCount increment setiap kali passkey digunakan untuk autentikasi. Server harus menyimpan dan memverifikasi ini:

swift
Server tersimpan signCount: 5
Assertion baru: signCount = 6 → ✓ Valid, update ke 6
Assertion baru: signCount = 6 → ❌ Replay attack atau klon credential!
Assertion baru: signCount = 0 → ⚠ Mungkin soft authenticator (iCloud Keychain)
                                   iCloud Keychain bisa return 0

Catatan penting: iCloud Keychain Passkey sering mengembalikan signCount = 0 karena sync antar device membuat counter tidak reliable. Jangan reject berdasarkan signCount = 0 untuk platform authenticator.

Client Data JSON

swift
// Struktur clientDataJSON yang dikirim dari device ke server
struct ClientData: Codable {
    let type: String      // "webauthn.create" atau "webauthn.get"
    let challenge: String  // Base64URL-encoded challenge
    let origin: String     // "https://example.com" atau "ios:bundle-id:com.example.app"
    let crossOrigin: Bool? // True jika cross-origin

    enum CodingKeys: String, CodingKey {
        case type, challenge, origin, crossOrigin
    }
}

// Origin format untuk iOS app (berbeda dari web)
// iOS: "https://example.com" (sesuai RP ID, bukan bundle ID langsung)