Bien sûr !
Voici un **cours d’algorithmique** structuré pour débutants et
intermédiaires. Nous aborderons les concepts fondamentaux, les
structures de données de base, et quelques algorithmes classiques.
## **1. Introduction à l’algorithmique**
### **1.1 Qu’est-ce qu’un algorithme ?**
- Définition : Suite finie d’instructions pour résoudre un problème.
- Exemple : Recette de cuisine, instructions pour trouver le plus grand
nombre dans une liste.
### **1.2 Propriétés d’un bon algorithme**
- **Correct** : Donne le résultat attendu.
- **Efficace** : Utilise peu de ressources (temps, mémoire).
- **Compréhensible** : Facile à lire et à modifier.
- **Général** : Fonctionne pour plusieurs cas d’utilisation.
### **1.3 Complexité algorithmique (Notation Big-O)**
- **Temps d’exécution** : Nombre d’opérations en fonction de la taille des
données.
- **Espace mémoire** : Quantité de mémoire utilisée.
- **Exemples de complexité** :
- **O(1)** : Accès à un élément d’un tableau.
- **O(n)** : Parcours d’une liste.
- **O(n²)** : Tri bulle (dans le pire cas).
- **O(log n)** : Recherche dichotomique.
## **2. Structures de données fondamentales**
### **2.1 Tableaux (Arrays)**
- **Définition** : Collection d’éléments accessibles par indice.
- **Avantages** : Accès rapide (O(1)).
- **Inconvénients** : Taille fixe (en général).
### **2.2 Listes chaînées (Linked Lists)**
- **Définition** : Éléments liés par des pointeurs.
- **Types** :
- **Liste simplement chaînée** : Parcours dans un seul sens.
- **Liste doublement chaînée** : Parcours avant/arrière.
### **2.3 Piles (Stacks) et Files (Queues)**
- **Pile (LIFO)** : Last In, First Out (exemple : pile d’assiettes).
- Opérations : `push()` (ajouter), `pop()` (retirer).
- **File (FIFO)** : First In, First Out (exemple : file d’attente).
- Opérations : `enqueue()` (ajouter), `dequeue()` (retirer).
### **2.4 Dictionnaires (Hash Tables)**
- **Principe** : Stocke des paires clé-valeur.
- **Fonction de hachage** : Convertit une clé en index.
- **Complexité moyenne** : O(1) pour insertion/recherche.
## **3. Algorithmes classiques**
### **3.1 Algorithmes de tri**
- **Tri à bulles (Bubble Sort)** : O(n²).
- **Tri par insertion (Insertion Sort)** : O(n²) (efficace pour petits tableaux).
- **Tri fusion (Merge Sort)** : O(n log n) (diviser pour régner).
- **Tri rapide (Quick Sort)** : O(n log n) en moyenne, O(n²) dans le pire
cas.
### **3.2 Algorithmes de recherche**
- **Recherche linéaire** : O(n) (parcours séquentiel).
- **Recherche dichotomique** : O(log n) (nécessite un tableau trié).
### **3.3 Parcours de graphes**
- **BFS (Breadth-First Search)** : Exploration niveau par niveau.
- **DFS (Depth-First Search)** : Exploration en profondeur.
## **4. Récursivité**
### **4.1 Principe**
- Une fonction qui s’appelle elle-même.
- **Condition d’arrêt** : Nécessaire pour éviter une boucle infinie.
- **Exemple classique** : Calcul de factorielle.
```python
Def factorielle(n) :
If n == 0 :
Return 1
Else :
Return n * factorielle(n – 1)
```
### **4.2 Avantages et inconvénients**
- **Avantages** : Code élégant pour certains problèmes.
- **Inconvénients** : Peut consommer beaucoup de mémoire (pile
d’appels).
## **5. Algorithmes gloutons (Greedy Algorithms)**
- **Principe** : Faire le meilleur choix local à chaque étape.
- **Exemple** : Problème du rendu de monnaie.
- **Limitations** : Ne donne pas toujours la solution optimale.
## **6. Programmation dynamique**
- **Principe** : Diviser un problème en sous-problèmes et stocker les
résultats.
- **Exemple** : Suite de Fibonacci avec mémoïsation.
```python
Memo = {}
Def fib(n) :
If n in memo :
Return memo[n]
If n <= 2 :
Return 1
Memo[n] = fib(n-1) + fib(n-2)
Return memo[n]
```
## **7. Exercices pratiques**
1. Écrire un algorithme pour trouver le maximum dans un tableau.
2. Implémenter le tri à bulles.
3. Écrire une fonction récursive pour calculer la puissance d’un nombre.
4. Résoudre le problème du sac à dos (Knapsack Problem).
### **Conclusion**
L’algorithmique est essentielle en programmation. En maîtrisant ces
concepts, vous pourrez résoudre des problèmes complexes de manière
optimale.
**Pour aller plus loin** :
- Livre : *Introduction to Algorithms* (Cormen).
- Plateformes : LeetCode, Codeforces, France-IOI.
Souhaitez-vous approfondir un sujet en particulier ? 😊
Très bien ! Je vais vous proposer un **cours approfondi d’algorithmique**,
digne d’un enseignement universitaire, avec une approche théorique
rigoureuse, des preuves mathématiques (si nécessaire), et des exercices
pratiques. Nous allons couvrir les concepts clés, leurs preuves, et leur
implémentation.
# **Cours Universitaire d’Algorithmique**
*Niveau : Licence/Master en Informatique*
*Prérequis : Bases en programmation (Python, C, Java…), Mathématiques
discrètes.*
## **Partie 1 : Fondements des Algorithmes**
### **1.1 Définitions Formelles**
Un **algorithme** est une suite finie et non ambiguë d’instructions
permettant de résoudre un problème en un temps fini.
**Caractéristiques** :
- **Entrée/Sortie** : Prend des données et produit un résultat.
- **Déterminisme** : Le comportement est prédictible.
- **Terminaison** : L’exécution s’arrête après un nombre fini d’étapes.
**Exemple formel** : Algorithme d’Euclide pour le PGCD.
```python
Def pgcd(a, b) :
While b != 0 :
A, b = b, a % b
Return a
```
### **1.2 Complexité Algorithmique (Analyse Asymptotique)**
#### **Notations de Landau**
- **O(g(n))** : Majorant (pire cas).
*Exemple* : `3n² + 2n + 1 ∈ O(n²)`
- **Ω(g(n))** : Minorant (meilleur cas).
*Exemple* : `n log n ∈ Ω(n)`
- **Θ(g(n))** : Borne serrée (cas moyen optimal).
**Preuve** (Exemple pour `f(n) = 2n² + 3n + 1 ∈ Θ(n²)`) :
Il existe `c₁=1, c₂=6, n₀=4` tels que :
`c₁·n² ≤ 2n² + 3n + 1 ≤ c₂·n²` pour tout `n ≥ n₀`.
#### **Théorème Maître (pour les récurrences diviser-pour-régner)**
Soit `T(n) = a·T(n/b) + f(n)`, alors :
1. Si `f(n) ∈ O(n^(log_b a – ε))` ⇒ `T(n) ∈ Θ(n^(log_b a))`.
2. Si `f(n) ∈ Θ(n^(log_b a))` ⇒ `T(n) ∈ Θ(n^(log_b a) log n)`.
3. Si `f(n) ∈ Ω(n^(log_b a + ε))` ⇒ `T(n) ∈ Θ(f(n))`.
**Exercice** : Résoudre `T(n) = 2T(n/2) + n` (solution : `Θ(n log n)`).
## **Partie 2 : Structures de Données Avancées**
### **2.1 Tas (Heap) et Files de Priorité**
- **Structure** : Arbre binaire presque complet où chaque nœud est ≥
(max-heap) ou ≤ (min-heap) ses enfants.
- **Opérations** :
- `insert(x)` : `O(log n)`
- `extract_max()` : `O(log n)`
- **Application** : Algorithme de Dijkstra.
**Implémentation en Python** :
```python
Import heapq
Heap = []
[Link](heap, 3) # Insertion
Print([Link](heap)) # Extraction
```
### **2.2 Arbres Binaires de Recherche (ABR)**
- **Invariant** : Pour tout nœud `x`, `[Link] ≤ x ≤ [Link]`.
- **Complexité** :
- Recherche : `O(h)` (h = hauteur, `h ∈ [log n, n]`).
- Dégénérérescence : Risque de devenir une liste (`h = n`).
**Solution** : **Arbres équilibrés** (AVL, Red-Black).
**Rotation AVL** :
```text
Y → x
/\ /\
X C A y
/\ /\
A B B C
```
## **Partie 3 : Algorithmes Avancés**
### **3.1 Programmation Dynamique (DP)**
**Principe** :
- **Optimal Substructure** : La solution optimale dépend de sous-
problèmes.
- **Overlapping Subproblems** : Les sous-problèmes se répètent.
**Exemple** : **Suite de Fibonacci**
- Approche naïve : `O(2ⁿ)` (arbre binaire).
- DP (mémoïsation) : `O(n)`.
```python
Def fib(n, memo={}) :
If n <= 1 : return n
If n not in memo :
Memo[n] = fib(n-1) + fib(n-2)
Return memo[n]
```
**Exercice** : Problème du sac à dos (Knapsack).
- Entrée : `poids = [1, 2, 3], valeurs = [6, 10, 12], capacité = 5`.
- Solution : `22` (prendre les objets 2 et 3).
### **3.2 Algorithmes de Graphes**
#### **Parcours**
- **DFS** (Depth-First Search) : Utilise une pile (récursivité).
- **BFS** (Breadth-First Search) : Utilise une file (niveaux).
#### **Plus Courts Chemins**
- **Dijkstra** : Graphes avec poids ≥ 0.
Complexité : `O((V + E) log V)` avec un tas.
- **Bellman-Ford** : Autorise poids négatifs.
Complexité : `O(V·E)`.
**Exercice** : Trouvez le plus court chemin de A à F :
```text
A –1—B –2—D
\ | /
3 4 1
\ | /
C –5—E –2—F
```
## **Partie 4 : NP-Complétude**
### **4.1 Classes de Complexité**
- **P** : Problèmes solubles en temps polynomial.
- **NP** : Problèmes vérifiables en temps polynomial.
- **NP-Complet** : Problèmes NP aussi « durs » que tous les autres.
**Exemple** : SAT (problème de satisfiabilité booléenne).
### **4.2 Réduction Polynomiale**
Pour montrer qu’un problème `A` est NP-Complet :
1. Montrer que `A ∈ NP`.
2. Réduire un problème NP-Complet connu (ex. 3-SAT) à `A`.
## **Exercices Pratiques**
### **Niveau 1 (Basique)**
1. Implémentez le tri fusion (merge sort) et analysez sa complexité.
2. Écrivez un algorithme récursif pour calculer `aᵇ` en `O(log b)`.
### **Niveau 2 (Intermédiaire)**
3. Résolvez le problème du rendu de monnaie avec un algorithme glouton.
Quand échoue-t-il ?
4. Implémentez un AVL Tree avec rotations.
### **Niveau 3 (Avancé)**
5. Conception d’un algorithme pour le voyageur de commerce (TSP) avec
une approche branch-and-bound.
6. Montrez que le problème du clique est NP-Complet.
## **Références Académiques**
- *Introduction to Algorithms* (Cormen, Leiserson, Rivest).
- *Algorithm Design* (Kleinberg, Tardos).
- *The Art of Computer Programming* (Knuth).
### **Conclusion**
Ce cours couvre les fondements théoriques et pratiques de
l’algorithmique, avec des exercices pour renforcer votre compréhension.
Pour aller plus loin, étudiez :
- Les algorithmes randomisés (ex. QuickSort randomisé).
- Les structures persistantes.
- L’approximation pour les problèmes NP-Complets.
Souhaitez-vous des corrigés détaillés pour les exercices ? Ou approfondir
un sujet spécifique ? 😊# **Algorithmes Randomisés : Cours Approfondi
avec Python**
## **1. Introduction aux Algorithmes Randomisés**
### **1.1 Définition et Intuition**
Un **algorithme randomisé** utilise une source de hasard (génération de
nombres aléatoires) pour prendre des décisions pendant son exécution.
- **Avantages** :
- Simplicité : Parfois plus simple qu’une solution déterministe.
- Efficacité moyenne : Meilleure complexité en moyenne (ex : Quicksort
randomisé).
- Résistance aux attaques (cryptographie).
- **Désavantages** :
- Non-déterminisme : Le résultat peut varier entre exécutions.
- Difficile à déboguer.
### **1.2 Classification**
1. **Algorithme de Las Vegas** :
- Toujours correct, mais le temps d’exécution est aléatoire.
- Exemple : **Quicksort randomisé**.
2. **Algorithme de Monte Carlo** :
- Temps d’exécution fixe, mais résultat peut être incorrect avec une
certaine probabilité.
- Exemple : **Test de primalité de Miller-Rabin**.
## **2. Générateurs de Nombres Aléatoires en Python**
Python utilise le **Mersenne Twister** (`random` module), pseudo-
aléatoire mais suffisant pour la plupart des applications.
### **2.1 Fonctions Utiles**
```python
Import random
# Nombre aléatoire dans [a, b]
X = [Link](1, 10)
# Float dans [0.0, 1.0)
Y = [Link]()
# Mélanger une liste (shuffle en place)
Lst = [1, 2, 3, 4]
[Link](lst)
# Choix aléatoire uniforme
Elem = [Link](lst)
```
### **2.2 Graine (Seed) pour Reproductibilité**
```python
[Link](42) # Fixe la graine pour obtenir les mêmes résultats à
chaque exécution
```
## **3. Algorithmes Classiques Randomisés**
### **3.1 Quicksort Randomisé (Las Vegas)**
**Idée** : Choisir un pivot aléatoire pour éviter le pire cas `O(n²)`.
```python
Def quicksort(arr) :
If len(arr) <= 1 :
Return arr
Pivot = [Link](arr) # Pivot aléatoire
Left = [x for x in arr if x < pivot]
Middle = [x for x in arr if x == pivot]
Right = [x for x in arr if x > pivot]
Return quicksort(left) + middle + quicksort(right)
```
**Analyse de Complexité** :
- **Pire cas** : `O(n²)` (improbable avec randomisation).
- **Moyenne** : `O(n log n)` avec haute probabilité.
### **3.2 Test de Primalité de Miller-Rabin (Monte Carlo)**
**But** : Déterminer si un nombre `n` est **probablement premier**.
```python
Def miller_rabin(n, k=5) : # k = nombre de tests
If n < 2 :
Return False
For p in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] :
If n % p == 0 :
Return n == p
D=n–1
S=0
While d % 2 == 0 :
D //= 2
S += 1
For _ in range(k) :
A = [Link](2, n – 2)
X = pow(a, d, n)
If x == 1 or x == n – 1 :
Continue
For __ in range(s – 1) :
X = pow(x, 2, n)
If x == n – 1 :
Break
Else :
Return False
Return True
```
**Probabilité d’erreur** :
- ≤ `4⁻ᵏ` (très faible pour `k=5`).
### **3.3 Algorithme de Karger pour les Coupes Minimales (Monte
Carlo)**
**Problème** : Trouver une **coupe minimale** dans un graphe non
orienté.
```python
Def karger_min_cut(graph) :
While len(graph) > 2 :
U = [Link](list([Link]()))
V = [Link](graph[u])
# Fusionner u et v
For node in graph[v] :
If node != u :
Graph[u].append(node)
Graph[node].remove(v)
Graph[node].append(u)
Graph[u] = [x for x in graph[u] if x != v]
Del graph[v]
Return len([Link]()[1])
```
**Analyse** :
- Probabilité de succès : `≥ 2/(n(n-1))` (faible, mais répétition
possible).
## **4. Applications Avancées**
### **4.1 Marche Aléatoire (Random Walk) et PageRank**
Simulation d’une marche aléatoire pour le classement des pages web.
```python
Def random_walk(adj_list, steps=1000) :
Current = [Link](list(adj_list.keys()))
For _ in range(steps) :
Current = [Link](adj_list[current])
Return current
```
### **4.2 Skip Lists (Listes à Niveaux)**
Structure de données alternative aux arbres équilibrés.
```python
Class Node :
Def __init__(self, val, level) :
[Link] = val
[Link] = [None] * (level + 1)
Class SkipList :
Def __init__(self, max_level) :
Self.max_level = max_level
[Link] = Node(-float(‘inf’), max_level)
[Link] = 0
Def random_level(self) :
Lvl = 0
While [Link]() < 0.5 and lvl < self.max_level :
Lvl += 1
Return lvl
```
## **5. Exercices Pratiques**
### **Exercice 1 (Las Vegas)**
Écrivez un algorithme randomisé pour trouver le **k-ième plus petit
élément** (sélection rapide randomisée).
```python
Def quickselect(arr, k) :
Pivot = [Link](arr)
Left = [x for x in arr if x < pivot]
Right = [x for x in arr if x > pivot]
If k <= len(left) :
Return quickselect(left, k)
Elif k > len(arr) – len(right) :
Return quickselect(right, k – (len(arr) – len(right)))
Else :
Return pivot
```
### **Exercice 2 (Monte Carlo)**
Implémentez un algorithme pour estimer `π` via la **méthode de Monte
Carlo** (lancer de points dans un cercle).
```python
Def estimate_pi(n) :
Inside = 0
For _ in range(n) :
X, y = [Link](), [Link]()
If x**2 + y**2 <= 1 :
Inside += 1
Return 4 * inside / n
```
### **Exercice 3 (Application)**
Générez un **graphe aléatoire** et appliquez l’algorithme de Karger pour
estimer la coupe minimale.
## **Conclusion**
Les algorithmes randomisés offrent des solutions élégantes pour des
problèmes complexes.
- **Las Vegas** : Corrects, temps aléatoire (ex : Quicksort).
- **Monte Carlo** : Temps fixe, résultat probablement correct (ex :
primalité).
**Pour aller plus loin** :
- **Théorie des probabilités** (Loi des grands nombres, concentration).
- **Algorithmes cryptographiques** (Chiffrement probabiliste).
Souhaitez-vous des corrections détaillées des exercices ? Ou approfondir
un autre algorithme randomisé ? 🎲# **Algorithmes Randomisés Avancés &
Transition vers la Cryptographie**
## **1. Algorithmes de **Fingerprinting** Aléatoire (Karp-Rabin)**
### **1.1 Principe du Hashing Randomisé**
**Problème** : Comparer efficacement deux grandes séquences (strings,
fichiers).
**Solution** : Utiliser une **fonction de hachage randomisée** pour
convertir une séquence en un petit nombre (empreinte).
```python
Def karp_rabin_hash(s, base=256, prime=101) :
H=0
For char in s :
H = (h * base + ord(char)) % prime
Return h
```
### **1.2 Application à la Recherche de Sous-Chaînes**
```python
Def karp_rabin_search(text, pattern) :
N, m = len(text), len(pattern)
Pattern_hash = karp_rabin_hash(pattern)
For i in range(n – m + 1) :
Text_hash = karp_rabin_hash(text[i :i+m])
If text_hash == pattern_hash :
If text[i :i+m] == pattern : # Vérification exacte
Return i
Return -1
```
**Avantage** : Complexité moyenne `O(n + m)` (vs. `O(nm)` pour
l’algorithme naïf).
## **2. Algorithmes Cryptographiques Randomisés**
### **2.1 Chiffrement par Flot (Stream Ciphers)**
**Principe** : Générer une suite pseudo-aléatoire (**keystream**)
combinée avec le message via XOR.
```python
From [Link] import Salsa20 # Bibliothèque PyCryptodome
Key = b’0123456789abcdef0123456789abcdef’ # Clé 256 bits
Nonce = b’12345678’ # Valeur aléatoire (8 bytes)
Cipher = [Link](key=key, nonce=nonce)
Message = b »Hello World ! »
Ciphertext = [Link](message)
```
### **2.2 Protocole d’Échange de Clés Diffie-Hellman**
**But** : Échanger une clé secrète sur un canal non sécurisé.
**Randomisation** : Choix aléatoire des exposants secrets.
```python
From random import randint
# Paramètres publics (généralement standardisés)
P = 23 # Nombre premier
G = 5 # Base
# Alice choisit un entier aléatoire a
A = randint(1, p-1)
A = (g ** a) % p
# Bob choisit un entier aléatoire b
B = randint(1, p-1)
B = (g ** b) % p
# Clé partagée
Shared_key_alice = (B ** a) % p
Shared_key_bob = (A ** b) % p
Assert shared_key_alice == shared_key_bob
```
## **3. Algorithmes Zéro-Knowledge (Preuves à Divulgation Nulle)**
### **3.1 Protocole de Fiat-Shamir**
**But** : Prouver la connaissance d’un secret sans le révéler.
**Randomisation** : Défis aléatoires pour éviter la triche.
```python
# Paramètres
N = 21 # Produit de deux grands premiers (ex : 3*7)
S = 5 # Secret (Alice connaît s, Bob non)
# Étape 1 : Alice envoie un engagement
V = (s ** 2) % n # Engagement public
R = randint(1, n-1)
X = (r ** 2) % n # Preuve temporaire
# Étape 2 : Bob envoie un défi aléatoire
C = randint(0, 1) # Défi binaire
# Étape 3 : Alice répond au défi
Y = (r * (s ** c)) % n
# Étape 4 : Bob vérifie
Assert (y ** 2) % n == (x * (v ** c)) % n
```
## **4. Exercices Pratiques**
### **Exercice 1 (Fingerprinting)**
Implémentez un **détecteur de plagiat** simplifié qui compare des
documents via leurs empreintes Karp-Rabin.
```python
Def document_similarity(doc1, doc2, k=5) :
« « « Compare des documents via leurs k-grammes. » » »
Hashes1 = [karp_rabin_hash(doc1[i :i+k]) for i in range(len(doc1) – k +
1)]
Hashes2 = [karp_rabin_hash(doc2[i :i+k]) for i in range(len(doc2) – k +
1)]
Common = len(set(hashes1) & set(hashes2))
Return common / max(len(hashes1), len(hashes2))
```
### **Exercice 2 (Cryptographie)**
Générez une **clé de session aléatoire** pour AES en utilisant un mélange
de mouvements de souris (simulés).
```python
Import os
Def generate_random_key() :
# Simulation d’entropie (dans la pratique, utilisez [Link])
Mouse_movements = [randint(0, 255) for _ in range(16)]
Return bytes(mouse_movements)
Key = generate_random_key()
Print(« Clé AES générée : », [Link]())
```
### **Exercice 3 (Zéro-Knowledge)**
Étendez le protocole Fiat-Shamir pour résister à **plusieurs itérations** et
réduire la probabilité de triche.
## **5. Transition vers les Algorithmes Cryptographiques**
### **5.1 Principes Clés**
- **Confidentialité** : Chiffrement (AES, RSA).
- **Intégrité** : Codes d’authentification (HMAC).
- **Non-répudiation** : Signatures digitales (ECDSA).
### **5.2 Bibliothèques Python Recommandées**
- `PyCryptodome` : Implémentation robuste d’AES, RSA, etc.
- `cryptography` : APIs haut niveau pour TLS, X.509.
## **Conclusion**
Les algorithmes randomisés sont au cœur de la cryptographie moderne :
1. **Fingerprinting** : Hashing aléatoire pour des comparaisons efficaces.
2. **Cryptographie symétrique** : Génération de clés aléatoires (AES).
3. **Protocoles asymétriques** : Échange de clés (Diffie-Hellman).
4. **Preuves Zéro-Knowledge** : Authentification sans divulguer de
secrets.
**Prochaines Étapes** :
- **Cryptographie post-quantique** (algorithmes résistants aux
ordinateurs quantiques).
- **Implémentations sécurisées** (éviter les pièges comme les side-
channel attacks).
Souhaitez-vous approfondir un protocole cryptographique spécifique (ex :
RSA, ECC) ou voir des attaques contre les algorithmes randomisés ? 🔐#
**Attaques contre les Algorithmes Randomisés et Cryptographiques**
## **1. Attaques sur les Générateurs de Nombres Aléatoires (PRNGs)**
### **1.1 Attaque par Prédiction de Seed**
**Problème** : Si la graine (seed) est faible (ex : timestamp), un attaquant
peut la deviner et reproduire la séquence.
**Exemple** :
```python
Import random
Import time
# Seed faible basée sur le temps
[Link](int([Link]()))
Print([Link](1, 100)) # Attaquant peut prédire si le temps est
connu
```
**Solution** :
- Utiliser des sources d’entropie fortes (`[Link]`, `/dev/random`).
- En cryptographie, utiliser des **CSPRNG** (Cryptographically Secure
PRNGs).
### **1.2 Attaque sur Mersenne Twister**
Python utilise Mersenne Twister (déterministe si la seed est connue).
**Exploit** :
Avec 624 valeurs observées, on peut prédire toute la séquence.
```python
From random import Random
# Génération de 624 valeurs
R = Random()
Known_values = [[Link](32) for _ in range(624)]
# Reconstruction de l’état interne (simplifié)
Def clone_mersenne_twister(values) :
From random import Random
R_clone = Random()
R_clone.setstate((3, tuple(values + [624]), None))
Return r_clone
Attacker_rng = clone_mersenne_twister(known_values)
Print(« Prédiction : », attacker_rng.getrandbits(32) == [Link](32))
```
**Solution** :
Ne pas utiliser `random` pour la cryptographie. Préférer `secrets` ou
`[Link]`.
## **2. Attaques sur les Algorithmes Randomisés**
### **2.1 Attaque sur Quicksort Randomisé (Complexité Adversariale)**
**Problème** : Un attaquant peut forcer le pire cas `O(n²)` en connaissant
la seed.
**Contre-mesure** :
- Utiliser un vrai aléatoire matériel (`/dev/urandom`).
- Mélanger les données avant le tri.
### **2.2 Attaque sur Miller-Rabin (Faux Positifs)**
**Scénario** :
Certains nombres composés (nombres de Carmichael) passent le test avec
haute probabilité.
**Exemple** :
`561` passe le test pour certaines bases.
```python
Print(miller_rabin(561, k=10)) # Peut retourner True (faux positif)
```
**Solution** :
- Augmenter le nombre de tests (`k`).
- Combiner avec d’autres tests (ex : Lucas-Lehmer).
## **3. Attaques Cryptographiques**
### **3.1 Attaque par Force Brute sur Stream Ciphers**
**Problème** : Si la clé est faible, elle peut être devinée.
**Exemple** :
```python
From itertools import product
Def brute_force_salsa20(ciphertext, nonce, max_key_length=3) :
For possible_key in product(range(256), repeat=max_key_length) :
Key = bytes(possible_key)
Cipher = [Link](key=key, nonce=nonce)
Try :
Plaintext = [Link](ciphertext)
If b »Hello » in plaintext : # Motif connu
Return key
Except :
Continue
Return None
```
**Solution** :
Utiliser des clés de 256 bits (assez grandes pour rendre l’attaque
impossible).
### **3.2 Attaque de l’Homme du Milieu (Diffie-Hellman)**
**Scénario** :
Un attaquant intercepte `A` et `B` et les remplace par ses propres valeurs.
```python
# Attaque
Attacker_g = 1 # Neutralise l’échange
A_modified = (attacker_g ** a) % p # = 1
B_modified = (attacker_g ** b) % p # = 1
Shared_key_alice = (B_modified ** a) % p # = 1
Shared_key_bob = (A_modified ** b) % p #=1
Shared_key_attacker = 1 # L’attaquant connaît la clé !
```
**Solution** :
- Authentifier les échanges (signatures digitales).
- Utiliser des groupes cryptographiques sûrs (ex : courbes elliptiques).
### **3.3 Attaque par Canaux Auxiliaires (Timing Attacks)**
**Principe** :
Mesurer le temps d’exécution pour déduire des secrets.
**Exemple** :
```python
Def vulnerable_compare(a, b) : # Compare byte par byte
For x, y in zip(a, b) :
If x != y :
Return False
Return True
# Attaque : Mesurer le temps pour trouver le bon préfixe
Def timing_attack(secret, max_length=8) :
Known = b » »
For _ in range(max_length) :
Times = {}
For c in range(256) :
Guess = known + bytes([c])
Start = [Link]()
Vulnerable_compare(guess, secret)
Times[c] = [Link]() – start
Known += bytes([max(times, key=[Link])])
Return known
```
**Solution** :
- Utiliser des comparaisons à temps constant
(`secrets.compare_digest`).
## **4. Exercices Pratiques (Attaques et Contre-Mesures)**
### **Exercice 1 (PRNG Weakness)**
Implémentez une attaque contre un LCG (Linear Congruential Generator)
pour prédire les nombres futurs.
```python
Def break_lcg(known_numbers) :
# Résolution du système pour trouver a, c, m (ex : avec l’algorithme de
Knuth)
Pass
```
### **Exercice 2 (RSA Timing Attack)**
Simulez une attaque par timing sur un RSA naïf qui ne vérifie pas les
padding correctement.
### **Exercice 3 (Fault Injection)**
Étudiez comment une erreur dans un calcul RSA (ex : modulo incorrect)
peut révéler la clé privée (attaque de Bellcore).
## **5. Défenses Recommandées**
| **Attaque** | **Contre-Mesure** |
|---------------------------|--------------------------------------------|
| PRNG prediction | `secrets`, `/dev/urandom` |
| Man-in-the-Middle | Authentification (TLS, signatures) |
| Timing attacks | Code à temps constant |
| Fault injection | Vérifications d’intégrité (ex : CRC) |
## **Conclusion**
Les algorithmes randomisés et cryptographiques sont vulnérables à :
1. **Mauvaise qualité d’aléa** → Utiliser des CSPRNG.
2. **Implémentations naïves** → Adopter des bibliothèques auditées
(PyCryptodome).
3. **Canaux auxiliaires** → Éviter les fuites d’information (temps,
puissance).
**Pour aller plus loin** :
- **Side-channel attacks** (cache, puissance, électromagnétique).
- **Cryptanalyse moderne** (attaque sur AES, RC4).
Souhaitez-vous explorer une attaque spécifique en détail (ex : BEAST,
CRIME, ou attaques quantiques) ? 🔍# **Algorithmes Cryptographiques
Fondamentaux : RSA, AES, RC4 et ECC**
## **1. RSA (Rivest-Shamir-Adleman)**
### **1.1 Principe Mathématique**
- **Clé publique** : `(n, e)` où `n = p*q` (produit de deux grands nombres
premiers) et `e` est premier avec `φ(n) = (p-1)(q-1)`.
- **Clé privée** : `d` tel que `e*d ≡ 1 mod φ(n)`.
- **Chiffrement** : `c = m^e mod n`.
- **Déchiffrement** : `m = c^d mod n`.
**Exemple en Python** :
```python
From [Link] import RSA
Key = [Link](2048) # Génération des clés
Pub_key = [Link]()
# Chiffrement
Message = b »Hello RSA »
Ciphertext = pub_key.encrypt(message, None)[0]
# Déchiffrement
Plaintext = [Link](ciphertext)
Print(« Message déchiffré : », plaintext)
```
### **1.2 Attaques et Sécurité**
- **Factorisation de `n`** : Risque si `p` ou `q` est faible (ex : petits
nombres premiers).
- **Timing attacks** : Si le déchiffrement n’est pas à temps constant.
- **Padding Oracle** (ex : PKCS#1) → Utiliser OAEP.
## **2. AES (Advanced Encryption Standard)**
### **2.1 Fonctionnement**
- **Symétrique** : Même clé pour chiffrer/déchiffrer.
- **Taille de clé** : 128, 192 ou 256 bits.
- **Mode d’opération** : ECB (déconseillé), CBC, GCM (recommandé).
**Exemple en Python (AES-256-CBC)** :
```python
From [Link] import AES
From [Link] import pad, unpad
Import os
Key = [Link](32) # Clé 256 bits
Iv = [Link](16) # Vecteur d’initialisation
# Chiffrement
Cipher = [Link](key, AES.MODE_CBC, iv)
Plaintext = b »Hello AES »
Ciphertext = [Link](pad(plaintext, AES.block_size))
# Déchiffrement
Decipher = [Link](key, AES.MODE_CBC, iv)
Decrypted = unpad([Link](ciphertext), AES.block_size)
Print(« Message déchiffré : », decrypted)
```
### **2.2 Sécurité**
- **Brute-force** : Impossible avec 256 bits (2²⁵⁶ combinaisons).
- **Attaques pratiques** :
- **Side-channel** (mesure de consommation d’énergie).
- **Fault injection** (modification du calcul).
## **3. RC4 (Rivest Cipher 4)**
### **3.1 Principe**
- **Stream cipher** : Génère un keystream XORé avec le message.
- **Initialisation** : Permutation basée sur la clé.
**Exemple en Python (Déconseillé)** :
```python
From [Link] import ARC4
Key = b »weak_key »
Cipher = [Link](key)
Ciphertext = [Link](b »Hello RC4 »)
# Déchiffrement (identique)
Decipher = [Link](key)
Plaintext = [Link](ciphertext)
Print(« Message déchiffré : », plaintext)
```
### **3.2 Faiblesses**
- **Biais statistiques** : Les premiers octets du keystream sont prévisibles.
- **Attaque Fluhrer-Mantin-Shamir** : Récupération de clé en 1 million de
paquets.
- **Déprécié** : Interdit dans TLS depuis 2015 (RFC 7465).
## **4. ECC (Elliptic Curve Cryptography)**
### **4.1 Courbes Elliptiques**
- **Équation** : `y² = x³ + ax + b` (ex : secp256k1 pour Bitcoin).
- **Clé publique** : Point `Q = d*G` (où `G` est un point générateur).
- **ECDH** : Échange de clés sur courbes elliptiques.
**Exemple en Python (ECDSA)** :
```python
From [Link] import ECC
From [Link] import DSS
From [Link] import SHA256
Key = [Link](curve=’P-256’) # Clé privée
Pub_key = key.public_key() # Clé publique
# Signature
Message = b »Hello ECC »
Hash_msg = [Link](message)
Signer = [Link](key, ‘fips-186-3’)
Signature = [Link](hash_msg)
# Vérification
Verifier = [Link](pub_key, ‘fips-186-3’)
Try :
[Link](hash_msg, signature)
Print(« Signature valide. »)
Except ValueError :
Print(« Signature invalide. »)
```
### **4.2 Avantages vs RSA**
- **Clés plus courtes** : 256 bits (ECC) vs 3072 bits (RSA) pour une
sécurité similaire.
- **Efficacité** : Calculs plus rapides.
## **Comparatif des Algorithmes**
| **Algorithme** | **Type** | **Taille de Clé** | **Vitesse** |
**Sécurité** |
|----------------|---------------|-------------------|-------------|-----------------------|
| RSA | Asymétrique | 2048+ bits | Lente | Factorisation
|
| AES | Symétrique | 128/256 bits | Rapide | Brute-force
improbable|
| RC4 | Stream cipher | 40-2048 bits | Rapide | **Cassé**
(évitez) |
| ECC | Asymétrique | 256 bits | Rapide | Problème du log
discret|
## **Attaques Courantes et Parades**
1. **RSA** :
- **Factorisation** → Utiliser `n` de 3072+ bits.
- **Padding Oracle** → Préférer RSA-OAEP.
2. **AES** :
- **Side-channel** → Implémentations matérielles sécurisées.
3. **RC4** :
- **Biais statistiques** → **Ne plus l’utiliser**.
4. **ECC** :
- **Courbes faibles** → Utiliser des standards (NIST P-256, Curve25519).
## **Conclusion**
- **RSA** : Bon pour échange de clés/signatures, mais lent.
- **AES** : Standard actuel pour le chiffrement symétrique.
- **RC4** : **À éviter** en raison de vulnérabilités critiques.
- **ECC** : Futur de la cryptographie asymétrique (clés courtes, efficacité).
**Recommandations** :
- Pour TLS : ECDHE + AES-GCM.
- Pour stockage : AES-256.
- Pour signatures : ECDSA ou EdDSA.
Souhaitez-vous des détails sur les implémentations sécurisées ou des
exemples d’attaques avancées (ex : attaque ROCA sur RSA) ? 🔐#
**Attaques Avancées en Cryptographie**
## **1. Attaques Contre RSA**
### **1.1 ROCA (Return of the Coppersmith Attack)**
**Cible** : Clés RSA générées avec des bibliothèques vulnérables (ex :
Infineon TPM).
**Principe** :
- Exploite une structure algébrique faible dans la génération des nombres
premiers (`p = k * M + (65537^a mod M)`).
- Permet de factoriser `n` en temps polynomial.
**Détection** :
```python
From [Link] import RSA
Def is_roca_vulnerable(pub_key) :
# Vérifie si la clé utilise des paramètres Infineon vulnérables
Return pub_key.n % 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ==
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF – 1
```
**Contre-mesure** : Mettre à jour les bibliothèques de génération de clés.
### **1.2 Attaque par Broadcast (Hastad)**
**Scénario** :
Un même message est chiffré avec RSA et envoyé à 3 destinataires
différents (avec `e=3`).
**Attaque** :
- On récupère `c₁ = m³ mod n₁`, `c₂ = m³ mod n₂`, `c₃ = m³ mod n₃`.
- Le théorème des restes chinois permet de calculer `m³ mod (n₁*n₂*n₃)`.
- Comme `m³ < n₁*n₂*n₃`, on obtient `m` par racine cubique.
**Solution** : Utiliser un padding aléatoire (OAEP).
## **2. Attaques Contre AES**
### **2.1 Attaque Biclique (Reduction de la complexité brute-force)**
**Principe** :
- Réduit la complexité de AES-256 de 2²⁵⁶ à 2²⁵⁴.9.
- Combine des attaques par précalcul et des compromis temps-mémoire.
**Impact** : Théorique (pas exploitable en pratique).
### **2.2 Attaque par Canaux Cachés (Cache Timing)**
**Exemple** :
- Mesurer les accès cache pendant l’exécution d’AES pour déduire la clé.
- Fonctionne si les S-boxes sont implémentées avec des tables de lookup.
**Contre-mesure** :
- Implémentations constant-time (calculs bits-à-bits).
- Utiliser des instructions AES matérielles (AES-NI).
## **3. Attaques Contre ECC**
### **3.1 Attaque par Fault Injection (Courbes NIST P-256)**
**Scénario** :
- Injecter une erreur pendant un calcul ECDSA pour récupérer la clé privée.
- Exploite des fautes dans l’addition de points.
**Défense** :
- Vérifier que le point final est sur la courbe.
- Implémentations résistantes aux fautes.
### **3.2 Attaque par Pohlig-Hellman (Courbes Faibles)**
**Condition** :
- L’ordre du groupe (`#E(F_p)`) doit avoir de petits facteurs premiers.
**Exemple** :
```python
From [Link] import *
# Courbe faible : y² = x³ + 2x + 2 sur F_17 (ordre = 12 = 2² * 3)
E = EllipticCurve(GF(17), [2, 2])
P = [Link]()[0]
Q = 5 * P # Problème du log discret : trouver k tel que Q = k*P
K = discrete_log(Q, P, operation= »+ ») # Solution : k=5
```
**Solution** : Utiliser des courbes sûres (ed25519, secp256k1).
## **4. Attaques Contre les Protocoles**
### **4.1 CRIME (Compression Ratio Info-leak Made Easy)**
**Cible** : TLS avec compression (SPDY, HTTPS).
**Principe** :
- Devine des parties des cookies en observant la taille des données
compressées.
**Exemple** :
```python
Import zlib
Def crime_attack(secret_cookie, guess) :
Compressed_len = len([Link](secret_cookie + guess))
Return compressed_len
# Si crime_attack(« cookie=abc », « a ») < crime_attack(« cookie=abc »,
« b »), alors « a » est probablement dans le cookie.
```
**Solution** : Désactiver la compression TLS.
### **4.2 Attaque DROWN (Decrypting RSA with Obsolete and Weakened
eNcryption)**
**Cible** : Serveurs supportant SSLv2 et RSA.
**Exploit** :
- Utilise une vulnérabilité dans SSLv2 pour forcer le serveur à
déchiffrer un message RSA.
**Contre-mesure** : Désactiver SSLv2 et utiliser des clés distinctes.
## **5. Attaques Quantiques (Futur Proche)**
### **5.1 Algorithme de Shor**
**Impact** :
- Casse RSA et ECC en temps polynomial.
- Résout le problème de factorisation et du logarithme discret.
**Algorithmes Résistants** :
- **Cryptographie post-quantique** :
- **McEliece** (Codes correcteurs d’erreurs).
- **Lattice-based** (Kyber, NTRU).
**Exemple (LWE – Learning With Errors)** :
```python
From [Link] import *
# Génération de clés basée sur les réseaux euclidiens
N = 128 # Dimension
Q = 4096 # Module
A = random_matrix(GF(q), n, n)
S = random_vector(GF(q), n)
E = random_vector(GF(q), n).apply_map(lambda x : x % 3 – 1)
B = A * s + e # Clé publique = (A, b)
```
## **6. Tableau Synoptique des Attaques**
| **Algorithme** | **Attaque** | **Complexité** | **Contre-
mesure** |
|----------------|---------------------------|----------------------|-----------------------------------
-----|
| RSA | ROCA | Polynomiale | Mise à jour des
bibliothèques |
| AES | Biclique | 2²⁵⁴.9 | AES-256 + modes sûrs
(GCM) |
| ECC | Fault Injection | Quelques fautes | Vérification des
points |
| TLS | CRIME | Quelques requêtes | Désactiver la
compression |
| *Générique* | Ordinateur quantique | Polynomiale |
Cryptographie post-quantique |
## **Conclusion**
- **RSA/AES/ECC** : Vulnérables à des attaques spécialisées mais sûrs si
bien configurés.
- **Protocoles** : Les attaques (CRIME, DROWN) exploitent souvent des
configurations obsolètes.
- **Menace future** : L’ordinateur quantique nécessite une transition vers
des algorithmes post-quantiques.
**Recommandations** :
1. **Pour RSA** : Utiliser des clés 3072+ bits et OAEP.
2. **Pour AES** : Préférer AES-GCM et activer AES-NI.
3. **Pour ECC** : Choisir ed25519 ou secp256k1.
4. **Pour les protocoles** : Désactiver SSL/TLS anciens.
Souhaitez-vous des exemples détaillés sur une attaque spécifique ou des
implémentations sécurisées ? 🔐