Retrouvez le Notebook correspondant à cet article dans
Capytale
La genèse de cet article est issue de la curiosité suscitée par cette expression « fonction de hachage SHA-256 » rencontrée lors de recherches sur le Web au sujet de la sécurisation des communications, chapitre que je devais enseigner l’année dernière en Terminale Spécialité NSI pour la première fois. Je ne trouvais que des définitions incomplètes (comme ici) et je percevais des mathématiques qui pouvaient m’intéresser et être étudiées par mes élèves (là je pensais surtout à mes élèves de maths expertes). Aussi je décidais de questionner une IA générative, Deep Seek pour répondre principalement aux questions mathématiques non abordées dans les sites rencontrés.
Cet article est le fruit de mes recherches sur le Web et de mes prompts échangés avec Deep Seek.
- Bonjour, je veux savoir ce qu’est mathématiquement parlant la fonction de hachage SHA-256 ?
- Chez le constructeur (ex : Texas Instruments, Numworks) :
- On prend le fichier de l’OS ou de l’application.
- On calcule son empreinte SHA-256 (une sorte de « carte d’identité » unique du fichier).
- On chiffre cette empreinte avec la clé privée secrète du constructeur (avec un algorithme comme RSA). Le résultat est une signature numérique.
- Cette signature est jointe au fichier original. Par exemple, Texas Instruments intègre cette signature à la fin de ses fichiers .8ek.
Tu écris que les valeurs initiales sont dérivées des racines carrées des 8 premiers nombres premiers. Peux-tu m’expliquer pourquoi ?
- Je veux bien une explication sur le calcul des constantes $K_t$.
- Peux-tu expliciter cela : $σ_0$ et $σ_1$ sont des fonctions de rotation et décalage ?
- Ton Exemple pour $σ_0(x)$ n’est pas clair, peux-tu le vérifier ?
- Dans la vérification avec Python tu indiques que le code affiche $0xF048D156$ or en pratique il affiche $0xf02468ac$, peux-tu vérifier ton calcul ou la fonction Python que tu as proposée ?
- Peux-tu me donner un exemple de cela ? :
Le message est complété pour que sa longueur soit congruente à 448 mod 512 bits (en ajoutant un 1 suivi de 0 autant que nécessaire). Les 64 derniers bits représentent la longueur originale du message en bits.
- Peux-tu illustrer les fonctions logiques présentes dans SHA-256 avec un exemple (boucle de compression) ?
- Je reviens sur le padding, j’aimerai ajouter un script python qui convertit le message original « abcdefghijklmnop » en son bloc final en hexadécimal.
- Peux-tu me proposer un script python pour le découpage en blocs de 512 bits d’un texte ?
La fonction de hachage SHA-256 (Secure Hash Algorithm 256-bit) est une fonction cryptographique qui transforme une entrée de taille arbitraire en une sortie fixe de 256 bits (32 octets), souvent représentée sous forme hexadécimale (64 caractères).
1 octet (composé de 8 bits) peut coder $2^8$ valeurs différentes, permettant ainsi de représenter 256 caractères, nombres ou combinaisons distincts. Pour coder un octet en forme hexadécimale il faut deux caractères puisque $16^2 = 256$ d’où les 64 caractères mentionnés ci-dessus.
Dans cet article, il est important de tester les scripts Python, par exemple dans
Basthon ou dans tout autre environnement Python.
Une fonction cryptographique est un algorithme mathématique conçu pour sécuriser les informations numériques. Elle transforme des données d’entrée (message, fichier, mot de passe) en une sortie protégée, en s’appuyant sur des principes :
- de confidentialité,
- d’intégrité et
- d’authentification.
Les principales propriétés d’une fonction cryptographique
- Sens unique (non-réversibilité) :
- Facile à calculer dans un sens, mais extrêmement difficile (voire impossible) à inverser.
- Exemple : Avec SHA-256, vous pouvez obtenir le hash d’un message, mais vous ne pouvez pas retrouver le message original à partir du hash. - Déterministe :
- Une même entrée produit toujours la même sortie.
- Exemple : SHA-256(« Bonjour ») donnera toujours le même résultat. - Résistance aux collisions :
- Impossible (en pratique) de trouver deux entrées différentes produisant le même résultat.
- Exemple : Trouver deux fichiers distincts avec le même hash SHA-256. - Effet avalanche :
- Une toute petite modification de l’entrée change complètement la sortie.
- Exemple : SHA-256(« bonjour ») et SHA-256(« bonjour ! ») produisent des hash complètement différents .
import hashlib
message = b"bonjour"
sha256 = hashlib.sha256(message)
print("Hash final :", sha256.hexdigest())
>>> Hash final : 2cb4b1431b84ec15d35ed83bb927e27e8967d75f4bcd9cc4b25c8d879ae23e18
message = b"bonjour!"
sha256 = hashlib.sha256(message)
print("Hash final :", sha256.hexdigest())
>>> Hash final : 594823a11143610743eba182ed58e895c97de90cf85eb378c5c80f1787f68083
Un lecteur attentif a soulevé un point technique crucial : la norme SHA-256 spécifie que tous les entiers 32 bits utilisés en entrée (comme les mots du message) et en sortie (les valeurs de hachage intermédiaires et finales) doivent être représentés en big-endian.
Qu’est-ce que le big-endian ?
C’est une convention d’ordre des octets (ou endianness) pour représenter des nombres plus grands qu’un octet. En big-endian, l’octet de poids fort (le plus significatif) est stocké en premier (à l’adresse la plus basse). C’est l’ordre dans lequel nous écrivons naturellement les nombres en base 16 (hexadécimal) : de gauche (poids fort) à droite (poids faible).
Où cela intervient-il dans l’article ?
Lors du padding (ajout de la longueur) : Dans l’exemple avec le message « abc », la longueur originale de 24 bits est bien encodée sur 64 bits en big-endian comme 0x00000000 00000018.
Dans le traitement des blocs : Lorsque le message de 512 bits est découpé en 16 mots de 32 bits, chaque mot est interprété comme un nombre de 32 bits en big-endian. Par exemple, les 4 premiers octets du message « abc » après padding sont 0x61, 0x62, 0x63, 0x80. Ces 4 octets forment le mot W[0] qui doit être interprété comme le nombre 0x61626380 (big-endian), et non 0x80636261 (little-endian). Toutes les opérations arithmétiques et logiques (additions, rotations, décalages) sont ensuite effectuées sur ces mots interprétés en big-endian.
Pour le résultat final : Le hachage final de 256 bits est obtenu en concaténant les 8 registres internes (A, B, C, D, E, F, G, H) de 32 bits, chacun d’eux étant également en représentation big-endian. Cela signifie que pour obtenir la chaîne hexadécimale finale correcte (comme ba7816bf... pour « abc »), il faut bien lire la valeur de chaque registre en big-endian.
Pourquoi cette distinction est-elle importante ?
Si vous deviez implémenter SHA-256 vous-même (plutôt que d’utiliser une bibliothèque comme hashlib), l’utilisation d’une convention d’ordre des octets différente (comme le little-endian, utilisé par certains processeurs) produirait un résultat complètement différent et invalide. Les bibliothèques standards, comme hashlib en Python, gèrent automatiquement cette conversion pour respecter la norme, d’où l’obtention du résultat correct sans que nous ayons à nous en soucier dans nos scripts.
Fonctionnement mathématique de SHA-256
SHA-256 est basée sur :
- des opérations bit à bit,
- des additions modulaires
- et des fonctions logiques.
Voici les principales étapes
=== 1. Préparation du message (Padding)
– Le message est complété pour que sa longueur soit congruente à 448 mod 512 bits (en ajoutant un 1 suivi de 0 autant que nécessaire).
– Les 64 derniers bits représentent la longueur originale du message en bits.
Prenons un message « abc » (3 caractères = 24 bits) et appliquons le padding SHA-256.
==== Message original (24 bits)
Texte : « abc »
Binaire : 01100001 (a) 01100010 (b) 01100011 (c) → Longueur : 24 bits.
==== Étape 1 : Ajout du bit « 1 »
On ajoute un 1 à la fin du message, puis des 0 jusqu’à ce que la longueur soit congruente à 448 mod 512.
Message après ajout du « 1 » : 01100001 01100010 01100011 1 → Longueur : 25 bits.
==== Étape 2 : Ajout des « 0 » jusqu’à 448 mod 512
– 448 [512] = 448 bits.
– Il faut 448 - 25 = 423 bits de « 0 » pour atteindre 448 bits.
– Message après padding : 01100001 01100010 01100011 10000000 00000000 00000000 ... 00000000
– Les ... représentent 416 bits de « 0 » supplémentaires (total : 1 + 423 = 424 bits ajoutés).
==== Étape 3 : Ajout de la longueur originale (64 bits)
– La longueur originale est 24 bits.
– On encode cette valeur sur 64 bits (en big-endian) : 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00011000
– Message final (512 bits) : 01100001 01100010 01100011 10000000 00000000 ... 00000000 00011000
– Bloc complet : 512 bits (64 octets).
=== Représentation Hexadécimale
Voici le bloc final en hexadécimal (plus lisible) : 61626380 00000000 00000000 00000000 00000000 00000000 00000000 00000018
– « 616263 » = « abc » en ASCII. (en effet ord(« a ») = 97 en décimal soit ord(« a ») = 61 en hexadécimal).
– « 80 » = Le « 1 » suivi de sept « 0 ». (0b10000000 = 0x80)
– « 00 » × 14 = Les 423 bits de padding (simplifié ici).
– « 0x00000018 = 16 + 8 = 24 » (64 bits de longueur).
=== Vérification Python
import hashlib
message = b"abc"
sha256 = hashlib.sha256(message)
print("Hash final :", sha256.hexdigest()) # Résultat connu : ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
=== Vérification avec la commande sha256sum
Un lecteur avisé suggère de signaler un outil très pratique, directement disponible sur les systèmes Linux et macOS (et utilisable sous Windows via WSL ou des utilitaires tiers) : la commande sha256sum.
Cette commande permet de calculer et de vérifier l’empreinte SHA-256 d’un fichier, exactement comme le fait la fonction hashlib en Python.
Principe :
- Vous avez un fichier, par exemple document.txt.
- La commande
sha256sum document.txt lit tout le contenu du fichier, applique l’algorithme SHA-256 (avec tout le processus de padding et de compression décrit dans cet article) et affiche le hash final suivi du nom du fichier.
Exemple concret à utiliser en lignes de commande dans un terminal :
Imaginons que vous créiez un petit fichier texte contenant le message « abc ».
# Créer un fichier avec le contenu "abc"
echo -n "abc" > mon_fichier.txt
# Calculer son empreinte SHA-256
sha256sum mon_fichier.txt
La sortie affichée sera :
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad mon_fichier.txt
Nous retrouvons exactement le même hash que celui calculé avec Python dans l’exemple précédent !
À quoi cela sert-il dans la vraie vie ?
- Cette commande est couramment utilisée pour :
Vérifier l’intégrité d’un fichier téléchargé. De nombreux sites de distribution de logiciels fournissent le hash SHA-256 attendu. Après le téléchargement, vous calculez le hash du fichier reçu avec sha256sum et vous comparez : s’ils sont identiques, le fichier n’a pas été corrompu pendant le transfert. - Détecter des doublons : deux fichiers identiques auront strictement le même hash.
- Pour aller plus loin :
La commande peut vérifier des listes de fichiers avec l’option -c.
Sous Windows, vous pouvez obtenir une fonctionnalité équivalente avec la commande Get-FileHash dans PowerShell.
=== Pourquoi 448 mod 512 ?
– SHA-256 travaille par blocs de 512 bits.
– Les 64 derniers bits sont réservés pour la longueur du message.
– Le padding occupe donc 512 - 64 = 448 bits maximum.
– Si le message dépasse 448 bits, un nouveau bloc de 512 bits est ajouté.
== Cas avec un Message Plus Long
Exemple pour « abcdefghijklmnop » (16 caractères = 128 bits) :
– Ajout de « 1 » → 129 bits.
– Ajout de 319 bits de « 0 » (448 - 129 = 319).
– Ajout de la longueur (128 → 0x0000000000000080 en hexadécimal, $8\times16 = 128$).
=== Bloc final :
61626364 65666768 696A6B6C 6D6E6F70 80000000 00000000 ... 00000080
== Résumé des Étapes
– Ajouter « 1 » au message.
– Ajouter des « 0 » jusqu’à ce que longueur ≡ 448 mod 512.
– Ajouter la longueur originale (64 bits).
Ce processus garantit que le message est toujours un multiple de 512 bits, prêt pour le hachage.
Voici un script Python complet qui convertit le message « abcdefghijklmnop » en son bloc final hexadécimal après application du padding SHA-256
def string_to_binary(message):
"""Convertit une chaîne en binaire (8 bits par caractère)"""
return ''.join(format(ord(c), '08b') for c in message)
def apply_padding(message):
"""Applique le padding SHA-256 et retourne le bloc final en hexadécimal"""
# 1. Conversion du message en binaire
binary_message = string_to_binary(message)
message_length = len(binary_message)
print(f"Message original : '{message}'")
print(f"Longueur en bits : {message_length}")
print(f"Binaire : {binary_message}")
print()
# 2. Ajout du bit '1'
padded = binary_message + '1'
# 3. Ajout des '0' jusqu'à 448 mod 512
k = 0
while (len(padded) + 64) % 512 != 0:
padded += '0'
k += 1
print(f"Après ajout du '1' et de {k} zéros :")
print(f"Longueur : {len(padded)} bits")
print(f"Binaire : {padded}")
print()
# 4. Ajout de la longueur originale sur 64 bits
length_bits = format(message_length, '064b')
padded += length_bits
print(f"Ajout de la longueur ({message_length} bits) : {length_bits}")
print(f"Longueur finale : {len(padded)} bits (doit être 512)")
print(f"Bloc final en binaire : {padded}")
print()
# 5. Conversion en hexadécimal (par blocs de 8 bits)
hex_result = ''
for i in range(0, len(padded), 8):
byte = padded[i:i+8]
hex_result += format(int(byte, 2), '02x')
return hex_result.upper()
# Test avec le message "abcdefghijklmnop"
message = "abcdefghijklmnop"
bloc_hex = apply_padding(message)
print("Bloc final en hexadécimal :")
# Formatage par groupes de 8 caractères hexadécimaux (4 octets) pour lisibilité
for i in range(0, len(bloc_hex), 8):
print(bloc_hex[i:i+8], end=' ')
print("\n")
# Vérification avec hashlib (pour le hash complet, pas le bloc)
import hashlib
hash_result = hashlib.sha256(message.encode()).hexdigest()
print(f"Hash SHA-256 complet : {hash_result}")
>>> # script executed
Message original : 'abcdefghijklmnop'
Longueur en bits : 128
Binaire : 011000010110001001100011011001000110010101100
1100110011101101000011010010110101001101011011011000110
1101011011100110111101110000
-
Après ajout du '1' et de 319 zéros :
Longueur : 448 bits
Binaire : 011000010110001001100011011001000110010101100
1100110011101101000011010010110101001101011011011000110
1101011011100110111101110000100000000000000000000000000
0000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000
000000000000000000
-
Ajout de la longueur (128 bits) : 000000000000000000000
0000000000000000000000000000000000010000000
Longueur finale : 512 bits (doit être 512)
Bloc final en binaire : 0110000101100010011000110110010
0011001010110011001100111011010000110100101101010011010
1101101100011011010110111001101111011100001000000000000
0000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000010000000
-
Bloc final en hexadécimal :
61626364 65666768 696A6B6C 6D6E6F70 80000000 00000000 0
0000000 00000000 00000000 00000000 00000000 00000000 00
000000 00000000 00000000 00000080
-
Hash SHA-256 complet : f39dac6cbaba535e2c207cd0cd8f1549
74223c848f727f98b3564cea569b41cf
Et une version plus condensée :
def message_to_sha256_block(message):
# Padding SHA-256
binary = ''.join(format(ord(c), '08b') for c in message)
length = len(binary)
binary += '1'
while (len(binary) + 64) % 512 != 0:
binary += '0'
binary += format(length, '064b')
# Conversion en hexadécimal
return ''.join(format(int(binary[i:i+8], 2), '02x') for i in range(0, 512, 8))
# Test
message = "abcdefghijklmnop"
bloc = message_to_sha256_block(message)
print(bloc)
>>> # script executed
6162636465666768696a6b6c6d6e6f70800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
=== 2. Découpage en blocs de 512 bits
– Le message est divisé en blocs de 512 bits (16 mots de 32 bits chacun).
import textwrap
def string_to_binary(message):
"""Convertit une chaîne en binaire (8 bits par caractère UTF-8)"""
# Utilisation de UTF-8 pour gérer les accents
bytes_data = message.encode('utf-8')
return ''.join(format(byte, '08b') for byte in bytes_data)
def create_blocks(message):
"""Découpe un message en blocs de 512 bits avec padding SHA-256"""
# 1. Conversion du message en binaire
binary_message = string_to_binary(message)
message_length = len(binary_message)
print("=" * 80)
print(f"TEXTE ORIGINAL : \"{message}\"")
print(f"Longueur en bits : {message_length} bits ({message_length // 8} octets)")
print("=" * 80)
print()
# 2. Préparation du padding
padded = binary_message + '1'
# Ajout des zéros jusqu'à ce que (longueur + 64) soit multiple de 512
while (len(padded) + 64) % 512 != 0:
padded += '0'
# Ajout de la longueur originale sur 64 bits
padded += format(message_length, '064b')
# 3. Découpage en blocs de 512 bits
blocks = []
for i in range(0, len(padded), 512):
block = padded[i:i+512]
blocks.append(block)
return blocks, padded, message_length
def block_to_hex(block_binary):
"""Convertit un bloc binaire en hexadécimal formaté"""
hex_result = ''
for i in range(0, len(block_binary), 8):
byte = block_binary[i:i+8]
hex_result += format(int(byte, 2), '02x')
return hex_result
def display_block_info(block_num, block_binary, message_length):
"""Affiche les informations détaillées d'un bloc"""
print(f"\n{'─' * 80}")
print(f"BLOG #{block_num + 1} (512 bits = 64 octet
-*
-* s)")
print(f"{'─' * 80}")
# Conversion en hexadécimal
hex_block = block_to_hex(block_binary)
# Affichage par lignes de 16 octets (32 caractères hex)
print("\nReprésentation hexadécimale :")
for i in range(0, len(hex_block), 32):
line = hex_block[i:i+32]
# Ajout d'un espace tous les 8 caractères pour lisibilité
formatted = ' '.join(line[j:j+8] for j in range(0, 32, 8))
print(f" {formatted}")
# Analyse du contenu du bloc
print("\nAnalyse du bloc :")
# Identifier les parties du bloc
if block_num == 0:
# Premier bloc : peut contenir le début du message
msg_end = min(message_length, 512 - 64) # 64 bits réservés pour la longueur
print(f" • Bits 0-{message_length-1} : Message original ({message_length} bits)")
if message_length > 512 - 64:
print(f" • Message continue dans le bloc suivant")
# Vérifier la présence du '1' de padding
block_str = str(block_binary)
if '1' in block_str[message_length:]:
pos = message_length + block_str[message_length:].find('1')
print(f" • Bit de padding '1' à la position {pos}")
# Vérifier la longueur à la fin
last_64 = block_binary[-64:]
length_value = int(last_64, 2)
if length_value == message_length:
print(f" • Longueur originale : {length_value} bits (dans les 64 derniers bits)")
print()
# Texte de "Mistral gagnant"
mistral_gagnant = """
À m'asseoir sur un banc cinq minutes avec toi
Et regarder les gens tant qu'y en a
Te parler du bon temps qu'est mort ou qui r'viendra
En serrant dans ma main tes p'tits doigts
Pi donner à bouffer à des pigeons idiots
Leur filer des coups d'pied pour de faux
Et entendre ton rire qui lézarde les murs
Qui sait surtout guérir mes blessures
Te raconter un peu comment j'étais, minot
Les bonbecs fabuleux qu'on piquait chez l'marchand
Car-en-sac et Mintho caramels à un franc
Et les Mistral gagnants
À marcher sous la pluie cinq minutes avec toi
Et regarder la vie tant qu'y en a
Te raconter la terre en te bouffant des yeux
Te parler de ta mère un p'tit peu
Et sauter dans les flaques pour la faire râler
Bousiller nos godasses et s'marrer
Et entendre ton rire comme on entend la mer
S'arrêter, repartir en arrière
Te raconter surtout les carambars d'antan et les coco-boers
Et les vrais roudoudous qui nous coupaient les lèvres et nous niquaient les dents
Et les Mistral gagnants
À m'asseoir sur un banc cinq minutes avec toi
Regarder le soleil qui s'en va
Te parler du bon temps qu'est mort et je m'en fous
Te dire que les méchants c'est pas nous
Que si moi je suis barge ce n'est que de tes yeux
Car ils ont l'avantage d'être deux
Et entendre ton rire s'envoler aussi haut
Que s'envolent les cris des oiseaux
Te raconter enfin qu'il faut aimer la vie et l'aimer même si
Le temps est assassin et emporte avec lui
Les rires des enfants et les Mistral gagnants
Et les Mistral gagnants
"""
print("\n" + "★" * 80)
print("DÉCOUPAGE EN BLOCS SHA-256 DU TEXTE 'MISTRAL GAGNANT'")
print("★" * 80)
# Création des blocs
blocks, padded_binary, original_length = create_blocks(mistral_gagnant)
# Affichage des informations
print(f"\nNombre de blocs nécessaires : {len(blocks)}")
print(f"Longueur totale avec padding : {len(padded_binary)} bits")
# Affichage détaillé de chaque bloc
for i, block in enumerate(blocks):
display_block_info(i, block, original_length)
# Vérification avec hashlib
import hashlib
hash_sha256 = hashlib.sha256(mistral_gagnant.encode('utf-8')).hexdigest()
print("★" * 80)
print(f"HASH SHA-256 FINAL : {hash_sha256}")
print("★" * 80)
# Fonction utilitaire supplémentaire : sauvegarde dans un fichier
def save_blocks_to_file(blocks, filename="mistral_blocks.txt"):
with open(filename, 'w', encoding='utf-8') as f:
f.write(f"Texte original : {mistral_gagnant[:50]}...\n")
f.write(f"Longueur originale : {original_length} bits\n")
f.write(f"Nombre de blocs : {len(blocks)}\n\n")
for i, block in enumerate(blocks):
f.write(f"Bloc #{i+1} (512 bits) :\n")
hex_block = block_to_hex(block)
# Écrire par lignes de 64 caractères hex
for j in range(0, len(hex_block), 64):
f.write(hex_block[j:j+64] + "\n")
f.write("\n")
# Sauvegarde optionnelle
save_blocks_to_file(blocks)
print("\nLes blocs ont été sauvegardés dans 'mistral_blocks.txt'")
>>> # script executed
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
DÉCOUPAGE EN BLOCS SHA-256 DU TEXTE 'MISTRAL GAGNANT'
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
================================================================================
TEXTE ORIGINAL : "
À m'asseoir sur un banc cinq minutes avec toi
...
Et les Mistral gagnants
"
Longueur en bits : 12024 bits (1503 octets)
================================================================================
Nombre de blocs nécessaires : 24
Longueur totale avec padding : 12288 bits
────────────────────────────────────────────────────────────────────────────────
BLOC #1 (512 bits = 64 octets)
────────────────────────────────────────────────────────────────────────────────
Représentation hexadécimale :
0ac38020 6d276173 73656f69 72207375
7220756e 2062616e 63206369 6e71206d
696e7574 65732061 76656320 746f690a
45742072 65676172 64657220 6c657320
Analyse du bloc :
• Bits 0-12023 : Message original (12024 bits)
• Message continue dans le bloc suivant
...
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
HASH SHA-256 FINAL : cad1a71dd1974f3e88f4996252623fda7a27a6cae706c189f1ba033ef93b8aaa
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
Les blocs ont été sauvegardés dans 'mistral_blocks.txt'
Fonctionnalités du script :
- Découpage automatique : Calcule le nombre de blocs nécessaires
- Affichage formaté : Montre chaque bloc en hexadécimal par lignes de 16 octets
- Analyse du padding : Identifie où se trouvent le bit ’1’ et la longueur
- Vérification : Compare avec le hash SHA-256 final de hashlib
- Sauvegarde : Exporte les blocs dans un fichier texte
Remarques importantes :
- Le texte avec accents est encodé en UTF-8 (2 octets pour ’à’, ’é’, etc.)
- Le nombre de blocs dépend de la longueur du texte (environ 24 blocs pour cet extrait)
- Le padding ajoute systématiquement un bit ’1’ et la longueur sur 64 bits
=== 3. Initialisation des constantes ($H$ et $K$)
Valeurs initiales $(H_i)$ : 8 constantes de 32 bits, dérivées des racines carrées des 8 premiers nombres premiers.
– $H_0 = 0x6a09e667$
– $H_1 = 0xbb67ae85$
– $H_2 = 0x3c6ef372$
– $H_3 = 0xa54ff53a$
– $H_4 = 0x510e527f$
– $H_5 = 0x9b05688c$
– $H_6 = 0x1f83d9ab$
– $H_7 = 0x5be0cd19$
Les valeurs initiales ($H_0$ à $H_7$) de SHA-256 sont effectivement dérivées des racines carrées des 8 premiers nombres premiers (2, 3, 5, 7, 11, 13, 17, 19). Voici pourquoi et comment cela fonctionne :
=== Pourquoi utiliser les racines carrées des nombres premiers ?
L’objectif est d’obtenir des constantes « aléatoires » en apparence, mais reproductibles :
– Neutralité : Les nombres premiers sont indépendants de tout contexte cryptographique, évitant des soupçons de backdoor.
– Uniformité : Les bits des parties fractionnaires de leurs racines carrées sont statistiquement bien distribués (pseudo-aléatoires).
– Reproductibilité : N’importe qui peut recalculer ces valeurs, ce qui renforce la confiance dans l’algorithme.
C’est une méthode classique en cryptographie (utilisée aussi dans MD5, SHA-1, etc.).
=== Méthode de calcul exacte
Pour chaque nombre premier p parmi les 8 premiers :
– Calculez $\sqrt{p}$ en virgule fixe (sur 64 bits de précision).
– Prenez la partie fractionnaire (ce qui suit la virgule).
– Convertissez-la en hexadécimal sur 32 bits (en ignorant les bits au-delà).
Exemple pour $H_0$ (premier nombre premier = 2) :
– $\sqrt{2} \approx 1.41421356237309504880$
– Partie fractionnaire : 0.41421356237309504880
– Convertir en hexadécimal : $0.41421356237309504880×2^{32} \approx 1779033703$. Soit 0x6a09e667 (valeur de $H_0$).
from math import sqrt
# Calcul de H_0
a=sqrt(2)
hex(int((a-int(a))*2**32))
>>>'0x6a09e667'
from math import sqrt
# Calcul de H_1
a=sqrt(3)
hex(int((a-int(a))*2**32))
>>>'0xbb67ae85'
=== Tableau complet des 8 constantes initiales
Voici comment chaque $H_i$ est dérivé :
| Nombre premier $p$ |
$\sqrt{p}$ (partie fractionnaire) |
Valeur hexadécimale ($H_0$ à $H_7$) |
| 2 |
0.41421356237309504880 |
0x6a09e667 |
| 3 |
0.73205080756887729352 |
0xbb67ae85 |
| 5 |
0.23606797749978969640 |
0x3c6ef372 |
| 7 |
0.64575131106459059050 |
0xa54ff53a |
| 11 |
0.31662479035539984911 |
0x510e527f |
| 13 |
0.60555127546398929311 |
0x9b05688c |
| 17 |
0.12310562561766054982 |
0x1f83d9ab |
| 19 |
0.35889894354067355228 |
0x5be0cd19 |
=== Pourquoi pas d’autres nombres ?
- Irrationalité garantie : Les racines carrées des nombres premiers sont irrationnelles, donc leur partie fractionnaire ne se répète pas.
- Éviter les biais : Des nombres arbitraires pourraient introduire des motifs cachés. Les nombres premiers sont « impartiaux ».
=== Conclusion
Cette méthode offre un compromis idéal entre :
- Transparence : N’importe qui peut vérifier les calculs.
- Neutralité : Pas de suspicion de manipulation.
- Propriétés cryptographiques : Bonne distribution des bits.
– Constantes $(K_t)$ : 64 valeurs de 32 bits, dérivées des racines cubiques des 64 premiers nombres premiers.
Les 64 constantes $K_t$ de SHA-256 sont dérivées de la même manière, mais avec les racines cubiques des 64 premiers nombres premiers (pour étendre la « randomisation »).
Les constantes $K_t$ (où $t = 0$ à 63) sont des valeurs clés dans la fonction de compression de SHA-256. Elles sont dérivées des racines cubiques des 64 premiers nombres premiers (2, 3, 5, ..., 311). Voici comment elles sont calculées, avec un exemple détaillé :
=== Pourquoi les racines cubiques des nombres premiers ?
– Objectif : Obtenir des constantes « pseudo-aléatoires » non liées à la structure de SHA-256, pour éviter toute vulnérabilité cachée.
– Méthode standard : Comme pour le calcul de $H_0 ... H_7$, on utilise les parties fractionnaires de nombres irrationnels (ici, les racines cubiques), car leurs bits sont bien distribués statistiquement.
=== Méthode de calcul pas à pas
Pour chaque nombre premier $p$ parmi les 64 premiers :
- Calculer $\sqrt[3]{p}$ (racine cubique).
- Extraire la partie fractionnaire (ce qui suit la virgule).
- Multiplier par $2^{32}$, puis prendre la partie entière pour obtenir un nombre sur 32 bits.
- Convertir en hexadécimal pour obtenir $K_t$.
==== Exemple avec le 1er nombre premier ($p = 2, t = 0$) :
- $\sqrt[3]{2} \approx 1.259921049894873232$
- Partie fractionnaire : $0.2599210498948732$
- $0.2599210498948732×2^{32} ≈ 1 116 352 240$
- En hexadécimal : $"0x428a2f98"$ (c’est $K_0$).
# Calcul de K_0
a=2**(1/3)
hex(int((a-int(a))*2**32))
>>>'0x428a2f98'
=== Tableau des 16 premières constantes $K_t$
Voici les valeurs pour $t = 0$ à $15$ (les autres suivent la même logique) :
| t |
Nombre premier p |
$\sqrt[3]{p}$ (partie fractionnaire) |
$K_t$ (hexadécimal) |
| 0 |
2 |
0.2599210498948732 |
0x428a2f98 |
| 1 |
3 |
0.4422495703074083 |
0x71374491 |
| 2 |
5 |
0.7099759466766969 |
0xb5c0fbcf |
| 3 |
7 |
0.9129311827723892 |
0xe9b5dba5 |
| 4 |
11 |
0.2239800905693155 |
0x3956c25b |
| 5 |
13 |
0.3472963553338607 |
0x59f111f1 |
| 6 |
17 |
0.4624967406300675 |
0x923f82a4 |
| 7 |
19 |
0.5198420997897464 |
0xab1c5ed5 |
| ... |
... |
... |
... |
| 63 |
311 |
0.6774234900977887 |
0xc67178f2 |
(La liste complète est dans la RFC 6234. Version pdf.)
=== Rôle dans SHA-256
- Fonction de compression : Les $K_t$ sont ajoutées à chaque itération de la boucle principale (64 tours) pour brouiller les calculs.
$T_1 = h + Σ_1(e) + Ch(e,f,g) + K_t + W_t$ - Effet cryptographique : Elles introduisent une non-linéarité et empêchent les attaques par motifs répétitifs.
=== Pourquoi 64 constantes ?
- Correspond aux 64 itérations de la boucle de compression.
- Chaque$K_t$ est unique, ce qui renforce la résistance aux collisions.
=== Conclusion
Les $K_t$ sont des « empreintes » des nombres premiers, mélangées au calcul pour assurer :
- Imprévisibilité : Grâce aux propriétés des irrationnels.
- Transparence : Personne ne peut les choisir malicieusement.
=== Vérification en Python
Vous pouvez recalculer $K_0$ avec ce code :
# Calcul de K_0
a=2**(1/3)
hex(int((a-int(a))*2**32))
=== 4. Compression (Fonction principale)
Pour chaque bloc de 512 bits :
==== 1. Préparation des 64 mots $W_t$ :
- Les 16 premiers mots viennent du bloc.
- Les 48 suivants sont calculés avec : $W_t = σ_1(W_{t−2}) + W_{t − 7} + σ_0(W_{t−15}) + W_{t − 16}$ où $σ_0$ et $σ_1$ sont des fonctions de rotation et décalage.
Dans SHA-256, les fonctions $σ_0$ et $σ_1$ sont des opérations de manipulation de bits appliquées aux mots de 32 bits pendant la préparation des blocs. Elles jouent un rôle clé pour introduire de la non-linéarité et diffuser les bits du message.
Voici une explication détaillée :
=== Définitions mathématiques
Pour un mot de 32 bits x, les fonctions sont définies comme suit :
- $σ_0(x) = (x⋙7) ⊕ (x⋙18) ⊕ (x≫3)$
- $σ_1(x) = (x⋙17) ⊕ (x⋙19) ⊕ (x≫10)$
Explications sur les notations ci-dessus :
- ⋙ : Rotation à droite (
ROTR, les bits sortants réapparaissent à gauche). - ≫ : Décalage à droite (
SHR, les bits sortants sont remplacés par des 0). - ⊕ :
OU exclusif (XOR).
=== Détail des opérations
==== Exemple pour $σ_0(x)$
Prenons $x = 0x12345678)$ (32 bits). Voici sa représentation binaire : $x = 00010010 00110100 01010110 01111000$, en effet, on part de l’écriture décimale
$x = 1×16^7 + 2×16^6 + 3×16^5 + 4×16^4 + 5×16^3 + 6×16^2 + 7×16^1 + 8×16^0$
$x = 1×2^{28} + 2×2^{24} + 3×2^{20} + 4×2^{16} + 5×2^{12} + 6×2^8 + 7×2^4 + 8×2^0$
$x = 0001 0010 0011 0100 0101 0110 0111 1000)$
- Étape 1 : $x ⋙ 7$
- Rotation à droite de 7 bits : les 7 bits de droite passent à gauche donc $x ⋙ 7 = 11110000 00100100 01101000 10101100$.
- On obtient donc : $0x12345678 → 0xF02468AC$.
- Étape 2 : $x ⋙ 18$
- Rotation à droite de 18 bits : les 18 bits de droite passent à gauche donc $x ⋙ 18 = 00010101 10011110 00000100 10001101$.
- On obtient donc : $0x12345678 → 0x159E048D$.
- Étape 3 : $x ≫ 3$
- Décalage à droite de 3 bits (les 3 bits de droite disparaissent, 0 à gauche) donc $x ≫ 3 = 00000010 01000110 10001010 11001111$.
- On obtient donc : $0x12345678 → 0x02468ACF$.
- Résultat final :
- XOR des trois résultats ci-dessus, soit :
$11110000001001000110100010101100⊕
00010101100111100000010010001101⊕
00000010010001101000101011001111$, ce qui donne $11100111111111001110011011101110$, c’est-à-dire $0xE7FCE6EE$.
=== Rôle dans SHA-256
==== Préparation des mots $W_t$ :
Pour $t ≥ 16, W_t$ est calculé à partir des $W_{t-2}, W_{t-7}, W_{t-15}$, et $W_{t-16}$ via : $W_t = σ_1(W_{t−2}) + W_{t − 7} + σ_0(W_{t−15}) + W_{t − 16}$ ;
- $σ_0$ et $σ_1$ étendent l’influence des bits sur tout le bloc.
==== Effet cryptographique :
- Diffusion : Un seul bit changé dans le message affecte de nombreux $W_t$.
- Résistance : Empêche les attaques par motifs répétitifs.
=== Différence entre rotation (⋙) et décalage (≫)
| Opération |
Symbole |
Exemple sur 0x12345678 (32 bits) |
| Rotation à droite |
⋙ |
0x12345678 → 0x81234567 (rotation de 4 bits) |
| Décalage à droite |
≫ |
0x12345678 → 0x01234567 (décalage de 4 bits, 0 ajoutés) |
=== Implémentation en Python
def sigma0(x):
return (rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3)) # ^ est le XOR de python
def sigma1(x):
return (rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10))
def rotr(x, n, bits=32):
return ((x >> n) | ((x << (bits - n)) & ( (1 << bits) - 1 ))) & 0xFFFFFFFF
# Test avec x = 0x12345678
x = 0x12345678
print("x >>> 7 :", hex(rotr(x, 7)))
print("x >>> 18 :", hex(rotr(x, 18)))
print("x >> 3 :", hex(x >> 3))
print("sigma0(x) :" , hex(sigma0(x))) # Affiche le résultat de σ₀(x)
>>> # script executed
x >>> 7 : 0xf02468ac
x >>> 18 : 0x159e048d
x >> 3 : 0x2468acf
sigma0(x) : 0xe7fce6ee
0xe7fce6ee
=== Pourquoi ces valeurs (7, 18, 3, etc.) ?
Les décalages et rotations ont été choisis pour :
- Maximiser la diffusion : Les valeurs évitent les symétries et garantissent que chaque bit influence plusieurs positions.
- Éviter les collisions : Empêcher des motifs simples dans les blocs.
=== Conclusion
Les fonctions $σ_0$ et $σ_1$ sont des briques cryptographiques essentielles pour :
- Mélanger les bits de manière irréversible.
- Assurer que SHA-256 résiste aux attaques analytiques.
=== 2. Boucle de compression (64 itérations) :
- Met à jour 8 variables $(a, b, c, d, e, f, g, h)$ initialisées avec $H_0$ à $H_7$.
- Utilise des fonctions logiques :
$Ch(e,f,g) = (e∧f)⊕(¬e∧g)$
$Maj(a,b,c) = (a∧b) ⊕ (a∧c) ⊕ (b∧c)$
$Σ_0(a) = (a⋙2) ⊕ (a⋙13) ⊕ (a⋙22)$
$Σ_1(e) = (e⋙6) ⊕ (e⋙11) ⊕ (e⋙25)$ - À chaque itération, on calcule :
$T_1 = h + Σ_1(e) + Ch(e,f,g) + K_t + W_t$
$T_2 = Σ_0(a) + Maj(a,b,c)$ - puis on met à jour les variables :
$h = g, g = f, f = e, e = d + T_1, d = c, c = b, b = a, a = T_1 + T_2$
3. Mise à jour des valeurs de hachage :
$H_i (nouveau) = H_i + variable_i (i = 0..7)$
=== 5. Résultat final
- Après tous les blocs, les 8 valeurs $H_0$ à $H_7$ sont concaténées pour former le hash SHA-256 de 256 bits.
Exemple de code (en Python)
import hashlib
message = "Bonjour".encode('utf-8')
hash_object = hashlib.sha256(message)
hex_dig = hash_object.hexdigest()
print(hex_dig) # Affiche le hash SHA-256 de "Bonjour"
Prenons des valeurs initiales simplifiées (sur 32 bits, mais nous utiliserons des petits nombres pour la lisibilité).
1. Valeurs initiales ($H_0$ à $H_7$)
Dans SHA-256 réel, ce sont les constantes que nous avons vues. Pour l’exemple, prenons :
$a=0x6a09e667$ (mais on simplifie en $a = 6$)
$b=0xbb67ae85→11$
$c=0x3c6ef372→3$
$d=0xa54ff53a→10$
$e=0x510e527f→5$
$f=0x9b05688c→9$
$g=0x1f83d9ab→1$
$h=0x5be0cd19→5$
*(Les vraies valeurs sont sur 32 bits, mais nous utilisons des entiers 0-15 pour simplifier.)*
2. Constantes pour $t=0$
$K_0 = 0x428a2f98$ (simplifié → 4)
$W_0$ est le premier mot du bloc de message (ici, on prend $W_0 = 2$).
3. Calcul des fonctions logiques
a) $Ch(e,f,g)$ avec $e=5,f=9,g=1$
$Ch(5,9,1)=(5∧9)⊕(¬5∧1)$
Binaire (sur 4 bits pour simplifier) :
$e=5 → 0101$
$f=9 → 1001$
$g=1 → 0001$
$¬e$ (complément sur 4 bits) : $1010$
$e∧f = 0101∧1001 = 0001 (1)$
$¬e∧g =1010∧0001 = 0000 (0)$
$Ch = 0001 ⊕ 0000 = 0001 (1)$
b) $Maj(a,b,c)$ avec $a=6,b=11,c=3$
$a=6 → 0110$
$b=11 → 1011$
$c=3 → 0011$
$a∧b=0110∧1011=0010(2)$
$a∧c=0110∧0011=0010(2)$
$b∧c=1011∧0011=0011(3)$
$Maj=0010⊕0010⊕0011=0011(3)$
c) $Σ_0 (a)$ (version simplifiée, sans rotations réelles)
Pour l’exemple, on approxime $Σ_0 (6) = 2$ (effet de diffusion).
d) $Σ_1 (e)$ (idem)
On prend $Σ_1 (5) = 3$.
4. Calcul de $T_1$ et $T_2$
$T_1 = h+Σ_1(e) + Ch(e,f,g) + K_0 + W_0 = 5 + 3 + 1 + 4 + 2 = 15$
$T_2 = Σ0(a)+Maj(a,b,c) = 2+3 = 5$
5. Mise à jour des variables
$h=g=1$
$g=f=9$
$f=e=5$
$e = d+T_1=10+15=25$ (on garde modulo $2^{32}$)
$d= c = 3$
$c= b = 11$
$b =a = 6$
$a=T_1 + T_2 = 15 + 5 = 20$
Nouvelles valeurs après la première itération :
$a=20, b=6, c=11, d=3, e=25, f=5, g=9, h=1$
Pourquoi ces fonctions sont importantes ?
- $Ch$ : Agit comme un sélecteur de bits (si un bit de e est 1, on prend f, sinon g).
- $Maj$ : Vote majoritaire (prend le bit le plus fréquent entre a,b,c).
- $Σ₀, Σ₁$ : Diffusent l’information via des rotations.
- Ajout de constantes : Évite les symétries.
def ch(e, f, g):
return (e & f) ^ ((~e) & g)
def maj(a, b, c):
return (a & b) ^ (a & c) ^ (b & c)
# Test avec nos valeurs (en 32 bits, mais on utilise des petits entiers)
e, f, g = 5, 9, 1
a, b, c = 6, 11, 3
print(f"Ch = {ch(e, f, g)}") # 1
print(f"Maj = {maj(a, b, c)}") # 3
>>> # script executed
Ch = 1
Maj = 3
Cet exemple utilise des valeurs simplifiées, mais l’algorithme réel manipule des entiers 32 bits avec des rotations précises. Les fonctions logiques sont essentielles pour :
- Confusion ($Ch, Maj$) : Relations non linéaires entre bits.
- Diffusion ($Σ₀, Σ₁$) : Propagation des changements.
Propriétés clés
- Déterministe : Une même entrée donne toujours le même hash.
- Résistance aux collisions : Il est difficile de trouver deux entrées distinctes avec le même hash.
- Effet avalanche : Une petite modification change complètement le hash.
SHA-256 est largement utilisé dans les blockchains (Bitcoin), les signatures numériques et la vérification d’intégrité.
import hashlib
message = "Bonjour".encode('utf-8')
hash_object = hashlib.sha256(message)
hex_dig = hash_object.hexdigest()
print(hex_dig) # Affiche le hash SHA-256 de "Bonjour"
Une application concrète : SHA-256 au cœur de la sécurité de nos calculatrices
Notre lecteur, poursuivant ses réflexions, nous propose un exemple d’application fascinant et très concret qui parle directement aux utilisateurs de calculatrices graphiques :
- l’utilisation de SHA-256 pour authentifier le système d’exploitation (OS) ou les applications.
Le principe : la signature numérique
Le but est de garantir qu’un OS ou une application téléchargée pour votre calculatrice provient bien du constructeur (authenticité) et n’a pas été modifié (intégrité). Pour cela, on utilise une combinaison de SHA-256 et de cryptographie à clé publique (comme RSA ou ECC).
Voici comment cela fonctionne, de la conception à l’utilisation :
- Chez le constructeur (ex : Texas Instruments, Numworks) :
- On prend le fichier de l’OS ou de l’application.
- On calcule son empreinte SHA-256 (une sorte de « carte d’identité » unique du fichier)./li>
- On chiffre cette empreinte avec la clé privée secrète du constructeur (avec un algorithme comme RSA). Le résultat est une signature numérique.
- Cette signature est jointe au fichier original. Par exemple, Texas Instruments intègre cette signature à la fin de ses fichiers .8ek.
- Sur votre calculatrice :
- Le bootloader (le tout premier programme qui démarre) contient la clé publique du constructeur, qui est mathématiquement liée à sa clé privée mais ne permet pas de signer de nouveaux fichiers.
- Lorsque vous transférez un nouvel OS :
- La calculatrice reçoit le fichier et sa signature.
- Elle calcule elle-même l’empreinte SHA-256 du fichier reçu.
- Parallèlement, elle déchiffre la signature avec la clé publique du constructeur pour obtenir l’empreinte originale.
- Enfin, elle compare les deux empreintes : celle qu’elle vient de calculer et celle issue du déchiffrement.
- Si elles sont identiques, la calculatrice a la preuve que le fichier est bien celui signé par le constructeur et qu’il n’a pas été altéré. L’installation ou le lancement est autorisé.
- Si elles diffèrent, le fichier est suspect (peut-être un logiciel malveillant ou corrompu). L’installation est bloquée.
Pourquoi SHA-256 est-il essentiel ici ?
- Rôle de l’empreinte : SHA-256 réduit le fichier, potentiellement volumineux, en une empreinte de seulement 256 bits. C’est cette empreinte que l’on va signer (chiffrer), car signer directement le fichier serait inefficace en termes de calcul et de taille de signature.
- Propriété de non-réversibilité : Un attaquant qui intercepte le fichier et sa signature ne peut pas retrouver la clé privée du constructeur à partir de ces seules informations.
- Résistance aux collisions : L’attaquant ne peut pas non plus fabriquer un OS malveillant qui produirait exactement la même empreinte SHA-256 que l’OS officiel, car c’est une tâche computationnellement impossible avec SHA-256.
Ainsi, la prochaine fois que vous mettrez à jour votre calculatrice, souvenez-vous que derrière cette opération simple se cache une chaîne de sécurité sophistiquée où SHA-256 joue un rôle de premier ordre pour protéger votre appareil contre les logiciels non authentiques.
SHA-256 est un algorithme de hachage cryptographique utilisé dans divers contextes, notamment pour assurer l’intégrité des données. Dans le cadre des calculatrices Numworks, SHA-256 intervient principalement dans la sécurité des applications externes, comme KhiCAS.
- Authentification des applications : Le lanceur de KhiCAS utilise une signature basée sur SHA-256 pour vérifier l’intégrité du fichier exécutable chargé depuis le slot B. Cette signature est cryptée avec une clé RSA 2048, et le lanceur calcule l’empreinte SHA-256 du fichier pour s’assurer qu’il n’a pas été altéré.
- Compatibilité et sécurité : Les modèles N0110, N0115 et N0120 peuvent être verrouillés si une mise à jour est effectuée via le site NumWorks. Dans ce cas, l’installation d’applications externes comme KhiCAS devient problématique. Une méthode de déverrouillage temporaire existe, mais elle nécessite des manipulations avancées et un accès au bootloader (en appuyant sur RESET + 6).
- Limitations techniques : NumWorks n’a pas encore rendu KhiCAS compatible avec le mode examen sur les modèles N0115/N0120 et les N0110 verrouillés, en raison de contraintes techniques et d’un manque d’intérêt économique de la part de NumWorks. Cette incompatibilité pourrait être levée avec une intervention de NumWorks pour intégrer le lanceur de KhiCAS directement dans le firmware.
Pour installer KhiCAS sur une NumWorks N120, suivez les étapes disponibles sur ce site, en utilisant le mode rescue si nécessaire.
Tableau de déchiffrement SHA256 :
Combien de temps faut-il pour craquer des mots de passe hachés avec SHA256 ?
SHA256 est un élément crucial des systèmes cryptographiques modernes, offrant un haut niveau de sécurité et d’intégrité pour les données numériques. Son adoption généralisée et sa conception robuste en font un choix privilégié pour de nombreuses applications de sécurité. Mais comment SHA256 résiste-t-il à l’attaque par force brute ?
Comme le montre le tableau ci-dessous, SHA256 est extrêmement sécurisé contre les attaques par force brute lorsqu’il est utilisé en combinaison avec des mots de passe longs et complexes. En revanche, les mots de passe courts et peu complexes peuvent encore être craqués relativement rapidement, soulignant les risques importants liés à l’utilisation de mots de passe faibles (et pourtant très courants) tels que « password », « 123456 » et « admin ». Toutefois, une fois que des combinaisons de caractères sont utilisées dans des mots de passe de plus de huit caractères, le temps nécessaire pour les craquer devient rapidement une tâche quasi impossible pour les hackers utilisant des méthodes de force brute.
| Nombre de caractères |
Chiffres uniquement (10) |
Lettres minuscules uniquement (26) |
Minuscules et majuscules (52) |
Chiffres, minuscules et majuscules (62) |
Chiffres, minuscules, majuscules et symboles (95) |
| 6 |
Instantané |
Instantané |
Instantané |
Instantané |
Instantané |
| 7 |
Instantané |
Instantané |
Instantané |
Instantané |
14 minutes |
| 8 |
Instantané |
Instantané |
11 minutes |
41 minutes |
21 heures |
| 9 |
Instantané |
Instantané |
9 heures |
2 jours |
3 mois |
| 10 |
Instantané |
27 minutes |
19 jours |
3 mois |
22 années |
| 11 |
Instantané |
12 heures |
2 années |
19 années |
2052 années |
| 12 |
Instantané |
13 jours |
141 années |
1164 années |
195k années |
| 13 |
2 minutes |
9 mois |
7332 années |
73k années |
19m années |
| 14 |
19 minutes |
24 années |
381k années |
4474k années |
1760m années |
| 15 |
4 heures |
605 années |
19m années |
277m années |
167.2b années |
| 16 |
2 jours |
15732 années |
1031m années |
18b années |
16t années |
| 17 |
14 jours |
410k années |
54b années |
1067b années |
1509t années |
| 18 |
5 mois |
11m années |
2788b années |
67t années |
144q années |
| 19 |
4 années |
277m années |
145t années |
4099t années |
14Q années |
| 20 |
37 années |
7189m années |
8q années |
255q années |
1294Q années |
=== Configuration matérielle
Le tableau ci-dessus montre le temps nécessaire pour craquer des hachages par force brute à l’aide de matériel moderne. Ces temps varient très fortement selon la puissance matérielle.
L’important ici est d’appréhender les ordres de grandeur des différents résultats.
Hive Systems a publié son tableau annuel des mots de passe en 2025, révélant une accélération inquiétante de la vitesse de cassage des mots de passe, notamment grâce à l’essor des puces d’IA. Comparé à 2024, le temps nécessaire pour craquer un mot de passe avec des GPU grand public a diminué de près de 20 %, passant à 3 semaines pour un mot de passe de 8 lettres minuscules. Avec des matériels d’IA (comme ceux utilisés pour ChatGPT), les temps de crack ont augmenté de plus de 1,8 milliard de pour cent, réduisant des délais de billions d’années à quelques heures seulement.
Le tableau repose sur une configuration de 12 GPU RTX 5090 et utilise bcrypt avec 10 itérations (1024), un algorithme de hachage plus robuste que le MD5 utilisé auparavant. Malgré cette amélioration, les mots de passe courts ou simples restent extrêmement vulnérables. Pour être « dans le vert » selon le critère de Hive Systems, un mot de passe doit comporter au moins 13 caractères utilisant 4 types de caractères (majuscules, minuscules, chiffres, symboles), ce qui rend le cassage estimé à 11 milliards d’années.
Il est important de noter que les variations entre les éditions 2024 et 2025 ne reflètent pas une baisse du risque, mais des ajustements méLethodologiques et des choix techniques, comme l’optimisation des caractères acceptés ou les paramètres de hachage. Le tableau reste un outil pédagogique essentiel pour sensibiliser à l’urgence de renforcer la gestion des mots de passe face à la montée en puissance des systèmes de calcul.
Hive Systems.
import hashlib
def hasher_mot_de_passe(mot_de_passe):
# Crée un objet de hachage
hacheur = hashlib.sha256()
# Met le mot de passe dans l'objet de hachage
hacheur.update(mot_de_passe.encode('utf-8'))
# Retourne l'empreinte de hachage
return hacheur.hexdigest()
# Exemple d'utilisation
mot_de_passe = "MotDePasse123"
empreinte = hasher_mot_de_passe(mot_de_passe)
print("Empreinte de hachage du mot de passe:", empreinte)
>>> Empreinte de hachage du mot de passe: a88fbb3c8ba5bc960b5eaf0c390cd1e9395bc342d64aadd1354b7994e0ea0721
Sur le Web :
– US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)
– blockchain et SHA-256
– SHA-256 dans dcode
– Qu’est-ce que l’algorithme SHA-256 et comment fonctionne-t-il ?
– Calcul en ligne SHA256
– Calculer un hachage SHA 256
– SHA256 online tool