Tutorial
advanced

Send verifiable IoT data from Arduino Nano 33 IoT to power MachineFi Dapps

This tutorial will teach you how to use the secure element integrated on the Arduino Nano 33 IoT to generate and transmit verifiable IoT data over MQTT

Post Header Image

08/05/2022

Updated: 08/16/2022


SHARE

Edit on Github

Introduction

In this tutorial we describe how to generate verifiable IoT data from the Arduino Nano 33 IoT board, making use of the ATECC608B integrated crypto chip. The data is then transmitted over WiFi unsing the MQTT protocol to be further processed by the IoTeX Layer-2 nodes to serve MachineFi Dapps the the IoTeX Blockchain.

Data message

This board integrates an accelerometer and a gyroscope, for the sake of simplicity for this tutorial we will just read accelerometer data and generate a simple data message that includes the 3 axis instant acceleration every few seconds. A real world application will probably send more meaningful messages, for example only send acceleration data when it's over a preset threshold in order to detect specific events.

Data signature

We make use of the ATECC608B secure crypto chip that is integrated in this board to generate and securely store and use a private key. The chip itself will be used to securely sign the data message using the private key, while we will use the hash of the correspondig public key as the unique id of the device (e.g. to be used to create a list of authorized devices on the blockchain).

Eventually, a data message will look like this:

{
    "message": {
        "ax": 0.01,
        "ay": -0.97,
        "az": 0.29,
        "timestamp": 1655543438
    },
    "signature": "4ea68ad12dafad17583a180...7fd5811e365261d0e8623c71a155323e7c82",
    "cryptography": "ECC",
    "curve": "secp256r1",
    "hashing": "sha256"
}

Verification

Notice that we added the signature to the message plus some metadata to let the receiver know how to verify the signature. Once this message is received by the IoTeX Layer-2 node, the signature can be used to recover the public key using elliptic curve cryptography with the secp256r1 curve. The public key can be hashed using SHA256 to obtain the device id which in turn could be verified as an authorized device if it's been registered as a DID on the IoTeX blockchain.

A note about the public key

The ATECC608B chip provides elliptic curve digital signature using the secp256r1 curve. Please notice that this is not compatible with IoTeX or Ethereum-compatible blockchain cryptography (which uses secp256k1 instead). Therefore this board will never be able to sign valid blockchain transactions: make sure you don't use any "address" derived from the public key to receive crypto currencies as they will be lost forever.

Requirements

To complete this tutorial you need:

  • IoT Nano 33 IoT board.
  • The Arduino IDE configured to flash firmware onto the Nano 33 IoT board
  • If you intend to use the data generated by the board into an IoTeX smart contract, it's highly recommended to also follow the MachineFi Get Started guide to deploy a W3bStream node as the data storage and compute layer that serves Dapps on the blockchain.

Configure the ATECC08 crypto chip

The ATECC608A needs to be configured and locked before it can be used.

The ArduinoECCX08 library provides a sketch that can be used to configure the chip.
Just flash the ECCX08CSR.ino sketch and follow the steps shown in the serial monitor:

image

At the end of the process, the device will produce a CSR (Certificate signing request). You can save this CSR for future use, but for the purposes of this example we will not be using certificates. You will anyway be able to regenerate tha certificate using the same sketch on an already locked board.

Coding the Sketch

The full code for this sketch is available at https://github.com/iotexproject/machinefi-getstarted-preview/tree/main/devices/nano-33-iot

Install dependencies

Using the Arduino library manager, install the following dependencies for the sketch:

Make sure you include them in your sketch:

#include <FlashStorage.h>
#include <FlashAsEEPROM.h>

#include <ArduinoMqttClient.h>
#include <WiFiNINA.h>
#include <ArduinoECCX08.h>
#include <Arduino_LSM6DS3.h>

Configure the sketch

The next section can be used to configure the MQTT endpoint intended to receive the data messages, and the WiFi network for the device to connect to the Internet:

// MQTT broker, eg. "test.mosquitto.org".
const char mqttBroker[] = "<YOUR_MQTT_BROKER>";
// MQTT port, eg. 1883. 
const int mqttPort = 1883;
// Wifi ssid.
const char wifiSsid[] = "<YOUR_WIFI_SSID>";
// Wifi password.
const char wifiPass[] = "<YOUR_WIFI_PASSWORD>";

WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);

The ATECC608B crypto chip provides 16 slots that can be provisioned with a key pair: make sure you use the slot that you configured in the previous step.

// The slot to use in the ATECC608A chip.
const int slot = 0;

Next we just have some global variables:

// Variables to store the deviceId and public key.
String deviceId = "";
byte publicKey[64] = {0};

// Variable to store the MQTT topic.
String topic = "";

Connect to WiFi and to the MQTT broker

The Arduino's setup() function is used to first connect to the WiFi network, then connect to the MQTT broker where the data messages will be sent.

