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:
31:7b10bcb3e0fc
--- a/WebServerInterface.cpp	Tue Apr 04 14:10:48 2017 -0500
+++ b/WebServerInterface.cpp	Mon Nov 06 17:34:13 2017 -0600
@@ -28,26 +28,26 @@
 * trademarks, maskwork rights, or any other form of intellectual
 * property whatsoever. Maxim Integrated Products, Inc. retains all
 * ownership rights.
-*******************************************************************************
-*/
+*******************************************************************************/
 
 #include <vector>
+#include <Serial.h>
+#include <wait_api.h>
+#include <MaximInterface/Devices/DS2465.hpp>
+#include <MaximInterface/Utilities/HexConversions.hpp>
 #include "WebServerInterface.hpp"
 #include "ESP8266.hpp"
-#include "Slaves/Authenticators/ISha256MacCoproc.h"
 #include "SensorData.hpp"
-#include "HexConversions.hpp"
-#include "Serial.h"
-#include "wait_api.h"
 
-using OneWire::ISha256MacCoproc;
+using namespace MaximInterface;
 
 const char WebServerInterface::wifiSsid[] = "WifiSsid";
 const char WebServerInterface::wifiPassword[] = "WifiPassword";
 const char WebServerInterface::serverAddress[] = "www.maxim-security.com";
 const unsigned int WebServerInterface::serverPort = 80;
 const char WebServerInterface::serverPostPath[] = "/maxrefdes143/post.php";
-const char WebServerInterface::serverChallengePath[] = "/maxrefdes143/challenge.php";
+const char WebServerInterface::serverChallengePath[] =
+    "/maxrefdes143/challenge.php";
 
 // HTTP formatting constants
 static const char keyValSeparator = '=';
@@ -61,32 +61,27 @@
 
 /// Select the Transport Secret for the web server in the Controller.
 /// @returns True on success.
