IoT and Blockchain as Back-End

Vincent OLLIVIER
Everysens
Published in
14 min readJan 4, 2022

--

Présentation

L’un des défaut de l’IoT autonome “long life”, c’est à dire des objets connectés qui peuvent fonctionner une décennie ou plus, c’est l’infrastructure qui permet de les exploiter. En effet, si l’entreprise qui s’occupe de la récupération et du traitement des données vient à disparaître, il est fort probable que ces objets deviendront orphelins.
A titre personnel, ça m’est arrivé ! Du jour au lendemain en 2018, Nike a décidé d’arrêter le support sur ma “vieille” montre connectée qui fonctionnait encore très bien : plus d’API, plus de services. Ma montre s’est pris en pleine face la violence de l’obsolescence programmée.

Comment, dans le monde de 2022 pourrait-on régler une partie de ce problème ? En fait, c’est possible avec la blockchain !

Solution Blockchain

L’idée générale est de se passer du service associé à l’objet connecté (API), souvent fourni par l’entreprise émettrice de l’objet et de le remplacer par un stockage dans la blockchain.

L’avantage de ce fonctionnement est l’indépendance de l’objet connecté vis-à-vis de son fournisseur initial. Il devient autonome en communication, il va stocker ses données d’acquisition dans la blockchain, sans intermédiaire. La majorité des blockchains sont libres en lecture, il suffit donc d’inspecter celle-ci pour retrouver les données de l’objet. Les blockchains sont décentralisées(1), résilientes avec une qualité de service proche de 100%, et leurs durées de vie sont statistiquement supérieures à n’importe quel service proposé par un acteur local centralisé.

Évidemment tout n’est pas parfait à l’heure actuelle, mais je pense que les avantages vont largement dominer les inconvénients dans un avenir proche.

Les avantages

  • Se dispenser d’un service critique, complexe et coûteux : API, database, scalabilité, disponibilité, sécurité, audits, etc …
  • Lecture libre et gratuite de la blockchain.
  • Changement de propriété de l’objet ou d’une flotte nettement facilité.
  • Le propriétaire assume le coût d’utilisation de la blockchain par son objet. (contrôle total).
  • Partage des données natif (lecture blockchain).
  • Hack proof.

Les inconvénients

  • Ne dispense pas de l’utilisation d’un réseau intermédiaire (Radio, GSM, Wifi, Bluetooth, etc).
  • Le coût des transactions (peut être largement compensé par l’absence du coût du service centralisé)
  • Communication bi-directionnelle plus complexe et probablement asynchrone.
  • Données libres d’accès (mais pouvant être chiffrées !).

Reprenons mon histoire avec ma montre connectée Nike : je possède cette montre, j’en ai la propriété, mais pas celle des données qu’elle émet. Nike les possédait et me les rendait grâcieusement via un web service et un front-end. Comme la notion de “grâcieusement” n’existe pas dans notre monde, Nike revendait/utilisait probablement ces données et donc j’étais le produit. Récapitulons : j’ai une montre, mais pas mes données, et comme il n’y a plus de service, elle est bonne à jeter.

Tous ces problèmes peuvent être réglés par la blockchain. Il suffirait que la montre se connecte via mon Wifi, fasse régulièrement des transactions, signées avec ma clé privé, dans une blockchain pour y déposer les données d’acquisition, et ensuite une simple page web avec un peu de javascript et exécutée localement peut relire la blockchain et me présenter mes données déchiffrées et formalisées.

Voyez à quel point la blockchain m’a rendu le pouvoir sur mon bien ! Non seulement la montre est à moi, les données aussi, mais en plus, personne d’autre que moi n’y a accès ! Et c’est bien ce que l’on veut de nos objets connectés : le contrôle et la possession totale.

Évidemment, dans le cas de la montre connectée, des transactions chiffrées et horodatées dans une blockchain semble un chouia “overkill”. Mais si votre objet connecté doit servir de base au calcul de l’utilisation du CO2 par votre supply chain, et que des millions d’euros sont en jeu, cette méthode prend tout son sens !

Ici, j’ai volontairement éludé les détails concernant le paiement des transactions ou la méthodologie de chiffrement dans un but de simplification. Si ces deux points ajoutent des strates de complexité technique, ils ne changent en rien le propos.

