TLS 1.3 for Arduino Nano

TLS 1.3 for Arduino Nano © GPL3+

Building of secured device-to-cloud channel with Arduino Nano Every, Sim7000E NB-IoT Hat, and Luner IoT SIM with SAFE2 applet.

  • 112 views
  • 0 comments
  • 1 respect

Components and supplies

Apps and online services

About this project

The project is intended to demonstrate Pod IoT security solution – SAFE2 applet with the simplest IoT module - Arduino Nano.

The SAFE2 applet is based on the expansion of the GSMA IoT SAFE idea. The applet is intended for extremely low cost and constrained IoT devices to provide secure delivery of telemetry data to the data server.

IoT SAFE

The IoT SAFE applet was introduced by the GSMA on December 3, 2019. It is intended to simplify IoT solutions and to provide a secure storage for cryptography keys and secure environment to execute cryptography functions for TLS protocol.

IoT SAFE is a kind of security library which runs on a protected environment. Such a model imply the complex architecture of IoT Application, which may include TLS layer, middleware and the Security Element (SIM) with the IoT SAFE applet.

The TLS Layer is responsible for the TLS handshake and protocol handling. The IoT SAFE Middleware is responsible for command transfer from the TLS Layer to the IoT SAFE applet. The IoT SAFE applet is responsible for cryptography operations execution.

SAFE2

The Pod SAFE2 solution was developed to simplify data delivery from SIM to a server. It includes not only the cryptography library but the TLS1.3 protocol implementation based on the SIM Toolkit functionality.

It significantly simplifies the interface between a data gathering application (for example IoT telemetry) and the SAFE2 applet.

An external application needs only to pass the data which shall be sent to the server.

The TLS Layer in the IoT Application is not required anymore as it is already implemented by the Pod SAFE2 applet.

The SIM is responsible for all the protocol steps including domain name resolving and for repeating attempts until the message will be delivered to the server.

To send some data via SAFE2 applet, the Device Application shall send specific commands to the SIM via Modem Middleware - AT commands for full SIM access, namely “AT+CSIM”.

First of all the supplementary logical channel must be open (to not interfere with the GSM session between the modem and the SIM in the base channel). Then the applet must be selected in the new channel and data shall be sent to the applet in the same channel. The channel shall be closed for reuse.

The AT+CSIM commands must encode the next APDU commands to the SIM according to ISO/IEC 7816-4:

  • MANAGE CHANNEL (Open);
  • SELECT (by name);
  • PUT DATA with proprietary tag ‘c1’;
  • MANAGE CHANNEL (Close).

Getting the PUT DATA command with application data, the SAFE2 applet creates a sending job. The job contains address info and application data. The applet will try to execute the job until it will succeed. Currently for each job the server name is resolved if necessary and TLS 1.3 session is established with the Pre-Shared Key stored in the SIM.

Execution Log

Below is the typical execution log of he Demo Scetch. The first part is a modem setup, waiting for network registration and finishing of GSM-related processes in the modem. The next part - Device ID registration - completes the setup. The last part - providing data to the applet.