-static bool setHttpPostSecret(ISha256MacCoproc & MacCoproc, const OneWire::RomId & sessionId)
-{
-  ISha256MacCoproc::DevicePage fillData;
-  fillData.fill(defaultPaddingByte);
-  ISha256MacCoproc::SlaveSecretData secretData;
-  secretData.fill(defaultPaddingByte);
-  std::copy(sessionId.buffer.begin(), sessionId.buffer.end(), secretData.begin());
-  return (MacCoproc.computeSlaveSecret(fillData, fillData, secretData) == ISha256MacCoproc::Success);
+static bool setHttpPostSecret(DS2465 & macCoproc, const RomId & sessionId) {
+  Sha256::SlaveSecretData data;
+  data.fill(defaultPaddingByte);
+  std::copy(sessionId.begin(), sessionId.end(), data.begin() + 64);
+  return !macCoproc.computeSlaveSecret(data);
 }
 
-bool WebServerInterface::initialize()
-{
+bool WebServerInterface::initialize() {
   esp8266.setPowered(true);
   esp8266.reset();
   bool result = (esp8266.performSelfTest() == ESP8266::AT_OK);
-  if (result)
-  {
-    result = (esp8266.setCurrentWifiMode(ESP8266::softAP_station_mode) == ESP8266::AT_OK);
+  if (result) {
+    result = (esp8266.setCurrentWifiMode(ESP8266::softAP_station_mode) ==
+              ESP8266::AT_OK);
   }
-  if (result)
-  {
+  if (result) {
     result = (esp8266.setMaxRFTXPower(10) == ESP8266::AT_OK);
   }
-  if (result)
-  {
-    result = (esp8266.joinCurrentAccessPoint(wifiSsid, wifiPassword) == ESP8266::AT_OK);
+  if (result) {
+    result = (esp8266.joinCurrentAccessPoint(wifiSsid, wifiPassword) ==
+              ESP8266::AT_OK);
   }
   return result;
 }
@@ -96,8 +91,9 @@
 /// @param path Web server location to retrieve.
 /// @param sessionId Session ID used to identify this controller.
 /// @returns GET request string.
-static std::string formatHttpGet(const std::string & host, const std::string & path, const std::string & sessionId)
-{
+static std::string formatHttpGet(const std::string & host,
+                                 const std::string & path,
+                                 const std::string & sessionId) {
   std::ostringstream httpGetStream;
   httpGetStream << "GET " << path;
   if (sessionId.length() > 0)
@@ -109,32 +105,35 @@
 }
 
 /// Computes a MAC using the Transport Secret to sign HTTP POST requests.
-/// @param macCoproc Coprocessor such as the DS2465 used to calculate the authentication MAC.
+/// @param macCoproc
+/// Coprocessor such as the DS2465 used to calculate the authentication MAC.
 /// @param input Message array used for MAC calculation.
 /// @param ilen Length of array input.
 /// @param output Calculated MAC output.
-static void calculateHttpPostMac(const ISha256MacCoproc & macCoproc, const uint8_t * input, size_t ilen, ISha256MacCoproc::Mac & output)
-{
-  ISha256MacCoproc::DeviceScratchpad block;
-  size_t index = 0;
-  ISha256MacCoproc::AuthMacData padding;
-  padding.fill(defaultPaddingByte);
+static void calculateHttpPostMac(const DS2465 & macCoproc,
+                                 const uint8_t * input, size_t ilen,
+                                 Sha256::Hash & output) {
+  const size_t blockSize = 32;
   output.fill(defaultPaddingByte); // Set initial hash value
-  while (index < ilen)
-  {
-    if ((index + block.size()) <= ilen) // Full block
+  Sha256::AuthMacData macData;
+  macData.fill(defaultPaddingByte);
+  while (ilen > 0) {
+    Sha256::AuthMacData::iterator macDataBegin =
+        std::copy(output.begin(), output.end(), macData.begin());
+    if (ilen >= blockSize) // Full block
     {
-      std::memcpy(block.data(), &input[index], block.size());
-      index += block.size();
-    }
-    else // Partial block with padding
+      std::copy(input, input + blockSize, macDataBegin);
+      input += blockSize;
+      ilen -= blockSize;
+    } else // Partial block with padding
     {
-      std::memcpy(block.data(), &input[index], ilen - index);
-      std::memset(&block[ilen - index], defaultPaddingByte, block.size() - (ilen - index));
-      index = ilen;
+      macDataBegin = std::copy(input, input + ilen, macDataBegin);
+      std::fill(macDataBegin, macDataBegin + (blockSize - ilen),
+                defaultPaddingByte);
+      ilen = 0;
     }
     // Write data to coprocessor and hash block
-    macCoproc.computeAuthMac(output, block, padding, output);
+    macCoproc.computeAuthMac(macData, output);
   }
 }
 
@@ -142,68 +141,70 @@
 /// @param host Web server address.
 /// @param path Web server location to receive POST.
 /// @param sessionId Session ID used to identify this Controller.
-/// @param macCoproc Coprocessor such as the DS2465 used to calculate the authentication MAC.
+/// @param macCoproc
+/// Coprocessor such as the DS2465 used to calculate the authentication MAC.
 /// @param event Event message type.
 /// @param initialPostBody Message body as determined by the event message type.
-/// @param challenge Challenge previously received from web server for use in authentication MAC.
+/// @param challenge
+/// Challenge previously received from web server for use in authentication MAC.
 /// @returns POST request string.
-static std::string formatHttpPost(const std::string & host, const std::string & path, const std::string & sessionId,
-                                  const ISha256MacCoproc & macCoproc, PostEvent event, const std::string & initialPostBody,
-                                  const uint8_t (&challenge)[challengeLen])
-{
+static std::string formatHttpPost(const std::string & host,
+                                  const std::string & path,
+                                  const std::string & sessionId,
+                                  const DS2465 & macCoproc, PostEvent event,
+                                  const std::string & initialPostBody,
+                                  const uint8_t (&challenge)[challengeLen]) {
   const size_t headerReserve = 115, bodyReserve = 200;
-  
+
   std::string httpPost;
   httpPost.reserve(initialPostBody.length() + headerReserve + bodyReserve);
-  
+
   // Add session ID to post body
-  if (sessionId.length() > 0)
-  {
+  if (sessionId.length() > 0) {
     httpPost += sessionIdKey;
     httpPost += keyValSeparator;
     httpPost += sessionId;
   }
-  
+
   // Add event to post body
   std::string eventString;
-  switch (event)
-  {
+  switch (event) {
   case SensorDataEvent:
     eventString = "SensorData";
     break;
-    
+
   case InvalidSensorEvent:
     eventString = "InvalidSensor";
     break;
   }
-  if (eventString.length() > 0)
-  {
+  if (eventString.length() > 0) {
     if (httpPost.length() > 0)
       httpPost += fieldSeparator;
     httpPost += "Event";
     httpPost += keyValSeparator;
     httpPost += eventString;
   }
-  
+
   // Add initial post body
-  if (initialPostBody.length() > 0)
-  {
+  if (initialPostBody.length() > 0) {
     if (httpPost.length() > 0)
       httpPost += fieldSeparator;
     httpPost += initialPostBody;
   }
-  
+
   // Combine initial post body with initial secret and hash
   std::vector<uint8_t> hashInput;
   hashInput.reserve(challengeLen + httpPost.length());
   hashInput.assign(challenge, challenge + challengeLen);
   hashInput.insert(hashInput.end(), httpPost.begin(), httpPost.end());
-  ISha256MacCoproc::Mac mac;
+  Sha256::Hash mac;
   calculateHttpPostMac(macCoproc, &hashInput[0], hashInput.size(), mac);
-  
+
   char contentLen[5];
-  snprintf(contentLen, sizeof(contentLen) / sizeof(contentLen[0]), "%u", (hashInput.size() - challengeLen) + (mac.size() * charsPerByte) + 5 /* &MAC= */);
-  
+  snprintf(contentLen, sizeof(contentLen) / sizeof(contentLen[0]), "%u",
+           (hashInput.size() - challengeLen) + (mac.size() * 2) +
+               5 /* &MAC= */);
+
   // Construct full post request
   httpPost = "";
   httpPost += "POST ";
@@ -222,56 +223,54 @@
   httpPost += newline;
   httpPost += newline;
   // Add post body
-  httpPost.append(reinterpret_cast<char *>(&hashInput[challengeLen]), hashInput.size() - challengeLen);
+  httpPost.append(reinterpret_cast<char *>(&hashInput[challengeLen]),
+                  hashInput.size() - challengeLen);
   // Convert hash to hex string and add to post body
   httpPost += fieldSeparator;
   httpPost += "MAC";
   httpPost += keyValSeparator;
-  byteArrayToHexString(mac.data(), mac.size(), httpPost);
+  httpPost += byteArrayToHexString(mac.data(), mac.size());
   httpPost += newline;
-  
+
   return httpPost;
 }
 
-bool WebServerInterface::authPostHttpEvent(ISha256MacCoproc & macCoproc, PostEvent event, const std::string & postData, bool setSecret)
-{
+bool WebServerInterface::authPostHttpEvent(DS2465 & macCoproc, PostEvent event,
+                                           const std::string & postData,
+                                           bool setSecret) {
   const std::string challengeSearch(newline + newline);
   bool result;
   uint8_t challenge[challengeLen];
   std::string response;
-  
+
   std::memset(challenge, defaultPaddingByte, challengeLen);
   response.reserve(300);
-  
-  if (setSecret)
-  {
+
+  if (setSecret) {
     result = setHttpPostSecret(macCoproc, m_sessionId);
     if (!result)
       return result;
   }
-  
+
   // Open connection
   esp8266.clearRecvData(); // Clear received data buffer
-  result = (esp8266.openConnection(ESP8266::TCP, serverAddress, 80) == ESP8266::AT_OK);
-  if (result)
-  {
+  result = (esp8266.openConnection(ESP8266::TCP, serverAddress, 80) ==
+            ESP8266::AT_OK);
+  if (result) {
     // Request challenge
-    result = (esp8266.sendData(formatHttpGet(serverAddress, serverChallengePath, m_sessionIdString)) == ESP8266::AT_OK);
-    if (result)
-    {
+    result =
+        (esp8266.sendData(formatHttpGet(serverAddress, serverChallengePath,
+                                        m_sessionIdString)) == ESP8266::AT_OK);
+    if (result) {
       // Receive server response
-      for (int i = 0; i < 10; i++)
-      {
-        while (esp8266.recvIpDataReadable())
-        {
+      for (int i = 0; i < 10; i++) {
+        while (esp8266.recvIpDataReadable()) {
           char read = esp8266.getcRecvIpData();
-          if (response.length() < response.capacity())
-          {
+          if (response.length() < response.capacity()) {
             response += read;
-          }
-          else
-          {
-            wait_ms(ESP8266::sendDataRecoveryTimeMs); // Wait for ESP8266 specified recovery time
+          } else {
+            // Wait for ESP8266 specified recovery time
+            wait_ms(ESP8266::sendDataRecoveryTimeMs);
             goto close_get_connection;
           }
         }
@@ -280,50 +279,59 @@
       // Close connection
     close_get_connection:
       esp8266.closeConnection();
-      
+
       // Parse challenge from response
       size_t challengePos = response.find(challengeSearch);
-      if ((challengePos != std::string::npos) && ((challengePos + challengeSearch.length() + (challengeLen * charsPerByte)) <= response.length()))
-      {
+      const size_t challengeStringLen = challengeLen * 2;
+      if ((challengePos != std::string::npos) &&
+          ((challengePos + challengeSearch.length() + challengeStringLen) <=
+           response.length())) {
         challengePos += challengeSearch.length();
-        for (size_t i = 0; i < challengeLen; i++)
-        {
-          std::sscanf(response.substr(challengePos + (i * charsPerByte), charsPerByte).c_str(), "%2hhx", &challenge[i]);
-        }
+        const std::vector<uint_least8_t> parsedChallenge = hexStringToByteArray(
+            response.substr(challengePos, challengeStringLen));
+        std::copy(parsedChallenge.begin(), parsedChallenge.end(), challenge);
       }
-      
+
       // Post sensor data
-      result = (esp8266.openConnection(ESP8266::TCP, serverAddress, serverPort) == ESP8266::AT_OK);
-      if (result)
-      {
-        result = (esp8266.sendData(formatHttpPost(serverAddress, serverPostPath, m_sessionIdString, macCoproc, event, postData, challenge)) == ESP8266::AT_OK);
-        wait_ms(ESP8266::sendDataRecoveryTimeMs); // Wait for ESP8266 specified recovery time
+      result = (esp8266.openConnection(ESP8266::TCP, serverAddress,
+                                       serverPort) == ESP8266::AT_OK);
+      if (result) {
+        result =
+            (esp8266.sendData(formatHttpPost(
+                 serverAddress, serverPostPath, m_sessionIdString, macCoproc,
+                 event, postData, challenge)) == ESP8266::AT_OK);
+        wait_ms(
+            ESP8266::
+                sendDataRecoveryTimeMs); // Wait for ESP8266 specified recovery time
       }
     }
-    
+
     // Close connection
     esp8266.closeConnection();
   }
-  
+
   return result;
 }
 
-std::string WebServerInterface::formatSensorDataPostBody(const SensorData & sensorData)
-{
+std::string
+WebServerInterface::formatSensorDataPostBody(const SensorData & sensorData) {
   // Create initial post body string from input data
   std::ostringstream postBodyStream;
-  postBodyStream << "Temp" << keyValSeparator << static_cast<int>(sensorData.temp);
+  postBodyStream << "Temp" << keyValSeparator
+                 << static_cast<int>(sensorData.temp);
   postBodyStream << fieldSeparator;
-  postBodyStream << "FilterLife" << keyValSeparator << static_cast<unsigned>(sensorData.filterLife);
+  postBodyStream << "FilterLife" << keyValSeparator
+                 << static_cast<unsigned>(sensorData.filterLife);
   postBodyStream << fieldSeparator;
-  postBodyStream << "TempAlarm" << keyValSeparator << (sensorData.tempAlarm() ? "true" : "false");
+  postBodyStream << "TempAlarm" << keyValSeparator
+                 << (sensorData.tempAlarm() ? "true" : "false");
   postBodyStream << fieldSeparator;
-  postBodyStream << "FilterLifeAlarm" << keyValSeparator << (sensorData.filterLifeAlarm() ? "true" : "false");
+  postBodyStream << "FilterLifeAlarm" << keyValSeparator
+                 << (sensorData.filterLifeAlarm() ? "true" : "false");
   return postBodyStream.str();
 }
 
-void WebServerInterface::setSessionId(const OneWire::RomId & sessionId)
-{
-    m_sessionIdString = byteArrayToHexString(sessionId.buffer.data(), sessionId.buffer.size());
-    m_sessionId = sessionId;
+void WebServerInterface::setSessionId(const RomId & sessionId) {
+  m_sessionIdString = byteArrayToHexString(sessionId.data(), sessionId.size());
+  m_sessionId = sessionId;
 }
\ No newline at end of file