DeepCover Embedded Security in IoT: Public-key Secured Data Paths

Dependencies:   MaximInterface

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers NormalOperationWindow.cpp Source File

NormalOperationWindow.cpp

00001 /*******************************************************************************
00002 * Copyright (C) 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 <stdint.h>
00034 #include <stdio.h>
00035 #include <cassert>
00036 #include <cstdio>
00037 #include <string>
00038 #include <MaximInterfaceCore/HexString.hpp>
00039 #include <MaximInterfaceCore/span.hpp>
00040 #include <MaximInterfaceDevices/DS28C36_DS2476.hpp>
00041 #include <rapidjson/stringbuffer.h>
00042 #include <rapidjson/writer.h>
00043 #include "DisplayGraphicWindow.hpp"
00044 #include "DisplayIdWindow.hpp"
00045 #include "ErrorWindow.hpp"
00046 #include "Image.hpp"
00047 #include "MakeFunction.hpp"
00048 #include "NormalOperationWindow.hpp"
00049 #include "Text.hpp"
00050 #include "WindowManager.hpp"
00051 
00052 #define TRY MaximInterfaceCore_TRY
00053 #define TRY_VALUE MaximInterfaceCore_TRY_VALUE
00054 
00055 namespace Core = MaximInterfaceCore;
00056 using MaximInterfaceDevices::DS2476;
00057 
00058 extern DS2476 coproc;
00059 extern SensorNode sensorNode;
00060 extern std::string webId;
00061 
00062 extern "C" void ComputeSHA256(unsigned char * message, short length,
00063                               unsigned short skipconst, unsigned short reverse,
00064                               unsigned char * digest);
00065 
00066 // Default allocation size for rapidjson.
00067 static const size_t defaultChunkSize = 256;
00068 
00069 // Number of decimal places to use when writing JSON.
00070 static const int jsonMaxDecimalPlaces = 2;
00071 
00072 // Separate multiple JSON commands received on the socket.
00073 // Returns a list of begin and end iterators within the input message.
00074 static std::vector<Core::span<const char> >
00075 separateCommands(Core::span<const char> receivedData) {
00076   std::vector<Core::span<const char> > commands;
00077   int counter = 0;
00078   Core::span<const char>::index_type beginIdx;
00079   for (Core::span<const char>::index_type i = 0; i < receivedData.size(); ++i) {
00080     if (receivedData[i] == '{') {
00081       if (counter == 0) {
00082         beginIdx = i;
00083       }
00084       ++counter;
00085     } else if (receivedData[i] == '}') {
00086       if (counter > 0) {
00087         --counter;
00088         if (counter == 0) {
00089           commands.push_back(
00090               Core::make_span(&receivedData[beginIdx], &receivedData[i + 1]));
00091         }
00092       }
00093     }
00094   }
00095   return commands;
00096 }
00097 
00098 Core::Result<void>
00099 NormalOperationWindow::addCommandChallenge(rapidjson::Document & document) {
00100   TRY(coproc.readRng(commandChallenge));
00101   document.AddMember("challenge",
00102                      rapidjson::Value(toHexString(commandChallenge).c_str(),
00103                                       document.GetAllocator())
00104                          .Move(),
00105                      document.GetAllocator());
00106   return Core::none;
00107 }
00108 
00109 Core::Result<void>
00110 NormalOperationWindow::signData(rapidjson::Document & document,
00111                                 bool validSignature,
00112                                 const std::vector<uint8_t> & challenge) {
00113   // Move contents of the document to a new location, and create an empty object
00114   // in the document.
00115   rapidjson::Value data(rapidjson::kObjectType);
00116   data.Swap(document);
00117   // Convert data to a string and generate a signature from that string.
00118   rapidjson::StringBuffer dataBuffer;
00119   rapidjson::Writer<rapidjson::StringBuffer> writer(dataBuffer);
00120   writer.SetMaxDecimalPlaces(jsonMaxDecimalPlaces);
00121   data.Accept(writer);
00122   std::vector<uint8_t> signDataBuffer(
00123       dataBuffer.GetString(), dataBuffer.GetString() + dataBuffer.GetLength());
00124   signDataBuffer.insert(signDataBuffer.end(), challenge.begin(),
00125                         challenge.end());
00126   uint8_t hash[32];
00127   ComputeSHA256(&signDataBuffer[0], signDataBuffer.size(), false, false, hash);
00128   TRY(coproc.writeBuffer(hash));
00129   Core::Ecc256::Signature::array signatureBuffer;
00130   TRY_VALUE(signatureBuffer, coproc.generateEcdsaSignature(DS2476::KeyNumA));
00131   if (!validSignature) {
00132     ++signatureBuffer.r[0];
00133   }
00134   // Construct the final document with the original data and the generated
00135   // signature.
00136   rapidjson::Value signature(rapidjson::kObjectType);
00137   signature.AddMember(
00138       "r",
00139       rapidjson::Value(Core::toHexString(signatureBuffer.r).c_str(),
00140                        document.GetAllocator())
00141           .Move(),
00142       document.GetAllocator());
00143   signature.AddMember(
00144       "s",
00145       rapidjson::Value(Core::toHexString(signatureBuffer.s).c_str(),
00146                        document.GetAllocator())
00147           .Move(),
00148       document.GetAllocator());
00149   document.AddMember("data", data, document.GetAllocator());
00150   document.AddMember("signature", signature, document.GetAllocator());
00151   return Core::none;
00152 }
00153 
00154 Core::Result<void> NormalOperationWindow::finalizeResponse(
00155     rapidjson::Document & document, bool validSignature,
00156     const std::vector<uint8_t> & responseChallenge) {
00157   TRY(addCommandChallenge(document));
00158   TRY(signData(document, validSignature, responseChallenge));
00159   return Core::none;
00160 }
00161 
00162 Core::Result<void>
00163 NormalOperationWindow::verifySignedData(rapidjson::Document & signedData,
00164                                         Core::span<const char> verifyDataIn) {
00165   using rapidjson::Value;
00166   using std::string;
00167 
00168   // Parse string and validate object schema.
00169   string verifyData(verifyDataIn.begin(), verifyDataIn.end());
00170   signedData.Parse(verifyData.c_str());
00171   if (!(signedData.IsObject() && signedData.HasMember("data") &&
00172         signedData.HasMember("signature"))) {
00173     signedData.RemoveAllMembers();
00174     return DS2476::AuthenticationError;
00175   }
00176   Value & data = signedData["data"];
00177   const Value & signature = signedData["signature"];
00178   if (!(data.IsObject() && signature.IsObject() && signature.HasMember("r") &&
00179         signature.HasMember("s"))) {
00180     signedData.RemoveAllMembers();
00181     return DS2476::AuthenticationError;
00182   }
00183   const Value & signatureR = signature["r"];
00184   const Value & signatureS = signature["s"];
00185   if (!(signatureR.IsString() && signatureS.IsString())) {
00186     signedData.RemoveAllMembers();
00187     return DS2476::AuthenticationError;
00188   }
00189 
00190   // Parse signature.
00191   Core::Optional<std::vector<uint8_t> > parsedBytes = Core::fromHexString(
00192       Core::make_span(signatureR.GetString(), signatureR.GetStringLength()));
00193   Core::Ecc256::Signature::array signatureBuffer;
00194   if (!(parsedBytes && parsedBytes->size() == signatureBuffer.r.size())) {
00195     signedData.RemoveAllMembers();
00196     return DS2476::AuthenticationError;
00197   }
00198   std::copy(parsedBytes->begin(), parsedBytes->end(),
00199             signatureBuffer.r.begin());
00200   parsedBytes = Core::fromHexString(
00201       Core::make_span(signatureS.GetString(), signatureS.GetStringLength()));
00202   if (!(parsedBytes && parsedBytes->size() == signatureBuffer.s.size())) {
00203     signedData.RemoveAllMembers();
00204     return DS2476::AuthenticationError;
00205   }
00206   std::copy(parsedBytes->begin(), parsedBytes->end(),
00207             signatureBuffer.s.begin());
00208 
00209   // Get data to hash.
00210   // Need to use string searching here since there isn't currently a way to
00211   // access raw elements in rapidjson, and creating another copy of the data
00212   // might consume too much memory.
00213   const string rawDataSearch("\"data\":");
00214   string::size_type rawDataBegin = verifyData.find(rawDataSearch);
00215   if ((rawDataBegin == string::npos) ||
00216       ((rawDataBegin + rawDataSearch.size()) >= verifyData.size())) {
00217     signedData.RemoveAllMembers();
00218     return DS2476::AuthenticationError;
00219   }
00220   rawDataBegin += rawDataSearch.size();
00221   string::size_type rawDataEnd =
00222       verifyData.find(",\"signature\"", rawDataBegin);
00223   if (rawDataEnd == string::npos) {
00224     signedData.RemoveAllMembers();
00225     return DS2476::AuthenticationError;
00226   }
00227   verifyData.erase(rawDataEnd);
00228   verifyData.erase(0, rawDataBegin);
00229   // Add in command challenge to data that will be verified.
00230   verifyData.append(commandChallenge.begin(), commandChallenge.end());
00231 
00232   // Compute hash of the data.
00233   Core::Result<void> result = computeMultiblockHash(
00234       coproc,
00235       Core::make_span(reinterpret_cast<const uint8_t *>(verifyData.data()),
00236                       verifyData.size()));
00237   if (!result) {
00238     signedData.RemoveAllMembers();
00239     return result;
00240   }
00241   // Verify signature.
00242   result = coproc.verifyEcdsaSignature(DS2476::KeyNumC, DS2476::THASH,
00243                                        signatureBuffer);
00244   if (!result) {
00245     signedData.RemoveAllMembers();
00246     return result;
00247   }
00248 
00249   // Strip signing information from document.
00250   rapidjson::Value swapObject(rapidjson::kObjectType);
00251   swapObject.Swap(data);
00252   swapObject.Swap(signedData);
00253   return Core::none;
00254 }
00255 
00256 void NormalOperationWindow::sendJson(const rapidjson::Value & document) {
00257   rapidjson::StringBuffer buffer;
00258   rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
00259   writer.SetMaxDecimalPlaces(jsonMaxDecimalPlaces);
00260   document.Accept(writer);
00261   socket->send(buffer.GetString(), buffer.GetLength());
00262 }
00263 
00264 void NormalOperationWindow::sendMessage(const char * message) {
00265   rapidjson::MemoryPoolAllocator<> allocator(defaultChunkSize);
00266   rapidjson::Document document(rapidjson::kObjectType, &allocator);
00267   document.AddMember("message", rapidjson::StringRef(message),
00268                      document.GetAllocator());
00269   sendJson(document);
00270 }
00271 
00272 static std::string getValidSignatureButtonText(bool validSignature) {
00273   return validSignature ? "Use invalid sig." : "Use valid sig.";
00274 }
00275 
00276 void NormalOperationWindow::showWebId(Button *) {
00277   if (windowManager()) {
00278     std::auto_ptr<Window> window(
00279         new DisplayIdWindow(DisplayIdWindow::PopupMode));
00280     windowManager()->push(window);
00281   }
00282 }
00283 
00284 void NormalOperationWindow::toggleValidSignature(Button *) {
00285   validSignature = !validSignature;
00286   validSignatureButton.setText(getValidSignatureButtonText(validSignature));
00287 }
00288 
00289 NormalOperationWindow::NormalOperationWindow (std::auto_ptr<TCPSocket> & socket)
00290     : socket(socket) /* Move construct */, sendChallenge(true),
00291       validSignature(true), lastSensorNodeState(SensorNode::Disconnected),
00292       lastObjectTemp(0), lastAmbientTemp(0) {
00293   assert(this->socket.get());
00294 
00295   validSignatureButton.setParent (this);
00296   validSignatureButton.setText(getValidSignatureButtonText(validSignature));
00297   validSignatureButton.setClickedHandler(
00298       makeFunction(this, &NormalOperationWindow::toggleValidSignature));
00299   showWebIdButton.setParent (this);
00300   showWebIdButton.setText("Show web ID");
00301   showWebIdButton.setClickedHandler(
00302       makeFunction(this, &NormalOperationWindow::showWebId));
00303   validSignatureButton.setFocused();
00304 }
00305 
00306 NormalOperationWindow::Result NormalOperationWindow::sendStatus(
00307     const std::vector<uint8_t> & responseChallenge) {
00308   rapidjson::MemoryPoolAllocator<> allocator(defaultChunkSize);
00309   rapidjson::Document document(rapidjson::kObjectType, &allocator);
00310 
00311   // Insert Web ID.
00312   document.AddMember("id", rapidjson::StringRef(webId.c_str()),
00313                      document.GetAllocator());
00314 
00315   // Insert device public key.
00316   rapidjson::Value publicKey(rapidjson::kObjectType);
00317   Core::Result<DS2476::Page::array> page =
00318       coproc.readMemory(DS2476::publicKeyAxPage);
00319   if (!page) {
00320     if (windowManager()) {
00321       std::auto_ptr<Window> window(
00322           new ErrorWindow("Failed to read Public Key A (x)"));
00323       windowManager()->push(window);
00324     }
00325     return WindowsChanged;
00326   }
00327   publicKey.AddMember("x",
00328                       rapidjson::Value(toHexString(page.value()).c_str(),
00329                                        document.GetAllocator())
00330                           .Move(),
00331                       document.GetAllocator());
00332   page = coproc.readMemory(DS2476::publicKeyAyPage);
00333   if (!page) {
00334     if (windowManager()) {
00335       std::auto_ptr<Window> window(
00336           new ErrorWindow("Failed to read Public Key A (y)"));
00337       windowManager()->push(window);
00338     }
00339     return WindowsChanged;
00340   }
00341   publicKey.AddMember("y",
00342                       rapidjson::Value(toHexString(page.value()).c_str(),
00343                                        document.GetAllocator())
00344                           .Move(),
00345                       document.GetAllocator());
00346   document.AddMember("publicKey", publicKey, document.GetAllocator());
00347 
00348   // Insert device certificate.
00349   rapidjson::Value certificate(rapidjson::kObjectType);
00350   page = coproc.readMemory(14);
00351   if (!page) {
00352     if (windowManager()) {
00353       std::auto_ptr<Window> window(
00354           new ErrorWindow("Failed to read User Data 14"));
00355       windowManager()->push(window);
00356     }
00357     return WindowsChanged;
00358   }
00359   certificate.AddMember("r",
00360                         rapidjson::Value(toHexString(page.value()).c_str(),
00361                                          document.GetAllocator())
00362                             .Move(),
00363                         document.GetAllocator());
00364   page = coproc.readMemory(15);
00365   if (!page) {
00366     if (windowManager()) {
00367       std::auto_ptr<Window> window(
00368           new ErrorWindow("Failed to read User Data 15"));
00369       windowManager()->push(window);
00370     }
00371     return WindowsChanged;
00372   }
00373   certificate.AddMember("s",
00374                         rapidjson::Value(toHexString(page.value()).c_str(),
00375                                          document.GetAllocator())
00376                             .Move(),
00377                         document.GetAllocator());
00378   document.AddMember("certificate", certificate, document.GetAllocator());
00379 
00380   // Sign data and transmit to server.
00381   if (!finalizeResponse(document, validSignature, responseChallenge)) {
00382     if (windowManager()) {
00383       std::auto_ptr<Window> window(new ErrorWindow("Failed to sign data"));
00384       windowManager()->push(window);
00385     }
00386     return WindowsChanged;
00387   }
00388   sendJson(document);
00389   return NoChange;
00390 }
00391 
00392 NormalOperationWindow::Result NormalOperationWindow::sendObjectTemp(
00393     const std::vector<uint8_t> & responseChallenge) {
00394   rapidjson::MemoryPoolAllocator<> allocator(defaultChunkSize);
00395   rapidjson::Document document(rapidjson::kObjectType, &allocator);
00396 
00397   // Read object temperature and add to document.
00398   double objectTemp;
00399   if (const Core::Result<double> sensorResult =
00400           sensorNode.readTemp(SensorNode::ObjectTemp)) {
00401     objectTemp = sensorResult.value();
00402   } else {
00403     if (windowManager()) {
00404       std::auto_ptr<Window> window(
00405           new ErrorWindow("Failed to read object temperature"));
00406       windowManager()->push(window);
00407     }
00408     return WindowsChanged;
00409   }
00410   document.AddMember("objectTemp", objectTemp, document.GetAllocator());
00411 
00412   // Sign data and transmit to server.
00413   if (!finalizeResponse(document, validSignature, responseChallenge)) {
00414     if (windowManager()) {
00415       std::auto_ptr<Window> window(new ErrorWindow("Failed to sign data"));
00416       windowManager()->push(window);
00417     }
00418     return WindowsChanged;
00419   }
00420   sendJson(document);
00421 
00422   lastObjectTemp = objectTemp;
00423   return NoChange;
00424 }
00425 
00426 NormalOperationWindow::Result NormalOperationWindow::sendAmbientTemp(
00427     const std::vector<uint8_t> & responseChallenge) {
00428   rapidjson::MemoryPoolAllocator<> allocator(defaultChunkSize);
00429   rapidjson::Document document(rapidjson::kObjectType, &allocator);
00430 
00431   // Read ambient temperature and add to document.
00432   double ambientTemp;
00433   if (const Core::Result<double> sensorResult =
00434           sensorNode.readTemp(SensorNode::AmbientTemp)) {
00435     ambientTemp = sensorResult.value();
00436   } else {
00437     if (windowManager()) {
00438       std::auto_ptr<Window> window(
00439           new ErrorWindow("Failed to read ambient temperature"));
00440       windowManager()->push(window);
00441     }
00442     return WindowsChanged;
00443   }
00444   document.AddMember("ambientTemp", ambientTemp, document.GetAllocator());
00445 
00446   // Sign data and transmit to server.
00447   if (!finalizeResponse(document, validSignature, responseChallenge)) {
00448     if (windowManager()) {
00449       std::auto_ptr<Window> window(new ErrorWindow("Failed to sign data"));
00450       windowManager()->push(window);
00451     }
00452     return WindowsChanged;
00453   }
00454   sendJson(document);
00455 
00456   lastAmbientTemp = ambientTemp;
00457   return NoChange;
00458 }
00459 
00460 void NormalOperationWindow::displayImage(
00461     const std::vector<uint8_t> & imageData) {
00462   if (windowManager()) {
00463     std::auto_ptr<Graphic> image(
00464         new Image(Bitmap(&imageData[0], imageData.size(), 64)));
00465     std::auto_ptr<Window> window(new DisplayGraphicWindow(image));
00466     windowManager()->push(window);
00467   }
00468 }
00469 
00470 NormalOperationWindow::Result
00471 NormalOperationWindow::processReceivedData(size_t recvBufSize) {
00472   // Separate commands and process each one.
00473   const std::vector<Core::span<const char> > commands =
00474       separateCommands(Core::make_span(recvBuf, recvBufSize));
00475   for (std::vector<Core::span<const char> >::const_iterator it =
00476            commands.begin();
00477        it != commands.end(); ++it) {
00478     rapidjson::MemoryPoolAllocator<> allocator(defaultChunkSize);
00479     rapidjson::Document data(&allocator);
00480     // Verify command signature.
00481     const Core::Result<void> verifySignedResult = verifySignedData(data, *it);
00482     if (verifySignedResult) {
00483       // Verify command schema.
00484       sendMessage("Received data is authentic");
00485       if (data.IsObject() && data.HasMember("command")) {
00486         const rapidjson::Value & command = data["command"];
00487         if (command.IsString()) {
00488           // Parse challenge if included.
00489           std::vector<uint8_t> responseChallenge;
00490           if (data.HasMember("challenge")) {
00491             const rapidjson::Value & challenge = data["challenge"];
00492             if (challenge.IsString()) {
00493               responseChallenge =
00494                   Core::fromHexString(
00495                       Core::make_span(challenge.GetString(),
00496                                       challenge.GetStringLength()))
00497                       .valueOr(std::vector<uint8_t>());
00498             }
00499           }
00500 
00501           // Execute the command.
00502           if (command == "getStatus") {
00503             const Result result = sendStatus(responseChallenge);
00504             if (result != NoChange) {
00505               return result;
00506             }
00507           } else if (command == "readObjectTemp") {
00508             if ((lastSensorNodeState == SensorNode::ValidLaserDisabled) ||
00509                 (lastSensorNodeState == SensorNode::ValidLaserEnabled)) {
00510               const Result result = sendObjectTemp(responseChallenge);
00511               if (result != NoChange) {
00512                 return result;
00513               }
00514               invalidate();
00515             }
00516           } else if (command == "readAmbientTemp") {
00517             if ((lastSensorNodeState == SensorNode::ValidLaserDisabled) ||
00518                 (lastSensorNodeState == SensorNode::ValidLaserEnabled)) {
00519               const Result result = sendAmbientTemp(responseChallenge);
00520               if (result != NoChange) {
00521                 return result;
00522               }
00523               invalidate();
00524             }
00525           } else if (command == "enableModule") {
00526             if (lastSensorNodeState == SensorNode::ValidLaserDisabled) {
00527               if (!sensorNode.setLaserEnabled(
00528                       true, makeFunction(
00529                                 this, &NormalOperationWindow::sendMessage))) {
00530                 lastSensorNodeState = SensorNode::ValidLaserEnabled;
00531                 invalidate();
00532               }
00533             }
00534           } else if (command == "disableModule") {
00535             if (lastSensorNodeState == SensorNode::ValidLaserEnabled) {
00536               if (!sensorNode.setLaserEnabled(
00537                       false, makeFunction(
00538                                  this, &NormalOperationWindow::sendMessage))) {
00539                 lastSensorNodeState = SensorNode::ValidLaserDisabled;
00540                 invalidate();
00541               }
00542             }
00543           } else if (command == "displayImage") {
00544             if (data.HasMember("image")) {
00545               const rapidjson::Value & image = data["image"];
00546               if (image.IsString()) {
00547                 displayImage(Core::fromHexString(
00548                                  Core::make_span(image.GetString(),
00549                                                  image.GetStringLength()))
00550                                  .valueOr(std::vector<uint8_t>()));
00551                 return WindowsChanged;
00552               }
00553             }
00554           }
00555         }
00556       }
00557     } else if (verifySignedResult.error() == DS2476::AuthenticationError) {
00558       const char message[] = "Received data is not authentic";
00559       sendMessage(message);
00560       std::auto_ptr<Graphic> messageText(new Text);
00561       Text & messageTextRef = *static_cast<Text *>(messageText.get());
00562       messageTextRef.setText(message);
00563       messageTextRef.setWordWrap(true);
00564       if (windowManager()) {
00565         std::auto_ptr<Window> window(new DisplayGraphicWindow(messageText));
00566         windowManager()->push(window);
00567       }
00568       return WindowsChanged;
00569     } else {
00570       const char message[] = "Unable to verify received data";
00571       sendMessage(message);
00572       if (windowManager()) {
00573         std::auto_ptr<Window> window(new ErrorWindow(message));
00574         windowManager()->push(window);
00575       }
00576       return WindowsChanged;
00577     }
00578   }
00579   return NoChange;
00580 }
00581 
00582 void NormalOperationWindow::resized() {
00583   showWebIdButton.resize(width(), showWebIdButton.preferredHeight());
00584   showWebIdButton.move(0, height() - showWebIdButton.height());
00585   validSignatureButton.resize(width(), validSignatureButton.preferredHeight());
00586   validSignatureButton.move(0, showWebIdButton.y() -
00587                                    validSignatureButton.height() - 1);
00588 }
00589 
00590 static std::string doubleToString(double input) {
00591   char inputString[8];
00592   snprintf(inputString, sizeof(inputString) / sizeof(inputString[0]), "%.2f",
00593            input);
00594   return std::string(inputString);
00595 }
00596 
00597 void NormalOperationWindow::doRender(Bitmap & bitmap, int xOffset,
00598                                      int yOffset) const {
00599   // Format current status text.
00600   std::string sensorNodeStateText;
00601   switch (lastSensorNodeState) {
00602   case SensorNode::Disconnected:
00603     sensorNodeStateText = "Disconnected";
00604     break;
00605 
00606   case SensorNode::Invalid:
00607     sensorNodeStateText = "Invalid";
00608     break;
00609 
00610   case SensorNode::ValidLaserDisabled:
00611     sensorNodeStateText = "Valid, laser disabled";
00612     break;
00613 
00614   case SensorNode::ValidLaserEnabled:
00615     sensorNodeStateText = "Valid, laser enabled";
00616     break;
00617   }
00618 
00619   Text description;
00620   description.setText("Object temp: " + doubleToString(lastObjectTemp) +
00621                       "\nAmbient temp: " + doubleToString(lastAmbientTemp) +
00622                       "\nSensor node: " + sensorNodeStateText);
00623   description.resize(width(), validSignatureButton.y());
00624   description.setWordWrap(true);
00625   description.render(bitmap, xOffset + x(), yOffset + y());
00626   validSignatureButton.render(bitmap, xOffset + x(), yOffset + y());
00627   showWebIdButton.render(bitmap, xOffset + x(), yOffset + y());
00628 }
00629 
00630 void NormalOperationWindow::updated() {
00631   // Detect sensor node.
00632   const SensorNode::State  sensorNodeState = sensorNode.detect();
00633   if (sensorNodeState != lastSensorNodeState) {
00634     lastSensorNodeState = sensorNodeState;
00635     invalidate();
00636   }
00637 
00638   // Send challenge on first connection.
00639   if (sendChallenge) {
00640     rapidjson::MemoryPoolAllocator<> allocator(defaultChunkSize);
00641     rapidjson::Document document(rapidjson::kObjectType, &allocator);
00642     if (addCommandChallenge(document)) {
00643       sendJson(document);
00644       sendChallenge = false;
00645     }
00646   } else {
00647     // Process socket data.
00648     const int recvResult =
00649         socket->recv(recvBuf, sizeof(recvBuf) / sizeof(recvBuf[0]));
00650     if (recvResult > 0) {
00651       std::printf("%*s\n", recvResult, recvBuf);
00652       const Result result = processReceivedData(recvResult);
00653       if (result != NoChange)
00654         return;
00655     } else if (recvResult != NSAPI_ERROR_WOULD_BLOCK) {
00656       if (windowManager()) {
00657         std::auto_ptr<Window> window(new ErrorWindow("Socket receive failed"));
00658         windowManager()->push(window);
00659       }
00660       return;
00661     }
00662   }
00663 }
00664 
00665 bool NormalOperationWindow::doProcessKey(Key key) {
00666   bool handled;
00667   switch (key) {
00668   case UpKey:
00669     validSignatureButton.setFocused();
00670     handled = true;
00671     break;
00672 
00673   case DownKey:
00674     showWebIdButton.setFocused();
00675     handled = true;
00676     break;
00677 
00678   default:
00679     handled = false;
00680     break;
00681   }
00682   return handled;
00683 }