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: main.cpp
- Revision:
- 1:e1c7c1c636af
- Child:
- 4:71d578d3af22
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu Apr 14 19:48:01 2016 +0000 @@ -0,0 +1,475 @@ +/******************************************************************************* +* 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 "common.hpp" +#include "WebServerInterface.hpp" +#include "Factory.hpp" +#include "SensorNode.hpp" +#include "OneWire_Masters/DS2465/DS2465.hpp" +#include "Display.hpp" +#include "RomId.hpp" +#include "ESP8266.hpp" +#include "mbed.h" + +/// Main status for the program. +enum Status +{ + InitializingController, ///< Configure DS2465 and connect to network. + DisplaySessionId, ///< Display ID for use with website. + SensorNodeNeedsDetection, ///< Prompt user to insert Sensor Node. + DetectingSensorNode, ///< Check if Sensor Node present. + SensorNodeNeedsProvision, ///< Sensor Node needs to be provisioned. + ProvisioningSensorNode, ///< Provisioning Sensor Node to factory defaults. + NormalOperation, ///< The normal demo operation state. + SensorNodeNotAuthentic, ///< Sensor Node failed authentication check. + ControllerInitializationError, ///< Failed to initialize Controller. + ControllerHardwareError, ///< Controller hardware failed unexpectedly. + SensorNodeHardwareError ///< Sensor Node hardware failed unexpectedly. +}; + +/// @{ +/// Configuration options. +static const unsigned int webPostIntervalMs = 10000; +static const unsigned int webPostRetryIntervalMs = 1000; +static const std::uint8_t maxConsecutiveWebPostErrors = 3; +/// @} + +/// @{ +/// LCD display colors. +static const Display::Color Teal(0x00, 0xB2, 0xA9); +static const Display::Color Red(0xFF, 0x00, 0x00); +static const Display::Color Green(0x00, 0xFF, 0x00); +/// @} + +/// @{ +/// Peripheral and pin definitions +static Serial pc(USBTX, USBRX); +static DigitalIn provisionButton(P2_0); +static DigitalIn invalidateButton(P1_5); +static DigitalOut tempAlarmLed(P2_1, 1); +static DigitalOut filterLifeAlarmLed(P2_3, 1); +static I2C i2c(P2_6, P2_7); +static Display lcd(i2c, 0x78, 0x98); +static DS2465 ds2465(i2c, 0x30); +static SensorNode sensorNode(i2c, 0x90, 0x94, ds2465); +static Factory factory; +static ESP8266 esp8266(P1_1, P1_0, P1_2, P1_3, 38400); +static WebServerInterface webIntf(esp8266, &pc); +/// @} + +static bool useInvalidSecret = false; ///< Imitate an invalid controller when posting to web server. +static unsigned int randomSeed = 0; ///< Create extra entropy for challenge. +static Status currentStatus = InitializingController; +static bool result = false; +static std::uint8_t consecutiveWebPostErrors = 0; ///< Account for a few network errors in case of flaky connection. +static Timer webPostTimer; ///< Software timer to track web posting interval. + +static void blinkLeds(unsigned int time_ms); ///< Invert LEDs for a given amount of time. +static bool buttonPressed(DigitalIn & button); ///< Checks if button is pressed (returns true) and waits for release. +static void displayStatus(Status status); ///< Display status message on LCD. +static void displaySensorData(const SensorData & sensorData); ///< Display sensor data on the LCD. +static bool readWebSessionId(std::string & sessionId); ///< Read device's web session ID from it's nonvolatile storage. + +#ifdef ASSEMBLY_TEST +#include "AssemblyTest.cpp" +#endif + +int main() +{ + blinkLeds(500); + +#ifdef ASSEMBLY_TEST + assemblyTest(); +#endif + + while (true) + { + Status nextStatus = currentStatus; + switch (currentStatus) + { + case InitializingController: + pc.baud(115200); + i2c.frequency(100000); + webPostTimer.start(); + + // Set initial LCD state + lcd.initialize(); + displayStatus(currentStatus); + + // Connect to Wifi network + result = webIntf.initialize(); + + // Read session ID + if (result) + { + result = readWebSessionId(webIntf.sessionId); + } + + // Provision DS2465 with master secret and page data + if (result) + { + result = factory.provision(ds2465); + } + + if (result) + { + nextStatus = DisplaySessionId; + } + else + { + nextStatus = ControllerInitializationError; + } + break; + + case DisplaySessionId: + // Wait for user to press Provision button + if (buttonPressed(provisionButton)) + { + nextStatus = SensorNodeNeedsDetection; + } + break; + + case SensorNodeNeedsDetection: + // Wait for user to press Provision button + if (buttonPressed(provisionButton)) + { + nextStatus = DetectingSensorNode; + } + break; + + case DetectingSensorNode: + // Perform Sensor Node detection sequence + switch (sensorNode.detect(randomSeed)) + { + case SensorNode::UnableToCommunicate: + default: + nextStatus = SensorNodeHardwareError; + break; + + case SensorNode::NotProvisioned: + nextStatus = SensorNodeNeedsProvision; + break; + + case SensorNode::NotAuthentic: + nextStatus = SensorNodeNotAuthentic; + break; + + case SensorNode::Authentic: + nextStatus = NormalOperation; + break; + } + break; + + case SensorNodeNeedsProvision: + // Wait for user to press Provision button + if (buttonPressed(provisionButton)) + { + nextStatus = ProvisioningSensorNode; + } + break; + + case ProvisioningSensorNode: + if (!buttonPressed(invalidateButton)) // Provision normally + { + if (factory.provision(sensorNode, true)) + { + nextStatus = NormalOperation; + } + else + { + nextStatus = SensorNodeNotAuthentic; + } + } + else // Invalidate button also pressed; Load invalid secret + { + // Provision with invalid secret + if (factory.provision(sensorNode, false)) + { + nextStatus = NormalOperation; + } + else + { + nextStatus = SensorNodeHardwareError; + } + } + break; + + case NormalOperation: + // Check if user pressed Provision button + if (buttonPressed(provisionButton)) + { + // Re-provision Sensor Node + nextStatus = ProvisioningSensorNode; + } + // Check if user pressed Invalidate button + else if (buttonPressed(invalidateButton)) + { + // Toggle between using valid and invalid secret + // 1 blink = invalid; 2 blinks = valid + useInvalidSecret = !useInvalidSecret; + blinkLeds(100); + if (!useInvalidSecret) + { + wait_ms(100); + blinkLeds(100); + } + } + // Check node and display measurements + else + { + SensorData sensorData; + // Read sensor data with authentication + switch (sensorNode.authenticatedReadSensorData(randomSeed, sensorData)) + { + case SensorNode::Authentic: + // Update measurements on LCD + displaySensorData(sensorData); + + // Update alarm LEDs + tempAlarmLed = !sensorData.tempAlarm(); // Active Low + filterLifeAlarmLed = !sensorData.filterLifeAlarm(); // Active Low + + // Send measurements to web if time interval reached + if (webPostTimer.read_ms() >= webPostIntervalMs) + { + // Format, sign, and transmit data to web server + result = webIntf.authPostHttpEvent(ds2465, SensorDataEvent, WebServerInterface::formatSensorDataPostBody(sensorData), !useInvalidSecret); + if (result) + { + // Reset timer count after logging sample complete + webPostTimer.reset(); + consecutiveWebPostErrors = 0; + } + // There was likely an error establishing a web connection + else if (++consecutiveWebPostErrors < maxConsecutiveWebPostErrors) + { + // Wait and try again + wait_ms(webPostRetryIntervalMs); + } + // Too many retry attempts + else + { + // Assume we have lost network connection + nextStatus = ControllerHardwareError; + } + } + break; + + case SensorNode::NotAuthentic: + nextStatus = SensorNodeNotAuthentic; + break; + + case SensorNode::UnableToCommunicate: + default: + nextStatus = SensorNodeHardwareError; + break; + } + } + break; + + case SensorNodeNotAuthentic: + // Wait for user to press Provision button + if (buttonPressed(provisionButton)) + { + nextStatus = ProvisioningSensorNode; + } + // Try to authenticate and return to normal operation + else if (webPostTimer.read_ms() >= webPostIntervalMs) + { + // Send event message to server + result = webIntf.authPostHttpEvent(ds2465, InvalidSensorEvent, "", !useInvalidSecret); + if (result) + { + // Reset timer count after logging complete + webPostTimer.reset(); + consecutiveWebPostErrors = 0; + } + else if (++consecutiveWebPostErrors < maxConsecutiveWebPostErrors) + { + // There was likely an error establishing a web connection + // Wait and try again + wait_ms(webPostRetryIntervalMs); + } + else + { + nextStatus = ControllerHardwareError; + } + + // Try to authenticate again + nextStatus = SensorNodeNeedsDetection; + } + break; + + case ControllerInitializationError: + case ControllerHardwareError: + case SensorNodeHardwareError: + default: + // Do nothing until user resets + break; + } + // Check if status changed + if (currentStatus != nextStatus) + { + currentStatus = nextStatus; + displayStatus(currentStatus); // Display status message on LCD + } + + // Change seed value on every loop pass + randomSeed++; + } +} + +/// Blink all LEDs for a certain amount of time. +/// @param time_ms Time in ms to blink for. +static void blinkLeds(unsigned int time_ms) +{ + tempAlarmLed = !tempAlarmLed; + filterLifeAlarmLed = !filterLifeAlarmLed; + wait_ms(time_ms); + tempAlarmLed = !tempAlarmLed; + filterLifeAlarmLed = !filterLifeAlarmLed; +} + +/// Check if a button is pressed and wait for it to be release. +/// @param button Active low button to check. +/// @returns True if pressed. +static bool buttonPressed(DigitalIn & button) +{ + const int buttonPressed = 0; // Active low + if (button == buttonPressed) + { + while (button == buttonPressed) ; + return true; + } + // else + return false; +} + +/// Display the current status of the Controller on the LCD display. +static void displayStatus(Status status) +{ + switch (status) + { + case InitializingController: + lcd.writeMessage("Initializing Controller..."); + lcd.setBackLightColor(Teal); + break; + + case DisplaySessionId: + lcd.writeLine("ID: " + webIntf.sessionId, Display::FirstLine); + lcd.writeLine("Provision to begin", Display::SecondLine); + lcd.setBackLightColor(Teal); + break; + + case SensorNodeNeedsDetection: + lcd.writeMessage("Insert Sensor Node and press Provision"); + lcd.setBackLightColor(Teal); + break; + + case DetectingSensorNode: + lcd.writeMessage("Detecting Sensor Node..."); + lcd.setBackLightColor(Teal); + break; + + case SensorNodeNeedsProvision: + lcd.writeMessage("Sensor Node Needs Provision"); + lcd.setBackLightColor(Teal); + break; + + case ProvisioningSensorNode: + lcd.writeMessage("Provisioning Sensor Node"); + lcd.setBackLightColor(Teal); + break; + + case NormalOperation: + // Everything handled in displaySensorData() + break; + + case SensorNodeNotAuthentic: + lcd.writeMessage("Sensor Node Not Authentic"); + lcd.setBackLightColor(Red); + break; + + case ControllerInitializationError: + lcd.writeMessage("Initialization Error Check Wi-Fi"); + lcd.setBackLightColor(Red); + break; + + case ControllerHardwareError: + lcd.writeMessage("Controller Hardware Error"); + lcd.setBackLightColor(Red); + break; + + case SensorNodeHardwareError: + lcd.writeMessage("Sensor Node Hardware Error"); + lcd.setBackLightColor(Red); + break; + } +} + +/// Display sensor data on the LCD display during normal operation. +static void displaySensorData(const SensorData & sensorData) +{ + std::ostringstream stream; + stream << "Chiller Temp: " << (int)sensorData.temp << "C"; + lcd.writeCompleteLine(stream.str(), Display::FirstLine); + stream.str(""); // Clear stream + stream << "Filter Life: " << (unsigned int)sensorData.filterLife << "%"; + lcd.writeCompleteLine(stream.str(), Display::SecondLine); + lcd.setBackLightColor((sensorData.tempAlarm() || sensorData.filterLifeAlarm()) ? Red : Green); +} + +/// Read the Session ID to use with the web server from ROM. +/// @note Session ID is taken from the ROM ID of the MAX66242. +/// @param[out] Session ID string. +/// @returns True on success. +static bool readWebSessionId(std::string & sessionId) +{ + const uint8_t I2C_address = 0x32; + const uint8_t ROM_address = 0x68; + RomId romId; + + // Set register pointer + if (i2c.write(I2C_address, (const char *)&ROM_address, 1) != 0) + return false; + // Read ROM ID + if (i2c.read(I2C_address, (char *)(&(static_cast<RomId::ByteBuffer &>(romId))), RomId::byteLen) != 0) + return false; + // Check if CRC valid + if (!romId.crc8Valid()) + return false; + sessionId = byteArrayToHexString(romId, RomId::byteLen); + return true; +} \ No newline at end of file