Alors... comment ça marche? (à lire avec la voix de Michel Chevalet)
Tout d'abord le message est encodé en base 64 (le cryptage/décryptage a lieu sur cet encodage). Cela permet d'encoder tout et n'importe quoi sans se soucier du type des caractères rencontrés
A la clé est ajoutée la somme de ses valeurs ordinales. Cette nouvelle clé est mélangée aléatoirement(*) en fonction de son propre contenu. L'intérêt réside dans le changement total du message chiffré dès qu'un seul caractère de la clé change.
La première lettre de cette nouvelle clé est convertie en chiffre
Il y a un alphabet composé de 65 lettres (correspondant à tous les caractères présents dans l'encodage base 64) :
Cet alphabet est mélangé aléatoirement(*) avec en paramètre le nombre obtenu à partir de la première lettre de la clé
Puis il y a une substitution lettre à lettre :
par exemple "a"=>"d","b"=>"s","c"=>"h", etc.
Puis on recommence avec le 2ième caractère du texte et le 2ième caractère de la cle. Mais le nouveau(?) principe est qu'avant chaque substitution de chaque lettre, pour éviter les séquences, le tableau est re-mélangé à partir du mélange précédent et avec nombre correspondant à la nouvelle position de la clé en paramètre. Chaque caractère du message est donc encodé avec un alphabet différent.
Pour le décodage, on fait exactement la même chose : mélange de la clé, mélange du tableau, décodage du premier caractère, mélange du tableau, décodage du 2nd caractères, etc. puis desencodage de la base 64
(*) aléatoirement mais toujours de la même façon grâce à mt_srand()
Cette substitution polyalphabétique me semble beaucoup plus difficile à casser qu'une autre car :
Le message chiffré est sûr pour un message inférieur ou égal à la taille de la clé (démontré pour la méthode du masque jetable), la force brute est impuissante sur la première partie du chiffrement.
Puisque l'alphabet est mélangé en fonction de la clé et en fonction du l'alphabet mélangé précédemment, même si la clef est plus courte que le message à chiffrer, on ne pourra pas trouver de séquences de caractères indiquant la longueur de la clef. Il est donc "impossible" de savoir si la clef est petite ou immense.
Pour info, la clé est un hash md5 du style 3118b474586c6e133b8a196a1e21e230 , donc de 32 caractères.
Je vous donne cette info pour vous dire que la clef se répète, que ce n'est pas un masque jetable.
Mais il y a quand même 4x1038 possibilités. Cette clé n'est pas stockée sur ce serveur et les messages non plus d'ailleurs ;-)
Je voudrai savoir si ce chiffrage est suffisamment costaud pour être utilisé dans la vie de tous les jours ou s'il est facilement cassable.
Pour obtenir l'algorithme (qui se trouve dans le source de cette page), il suffit de cliquer sur le bouton ci-dessous. Vous pouvez me contacter ici.
// tous les caractères de l'encodage base64 $liste=array( 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', '0','1','2','3','4','5','6','7','8','9','+','/','=' );
function creerTableauAssoc($pListe){ $tab=array(); for($i=0;$i<count($pListe);$i++){ $tab[$pListe[$i]]=$pListe[$i]; } return $tab; }
function genererCle($pCle){ global $VOIR_DETAILS; global $DETAILS; $resultat=0; $pCle=trim($pCle); // mélange de la clef for($i=0;$i<strlen($pCle);$i++){ $resultat+=ord(substr($pCle,$i,1)); } $DETAILS.='<tr><td>Clé 1 : </td><td><b>'.htmlspecialchars($pCle).'</b></td></tr>'; $pCle=$pCle.$resultat; $DETAILS.='<tr><td>Clé générée 2 (1+ajout valeur ord): </td><td><b>'.htmlspecialchars($pCle).'</b></td></tr>'; mt_srand($resultat); $tabCle=preg_split('//u', $pCle, -1, PREG_SPLIT_NO_EMPTY); for($i=count($tabCle)-1;$i>0;$i--){ $j=mt_rand(0, count($tabCle)-1); $tmp=$tabCle[$i]; $tabCle["".strval($i)]=$tabCle[$j]; $tabCle["".strval($j)]=$tmp; } $DETAILS.='<tr><td>Clé générée 3 (2+mélange): </td><td><b>'.htmlspecialchars(implode($tabCle)).'</b></td></tr>'; return base64_encode(implode($tabCle)); }
function chiffrer($pMessage,$pCle,$pListeCar){ global $VOIR_DETAILS; global $DETAILS; $retour=''; $listeCar=$pListeCar; $messageOrigine=$pMessage; $cleOrigine=$pCle; $DETAILS.='<tr><td>Alphabet d\'origine : </td><td><b>'.implode($listeCar).'</b></td></tr>'; $pMessage=mixMessage($pMessage,$pCle); $DETAILS.='<tr><td>Message mélangé : </td><td><b>'.$pMessage.'</b></td></tr>'; $pMessage=base64_encode($pMessage); $DETAILS.='<tr><td>Message encodé en base 64 : </td><td><b>'.$pMessage.'</b></td></tr>'; $pCle=genererCle($pCle);
$DETAILS.='<tr><td>Clé générée utilisée (3+base64) : </td><td><b>'.$pCle.'</b></td></tr>'; $listeCar=melanger($listeCar,$pCle,0); for($i=0;$i<strlen($pMessage);$i++){ $listeCar=melanger($listeCar,$pCle,$i); $lettre=substr($pMessage,$i,1); if(isset($listeCar[$lettre])){ $lettreCodee=$listeCar[$lettre]; }else{ $lettreCodee=$listeCar['?']; } $DETAILS.='<tr><td>Substitution : "<b>'.$lettre.'</b>" ⇢ "<b>'.$lettreCodee.'</b>" alphabet :</td><td>'.str_replace($lettre,'<b class="lettre">'.$lettre.'</b>',implode(array_keys($listeCar))).' <b>⇢</b> '.str_replace($lettreCodee,'<b class="lettre">'.$lettreCodee.'</b>',implode($listeCar)).'</td></tr>'; $retour.=$lettreCodee; } return '='.$retour.'=';//ajout des "=" pour bien délimiter la chaine }
function dechiffrer($pMessageCode,$pCle,$pListeCar){ global $VOIR_DETAILS; global $DETAILS; $retour=''; $pCleOrigine=$pCle; $listeCar=$pListeCar; if( (strlen($pMessageCode)<3) || (substr($pMessageCode,0,1)!='=') || (substr($pMessageCode,-1)!='=')){ $retour='Cette chaine ne peut être déchiffrée.'; }else{ $pMessageCode=substr($pMessageCode,1,strlen($pMessageCode)-2);//suppression des "=" au début et à la fin de la chaine $DETAILS.='<tr><td>Alphabet d\'origine : </td><td><b>'.implode($listeCar).'</b></td></tr>'; $pCle=genererCle($pCle); $DETAILS.='<tr><td>Clé générée utilisée (base64): </td><td><b>'.$pCle.'</b></td></tr>'; $listeCar=melanger($listeCar,$pCle,0); for($i=0;$i<strlen($pMessageCode);$i++){ $listeCar=melanger($listeCar,$pCle,$i); $listeCar2=array_flip($listeCar); $lettre=substr($pMessageCode,$i,1); $lettreDecodee=$listeCar2[$lettre]; $retour.=$lettreDecodee; $DETAILS.='<tr><td>Substitution "<b>'.$lettre.'</b>" ⇢ "<b>'.$lettreDecodee.'</b>"alphabet :</td><td>'.str_replace($lettre,'<b class="lettre">'.$lettre.'</b>',implode(array_keys($listeCar2))).' <b>⇢</b> '.str_replace($lettreDecodee,'<b class="lettre">'.$lettreDecodee.'</b>',implode(array_values($listeCar2))).'</td></tr>'; } $retour=base64_decode($retour); $DETAILS.='<tr><td>Message mélangé : </td><td><b>'.$retour.'</b></td></tr>'; $retour=unmixMessage($retour,$pCleOrigine); $DETAILS.='<tr><td>Message ordonné : </td><td><b>'.$retour.'</b></td></tr>'; } if($retour==''){ $retour='Cette chaine ne peut être déchiffrée.'; } return $retour; } function mixMessage($pMessage,$pCle){ $l=strlen($pMessage.$pCle); for($i=0;$i<$l;$i++){ $pMessage=substr($pMessage,-1).substr($pMessage,0,-1); } return $pMessage; } function unmixMessage($pMessage,$pCle){ $l=strlen($pMessage.$pCle); for($i=$l;$i>0;$i--){ $pMessage=substr($pMessage,1).substr($pMessage,0,1); } return $pMessage; } function melanger($listeCar,$pCle,$pI){ $listeCarOrigine=$listeCar; //création d'un tableau intermediaire $tab=array(); foreach($listeCar as $key=>$value){ $tab[]=$value; } //détermination de la position if($pI>strlen($pCle)){ $pI=$pI%strlen($pCle); } mt_srand(ord(substr($pCle,$pI,1))); for($i=count($tab)-1;$i>0;$i--){//on prend 2 éléments et on inverse leur valeur $j=mt_rand(0, $i); $tmp=$tab[$i]; $tab[$i]=$tab[$j]; $tab[$j]=$tmp; } $i=0; foreach($listeCar as $key=>$value){ $listeCarOrigine[$key]=$tab[$i]; $i++; } return $listeCarOrigine; } function dateMicrosecondes(){ list($usec, $sec)=explode(' ', microtime()); return ((float)$usec + (float)$sec); } ?> <html> <head> <title>Enigma2015</title> <style> *{margin:0;padding:0;font-size:12px;cursor:default;} body{padding:10px 10px 40px 10px;height:100%;background:no-repeat fixed; background-image:-moz-linear-gradient(top,#fff,#a4c3df); background-image:-webkit-linear-gradient(top,#fff,#a4c3df); background-image:-ms-linear-gradient(top,#fff,#a4c3df); background-image:-o-linear-gradient(top,#fff,#a4c3df); background-image:linear-gradient(top,#fff,#a4c3df); background-color:#a4c3df; } h1{font-size:20px;} textarea{cursor:text;width:100%;height:200px;padding:1px 5px 1px 5px;} input,button,a,label{cursor:pointer;} ol,ul{padding-left:20px;} lh{font-weight:700;padding-top:10px;} table{width:100%;} small{font-size:10px;} .surbrillance{display:inline-block;font-family: Monaco, "DejaVu Sans Mono", "Lucida Console", "Andale Mono", monospace;margin:5px 0 5px 0;padding:5px;color:#555;border:2px #fff solid;border-radius:5px;box-shadow:2px 2px 7px #555;} .surbrillance b{color:#000;} .surbrillance b.lettre{background-color:#55f;border-radius:3px;color:#fff;padding:0 1px 0 1px;} .surbrillance table tr:hover{background-color:#fff;} .surbrillance table tr td:first-child{text-align:right;width:280px;} #chargement{position:absolute;top:100px;left:400px;padding:10px;font-weight:700;width:200px;border:1px #f00 solid;border-radius:7px;background-color:#eee;box-shadow:2px 2px 7px #555;text-align:center;} </style> </head> <body> <h1 style="font-weight:700;text-align:center;">ENIGMA 2015... la suite <small>;-)</small></h1><br><br> <?php $message=''; $cle=''; $messageCode=''; $messageDecode=''; $resultat=''; $duree=0; $texte='Résultat :'; $listeCaractere=creerTableauAssoc($liste); if(isset($_POST['chiffrer'])){ $message=trim($_POST['message']); $cle=trim($_POST['cle']); $t=dateMicrosecondes(); $resultat=chiffrer($message,$cle,$listeCaractere); $duree=dateMicrosecondes()-$t; $texte='Message de '.strlen($message).' caractères chiffré en '.substr($duree,0,5).'sec :'; }elseif(isset($_POST['dechiffrer'])){ $message=trim($_POST['message']); $cle=trim($_POST['cle']); $t=dateMicrosecondes(); $resultat=dechiffrer($message,$cle,$listeCaractere); $duree=dateMicrosecondes()-$t; $texte='Message de '.strlen($message).' caractères déchiffré en '.substr($duree,0,5).'sec :'; } ?> <a href="enigma.php" style="text-decoration:none;font-size:18px;" title="Recharger la page">⟳</a> <button onClick="genererClef()">Générer une clef</button><br> <br> <?php if($VOIR_DETAILS){ echo '<div class="surbrillance"><table>'.$DETAILS.'</table></div>'; } ?> <form method=post> <table> <tr> <td style="width:30%;">Clé secrète (*) :<br> <textarea name="cle" id="cle" onFocus="focus();select();"><?php echo stripslashes($cle)?></textarea> </td> <td style="width:70%;">Message à chiffrer/déchiffrer :<br> <textarea name="message" onFocus="focus();select();"><?php echo stripslashes($message)?></textarea> </td> </tr> </table> <small>(*) plus la clef est longue plus le chiffrage est sûr. Une clef aussi longue que votre message rend le chiffrage absolument impossible à décoder de manière certaine.</small><br> <input type="submit" value="Chiffrer ⭫" name="chiffrer" onclick="document.getElementById('chargement').style.display='block';"> <input type="submit" value="Déchiffrer ⭫" name="dechiffrer" onclick="document.getElementById('chargement').style.display='block';"> <input type="checkbox" id="voir_details" name="voir_details"<?php echo (($VOIR_DETAILS)?' checked':'')?>><label for="voir_details"> Voir le détail du chiffrage</label> <br> <br> <?php echo $texte?><br> <textarea onFocus="focus();select();" readonly><?php echo stripslashes($resultat)?></textarea><br> </form> <br><br>
<h3>Le principe</h3> <div id="cachePrincipe" class="surbrillance"> L'idée m'est venue en lisant l'article sur <a href="http://fr.wikipedia.org/wiki/Enigma_%28machine%29" target="_blank">la machine Enigma</a>, c'est un chiffrement de type <a href="http://fr.wikipedia.org/wiki/Chiffrement_par_substitution#La_substitution_polyalphab.C3.A9tique" target="_blank">substitution polyalphabétique</a><br> <br> <ol> <lh>Alors... comment ça marche? (à lire avec la voix de <a href="www.youtube.com/watch?v=Hog32shRwTY" target="_blank">Michel Chevalet</a>)</lh> <li>Tout d'abord le message est encodé en <a href="http://fr.wikipedia.org/wiki/Base64" target="_blank">base 64</a> (le cryptage/décryptage a lieu sur cet encodage). Cela permet d'encoder tout et n'importe quoi sans se soucier du type des caractères rencontrés</li> <li>A la clé est ajoutée la somme de ses valeurs ordinales. Cette nouvelle clé est mélangée aléatoirement(*) en fonction de son propre contenu. L'intérêt réside dans le changement total du message chiffré dès qu'un seul caractère de la clé change.</li> <li>La première lettre de cette nouvelle clé est convertie en chiffre</li> <li>Il y a un alphabet composé de 65 lettres (correspondant à tous les caractères présents dans l'encodage base 64) :<br> <pre> 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', '0','1','2','3','4','5','6','7','8','9','+','/','=' </pre> Cet alphabet est mélangé aléatoirement(*) avec en paramètre le nombre obtenu à partir de la première lettre de la clé</li> <li>Puis il y a une substitution lettre à lettre :<br> par exemple "a"=>"d","b"=>"s","c"=>"h", etc.<br> </li> <li>Puis on recommence avec le 2ième caractère du texte et le 2ième caractère de la cle. Mais le nouveau(?) principe est qu'avant chaque substitution de chaque lettre, pour éviter les séquences, le tableau est re-mélangé à partir du mélange précédent et avec nombre correspondant à la nouvelle position de la clé en paramètre. <b>Chaque caractère du message est donc encodé avec un alphabet différent.</b></li> <li>Pour le décodage, on fait exactement la même chose : mélange de la clé, mélange du tableau, décodage du premier caractère, mélange du tableau, décodage du 2nd caractères, etc. puis desencodage de la base 64</li> </ol> <i>(*) aléatoirement mais toujours de la même façon grâce à <a href="http://php.net/manual/fr/function.mt-srand.php" target="_blank">mt_srand()</a></i> <br><br><br> <ul><lh>Cette substitution polyalphabétique me semble beaucoup plus difficile à casser qu'une autre car :</lh> <li>Le message chiffré est sûr pour un message inférieur ou égal à la taille de la clé (démontré pour <a href="http://fr.wikipedia.org/wiki/Masque_jetable" target="_blank">la méthode du masque jetable</a>), la force brute est impuissante sur la première partie du chiffrement.</li> <li>Puisque l'alphabet est mélangé en fonction de la clé et en fonction du l'alphabet mélangé précédemment, même si la clef est plus courte que le message à chiffrer, on ne pourra pas trouver de séquences de caractères indiquant la longueur de la clef. Il est donc "impossible" de savoir si la clef est petite ou immense.</li> <li>De plus, je pense qu'il n'est donc pas possible de faire une <a href="http://fr.wikipedia.org/wiki/Analyse_fr%C3%A9quentielle" target="_blank">analyse fréquentielle</a></li> </ul><br> Inconvénient : l'encodage en base 64 augmente d'environ 30% la taille du message. </div> <br><br>
<h3>Le challenge</h3> <div class="surbrillance" id="cacheDefi"> <i>Début du challenge : 21/05/2015</i><br> Un paquet de carambars au premier qui pourra déchiffrer ces 2 messages (chiffrés avec la même clé) :<br> <div style="background-color:#fff;padding:5px;border-radius:5px;"><b> =hWakMWl77VkbixJcpWfUaJasNg6ay+umpiqUpC=3QhN6yiNDB=ZYi1zcDuXrWwBDC/hBl/e9jl0Nvs2dFENSW31f7jZOK1IcvEASkMKc7MLdPZm5t0GSeZ5iSqo7oy26QVmtKx8k= </b></div><br> <div style="background-color:#fff;padding:5px;border-radius:5px;"><b> =hk=dU17oCVDPZ6SHeCPpYkKbad6PNxOzdOnJ8wH0lhYAHiNL4B4nqUaZ6smNWNeBfjIaK78IuVsFKg6BPZH2JgDf0PZhJIAMpXnSoNQghIcjbQ=5e3M/yMDt5eZM=g7rKL/LkpmAy9ouvzOVCZF6NVQFMRlsSIuQlWW5fEZgo2Gjhvx2iDDxUCi2F53C8K=CkyGAmjdqRzCZWpzEo8HoG4FQ5QDs+0radjcyRGm5H2f3P3joKl9a8PDJjKgIrG1jzW1l7k7WO5W7i8/whNs4rRipeZzYCItVSjnZBXN1u=+aH1cYK7FDf+Q++ofDR1rPPBDt8sIBMiy7ELYNtilN5jELfZtrb5hn/kKAVYOMeH5toN=cNj0GVi=jBCoQ5B=mhn2Pesxl0hDS1C6oKmx9fxzNxlrY=iYij8D6= </b></div><br> Pour info, la clé est un hash md5 du style <b><?php echo md5(time())?></b> , donc de 32 caractères.<br> Je vous donne cette info pour vous dire que la clef se répète, que ce n'est pas un masque jetable. Mais il y a quand même 4x10<sup>38</sup> possibilités. Cette clé n'est pas stockée sur ce serveur et les messages non plus d'ailleurs ;-)<br> Je voudrai savoir si ce chiffrage est suffisamment costaud pour être utilisé dans la vie de tous les jours ou s'il est facilement cassable.<br> Pour obtenir l'algorithme (qui se trouve dans le source de cette page), il suffit de cliquer sur le bouton ci-dessous. Vous pouvez <a href="http://trucs.xig.fr/contact/">me contacter ici</a>.<br> </div> <br><br>
<button onclick="cacherVoir('cacheSource');return false;">Source de cette page (=le code du chiffrage) ⭫</button> <div id="cacheSource" class="surbrillance" style="display:none;"> <?php highlight_file('enigma.php'); ?> </div>
<div id="chargement" style="display:none;">Opération en cours...</div> <script> function cacherVoir(pIdElement){ if(document.getElementById(pIdElement).style.display=='none'){ document.getElementById(pIdElement).style.display='inline-block'; }else{ document.getElementById(pIdElement).style.display='none'; } } function genererClef(){ var chaine='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY1234567890*-/+&!?*()[]{}<>$€£,;:.~@'; c=''; for (i=0; i<100; i++) { c += chaine[Math.floor(Math.random() * chaine.length)]; } document.getElementById("cle").value=c; } </script> </body> </html>