TI1 F Chaines
TI1 F Chaines
TI1 F Chaines
1 Préambule Nous allons donc compter le nombre d’occurrence de chacune des lettres dans le texte
pour déterminer les cinq plus courantes, et donc la langue probable du mail.
Chargeons tout d’abord deux modules qui seront utiles dans ce TP :
1. Écrire une fonction CompteCar(c, ch) qui prend en argument un caractère c et une
2 Mission 1 : identifier la langue d’un texte chaine ch et retourne le nombre d’occurrences de c dans la chaîne ch.
2. Écrire une fonction CompteAlpha(ch) qui prend en argument une chaîne ch et affiche
Un message électronique a été intercepté sur la boîte au lettres d’un suspect, dont le le nombre d’occurrence de chacune des lettres (minuscules) de l’alphabet.
début est reproduit ci-dessous. Malheureusement, il n’est manifestement pas écrit en
français ou en anglais. On cherche à déterminer à quel traducteur il faudrait faire appel.
• On rappelle que l’instruction print("a", ":", 5) permet d’afficher a : 5 à l’écran.
On pourra se servir de ce genre d’instruction pour mettre en forme les résultats.
2.1 Charger le contenu du mail en mémoire • le module string contient la chaîne de caractères string.lowercase qui contient la
liste des lettres minuscules, ce qui pourra éventuellement vous être utile.
Le fichier texte.dat contient une seule ligne correspondant à l’intégralité du contenu
dudit mail. Pour charger ce mail dans Python, on exécute les commandes suivantes (on Pour obtenir les cinq caractères les plus fréquents, par fréquences décroissantes :
reviendra ultérieurement plus en détail sur les méthodes pour lire et écrire dans un fichier) : • on construit une liste contenant autant d’éléments qu’il y a de caractères dans
string.ascii_lowercase, ces éléments étant des listes contenant le nombre d’oc-
fichier = open("D:/TPChaines/texte.dat") curence de chaque caractère et le caractère en question
texte = fichier.readline() L = [ [ 25, 'a' ], [ 1, 'b' ], [ 2, 'c' ] ... ]
fichier.close() • puis on trie la liste avec L.sort() (le tri se fait par ordre lexicographique, donc sur le
premier élément de chaque liste, et en cas d’égalité seulement, sur le second) et on
La première commande ouvre le fichier texte.dat. On peut alors faire référence à son ne conserve que les cinq premières listes
contenu avec le nom fichier. La seconde commande prend la première (et unique ici) 3. Écrire une fonction CarPlusFrequents(ch) qui prend en argument une chaîne de
ligne du fichier et la range sous le nom texte. La dernière commande referme le fichier. caractères ch et retourne les cinq caractères présents en plus grand nombre dans la chaîne
Faute de reconnaître la langue, il faudra procéder autrement. On connaît, pour une série et leur fréquence, en mettant en œuvre l’approche précédente.
de langues, les cinq lettres les plus courantes (par fréquence décroissante) : 4. En quelle langue le texte est-il vraisemblablement écrit ?
Remarque : dans le texte, il y a des majuscules qui ne sont pas comptées. Si l’on veut amé-
• Anglais : etaoi • Italien : eaion • Turc : aeinr
liorer le décompte, il est utile de transformer les majuscules dans le texte en minuscules,
• Français : esait • Allemand : enisr • Danois : enati
ce que l’on peut faire avec la commande
• Espagnol : eaosr • Suédois : eantr • Polonais : aoien
• Portugais : aeosr texte = texte.lower()
1
3 Mission 2 : déterminer des contacts communs On peut ensuite regarder par exemple les cinq premiers mots de la liste (il n’est pas
recommandé de tout afficher !) avec
Dans l’enquête en cours, deux suspects ont été identifiés, qui avaient chacun un télé-
phone portable. De ces deux téléphones portables ont été extraits deux listes de numéros mots[0:5]
qui ont été utilisés sur chacun des portables. On cherche à savoir si les deux suspects ont
un contact en commun en comparant les deux listes de numéros. La commande len(mots) nous indique qu’il y a 386264 éléments dans la liste, soit autant
de mots (et déclinaisons) de moins de 15 lettres valides en français.
Tant que la liste L n’est pas vide, on prend un élément au milieu de la liste, que l’on
3.2 Charger un dictionnaire compare à ch.
• s’ils sont identiques, alors on a trouvé la chaîne ch dans la liste ;
Le fichier mots.dat contient l’ensemble des mots français (avec leurs déclinaisons • si la chaîne ch se trouve avant l’élément au milieu de la liste, alors on ne conserve
et conjugaisons) de moins de 15 lettres. Nous allons ouvrir ce fichier et le lire, afin de de la liste L que les éléments figurant avant l’élément que l’on vient d’extraire ;
mémoriser tous ces mots dans une liste appelée mots, grâce aux commandes suivantes : • si la chaîne ch se trouve après l’élément au milieu de la liste, alors on ne conserve
de la liste L que les éléments figurant après l’élément que l’on vient d’extraire.
fichier = open("D:/TPChaines/mots.dat")
mots = [ ligne.rstrip() for ligne in fichier ] Si l’on vide la liste sans trouver l’élément, c’est qu’il n’était pas présent dans la liste.
fichier.close()
4. Écrire une fonction RechercheDichotomique(ch, L) prenant en argument une chaîne
ch et une liste de chaînes L, et qui renvoie True si l’élément figure dans la liste et False
Les commandes ressemblent à celles utilisées pour lire un fichier contenant une unique
dans le cas contraire.
ligne. Mais ici, il y a un grand nombre de lignes : une pour chaque mot. La seconde
commande permet donc de lire chacune de ces lignes d’un seul coup, et de les ranger dans 5. Écrire une nouvelle fonction VerifieVite(ch) qui prend en argument une chaîne de
une liste. Chaque ligne du fichier devient un élément de la liste sous la forme d’une chaîne caractère ch et retourne True si la chaîne est présente dans la liste mots et False dans le
de caractère. cas contraire, mais cette fois-ci en utilisant RechercheDichotomique(ch, L) pour effectuer
la recherche plus rapidement.
La fonction .rstrip() appliquée à chaque ligne permet de retirer le caractère à la fin de
chacune d’entre elles qui indique la fin de la ligne (vous pouvez essayer d’exécuter les trois 6. En sachant que 386264 < 219 , combien de tests faudra-t-il effectuer pour conclure
commandes précédentes sans ce .rstrip() et voir la différence). qu’un mot est incorrect cette fois-ci ?
2
3.5 Recherche du contact commun 2. Charger ces deux fichiers dans deux listes de chaînes de caractères, appelée par
exemple baseADN et fragments.
Les fichiers contacts1.dat et contacts2.dat contiennent chacun une liste de numéros 3. Essayer grâce à la fonction Contenue(ch1, ch2) de trouver une correspondance entre
de téléphone, un par ligne, triées par numéros croissants. les signatures ADN et les fragments. On essaiera de déterminer les numéros des lignes du
fichier ADN dont les signature correspondent à un ou plusieurs prélèvements.
7. En s’inspirant des commandes précédentes, ouvrir les deux fichiers et construire
deux listes liste1 et liste2 contenant chacune les listes triées de chaînes de caractères
correspondant aux numéros appelés par chacun des deux téléphones.
5 Mission 4 : décrypter un message
8. Proposer une fonction, faisant appel à RechercheDichotomique(ch, L), fournissant
la liste des numéros de téléphone qui ont été appelés par les deux suspects. Un message entre trafiquants a été intercepté. Il est reproduit dans le fichier
message.dat. Malheureusement, il est « crypté ». Le principe du cryptage est de remplacer
les caractères constituant le message par d’autres caractères, en utilisant un ensemble de
4 Mission 3 : identifier un ADN règles, de façon à rendre le message illisible pour quinconque ne connaît pas les règles en
question.
Sur les lieux du crime, on a pu récupérer 5 empreintes partielles d’ADN. On souhaite L’une des plus anciennes façon de crypter un message est le code de César. Il consiste à
savoir si ces empreintes ADN figurent dans la base de données regroupant les empreintes remplacer chaque caractère par le suivant dans l’alphabet. Par exemple, « MPSI » devient
ADN de personnes précédemment mises en cause. « NQTJ ». Le Z est, quant à lui, remplacé par un A.
L’ADN est une macro-molécule ressemblant à ruban torsadé, dans lequel se succèdent Une version un peu plus évoluée consiste à décaler les lettres non pas d’un rang dans
des bases appelées adénine, cytosine, guanine, thymine. On peut donc représenter un chro- l’alphabet, mais de n rangs. Par exemple, si n = 5, A devient F, C devient H et X devient C. Et
mosome ou un morceau d’ADN comme une chaîne de caractères contenant uniquement « MPSI » devient donc « RUXN ».
les lettres A, C, G et T.
Le code de César porte ce nom car César l’aurait sensément utilisé (avec un décalage de
Pour savoir si un fragment d’ADN fait partie de l’ADN d’une personne, il suffit de regarder trois, et en travaillant avec l’alphabet grec, inconnu des gaulois), même s’il est possible
si la série de lettres A, C, G, T correspondant au fragment est présente quelque part dans qu’il n’ait pas été le premier à le faire. On ne connaît pas avec certitude la fiabilité qu’avait
la totalité de la signature ADN. Il s’agit donc ici de déterminer si une chaîne est présente un tel cryptage à cette époque (les premières études sérieuses de cryptanalyse sont à porter
dans une sous-chaîne. au crédit des arabes au IX e siècle, mais cela ne signifie pas que les messages codés dans
Il se trouve qu’en Python, il est simple de savoir si une sous-chaîne ch1 est incluse dans l’antiquité étaient impossibles à déchiffrer par tâtonnement).
une chaîne plus longue ch2. Il suffit d’utiliser le test (ch1 in ch2). Cela dit, le but de ce 1. Écrire une fonction Decale(ch, n) qui prend en argument une chaîne de caractères
TP étant d’apprendre à manipuler les chaînes de caractères, cette fois encore, on fera dans ch et un entier n et crée une nouvelle chaîne dans laquelle les caractères sont ceux de ch,
la suite comme si cette comparaison, bien pratique, n’existe pas ! décalés de n rang si ce sont des lettres majuscules ou minuscules.
Pour déterminer la présence de la chaîne ch1 à l’intérieur de la chaîne ch2, il est possible
d’utiliser l’algorithme « naif » suivant :
On rappelle que la fonction Python chr(c) prend en argument un caractère et retourne
son point Unicode, et que la fonction ord(n) réalise l’opération inverse. Les majuscules
Pour tout i entre 0 et len(ch2)-len(ch1) (inclus) ont pour points unicodes, dans l’ordre, les entiers 65 (pour A) à 90 (pour Z), et les
si ch1[0]=ch2[i] ET ch1[1]=ch2[i+1] ET ... ET ch1[len(ch1)-1]=ch2[i+len(ch1)-1] minuscules les points 97 (pour a) à 122 (pour z).
alors la chaîne ch1 est présente dans la chaîne ch2 à partir du i-ème caractère
Sinon la chaîne ch1 n’est pas présente dans la chaîne ch2 2. Si le message est crypté avec un décalage de n, comment peut-on le décrypter ?
3. Décrypter le message fourni.
1. Écrire une fonction Contenue(ch1, ch2) qui renvoie True si ch1 est présente à l’inté-
4. Voyez-vous une façon de « deviner » la valeur de n sans essayer les 25 décalages
rieur de ch2 et False dans le cas contraire.
possibles, sachant que le message est écrit en français ?
Le fichier baseADN.dat contient 200 empreintes ADN, à raison d’une empreinte par
C’est le principe de base de la cryptanalyse. Pour corriger ce défaut, le code de Vigenère
ligne. Le fichier fragments.dat contient des fragments incomplets d’ADN provenant de 5
utilise un décalage qui change à chaque lettre. Si la « clé » (la liste des décalages) est aussi
prélèvements (un par ligne) effectués sur les lieux du crime.
3
longue que le message et n’est utilisée qu’une seule fois, le message est impossible à mot français comme mot de passe.
déchiffrer sans la connaissance de la clé.
1. Essayer d’accéder au compte "adupont" en essayant les mots du dictionnaire que l’on
a précédemment chargés comme mots de passe (c’est ce que l’on appelle une attaque
dictionnaire). Pourquoi est-il dangereux d’utiliser des mots du dictionnaire, des noms ou
6 Mission 5 : casser un mot de passe des dates de naissance comme mot de passe ?
Des documents importants pour l’enquête ont été enregistrés dans un ordinateur. Mal- 2. Essayer d’accéder au second compte, "bdurand". Cela fonctionne-t-il ?
heureusement, il est nécessaire d’avoir un login et un mot de passe pour se connecter
et récupérer les données. Il a été possible de récupérer les logins, qui sont adupont et
6.2 Attaque par analyse du temps de réponse
bdurand. Malheureusement, on ne dispose pas des mots de passe, il va falloir les « casser ».
On commencera par charger le module permettant de se connecter aux comptes avec la Nous allons envisager une autre méthode pour « deviner » le second mot de passe.
commande :
3. En se servant de la fonction Compare(ch1, ch2) qui a été écrite plus tôt, écrire une
exec(open("D:/TPChaines/log.py").read()) fonction TestMdP(ch) qui renvoie True si la chaîne fournie est "azerty" et False dans le
cas contraire.
Pour essayer de se connecter au compte, on exécute la fonction On va s’intéresser au temps nécessaire à la fonction compare pour répondre True ou
False. Pour déterminer précisément le temps d’exécution de la fonction testMdP(ch)
Login("monlogin", "monmotdepasse") lorsqu’on lui fournit la chaîne 'qsdfgh', on peut utiliser la commande :
Lorsque le couple login/mot de passe est incorrect, cette fonction retourne False et timeit.Timer("TestMdP('qsdfgh')", "from __main__ import TestMdP").timeit(100)
n’affiche rien. Si le couple login/mot de passe est correct, elle affiche True et donne accès
aux données. Cette commande permet d’exécuter 100 fois la commande TestMdP('qsdfgh') et re-
tourne le temps qu’il a fallu pour cette exécution. Elle utilise le module timeit que l’on a
chargé tantôt avec la commande
6.1 Attaque brute par dictionnaire
import timeit
Une première méthode pour « casser » un mot de passe est d’utiliser un dictionnaire
contenant tous les mots de passe courants. Diverses études montrent que la plupart des 4. Déterminer le temps qu’il faut à la fonction pour répondre False si l’on utilise les
personnes utilisent en effet de très mauvais mots de passes, trop faciles à deviner. chaînes suivantes comme argument :
• qwerty
Ainsi, une étude a montré que les 25 mots de passes les plus courants en 2014 (d’après
• passwd
ceux rendus publics suite à des failles de sécurité), et donc aisément cassés, étaient :
• azimut
• azures
• 123456 • football • master
• azertx
• password • 1234567 • michael
• 12345 • monkey • superman 5. En se basant sur les résultats de la question précédente, imaginer une méthode
• 12345678 • letmein • 696969 permettant de trouver le mot de passe vérouillant le compte "bdurand". Pour limiter les
• qwerty • abc123 • 123123 calculs, on admettra qu’il est composé de six caractères, tous des minuscules sans accent.
• 123456789 • 111111 • batman
Avant que vous ne vous inquiétiez pour la sécurité de vos comptes, les programmes
• 1234 • mustang • trustno1
bien conçus demandant un mot de passe attendent toujours un temps identique avant
• baseball • access
de répondre. Mais cela montre qu’écrire une fonction qui vérifie un mot de passe est un
• dragon • shadow
exercice particulièrement délicat.
Pire, il faut aussi que le processeur fasse autant de calculs que le mot de passe soit bon
Si vous reconnaissez le votre, il est temps d’en changer !
ou faux. Il ne suffit pas d’attendre sans rien faire car le logiciel malveillant peut essayer de
On va essayer de voir si les suspects ont été suffisamment imprudents pour utiliser un savoir si l’ordinateur calcule ou attends sans rien faire. Même sans accès à la machine, le
4
courant consommé, les vibrations... beaucoup de choses peuvent renseigner quelqu’un de
malveillant sur le fonctionnement d’un programme.
On a même montré récemment qu’un simple téléphone, en écoutant le bruit produit par
un ordinateur, était capable de savoir s’il travaillait ou attendait sans rien faire, et même
partiellement déterminer ce qu’il était en train de calculer :
http://www.cs.tau.ac.il/\textasciitilde tromer/acoustic/
Une application malveillante sur votre téléphone peut aussi, en utilisant le gyroscope,
déterminer en partie ce que vous tapez sur votre téléphone, voire approximativement ce
que vous tapez sur un clavier posé sur la même table que le téléphone !