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
--- 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