PD-101 — Upload probatoire mobile chiffré avec progression, reprise et annulation orchestrée¶
1. Objectif¶
Spécifier contractuellement le flux de dépôt mobile d'un document dans ProbatioVault, de la sélection locale jusqu'à la confirmation backend, avec chiffrement local préalable, suivi de progression, gestion d'erreurs, annulation orchestrée et mode optionnel de capture optimisée, sans compromettre le modèle zero-knowledge ni l'intégrité probatoire.
2. Périmètre / Hors périmètre¶
Inclus¶
- Sélection document via Photos picker et File picker.
- Détection de transcodage iOS côté Photos picker et avertissement utilisateur.
- Vérification de taille fichier et rejet si dépassement.
- Chiffrement local FILE-LEVEL avant envoi réseau (AES-256-GCM, clé dérivée HKDF).
- Calcul du hash SHA3-256 du fichier effectivement probatoire avant chiffrement.
- Upload simple (< 10 MB) et multipart (>= 10 MB) via URL pré-signées.
- Suivi UX de progression (pourcentage, ETA, vitesse), y compris en background.
- Retry automatique avec backoff exponentiel et jitter.
- Annulation orchestrée client + S3 + backend (statut
CANCELLED). - Mode "capture optimisée" explicite avec consentement traçable.
Exclu¶
- Proxy Re-Encryption (PRE) et gestion Legal PRE.
- Ancrage blockchain immédiat côté mobile.
- Génération de preuve composite.
- Synchronisation multi-device.
- Suppression sélective de métadonnées EXIF.
3. Définitions¶
Fichier original: fichier binaire présent dans la photothèque ou le système de fichiers avant toute interaction avec l'app.Fichier soumis: fichier binaire effectivement reçu par l'app après sélection (peut différer de l'original si transcodage iOS).Fichier probatoire: fichier sur lequel porte le hash probatoire et qui sera scellé. En mode par défaut = fichier soumis. En mode optimisé = version transformée.Mode par défaut: dépôt strictement fidèle bit-à-bit au fichier soumis (reçu par l'app).Mode capture optimisée: dépôt d'une version transformée après consentement explicite.Zero-knowledge: backend incapable d'accéder au contenu en clair.doc_id: identifiant unique du dépôt, format UUID v4.Upload simple: envoi en une seule requête pour fichier < 10 MB (10 000 000 bytes, SI base 10).Upload multipart: envoi chunké pour fichier >= 10 MB (10 000 000 bytes, SI base 10).Retryable: erreur pouvant déclencher un retry automatique.Terminal: état sans transition sortante autorisée.
3.1 Modèle d'états contractuel¶
- États UI :
UPLOADING,FAILED,COMPLETED,CANCELLED. - États backend document :
PENDING,READY,PENDING_SEAL,CANCELLED.
3.2 Formats et contraintes de données (source unique)¶
| Donnée | Format/encodage | Taille | Jeu de caractères | Case | Regex/validation | Si invalide |
|---|---|---|---|---|---|---|
doc_id | UUID v4 texte | 36 caractères | [0-9a-f-] | insensitive pour hex | ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$ | Rejet (400) |
sha3_256 | hex | 64 caractères (32 bytes) | [0-9a-f] | case-sensitive (lowercase requis) | ^[a-f0-9]{64}$ + vérification fonctionnelle côté mobile (recalcul post-chiffrement) | Rejet (400) |
nonce AES-GCM | binaire (base64 en transport) | 12 bytes | N/A binaire | N/A | longueur exacte 12 bytes + unicité par doc_id | Échec chiffrement, flux abort |
auth_tag GCM | binaire (base64 en transport) | 16 bytes | N/A | N/A | longueur exacte 16 bytes | Rejet (400) |
optimized | booléen JSON | 1 booléen | true/false | N/A | type booléen strict | Rejet (400) |
ios_transcoded | booléen JSON | 1 booléen | true/false | N/A | type booléen strict | Rejet (400) |
mime_type | MIME RFC 6838 | 3..255 caractères | ASCII printable | case-insensitive | pattern MIME standard | Rejet (400) |
filename | UTF-8 | 1..255 caractères | UTF-8 sans \0 | case-sensitive | non vide | Rejet UX |
file_size_bytes | entier | 1..524288000 bytes | N/A | N/A | entier positif <= 500 MB | Refus UX + aucun upload |
Règle de référence : toute section citant ces données doit référencer cette table, sans redéfinition de format.
3.3 Architecture de chiffrement (FILE-LEVEL)¶
Le chiffrement s'applique au fichier entier (FILE-LEVEL), avant tout découpage multipart :
- Le mobile génère un nonce unique de 12 bytes par
doc_id. - Le fichier probatoire complet est chiffré en une seule opération AES-256-GCM avec ce nonce.
- Le ciphertext résultant (fichier chiffré complet) est ensuite découpé en chunks pour l'upload multipart si nécessaire.
- Les chunks uploadés sont des morceaux du ciphertext, jamais du plaintext.
- Le
auth_tagGCM est calculé sur la totalité du fichier chiffré.
Interdit : chiffrement chunk-level (un nonce par chunk) — un nonce unique par chunk avec la même clé créerait une réutilisation de (key, nonce) et briserait la sécurité GCM.
3.4 Format de preuve de consentement¶
Pour le mode capture optimisée (INV-04), la preuve de consentement est un objet JSON :
{
"action": "optimized_consent",
"timestamp": "2026-03-10T14:30:00.000Z",
"doc_id": "550e8400-e29b-41d4-a716-446655440000",
"user_confirmed": true
}
Cet objet est : - Persisté dans les metadata backend du document. - Enregistré dans le journal d'audit. - Horodaté en ISO 8601 UTC.
4. Invariants (non négociables)¶
| ID | Règle | Justification |
|---|---|---|
| INV-01-zero-knowledge | Aucun octet en clair ne doit être envoyé au backend ou à S3. | Respect architecture zero-knowledge. |
| INV-02-pre-encryption | Le chiffrement FILE-LEVEL doit être terminé avant la première requête d'upload. | Empêche fuite réseau de contenu clair. |
| INV-03-probatory-fidelity-default | En mode par défaut, le fichier probatoire est bit-à-bit identique au fichier soumis (reçu par l'app). | Intégrité juridique de la preuve. |
| INV-04-explicit-optimized-consent | Toute transformation (compression/conversion) exige consentement explicite traçable avant dépôt (cf. §3.4). | Interdiction des transformations silencieuses. |
| INV-05-hash-functional-validation | Le hash SHA3-256 est validé fonctionnellement côté mobile par recalcul contrôlé (hash avant chiffrement, puis recalcul sur le plaintext déchiffré pour vérifier l'intégrité du pipeline). Le backend ne fait que valider le format (regex + longueur). | Évite faux positifs de conformité tout en respectant le zero-knowledge. |
| INV-06-nonce-uniqueness | Le nonce AES-GCM est unique par document (doc_id). Un seul nonce par fichier (chiffrement FILE-LEVEL). | Sécurité cryptographique GCM. |
| INV-07-sensitive-purge | Aucun artefact sensible en clair persistant sur disque ; purge fichier stricte + purgeStale() au démarrage du flux. La purge mémoire RAM est best-effort sur Hermes/React Native (cf. H-06). | Résilience crash et conformité sécurité. |
| INV-08-envelope-encryption | Tout artefact cryptographique temporaire (clé, fragment, DEK, ReKey) est chiffré au repos (AES-256-GCM ou HSM envelope). Aucun secret en clair en base. | Invariant crypto-proof obligatoire. |
| INV-09-state-transitions | Toute transition d'état (UI et backend) doit être explicitement autorisée ou interdite (cf. §5.6). | Évite ambiguïtés de machine à états. |
| INV-10-no-sensitive-logs | Les logs ne doivent contenir ni plaintext, ni clés, ni nonce brut, ni hash corrélable au contenu sans besoin métier explicite. | Réduction surface de fuite. |
5. Flux nominaux¶
5.1 Flux nominal A — Dépôt fidèle (mode par défaut)¶
- L'utilisateur sélectionne un fichier (Photos ou File picker).
- Le système récupère métadonnées locales (
filename,mime_type,file_size_bytes) et génèredoc_id. - Contrôle taille :
file_size_bytes <= 500 MB; sinon refus. - Si source Photos et transcodage détecté :
- Affichage avertissement explicite.
- L'utilisateur peut continuer ou changer de source.
- Si l'utilisateur continue,
ios_transcoded: trueest enregistré dans les metadata. Le fichier probatoire est le fichier reçu par l'app (post-transcodage), et INV-03 s'applique à ce fichier soumis. - Affichage disclaimer métadonnées EXIF : l'utilisateur doit confirmer explicitement avant que l'upload ne puisse continuer.
- Calcul
sha3_256sur fichier probatoire (ici = fichier soumis), puis chiffrement local AES-256-GCM FILE-LEVEL (cf. §3.3). - Vérification d'intégrité mobile : recalcul du hash sur le plaintext conservé en mémoire et comparaison avec le hash calculé à l'étape 6. Si divergence, abort du flux (ERR-02).
- Initialisation backend : création enregistrement
PENDING+ URL pré-signée(s). - Sélection stratégie upload :
< 10 MB (10 000 000 bytes): upload simple.>= 10 MB (10 000 000 bytes): multipart (chunks du ciphertext).- Exécution upload avec progression UI en continu.
- Confirmation de finalisation au backend ; transition backend
PENDING -> READY -> PENDING_SEAL.
5.2 Flux nominal B — Dépôt en mode capture optimisée¶
- L'utilisateur active explicitement "Capture optimisée".
- Le système affiche avertissement d'altération probatoire ; consentement explicite requis (cf. §3.4 pour le format de preuve).
- Le fichier probatoire devient la version transformée.
optimized = trueest enregistré. La preuve de consentement (§3.4) est persistée en metadata backend et dans le journal d'audit.- Le hash probatoire porte sur la version transformée.
- Chiffrement et upload suivent le même flux que 5.1.
- Retour vers mode fidèle pour ce dépôt : interdit après confirmation.
5.3 Flux nominal C — Continuité en background¶
- Si application passe en arrière-plan pendant
UPLOADING, l'upload continue. - À terminaison, notification locale unique (succès ou échec) si app non active.
- Au retour foreground, l'état affiché reflète l'état réel sans doublon.
5.4 Flux nominal D — Retry après échec¶
Après FAILED, l'utilisateur peut retenter le dépôt. Le retry crée un nouveau flux complet : nouveau doc_id, nouveau nonce, nouveau chiffrement, nouvel upload. L'ancien enregistrement backend reste en état FAILED / PENDING (non modifié). Aucune réutilisation de doc_id ou de nonce n'est autorisée.
5.5 Paramètres numériques contractuels¶
| Paramètre | Défaut | Min | Max | Unité | Contexte | Hors bornes |
|---|---|---|---|---|---|---|
| Taille max fichier | 500 | 1 | 500 | MB | Tous devices supportés app | Rejet UX |
| Seuil multipart | 10 | 10 | 10 | MB (10 000 000 bytes, SI base 10) | Règle fixe produit | N/A (règle fixe) |
| Taille chunk multipart | 5 | 5 | 50 | MB | Mobile, streaming, RAM maîtrisée | Clamp [5,50] puis upload |
| Retry max | 3 | 0 | 5 | tentatives | Par chunk multipart / global simple | Clamp [0,5] |
| Backoff base | 1 | 1 | 10 | seconde | Réseau mobile/WiFi | Clamp [1,10] |
| Backoff séquence | 1,2,4 | N/A | N/A | secondes | Tentatives 1..3 | N/A |
| Jitter | activé | N/A | N/A | booléen | Anti-thundering herd | Si désactivé: non conforme |
| Mémoire claire en RAM | N/A | 0 | 100 | MB | Device iOS cible | Échec flux + erreur UX |
| Upload perf cible 100MB | 30 | N/A | 30 | secondes (P95) | WiFi stable, device de référence iPhone 12/A14 | Alerte perf (non blocant métier) |
5.6 SLA temporels¶
- Aucune transition temporelle réglementaire de type TTL/expiration métier n'est identifiée dans cette story.
- Point à clarifier : TTL des URL pré-signées non fourni dans le besoin (impact sécurité/disponibilité).
5.7 Transitions d'états (avec transitions inverses)¶
| État | Transitions sortantes autorisées | Transitions interdites |
|---|---|---|
UI UPLOADING | -> COMPLETED, -> FAILED, -> CANCELLED | -> UPLOADING (boucle sans événement) |
UI FAILED | -> UPLOADING (nouveau flux complet, cf. §5.4) | -> COMPLETED direct |
UI COMPLETED (terminal) | aucune | -> * : INTERDITE (état terminal) |
UI CANCELLED (terminal) | aucune | -> * : INTERDITE (état terminal) |
Backend PENDING | -> READY, -> CANCELLED | -> PENDING_SEAL direct |
Backend READY | -> PENDING_SEAL, -> CANCELLED (garde : si PENDING_SEAL non encore atteint) | -> PENDING |
Backend PENDING_SEAL (hors scope story) | géré par worker d'ancrage | -> PENDING, -> CANCELLED |
Backend CANCELLED (terminal) | aucune | -> * : INTERDITE (état terminal, résolution manuelle uniquement) |
Règle retour explicite : toute transition inverse non listée est interdite.
Race condition documentée : une annulation READY -> CANCELLED peut être en concurrence avec le worker d'ancrage effectuant READY -> PENDING_SEAL. La garde est implémentée par un UPDATE ... WHERE status = 'READY' atomique. Si le worker a déjà transitionné vers PENDING_SEAL, l'annulation échoue et le client reçoit une erreur indiquant que le scellement est déjà en cours. Le document ne peut plus être annulé après PENDING_SEAL.
5.8 Atomicité multi-composant (DB + async)¶
| Scope | Synchrone/Async | Garantie |
|---|---|---|
| Création/MAJ enregistrement backend upload | Synchrone (transaction DB) | Atomicité ACID |
Notification/traitement d'ancrage (READY -> PENDING_SEAL) | Async post-commit | Idempotent, retry-safe |
Annulation READY -> CANCELLED | Synchrone (UPDATE WHERE atomique) | Exclusion mutuelle avec transition PENDING_SEAL |
AbortMultipartUpload S3 | Async, best-effort | Reconciliation par job périodique (cf. §5.9) |
| Crash pré-commit | N/A | Rollback complet, aucun état persistant incomplet |
| Crash post-commit | N/A | État DB valide, reprise asynchrone par worker de réconciliation |
5.9 Nettoyage S3 best-effort¶
AbortMultipartUpload est best-effort côté S3. Des parts orphelines peuvent persister en cas d'échec de l'appel d'abort. Deux mécanismes complémentaires assurent le nettoyage :
- S3 Lifecycle Policy : règle d'expiration automatique des uploads multipart incomplets (configurable, recommandé 7 jours).
- Job de réconciliation backend : job périodique qui liste les uploads multipart actifs (
ListMultipartUploads) et aborte ceux dont ledoc_idassocié est en étatCANCELLEDouFAILEDdepuis plus de 24h.
5.10 Contraintes inter-modules¶
Aucune contrainte inter-module applicable (aucun guard cross-module requis par cette story).
5.11 Migration DDL¶
Aucune modification de colonne existante identifiée dans le périmètre de cette story.
5bis. Diagrammes¶
5bis.1 Diagramme d'états — UI (client mobile)¶
stateDiagram-v2
[*] --> UPLOADING : Dépôt confirmé (chiffrement terminé, INV-02)
UPLOADING --> COMPLETED : Upload finalisé + backend READY
UPLOADING --> FAILED : Erreur non retryable ou retries épuisés (ERR-02..06)
UPLOADING --> CANCELLED : Annulation utilisateur (ERR-07)
FAILED --> UPLOADING : Retry utilisateur (nouveau flux complet §5.4)
COMPLETED --> [*]
CANCELLED --> [*]
note right of COMPLETED : Terminal — aucune transition sortante (INV-09)
note right of CANCELLED : Terminal — aucune transition sortante (INV-09)
note left of FAILED : Retry = nouveau doc_id, nouveau nonce (INV-06) 5bis.2 Diagramme d'états — Backend (document)¶
stateDiagram-v2
[*] --> PENDING : Initialisation upload (création enregistrement)
PENDING --> READY : Upload finalisé avec succès
PENDING --> CANCELLED : Annulation utilisateur
READY --> PENDING_SEAL : Worker d'ancrage (async post-commit)
READY --> CANCELLED : Annulation utilisateur (garde atomique UPDATE WHERE)
PENDING_SEAL --> [*] : Hors scope story (worker ancrage PD-55)
CANCELLED --> [*]
note right of PENDING_SEAL : Après PENDING_SEAL, annulation impossible (§5.7)
note right of CANCELLED : Terminal — résolution manuelle uniquement (INV-09)
note left of READY : Race condition READY→CANCELLED vs READY→PENDING_SEAL\nExclusion mutuelle par UPDATE WHERE atomique (§5.7) 5bis.3 Diagramme de séquence — Flux nominal upload (mode par défaut)¶
sequenceDiagram
actor User
participant App as Mobile App
participant Crypto as Module Crypto (local)
participant Backend as Backend API
participant S3 as S3 (stockage)
User->>App: Sélectionne fichier (Photos/File picker)
App->>App: Métadonnées (filename, mime, size) + génère doc_id
App->>App: Contrôle taille ≤ 500 MB (ERR-01 si KO)
opt Transcodage iOS détecté
App->>User: Avertissement transcodage
User->>App: Confirme (ios_transcoded=true)
end
App->>User: Disclaimer EXIF
User->>App: Confirmation explicite
App->>App: purgeStale() — purge artefacts résiduels (INV-07)
App->>Crypto: Hash SHA3-256 du fichier probatoire (INV-05)
Crypto-->>App: sha3_256
App->>Crypto: Chiffrement AES-256-GCM FILE-LEVEL (nonce unique, INV-06)
Crypto-->>App: ciphertext + auth_tag + nonce
App->>Crypto: Recalcul hash sur plaintext en mémoire (INV-05)
Crypto-->>App: sha3_256_verify
App->>App: Comparaison hash (abort si divergence, ERR-02)
App->>Backend: POST /upload/init {doc_id, sha3_256, nonce, auth_tag, metadata}
Backend-->>App: URL(s) pré-signée(s) + état PENDING
alt Fichier < 10 MB (upload simple)
App->>S3: PUT ciphertext via URL pré-signée
S3-->>App: 200 OK
else Fichier ≥ 10 MB (upload multipart)
loop Pour chaque chunk du ciphertext
App->>S3: PUT chunk via URL pré-signée
S3-->>App: 200 OK + ETag
App->>User: Progression (%, ETA, vitesse)
end
end
App->>Backend: POST /upload/finalize {doc_id, parts}
Backend->>Backend: PENDING → READY (transaction ACID)
Backend-->>App: 200 OK
Backend-)Backend: READY → PENDING_SEAL (async, worker ancrage)
App->>User: État COMPLETED
App->>Crypto: Purge artefacts sensibles (INV-07) 6. Cas d'erreur¶
| ID | Cas | Type | Réponse attendue |
|---|---|---|---|
| ERR-01 | file_size_bytes > 500 MB | Métier | Refus immédiat, message utilisateur clair, aucun upload initié |
| ERR-02 | Échec chiffrement local ou échec vérification intégrité hash (§5.1 étape 7) | Technique | Abort immédiat, purge artefacts sensibles, état FAILED |
| ERR-03 | Timeout réseau/S3 | Retryable | Retry auto selon stratégie, puis FAILED si épuisé |
| ERR-04 | Perte connectivité | Retryable | Retry auto, conservation état UPLOADING jusqu'à épuisement |
| ERR-05 | HTTP 5xx backend/S3 | Retryable | Retry auto selon mode |
| ERR-06 | HTTP 4xx fonctionnel (400, 403, 404, 409, 413, 422) | Non retryable | Échec immédiat, état FAILED, message explicite mappé au code HTTP |
| ERR-07 | Annulation utilisateur | Contrôlée | Stop client, abort multipart si initié (best-effort, cf. §5.9), backend CANCELLED (avec garde si READY, cf. §5.7), purge async |
| ERR-08 | Duplication notification retour foreground | UX | Interdit : au plus une notification locale par session d'upload |
| ERR-09 | Format métadonnée invalide (doc_id, sha3_256, etc.) | Validation | Rejet 400 côté API, pas de transition READY |
| ERR-10 | Annulation en état READY alors que worker a déjà transitionné vers PENDING_SEAL | Race condition | L'annulation échoue, le client reçoit un message d'erreur indiquant que le scellement est en cours |
7. Critères d'acceptation (testables)¶
| ID | Critère | Observable |
|---|---|---|
| CA-01 | En mode par défaut, aucune transformation silencieuse n'est appliquée. | Hash du fichier soumis (reçu par l'app) = hash probatoire stocké. |
| CA-02 | En mode optimisé, consentement explicite est requis et optimized=true est tracé. | Journal d'audit + metadata backend contiennent la preuve de consentement (§3.4). |
| CA-03 | Aucun upload en clair n'est possible. | Inspection trafic : pas de payload document en clair. |
| CA-04 | Nonce unique par document. | Deux dépôts distincts n'ont jamais le même nonce. |
| CA-05 | Fichier > 500 MB est refusé avant chiffrement/upload. | Message UI + absence d'appel upload. |
| CA-06 | Seuil multipart appliqué strictement à 10 MB (10 000 000 bytes). | 9.99 MB simple, 10.00 MB multipart. |
| CA-07 | Retry multipart s'applique par chunk, retry simple s'applique globalement. | Traces d'exécution conformes. |
| CA-08 | Annulation orchestre client + S3 + backend CANCELLED. | État final CANCELLED + absence de parts actives S3. |
| CA-09 | Upload continue en background et état reste cohérent au retour. | Pas de perte de progression ni doublon notification. |
| CA-10 | purgeStale() est exécuté au démarrage d'un nouveau flux. | Traces startup flux + suppression artefacts obsolètes. |
| CA-11 | Hash SHA3-256 validé fonctionnellement côté mobile (recalcul post-chiffrement), pas seulement syntaxiquement. | Test de corruption détecté et rejeté. |
| CA-12 | Performance cible : upload 100 MB <= 30 s en WiFi P95 sur iPhone 12/A14. | Campagne de mesure P95 conforme. |
| CA-13 | Aucun secret cryptographique (DEK, KEK, nonce brut) n'est persisté en clair en base de données. | Inspection DB : tous les secrets au repos sont chiffrés (envelope encryption). |
| CA-14 | Aucun log ne contient de plaintext, clé, nonce brut, ou hash corrélable au contenu. | Scan automatisé des journaux d'audit sans correspondance de patterns sensibles. |
8. Scénarios de test (Given / When / Then)¶
-
ST-01 (flux fidèle nominal) Given un fichier valide de 20 MB en mode par défaut When l'utilisateur confirme le dépôt Then le flux est multipart, l'état final est
COMPLETED, backend passePENDING -> READY -> PENDING_SEAL, et le hash probatoire correspond au fichier soumis (reçu par l'app). -
ST-02 (seuil multipart) Given un fichier de 9 MB puis un fichier de 10 MB When l'upload démarre Then le premier est en simple upload et le second en multipart.
-
ST-03 (annulation orchestrée multipart) Given un upload multipart en cours When l'utilisateur annule Then la tâche client s'arrête,
AbortMultipartUploadest déclenché (best-effort), backend estCANCELLED, et les artefacts temporaires sont purgés. -
ST-04 (retry chunk) Given une erreur réseau transitoire sur un chunk multipart When la tentative échoue Then ce chunk est retenté au plus 3 fois avec backoff 1s/2s/4s + jitter.
-
ST-05 (mode optimisé explicite) Given un utilisateur active "Capture optimisée" When il confirme l'avertissement Then
optimized=true, le hash porte sur la version optimisée, la preuve de consentement (§3.4) est persistée, et aucune réversion vers original n'est autorisée pour ce dépôt. -
ST-06 (transcodage Photos détecté) Given une photo transcodée par iOS When l'utilisateur la sélectionne via Photos picker et continue malgré l'avertissement Then
ios_transcoded=trueest enregistré dans les metadata, le hash probatoire porte sur le fichier reçu par l'app. -
ST-07 (purge crash recovery) Given des artefacts sensibles résiduels d'un crash précédent When un nouveau flux démarre Then
purgeStale()supprime ces artefacts avant toute opération de chiffrement. -
ST-08 (fichier hors limite) Given un fichier de 700 MB When l'utilisateur tente le dépôt Then le dépôt est refusé immédiatement avec message clair et sans appel backend d'initialisation.
-
ST-09 (retry après FAILED) Given un flux d'upload terminé en état
FAILEDWhen l'utilisateur retente le dépôt Then un nouveau flux complet démarre (nouveaudoc_id, nouveau nonce, nouveau chiffrement). L'ancien enregistrement n'est pas modifié. -
ST-10 (disclaimer EXIF) Given un fichier contenant des métadonnées EXIF When l'utilisateur sélectionne le fichier pour dépôt Then un disclaimer EXIF est affiché et une confirmation explicite est requise avant que l'upload puisse continuer.
-
ST-11 (annulation en READY — race condition) Given un document en état backend
READYWhen l'utilisateur annule ET le worker d'ancrage tente simultanémentREADY -> PENDING_SEALThen un seul des deux réussit (atomicitéUPDATE WHERE). Si le worker gagne, l'annulation échoue et le client est informé. -
ST-12 (chiffrement FILE-LEVEL vérifié) Given un fichier de 25 MB When le chiffrement est effectué Then un seul nonce est utilisé pour l'ensemble du fichier. Les chunks uploadés sont des morceaux du ciphertext, pas du plaintext.
9. Hypothèses explicites¶
| ID | Hypothèse | Impact si faux |
|---|---|---|
| H-01 | Le projet cible est ProbatioVault-app (React Native + Expo SDK 54 + TypeScript), et non une app Swift native. | §10 contraint technique invalide, gate de conformité échoue. |
| H-02 | Le backend expose les endpoints pré-signés et de finalisation attendus (PD-63 DONE). | Blocage du flux d'upload en production. |
| H-03 | Le worker d'ancrage asynchrone (PD-55 DONE) traite READY/PENDING_SEAL. | Perte de continuité probatoire post-upload. |
| H-04 | La taille de chunk multipart peut être bornée à [5 MB, 50 MB] côté configuration. | Si non accepté, critères perf/mémoire doivent être redéfinis. |
| H-05 | Le format de transport des champs binaires (nonce, auth_tag) est base64 côté API. | Contrat API incompatible, erreurs de validation. |
| H-06 | La purge mémoire RAM sur Hermes/React Native est best-effort. Le garbage collector Hermes ne garantit pas l'effacement immédiat des buffers en mémoire. La purge fichier disque est stricte (suppression explicite). | Les tests de purge RAM sont limités à la vérification que les références sont nullifiées, sans garantie d'effacement physique immédiat. |
| H-07 | La protection TLS (HTTPS) est supposée pour tous les transports réseau (client ↔ backend, client ↔ S3). | Fuite de données en transit si TLS absent. |
10. Points à clarifier¶
| ID | Point manquant | Pourquoi c'est bloquant ou risqué |
|---|---|---|
| Q-01 | Nom exact de l'épopée (valeur "Référence épique" non fournie). | Référence documentaire incomplète. |
| Q-02 | TTL exact des URL pré-signées (défaut/min/max, comportement à expiration). | Risque d'échec upload et faille d'exposition temporelle. |
| Q-03 | Règles exactes de validation MIME/UTType autorisés (liste blanche). | Ambiguïté sécurité et UX de rejet. |
| Q-04 | Politique de persistance des états UI après redémarrage app. | Incohérence possible expérience utilisateur. |
| Q-05 | Table/contrat backend exact pour optimized, ios_transcoded et champs probatoires. | Risque de divergence mobile/backend sur traçabilité légale. |
| Q-06 | Exigence formelle de nettoyage mémoire (niveau/langage/critère d'audit). | Difficilement testable sans protocole d'audit défini (cf. H-06). |
Références¶
- Epic : Référence épique (à compléter)
- JIRA : PD-101
- Repos concernés :
ProbatioVault-app,ProbatioVault-backend - Documents associés : PD-63 (URL pré-signées), PD-55 (worker ancrage), expression de besoin PD-101, learnings PD-264 / PD-251 / PD-283 / PD-262 / PD-282, contraintes constitutionnelles de gouvernance IA.