Why Mobile Biometric Auth Needs a New Foundation
Mobile authentication is converging on biometrics. Apple's Face ID, Android's BiometricPrompt, and third-party face and fingerprint SDKs have trained over three billion users to expect seamless, passwordless authentication. But the security model behind nearly every mobile biometric implementation shares the same structural weakness: biometric templates are stored or compared in plaintext somewhere in the pipeline.
Even when templates are encrypted at rest, the comparison step — the moment the server decides "does this face match that enrollment?" — requires decryption. That decrypted moment is the attack surface. It is vulnerable to memory scraping, side-channel extraction, and, increasingly, quantum-accelerated cryptanalysis. If an adversary records the encrypted template today and gains access to a sufficiently powerful quantum computer within the next decade, they can retroactively decrypt and forge every biometric credential in the database. This is the harvest-now-decrypt-later threat, and mobile devices — with their long-lived credentials and billions of enrolled users — are the primary target.
H33 eliminates this attack surface entirely. The H33 Mobile SDK captures biometric embeddings on-device, transmits them to H33's fully homomorphic encryption (FHE) pipeline, and the matching computation happens entirely on encrypted data. The server never sees a plaintext template — not during enrollment, not during verification, not ever. The result is an authentication system that achieves ~50 microseconds per auth, scales to 1.2 million authentications per second on production hardware, and is secured by post-quantum cryptography from day one.
This guide walks through every step: SDK installation, platform permissions, initialization, enrollment, verification, error handling, offline resilience, and enterprise deployment. All code examples are production-ready Swift (iOS) and Kotlin (Android).
What You Will Build
A native mobile application with FHE-encrypted biometric authentication: face enrollment, face verification, session management, and graceful degradation — all backed by H33's post-quantum pipeline. The same patterns apply to fingerprint, iris, and voice modalities.
Architecture Overview
Before writing code, it helps to understand the data flow. The H33 Mobile SDK is a thin orchestration layer. It does not contain cryptographic primitives — those live server-side in H33's Rust core. The SDK's job is to capture biometric data, extract embeddings via on-device ML models, and transmit those embeddings securely to H33's API.
| Step | Location | What Happens | Sensitive Data Exposed? |
|---|---|---|---|
| 1. Capture | Device | Camera captures face/fingerprint/iris image | Raw biometric on device only |
| 2. Liveness | Device | On-device liveness detection rejects spoofing attacks | On device only |
| 3. Embedding | Device | ML model extracts a 128-512 dimension float vector | On device only |
| 4. Transmission | TLS 1.3 | Embedding sent to H33 API over Kyber-secured TLS | Encrypted in transit |
| 5. FHE Encrypt | H33 Server | BFV encryption turns float vector into lattice ciphertext | Plaintext exists for <1ms, then zeroed |
| 6. Match | H33 Server | Cosine similarity computed homomorphically on ciphertext | Never decrypted |
| 7. Attest | H33 Server | ZK proof + Dilithium signature attest to match result | Post-quantum attestation |
| 8. Response | TLS 1.3 | Match/no-match + attestation returned to device | No biometric data in response |
The raw biometric image never leaves the device. Only the extracted embedding vector is transmitted. On the server, that embedding is encrypted immediately upon receipt and all subsequent computation occurs on ciphertext. There is no "decryption for comparison" step. This is the fundamental difference between H33 and legacy biometric systems.
Step 1: SDK Installation
The H33 Mobile SDK is distributed as a native framework for each platform. Choose your preferred dependency manager. The SDK has a minimal footprint — ~2.4 MB on iOS, ~1.8 MB on Android — with zero transitive dependencies beyond platform TLS.
iOS — Swift Package Manager
// In Xcode: File → Add Package Dependencies // URL: https://github.com/h33-ai/h33-ios-sdk dependencies: [ .package( url: "https://github.com/h33-ai/h33-ios-sdk", from: "2.0.0" ) ]
iOS — CocoaPods
platform :ios, '15.0' target 'MyApp' do use_frameworks! pod 'H33SDK', '~> 2.0' end
Android — Gradle (Kotlin DSL)
// Add H33 Maven repository repositories { maven { url = uri("https://maven.h33.ai/releases") } } dependencies { implementation("ai.h33:h33-android-sdk:2.0.0") implementation("ai.h33:h33-biometric:2.0.0") // Biometric capture module }
iOS Requirements
- iOS 15.0+
- Swift 5.9+
- Xcode 15.0+
- Devices with TrueDepth camera (Face ID) or Touch ID
Android Requirements
- Android API 28+ (Android 9 Pie)
- Kotlin 1.9+
- CameraX 1.3+
- BiometricPrompt API (AndroidX)
Step 2: Platform Permissions
Biometric authentication requires explicit permission grants on both platforms. The permission model differs significantly between iOS and Android, and getting it wrong causes silent failures that are difficult to diagnose in production. Configure permissions before initializing the SDK.
iOS — Info.plist
<!-- Camera access for face capture --> <key>NSCameraUsageDescription</key> <string>H33 uses your camera for secure biometric enrollment and verification. Your face data never leaves the device as a raw image.</string> <!-- Face ID usage --> <key>NSFaceIDUsageDescription</key> <string>H33 uses Face ID to confirm your identity before accessing protected features.</string>
Android — AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.USE_BIOMETRIC" /> <uses-permission android:name="android.permission.INTERNET" /> <!-- Require biometric hardware --> <uses-feature android:name="android.hardware.camera" android:required="true" /> <uses-feature android:name="android.hardware.biometrics.face" android:required="false" /> <uses-feature android:name="android.hardware.fingerprint" android:required="false" />
The CAMERA permission on Android requires a runtime request starting with API 23. The H33 SDK provides a H33PermissionHelper utility that handles the request lifecycle, but you must call it before invoking any capture method. Failing to do so throws H33PermissionDeniedException.
Step 3: SDK Initialization
Initialize the H33 client once at application launch. The client maintains an internal connection pool, handles token refresh, and manages retry logic. Creating multiple client instances is wasteful and unsupported — the SDK enforces a singleton pattern.
iOS Initialization
import H33SDK @main class AppDelegate: UIResponder, UIApplicationDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { // Initialize H33 with your API key let config = H33Config( apiKey: "h33_pk_your_api_key_here", environment: .production, region: .usEast1 ) // Optional: configure biometric capture settings config.biometric.captureMode = .faceOnly config.biometric.livenessCheck = .active // Require active liveness config.biometric.minConfidence = 0.85 // Embedding quality gate config.biometric.maxRetries = 3 // Network settings config.network.timeout = 10 // seconds config.network.retryPolicy = .exponentialBackoff config.network.certificatePinning = true H33.initialize(with: config) return true } }
Android Initialization
import ai.h33.sdk.H33 import ai.h33.sdk.H33Config import ai.h33.sdk.BiometricMode import ai.h33.sdk.LivenessLevel class MainApplication : Application() { override fun onCreate() { super.onCreate() val config = H33Config.builder() .apiKey("h33_pk_your_api_key_here") .environment(H33Config.Environment.PRODUCTION) .region(H33Config.Region.US_EAST_1) // Biometric capture settings .biometricMode(BiometricMode.FACE_ONLY) .livenessLevel(LivenessLevel.ACTIVE) .minConfidence(0.85f) .maxRetries(3) // Network settings .timeout(10_000L) // milliseconds .certificatePinning(true) .build() H33.initialize(this, config) } }
Never hardcode your API key in source files. On iOS, store it in the Keychain or use a .xcconfig file excluded from version control. On Android, use the BuildConfig mechanism with a local.properties entry or Android Keystore. The H33 SDK accepts keys from SecureStorage on both platforms — see the API documentation for the recommended pattern.
Step 4: Biometric Enrollment
Enrollment is the one-time process of creating an encrypted biometric template for a user. The SDK captures a face (or fingerprint, iris, or voice sample), runs on-device liveness detection, extracts an embedding vector, and transmits it to H33 for FHE encryption and storage. The enrolled template exists only as a lattice-based ciphertext — it cannot be reversed to reconstruct the original biometric.
iOS Enrollment Flow
import H33SDK import UIKit class EnrollmentViewController: UIViewController { private let captureView = H33BiometricCaptureView() override func viewDidLoad() { super.viewDidLoad() setupCaptureView() } private func setupCaptureView() { captureView.delegate = self captureView.mode = .enrollment captureView.overlay = .faceGuide // Shows face alignment oval view.addSubview(captureView) captureView.frame = view.bounds } func startEnrollment(userId: String) { Task { do { // Step 1: Capture face + run liveness check let capture = try await captureView.capture() // Step 2: Verify liveness passed guard capture.livenessResult == .live else { showError("Liveness check failed. Please try again.") return } // Step 3: Enroll with H33 let enrollment = try await H33.biometric.enroll( userId: userId, embedding: capture.embedding, // Float vector from on-device ML modality: .face, metadata: EnrollmentMetadata( deviceModel: UIDevice.current.model, osVersion: UIDevice.current.systemVersion, captureQuality: capture.qualityScore ) ) // Step 4: Store enrollment reference locally try H33SecureStorage.save( key: "h33_enrollment_\(userId)", value: enrollment.enrollmentId ) showSuccess("Enrollment complete. Template ID: \(enrollment.enrollmentId)") } catch let error as H33Error { handleH33Error(error) } } } } // MARK: - H33BiometricCaptureDelegate extension EnrollmentViewController: H33BiometricCaptureDelegate { func captureDidDetectFace(_ aligned: Bool) { // Update UI: show green border when face is aligned captureView.borderColor = aligned ? .systemGreen : .systemRed } func captureDidFail(error: H33CaptureError) { showError(error.localizedDescription) } }
Android Enrollment Flow
import ai.h33.sdk.H33 import ai.h33.sdk.biometric.H33CaptureView import ai.h33.sdk.biometric.CaptureMode import ai.h33.sdk.biometric.Modality import ai.h33.sdk.model.EnrollmentMetadata class EnrollmentActivity : AppCompatActivity() { private lateinit var captureView: H33CaptureView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_enrollment) captureView = findViewById(R.id.h33CaptureView) captureView.mode = CaptureMode.ENROLLMENT captureView.overlay = CaptureMode.FACE_GUIDE } fun startEnrollment(userId: String) { lifecycleScope.launch { try { // Step 1: Capture face + liveness val capture = captureView.capture() // Step 2: Verify liveness if (!capture.isLive) { showError("Liveness check failed.") return@launch } // Step 3: Enroll with H33 API val enrollment = H33.biometric.enroll( userId = userId, embedding = capture.embedding, modality = Modality.FACE, metadata = EnrollmentMetadata( deviceModel = Build.MODEL, osVersion = Build.VERSION.RELEASE, captureQuality = capture.qualityScore ) ) // Step 4: Store enrollment reference in EncryptedSharedPreferences H33SecureStorage.save( context = this@EnrollmentActivity, key = "h33_enrollment_$userId", value = enrollment.enrollmentId ) showSuccess("Enrollment complete: ${enrollment.enrollmentId}") } catch (e: H33Exception) { handleH33Error(e) } } } }
What Happens Server-Side During Enrollment
When H33.biometric.enroll() is called, the SDK makes a single API call to H33's enrollment endpoint. Server-side, the following pipeline executes in approximately 1.5 milliseconds:
- Input validation — Embedding dimension check, NaN/Inf rejection, L2 normalization verification.
- BFV encryption — The float vector is quantized and encrypted into a BFV ciphertext. The plaintext is zeroed from memory immediately after encryption.
- SIMD packing — Up to 32 user templates are packed into a single ciphertext via SIMD batching, reducing storage to approximately 256KB per user.
- Attestation — A Dilithium signature is generated over the enrollment event, creating an auditable post-quantum proof that the enrollment occurred.
- Storage — The encrypted template is persisted. The original embedding is permanently erased.
For highest accuracy in production, capture 3-5 samples during enrollment rather than a single image. The H33 SDK supports multi-sample enrollment via captureView.captureMultiple(count: 5). The server averages the encrypted embeddings to produce a more robust template, reducing false rejection rates by approximately 40% compared to single-sample enrollment.
Step 5: Biometric Verification
Verification is the recurring authentication event. The user presents their face (or fingerprint), the SDK captures and extracts an embedding, and H33 compares it against the enrolled template entirely in the encrypted domain. No decryption occurs during the match. The response includes a match score, a boolean result, and a zero-knowledge proof attesting to the computation's integrity.
iOS Verification
import H33SDK class AuthViewController: UIViewController { private let captureView = H33BiometricCaptureView() func authenticate(userId: String) { Task { do { // Capture + liveness in one call let capture = try await captureView.capture() guard capture.livenessResult == .live else { showError("Liveness check failed.") return } // Verify against enrolled template let result = try await H33.biometric.verify( userId: userId, embedding: capture.embedding, options: VerifyOptions( threshold: 0.75, // Cosine similarity threshold requireZkProof: true, // Include ZK attestation requirePqAttestation: true // Include Dilithium sig ) ) if result.matched { // Authentication succeeded let session = result.session print("Auth latency: \(result.latencyMicroseconds)µs") print("Similarity: \(result.score)") print("ZK proof valid: \(result.zkProofValid)") print("PQ attestation: \(result.pqAttestationValid)") // Navigate to protected content navigateToHome(session: session) } else { showError("Biometric verification failed. Score: \(result.score)") } } catch let error as H33Error { handleH33Error(error) } } } }
Android Verification
import ai.h33.sdk.H33 import ai.h33.sdk.biometric.VerifyOptions class AuthActivity : AppCompatActivity() { private lateinit var captureView: H33CaptureView fun authenticate(userId: String) { lifecycleScope.launch { try { val capture = captureView.capture() if (!capture.isLive) { showError("Liveness check failed.") return@launch } val result = H33.biometric.verify( userId = userId, embedding = capture.embedding, options = VerifyOptions( threshold = 0.75f, requireZkProof = true, requirePqAttestation = true ) ) if (result.matched) { Log.d("H33", "Auth latency: ${result.latencyMicroseconds}µs") Log.d("H33", "Similarity: ${result.score}") Log.d("H33", "ZK proof: ${result.zkProofValid}") navigateToHome(result.session) } else { showError("Verification failed. Score: ${result.score}") } } catch (e: H33Exception) { handleH33Error(e) } } } }
Understanding the Verification Response
| Field | Type | Description |
|---|---|---|
| matched | Bool | Whether the cosine similarity exceeds the configured threshold |
| score | Float | Cosine similarity score (0.0 to 1.0), computed homomorphically |
| latencyMicroseconds | UInt64 | Server-side auth latency (~50µs in production) |
| zkProofValid | Bool | Whether the STARK proof verified successfully |
| pqAttestationValid | Bool | Whether the Dilithium signature on the result is valid |
| session | H33Session | Authenticated session object with token and expiry |
Step 6: Session Management
After successful verification, the SDK returns an H33Session object containing a signed JWT and a session resume token. The JWT is short-lived (default: 15 minutes). The resume token allows transparent session extension without re-capturing biometrics, as long as the device has not left the user's possession (measured via continuous device attestation).
iOS Session Handling
class SessionManager { static let shared = SessionManager() private var currentSession: H33Session? /// Check if the current session is still valid var isAuthenticated: Bool { guard let session = currentSession else { return false } return !session.isExpired } /// Store a new session after successful verification func setSession(_ session: H33Session) { currentSession = session // Persist resume token securely try? H33SecureStorage.save( key: "h33_resume_token", value: session.resumeToken ) } /// Attempt to resume an expired session without re-capture func resumeSession() async throws -> H33Session { guard let resumeToken = try? H33SecureStorage.load( key: "h33_resume_token" ) else { throw H33Error.sessionExpired } let session = try await H33.session.resume(token: resumeToken) setSession(session) return session } /// Sign out and invalidate all tokens func signOut() async { if let session = currentSession { try? await H33.session.revoke(session: session) } currentSession = nil try? H33SecureStorage.delete(key: "h33_resume_token") } }
Step 7: Error Handling and Edge Cases
Production mobile applications must handle every failure mode gracefully. The H33 SDK defines a structured error hierarchy that maps directly to actionable recovery paths. Treating all errors as generic failures is the single most common integration mistake — each error type has a distinct correct response.
| Error | Cause | Correct Recovery |
|---|---|---|
| H33Error.networkTimeout | Server unreachable or slow connection | Retry with exponential backoff (SDK handles this) |
| H33Error.livenessCheckFailed | Presentation attack detected (photo/video/mask) | Show "Try again" with guidance; log for fraud review |
| H33Error.lowQualityCapture | Poor lighting, blur, or partial face | Show capture quality hints; allow retry |
| H33Error.userNotEnrolled | No template exists for this userId | Redirect to enrollment flow |
| H33Error.templateExpired | Template exceeded configured TTL | Re-enrollment required |
| H33Error.rateLimited | Too many verification attempts | Show cooldown timer; do NOT allow brute force |
| H33Error.attestationFailed | ZK proof or Dilithium signature invalid | Reject auth; this may indicate server tampering |
| H33Error.cameraPermissionDenied | User denied camera access | Show settings deep-link to re-grant permission |
Comprehensive Error Handler (iOS)
func handleH33Error(_ error: H33Error) { switch error { case .networkTimeout, .networkError: // SDK already retried with exponential backoff. // Show offline state if all retries exhausted. showOfflineState() case .livenessCheckFailed(let attempts): if attempts >= 3 { // Possible spoofing attack — lock out temporarily lockAccount(duration: .minutes(5)) logSecurityEvent(.livenessFailure, attempts: attempts) } else { showRetryWithGuidance("Ensure good lighting and look directly at the camera.") } case .lowQualityCapture: showRetryWithGuidance("Move to a well-lit area and hold the device steady.") case .userNotEnrolled: navigateToEnrollment() case .rateLimited(let retryAfter): showCooldown(seconds: retryAfter) case .attestationFailed: // Critical security event — do NOT silently continue logSecurityEvent(.attestationFailure) showCriticalError("Authentication integrity check failed.") case .cameraPermissionDenied: showSettingsDeepLink( message: "Camera access is required for biometric authentication." ) default: showGenericError(error.localizedDescription) } }
If zkProofValid or pqAttestationValid returns false, the authentication response may have been tampered with. This is not a transient error — it is a potential security incident. Your application must reject the authentication and log the event for investigation. Do not fall back to a "trust the match score anyway" path.
Offline and Degraded-Network Handling
Mobile applications frequently lose connectivity — elevators, subways, airplane mode, poor cellular coverage. H33's architecture is server-side, which means full FHE verification requires network access. However, the SDK provides two strategies for degraded connectivity:
Strategy 1: Session Resume (Preferred)
If the user was recently authenticated, the locally cached resume token can re-establish the session when connectivity returns. The SDK queues the resume request and executes it as soon as the network is available. For offline intervals under the session TTL (configurable, default 15 minutes), this is transparent to the user.
Strategy 2: Device-Local Biometric Fallback
For applications that must allow access during extended offline periods, the SDK supports a device-local fallback using the platform's native biometric API (Face ID / Touch ID on iOS, BiometricPrompt on Android). This does NOT use H33's FHE pipeline — it relies on the device's Secure Enclave or TEE. Access granted via fallback is flagged in the session metadata so your backend can enforce re-verification when connectivity resumes.
func authenticateWithFallback(userId: String) async { do { // Primary: H33 FHE-encrypted verification let result = try await H33.biometric.verify( userId: userId, embedding: capture.embedding ) handleVerification(result) } catch H33Error.networkError { // Fallback: device-local biometric check let context = LAContext() let reason = "Authenticate offline using Face ID" do { let success = try await context.evaluatePolicy( .deviceOwnerAuthenticationWithBiometrics, localizedReason: reason ) if success { // Grant limited access; flag for re-verification grantOfflineAccess(userId: userId) } } catch { showError("Offline authentication failed.") } } }
Device-local fallback provides convenience but reduces the security guarantee. The biometric match occurs in the device's Secure Enclave, not in H33's FHE pipeline. There is no ZK proof, no post-quantum attestation, and no server-side rate limiting. Use this only when connectivity loss is expected and the risk profile justifies it. Always enforce re-verification via H33 when the network returns.
H33 API Endpoints Reference
The mobile SDK wraps these REST endpoints. You can also call them directly from your backend for server-to-server flows. All endpoints require an Authorization: Bearer header with your H33 API key.
| Method | Endpoint | Description | Latency |
|---|---|---|---|
| POST | /v1/biometric/enroll | Create encrypted biometric template | ~1.5ms |
| POST | /v1/biometric/verify | FHE-encrypted match + ZK proof + PQ attestation | ~50µs |
| POST | /v1/session/resume | Resume session with resume token | ~2ms |
| POST | /v1/session/revoke | Invalidate active session | ~1ms |
| DELETE | /v1/biometric/template/{userId} | Delete enrolled template (GDPR right to erasure) | ~3ms |
| GET | /v1/biometric/status/{userId} | Check enrollment status and template age | ~1ms |
Direct API Call Example
curl -X POST https://api.h33.ai/v1/biometric/verify \ -H "Authorization: Bearer $H33_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "user_id": "user_abc123", "embedding": [0.0234, -0.1893, 0.4521, ...], "modality": "face", "options": { "threshold": 0.75, "require_zk_proof": true, "require_pq_attestation": true } }' // Response { "matched": true, "score": 0.9127, "latency_us": 48, "zk_proof_valid": true, "pq_attestation_valid": true, "session": { "token": "eyJhbG...", "expires_at": "2026-02-24T15:30:00Z", "resume_token": "rt_9xKm..." } }
Liveness Detection Deep Dive
Liveness detection is the first line of defense against presentation attacks — printed photos, video replays, silicone masks, and deepfake injections. The H33 SDK supports three liveness levels, each trading convenience for security.
Passive Liveness
Analyzes a single captured frame for artifacts: moire patterns, screen reflections, paper edges, inconsistent specular highlights. Zero user interaction. Catches printed photos and basic screen replays.
Use case: Low-risk consumer apps
Detection rate: ~94%
Active Liveness (Recommended)
Prompts the user to perform a random micro-action: blink, turn head slightly, smile. Multi-frame analysis detects temporal inconsistencies that defeat static spoofs and most video replay attacks.
Use case: Financial, healthcare, enterprise
Detection rate: ~99.3%
Depth-Based Liveness
Uses TrueDepth (iOS) or structured-light sensors (select Android devices) to build a 3D depth map. Rejects all 2D presentation attacks and most 3D masks. Requires specific hardware.
Use case: Government ID, high-value transactions
Detection rate: ~99.8%
Injection Attack Detection
The SDK validates that the camera feed originates from the physical camera hardware, not from a virtual camera or hooked video pipeline. Detects Xposed/Frida/Magisk-based injection on Android and jailbreak-based camera spoofing on iOS.
Use case: All production deployments
Always enabled
// Configure liveness level during SDK initialization config.biometric.livenessCheck = .active // .passive | .active | .depth config.biometric.livenessActions = [.blink, .turnLeft, .smile] // Random subset used config.biometric.injectionDetection = true // Always recommended
Multimodal Biometric Fusion
Single-modality biometrics (face alone, fingerprint alone) have well-known failure modes: identical twins defeat face recognition; wet fingers defeat capacitive sensors; voice changes with illness. Multimodal fusion combines two or more modalities, dramatically reducing both false acceptance rates (FAR) and false rejection rates (FRR).
The H33 SDK supports enrolling and verifying with multiple modalities in a single API call. The server performs score-level fusion in the encrypted domain, producing a combined match score that is more robust than any individual modality.
// Capture face + voice in sequence val faceCapture = faceCaptureView.capture() val voiceCapture = voiceCaptureView.captureVoice( phrase = "My voice is my password", durationMs = 3000 ) // Verify with both modalities in one call val result = H33.biometric.verifyMultimodal( userId = userId, embeddings = mapOf( Modality.FACE to faceCapture.embedding, Modality.VOICE to voiceCapture.embedding ), fusionStrategy = FusionStrategy.WEIGHTED_SUM, // or .MAX, .PRODUCT weights = mapOf( Modality.FACE to 0.7f, Modality.VOICE to 0.3f ) ) Log.d("H33", "Fused score: ${result.score}, matched: ${result.matched}")
Privacy and Compliance
Biometric data is the most heavily regulated category of personal information globally. The H33 SDK is designed to help you meet compliance requirements across jurisdictions, but the legal obligation ultimately rests with your organization. Here is how H33's architecture maps to major regulatory frameworks.
| Regulation | Requirement | How H33 Addresses It |
|---|---|---|
| GDPR (EU) | Biometric data is "special category" — requires explicit consent, purpose limitation, data minimization, right to erasure | FHE encryption ensures data minimization (server never sees plaintext). Template deletion via DELETE /v1/biometric/template/{userId}. Consent management is your responsibility. |
| BIPA (Illinois) | Written consent, retention schedule, no sale of biometric data, private right of action | H33 never possesses plaintext biometric data. Encrypted templates have configurable TTL. H33 does not sell or share data. |
| CCPA (California) | Right to know, right to delete, right to opt out of sale | Full audit trail via Dilithium-attested events. Deletion API. No data sale. |
| PIPL (China) | Separate consent for biometrics, data localization, security assessment for cross-border transfer | H33 supports region-specific deployment (China region available). Consent is your responsibility. |
The H33 SDK includes a ConsentManager utility that records consent timestamps, consent versions, and purpose descriptions. It stores consent records both locally (for offline access) and server-side (for audit). Use H33.consent.record(userId:, purpose:, version:) before calling any enrollment method. This does not replace legal review — it provides the technical mechanism.
Testing Your Integration
The H33 SDK provides a sandbox environment and mock capture utilities for automated testing. Never use real biometric data in CI/CD pipelines.
Sandbox Environment
// Point to H33 sandbox for integration tests let config = H33Config( apiKey: "h33_sk_test_your_sandbox_key", environment: .sandbox, // Uses sandbox API, no billing region: .usEast1 ) // Use mock capture for unit tests (no camera required) let mockEmbedding = H33TestUtils.generateMockEmbedding( dimensions: 128, similarity: 0.92 // Will produce a match at threshold 0.75 ) let result = try await H33.biometric.verify( userId: "test_user_001", embedding: mockEmbedding ) XCTAssertTrue(result.matched)
Sandbox Limits
- 1,000 enrollments
- 10,000 verifications/month
- Templates expire after 24 hours
- No SLA guarantees
Test Utilities
H33TestUtils.generateMockEmbedding()H33TestUtils.generateNonMatchEmbedding()H33TestUtils.simulateNetworkFailure()H33TestUtils.simulateLivenessFail()
Production Deployment Checklist
Before shipping to the App Store or Google Play, verify every item on this checklist. Each item addresses a failure mode observed in production mobile biometric deployments.
1. API Key in Secure Storage
Verify the API key is loaded from Keychain (iOS) or EncryptedSharedPreferences (Android), not hardcoded in source.
2. Certificate Pinning Enabled
Confirm certificatePinning = true in SDK config. This prevents MITM attacks on the API connection.
3. Liveness Set to Active or Depth
Passive liveness is insufficient for production. Require .active at minimum.
4. Attestation Verification Enforced
Verify your code rejects auth results where zkProofValid or pqAttestationValid is false.
5. Rate Limiting Respected
Confirm your error handler shows a cooldown when H33Error.rateLimited is received. Do not allow infinite retries.
6. Consent Recorded Before Enrollment
Verify H33.consent.record() is called before every enrollment, with the correct purpose and consent version.
7. Template Deletion Implemented
Confirm your settings screen includes a "Delete my biometric data" option that calls DELETE /v1/biometric/template/{userId}.
8. Offline Fallback Behavior Tested
Test with airplane mode: verify session resume works within TTL and device-local fallback works outside TTL.
9. Multi-Sample Enrollment
Production enrollment should capture 3-5 samples for robust template creation.
10. App Transport Security (iOS)
Verify no ATS exceptions are configured. The H33 API supports TLS 1.3 exclusively.
Performance Characteristics
The H33 Mobile SDK adds minimal overhead to the authentication flow. The dominant latency components are network round-trip time (varies by connection quality) and on-device capture/liveness processing. Server-side auth is ~50 microseconds — negligible compared to network latency.
End-to-End Latency Breakdown (Typical)
The ~50 microsecond server-side latency means H33 is never the bottleneck. For detailed server-side benchmarks, see the full auth pipeline benchmark and the January 2026 benchmark report.
React Native and Flutter
For cross-platform applications, H33 provides thin wrapper packages around the native SDKs. These wrappers expose the same API surface with platform-idiomatic bindings.
React Native
npm install @h33/react-native-sdk
Uses the native iOS and Android SDKs under the hood via JSI bridge. Requires react-native >= 0.73.
Flutter
flutter pub add h33_sdk
Platform channel implementation delegates to native SDKs. Supports Dart >= 3.2 and Flutter >= 3.16.
Both wrappers support the full feature set: enrollment, verification, multimodal fusion, session management, and offline fallback. The native capture views are embedded as platform views (not rendered in the cross-platform UI layer) to maintain camera performance.
Enterprise Deployment Patterns
Enterprise mobile deployments introduce additional requirements: MDM integration, multi-tenant isolation, audit logging, and compliance with industry-specific regulations like HIPAA (healthcare), PCI DSS (payments), and SOX (financial reporting).
Multi-Tenant Configuration
Enterprise apps often serve multiple organizations from a single binary. The H33 SDK supports per-tenant isolation via the tenantId parameter. Each tenant has isolated template storage, independent rate limits, and separate audit logs.
// Switch tenant context (e.g., after user selects organization) H33.setTenant("tenant_acme_corp") // All subsequent SDK calls are scoped to this tenant val result = H33.biometric.verify( userId = "employee_12345", embedding = capture.embedding ) // Tenant isolation is enforced server-side: // - employee_12345 in tenant_acme_corp cannot match // against templates in tenant_other_org // - Rate limits are per-tenant // - Audit logs are per-tenant
MDM and App Configuration
For MDM-managed devices, the SDK reads configuration from managed app configuration keys (iOS) and Android Enterprise managed configurations. This allows IT administrators to set the API key, environment, liveness level, and allowed modalities without modifying the application binary.
Audit Logging
Every enrollment and verification event is logged with a Dilithium-signed audit trail. The audit record includes: timestamp, user ID, device fingerprint, liveness result, match result, and attestation hashes. Audit logs are immutable and queryable via the H33 Admin API.
Migrating from Legacy Biometric Systems
If you are migrating from a legacy biometric provider (Auth0, Firebase Auth, AWS Cognito with custom biometric, or a bespoke system), the H33 SDK supports a phased migration strategy:
- Dual-enrollment phase — New enrollments go to H33. Existing users continue authenticating against the legacy system.
- Progressive re-enrollment — When a legacy user authenticates successfully, silently capture a new embedding and enroll it with H33 in the background.
- Cutover — Once re-enrollment reaches your target percentage (typically 95%+), switch primary authentication to H33 and decommission the legacy system.
- Template deletion — Delete all plaintext templates from the legacy system. They are now replaced by FHE-encrypted templates in H33.
During the dual-enrollment phase, the SDK provides a H33.migration.enrollSilently() method that runs enrollment in the background without interrupting the user's authentication flow. This method captures a fresh embedding from the current verification session and enrolls it with H33. The user experiences zero additional friction.
What's Next
This guide covered the core integration path: installation, permissions, initialization, enrollment, verification, session management, error handling, offline fallback, liveness, multimodal fusion, compliance, testing, and enterprise deployment. For deeper dives into specific topics:
- Biometric Authentication: Complete Guide for 2026 — FAR/FRR metrics, matching algorithms, and template protection theory.
- FHE Biometric Authentication — How the encrypted matching pipeline works under the hood.
- Mobile Biometric Security — Device-level security, TEE integration, and anti-tampering.
- What Are Zero-Knowledge Proofs? — Understanding the ZK attestation layer.
- Error Handling Best Practices — Comprehensive patterns for production resilience.
- Biometric SDK Full Reference — Complete API documentation for all SDK methods.
- Testing Authentication Flows — Automated testing strategies for biometric auth.
The H33 Mobile SDK ships post-quantum biometric authentication to the devices your users carry every day. Three billion smartphones, each one a potential enrollment point for FHE-encrypted, zero-knowledge-attested, quantum-resistant identity. Get started with a free API key and have your first enrollment running in under five minutes.
Ready to Go Quantum-Secure?
Start protecting your users with post-quantum authentication today. 1,000 free auths, no credit card required.
Get Free API Key →