Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: MaximInterface mbed
WebServerInterface.cpp
- Committer:
- IanBenzMaxim
- Date:
- 2016-04-20
- Revision:
- 5:63232ef22abe
- Parent:
- 4:71d578d3af22
- Child:
- 6:b6bafd0a7013
File content as of revision 5:63232ef22abe:
/*******************************************************************************
* 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 <vector>
#include "WebServerInterface.hpp"
#include "ESP8266.hpp"
#include "OneWire_Masters/ISha256MacCoprocessor.hpp"
#include "common.hpp"
#include "mbed.h"
const char WebServerInterface::wifiSsid[] = "WifiSsid";
const char WebServerInterface::wifiPassword[] = "WifiPassword";
const char WebServerInterface::serverAddress[] = "website.com";
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 std::size_t challengeLen = 32;
static const std::uint8_t defaultPaddingByte = 0x00;
/// Select the Transport Secret for the web server in the Controller.
/// @returns True on success.
static bool setHttpPostSecret(ISha256MacCoprocessor & MacCoproc)
{
ISha256MacCoprocessor::DevicePage::Buffer fillData;
std::memset(fillData, defaultPaddingByte, ISha256MacCoprocessor::DevicePage::length);
// static_assert(ISha256MacCoprocessor::DevicePage::length > ISha256MacCoprocessor::SlaveSecretData::length);
return (MacCoproc.computeSlaveSecret(fillData, fillData, reinterpret_cast<const ISha256MacCoprocessor::SlaveSecretData::Buffer &>(fillData)) == ISha256MacCoprocessor::Success);
}
WebServerInterface::WebServerInterface(ESP8266 & esp8266, mbed::Serial * pc)
: esp8266(esp8266), pc(pc)
{
}
bool WebServerInterface::initialize()
{
esp8266.setPowered(true);
esp8266.reset();
bool result = (esp8266.performSelfTest() == ESP8266::AT_OK);
if (!result)
{
return false;
}
result = (esp8266.setCurrentWifiMode(ESP8266::softAP_station_mode) == ESP8266::AT_OK);
if (!result)
{
return false;
}
result = (esp8266.setMaxRFTXPower(10) == ESP8266::AT_OK);
if (!result)
{
return false;
}
result = (esp8266.joinCurrentAccessPoint(wifiSsid, wifiPassword) == ESP8266::AT_OK);
if (!result)
{
return false;
}
return true;
}
/// 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 ISha256MacCoprocessor & macCoproc, const std::uint8_t * input, std::size_t ilen, ISha256MacCoprocessor::Mac & output)
{
ISha256MacCoprocessor::DeviceScratchpad block;
std::size_t index = 0;
ISha256MacCoprocessor::AuthMacData padding;
std::memset(padding, defaultPaddingByte, padding.length);
std::memset(output, defaultPaddingByte, output.length); // Set initial hash value
while (index < ilen)
{
if ((index + block.length) <= ilen) // Full block
{
std::memcpy(block, &input[index], block.length);
index += block.length;
}
else // Partial block with padding
{
std::memcpy(block, &input[index], ilen - index);
std::memset(&block[ilen - index], defaultPaddingByte, block.length - (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 ISha256MacCoprocessor & macCoproc, PostEvent event, const std::string & initialPostBody,
const std::uint8_t (&challenge)[challengeLen])
{
const std::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<std::uint8_t> hashInput;
hashInput.reserve(challengeLen + httpPost.length());
hashInput.insert(hashInput.end(), challenge, challenge + challengeLen);
hashInput.insert(hashInput.end(), httpPost.begin(), httpPost.end());
ISha256MacCoprocessor::Mac mac;
calculateHttpPostMac(macCoproc, &hashInput[0], hashInput.size(), mac);
char contentLen[5];
std::snprintf(contentLen, sizeof(contentLen) / sizeof(char), "%u", (hashInput.size() - challengeLen) + (mac.length * 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, mac.length, httpPost);
httpPost += newline;
return httpPost;
}
bool WebServerInterface::authPostHttpEvent(ISha256MacCoprocessor & macCoproc, PostEvent event, const std::string & postData, bool setSecret)
{
const std::string challengeSearch(newline + newline);
bool result;
std::uint8_t challenge[challengeLen];
std::string response;
std::memset(challenge, defaultPaddingByte, challengeLen);
response.reserve(300);
if (setSecret)
{
result = setHttpPostSecret(macCoproc);
if (!result)
return result;
}
// Open connection
esp8266.clearRecvData(); // Clear received data buffer
result = (esp8266.openConnection(ESP8266::TCP, serverAddress, 80) == ESP8266::AT_OK);
if (result)
{
// Request challenge
result = (esp8266.sendData(formatHttpGet(serverAddress, serverChallengePath, sessionId)) == ESP8266::AT_OK);
if (result)
{
// Receive server response
for (int i = 0; i < 10; i++)
{
while (esp8266.recvIpDataReadable())
{
char read = esp8266.getcRecvIpData();
if (pc != NULL)
pc->putc(read);
if (response.length() < response.capacity())
{
response += read;
}
else
{
wait_ms(ESP8266::sendDataRecoveryTimeMs); // Wait for ESP8266 specified recovery time
goto close_get_connection;
}
}
wait_ms(100);
}
// Close connection
close_get_connection:
esp8266.closeConnection();
// Parse challenge from response
std::size_t challengePos = response.find(challengeSearch);
if ((challengePos != std::string::npos) && ((challengePos + challengeSearch.length() + (challengeLen * charsPerByte)) <= response.length()))
{
challengePos += challengeSearch.length();
for (std::size_t i = 0; i < challengeLen; i++)
{
std::sscanf(response.substr(challengePos + (i * charsPerByte), charsPerByte).c_str(), "%2hhx", &challenge[i]);
}
}
// Post sensor data
result = (esp8266.openConnection(ESP8266::TCP, serverAddress, serverPort) == ESP8266::AT_OK);
if (result)
{
result = (esp8266.sendData(formatHttpPost(serverAddress, serverPostPath, sessionId, 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)
{
// 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();
}
MAXREFDES143#: DeepCover Embedded Security in IoT Authenticated Sensing & Notification