Blockchain Ethereum

Afin d’aborder la partie démonstration technique de manière plus sereine on va, dans un premier temps, faire un petit rappel de la base du fonctionnement d’une blockchain très connue et pleinement opérationnelle : Ethereum.
Ethereum est une blockchain à smart-contract, sans rentrer dans des détails “affreusement” techniques il faut juste comprendre que c’est une sorte d’ordinateur géant décentralisé capable d’exécuter les lignes de code de votre programme (smart-contract) en échange d’une petite compensation financière. Cette blockchain en allant plus loin que la simple notion de stockage de données dans une transaction et en proposant l’exécution de code à ouvert une nouvelle vision sur l’avenir du web, et par extension, du monde. (voir web3)

Paradoxalement, on ne va pas utiliser cette fonctionnalité pour notre démonstration, on va se focaliser sur une transaction, c’est suffisant et déjà bien assez dense comme ça !

C’est quoi une “transaction blockchain” ?

Faisons la démonstration à l’envers : une transaction c’est une action faite sur le réseau Ethereum (un changement d’état), par exemple transférer des ethers (“monnaie”) d’une adresse à une autre. Pour avoir une adresse Ethereum il faut une clé publique et pour fabriquer une clé publique il faut une clé privée. Et n’importe qui peut se générer une clé privée.

Pour comprendre comment un objet connecté peut, de lui-même, s’interfacer avec une blockchain, il faut comprendre la notion d’adresse et de clé cryptographique privé/publique. Ici, je vais simplifier à l’extrême juste pour poser les bases. Par ailleurs, des centaines d’articles techniques expliqueront bien mieux que moi les notions cryptographiques du réseau, exemple : lien.

  • 🔑 Clé privée : c’est un numéro aléatoire si grand qu’il rend la probabilité de collision statistiquement proche de 0. C’est à dire qu’avec une bonne entropie initiale vous pouvez générer des clés privées jusqu’à transformation de notre soleil en supernova sans jamais tomber sur la même ! 😜
    La clé privée comme son nom l’indique n’est jamais partagée, elle doit rester absolument secrète sous peine de perdre le contrôle sur vos données.
  • 🔑 Clé publique : elle est dérivée de la clé privée via un algorithme unidirectionnel (elliptic curve). En simple : on ne peut pas retrouver la clé privée avec la clé publique (unidirectionnalité). Mais par contre on peut s’assurer qu’une donnée a été chiffrée avec une certaine clé privée seulement avec la clé publique ! Cette clé peut être partagée au monde sans aucun risque. La magie de la cryptographie !
  • 💲Adresse : elle est dérivée de la clé publique via un algorithme de hachage (Keccak-256). C’est l’équivalent de votre “portefeuille” (wallet) sur la blockchain Ethereum. Seule votre clé privée peut manipuler son contenu.

J’espère que je ne vous ai pas perdu en route avec ces histoires de clé. C’était la partie la plus complexe, le reste de l’explication est plus simple.

Imaginons donc que notre objet connecté possède sa propre clé privée (qu’on aura généré pour lui et qu’on conservera), et une adresse sur Ethereum. Il est en mesure de faire des transactions vers une adresse, moyennant une petite soulte au réseau. Dans cette transaction il peut passer des méta-données. Ces méta-données peuvent être la payload (chiffrée ou pas) du message que veut nous faire parvenir notre objet connecté. Comme la blockchain est libre en lecture, n’importe qui connaissant l’adresse de destination de la payload peut la lire, gratuitement.
Par contre, personne ne peut supprimer ou corrompre ces données, même pas le propriétaire de la clé privée associée à cette adresse ! C’est la force de la blockchain : ce qui est écrit une fois, l’est pour l’éternité.

Imaginons maintenant que je sois le propriétaire de cette adresse de destination, c’est-à-dire un numéro aléatoire (dérivé) au milieu de milliards d’adresses et de transactions. Il me suffit de venir scruter cette adresse pour savoir si mon objet connecté vient d’envoyer une “payload”. Je peux faire cette action depuis etherscan ou via une requête https depuis le terminal de mon ordinateur ou, de manière professionnelle, depuis un node Ethereum hébergé sous AWS, par exemple.

