MAXREFDES143#: DeepCover Embedded Security in IoT Authenticated Sensing & Notification

Dependencies:   MaximInterface mbed

The MAXREFDES143# is an Internet of Things (IoT) embedded security reference design, built to protect an industrial sensing node by means of authentication and notification to a web server. The hardware includes a peripheral module representing a protected sensor node monitoring operating temperature and remaining life of a filter (simulated through ambient light sensing) and an mbed shield representing a controller node responsible for monitoring one or more sensor nodes. The design is hierarchical with each controller node communicating data from connected sensor nodes to a web server that maintains a centralized log and dispatches notifications as necessary. The mbed shield contains a Wi-Fi module, a DS2465 coprocessor with 1-Wire® master function, an LCD, LEDs, and pushbuttons. The protected sensor node contains a DS28E15 authenticator, a DS7505 temperature sensor, and a MAX44009 light sensor. The mbed shield communicates to a web server by the onboard Wi-Fi module and to the protected sensor node with I2C and 1-Wire. The MAXREFDES143# is equipped with a standard shield connector for immediate testing using an mbed board such as the MAX32600MBED#. The simplicity of this design enables rapid integration into any star-topology IoT network requiring the heightened security with low overhead provided by the SHA-256 symmetric-key algorithm.

More information about the MAXREFDES143# is available on the Maxim Integrated website.

Revision:
32:0a09505a656d
Parent:
29:590a7561318b
--- a/main.cpp	Tue Apr 04 14:10:48 2017 -0500
+++ b/main.cpp	Mon Nov 06 17:34:13 2017 -0600
@@ -28,278 +28,255 @@
 * trademarks, maskwork rights, or any other form of intellectual
 * property whatsoever. Maxim Integrated Products, Inc. retains all
 * ownership rights.
-*******************************************************************************
-*/
+*******************************************************************************/
 
 #include <sstream>
-
+#include <mbed.h>
+#include <MaximInterface/Devices/DS2465.hpp>
+#include <MaximInterface/Platforms/mbed/I2CMaster.hpp>
+#include <MaximInterface/Platforms/mbed/Sleep.hpp>
+#include <MaximInterface/Utilities/RomId.hpp>
 #include "SensorData.hpp"
 #include "WebServerInterface.hpp"
 #include "Factory.hpp"
 #include "SensorNode.hpp"
-#include "Masters/DS2465/DS2465.h"
 #include "Display.hpp"
-#include "RomId/RomId.h"
 #include "ESP8266.hpp"
-#include "mbed.h"
 
-using OneWire::RomId;
-using OneWire::DS2465;
+using namespace MaximInterface;
 
 /// Main status for the program.