15:35:05.588 -> nano started!
15:35:06.589 -> put 'at'
15:35:06.988 -> put 'at'
15:35:07.389 -> put 'at'
15:35:07.789 -> put 'at'
15:35:08.189 -> put 'at'
15:35:08.589 -> put 'at'
15:35:08.988 -> put 'at'
15:35:09.387 -> put 'at'
15:35:09.787 -> put 'at'
15:35:10.186 -> put 'at'
15:35:10.286 -> at
15:35:10.319 -> OK
15:35:10.386 ->
15:35:10.386 -> +CFUN: 1
15:35:10.485 ->
15:35:10.519 -> +CPIN: READY
15:35:10.651 ->
15:35:10.685 -> SMS Ready
15:35:11.581 -> put 'at+creg?'
15:35:11.581 -> 0:2
15:35:11.581 -> cntr: 0
15:35:13.074 -> put 'at+creg?'
15:35:13.074 -> 0:2
15:35:13.074 -> cntr: 0
15:35:14.600 -> put 'at+creg?'
15:35:14.600 -> 0:5
15:35:14.600 -> cntr: 1
15:35:16.093 -> put 'at+creg?'
15:35:16.093 -> 0:5
15:35:16.093 -> cntr: 2
15:35:17.587 -> put 'at+creg?'
15:35:17.587 -> 0:5
15:35:17.587 -> cntr: 3
15:35:19.080 -> put 'at+creg?'
15:35:19.113 -> 0:5
15:35:19.113 -> cntr: 4
15:35:20.606 -> put 'at+creg?'
15:35:20.606 -> 0:5
15:35:20.606 -> cntr: 5
15:35:22.099 -> put 'at+creg?'
15:35:22.099 -> 0:5
15:35:22.099 -> cntr: 6
15:35:23.592 -> put 'at+creg?'
15:35:23.625 -> 0:5
15:35:23.625 -> cntr: 7
15:35:24.621 -> setup finished
15:35:24.621 -> at+csim=10,"0070000001"
15:35:24.621 -> +CSIM: 6,"019000"
15:35:24.621 ->
15:35:24.621 -> OK
15:35:24.654 -> at+csim=34,"01A404000CF0706F646773616665320101"
15:35:24.654 -> +CSIM: 4,"9000"
15:35:24.654 ->
15:35:24.654 -> OK
15:35:24.787 -> at+csim=30,"01DA02C00A51523143572089723526"
15:35:24.787 -> +CSIM: 4,"9000"
15:35:24.787 ->
15:35:24.787 -> OK
15:35:24.820 -> at+csim=10,"0070800100"
15:35:24.820 -> +CSIM: 4,"9000"
15:35:24.820 ->
15:35:24.820 -> OK
15:35:24.820 -> Set DeviceID: OK
15:35:24.821 -> Humidity: 40
15:35:24.821 -> Temperature: 25
15:35:24.821 ->
15:35:24.821 -> HT data:00280019
15:35:24.821 -> at+csim=10,"0070000001"
15:35:24.821 -> +CSIM: 6,"019000"
15:35:24.821 ->
15:35:24.821 -> OK
15:35:24.854 -> at+csim=34,"01A404000CF0706F646773616665320101"
15:35:24.854 -> +CSIM: 4,"9000"
15:35:24.854 ->
15:35:24.854 -> OK
15:35:25.650 -> at+csim=18,"01DA02C10400280019"
15:35:25.650 -> +CSIM: 4,"9000"
15:35:25.650 ->
15:35:25.650 -> OK
15:35:25.650 -> at+csim=10,"0070800100"
15:35:25.650 -> +CSIM: 4,"9000"
15:35:25.650 ->
15:35:25.650 -> OK 

Data Stored

Now we are able to check the data stored by sending a https query to the server. The server responded with the data as below.

[
    {
        "sim_attribute_update_id": 11,
        "iccid": "894450250918638963",
        "deviceid": "51523143572089723526",
        "createdAt": "2020-10-01",
        "updatedAt": "2020-10-01",
        "sim_attributes": [
            {
                "id": 5,
                "sim_attribute_update_id": 11,
                "key": "data",
                "value": "00280019",
                "createdAt": "2020-10-01",
                "updatedAt": "2020-10-01"
            }
        ]
    }
] 

Code

Demo SketchArduino
#include "dht.h"


// Waveshare Sim7000E NB-IoT HAT
#define PIN_DTR  4

// AM2302 (DHT22)
#define PIN_DHT  5

// number of loops to wait for the end of initialization 
#define TECHNOLOGY_INIT_WAITING  7

#define LEN_MODEM_AT  4
const unsigned char MODEM_AT[LEN_MODEM_AT] = {'a', 't', '\r', '\n'};


#define LEN_MODEM_AT_CREG  10
const unsigned char MODEM_AT_CREG[LEN_MODEM_AT_CREG] = {'a', 't', '+', 'c', 'r', 'e', 'g', '?', '\r', '\n'};

