pycrypto
¶
Par Luca Srdjenovic 1
Introduction¶
pycrypto est une bibliothèque qui fournit des outils de cryptographie primitive implémententés en langage Python. Par exemple quand on fait du réseau, on a souvent besoin d’utiliser la cryptographie. En effet des méchants peuvent se situer entres deux machines qui commmuniquent pour les écouter. Et le méchant s’il le désire peut faire encore pire, c’est-à-dire remplacer un bout ou totalement une partie du message envoyé. À noter que pour des raisons de sécurité, il est recommandé d’utiliser une bibliothèque de haut niveau ce qui donnera moins de travail au programmeur pour éviter des longues analyses de sécurité. pycrypto correspond bien à cette demande mais est une approche dangereuse qui demande de porter un grand détail à son fonctionnement. Cependant il permet de faire ce que l’on désire en cryptographie.
Mots clefs¶
Quelques mots clefs pour aider la compréhension pour la suite :
- plaintext
message original
- ciphertext
message après la transformation cryptograhpique appliqué au message original
- encrypt
produit le ciphertext en appliquant la transformation cryptographique au plaintext
- decrypt
produit le plaintext en appliquant la transformation cryptographique au ciphertext
- cipher
une composition particulière de la transformation cryptographique fournissant encryptage et décryptage
- hash
transformation cryptographique qui prend une grande entrée et la transforme en une unique sortie (de taille fixée).
Type de cryptage¶
En cryptographie, il existe deux types de chiffrement. Le symétrique et l’asymétrique. Comme PyCrypto est vaste, nous allons juste nous intéresser au chiffrement symétrique.
- Pour le symétrique, il y a deux types de clef :
stream ciphers
: opération sur des flux de données un byte à la foisblock ciphers
: opération de blocks sur des données, en 16 bytes à la fois.
Nous allons utiliser le block cipher. Le plus commun et standard avancé est l”AES si on n’a pas trop d’idée de quel algorithme de cryptage à utiliser. Un peu moins commun est le DES.c’est le standard de la cryptographie car il était très utilisé dans le passé mais sa clef est trop petitE aujourd’hui à cause de ces 56-bit de taille.
Voici une liste des différents algorithmes possibles :
Nom du module
Type
Crypto.Cipher.AES
Block
Crypto.Cipher.ARC2
Block
Crypto.Cipher.ARC4
Stream
Crypto.Cipher.Blowfish
Block
Crypto.Cipher.CAST
Block
Crypto.Cipher.DES
Block
Crypto.Cipher.DES3
Block
Crypto.Cipher.XOR
Stream
Cryptographie symétrique AES
¶
Les données d’entrées qui sont la plupart du temps des chaînes de caractères
sont transformées en une sorte de variable appelé clef
ce qui va
produire le ciphertext
. Ceci est engendré par des algorithmes
d’encryptions.
Les block ciphers qui prennent des entrées d’une taille fixe entre 8 et 16 octets
les chiffrent. Les cipher blocks requierent des modes. ECB
(Electronic Code Book)
est le mode le plus simple. Ce n’est pas le meilleur parce qu’il
a une faille quand des fichiers contiennent du code qui a une longueur plus grande
que celle des blocks.
Pour palier à ce problème, il y a CBC
(Cipher Block Chaining) qui
combine le texte avec le ciphertext avant chaque encryption et encrypte block
par block. Ce mode est plus lent que le ECB.
Il y a aussi le CFB
(Cipher FeedBack) qui encrypte octet
par octet. Ce mode est encore plus lent que CBC. En plus le CFB demande une
chaine de caractère de base à 8 ou à 16 octets à l’initialisation. CBC et CFB
sont les modes les plus communs.
Exemple¶
Pour les exemples nous utiliserons le mode CBC. Avant de commencer, il nous faut créer un nombre aléatoire qui possède la même taille du bloc car la cryptographie dépend cruciellement d’un nombre aléatoire. Nous n’utiliserons pas le random() générateur de python mais celui de PyCrpyto car ce premier est inaproprié. Ces données aléatoires sont appellées comme un vecteur d’initialisation (Initialisation Vector). Donc nous allons écrire une fonction qui nous retourne un nombre aléatoire.
import Crypto.Random.OSRNG.posix as RNG
def randomCryptoNumber():
return RNG.new().read(AES.block_size) #Retourne un nombre aléatoire
Avant de commencer à crypter notre message, nous devons d’abord s’assurer que la longueur du bloc de donnée est multiplié par la taille du block. Par exemple, si notre message est « Bonjoure Bonjoure », nous devons ajouter encore 8 byte. Un schéma d’ajout est de mettre « 0x80 » au premier byte et « 0x00 » aux byte qui suivent le reste de l’ajout. Ceci donnerai alors sur l’exemple: « Bonjoure Bonjoure0x800x000x000x000x000x000x000x000x00 ».
def ajout_data(data):
# si la taille de donnée fait 16 byte, aucun ajout
if len(data) % 16 == 0:
return data
# On enlève un byte pour ajouter le 0x80
dataAjouter = 15 - (len(data) % 16)
data = '%s\x80' % data
data = '%s%s' % (data, '\x00' * dataAjouter)
return data
La fonction pour enlever l’ajout
def desajout_data(data):
if not data:
return data
data = data.rstrip('\x00')
if data[-1] == '\x80':
return data[:-1]
else:
return data
Maintenant il nous faut une clef pour le symétrique ciphers. Il y a trois tailles de clefs :
16 bytes (128 bits),
24 bytes (192 bits),
et 32 bytes (256 bits).
Nous allons simplement générer une clef aléatoire de 32 bytes avec une fonction.
taille_clef = 32
def generateCryptoKey():
return RNG.new().read(taille_clef)
Maintenant, on peut utiliser cette clef pour crypter et decrypter des données.
import Crypto.Cipher.AES as AES
def encrypt(data, clef):
#Encrypte les donnée utilisant AES en mode CBC
data = ajout_data(data)
number = randomCryptoNumber()
aes = AES.new(clef, AES.MODE_CBC, number)
msg_crypt = aes.encrypt(data)
return numbet + msg_crypt
def decrypt(ciphertext, clef):
#Decrypt un ciphertext encrypté avec l'AES en mode CBC
if len(ciphertext) <= AES.block_size:
raise Exception("Invalid ciphertext.")
number = ciphertext[:AES.block_size]
ciphertext = ciphertext[AES.block_size:]
aes = AES.new(clef, AES.MODE_CBC, number)
data = aes.decrypt(ciphertext)
return desajout_data(data) #Enleve le padding ajouter
Nous avons sécurisé notre message maintenant, mais ce n’est pas encore optimale. Il faut ajouter un algorithme de hashage à nos fonctions encrypt et decrypt. Nous allons utiliser un SHA-384 qui va augmenter de 48 bytes notre AES qui lui est à 32 bytes pour qu’on soit vraiment performant.
Par exemple, crééons un hash:
>>> import hashlib
>>> sha = hashlib.sha1(b'Hello Python').hexdigest()
>>> sha
'422fbfbc67fe17c86642c5eaaa48f8b670cbed1b'
Cryptographie symétrique DES
¶
Voici un code pour crypter un string en DES
>>> from Crypto.Cipher import DES
>>> key = 'abcdefgh'
>>> def pad(text):
while len(text) % 8 != 0:
text += ' '
return text
>>> des = DES.new(key, DES.MODE_ECB)
>>> text = 'test'
>>> padded_text = pad(text)
>>> encrypted_text = des.encrypt(padded_text)
>>> encrypted_text
b'>\xfc\x1f\x16x\x87\xb2\x93\x0e\xfcH\x02\xd59VQ'
La taille de la clef en DES est de 8 bytes. Pour encrypter le string, il doit être multiplié par 8 dans sa longueur. La fonction qui fait ça s’appelle « pad ». Elle ajoute des espaces jusqu’a que ce soit un multiple de 8. Ensuite on crée une instance DES avec le text qu’on veut encrypter.
Pour décrypter, il suffit simplement de faire la commande suivante :
>>> des.decrypt(encrypted_text)
b'test'
Chiffrement avec clés publiques¶
Quand on fait du chiffrement avec clefs, on va crée des clés publiques. Si une personne voudra recevoir des fichiers auxquels il sera le seul à avoir l’authorisation de lire, il devra avoir une clef privée. Avec la clef pubique, tous pourront y avoir accès quand elle est diffusée. Mais seulement les personnes qui ont la clef privée pourront decrypter le message.
Exemple code avec AES et DES en mode CFB¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | """Example de Pycrypto."""
from Crypto import Random
from Crypto.Cipher import AES, DES
# Vecteurs d'initialisation
iv_AES = Random.new().read(AES.block_size)
iv_DES = Random.get_random_bytes(8)
key_AES = 'abcdefghijklmnop'
key_DES = 'abcdefgh'
aese = AES.new(key_AES, AES.MODE_CFB, iv_AES)
aesd = AES.new(key_AES, AES.MODE_CFB, iv_AES)
dese = DES.new(key_DES, DES.MODE_CFB, iv_DES)
desd = DES.new(key_DES, DES.MODE_CFB, iv_DES)
plaintext = 'Hello! World'
plaintext = aesd.decrypt(aese.encrypt(plaintext))
print(plaintext)
plaintext = desd.decrypt(dese.encrypt(plaintext))
print(plaintext)
|
Resultat:
#Output AES
b'Hello! World'
#Output DES
b'Hello! World'
Conclusion¶
Aujourd’hui, la cryptographie est une importante partie pour quiconque voudrait rendre sûr leur donnée. Il existe plus de dizaines de système de chiffrement rien qu’en Python. Si l’on désire faire des systèmes de communications sécurisés ou des votes électroniques, il est recommandé d’utiliser des protocoles de haut niveau. Python fournit un large panel d’outils pour y remédier, c’est pour ça qu’il est important d’avoir quelques notions pour éviter de se perdre.