src | ||
.gitignore | ||
README.md | ||
requirements.txt |
Sand
Partage de petits fichiers* de façon sécurisée avec RSA.
* ne supportes pas les binaires
Exécution avec Python 3.9.2
Préparation de l'environnement
$ python3 -m venv .
$ source bin/activate
$ pip install -r requirements.txt
Lancement
$ python3 -m flask --app src/app.py run
Ressources utilisés
- Rilu (police)
- Forge (nombres premiers)
- bigint-mod-arith (inverse modulaire)
Fonctionnement
Explication
Le but est que le serveur n'est jamais accès :
- au fichier déchiffré
- aux clefs permettant de lire le fichier
Pour cela, tout le chiffrement est fait en local dans le navigateur. Lors de la réception du fichier, une paire de clef RSA (sur 1024 bits) est créer.
À cause des limitations du Javascript, la clef est limitée à 1024 bits, sinon le chiffrement est trop long. Aussi la taille des fichiers est limitée à environ 500 ko.
À cause de RSA, il est recommandé de n'envoyer que des petits fichiers de quelques octets, plus le fichier est lourd, plus le chiffrement sera long. Pendant le chiffrement, l'interface se figera car les calculs sont faits sur le thread principal.
Le fichier reçu est ensuite chiffré avec la clef secrète générée précédemment. Le nom du fichier est également chiffré avec la même clef.
Les deux chiffrés sont envoyés au serveur. Un hash du fichier chiffré est généré
par le serveur, cet hash ainsi que le nom du fichier chiffré est stocké dans une
base de données SQLite, le fichier est quant à lui enregistrer sur le disque (par
défaut dans le dossier uploads/
avec comme nom de fichier l'hash).
L'hash est renvoyé par le serveur au client, une fois reçu le client affiche à l'utilisateur l'URL pour télécharger le fichier, qui se présente ainsi :
HTTP(S)://IP:PORT/file/HASH#CLEF
La clef publique étant composé du module de chiffrement (n
) et de l'exposant de
chiffrement (e
), les deux nombres sont séparés, soit CLEF
= e:n
.
Quand le second utilisateur clique sur le lien, il peut donc télécharger le fichier car :
- l'hash permettant de demander au serveur le fichier est contenu dans la première partie de l'URL (avant le #)
- la clef permettant de déchiffrer le chiffré est contenue dans la seconde partie de l'URL (après le #)
Le nom du fichier est stocké pour permettre de garder le même nom de fichier quand on reçoit le fichier.
Le serveur ne connaît donc jamais la clef permettant de déchiffrer les fichiers qu'il reçoit et stocke.
Implémentation et limite
Pour le chiffrement, je transforme le fichier en base64, puis je regarde le code
ASCII, par exemple "<3" devient "060051" pour 60 et 51. Je vais devoir convertir
mes string en bigint pour le chiffrement RSA, alors pour rester dans la limite de
bigint, je garde la taille de mes strings fixe à max 100 caractères. Au maximum un
code ASCII est composé de 3 chiffres alors je sais que pour retrouver mes chiffres
je vais devoir découper mon string tous les 3 caractères. Pour garder mon code
"060051" qui sera 60051
en int, j'ajoute un 1
au début, que je retirerais
lors de la transformation en bigint. Ces opérations sont faites dans le fichier
rsa.js
(cf. l'arborescence expliquée en dessous) dans str_to_int
et int_to_str
. Cette implémentation n'est pas optimisée et c'est ce qu'il
faudrait changer pour pouvoir supporter les fichiers binaires.
Le chiffrement se fait donc par bloc, tous les 100 chiffres, dans RSA_enc_data
.
Le fait que JS ne gère pas aussi bien les très gros nombre que Python est un problème qui fragilise encore plus le RSA, car je dois envoyer le chiffré découpé par des virgules tous les x caractères pour qu'il soit correctement déchiffré. Ce qui rend reconnaissable le chiffré produit car il contient une suite de nombres séparés régulièrement par des virgules.
Organisation du projet
src
├── app.py ------------> Point d'entrée pour lancer l'application
├── config.py ---------> Configuration globale + télécharge les dépendances
├── public ------------> Accessible depuis le client
│ ├── js
│ │ ├── download.js -> Gère la partie téléchargement
│ │ ├── index.js ----> Gère la partie téléversement
│ │ └── rsa.js ------> Implémentation de RSA
│ └── styles
│ └── style.css
├── routes
│ ├── api ------------> /api
│ │ ├── download.py -> /api/download -> Envoie les données au client
│ │ └── upload.py ---> /api/upload ---> Reçoit les données du client
│ ├── file.py --------> /file ---------> Interface de téléchargement
│ └── index.py -------> / -------------> Interface de téléversement
├── templates ---------> Fichier HTML
│ ├── download.html
│ └── index.html
└── utils
├── font.py -------> Permets de télécharger la police
├── libjs.py ------> Permets de télécharger la librairie Javascript
├── misc.py -------> Entre autres, implémente la fonction de hashage
└── sqlite.py -----> Gère ce qui est en rapport avec SQLite