#define LEN_MODEM_CREG  8
const char MODEM_CREG[LEN_MODEM_CREG] = {'+', 'C', 'R', 'E', 'G', ':', ' ', '\0'};

#define LEN_MODEM_AT_CSIM  8
const unsigned char MODEM_AT_CSIM[LEN_MODEM_AT_CSIM] = {'a', 't', '+', 'c', 's', 'i', 'm', '='};

#define LEN_MODEM_CSIM  8
const char MODEM_CSIM[LEN_MODEM_CSIM] = {'+', 'C', 'S', 'I', 'M', ':', ' ', '\0'};

#define LEN_APDU_HEADER  5

const char APDU_MANAGE_CHANNEL[LEN_APDU_HEADER] = { 0x00, 0x70, 0x00, 0x00, 0x00 };

const char APDU_SELECT[LEN_APDU_HEADER] = { 0x00, 0xa4, 0x04, 0x00, 0x00 };

#define LEN_APDU_PUT_DATA  5
const char APDU_PUT_DATA[LEN_APDU_PUT_DATA] = { 0x00, 0xda, 0x02, 0xc1, 0x00 };

#define LEN_AID_SAFE2  12
const char AID_SAFE2[LEN_AID_SAFE2] = {0xf0, 0x70, 0x6F, 0x64, 0x67, 0x73, 0x61, 0x66, 0x65, 0x32, 0x01, 0x01 };

#define LEN_MODEM_OK  4
const char MODEM_OK[LEN_MODEM_OK] = {'O', 'K', '\r', '\0'};

#define LEN_MODEM_ERROR  7
const char MODEM_ERROR[LEN_MODEM_ERROR] = {'E', 'R', 'R', 'O', 'R', '\r', '\0'};

#define LEN_1_CHAR_MAX  10
#define LEN_2_CHARS_MAX  100

#define CR   '\r'
#define EOL  '\n'

#define LEN_IO_BUFFER  255
char buf[LEN_IO_BUFFER];

#define LEN_DATA_BUF  4
unsigned char dataBuf[LEN_DATA_BUF];

unsigned char regFlag;

dht sensor;


char registered(char * resp) {
  char b = *resp;
  b &= 0x0f;
  resp++;
  if (*resp != CR) {
    // 2-chars <stat> value
    b *= 10;
    b += *resp - 0x30;
  }
  switch (b) {
    case 1: // 1: registered, home network
    case 5: // 5: registered, roaming
    case 6: // 6: registered for "SMS only", home network (applicable only when <AcTStatus>
            //    indicates E-UTRAN)
    case 7: // 7: registered for "SMS only", roaming (applicable only when <AcTStatus> indicates
            //    E-UTRAN)
    case 9: // 9: registered for "CSFB not preferred", home network (applicable only when
            //    <AcTStatus> indicates E-UTRAN)
    case 10:// 10:registered for "CSFB not preferred", roaming (applicable only when <AcTStatus>
            //    indicates E-UTRAN)
      return true;
    
    case 0: // 0: not registered, the MT is not currently searching a new operator to register to
    case 2: // 2: not registered, but the MT is currently searching a new operator to register to
    case 3: // 3: registration denied
    case 4: // 4: unknown (e.g. out of GERAN/UTRAN/E-UTRAN coverage)
    case 8: // 8: attached for emergency bearer services only (see 3GPP TS 24.008 [12] and 3GPP
            //    TS 24.301 [68] that specify the condition when the MS is considered as attached
            //    for emergency bearer services)
    default:
      return false;
  }
}

void logData(char * dataBuf, char len) {
  int dataLen = (unsigned char)len;
  int blockLen;
  int ofs = 0;
  while (dataLen > 0) {
    blockLen = Serial.availableForWrite();
    if (blockLen < 1) {
      delay(1);
      continue;
    }
    if (blockLen > dataLen) {
      blockLen = dataLen;
    }
    Serial.write(&dataBuf[ofs], blockLen);
    ofs += blockLen;
    dataLen -= blockLen;
  }
  Serial.flush();
}


