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.
WebServerInterface.cpp
- Committer:
- IanBenzMaxim
- Date:
- 2017-01-26
- Revision:
- 28:e5cdaf13d299
- Parent:
- 27:81a87d29bedd
- Child:
- 29:590a7561318b
File content as of revision 28:e5cdaf13d299:
/******************************************************************************* * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of Maxim Integrated * Products, Inc. shall not be used except as stated in the Maxim Integrated * Products, Inc. Branding Policy. * * The mere transfer of this software does not imply any licenses * of trade secrets, proprietary technology, copyrights, patents, * trademarks, maskwork rights, or any other form of intellectual * property whatsoever. Maxim Integrated Products, Inc. retains all * ownership rights. ******************************************************************************* */ #include <sstream> #include <vector> #include "WebServerInterface.hpp" #include "NetworkStack.h" #include "TCPSocket.h" #include "Slaves/Authenticators/ISha256MacCoproc.h" #include "SensorData.hpp" #include "HexConversions.hpp" #include "Serial.h" #include "wait_api.h" using OneWire::ISha256MacCoproc; const char WebServerInterface::serverAddress[] = "www.mxim-security.us"; const unsigned int WebServerInterface::serverPort = 80; const char WebServerInterface::serverPostPath[] = "/post.php"; const char WebServerInterface::serverChallengePath[] = "/challenge.php"; // HTTP formatting constants static const char keyValSeparator = '='; static const char fieldSeparator = '&'; static const std::string newline = "\r\n"; static const char sessionIdKey[] = "SessionId"; // Authentication MAC constants static const size_t challengeLen = 32; static const uint8_t defaultPaddingByte = 0x00; /// 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); } /// Format an HTTP GET request as a string for transmission. /// @param host Web server address. /// @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) { std::ostringstream httpGetStream; httpGetStream << "GET " << path; if (sessionId.length() > 0) httpGetStream << '?' << sessionIdKey << keyValSeparator << sessionId; httpGetStream << " HTTP/1.1" << newline; httpGetStream << "Host: " << host << newline; httpGetStream << newline; return httpGetStream.str(); } /// 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 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); output.fill(defaultPaddingByte); // Set initial hash value while (index < ilen) { if ((index + block.size()) <= ilen) // Full block { std::memcpy(block.data(), &input[index], block.size()); index += block.size(); } 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; } // Write data to coprocessor and hash block macCoproc.computeAuthMac(output, block, padding, output); } } /// Format an HTTP POST request as a string for transmission. /// @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 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. /// @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]) { 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) { httpPost += sessionIdKey; httpPost += keyValSeparator; httpPost += sessionId; } // Add event to post body std::string eventString; switch (event) { case SensorDataEvent: eventString = "SensorData"; break; case InvalidSensorEvent: eventString = "InvalidSensor"; break; } 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 (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; 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= */); // Construct full post request httpPost = ""; httpPost += "POST "; httpPost += path; httpPost += " HTTP/1.1"; httpPost += newline; httpPost += "Host: "; httpPost += host; httpPost += newline; httpPost += "Accept: */*"; httpPost += newline; httpPost += "Content-Length: "; httpPost += contentLen; httpPost += newline; httpPost += "Content-Type: application/x-www-form-urlencoded"; httpPost += newline; httpPost += newline; // Add post body 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 += newline; return httpPost; } bool WebServerInterface::authPostHttpEvent(ISha256MacCoproc & macCoproc, PostEvent event, const std::string & postData, bool setSecret) { bool result; if (setSecret) { result = setHttpPostSecret(macCoproc, m_sessionId); if (!result) return result; } // Open connection TCPSocket socket(&networkStack); result = (socket.connect(serverAddress, 80) == 0); if (result) { // Request challenge std::string httpData = formatHttpGet(serverAddress, serverChallengePath, m_sessionIdString); result = (socket.send(httpData.data(), httpData.size()) == httpData.size()); if (result) { // Receive server response int recvResult = socket.recv(recvBuf, sizeof(recvBuf) / sizeof(recvBuf[0])); result = recvResult > 0; if (result) { // Parse challenge from response const std::string challengeSearch(newline + newline); httpData.assign(recvBuf, recvResult); size_t challengePos = httpData.find(challengeSearch); if ((challengePos != std::string::npos) && ((challengePos + challengeSearch.length() + (challengeLen * charsPerByte)) <= httpData.length())) { uint8_t challenge[challengeLen]; challengePos += challengeSearch.length(); for (size_t i = 0; i < challengeLen; i++) { std::sscanf(httpData.substr(challengePos + (i * charsPerByte), charsPerByte).c_str(), "%2hhx", &challenge[i]); } // Post sensor data httpData = formatHttpPost(serverAddress, serverPostPath, m_sessionIdString, macCoproc, event, postData, challenge); result = (socket.send(httpData.data(), httpData.size()) == httpData.size()); if (result) { result = (socket.recv(recvBuf, sizeof(recvBuf) / sizeof(recvBuf[0])) >= 0); } } } } // Close connection socket.close(); } return result; } 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 << fieldSeparator; postBodyStream << "FilterLife" << keyValSeparator << static_cast<unsigned>(sensorData.filterLife); postBodyStream << fieldSeparator; postBodyStream << "TempAlarm" << keyValSeparator << (sensorData.tempAlarm() ? "true" : "false"); postBodyStream << fieldSeparator; 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; }