MAXREFDES143#: DeepCover Embedded Security in IoT Authenticated Sensing & Notification
Dependencies: MaximInterface mbed
WebServerInterface.cpp
00001 /******************************************************************************* 00002 * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved. 00003 * 00004 * Permission is hereby granted, free of charge, to any person obtaining a 00005 * copy of this software and associated documentation files (the "Software"), 00006 * to deal in the Software without restriction, including without limitation 00007 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00008 * and/or sell copies of the Software, and to permit persons to whom the 00009 * Software is furnished to do so, subject to the following conditions: 00010 * 00011 * The above copyright notice and this permission notice shall be included 00012 * in all copies or substantial portions of the Software. 00013 * 00014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 00015 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 00017 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES 00018 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 00019 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 00020 * OTHER DEALINGS IN THE SOFTWARE. 00021 * 00022 * Except as contained in this notice, the name of Maxim Integrated 00023 * Products, Inc. shall not be used except as stated in the Maxim Integrated 00024 * Products, Inc. Branding Policy. 00025 * 00026 * The mere transfer of this software does not imply any licenses 00027 * of trade secrets, proprietary technology, copyrights, patents, 00028 * trademarks, maskwork rights, or any other form of intellectual 00029 * property whatsoever. Maxim Integrated Products, Inc. retains all 00030 * ownership rights. 00031 *******************************************************************************/ 00032 00033 #include <vector> 00034 #include <Serial.h> 00035 #include <wait_api.h> 00036 #include <MaximInterface/Devices/DS2465.hpp> 00037 #include <MaximInterface/Utilities/HexConversions.hpp> 00038 #include "WebServerInterface.hpp" 00039 #include "ESP8266.hpp" 00040 #include "SensorData.hpp" 00041 00042 using namespace MaximInterface; 00043 00044 const char WebServerInterface::wifiSsid[] = "WifiSsid"; 00045 const char WebServerInterface::wifiPassword[] = "WifiPassword"; 00046 const char WebServerInterface::serverAddress[] = "www.maxim-security.com"; 00047 const unsigned int WebServerInterface::serverPort = 80; 00048 const char WebServerInterface::serverPostPath[] = "/maxrefdes143/post.php"; 00049 const char WebServerInterface::serverChallengePath[] = 00050 "/maxrefdes143/challenge.php"; 00051 00052 // HTTP formatting constants 00053 static const char keyValSeparator = '='; 00054 static const char fieldSeparator = '&'; 00055 static const std::string newline = "\r\n"; 00056 static const char sessionIdKey[] = "SessionId"; 00057 00058 // Authentication MAC constants 00059 static const size_t challengeLen = 32; 00060 static const uint8_t defaultPaddingByte = 0x00; 00061 00062 /// Select the Transport Secret for the web server in the Controller. 00063 /// @returns True on success. 00064 static bool setHttpPostSecret(DS2465 & macCoproc, const RomId & sessionId) { 00065 Sha256::SlaveSecretData data; 00066 data.fill(defaultPaddingByte); 00067 std::copy(sessionId.begin(), sessionId.end(), data.begin() + 64); 00068 return !macCoproc.computeSlaveSecret(data); 00069 } 00070 00071 bool WebServerInterface::initialize() { 00072 esp8266.setPowered(true); 00073 esp8266.reset(); 00074 bool result = (esp8266.performSelfTest() == ESP8266::AT_OK); 00075 if (result) { 00076 result = (esp8266.setCurrentWifiMode(ESP8266::softAP_station_mode) == 00077 ESP8266::AT_OK); 00078 } 00079 if (result) { 00080 result = (esp8266.setMaxRFTXPower(10) == ESP8266::AT_OK); 00081 } 00082 if (result) { 00083 result = (esp8266.joinCurrentAccessPoint(wifiSsid, wifiPassword) == 00084 ESP8266::AT_OK); 00085 } 00086 return result; 00087 } 00088 00089 /// Format an HTTP GET request as a string for transmission. 00090 /// @param host Web server address. 00091 /// @param path Web server location to retrieve. 00092 /// @param sessionId Session ID used to identify this controller. 00093 /// @returns GET request string. 00094 static std::string formatHttpGet(const std::string & host, 00095 const std::string & path, 00096 const std::string & sessionId) { 00097 std::ostringstream httpGetStream; 00098 httpGetStream << "GET " << path; 00099 if (sessionId.length() > 0) 00100 httpGetStream << '?' << sessionIdKey << keyValSeparator << sessionId; 00101 httpGetStream << " HTTP/1.1" << newline; 00102 httpGetStream << "Host: " << host << newline; 00103 httpGetStream << newline; 00104 return httpGetStream.str(); 00105 } 00106 00107 /// Computes a MAC using the Transport Secret to sign HTTP POST requests. 00108 /// @param macCoproc 00109 /// Coprocessor such as the DS2465 used to calculate the authentication MAC. 00110 /// @param input Message array used for MAC calculation. 00111 /// @param ilen Length of array input. 00112 /// @param output Calculated MAC output. 00113 static void calculateHttpPostMac(const DS2465 & macCoproc, 00114 const uint8_t * input, size_t ilen, 00115 Sha256::Hash & output) { 00116 const size_t blockSize = 32; 00117 output.fill(defaultPaddingByte); // Set initial hash value 00118 Sha256::AuthMacData macData; 00119 macData.fill(defaultPaddingByte); 00120 while (ilen > 0) { 00121 Sha256::AuthMacData::iterator macDataBegin = 00122 std::copy(output.begin(), output.end(), macData.begin()); 00123 if (ilen >= blockSize) // Full block 00124 { 00125 std::copy(input, input + blockSize, macDataBegin); 00126 input += blockSize; 00127 ilen -= blockSize; 00128 } else // Partial block with padding 00129 { 00130 macDataBegin = std::copy(input, input + ilen, macDataBegin); 00131 std::fill(macDataBegin, macDataBegin + (blockSize - ilen), 00132 defaultPaddingByte); 00133 ilen = 0; 00134 } 00135 // Write data to coprocessor and hash block 00136 macCoproc.computeAuthMac(macData, output); 00137 } 00138 } 00139 00140 /// Format an HTTP POST request as a string for transmission. 00141 /// @param host Web server address. 00142 /// @param path Web server location to receive POST. 00143 /// @param sessionId Session ID used to identify this Controller. 00144 /// @param macCoproc 00145 /// Coprocessor such as the DS2465 used to calculate the authentication MAC. 00146 /// @param event Event message type. 00147 /// @param initialPostBody Message body as determined by the event message type. 00148 /// @param challenge 00149 /// Challenge previously received from web server for use in authentication MAC. 00150 /// @returns POST request string. 00151 static std::string formatHttpPost(const std::string & host, 00152 const std::string & path, 00153 const std::string & sessionId, 00154 const DS2465 & macCoproc, PostEvent event, 00155 const std::string & initialPostBody, 00156 const uint8_t (&challenge)[challengeLen]) { 00157 const size_t headerReserve = 115, bodyReserve = 200; 00158 00159 std::string httpPost; 00160 httpPost.reserve(initialPostBody.length() + headerReserve + bodyReserve); 00161 00162 // Add session ID to post body 00163 if (sessionId.length() > 0) { 00164 httpPost += sessionIdKey; 00165 httpPost += keyValSeparator; 00166 httpPost += sessionId; 00167 } 00168 00169 // Add event to post body 00170 std::string eventString; 00171 switch (event) { 00172 case SensorDataEvent: 00173 eventString = "SensorData"; 00174 break; 00175 00176 case InvalidSensorEvent: 00177 eventString = "InvalidSensor"; 00178 break; 00179 } 00180 if (eventString.length() > 0) { 00181 if (httpPost.length() > 0) 00182 httpPost += fieldSeparator; 00183 httpPost += "Event"; 00184 httpPost += keyValSeparator; 00185 httpPost += eventString; 00186 } 00187 00188 // Add initial post body 00189 if (initialPostBody.length() > 0) { 00190 if (httpPost.length() > 0) 00191 httpPost += fieldSeparator; 00192 httpPost += initialPostBody; 00193 } 00194 00195 // Combine initial post body with initial secret and hash 00196 std::vector<uint8_t> hashInput; 00197 hashInput.reserve(challengeLen + httpPost.length()); 00198 hashInput.assign(challenge, challenge + challengeLen); 00199 hashInput.insert(hashInput.end(), httpPost.begin(), httpPost.end()); 00200 Sha256::Hash mac; 00201 calculateHttpPostMac(macCoproc, &hashInput[0], hashInput.size(), mac); 00202 00203 char contentLen[5]; 00204 snprintf(contentLen, sizeof(contentLen) / sizeof(contentLen[0]), "%u", 00205 (hashInput.size() - challengeLen) + (mac.size() * 2) + 00206 5 /* &MAC= */); 00207 00208 // Construct full post request 00209 httpPost = ""; 00210 httpPost += "POST "; 00211 httpPost += path; 00212 httpPost += " HTTP/1.1"; 00213 httpPost += newline; 00214 httpPost += "Host: "; 00215 httpPost += host; 00216 httpPost += newline; 00217 httpPost += "Accept: */*"; 00218 httpPost += newline; 00219 httpPost += "Content-Length: "; 00220 httpPost += contentLen; 00221 httpPost += newline; 00222 httpPost += "Content-Type: application/x-www-form-urlencoded"; 00223 httpPost += newline; 00224 httpPost += newline; 00225 // Add post body 00226 httpPost.append(reinterpret_cast<char *>(&hashInput[challengeLen]), 00227 hashInput.size() - challengeLen); 00228 // Convert hash to hex string and add to post body 00229 httpPost += fieldSeparator; 00230 httpPost += "MAC"; 00231 httpPost += keyValSeparator; 00232 httpPost += byteArrayToHexString(mac.data(), mac.size()); 00233 httpPost += newline; 00234 00235 return httpPost; 00236 } 00237 00238 bool WebServerInterface::authPostHttpEvent(DS2465 & macCoproc, PostEvent event, 00239 const std::string & postData, 00240 bool setSecret) { 00241 const std::string challengeSearch(newline + newline); 00242 bool result; 00243 uint8_t challenge[challengeLen]; 00244 std::string response; 00245 00246 std::memset(challenge, defaultPaddingByte, challengeLen); 00247 response.reserve(300); 00248 00249 if (setSecret) { 00250 result = setHttpPostSecret(macCoproc, m_sessionId); 00251 if (!result) 00252 return result; 00253 } 00254 00255 // Open connection 00256 esp8266.clearRecvData(); // Clear received data buffer 00257 result = (esp8266.openConnection(ESP8266::TCP, serverAddress, 80) == 00258 ESP8266::AT_OK); 00259 if (result) { 00260 // Request challenge 00261 result = 00262 (esp8266.sendData(formatHttpGet(serverAddress, serverChallengePath, 00263 m_sessionIdString)) == ESP8266::AT_OK); 00264 if (result) { 00265 // Receive server response 00266 for (int i = 0; i < 10; i++) { 00267 while (esp8266.recvIpDataReadable()) { 00268 char read = esp8266.getcRecvIpData(); 00269 if (response.length() < response.capacity()) { 00270 response += read; 00271 } else { 00272 // Wait for ESP8266 specified recovery time 00273 wait_ms(ESP8266::sendDataRecoveryTimeMs); 00274 goto close_get_connection; 00275 } 00276 } 00277 wait_ms(100); 00278 } 00279 // Close connection 00280 close_get_connection: 00281 esp8266.closeConnection(); 00282 00283 // Parse challenge from response 00284 size_t challengePos = response.find(challengeSearch); 00285 const size_t challengeStringLen = challengeLen * 2; 00286 if ((challengePos != std::string::npos) && 00287 ((challengePos + challengeSearch.length() + challengeStringLen) <= 00288 response.length())) { 00289 challengePos += challengeSearch.length(); 00290 const std::vector<uint_least8_t> parsedChallenge = hexStringToByteArray( 00291 response.substr(challengePos, challengeStringLen)); 00292 std::copy(parsedChallenge.begin(), parsedChallenge.end(), challenge); 00293 } 00294 00295 // Post sensor data 00296 result = (esp8266.openConnection(ESP8266::TCP, serverAddress, 00297 serverPort) == ESP8266::AT_OK); 00298 if (result) { 00299 result = 00300 (esp8266.sendData(formatHttpPost( 00301 serverAddress, serverPostPath, m_sessionIdString, macCoproc, 00302 event, postData, challenge)) == ESP8266::AT_OK); 00303 wait_ms( 00304 ESP8266:: 00305 sendDataRecoveryTimeMs); // Wait for ESP8266 specified recovery time 00306 } 00307 } 00308 00309 // Close connection 00310 esp8266.closeConnection(); 00311 } 00312 00313 return result; 00314 } 00315 00316 std::string 00317 WebServerInterface::formatSensorDataPostBody(const SensorData & sensorData) { 00318 // Create initial post body string from input data 00319 std::ostringstream postBodyStream; 00320 postBodyStream << "Temp" << keyValSeparator 00321 << static_cast<int>(sensorData.temp); 00322 postBodyStream << fieldSeparator; 00323 postBodyStream << "FilterLife" << keyValSeparator 00324 << static_cast<unsigned>(sensorData.filterLife); 00325 postBodyStream << fieldSeparator; 00326 postBodyStream << "TempAlarm" << keyValSeparator 00327 << (sensorData.tempAlarm() ? "true" : "false"); 00328 postBodyStream << fieldSeparator; 00329 postBodyStream << "FilterLifeAlarm" << keyValSeparator 00330 << (sensorData.filterLifeAlarm() ? "true" : "false"); 00331 return postBodyStream.str(); 00332 } 00333 00334 void WebServerInterface::setSessionId(const RomId & sessionId) { 00335 m_sessionIdString = byteArrayToHexString(sessionId.data(), sessionId.size()); 00336 m_sessionId = sessionId; 00337 }
Generated on Wed Jul 13 2022 21:31:03 by 1.7.2