void atCommand(char * cmdBuf, char len) {
  int cmdLen = (unsigned char)len;
  int blockLen;
  int ofs = 0;
  while (cmdLen > 0) {
    blockLen = Serial1.availableForWrite();
    if (blockLen < 1) {
      delay(1);
      continue;
    }
    if (blockLen > cmdLen) {
      blockLen = cmdLen;
    }
    Serial1.write(&cmdBuf[ofs], blockLen);
    ofs += blockLen;
    cmdLen -= blockLen;
  }
  Serial1.flush();
}

#define CNTR_MAX  10000

char atResponse(char * respBuf) {
  // read AT response
  char b = 0;
  short cntr = 0;
  char * ptr;
  int len = 0;
  int blockLen;
  //delay(10);
  // initialize for timeout
  respBuf[0] = 0;
  do {
    blockLen = Serial1.readBytesUntil(EOL, &respBuf[len], (int)(LEN_IO_BUFFER - len));
    if (blockLen > 0) {
      len += blockLen;
      respBuf[len++] = EOL;
      respBuf[len] = 0;
    }

    ptr = strstr(respBuf, MODEM_OK);
    if (ptr == NULL) {
      ptr = strstr(respBuf, MODEM_ERROR);
    }
    
  } while (ptr == NULL);
  return len;
}

char decimalPut(int v, char * dst) {
  char ofs = 0;
  // number of chars populated
  char cnt = 1;
  char b;
  // safety check - shall be redesigned for negative numbers
  if (v < 0) {
    v = 0;
  }
  if (v >= LEN_1_CHAR_MAX) {
    ofs++;
    cnt++;
  }
  if (v >= LEN_2_CHARS_MAX) {
    ofs++;
    cnt++;
  }
  // at least 1 digit shall be populated
  do {
    b = (char)(v % 10);
    dst[ofs--] = '0' + b;
    v -= b;
    v /= 10;
  } while (v > 0);
  return cnt;
}

