Couches 5 & 6 : Cryptographie et TLS.
Cryptographie : motivations & besoins
Nous pouvons désormais envoyer un message de l'expéditeur au service destinataire. Cependant, les donnés échangées peuvent être sensibles, et ainsi nécessiter une protection. Imaginez par exemple qu'un intermédiaire malhonnête soit entre vous et un service bancaire. Il pourrait alors :
- voir vos identifiants bancaires, et ainsi usurper votre identité ;
- modifier vos opérations bancaires, e.g. modifier le montant et destinataire d'un virement ;
- créer de fausses opérations bancaires à votre nom.
Nous souhaitons donc que nos canaux de communications garantissent les propriétés de sécurité suivantes :
- Confidentialité : seul le destinataire (et l'expéditeur) du message peut en lire le contenu.
- Intégrité : toute modification du message par un tiers est détecté (le message est alors rejeté).
- Authenticité : le message provient bien de l'expéditeur.
💡 L'intégrité est souvent présentée comme l'impossibilité de modifier le message. Ce n'est pas exact dans le sens où on ne peut pas empêcher l'altération des bits qui circulent.
Pour cela on utilise le protocole cryptographique TLS (couches 5 & 6) afin de sécuriser les canaux de communications. On distingue :
- cryptographie (-graphie = écriture) : ensemble de méthodes pour protéger l'informations.
- cryptanalyse (-analyse) : analyse d'un système cryptographique pour en trouver les faiblesses.
- cryptologie (-logos = le discours) : englobe les 2 précédents.
Les protocoles cryptographiques sont construits autours de briques cryptographiques permettant de garantir différentes propriétés répondant à des besoins spécifiques. Ces briques sont usuellement basées sur des problèmes mathématiques :
- "facile dans un sens, difficile dans l'autre" : e.g. est facile, f-1 (i.e. trouver les diviseurs) est difficile.
- "difficile sans la connaissance d'un secret" : e.g. sans la connaissance du secret .
Les protocoles cryptographiques apportent des preuves de sécurité face à différents modèles d'attaquants. Un attaquant est une personne qui va tenter de compromettre la sécurité du système et pour lequel on va faire différentes hypothèses quant à ses capacités. On peut alors établir des preuves formelles de sécurité pour le modèle d'attaque étudié. Une brique cryptographique peut ainsi tomber si le problème mathématique à sa base devient facile.
On distingue généralement 2 niveaux de sécurité d'un protocole (ou d'une brique) :
- Sécurité inconditionnelle : même avec un attaquant possédant des ressources infinies, le système est sûr.
- Sécurité calculatoire : casser le système nécessite nécessite des ressources qu'un attaquant ne peut posséder.
La sécurité ad hoc désigne un système dont la sécurité dépend d'une série de raisonnements sans preuve réelle de sécurité. Il est donc difficile d'avoir confiance en la sécurité d'un tel système. Le raisonnement peut être juste, mais peut négliger des hypothèses ou des cas d'attaques possibles. Par exemple, le cas d'un attaquant envoyant des messages ne respectant pas le protocole.
La sécurité par l'obscurité désigne un système dont la sécurité dépend du secret de son fonctionnement. Un tel système est considéré comme étant très peu sûr : le fonctionnement du système peut fuiter, être retrouvé par reverse engineering, etc.
Les systèmes de sécurité peuvent dépendre d'un secret, appelé clef. En cas de clef compromise, il suffit alors de changer la clef. Le système cryptographique en lui-même n'est alors pas compromis. Idéalement, on vise aussi le backward secrecy et le forward secrecy, i.e. la compromission du système à un instant t ne compromet ni les échanges précédents, ni les échanges futurs.
💡 Les briques cryptographiques peuvent aussi être utilisées pour divers usages, e.g. :
- Stockage d'informations
- Authentifications
- Votes numériques
- Preuves d'antériorités
Hachage
Une fonction de hashage est une fonction transformant des données en une empreinte de taille inférieure et fixe. Permettant ensuite plusieurs usages, e.g. :
- stocker le hash d'un fichier, pour ensuite vérifier si ce dernier a été modifié (économie d'espace disque).
- comparer rapidement deux fichiers (sous l'hypothèse que les hashs ont déjà été calculés).
- structures de données dont la recherche est à temps constant (e.g. HashSet, HashMap).
Lorsque deux données différentes produisent le même hash, on parle alors de collision. L'objectif est bien évidemment d'éviter les collisions. Par exemple, le SHA-512 est un hash sur 512 bits (64 octets), avec une probabilité de collision de 2-512, ce qui est considéré improbable.
De même, on souhaite prendre en entrée des données de tailles arbitraires. Pour cela on va utiliser des fonctions éponges. Pour faire simple, elle va absorber des données puis les essorer afin qu'ils rentrent dans son état interne de taille fixe. On peut alors extraire des données de taille fixe en sortie.
Les hash cryptographiques sont des hash résistants à diverses attaques et grantissant certaines propriétés :
- non-inversible : difficile de retrouver les données originelles à partir du hash.
- résistances aux collisions : difficile de trouver une collision.
- difficile de trouver une collision d'une donnée connue.
La non-inversibilité implique que la sortie ne doit pas permettre de déduire d'informations sur l'entrée, i.e. une absence de corrélations. Notamment, des données similaires doivent produire des hashs dissimilaires. En effet, dans le cas contraire, la fonction de hachage serait vulnérable au hill climbing attack : on part d'une donnée qu'on modifie progressivement pour que son hash se rapproche du hash qu'on souhaite inverser. En effet, dans ce cas, des hashs similaires correspondant à des données similaires, la modification effectuée nous rapproche donc des données originelles.
Mots de passes hashés
Les hashs cryptographiques sont notamment utilisés pour stocker des mots de passes, sans qu'ils soient compromis par une fuite de la base de donnée. Cependant, les mots de passes sont généralement courts, pas vraiment aléatoires, réutilisés entre plusieurs comptes, etc. rendant l'inversion de leur hash plus facile. Le craquage de mot de passe consiste alors souvent à tester des mots de passes possibles (appelés candidats), et d'en comparer le hash.
Une première attaque est donc une attaque par force brute, i.e. de tester toutes les possibilités (avec potentiellement quelques heuristiques issues de l'étude du comportement humain). Par exemple, si le mot de passe est une date de naissance, on aura ~36 500 possibilités à tester. Sachant qu'un ASIC spécialisé dans le minage de cryptomonnaie est capable de tester plusieurs trilliards de hash par seconde (1012/s). La sécurité d'un mot de passe dépend donc du temps nécessaire à le craquer. Par exemple un mot de passe de 12 caractères alphanumériques, représente 8x1066 possibilités, sera donc cassé en ~6x1048 années.
Les mots de passes étaient usuellement courts, on va aussi tenter de réduire le nombre de hashs calculable à la seconde, en augmentant les ressources nécessaires à son calcul (e.g. temps, mémoire). Pour cela on utilise des algorithmes de hachage dédié au stockage de mot de passe, comme bcrypt qui intègre une notion de tours (round), i.e. au lieu de ne hasher le mot de passe qu'une seule fois, on le hash plusieurs fois afin d'en augmenter le coût.
Un attaquant ne cherche aussi généralement pas à attaquer un mot de passe, mais un ensemble de mot de passes. Si on peut comparer les mots de passes entre eux à l'aide de leur hash, le système a alors 2 vulnérabilités :
- attaque par fréquence : les hashs les plus fréquents correspondent aux mots de passes les plus fréquents.
- attaque par table arc-en-ciel : les mots de passes les plus fréquents sont pré-hashés, et comparés à la base.
Pour s'en protéger, avant de hasher le mot de passe, on peut lui ajouter un sel, i.e. un aléa connu et unique à chaque entrée. Ainsi les hash ne sont plus comparables entre eux, et un attaquant doit calculer des hashs pour chaque entrées.
Il est cependant toujours possible d'effectuer une attaque par dictionnaire, i.e. de tester des mots de passes déjà connus, venant par exemple de bases déjà craquées. Pour s'en protéger, on peut aussi ajouter un poivre au mot de passe avant de le hasher. Contrairement au sel, qui est stocké avec hash, le poivre est un secret partagé avec toutes les entrées, mais qui n'est pas stocké dans la base de données.
Autres usages
Les MAC
Les MAC (à ne pas confondre avec les adresses MAC), sont une sorte de hash avec clef secrète. Comme on ne peut calculer le MAC qu'en ayant connaissance de la clef secrètes, ils sont ainsi utlilisés dans les protocoles réseaux pour garantir non seulement l'intégrité, mais aussi l'authenticité des messages.
💡 Les HMAC sont est un MAC basé sur une fonction de hachage, quand CBC-MAC est basé sur une fonction de chiffrement. Pour faire simple HMAC revient à concaténer la clef avec la donnée, avant de hasher le tout.
Les générateurs
Une fonction de hachage peut être utilisée comme d'un générateur de nombre pseudo-aléatoire. Cela est notamment utilisé dans la dérivation de clefs, i.e. générer des secrets à partir d'un secret maître.
Preuve de connaissances
Un hash peut être utilisé dans le cadre de protocoles de preuve de connaissances (e.g. authentifications). En effet, être capable de calculer un hash est la preuve qu'on a soit connaissance du hash, soit de la donnée à l'origine du hash.
Chiffrement symétrique
Le chiffrement symétrique consiste à transformer les données à l'aide d'un secret, de sorte à ce que l'inversion ne soit possible qu'à l'aide de ce même secret. On utilise alors le vocabulaire suivant :
- clair : les données avant chiffrement ;
- chiffré : les données après chiffrement ;
- chiffrer : transformer le clair en chiffré.
- déchiffrer : transformer le chiffré en clair.
- décrypter : déchiffrer sans avoir connaissance du secret (attaque).
Les algorithmes de chiffrements sont usuellement basés sur un chiffrement de Vernam (aussi appelé one-time pad), consistant à effectuer un xor des données avec la clef secrète. Sans la connaissance du secret, il est alors mathématiquement impossible de retrouver les données originelles à partir du chiffré. Même avec une puissance de calcul infini, le chiffré ne dévoile aucune information sur les données originelles. On parle alors de chiffrement parfait.
Par exemple un chiffré de 2 bits peut correspondre à :
- si la clef est
- si la clef est
- si la clef est
- si la clef est
Cependant ce chiffrement a deux limitations :
- la clef doit être de la taille des données échangées.
- la clef doit pouvoir être échangée de manière sécurisée entre l'expéditeur et le destinataire.
Deux stratégies sont alors employées :
- Chiffrement par bloc (e.g. AES): chiffre des blocs de tailles fixes.
- Chiffrement par flot (e.g. ChaCha20): on génère une clef de taille arbitraire grâce à un générateur pseudo-aléatoire.
Utilisé seul, le chiffrement par bloc est grandement vulnérable à diverses attaques. Par exemple, si un même bloc de donnée produit le même bloc chiffré, des attaques par analyse de fréquence, par rejeux, etc. deviennent alors possibles. Les chiffrements par blocs sont ainsi généralement utilisé avec un mode opératoire (e.g. AES-XTS) permettant de pallier à ces vulnérabilités. Certains modes opératoires transforment un chiffrement par blocs en chiffrement par flot (e.g. AES-CTR).
Le chiffrement par flot a lui aussi ses vulnérabilités, liées au générateur pseudo-aléatoire :
- périodicité : le résultat fini par se répéter, par boucler (implique une taille de clef très grande, mais pas infinie).
- biais : le résultat n'est pas totalement aléatoire.
On distingue alors généralement 4 types de modèle d'attaquant :
- chiffré seul : l'attaquant connaît seulement des chiffrés.
- clair connu : l'attaquant connaît des messages clairs et leurs chiffrés.
- clair choisi : l'attaquant peut chiffrer des messages.
- chiffré choisi : l'attaquant peut déchiffrer des messages.
💡 Une clef symétrique peut être générée à partir du hash d'un mot de passe. C'est par exemple ce qui est utilisé pour les disques/fichiers chiffrés par mot de passe (e.g. mot de passe maître d'un gestionnaire de mot de passe). Pour permettre le changement du mot de passe sans avoir à re-chiffrer tout le disque/fichier, le mot de passe sert usuellement à déchiffrer la clef symétrique (stockée chiffrée) permettant à son tour de (dé)chiffrer le disque/fichier. Ainsi le changement du mot de passe nécessite juste de re-chiffrer la clef.
Cryptographie asymétrique.
Contrairement à la cryptographie symétrique où un unique secret est partagé entre l'expéditeur et le destinataire, la cryptographie asymétrique possède 2 clefs :
- une clef publique, connue de tous.
- une clef privée, secrète.
On distingue alors le chiffrement de la signature :
- chiffrement (e.g. RSA) : la clef publique chiffre les données, la clef privée déchiffre = tout le monde peut chiffrer.
- signature (e.g. DSA) : la clef privée "chiffre" les données, la clef publique "déchiffre" = tout le monde peut déchiffrer.
💡 Les fonctions avec trappe sont notamment utilisée afin de permettre aux autorités compétentes de lever la confidentialité e.g. de données stockées. Que ce soit pour des raisons d'enquêtes/investigations, d'espionage, ou pour déchiffrer des données lorsque leur propriétaire ne peut le faire (e.g. perte du mot de passe, comas -dans les deux cas, c'est une perte de connaissance 🤡-).
La cryptographie asymétrique est relativement coûteuse. Ainsi la signature d'un fichier sera généralement une signature de son hash. De même, le chiffrement d'un fichier sera généralement le chiffrement d'une clef symétrique permettant de le chiffrer/déchiffrer.
Par exemple, le handshake TLS utilise la cryptographie asymétrique afin de garantir le destinataire (et/ou l'expéditeur) et d'échanger une clef qui servira au MAC ainsi qu'au chiffrement symétrique. C'est aussi à ce moment là que le client et le serveur négocient des paramètres de sécurités.
💡 Une attaque consiste alors à tenter de dégrader les paramètres de sécurité afin de faciliter les attaques.
L'échange de clef est aussi de la cryptographie asymétrique. Une manière de le faire est de simplement chiffrer une clef symétrique et de la transmettre au serveur. En pratique, on utilise un algorithme d'échange de clef : Diffie-Hellman (DH), le secret est en pratique calculé à partir d'un nombre aléatoire choisi par le client, et un nombre aléatoire choisi par le serveur. DH se base sur le problème du logarithme discret ( gx mod p, g et p connus, difficile de retrouver x ):
- 2 nombres publiques: p (nombre premier) et g ("générateur").
- Le client et le serveur choisissent un secret, respectivement a et b.
- Le client envoie ga mod p, le serveur envoie gb mod p (difficile à inverser).
- Le client calcule (gb)a mod p, le serveur calcule (ga)b mod p.
- Ils partagent alors le même secret : gab mod p
RSA est basé sur le problème de la factorisation d'entiers. Cependant, les ordinateurs quantiques rendent ce problème plus difficile. Heureusement, il faut intriquer ~2N qbits pour casser une clef RSA de N bits (algorithme de Shor). Les ordinateurs quantiques ayant du mal à intriquer de nombreux qbits (record à ~50qbit actuellement ?), il suffit pour le moment d'augmenter les tailles des clefs RSA.
Pour DSA et DH, on utilise actuellement le problème des logarithmes discrets sur des courbes elliptiques (ECC). On parle alors de ECDH, et ECDSA, permettant d'avoir le même niveau de sécurité avec des tailles de clefs plus petites. Cependant, ce système aussi sera cassé par des ordinateurs quantiques assez puissants.
Plusieurs propositions d'algorithmes post-quantiques existent, mais sont actuellement peu déployés. Il est en effet peu utile de remplacer les algorithmes existants, qui ont passé l'épreuve du temps, alors même qu'il n'est pas sûr que la création d'ordinateur quantiques assez puissant soit possible. En effet, intriquer des qbits et les garder intriqué est très compliqué. Les ordinateurs quantiques ne sont d'ailleurs pas récent, le premier datant de 1998 (IBM).
Autres usages de la cryptographie
Le chiffrement homomorphe permet d'effectuer des opérations dans l'espace des chiffrés. Il est ainsi utilisé pour faire effectuer des calculs par un tiers, sans lui divulger les données ou le résultat.
Le chiffrement fonctionnel, permet de "chiffrer" une fonction. On lui passe des données chiffrées et il nous donne le résultat en clair. Utilisé pour qu'un tiers puisse faire des calculs et en connaître le résultats.
Calculs multi-parti sécurisé, plusieurs entités ont des données sur lesquelles ils veulent faire un calcul en n'en révélant que le résultat (e.g. votes).
Cryptosystème à seuil, au moins N personnes sont requises pour chiffrer (ou déchiffrer).