# TP3 - Générateur de mots de passe - Listes et dictionnaires

## Objectifs du TP

Cette 3e itération a pour objectif de développer la fonctionnalité de génération de passphrase de notre générateur de mots de passe (3e et 4e options du menu). Nous utiliserons pour cela le processus décrit dans le document [Dice-Generated Passphrase](https://www.eff.org/dice) fourni par l'Electronic Frontier Foundation.

### Objectifs pédagogiques

- **Cybersécurité** : Comprendre l'intérêt et l'usage des passphrases
- **Listes** : Manipuler les listes en Python
- **Fonctions** : Renforcer la maîtrise des fonctions
- **Dictionnaires** : Manipuler les dictionnaires en Python

### Objectifs techniques

- **Passphrase** : Générer une passphrase sécurisée

## Validation

### Livrables

- Livrez le code final en plus du compte-rendu du TP dans un répertoire avec le nom suivant `YYYY-MM-DD_TPX_NOM1_Prenom1_NOM2_Prenom2`.
- Le compte rendu peut-être livré au format Markdown ou PDF. 
- Si vous choisissez le Markdown, attention à bien livrer les éventuelles images et à la validité des liens.

### Résultats attendus

- [ ] Le programme se lance sans erreur.
- [ ] La fonction `generer_passphrase` est créée avec les bons paramètres et renvoie une chaîne de caractères (str).
- [ ] La fonction `tirer_les_des` est créée avec les bons paramètres et renvoie une chaîne de caractères (str).
- [ ] La passphrase générée avec l'option 3 du menu contient 6 mots séparés par des tirets sur une seule ligne.
- [ ] La passphrase générée avec l'option 4 du menu contient un nombre de mots paramétrable via une interaction avec l'utilisateurice.
- [ ] Chaque fonction contient une docstring minimale expliquant sa fonctionnalité. Bonus : Doctsrings complètes au format Numpy.
- [ ] Le code est propre, correctement formaté, les variables, fonctions et paramètres sont bien nommés, les conventions sont respectées.

### Critères d'évaluation

- 50% Technique (10pts)
    - Code fonctionnel
    - Réponses dans le compte-rendu
    - Questions orale et compréhension générale
    - Connaissance du cours
- 50% Professionnalisme (10pts)
    - Posture professionnelle durant l'activité (3pts)
    - Qualité du rendu (Compte rendu - Documentation - Code) (3pts)
    - Respect des délais/horaires (2pts)
    - Autonomie dans la progression (2pts)
- Bonus +2pts

## Documents fournis

- Cours :
    - Python - 4 - Les structures de données : Détaille le fonctionnement des structures de données en Python (listes, dictionnaires, tuples, sets)
- Documents :
    - [Dice-generated passphrases (en)](https://www.eff.org/dice) : Propose un processus pour générer des passphrases sécurisées et précise le raisonnement ainsi que le contexte d'utilisation recommandé
    - [Python - Documentation - Data structures](https://docs.python.org/3/tutorial/datastructures.html)
- Fichiers
    - `eff_words.py` : fichier contenant des méthodes retournant l'ensemble des mots que l'on utilisera pour générer des passphrases.

## Étapes

### 1 - Etude documentaire

**Objectif :** Comprendre pourquoi et comment générer des passphrases

```admonish travail
- Lire le document de l'EFF [Dice-generated passphrases (en)](https://www.eff.org/dice)
```

```admonish note title="Dans le compte rendu"
1. Qu'est-ce que l'Electronic Frontier Foundation en quelques mots ?
2. Expliquez en quelques lignes le principe de la génération de passphrases selon le document
3. Dans quels cas l'EFF recommande d'utiliser les passphrases plutôt que des mots de passes aléatoires classiques ?
```

### 2 - Créer la fonction `generer_passphrase`

**Objectif :** Créer une fonction pour générer les passphrase et l'appeler dans le choix n°3 du menu

```admonish travail
1. Dans le module `mdp`, créer une nouvelle fonction `generer_passphrase` qui ne prend aucun paramètre.
2. Dans le corps de la fonction, **retourner uniquement** `passphrase-generee-par-defaut`. Ce sera temporaire, nous modifierons cette fonction ensuite.
3. Dans votre module principal (`main.py`), modifier le choix n°3 du menu pour **afficher** le résultat de la fonction `generer_passphrase` au lieu du print actuel.
4. Tester le programme, en choisissant le choix n°3 et en vérifiant que le programme vous affiche bien `passphrase-generee-par-defaut`
```

```admonish note title="Dans le compte rendu"
1. Mettre le code de la fonction `generer_passphrase`
2. Mettre le code de la fonction `choix_utilisateur` que vous avez modifiée
```

### 3 - Générer une phrase de passe à partir d'une liste fixe

**Objectif :** Générer la même phrase de passe toujours identique, mais cette fois en utilisant une liste de mots

```admonish warning title="Attention"
Penser à bien utiliser le cours pour cette question
```

```admonish travail
1. Dans le module `mdp`, la fonction `generer_passphrase` : créer une variable `words` qui sera une liste. Cette liste contiendra les mots "passphrase" "generee" "par" "defaut".
2. Créer une variable `passphrase` dans laquelle vous allez **concaténer** (c'est à dire mettre bout à bout) les éléments de votre liste `words`, grâce à la fonction `join`. Les mots devront être séparés par un tiret `-`.
3. Retournez votre nouvelle variable `passphrase`
4. Vérifiez que votre code fonctionne toujours à l'identique en lançant votre programme avec le choix n°3
```

```admonish help title="Aide"
Syntaxe pour créer une liste et la stocker dans une variable :
~~~python
ma_liste = ["elements", "de", "ma", "liste"]
ma_liste_de_nombres = [1, 2, 3, 4]
~~~
```

```admonish note title="Dans le compte rendu"
1. Mettre le code de la fonction `generer_passphrase`
2. Est-ce qu'une liste contient les éléments dans un ordre fixe ?
```

### 4 - Générer une phrase de passe aléatoire

**Objectif :** Générer une phrase de passe aléatoire à partir d'une liste de mots fournie

```admonish travail
1. Copier le fichier `eff_words.py` dans votre répertoire de travail. Il contient une fonction `get_word_list` qui vous retournera une liste de mots pour générer vos passphrases. Cette liste est directement tirée de l'article de l'EFF lu à l'étape 1.
2. Importer la fonction `get_word_list` du module `eff_words` que vous venez de copier. Utilisez la syntaxe `from ... import ...`.
3. Utiliser cette fonction pour affecter la liste de mots à votre variable `words` dans la fonction `generer_passphrase`
4. La liste contient des milliers de mots, il va donc falloir maintenant tirer au sort quelques mots pour créer notre passphrase. Comme dans le TP précédent, nous allons utiliser `secrets.choice`, mais cette fois au lieu de tirer des lettres dans `ALPHABET`, nous allons tirer des mots dans `words`.
    1. Créer une variable `passphrase_words` qui sera une liste vide, et qui nous servira à stocker les mots tirés au sort
    1. Créer une boucle qui bouclera 6 fois (avec `range`) (pour avoir 6 mots, ce qui est la recommandation minimale de l'EFF)
    2. Dans cette boucle, nous ajouterons un mot tiré au sort dans notre liste.
5. Modifier le calcul de notre variable `passphrase` (celle dans laquell on vient assembler les mots) pour utiliser notre liste de mots tirés au sort
6. Tester le programme pour vérifier que nos passphrases sont bien générées aléatoirement
```

```admonish help title="Aide"
Syntaxe pour ajouter un élément dans une liste ([doc](https://docs.python.org/3/tutorial/datastructures.html)):
~~~python
ma_liste.append(mon_nouvel_element)
~~~
```

```admonish tip title="Remarque"
Les chaînes de caractères et les listes ont souvent des comportements comparables. En réalité, une chaîne de caractères est presque comme une liste de caractères. C'est pour cela que de nombreuses choses qui fonctionnent avec les listes marchent aussi avec les chaînes de caractères.

La fonction `secrets.choice` par exemple va tirer un élément au hasard dans un ensemble. Cela fonctionne aussi bien avec les listes que les chaînes de caractères.
```

```admonish note title="Dans le compte rendu"
1. Donnez le code de la fonction `generer_passphrase`
```

### 5 - Rendre paramétrable la longueur de la passphrase

**Objectif :** Modifier la fonction `generer_passphrase` pour y ajouter un paramètre permettant de choisir le nombre de mots

```admonish travail
1. Ajouter un paramètre `nb_words` dans la fonction `generer_passphrase`, qui aura pour valeur par défaut 6
2. Modifier la boucle pour qu'elle tire au sort `nb_words` mots.
3. Vérifier que votre choix n°3 fonctionne toujours sans autre modification
4. Modifier le choix n°4 pour :
    - Demander à l'utilisateur le nombre de mots souhaité
    - Appeler votre fonction `generer_passphrase` en lui passant en paramètre le nombre de mots demandé par l'utilisateur
```

```admonish note title="Dans le compte rendu"
1. Mettre le code de la fonction `generer_passphrase`
```

### 6 - Utiliser un dictionnaire et un jet de 5 dés

**Objectif** : Respecter rigoureusement la procédure de l'EFF en lançant des dés qui serviront à choisir les mots de la passphrase

```admonish travail
1. Créer une fonction `generer_dice_passphrase` qui sera une copie de `generer_passphrase` pour commencer
2. Créer une fonction `tirer_les_des` qui lancera 5 dés grâce à [`secrets.randbelow`](https://docs.python.org/3/library/secrets.html#secrets.randbelow). Cette fonction concatènera les 5 résultats en une seule chaîne de caractère. Ex: si on tire successivement 1, 2, 3, 4 et 5 elle renverra `"12345"`
3. Remplacer l'appel à `get_word_list` par `get_word_dict` pour obtenir un dictionnaire comportant les valeurs des jets de dés et les mots. Penser à modifier votre import depuis `eff_words` si besoin.
4. Modifier la boucle de `generer_dice_passphrase` pour 
    - appeler `tirer_les_des` et récupérer le jet de dés
    - récupérer le mot dans le dictionnaire `words` à l'aide du jet de dés, et l'ajouter à `passphrase_words`
5. Modifier le code de votre menu pour utiliser la nouvelle fonction `generer_dice_passphrase`
6. Vérifier que tout fonctionne correctement
```

```admonish help title="Aide"
- Attention, un dé a un résultat entre 1 et 6. `secrets.randbelow` donnera un résultat entre zéro et l'argument que vous lui fournirez.
- `join` s'utilise uniquement avec des chaines de caractères, pas des entiers. Il faudra penser à convertir.
- Récupérer une valeur dans un dictionnaire et la stocker dans une variable :
    ~~~python
    ma_valeur = mon_dict[key]
    ~~~
    Dans notre cas, le tirage de dés est la clé (key) et la valeur souhaitée est le mot.
```

```admonish note title="Dans le compte rendu"
1. Mettre le code de votre fonction `generer_dice_passphrase`
```


## Pour aller plus loin

### Bonus facile - Docstrings au format Numpy

**Objectif**: Documenter votre code en ajoutant des docstrings au format Numpy


### Bonus intermédiaire - Complexifier les passphrases générées

**Objectif** : Tous les mots de la passphrase commencent par une majuscule et se terminent par un caractère spécial ou un chiffre

```admonish travail
1. Modifier les mots tirés au sort [avec la **méthode** `capitalize`](https://docs.python.org/3/library/stdtypes.html#str.capitalize) pour qu'ils commencent par une majuscule
2. Modifier les mots pour ajouter au bout de chaque mot un caractère spécial ou un chiffre tiré au sort avec `secrets.choice`
3. Rendre paramétrables ces deux possibilités dans le choix n°4 du menu (et les désactiver par défaut dans le choix n°3)
```