MAXREFDES143#: DeepCover Embedded Security in IoT Authenticated Sensing & Notification

Dependencies:   MaximInterface mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.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 <sstream>
00034 #include <mbed.h>
00035 #include <MaximInterface/Devices/DS2465.hpp>
00036 #include <MaximInterface/Platforms/mbed/I2CMaster.hpp>
00037 #include <MaximInterface/Platforms/mbed/Sleep.hpp>
00038 #include <MaximInterface/Utilities/RomId.hpp>
00039 #include "SensorData.hpp"
00040 #include "WebServerInterface.hpp"
00041 #include "Factory.hpp"
00042 #include "SensorNode.hpp"
00043 #include "Display.hpp"
00044 #include "ESP8266.hpp"
00045 
00046 using namespace MaximInterface;
00047 
00048 /// Main status for the program.
00049 enum Status {
00050   InitializingController,   ///< Configure DS2465 and connect to network.
00051   DisplaySessionId,         ///< Display ID for use with website.
00052   SensorNodeNeedsDetection, ///< Prompt user to insert Sensor Node.
00053   DetectingSensorNode,      ///< Check if Sensor Node present.
00054   SensorNodeNeedsProvision, ///< Sensor Node needs to be provisioned.
00055   ProvisioningSensorNode,   ///< Provisioning Sensor Node to factory defaults.
00056   NormalOperation,          ///< The normal demo operation state.
00057   SensorNodeNotAuthentic,   ///< Sensor Node failed authentication check.
00058   ControllerInitializationError, ///< Failed to initialize Controller.
00059   ControllerHardwareError,       ///< Controller hardware failed unexpectedly.
00060   SensorNodeHardwareError        ///< Sensor Node hardware failed unexpectedly.
00061 };
00062 
00063 /// @{
00064 /// Configuration options.
00065 static const int webPostIntervalMs = 10000;
00066 static const int webPostRetryIntervalMs = 1000;
00067 static const int maxConsecutiveWebPostErrors = 3;
00068 /// @}
00069 
00070 /// @{
00071 /// LCD display colors.
00072 static const Display::Color Teal = {0x00, 0xB2, 0xA9};
00073 static const Display::Color Red = {0xFF, 0x00, 0x00};
00074 static const Display::Color Green = {0x00, 0xFF, 0x00};
00075 static const Display::Color Purple = {0x6E, 0x25, 0x85};
00076 /// @}
00077 
00078 /// @{
00079 /// Peripheral and pin definitions
00080 static DigitalIn provisionButton(D13);
00081 static DigitalIn invalidateButton(D5);
00082 static DigitalOut tempAlarmLed(D11, 1);
00083 static DigitalOut filterLifeAlarmLed(D10, 1);
00084 static I2C i2c(D14, D15);
00085 static Display lcd(i2c, 0x78, 0x98);
00086 static MaximInterface::mbed::I2CMaster i2cWrapper(i2c);
00087 static DS2465 ds2465(MaximInterface::mbed::Sleep::instance(), i2cWrapper, 0x30);
00088 static SensorNode sensorNode(i2c, 0x90, 0x94, ds2465);
00089 static ESP8266 esp8266(D1, D0, D2, D3, 38400);
00090 static WebServerInterface webIntf(esp8266);
00091 /// @}
00092 
00093 /// Imitate an invalid controller when posting to web server.
00094 static bool useInvalidSecret = false;
00095 static unsigned int randomSeed = 0; ///< Create extra entropy for challenge.
00096 static Status currentStatus = InitializingController;
00097 static bool result = false;
00098 /// Account for a few network errors in case of flaky connection.
00099 static uint8_t consecutiveWebPostErrors = 0; 
00100 static Timer webPostTimer; ///< Software timer to track web posting interval.
00101 static Timer retryTimer;   ///< Software timer to track authentication retries.
00102 
00103 /// Invert LEDs for a given amount of time.
00104 static void blinkLeds(int time_ms);
00105 /// Checks if button is pressed (returns true) and waits for release.
00106 static bool buttonPressed(DigitalIn & button); 
00107 /// Display status message on LCD.
00108 static void displayStatus(Status status); 
00109 /// Display sensor data on the LCD.
00110 static void displaySensorData(const SensorData & sensorData); 
00111 /// Read device's web session ID from it's nonvolatile storage.
00112 static bool readWebSessionId(RomId & sessionId); 
00113 
00114 #ifdef ASSEMBLY_TEST
00115 #include "AssemblyTest.hpp"
00116 #endif
00117 
00118 int main() {
00119   blinkLeds(500);
00120 
00121 #ifdef ASSEMBLY_TEST
00122   assemblyTest();
00123 #endif
00124 
00125   while (true) {
00126     Status nextStatus = currentStatus;
00127     switch (currentStatus) {
00128     case InitializingController:
00129       i2c.frequency(100000);
00130       webPostTimer.start();
00131 
00132       // Set initial LCD state
00133       lcd.initialize();
00134       displayStatus(currentStatus);
00135 
00136       // Connect to Wifi network
00137       result = webIntf.initialize();
00138 
00139       // Read session ID
00140       if (result) {
00141         RomId sessionId;
00142         result = readWebSessionId(sessionId);
00143         if (result) {
00144           webIntf.setSessionId(sessionId);
00145         }
00146       }
00147 
00148       // Provision DS2465 with master secret and page data
00149       if (result) {
00150         result = provisionCoprocessor(ds2465);
00151       }
00152 
00153       if (result) {
00154         nextStatus = DisplaySessionId;
00155       } else {
00156         nextStatus = ControllerInitializationError;
00157       }
00158       break;
00159 
00160     case DisplaySessionId:
00161       // Wait for user to press Provision button
00162       if (buttonPressed(provisionButton)) {
00163         nextStatus = SensorNodeNeedsDetection;
00164       }
00165       break;
00166 
00167     case SensorNodeNeedsDetection:
00168       // Wait for user to press Provision button
00169       if (buttonPressed(provisionButton)) {
00170         nextStatus = DetectingSensorNode;
00171       }
00172       break;
00173 
00174     case DetectingSensorNode:
00175       // Perform Sensor Node detection sequence
00176       switch (sensorNode.detect(randomSeed)) {
00177       case SensorNode::UnableToCommunicate:
00178       default:
00179         nextStatus = SensorNodeHardwareError;
00180         break;
00181 
00182       case SensorNode::NotProvisioned:
00183         nextStatus = SensorNodeNeedsProvision;
00184         break;
00185 
00186       case SensorNode::NotAuthentic:
00187         nextStatus = SensorNodeNotAuthentic;
00188         break;
00189 
00190       case SensorNode::Authentic:
00191         nextStatus = NormalOperation;
00192         break;
00193       }
00194       break;
00195 
00196     case SensorNodeNeedsProvision:
00197       // Wait for user to press Provision button
00198       if (buttonPressed(provisionButton)) {
00199         nextStatus = ProvisioningSensorNode;
00200       }
00201       break;
00202 
00203     case ProvisioningSensorNode:
00204       if (!buttonPressed(invalidateButton)) { // Provision normally
00205         if (provisionSensorNode(sensorNode, true)) {
00206           nextStatus = NormalOperation;
00207         } else {
00208           nextStatus = SensorNodeNotAuthentic;
00209         }
00210       } else { // Invalidate button also pressed; Load invalid secret
00211         // Provision with invalid secret
00212         if (provisionSensorNode(sensorNode, false)) {
00213           nextStatus = NormalOperation;
00214         } else {
00215           nextStatus = SensorNodeHardwareError;
00216         }
00217       }
00218       break;
00219 
00220     case NormalOperation:
00221       // Check if user pressed Provision button
00222       if (buttonPressed(provisionButton)) {
00223         // Re-provision Sensor Node
00224         nextStatus = ProvisioningSensorNode;
00225       }
00226       // Check if user pressed Invalidate button
00227       else if (buttonPressed(invalidateButton)) {
00228         // Toggle between using valid and invalid secret
00229         // 1 blink = invalid; 2 blinks = valid
00230         useInvalidSecret = !useInvalidSecret;
00231         blinkLeds(100);
00232         if (!useInvalidSecret) {
00233           wait_ms(100);
00234           blinkLeds(100);
00235         }
00236       }
00237       // Check node and display measurements
00238       else {
00239         SensorData sensorData;
00240         // Read sensor data with authentication
00241         switch (
00242             sensorNode.authenticatedReadSensorData(randomSeed, sensorData)) {
00243         case SensorNode::Authentic:
00244           // Update measurements on LCD
00245           displaySensorData(sensorData);
00246 
00247           // Update alarm LEDs
00248           tempAlarmLed = !sensorData.tempAlarm();             // Active Low
00249           filterLifeAlarmLed = !sensorData.filterLifeAlarm(); // Active Low
00250 
00251           // Send measurements to web if time interval reached
00252           if (webPostTimer.read_ms() >= webPostIntervalMs) {
00253             // Format, sign, and transmit data to web server
00254             result = webIntf.authPostHttpEvent(
00255                 ds2465, SensorDataEvent,
00256                 WebServerInterface::formatSensorDataPostBody(sensorData),
00257                 !useInvalidSecret);
00258             if (result) {
00259               // Reset timer count after logging sample complete
00260               webPostTimer.reset();
00261               consecutiveWebPostErrors = 0;
00262             }
00263             // There was likely an error establishing a web connection
00264             else if (++consecutiveWebPostErrors < maxConsecutiveWebPostErrors) {
00265               // Wait and try again
00266               wait_ms(webPostRetryIntervalMs);
00267             }
00268             // Too many retry attempts
00269             else {
00270               // Assume we have lost network connection
00271               nextStatus = ControllerHardwareError;
00272             }
00273           }
00274           break;
00275 
00276         case SensorNode::NotAuthentic:
00277           nextStatus = SensorNodeNotAuthentic;
00278           break;
00279 
00280         case SensorNode::UnableToCommunicate:
00281         default:
00282           nextStatus = SensorNodeHardwareError;
00283           break;
00284         }
00285       }
00286       break;
00287 
00288     case SensorNodeNotAuthentic:
00289       // Wait for some time before retrying authentication
00290       retryTimer.reset();
00291       retryTimer.start();
00292       do {
00293         // Wait for user to press Provision button
00294         if (buttonPressed(provisionButton)) {
00295           nextStatus = ProvisioningSensorNode;
00296           break;
00297         }
00298         // Try to authenticate and return to normal operation
00299         else if (webPostTimer.read_ms() >= webPostIntervalMs) {
00300           // Send event message to server
00301           result = webIntf.authPostHttpEvent(ds2465, InvalidSensorEvent, "",
00302                                              !useInvalidSecret);
00303           if (result) {
00304             // Reset timer count after logging complete
00305             webPostTimer.reset();
00306             consecutiveWebPostErrors = 0;
00307 
00308             // Try to authenticate again
00309             nextStatus = SensorNodeNeedsDetection;
00310           } else if (++consecutiveWebPostErrors < maxConsecutiveWebPostErrors) {
00311             // There was likely an error establishing a web connection
00312             // Wait and try again
00313             wait_ms(webPostRetryIntervalMs);
00314           }
00315           // Too many retry attempts
00316           else {
00317             // Assume we have lost network connection
00318             nextStatus = ControllerHardwareError;
00319             break;
00320           }
00321         }
00322       } while (retryTimer.read_ms() < webPostIntervalMs);
00323       retryTimer.stop();
00324       break;
00325 
00326     case ControllerInitializationError:
00327     case ControllerHardwareError:
00328     case SensorNodeHardwareError:
00329     default:
00330       // Do nothing until user resets
00331       break;
00332     }
00333     // Check if status changed
00334     if (currentStatus != nextStatus) {
00335       currentStatus = nextStatus;
00336       displayStatus(currentStatus); // Display status message on LCD
00337     }
00338 
00339     // Change seed value on every loop pass
00340     randomSeed++;
00341   }
00342 }
00343 
00344 /// Blink all LEDs for a certain amount of time.
00345 /// @param time_ms Time in ms to blink for.
00346 static void blinkLeds(int time_ms) {
00347   tempAlarmLed = !tempAlarmLed;
00348   filterLifeAlarmLed = !filterLifeAlarmLed;
00349   wait_ms(time_ms);
00350   tempAlarmLed = !tempAlarmLed;
00351   filterLifeAlarmLed = !filterLifeAlarmLed;
00352 }
00353 
00354 /// Check if a button is pressed and wait for it to be release.
00355 /// @param button Active low button to check.
00356 /// @returns True if pressed.
00357 static bool buttonPressed(DigitalIn & button) {
00358   const int buttonPressed = 0; // Active low
00359   if (button == buttonPressed) {
00360     while (button == buttonPressed)
00361       ;
00362     return true;
00363   }
00364   // else
00365   return false;
00366 }
00367 
00368 /// Display the current status of the Controller on the LCD display.
00369 static void displayStatus(Status status) {
00370   switch (status) {
00371   case InitializingController:
00372     lcd.writeMessage("Initializing Controller...");
00373     lcd.setBackLightColor(Teal);
00374     break;
00375 
00376   case DisplaySessionId:
00377     lcd.writeLine("ID: " + webIntf.sessionIdString(), Display::FirstLine);
00378     lcd.writeLine("Provision to begin", Display::SecondLine);
00379     lcd.setBackLightColor(Teal);
00380     break;
00381 
00382   case SensorNodeNeedsDetection:
00383     lcd.writeMessage("Insert Sensor Node and press Provision");
00384     lcd.setBackLightColor(Teal);
00385     break;
00386 
00387   case DetectingSensorNode:
00388     lcd.writeMessage("Detecting Sensor Node...");
00389     lcd.setBackLightColor(Teal);
00390     break;
00391 
00392   case SensorNodeNeedsProvision:
00393     lcd.writeMessage("Sensor Node Needs Provision");
00394     lcd.setBackLightColor(Teal);
00395     break;
00396 
00397   case ProvisioningSensorNode:
00398     lcd.writeMessage("Provisioning Sensor Node");
00399     lcd.setBackLightColor(Teal);
00400     break;
00401 
00402   case NormalOperation:
00403     // Everything handled in displaySensorData()
00404     break;
00405 
00406   case SensorNodeNotAuthentic:
00407     lcd.writeMessage("Sensor Node Not Authentic");
00408     lcd.setBackLightColor(Purple);
00409     break;
00410 
00411   case ControllerInitializationError:
00412     lcd.writeMessage("Initialization Error Check Wi-Fi");
00413     lcd.setBackLightColor(Red);
00414     break;
00415 
00416   case ControllerHardwareError:
00417     lcd.writeMessage("Controller Hardware Error: Check Wi-Fi");
00418     lcd.setBackLightColor(Red);
00419     break;
00420 
00421   case SensorNodeHardwareError:
00422     lcd.writeMessage("Sensor Node Hardware Error");
00423     lcd.setBackLightColor(Red);
00424     break;
00425   }
00426 }
00427 
00428 /// Display sensor data on the LCD display during normal operation.
00429 static void displaySensorData(const SensorData & sensorData) {
00430   std::ostringstream stream;
00431   stream << "Chiller Temp: " << static_cast<int>(sensorData.temp) << "C";
00432   lcd.writeCompleteLine(stream.str(), Display::FirstLine);
00433   stream.str(""); // Clear stream
00434   stream << "Filter Life: " << static_cast<unsigned int>(sensorData.filterLife)
00435          << "%";
00436   lcd.writeCompleteLine(stream.str(), Display::SecondLine);
00437   lcd.setBackLightColor(Green);
00438 }
00439 
00440 /// Read the Session ID to use with the web server from ROM.
00441 /// @note Session ID is taken from the ROM ID of the MAX66242.
00442 /// @param[out] Session ID string.
00443 /// @returns True on success.
00444 static bool readWebSessionId(RomId & sessionId) {
00445   const uint8_t I2C_address = 0x32;
00446   const uint8_t ROM_address = 0x68;
00447   RomId romId;
00448 
00449   // Set register pointer
00450   if (i2c.write(I2C_address, reinterpret_cast<const char *>(&ROM_address), 1) != 0)
00451     return false;
00452   // Read ROM ID
00453   if (i2c.read(I2C_address, reinterpret_cast<char *>(romId.data()),
00454                romId.size()) != 0)
00455     return false;
00456   // Check if CRC valid
00457   if (!valid(romId))
00458     return false;
00459   sessionId = romId;
00460   return true;
00461 }