Chiffrement AES avec python

 

Nous allons commencer cette longue série sur la cryptographie appliquée avec python.

Nous allons commencer par AES.

Qu'est-ce que le chiffrement AES ?

L'Advanced Encryption Standard (AES) est le chiffrement symétrique le plus largement utilisé. Aujourd'hui, bien que le terme "Standard" dans son nom se réfère uniquement au gouvernement américain, le chiffrement en masse AES est également obligatoire dans plusieurs normes de l'industrie et est utilisé dans de nombreux systèmes commerciaux. Les normes commerciales des systèmes AES incluent la norme de sécurité Internet IPsec, TLS, le chiffrement Wi-Fi, la norme IEEE 802.11i, le protocole réseau SSH (Secure Shell), le téléphone Internet Skype et de nombreux produits de sécurité dans le monde. À ce jour, il n'y a pas de meilleure attaque que la force brute connue contre AES.

Avec AES, nous avons des blocs de 16 octets (128 bits) et des tailles de clé de 16, 24, 32 octets

Paramètres d'entrée/sortie AES

Pour en savoir plus sur le cryptosystème AES, vous pouvez regarder la vidéo de Christof Paar.

Nous passons par un certain nombre de processus et où nous opérons sur 16 octets en entrée et en sortie. Chaque bloc, appelé état, est exploité comme une matrice 4x4, telle que :

01 02 03 04
05 06 06 07
08 09 0A 0B
0C 0D 0E 0F
Pour différentes tailles de clés, on passe par un certain nombre de tours (N) :

1. Clé 128 bits (16 octets) -> N=10 tours
2. Clé 192 bits (24 octets) -> N=12 tours
3. Clé 256 bits (32 octets) -> N=14 tours

La figure 1 ci-dessous décrit le processus de chiffrement 128 bits, et où nous avons 10 tours. Pour une clé de 128 bits, elle est étendue à 44 mots de 33 bits chacun, et où chaque tour utilise quatre mots (128 bits) en entrée pour chaque tour. Au tour 0, la transformation initiale consiste à ajouter un clé en main. Les tours suivants (hormis le tour final) consistent en :

1. Octets de substitution.
2. Décalez la ligne.
3. Colonne de mélange.
4.Ajoutez une clé d'arrondi.

Et le tour final consiste en :

1. Remplacez les octets.
2. Décaler la ligne.
3.Ajoutez une clé d'arrondi.

Figure 1

S-box et S-box inversée

Dans le cadre du processus, transforme les entrées en une nouvelle valeur en tant que sortie de chaque état en une nouvelle valeur à l'aide d'un tableau S-box (comme le tableau 1). Dans ce cas, la table S-Box est une matrice 16x16 qui prend chaque valeur d'entrée, où les quatre premiers bits sont utilisés pour définir la ligne de la table, et les quatre bits suivants définissent la colonne (Figure 2.a). Par exemple, si l'octet d'entrée est CF, la sortie sera 8A. La S-box inverse le processus de la S-box, de sorte que le DF se réfère au CF (Figure 2.b).

Figure 2

Voici quelques exemples de code Python3 qui implémente la S-box et la S-box inversée :

 
    sbox = [ 
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
]
sboxInv = [
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
]



def subBytes(state):
for i in range(len(state)):
state[i] = sbox[state[i]]



def subBytesInv(state):
for i in range(len(state)):
state[i] = sboxInv[state[i]]


state=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

subBytes(state)
print("s-box: ", state)

subBytesInv(state)
print("inverse of s-box: ", state)

Si nous exécutons quelques exemples de données, nous pouvons voir que nous récupérons les données d'origine lorsque nous implémentons la S-box inverse :

s-box:  [124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202] 
inverse of s-box: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]

Transformation de ligne de décalage (Shift Row Transformation)

Avec ce processus, la transformation suivante est appliquée :
1. La première ligne reste inchangée.
2. La deuxième ligne a un décalage circulaire d'un octet vers la gauche.
3. La troisième ligne est décalée de deux octets vers la gauche.
4. La quatrième ligne est décalée de trois octets vers la gauche.