-enum Status
-{
-  InitializingController, ///< Configure DS2465 and connect to network.
-  DisplaySessionId, ///< Display ID for use with website.
+enum Status {
+  InitializingController,   ///< Configure DS2465 and connect to network.
+  DisplaySessionId,         ///< Display ID for use with website.
   SensorNodeNeedsDetection, ///< Prompt user to insert Sensor Node.
-  DetectingSensorNode, ///< Check if Sensor Node present.
+  DetectingSensorNode,      ///< Check if Sensor Node present.
   SensorNodeNeedsProvision, ///< Sensor Node needs to be provisioned.
-  ProvisioningSensorNode, ///< Provisioning Sensor Node to factory defaults.
-  NormalOperation, ///< The normal demo operation state.
-  SensorNodeNotAuthentic, ///< Sensor Node failed authentication check.
+  ProvisioningSensorNode,   ///< Provisioning Sensor Node to factory defaults.
+  NormalOperation,          ///< The normal demo operation state.
+  SensorNodeNotAuthentic,   ///< Sensor Node failed authentication check.
   ControllerInitializationError, ///< Failed to initialize Controller.
-  ControllerHardwareError, ///< Controller hardware failed unexpectedly.
-  SensorNodeHardwareError ///< Sensor Node hardware failed unexpectedly.
+  ControllerHardwareError,       ///< Controller hardware failed unexpectedly.
+  SensorNodeHardwareError        ///< Sensor Node hardware failed unexpectedly.
 };
 
 /// @{
 /// Configuration options.
-static const unsigned int webPostIntervalMs = 10000;
-static const unsigned int webPostRetryIntervalMs = 1000;
-static const uint8_t maxConsecutiveWebPostErrors = 3;
+static const int webPostIntervalMs = 10000;
+static const int webPostRetryIntervalMs = 1000;
+static const int maxConsecutiveWebPostErrors = 3;
 /// @}
 
 /// @{
 /// LCD display colors.
-static const Display::Color Teal = { 0x00, 0xB2, 0xA9 };
-static const Display::Color Red = { 0xFF, 0x00, 0x00 };
-static const Display::Color Green = { 0x00, 0xFF, 0x00 };
-static const Display::Color Purple = { 0x6E, 0x25, 0x85 };
+static const Display::Color Teal = {0x00, 0xB2, 0xA9};
+static const Display::Color Red = {0xFF, 0x00, 0x00};
+static const Display::Color Green = {0x00, 0xFF, 0x00};
+static const Display::Color Purple = {0x6E, 0x25, 0x85};
 /// @}
 
 /// @{
 /// Peripheral and pin definitions
-static Serial pc(USBTX, USBRX);
 static DigitalIn provisionButton(D13);
 static DigitalIn invalidateButton(D5);
 static DigitalOut tempAlarmLed(D11, 1);
 static DigitalOut filterLifeAlarmLed(D10, 1);
 static I2C i2c(D14, D15);
 static Display lcd(i2c, 0x78, 0x98);
-static DS2465 ds2465(i2c, 0x30);
+static MaximInterface::mbed::I2CMaster i2cWrapper(i2c);
+static DS2465 ds2465(MaximInterface::mbed::Sleep::instance(), i2cWrapper, 0x30);
 static SensorNode sensorNode(i2c, 0x90, 0x94, ds2465);
 static ESP8266 esp8266(D1, D0, D2, D3, 38400);
 static WebServerInterface webIntf(esp8266);
 /// @}
 
-static bool useInvalidSecret = false; ///< Imitate an invalid controller when posting to web server.
+/// Imitate an invalid controller when posting to web server.
+static bool useInvalidSecret = false;
 static unsigned int randomSeed = 0; ///< Create extra entropy for challenge.
 static Status currentStatus = InitializingController;
 static bool result = false;
-static uint8_t consecutiveWebPostErrors = 0; ///< Account for a few network errors in case of flaky connection.
+/// Account for a few network errors in case of flaky connection.
+static uint8_t consecutiveWebPostErrors = 0; 
 static Timer webPostTimer; ///< Software timer to track web posting interval.
-static Timer retryTimer; ///< Software timer to track authentication retries.
+static Timer retryTimer;   ///< Software timer to track authentication retries.
 
-static void blinkLeds(unsigned int time_ms); ///< Invert LEDs for a given amount of time.
-static bool buttonPressed(DigitalIn & button); ///< Checks if button is pressed (returns true) and waits for release.
-static void displayStatus(Status status); ///< Display status message on LCD.
-static void displaySensorData(const SensorData & sensorData); ///< Display sensor data on the LCD.
-static bool readWebSessionId(OneWire::RomId & sessionId); ///< Read device's web session ID from it's nonvolatile storage.
+/// Invert LEDs for a given amount of time.
+static void blinkLeds(int time_ms);
+/// Checks if button is pressed (returns true) and waits for release.
+static bool buttonPressed(DigitalIn & button); 
+/// Display status message on LCD.
+static void displayStatus(Status status); 
+/// Display sensor data on the LCD.
+static void displaySensorData(const SensorData & sensorData); 
+/// Read device's web session ID from it's nonvolatile storage.
+static bool readWebSessionId(RomId & sessionId); 
 
 #ifdef ASSEMBLY_TEST
-#include "AssemblyTest.cpp"
+#include "AssemblyTest.hpp"
 #endif
 
-int main()
-{
+int main() {
   blinkLeds(500);
-  
+
 #ifdef ASSEMBLY_TEST
   assemblyTest();
 #endif
-  
-  while (true)
-  {
+
+  while (true) {
     Status nextStatus = currentStatus;
-    switch (currentStatus)
-    {
+    switch (currentStatus) {
     case InitializingController:
-      pc.baud(115200);
       i2c.frequency(100000);
       webPostTimer.start();
-      
+
       // Set initial LCD state
       lcd.initialize();
       displayStatus(currentStatus);
-      
+
       // Connect to Wifi network
       result = webIntf.initialize();
-      
+
       // Read session ID
-      if (result)
-      {
-        OneWire::RomId sessionId;
+      if (result) {
+        RomId sessionId;
         result = readWebSessionId(sessionId);
-        if (result)
-        {
-            webIntf.setSessionId(sessionId);
+        if (result) {
+          webIntf.setSessionId(sessionId);
         }
       }
-      
+
       // Provision DS2465 with master secret and page data
-      if (result)
-      {
+      if (result) {
         result = provisionCoprocessor(ds2465);
       }
-      
-      if (result)
-      {
+
+      if (result) {
         nextStatus = DisplaySessionId;
-      }
-      else
-      {
+      } else {
         nextStatus = ControllerInitializationError;
       }
       break;
-      
+
     case DisplaySessionId:
       // Wait for user to press Provision button
-      if (buttonPressed(provisionButton))
-      {
+      if (buttonPressed(provisionButton)) {
         nextStatus = SensorNodeNeedsDetection;
       }
       break;
-      
+
     case SensorNodeNeedsDetection:
       // Wait for user to press Provision button
-      if (buttonPressed(provisionButton))
-      {
+      if (buttonPressed(provisionButton)) {
         nextStatus = DetectingSensorNode;
       }
       break;
-      
+
     case DetectingSensorNode:
       // Perform Sensor Node detection sequence
-      switch (sensorNode.detect(randomSeed))
-      {
+      switch (sensorNode.detect(randomSeed)) {
       case SensorNode::UnableToCommunicate:
       default:
         nextStatus = SensorNodeHardwareError;
         break;
-        
+
       case SensorNode::NotProvisioned:
         nextStatus = SensorNodeNeedsProvision;
         break;
-        
+
       case SensorNode::NotAuthentic:
         nextStatus = SensorNodeNotAuthentic;
         break;
-        
+
       case SensorNode::Authentic:
         nextStatus = NormalOperation;
         break;
       }
       break;
-      
+
     case SensorNodeNeedsProvision:
       // Wait for user to press Provision button
-      if (buttonPressed(provisionButton))
-      {
+      if (buttonPressed(provisionButton)) {
         nextStatus = ProvisioningSensorNode;
       }
       break;
-      
-    case ProvisioningSensorNode:      
-      if (!buttonPressed(invalidateButton)) // Provision normally
-      {
-        if (provisionSensorNode(sensorNode, true))
-        {
+
+    case ProvisioningSensorNode:
+      if (!buttonPressed(invalidateButton)) { // Provision normally
+        if (provisionSensorNode(sensorNode, true)) {
           nextStatus = NormalOperation;
-        }
-        else
-        {
+        } else {
           nextStatus = SensorNodeNotAuthentic;
         }
-      }
-      else // Invalidate button also pressed; Load invalid secret
-      {        
+      } else { // Invalidate button also pressed; Load invalid secret
         // Provision with invalid secret
-        if (provisionSensorNode(sensorNode, false))
-        {
+        if (provisionSensorNode(sensorNode, false)) {
           nextStatus = NormalOperation;
-        }
-        else
-        {
+        } else {
           nextStatus = SensorNodeHardwareError;
         }
       }
       break;
-      
-    case NormalOperation:      
+
+    case NormalOperation:
       // Check if user pressed Provision button
-      if (buttonPressed(provisionButton))
-      {
+      if (buttonPressed(provisionButton)) {
         // Re-provision Sensor Node
         nextStatus = ProvisioningSensorNode;
       }
       // Check if user pressed Invalidate button
-      else if (buttonPressed(invalidateButton))
-      {
+      else if (buttonPressed(invalidateButton)) {
         // Toggle between using valid and invalid secret
         // 1 blink = invalid; 2 blinks = valid
         useInvalidSecret = !useInvalidSecret;
         blinkLeds(100);
-        if (!useInvalidSecret)
-        {
+        if (!useInvalidSecret) {
           wait_ms(100);
           blinkLeds(100);
         }
       }
       // Check node and display measurements
-      else
-      {
+      else {
         SensorData sensorData;
         // Read sensor data with authentication
-        switch (sensorNode.authenticatedReadSensorData(randomSeed, sensorData))
-        {
+        switch (
+            sensorNode.authenticatedReadSensorData(randomSeed, sensorData)) {
         case SensorNode::Authentic:
           // Update measurements on LCD
           displaySensorData(sensorData);
-          
+
           // Update alarm LEDs
-          tempAlarmLed = !sensorData.tempAlarm(); // Active Low
+          tempAlarmLed = !sensorData.tempAlarm();             // Active Low
           filterLifeAlarmLed = !sensorData.filterLifeAlarm(); // Active Low
-          
+
           // Send measurements to web if time interval reached
-          if (static_cast<unsigned int>(webPostTimer.read_ms()) >= webPostIntervalMs)
-          {
+          if (webPostTimer.read_ms() >= webPostIntervalMs) {
             // Format, sign, and transmit data to web server
-            result = webIntf.authPostHttpEvent(ds2465, SensorDataEvent, WebServerInterface::formatSensorDataPostBody(sensorData), !useInvalidSecret);
-            if (result)
-            {
+            result = webIntf.authPostHttpEvent(
+                ds2465, SensorDataEvent,
+                WebServerInterface::formatSensorDataPostBody(sensorData),
+                !useInvalidSecret);
+            if (result) {
               // Reset timer count after logging sample complete
               webPostTimer.reset();
               consecutiveWebPostErrors = 0;
             }
             // There was likely an error establishing a web connection
-            else if (++consecutiveWebPostErrors < maxConsecutiveWebPostErrors)
-            {
+            else if (++consecutiveWebPostErrors < maxConsecutiveWebPostErrors) {
               // Wait and try again
               wait_ms(webPostRetryIntervalMs);
             }
             // Too many retry attempts
-            else
-            {
+            else {
               // Assume we have lost network connection
               nextStatus = ControllerHardwareError;
             }
           }
           break;
-          
+
         case SensorNode::NotAuthentic:
           nextStatus = SensorNodeNotAuthentic;
           break;
-          
+
         case SensorNode::UnableToCommunicate:
         default:
           nextStatus = SensorNodeHardwareError;
@@ -307,51 +284,45 @@
         }
       }
       break;
-      
+
     case SensorNodeNotAuthentic:
       // Wait for some time before retrying authentication
       retryTimer.reset();
       retryTimer.start();
-      do
-      {
+      do {
         // Wait for user to press Provision button
-        if (buttonPressed(provisionButton))
-        {
+        if (buttonPressed(provisionButton)) {
           nextStatus = ProvisioningSensorNode;
           break;
         }
         // Try to authenticate and return to normal operation
-        else if (static_cast<unsigned int>(webPostTimer.read_ms()) >= webPostIntervalMs)
-        {
+        else if (webPostTimer.read_ms() >= webPostIntervalMs) {
           // Send event message to server
-          result = webIntf.authPostHttpEvent(ds2465, InvalidSensorEvent, "", !useInvalidSecret);
-          if (result)
-          {
+          result = webIntf.authPostHttpEvent(ds2465, InvalidSensorEvent, "",
+                                             !useInvalidSecret);
+          if (result) {
             // Reset timer count after logging complete
             webPostTimer.reset();
             consecutiveWebPostErrors = 0;
-            
+
             // Try to authenticate again
             nextStatus = SensorNodeNeedsDetection;
-          }
-          else if (++consecutiveWebPostErrors < maxConsecutiveWebPostErrors)
-          {
+          } else if (++consecutiveWebPostErrors < maxConsecutiveWebPostErrors) {
             // There was likely an error establishing a web connection
             // Wait and try again
             wait_ms(webPostRetryIntervalMs);
           }
           // Too many retry attempts
-          else
-          {
+          else {
             // Assume we have lost network connection
             nextStatus = ControllerHardwareError;
             break;
           }
         }
-      } while (static_cast<unsigned int>(retryTimer.read_ms()) < webPostIntervalMs);
+      } while (retryTimer.read_ms() < webPostIntervalMs);
       retryTimer.stop();
       break;
-      
+
     case ControllerInitializationError:
     case ControllerHardwareError:
     case SensorNodeHardwareError:
@@ -360,12 +331,11 @@
       break;
     }
     // Check if status changed
-    if (currentStatus != nextStatus)
-    {
+    if (currentStatus != nextStatus) {
       currentStatus = nextStatus;
       displayStatus(currentStatus); // Display status message on LCD
     }
-    
+
     // Change seed value on every loop pass
     randomSeed++;
   }
@@ -373,8 +343,7 @@
 
 /// Blink all LEDs for a certain amount of time.
 /// @param time_ms Time in ms to blink for.
-static void blinkLeds(unsigned int time_ms)
-{
+static void blinkLeds(int time_ms) {
   tempAlarmLed = !tempAlarmLed;
   filterLifeAlarmLed = !filterLifeAlarmLed;
   wait_ms(time_ms);
@@ -385,12 +354,11 @@
 /// Check if a button is pressed and wait for it to be release.
 /// @param button Active low button to check.
 /// @returns True if pressed.
-static bool buttonPressed(DigitalIn & button)
-{
+static bool buttonPressed(DigitalIn & button) {
   const int buttonPressed = 0; // Active low
-  if (button == buttonPressed)
-  {
-    while (button == buttonPressed) ;
+  if (button == buttonPressed) {
+    while (button == buttonPressed)
+      ;
     return true;
   }
   // else
@@ -398,60 +366,58 @@
 }
 
 /// Display the current status of the Controller on the LCD display.
-static void displayStatus(Status status)
-{
-  switch (status)
-  {
+static void displayStatus(Status status) {
+  switch (status) {
   case InitializingController:
     lcd.writeMessage("Initializing Controller...");
     lcd.setBackLightColor(Teal);
     break;
-    
+
   case DisplaySessionId:
     lcd.writeLine("ID: " + webIntf.sessionIdString(), Display::FirstLine);
     lcd.writeLine("Provision to begin", Display::SecondLine);
     lcd.setBackLightColor(Teal);
     break;
-    
+
   case SensorNodeNeedsDetection:
     lcd.writeMessage("Insert Sensor Node and press Provision");
     lcd.setBackLightColor(Teal);
     break;
-    
+
   case DetectingSensorNode:
     lcd.writeMessage("Detecting Sensor Node...");
     lcd.setBackLightColor(Teal);
     break;
-    
+
   case SensorNodeNeedsProvision:
     lcd.writeMessage("Sensor Node Needs Provision");
     lcd.setBackLightColor(Teal);
     break;
-    
+
   case ProvisioningSensorNode:
     lcd.writeMessage("Provisioning Sensor Node");
     lcd.setBackLightColor(Teal);
     break;
-    
+
   case NormalOperation:
     // Everything handled in displaySensorData()
     break;
-    
+
   case SensorNodeNotAuthentic:
     lcd.writeMessage("Sensor Node Not Authentic");
     lcd.setBackLightColor(Purple);
     break;
-    
+
   case ControllerInitializationError:
     lcd.writeMessage("Initialization Error Check Wi-Fi");
     lcd.setBackLightColor(Red);
     break;
-    
+
   case ControllerHardwareError:
     lcd.writeMessage("Controller Hardware Error: Check Wi-Fi");
     lcd.setBackLightColor(Red);
     break;
-    
+
   case SensorNodeHardwareError:
     lcd.writeMessage("Sensor Node Hardware Error");
     lcd.setBackLightColor(Red);
@@ -460,13 +426,13 @@
 }
 
 /// Display sensor data on the LCD display during normal operation.
-static void displaySensorData(const SensorData & sensorData)
-{ 
+static void displaySensorData(const SensorData & sensorData) {
   std::ostringstream stream;
   stream << "Chiller Temp: " << static_cast<int>(sensorData.temp) << "C";
   lcd.writeCompleteLine(stream.str(), Display::FirstLine);
   stream.str(""); // Clear stream
-  stream << "Filter Life: " << static_cast<unsigned int>(sensorData.filterLife) << "%";
+  stream << "Filter Life: " << static_cast<unsigned int>(sensorData.filterLife)
+         << "%";
   lcd.writeCompleteLine(stream.str(), Display::SecondLine);
   lcd.setBackLightColor(Green);
 }
@@ -475,20 +441,20 @@
 /// @note Session ID is taken from the ROM ID of the MAX66242.
 /// @param[out] Session ID string.
 /// @returns True on success.
-static bool readWebSessionId(OneWire::RomId & sessionId)
-{  
+static bool readWebSessionId(RomId & sessionId) {
   const uint8_t I2C_address = 0x32;
   const uint8_t ROM_address = 0x68;
   RomId romId;
-  
+
   // Set register pointer
   if (i2c.write(I2C_address, reinterpret_cast<const char *>(&ROM_address), 1) != 0)
     return false;
   // Read ROM ID
-  if (i2c.read(I2C_address, reinterpret_cast<char *>(romId.buffer.data()), romId.buffer.size()) != 0)
+  if (i2c.read(I2C_address, reinterpret_cast<char *>(romId.data()),
+               romId.size()) != 0)
     return false;
   // Check if CRC valid
-  if (!romId.valid())
+  if (!valid(romId))
     return false;
   sessionId = romId;
   return true;