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)