Cryptographie pratique IoT sur l’espressif ESP8266
L’espressif ESP8266 Chipset fait des planches de développement «Internet des objets» de trois dollars une réalité économique. Selon le célèbre site automatique du micrologiciel du microprogramme Nodemcu-Builds, au cours des 60 derniers jours, il y a eu 13 341 créations de microprogrammes personnalisées pour cette plate-forme. Parmi ceux-ci, seuls 19% ont un soutien SSL et 10% incluent le module de cryptographie.
Nous sommes souvent critiques du manque de sécurité dans le secteur de l’IOT et de couvrir fréquemment des botnettes et d’autres attaques, mais nous tiendrons-nous nos projets aux mêmes normes que nous demandons? Allons-nous arrêter d’identifier le problème ou pouvons-nous faire partie de la solution?
Cet article se concentrera sur l’application des fonctions d’autorisation de cryptage AES et de hachage au protocole MQTT à l’aide du célèbre micrologiciel ESP8266 PUCK Exécuter NODEMCU. Notre objectif n’est pas de fournir une panacée de copie / pâte, mais de suivre le processus étape par étape, d’identifier les défis et les solutions en cours de route. Le résultat est un système de bout en bout crypté et authentifié et authentifié, empêchant les écoutes d’écoute en cours de route et l’usurpation de données valides, sans s’appuyer sur SSL.
Nous sommes conscients qu’il existe également des plates-formes plus puissantes qui peuvent facilement soutenir SSL (PI de Raspberry Pi, Orange Pi, Friendlarm), mais commençons par le matériel le moins cher que la plupart d’entre nous ont couché et un protocole adapté à bon nombre de nos projets. . AES est quelque chose que vous pourriez implémenter sur une AVR si vous aviez besoin.
Théorie
MQTT est un protocole de messagerie léger qui fonctionne sur TCP / IP et est fréquemment utilisé pour les projets IOT. Les périphériques clients s’abonnent ou publient sur des sujets (E.G. capteurs / températures / cuisine), et ces messages sont relayés par un courtier MQTT. Plus d’informations sur MQTT sont disponibles sur leur page Web ou dans notre propre série de démarches.
Le protocole MQTT n’a pas de fonctionnalités de sécurité intégrées au-delà de l’authentification Nom d’utilisateur / mot de passe, il est donc courant de crypter et d’authentifier sur un réseau avec SSL. Cependant, SSL peut être assez exigeant pour l’ESP8266 et lorsqu’il est activé, vous êtes laissé avec beaucoup moins de mémoire pour votre demande. En tant que alternative légère, vous pouvez chiffrer uniquement la charge utile de données envoyée et utiliser une fonction d’identification de session et de hachage pour l’authentification.
Un moyen simple de le faire consiste à utiliser Lua et le module de Crypto Nodemcu, qui comprend la prise en charge de l’algorithme AES en mode CBC ainsi que la fonction HDCH Hash. Utilisation du cryptage AES nécessite correctement trois choses pour produire CIPHERText: un message, une clé et un vecteur d’initialisation (IV). Les messages et les clés sont des concepts simples, mais le vecteur d’initialisation en vaut une discussion.
Lorsque vous encadrez un message dans AES avec une clé statique, elle produira toujours la même sortie. Par exemple, le message “USERNAMPASSWORD” chiffré avec clé “1234567890ABCDEF” pourrait produire un résultat comme “E40D86C04D723aff”. Si vous exécutez à nouveau le cryptage avec la même clé et le même message, vous obtiendrez le même résultat. Cela vous ouvre à plusieurs types d’attaques courants, en particulier d’analyse de motif et d’attaques de relecture.
Dans une attaque d’analyse de modèle, vous utilisez les connaissances selon lesquelles un élément de données donné produira toujours le même chiffretext à deviner quel est le but ou le contenu de différents messages sans connaître la clé secrète. Par exemple, si le message “E40D86C04D723AFFF” est envoyé avant toutes les autres communications, on peut deviner rapidement que c’est une connexion. En bref, si le système de connexion est simpliste, l’envoi de ce paquet (une attaque de relecture) pourrait suffire à vous identifier en tant qu’utilisateur autorisé et que le chaos s’ensuit.
Les IV rendent l’analyse de modèle plus difficile. Un IV est un élément de données envoyé avec la clé qui modifie le résultat final CIPHERText. Comme son nom l’indique, il initie l’état de l’algorithme de cryptage avant que les données ne pénètrent. Le IV doit être différent pour chaque message envoyé de sorte que les données répétées cryptes dans différents chiffres de cipherext, et certains chiffres (comme AES-CBC) exigent qu’il soit imprévisible – un moyen pratique d’accomplir cela est simplement de le randiriser à chaque fois. Les IV ne doivent pas être gardés secrets, mais il est typique de les obscurcir d’une certaine manière.
Bien que cela protège contre l’analyse des motifs, cela n’aide pas aux attaques de replay. Par exemple, la retransmission d’un ensemble donné de données cryptées dupliquera toujours le résultat. Pour éviter cela, nous devons authentifier l’expéditeur. Nous utiliserons un identifiant de session public et pseudo-pseudo-programme pour chaque message. Cet ID de session peut être généré par le périphérique de réception en publiant sur un sujet MQTT.
La prévention de ces types d’attaques est importante dans quelques cas de consommation courants. Des poêles à contrôle Internet existent et des utilités douteuses de côté, ce serait bien s’ils n’utilisaient pas des commandes non sécurisées. Deuxièmement, si je suis datalogue d’une centaine de capteurs, je ne veux pas que quelqu’un remplisse ma base de données avec des ordures.
Cryptage pratique
La mise en œuvre ci-dessus sur le NODEMCU nécessite des efforts. Vous aurez besoin d’un micrologiciel compilé pour inclure le module ‘Crypto’ en plus des autres que vous penchez.ire pour votre demande. Le support SSL n’est pas requis.
Tout d’abord, supposons que vous êtes connecté à un courtier MQTT avec quelque chose comme ce qui suit. Vous pouvez implémenter cela comme une fonction distincte de la cryptographie pour garder les choses propres. Le client s’abonne à une chaîne de sessionID, qui publie des ID de session de Pseudorandom de manière appropriée. Vous pouvez les chiffrer, mais ce n’est pas nécessaire.
1
2
3
4
5
6
7
8
9
dix
11
12
13
14
15
m = mqtt.client (& quot; client et quot; 120)
m: connectez (& quot; myserver.com & quot ;, 1883, 0,
fonction (client)
Imprimer (& quot; connecté & quot;)
Client: Abonnez-vous (& Quot; MyTopic / SessionID & Quot ;, 0,
Fonction (client) Imprimer (& quot; Soutien Succès & Quot;) Fin
)
finir,
fonction (client, raison)
Imprimer (& quot; Raison échouée: & quot; .. raison)
finir
)
M: sur (& quot; message “, fonction (client, sujet, sessionId) fin)
Aller sur, l’ID de nœud est un moyen pratique d’aider à identifier les sources de données. Vous pouvez utiliser n’importe quelle chaîne que vous souhaitez cependant: NodeID = Node.Chipid ().
Ensuite, nous avons configuré un vecteur d’initialisation statique et une clé. Ceci n’est utilisé que pour obscurcir le vecteur d’initialisation randomisé envoyé avec chaque message, non utilisé pour toutes les données. Nous choisissons également une clé distincte pour les données. Ces clés sont des hexagonales de 16 bits, il suffit de les remplacer par le vôtre.
Enfin, nous aurons besoin d’une phrase secrète pour une fonction de hachage que nous utiliserons plus tard. Une chaîne de longueur raisonnable va bien.
1
2
3
4
staticiv = & quot; abcdef2345678901 ”
Evkey = & quot; 2345678901ABCDEF & quot;
DATAKEY = & quot; 0123456789ABCDEF & quot;
Passphrase = & quot; mypassphrase ”
Nous supposerons également que vous avez une source de données. Pour cet exemple, ce sera une valeur lue de l’ADC. Data = ADC.Lead (0)
Maintenant, nous générons un vecteur d’initialisation Pseudorandom. Un numéro hexagonal à 16 chiffres est trop grand pour la fonction de numéro de pseudo-pseudorandom, donc nous la générerons dans deux moitiés (16 ^ 8 moins 1) et les concaténer.
1
2
3
4
5
Demi1 = nœud.random (4294967295)
Demi2 = nœud.random (4294967295)
I = string.format (& quot;% 8x & quot;, demi -1)
V = string.format (& quot;% 8x & quot;, demi2)
IV = i .. v
Nous pouvons maintenant exécuter le cryptage réel. Ici, nous crypterons le vecteur d’initialisation actuel, l’identifiant de nœud et une donnée de capteurs.
1
2
3
crypté_iv = crypto.encypt (& quot; AES-CBC & quot;, editkey, IV, staticiv)
chiffré_nodeid = crypto.encypt (& quot; AES-CBC & quot ;, Datakey, NodeID, IV)
crypté_data = crypto.crypt (& quot; AES-CBC & quot ;, Datakey, données, IV)
Nous appliquons maintenant la fonction Hash pour l’authentification. Nous allons d’abord combiner les données NodeID, IV, les données et la session dans un seul message, puis calculez un hachage HMAC SHA1 à l’aide de la phrase secrète que nous avons définie plus tôt. Nous le convertissons en hexagone pour en faire un peu plus lisible par l’homme pour tout débogage.
1
2
FullMessage = NodeID .. IV .. Données .. SéditionId
hmac = crypto.tohex (crypto.hmac (& quot; sha1 & quotidiend ;, fullMessage, phrase secrète))
Maintenant que les contrôles de cryptage et d’authentification sont en place, nous pouvons placer toutes ces informations dans une structure et l’envoyer. Ici, nous utiliserons des valeurs séparées par des virgules car il est pratique:
1
2
Charge utile = table.concat ({crypted_iv, eid, data1, hmac}, “,”, “,”
M: Publier (& quot; Yourmqtttopic & quot ;, Charge utile, 2, 1, Fonction (Client) P = & Quot; Envoyé & quot; Print (P) Fin)
Lorsque nous exécutons le code ci-dessus sur un Nodemcu réel, nous recevrons quelque chose comme ceci:
1D54DD1AF0F75A91A00D4DCD8F4AD28D,
D1A0B14D187C5ADFC948DFD77C2B2EE5,
564633A4A053153BCBD6ED25370346D5,
C66697DF7E7D467112757C841BFB6BCE051D6289
Tous ensemble, le programme de cryptage est le suivant (Sections MQTT exclues pour la clarté):
1
2
3
4
5
6
7
8
9
dix
11
12
13
14
15
16
17
18
19
nodeid = nœud.chipid ()
staticiv = & quot; abcdef2345678901 ”
Evkey = & quot; 2345678901ABCDEF & quot;
DATAKEY = & quot; 0123456789ABCDEF & quot;
Passphrase = & quot; mypassphrase ”
Data = ADC.Lead (0)
Demi1 = nœud.random (4294967295)
Half2 = nœud.random (4294967295)
I = string.format (& quot;% 8x & quot;, demi -1)
V = string.format (& quot;% 8x & quot;, demi2)
IV = i .. v
crypté_iv = crypto.encypt (& quot; AES-CBC & quot;, editkey, IV, staticiv)
chiffré_nodeid = crypto.encypt (& quot; AES-CBC & quot ;, Datakey, NodeID, IV)
crypté_data = crypto.crypt (& quot; AES-CBC & quot ;, Datakey, données, IV)
FullMessage = NodeID .. IV .. Données .. SéditionId
hmac = crypto.tohex (crypto.hmac (& quot; Sha1 “, FullMessage, Passphrase))
Charge utile = table.concat ({crypted_iv, crypted_nodeid, crypted_data, hmac} et quot; et quot;)
Déchiffrement
MAINTENANT, votre courtier MQTT ne sait pas ou ne se souciait pas que les données sont cryptées, cela passe simplement. Ainsi, vos autres clients MQTT souscrit au sujet devront savoir comment déchiffrer les données. Sur Nodemcu, c’est plutôt facile. Il suffit de diviser les données reçues en chaînes via les virgules et faites quelque chose comme ci-dessous. Remarque Cette fin aura généré l’identifiant de la session le sait déjà.
1
2
3
4
5
6
7
8
9
dix
staticiv = & quot; abcdef2345678901 ”
Evkey = & quot; 2345678901ABCDEF & quot;
DATAKEY = & quot; 0123456789ABCDEF & quot;
Passphrase = & quot; mypassphrase ”
IV = crypto.decrypt (& quot; AES-CBC & quot;, gevkey, chiffrement_iv, staticiv)
nodeid = crypto.decrypt (& quot; AES-CBC & QOT ;, Datakey, chiffré_nodeid, IV)
Data = Crypto.Decrypt (& quot; AES-CBC & quot;, Datakey, chiffréd_data, IV)
FullMessage = NodeID .. IV .. Données .. SéditionId
hmac = crypto.tohex (crypto.hmac (& quot; Sha1 “, FullMessage, Passphrase))
Comparez ensuite le HMAC reçu et calculé, et quel que soit le résultat, invalendez cet identifiant de session en générant un nouveau.
Une fois de plus, en python
Pour un peu de variété, considérons comment nous gérerions le déchiffrement dans Python, si nous avions un client MQTT sur la même machine virtuelle que le courtier qui analysait les données ou le stocker dans une base de données. Supposons que vous ayez reçu les données comme une chaîne «Boîte de paiement», de quelque chose comme l’excellent client Paho MQTT pour Python.
Dans ce cas, il est pratique d’encoder les données cryptées sur le NODEMCU avant de transmettre. Donc, sur le Nodemcu, nous convertissons toutes les données cryptées en hex, par exemple: crypted_iv = crypto.tohex (crypto.encrypt (“AES-CBC”, Evkey, IV, Staticiv))
La publication d’une sessionID randomisée n’est pas discutée ci-dessous, mais est suffisamment facile à l’aide d’OS.URANDOM () et du client PAHO MQTT. Le décryptage est traité comme suit:
1
2
3
4
5
6
7
8
9
dix
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
de crypto.chipher importer aes
Importer binascii
de crypto.hash Importer Sha, HMAC
# Définir toutes les clés
Evkey = ‘2345678901Abcdef’
DATAKEY = ‘0123456789ABCDEF’
staticiv = ‘abcdef2345678901’
Passphrase = ‘mypassphrase’
# Convertir la chaîne reçue en liste
Data = paylard.split (& quot;, “,”;)
# Extraire des éléments de liste
chiffré_iv = binascii.unhexLify (données [0])
chiffré_nodeid = binascii.unhexLify (données [1])
chiffré_data = binascii.unhexLify (données [2])
reçu_hash = binascii.unhexLify (données [3])
# décrypter le vecteur d’initialisation
IV_Decryption_suite = AES.New (geavy, aes.mode_cbc, staticiv)
IV = iv_decryption_suite.decrypt (crypté_iv)
# décrypter les données à l’aide du vecteur d’initialisation
id_decryption_suite = AES.New (Datakey, AES.MODE_CBC, IV)
NodeID = id_decryption_suite.decrypt (chiffréd_nodeid)
data_decryption_suite = AES.New (Datakey, AES.MODE_CBC, IV)
Sensordata = data_decryption_suite.decrypt (chiffréd_data)
# Compute la fonction de hachage à comparer à recevoir_hash
FullMessage = S.Join ([NodeID, IV, Sensordata, Sédition])
hmac = hmac.new (Passphrase, FullMessage, Sha)
calculé_hash = hmac.hexdigest ()
# Voir docs.python.org/2/library/hmac.html pour savoir comment comparer les hachages de manière sécurisée
La fin, le début
Maintenant, nous avons un système qui envoie des messages cryptés et authentifiés via un serveur MQTT à un autre client ESP8266 ou à un système plus grand exécutant Python. Il y a toujours des extrémités lâches importantes pour que vous puissiez attacher cela vous-même. Les clés sont toutes stockées dans la mémoire flash ESP8266S, afin que vous souhaitiez contrôler l’accès à ces périphériques pour empêcher l’ingénierie inverse. Les clés sont également stockées dans le code de l’ordinateur recevant les données, ici en cours d’exécution Python. En outre, vous voulez probablement que chaque client ait une clé et une phrase secrète différentes. C’est beaucoup de matériel secret pour rester en sécurité et potentiellement mise à jour si nécessaire. La résolution du problème de la répartition clé est laissée comme un exercice pour le lecteur motivé.
Et sur une note de clôture, l’une des terribles choses sur la rédaction d’un article impliquant la cryptographie est la possibilité d’avoir tort sur Internet. Il s’agit d’une application assez simple du mode AES-CBC testé et true avec HMAC, il devrait donc être assez solide. Néanmoins, si vous trouvez des lacunes intéressantes dans ce qui précède, veuillez nous faire savoir dans les commentaires.