ChipWhisperer: Attaque d'énergie contre Magma

Posté par rakf



Dans le cadre du Summer of Hack 2019 à Digital Security, j'ai fait face à une attaque de puissance et j'ai travaillé avec ChipWhisperer.


Qu'est ce que c'est


L'analyse d'énergie est un type d'attaque via des canaux tiers. Autrement dit, les attaques qui utilisent des informations qui apparaissent à la suite de la mise en œuvre physique du système.


Quelles informations peuvent être utiles à un attaquant:


  • temps de conversion cryptographique;
  • la consommation d'énergie;
  • champs électromagnétiques;
  • bruit etc.

Une attaque énergétique est considérée comme la plus universelle.


Pourquoi ça marche?


Les microprocesseurs, microcontrôleurs, RAM et de nombreux autres circuits logiques sont basés sur la technologie CMOS. La consommation d'énergie des circuits CMOS se compose de deux composants: statique et dynamique. La consommation d'électricité statique est très faible, ce qui est l'une des raisons de la monopolisation de la technologie. La puissance dynamique est due à la commutation des transistors et dépend des données traitées et des opérations effectuées. Étant donné que la composante statique est principalement constante, la variation de la puissance totale est due exclusivement à la puissance dynamique et, par conséquent, la consommation totale d'énergie peut être utilisée pour l'analyse.


Boîte à outils


ChipWhisperer , j'ai utilisé la version en 2 parties:


Version en 2 parties ChipWhisperer


ChipWhisperer est une boîte à outils open source pour rechercher la sécurité des appareils embarqués. Il permet l'analyse d'énergie et les attaques basées sur les erreurs.


Les frais coûteront 250 $. Les développeurs la positionnent comme une solution révolutionnaire, car de tels complexes, selon les créateurs, coûtent 30 000 $. L'appareil se compose de 2 cartes: la cible et la carte de capture.


Il existe d'autres versions, avec leurs avantages (mais aussi à un coût élevé), vous pouvez également acheter séparément différentes cartes cibles, cartes d'extension, sonde pour une analyse complète de vos appareils et utiliser ChipWhisperer uniquement pour le retrait.


ChipWhisperer a un excellent wiki , de petits laboratoires de développement, et par la 5ème version, ils ont même fait une bonne documentation API. Les pistes sont supprimées de l'appareil connecté à l'aide de leur logiciel et enregistrées en tant que matrice NumPy.


