Skip to content

Add IoT SAFE support #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ This code needs 2 ~~3~~ external libraries to run, that you can install using th
- [MKRGSM](https://www.arduino.cc/en/Reference/MKRGSM) in order to handle the GSM module on **Arduino MKR GSM 1400**
- [WiFiNINA](https://www.arduino.cc/en/Reference/WiFiNINA) in order to handle WiFi module on **Arduino MKRWIFI 1010** and **Arduino Nano 33 IoT**
- [WiFi101](https://www.arduino.cc/en/Reference/WiFi101) in order to handle WiFi module on **Arduino MKR 1000**
- [ArduinoBearSSL](https://www.arduino.cc/reference/en/libraries/arduinobearssl) in order to handle 2-way MQTTS connection through **IoT SAFE** (disabled by default, must be enabled by defining LIVE_OBJECTS_IOT_SAFE in LiveObjectsConfig.h)

- ~~[ArduinoMqttClient](https://github.com/arduino-libraries/ArduinoMqttClient) that implements a MQTT client for Arduino~~ *

Expand All @@ -42,6 +43,10 @@ This code needs 2 ~~3~~ external libraries to run, that you can install using th
*mandatory for both Arduino, ESP and Adafruit boards
- [ArduinoJson](https://arduinojson.org/), a powerful library used to parse, store and handle JSON easily

#### Library developed by Orange
*mandatory to handle 2-way MQTTS connection through **IoT SAFE** (disabled by default, must be enabled by defining LIVE_OBJECTS_IOT_SAFE)
- [IoT-SAFE-APDU-library](https://github.com/Orange-OpenSource/IoT-SAFE-APDU-library)

#### SAMD21 Arduino core
- You also need to install the Arduino core for Atmel SAMD21 processor, used on the boards of the MKR family. Open the [Boards Manager](https://www.arduino.cc/en/guide/cores) and install the package called "Arduino SAMD Boards (32-bit ARM Cortex-M0+)".

Expand Down
37 changes: 37 additions & 0 deletions examples/7_send_data_iot_safe/7_send_data_iot_safe.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/******************************************************************************
INCLUDES
******************************************************************************/
#include "arduino_secrets.h"
#include <LiveObjects.h>
/******************************************************************************
USER VARIABLES
******************************************************************************/
uint32_t messageRate = 5000; // stores the current data message rate in Milliseconds
unsigned long uptime; // stores the device uptime (sent as fake sensor data)
unsigned long lastMessageTime = 0; // stores the time when last data message was sent

/******************************************************************************
USER PROGRAM
******************************************************************************/
void setup() {
Serial.begin(115200);
Serial.print("\n*** Live Objects for Arduino MKR boards, revision ");
Serial.print(SW_REVISION);
Serial.println("***");
lo.setSecurity(MUTUAL_TLS_WITH_IOT_SAFE);
lo.begin(MQTT, TEXT, true);
lo.connect(); // connects to the network + Live Objects
}

void loop() {
if (millis() - lastMessageTime > messageRate) {
// collect data periodically
Serial.println("Sampling data");
uptime = millis();
lo.addToPayload("uptime", uptime); // adding 'uptime' value to the current payload
Serial.println("Sending data to Live Objects");
lo.sendData(); // send the data to Live Objects
lastMessageTime = millis();
}
lo.loop(); // don't forget to keep this in your main loop
}
28 changes: 28 additions & 0 deletions examples/7_send_data_iot_safe/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Send data to Live Objects thanks to IoT SAFE

This example shows how to send some sample data (device uptime) to Live Objects using Arduino MKR NB 1500.
![diagram](img/send_data_diagram.png)
This example uses the IoT SAFE APDU library to establish a MQTTS connection with a client certificate and a private key securely stored in the (e)SIM.
[2-way authentication](https://liveobjects.orange-business.com/doc/html/lo_manual_v2.html#_go_further_and_use_2_way_authentication) must have been enabled for your API key.
The client certificate embedded in the IoT SAFE applet inside the (e)SIM must have been signed with one of the Certificate Authority associated with your API key.

## Running
First of all, be sure that you installed the required libraries and generated an API key mentioned in the main README file, then:
1. Open "7_send_data.ino" sketch using Arduino IDE
2. Replace ```const char SECRET_LIVEOBJECTS_API_KEY[]="...";``` in arduino_secrets.h with API key you generated
3. Replace ```const char IOT_SAFE_CUSTOM_AID[]="...";``` in arduino_secrets.h with the Application ID of the IoT SAFE applet
4. Replace ```const char IOT_SAFE_CLIENT_CERTIFICATE_FILE_ID[]="...";``` in arduino_secrets.h with the ID of the client certificate file you saved in the IoT SAFE applet.
5. Replace ```const char IOT_SAFE_PRIVATE_KEY_ID[]="...";``` in arduino_secrets.h with the ID of the private key you saved in the IoT SAFE applet.
6. Upload *7_send_data.ino* sketch to your Arduino MKR NB 1500 board


## Verify
**Is device online:**<br>
If all went fine under **devices** tab on Live Live Objects portal you should see online your device identified by its modem IMEI:

![device_online](img/device_online.png)

**Is device sending data:**<br>
Under data tab on Live Objects portal you should see messages sent by your device, along with values *{ "uptime": xxxxx }*

![data_portal](img/data_portal.png)
29 changes: 29 additions & 0 deletions examples/7_send_data_iot_safe/arduino_secrets.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Cellular connection cerdentials, used only for GSM boards
extern const String SECRET_PINNUMBER = ""; // unless PIN is deactivated, specify your SIM card PIN number
extern const String SECRET_APN = ""; // specify the APN name (if needed)
extern const String SECRET_APN_USER = ""; // specify the username for your APN (if needed)
extern const String SECRET_APN_PASS = ""; // specify the password for your APN (if needed)
extern const String SECRET_SERVER_MSISDN = ""; // specify the number of server(gate)

// WIFI connection credentials, used only for WiFi boards
extern const String SECRET_SSID = ""; // unless PIN is deactivated, specify your SIM card PIN number
extern const String SECRET_WIFI_PASS = ""; // specify the APN name (if needed)

// IoT SAFE
// Use a custom AID
extern const uint8_t IOT_SAFE_CUSTOM_AID[] = {
0xA0, 0x00, 0x00, 0x02, 0x48, 0x04, 0x00
};
extern const uint8_t IOT_SAFE_CUSTOM_AID_LEN = sizeof(IOT_SAFE_CUSTOM_AID);
// Define the private key ID inside the IoT SAFE applet
extern const uint8_t IOT_SAFE_PRIVATE_KEY_ID[] = { 0x01 };
extern const uint8_t IOT_SAFE_PRIVATE_KEY_ID_LEN = sizeof(IOT_SAFE_PRIVATE_KEY_ID);
// Define the certificate file ID inside the IoT SAFE applet
extern const uint8_t IOT_SAFE_CLIENT_CERTIFICATE_FILE_ID[] = { 0x02 };
extern const uint8_t IOT_SAFE_CLIENT_CERTIFICATE_FILE_ID_LEN = sizeof(IOT_SAFE_CLIENT_CERTIFICATE_FILE_ID);

// Live Objects credential: paste below your API key (see Configuration > API keys on the portal).
// You API key must have at least the predefined 'MQTT Device' rights profile
// (alternatively: 'Device Access' read + write rights if need to customise the rights).
// Please note that you *must* use a TLS connection (MQTTS) if you grant more rights to the API key.
extern const String SECRET_LIVEOBJECTS_API_KEY = "...";
135 changes: 135 additions & 0 deletions src/IoTSAFE.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Copyright (C) Orange
*
* This software is distributed under the terms and conditions of the 'MIT'
* license which can be found in the file 'LICENSE.md' in this package distribution
*/

#include "LiveObjectsBase.h"

#ifdef LIVE_OBJECTS_IOT_SAFE

#include "IoTSAFE.h"

#define IOT_SAFE_MAX_COMMON_NAME_SIZE 64

IoTSAFE *g_iot_safe_ptr = nullptr;

IoTSAFE::IoTSAFE(const uint8_t* pAID, uint8_t nAIDLength)
:
m_AID(pAID),
m_nAIDLength(nAIDLength),
m_nChannel(0)
{
g_iot_safe_ptr = this;
}

IoTSAFE::~IoTSAFE()
{}

iot_safe_error_t IoTSAFE::init()
{
return iot_safe_init(m_AID, m_nAIDLength, &m_nChannel);
}

void IoTSAFE::finish()
{
iot_safe_finish(m_nChannel);
}

String IoTSAFE::getClientCertificateCommonName()
{
char client_common_name[IOT_SAFE_MAX_COMMON_NAME_SIZE];

memset(client_common_name, 0, sizeof(client_common_name));

for (int i = 5; i < sizeof(m_ClientCertificate); i++) {
// Find common name
if (m_ClientCertificate[i-5] == 0x55 && m_ClientCertificate[i-4] == 0x04 &&
m_ClientCertificate[i-3] == 0x03)
{

if (m_ClientCertificate[i-1] > sizeof(client_common_name))
return String("");

memcpy(client_common_name, &m_ClientCertificate[i],
m_ClientCertificate[i-1]);
}
}
return String(client_common_name);
}

br_x509_certificate IoTSAFE::readClientCertificate()
{
memset(m_ClientCertificate, 0, sizeof(m_ClientCertificate));

if (init() == IOT_SAFE_SUCCESS)
{
iot_safe_read_file(m_nChannel, IOT_SAFE_CLIENT_CERTIFICATE_FILE_ID,
IOT_SAFE_CLIENT_CERTIFICATE_FILE_ID_LEN, NULL, 0,
m_ClientCertificate, sizeof(m_ClientCertificate));

finish();
}

br_x509_certificate br_client_cert =
{ (unsigned char *)m_ClientCertificate,
sizeof(m_ClientCertificate) };
return br_client_cert;
}

size_t IoTSAFE::trampolineSign(const br_ec_impl *pImpl, const br_hash_class *pHF,
const void *pHashValue, const br_ec_private_key *pSk, void *pSig)
{
if (g_iot_safe_ptr)
return g_iot_safe_ptr->sign(pImpl, pHF, pHashValue, pSk, pSig);
}

size_t IoTSAFE::sign(const br_ec_impl *pImpl, const br_hash_class *pHF,
const void *pHashValue, const br_ec_private_key *pSk, void *pSig)
{
iot_safe_hash_t IoTSAFEHash = IOT_SAFE_HASH_SHA_256;
uint8_t nHashSize = (pHF->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK;
uint16_t nSignatureLength = 0;

switch((pHF->desc >> BR_HASHDESC_ID_OFF) & BR_HASHDESC_ID_MASK) {
case 0:
// IoT SAFE does not support none as an hash algorithm
return -1;
case 1:
// IoT SAFE does not support the unsecure md5 hash algorithm
return -1;
case 2:
// IoT SAFE does not support the unsecure sha1 hash algorithm
return -1;
case 3:
// IoT SAFE does not support sha224 hash algorithm
return -1;
case 4:
IoTSAFEHash = IOT_SAFE_HASH_SHA_256;
break;
case 5:
IoTSAFEHash = IOT_SAFE_HASH_SHA_384;
break;
case 6:
IoTSAFEHash = IOT_SAFE_HASH_SHA_512;
break;
default:
// Unknown hash algorithm
return -1;
}

if (init() == IOT_SAFE_SUCCESS)
{
iot_safe_sign(m_nChannel, IOT_SAFE_SIGNATURE_OPERATION_MODE_PAD_AND_SIGN,
IoTSAFEHash, IOT_SAFE_SIGNATURE_ECDSA, IOT_SAFE_PRIVATE_KEY_ID,
IOT_SAFE_PRIVATE_KEY_ID_LEN, NULL, 0,
(uint8_t*)pHashValue, nHashSize, (uint8_t*)pSig, 72, &nSignatureLength);

finish();
}

return br_ecdsa_raw_to_asn1(pSig, nSignatureLength);
}

#endif
45 changes: 45 additions & 0 deletions src/IoTSAFE.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) Orange
*
* This software is distributed under the terms and conditions of the 'MIT'
* license which can be found in the file 'LICENSE.md' in this package distribution
*/
#pragma once

#ifdef LIVE_OBJECTS_IOT_SAFE

#include <iot_safe.h>
#include <ArduinoBearSSL.h>

#define IOT_SAFE_MAX_FILE_SIZE 512

class IoTSAFE
{
public:
IoTSAFE(const uint8_t *pAID=IOT_SAFE_AID,
uint8_t nAIDLength=sizeof(IOT_SAFE_AID));
~IoTSAFE();
br_x509_certificate readClientCertificate();
String getClientCertificateCommonName();
size_t sign(const br_ec_impl *pImpl, const br_hash_class *pHF,
const void *pHashValue, const br_ec_private_key *pSk, void *pSig);
static size_t trampolineSign(const br_ec_impl *pImpl, const br_hash_class *pHF,
const void *pHashValue, const br_ec_private_key *pSk, void *pSig);
private:
iot_safe_error_t init();
void finish();
private:
const uint8_t *m_AID;
uint8_t m_nAIDLength;
uint8_t m_nChannel;
uint8_t m_ClientCertificate[IOT_SAFE_MAX_FILE_SIZE];
};

extern const uint8_t IOT_SAFE_CUSTOM_AID[];
extern const uint8_t IOT_SAFE_CUSTOM_AID_LEN;
extern const uint8_t IOT_SAFE_PRIVATE_KEY_ID[];
extern const uint8_t IOT_SAFE_PRIVATE_KEY_ID_LEN;
extern const uint8_t IOT_SAFE_CLIENT_CERTIFICATE_FILE_ID[];
extern const uint8_t IOT_SAFE_CLIENT_CERTIFICATE_FILE_ID_LEN;

#endif
10 changes: 9 additions & 1 deletion src/LiveObjectsBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
*/
#pragma once

#if defined __has_include
#if __has_include (<LiveObjectsConfig.h>)
#include <LiveObjectsConfig.h>
#endif
#endif

/******************************************************************************
DEFAULT VALUES FOR LIVEOBJECTS
Expand Down Expand Up @@ -101,6 +106,9 @@ enum Security
NONE
,TLS
//,DTLS
#ifdef LIVE_OBJECTS_IOT_SAFE
,MUTUAL_TLS_WITH_IOT_SAFE
#endif
};

enum Encoding
Expand Down Expand Up @@ -422,4 +430,4 @@ void LiveObjectsBase::addToStringPayload(T val, Args ... args)
addToStringPayload(args...);
}

extern const String SECRET_LIVEOBJECTS_API_KEY;
extern const String SECRET_LIVEOBJECTS_API_KEY;
45 changes: 45 additions & 0 deletions src/LiveObjectsCellular.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ LiveObjectsCellular::LiveObjectsCellular()
#ifdef GSMD
,m_GPRSAcces()
#endif
#ifdef LIVE_OBJECTS_IOT_SAFE
,m_IoTSafe(IOT_SAFE_CUSTOM_AID, IOT_SAFE_CUSTOM_AID_LEN)
#endif
{
String num = SECRET_SERVER_MSISDN;
for (int i = 0; i < num.length(); i+=2)
Expand Down Expand Up @@ -47,6 +50,18 @@ void LiveObjectsCellular::begin(Protocol p, Encoding s, bool bDebug)
{
switch(m_Security)
{
#ifdef LIVE_OBJECTS_IOT_SAFE
case MUTUAL_TLS_WITH_IOT_SAFE:
#ifdef NBD
m_pClient = new NBClient();
#elif defined GSMD
m_pClient = new GSMClient();
#endif
m_pBearSSLClient = new BearSSLClient(*m_pClient, TAs, 1);
m_pMqttclient = new MqttClient(m_pBearSSLClient);
m_nPort = 8883;
break;
#endif
case TLS:
#ifdef NBD
m_pClient = new NBSSLClient();
Expand Down Expand Up @@ -81,6 +96,19 @@ void LiveObjectsCellular::begin(Protocol p, Encoding s, bool bDebug)
}
m_bInitialized = true;
}

#ifdef LIVE_OBJECTS_IOT_SAFE
unsigned long LiveObjectsCellular::getTime() {
outputDebug(INFO,"Getting time from the cellular module...");
// get the current time from the cellular module
return m_Acces.getTime();
}

unsigned long LiveObjectsCellular::trampolineGetTime() {
return LiveObjects::get().getTime();
}
#endif

void LiveObjectsCellular::connectNetwork()
{
//Set client id as IMEI
Expand All @@ -102,6 +130,23 @@ void LiveObjectsCellular::connectNetwork()
#endif
if(modem.begin())
{
#ifdef LIVE_OBJECTS_IOT_SAFE
if (m_Security == MUTUAL_TLS_WITH_IOT_SAFE)
{
// Set a callback to get the current time used to validate the servers certificate
ArduinoBearSSL.onGetTime(trampolineGetTime);

// Wait a little before sending command to applet as we're getting strange
// behavior otherwise (i.e. an empty file is returned)
delay(2000);

br_x509_certificate client_certificate = m_IoTSafe.readClientCertificate();
m_pBearSSLClient->setEccCert(client_certificate);
m_sMqttid = m_IoTSafe.getClientCertificateCommonName();
outputDebug(INFO,"m_sMqttid retrieved from client certificate: ", m_sMqttid);
m_pBearSSLClient->setEccSign(m_IoTSafe.trampolineSign);
}
#endif
if(m_sMqttid.length()==0)
{
String imei="";
Expand Down
Loading