La boucle est bouclée ! Mon objet connecté stocke des payloads sur une blockchain publique sans aucun intermédiaire et pour un coût dérisoire. Et moi je peux relire la blockchain gratuitement. Mes données sont cachées/chiffrées et sont préservées tant que la blockchain existera.

Note technique : tant qu’on alimentera le wallet de notre objet connecté il pourra payer ses transactions, et si on stoppe ou retire les fonds du wallet, l’objet deviendra muet, ses transactions échouant faute de pouvoir les payer.

Ethereum a été choisi pour cette explication car il possède un très large écosystème de développement et est fortement documenté. Il se prête donc bien au “PoC quick & dirty”. Cependant son coût d’utilisation le disqualifie (momentanément) du monde de l’IoT.

Mais il existe bien d’autres blockchains fonctionnant plus ou moins de la même manière, certaines étant spécialisées dans l’IoT comme Helium ou IOTA.

Limitations Techniques

Dans la présentation on parlait d’IoT autonome “long life”. En effet c’est tout le challenge de cet article : réussir à faire communiquer vers une blockchain un device utilisant un hardware optimisé pour la basse consommation électrique. Cette optimisation pour la consommation passe par l’utilisation d’un microprocesseur conçu précisément pour ce besoin et dont les performances en termes de puissance de calcul sont extrêmement réduites. Littéralement des “performances” du début des années ’80 💾. De manière générale, ils ne possèdent même pas d’unité de calcul en virgule flottante (FPU). Dans ces conditions, faire tourner des algorithmes cryptographiques qui font largement appel aux mathématiques, est une gageure ! 😉
Si la première intuition est de se dire “ça va pas être possible”, en réalité, en grattant un peu on se rends compte que y a peut être moyen ! Surtout que notre besoin est extrêmement limité : signer une transaction et l’émettre vers Ethereum.

A noter qu’il existe des coprocesseurs cryptographiques, mais leur utilisation dépasse largement le cadre de cet article. Surtout ils ont un coût non négligeable et prennent un emplacement physique sur la carte mère de votre device. Ici on va s’en passer, sans pour autant renier l’utilité de leur existence !

Démonstration Technique

Le microprocesseur (ou plutôt le microcontrôleur) que j’ai ciblé est le STM32L0. C’est une technologie 32 bits ARM Cortex M0 produite par STMicroelectronics et configurée pour le Ultra Low Power. Sa fréquence d’horloge ajustable est de l’ordre de quelques MHz. Autant dire une puissance de calcul très faible.
Par contre, il est parfait pour l’IoT basse consommation avec un courant de fuite de l’ordre des nA en deep sleep. 128KB de Flash et 20Kb de RAM sont suffisants pour réaliser les opérations dédiées à ce type de matériel.

Par contre qu’en est-il de l’aspect cryptographique ? Nativement aucun support n’est proposé.

La première étape c’est de vérifier qu’on est en mesure techniquement de signer une transaction Ethereum avec notre valeureux STM32L0.

Signer une transaction (théorie)

Création

Avant de pouvoir signer une transaction, il faut “rédiger” cette transaction. La structure est plutôt simple, une succession de clé/valeur (JSON), comme ci-dessous :

Pour éviter de se noyer dans les détails techniques, on va se focaliser uniquement sur les deux champs surlignés :

  • To : qui correspond à l’adresse qui va recevoir la transaction.
  • Data : qui contient la payload du message de notre device.

Signature

Maintenant que la structure de la transaction est complétée on peut la signer avec notre clé privée.

Techniquement voilà comment ça doit se passer :

  1. Sérialisation du “JSON” sous le format RLP (format d’encodage “home made” de Ethereum).
  2. Calcul du hash Keccak-256 du RLP précédemment créé.
  3. Calcul de la signature ECDSA du hash précédemment créé.
  4. Ajout de cette signature dans la transaction sérialisée.

Voilà, notre transaction est prête à être envoyée au réseau Ethereum. Si elle est valide et qu’on a de quoi payer son exécution elle aboutira, dans le cas contraire elle sera rejetée.

