Faire de la cryptographie dans le navigateur se révèle bien plus simple que prévu.
Laissez tomber les portages de libsodium & co. Quasiment tous les navigateurs supportent désormais une API native dédiée. Seul IE11 ne le fait pas totalement mais il a au moins le minimum qu’est la génération de nombres réellement aléatoires. Ceux qui veulent vraiment pourront compléter l’implémentation d’IE11 avec un polyfill sans risquer de problème de sécurité.
Il y a plein de jolis exemples sur qnimate mais certaines choses datent un peu. J’ai tenté de résumer ici pour ceux que ça intéresse. Ici pour du déchiffrement à partir d’une clef secrète.
Avant-propos
Je ne suis pas un expert en chiffrement et ce genre de choses est toujours à manier avec précaution. Si vous faites quelque chose de sérieux, ne vous contentez pas d’exemples et embauchez quelqu’un qui sait.
Rien que choisir l’algorithme pertinent demande de savoir ce qu’on fait. AES-CTR semble pertinent pour mon cas d’usage où n’ai pas besoin de vérifier l’authenticité du message, où je n’ai pas envie de me coltiner les questions de padding, et où je serai heureux de profiter des multiples cœurs des smartphones.
Si l’algorithme choisi ou autre chose vous dérange et que vous en savez plus que moi, n’hésitez pas à commenter.
Récupérer une clef
Le plus simple est de récupérer directement une clef au format JSON Web Key (en gros la clef en base64url plus un peu de métadonnées). Dans ce cas il suffit de passer par importKey :
const crypto = window.crypto.subtle; const jwk = { "kty": "oct", "use": "enc", "k": "SDRSTgdOpUGfgn3iAwiC1cpzsevLM98r_6ehMXlK1Gk", "alg": "A256CTR" }; const algo = {name: "AES-CTR"}; const exportable = false; const usage = ["decrypt"]; const key = await crypto.importKey("jwk", jwk, algo, exportable, usage);
Dériver une clef
Si on veut partir d’une phrase secrète mémorisable et non d’une clef complète, on commence par créer une clef temporaire et on utilise un algorithme de dérivation comme PBKDF2.
Malheureusement pour créer cette première clef il faut passer par un TextEncoder et ArrayBuffer. peut toujours dériver la clef à partir de là. TextEncoder n’existe pas sous Edge et IE11, il vous faudra utiliser une fonction comme unibabel qui fait ça pour vous.
const crypto = window.crypto.subtle;
const encoder = TextEncoder();
const passphrase = "l'eau ça mouille, le feu ça brûle";
const buffer = encoder.encode( passphrase );
const d_algo = {name: 'PBKDF2'};
const d_exportable = false;
const d_usage = ['deriveBits', 'deriveKey'];
const d_key = await crypto.importKey('raw', buffer, d_algo, d_exportable, d_usage);
const deriv = {
name: 'PBKDF2',
salt: encoder.encode( "c'est le week-end !" ),
iterations = 25,
hash: 'SHA-256',
};
const algo = {name: "AES-CTR", length: 256};
const exportable = false;
const usage = ["decrypt"];
const key = await crypto.deriveKey(deriv, d_key, algo, exportable, usage);
La sécurité de tout ça dépend de la longueur et de l’unicité de votre phrase secrète initiale. À vous de trouver le bon compromis entre la sécurité et la puissance des smartphones qui risquent d’utiliser votre code. Le 25 ici est purement arbitraire et le temps de calcul nécessaire est proportionnel.
Déchiffrer
Déchiffrer n’est pas plus difficile.
Le vecteur d’initialisation (iv) et la donnée chiffrée (encrypted) sont attendus sous forme d’ArrayBuffer. Il faudra de nouveau passer par unibabel ou une autre solution si vous avez ça sous forme de chaîne binaire ou codé en base64.
Le résultat de decrypt() vous est retourné sous la même forme. S’il est petit le plus simple est d’utiliser TextDecoder ou unibabel. Si vous avez quelque chose de plus volumineux vous pouvez aussi passer par un Blob et un FileReader.
const crypto = window.crypto.subtle; const decoder = TextDecoder const key = ... const iv = ... const encrypted = ... const algo = { name: 'AES-CTR', iv: iv } const buffer = await crypto.decrypt(alg, key, encryted);
Laisser un commentaire