Par exemple:

54 33 AB C1 -> 54 33 AB C1

32 15 8D BB -> 15 8D BB 32

5A 73 D5 52 -> D5 52 5A 73

31 91 CC 98 -> 98 31 91 CC

Pour le processus inverse, un décalage vers la droite sera utilisé. Voici un exemple de code de décalage :

def rotate(word, n):
return word[n:]+word[0:n]

def shiftRows(state):
for i in range(4):
state[i*4:i*4+4] = rotate(state[i*4:i*4+4],i)


def shiftRowsInv(state):
for i in range(4):
state[i*4:i*4+4] = rotate(state[i*4:i*4+4],-i)


state=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

shiftRows(state)
print("row: ", state)


shiftRowsInv(state)
print("row inverse: ", state)

Un exemple d'exécution donne :

row:  [1, 2, 3, 4, 6, 7, 8, 5, 11, 12, 9, 10, 16, 13, 14, 15]
row inverse: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]

Dans cette transformation, chaque colonne est prise une par une et chaque octet de la colonne est transformé en une nouvelle valeur basée sur les quatre octets de la colonne. Pour chaque colonne (a0, a1, a2 et a3) nous avons (où nous utilisons la multiplication de Galois)

Le code Python pour la transformation de colonne (MIX Column) pour une seule colonne est :

from copy import copy

def galoisMult(a, b):
p = 0
hiBitSet = 0
for i in range(8):
if b & 1 == 1:
p ^= a
hiBitSet = a & 0x80
a <<= 1
if hiBitSet == 0x80:
a ^= 0x1b
b >>= 1
return p % 256

def mixColumn(column):
temp = copy(column)
column[0] = galoisMult(temp[0],2) ^ galoisMult(temp[3],1) ^ \
galoisMult(temp[2],1) ^ galoisMult(temp[1],3)
column[1] = galoisMult(temp[1],2) ^ galoisMult(temp[0],1) ^ \
galoisMult(temp[3],1) ^ galoisMult(temp[2],3)
column[2] = galoisMult(temp[2],2) ^ galoisMult(temp[1],1) ^ \
galoisMult(temp[0],1) ^ galoisMult(temp[3],3)
column[3] = galoisMult(temp[3],2) ^ galoisMult(temp[2],1) ^ \
galoisMult(temp[1],1) ^ galoisMult(temp[0],3)

def mixColumnInv(column):
temp = copy(column)
column[0] = galoisMult(temp[0],14) ^ galoisMult(temp[3],9) ^ \
galoisMult(temp[2],13) ^ galoisMult(temp[1],11)
column[1] = galoisMult(temp[1],14) ^ galoisMult(temp[0],9) ^ \
galoisMult(temp[3],13) ^ galoisMult(temp[2],11)
column[2] = galoisMult(temp[2],14) ^ galoisMult(temp[1],9) ^ \
galoisMult(temp[0],13) ^ galoisMult(temp[3],11)
column[3] = galoisMult(temp[3],14) ^ galoisMult(temp[2],9) ^ \
galoisMult(temp[1],13) ^ galoisMult(temp[0],11)

g = [1,2,3,4]

mixColumn(g)
print ('Mixed: ',g)

mixColumnInv(g)
print ('Inverse mixed', g)
Le résultat donne :
 
Mixed:  [3, 4, 9, 10]
Inverse mixed [1, 2, 3, 4]

Ajouter une transformation de clé (Round Key)

Avec cette transformation, nous implémentons une opération XOR entre la round key et les bits d'entrée. Une méthode Python pour implémenter ceci est:

def addRoundKey(state, roundKey):
for i in range(len(state)):
state[i] = state[i] ^ roundKey[i]

state=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
roundkey=[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1]

addRoundKey(state,roundkey)
print(state)

addRoundKey(state,roundkey)
print(state)

Un exemple d'exécution effectue :

[3, 1, 7, 1, 3, 1, 15, 1, 3, 1, 7, 1, 3, 1, 31, 17]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]

Aucun commentaire:

Enregistrer un commentaire

Pages