Série 7 - Flots et cryptographie
Introduction
Le masque jetable (one-time pad en anglais) est une technique de chiffrement très facile à mettre en œuvre mais malgré tout inviolable, sous certaines conditions malheureusement difficiles à satisfaire en pratique. Le but de cette série est d'écrire un programme permettant de faire le chiffrage et le déchiffrage de fichiers au moyen d'un masque jetable. Ce faisant, vous utiliserez deux patrons de conception vus au cours.
Pour chiffrer une suite d'octets au moyen de la technique du masque jetable, il faut posséder une autre séquence d'octets—le masque—qui soit aléatoire et ait au moins la longueur des données à chiffrer. Le ie octet chiffré s'obtient simplement en faisant le OU exclusif entre le ie octet du message à chiffrer et le ie octet du masque. Pour déchiffrer une suite d'octets ainsi chiffrée, il suffit de faire la même opération avec le même masque et le tour est joué.
La table ci-dessous illustre la technique avec une séquence de trois octets. Toutes les valeurs sont données en binaire, et on vérifie facilement que chaque octet chiffré est le OU exclusif (bit-à-bit) des octets correspondants de la séquence avant chiffrement et du masque. Et aussi que chaque octet déchiffré est le OU exclusif des octets correspondants de la séquence chiffrée et du masque.
Octet 1 | Octet 2 | Octet 3 | |
---|---|---|---|
Avant chiffrement | 001010102 | 010010102 | 010010012 |
Masque | 100101002 | 111111002 | 001001102 |
Après chiffrement | 101111102 | 101101102 | 011011112 |
Flot « OU exclusif »
Le but du premier exercice est d'écrire un flot d'entrée qui, étant donnés deux flots d'entrée, produit un nouveau flot dont chaque octet est le OU exclusif des octets correspondants des deux flots. Ce nouveau flot doit se terminer dès que l'un des deux flots d'entrée se termine.
Pour ce faire, écrivez une classe XOrInputStream
qui hérite de java.io.InputStream
et qui prenne deux flots d'entrée (de type InputStream
) en argument et se comporte comme décrit ci-dessus.
Pensez à redéfinir les méthodes read
et close
comme pour la série précédente. Bien entendu, le flot XOrInputStream
est une instance du patron de conception Composite !
Note de programmation : en Java, le OU exclusif entre deux valeurs (entières) se fait au moyen de l'opérateur infixe ^
, comme l'illustre l'exemple ci-dessous :
byte r = 0b00101010 ^ 0b10010100;
qui donne la valeur 101111102 à la variable r
. Rappelez-vous qu'un entier commençant par 0b
est spécifié en format binaire.
Pour vérifier votre flot, vous pourriez écrire un test unitaire avec JUnit, comme d'habitude. Mais nous vous proposons plutôt d'écrire un petit programme qui, étant donné un premier fichier contenant des données chiffrées avec un masque jetable et un second fichier contenant ce masque, produise la version déchiffrée dans un troisième fichier. Une fois cela fait, exécutez-le sur la paire de fichiers que nous mettons à votre disposition (dans cette archive zip). Vous devriez obtenir en sortie un message de l'armée allemande qui avait été intercepté et décrypté par les alliés lors de la seconde guerre mondiale. Notez au passage que le texte en question n'avait pas été chiffré au moyen d'un masque jetable, mais de la machine Enigma.
Itérateur pour flots
Comme vous l'avez certainement constaté lors de l'écriture de votre programme de chiffrement/déchiffrement, les flots ne sont pas très agréables à l'usage, étant donnée la manière dont la fin de flot est signalée.
Pour rendre le code de votre programme principal plus lisible, écrivez une classe InputStreamToIterable
permettant de transformer un flot d'entrée de type InputStream
en un objet implémentant l'interface Iterable<Byte>
. Une fois cela fait, vous devriez être capable de parcourir les octets d'un flot d'entrée inputStream
quelconque au moyen de la boucle for-each, ainsi :
for (byte b: new InputStreamToIterable(inputStream)) { // ... code utilisant b }
Attention à bien gérer les exceptions qui pourraient être levées par la méthode read
et à fermer le flot une fois sa fin atteinte.
Une fois votre classe écrite, modifiez votre programme de chiffrement/déchiffrement pour qu'il parcourt les octets du flot XOrInputStream
au moyen de la boucle for-each.
Vous l'aurez remarqué, la classe InputStreamToIterable
est un adaptateur (au sens du patron Adapter) permettant de transformer un flot d'entrée en un objet itérable.