Catégories
javascript navigateurs et API Sécurité informatique

Comment on fait de la crypto dans le navi­ga­teur ?

Faire de la cryp­to­gra­phie dans le navi­ga­teur se révèle bien plus simple que prévu.

Lais­sez tomber les portages de libso­dium & co. Quasi­ment tous les navi­ga­teurs supportent désor­mais une API native dédiée. Seul IE11 ne le fait pas tota­le­ment mais il a au moins le mini­mum qu’est la géné­ra­tion de nombres réel­le­ment aléa­toires. Ceux qui veulent vrai­ment pour­ront complé­ter l’im­plé­men­ta­tion d’IE11 avec un poly­fill sans risquer de problème de sécu­rité.

Il y a plein de jolis exemples sur qnimate mais certaines choses datent un peu. J’ai tenté de résu­mer ici pour ceux que ça inté­resse. Ici pour du déchif­fre­ment à partir d’une clef secrète.

Avant-propos

Je ne suis pas un expert en chif­fre­ment et ce genre de choses est toujours à manier avec précau­tion. Si vous faites quelque chose de sérieux, ne vous conten­tez pas d’exemples et embau­chez quelqu’un qui sait.

Rien que choi­sir l’al­go­rithme perti­nent demande de savoir ce qu’on fait. AES-CTR semble perti­nent pour mon cas d’usage où n’ai pas besoin de véri­fier l’au­then­ti­cité du message, où je n’ai pas envie de me colti­ner les ques­tions de padding, et où je serai heureux de profi­ter des multiples cœurs des smart­phones.

Si l’al­go­rithme choisi ou autre chose vous dérange et que vous en savez plus que moi, n’hé­si­tez pas à commen­ter.

Récu­pé­rer une clef

Le plus simple est de récu­pé­rer direc­te­ment une clef au format JSON Web Key (en gros la clef en base64url plus un peu de méta­don­né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éri­ver une clef

Si on veut partir d’une phrase secrète mémo­ri­sable et non d’une clef complète, on commence par créer une clef tempo­raire et on utilise un algo­rithme de déri­va­tion comme PBKDF2.

Malheu­reu­se­ment pour créer cette première clef il faut passer par un TextEn­co­der et ArrayBuf­fer. peut toujours déri­ver la clef à partir de là. TextEn­co­der n’existe pas sous Edge et IE11, il vous faudra utili­ser une fonc­tion comme uniba­bel 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écu­rité de tout ça dépend de la longueur et de l’uni­cité de votre phrase secrète initiale. À vous de trou­ver le bon compro­mis entre la sécu­rité et la puis­sance des smart­phones qui risquent d’uti­li­ser votre code. Le 25 ici est pure­ment arbi­traire et le temps de calcul néces­saire est propor­tion­nel.

Déchif­frer

Déchif­frer n’est pas plus diffi­cile.

Le vecteur d’ini­tia­li­sa­tion (iv) et la donnée chif­frée (encryp­ted) sont atten­dus sous forme d’Ar­rayBuf­fer. Il faudra de nouveau passer par uniba­bel ou une autre solu­tion si vous avez ça sous forme de chaîne binaire ou codé en base64.

Le résul­tat de decrypt() vous est retourné sous la même forme. S’il est petit le plus simple est d’uti­li­ser TextDe­co­der ou uniba­bel. Si vous avez quelque chose de plus volu­mi­neux vous pouvez aussi passer par un Blob et un FileRea­der.

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

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *