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.
Diff: WebServerInterface.cpp
- Revision:
- 32:0a09505a656d
- Parent:
- 31:7b10bcb3e0fc
diff -r 7b10bcb3e0fc -r 0a09505a656d WebServerInterface.cpp
--- 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
MAXREFDES143#: DeepCover Embedded Security in IoT Authenticated Sensing & Notification