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 fois

  • block 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.

Tux encrypté avec ECB et CBC.

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.