Aller au contenu

PD-101 — Expression de besoin

1. Contexte

L'application iOS ProbatioVault permet le dépôt de documents chiffrés côté client (modèle zero-knowledge). L'architecture impose que :

  • Le chiffrement soit réalisé localement sur le device (AES-256-GCM).
  • Le backend ne manipule que des blobs chiffrés.
  • L'upload soit compatible avec le stockage S3 (OVH Object Storage).
  • Le fichier soit ensuite scellé WORM et intégré dans la chaîne probatoire.

Actuellement, l'upload mobile ne dispose pas d'un suivi de progression utilisateur, d'un contrôle UX robuste (retry, annulation), ni d'une gestion fine des erreurs réseau.

PD-101 vise à industrialiser l'expérience mobile d'upload tout en garantissant l'intégrité probatoire du fichier déposé.

2. Objectif

Permettre à l'utilisateur iOS de :

  1. Sélectionner un document (image ou fichier)
  2. Le chiffrer localement (AES-256-GCM, zero-knowledge)
  3. L'uploader via multipart upload avec URL pré-signée
  4. Visualiser une barre de progression temps réel
  5. Gérer annulation / erreur / reprise

Le tout sans compromettre le modèle zero-knowledge et en garantissant le stockage bit-à-bit du fichier soumis.

3. Invariant fondamental — Intégrité probatoire

Par défaut, ProbatioVault stocke une représentation strictement identique du fichier soumis par l'utilisateur ; aucune transformation de contenu ne peut être appliquée avant scellement sans consentement explicite et traçable.

Cet invariant découle directement de la promesse produit :

  • La preuve porte sur le fichier tel que soumis.
  • La vérification d'intégrité repose sur l'égalité hash(document présenté) == hash(document scellé).
  • Le WORM garantit l'immutabilité post-dépôt ; toute transformation avant scellement change l'objet probatoire.
  • Pour les cas d'usage d'antériorité créative (photo, PI, brevet), le fichier original est l'objet juridique pertinent.

Conséquences directes :

  • Aucune compression automatique par défaut.
  • Aucune conversion HEIC → JPEG silencieuse.
  • Aucune suppression de métadonnées EXIF silencieuse.
  • Le fichier chiffré puis stocké est strictement celui fourni par l'utilisateur.

4. Périmètre fonctionnel

4.1 Inclus

  • Image picker (Photos framework) avec détection de transcodage iOS
  • File picker (UIDocumentPickerViewController)
  • Chiffrement local AES-256-GCM (clé K_doc dérivée via HKDF)
  • Multipart upload S3 (URL pré-signée) pour fichiers ≥ 10 MB
  • Upload simple pour fichiers < 10 MB
  • Barre de progression temps réel
  • Upload en background (URLSession background task)
  • Notification locale (succès/échec) si app hors premier plan
  • Gestion d'annulation orchestrée
  • Retry automatique avec backoff exponentiel
  • Mode "capture optimisée" avec consentement explicite (cf. §6)
  • États UI (Uploading / Failed / Completed / Cancelled)

4.2 Exclus

  • Traitement PRE (Legal PRE)
  • Ancrage blockchain immédiat
  • Génération de preuve composite
  • Synchronisation multi-device
  • Suppression sélective de métadonnées EXIF (scope futur)

5. Flux nominal — Dépôt probatoire fidèle (mode par défaut)

Étape 1 — Sélection

L'utilisateur choisit "Importer" puis sélectionne :

  • Via Photos picker : l'app récupère le fichier. Si iOS a transcodé le fichier (ex: HEIC → JPEG), l'app détecte le mismatch (PHAssetResource.originalFilename, UTType, metadata EXIF) et affiche un avertissement :

iOS peut fournir une version convertie de la photo (ex : JPEG au lieu du fichier original). Pour une preuve stricte du fichier original, utilisez « Importer un fichier ».

  • Via File picker : l'app récupère le fichier tel quel, sans transformation.

L'app :

  • Récupère les metadata locales (nom, taille, MIME, UTType)
  • Génère un doc_id (UUID v4)
  • Vérifie la taille ≤ 500 MB. Si dépassement : message clair + refus.

Étape 2 — Disclaimer métadonnées

Avant le dépôt, l'app affiche :