Vous devez d'abord écrire le firmware du périphérique cible. Dans le cadre du laboratoire, les chiffres AES, DES, TEA sont considérés. Pour eux, il existe déjà un firmware et des paramètres prêts à l'emploi pour supprimer les traces (nombre d'échantillons à prélever, décalage, fréquence ADC, etc.). Pour des recherches indépendantes, les paramètres devront être sélectionnés expérimentalement.


Comme mentionné ci-dessus, vous pouvez provoquer une défaillance de la carte cible: provoquer un dysfonctionnement du signal d'horloge, ignorer les instructions et extraire des informations secrètes.


Dans les grands complexes, un oscilloscope est utilisé pour tracer les traces.


Analyse


Il existe plusieurs méthodes d'analyse de base:


  • analyse de puissance simple (SPA);
  • analyse de puissance différentielle (DPA);
  • analyse de corrélation de puissance (CPA).

Une simple analyse de puissance comprend une analyse visuelle du graphique de puissance. Par exemple, un mot de passe peut être obtenu un caractère à la fois en observant le profil de puissance lorsque le bon caractère est entré et en le comparant avec le mauvais.


Analyse de la puissance des mots de passe


Ou vous pouvez voir les tours de cryptage sur les pistes. Ce n'est pas assez de données pour obtenir une clé, mais on peut supposer quel algorithme est exécuté. La figure montre clairement 10 séries d'AES.


AES SPA


L'analyse différentielle (DPA) est beaucoup plus efficace que la simple analyse. La DPA est basée sur des méthodes d'analyse statistique pour détecter les différences de traces de puissance. Cependant, un outil très efficace pour «ouvrir» la clé nécessitera un grand nombre de routes. Je n'ai pas utilisé cette méthode pour l'analyse, mais au final je donnerai quelques liens vers des sources où elle est bien décrite.


La base de l'analyse de corrélation est un appareil statistique pour déterminer la clé de cryptage secrète. Parfois, il est isolé dans un type distinct, parfois appelé DPA. Cette méthode nécessite moins de traces que DPA, je l'ai utilisée pour l'analyse de puissance. Je vais vous en dire plus.


La tâche principale est de construire un modèle précis de consommation d'énergie du dispositif à l'étude. Si un modèle précis est construit, il existe une corrélation notable entre la valeur prévue et la valeur réelle.


L'un des modèles de puissance qui peut être utilisé est le modèle de poids Hamming. Le poids de Hamming est le nombre de valeurs non nulles en représentation binaire. L'hypothèse de ce modèle est que le nombre de bits définis dans le registre sera en corrélation avec l'énergie consommée par le dispositif. Le poids de Hamming lui-même est utilisé ci-après comme unité d'énergie conventionnelle. Le modèle de distance de Hamming est également utilisé - le nombre de bits différents en 2 valeurs.


Pour comparer le modèle de poids de Hamming et la consommation électrique réelle, un coefficient de Pearson linéaire est utilisé. Il montre la dépendance linéaire d'une quantité par rapport à une autre. Avec un modèle correctement construit, ce coefficient tendra à 1.


L'algorithme CPA généralisé comprend les étapes principales suivantes:


  • supprimer la consommation d'énergie lors de la conversion des messages sur une clé inconnue;
  • nous construisons un modèle de consommation d'énergie de la puce lors de la conversion des mêmes messages sur toutes les variantes possibles du sous-bloc clé (256 options pour chaque octet);
  • nous trouvons le coefficient de corrélation linéaire pour la consommation d'énergie simulée et réelle. Dans le cas d'une vraie clé, le coefficient tendra à 1;
  • l'algorithme est répété pour les sous-blocs clés restants.

En conséquence, la clé est restaurée séquentiellement, en petites parties. Si nous attaquons un octet de la clé à la fois, nous utilisons 2 8 tentatives pour chaque clé. Par exemple, si vous choisissez AES - 128, le nombre total de tentatives pour 16 octets de la clé sera de 2 12 , ce qui est bien mieux que d'appuyer sur 2 128 .


Analyse du chiffre magma


Magma est un code défini dans GOST R 34.12-2015, en fait le même GOST 28147-89, uniquement de profil. Le bloc chiffré passe par 32 tours dans lesquels certaines transformations se produisent. La clé se compose de 256 bits, chaque clé ronde fait partie de l'original.


Fonction Feistel GOST


Nous analyserons les données obtenues en utilisant la méthode CPA.


Tout d'abord, vous devez choisir une valeur intermédiaire de l'algorithme, qui dépend des données connues et d'une petite partie de la clé et peut être calculée par de simples transformations. Habituellement, cette valeur est la sortie de la S-box (Magma a maintenant 1 table de substitution, il est donc plus facile de mener de telles attaques) du premier (les textes ouverts sont connus) ou du dernier tour (les textes chiffrés sont connus).


J'ai recherché une clé avec des textes ouverts bien connus, donc cette option sera examinée plus avant. Dans l'algorithme Magma, contrairement à DES, AES, l'ajout d'un bloc 32 bits avec une clé ronde se produit modulo 2 32 , par conséquent, il est préférable de démarrer l'analyse à partir des dernières sorties S-box, car l'ajout des valeurs les plus élevées n'affecte pas les plus jeunes. Nous considérons la sortie de chaque S-box: 8 premiers, puis 8, 7 et ainsi de suite jusqu'au premier. La suppression des pistes a été effectuée sur un microcontrôleur 8 bits, et nous pouvons supposer que cela fonctionnait avec deux boîtiers S. Par conséquent, j'attaquerai immédiatement de 1 octet.


Calcul du modèle énergétique pour le dernier octet clé:


for kguess in range(0, 256): #Initialize arrays & variables to zero sumnum = np.zeros(numpoint) sumden1 = np.zeros(numpoint) sumden2 = np.zeros(numpoint) hyp = np.zeros(numtraces) for tnum in range(numtraces): hyp[tnum] = HW[leak("Gost28147_tc26_ParamZ", kguess, block2ns(text[tnum])[0], 0)] 

où la fonction de fuite renvoie simplement la sortie S-box du dernier octet.


Nous calculons le coefficient de Pearson linéaire pour les valeurs simulées et réelles selon la formule:


Corrélation


  cpaoutput = [0]*256 maxcpa = [0]*256 #Mean of hypothesis meanh = np.mean(hyp, dtype=np.float64) #Mean of all points in trace meant = np.mean(traces, axis=0, dtype=np.float64) #For each trace, do the following for tnum in range(0, numtraces): hdiff = (hyp[tnum] - meanh) tdiff = traces[tnum,:] - meant sumnum = sumnum + (hdiff*tdiff) sumden1 = sumden1 + hdiff*hdiff sumden2 = sumden2 + tdiff*tdiff cpaoutput[kguess] = sumnum / np.sqrt( sumden1 * sumden2 ) maxcpa[kguess] = max(abs(cpaoutput[kguess])) 

Lors du choix d'une vraie sous-clé, le coefficient de corrélation aura une augmentation significative:


Corrélation1


Ainsi, chaque octet de la clé ronde est analysé.


  for bnum in range(0, 4): cpaoutput = [0]*256 maxcpa = [0]*256 for kguess in range(0, 256): best_round_key = kguess << (bnum * 8) | best_round ... 

Étant donné la première clé ronde, nous pouvons calculer les 2 clés rondes et les suivantes de la même manière. Une clé Magma complète peut être obtenue en retirant 8 clés rondes.


Dans le processus de résolution de ce problème, plusieurs nuances apparaissent. Contrairement aux chiffres AES, DES et Grasshopper, l'ajout d'une clé ronde en texte brut se produit modulo 2 32 . L'ajout de bits faibles affecte le haut, contrairement à XORa. Lors du calcul de chaque sous-clé suivante, vous devez prendre en compte les résultats du passé. De même avec des touches rondes. Les données sont très sensibles. Si l'une des valeurs n'est pas calculée correctement, un calcul supplémentaire de la clé entière sera incorrect.


Il convient également de considérer qu'il est maintenant assez difficile de trouver une puce ayant une architecture à quatre bits: la plupart des puces ont une puce à huit bits. Bien sûr, il existe des puces cryptographiques spécialisées qui sont conçues pour un algorithme de conversion de sécurité spécifique (ou plusieurs algorithmes) et qui ont l'architecture la plus efficace.


Pour exécuter le chiffrement DES, un tel cryptoprocesseur peut avoir une architecture à six bits, pour Magma - une architecture à quatre bits, qui permet à chaque S-box d'être traitée séparément. Mon appareil cible est 8 bits, et dans le cas de "Magma", la conversion sera effectuée sur huit bits dans une approche, c'est-à-dire le remplacement aura lieu sur 2 S-box, la consommation électrique sera prise en compte pour 2 S-box. Si l'une des boîtes S, senior ou junior, correspond à la vraie clé et que l'autre ne correspond pas, des salves de corrélation élevées peuvent se produire.


Compte tenu de tout ce qui précède, à la sortie, nous avons le script suivant pour l'analyse des chemins de consommation d'énergie pour le chiffre Magma:


Script Python
  import numpy as np path = r'C:\Users\user\chipwhisperer\projects\gost_10000_2_data\traces\2019.08.11-19.53.25_' traces = np.load(path + 'traces.npy') text = np.load(path + 'textin.npy') keys = np.load(path + 'keylist.npy') HW = [bin(n).count("1") for n in range(0,256)] SBOXES = {"Gost28147_tc26_ParamZ": ( (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1), (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15), (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0), (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11), (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12), (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0), (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7), (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2), )} def _K(s, _in): """ S-box substitution :param s: S-box :param _in: 32-bit word :returns: substituted 32-bit word """ return ( (s[0][(_in >> 0) & 0x0F] << 0) + (s[1][(_in >> 4) & 0x0F] << 4) + (s[2][(_in >> 8) & 0x0F] << 8) + (s[3][(_in >> 12) & 0x0F] << 12) + (s[4][(_in >> 16) & 0x0F] << 16) + (s[5][(_in >> 20) & 0x0F] << 20) + (s[6][(_in >> 24) & 0x0F] << 24) + (s[7][(_in >> 28) & 0x0F] << 28) ) def block2ns(data): """ Convert block to N1 and N2 integers """ data = bytearray(data) return ( data[7] | data[6] << 8 | data[5] << 16 | data[4] << 24, data[3] | data[2] << 8 | data[1] << 16 | data[0] << 24, ) def addmod(x, y, mod=2 ** 32): """ Modulo adding of two integers """ r = x + int(y) return r if r < mod else r - mod def _shift11(x): """ 11-bit cyclic shift """ return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1)) def round(sbox, key, data, byte): s = SBOXES[sbox] _in = addmod(data, key) sbox_leak = _K(s, _in); return (sbox_leak >> (8 * byte)) & 0xFF def Feistel(sbox, key, data, nround): s = SBOXES[sbox] w = bytearray(key) x = [ w[3 + i * 4] | w[2 + i * 4] << 8 | w[1 + i * 4] << 16 | w[0 + i * 4] << 24 for i in range(8) ] n1, n2 = block2ns(data) for i in range(nround): n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1 return n1 numtraces = len(traces) numpoint = np.shape(traces)[1] bestguess = [0]*32 round_data = [0] * numtraces for i in range(numtraces): round_data[i] = [0] * 8 for rnum in range(0, 8): best_round = 0 for tnum_r in range(numtraces): round_data[tnum_r][rnum] = Feistel("Gost28147_tc26_ParamZ", bestguess, text[tnum_r], rnum) for bnum in range(0, 4): cpaoutput = [0]*256 maxcpa = [0]*256 for kguess in range(0, 256): #Initialize arrays & variables to zero best_round_key = kguess << (bnum * 8) | best_round sumnum = np.zeros(numpoint) sumden1 = np.zeros(numpoint) sumden2 = np.zeros(numpoint) hyp = np.zeros(numtraces) for tnum in range(numtraces): hyp[tnum] = HW[round("Gost28147_tc26_ParamZ", best_round_key, round_data[tnum][rnum], bnum)] #Mean of hypothesis meanh = np.mean(hyp, dtype=np.float64) #Mean of all points in trace meant = np.mean(traces, axis=0, dtype=np.float64) #For each trace, do the following for tnum in range(0, numtraces): hdiff = (hyp[tnum] - meanh) tdiff = traces[tnum,:] - meant sumnum = sumnum + (hdiff*tdiff) sumden1 = sumden1 + hdiff*hdiff sumden2 = sumden2 + tdiff*tdiff cpaoutput[kguess] = sumnum / np.sqrt( sumden1 * sumden2 ) maxcpa[kguess] = max(abs(cpaoutput[kguess])) best_round = best_round | (np.argmax(maxcpa) << (bnum * 8)) bestguess[((rnum + 1) * 4)-bnum - 1] = np.argmax(maxcpa) print "Best Key Guess: " for b in bestguess: print "%02x "%b, 

Référentiel de résultats GitHub


Conclusions


Dans le cadre de l'étude, j'ai travaillé avec ChipWhisperer. Malgré le fait que je n'ai pas essayé tous les outils (par exemple, glitching), je trouve certainement ChipWhisperer utile, surtout si vous ne voulez pas acheter du matériel spécial coûteux.


Quant à l'analyse de notre chiffre pour la consommation d'énergie, il est plus résistant à cette attaque que les chiffres décrits ci-dessus, mais avec suffisamment de données, la clé peut être obtenue sans problème.


Matériaux intéressants:


Source: https://habr.com/ru/post/fr477730/


All Articles