Résumé
- RFC 3218 considérait le texte de l’erreur, le comportement du service et son temps de réponse comme des sorties cryptographiques. Distinguer un bloc RSAES-PKCS1-v1_5 mal formé d’un bloc valide contenant une mauvaise clé suffisait à créer un oracle.
- Le « remplissage aléatoire » remplaçait un déballage invalide par une CEK neuve, de la longueur attendue, puis laissait continuer le déchiffrement et les contrôles d’intégrité. Cette clé n’était pas une solution de secours : son seul rôle était de déplacer l’échec vers un chemin ordinaire et indistinguable.
Le message n’avait pas besoin de révéler son texte clair. Il suffisait que le destinataire dise, par un signe minuscule, si le texte clair caché avait la bonne forme.
C’est la leçon que Daniel Bleichenbacher avait rendue concrète en 1998. Un attaquant capturait un chiffré RSA, le transformait mathématiquement, le présentait au détenteur de la clé privée et observait une réponse binaire : le bloc déchiffré respecte-t-il le format PKCS #1 v1.5 ? Chaque réponse réduisait l’intervalle des valeurs encore possibles. La clé privée ne quittait jamais la machine ; la succession de décisions de la machine accomplissait pourtant le travail de déchiffrement.
RFC 3218 appliqua ce problème à la Cryptographic Message Syntax. Publié en janvier 2002 avec le statut Informational, il ne proposait ni un nouveau contenu signé, ni une nouvelle preuve d’identité. Il devait protéger les installations qui transportaient déjà la clé symétrique du message avec RSA PKCS #1 v1.5 et ne pouvaient pas basculer instantanément vers un autre format.
Le bloc v1.5 possédait une grammaire stricte : les octets 00 02, au moins huit octets aléatoires non nuls, un séparateur 00, puis la valeur transportée. Dans CMS, cette valeur finale était la clé de chiffrement du contenu, ou CEK. L’opération RSA pouvait donc produire une suite d’octets sans produire un bloc acceptable.
Cette séparation devait rester visible à l’intérieur et invisible à l’extérieur. Avoir terminé l’exponentiation RSA ne prouvait pas la validité du format. Un format valide ne prouvait ni la longueur attendue de la CEK, ni sa justesse. Une CEK de bonne longueur pouvait tout de même être fausse. Le déchiffrement du contenu pouvait retourner des octets qui échoueraient au bourrage, au MAC, à la signature ou au parseur applicatif.
Le destinataire avait besoin de ces distinctions pour se gouverner. L’attaquant ne devait pas pouvoir les interroger.
La « Million Message Attack » décrite par RFC 3218 partait d’un chiffré capturé C. L’attaquant choisissait un multiplicateur S et envoyait C' = C * S^e mod n. Une transformation sur environ 2^16 produisait un texte clair commençant par 00 02. Dans le scénario CMS étudié, l’ensemble de la recherche pouvait demander de l’ordre de 2^20 messages et réponses.
Ce chiffre n’était pas un quota universel. Il montrait pourquoi l’automatisation changeait la menace. Un million de sollicitations adressées à un humain seraient voyantes et coûteuses. Un agent de liste de diffusion, une passerelle ou un service sans surveillance pouvait recevoir, déchiffrer et répondre mécaniquement. L’adversaire apportait les requêtes ; le destinataire apportait la répétition.
Après une requête, plusieurs vérités internes étaient possibles. Le bloc PKCS #1 pouvait être mal formé. Il pouvait être bien formé mais contenir une CEK inventée. Cette CEK pouvait produire un échec d’intégrité. Sans intégrité authentifiée, un texte clair aléatoire pouvait même présenter par hasard un bourrage CBC plausible. Retrouver la vraie CEK restait extraordinairement improbable.
L’attaque n’exigeait pas de reconnaître tous ces cas. Elle devait seulement séparer le premier des suivants. Un libellé d’erreur distinct suffisait. Un silence au lieu d’une réponse, une fermeture de connexion, un rebond de courriel, une alerte de signature ou une latence stable pouvaient fournir le même bit.
La contre-mesure principale de RFC 3218 fut baptisée « random filling ». Quand le décodage PKCS #1 échouait, le destinataire générait une CEK cryptographiquement aléatoire, de la longueur exigée par l’algorithme de contenu. Il poursuivait ensuite exactement comme si RSA avait livré cette valeur.
Le contenu était déchiffré avec la clé inventée. Les contrôles de bourrage, de MAC, de signature et d’application avaient encore lieu. La CEK aléatoire devait conduire, presque certainement, à un échec plus tardif. L’erreur observable et le temps nécessaire devaient ressembler à ceux d’un bloc correctement encodé qui transportait simplement la mauvaise clé.
La CEK de remplacement n’était donc pas une clé de récupération. Elle ne sauvait aucune donnée, ne reproduisait pas le secret de l’expéditeur et ne validait pas le message. Elle constituait un matériau jetable dont la fausseté permettait au destinataire de continuer sans révéler pourquoi il allait échouer.
Une clé de remplacement fixe aurait inversé ce raisonnement. Un développeur pouvait être tenté de choisir une constante pour rendre les tests reproductibles et éviter un appel au générateur aléatoire. Mais un adversaire connaissant cette constante pouvait chiffrer un contenu CMS avec elle, joindre une clé RSA volontairement mal formée et envoyer l’ensemble. Si la branche de secours était prise, le contenu devenait valide ; sinon il échouait. Le camouflage produisait alors un oracle encore plus net.
La valeur devait être neuve, imprévisible et de la bonne longueur à chaque usage. Sa réutilisation aurait donné une identité stable à une branche censée disparaître.
Même une bonne source aléatoire pouvait trahir la branche si elle n’était appelée qu’après une erreur et si l’appel prenait un temps mesurable. RFC 3218 proposait donc de générer un candidat aléatoire pour chaque message, puis de le jeter quand le déballage était valide. Le coût du hasard cessait ainsi d’indiquer quel chemin avait été suivi.
Cette stratégie ne garantissait pas un programme entièrement en temps constant. Le parseur, la mémoire, la journalisation, les files de tâches, la réponse réseau et les opérations applicatives pouvaient encore diverger. Elle rappelait simplement qu’un message d’erreur identique ne corrige pas à lui seul un comportement temporel différent.
La longueur de la CEK créait une seconde frontière. Une bibliothèque RSA générique pouvait savoir que le bloc était conforme sans savoir si l’application attendait 8, 16, 24 ou 32 octets. Si elle retournait les octets et si la couche CMS rejetait ensuite une longueur incorrecte par une exception distinctive, l’oracle avait seulement changé d’étage.
La couche qui connaissait l’algorithme de contenu devait elle aussi substituer une clé aléatoire. La défense traversait ainsi la frontière entre primitive cryptographique et protocole. Le composant le plus bas ne pouvait pas assurer seul une propriété qui dépendait d’un contexte qu’il ne possédait pas.
Des vérifications supplémentaires augmentaient le coût de l’attaque : contrôler tous les octets du bourrage, la longueur de la clé et, pour les familles DES, certains bits de parité. Elles réduisaient la probabilité qu’un bloc transformé passe les tests. Elles ne transformaient pas un oracle rare en absence d’oracle. Tant que la réponse exposait la classification, la recherche pouvait continuer.
Le cas du CBC non authentifié montrait aussi la faiblesse d’un « succès » de déchiffrement. Un texte clair aléatoire peut se terminer comme un bourrage. Si l’implémentation utilise seulement le dernier octet comme longueur à retirer, RFC 3218 évaluait la probabilité apparente à environ 1/32. Vérifier tous les octets requis la ramenait vers 1/255.
Ces valeurs ne donnaient aucun sens au contenu aléatoire. Elles montraient qu’une mémoire tampon correctement rembourrée n’était pas une preuve. Un MAC ou une signature créait un meilleur point de rejet, sans autoriser pour autant la divulgation séparée du verdict PKCS #1 initial.
OAEP indiquait une migration cryptographique plus propre. PKCS #1 v2.0 avait normalisé RSAES-OAEP, et RFC 3218 estimait que l’attaque décrite ne s’y appliquait pas. Mais OAEP n’était pas compatible sur le fil avec v1.5. Expéditeurs, destinataires, certificats, identifiants d’algorithme et logiciels CMS déployés devaient converger.
Nommer la meilleure primitive ne faisait donc pas disparaître l’ancienne. Le « random filling » était un dispositif de coexistence : limiter ce qu’une branche historique pouvait apprendre à l’extérieur pendant que l’écosystème migrait.
TLS avait rencontré une difficulté analogue avec ses secrets prémaîtres RSA. Ses spécifications demandaient au serveur de poursuivre avec une valeur aléatoire plutôt que de révéler une faute de format ou de version. La parenté était réelle, mais les traces ultérieures ne l’étaient pas : un traitement CMS, un rebond de courrier et une poignée de main TLS avaient des machines d’états différentes.
La règle générale se situait donc au-dessus de la primitive. Un parseur interne ne doit pas répondre à une question que le protocole externe ne peut révéler sans risque. Il revient à ce protocole de choisir le travail restant, l’erreur finale et les effets observables.
RFC 3218 ne démontrait ni l’authenticité du message, ni l’absence de toute fuite temporelle, ni la sécurité d’une implémentation donnée. Il ne recensait aucun déploiement. Il formulait une obligation plus étroite : quand une différence interne devient interrogeable en série, le chemin d’erreur fait partie du système cryptographique.
Briefing des membres
Contexte approfondi du profil
Connectez-vous avec le bon niveau d'adhésion pour débloquer le briefing complet et les notes de source.
Réservé à Strategic Circle
Strategic Circle
Ouvert à tous les lecteurs. Débloquez les briefings de profil après adhésion et connexion.
Rejoindre Strategic CircleRéservé aux membres de Leadership Alliance
Leadership Alliance
Réservé aux propriétaires et dirigeants qualifiés d'actifs IP ; connectez-vous pour débloquer les briefings Alliance.
Rejoindre Leadership Alliance
