6.4 KiB
6.4 KiB
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 parroles+ 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 (storedata) viahandleApiReturn. - Les clés partagées (confirmées ou en attente) sont stockées dans
shared_secrets/unconfirmed_secretset injectées dans le wasm viaset_shared_secrets.
- Les données chiffrées sont référencées par un hash (
- 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
- L’app sépare
privateDataetpublicData. - Encodage des deux jeux de données via wasm.
create_new_process(wasm) produitupdated_processavec:current_process.states[*].pcd_commitment[attribute](hash → blob chiffré)current_process.states[*].keys[attribute]pour les membres autorisés
handleApiReturn:- Sauvegarde des blobs dans IndexedDB (
data), des diffs, et du process dansprocesses. - Alimente
shared_secrets/unconfirmed_secretsetset_shared_secrets→ wasm mémorise les secrets.
- Sauvegarde des blobs dans IndexedDB (
checkConnectionss’assure que les pairs nécessaires sont connectés (fonds « faucet » si besoin), puisrequest_datapeut être utilisé pour obtenir des clés/manquants.
Pseudo-code (TypeScript, côté ihm_client)
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.
- Préconditions:
- Appareil pairé (
is_paired()etservices.isPaired()→ true) - Au moins un handshake relay reçu (liste des membres non vide)
restoreSecretsFromDB()exécuté (injecte les secrets en wasm)
- Appareil pairé (
- Côté hôte, appeler l’iframe via
RETRIEVE_DATA { processId, stateId, accessToken }. - 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)
- Vérifie
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)puissdk.decode_value(clear)
- Si clé absente mais rôle autorisé →
- Réponse
DATA_RETRIEVEDavec les champs déchiffrés.
Pseudo-code déchiffrement (extrait)
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
- L’app calcule
privateData/publicDatadu delta. update_process(wasm) produitupdated_processet, si nécessaire, de nouvelles clés par attribut.handleApiReturnmet à jour blobs/diffs/process/secrets.- Les pairs reçoivent ciphers/keys via relay;
request_datasert à 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 →
handleApiReturnn’a pas été invoqué sur unupdated_processcontenantencrypted_data - Absence de clé → relancer
checkConnections+requestDataFromPeers, vérifier la connectivité relay/fonds
- Absence de blob →
Exemple de flux hôte ↔ iframe (postMessage)
// 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_DATAetdecryptAttribute, 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.