Dans les quatres opérations nécessaires à la signature de la transaction, l’une d’entre elles est particulièrement complexe à exécuter pour notre STM32L0 : la numéro 3, la signature à courbe elliptique. Car l’algorithme provoque d’intenses calculs en virgules flottantes que notre MCU va devoir “émuler” faute d’être équipé d’une FPU.

Signer une transaction (pratique)

Après la théorie, la pratique. Première difficulté : réussir à faire de l’ECDSA sur un notre plateforme STM32L0, et en plus avec les paramètres de courbe elliptique de Ethereum (et Bitcoin) : secp256k1.

Micro-ECC

Pour régler ce problème je cherche et trouve une pépite sur Github : kmackay/micro-ecc

Ce gars a écrit une implémentation ECDSA en langage C cross architecture et compatible ARM. Non seulement l’algo ECDSA de Ethereum/Bitcoin est implémenté mais aussi les opérations mathématiques de base (en virgule flottante) écrites en assembleur. Bon, y a clairement du niveau, c’est du lourd, et ça va peut-être nous débloquer !

Aleth

Pour tester la compatibilité du code de notre ami Ken Mackay je vais cloner le projet Aleth, une implémentation d’un client Ethereum en langage C++. A partir de ce projet, je vais créer un sous projet qui aura pour but de fabriquer une transaction basique et la signer en utilisant les méthodes de Aleth. Après quelques tâtonnements au milieu d’un projet 78K de lignes de code j’arrive à mettre en place une séquence d’appel de méthodes qui mène à la sérialisation et la signature d’une transaction. En décomposant et explorant le code de chacune des actions, je comprends mieux le process de sérialisation (RLP), hash et signature qui mène à une transaction valide.

Ganache

Mais je dois tout de même faire valider cette transaction par un véritable réseau pour m’assurer de la rigueur de mon code. Pour se faire, j’installe et lance Ganache, un nœud local de test Ethereum avec une UI. Ganache crée une mini blockchain Ethereum de test. On peut y générer des clés privées, et des adresses. J’utilise ces clés et adresses pour une transaction de test et la propose à mon instance Ganache locale via web3js qui logiquement la considère valide et mine un nouveau bloc avec cette transaction dedans.

Ganache UI

Web3js

Web3js est une collection de librairies javascript qui permettent d’interagir avec un nœud Ethereum local ou distant. En seulement quelques lignes de code javascript on peut signer et envoyer une transaction. J’ai créé ce projet EthereumTransactionTest afin que vous puissiez comprendre rapidement comment tester une transaction via web3js, le tout dans une page exécutée localement !

Une fois la constante ACCOUNT_MONEY_PRK configurée avec une private key provenant d’un account valide de votre instance Ganache (ainsi qu’une adresse destinataire valide), le code suivant devrait s’exécuter avec succès.

Et le “miracle” s’accomplit ! En explorant le dernier block miné dans Ganache on retrouve notre transaction et notre “payload” (0x112233AABBCCD) dans le champ “TX DATA” de la transaction (en rouge). Et on constate que l’adresse destinataire est conforme au contenu de la transaction (en vert) !

Warning : le code de ce projet qui utilise web3js 1.4.0 est fait pour communiquer avec Ganache 2.5.4 et le hardfork Ethereum “MUIRGLACIER”. Je ne peux pas garantir qu’il fonctionnera avec les prochains forks (et ça ne sera surement pas le cas !)

STM32L0

Maintenant que j’ai une instance Ganache et un projet web3.js totalement fonctionnel nous pouvons tester la validité de diverses transactions facilement. Nous allons donc tenter de créer une nouvelle transaction de toute pièce sur le STM32L0, la lui faire signer, et vérifier qu’elle est valide en la poussant dans Ganache via web3.js.

J’ai mis à disposition le code du STM32 ici : https://gitlab.com/vinkerozen/stm32l0-elliptic-curve-cryptography
Par contre ce n’est pas un projet “compilable” car j’ai développé mon code au sein d’un firmware propriétaire (Everysens), dont je ne peux, naturellement pas, partager le code. Mais la totalité du code de signature est dans ce projet.

RLP

