Exercices d'entraînement☘
Ces exercices doivent être utilisés pour vous entraîner à programmer. Ils sont généralement accompagnés d'aide et de leur solution pour vous permettre de progresser.
Avant de vous précipiter sur ces solutions dès la première difficulté, n'oubliez pas les conseils suivants :
- Avez-vous bien fait un schéma au brouillon pour visualiser le problème posé ?
- Avez-vous essayé de rédiger un algorithme en français, avec vos propres mots, avant de vous lancer dans la programmation sur machine ?
- Avez-vous utilisé des affichages intermédiaires, des
print()
, pour visualiser au fur et à mesure le contenu des variables ? - Avez-vous testé le programme avec les propositions de tests donnés dans l'exercice ?
- Avez-vous testé le programme avec de nouveaux tests, différents de ceux proposés ?
Rappels
- Chaque programme Python doit être sauvegardé sous forme de fichier texte
avec l'extension
.py
.
Enregistrez ce fichier dans le dossier[C03-Securisation]
avec le nom donné à l'exercice :ProgC03.51.py
,ProgC03.52.py
, etc... - Pour exécuter ce programme, il suffit de le sauvegarder puis d'appuyer
sur la touche
[F5]
. - Le programme principal doit contenir un appel au module
doctest
:##----- Programme principal et tests -----## if __name__ == '__main__': import doctest doctest.testmod()
Jeux sérieux
Le concours Alkindi permet de s'initier et s'exercer à la cryptographie.
ProgC03.51 - Chiffrement de César☘
Vous allez appliquer le chiffrement de César à des messages constitués
uniquement de lettres majuscules. Pour cela, vous vous réfèrerez uniquement
à la chaîne alphabet
définie en constante globale du programme.
En d'autres termes, l'usage des fonctions chr()
et ord()
n'est pas
autorisé.
Copiez, collez et complétez la définition de la fonction crypt_cesar()
en respectant ses spécifications.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Une piste
Vous pouvez effectuer un pré-traitement en associant à chaque lettre majuscule la lettre correspondante après le décalage.
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
ProgC03.52 - Algorithme d'Euclide étendu☘
-
Sur votre cahier, recherchez deux entiers u et v tels que 287 u + 29 v = 1.
-
À partir de l'exercice réalisé en TD, complétez la définition de la fonction
euclide_etendu()
qui prend en paramètres deux entiersa
etb
. Cette fonction renvoie un triplet d'entiers(r, u, v)
tels que :r
est le PGCD dea
etb
;u
etv
sont deux entiers tels quea*u + b*v = r
1 2 3 4 5 6 7 8
def euclide_etendu(a, b): """ a, b - int, entiers strictement positifs Sortie: tuple - Triplet d'entiers (r, u, v) tels que r = pgcd(a, b) et a*u + b*v = r. >>> euclide_etendu(287, 29) (1, -10, 99) """
Une solution
Voici un exemple utilisant des dictionnaires, ce qui n'est pas nécessaire mais peut clarifier l'algorithme pour certains.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
def euclide_etendu(a, b): """ a, b - int, entiers strictement positifs Sortie: tuple - Triplet d'entiers (r, u, v) tels que r = pgcd(a, b) et a*u + b*v = r. >>> euclide_etendu(287, 29) (1, -10, 99) """ L1 = {'r': a, 'u': 1, 'v': 0} L2 = {'r': b, 'u': 0, 'v': 1} while L2['r'] != 0: q = L1['r']//L2['r'] r = L1['r']-q*L2['r'] u = L1['u']-q*L2['u'] v = L1['v']-q*L2['v'] L1['r'], L2['r'] = L2['r'], r L1['u'], L2['u'] = L2['u'], u L1['v'], L2['v'] = L2['v'], v return L1['r'], L1['u'], L1['v']
-
Proposez une version récursive de la fonction précédente :
1 2 3 4 5 6 7 8 9
def euclide_etendu_rec(a, b, u0=1, u1=0, v0=0, v1=1): """ a, b - int, entiers strictement positifs u0, u1, v0, v1 - int Sortie: tuple - Triplet d'entiers (r, u, v) tels que r = pgcd(a, b) et a*u + b*v = r. >>> euclide_etendu_rec(287, 29) (1, -10, 99) """
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
def euclide_etendu_rec(a, b, u0=1, u1=0, v0=0, v1=1): """ a, b - int, entiers strictement positifs u0, u1, v0, v1 - int Sortie: tuple - Triplet d'entiers (r, u, v) tels que r = pgcd(a, b) et a*u + b*v = r. >>> euclide_etendu_rec(287, 29) (1, -10, 99) """ if b == 0: return a, u0, v0 else: q = a//b r = a%b return euclide_etendu_rec(b, r, u1, u0-q*u1, v1, v0-q*v1)
ProgC03.53 - Chiffrement symétrique ECB☘
Pour renforcer les algorithmes de chiffrement symétrique, on peut utiliser le mode opératoire ECB (Electronic CodeBook) : le message est découpé en blocs puis on utilise l'algorithme de chiffrement sur chaque bloc.
Chiffrement par permutation☘
Une permutation peut-être représentée par un tableau contenant les
n
entiers de 0
à n-1
. L'élément de valeur j
et d'indice i
sera
ainsi placé à l'indice j
après la permutation.
Exemple
Le tableau permut = [2, 0, 3, 1]
signifie que, pour un bloc de taille 4 :
- l'élément d'indice 0 ira à l'indice 2 ;
- l'élément d'indice 1 ira à l'indice 0 ;
- etc...
-
On considère le bloc
LISE
de taille 4.
Quel bloc obtient-on après application de la permutation représentée par le tableaupermut = [2, 0, 3, 1]
?Une réponse
On obtient le bloc
IELS
. -
Comment retrouver le bloc initial ?
Une réponse
En appliquant la permutation réciproque, c'est-à-dire la permutation représentée parle tableau
permut_rec = [1, 3, 0, 2]
. -
Copiez, collez et complétez la définition de la fonction
creer_permut()
en respectant ses spécifications.1 2 3 4 5
def creer_permut(n): """ n - int, entier strictement positif Sortie: list - tableau contenant les entiers de 0 à n-1 permutés aléatoirement """
Exemple de test
>>> creer_permut(7) [1, 3, 0, 4, 5, 2, 6] >>> creer_permut(7) [6, 4, 1, 0, 2, 5, 3] >>> creer_permut(7) [5, 2, 3, 6, 0, 1, 4]
Une piste
Pensez à importer un module permettant de simuler du hasard...
Une solution
1 2 3 4 5 6 7 8 9 10
from random import shuffle def creer_permut(n): """ n - int, entier strictement positif Sortie: list - tableau contenant les entiers de 0 à n-1 permutés aléatoirement """ tab = [i for i in range(n)] shuffle(tab) return tab
-
Copiez, collez et complétez la définition de la fonction
creer_permut_inverse()
en respectant ses spécifications.1 2 3 4 5 6 7 8 9
def creer_permut_inverse(tab): """ tab - list, tableau de taille n représentant une permutation des entiers de 0 à n-1 Sortie: list - tableau correspondant à la permutation réciproque >>> permut = [2, 0, 3, 1] >>> depermut = creer_permut_inverse(permut) >>> depermut [1, 3, 0, 2] """
Exemple de test
>>> permut = creer_permut(7) >>> permut [4, 1, 0, 3, 5, 6, 2] >>> creer_permut_inverse(permut) [2, 1, 6, 3, 0, 4, 5]
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13
def creer_permut_inverse(tab): """ tab - list, tableau de taille n représentant une permutation des entiers de 0 à n-1 Sortie: list - tableau correspondant à la permutation réciproque >>> permut = [2, 0, 3, 1] >>> depermut = creer_permut_inverse(permut) >>> depermut [1, 3, 0, 2] """ result = [0 for i in range(len(tab))] for i in range(len(tab)): result[tab[i]] = i return result
-
Copiez, collez et complétez la définition de la fonction
permut_bloc()
en respectant ses spécifications.1 2 3 4 5 6 7 8 9 10 11
def permut_bloc(permut, bloc): """ permut - list, tableau de taille n représentant une permutation des entiers de 0 à n-1 bloc - list, tableau contenant n, où n est la taille de permut Sortie: list - tableau contenant les mêmes éléments que bloc, mais permutés selon la permutation représentée par permut >>> permut = [2, 0, 3, 1] >>> bloc = ['L', 'I', 'S', 'E'] >>> permut_bloc(permut, bloc) ['I', 'E', 'L', 'S'] """
Exemple de test
Attention de bien lever l'erreur d'assertion :
>>> permut = [2, 0, 3, 1] >>> bloc = ['I', 'E', 'L', 'S'] >>> depermut = creer_permut_inverse(permut) >>> depermut [1, 3, 0, 2] >>> permut_bloc(depermut, bloc) ['L', 'I', 'S', 'E'] >>> permut_bloc([2, 1, 6, 3, 0, 4, 5], ['L', 'I', 'S', 'E']) AssertionError: Les deux tableaux n'ont pas la même taille
Une piste
Vous pouvez initialiser un « tableau de
None
».Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
def permut_bloc(permut, bloc): """ permut - list, tableau de taille n représentant une permutation des entiers de 0 à n-1 bloc - list, tableau contenant n, où n est la taille de permut Sortie: list - tableau contenant les mêmes éléments que bloc, mais permutés selon la permutation représentée par permut >>> permut = [2, 0, 3, 1] >>> bloc = ['L', 'I', 'S', 'E'] >>> permut_bloc(permut, bloc) ['I', 'E', 'L', 'S'] """ assert len(bloc) == len(permut), "Les deux tableaux n'ont pas la même taille" result = [None for i in range(len(permut))] for i in range(len(permut)): result[permut[i]] = bloc[i] return result
Chiffrement ECB☘
-
Copiez, collez et complétez la définition de la fonction
decoupe_en_blocs()
en respectant ses spécifications.1 2 3 4 5 6 7 8
def decoupe_en_blocs(message, n): """ message - str, le message à découper en blocs n - int, entier strictement positif - longueur d'un bloc Sortie: list - tableau de chaînes de caractères de taille n, éventuellement complétées par un caractère aléatoire de code décimal ASCII compris entre 32 et 126 ou entre 161 et 255 (92 et 173 exclus). """
Exemple de test
>>> message = "Ceci est un exemple !" >>> decoupe_en_blocs(message, 5) ['Ceci ', 'est u', 'n exe', 'mple ', '!+4d7'] >>> decoupe_en_blocs(message, 5) ['Ceci ', 'est u', 'n exe', 'mple ', '!kY3|']
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
def decoupe_en_blocs(message, n): """ message - str, le message à découper en blocs n - int, entier strictement positif - longueur d'un bloc Sortie: list - tableau de chaînes de caractères de taille n, éventuellement complétées par un caractère aléatoire de code décimal ASCII compris entre 32 et 126 (92 exclus). """ result = [] for i in range(0, len(message), n): temp = "" for k in range(n): if i+k < len(message): temp += message[i+k] else: code = randint(32, 126) while code == 92: code = randint(32, 126) temp += chr(code) result.append(temp) return result
-
Copiez, collez et complétez la définition de la fonction
permute_chaque_bloc()
en respectant ses spécifications.
Cette fonction devra lever une assertion si la longueur des blocs ne correspond pas à la longueur de la permutation (cf. exemple).1 2 3 4 5 6 7 8 9 10 11
def permute_chaque_bloc(tab, permut): """ tab - list, tableau de chaînes de caractères de taille n permut - list, tableau de taille n représentant une permutation des entiers de 0 à n-1 Sortie: list - tableau contenant les éléments de tab. Chaque chaîne de caractères voit caractères mélangés selon la permutation représentée par permut >>> tab = ['Ceci ', 'est u', 'n exe', 'mple ', '!+4d7'] >>> permute_chaque_bloc(tab, [2, 4, 0, 3, 1]) ['c Cie', 'tue s', 'eenx ', 'l mep', '47!d+'] """
Exemple de test
>>> tab = ['Ceci ', 'est u', 'n exe', 'mple ', '!+4d7'] >>> permute_chaque_bloc(tab, [2, 0, 3, 1]) AssertionError: La longueur des chaînes ne correspond pas à la longeur de la permutation
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
def permute_chaque_bloc(tab, permut): """ tab - list, tableau de chaînes de caractères de taille n permut - list, tableau de taille n représentant une permutation des entiers de 0 à n-1 Sortie: list - tableau contenant les éléments de tab. Chaque chaîne de caractères voit caractères mélangés selon la permutation représentée par permut >>> tab = ['Ceci ', 'est u', 'n exe', 'mple ', '!+4d7'] >>> permute_chaque_bloc(tab, [2, 4, 0, 3, 1]) ['c Cie', 'tue s', 'eenx ', 'l mep', '47!d+'] """ assert len(tab[0]) == len(permut), "La longueur des chaînes ne correspond pas à la longeur de la permutation" result = [] for mot in tab: temp = list(mot) temp = permut_bloc(permut, temp) mot = "" for carac in temp: mot += carac result.append(mot) return result
-
Copiez, collez et complétez la définition de la fonction
crypte_ECB()
en faisant appel aux fonctions précédentes et en respectant ses spécifications.1 2 3 4 5
def crypte_ECB(message, permut): """ message - str, le message à découper en blocs permut - list, tableau de taille n représentant une permutation des entiers de 0 à n-1 Sortie: str - message crypté par le chiffrement ECB
Exemple de test
>>> message = "Ceci est un exemple !" >>> crypte_ECB(message, [2, 4, 0, 3, 1]) 'c Cietue seenx l mep32!1H'
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13
def crypte_ECB(message, permut): """ message - str, le message à découper en blocs permut - list, tableau de taille n représentant une permutation des entiers de 0 à n-1 Sortie: str - message crypté par le chiffrement ECB """ n = len(permut) tab = decoupe_en_blocs(message, n) tab_crypte = permute_chaque_bloc(tab, permut) message_crypte = "" for mot in tab_crypte: message_crypte += mot return message_crypte
-
Copiez, collez et complétez la définition de la fonction
decrypte_ECB()
en faisant appel aux fonctions précédentes et en respectant ses spécifications.1 2 3 4 5 6 7 8 9
def decrypte_ECB(message_crypte, permut): """ message_crypte - str, le message à décrypter permut - list, tableau de taille n représentant une permutation des entiers de 0 à n-1 Sortie: str - message décrypté par le déchiffrement ECB >>> message_crypte = 'c Cietue seenx l mep32!1H' >>> decrypte_ECB(message_crypte, [2, 4, 0, 3, 1]) 'Ceci est un exemple !H312' """
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
def decrypte_ECB(message_crypte, permut): """ message_crypte - str, le message à décrypter permut - list, tableau de taille n représentant une permutation des entiers de 0 à n-1 Sortie: str - message décrypté par le déchiffrement ECB >>> message_crypte = 'c Cietue seenx l mep32!1H' >>> decrypte_ECB(message_crypte, [2, 4, 0, 3, 1]) 'Ceci est un exemple !H312' """ n = len(permut) permut_inverse = creer_permut_inverse(permut) tab_crypte = decoupe_en_blocs(message_crypte, n) tab_decrypte = permute_chaque_bloc(tab_crypte, permut_inverse) message_decrypte = "" for mot in tab_decrypte: message_decrypte += mot return message_decrypte
-
Vous recevez un message crypté et vous savez que le chiffrement utilisé est ECB. Quelle(s) méthode(s) pourriez-vous utiliser pour décrypter de message ?
Appelez l'enseignant pour lui faire part de cette méthode avant de vous précipiter sur la solution...Une réponse
-
On commence par déterminer la longueur de la permutation qui est un diviseur de la longueur du message.
-
Pour chaque longueur possible, on extrait les deux ou trois premiers blocs de même longueur dans le message et on leur applique chacune des permutations possibles pour cette longueur.
-
Pour aller plus loin☘
On considère un message comportant des caractères aléatoires dont la représentation ASCII décimale est comprise entre 32 et 126.
Convertissez en premier lieu chaque caractère de ce message par l'octet
correspondant (chaîne de '0'
et de '1'
) puis appliquez les fonctions
de cryptage et décryptage à cette chaîne de bits plutôt qu'à la chaîne de
caractères.