PD-107 — Tests & Validation Authentification Biométrique iOS
1. Matrice de traçabilité (CA -> tests)
| Critère | Description | Tests liés (ID global) | Type |
| CA-107-01 | Activation nécessite confirmation mot de passe | TC-107-001, TC-107-021, TC-107-041 | unit, integration, e2e/manual |
| CA-107-02 | Déverrouillage Face ID/Touch ID < 1s | TC-107-004, TC-107-042, TC-107-081 | unit, e2e, performance |
| CA-107-03 | Fallback mot de passe toujours disponible | TC-107-013, TC-107-043 | unit, e2e/manual |
| CA-107-04 | Après 3 échecs -> mot de passe obligatoire | TC-107-010, TC-107-043 | unit, e2e |
| CA-107-05 | Modification biométrie système -> révocation | TC-107-006, TC-107-071 | unit, manual (device réel) |
| CA-107-06 | Réinstallation -> réactivation manuelle requise | TC-107-072 | manual (device réel) |
| CA-107-07 | Inactivité 30 min -> mot de passe obligatoire | TC-107-019, TC-107-044 | unit, e2e |
| CA-107-08 | Changement MDP -> biométrie révoquée | TC-107-024, TC-107-045 | integration, e2e |
| CA-107-09 | Aucun secret en clair dans storage | TC-107-011, TC-107-051 | unit, security |
| CA-107-10 | Journalisation complète de tous les événements | TC-107-026, TC-107-052, TC-107-031 | integration, security, integration |
| CA-107-11 | Pas de contournement SRP-6a | TC-107-053, TC-107-055 | security |
| CA-107-12 | Logs signés et append-only | TC-107-032, TC-107-054 | integration, security |
Note: les scénarios Detox demandés sont couverts par TC-107-041 a TC-107-045, avec alias TC-E2E-107-01 a TC-E2E-107-05.
2. Tests unitaires (Jest)
2.1 BiometricService.test.ts
describe('BiometricService', () => {
describe('enableBiometrics', () => {
it('TC-107-001: should require valid password confirmation before enabling', () => {
// Arrange
// Act
// Assert
});
it('TC-107-002: should return ERR-107-001 when password confirmation fails', () => {
// Arrange
// Act
// Assert
});
});
describe('authenticate', () => {
it('TC-107-003: should authenticate successfully with enrolled biometrics', () => {
// Arrange
// Act
// Assert
});
it('TC-107-004: should resolve biometric flow under 1000ms in nominal path', () => {
// Arrange
// Act
// Assert
});
it('TC-107-005: should map native cancellation to ERR-107-020', () => {
// Arrange
// Act
// Assert
});
});
describe('handleBiometricSetChanged', () => {
it('TC-107-006: should revoke biometric eligibility when iOS biometric set changes', () => {
// Arrange
// Act
// Assert
});
it('TC-107-007: should emit audit event BIOMETRIC_REVOKED_SYSTEM_CHANGE', () => {
// Arrange
// Act
// Assert
});
});
});
2.2 KeychainService.test.ts
describe('KeychainService', () => {
describe('storeEncryptedToken', () => {
it('TC-107-008: should store only encrypted payload with biometryCurrentSet access control', () => {
// Arrange
// Act
// Assert
});
it('TC-107-009: should return ERR-107-040 when keychain write fails', () => {
// Arrange
// Act
// Assert
});
});
describe('readToken', () => {
it('TC-107-010: should increment failed biometric counter on LAContext auth failure', () => {
// Arrange
// Act
// Assert
});
it('TC-107-011: should never expose clear secret in returned object or logs', () => {
// Arrange
// Act
// Assert
});
});
describe('clearBiometricArtifacts', () => {
it('TC-107-012: should delete biometric-bound entries and keep password login path intact', () => {
// Arrange
// Act
// Assert
});
});
});
2.3 UnlockOrchestrator.test.ts
describe('UnlockOrchestrator', () => {
describe('unlock', () => {
it('TC-107-013: should fallback to password challenge when biometric unavailable', () => {
// Arrange
// Act
// Assert
});
it('TC-107-014: should force password mode after 3 consecutive biometric failures', () => {
// Arrange
// Act
// Assert
});
it('TC-107-015: should return ERR-107-060 when policy denies biometric unlock', () => {
// Arrange
// Act
// Assert
});
});
describe('recordAttempt', () => {
it('TC-107-016: should write audit log for each unlock attempt with outcome and reason', () => {
// Arrange
// Act
// Assert
});
});
});
2.4 useBiometricSettings.test.ts
describe('useBiometricSettings', () => {
describe('toggleBiometrics', () => {
it('TC-107-017: should request password confirmation on enable action', () => {
// Arrange
// Act
// Assert
});
it('TC-107-018: should show deterministic error message mapped from ERR-107-001..090', () => {
// Arrange
// Act
// Assert
});
});
});
2.5 useAppUnlock.test.ts
describe('useAppUnlock', () => {
describe('onAppForeground', () => {
it('TC-107-019: should require password after 30 minutes inactivity timeout', () => {
// Arrange
// Act
// Assert
});
it('TC-107-020: should trigger biometric prompt only when policy and session state are valid', () => {
// Arrange
// Act
// Assert
});
});
});
3. Tests d'intégration
| ID | Type | Intégration | Objectif | Préconditions | Résultat attendu |
| TC-107-021 | integration | Keychain <-> BiometricService | Vérifier activation biométrie après confirmation MDP | Session authentifiée + Face ID disponible | Clé biométrique créée et token chiffré stocké |
| TC-107-022 | integration | Keychain <-> BiometricService | Vérifier révocation automatique après changement set biométrique | Biométrie active puis empreinte ajoutée côté iOS | Entrée keychain invalide + flag biometricRevoked=true |
| TC-107-023 | integration | Policy engine <-> UnlockOrchestrator | Valider politique 3 échecs -> password-only | Compteur à 2, puis 1 échec | Biometric désactivé temporairement + challenge MDP |
| TC-107-024 | integration | Policy engine <-> UnlockOrchestrator | Changement mot de passe révoque biométrie | Biométrie active + événement password_changed | Révocation immédiate + nouvel opt-in requis |
| TC-107-025 | integration | Audit logs <-> backend API | Vérifier livraison fiable des événements unlock | File d'événements locale non vide | 200 backend, ack signé, retries stop |
| TC-107-026 | integration | Audit logs <-> backend API | Vérifier complétude des événements (success, fail, fallback, revoke) | Simuler 4 types d'actions | 4 événements reçus avec corrélation ID |
| TC-107-031 | integration | Logs signés <-> backend verifier | Vérifier signature serveur et chaîne append-only | Clef de signature backend valide | Rejet de toute altération intermédiaire |
| TC-107-032 | integration | Logs append-only | Vérifier impossibilité d'update/delete d'un event existant | Event déjà persisté | API retourne conflit/forbidden, audit d'anomalie émis |
4. Tests E2E (Detox)
| ID global | Alias scénario | Type | Scénario | Assertions clés |
| TC-107-041 | TC-E2E-107-01 | e2e | Activation biométrie (flux complet) | Activation demande MDP, succès active toggle, log BIOMETRIC_ENABLED |
| TC-107-042 | TC-E2E-107-02 | e2e | Déverrouillage biométrique nominal | App lockée -> prompt biométrique -> home < 1s |
| TC-107-043 | TC-E2E-107-03 | e2e | Fallback après 3 échecs | 3 refus biométriques -> écran MDP obligatoire, pas de 4e prompt bio |
| TC-107-044 | TC-E2E-107-04 | e2e | Timeout 30 min | Avancer timer 30 min -> retour app -> MDP obligatoire |
| TC-107-045 | TC-E2E-107-05 | e2e | Révocation après changement MDP | MDP changé -> biométrie OFF -> réactivation manuelle exigée |
Notes d'exécution Detox
- Utiliser
device.setBiometricEnrollment(true) et device.matchFace() / device.unmatchFace() selon API iOS Simulator. - Pour CA-107-05 et CA-107-06, compléter sur device réel (non fiabilisé en simulateur).
- Capturer métriques de temps via instrumentation custom Hermes + marker JS.
5. Tests de sécurité
| ID | Type | Contrôle | Méthode | Résultat attendu |
| TC-107-051 | security | Aucun secret en clair en storage | Audit automatique Keychain payload + AsyncStorage dump + grep patterns secrets | Aucun token/secret lisible en clair |
| TC-107-052 | security | Journalisation complète sans fuite | Revue des champs loggés + test intégration de tous événements | Logs complets, sans secret brut |
| TC-107-053 | security | Pas de bypass SRP-6a (unlock biométrique) | Forcer appel API sans challenge SRP, puis via token altéré | Rejet systématique côté backend |
| TC-107-054 | security | Logs signés, append-only | Tentative de modification locale d'un log signé puis envoi | Signature invalide détectée, événement rejeté |
| TC-107-055 | security | Anti-replay session unlock | Rejouer nonce/session proof ancien | Réponse refusée + alerte sécurité |
| TC-107-056 | security | Zeroization mémoire | Tests natifs + JS heap snapshots post-unlock | Buffers sensibles nettoyés, non récupérables |
| ID | Type | Mesure | Protocole | Seuil |
| TC-107-081 | performance | Latence unlock biométrique (P95) | 200 unlocks sur iPhone réel (A15+), collecte percentile | P95 < 1000ms |
| TC-107-082 | performance | Latence accès Keychain lecture | 500 lectures séquentielles et concurrentes | P95 < 150ms |
| TC-107-083 | performance | Overhead audit log à l'unlock | Unlock avec/without audit signing | Surcoût median < 50ms |
7. Tests manuels (device réel)
Checklist exécutable en recette iOS réelle (Face ID/Touch ID actif).
| ID | Type | Scénario manuel | Étapes | Résultat attendu |
| TC-107-071 | manual | Changement empreinte/visage système | Activer biométrie app -> ajouter empreinte dans iOS -> relancer app | Biométrie révoquée, MDP demandé, événement d'audit présent |
| TC-107-072 | manual | Réinstallation application | Activer biométrie -> désinstaller -> réinstaller -> login | Biométrie inactive par défaut, réactivation manuelle requise |
| TC-107-073 | manual | Redémarrage device | Activer biométrie -> redémarrer iPhone -> ouvrir app | Premier unlock exige MDP iOS/app selon politique, puis biométrie possible |
| TC-107-074 | manual | Capteur indisponible temporairement | Simuler capteur indisponible (doigt humide/masque) | Fallback MDP visible immédiatement |
8. Mocks et fixtures
8.1 Mock LAContext (biométrie simulée)
MockLAContextSuccess: canEvaluatePolicy=true, evaluatePolicy -> success. MockLAContextFail: evaluatePolicy -> error (codes mappés vers ERR-107-020, ERR-107-030). MockLAContextChangedSet: renvoie invalidation biometryCurrentSet.
8.2 Mock Keychain
MockKeychainOk: setGenericPassword/getGenericPassword/resetGenericPassword succès. MockKeychainWriteError: échec write -> ERR-107-040. MockKeychainReadCorrupted: payload corrompu -> révocation et fallback MDP.
8.3 Fixture utilisateur authentifié
fixtureUserAuthenticated: utilisateur avec SRP-6a session valide, device trusté. fixtureUserBiometricsEnabled: utilisateur avec opt-in biométrie actif et clés dérivées présentes. fixtureUserPostPasswordChange: utilisateur après rotation MDP (doit invalider biométrie).
9. Couverture des erreurs et invariants (synthèse)
ERR-107-001 a ERR-107-090: couverts via tests unitaires de mapping erreurs (TC-107-018) + e2e négatifs (TC-107-043) + sécurité (TC-107-053, TC-107-055). INV-107-01 a INV-107-10: couverts par combinaisons unit/integration/security ci-dessus, avec focus fort sur non-régression sécurité (TC-107-051 a TC-107-056) et politique d'orchestration (TC-107-014, TC-107-023, TC-107-024).
10. Stratégie d'exécution CI/CD + device réel
- Pipeline PR: exécuter unit + integration + security statique (
TC-107-001 a TC-107-056, hors device-only). - Pipeline nightly iOS simulator: exécuter E2E Detox (
TC-107-041 a TC-107-045) + perf smoke (TC-107-082). - Campagne release candidate sur device réel: exécuter
TC-107-071 a TC-107-074 + perf complète TC-107-081. - Critère de sortie QA: 100% des CA-107-01..12 couverts et passants; aucun test sécurité en warning; P95 unlock < 1s validé sur device réel.