docs(process): spec chiffrement/déchiffrement + séquences et pseudo-code

This commit is contained in:
LeCoffre Deployment 2025-10-05 12:22:38 +00:00
parent 8ea50fed9a
commit f63bf086cf

View File

@ -0,0 +1,109 @@
### Chiffrement des données de processus et déchiffrement (4NK)
Ce document décrit comment les données privées dun processus sont chiffrées, stockées et déchiffrées côté client dans lécosystème 4NK, en salignant 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 dun 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 dun processus avec données privées
1) Lapp 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` sassure 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 dun é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 liframe 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) Lapp 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 dobtenir des clés.
- Vérifier que `rolesContainsUs(roles)` est vrai pour les attributs visés — sinon pas daccès aux clés.
- Confirmer quIndexedDB contient:
- `data[hash]` (blob chiffré)
- `shared_secrets[...]` utiles → `restoreSecretsFromDB()` doit tourner à linit
- En cas déchec de déchiffrement:
- Absence de blob → `handleApiReturn` na 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) Lhôte liste les processus (publique) puis demande les privés dun é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 seffectue 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.