void setup() {
  Serial.begin(9600);
  while (!Serial);

  Serial.println("Verifiable IoT data from Arduino");
  Serial.println();

  // Init the ATECC608A crypto chip.
  if (!ECCX08.begin()) {
    Serial.println("Failed to communicate with ECC508/ECC608!");
    while (1);
  }

  // Verify the crypto chip is locked (ie. configured.).
  if (!ECCX08.locked()) {
    Serial.println("The ECC508/ECC608 is not locked!");
    Serial.println("ECC508/ECC608 needs to be configured and locked before proceeding");
    while (1);
  }

  // Connecto to the WiFi network.
  initWiFi();

  // Connect to the MQTT broker
  initMqtt();
  
  ...

Generate the device unique id

The setup()function ends with obtaining the public key from the ATECC608B secure element

...
  // Retrieve the public key for the corresponding slot in the ECC508/ECC608.
  ECCX08.generatePublicKey(slot, publicKey);
...

and creating an hypotetical device unique id by hashing it (see the getDeviceIdfunction below). The unique id can then be used the foundation for a decentralized identity for this device on the IoTeX blockchain that would be created during the device manufacturing process.

  // Generate the device id from the public key.
  deviceId = getDeviceId(publicKey);
  Serial.println  ("Device id: " + deviceId);
  topic = "/device/" + deviceId + "/data";
  Serial.print("Topic: ");
  Serial.println(topic);
}

We have also pre-computed an hypotetical MQTT destination topic for data collection in the form of /device/<deviceId>/data.

Send verifiable data

The loop() function is finally in charge of reading some accelerometer, sign the data and send it to the MQTT broker. The code included in the loop() is pretty self-explanatory:

First we generate the data message using accelerometer data:```

 void loop() {
  // Wait until some relevant accelerometer data is available
  readAccelerometer(x,y,z);

Next, we hash the message (here, we used the hardware-accelerated SHA256 hashing function provided by the ATECC608B to hash the message)

  // Hash the message using sha256.
  byte hash[64] = {0};
  ECCX08.beginSHA256();
  ECCX08.endSHA256((byte*)message.c_str(), message.length(), hash);

we then use the ATECC608B to securely sign the message hash:

  // Sign the message using ECC cryptography + secp256r1 curve
  byte signature[64];
  ECCX08.ecSign(slot, hash, signature);

finally, we can use the mqtt client to send out the signed message:

  // Publish the message over MQTT.
  String mqttMessage = buildSignedMessage(message, signature, sizeof(signature));
  Serial.print("Sending mqtt message: ");
  Serial.println(mqttMessage);
  mqttClient.beginMessage(topic);
  mqttClient.print(mqttMessage);
  mqttClient.endMessage();

So to summarize the steps:

  1. Obtain the public key from the ATECC608A.
  2. Calculate the device id from the public key (note that this is application specific. Any application can choose it's own process for creating device ids and use it later to verify device identities).
  3. Produce a JSON message with sensor data
  4. Get the sha256 hash of the message from the ATECC608A.
  5. Get the ECC signature of the message hash from the ATECC608A.
  6. The message along with the signature and some metadata is sent to the server.

Given that the MQTT topic itself contains the device id, so we do not include it in the message.

Check out the MachineFi get started guide to learn how to deploy a Layer-2 network component that can receive ad verify IoT data, and use it to build proof of real-world facts that can then fuel blockchain MachineFi Dapps.

Utility functions

Let's also take a quick look at some relevant utility functions included in the code:

getDeviceId()can be customized and it's only intended to generate an id that would never change for the device: the public key from the ATECC608B is a good candidate, we just hash it to hide the public key.

Therefore, this functions simply takes the public key, hashes it using the SHA256 hashing algorithm:

// Gets the device id from the public key.
String getDeviceId(const byte publicKey[64])
{
  byte hash[64] = {0};
  ECCX08.beginSHA256();
  ECCX08.endSHA256(publicKey, 64, hash);
  Serial.print("Hash of public key is:   ");
  printBufferHex(hash, sizeof(hash));
  return (char*)hash;
}

buildMessage() takes accelerometer readings and timestamp to build the JSON message object containing the actual data:

// Builds a data message given x,y,z accelerations and timestamp.
String buildMessage(float x, float y, float z, String timestamp)
{
  Serial.println("Buildong message:");
  String message = "{\"ax\":";
  message += String(x);
  message += ",\"ay\":";
  message += String(y);
  message += ",\"az\":";
  message += String(z);
  message += ",\"timestamp\":";
  message += timestamp;
  message += "}";
  return message;
}

buildSignedMessage(): this function takes the JSON of the data message and the signature, and builds tha actual JSON message that will be sent out over MQTT, also adding some metadata about the cryptography used to generate the signature:

// Constructs a signed message from a message and a signature.
String buildSignedMessage(String message, byte* signature, int signatureSize)
{
  char base64Signature[signatureSize * 2] = {0};
  encoder.base64_encode((uint8_t*)signature, signatureSize, base64Signature);
  String messageWithSignature = "{\"message\":";
  messageWithSignature += message;
  messageWithSignature += ",\"signature\":\"";
  messageWithSignature += base64Signature;
  messageWithSignature += "\"}";
  return messageWithSignature;
}

the final message will look like the exampe have seen in the firs section of this tutorial.

// Builds a message given a heart rate value and timestamp.
String buildMessage(String heartRate, String timestamp)
{
  String message = "{\"heartRate\":";
  message += heartRate;
  message += ",\"timestamp\":";
  message += timestamp;
  message += "}";
  return message;
}

Check out the full source code on the IoTeX official GitHub.


Docs

IoTeX Docs


IoTeX Developerslogo

devs@developers.iotex.io