const char HEX_CONVERSION[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

void hexPut(char b, char * dst) {
  dst[0] = HEX_CONVERSION[(b >> 4) & 0x0f];
  dst[1] = HEX_CONVERSION[b & 0x0f];
}

#define LEN_BYTE_HEX  2

char buildAtCsim(char * cmdBuf, char cla, const char * apduBuf, char * dataBuf, unsigned char dataLen) {
  int len = LEN_APDU_HEADER; 
  char ofs = 0;
  if (dataBuf != NULL) {
    len += dataLen;
  }
  memcpy(cmdBuf, MODEM_AT_CSIM, LEN_MODEM_AT_CSIM);
  ofs = LEN_MODEM_AT_CSIM;
  ofs += decimalPut(len << 1, &cmdBuf[ofs]);
  cmdBuf[ofs++] = ',';
  cmdBuf[ofs++] = '"';
  
  // put CLA
  hexPut(apduBuf[0] | cla, &cmdBuf[ofs]);
  ofs += LEN_BYTE_HEX;
  // put INS, P1, P2
  for (char i=1; i<4; i++) {
    hexPut(apduBuf[i], &cmdBuf[ofs]);
    ofs += LEN_BYTE_HEX;
  }
  // put LEN
  hexPut(dataLen, &cmdBuf[ofs]);
  ofs += LEN_BYTE_HEX;
  if ((dataLen > 0) && (dataBuf != NULL)) {
    // put Data
    for (char i=0; i<dataLen; i++) {
      hexPut(dataBuf[i], &cmdBuf[ofs]);
      ofs += LEN_BYTE_HEX;
    }
  }
  
  cmdBuf[ofs++] = '"';
  cmdBuf[ofs++] = '\r';
  cmdBuf[ofs++] = '\n';
  cmdBuf[ofs] = 0;
  return ofs;
}

#define ERR_INVALID_CSIM_RESPONSE  0x01
#define ERR_INVALID_OPEN_CHANNEL_RESPONSE  0x02
#define ERR_INVALID_CHANNEL_ID  0x03

#define OFS_APDU_P1  0x02
#define OFS_APDU_P2  0x03

#define MODE_CHANNEL_CLOSE  0x80



char send(unsigned char * dataPtr, char dataLen) {

  char cmdLen = 0;
  char respLen = 0;
  char chan = 0;
  // put AT SIM command: Open Supplementary Logical Channel
  //Serial.println("put 'at+csim: 00 70 00 00 01'");
  cmdLen = buildAtCsim(buf, 0, APDU_MANAGE_CHANNEL, NULL, 1);
  //Serial.println(buf);
  //Serial.flush();
  atCommand(buf, cmdLen);
  respLen = atResponse(buf);
  logData(buf, respLen);
  // analyze response
  char * ptr;
  ptr = strstr(buf, MODEM_CSIM);
  if (ptr == NULL) {
    Serial.println("CSIM not detected in the response");
    return ERR_INVALID_CSIM_RESPONSE;
  }

  if ((ptr[LEN_MODEM_CSIM - 1] != '6') ||
      (ptr[LEN_MODEM_CSIM + 0] != ',') || 
      (ptr[LEN_MODEM_CSIM + 1] != '"') ||
      (ptr[LEN_MODEM_CSIM + 2] != '0')) {

    Serial.println("Unexpected response for MANAGE CHANNEL");
    return ERR_INVALID_OPEN_CHANNEL_RESPONSE;
  }

  // extract channel ID
  chan = ptr[LEN_MODEM_CSIM + 3] - '0';
  if ((chan <= 0) || (chan >= 4)) {
    Serial.print("Unexpected response (chan ID) for MANAGE CHANNEL: ");
    Serial.println(chan);
    return ERR_INVALID_CHANNEL_ID;
  }
  // SELECT (by AID) SAFE2
  cmdLen = buildAtCsim(buf, chan, APDU_SELECT, (char *)AID_SAFE2, LEN_AID_SAFE2);
  //Serial.println(buf);
  //Serial.flush();
  atCommand(buf, cmdLen);
  respLen = atResponse(buf);
  logData(buf, respLen);
  
  // PUT DATA (from dataBuffer)
  cmdLen = buildAtCsim(buf, chan, APDU_PUT_DATA, (char *)dataPtr, dataLen);
  //Serial.println(buf);
  //Serial.flush();
  atCommand(buf, cmdLen);
  respLen = atResponse(buf);
  logData(buf, respLen);
  
  // close supplementary logical channel
  char apdu_close_channel[LEN_APDU_HEADER];
  memcpy(apdu_close_channel, APDU_MANAGE_CHANNEL, LEN_APDU_HEADER);
  apdu_close_channel[OFS_APDU_P1] = MODE_CHANNEL_CLOSE;
  apdu_close_channel[OFS_APDU_P2] = chan;
  cmdLen = buildAtCsim(buf, 0, apdu_close_channel, NULL, 0);
  //Serial.println(buf);
  //Serial.flush();
  atCommand(buf, cmdLen);
  respLen = atResponse(buf);
  logData(buf, respLen);
}


void setup() {
  // configure pins (DTR - mandatory)
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(PIN_DTR, OUTPUT);
  

  // Open serial communications and wait for port to open:
  Serial.begin(57600);
  while (!Serial) {
    ; // wait for serial port to connect
  }
  Serial.println("nano started!");

  // wake up Sim7000 UART
  digitalWrite(PIN_DTR, LOW);   
  delay(1000);

  // set the data rate for the modem port
  Serial1.begin(57600);

  regFlag = false;
  char ready = 0;
  unsigned char b = 0;
  unsigned char pos = 0;
  do {
    // put AT command
    Serial.println("put 'at'");
    Serial1.write(MODEM_AT, LEN_MODEM_AT);
    delay(100);
    // modem could not respond during initialization
    if (Serial1.available()) {
      ready = 1;
      while (Serial1.available()) {
        b = Serial1.read();
        Serial.write(b);
        delay(10);
      }
    }
    delay(300);
  } while (ready == 0);

  unsigned char cntr = 0;
  while (cntr < TECHNOLOGY_INIT_WAITING) {
    // turn the LED on
    digitalWrite(LED_BUILTIN, HIGH);  
    // wait for a half of a second  
    delay(500);                       

    // put AT command
    Serial.println("put 'at+creg?'");
    atCommand(MODEM_AT_CREG, LEN_MODEM_AT_CREG);
    // read AT response
    pos = atResponse(buf);
    
    // turn the LED off by making the voltage LOW
    digitalWrite(LED_BUILTIN, LOW);    

    // analyze response
    char * ptr;
    ptr = strstr(buf, MODEM_CREG);
    if (ptr != NULL) {
      regFlag = registered(&ptr[LEN_MODEM_CREG + 1]);
      Serial.write(ptr[LEN_MODEM_CREG - 1]);
      Serial.write(':');
      Serial.write(ptr[LEN_MODEM_CREG + 1]);
      Serial.println();
    } else {
      Serial.println("not found `+CREG`");
    }
    if (regFlag) {
      cntr++;
    }
    Serial.print("cntr: ");
    Serial.println(cntr);
    // wait 1 sec before repeat
    delay(1000);
  }
  Serial.println("setup finished");
}

void loop() {
  
  if (sensor.read22(PIN_DHT) != DHTLIB_OK) {
  //float h = dht.readHumidity();
  //float t = dht.readTemperature();

  //if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from the sensor");
    delay(10000);
    return;
  }
  float h = sensor.humidity;
  float t = sensor.temperature;
  int ih = round(h);
  int it = round(t);

  Serial.print("Humidity: ");
  Serial.println(ih);
  Serial.print("Temperature: ");
  Serial.println(it);
  Serial.println();

  // put humidity as BigEndian short
  dataBuf[0] = (unsigned char)(ih >> 8);
  dataBuf[1] = (unsigned char)(ih & 0xff);
  // put Temperature as BigEndian short
  dataBuf[2] = (unsigned char)(it >> 8);
  dataBuf[3] = (unsigned char)(it & 0xff);

  // Dump sensor data
  char b;
  Serial.print("HT data:");
  for (byte i = 0; i < LEN_DATA_BUF; i++) {
    b = dataBuf[i];
    if (b < 0x10) {
      Serial.print("0");
    }
    Serial.print(b, HEX);
  } 
  Serial.println();
  
  send(&dataBuf[0], LEN_DATA_BUF);

  unsigned char minutes = 30;
  unsigned char secs;
  while (minutes > 0) {
    for (secs=0; secs < 60; secs++) {
      delay(1000);
    }
    minutes -= 1;
    Serial.print('.');
  }
}

Schematics

Connection schema
Arduino Nano Every + Sim7000E + DHT22
Nano every schema kqlbopqygy

Comments

Similar projects you might like

MQTT Communication With the Nano 33 IoT & WeMos D1 Boards

Project tutorial by BnBe Club

  • 13,115 views
  • 2 comments
  • 23 respects

Telegram-Enabled Thermal Printer w/ Arduino Nano 33 IoT

Project tutorial by Kutluhan Aktar

  • 10,829 views
  • 1 comment
  • 23 respects

Handheld Geiger Counter with Arduino Nano

Project tutorial by Pedro52

  • 5,751 views
  • 6 comments
  • 16 respects

Arduino Nano Easy Sensors LoRa Field Gateway Client

Project tutorial by Bryn Lewis

  • 4,932 views
  • 0 comments
  • 5 respects

Nano Piano

by Christian Madlansacay

  • 2,494 views
  • 0 comments
  • 7 respects
Add projectSign up / Login