Deuxième difficulté (après l’ECDSA) : On ne peut pas faire du “JSON to RLP” sur notre MCU. Il faut qu’on fabrique la transaction directement en RLP (donc sérialisée). Ça nécessite de comprendre les bases du RLP et donc de procéder à quelques conversions ascii to hex.

On crée donc un bon gros buffer static de 120 bytes : RLP, qui correspondra à la transaction RLP, puis on va venir le remplir avec les différents champs obligatoires :

Voilà, l’essentiel du contenu de la transaction au format RLP est complété. Reste à signer cette transaction. Voir la note (2) concernant le “recovery identifier”.

Hash Keccak256

La signature ECDSA se fait sur un ensemble de données, mais pas le contenu du RLP directement comme on pourrait le croire. La signature se fait sur le hash Keccak256 du RLP ! (Pour information, “Keccak256” n’est qu’une variante paramétrique de SHA-3 standard).
Ça veut dire que notre code doit d’abord calculer le hash SHA-3 de notre buffer RLP. Pour se faire on récupère ce projet Github : SHA3IUF, on le paramètre pour Keccak et on récupère un pointeur qui contient la valeur du hash :

Random Number Generator

Troisième difficulté : le RNG ! Ha oui, l’algorithme ECDSA a besoin de générer des nombre aléatoires. Vraiment aléatoires ! Sous peine de pouvoir découvrir la clé privée via la clé publique. Oui, je vous ai dit plus haut que c’était impossible, et c’est le cas, mais uniquement si l’entropie du RNG est suffisante.

Le STM32L0, ne possède pas de RNG hardware, donc on va le faire nous même en software. Mais comme on est en mode “PoC”, j’ai écrit un RNG très basique à l’entropie vraiment “limite”. En production il faudrait ajouter du sel : timestamp, bruit sur un ADC, température ambiante, tension de pile, etc …

Ici je prends juste la valeur du ticker et je la copie dans le buffer d’entrée x fois, c’est du quick &dirty !

Signature

Enfin la signature ! Hé oui, on y arrive ! Maintenant qu’on a tous les éléments il suffit d’appeler la bonne fonction de l’implémentation de kmackay/micro-ecc qui va signer en utilisant l’ECDSA paramétrée avec la courbe elliptique secp256k1 :

Le temps de traitement de la signature est d’environ 4 secondes (à 4MHz). Ce qui est étonnamment court pour un CPU qui doit faire l’intégralité de ses calculs en virgules flottante de manière non native (logiciel) !

Reste à ajouter le résultat de la signature “sig” dans le RLP afin de le rendre conforme à la spécification Ethereum :

Exécution

Le code terminé, il serait temps de l’exécuter !
Etant donné que j’ai écrit mon code sous forme de commande dans notre firmware (Everysens), je n’ai qu’à flasher ce firmware sur mon device de développement via le ST-Link :

Device and ST-Link

Puis me connecter à l’interpréteur de commandes et lancer la bien nommée “test:ecc” qui me retourne ceci :

Device terminal

Contrôle

Pour contrôler que notre STM32L0 a correctement signé cette transaction et généré un RLP conforme, on copie cette string pour la coller dans notre projet web3.js. Hallelujah ! Web3 et Ganache acceptent la transaction !

Conclusion

Je dois avouer que c’est dense et qu’il faut se concentrer pour rentrer dedans. Mais l’essentiel c’est de comprendre que la possibilité de signer des transactions offre la capacité d’interfacer n’importe quel produit équipé d’un microcontrôleur basique avec une blockchain (via Wifi ou 4G) dès maintenant, en 2022 !

Notes :

(1) Décentralisation : Toutes les blockchains de ne sont pas décentralisées. Certaines le sont plus que d’autres, certaines aussi sont publiques, ou permissionnées et/ou privées.

(2) Recovery Identifier : ce champ a une structure tordue car il contient plusieurs informations sur un même octet. Le problème ici est l’incapacité du code actuel à définir si la clé public éphémère générée lors de la signature est paire ou impaire. Comme j’ai fixé ce champ à 0x1C (faute de temps de dev’), la transaction obtenue en sortie est parfois valide, parfois invalide. (voir page 121 de “Mastering Ethereum”).

Livre de référence : Mastering Ethereum de Andreas M. Antonopoulos chez O’Reilly.

--

--