• Un peu de chiffrement avec pyFC

     

     

    Aujourd'hui, nous allons voir un peu comment protéger nos fichiers avec Python3. Le but est de chiffrer (crypter pour les malpolis :-)) n'importe quel fichier ou répertoire de manière à pouvoir l'envoyer sur un cloud ou sur une boite mail sans que le fournisseur de services puisse mettre son nez dedans...

    Oui, je sais Google, “ tes serveurs s'ennuient ” ... Vous pourrez voir ce charmant message si vous videz complètement votre compte Gmail... Comme quoi, ils sont bien occupés à lire vos courriers si vous utilisez leurs services...

    Bref, en chiffrant vos fichiers avec le script pyFC, ces divers fournisseurs pourront toujours les consulter (en utilisant un éditeur hexadécimal, par exemple), mais de là à en tirer quelque chose, c'est une autre histoire...

    ...enfin revenons à nos moutons : le masque jetable, vous connaissez ? Voici la définition de wikipedia :

    “Le masque jetable, également appelé chiffre de Vernam, est un algorithme de cryptographie inventé par Gilbert Vernam en 1917 et perfectionné par Joseph Mauborgne, qui rajouta la notion de clé aléatoire....
    ...
    ....tant pour le codage que pour le décodage, ce chiffrement est théoriquement impossible à casser,....”

     

    Le principe de ce chiffrement est de crypter tous les caractères du message ou du fichier à protéger, et ce de manière unique et aléatoire. La clé de cryptage devra donc être de longueur au moins équivalente au message d'origine, ne devra être utilisée qu'une seule fois, et être générée aléatoirement.

    L'implémentation de ce script respecte en outre le principe de Kerckhoffs (reformulé par Claude Shannon), qui stipule que « l'adversaire connaît le système ». Ce système de chiffrement et son implémentation n'auront donc rien de secret, la sécurité reposant essentiellement sur le secret des clés.

    Le système de chiffrement dont il est question ici est un système à clé symétrique : une même clé sert à la fois au chiffrement et au déchiffrement.

     

    Le script pyFC :

    Pour rappel, le but de pyFC est de chiffrer vos fichiers pour votre utilisation personnelle essentiellement. Il n'est pas question ici pour le moment de transmettre des clés à un éventuel correspondant (voir la section "Évolutions futures" pour ce point) afin qu'il déchiffre les fichiers transmis. Cela reste possible, mais il vous faudra alors transmettre vos clés de chiffrement par un autre moyen (clé usb essentiellement...).

     

    1ère étape : Génération des clés :

    Pour générer les clés de chiffrement, placez-vous dans le répertoire du script puis dans une console entrez:

     you@yourComputer:~/pyFC$ python3 pyFC.py -init

    Le script vous demande alors confirmation comme quoi vous souhaitez bien générer un nouveau jeu de clés, entrez 'OK'.

    Je parle ici de clés au pluriel, ou de jeu de clés. En fait, le script va générer 5 fichiers remplis d'octets aléatoires dont les tailles feront respectivement 100MBytes, 200MBytes, 300MBytes, 400MBytes et 500MBytes. Ces fichiers seront créés chacun dans une arborescence de répertoires contenant 28 niveaux, et seront stockés au 'dernier étage' (ou bien dernier sous-sol, selon la manière dont on visualise cela) de cette arborescence. Les noms de chacun de ses répertoires seront générés aléatoirement (sur 3 lettres et/ou chiffres) et précédés d'un point ('.') permettant ainsi de cacher chaque niveau de ces arborescences. Les noms des fichiers clés seront respectivement  '.key1', '.key2', '.key3', '.key4', '.key5'.

    Une fois ces fichiers clés générés, le script vous demandera d'entrer l'emplacement (répertoire) où vous souhaitez stocker ces arborescences de clés. Il est préférable de les placer ailleurs que dans le répertoire du script, pour des raisons évidentes de sécurité. Si vous n'entrez rien, les clés resteront dans ce répertoire courant.

    J'ai choisi volontairement des clés relativement volumineuses afin qu'elle ne puissent pas être facilement transmises par le réseau. La sécurité de ce système est assuré tant que le secret des clés l'est, ce qui est vrai pour n'importe quel système de chiffrement. 

    La liste des emplacement de ces 5 arborescences de clés sera sauvée dans votre dossier utilisateur dans le fichier de configuration :

    /home/user/.pyFC/.pyFC.conf

    Ce fichier de configuration ainsi que toutes les arborescences où sont stockées les clés verront tous leurs droits d'accès mis à 0 (chmod 0o000). Il ne seront ainsi plus lisibles directement (même si l'utilisateur courant peut changer ces droits). Les droits seront gérés par le script selon les besoins, mais seront toujours remis à 0o000 après toute opération de chiffrement ou déchiffrement. Seule l'option "-showconf" passée en paramètre (voir plus bas son utilisation) permet de dévérouiller la sécurité, jusqu'au prochain lancement du script.

     

    2ème étape : Chiffrement d'un fichier ou d'un répertoire :

    Dans la console, entrez :

     you@yourComputer:~/pyFC$ python3 /cheminvers/pyFC.py -e /chemindufichier/oudurépertoire/àchiffrer

    Par exemple :

     you@yourComputer:~/pyFC$ python3 /home/adam/pyFC/pyFC.py -e /DataDisk2/mesPhotos/390485.mov

    Ici, le paramètre -e indique que l'on souhaite chiffrer le fichier 390485.mov. pyFC va alors créer une archive zip temporaire nommée crypted.zip. Cela permettra à la fois de ne travailler que sur cette seule archive (donc process identique que l'on ait à chiffrer un simple fichier ou un répertoire complet), et en même temps de ne pas avoir à se préoccuper du stockage du nom de fichier/répertoire d'origine puisque celui-ci sera contenu dans cette archive. Ensuite, pyFC va générer 5 nombres aléatoires (1 par fichier clé, chaque nombre étant >=0 et < (à la taille de clé - la taille de l'archive précédemment créée). Ce nombre indique l'offset dans chaque fichier clé à partir duquel on va lire une série de N bytes, N étant la taille de l'archive zip. On a maintenant 5 bytearray(s) avec lesquels on va chiffrer (par opération XOR) notre archive. Pour chaque byte 'en clair' de l'archive, on obtient le byte chiffré par l'opération suivante :

     

    byteChiffré = byteClair^byteSérie1^byteSérie2^byteSérie3^byteSérie4^byteSérie5

     

    Il ne nous reste plus qu'à stocker notre nouveau tableau de bytes chiffrés dans un fichier, sans oublier d'y placer en entête les 5 offsets utilisés qui seront nécessaires au déchiffrage. On peut considérer ces offsets comme une partie de la clé, aussi pour ne pas dévoiler une partie du secret trop facilement, on va coder ces informations. Dans un premier temps, on va encoder chaque offset sur 4 octets (format integer 32 bits), 'mélanger' l'ordre de leurs octets [Byte3 Byte2 Byte1 Byte0] devient [Byte0 Byte2 Byte3 Byte1], puis les combiner par opération XOR chacun avec 2 séries de 4 octets pris dans les fichiers clés, l'offset choisi pour chaque série dépendant de la taille de l'archive à chiffrer passée par une opération modulo. Vous pouvez voir les détails de ce process directement dans le code source plus bas.

    De manière totalement subjective, j'ai choisi de nommer les fichiers chiffrés à partir de la valeur 'Unix timestamp' relevée juste après la création de l'archive. L'extension par défaut est ".000".

     

    Cas de fichier d'archive >100MBytes :

    Tel que défini plus haut, on peut voir qu'il n'est pas prévu de chiffrer des fichiers de taille > 100MBytes (puisque 100MBytes est la taille de la plus petite clé). Cela est résolu en découpant l'archive en 'tranches' plus petites. Après quelques séries de tests, je me suis aperçu que chiffrer des fichiers de quelques dizaines de mégabytes était de toute façon une opération assez longue. Aussi, j'ai choisi de découper les fichiers selon la formule suivante afin de gagner en temps, ou tout au moins avoir une meilleure visibilité de ce qui se passe lors du chiffrement/déchiffrement : 

    Taille_Tranche_Max = 3MBytes + 1MByte*[(Taille de l'archive en clair(en MBytes)) / 15]

    Pour un fichier de 5MBytes, vous obtiendrez Taille_Tranche_Max = 3 MBytes                                                                          --> soit 2 tranches de 2.5 MBytes

    Pour un fichier de 33MBytes, vous obtiendrez Taille_Tranche_Max = 5 MBytes                                                                         --> soit 7 tranches de 4.7 MBytes

    Pour un fichier de 189MBytes, vous obtiendrez Taille_Tranche_Max = 15 MBytes                                                                       --> soit 13 tranches de 14.5 MBytes

     

    La limite haute de Taille_Tranche_Max est de 50MBytes. Le nombre de fichiers résultant(s) est calculé automatiquement, et pour savoir qu'un ensemble de fichiers fait référence à une même archive, ceux-ci porteront tous le même nom (le 'Unix timestamp'). Seule l'extension changera : le 1er fichier portant l'extension ".000", le suivant ".001", le suivant ".002", etc... selon le nombre de fichiers nécessaire(s)...

     

    3ème étape : Déchiffrement d'un fichier "crypté" :

    Dans la console, entrez :

     you@yourComputer:~/pyFC$ python3 /cheminvers/pyFC.py -d /chemindufichier/oudurépertoire/àdéchiffrer

    Par exemple :

     you@yourComputer:~/pyFC$ python3 /home/adam/pyFC/pyFC.py -d /DataDisk2/mesPhotos/1561243871.000

    Ici, le paramètre -d indique que l'on souhaite déchiffrer le fichier "1561243871.000". Dans le principe, si d'autres fichiers dans le même répertoire portant le même nom avec d'autres extensions (".001", ".002", etc) existent, ils seront pris en compte comme faisant partie d'un même ensemble de fichiers à déchiffrer. Le fichier ou répertoire d'origine sera alors extrait / recréé à partir de l'ensemble des fichiers chiffrés. Attention, le nom du fichier passé en paramètre pour déchiffrement doit forcément avoir une extension en ".000" ! N'essayez pas de déchiffrer un fichier avec extension ".001", ".002" ou autres extension, cela ne fonctionnera pas ! 

     

    Autres options du script :

    Pour obtenir de l'aide :

     you@yourComputer:~/pyFC$ python3 pyFC.py -help

    Pour accéder au fichier de configuration ainsi qu'aux clés (ré-autorise les accès en lecture/écriture aux fichiers et répertoires de clés) :

     you@yourComputer:~/pyFC$ python3 pyFC.py -showconf

    Pour interdire les accès au fichier de configuration ainsi qu'aux clés (interdit les accès en lecture/écriture) :

     you@yourComputer:~/pyFC$ python3 pyFC.py -hideconf

    L'option -showconf peut-être utilisée si vous souhaitez (dé)placer vos clés dans des répertoires de base différents. Pourquoi pas dans ce cas en placer une ou deux sur clé(s) USB, qui servirai(en)t de dongle de sécurité, par exemple? Dans ce cas, il vous faudra modifier à la mano les chemins vers les clés dans le fichier /home/user/.pyFC/.pyFC.conf

     

    Évolutions futures :

    Une première évolution utile serait de pouvoir produire plusieurs jeux de clés et de les nommer afin de pouvoir les utiliser en tant que clés partagées avec divers correspondants. Une autre possible serait de pouvoir choisir la taille ainsi que le nombre de clés à utiliser, même si cela me parait moins utile pour le moment... Disons que le script est déjà assez long (je l'imaginais plus court au début de ce projet), et me parait tout à fait utilisable et cohérent pour une première version...

    Je reconnais aussi qu'utiliser un script en ligne de commande, c'est parfois un peu lourd, ça peut freiner un peu l'envie d'essayer... Pour ce point, j'écrirai un prochain article où j'explique comment je l'utilise directement à partir des options disponibles en clic droit dans mon navigateur de fichiers (caja). 

    Vous trouverez le code de ce script juste ci-après, et vous pouvez le télécharger ici. C'est un fichier zip qui contient un répertoire /pyFC qui contient lui-même le fichier pyFC.py ainsi qu'un fichier secrets.py. Ce dernier fichier (secrets.py) fait partie de la librairie standard de python3.6, mais n'est pas fourni dans les versions précédentes. Le fait de l'avoir directement dans /pyFC permet de faire fonctionner pyFC.py sous python3.5 sans problème, mais je n'ai pas pu tester l'ensemble sous une version encore antérieure... Le script a été testé sous LinuxMint 19.1 et Debian 9.9.

    Si vous avez d'autres idées, des questions ou des suggestions, n'hésitez pas à m'en faire part dans vos commentaires. En attendant, prenez bien soin de votre vie privée :-)

    PS : Au passage, un grand merci à mon beta-testeur préféré qui m'a permis de constater cette différence entre les versions de python3.5 et 3.6 :-)

     

    Script pyFC.py :

        1 #!/usr/bin/env python3
        2 # -*- coding: utf-8 -*-
        3 # V0.1 du 24/06/2019
        4 
        5 
        6 
        7 ####
        8 #    pyFC est le diminutif de pyFileCrypter (qui était le nom originel de ce projet). Trouvant 'pyFileCrypter' un 
        9 #    peu long, j'ai préféré le raccourcir un peu pour gagner en concision...
       10 #
       11 #    NB : Ce script est prévu pour fonctionner sous Linux essentiellement (mais si vous êtes motivé(s), il devrait
       12 #    être adaptable assez facilement pour d'autres OS...)
       13 #
       14 #    Ce script à pour but de chiffrer/déchiffrer des fichiers ou répertoires en utilisant l'algorithme
       15 #    du masque jetable, ou chiffre de Vernam.
       16 #    
       17 #    INITIALISATION:
       18 #    Au 1er lancement du script, il faudra lui passer l'argument '-init' (puis valider cette opération par 'OK'),
       19 #    afin que le jeu de clés de chiffrement soit créé.
       20 #    Ce jeu consiste en 5 fichiers respectivement de 100, 200, 300, 400 et 500 Mega-Octets, contenant
       21 #    chacun un ensemble d'octets générés aléatoirement. Les grandes tailles de ces fichiers limitent les risques
       22 #    de copie, mais surtout d'upload par un "processus pirate" (un upload d'~ 1.5 Giga Octets prendrait beaucoup
       23 #    de temps !). Chaque fichier clé sera généré dans une nouvelle arborescence de répertoires de 28 'étages'
       24 #    Une fois ces fichiers clés générés, le script va vous demander d'entrer le répertoire dans lequel vous
       25 #    souhaitez stoker ces clés.
       26 #    La console vous indique la marche à suivre.
       27 #    Après validation (entrée de 'OK' dans la console), le fichier pyFC.conf sera créé dans votre 
       28 #    /home, placé dans un nouveau répertoire caché /.pyFC, puis renommé .pyFC.conf de manière
       29 #    à devenir un fichier caché lui aussi.
       30 #    Les droits du répertoire /.pyFC et du fichier .pyFC.conf vont être mis à 0o000 afin d'
       31 #    améliorer un peu la sécurité (ils deviennent donc illisible tant que les droits de lecture n'auront pas 
       32 #    été réactivés.)
       33 #    Ces droits de lecture seront réactivés temporairement au prochain lancement du script pyFC.py (par
       34 #    le script lui-même afin que celui-ci puisse accéder aux clés), puis re-désactivés.
       35 #    Ce système de protection est aussi appliqué à toute l'arborescence des clés.
       36 #
       37 #    Pour le cas où vous souhaitez pouvoir chiffrer/déchiffrer vos fichiers sur une autre machine avec le même
       38 #    jeu de clés, il suffira de recopier l'arborescence des clés ainsi que le fichier de configuration sur
       39 #    ladite machine aux endroits choisis.
       40 #    Il ne faudra en aucun cas relancer le script avec l'argument '-init', sinon celui-ci écraserait la 
       41 #    configuration du le jeu de clés actuel avec une nouvelle configuration (les clés elles-même ne seront pas
       42 #    effacées)! Dans une version future, si le besoin s'en fait sentir, j'implémenterai probablement la
       43 #    possibilité d'avoir plusieurs jeux de clés (ce qui permettrait par exemple d'avoir des jeux de clés
       44 #    partagés avec différents correspondants...)
       45 #
       46 #    FONCTIONNEMENT & UTILISATION :
       47 #    Pour chaque fichier à chiffrer, le script va générer aléatoirement 5 offsets différents (1 pour chaque fichier
       48 #    clé), puis dans chaque fichier clé aller chercher à partir de cet offset un nombre d'octets équivalent à la
       49 #    taille du fichier à chiffrer. Ensuite, le chiffrement va s'effectuer par un XOR octet par octet comme suit :
       50 #    temp1 = fichier_à_chiffrer ^ fichier_clé1[offset1, size]
       51 #    temp2 = temp1 ^ fichier_clé2[offset2, size]
       52 #    temp3 = temp2 ^ fichier_clé3[offset3, size]
       53 #    temp4 = temp3 ^ fichier_clé4[offset4, size]
       54 #    temp5 = temp4 ^ fichier_clé5[offset5, size]
       55 #
       56 #
       57 #    Il est aussi nécessaire de stocker les offsets utilisés au sein de chaque fichier chiffré. Ces offsets
       58 #    sont codés sur 4 octets (nombre hexa 32bits).
       59 #    A partir de ces données, le script va créer le header du fichier une fois chiffré :
       60 #
       61 #    XXXX -> 4 octets representant l'offset1 : offset1 ^ fichier_clé1 [10000 +  29*(TailleFichier % 9999), 4]
       62 #                                                      ^ fichier_clé2 [20000 + 113*(TailleFichier % 8888), 4]
       63 #                                                        -> Poids des octets dans le codage : 0231
       64 #    XXXX -> 4 octets representant l'offset2 : offset2 ^ fichier_clé2 [30000 + 229*(TailleFichier % 7777), 4]
       65 #                                                      ^ fichier_clé3 [40000 + 349*(TailleFichier % 6666), 4]
       66 #                                                        -> Poids des octets dans le codage : 0231
       67 #    XXXX -> 4 octets representant l'offset3 : offset3 ^ fichier_clé3 [50000 + 463*(TailleFichier % 5555), 4]
       68 #                                                      ^ fichier_clé4 [60000 + 601*(TailleFichier % 4444), 4]
       69 #                                                        -> Poids des octets dans le codage : 0231
       70 #    XXXX -> 4 octets representant l'offset4 : offset4 ^ fichier_clé4 [70000 + 733*(TailleFichier % 3333), 4]
       71 #                                                      ^ fichier_clé5 [80000 + 863*(TailleFichier % 2222), 4]
       72 #                                                        -> Poids des octets dans le codage : 0231
       73 #    XXXX -> 4 octets representant l'offset5 : offset5 ^ fichier_clé5 [90000 + 1013*(TailleFichier % 1111), 4]
       74 #                                                      ^ fichier_clé1 [100000 + 1151*(TailleFichier % 5432), 4]
       75 #                                                        -> Poids des octets dans le codage : 0231
       76 #    
       77 #    Une fois ce header généré, il sera mis en entête de temp5 (voir plus haut), puis le fichier sera sauvegardé
       78 #    sous le nom int([time]).EXT ([time] étant le nombre de secondes écoulées depuis le 01/01/1970
       79 #    EXT aura pour valeur l'index '000', '001', '002' selon le rang du fichier compressé correspondant à
       80 #    un même fichier non compressé d'origine (cas des fichiers > 100MBytes)
       81 #    Dans le cas d'une compression en plusieurs 'volumes' (.000, .001, etc...), le time servant à définir
       82 #    le nom de fichier devra être le même.
       83 #
       84 #
       85 #    CAS DU CHIFFREMENT D'UN REPERTOIRE :
       86 #    Plutôt qu'un simple fichier, pyFC peut chiffrer un répertoire complet en y incluant tous ses
       87 #    fichiers et sous-répertoires. Pour cela, le répertoire à chiffrer sera dans un premier temps
       88 #    compressé en fichier zip, puis ensuite traité comme un simple fichier normal.
       89 #    NB : Il en est de même maintenant pour le chiffrement d'un fichier 'simple'. Celui-ci sera
       90 #    préalablement archivé dans un zip, avant que ce zip soit chiffré. Cela présente l'avantage de
       91 #    ne pas avoir à stocker le nom d'origine du fichier (puisqu'il figurera dans l'archive zip).
       92 #
       93 #
       94 #    ARGUMENTS :
       95 #    Pour chiffrer un fichier / répertoire : --> python3 pyFC.py -e [chemin complet vers le fichier
       96 #                                                                         ou répertoire à chiffrer]
       97 #    Pour déchiffrer un fichier            : --> python3 pyFC.py -d [chemin complet vers le fichier
       98 #                                                                             chiffré à déchiffrer]
       99 #
      100 #
      101 #
      102 ####
      103 
      104 
      105 
      106 
      107 import os
      108 import sys
      109 import shutil
      110 import secrets
      111 import time
      112 import random
      113 import string
      114 import zipfile
      115 
      116 
      117 
      118 
      119 
      120 def setConfFileAccess(accessibility):
      121     ''' Permet de rendre /home/user/.pyFC/.pyFC.conf visible et accessible ou non selon les besoins'''
      122     if accessibility:
      123         os.chmod(pfcDir, 0o774) # On met les droits de lecture/écriture sur le répertoire
      124         os.chmod(pfcConfFile, 0o774) # On met les droits de lecture/écriture sur le fichier conf
      125         #todo : gérer le attrib sous win
      126     else:
      127         os.chmod(pfcDir, 0o774) # On met les droits de lecture/écriture sur le répertoire
      128         try:
      129             os.chmod(pfcConfFile, 0o000)# On supprime les droits de lecture/écriture sur le répertoire
      130             os.chmod(pfcDir, 0o000)# On supprime les droits de lecture/écriture sur le répertoire
      131             #todo : gérer le attrib sous win
      132         except PermissionError:
      133             pass
      134 
      135 
      136 
      137 def setKeyfilesAccess(accessibility):
      138     ''' Permet de rendre les 5 fichiers clé keyFilePath visibles et accessibles ou non selon les besoins.'''
      139     nindex = 0
      140     for fullPath in keyFilepathList:
      141         if accessibility:  # On rend les clés accessibles
      142             fullPath = fullPath[0:len(fullPath)-145]
      143             for n in range(29):
      144                 dirStr = keyFilepathList[nindex][0:len(fullPath)+(n+1)*5]
      145                 os.chmod(dirStr, 0o774)
      146                 #todo : gérer le attrib sous win
      147         else:  # On rend les clés inaccessibles
      148             for n in range(29):
      149                 dirStr = keyFilepathList[nindex][0:len(fullPath)-n*5]
      150                 try:
      151                     os.chmod(dirStr, 0o000)
      152                     #todo : gérer le attrib sous win
      153                 except PermissionError:
      154                     pass
      155         nindex += 1
      156 
      157 
      158 
      159 def removeRNFromListe(listName):
      160     ''' Enlève les caractères \r ou \r\n en fin de ligne pour chaque string contenue dans listName '''
      161     newList = []
      162     for oneLine in listName:
      163         if oneLine.endswith("\r\n"):
      164             oneLine = oneLine[0:(len(oneLine)-2)]
      165         if oneLine.endswith("\n"):
      166             oneLine = oneLine[0:(len(oneLine)-1)]
      167         if len(oneLine) >0:
      168             newList.append(oneLine)
      169     return newList
      170 
      171 
      172 
      173 def readFileLines(filename):
      174     ''' Lit le fichier filename (fichier texte), et stocke chacune de ses lignes dans la <list> newList ''' 
      175     if os.path.exists(filename):
      176         oneStringList = []
      177         with open(filename, 'r') as myFile:
      178             oneStringList = myFile.readlines()
      179         newList = removeRNFromListe(oneStringList)
      180         return newList
      181     else:
      182         return ("Le fichier "+filename+" n'existe pas!")
      183 
      184 
      185 
      186 def readConfFile():
      187     ''' Lit le fichier /home/user/.pyFC/.pyFC.conf et 
      188         stocke son contenu ligne par ligne dans keyFilepathList '''
      189     global keyFilepathList
      190     setConfFileAccess(True)
      191     keyFilepathList = readFileLines(pfcConfFile)
      192     setConfFileAccess(False)
      193 
      194 
      195 
      196 def print4(message):
      197     ''' Simple fonction print() affichant une ligne avant le message et deux lignes après '''
      198     print()
      199     print(message)
      200     print()
      201     print()
      202 
      203 
      204 
      205 def createKeyFiles():
      206     ''' Permet de créer les 5 fichiers clés (fichiers 'binaires') contenant nbBytes générés aléatoirement. '''
      207     global pfcConfFile
      208     global pfcDir
      209     global keyFilepathList
      210     print("Création d'un nouveau jeu de clés de chiffrement. Cela peut prendre un moment...")
      211     pathToScript = os.path.dirname(os.path.realpath(sys.argv[0]))
      212 
      213     # Création de 5 arborescences contenant 28 niveaux de répertoires
      214     # Chaque fichier clé sera généré dans le répertoire le plus loin de la racine (niveau 28)
      215     for keyIndex in range(5):
      216         for rep in range(28):
      217             newrepName = '/.'
      218             rangeSize = 3
      219             if rep == 0:
      220                 newrepName += str(keyIndex+1)
      221                 rangeSize = 2;
      222             for _ in range(rangeSize):
      223                 newrepName += random.choice(string.digits+string.ascii_letters)
      224             pathToScript += newrepName
      225             os.mkdir(pathToScript)
      226             os.chmod(pathToScript, 0o774)
      227         nbBytes = 100*(keyIndex+1)*(2**20)
      228         myBytes = os.urandom(nbBytes)
      229         oneFileName = pathToScript+'/.key'+str(keyIndex+1)
      230         with open(oneFileName, 'wb') as keyFile:  # On écrit les 5 fichiers 'clé'
      231             keyFile.write(myBytes)
      232         print("Fichier key"+str(keyIndex+1), "créé")
      233         pathToScript = os.path.dirname(os.path.realpath(sys.argv[0]))
      234         keyFilepathList.append(oneFileName)  # On mémorise le chemin vers chaque clé
      235     
      236     print()
      237     print("Les 5 nouvelles clés de chiffrement ont été créées. Pour des raisons de sécurité, il est")
      238     print("préférable de placer ces fichiers clés ailleurs que dans le répertoire .../pyFile")
      239     print("Néanmoins, le programme fonctionnera correctement si vous n'entrez rien (simple appui sur [ENTER].")
      240     print("Dans ce cas, les fichiers clés resteront simplement dans .../pyFile")
      241     print()
      242     print("Pour la ligne ci-dessous, si vous êtes sous Windows, veillez à")
      243     print("remplacer les \ par des / dans la définition du chemin.")
      244     print()
      245     
      246     newPathStr = input("Entrez le chemin où sera installé le nouveau jeu de clés : ")
      247     if len(newPathStr) > 0:
      248         while not os.path.exists(newPathStr):
      249             print(newPathStr,"n'est pas un répertoire valide !")
      250             newPathStr = input("Entrez le chemin où sera installé le nouveau jeu de clé : ")
      251             print()
      252         if '/' in newPathStr[-1]:  # Pour éliminer le / final si présent
      253             newPathStr = newPathStr[0:len(newPathStr)-1]
      254 
      255         # Déplacement des clés vers leur nouveau répertoire et M.A.J de keyFilepathList
      256         for nindex in range(5):
      257             end = len(keyFilepathList[nindex])-141
      258             print(keyFilepathList[nindex][0:end])
      259             print()
      260             print("Déplacement du fichier key"+str(nindex))
      261             shutil.move(keyFilepathList[nindex][0:end], newPathStr)
      262             keyFilepathList[nindex] = newPathStr+keyFilepathList[nindex][end-5:-1]+str(nindex+1)
      263         print()
      264 
      265     # Création de /home/user/.pyFC si nécessaire & gestion des droits...
      266     if not os.path.exists(pfcDir):
      267         os.mkdir(pfcDir)
      268     os.chmod(pfcDir, 0o774) # On met les droits de lecture/écriture sur le répertoire
      269     if os.path.exists(pfcConfFile):
      270         os.chmod(pfcConfFile, 0o774) # On met les droits de lecture/écriture sur le fichier conf
      271         os.remove(pfcConfFile)  # Puis on efface le fichier existant (obsolète)
      272 
      273     for nindex in range(5):
      274         with open(pfcConfFile, 'a') as confFile:  # On reporte keyFilepathList dans pyFC.conf
      275             confFile.write(keyFilepathList[nindex]+'\r\n')
      276 
      277     # La configuration est maintenant écrite dans /home/user/.pyFC/.pyFC.conf,
      278     # On va lire la configuration pour protéger les répertoires qui contiennent les clés
      279     readConfFile()
      280     # on protège les répertoires des clés en supprimant les droits de lecture de ces répertoires
      281     setKeyfilesAccess(False)
      282     print4("Le nouveau jeu de clés est maintenant activé")
      283 
      284 
      285 def printUsage():
      286     ''' Affiche l'aide, le 'mode d'emploi' de pyFC '''
      287     print()
      288     print("Utilisation :")
      289     print("pyFC.py              --> affiche ce mode d'emploi")
      290     print("pyFC.py -help        --> affiche ce mode d'emploi")
      291     print("pyFC.py -init        --> pour initialiser pyFC avec un nouveau jeu de clé de chiffrement")
      292     print("pyFC.py -e filename  --> pour chiffrer (-e --> encrypt) le fichier nommé 'filename'")
      293     print("pyFC.py -e dirname   --> pour chiffrer (-e --> encrypt) le répertoire nommé 'dirname'")
      294     print("pyFC.py -d filename  --> pour déchiffrer (-d --> decrypt) le fichier nommé 'filename'. Ici, l'")
      295     print("                         argument filename peut désigner soit un fichier, soit un répertoire")
      296     print("                         contenant d'autres fichiers ou répertoires")
      297     print("pyFC.py -showconf    --> change les droits sur ~/.pyFC/.pyFC.conf pour le rendre accessible") 
      298     print("                         --> os.chmod(0o774)") 
      299     print("pyFC.py -hideconf    --> change les droits sur ~/.pyFC/.pyFC.conf pour le rendre inaccessible") 
      300     print("                         --> os.chmod(0o000)") 
      301     print("                     Attention, les arguments -showconf et -hideconf ne doivent être")
      302     print("                     utilisés qu'après la mise en place d'un jeu de clé valide !")
      303     print()
      304     print()
      305 
      306 
      307 
      308 def XOR2Arrays(bytesArray1, bytesArray2):    
      309     ''' Effectue un XOR byte à byte sur les 2 tableaux de bytes transmis,
      310         puis retourne le bytearray qui en resulte '''
      311     return bytearray(map(ord, ''.join(chr(a ^ b) for a,b in zip(bytesArray1, bytesArray2))))
      312 
      313 
      314 
      315 def setCryptoBytes(mode, fileLength, offsetIntArray):
      316     ''' Cette fonction permet de créer un tableau de bytes qui sera utilisé pour
      317         le chiffrement/déchiffrement du fichier à traiter '''
      318     cryptoArray = []
      319     with open(keyFilepathList[0], 'rb') as currentKeyFile1:
      320         if not 'decryptData' in mode:
      321             currentKeyFile1.seek(10000 + 29*(fileLength % 9999))
      322             byteArrayOff11 = currentKeyFile1.read(4)
      323             currentKeyFile1.seek(100000 + 1151*(fileLength % 5432))
      324             byteArrayOff52 = currentKeyFile1.read(4)
      325         if not 'decryptOffset' in mode:
      326             currentKeyFile1.seek(offsetIntArray[0])
      327             byteArrayData1 = currentKeyFile1.read(fileLength)
      328     with open(keyFilepathList[1], 'rb') as currentKeyFile2:
      329         if not 'decryptData' in mode:
      330             currentKeyFile2.seek(20000 + 113*(fileLength % 8888))
      331             byteArrayOff12 = currentKeyFile2.read(4)
      332             currentKeyFile2.seek(30000 + 229*(fileLength % 7777))
      333             byteArrayOff21 = currentKeyFile2.read(4)
      334         if not 'decryptOffset' in mode:
      335             currentKeyFile2.seek(offsetIntArray[1])
      336             byteArrayData2 = currentKeyFile2.read(fileLength)
      337     with open(keyFilepathList[2], 'rb') as currentKeyFile3:
      338         if not 'decryptData' in mode:
      339             currentKeyFile3.seek(40000 + 349*(fileLength % 6666))
      340             byteArrayOff22 = currentKeyFile3.read(4)
      341             currentKeyFile3.seek(50000 + 463*(fileLength % 5555))
      342             byteArrayOff31 = currentKeyFile3.read(4)
      343         if not 'decryptOffset' in mode:
      344             currentKeyFile3.seek(offsetIntArray[2])
      345             byteArrayData3 = currentKeyFile3.read(fileLength)
      346     with open(keyFilepathList[3], 'rb') as currentKeyFile4:
      347         if not 'decryptData' in mode:
      348             currentKeyFile4.seek(60000 + 601*(fileLength % 4444))
      349             byteArrayOff32 = currentKeyFile4.read(4)
      350             currentKeyFile4.seek(70000 + 733*(fileLength % 3333))
      351             byteArrayOff41 = currentKeyFile4.read(4)
      352         if not 'decryptOffset' in mode:
      353             currentKeyFile4.seek(offsetIntArray[3])
      354             byteArrayData4 = currentKeyFile4.read(fileLength)
      355     with open(keyFilepathList[4], 'rb') as currentKeyFile5:
      356         if not 'decryptData' in mode:
      357             currentKeyFile5.seek(80000 + 863*(fileLength % 2222))
      358             byteArrayOff42 = currentKeyFile5.read(4)
      359             currentKeyFile5.seek(90000 + 1013*(fileLength % 1111))
      360             byteArrayOff51 = currentKeyFile5.read(4)
      361         if not 'decryptOffset' in mode:
      362             currentKeyFile5.seek(offsetIntArray[4])
      363             byteArrayData5 = currentKeyFile5.read(fileLength)
      364     
      365     if not 'decryptData' in mode:
      366         cryptoArray.append(byteArrayOff11)
      367         cryptoArray.append(byteArrayOff12)
      368         cryptoArray.append(byteArrayOff21)
      369         cryptoArray.append(byteArrayOff22)
      370         cryptoArray.append(byteArrayOff31)
      371         cryptoArray.append(byteArrayOff32)
      372         cryptoArray.append(byteArrayOff41)
      373         cryptoArray.append(byteArrayOff42)
      374         cryptoArray.append(byteArrayOff51)
      375         cryptoArray.append(byteArrayOff52)
      376     if not 'decryptOffset' in mode:
      377         cryptoArray.append(byteArrayData1)
      378         cryptoArray.append(byteArrayData2)
      379         cryptoArray.append(byteArrayData3)
      380         cryptoArray.append(byteArrayData4)
      381         cryptoArray.append(byteArrayData5)
      382     return cryptoArray
      383 
      384 
      385 
      386 def encrypt(savedName, filesAsByteArray, fileIndex, nbSlices):
      387     ''' Fonction qui permet de procéder au chiffrement du tableau de bytes
      388         'filesAsByteArray' et de le sauver sous le nom savedName '''
      389     setKeyfilesAccess(True)
      390     offsetIntArray = []
      391     offsetBytesArray = []
      392 
      393     # ici, on créé le tableau d'offsets 
      394     # On aura toujours len(filesAsByteArray) < keyFileSize (donc < 100 MBytes)
      395     for index in range(5):
      396         intOffset = secrets.randbelow(100*(index+1)*(2**20)-len(filesAsByteArray))    
      397         byte3 = intOffset >> 24
      398         byte2 = (intOffset - byte3*(256**3)) >> 16
      399         byte1 = (intOffset - byte3*(256**3) - byte2*(256**2)) >> 8
      400         byte0 = (intOffset - byte3*(256**3) - byte2*(256**2) - byte1*256)
      401         bytesList = [byte0, byte2, byte3, byte1]
      402         offsetIntArray.append(intOffset) # offsetIntArray contient les offsets X au format integer normal
      403         offsetBytesArray.append(bytearray(bytesList)) # offsetBytesArray contient les offsets X au format 0231
      404 
      405     # On va créer le byteArray à partir duquel on va chiffrer les offsets et les données
      406     cryptoArray = setCryptoBytes('encrypt', len(filesAsByteArray), offsetIntArray)
      407 
      408     # On procède au chiffrement des offsets
      409     headerXOffset = []
      410     headerXOffset.append(XOR2Arrays(XOR2Arrays(offsetBytesArray[0], cryptoArray[0]), cryptoArray[1]))
      411     headerXOffset.append(XOR2Arrays(XOR2Arrays(offsetBytesArray[1], cryptoArray[2]), cryptoArray[3]))
      412     headerXOffset.append(XOR2Arrays(XOR2Arrays(offsetBytesArray[2], cryptoArray[4]), cryptoArray[5]))
      413     headerXOffset.append(XOR2Arrays(XOR2Arrays(offsetBytesArray[3], cryptoArray[6]), cryptoArray[7]))
      414     headerXOffset.append(XOR2Arrays(XOR2Arrays(offsetBytesArray[4], cryptoArray[8]), cryptoArray[9]))
      415 
      416     # On procède au chiffrement des données du fichier
      417     print("Tranche "+str(fileIndex+1)+"/"+str(nbSlices)+":")
      418     print("--> Chiffrement clé 1/5")
      419     tempData1 = XOR2Arrays(filesAsByteArray, cryptoArray[10])
      420     print("--> Chiffrement clé 2/5")
      421     tempData2 = XOR2Arrays(tempData1, cryptoArray[11])
      422     print("--> Chiffrement clé 3/5")
      423     tempData3 = XOR2Arrays(tempData2, cryptoArray[12])
      424     print("--> Chiffrement clé 4/5")
      425     tempData4 = XOR2Arrays(tempData3, cryptoArray[13])
      426     print("--> Chiffrement clé 5/5")
      427     finalData = XOR2Arrays(tempData4, cryptoArray[14])
      428 
      429     # On sauve le fichier chiffré
      430     with open(savedName, 'wb') as treatedFile:
      431         for oneOffset in headerXOffset:
      432             treatedFile.write(oneOffset)
      433         treatedFile.write(finalData)
      434     print("Chiffrement terminé -> Création du fichier " + savedName )
      435     print()    
      436     setKeyfilesAccess(False)
      437 
      438 
      439 
      440 def addDirToZip(zipHandle, path, basePath=""):
      441     ''' On va ici placer dans l'archive zipHandle tout le contenu du répertoire path '''
      442     basePath = basePath.rstrip("\\/") + ""
      443     basePath = basePath.rstrip("\\/")
      444     for root, dirs, files in os.walk(path):
      445         # add dir itself (needed for empty dirs)
      446         zipHandle.write(os.path.join(root, "."))
      447         # add files
      448         for oneFile in files:
      449             filePath = os.path.join(root, oneFile)
      450             inZipPath = filePath.replace(basePath, "", 1).lstrip("\\/")
      451             #print filePath + " , " + inZipPath
      452             zipHandle.write(filePath, inZipPath)
      453 
      454 
      455 
      456 def sliceAndEncrypt(filePath):
      457     ''' Pour simplifier les choses au niveau de la fonction de chiffrement (encrypt(filePath)), on impose
      458         que pour tout fichier à chiffrer, la taille  doit être inférieure à celle de la clé de chiffement
      459         la plus petite, soit 100MBytes (100 * 2**20). Si le fichier à chiffrer est de taille supérieure, 
      460         on va alors ici le découper en plusieurs tranches de tailles ~ équivalentes et inférieures à cette
      461         limite de 100 MBytes. On chiffrera ensuite l'ensemble des fichiers obtenus
      462         La variable limitSize (voir un peu plus bas après la ligne 'if proceed:') contiendra la limite de
      463         taille réellement choisie '''
      464     
      465     print()
      466     proceed = False
      467     # Si filePath pointe vers un fichier, on va le zipper à partir du fichier avant le chiffrement
      468     # cela permettra de ne pas avoir à se préoccuper du nom du fichier original (qui sera contenu dans le zip)
      469     if os.path.isfile(filePath):  
      470         # On va créer le zip dans le répertoire courant (currentDir)
      471         # Attention à bien avoir les droits d'écriture dans ce répertoire !
      472         simpleFilename = filePath.split('/')[-1] # Dans l'archive, on ne mettra que le nom du fichier, sans son path
      473         print("Chiffrement de " +simpleFilename)
      474         print()
      475         print("Étape 1 : Création de l'archive principale")
      476         zipHandle = zipfile.ZipFile('crypted.zip', 'w', zipfile.ZIP_DEFLATED)
      477         zipHandle.write(simpleFilename) 
      478         zipHandle.close()
      479         proceed = True
      480    
      481     # Si filePath pointe vers un répertoire, on va le zipper afin de pouvoir le traiter comme un fichier
      482     if os.path.isdir(filePath):
      483         # On va créer le zip dans le répertoire courant (currentDir)
      484         # Attention à bien avoir les droits d'écriture dans ce répertoire !
      485         zipHandle = zipfile.ZipFile('crypted.zip', 'w', zipfile.ZIP_DEFLATED)
      486         savedCurrentDir = currentDir
      487         repList = filePath.split('/')
      488         fRepList = []
      489         repToGo = ''
      490         proceed = True
      491 
      492         # Si l'on veut par exemple crypter le répertoire /home/toto/Documents , on va se placer dans /home/toto de
      493         # manière à se trouver à un étage 'supérieur' au répertoire 'Documents'. Ainsi, on pourra plus facilement
      494         # inclure ce nom de répertoire (et uniquement lui) dans l'archive zip, qui contiendra alors le répertoire
      495         #'/Documents' dans lequel on retrouvera tous ses fichiers et sous-répertoires
      496         for nStr in repList:
      497             if len(nStr)>0:
      498                 fRepList.append(nStr)
      499                 zipThisDir = nStr # Selon notre exemple, zipThisDir contiendrait 'Documents'
      500        
      501         if len(fRepList) == 0:
      502             proceed = False
      503             print4("Erreur, vous ne pouvez pas chiffrer l'ensemble de la racine (/) ! \r\n \
      504                     Sélectionnez un répertoire existant !")
      505         
      506         if len(fRepList) == 1:
      507             repToGo = '/'
      508             os.chdir(repToGo) 
      509             print("Chiffrement de " +filePath)
      510             print()
      511             print("Étape 1 : Création de l'archive principale")
      512             addDirToZip(zipHandle, zipThisDir) 
      513             zipHandle.close()
      514             os.chdir(savedCurrentDir) 
      515         
      516         if len(fRepList)>1:
      517             del fRepList[-1]
      518             for oneDir in fRepList:
      519                 repToGo += '/' + oneDir
      520             os.chdir(repToGo) # Selon notre exemple, on remonte ici dans le répertoire '/home/toto'
      521             print("Chiffrement de " +filePath)
      522             print()
      523             print("Étape 1 : Création de l'archive principale")
      524             addDirToZip(zipHandle, zipThisDir) # Ici, on va ajouter à l'archive zip tout le
      525             zipHandle.close()                  # contenu de 'Documents' vu de '/home/toto'
      526             os.chdir(savedCurrentDir) # On retourne ici dans le répertoire d'où l'on a appelé le script
      527 
      528     if proceed :
      529         print()
      530         print("Étape 2 : Découpe et Chiffrement :")
      531         filePath = currentDir+'/crypted.zip'
      532         fileSize = os.path.getsize(filePath)
      533 
      534         # On va charger le fichier à traiter, puis le découper en byteArray(s) de taille < 100MBytes si besoin
      535         # On a le choix ici de la limite de taille des byteArrays (doit être impérativement < 100MBytes)
      536         # Plus on abaisse cette limite, plus le chiffrement sera rapide pour les fichiers de taille supérieure
      537         # En contrepartie, on aura un chiffrement en plusieurs étapes (autant que de tranches, après découpe
      538         # du fichier crypted.zip)
      539         limitSize = (3+int(fileSize/(15*(2**20)))) * (2**20)
      540         if limitSize > (50*(2**20)):
      541             limitSize = 50*(2**20)
      542         print(limitSize)
      543         if fileSize < limitSize:
      544             nbSlices = 1
      545             rawSliceSize = fileSize
      546         else:
      547             nbSlices = int(fileSize/limitSize) + 1
      548             rawSliceSize = int(fileSize/nbSlices) + 1
      549       
      550         filesAsByteArray = []
      551         with open(filePath, 'rb') as treatedFile:
      552             for n in range(nbSlices):
      553                 treatedFile.seek(n*rawSliceSize)
      554                 filesAsByteArray.append(treatedFile.read(rawSliceSize))
      555 
      556         # On va maintenant chiffrer chaque byteArray, puis le sauver en tant que fichier chiffré
      557         namePart = '/' + str(int(time.time()))
      558         print(nbSlices)
      559         for n in range(nbSlices):
      560             fileExt = ".00"
      561             if 9 < n < 100:
      562                 fileExt = ".0"
      563             if 1000 > n > 99:
      564                 fileExt = "."        
      565             saveName = currentDir + namePart + fileExt + str(n)
      566             encrypt(saveName, filesAsByteArray[n], n, nbSlices)
      567             
      568         # On va supprimer le fichier crypted.zip qui n'est plus utile
      569         os.remove(filePath)
      570         time.sleep(2) # Petite pause avant fermeture du terminal si lancé en clic-droit depuis caja
      571 
      572 
      573 
      574 def decrypt(filePath, nbSlices):
      575     ''' Fonction qui permet de procéder au déchiffrement du fichier filePath '''
      576     if os.path.isfile(filePath):  # Si filePath pointe bien vers un fichier
      577         setKeyfilesAccess(True)
      578         usedLength = os.path.getsize(filePath) - 20 # On retire les 20 octets d'entête qui ont été 
      579         headerXOffset = []                          # ajouté lors du chiffrement
      580 
      581         # On va reconstituer le byteArray à partir duquel on a chiffré les offsets
      582         offsetIntArray = 0
      583         cryptoArray = setCryptoBytes('decryptOffset', usedLength, offsetIntArray)
      584 
      585         # Lecture et déchiffrement 1/2 des 5 offsets chiffrés
      586         with open(filePath, 'rb') as cryptedFile:
      587             off1 = cryptedFile.read(4)
      588             headerXOffset.append(XOR2Arrays(XOR2Arrays(off1, cryptoArray[0]), cryptoArray[1]))
      589             cryptedFile.seek(4)
      590             off2 = cryptedFile.read(4)
      591             headerXOffset.append(XOR2Arrays(XOR2Arrays(off2, cryptoArray[2]), cryptoArray[3]))
      592             cryptedFile.seek(8)
      593             off3 = cryptedFile.read(4)
      594             headerXOffset.append(XOR2Arrays(XOR2Arrays(off3, cryptoArray[4]), cryptoArray[5]))
      595             cryptedFile.seek(12)
      596             off4 = cryptedFile.read(4)
      597             headerXOffset.append(XOR2Arrays(XOR2Arrays(off4, cryptoArray[6]), cryptoArray[7]))
      598             cryptedFile.seek(16)
      599             off5 = cryptedFile.read(4)
      600             headerXOffset.append(XOR2Arrays(XOR2Arrays(off5, cryptoArray[8]), cryptoArray[9]))
      601             cryptedFile.seek(20)
      602             cypherData = cryptedFile.read(usedLength)
      603 
      604         # Reconstitution des offsets (Déchiffrement 2/2)
      605         intOffset = []
      606         for oneHeaderOffset in headerXOffset:
      607             byte0 = oneHeaderOffset[0]
      608             byte2 = oneHeaderOffset[1]
      609             byte3 = oneHeaderOffset[2]
      610             byte1 = oneHeaderOffset[3]
      611             intOffset.append(int(byte3*(256**3) + byte2*(256**2)+ byte1*256 + byte0))
      612 
      613         # On va reconstituer le byteArray à partir duquel on a chiffré les données
      614         cryptoArray = setCryptoBytes('decryptData', usedLength, intOffset)
      615 
      616         # On procède au déchiffrement des données
      617         fileExt = filePath.split('.')[-1]
      618         intFileExt = int(fileExt)
      619         print("Tranche "+str(intFileExt+1)+"/"+str(nbSlices)+":")
      620         print("--> Déchiffrement clé 1/5")
      621         tempData1 = XOR2Arrays(cypherData, cryptoArray[4])
      622         print("--> Déchiffrement clé 2/5")
      623         tempData2 = XOR2Arrays(tempData1, cryptoArray[3])
      624         print("--> Déchiffrement clé 3/5")
      625         tempData3 = XOR2Arrays(tempData2, cryptoArray[2])
      626         print("--> Déchiffrement clé 4/5")
      627         tempData4 = XOR2Arrays(tempData3, cryptoArray[1])
      628         print("--> Déchiffrement clé 5/5")
      629         finalData = XOR2Arrays(tempData4, cryptoArray[0])
      630 
      631         # On sauve le fichier déchiffré
      632         with open(currentDir+'/decypher.'+fileExt, 'wb') as treatedFile:
      633             treatedFile.write(finalData)
      634         print("Déchiffrement terminé -> Écriture du fichier " + currentDir+'/decypher.'+fileExt)
      635         print()    
      636         setKeyfilesAccess(False)
      637         return(currentDir+'/decypher.'+fileExt) # On va retourner le nom du fichier déchiffré, 
      638                                                 # afin de pouvoir le ré-assembler si besoin
      639 
      640 
      641 
      642 def getSlicesAndDecrypt(filePath):
      643     ''' On va déterminer ici en combien de tranches le fichier original a été découpé avant chiffrement.
      644         On devra déchiffrer chaque tranche, puis ré-assembler le tout pour reconstituer l'archive originale.
      645         Dans un dernier temps, on pourra enfin décompresser l'archive '''
      646 
      647     print()
      648     if os.path.exists(filePath):
      649         print("Déchiffrement de " +filePath)
      650         print()
      651         repList = filePath.split('.')
      652         if '000' in repList[-1]: # On s'assure que le fichier à déchiffrer possède bien une extension en .000
      653             myCypherList = []
      654             myCypherList.append(filePath)
      655             index = 1
      656             bitContinue = True
      657             while bitContinue:
      658                 fileExt = ".00"
      659                 if 9 < index < 100:
      660                     fileExt = ".0"
      661                 if 1000 > index > 99:
      662                     fileExt = "."        
      663                 tryThisFile = repList[0]+fileExt+str(index)
      664                 index += 1
      665                 if os.path.exists(tryThisFile):
      666                     myCypherList.append(tryThisFile) # myCypherList contiendra la liste des fichiers à déchiffrer
      667                 else:
      668                     bitContinue = False
      669             decypheredList = []
      670             decypheredByteArray = []
      671             for oneCryptedSlice in myCypherList:
      672                 decypheredList.append(decrypt(oneCryptedSlice, len(myCypherList)))
      673             for oneFile in decypheredList:
      674                 with open(oneFile, 'rb') as decypheredSlice:
      675                     tmpArray = decypheredSlice.read()
      676                     decypheredByteArray.append(tmpArray)
      677                 os.remove(oneFile)
      678             with open('finalFile.zip', 'wb') as finalFile:
      679                 for oneByteArray in decypheredByteArray:
      680                     finalFile.write(oneByteArray)
      681             finalZip = zipfile.ZipFile('finalFile.zip', 'r')
      682             try:
      683                rootArcName = finalZip.namelist()[0] # On vient chercher le nom du répertoire racine contenu
      684             except:                                 # dans finalFile.zip
      685                print("L'archive semble vide...")
      686                print()
      687             finalZip.extractall(currentDir)
      688             os.remove('finalFile.zip')
      689             print4("Extraction / création de " +rootArcName+ "  --> Déchiffrement terminé")
      690             time.sleep(2) # Petite pause avant fermeture du terminal si lancé en clic-droit depuis caja
      691         else:
      692             print4(filePath+" n'est pas un fichier valide pour déchiffrement !")
      693             time.sleep(2) # Petite pause avant fermeture du terminal si lancé en clic-droit depuis caja
      694         
      695 
      696 
      697 
      698 ###################### Lancement du programme principal ######################
      699 #
      700 if __name__ == "__main__":
      701     pfcDir = os.path.expanduser('~')+'/.pyFC'
      702     pfcConfFile = pfcDir+'/.pyFC.conf'
      703     currentDir = os.getcwd()
      704     keyFilepathList = []
      705     if len(sys.argv) == 1:
      706         printUsage()
      707     if len(sys.argv) == 2:
      708         foundBit2 = False
      709         if '-help' in (sys.argv[1]):
      710             foundBit2 = True
      711             printUsage()
      712         if '-init' in (sys.argv[1]):
      713             foundBit2 = True
      714             print("Cette opération va générer un nouveau jeu de clés de chiffrement.")
      715             strOK = input("Voulez-vous vraiment continuer ? (Abandon si différent de OK) : ")
      716             if len(strOK)==2 and 'OK' in strOK:
      717                 createKeyFiles()
      718             else:
      719                 print4("Abandon...")
      720         if '-showconf' in (sys.argv[1]):
      721             foundBit2 = True
      722             readConfFile()
      723             setConfFileAccess(True)
      724             setKeyfilesAccess(True)
      725             print4("Configuration et arborescence de clés accessibles.")
      726         if '-hideconf' in (sys.argv[1]):
      727             foundBit2 = True
      728             readConfFile()
      729             setConfFileAccess(False)
      730             setKeyfilesAccess(False)
      731             print4("Configuration et arborescence de clés protégées.")
      732         if not foundBit2: # Cas où l'on oublie par exemple de fournir un chemin pour le chiffrement/déchiffrement
      733             printUsage()
      734     if len(sys.argv) == 3:
      735         ed = False
      736         badFilePath = False
      737         if '-e' in (sys.argv[1]) or '-d' in (sys.argv[1]):
      738             ed = True
      739             if len(sys.argv[2]) > 1:
      740                 if os.path.exists(sys.argv[2]):  # On va ici traiter le chiffrement ou le déchiffrement 
      741                     readConfFile()  # On lit le fichier conf pour accéder aux chemins des fichiers clés
      742                     if '-e' in (sys.argv[1]):
      743                         sliceAndEncrypt(sys.argv[2])
      744                     if '-d' in (sys.argv[1]):
      745                         getSlicesAndDecrypt(sys.argv[2])
      746                 else:
      747                     badFilePath = True
      748         if not ed:
      749             # Erreur sur argument -e ou -d
      750             print4("Erreur : commande incompréhensible !")
      751             printUsage()
      752         if badFilePath:
      753             # Erreur sur chemin vers fichier ou répertoire passé en argument
      754             print4("Erreur : la chaine à traiter n'existe pas, n'est ni un fichier ni un répertoire !")
      755     if len(sys.argv) > 3: # Trop de paramètres passés en argument !
      756         print4("Erreur : trop de paramètres passés en argument !")
      757         printUsage()
      758 
    

    Tags Tags : , , , ,
  • Commentaires

    Aucun commentaire pour le moment

    Suivre le flux RSS des commentaires


    Ajouter un commentaire

    Nom / Pseudo :

    E-mail (facultatif) :

    Site Web (facultatif) :

    Commentaire :