Guide : bibliothèque cliente
La bibliothèque de référence (src/lib/ du dépôt 014-Drive-Worker-WEBCLIENT, celle de l'application web) implémente toute la cryptographie de la
spécification et l'accès à l'API. Le serveur ne reçoit jamais le mot de passe, les clés privées, les contenus ni les
noms en clair : tout est chiffré et déchiffré dans ce code, côté client.
Installation
npm install libsodium-wrappers-sumo
# puis copier src/lib du dépôt 014-Drive-Worker-WEBCLIENT dans le projet
La bibliothèque fonctionne dans le navigateur et dans Node.js 20 ou plus (WebCrypto et fetch natifs).
Choisir un serveur
import { Transport } from "./lib";
// Le compte maître renvoie les serveurs disponibles, du moins chargé au plus chargé.
const t = await Transport.discover("https://drive-master.master-014drive.workers.dev");
Le transport bascule seul sur le serveur suivant en cas de panne ou si l'en-tête X-Server-Usage dépasse 80 %, et
renouvelle la session (15 minutes) par un challenge signé, sans redemander le mot de passe.
Compte
import { Drive } from "./lib";
const drive = await Drive.register(t, "alice", "une longue phrase de passe");
// plus tard :
const drive = await Drive.login(t, "alice", "une longue phrase de passe");
Le mot de passe dérive (Argon2id, 64 Mio) une clé qui déchiffre localement le bundle de clés. Un mauvais mot de passe et un compte inexistant sont indiscernables. Imposez au moins 12 caractères et une estimation d'entropie (zxcvbn) : le bundle chiffré est accessible avant authentification.
await drive.changePassword("nouvelle phrase"); // ne re-chiffre aucun fichier
const code = await drive.createRecoveryCode(); // 16 octets, à montrer une seule fois
await Drive.loginWithRecovery(t, "alice", code);
Dossiers et fichiers
const photos = await drive.createFolder(drive.rootFolderId, "Photos");
const id = await drive.upload(photos, "vacances.jpg", fileOrBytes, "image/jpeg");
const listing = await drive.list(photos); // noms déchiffrés
const { meta, data, author } = await drive.download(id);
await drive.renameFile(id, photos, "plage.jpg");
await drive.moveFile(id, photos, drive.rootFolderId, "plage.jpg");
await drive.deleteFile(id);
upload accepte un Uint8Array ou un Blob (lu morceau par morceau). Chaque morceau de 19 Mio est chiffré en
XChaCha20-Poly1305 avec une clé propre au fichier, liée à sa position : un morceau déplacé ou remplacé est détecté au
déchiffrement. L'index du fichier est signé ; download vérifie cette signature.
Deux éléments d'un même dossier ne peuvent pas avoir le même nom (casse et accents normalisés) : le serveur le détecte grâce à une empreinte, sans connaître les noms.
Partager avec un utilisateur
const { fingerprint, boxPk } = await drive.userKeys("bob");
// Comparez l'empreinte avec Bob (de vive voix, par un autre canal) avant le premier partage.
await drive.shareWithUser("folder", photos, "Photos", "bob", { boxPk, permissions: "read" });
Côté destinataire :
const shares = await bob.incomingShares(); // clés déchiffrées avec sa clé privée
const listing = await bob.list(shares[0].resourceId);
La révocation (revokeShare) coupe l'accès immédiatement, mais n'efface pas ce que le destinataire a déjà téléchargé.
Liens publics
const { shareId, secret } = await drive.createLink("file", id, "plage.jpg", { maxDownloads: 5, password: "facultatif" });
const url = `https://app-014drive.pages.dev/s/${shareId}#${secret}`;
Le secret est dans le fragment de l'URL (après #) : le navigateur ne l'envoie jamais au serveur. Pour ouvrir le lien :
import { LinkSession } from "./lib";
const s = await LinkSession.open(t, shareId, secret, motDePasseSiBesoin);
const { meta, data } = await s.download();
Passkeys (navigateur)
await drive.addPasskey("Téléphone"); // WebAuthn + extension PRF
const drive = await Drive.loginWithPasskey(t); // sans nom d'utilisateur
await drive.setPasswordLogin(false); // « passkey uniquement » (2 passkeys ou un code de récupération)
La sortie PRF de l'authentificateur déchiffre une copie du bundle propre à chaque passkey. Un authentificateur sans PRF est refusé à l'enregistrement : il ne pourrait pas déverrouiller les clés.
Gros fichiers
downloadTo(fileId, prepare) remet les morceaux déchiffrés un par un : l'application web les écrit directement sur le
disque (API File System Access), sans garder le fichier entier en mémoire. upload accepte un Blob lu morceau par
morceau et signale la progression.
Erreurs
Les erreurs de l'API sont des ApiError (status, code, message, retryAfter). Les codes sont listés dans la
référence de l'API. Les erreurs 503 STORAGE_BUSY (Telegram saturé) sont réessayées automatiquement pendant l'upload
et le téléchargement.
Compatibilité
Un client écrit dans un autre langage doit reproduire exactement les formats : utilisez les vecteurs de test, qui donnent les sorties attendues pour chaque dérivation et chaque format à partir d'entrées fixes.