### Chiffrement des données de processus et déchiffrement (4NK) Ce document décrit comment les données privées d’un processus sont chiffrées, stockées et déchiffrées côté client dans l’écosystème 4NK, en s’alignant sur les composants existants (`ihm_client`, service worker/IndexedDB, relays, wasm `sdk_client`). ### Principes - **Séparation public/privé**: lors de la création/mise à jour d’un process, le client sépare explicitement les champs privés des champs publics. - **Encodage**: les objets JSON simples et binaires sont encodés via le wasm (`encode_json`, `encode_binary`). - **Chiffrement & Clés**: le wasm chiffre les champs privés et associe des clés par attribut dans `state.keys[attribute]` pour les membres autorisés (déterminés par `roles` + règles de validation). - **Stockage**: - Les données chiffrées sont référencées par un hash (`pcd_commitment[attribute]`) et stockées localement en IndexedDB (store `data`) via `handleApiReturn`. - Les clés partagées (confirmées ou en attente) sont stockées dans `shared_secrets` / `unconfirmed_secrets` et injectées dans le wasm via `set_shared_secrets`. - **Rôle du relay**: les pairs publient/échangent ciphers, diffs, clés via les connections relay. Au moins un « handshake » relay doit avoir eu lieu pour récupérer/échanger les clés. ### Séquence — Création d’un processus avec données privées 1) L’app sépare `privateData` et `publicData`. 2) Encodage des deux jeux de données via wasm. 3) `create_new_process` (wasm) produit `updated_process` avec: - `current_process.states[*].pcd_commitment[attribute]` (hash → blob chiffré) - `current_process.states[*].keys[attribute]` pour les membres autorisés 4) `handleApiReturn`: - Sauvegarde des blobs dans IndexedDB (`data`), des diffs, et du process dans `processes`. - Alimente `shared_secrets`/`unconfirmed_secrets` et `set_shared_secrets` → wasm mémorise les secrets. 5) `checkConnections` s’assure que les pairs nécessaires sont connectés (fonds « faucet » si besoin), puis `request_data` peut être utilisé pour obtenir des clés/manquants. Pseudo-code (TypeScript, côté `ihm_client`) ```ts const { jsonCompatibleData, binaryData } = splitData(privateData); const encodedPrivate = { ...sdk.encode_json(jsonCompatibleData), ...sdk.encode_binary(binaryData) }; const encodedPublic = encode(publicData); const res = sdk.create_new_process(encodedPrivate, roles, encodedPublic, relayAddress, feeRate, services.getAllMembers()); await services.handleApiReturn(res); // stocke blobs + diffs + process + secrets await services.checkConnections(res.updated_process.current_process); ``` ### Séquence — Récupération et déchiffrement des données privées Objectif: obtenir les champs déchiffrés d’un état particulier. 1) Préconditions: - Appareil pairé (`is_paired()` et `services.isPaired()` → true) - Au moins un handshake relay reçu (liste des membres non vide) - `restoreSecretsFromDB()` exécuté (injecte les secrets en wasm) 2) Côté hôte, appeler l’iframe via `RETRIEVE_DATA { processId, stateId, accessToken }`. 3) Routeur `ihm_client` → `handleDecryptState`: - Vérifie `validateToken(accessToken, origin)` - Charge le `process` → `services.getProcess(processId)` - Vérifie/établit les connexions (`checkConnections(process, stateId)`) pour récupérer clés manquantes - Pour chaque attribut privé: `decryptAttribute(processId, state, attribute)` 4) `decryptAttribute`: - Si clé absente mais rôle autorisé → `requestDataFromPeers(processId, [stateId], [roles])` puis retries - Lit le blob chiffré en IndexedDB via `pcd_commitment[attribute]` - Déchiffre avec `sdk.decrypt_data(key, cipher)` puis `sdk.decode_value(clear)` 5) Réponse `DATA_RETRIEVED` avec les champs déchiffrés. Pseudo-code déchiffrement (extrait) ```ts async function decryptAttribute(processId: string, state: ProcessState, attribute: string) { const hash = state.pcd_commitment[attribute]; let key = state.keys[attribute]; if (!key && rolesContainUs(state.roles, getPairingProcessId())) { await services.checkConnections(await services.getProcess(processId), state.state_id); await services.requestDataFromPeers(processId, [state.state_id], [state.roles]); await retryDelay(); key = state.keys[attribute]; } if (!hash || !key) return null; const blob = await services.getBlobFromDb(hash); if (!blob) return null; const cipher = new Uint8Array(await blob.arrayBuffer()); const clear = sdk.decrypt_data(hexToU8(key), cipher); return sdk.decode_value(clear); } ``` ### Séquence — Mise à jour avec nouveaux champs privés 1) L’app calcule `privateData`/`publicData` du delta. 2) `update_process` (wasm) produit `updated_process` et, si nécessaire, de nouvelles clés par attribut. 3) `handleApiReturn` met à jour blobs/diffs/process/secrets. 4) Les pairs reçoivent ciphers/keys via relay; `request_data` sert à combler les manquants. ### Bonnes pratiques et diagnostics - Toujours valider le token (`VALIDATE_TOKEN`) avant des opérations sensibles. - Attendre un **handshake relay** au démarrage avant de tenter d’obtenir des clés. - Vérifier que `rolesContainsUs(roles)` est vrai pour les attributs visés — sinon pas d’accès aux clés. - Confirmer qu’IndexedDB contient: - `data[hash]` (blob chiffré) - `shared_secrets[...]` utiles → `restoreSecretsFromDB()` doit tourner à l’init - En cas d’échec de déchiffrement: - Absence de blob → `handleApiReturn` n’a pas été invoqué sur un `updated_process` contenant `encrypted_data` - Absence de clé → relancer `checkConnections` + `requestDataFromPeers`, vérifier la connectivité relay/fonds ### Exemple de flux hôte ↔ iframe (postMessage) ```ts // 1) L’hôte liste les processus (publique) puis demande les privés d’un état postMessage({ type: 'GET_PROCESSES', messageId, accessToken }); // ... reçoit PROCESSES_RETRIEVED postMessage({ type: 'RETRIEVE_DATA', messageId, processId, stateId, accessToken }); // ... reçoit DATA_RETRIEVED { data: { monChampPrive: '...' } } ``` ### Résumé - Les données privées ne sont jamais renvoyées en clair par `GET_PROCESSES`. - Le déchiffrement s’effectue côté client via `RETRIEVE_DATA` et `decryptAttribute`, avec clés/ blobs provenant du relay + IndexedDB. - La disponibilité des clés dépend des rôles, du pairing, de la connectivité et des secrets chargés.