Aller au contenu

Temps constant : Programmation en temps constant

Cryptographie

Manière d'écrire un code qui manipule des secrets pour que sa durée d'exécution, ses branchements et ses accès mémoire ne dépendent jamais de la valeur de ces secrets. Objectif : qu'un chronomètre, même très précis, n'apprenne rien.

Schéma de séquence entre trois acteurs : Attaquant, Serveur et Comparaison. Étape 1 : l'attaquant envoie au serveur un jeton candidat dont il fait varier le premier octet. Étape 2 : le serveur confie la vérification, octet par octet, à la comparaison. Étape 3, deux cas : si le premier octet est faux, la comparaison s'arrête tout de suite ; s'il est juste, elle fait un tour de boucle de plus. Étape 4 : le serveur répond à l'attaquant, un peu plus lentement si l'octet est juste. Étape 5 : une fois l'octet trouvé, l'attaquant refait la même chose sur l'octet suivant.

Ce qui trahit : Une comparaison qui s'arrête au premier octet faux, un `if` sur un bit de clé, une table indexée par une valeur secrète : chacun laisse une trace mesurable en durée ou dans le cache. À distance, sur des milliers de requêtes, la statistique fait le reste.

Les techniques : On remplace les branches par des masques binaires et des sélections arithmétiques, on parcourt toujours le tampon entier, on évite les tables indexées par un secret. Les bibliothèques fournissent des primitives dédiées (`CRYPTO_memcmp`, `subtle.ConstantTimeCompare`) qu'il vaut mieux utiliser que réécrire.

Le piège du compilateur : L'optimiseur peut réintroduire une sortie anticipée, et certaines instructions (division, multiplication sur quelques processeurs) ont une latence qui varie selon les opérandes. Le compilateur n'a jamais lu le modèle de menace : il voit une boucle qu'on peut abréger, et il l'abrège avec zèle.

À ne pas confondre : Rien à voir avec le O(1) de l'algorithmique, qui décrit la croissance du coût avec la taille de l'entrée. Ici, on exige la même durée pour toutes les valeurs secrètes, à taille égale.

Ce qui arrive si on s'en passe

Sans code en temps constant, la durée d'une opération devient un canal de fuite. Une comparaison de MAC ou de jeton qui s'arrête au premier octet différent révèle combien d'octets sont corrects : en mesurant les temps de réponse, un attaquant reconstitue la valeur attendue octet par octet au lieu de devoir tout deviner d'un coup. Un branchement qui dépend des bits d'une clé (une exponentiation modulaire naïve, par exemple) trahit ces bits par sa durée. Un accès à une table indexé par un secret laisse aussi une empreinte dans le cache, observable par un autre processus qui partage le même processeur. Le bruit du réseau ne protège pas : en répétant les mesures, les statistiques le filtrent. Ces attaques n'emploient que des requêtes ordinaires, si bien que les journaux et les détecteurs n'y voient rien d'anormal.

Explication de la marmotte

Imagine un gardien qui vérifie un mot de passe lettre par lettre et te renvoie dès la première erreur. Si tu chronomètres sa réponse, tu remarques qu'il met un peu plus longtemps quand ta première lettre est bonne. Tu peux alors deviner le mot de passe lettre après lettre, sans jamais le voir. Travailler « en temps constant », c'est obliger le gardien à toujours tout relire, du début à la fin, et à mettre exactement le même temps, que tu aies juste ou faux. Le chronomètre ne t'apprend plus rien.

Où ce terme est utilisé

← Tout le glossaire