Ce fichier sera conservé strictement tel quel, y compris ses métadonnées (EXIF). Celles-ci peuvent contenir des informations comme la localisation GPS ou le modèle d'appareil.

[ Déposer tel quel ]

Étape 3 — Chiffrement local

  • Dérivation K_doc via HKDF conforme aux specs techniques
  • Génération K_share
  • Chiffrement AES-256-GCM avec nonce unique par document
  • Génération tag d'authentification
  • Production : document.enc + metadata neutres (hash SHA3-256 du fichier original avant chiffrement)

Aucun fichier clair persistant sur disque. Purge immédiate après chiffrement. purgeStale() au démarrage du flux pour couvrir les crashes précédents.

Étape 4 — Initialisation upload

  • Appel API backend → création enregistrement PENDING
  • Récupération URL(s) pré-signée(s)
  • Si taille ≥ 10 MB → multipart upload (chunks configurables, défaut 5 MB)
  • Si taille < 10 MB → upload simple

Étape 5 — Upload avec progression

Utilisation URLSessionUploadTask (background-capable).

Progression :

  • Barre de progression : 0–100% (uploadedBytes / totalBytes)
  • ETA estimée
  • Vitesse instantanée

États UI :

État Description
Uploading Upload en cours, progression visible
Failed Échec après épuisement des retries
Completed Upload finalisé avec succès
Cancelled Annulé par l'utilisateur

Background : si l'app passe en background, l'upload continue via URLSession background task. Notification locale au retour (succès ou échec). Si l'utilisateur revient dans l'app, l'état est affiché normalement sans doublon de notification.

Étape 6 — Finalisation

  • L'app confirme l'upload terminé au backend
  • Le backend passe le statut de PENDING à READY → PENDING_SEAL
  • Le worker d'ancrage (PD-55, DONE) prend en charge le scellement ultérieur

6. Mode "Capture optimisée" (compression explicite)

Ce mode est proposé pour les cas d'usage où la fidélité bit-à-bit n'est pas prioritaire : captures d'écran, envoi rapide en mobilité, faible réseau.

6.1 Activation

Le mode n'est jamais activé par défaut. L'utilisateur doit :

  1. Choisir explicitement l'option "Capture optimisée" avant le dépôt
  2. Voir un avertissement clair :

Vous déposez une version optimisée, non strictement identique à l'original. La preuve portera sur cette version compressée.

  1. Confirmer son choix

6.2 Transformations autorisées

  • Compression JPEG qualité configurable (défaut 0.8)
  • Conversion HEIC → JPEG si nécessaire

