fhe-cli 1.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fhe-cli might be problematic. Click here for more details.
- fhe_cli-1.3.1.dist-info/METADATA +280 -0
- fhe_cli-1.3.1.dist-info/RECORD +37 -0
- fhe_cli-1.3.1.dist-info/WHEEL +5 -0
- fhe_cli-1.3.1.dist-info/entry_points.txt +2 -0
- fhe_cli-1.3.1.dist-info/licenses/LICENSE +21 -0
- fhe_cli-1.3.1.dist-info/top_level.txt +1 -0
- fhecli/__init__.py +5 -0
- fhecli/__main__.py +6 -0
- fhecli/cli.py +220 -0
- fhecli/data/info.toml +77 -0
- fhecli/data/originals/ex10_ckks_bonus.py +84 -0
- fhecli/data/originals/ex1_cesar.py +92 -0
- fhecli/data/originals/ex2_xor_malleability.py +102 -0
- fhecli/data/originals/ex3_fernet_tamper.py +92 -0
- fhecli/data/originals/ex4_modpow.py +73 -0
- fhecli/data/originals/ex5_modinv.py +77 -0
- fhecli/data/originals/ex6_paillier_keygen.py +85 -0
- fhecli/data/originals/ex7_paillier_enc_dec.py +118 -0
- fhecli/data/originals/ex8_paillier_homomorphic.py +144 -0
- fhecli/data/originals/ex9_scoring_paillier.py +143 -0
- fhecli/data/solutions/ex10_ckks_bonus.sol +8 -0
- fhecli/data/solutions/ex1_cesar.sol +6 -0
- fhecli/data/solutions/ex2_xor_malleability.sol +7 -0
- fhecli/data/solutions/ex3_fernet_tamper.sol +7 -0
- fhecli/data/solutions/ex4_modpow.sol +5 -0
- fhecli/data/solutions/ex5_modinv.sol +6 -0
- fhecli/data/solutions/ex6_paillier_keygen.sol +8 -0
- fhecli/data/solutions/ex7_paillier_enc_dec.sol +11 -0
- fhecli/data/solutions/ex8_paillier_homomorphic.sol +12 -0
- fhecli/data/solutions/ex9_scoring_paillier.sol +15 -0
- fhecli/info.py +46 -0
- fhecli/resources.py +28 -0
- fhecli/runner.py +72 -0
- fhecli/solutions.py +30 -0
- fhecli/ui.py +70 -0
- fhecli/watch.py +116 -0
- fhecli/workspace.py +58 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fhe-cli
|
|
3
|
+
Version: 1.3.1
|
|
4
|
+
Summary: Atelier d'exercices en ligne de commande, à la rustlings : chiffrement classique et chiffrement homomorphe (FHE)
|
|
5
|
+
Author: Ravin THILAGARASA, Thomas GRUNDSZTEJN
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Defl3ct-941/Scoring_FHE
|
|
8
|
+
Project-URL: Source, https://github.com/Defl3ct-941/Scoring_FHE/tree/main/03_cas_pratiques/fhe-exercices-cli
|
|
9
|
+
Project-URL: Issues, https://github.com/Defl3ct-941/Scoring_FHE/issues
|
|
10
|
+
Project-URL: GitLab, https://gitlab.securechat.fr/esgi/5a/Scoring_FHE
|
|
11
|
+
Project-URL: Changelog, https://github.com/Defl3ct-941/Scoring_FHE/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: fhe,chiffrement homomorphe,homomorphic encryption,cryptographie,cryptography,exercices,rustlings,enseignement,scoring
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Education
|
|
16
|
+
Classifier: Natural Language :: French
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
25
|
+
Classifier: Topic :: Education
|
|
26
|
+
Classifier: Topic :: Security :: Cryptography
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: pytest<10,>=9.2
|
|
31
|
+
Requires-Dist: cryptography<51,>=50.0
|
|
32
|
+
Provides-Extra: ckks
|
|
33
|
+
Requires-Dist: tenseal<0.4,>=0.3.16; extra == "ckks"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# fhecli : atelier CLI de chiffrement classique et FHE
|
|
37
|
+
|
|
38
|
+
Un atelier d'exercices en ligne de commande, inspiré de
|
|
39
|
+
[rustlings](https://github.com/rust-lang/rustlings) : vous éditez les fichiers
|
|
40
|
+
d'exercice dans votre propre éditeur, avec votre propre environnement Python,
|
|
41
|
+
et un petit outil (`fhecli`) surveille vos fichiers et relance les tests à
|
|
42
|
+
chaque sauvegarde.
|
|
43
|
+
|
|
44
|
+
Il accompagne le cours « Scoring financier sur données chiffrées via FHE »
|
|
45
|
+
(FYC, ESGI) : c'est la même progression pédagogique que l'atelier web du
|
|
46
|
+
projet, adaptée pour tourner directement dans votre terminal.
|
|
47
|
+
|
|
48
|
+
> **Version actuelle : 3 exercices.** Seul le module A (chiffrement
|
|
49
|
+
> classique) est publié pour le moment. Les modules suivants (arithmétique
|
|
50
|
+
> modulaire, Paillier from scratch, scoring chiffré, bonus CKKS) arriveront
|
|
51
|
+
> dans une prochaine mise à jour.
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
Python 3.11 ou plus récent est requis.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python -m venv .venv
|
|
59
|
+
# Windows
|
|
60
|
+
.venv\Scripts\activate
|
|
61
|
+
# macOS / Linux
|
|
62
|
+
source .venv/bin/activate
|
|
63
|
+
|
|
64
|
+
pip install fhe-cli
|
|
65
|
+
fhecli init # crée le dossier ./fhe-exercices avec les exercices
|
|
66
|
+
cd fhe-exercices
|
|
67
|
+
fhecli watch
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Le paquet s'installe sous le nom **`fhe-cli`** ; la commande et le module
|
|
71
|
+
Python restent `fhecli`.
|
|
72
|
+
|
|
73
|
+
`fhecli init [dossier]` crée un **espace de travail** : le dossier
|
|
74
|
+
`exercises/` (vos fichiers), un `pytest.ini` et un fichier marqueur
|
|
75
|
+
`fhecli.toml`. Les commandes de `fhecli` retrouvent cet espace depuis n'importe
|
|
76
|
+
quel sous-dossier. `fhecli init` refuse d'écraser un espace existant.
|
|
77
|
+
|
|
78
|
+
### Sans Python : exécutable autonome
|
|
79
|
+
|
|
80
|
+
Pour chaque OS, un exécutable unique embarque tout (Python, `fhecli`,
|
|
81
|
+
`pytest`, `cryptography` et les exercices) : **aucune installation de Python
|
|
82
|
+
n'est nécessaire**. Téléchargez celui de votre système
|
|
83
|
+
(`fhecli-<version>-windows-x86_64.exe`, `-linux-x86_64`, `-macos-arm64` ou
|
|
84
|
+
`-macos-x86_64`), puis :
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Windows (PowerShell)
|
|
88
|
+
.hecli-1.2.0-windows-x86_64.exe init
|
|
89
|
+
# macOS / Linux
|
|
90
|
+
chmod +x fhecli-1.2.0-linux-x86_64
|
|
91
|
+
./fhecli-1.2.0-linux-x86_64 init
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Renommez-le en `fhecli` (ou `fhecli.exe`) et placez-le dans un dossier du
|
|
95
|
+
`PATH` pour l'utiliser comme dans les exemples ci-dessous. Le code et les
|
|
96
|
+
solutions y sont embarqués compressés, jamais en clair. Sous macOS, au premier
|
|
97
|
+
lancement, autorisez le binaire dans Réglages système > Confidentialité et
|
|
98
|
+
sécurité (il n'est pas signé).
|
|
99
|
+
|
|
100
|
+
## Utilisation
|
|
101
|
+
|
|
102
|
+
### Mode guidé (recommandé pour commencer)
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
fhecli watch
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Affiche l'exercice courant, relance ses tests à chaque fois que vous
|
|
109
|
+
sauvegardez le fichier, et passe automatiquement au suivant une fois qu'il est
|
|
110
|
+
résolu (en proposant alors d'afficher la solution de référence pour comparer).
|
|
111
|
+
Ctrl+C pour quitter à tout moment : rien n'est perdu, vos fichiers restent sur
|
|
112
|
+
le disque.
|
|
113
|
+
|
|
114
|
+
### Autres commandes
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
fhecli list # vue d'ensemble des exercices et de leur statut
|
|
118
|
+
fhecli run ex1_cesar # lance les tests d'un seul exercice
|
|
119
|
+
fhecli hint ex1_cesar # affiche un indice ciblé
|
|
120
|
+
fhecli solution ex1_cesar # affiche la solution (une fois l'exercice résolu)
|
|
121
|
+
fhecli reset ex1_cesar # remet CET exercice à son énoncé de départ (avec confirmation)
|
|
122
|
+
fhecli reset --all # remet TOUS les exercices à leur énoncé de départ (avec confirmation)
|
|
123
|
+
fhecli reset --all -y # idem, sans confirmation
|
|
124
|
+
fhecli --version
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Codes de sortie de `fhecli run` : `0` exercice résolu, `1` tests en échec
|
|
128
|
+
(ou erreur), `2` tests OK mais ligne `# I AM NOT DONE` encore présente.
|
|
129
|
+
|
|
130
|
+
Vous pouvez aussi lancer les tests vous-même avec `python -m pytest` depuis
|
|
131
|
+
l'espace de travail : le `pytest.ini` fourni collecte bien les exercices.
|
|
132
|
+
|
|
133
|
+
## Comment marche un exercice
|
|
134
|
+
|
|
135
|
+
Chaque exercice est un unique fichier `.py` dans `exercises/`, qui contient
|
|
136
|
+
tout :
|
|
137
|
+
|
|
138
|
+
1. Un docstring en haut du fichier avec l'énoncé complet (contexte,
|
|
139
|
+
formules, ce qu'il faut implémenter, indices).
|
|
140
|
+
2. Une ligne `# I AM NOT DONE` juste après : tant qu'elle est présente,
|
|
141
|
+
l'exercice est considéré comme « pas terminé », même si les tests passent
|
|
142
|
+
déjà. **Retirez-la vous-même** une fois satisfait de votre solution :
|
|
143
|
+
c'est un geste délibérément manuel, pour vous forcer à vraiment valider
|
|
144
|
+
votre travail.
|
|
145
|
+
3. La ou les fonctions à compléter (marquées `raise NotImplementedError`).
|
|
146
|
+
4. Une section de tests en bas du fichier (à ne pas modifier), exécutée par
|
|
147
|
+
`pytest` à chaque `fhecli run` / `fhecli watch`.
|
|
148
|
+
|
|
149
|
+
## Solutions
|
|
150
|
+
|
|
151
|
+
La solution de référence d'un exercice est **verrouillée** tant que
|
|
152
|
+
l'exercice n'est pas résolu : `fhecli solution` ne l'affiche qu'une fois que
|
|
153
|
+
les tests passent **et** que la ligne `# I AM NOT DONE` a été retirée.
|
|
154
|
+
|
|
155
|
+
Dans le paquet, les solutions ne sont pas stockées en clair : elles sont
|
|
156
|
+
compressées et encodées (zlib + base64). C'est une **dissuasion**, pour
|
|
157
|
+
éviter de tomber sur la correction par hasard en fouillant le dossier du
|
|
158
|
+
paquet, **pas une protection** : rien n'empêche quelqu'un de déterminé de les
|
|
159
|
+
décoder. Le vrai garde-fou reste pédagogique.
|
|
160
|
+
|
|
161
|
+
## Progression (dans l'ordre suivi par `fhecli watch`)
|
|
162
|
+
|
|
163
|
+
| # | Exercice | Catégorie |
|
|
164
|
+
|---|----------|-----------|
|
|
165
|
+
| 1 | ex1_cesar | A. Chiffrement classique |
|
|
166
|
+
| 2 | ex2_xor_malleability | A. Chiffrement classique |
|
|
167
|
+
| 3 | ex3_fernet_tamper | A. Chiffrement classique |
|
|
168
|
+
| 4 | ex4_modpow | B. Briques mathématiques |
|
|
169
|
+
| 5 | ex5_modinv | B. Briques mathématiques |
|
|
170
|
+
| 6 | ex6_paillier_keygen | C. Paillier, pas à pas |
|
|
171
|
+
| 7 | ex7_paillier_enc_dec | C. Paillier, pas à pas |
|
|
172
|
+
| 8 | ex8_paillier_homomorphic | C. Paillier, pas à pas |
|
|
173
|
+
| 9 | ex9_scoring_paillier | D. Scoring chiffré |
|
|
174
|
+
| 10 | ex10_ckks_bonus | E. Bonus : FHE réel avec CKKS |
|
|
175
|
+
|
|
176
|
+
Le parcours va du chiffrement classique (où tout calcul sur le chiffré est
|
|
177
|
+
impossible) à Paillier reconstruit à la main, puis à un score bancaire chiffré
|
|
178
|
+
de bout en bout, et enfin à un vrai calcul FHE avec CKKS.
|
|
179
|
+
|
|
180
|
+
Le bonus **ex10 (CKKS)** s'appuie sur [TenSEAL](https://github.com/OpenMined/TenSEAL),
|
|
181
|
+
une dépendance native qui n'est pas installée par défaut (et qui n'est pas
|
|
182
|
+
embarquée dans les exécutables autonomes). Pour le faire :
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
pip install "fhe-cli[ckks]"
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Sans TenSEAL, les tests de l'ex10 sont simplement ignorés (skipped).
|
|
189
|
+
|
|
190
|
+
## Développement (depuis le dépôt Scoring FHE)
|
|
191
|
+
|
|
192
|
+
Le dossier `03_cas_pratiques/fhe-exercices-cli` du dépôt est lui-même un
|
|
193
|
+
espace de travail (il contient `fhecli.toml`, `pytest.ini` et `exercises/`) :
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
cd 03_cas_pratiques/fhe-exercices-cli
|
|
197
|
+
python -m venv .venv
|
|
198
|
+
.venv\Scripts\activate # macOS / Linux : source .venv/bin/activate
|
|
199
|
+
pip install -e .
|
|
200
|
+
fhecli watch
|
|
201
|
+
python -m pytest tests -q # tests de l'outil lui-même
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Avant de commiter : `fhecli reset --all -y`, pour ne jamais versionner de
|
|
205
|
+
réponses dans `exercises/`.
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
fhe-exercices-cli/
|
|
209
|
+
├── pyproject.toml # empaquetage (PyPI), point d'entrée `fhecli`
|
|
210
|
+
├── LICENSE, MANIFEST.in
|
|
211
|
+
├── fhecli/ # le paquet
|
|
212
|
+
│ ├── cli.py # commandes : init, watch, list, run, hint, solution, reset
|
|
213
|
+
│ ├── workspace.py # création et recherche de l'espace de travail
|
|
214
|
+
│ ├── watch.py # boucle de surveillance des fichiers
|
|
215
|
+
│ ├── runner.py # exécution de pytest en sous-processus isolé
|
|
216
|
+
│ ├── info.py, resources.py # liste des exercices, accès aux données du paquet
|
|
217
|
+
│ ├── solutions.py # encodage / décodage des solutions
|
|
218
|
+
│ ├── ui.py # couleurs, UTF-8, confirmations
|
|
219
|
+
│ └── data/ # embarqué dans le paquet (lecture seule)
|
|
220
|
+
│ ├── info.toml # métadonnées des exercices (titre, catégorie, indice)
|
|
221
|
+
│ ├── originals/ # énoncés d'origine (fhecli init / reset)
|
|
222
|
+
│ └── solutions/ # solutions ENCODÉES (.sol)
|
|
223
|
+
├── solutions_src/ # solutions en clair : dans le dépôt, JAMAIS dans la wheel ni le sdist
|
|
224
|
+
├── scripts/ # encode_solutions.py, build_binary.py, check_binary.py
|
|
225
|
+
├── packaging/ # spécification PyInstaller (exécutables autonomes)
|
|
226
|
+
├── tests/ # tests automatisés de l'outil
|
|
227
|
+
├── exercises/ # espace de travail de développement
|
|
228
|
+
├── fhecli.toml, pytest.ini # marqueur et configuration de cet espace
|
|
229
|
+
└── requirements.txt
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Après toute modification d'une solution dans `solutions_src/` :
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
python scripts/encode_solutions.py
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Construire le paquet (sans le publier) :
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
python -m pip install build
|
|
242
|
+
python -m build # dist/fhe_cli-<version>-py3-none-any.whl et .tar.gz
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Exécutables, CI et publication
|
|
246
|
+
|
|
247
|
+
Construire et vérifier l'exécutable de l'OS courant (PyInstaller ne fait pas
|
|
248
|
+
de compilation croisée) :
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
pip install -e . pyinstaller
|
|
252
|
+
python scripts/build_binary.py # dist/fhecli-<version>-<os>-<arch>[.exe]
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`scripts/build_binary.py` lance ensuite `scripts/check_binary.py`, qui
|
|
256
|
+
rejoue le parcours d'un étudiant avec le binaire seul (sans Python dans le
|
|
257
|
+
`PATH`) et vérifie qu'aucun source, aucune solution ni aucun énoncé n'apparaît
|
|
258
|
+
en clair dans le fichier.
|
|
259
|
+
|
|
260
|
+
La CI **GitHub Actions** (`.github/workflows/fhecli.yml`) fait tout cela
|
|
261
|
+
automatiquement : tests sur Linux, Windows et macOS (Python 3.11 et 3.15),
|
|
262
|
+
exécutables Windows, Linux, macOS arm64 et macOS x86_64, wheel et sdist.
|
|
263
|
+
|
|
264
|
+
Pour publier une version : mettre à jour `fhecli.__version__` et le
|
|
265
|
+
CHANGELOG, puis pousser un tag `fhecli-vX.Y.Z` (il doit correspondre à la
|
|
266
|
+
version). GitHub crée alors une release avec les exécutables et publie sur
|
|
267
|
+
PyPI. Une version publiée sur PyPI ne peut pas être republiée : en cas de
|
|
268
|
+
problème, publier la version suivante.
|
|
269
|
+
|
|
270
|
+
Configuration à faire une seule fois :
|
|
271
|
+
|
|
272
|
+
- PyPI, « trusted publishing » : sur pypi.org, ajouter un *pending publisher*
|
|
273
|
+
pour le projet `fhecli` (dépôt `Defl3ct-941/Scoring_FHE`, workflow
|
|
274
|
+
`fhecli.yml`, environnement `pypi`), et créer l'environnement `pypi` dans
|
|
275
|
+
les réglages du dépôt GitHub (limité aux tags `fhecli-v*`). Aucun jeton
|
|
276
|
+
API n'est nécessaire.
|
|
277
|
+
|
|
278
|
+
## Licence
|
|
279
|
+
|
|
280
|
+
MIT, voir [LICENSE](LICENSE).
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
fhe_cli-1.3.1.dist-info/licenses/LICENSE,sha256=vWfv3xoZi_mWXZkX8miHXnN_V6U6cAi6gLb3YhgiZys,1094
|
|
2
|
+
fhecli/__init__.py,sha256=KsQ8hLzDhFfh7u8kltwTRXdOzHkbD_MTvZbjIJstyw8,177
|
|
3
|
+
fhecli/__main__.py,sha256=r6-2p5dAcP4a77CVKwjBx9EmW267PfbI9bTPBJQvC2o,119
|
|
4
|
+
fhecli/cli.py,sha256=U3dj7bWb5WQM26-UH31GCuilkb9iZKrV8ehYqviMyCo,8223
|
|
5
|
+
fhecli/info.py,sha256=3iVuPgPIzGl3KMODPOYAJjm2vzj2meEDsTI3Z-0U16o,1201
|
|
6
|
+
fhecli/resources.py,sha256=FcCEhubBAeEhL9aCCweJOz-H49zptA6o6usVmY-DNQw,842
|
|
7
|
+
fhecli/runner.py,sha256=m_jkNm1Pgb7KSm1890KieCVGCMxGZ2dZQ4LwcvaZyMA,2371
|
|
8
|
+
fhecli/solutions.py,sha256=1LyTfymz8AbZAWoL3v1elzBh2yxxQHjabbyDO36nTA4,1173
|
|
9
|
+
fhecli/ui.py,sha256=nZC5y4lOj40MXrwvIkoscnIIHQUlVRvB_8OQHYUgSjg,1760
|
|
10
|
+
fhecli/watch.py,sha256=6Dg37DiEBV2luN0mzHc_F3KUFlHQ4k0EOtYi_ecKJYk,4422
|
|
11
|
+
fhecli/workspace.py,sha256=dnaZRfQIo13JMv6u3Thf59ASOzqMTaDGNPP5mG2KSFU,2059
|
|
12
|
+
fhecli/data/info.toml,sha256=yk9RDSMCHvFrjG9pD2JJrzpPpW514iyPyzfLLtHO4QQ,4024
|
|
13
|
+
fhecli/data/originals/ex10_ckks_bonus.py,sha256=-54y7tm7WvFKrkVwMMo5262cmknfdd_09qxmIOIAhms,3181
|
|
14
|
+
fhecli/data/originals/ex1_cesar.py,sha256=8fAbsJHWLTe5venxOINIDB5UU6HY_SEi1rrLOSGIklc,3165
|
|
15
|
+
fhecli/data/originals/ex2_xor_malleability.py,sha256=mLUOa-1c9KiIwd8pm9t-rPrWWz4STE_MAgGzHYbsaH4,3767
|
|
16
|
+
fhecli/data/originals/ex3_fernet_tamper.py,sha256=dSfH_zeHrsFVzJih0kyQ25O-7VJ6yv565S4RcMmigTI,3180
|
|
17
|
+
fhecli/data/originals/ex4_modpow.py,sha256=7E0MDUDDQIuwy_UfWa78W3ThfJYws9qovpl51ab-FLA,2526
|
|
18
|
+
fhecli/data/originals/ex5_modinv.py,sha256=e-zUlyF-1WnuY69r-wx1uTQdo3x1U-7y77ScrzDHKFo,2535
|
|
19
|
+
fhecli/data/originals/ex6_paillier_keygen.py,sha256=Fyhcd-rpZDDbzvzGDRrgspXGrv6LgIbu_wBq2a6LL7o,2723
|
|
20
|
+
fhecli/data/originals/ex7_paillier_enc_dec.py,sha256=gxQ3ciXMR5hFjbed4_nBlFt3MwIU_hnxiYb2XvCBodk,3670
|
|
21
|
+
fhecli/data/originals/ex8_paillier_homomorphic.py,sha256=HkUJEO-l-_m6y0mBJv5UtdMdmb8TMiZ75uvN24Pq8d4,4494
|
|
22
|
+
fhecli/data/originals/ex9_scoring_paillier.py,sha256=sZesevdix9KJOdbJ3ul4fBCguKIGN3xoStaJrEdDMmE,4497
|
|
23
|
+
fhecli/data/solutions/ex10_ckks_bonus.sol,sha256=Qc_Wehf7gmy5gkgbzB7weHLeg8zvlk7PJ2q14wDWyKU,545
|
|
24
|
+
fhecli/data/solutions/ex1_cesar.sol,sha256=uKkiDnpdzTw344mxT57k9BqHcm_KfW0JaMeOLcSLD7U,363
|
|
25
|
+
fhecli/data/solutions/ex2_xor_malleability.sol,sha256=ftzmK_TBaHumWqA39BvcxrNO2EJkUosijrkKyKQDUhA,440
|
|
26
|
+
fhecli/data/solutions/ex3_fernet_tamper.sol,sha256=BTpUkFwci0CxEuoRiLg4pf36-k_7TjR6sS760qo4wzk,424
|
|
27
|
+
fhecli/data/solutions/ex4_modpow.sol,sha256=SYBr20o1WesQSotVBoary2ErOTmpFDESjEq2K_T95sw,286
|
|
28
|
+
fhecli/data/solutions/ex5_modinv.sol,sha256=eZQLwqfghF7w8XpEydcLkO_LE-Gl3ehKm4yF7d-YWys,391
|
|
29
|
+
fhecli/data/solutions/ex6_paillier_keygen.sol,sha256=rEPnicNhe3xaHqAJn447zdr02uT_G0wlvc38OSllpu8,549
|
|
30
|
+
fhecli/data/solutions/ex7_paillier_enc_dec.sol,sha256=gnaw9EHj9h4S0ZmI4Y7W1UYuKTSQe9VYhk2_1y0U6xo,716
|
|
31
|
+
fhecli/data/solutions/ex8_paillier_homomorphic.sol,sha256=D0zoixYhg8PlmKbxROoT0hPGUAlN3nPzI9iHapq3_w0,817
|
|
32
|
+
fhecli/data/solutions/ex9_scoring_paillier.sol,sha256=9tBin-kI7puipz0VJ66cR7aeswpf71cCr7CVHhKhbDU,1068
|
|
33
|
+
fhe_cli-1.3.1.dist-info/METADATA,sha256=atRRMKbxGoO_SG7ry9AazKK82DvmdIRnzwbIV9cKgLM,11897
|
|
34
|
+
fhe_cli-1.3.1.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
35
|
+
fhe_cli-1.3.1.dist-info/entry_points.txt,sha256=Rhwd2ixRkUJlh3COEK8uOd72ZBYVqqjSB0Oih5lhC-E,43
|
|
36
|
+
fhe_cli-1.3.1.dist-info/top_level.txt,sha256=X1XW-ry2gtjQW9sgKA8Z9Q_5Q1_XTOb8AxVZCpTskVs,7
|
|
37
|
+
fhe_cli-1.3.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ravin THILAGARASA, Thomas GRUNDSZTEJN
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fhecli
|
fhecli/__init__.py
ADDED
fhecli/__main__.py
ADDED
fhecli/cli.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""Point d'entrée de la commande `fhecli`."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from fhecli import __version__, resources, ui
|
|
8
|
+
from fhecli.info import exercise_names, find_exercise, load_exercises
|
|
9
|
+
from fhecli.runner import PYTEST_SUBCOMMAND, has_marker, run_tests, status
|
|
10
|
+
from fhecli.watch import watch as watch_loop
|
|
11
|
+
from fhecli.workspace import DEFAULT_DIR, WorkspaceNotFound, find_workspace, init_workspace
|
|
12
|
+
|
|
13
|
+
# Codes de sortie
|
|
14
|
+
EXIT_OK = 0
|
|
15
|
+
EXIT_ERROR = 1 # tests en échec, exercice inconnu, pas d'espace de travail...
|
|
16
|
+
EXIT_MARKER_LEFT = 2 # tests OK mais « # I AM NOT DONE » encore présent
|
|
17
|
+
|
|
18
|
+
STATUS_ICON = {
|
|
19
|
+
"done": ui.success("[x]"),
|
|
20
|
+
"almost": ui.warn("[~]"),
|
|
21
|
+
"pending": ui.failure("[ ]"),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _workspace() -> Path:
|
|
26
|
+
try:
|
|
27
|
+
return find_workspace()
|
|
28
|
+
except WorkspaceNotFound:
|
|
29
|
+
print(ui.failure("Aucun espace de travail fhecli trouvé dans ce dossier ni dans ses parents."))
|
|
30
|
+
print(f"Lancez d'abord {ui.bold('fhecli init')} (il crée le dossier ./{DEFAULT_DIR}),")
|
|
31
|
+
print("puis placez-vous dans ce dossier.")
|
|
32
|
+
raise SystemExit(EXIT_ERROR)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _exercise_or_exit(name: str):
|
|
36
|
+
ex = find_exercise(_workspace(), name)
|
|
37
|
+
if ex is None:
|
|
38
|
+
print(ui.failure(f"Exercice inconnu : {name}"))
|
|
39
|
+
print(f"Utilisez {ui.bold('fhecli list')} pour voir les noms disponibles.")
|
|
40
|
+
raise SystemExit(EXIT_ERROR)
|
|
41
|
+
return ex
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def cmd_init(args) -> int:
|
|
45
|
+
target = Path(args.directory)
|
|
46
|
+
try:
|
|
47
|
+
ws = init_workspace(target, exercise_names())
|
|
48
|
+
except FileExistsError:
|
|
49
|
+
print(ui.failure(f"Un espace de travail existe déjà dans {target.resolve()}."))
|
|
50
|
+
print("Rien n'a été modifié. Choisissez un autre dossier : fhecli init <dossier>")
|
|
51
|
+
return EXIT_ERROR
|
|
52
|
+
print(ui.success(f"Espace de travail créé : {ws}"))
|
|
53
|
+
print()
|
|
54
|
+
print("Pour commencer :")
|
|
55
|
+
print(f" cd {ws}")
|
|
56
|
+
print(" fhecli watch")
|
|
57
|
+
return EXIT_OK
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def cmd_list(args) -> int:
|
|
61
|
+
exercises = load_exercises(_workspace())
|
|
62
|
+
print(ui.bold(f"{len(exercises)} exercices :"))
|
|
63
|
+
print()
|
|
64
|
+
last_category = None
|
|
65
|
+
done_count = 0
|
|
66
|
+
for ex in exercises:
|
|
67
|
+
if ex.category != last_category:
|
|
68
|
+
print(ui.info(ex.category))
|
|
69
|
+
last_category = ex.category
|
|
70
|
+
st = status(ex.path)
|
|
71
|
+
if st == "done":
|
|
72
|
+
done_count += 1
|
|
73
|
+
bonus_tag = ui.warn(" (bonus)") if ex.bonus else ""
|
|
74
|
+
print(f" {STATUS_ICON[st]} {ex.name:<24} {ex.title}{bonus_tag}")
|
|
75
|
+
print()
|
|
76
|
+
print(f"Progression : {done_count}/{len(exercises)} exercices résolus.")
|
|
77
|
+
print(ui.DIM + "[x] résolu [~] tests OK, marqueur à retirer [ ] pas encore bon" + ui.RESET)
|
|
78
|
+
return EXIT_OK
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def cmd_run(args) -> int:
|
|
82
|
+
ex = _exercise_or_exit(args.name)
|
|
83
|
+
print(ui.bold(f"{ex.name} - {ex.title}"))
|
|
84
|
+
print(f"Fichier : {ex.path}")
|
|
85
|
+
print()
|
|
86
|
+
passed, output = run_tests(ex.path)
|
|
87
|
+
print(output)
|
|
88
|
+
print()
|
|
89
|
+
if passed and has_marker(ex.path):
|
|
90
|
+
print(ui.warn(
|
|
91
|
+
"Tests OK, mais le fichier contient encore « # I AM NOT DONE ». "
|
|
92
|
+
"Retirez cette ligne une fois satisfait de votre solution."
|
|
93
|
+
))
|
|
94
|
+
return EXIT_MARKER_LEFT
|
|
95
|
+
if passed:
|
|
96
|
+
print(ui.success("Tous les tests passent."))
|
|
97
|
+
return EXIT_OK
|
|
98
|
+
print(ui.failure("Des tests échouent encore."))
|
|
99
|
+
print(f"Besoin d'un coup de pouce ? {ui.bold('fhecli hint ' + ex.name)}")
|
|
100
|
+
return EXIT_ERROR
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def cmd_hint(args) -> int:
|
|
104
|
+
ex = _exercise_or_exit(args.name)
|
|
105
|
+
print(ui.bold(f"Indice pour {ex.name} :"))
|
|
106
|
+
print(ex.hint or "Pas d'indice supplémentaire : relisez le docstring en haut du fichier.")
|
|
107
|
+
return EXIT_OK
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def cmd_solution(args) -> int:
|
|
111
|
+
ex = _exercise_or_exit(args.name)
|
|
112
|
+
if not resources.has_solution(ex.name):
|
|
113
|
+
print(ui.failure("Pas de solution disponible pour cet exercice."))
|
|
114
|
+
return EXIT_ERROR
|
|
115
|
+
if status(ex.path) != "done":
|
|
116
|
+
print(ui.warn(f"La solution de {ex.name} est verrouillée."))
|
|
117
|
+
print("Elle se débloque quand l'exercice est résolu : les tests passent ET la")
|
|
118
|
+
print("ligne « # I AM NOT DONE » a été retirée du fichier.")
|
|
119
|
+
print(f"Vérifiez où vous en êtes avec {ui.bold('fhecli run ' + ex.name)}, "
|
|
120
|
+
f"ou demandez un indice avec {ui.bold('fhecli hint ' + ex.name)}.")
|
|
121
|
+
return EXIT_ERROR
|
|
122
|
+
print(ui.bold(f"Solution de référence pour {ex.name} :"))
|
|
123
|
+
print(ui.rule())
|
|
124
|
+
print(resources.solution_text(ex.name))
|
|
125
|
+
print(ui.rule())
|
|
126
|
+
return EXIT_OK
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def cmd_reset(args) -> int:
|
|
130
|
+
ws = _workspace()
|
|
131
|
+
if args.all:
|
|
132
|
+
targets = load_exercises(ws)
|
|
133
|
+
question = (f"Remettre les {len(targets)} exercices à leur énoncé de départ ? "
|
|
134
|
+
"Tout votre code sera perdu.")
|
|
135
|
+
elif args.name:
|
|
136
|
+
targets = [_exercise_or_exit(args.name)]
|
|
137
|
+
question = f"Remettre {targets[0].name} à son énoncé de départ ? Votre code sera perdu."
|
|
138
|
+
else:
|
|
139
|
+
print(ui.failure("Précisez un nom d'exercice, ou utilisez --all."))
|
|
140
|
+
return EXIT_ERROR
|
|
141
|
+
|
|
142
|
+
if not args.yes and not ui.confirm(question):
|
|
143
|
+
print("Annulé, rien n'a été modifié.")
|
|
144
|
+
return EXIT_OK
|
|
145
|
+
|
|
146
|
+
for ex in targets:
|
|
147
|
+
ex.path.parent.mkdir(parents=True, exist_ok=True)
|
|
148
|
+
ex.path.write_text(resources.original_text(ex.name), encoding="utf-8")
|
|
149
|
+
if args.all:
|
|
150
|
+
print(ui.success(f"Les {len(targets)} exercices ont été réinitialisés."))
|
|
151
|
+
else:
|
|
152
|
+
print(ui.success(f"{targets[0].name} a été réinitialisé à son énoncé de départ."))
|
|
153
|
+
return EXIT_OK
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def cmd_watch(args) -> int:
|
|
157
|
+
watch_loop(load_exercises(_workspace()))
|
|
158
|
+
return EXIT_OK
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
162
|
+
parser = argparse.ArgumentParser(
|
|
163
|
+
prog="fhecli",
|
|
164
|
+
description=(
|
|
165
|
+
"Atelier d'exercices en ligne de commande : chiffrement classique "
|
|
166
|
+
"et chiffrement homomorphe (FHE). Inspiré de rustlings."
|
|
167
|
+
),
|
|
168
|
+
epilog="Pour commencer : fhecli init, puis cd fhe-exercices et fhecli watch.",
|
|
169
|
+
)
|
|
170
|
+
parser.add_argument("--version", action="version", version=f"fhecli {__version__}")
|
|
171
|
+
sub = parser.add_subparsers(dest="command", required=True, metavar="commande")
|
|
172
|
+
|
|
173
|
+
p_init = sub.add_parser("init", help="Crée un espace de travail avec les exercices")
|
|
174
|
+
p_init.add_argument("directory", nargs="?", default=DEFAULT_DIR,
|
|
175
|
+
help=f"Dossier à créer (défaut : {DEFAULT_DIR})")
|
|
176
|
+
p_init.set_defaults(func=cmd_init)
|
|
177
|
+
|
|
178
|
+
p_watch = sub.add_parser("watch", help="Mode guidé : avance exercice par exercice")
|
|
179
|
+
p_watch.set_defaults(func=cmd_watch)
|
|
180
|
+
|
|
181
|
+
p_list = sub.add_parser("list", help="Liste tous les exercices et leur statut")
|
|
182
|
+
p_list.set_defaults(func=cmd_list)
|
|
183
|
+
|
|
184
|
+
p_run = sub.add_parser("run", help="Lance les tests d'un seul exercice")
|
|
185
|
+
p_run.add_argument("name", help="Nom de l'exercice (ex : ex1_cesar)")
|
|
186
|
+
p_run.set_defaults(func=cmd_run)
|
|
187
|
+
|
|
188
|
+
p_hint = sub.add_parser("hint", help="Affiche un indice pour un exercice")
|
|
189
|
+
p_hint.add_argument("name")
|
|
190
|
+
p_hint.set_defaults(func=cmd_hint)
|
|
191
|
+
|
|
192
|
+
p_solution = sub.add_parser("solution", help="Affiche la solution (une fois l'exercice résolu)")
|
|
193
|
+
p_solution.add_argument("name")
|
|
194
|
+
p_solution.set_defaults(func=cmd_solution)
|
|
195
|
+
|
|
196
|
+
p_reset = sub.add_parser("reset", help="Remet un exercice à son énoncé de départ")
|
|
197
|
+
p_reset.add_argument("name", nargs="?", help="Nom de l'exercice (à omettre avec --all)")
|
|
198
|
+
p_reset.add_argument("--all", action="store_true", help="Réinitialise tous les exercices")
|
|
199
|
+
p_reset.add_argument("-y", "--yes", action="store_true", help="Ne pas demander de confirmation")
|
|
200
|
+
p_reset.set_defaults(func=cmd_reset)
|
|
201
|
+
|
|
202
|
+
return parser
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def main(argv: list[str] | None = None) -> int:
|
|
206
|
+
ui.setup_console()
|
|
207
|
+
argv = sys.argv[1:] if argv is None else argv
|
|
208
|
+
if argv and argv[0] == PYTEST_SUBCOMMAND:
|
|
209
|
+
# Usage interne (exécutable autonome) : `fhecli _run-pytest <args>`
|
|
210
|
+
# exécute le pytest embarqué. Absent de l'aide.
|
|
211
|
+
import pytest
|
|
212
|
+
|
|
213
|
+
return int(pytest.main(argv[1:]))
|
|
214
|
+
parser = build_parser()
|
|
215
|
+
args = parser.parse_args(argv)
|
|
216
|
+
return args.func(args)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
if __name__ == "__main__":
|
|
220
|
+
sys.exit(main())
|
fhecli/data/info.toml
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Liste des exercices, dans l'ordre pédagogique suivi par `fhecli watch`.
|
|
2
|
+
#
|
|
3
|
+
# Parcours : du chiffrement classique (où tout calcul sur le chiffré est
|
|
4
|
+
# impossible) jusqu'au chiffrement homomorphe, en reconstruisant Paillier à la
|
|
5
|
+
# main, puis un score bancaire chiffré de bout en bout, et enfin un vrai calcul
|
|
6
|
+
# FHE avec CKKS (bonus).
|
|
7
|
+
|
|
8
|
+
[[exercises]]
|
|
9
|
+
name = "ex1_cesar"
|
|
10
|
+
category = "A. Chiffrement classique"
|
|
11
|
+
title = "Le chiffrement de César"
|
|
12
|
+
difficulty = "facile"
|
|
13
|
+
hint = "Pour tester si `c` est une lettre, utilisez 'a' <= c <= 'z' (et 'A' <= c <= 'Z'), pas c.islower() / c.isupper() : ces méthodes renvoient aussi True pour les lettres accentuées comme « é » ou « à », qui doivent rester inchangées. La nouvelle position est (position + shift) % 26."
|
|
14
|
+
|
|
15
|
+
[[exercises]]
|
|
16
|
+
name = "ex2_xor_malleability"
|
|
17
|
+
category = "A. Chiffrement classique"
|
|
18
|
+
title = "XOR et le danger de la malléabilité"
|
|
19
|
+
difficulty = "facile"
|
|
20
|
+
hint = "reveal_xor_of_plaintexts ne fait qu'une seule chose : XOR de c1 et c2 entre eux (réutilisez xor_bytes). C'est le XOR qui annule la clé, pas une astuce compliquée."
|
|
21
|
+
|
|
22
|
+
[[exercises]]
|
|
23
|
+
name = "ex3_fernet_tamper"
|
|
24
|
+
category = "A. Chiffrement classique"
|
|
25
|
+
title = "Chiffrement authentifié avec Fernet"
|
|
26
|
+
difficulty = "facile"
|
|
27
|
+
hint = "bytearray(token) permet de modifier un octet d'un objet bytes immuable : data[0] ^= 0xFF. Attrapez cryptography.fernet.InvalidToken avec un try/except."
|
|
28
|
+
|
|
29
|
+
[[exercises]]
|
|
30
|
+
name = "ex4_modpow"
|
|
31
|
+
category = "B. Briques mathématiques"
|
|
32
|
+
title = "Exponentiation modulaire rapide"
|
|
33
|
+
difficulty = "facile"
|
|
34
|
+
hint = "À chaque tour : si le bit de poids faible de exp vaut 1 (exp & 1), multipliez result par base modulo mod ; puis élevez base au carré modulo mod et faites exp //= 2. Ne calculez jamais base ** exp directement."
|
|
35
|
+
|
|
36
|
+
[[exercises]]
|
|
37
|
+
name = "ex5_modinv"
|
|
38
|
+
category = "B. Briques mathématiques"
|
|
39
|
+
title = "PGCD étendu et inverse modulaire"
|
|
40
|
+
difficulty = "moyen"
|
|
41
|
+
hint = "extended_gcd est récursif : cas de base b == 0 -> (a, 1, 0) ; sinon g, x1, y1 = extended_gcd(b, a % b) puis renvoyez (g, y1, x1 - (a // b) * y1). Dans mod_inverse, si g != 1 levez ValueError, sinon renvoyez x % mod (le % gère les x négatifs)."
|
|
42
|
+
|
|
43
|
+
[[exercises]]
|
|
44
|
+
name = "ex6_paillier_keygen"
|
|
45
|
+
category = "C. Paillier, pas à pas"
|
|
46
|
+
title = "Générer une paire de clés Paillier"
|
|
47
|
+
difficulty = "moyen"
|
|
48
|
+
hint = "n = p*q, g = n+1, lam = (p-1)*(q-1), et mu = _mod_inverse(lam, n) (la fonction est déjà fournie). Renvoyez public_key = {\"n\": n, \"g\": g, \"n2\": n*n} et private_key = {\"lam\": lam, \"mu\": mu}."
|
|
49
|
+
|
|
50
|
+
[[exercises]]
|
|
51
|
+
name = "ex7_paillier_enc_dec"
|
|
52
|
+
category = "C. Paillier, pas à pas"
|
|
53
|
+
title = "Chiffrer et déchiffrer avec Paillier"
|
|
54
|
+
difficulty = "moyen"
|
|
55
|
+
hint = "encrypt : ((1 + m*n) % n2) * pow(r, n, n2) % n2 (pensez à m = m % n). decrypt : u = pow(c, lam, n2), puis m = (_l(u, n) * mu) % n, et enfin si m > n // 2 : m -= n (représentation symétrique pour les négatifs)."
|
|
56
|
+
|
|
57
|
+
[[exercises]]
|
|
58
|
+
name = "ex8_paillier_homomorphic"
|
|
59
|
+
category = "C. Paillier, pas à pas"
|
|
60
|
+
title = "Les propriétés homomorphes de Paillier"
|
|
61
|
+
difficulty = "moyen"
|
|
62
|
+
hint = "add_encrypted : (c1 * c2) % n2 (multiplier les chiffrés = additionner les clairs). mul_by_scalar : pow(c, k, n2) si k >= 0 ; si k < 0, pow(_mod_inverse(c, n2), -k, n2)."
|
|
63
|
+
|
|
64
|
+
[[exercises]]
|
|
65
|
+
name = "ex9_scoring_paillier"
|
|
66
|
+
category = "D. Scoring chiffré"
|
|
67
|
+
title = "Un score bancaire chiffré de bout en bout"
|
|
68
|
+
difficulty = "difficile"
|
|
69
|
+
hint = "Chiffrez chaque feature avec encrypt(pub, x, _random_r(pub[\"n\"])), multipliez chaque chiffré par son poids avec mul_by_scalar, partez de total = encrypt(pub, bias, 1), additionnez tous les termes avec add_encrypted, et ne déchiffrez qu'à la toute fin."
|
|
70
|
+
|
|
71
|
+
[[exercises]]
|
|
72
|
+
name = "ex10_ckks_bonus"
|
|
73
|
+
category = "E. Bonus : FHE réel avec CKKS"
|
|
74
|
+
title = "Votre premier calcul FHE avec CKKS (TenSEAL)"
|
|
75
|
+
difficulty = "bonus"
|
|
76
|
+
bonus = true
|
|
77
|
+
hint = "Trois lignes : enc_vector = ts.ckks_vector(context, values) ; enc_result = enc_vector.dot(weights) ; return enc_result.decrypt()[0]. Ce bonus nécessite TenSEAL : pip install \"fhe-cli[ckks]\"."
|