Un corrigé pédagogique pour comprendre et justifier vos réponses. Les conseils de rédaction ne constituent pas un barème officiel détaillé.
Exercice 1 · 6 points
Maillage RIP/OSPF et calculs d’adresses IPv4
Partie A : choisir une route
Le cœur de réseau contient six routeurs. Derrière R1 à R5, un switch distribue le LAN correspondant ; R6 relie le maillage à Internet. Les débits des liens sont indiqués sur la figure.
Lire les connexions du schéma
- R1 / LAN1 relié à R2 / LAN2 : 10
- R1 / LAN1 relié à R4 / LAN4 : 1
- R1 / LAN1 relié à R5 / LAN5 : 1
- R2 / LAN2 relié à R3 / LAN3 : 100
- R2 / LAN2 relié à R4 / LAN4 : 10
- R2 / LAN2 relié à R5 / LAN5 : 10
- R3 / LAN3 relié à R4 / LAN4 : 100
- R4 / LAN4 relié à R5 / LAN5 : 100
- R5 / LAN5 relié à R6 / Internet : 100
RIP minimise le nombre de routeurs intermédiaires selon la convention de la table de ce sujet. OSPF minimise la somme des coûts des liaisons.
| Destination | Prochain saut | Distance |
|---|---|---|
| R2 | R2 | 0 |
| R3 | R2 | 1 |
| R4 | … | … |
| R5 | … | … |
| R6 | … | … |
Question 1
#Compléter la table RIP de R1 : destination, prochain saut et distance.
Comprendre la correction
| Destination | Prochain saut | Distance du sujet |
|---|---|---|
| R2 | R2 | 0 |
| R3 | R2 | 1 |
| R4 | R4 | 0 |
| R5 | R5 | 0 |
| R6 | R5 | 1 |
R4 et R5 sont directement reliés à R1 ; R6 se rejoint via R5. On conserve la convention de l’énoncé, qui attribue 0 à un voisin direct et 1 à une destination atteinte après un intermédiaire. Si l’on compte les liaisons franchies plutôt que les intermédiaires, ces nombres augmentent de 1, mais les meilleurs chemins restent les mêmes.
Question 2
#Un utilisateur de LAN1 interroge un serveur Internet depuis son navigateur. Détailler la route suivie dans le maillage selon RIP.
Comprendre la correction
R1 → R5 → R6 → Internet. Seul R6 possède la sortie Internet ; l’atteindre via le voisin direct R5 minimise les intermédiaires. RIP ne tient pas compte ici du faible débit du lien R1-R5.
Le coût est 100 / débit, débit en Mbit/s. Exemple R1-R2 : 100 / 10 = 10. Les coûts directs depuis R1 sont 10 vers R2, 100 vers R4 et 100 vers R5. La ligne R2 de la table OSPF est fournie : prochain saut R2, distance 10.
Question 3
#Compléter la table OSPF de R1, où la distance est la somme des coûts des liaisons.
Indice
Un détour sur plusieurs liens rapides peut coûter moins qu’un lien direct.
Comprendre la correction
| Destination | Prochain saut | Distance | Chemin choisi |
|---|---|---|---|
| R2 | R2 | 10 | R1-R2 |
| R3 | R2 | 11 | R1-R2-R3 |
| R4 | R2 | 12 | R1-R2-R3-R4 |
| R5 | R2 | 13 | R1-R2-R3-R4-R5 |
| R6 | R2 | 14 | R1-R2-R3-R4-R5-R6 |
Un lien à 100 Mbit/s coûte 1 ; à 10 Mbit/s, 10 ; à 1 Mbit/s, 100. Le détour par plusieurs liens rapides évite les liens directs très coûteux. Le prochain saut est toujours R2, même lorsque la destination finale est un autre routeur.
Question 4
#Donner la route vers Internet depuis LAN1 selon OSPF.
Comprendre la correction
R1 → R2 → R3 → R4 → R5 → R6 → Internet. Le coût dans le maillage vaut 10 + 1 + 1 + 1 + 1 = 14, contre 101 pour R1-R5-R6. OSPF accepte davantage de sauts si leur coût total est plus petit.
Question 5
#R2 tombe en panne. Donner la nouvelle route OSPF de LAN1 vers Internet et sa distance.
Comprendre la correction
R1 → R5 → R6, de coût 100 + 1 = 101. L’autre départ disponible, R1-R4-R5-R6, coûte 102. Aucun chemin ne peut utiliser le routeur en panne ; on retire donc R2 et toutes ses liaisons avant de comparer.
Partie B : DHCP et préfixes
Un serveur DHCP attribue des paramètres IPv4. Le sigle développé correctement est Dynamic Host Configuration Protocol ; le document imprime « Control ». Une IPv4 a quatre octets. En notation CIDR /n, les n premiers bits définissent le réseau ; le masque contient n bits à 1 suivis de zéros. Le ET bit à bit entre une adresse et le masque donne l’adresse réseau.
192 : 11000000
255 : 11111111
ET : 11000000 = 192| Tableau à compléter | Octet 1 | Octet 2 | Octet 3 | Octet 4 |
|---|---|---|---|---|
| Machine | 11000000 | 10101000 | … | … |
| Masque | 11111111 | 11000000 | 00000000 | 00000000 |
| Réseau binaire | 11000000 | … | … | … |
| Réseau décimal | 192 | … | … | … |
Question 6
#Compléter le tableau pour la machine 192.168.1.100 et le masque 255.192.0.0 afin de déterminer l’adresse réseau.
Comprendre la correction
| Donnée | Octet 1 | Octet 2 | Octet 3 | Octet 4 |
|---|---|---|---|---|
| Machine binaire | 11000000 | 10101000 | 00000001 | 01100100 |
| Masque binaire | 11111111 | 11000000 | 00000000 | 00000000 |
| Réseau binaire | 11000000 | 10000000 | 00000000 | 00000000 |
| Réseau décimal | 192 | 128 | 0 | 0 |
Le réseau est 192.128.0.0. Au deuxième octet, seuls les deux bits de poids fort sont conservés par le masque : 10101000 AND 11000000 = 10000000. Ce masque correspond à /10, et ne se traite donc pas comme un /24.
Complémenter un octet échange ses 0 et ses 1. L’adresse de diffusion est le OU bit à bit entre l’adresse réseau et le complément du masque. Exemple :
12 : 00001100
9 : 00001001
OU : 00001101 = 13Question 7
#Compléter le tableau de broadcast du réseau 192.128.0.0 de masque 255.192.0.0.
Comprendre la correction
| Donnée | Octet 1 | Octet 2 | Octet 3 | Octet 4 |
|---|---|---|---|---|
| Réseau | 11000000 | 10000000 | 00000000 | 00000000 |
| Masque | 11111111 | 11000000 | 00000000 | 00000000 |
| Complément | 00000000 | 00111111 | 11111111 | 11111111 |
| Broadcast | 11000000 | 10111111 | 11111111 | 11111111 |
| Décimal | 192 | 191 | 255 | 255 |
Le broadcast est 192.191.255.255. Le complément du masque met à 1 tous les bits hôtes ; le OU les ajoute au préfixe réseau conservé.
Question 8
#Une machine reçoit 172.16.1.100, masque 255.255.0.0, passerelle 172.16.255.254. Donner l’adresse de LAN1, le broadcast et le nombre d’adresses distribuables, sans restrictions administratives supplémentaires.
Comprendre la correction
| Information | Résultat |
|---|---|
| Réseau | 172.16.0.0/16 |
| Broadcast | 172.16.255.255 |
| Adresses du bloc | 65536 |
| Adresses hôtes utilisables | 65534 |
| Adresses restantes si la passerelle donnée est déjà réservée | 65533 |
Il reste 16 bits hôtes, donc 2¹⁶ = 65536 combinaisons. Réseau et diffusion ne s’attribuent pas à une machine, d’où 65534 adresses hôtes. La passerelle occupe déjà l’une d’elles : un pool DHCP qui doit l’exclure en conserve au plus 65533, avant d’éventuelles autres réservations. Le nombre habituellement attendu pour la capacité du sous-réseau est 65534 ; préciser ce qui est compté évite de redistribuer l’adresse de la passerelle.
split sépare une chaîne : 'ab-pq-rs'.split('-') donne ['ab','pq','rs']. join assemble des chaînes : '.'.join(['ab','pq','rs']) donne 'ab.pq.rs'. L’opérateur & réalise un ET bit à bit entre entiers : 192 & 255 = 192.
class IPv4(object):
def __init__(self, adresse: str):
self.adresse = adresse
def octets(self) -> list[int]:
return [int(i) for i in self.adresse.split('.')]def masquer(self, masque: str) -> str:
# IPv4("192.168.1.100").masquer("255.192.0.0") donne "192.128.0.0"
tmp = []
ip = self.octets()
crible = IPv4(masque).octets()
for i in range(4):
tmp.append(... & ...)
return ".".join(...)Question 9
#Compléter les lignes 15 et 16 de masquer pour appliquer le masque et renvoyer le résultat décimal pointé.
Indice
Les quatre opérations ET donnent des entiers ; join attend des chaînes.
Comprendre la correction
def masquer(self, masque: str) -> str:
tmp = []
ip = self.octets()
crible = IPv4(masque).octets()
for i in range(4):
tmp.append(ip[i] & crible[i])
return '.'.join([str(octet) for octet in tmp])Le ET porte sur les entiers correspondant au même octet. join attend des chaînes : on convertit donc chaque résultat avec str. Pour 192.168.1.100 et 255.192.0.0, les entiers calculés sont 192,128,0,0.
def adresse_suivante(self, adresse_max: str) -> str:
# 192.168.1.100 -> 192.168.1.101
# 192.168.1.255 -> 192.168.2.0 si la borne est 192.168.255.254
assert self.adresse < adresse_max
liste_courante = self.octets()
liste_suivante = list()
retenue = 1
for index in range(4):
somme = liste_courante[3 - index] + retenue
valeur, retenue = ..., ...
liste_suivante = ...
return '.'.join(liste_suivante)Question 10
#Compléter les lignes 19 et 20 de adresse_suivante, qui calcule l’adresse immédiatement suivante sous une borne maximale.
Indice
256 est la base de chaque octet : reste pour la valeur, quotient pour la retenue.
Comprendre la correction
def adresse_suivante(self, adresse_max: str) -> str:
assert self.adresse < adresse_max
liste_courante = self.octets()
liste_suivante = []
retenue = 1
for index in range(4):
somme = liste_courante[3 - index] + retenue
valeur, retenue = somme % 256, somme // 256
liste_suivante = [str(valeur)] + liste_suivante
return '.'.join(liste_suivante)Une IPv4 se comporte comme un nombre à quatre chiffres en base 256. On ajoute 1 en partant du dernier octet. Le reste modulo 256 donne l’octet résultat, le quotient entier donne la retenue. On ajoute chaque nouvelle chaîne devant la liste puisque les octets sont calculés de droite à gauche.
Limite de l’assertion fournie : comparer les adresses décimales comme des chaînes ne compare pas correctement leurs valeurs numériques. Par exemple, '192.168.1.9' < '192.168.1.10' est faux. Pour rendre la borne fiable, remplacer la ligne 13 par assert self.octets() < IPv4(adresse_max).octets(). Le calcul des retenues, lui, est correct. L’exemple .255 → octet suivant .0 est un successeur numérique, pas une promesse que cette adresse est attribuable sur tout masque.
Choisissez une route et coupez un routeurUn atelier pour expérimenter
Sélectionnez RIP ou OSPF, puis simulez la panne de R2. L’atelier énumère les chemins simples vers R6 et visualise celui de coût minimal.
Lire le résultat de l’expérience initiale
R1 → R2 → R3 → R4 → R5 → R6
RIP et OSPF évaluent les mêmes chemins avec des critères différents. Le prochain saut est seulement le premier routeur après R1.
| Chemin | Coût OSPF | Intermédiaires |
|---|---|---|
| R1 → R2 → R3 → R4 → R5 → R6 | 14 | 4 |
| R1 → R2 → R5 → R6 | 21 | 2 |
| R1 → R2 → R4 → R5 → R6 | 22 | 3 |
| R1 → R5 → R6 | 101 | 1 |
| R1 → R4 → R5 → R6 | 102 | 2 |
| R1 → R4 → R3 → R2 → R5 → R6 | 113 | 4 |
| R1 → R4 → R2 → R5 → R6 | 121 | 3 |
Le critère de distance doit être annoncé avant les calculs. Une panne retire les sommets et liaisons concernés, puis impose de recalculer les routes.
Exercice 2 · 6 points
Résoudre le baguenaudier par récursivité mutuelle
Le baguenaudier est une réglette de n cases numérotées 1 àn. Une case est vide ou contient un pion. Jouer une case inverse cet état. Initialement toutes sont vides ; le but est de toutes les remplir.
- On joue une seule case à la fois et elle contient au plus un pion.
- On peut toujours jouer la case 1.
- Si le jeu n’est ni vide ni rempli, on peut aussi jouer la case qui suit la première case remplie.
- Aucune autre case n’est jouable.
| Figure 1 | Case 1 | Case 2 | Case 3 | Case 4 | Case 5 |
|---|---|---|---|---|---|
| État | ○ | ○ | ● | ● | ○ |
Dans cet exemple, on peut remplir 1 ou vider 4. Le tableau Python correspondant est [False, False, True, True, False]. Les indices Python commencent à 0, contrairement aux numéros de cases du jeu.
Question 1
#Un baguenaudier à 4 cases a ses trois dernières cases remplies. Désigner les cases jouables et dessiner l’état après chacun des coups possibles.
Comprendre la correction
| Situation | Case 1 | Case 2 | Case 3 | Case 4 |
|---|---|---|---|---|
| Départ | ○ | ● | ● | ● |
| Jouer 1 | ● | ● | ● | ● |
| Jouer 3 | ○ | ● | ○ | ● |
On peut jouer les cases 1 et 3. La première remplie est 2, donc son successeur est 3. En Python, ces coups auront pour indices 0 et 2.
Question 2
#Écrire initialiser(n), qui renvoie un baguenaudier vide. Exemple initialiser(3) : [False, False, False].
Comprendre la correction
def initialiser(n):
return [False for _ in range(n)]Chaque case est un booléen faux. Une compréhension crée exactement n positions ; [False] * n conviendrait aussi puisque les booléens sont immuables.
def victoire(tab):
for etat_case in tab:
if etat_case == ...:
return ...
...Question 3
#Compléter les lignes 3,4 et 5 de victoire, qui indique si toutes les cases sont remplies.
Comprendre la correction
def victoire(tab):
for etat_case in tab:
if etat_case == False:
return False
return TrueDès qu’une case vide est rencontrée, la victoire est impossible et on renvoie faux. Le retour vrai doit attendre la fin de la boucle. Pour une liste vide, cette définition renvoie vrai par convention logique ; le jeu physique suppose normalement n positif.
indice_premiere_case_occupee([True, True, True]) # 0
indice_premiere_case_occupee([False, False, False, True, True]) # 3
indice_premiere_case_occupee([False, False, False, False]) # NoneQuestion 4
#Écrire indice_premiere_case_occupee(tab), qui renvoie le premier indice rempli ou None si tout est vide.
Comprendre la correction
def indice_premiere_case_occupee(tab):
for i in range(len(tab)):
if tab[i]:
return i
return NoneOn parcourt les indices de gauche à droite et on retourne immédiatement le premier vrai. Si aucun n’est trouvé, le retour est None. L’indice 0 est une réponse valide : il ne doit pas être confondu avec l’absence de résultat.
Question 5
#Écrire coup_valide(tab, case), qui vérifie les règles et que l’indice est un entier entre 0 inclus et la longueur exclue.
PythonAutoriser le bon anneau du baguenaudierÉcrivez votre solution et mettez-la à l’épreuve
Écrivez coup_valide(tab, case). Le plateau est une liste de booléens, True pour une case occupée. Un indice entier valide vaut de 0 à len(tab)-1. La case 0 peut toujours être changée. Une autre case peut être changée si elle suit immédiatement la première case occupée et si le plateau n’est pas entièrement occupé. Les fonctions indice_premiere_case_occupee et victoire sont fournies. Le plateau ne doit pas être modifié.
def coup_valide(tab, case):
# À vous de jouer
passLes cas de test proposés :
- La case zéro reste accessible : Les autres restrictions concernent les cases suivantes, pas la case zéro.
- Un premier pion à l’indice zéro : L’indice 0 n’est pas l’absence de pion : 1 est la case autorisée.
- Des cases vides avant le premier pion : Il faut trouver le premier
True, puis viser exactement la position suivante. - Pas de pion ou un plateau plein : Sans premier pion, aucune case après zéro ne convient ; le plateau plein est un autre cas explicitement exclu.
- Bornes et types invalides : Un indice flottant, un booléen ou une chaîne ne satisfait pas le contrat d’un véritable entier d’indice.
- La vérification ne joue pas le coup : La modification du plateau appartient à
changer_case, pas àcoup_valide.
Indice 1
Contrôlez les bornes avant toute lecture de tab[case].
Indice 2
La première case remplie peut avoir l’indice 0 : comparez explicitement à None.
Comprendre la correction
def coup_valide(tab, case):
if type(case) is not int or case < 0 or case >= len(tab):
return False
if case == 0:
return True
premier = indice_premiere_case_occupee(tab)
return premier is not None and not victoire(tab) and case == premier + 1On contrôle les bornes avant de manipuler la case. L’indice 0 est toujours autorisé s’il existe. Pour les autres indices, il faut un premier pion, un plateau pas déjà plein, et viser exactement la position suivante.
Tester premier is not None est nécessaire : un premier pion d’indice 0 autorise précisément l’indice 1. Un test if premier le rejetterait par erreur. Le test de type exclut aussi les booléens, qui sont des sous-types d’entier en Python.
changer_case([False, False, False], 1) # [False, False, False]
changer_case([False, False, False], 0) # [True, False, False]
changer_case([False, True, False], 2) # [False, True, True]Question 6
#Écrire changer_case(tab, case). Un coup valide inverse la case et renvoie le plateau ; un coup invalide renvoie le plateau sans modification.
Comprendre la correction
def changer_case(tab, case):
if coup_valide(tab, case):
tab[case] = not tab[case]
return tabnot inverse directement un booléen. Le tableau est modifié en place uniquement dans la branche valide, puis le même objet est retourné. La fonction n’a pas à dupliquer les règles de légalité : elle réutilise coup_valide.
Les figures 2 et 3 donnent les suites d’états suivantes, à lire de haut en bas :
| Étape | Figure 2 :3 cases | Figure 3 :4 cases |
|---|---|---|
| 0 | ○ | ○ | ○ | ○ | ○ | ○ | ○ |
| 1 | ● | ○ | ○ | ● | ○ | ○ | ○ |
| 2 | ● | ● | ○ | ● | ● | ○ | ○ |
| 3 | ○ | ● | ○ | ○ | ● | ○ | ○ |
| 4 | ○ | ● | ● | ○ | ● | ● | ○ |
| 5 | ● | ● | ● | ● | ● | ● | ○ |
| 6 | ● | ○ | ● | ○ | |
| 7 | ○ | ○ | ● | ○ | |
| 8 | ○ | ○ | ● | ● | |
| 9 | ● | ○ | ● | ● | |
| 10 | ● | ● | ● | ● |
Les fonctions récursives vider et remplir affichent les actions sur les n premières cases. À partir d’ici, les cases sont numérotées 1 à n. vider(0) et remplir(0) ne font rien.
def vider(n):
if n == 1:
print('Vider case 1')
elif n > 1:
vider(n - 2)
print(...)
remplir(n - 2)
vider(n - 1)Question 7
#Compléter la ligne 6 de vider pour afficher « Vider case » suivi de n.
Comprendre la correction
print('Vider case ' + str(n))On convertit l’entier en chaîne avant la concaténation. L’alternative print('Vider case', n) donne également le bon affichage grâce à l’espace ajouté par print.
Question 8
#Sachant que remplir(1) affiche « Remplir case 1 » et que remplir(0) ne fait rien, donner les affichages de vider(3).
Comprendre la correction
Vider case 1
Vider case 3
Remplir case 1
Vider case 2
Vider case 1L’appel commence par vider(1), puis vide 3, rétablit 1 et termine avec vider(2). Ce dernier ne produit rien pour ses appels d’argument 0, puis affiche vider 2 et vider 1. On doit suivre l’ordre d’exécution, pas seulement lister les appels directs de vider 3.
Question 9
#En vous inspirant de vider et des résolutions données, écrire la fonction récursive remplir.
Indice 1
Inversez les opérations de vider et l’ordre dans lequel elles sont faites.
Indice 2
Avant de jouer la case n, il faut que la première case occupée soit n-1.
Comprendre la correction
def remplir(n):
if n == 1:
print('Remplir case 1')
elif n > 1:
remplir(n - 1)
vider(n - 2)
print('Remplir case ' + str(n))
remplir(n - 2)Pour remplir n, on commence par remplir les n-1 premières cases. On vide ensuite les n-2 premières : la première case occupée devient n-1 et autorise alors n. On remplit n, puis on restaure les n-2 premières. C’est l’inversion, dans l’ordre inverse, des opérations de vider.
Les fonctions manipulent des préfixes d’un plateau plus grand : « vider les deux premières cases » ne signifie pas que le plateau global a seulement deux cases. Cette distinction permet de relier leurs appels récursifs aux règles du jeu.
Question 10
#La fonction vider est-elle adaptée à 2000 cases ? Justifier.
Comprendre la correction
Non. Le nombre d’actions suit T(n) = T(n - 1) + 2T(n - 2) + 1, avec T(0)=0 et T(1)=1. Il croît exponentiellement, de l’ordre de 2ⁿ. Les premières valeurs 1,2,5,10,21 montrent déjà la croissance ; à 2000, afficher la solution est irréaliste.
En plus, la profondeur de récursion croît avec n et peut dépasser la limite de Python. Augmenter cette limite ou écrire une version itérative n’éliminerait pas le nombre astronomique de coups à produire.
Jouez une séquence et voyez où elle bloqueUn atelier pour expérimenter
Entrez les numéros des cases à jouer, séparés par des espaces ou virgules. Le plateau démarre vide. L’atelier exécute chaque coup autorisé, explique les refus et indique les prochains coups possibles.
Lire le résultat de l’expérience initiale
Toutes les cases sont remplies !
Les numéros saisis commencent à1 ; le code Python utilise des indices à partir de0. Un coup refusé ne change aucune case.
| Case demandée | Validité | Plateau après le coup | Explication |
|---|---|---|---|
| 1 | Joué | ● ○ ○ | La case a changé d’état |
| 2 | Joué | ● ● ○ | La case a changé d’état |
| 1 | Joué | ○ ● ○ | La case a changé d’état |
| 3 | Joué | ○ ● ● | La case a changé d’état |
| 1 | Joué | ● ● ● | La case a changé d’état |
Un appel récursif résout une partie du plateau pour rendre possible le coup décisif, puis rétablit les cases temporairement vidées. Le détour fait partie de la solution.
Revoir les notions de cet exercice
Exercice 3 · 8 points
Gestionnaire de mots de passe : génération, SQL, XOR et droits Linux
Alice crée un gestionnaire pour rassembler ses informations de connexion. Cette étude reprend un modèle d’examen ; les programmes de génération et de chiffrement servent à comprendre leurs mécanismes et leurs limites.
Partie A : générer des mots de passe
La figure 1 donne les codes ASCII décimaux et hexadécimaux. Les caractères spéciaux encadrés sont exactement ceux des plages 33 à 47 et 58 à 64.
| Décimal | Hexadécimal | Caractère | Catégorie |
|---|---|---|---|
| 32 | 20 | SP | Non retenu dans gen_mdp |
| 33 | 21 | ! | Spécial autorisé |
| 34 | 22 | " | Spécial autorisé |
| 35 | 23 | # | Spécial autorisé |
| 36 | 24 | $ | Spécial autorisé |
| 37 | 25 | % | Spécial autorisé |
| 38 | 26 | & | Spécial autorisé |
| 39 | 27 | ' | Spécial autorisé |
| 40 | 28 | ( | Spécial autorisé |
| 41 | 29 | ) | Spécial autorisé |
| 42 | 2A | * | Spécial autorisé |
| 43 | 2B | + | Spécial autorisé |
| 44 | 2C | , | Spécial autorisé |
| 45 | 2D | - | Spécial autorisé |
| 46 | 2E | . | Spécial autorisé |
| 47 | 2F | / | Spécial autorisé |
| 48 | 30 | 0 | Non retenu dans gen_mdp |
| 49 | 31 | 1 | Non retenu dans gen_mdp |
| 50 | 32 | 2 | Non retenu dans gen_mdp |
| 51 | 33 | 3 | Non retenu dans gen_mdp |
| 52 | 34 | 4 | Non retenu dans gen_mdp |
| 53 | 35 | 5 | Non retenu dans gen_mdp |
| 54 | 36 | 6 | Non retenu dans gen_mdp |
| 55 | 37 | 7 | Non retenu dans gen_mdp |
| 56 | 38 | 8 | Non retenu dans gen_mdp |
| 57 | 39 | 9 | Non retenu dans gen_mdp |
| 58 | 3A | : | Spécial autorisé |
| 59 | 3B | ; | Spécial autorisé |
| 60 | 3C | < | Spécial autorisé |
| 61 | 3D | = | Spécial autorisé |
| 62 | 3E | > | Spécial autorisé |
| 63 | 3F | ? | Spécial autorisé |
| 64 | 40 | @ | Spécial autorisé |
| 65 | 41 | A | Majuscule autorisée |
| 66 | 42 | B | Majuscule autorisée |
| 67 | 43 | C | Majuscule autorisée |
| 68 | 44 | D | Majuscule autorisée |
| 69 | 45 | E | Majuscule autorisée |
| 70 | 46 | F | Majuscule autorisée |
| 71 | 47 | G | Majuscule autorisée |
| 72 | 48 | H | Majuscule autorisée |
| 73 | 49 | I | Majuscule autorisée |
| 74 | 4A | J | Majuscule autorisée |
| 75 | 4B | K | Majuscule autorisée |
| 76 | 4C | L | Majuscule autorisée |
| 77 | 4D | M | Majuscule autorisée |
| 78 | 4E | N | Majuscule autorisée |
| 79 | 4F | O | Majuscule autorisée |
| 80 | 50 | P | Majuscule autorisée |
| 81 | 51 | Q | Majuscule autorisée |
| 82 | 52 | R | Majuscule autorisée |
| 83 | 53 | S | Majuscule autorisée |
| 84 | 54 | T | Majuscule autorisée |
| 85 | 55 | U | Majuscule autorisée |
| 86 | 56 | V | Majuscule autorisée |
| 87 | 57 | W | Majuscule autorisée |
| 88 | 58 | X | Majuscule autorisée |
| 89 | 59 | Y | Majuscule autorisée |
| 90 | 5A | Z | Majuscule autorisée |
| 91 | 5B | [ | Non retenu dans gen_mdp |
| 92 | 5C | \ | Non retenu dans gen_mdp |
| 93 | 5D | ] | Non retenu dans gen_mdp |
| 94 | 5E | ^ | Non retenu dans gen_mdp |
| 95 | 5F | _ | Non retenu dans gen_mdp |
| 96 | 60 | ` | Non retenu dans gen_mdp |
| 97 | 61 | a | Minuscule autorisée |
| 98 | 62 | b | Minuscule autorisée |
| 99 | 63 | c | Minuscule autorisée |
| 100 | 64 | d | Minuscule autorisée |
| 101 | 65 | e | Minuscule autorisée |
| 102 | 66 | f | Minuscule autorisée |
| 103 | 67 | g | Minuscule autorisée |
| 104 | 68 | h | Minuscule autorisée |
| 105 | 69 | i | Minuscule autorisée |
| 106 | 6A | j | Minuscule autorisée |
| 107 | 6B | k | Minuscule autorisée |
| 108 | 6C | l | Minuscule autorisée |
| 109 | 6D | m | Minuscule autorisée |
| 110 | 6E | n | Minuscule autorisée |
| 111 | 6F | o | Minuscule autorisée |
| 112 | 70 | p | Minuscule autorisée |
| 113 | 71 | q | Minuscule autorisée |
| 114 | 72 | r | Minuscule autorisée |
| 115 | 73 | s | Minuscule autorisée |
| 116 | 74 | t | Minuscule autorisée |
| 117 | 75 | u | Minuscule autorisée |
| 118 | 76 | v | Minuscule autorisée |
| 119 | 77 | w | Minuscule autorisée |
| 120 | 78 | x | Minuscule autorisée |
| 121 | 79 | y | Minuscule autorisée |
| 122 | 7A | z | Minuscule autorisée |
| 123 | 7B | { | Non retenu dans gen_mdp |
| 124 | 7C | | | Non retenu dans gen_mdp |
| 125 | 7D | } | Non retenu dans gen_mdp |
| 126 | 7E | ~ | Non retenu dans gen_mdp |
gen_mdp(longueur, cont_min, cont_maj, cont_spe) renvoie une chaîne de la longueur demandée. Chaque booléen indique si la catégorie peut apparaître. L’opérateur+concatène aussi les listes ou les chaînes. chr(97) donne a ; chr(33) donne !.
from random import randint
def gen_mdp(longueur, cont_min, cont_maj, cont_spe):
assert cont_min or cont_maj or cont_spe
minuscules = [chr(i) for i in ...]
majuscules = [...]
caracteres_speciaux = ... + ...
jeu_caracteres = []
if cont_min:
...
...
...
...
...
mot_de_passe = ''
n = len(jeu_caracteres)
for i in range(longueur):
mot_de_passe = ...
return mot_de_passeQuestion 1
#Écrire un appel générant au moins 8 caractères, uniquement des majuscules et minuscules.
Comprendre la correction
gen_mdp(8, True, True, False)8 satisfait la longueur minimale et False interdit les caractères spéciaux. Cet appel autorise les deux types de lettres ; il ne garantit pas que chacun apparaisse, distinction étudiée en Q5.
Question 2
#Compléter les initialisations de minuscules, majuscules et caractères spéciaux en utilisant obligatoirement des compréhensions.
Comprendre la correction
minuscules = [chr(i) for i in range(97, 123)]
majuscules = [chr(i) for i in range(65, 91)]
caracteres_speciaux = ([chr(i) for i in range(33, 48)]
+ [chr(i) for i in range(58, 65)])Les bornes droites de range sont exclues :123 inclut le code 122 de z,91 inclut 90 de Z. Les deux intervalles spéciaux évitent les chiffres 48 à 57, qui ne sont pas autorisés dans cette fonction. On obtient 26 minuscules,26 majuscules et 22 spéciaux.
Question 3
#Compléter la construction de jeu_caracteres, la liste de tous les caractères autorisés.
Indice
Plusieurs catégories peuvent être autorisées simultanément.
Comprendre la correction
if cont_min:
jeu_caracteres = jeu_caracteres + minuscules
if cont_maj:
jeu_caracteres = jeu_caracteres + majuscules
if cont_spe:
jeu_caracteres = jeu_caracteres + caracteres_speciauxLes trois tests sont indépendants : plusieurs catégories peuvent être activées à la fois. Une chaîne de elif n’en ajouterait qu’une, ce qui serait faux. L’assertion garantit qu’au moins une catégorie est choisie, donc que la liste n’est pas vide.
Question 4
#Compléter la ligne 21. randint(a,b) inclut ses deux bornes.
Comprendre la correction
mot_de_passe = mot_de_passe + jeu_caracteres[randint(0, n - 1)]Les indices valides vont de 0 à n-1. Utiliser n comme borne pourrait produire un indice hors liste. Chaque tirage ajoute un caractère à la chaîne en construction.
Question 5
#Un site exige au moins 12 caractères, dont au moins un spécial et au moins une minuscule. Pourquoi gen_mdp peut-il produire un mot de passe non conforme ?
Comprendre la correction
Même avec gen_mdp(12, True, False, True), chaque tirage peut choisir une minuscule : il est possible d’obtenir 12 lettres sans aucun spécial. Inversement, tous les tirages peuvent choisir des spéciaux. Les booléens autorisent une catégorie, ils n’imposent pas sa présence.
Pour garantir la composition, il faudrait choisir d’abord un caractère de chaque catégorie obligatoire, compléter jusqu’à la longueur voulue et mélanger les positions. Pour un véritable secret, on utiliserait aussi une source aléatoire adaptée à la sécurité, comme secrets, plutôt que le générateur random de l’exercice.
Partie B : comptes et sites
| mot_de_passe (clé primaire) | utilisateur | renouvellement | id_site |
|---|---|---|---|
| Asrtg!Myfj | aliceB24 | 2022-06-30 | 1 |
| @rDfohpj!& | aliceB24 | 2021-03-12 | 2 |
| GxRGDxc(u-PM | alice_B@votremailp.me | 2018-10-14 | 4 |
| Ghcj=+f*AZs | alice1276 | 2022-06-30 | 3 |
| cYFgt!:Ehr; | alice_B2@votremailp.me | 2022-06-30 | 4 |
| id (clé primaire) | nom_site | url |
|---|---|---|
| 1 | Vosnotes | https://logi-educ.net/vosnotes/eleve.html |
| 2 | Banque Perso | https://www.banqueperso.fr/connexion.html |
| 3 | Elec verte | https://espace-client.ev.fr/login |
| 4 | Votremailp | https://account.votremailp.me/login |
compte.id_site référence site.id. renouvellement est une chaîne AAAA-MM-JJ, par exemple 2025-02-21 pour le 21 février 2025.
Question 6
#Pourquoi Alice ne peut-elle pas utiliser le même mot de passe pour deux sites dans ce modèle ?
Comprendre la correction
mot_de_passe est la clé primaire de compte, donc chaque valeur doit être unique dans toute la table. Une seconde ligne contenant le même mot de passe serait rejetée même si son site diffère. C’est une contrainte du schéma choisi, pas une impossibilité générale du Web.
Question 7
#Écrire la requête affichant toutes les URL enregistrées.
Comprendre la correction
SELECT url FROM site;Une ligne de site porte une URL. La table compte n’est pas nécessaire pour cette liste.
Question 8
#Remplacer le mot de passe @rDfohpj!& par yhTS?d@UTJe pour Banque Perso. Ignorer volontairement le champ renouvellement.
Comprendre la correction
UPDATE compte
SET mot_de_passe = 'yhTS?d@UTJe'
WHERE mot_de_passe = '@rDfohpj!&';Le filtre sur l’ancienne clé identifie une seule ligne. La question demande explicitement de ne pas changer la date ; une application réelle devrait normalement mettre cette information à jour aussi.
Question 9
#Au 20 mars 2025, lister les id_site des mots de passe non renouvelés depuis plus d’un an.
Comprendre la correction
SELECT DISTINCT id_site
FROM compte
WHERE renouvellement < '2024-03-20';« Plus d’un an » exclut la date exactement égale au 20 mars 2024 : on utilise un test strict. DISTINCT évite de répéter un site qui possède plusieurs comptes anciens, comme Votremailp dans l’extrait.
Question 10
#Pourquoi préférer AAAA-MM-JJ à JJ-MM-AAAA pour une date stockée en texte ?
Comprendre la correction
À longueur fixe et avec les zéros initiaux, l’ordre lexicographique compare d’abord l’année, puis le mois, puis le jour : il coïncide avec l’ordre chronologique. En JJ-MM-AAAA, comparer les jours d’abord ferait par exemple placer 02-01-2025 avant 31-12-2024, alors que la première date est plus récente.
Question 11
#Afficher utilisateurs et mots de passe du ou des sites nommés Votremailp, sans connaître leur identifiant, par ordre chronologique de renouvellement.
SQLClasser les comptes Votremailp par renouvellementÉcrivez votre solution et mettez-la à l’épreuve
Affichez utilisateur et mot_de_passe pour le ou les sites de nom Votremailp. L’identifiant du site est inconnu ; triez les lignes par date de renouvellement croissante.
SELECT ...
FROM ...
WHERE ...;Les cas de test proposés :
- Comptes fictifs donnés dans le sujet : Les données sont les comptes fictifs imprimés dans le PDF. Le compte renouvelé en 2018 doit précéder celui de 2022.
- Cas complémentaire : ordre physique trompeur : Jeu complémentaire pédagogique, distinct des données officielles. Ces secrets sont des chaînes factices de test. Le nom peut désigner deux lignes de site ; l’identifiant 4 ne convient plus et l’insertion est dans l’ordre inverse du résultat.
Comprendre la correction
SELECT compte.utilisateur, compte.mot_de_passe
FROM compte JOIN site ON compte.id_site = site.id
WHERE site.nom_site = 'Votremailp'
ORDER BY compte.renouvellement;La jointure permet de filtrer avec le nom du site tout en affichant les données des comptes. Le tri croissant met le renouvellement de 2018 avant celui de 2022. Ne pas coder directement l’identifiant 4 : la requête doit rester valable si cet identifiant change.
Question 12
#Donner un avantage d’avoir séparé site et compte plutôt que tout mettre dans une seule table.
Comprendre la correction
Les données d’un site, son nom et son URL, ne sont stockées qu’une fois même si Alice y possède plusieurs comptes. Si l’URL change, une seule mise à jour suffit et tous les comptes retrouvent la même valeur via la référence. Cela réduit la redondance et les incohérences possibles.
Partie C : protéger le fichier
Alice travaille sur un système Linux multi-utilisateur et possède les droits nécessaires dans l’arborescence donnée. La fonction chiffrement(f_source, f_dest, f_cle) lit la source, écrit un fichier chiffré et utilise la clé du troisième fichier ; les trois arguments sont des chemins de type str.
Lire les connexions du schéma
- / relié à home
- home relié à Documents
- home relié à Perso
- Documents relié à chiffrement.py
- Documents relié à gestionnaire.db
- Perso relié à cle
Question 13
#Depuis le répertoire Documents, écrire l’appel chiffrant gestionnaire.db avec la clé cle pour créer secret.db dans Perso.
Comprendre la correction
chiffrement('gestionnaire.db', '../Perso/secret.db', '../Perso/cle')La source se trouve dans le répertoire courant. .. remonte de Documents vers home, puis on entre dans Perso pour la destination et la clé. Un chemin est un argument de la fonction : il n’est pas nécessaire de déplacer le répertoire courant.
Le chiffrement applique XOR bit à bit entre le fichier source et une clé de même taille. La sortie possède aussi cette taille. Rappel : la base 16 utilise 0 à 9 puis AàF.
| a | b | a XOR b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
Question 14
#Donner en hexadécimal le XOR des octets A3 et 59.
Comprendre la correction
A3 : 10100011
59 : 01011001
XOR : 11111010 = FAOn compare les huit positions indépendamment : le résultat est 1 si les bits diffèrent. On regroupe ensuite en deux paquets de quatre :1111 vaut F et 1010 vaut A.
Question 15
#Montrer que pour tous bits a, b, (a XOR b) XOR b = a.
Comprendre la correction
| a | b | a XOR b | (a XOR b) XOR b |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 1 | 1 |
| 1 | 1 | 0 | 1 |
Les quatre possibilités donnent a dans la dernière colonne. On peut aussi utiliser l’associativité : (a XOR b) XOR b = a XOR (b XOR b) = a XOR0 = a.
Question 16
#Le chiffrement est-il symétrique ou asymétrique ? Justifier.
Comprendre la correction
Il est symétrique : la même clé permet de chiffrer et déchiffrer. Réappliquer XOR avec cette clé à chaque bit du texte chiffré rend le bit source, d’après la propriété démontrée.
La seule égalité de taille ne suffit pas à prouver la sécurité : une clé de masque jetable doit être aléatoire, secrète et non réutilisée. Ces propriétés ne sont pas garanties par la simple description de la fonction.
-rw-r--r-- 1 alice eleves 42480 mars 25 11:57 secret.dbQuestion 17
#Avec les droits affichés, pourquoi un attaquant peut-il tenter de déchiffrer secret.db et que devrait faire Alice ?
Indice
La première lettre de chaque bloc rwx indique la lecture.
Comprendre la correction
chmod 600 secret.db-rw-r--r-- donne lecture-écriture à Alice, lecture au groupe eleves et lecture aux autres utilisateurs. Tout utilisateur autorisé à traverser les répertoires peut donc lire et copier le fichier pour l’analyser hors ligne.
chmod600 garde lecture et écriture pour Alice et retire les droits au groupe et aux autres. Elle doit aussi protéger la clé, le fichier source non chiffré et les répertoires concernés. Restreindre l’accès ne renforce pas mathématiquement le chiffrement, mais réduit les possibilités d’en récupérer une copie.
Partie D : pratiques à confronter au projet
Le sujet cite quatre pratiques issues de cyber.gouv.fr/bonnes-pratiques-protegez-vous : P1, utiliser un mot de passe distinct pour chaque accès ; P2, créer un mot de passe long, complexe et inattendu, au moins 8 caractères avec minuscules, majuscules, chiffres et spéciaux ; P3, ne jamais le communiquer à un tiers ; P4, utiliser un gestionnaire qui mémorise les mots de passe et permet d’en générer.
Question 18
#En justifiant, indiquer si les choix d’Alice respectent chacune des pratiques P1à P4 du sujet.
Comprendre la correction
| Pratique citée dans le sujet | Analyse du modèle d’Alice |
|---|---|
| P1 : un mot de passe différent par accès | La clé primaire mot_de_passe interdit deux valeurs identiques dans compte. Cela encourage la différence entre les comptes stockés, sans contrôler d’autres comptes éventuels hors de la base. |
| P2 : au moins 8 caractères, minuscules, majuscules, chiffres et spéciaux | Pas garanti : longueur est libre, la présence des catégories autorisées n’est pas imposée et aucun chiffre ne figure dans le jeu de caractères. |
| P3 : ne jamais communiquer son mot de passe à un tiers | L’énoncé ne dit pas qu’Alice les divulgue volontairement. En revanche, les permissions trop ouvertes exposent des fichiers et doivent être corrigées. On ne peut pas déduire son comportement personnel du seul code. |
| P4 : utiliser un gestionnaire capable de mémoriser et générer | Le projet mémorise les comptes et génère des chaînes, mais cela ne certifie pas sa sécurité. La qualité du hasard, la clé et les accès demandent les contrôles étudiés. |
Cette question analyse les quatre critères reproduits dans le PDF. Il ne faut pas transformer une recommandation datée de longueur minimale en garantie actuelle suffisante pour tous les comptes.
XOR deux fois : le secret réapparaîtUn atelier pour expérimenter
Choisissez deux octets. Observez chaque bit du message, de la clé et du texte chiffré, puis le second XOR qui restitue le message.
Lire le résultat de l’expérience initiale
A3 XOR 59 = FA ; FA XOR 59 = A3
Le même octet de clé inverse deux fois les mêmes bits : ceux à1dans la clé basculent, puis rebasculent. Une clé nulle laisse le message inchangé.
| Position | Message | Clé | Chiffré | Déchiffré |
|---|---|---|---|---|
| 7 | 1 | 0 | 1 | 1 |
| 6 | 0 | 1 | 1 | 0 |
| 5 | 1 | 0 | 1 | 1 |
| 4 | 0 | 1 | 1 | 0 |
| 3 | 0 | 1 | 1 | 0 |
| 2 | 0 | 0 | 0 | 0 |
| 1 | 1 | 0 | 1 | 1 |
| 0 | 1 | 1 | 0 | 1 |
La réversibilité du XOR démontre comment déchiffrer ; elle ne démontre pas que la clé est imprévisible ou protégée. Correction du calcul et sécurité de son usage sont deux vérifications distinctes.
Du sujet à la méthode
Votre prochaine séance de révision
- Sur RIP, suivez la convention numérique des lignes déjà remplies au lieu de plaquer un autre comptage.
- Pour le baguenaudier, écrivez sur la copie si vous utilisez les cases 1 à n ou les indices 0 à n-1.
- Dans l’analyse de sécurité, distinguez ce que le code garantit, ce qu’il autorise et ce que l’énoncé ne permet pas de conclure.
Retrouver ces notions dans d’autres sujets
Toutes les annales de NSI · Le guide pour préparer le bac NSI 2027
Énoncé : sujet 25-NSIJ1PO1 (PDF). Corrigé et explications pédagogiques proposés par Sofien.