6.3 Traçabilité

  • Le flag optimized: true est enregistré dans les metadata du document
  • Le hash probatoire porte sur la version compressée (pas l'original)
  • L'enregistrement backend mentionne explicitement le mode utilisé
  • Ce choix est irréversible : une fois déposé en mode optimisé, on ne peut pas "upgrader" vers l'original

6.4 Ce qui ne change PAS

  • Le chiffrement reste identique (AES-256-GCM)
  • L'upload suit le même flux (multipart si ≥ 10 MB)
  • La chaîne probatoire reste valide (hash du fichier effectivement déposé)

7. Taille maximale

Règle Valeur
Taille maximale par fichier 500 MB
Upload simple < 10 MB
Multipart upload ≥ 10 MB
Taille chunk multipart 5 MB (configurable)

Justification : couvre RAW plein format (30-80 MB), RAW moyen format (80-150 MB), vidéos 4K courtes (50-300 MB).

8. Gestion des erreurs et retry

8.1 Stratégie de retry

Mode Stratégie
Multipart Retry x3 par chunk échoué
Upload simple Retry x3 sur l'upload complet
Backoff Exponentiel : 1s, 2s, 4s + jitter

8.2 Erreurs retryables vs non-retryables

Retryable Non retryable
Timeout réseau 4xx fonctionnels
Perte de connexion Fichier invalide
5xx serveur Erreur de chiffrement
Erreur transitoire S3 Annulation utilisateur

8.3 Cas d'erreur

Cas Comportement
Perte réseau Retry automatique x3 par chunk/upload
Timeout S3 Retry avec backoff
App en background Upload continue (URLSession background)
Annulation utilisateur Annulation orchestrée (cf. §9)
Fichier > 500 MB Message clair + refus
Échec chiffrement Abort immédiat + purge fichiers temporaires

9. Annulation

L'annulation est orchestrée, pas un simple abandon côté client :

  1. Arrêt de l'upload côté client
  2. AbortMultipartUpload si multipart initié (nettoyage parts S3)
  3. Appel API backend pour passer l'enregistrement en CANCELLED (pas de hard delete : trace pour audit et réconciliation)
  4. Purge asynchrone des artefacts temporaires (fichiers locaux chiffrés, metadata)

Un job de nettoyage côté backend traite les enregistrements CANCELLED et les parts S3 orphelines.

10. Contraintes techniques non négociables

Invariants cryptographiques

  • Le fichier DOIT être chiffré avant tout envoi réseau
  • Le backend ne doit jamais voir le contenu en clair
  • Le nonce AES doit être unique par document
  • La clé K_doc doit être dérivée via HKDF conforme aux specs techniques
  • Aucun log ne doit contenir de contenu déchiffré
  • Le hash SHA3-256 du fichier original DOIT être calculé avant chiffrement et vérifié fonctionnellement (pas seulement validé en format)

Invariants d'intégrité probatoire

  • Par défaut, le fichier stocké est strictement identique au fichier soumis
  • En mode "capture optimisée", le flag optimized: true est obligatoire dans les metadata
  • Le hash probatoire porte toujours sur le fichier effectivement stocké
  • Aucune transformation silencieuse n'est autorisée

Contraintes techniques

  • URL pré-signées backend (PD-63, DONE)
  • Upload multipart obligatoire pour fichiers ≥ 10 MB
  • Upload chunké configurable (défaut : 5 MB)
  • Gestion mémoire maîtrisée : streaming encryption, pas de chargement complet en RAM > 100 MB
  • Fichiers temporaires sensibles : purgeStale() au démarrage du flux (crash bypass finally)
  • Background upload via URLSession background task

11. UX attendue

  • Feedback visuel immédiat
  • Progression fluide (pas de blocage UI)
  • Indication claire du statut
  • Toast succès / message explicite en cas d'échec
  • Aucun jargon technique visible
  • Avertissement transcodage iOS si détecté (Photos picker)
  • Disclaimer métadonnées EXIF avant dépôt
  • Choix explicite et visible si mode "capture optimisée"

12. Sécurité & conformité

  • Pas de sauvegarde en clair dans Documents/
  • Fichier temporaire supprimé immédiatement après chiffrement
  • Mémoire nettoyée après usage
  • Logs anonymisés
  • Respect RGPD by design (disclaimer EXIF)
  • Conforme à l'architecture zero-knowledge

13. Critères d'acceptation (Definition of Done)

Fonctionnel

  • Image importable via Photos picker
  • Fichier importable via File picker
  • Détection transcodage iOS avec avertissement
  • Progress bar fonctionnelle (%, ETA, vitesse)
  • Multipart upload opérationnel (≥ 10 MB)
  • Upload simple opérationnel (< 10 MB)
  • Retry automatique validé (par chunk en multipart, global sinon)
  • Annulation orchestrée (client + S3 + backend CANCELLED)
  • Background upload fonctionnel + notification locale
  • Mode "capture optimisée" avec consentement explicite
  • Refus fichier > 500 MB avec message clair

Sécurité

  • Aucun upload en clair possible
  • Nonce unique validé par document
  • Aucun log sensible
  • Hash SHA3-256 calculé avant chiffrement et vérifié fonctionnellement
  • purgeStale() au démarrage du flux
  • Aucune transformation silencieuse du fichier

Performance

  • Upload 100 MB < 30s en WiFi
  • Pas de crash mémoire (streaming encryption)
  • UI fluide (pas de blocage main thread)

14. Dépendances

Story Statut Rôle
PD-63 DONE Endpoint download + URL pré-signées
PD-55 DONE Worker ancrage blockchain

15. Learnings injectés

  • PD-264 : Clarifier dès la spec la distinction transaction synchrone vs post-commit asynchrone
  • PD-251 : Stubs inter-PD documentés avec story destination obligatoire
  • PD-283/PD-262 : purgeStale() au démarrage du flux, pas seulement finally (crash bypass)
  • PD-283/PD-282 : Validation format ≠ validation fonctionnelle — hash vérifié fonctionnellement, pas juste en format