Élements de correction

publicité
Cryptographie
Élements de correction
TP1 d’informatique deuxième année
Vu que vous avez tous réussi les questions, les principaux enseignements que vous trouverez dans ce
document sont une façon propre de faire le chiffrement de César et des informations sur les différentes façons
de parcourir et de construire des séquences (listes, chaı̂nes, ...). Le fait qu’une boucle for puisse parcourir
toute sorte de séquence ordonnée (les types itérables), et la construction d’une liste par compréhension, qui
est plus rapide à écrire, quand on comprend bien ce qu’elle fait.
Lisez bien la partie sur la fonction sans combi, et surtout la différence entre liste.append(element)
et liste=liste+[element] ! Faites VRAIMENT l’expérience de comparer les deux versions sur un texte
un peu long comme votre oeuvre complète !
ˆ Préliminaire : pour le faire soi-même on utilise une boucle for. En python cette boucle peut itérer sur
tout ce qui représente une séquence : un range, une liste, une chaı̂ne... On a donc ici deux possibilités
naturelles : itérer sur le range des indices, ou sur la liste elle-même.
def rassembler(liste):
s=’’
for i in range(len(liste)): #iteration sur les indices
s=s+liste[i]
return s
def rassembler(liste):
s=’’
for chaine in liste: #iteration sur la liste
s=s+chaine
return s
Sinon, voir help(str.join), et essayer s="xxx".join(["nous","sommes","réunies"]),
puis s="".join(["nous","sommes","réunies"]).
Unicode
10F F F F 16 = 1 × 165 + 0 × 164 + 15 × 163 + 15 × 162 + 15 × 161 + 15 × 160 . Pour donner à python un
nombre représenté en base 2 (binaire), 8 (octale), ou 16 (héxadécimale), on le préfixe d’un zéro et de
la lettre b, o, ou x. Donc python donne la réponse à la première question si on lui demande de calculer
0x10FFFF+1. Un peu plus d’un million de codepoints : de quoi encoder l’ensemble des caractères de
toutes les langues, chinois compris.
ˆ Fabriquer la liste des majuscules ["A","B",...,"Z"].
Réponse : Ce sont les codepoints 65 à 90, donc :
alpha=[]
for i in range(65,91):
alpha.append(chr(i))
1
ou encore, en définissant cette liste par compréhension :
alpha=[chr(i) for i in range(65,91)]
ˆ Écrire une fonction qui prend un texte en argument et retourne le même texte où tous les caractères
de combinaison ont disparu :
On ne peut pas modifier une chaı̂ne de caractères, c’est pourquoi on doit construire une nouvelle chaı̂ne.
On décompose notre texte pour séparer les lettres de leurs accents, et on copie tous ses caractères qui
ne sont pas des caractères de combinaison.
On utilise une boucle for, et on peut soit itérer sur la chaı̂ne de caractères elle-même, soit itérer sur
un ensemble d’indices (entiers). Voir les deux versions ci-dessous :
def sans_combi(texte):
s=unicodedata.normalize("NFD",texte)
nouveau_texte=""
for caractere in s: # iteration sur la chaine
if unicodedata.combining(caractere)==0:
nouveau_texte=nouveau_texte+caractere
return nouveau_texte
def sans_combi(texte):
s=unicodedata.normalize("NFD",texte)
nouveau_texte=""
for i in range(len(s)): #iteration sur les indices
if unicodedata.combining(s[i])==0:
nouveau_texte=nouveau_texte+s[i]
return nouveau_texte
Certains ont préféré construire une liste au lieu de construire une chaı̂ne de caractères, puis à la fin
rassembler la liste en une seule chaı̂ne. Mais attention ! Pour construire une liste élément par élément
on utilise liste.append(element) qui modifie l’objet liste, et surtout pas liste=liste+[element],
qui crée une toute nouvelle liste puis la remplit avec le contenu de l’ancienne et le nouvel élément !
def sans_combi(texte):
s=unicodedata.normalize("NFD",texte)
nouvelle_liste=[]
for i in range(len(s)):
if unicodedata.combining(s[i])==0:
nouvelle_liste.append(s[i])
# remplacez la ligne ci-dessus par : nouvelle_liste=nouvelle_liste+[s[i]]
# et comparez le temps d’execution sur une chaine de 10^5 ou 10^6 caracteres.
return rassembler(nouvelle_liste)
On peut aussi construire cette liste “par compréhension” :
def sans_combi(texte):
s=unicodedata.normalize("NFD",texte)
nouvelle_liste=[c for c in s if unicodedata.combining(c)==0]
return rassembler(nouvelle_liste)
Toutes ces solutions sont correctes et se valent à peu près du point de vue du temps de calcul.
ˆ Fonction clean :
2
def clean(texte):
return sans_combi( texte.upper() )
# ou bien :
return sans_combi(texte).upper()
Chiffrement de César
Il en existe 25 versions si on ne compte pas la fonction identité comme un chiffrement.
ˆ Écrire une fonction cesar caractere(c,k) qui retourne le caractère c décalé de k rangs dans l’alphabet
si c’est une lettre majuscule, et retourne c inchangé sinon :
Pour tester si on a affaire à une lettre majuscule, il suffit de vérifier si son codepoint est entre 65
et 90. Ce test est plus rapide que de parcourir la liste des lettres majuscules pour vérifier si c s’y
trouve. Concernant le décalage de k rangs, vous avez presque tous pu vous rendre compte qu’une
solution pas assez rigoureuse à ce genre de question mène à une longue séance de débuggage. Donc
procédons méthodiquement : prenons notre lettre, et considérons son codepoint. Changeons-le en
un nombre entre 0 et 25 en retranchant 65. Ajoutons k et ramenons-nous dans l’intervalle [[0, 25]] à
l’aide de l’opérateur modulo : %. Ajoutons 65 pour obtenir le codepoint de la lettre correspondante, et
appliquons chr pour avoir cette lettre :
def cesar_caractere(c,k):
codepoint=ord(c)
if codepoint < 65 or codepoint > 90:
return c
else :
numero=codepoint-65
numero_decale=(numero+65)%26
codepoint_decale=65+numero_decale
return chr(codepoint_decale)
Cette fonction fonctionne avec n’importe quel k ∈ Z.
ˆ Écrire une fonction cesar(s,k) qui chiffre un texte avec le code de César avec le décalage fourni.
Comme pour retirer les caractères de combinaison, on avait le choix entre plein de variantes très proches
les unes des autres. On pouvait itérer sur la chaı̂ne, ou sur ses indices, ou sur la liste de ses caractères,
et on pouvait construire directement une chaı̂ne ou contruire une liste pour la rassembler ensuite. Par
exemple, en itérant sur la chaı̂ne et en construisant directement une chaı̂ne :
def cesar(texte,k):
resultat=""
for c in texte:
resultat=resultat+cesar_caractere(c,k)
return resultat
ou bien en construisant une liste par compréhension pour ensuite la rassembler :
def cesar(texte,k):
liste_resultat=[cesar_caractere(c,k) for c in texte]
return rassembler(liste_resultat)
ˆ Pour décoder un texte codé par un décalage de k, on effectue un décalage de −k.
ˆ Pour déchiffrer le texte “V’XLM NG IXN VHNKM, CXNGX AHFFX”, on pouvait essayer les 26
décalages possibles et trouver celui qui retournait un texte lisible.
3
Analyse fréquentielle
ˆ Échantillon de texte : attention à le sauver sous la forme d’un fichier texte “brut”, pas un document
word ou autre. Pour cela, utiliser le logiciel “bloc-notes”, et si il n’est pas dans votre menu Démarrer,
alors dans le menu Démarrer, cliquer sur “Éxécuter...” et taper “notepad”.
ˆ Pour lire son contenu depuis python, ne pas oublier l’extension .txt du fichier, et si le chemin contient
des backslash, mieux vaut les doubler. Python interprétera toujours un double backslash comme le
caractère ‘\’. En revanche, un simple backslash suivi de certaines lettres comme ‘a’, ‘t’, ‘r’ ou ‘n’ sera
compris comme un caractère spécial (tabulation, saut de ligne...)
f=open("chemin_vers_le_fichier.txt")
texte=f.read()
ˆ Compter le nombre d’occurrences de chaque lettre dans l’échantillon, et en déduire la fréquence de
chaque lettre :
occurrences=[0]*26
for c in texte :
codepoint=ord(c)
if codepoint>=65 and codepoint <=90 :
occurrences[codepoint-65]=occurrences[codepoint-65]+1
total=sum(occurrences) # merci la fonction ’sum’ de python
frequences=[]
for n in occurrences:
frequences.append(n/total)
ˆ Histogramme des fréquences : les abscisses des bâtons seront 1, 2, 3, ..., 26 : pour les obtenir, on
convertit le range(1,27) en une liste. Leurs hauteurs seront les fréquences. Leurs noms seront les
lettres de l’alphabet, dont on a fabriqué la liste alpha précédemment.
import matplotlib.pyplot as plt
abscisses=list(range(1,27))
plt.bar(abscisses, frequences)
plt.xticks(abscisses, alpha)
plt.show()
Codage par substitution
Pas de corrigé sur cette partie.
4
Téléchargement