EnigmaIOT  0.9.8
Secure sensor and gateway platform based on ESP8266 and ESP32
EnigmaIOTGateway.cpp
Go to the documentation of this file.
1 
9 #include "EnigmaIOTGateway.h"
10 #include <FS.h>
11 #include "libb64/cdecode.h"
12 #include <ArduinoJson.h>
13 #ifdef ESP8266
14 #include <Updater.h>
15 #elif defined ESP32
16 #include <Update.h>
17 #include <esp_wifi.h>
18 #endif
19 
20 #include "cryptModule.h"
21 #include "helperFunctions.h"
22 #include <cstddef>
23 #include <cstdint>
24 #include <regex>
25 
26 #if SUPPORT_HA_DISCOVERY
27 #include "haEntity.h"
28 #include "haBinarySensor.h"
29 #include "haCover.h"
30 #include "haSensor.h"
31 #include "haSwitch.h"
32 #include "haTrigger.h"
33 #endif // SUPPORT_HA_DISCOVERY
34 
35 const char CONFIG_FILE[] = "/config.json";
36 
37 bool shouldSave = false;
38 bool OTAongoing = false;
39 time_t lastOTAmsg = 0;
40 
41 
43  DEBUG_INFO ("Configuration saving activated");
44  shouldSave = true;
45 }
46 
48  DEBUG_INFO ("Configuration reset started");
49  if (FILESYSTEM.remove (CONFIG_FILE)){
50  DEBUG_WARN ("Configuration file removed");
51  }
52  ESP.restart ();
53 }
54 
56  return (shouldSave);
57 }
58 
59 void EnigmaIOTGatewayClass::setTxLed (uint8_t led, time_t onTime) {
60  this->txled = led;
61  txLedOnTime = onTime;
62  pinMode (txled, OUTPUT);
63  digitalWrite (txled, LED_OFF);
64 }
65 
66 void EnigmaIOTGatewayClass::setRxLed (uint8_t led, time_t onTime) {
67  this->rxled = led;
68  rxLedOnTime = onTime;
69  pinMode (rxled, OUTPUT);
70  digitalWrite (rxled, LED_OFF);
71 }
72 
73 const void* memstr (const void* str, size_t str_size,
74  const char* target, size_t target_size) {
75  const uint8_t* pointer = (const uint8_t*)str;
76  for (size_t i = 0; i != str_size - target_size; ++i) {
77  if (!memcmp (pointer + i, target, target_size)) {
78  return pointer + i;
79  }
80  }
81 
82  return NULL;
83 }
84 
85 bool buildGetVersion (uint8_t* data, size_t& dataLen, const uint8_t* inputData, size_t inputLen) {
86  DEBUG_DBG ("Build 'Get Version' message from: %s", printHexBuffer (inputData, inputLen));
87  if (dataLen < 1) {
88  return false;
89  }
90  data[0] = (uint8_t)control_message_type::VERSION;
91  dataLen = 1;
92  return true;
93 }
94 
95 bool buildGetSleep (uint8_t* data, size_t& dataLen, const uint8_t* inputData, size_t inputLen) {
96  DEBUG_VERBOSE ("Build 'Get Sleep' message from: %s", printHexBuffer (inputData, inputLen));
97  if (dataLen < 1) {
98  return false;
99  }
101  dataLen = 1;
102  return true;
103 }
104 
105 bool buildSetIdentify (uint8_t* data, size_t& dataLen, const uint8_t* inputData, size_t inputLen) {
106  DEBUG_VERBOSE ("Build 'Set Identify' message from: %s", printHexBuffer (inputData, inputLen));
107  if (dataLen < 1) {
108  return false;
109  }
110  data[0] = (uint8_t)control_message_type::IDENTIFY;
111  dataLen = 1;
112  return true;
113 }
114 
115 bool buildGetRSSI (uint8_t* data, size_t& dataLen, const uint8_t* inputData, size_t inputLen) {
116  DEBUG_VERBOSE ("Build 'Get RSSI' message from: %s", printHexBuffer (inputData, inputLen));
117  if (dataLen < 1) {
118  return false;
119  }
120  data[0] = (uint8_t)control_message_type::RSSI_GET;
121  dataLen = 1;
122  return true;
123 }
124 
125 bool buildGetName (uint8_t* data, size_t& dataLen, const uint8_t* inputData, size_t inputLen) {
126  DEBUG_VERBOSE ("Build 'Get Node Name and Address' message from: %s", printHexBuffer (inputData, inputLen));
127  if (dataLen < 1) {
128  return false;
129  }
130  data[0] = (uint8_t)control_message_type::NAME_GET;
131  dataLen = 1;
132  return true;
133 }
134 
135 bool buildSetName (uint8_t* data, size_t& dataLen, const uint8_t* inputData, size_t inputLen) {
136  DEBUG_VERBOSE ("Build 'Set Node Name' message from: %s", printHexBuffer (inputData, inputLen));
137  if (dataLen < NODE_NAME_LENGTH + 1) {
138  DEBUG_ERROR ("Not enough space to build message");
139  return false;
140  }
141  if (inputLen < 2 || inputLen > NODE_NAME_LENGTH) {
142  DEBUG_ERROR ("Name too short");
143  return false;
144  }
145  data[0] = (uint8_t)control_message_type::NAME_SET;
146  memcpy (data + 1, inputData, inputLen);
147  dataLen = 1 + inputLen;
148  return true;
149 }
150 
151 bool buildSetResetConfig (uint8_t* data, size_t& dataLen, const uint8_t* inputData, size_t inputLen) {
152  DEBUG_VERBOSE ("Build 'Reset Config' message from: %s", printHexBuffer (inputData, inputLen));
153  if (dataLen < 1) {
154  return false;
155  }
156  data[0] = (uint8_t)control_message_type::RESET;
157  dataLen = 1;
158  return true;
159 }
160 
161 bool buildRestartNode (uint8_t* data, size_t& dataLen, const uint8_t* inputData, size_t inputLen) {
162  DEBUG_VERBOSE ("Build 'Restart Node' message from: %s", printHexBuffer (inputData, inputLen));
163  if (dataLen < 1) {
164  return false;
165  }
167  dataLen = 1;
168  return true;
169 }
170 
171 bool buildSendBrcastKey (uint8_t* data, size_t& dataLen, const uint8_t* key, size_t keyLen) {
172  DEBUG_VERBOSE ("Build 'Send Broadcast Key' message from: %s", printHexBuffer (key, keyLen));
173  if (key && keyLen == KEY_LENGTH) {
175  memcpy (data + 1, key, keyLen);
176  dataLen = keyLen + 1;
177  return true;
178  } else {
179  return false;
180  }
181 
182 }
183 
184 int getNextNumber (char*& data, size_t& len/*, char* &position*/) {
185  char strNum[10];
186  int number;
187  char* tempData = data;
188  size_t tempLen = len;
189 
190  for (int i = 0; i < 10; i++) {
191  //DEBUG_DBG ("Processing char: %c", tempData[i]);
192  if (tempData[i] != ',') {
193  if (tempData[i] >= '0' && tempData[i] <= '9') {
194  strNum[i] = tempData[i];
195  } else {
196  DEBUG_ERROR ("OTA message format error. Message number not found");
197  number = -1;
198  }
199  if (i == 9) {
200  DEBUG_ERROR ("OTA message format error, separator not found");
201  number = -2;
202  }
203  } else {
204  if (i == 0) {
205  DEBUG_ERROR ("OTA message format error, cannot find a number");
206  number = -3;
207  }
208  strNum[i] = '\0';
209  //DEBUG_DBG ("Increment pointer by %d", i);
210  tempData += i;
211  tempLen -= i;
212  break;
213  }
214  }
215  if (tempData[0] == ',' && tempLen > 0) {
216  tempData++;
217  tempLen--;
218  } else {
219  DEBUG_WARN ("OTA message format warning. separator not found");
220  }
221  number = atoi (strNum);
222  data = tempData;
223  len = tempLen;
224  DEBUG_DBG ("Extracted number %d", number);
225  DEBUG_DBG ("Resulting data %s", data);
226  //DEBUG_WARN ("Resulting length %d", len);
227  return number;
228 }
229 
230 bool isHexChar (char c) {
231  //DEBUG_DBG ("Is Hex Char %c", c);
232  return (
233  (c >= '0' && c <= '9')
234  || (c >= 'a' && c <= 'f')
235  //|| c >= 'A' && c <= 'F'
236  );
237 }
238 
239 bool buildOtaMsg (uint8_t* data, size_t& dataLen, const uint8_t* inputData, size_t inputLen) {
240  char* payload;
241  size_t payloadLen;
242  int number;
243  uint8_t* tempData = data;
244 
245  DEBUG_VERBOSE ("Build 'OTA' message from: %s", inputData);
246 
247  payload = (char*)inputData;
248  payloadLen = inputLen;
249 
250  // Get message number
251  number = getNextNumber (payload, payloadLen);
252  if (number < 0) {
253  return false;
254  }
255  uint16_t msgIdx = number;
256 
257  tempData[0] = (uint8_t)control_message_type::OTA;
258  tempData++;
259  memcpy (tempData, &msgIdx, sizeof (uint16_t));
260  size_t decodedLen = sizeof (uint8_t) + sizeof (uint16_t);
261  tempData += sizeof (uint16_t);
262 
263  DEBUG_INFO ("OTA message number %u", msgIdx);
264  //DEBUG_INFO ("Payload len = %u", payloadLen);
265  //DEBUG_INFO ("Payload data: %s", payload);
266 
267  if (msgIdx > 0) {
268  decodedLen += base64_decode_chars (payload, payloadLen, (char*)(data + 1 + sizeof (uint16_t)));
269  lastOTAmsg = millis ();
270  } else {
271  OTAongoing = true;
272  lastOTAmsg = millis ();
273 
274  if (inputLen < 39) {
275  DEBUG_ERROR ("OTA message format error. Message #0 too short to be a MD5 string");
276  return false;
277  }
278 
279  // Get firmware size
280  number = getNextNumber (payload, payloadLen);
281  if (number < 0) {
282  return false;
283  }
284  uint32_t fileSize = number;
285 
286  memcpy (tempData, &fileSize, sizeof (uint32_t));
287  tempData += sizeof (uint32_t);
288  decodedLen += sizeof (uint32_t);
289 
290 
291  // Get number of chunks
292  number = getNextNumber (payload, payloadLen);
293  if (number < 0) {
294  return false;
295  }
296  uint16_t msgNum = number;
297 
298  memcpy (tempData, &msgNum, sizeof (uint16_t));
299  tempData += sizeof (uint16_t);
300  decodedLen += sizeof (uint16_t);
301 
302  DEBUG_WARN ("Number of OTA chunks %u", msgNum);
303  DEBUG_WARN ("OTA length = %u bytes", fileSize);
304  //DEBUG_INFO ("Payload data: %s", payload);
305 
306  if (payloadLen < 32) {
307  DEBUG_ERROR ("OTA message format error. MD5 is too short: %d", payloadLen);
308  return false;
309  }
310 
311  for (size_t i = 0; i < 32; i++) {
312  if (!isHexChar (payload[i])) {
313  DEBUG_ERROR ("OTA message format error. MD5 string has no valid format");
314  return false;
315  }
316  *tempData = (uint8_t)payload[i];
317  tempData++;
318  decodedLen++;
319  }
320 
321  DEBUG_VERBOSE ("Payload data: %s", printHexBuffer (data, decodedLen));
322  }
323 
324  if ((decodedLen) > MAX_MESSAGE_LENGTH) {
325  DEBUG_ERROR ("OTA message too long. %u bytes.", decodedLen);
326  return false;
327  }
328  dataLen = decodedLen;
329  DEBUG_VERBOSE ("Payload has %u bytes of data: %s", dataLen, printHexBuffer (data, dataLen));
330  return true;
331 }
332 
333 bool buildSetSleep (uint8_t* data, size_t& dataLen, const uint8_t* inputData, size_t inputLen) {
334  DEBUG_VERBOSE ("Build 'Set Sleep' message from: %s", printHexBuffer (inputData, inputLen));
335  if (dataLen < 5) {
336  DEBUG_ERROR ("Not enough space to build message");
337  return false;
338  }
339 
340  if (inputLen <= 1) {
341  DEBUG_ERROR ("Set sleep time value is empty");
342  return false;
343  }
344 
345  for (unsigned int i = 0; i < (inputLen - 1); i++) { // Check if all digits are number
346  if (inputData[i] < 30 || inputData[i] > '9') {
347  DEBUG_ERROR ("Set sleep time value is not a number on position %d: %d", i, inputData[i]);
348  return false;
349  }
350  }
351  if (inputData[inputLen - 1] != 0) { // Array should end with \0
352  DEBUG_ERROR ("Set sleep time value does not end with \\0");
353  return false;
354  }
355 
356  uint32_t sleepTime = atoi ((char*)inputData);
357 
359  memcpy (data + 1, &sleepTime, sizeof (uint32_t));
360  dataLen = 5;
361  return true;
362 }
363 
364 bool EnigmaIOTGatewayClass::sendDownstream (uint8_t* mac, const uint8_t* data, size_t len, control_message_type_t controlData, gatewayPayloadEncoding_t encoding, char* nodeName) {
365  Node* node;
366  if (nodeName) {
367  node = nodelist.getNodeFromName (nodeName);
368  if (node) {
369  DEBUG_DBG ("Message to node %s with address %s", nodeName, mac2str (node->getMacAddress ()));
370  }
371  } else {
372  node = nodelist.getNodeFromMAC (mac);
373  }
374 
375  uint8_t downstreamData[MAX_MESSAGE_LENGTH];
376 
377  if (len == 0 && (controlData == USERDATA_GET || controlData == USERDATA_SET))
378  return false;
379 
380  DEBUG_VERBOSE ("Downstream: %s", printHexBuffer (data, len));
381  DEBUG_DBG ("Downstream message type 0x%02X", controlData);
382 
383  size_t dataLen = MAX_MESSAGE_LENGTH;
384 
385  switch (controlData) {
387  if (!buildGetVersion (downstreamData, dataLen, data, len)) {
388  DEBUG_ERROR ("Error building get Version message");
389  return false;
390  }
391  DEBUG_VERBOSE ("Get Version. Len: %d Data %s", dataLen, printHexBuffer (downstreamData, dataLen));
392  break;
394  if (!buildGetSleep (downstreamData, dataLen, data, len)) {
395  DEBUG_ERROR ("Error building get Sleep message");
396  return false;
397  }
398  DEBUG_VERBOSE ("Get Sleep. Len: %d Data %s", dataLen, printHexBuffer (downstreamData, dataLen));
399  break;
401  if (!buildSetSleep (downstreamData, dataLen, data, len)) {
402  DEBUG_ERROR ("Error building set Sleep message");
403  return false;
404  }
405  DEBUG_VERBOSE ("Set Sleep. Len: %d Data %s", dataLen, printHexBuffer (downstreamData, dataLen));
406  break;
408  if (!buildOtaMsg (downstreamData, dataLen, data, len)) {
409  DEBUG_ERROR ("Error building OTA message");
410  return false;
411  }
412  DEBUG_VERBOSE ("OTA message. Len: %d Data %s", dataLen, printHexBuffer (downstreamData, dataLen));
413  break;
415  if (!buildSetIdentify (downstreamData, dataLen, data, len)) {
416  DEBUG_ERROR ("Error building Identify message");
417  return false;
418  }
419  DEBUG_VERBOSE ("Identify message. Len: %d Data %s", dataLen, printHexBuffer (downstreamData, dataLen));
420  break;
422  if (!buildSetResetConfig (downstreamData, dataLen, data, len)) {
423  DEBUG_ERROR ("Error building Reset message");
424  return false;
425  }
426  DEBUG_VERBOSE ("Reset Config message. Len: %d Data %s", dataLen, printHexBuffer (downstreamData, dataLen));
427  break;
429  if (!buildGetRSSI (downstreamData, dataLen, data, len)) {
430  DEBUG_ERROR ("Error building get RSSI message");
431  return false;
432  }
433  DEBUG_VERBOSE ("Get RSSI message. Len: %d Data %s", dataLen, printHexBuffer (downstreamData, dataLen));
434  break;
436  if (!buildGetName (downstreamData, dataLen, data, len)) {
437  DEBUG_ERROR ("Error building get name message");
438  return false;
439  }
440  DEBUG_VERBOSE ("Get name message. Len: %d Data %s", dataLen, printHexBuffer (downstreamData, dataLen));
441  break;
443  if (!buildSetName (downstreamData, dataLen, data, len)) {
444  DEBUG_ERROR ("Error building set name message");
445  return false;
446  }
447  DEBUG_VERBOSE ("Set name message. Len: %d Data %s", dataLen, printHexBuffer (downstreamData, dataLen));
448  break;
450  if (!buildRestartNode (downstreamData, dataLen, data, len)) {
451  DEBUG_ERROR ("Error building restart node message");
452  return false;
453  }
454  DEBUG_VERBOSE ("Restart node message. Len: %d Data %s", dataLen, printHexBuffer (downstreamData, dataLen));
455  break;
457  if (!buildSendBrcastKey (downstreamData, dataLen, nodelist.getBroadcastNode ()->getEncriptionKey (), KEY_LENGTH)) {
458  DEBUG_ERROR ("Error building broadcast key message");
459  return false;
460  }
461  DEBUG_VERBOSE ("Broadcast key message. Len: %d Data %s", dataLen, printHexBuffer (downstreamData, dataLen));
462  break;
464  DEBUG_INFO ("Data message GET");
465  break;
467  DEBUG_INFO ("Data message SET");
468  break;
469  default:
470  return false;
471  }
472 
473 
474  DEBUG_INFO ("Send downstream");
475 
476  if (node) {
477  if (controlData != control_message_type::USERDATA_GET && controlData != control_message_type::USERDATA_SET)
478  return downstreamDataMessage (node, downstreamData, dataLen, controlData);
479  else if (controlData == control_message_type::OTA) {
480  if (node->getSleepy ()) {
481  DEBUG_ERROR ("Node must be in non sleepy mode to receive OTA messages");
482  return false;
483  } else
484  return downstreamDataMessage (node, data, len, controlData);
485  } else
486  return downstreamDataMessage (node, data, len, controlData, encoding);
487  } else {
488  //char addr[ENIGMAIOT_ADDR_LEN * 3];
489  DEBUG_ERROR ("Downlink destination %s not found", nodeName ? nodeName : mac2str (mac));
490  return false;
491  }
492 }
493 
495  server = new AsyncWebServer (80);
496  dns = new DNSServer ();
497  wifiManager = new AsyncWiFiManager (server, dns);
498 
499  char networkKey[33] = "";
500  //char networkName[NETWORK_NAME_LENGTH] = "";
501  char channel[4];
502  //String (gwConfig.channel).toCharArray (channel, 4);
503  snprintf (channel, 4, "%u", gwConfig.channel);
504 
505  //AsyncWiFiManager wifiManager (&server, &dns);
506  AsyncWiFiManagerParameter netNameParam ("netname", "Network Name", gwConfig.networkName, (int)NETWORK_NAME_LENGTH - 1, "required type=\"text\" pattern=\"^[^/\\\\]+$\" maxlength=20");
507  AsyncWiFiManagerParameter netKeyParam ("netkey", "NetworkKey", networkKey, 33, "required type=\"password\" minlength=\"8\" maxlength=\"32\"");
508  AsyncWiFiManagerParameter channelParam ("channel", "WiFi Channel", channel, 4, "required type=\"number\" min=\"0\" max=\"13\" step=\"1\"");
509 
510  wifiManager->setCustomHeadElement ("<style>input:invalid {border: 2px dashed red;input:valid{border: 2px solid black;}</style>");
511  wifiManager->addParameter (&netKeyParam);
512  wifiManager->addParameter (&channelParam);
513  wifiManager->addParameter (&netNameParam);
514  wifiManager->addParameter (new AsyncWiFiManagerParameter ("<br>"));
515 
518  }
519 
520  wifiManager->setDebugOutput (true);
521 #if CONNECT_TO_WIFI_AP != 1
522  wifiManager->setBreakAfterConfig (true);
523 #endif // CONNECT_TO_WIFI_AP
524  wifiManager->setTryConnectDuringConfigPortal (false);
525  wifiManager->setSaveConfigCallback (doSave);
526  wifiManager->setConfigPortalTimeout (150);
527 
528 #if CONNECT_TO_WIFI_AP == 1
529  boolean result = wifiManager->autoConnect ("EnigmaIoTGateway", NULL, 3, 2000);
530 #else
531  boolean result = wifiManager->startConfigPortal ("EnigmaIoTGateway", NULL);
532  result = true; // Force true if this should not connect to a WiFi
533 #endif // CONNECT_TO_WIFI_AP
534 
535  DEBUG_INFO ("==== Config Portal result ====");
536  DEBUG_INFO ("Network Name: %s", netNameParam.getValue ());
537  DEBUG_INFO ("Network Key: %s", netKeyParam.getValue ());
538  DEBUG_INFO ("Channel: %s", channelParam.getValue ());
539  DEBUG_INFO ("Status: %s", result ? "true" : "false");
540  DEBUG_INFO ("Save config: %s", shouldSave ? "yes" : "no");
541  if (result) {
542  if (shouldSave) {
543  bool regexResult;
544 #ifdef ESP32
545  std::regex networkNameRegex ("^[^/\\\\]+$");
546  regexResult = std::regex_match (netNameParam.getValue (), networkNameRegex);
547 #else
548  regexResult = true;
549 #endif
550  if (regexResult) {
551  strncpy (this->gwConfig.networkName, netNameParam.getValue (), NETWORK_NAME_LENGTH - 1);
552  DEBUG_DBG ("Network name: %s", gwConfig.networkName);
553  } else {
554  DEBUG_WARN ("Network name parameter error");
555  result = false;
556  }
557 
558 #ifdef ESP32
559  std::regex netKeyRegex ("^.{8,32}$");
560  regexResult = std::regex_match (netKeyParam.getValue (), netKeyRegex);
561 #endif
562  if (regexResult) {
563  uint8_t keySize = netKeyParam.getValueLength ();
564  if (keySize > KEY_LENGTH)
565  keySize = KEY_LENGTH;
566  const char* netKey = netKeyParam.getValue ();
567  if (netKey && (netKey[0] != '\0')) {// If password is empty, keep the old one
568  memset (this->gwConfig.networkKey, 0, KEY_LENGTH);
569  memcpy (this->gwConfig.networkKey, netKey, keySize);
570  memcpy (this->plainNetKey, netKey, keySize);
572  DEBUG_DBG ("Raw network Key: %s", printHexBuffer (this->gwConfig.networkKey, KEY_LENGTH));
573  } else {
574  DEBUG_INFO ("Network key password field empty. Keeping the old one");
575  }
576  } else {
577  DEBUG_WARN ("Network key parameter error");
578  result = false;
579  }
580 
581 #ifdef ESP32
582  std::regex channelRegex ("^([0-9]|[0-1][0-3])$");
583  regexResult = std::regex_match (channelParam.getValue (), channelRegex);
584 #endif
585  if (regexResult) {
586  this->gwConfig.channel = atoi (channelParam.getValue ());
587  DEBUG_DBG ("WiFi ESP-NOW channel: %d", this->gwConfig.channel);
588  } else {
589  DEBUG_WARN ("Network name parameter error");
590  result = false;
591  }
592  } else {
593  DEBUG_DBG ("Configuration does not need to be saved");
594  }
595  } else {
596  DEBUG_ERROR ("WiFi connection unsuccessful. Restarting");
597  ESP.restart ();
598  }
599 
600  if (notifyWiFiManagerExit) {
601  notifyWiFiManagerExit (result);
602  }
603 
604  delete (server);
605  delete (dns);
606  delete (wifiManager);
607 
608  return result;
609 }
610 
612  //FILESYSTEM.remove (CONFIG_FILE); // Only for testing
613  bool json_correct = false;
614 
615  if (FILESYSTEM.exists (CONFIG_FILE)) {
616 
617  DEBUG_DBG ("Opening %s file", CONFIG_FILE);
618  File configFile = FILESYSTEM.open (CONFIG_FILE, "r");
619  if (configFile) {
620  //size_t size = configFile.size ();
621  DEBUG_DBG ("%s opened. %u bytes", CONFIG_FILE, configFile.size ());
622 
623  const size_t capacity = JSON_OBJECT_SIZE (4) + 160;
624  DynamicJsonDocument doc (capacity);
625 
626  DeserializationError error = deserializeJson (doc, configFile);
627  if (error) {
628  DEBUG_ERROR ("Failed to parse file");
629  } else {
630  DEBUG_DBG ("JSON file parsed");
631  }
632 
633  configFile.close ();
634 
635  if (doc.containsKey ("type")) {
636  if (!strcmp ("gw", doc["type"])) {
637 
638  if (doc.containsKey ("channel") && doc.containsKey ("networkKey")
639  && doc.containsKey ("networkName")) {
640  json_correct = true;
641  }
642  } else {
643  FILESYSTEM.remove (CONFIG_FILE);
644  DEBUG_ERROR ("Wrong configuration. Removing file %s", CONFIG_FILE);
645  return false;
646  }
647  }
648 
649  gwConfig.channel = doc["channel"].as<int> ();
650  strncpy ((char*)gwConfig.networkKey, doc["networkKey"] | "", sizeof (gwConfig.networkKey));
651  strncpy (gwConfig.networkName, doc["networkName"] | "", sizeof (gwConfig.networkName));
652 
653  if (json_correct) {
654  DEBUG_VERBOSE ("Gateway configuration successfuly read");
655  }
656  DEBUG_DBG ("==== EnigmaIOT Gateway Configuration ====");
657  DEBUG_DBG ("Network name: %s", gwConfig.networkName);
658  DEBUG_DBG ("WiFi channel: %u", gwConfig.channel);
659  DEBUG_VERBOSE ("Network key: %s", gwConfig.networkKey);
660  strncpy (plainNetKey, (char*)gwConfig.networkKey, KEY_LENGTH);
662  DEBUG_VERBOSE ("Raw Network key: %s", printHexBuffer (gwConfig.networkKey, KEY_LENGTH));
663 
664 #if DEBUG_LEVEL >= DBG
665  char* output;
666  size_t json_len = measureJsonPretty (doc) + 1;
667  output = (char*)malloc (json_len);
668  serializeJsonPretty (doc, output, json_len);
669 
670  DEBUG_DBG ("JSON file %s", output);
671  free (output);
672 #endif
673 
674  } else {
675  DEBUG_WARN ("Error opening %s", CONFIG_FILE);
676  }
677  } else {
678  DEBUG_WARN ("%s do not exist", CONFIG_FILE);
679  //FILESYSTEM.format (); // Testing only
680  //WiFi.begin ("0", "0"); // Delete WiFi credentials
681  //DEBUG_WARN ("Dummy STA config loaded");
682  //return false;
683  }
684 
685  if (!json_correct) {
686  WiFi.begin ("0", "0"); // Delete WiFi credentials
687  DEBUG_WARN ("Dummy STA config loaded");
688  }
689  return json_correct;
690 }
691 
693  File configFile = FILESYSTEM.open (CONFIG_FILE, "w");
694  if (!configFile) {
695  DEBUG_WARN ("failed to open config file %s for writing", CONFIG_FILE);
696  return false;
697  }
698 
699  const size_t capacity = JSON_OBJECT_SIZE (4) + 160;
700  DynamicJsonDocument doc (capacity);
701 
702  doc["type"] = "gw";
703  doc["channel"] = gwConfig.channel;
704  doc["networkKey"] = plainNetKey;
705  doc["networkName"] = gwConfig.networkName;
706 
707  if (serializeJson (doc, configFile) == 0) {
708  DEBUG_ERROR ("Failed to write to file");
709  configFile.close ();
710  //FILESYSTEM.remove (CONFIG_FILE); // Testing only
711  return false;
712  }
713 
714 #if DEBUG_LEVEL >= DBG
715  char* output;
716  size_t json_len = measureJsonPretty (doc) + 1;
717  output = (char*)malloc (json_len);
718  serializeJsonPretty (doc, output, json_len);
719 
720  DEBUG_DBG ("\n%s", output);
721 
722  free (output);
723 #endif
724 
725  configFile.flush ();
726  //size_t size = configFile.size ();
727 
728  configFile.close ();
729 
730  //memset (networkKey, 0, KEY_LENGTH);
731 
732  DEBUG_DBG ("Gateway configuration saved to flash. %u bytes", configFile.size ());
733  return true;
734 }
735 
736 void EnigmaIOTGatewayClass::begin (Comms_halClass* comm, uint8_t* networkKey, bool useDataCounter) {
738  this->comm = comm;
739  this->useCounter = useDataCounter;
740 
741  uint8_t broadcastKey[KEY_LENGTH];
743  CryptModule::random (broadcastKey, KEY_LENGTH); // Generate random broadcast key
744  DEBUG_DBG ("Broadcast key: %s", printHexBuffer (broadcastKey, KEY_LENGTH));
745  nodelist.getBroadcastNode ()->setEncryptionKey (broadcastKey);
746 
747  if (networkKey) {
748  memcpy (this->gwConfig.networkKey, networkKey, KEY_LENGTH);
749  strncpy (plainNetKey, (char*)networkKey, KEY_LENGTH);
751  } else {
752  if (!FILESYSTEM.begin ()) {
753  DEBUG_ERROR ("Error mounting flash");
754  FILESYSTEM.format ();
755  DEBUG_ERROR ("Formatted");
756  ESP.restart ();
757  return;
758  }
759  if (!loadFlashData ()) { // Load from flash
760  if (configWiFiManager ()) {
761  if (shouldSave) {
762  DEBUG_DBG ("Got configuration. Storing");
763  if (saveFlashData ()) {
764  DEBUG_DBG ("Network Key stored on flash");
765  } else {
766  DEBUG_ERROR ("Error saving data on flash");
767  }
768  ESP.restart ();
769  } else {
770  DEBUG_INFO ("Configuration has not to be saved");
771  }
772  } else {
773  DEBUG_ERROR ("Configuration error. Restarting");
774  ESP.restart ();
775  }
776  } else {
777  DEBUG_INFO ("Configuration loaded from flash");
778  }
779 
782  comm->onDataRcvd (rx_cb);
783  comm->onDataSent (tx_cb);
784 
785 #if ENABLE_REST_API
786  DEBUG_INFO ("GW API started");
787  GwAPI.begin ();
788 #endif
789  }
790 }
791 
792 bool EnigmaIOTGatewayClass::addInputMsgQueue (const uint8_t* addr, const uint8_t* msg, size_t len) {
793  msg_queue_item_t message;
794 
795  message.len = len;
796  memcpy (message.data, msg, len);
797  memcpy (message.addr, addr, ENIGMAIOT_ADDR_LEN);
798 
799 #ifdef ESP32
800  portENTER_CRITICAL (&myMutex);
801 #else
802  noInterrupts ();
803 #endif
804  input_queue->push (&message);
805  //char macstr[ENIGMAIOT_ADDR_LEN * 3];
806  DEBUG_DBG ("Message 0x%02X added from %s. Size: %d", message.data[0], mac2str (message.addr), input_queue->size ());
807 #ifdef ESP32
808  portEXIT_CRITICAL (&myMutex);
809 #else
810  interrupts ();
811 #endif
812  return true;
813 }
814 
816 
817  msg_queue_item_t* message;
818 #ifdef esp32
819  portENTER_CRITICAL (&myMutex);
820 #else
821  noInterrupts ();
822 #endif
823  message = input_queue->front ();
824  if (message) {
825  DEBUG_DBG ("EnigmaIOT message got from queue. Size: %d", input_queue->size ());
826  memcpy (buffer->data, message->data, message->len);
827  memcpy (buffer->addr, message->addr, ENIGMAIOT_ADDR_LEN);
828  buffer->len = message->len;
829  popInputMsgQueue ();
830  }
831 #ifdef esp32
832  portEXIT_CRITICAL (&myMutex);
833 #else
834  interrupts ();
835 #endif
836  if (message) {
837  return buffer;
838  } else {
839  return NULL;
840  }
841 }
842 
844  if (input_queue->pop ()) {
845  DEBUG_DBG ("EnigmaIOT message pop. Size %d", input_queue->size ());
846  }
847 }
848 
849 void EnigmaIOTGatewayClass::rx_cb (uint8_t* mac_addr, uint8_t* data, uint8_t len) {
850 
851  EnigmaIOTGateway.addInputMsgQueue (mac_addr, data, len);
852 }
853 
854 void EnigmaIOTGatewayClass::tx_cb (uint8_t* mac_addr, uint8_t status) {
855  EnigmaIOTGateway.getStatus (mac_addr, status);
856 }
857 
858 void EnigmaIOTGatewayClass::getStatus (uint8_t* mac_addr, uint8_t status) {
859  //char buffer[ENIGMAIOT_ADDR_LEN * 3];
860 #ifdef ESP8266
861  DEBUG_VERBOSE ("SENDStatus %s. Peer %s", status == 0 ? "OK" : "ERROR", mac2str (mac_addr));
862 #elif defined ESP32
863  DEBUG_VERBOSE ("SENDStatus %d. Peer %s", status, mac2str (mac_addr));
864 #endif
865 }
866 
868  //#ifdef ESP8266
869  static unsigned long rxOntime;
870  static unsigned long txOntime;
871 
872  if (flashRx) {
873  DEBUG_DBG ("EnigmaIOTGatewayClass::flashrx");
874 
875  if (rxled == txled) {
876  flashTx = true;
877  } else {
878  rxOntime = millis ();
879  digitalWrite (rxled, LED_ON);
880  }
881  flashRx = false;
882  }
883 
884  if (rxled != txled) {
885  if ( millis () - rxOntime > rxLedOnTime) {
886  digitalWrite (rxled, LED_OFF);
887  }
888  }
889 
890  if (flashTx) {
891  txOntime = millis ();
892  digitalWrite (txled, LED_ON);
893  flashTx = false;
894  }
895 
896  if ( millis () - txOntime > txLedOnTime) {
897  digitalWrite (txled, LED_OFF);
898  }
899  //#endif
900 
901  // Clean up dead nodes
902  for (int i = 0; i < NUM_NODES; i++) {
904  if (MAX_NODE_INACTIVITY > 0) {
905  if (node->isRegistered () && millis () - node->getLastMessageTime () > MAX_NODE_INACTIVITY) {
906  // TODO. Trigger node expired event
907  node->reset ();
908  }
909  }
910  }
911 
912  if (OTAongoing) {
913  time_t currentTime = millis ();
914  if ((currentTime - lastOTAmsg) > OTA_GW_TIMEOUT) {
915  OTAongoing = false;
916  DEBUG_WARN ("OTA ongoing = false");
917  DEBUG_WARN ("millis() = %u, lastOTAmsg = %u, diff = %d", currentTime, lastOTAmsg, currentTime - lastOTAmsg);
918  }
919  }
920 
921  // Check input EnigmaIOT message queue
922 
923  if (!input_queue->empty ()) {
924  msg_queue_item_t* message;
925 
926  message = getInputMsgQueue (&tempBuffer);
927 
928  if (message) {
929  DEBUG_DBG ("EnigmaIOT input message from queue. MsgType: 0x%02X", message->data[0]);
930  manageMessage (message->addr, message->data, message->len);
931  }
932  }
933 }
934 
935 void EnigmaIOTGatewayClass::manageMessage (const uint8_t* mac, uint8_t* buf, uint8_t count) {
936  Node* node;
937 
938  DEBUG_INFO ("Reveived message. Origin MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
939  DEBUG_VERBOSE ("Received data: %s", printHexBuffer (buf, count));
940 
941  if (count <= 1) {
942  DEBUG_WARN ("Empty message");
943  return;
944  }
945 
946  node = nodelist.getNewNode (mac);
947 
948  flashRx = true;
949 
950  int espNowError = 0; // May I remove this??
951 
952  switch (buf[0]) {
953  case CLIENT_HELLO:
954  // TODO: Do no accept new Client Hello if registration is on process on any node?? Possible DoS Attack??
955  // May cause undesired behaviour in case a node registration message is lost
956  DEBUG_INFO (" <------- CLIENT HELLO");
957  //if (!OTAongoing) {
958  if (espNowError == 0) {
959  if (processClientHello (mac, buf, count, node)) {
960  if (serverHello (myPublicKey, node)) {
961  DEBUG_INFO ("Server Hello sent");
962  node->setStatus (REGISTERED);
963  node->setKeyValidFrom (millis ());
964  node->setLastMessageCounter (0);
965  node->setLastControlCounter (0);
966  node->setLastDownlinkMsgCounter (0);
967  node->setLastMessageTime ();
968  if (notifyNewNode) {
969  notifyNewNode (node->getMacAddress (), node->getNodeId (), NULL);
970  }
971 #if DEBUG_LEVEL >= INFO
973 #endif
974  if (node->broadcastIsEnabled ()) {
975  if (!sendBroadcastKey (node)) {
976  DEBUG_WARN ("Error sending broadcast key to node");
977  } else {
978  node->setBroadcastKeyRequested (false);
979  DEBUG_INFO ("Broadcast key sent to node");
980  }
981  }
982  } else {
983  node->reset ();
984  DEBUG_INFO ("Error sending Server Hello");
985  }
986 
987  } else {
988  // Ignore message in case of error
989  //invalidateKey (node, WRONG_CLIENT_HELLO);
990  node->reset ();
991  DEBUG_ERROR ("Error processing client hello");
992  }
993  } else {
994  DEBUG_ERROR ("Error adding peer %d", espNowError);
995  }
996  //} else {
997  // DEBUG_WARN ("OTA ongoing. Registration ignored");
998  //}
999  break;
1000  case CONTROL_DATA:
1001  DEBUG_INFO (" <------- CONTROL MESSAGE");
1002  if (node->getStatus () == REGISTERED) {
1003  if (processControlMessage (mac, buf, count, node)) {
1004  DEBUG_INFO ("Control message OK");
1005  if (MAX_KEY_VALIDITY > 0) {
1006  if (millis () - node->getKeyValidFrom () > MAX_KEY_VALIDITY) {
1008  }
1009  }
1010  } else {
1013  }
1014  DEBUG_WARN ("Control message not OK");
1015  }
1016  } else {
1018  }
1019  break;
1020  case SENSOR_DATA:
1021  case UNENCRYPTED_NODE_DATA:
1022 #if SUPPORT_HA_DISCOVERY
1023  case HA_DISCOVERY_MESSAGE:
1024 #endif // SUPPORT_HA_DISCOVERY
1025  {
1026  bool encrypted = false;
1027  if (buf[0] == SENSOR_DATA) {
1028  DEBUG_INFO (" <------- ENCRYPTED DATA");
1029  encrypted = true;
1030  }
1031 #if SUPPORT_HA_DISCOVERY
1032  else if (buf[0] == HA_DISCOVERY_MESSAGE) {
1033  DEBUG_INFO (" <------- HA_DISCOVERY_MESSAGE");
1034  encrypted = true;
1035  }
1036 #endif // SUPPORT_HA_DISCOVERY
1037  else {
1038  DEBUG_INFO (" <------- UNENCRYPTED DATA");
1039  encrypted = false;
1040  }
1041  //if (!OTAongoing) {
1042  if (node->getStatus () == REGISTERED) {
1043  float packetsHour = (float)1 / ((millis () - node->getLastMessageTime ()) / (float)3600000);
1044  node->updatePacketsRate (packetsHour);
1045  if (processDataMessage (mac, buf, count, node, encrypted)) {
1046  node->setLastMessageTime ();
1047  DEBUG_INFO ("Data OK");
1048  DEBUG_VERBOSE ("Key valid from %lu ms", millis () - node->getKeyValidFrom ());
1049  if (MAX_KEY_VALIDITY > 0) {
1050  if (millis () - node->getKeyValidFrom () > MAX_KEY_VALIDITY) {
1052  }
1053  }
1054  } else {
1057  }
1058  DEBUG_WARN ("Data not OK");
1059  }
1060  } else {
1062  node->reset ();
1063  }
1064  //} else {
1065  // DEBUG_WARN ("Data ignored. OTA ongoing");
1066  //}
1067  break;
1068  }
1069  case CLOCK_REQUEST:
1070  DEBUG_INFO (" <------- CLOCK REQUEST");
1071  if (node->getStatus () == REGISTERED) {
1072  if (processClockRequest (mac, buf, count, node)) {
1073  DEBUG_INFO ("Clock request OK");
1074  if (MAX_KEY_VALIDITY > 0) {
1075  if (millis () - node->getKeyValidFrom () > MAX_KEY_VALIDITY) {
1077  }
1078  }
1079  } else {
1081  DEBUG_WARN ("Clock request not OK");
1082  }
1083 
1084  } else {
1086  }
1087  break;
1088  case NODE_NAME_SET:
1089  DEBUG_INFO (" <------- NODE NAME REQUEST");
1090  if (node->getStatus () == REGISTERED) {
1091  if (processNodeNameSet (mac, buf, count, node)) {
1092  DEBUG_INFO ("Node name for node %d set to %s", node->getNodeId (), node->getNodeName ());
1093  if (notifyNewNode) {
1094  notifyNewNode (node->getMacAddress (), node->getNodeId (), node->getNodeName ());
1095  }
1096  } else {
1097  DEBUG_WARN ("Error setting node name for node %d", node->getNodeId ());
1098  }
1099  }
1100  break;
1101  default:
1102  DEBUG_INFO ("Received unknown EnigmaIOT message 0x%02X");
1103  }
1104 }
1105 
1107  /*
1108  * ------------------------------------------------------------------
1109  *| msgType (1) | IV (12) | Counter (2) | Result code (1) | tag (16) |
1110  * ------------------------------------------------------------------
1111  */
1112  struct __attribute__ ((packed, aligned (1))) {
1113  uint8_t msgType;
1114  uint8_t iv[IV_LENGTH];
1115  uint16_t counter;
1116  int8_t errorCode;
1117  uint8_t tag[TAG_LENGTH];
1118  } nodeNameSetResponse_msg;
1119 
1120  uint16_t counter;
1121 
1122  const unsigned int NNSRMSG_LEN = sizeof (nodeNameSetResponse_msg);
1123 
1124  nodeNameSetResponse_msg.msgType = NODE_NAME_RESULT;
1125 
1126  if (useCounter) {
1127  counter = node->getLastDownlinkMsgCounter () + 1;
1128  node->setLastDownlinkMsgCounter (counter);
1129  } else {
1130  counter = (uint16_t)(Crypto.random ());
1131  }
1132  DEBUG_INFO ("Downlink message #%d", counter);
1133 
1134  memcpy (&(nodeNameSetResponse_msg.counter), &counter, sizeof (uint16_t));
1135 
1136  DEBUG_DBG ("Set node name Response. Error code: %d", error);
1137 
1138  CryptModule::random (nodeNameSetResponse_msg.iv, IV_LENGTH);
1139 
1140  DEBUG_VERBOSE ("IV: %s", printHexBuffer (nodeNameSetResponse_msg.iv, IV_LENGTH));
1141 
1142  nodeNameSetResponse_msg.errorCode = error;
1143 
1144  const uint8_t addDataLen = 1 + IV_LENGTH;
1145  uint8_t aad[AAD_LENGTH + addDataLen];
1146 
1147  memcpy (aad, (uint8_t*)&nodeNameSetResponse_msg, addDataLen); // Copy message upto iv
1148 
1149  // Copy 8 last bytes from node key
1150  memcpy (aad + addDataLen, node->getEncriptionKey () + KEY_LENGTH - AAD_LENGTH, AAD_LENGTH);
1151 
1152  if (!CryptModule::encryptBuffer ((uint8_t*)&(nodeNameSetResponse_msg.errorCode), sizeof (int8_t), // Encrypt error code only, 1 byte
1153  nodeNameSetResponse_msg.iv, IV_LENGTH,
1154  node->getEncriptionKey (), KEY_LENGTH - AAD_LENGTH, // Use first 24 bytes of network key
1155  aad, sizeof (aad), nodeNameSetResponse_msg.tag, TAG_LENGTH)) {
1156  DEBUG_ERROR ("Error during encryption");
1157  return false;
1158  }
1159 
1160  DEBUG_VERBOSE ("Encrypted set node name response message: %s", printHexBuffer ((uint8_t*)&nodeNameSetResponse_msg, NNSRMSG_LEN));
1161 
1162  DEBUG_INFO (" -------> SEND SET NODE NAME RESPONSE");
1163  uint8_t* addr = node->getMacAddress ();
1164  //char addrStr[ENIGMAIOT_ADDR_LEN * 3];
1165  if (comm->send (addr, (uint8_t*)&nodeNameSetResponse_msg, NNSRMSG_LEN) == 0) {
1166  DEBUG_INFO ("Set Node Name Response message sent to %s", mac2str (addr));
1167  return true;
1168  } else {
1170  DEBUG_ERROR ("Error sending Set Node Name Response message to %s", mac2str (addr));
1171  return false;
1172  }
1173 }
1174 
1175 bool EnigmaIOTGatewayClass::processNodeNameSet (const uint8_t mac[ENIGMAIOT_ADDR_LEN], uint8_t* buf, size_t count, Node* node) {
1176  /*
1177  * ------------------------------------------------------------------------------------
1178  *| msgType (1) | IV (12) | NodeID (2) | Counter (2) | Node name (up to 32) | tag (16) |
1179  * ------------------------------------------------------------------------------------
1180  */
1181  int8_t error = 0;
1182 
1183  char nodeName[NODE_NAME_LENGTH];
1184  memset ((void*)nodeName, 0, NODE_NAME_LENGTH);
1185 
1186  uint8_t iv_idx = 1;
1187  uint8_t nodeId_idx = iv_idx + IV_LENGTH;
1188  uint8_t counter_idx = nodeId_idx + sizeof (int16_t);
1189  uint8_t nodeName_idx = counter_idx + sizeof (int16_t);
1190  uint8_t tag_idx = count - TAG_LENGTH;
1191 
1192  uint16_t counter;
1193 
1194  const uint8_t addDataLen = 1 + IV_LENGTH;
1195  uint8_t aad[AAD_LENGTH + addDataLen];
1196 
1197  memcpy (aad, buf, addDataLen); // Copy message upto iv
1198  // Copy 8 last bytes from NetworkKey
1199  memcpy (aad + addDataLen, node->getEncriptionKey () + KEY_LENGTH - AAD_LENGTH, AAD_LENGTH);
1200 
1201  uint8_t packetLen = count - TAG_LENGTH;
1202 
1203  if (!CryptModule::decryptBuffer (buf + nodeId_idx, packetLen - 1 - IV_LENGTH, // Decrypt from nodeId
1204  buf + iv_idx, IV_LENGTH,
1205  node->getEncriptionKey (), KEY_LENGTH - AAD_LENGTH, // Use first 24 bytes of network key
1206  aad, sizeof (aad), buf + tag_idx, TAG_LENGTH)) {
1207  DEBUG_ERROR ("Error during decryption");
1208  error = -4; // Message error
1209  }
1210 
1211  memcpy (&counter, &(buf[counter_idx]), sizeof (uint16_t));
1212  DEBUG_INFO ("Node Id %d. Control message #%d", node->getNodeId (), counter);
1213  if (useCounter) {
1214  if (counter > node->getLastControlCounter ()) {
1215  DEBUG_INFO ("Accepted");
1216  node->setLastControlCounter (counter);
1217  } else {
1218  DEBUG_WARN ("Control message rejected");
1219  return false;
1220  }
1221  }
1222 
1223  if (!error) {
1224  DEBUG_VERBOSE ("Decripted node name set message: %s", printHexBuffer (buf, count - TAG_LENGTH));
1225 
1226  size_t nodeNameLen = tag_idx - nodeName_idx;
1227 
1228  DEBUG_DBG ("Node name length: %d bytes", nodeNameLen);
1229 
1230  if (nodeNameLen >= NODE_NAME_LENGTH) {
1231  nodeNameLen = NODE_NAME_LENGTH - 1;
1232  }
1233 
1234  memcpy ((void*)nodeName, (void*)(buf + nodeName_idx), nodeNameLen);
1235 
1236  error = nodelist.checkNodeName (nodeName, mac);
1237  }
1238 
1239  //nodeNameSetRespose (node, error);
1240 
1241  if (error) {
1242  return false;
1243  } else {
1244  node->setNodeName (nodeName);
1245  DEBUG_INFO ("Node name set to %s", node->getNodeName ());
1246  return true;
1247  }
1248 }
1249 
1250 bool EnigmaIOTGatewayClass::processControlMessage (const uint8_t mac[ENIGMAIOT_ADDR_LEN], uint8_t* buf, size_t count, Node* node) {
1251  /*
1252  * ----------------------------------------------------------------------------------------
1253  *| msgType (1) | IV (12) | length (2) | NodeId (2) | Counter (2) | Data (....) | Tag (16) |
1254  * ----------------------------------------------------------------------------------------
1255  */
1256 
1257  uint8_t iv_idx = 1;
1258  uint8_t length_idx = iv_idx + IV_LENGTH;
1259  uint8_t nodeId_idx = length_idx + sizeof (int16_t);
1260  uint8_t counter_idx = nodeId_idx + sizeof (int16_t);
1261  uint8_t data_idx = counter_idx + sizeof (int16_t);
1262  uint8_t tag_idx = count - TAG_LENGTH;
1263 
1264  uint16_t counter;
1265 
1266  const uint8_t addDataLen = 1 + IV_LENGTH;
1267  uint8_t aad[AAD_LENGTH + addDataLen];
1268 
1269  memcpy (aad, buf, addDataLen); // Copy message upto iv
1270 
1271  // Copy 8 last bytes from NetworkKey
1272  memcpy (aad + addDataLen, node->getEncriptionKey () + KEY_LENGTH - AAD_LENGTH, AAD_LENGTH);
1273 
1274  uint8_t packetLen = count - TAG_LENGTH;
1275 
1276  if (!CryptModule::decryptBuffer (buf + length_idx, packetLen - 1 - IV_LENGTH, // Decrypt from nodeId
1277  buf + iv_idx, IV_LENGTH,
1278  node->getEncriptionKey (), KEY_LENGTH - AAD_LENGTH, // Use first 24 bytes of network key
1279  aad, sizeof (aad), buf + tag_idx, TAG_LENGTH)) {
1280  DEBUG_ERROR ("Error during decryption");
1281  return false;
1282  }
1283 
1284  DEBUG_VERBOSE ("Decripted control message: %s", printHexBuffer (buf, count - TAG_LENGTH));
1285 
1286  memcpy (&counter, &(buf[counter_idx]), sizeof (uint16_t));
1287  DEBUG_INFO ("Node Id %d. Control message #%d", node->getNodeId (), counter);
1288  if (useCounter) {
1289  if (counter > node->getLastControlCounter ()) {
1290  DEBUG_INFO ("Accepted");
1291  node->setLastControlCounter (counter);
1292  } else {
1293  DEBUG_WARN ("Control message rejected. Last counter: %u. Current counter", node->getLastControlCounter (), counter);
1294  return false;
1295  }
1296  }
1297 
1298  // Check if command informs about a sleepy mode change
1299  const uint8_t* payload = buf + data_idx;
1300  if (payload[0] == control_message_type::SLEEP_ANS && (tag_idx - data_idx) >= 5) {
1301  uint32_t sleepTime;
1302  DEBUG_DBG ("Check if sleepy mode has changed for node");
1303  memcpy (&sleepTime, payload + 1, sizeof (uint32_t));
1304  if (sleepTime > 0) {
1305  DEBUG_DBG ("Set node to sleepy mode");
1306  node->setSleepy (true);
1307  } else {
1308  DEBUG_DBG ("Set node to non sleepy mode");
1309  node->setSleepy (false);
1310  }
1311  }
1312 
1313  DEBUG_DBG ("Payload length: %d bytes", tag_idx - data_idx);
1314 
1315  char* nodeName = node->getNodeName ();
1316 
1317  if (notifyData) {
1318  notifyData (const_cast<uint8_t*>(mac), buf + data_idx, tag_idx - data_idx, 0, true, ENIGMAIOT, nodeName ? nodeName : NULL);
1319  }
1320 
1321  return true;
1322 }
1323 
1324 bool EnigmaIOTGatewayClass::processUnencryptedDataMessage (const uint8_t mac[ENIGMAIOT_ADDR_LEN], uint8_t* buf, size_t count, Node* node) {
1325  /*
1326  * ------------------------------------------------------------------------
1327  *| msgType (1) | NodeId (2) | Counter (2) | PayloadType (1) | Data (....) |
1328  * ------------------------------------------------------------------------
1329  */
1330 
1331  uint8_t nodeId_idx = 1;
1332  uint8_t counter_idx = nodeId_idx + sizeof (int16_t);
1333  uint8_t payloadType_idx = counter_idx + sizeof (int16_t);
1334  uint8_t data_idx = payloadType_idx + sizeof (int8_t);
1335 
1336  uint16_t counter;
1337  size_t lostMessages = 0;
1338 
1339  //uint8_t packetLen = count; // Not used
1340 
1341  DEBUG_VERBOSE ("Unencrypted data message: %s", printHexBuffer (buf, count));
1342 
1343  node->packetNumber++;
1344 
1345  memcpy (&counter, &buf[counter_idx], sizeof (uint16_t));
1346  if (useCounter) {
1347  if (counter > node->getLastMessageCounter ()) {
1348  lostMessages = counter - node->getLastMessageCounter () - 1;
1349  node->packetErrors += lostMessages;
1350  node->setLastMessageCounter (counter);
1351  } else {
1352  DEBUG_WARN ("Data counter error %d : %d", counter, node->getLastMessageCounter ());
1353  return false;
1354  }
1355  }
1356 
1357  char* nodeName = node->getNodeName ();
1358 
1359  if (notifyData) {
1360  notifyData (const_cast<uint8_t*>(mac), &(buf[data_idx]), count - data_idx, lostMessages, false, RAW, nodeName ? nodeName : NULL);
1361  }
1362 
1363  if (node->getSleepy ()) {
1364  if (node->qMessagePending) {
1365  DEBUG_INFO (" -------> DOWNLINK QUEUED DATA");
1366  flashTx = true;
1367  node->qMessagePending = false;
1368  return comm->send (node->getMacAddress (), node->queuedMessage, node->qMessageLength) == 0;
1369  }
1370  }
1371 
1372  return true;
1373 
1374 }
1375 
1376 
1377 bool EnigmaIOTGatewayClass::processDataMessage (const uint8_t mac[ENIGMAIOT_ADDR_LEN], uint8_t* buf, size_t count, Node* node, bool encrypted) {
1378  /*
1379  * ----------------------------------------------------------------------------------------
1380  *| msgType (1) | IV (12) | length (2) | NodeId (2) | Counter (2) | Data (....) | Tag (16) |
1381  * ----------------------------------------------------------------------------------------
1382  */
1383 
1384  if (!encrypted) {
1385  return processUnencryptedDataMessage (mac, buf, count, node);
1386  }
1387 
1388  uint8_t iv_idx = 1;
1389  uint8_t length_idx = iv_idx + IV_LENGTH;
1390  uint8_t nodeId_idx = length_idx + sizeof (int16_t);
1391  uint8_t counter_idx = nodeId_idx + sizeof (int16_t);
1392  uint8_t encoding_idx = counter_idx + sizeof (int16_t);
1393  uint8_t data_idx = encoding_idx + sizeof (int8_t);
1394  uint8_t tag_idx = count - TAG_LENGTH;
1395 
1396  uint16_t counter;
1397  size_t lostMessages = 0;
1398 
1399  const uint8_t addDataLen = 1 + IV_LENGTH;
1400  uint8_t aad[AAD_LENGTH + addDataLen];
1401 
1402  memcpy (aad, buf, addDataLen); // Copy message upto iv
1403 
1404  // Copy 8 last bytes from NetworkKey
1405  memcpy (aad + addDataLen, node->getEncriptionKey () + KEY_LENGTH - AAD_LENGTH, AAD_LENGTH);
1406 
1407  uint8_t packetLen = count - TAG_LENGTH;
1408 
1409  if (!CryptModule::decryptBuffer (buf + length_idx, packetLen - 1 - IV_LENGTH, // Decrypt from nodeId
1410  buf + iv_idx, IV_LENGTH,
1411  node->getEncriptionKey (), KEY_LENGTH - AAD_LENGTH, // Use first 24 bytes of network key
1412  aad, sizeof (aad), buf + tag_idx, TAG_LENGTH)) {
1413  DEBUG_ERROR ("Error during decryption");
1414  return false;
1415  }
1416  DEBUG_VERBOSE ("Decrypted data message: %s", printHexBuffer (buf, count - TAG_LENGTH));
1417  DEBUG_DBG ("Data payload encoding: 0x%02X", buf[encoding_idx]);
1418  node->packetNumber++;
1419 
1420  memcpy (&counter, &(buf[counter_idx]), sizeof (uint16_t));
1421  DEBUG_INFO ("Node Id %d. Data message #%d", node->getNodeId (), counter);
1422  if (useCounter) {
1423  if (counter > node->getLastMessageCounter ()) {
1424  DEBUG_INFO ("Accepted");
1425  lostMessages = counter - node->getLastMessageCounter () - 1;
1426  node->packetErrors += lostMessages;
1427  node->setLastMessageCounter (counter);
1428  } else {
1429  DEBUG_WARN ("Data message rejected");
1430  return false;
1431  }
1432  }
1433 
1434  char* nodeName = node->getNodeName ();
1435 #if SUPPORT_HA_DISCOVERY
1436  if (buf[0] == HA_DISCOVERY_MESSAGE) {
1437  sendHADiscoveryJSON (const_cast<uint8_t*>(mac), &(buf[data_idx]), tag_idx - data_idx, gwConfig.networkName, nodeName ? nodeName : NULL);
1438  } else
1439 #endif // SUPPORT_HA_DISCOVERY
1440  if (buf[0] == SENSOR_DATA && notifyData) {
1441  //DEBUG_WARN ("Notify data %d", input_queue->size());
1442  notifyData (const_cast<uint8_t*>(mac), &(buf[data_idx]), tag_idx - data_idx, lostMessages, false, (gatewayPayloadEncoding_t)(buf[encoding_idx]), nodeName ? nodeName : NULL);
1443  } else {
1444  DEBUG_WARN ("Wrong message type. Possible memory corruption");
1445  }
1446 
1447  if (node->getSleepy ()) {
1448  if (node->qMessagePending) {
1449  DEBUG_INFO (" -------> DOWNLINK QUEUED DATA");
1450  flashTx = true;
1451  node->qMessagePending = false;
1452  return comm->send (node->getMacAddress (), node->queuedMessage, node->qMessageLength) == 0;
1453  }
1454  }
1455 
1456  return true;
1457 
1458 }
1459 
1460 double EnigmaIOTGatewayClass::getPER (uint8_t* address) {
1461  Node* node = nodelist.getNewNode (address);
1462 
1463  if (node->packetNumber > 0) {
1464  node->per = (double)node->packetErrors / (double)node->packetNumber;
1465  }
1466 
1467  return node->per;
1468 }
1469 
1470 uint32_t EnigmaIOTGatewayClass::getTotalPackets (uint8_t* address) {
1471  Node* node = nodelist.getNewNode (address);
1472 
1473  return node->packetNumber + getErrorPackets (address);
1474 }
1475 
1476 uint32_t EnigmaIOTGatewayClass::getErrorPackets (uint8_t* address) {
1477  Node* node = nodelist.getNewNode (address);
1478 
1479  return node->packetErrors;
1480 }
1481 
1482 double EnigmaIOTGatewayClass::getPacketsHour (uint8_t* address) {
1483  Node* node = nodelist.getNewNode (address);
1484 
1485  return node->packetsHour;
1486 }
1487 
1488 
1489 bool EnigmaIOTGatewayClass::downstreamDataMessage (Node* node, const uint8_t* data, size_t len, control_message_type_t controlData, gatewayPayloadEncoding_t encoding) {
1490  /*
1491  * ----------------------------------------------------------------------------------------
1492  *| msgType (1) | IV (12) | length (2) | NodeId (2) | Counter (2) | Data (....) | Tag (16) |
1493  * ----------------------------------------------------------------------------------------
1494  */
1495 
1496  uint8_t buffer[MAX_MESSAGE_LENGTH];
1497  uint16_t packet_length;
1498  bool broadcast = false;
1499 
1500  if (!node->isRegistered ()) {
1501  DEBUG_VERBOSE ("Error sending downstream. Node is not registered");
1502  return false;
1503  }
1504 
1505  uint16_t nodeId = node->getNodeId ();
1506  uint16_t counter;
1507 
1508  uint8_t iv_idx = 1;
1509  uint8_t length_idx = iv_idx + IV_LENGTH;
1510  uint8_t nodeId_idx = length_idx + sizeof (int16_t);
1511  uint8_t counter_idx = nodeId_idx + sizeof (int16_t);
1512  uint8_t data_idx;
1513  uint8_t encoding_idx; // Only for user data
1514  if (controlData == USERDATA_GET || controlData == USERDATA_SET) {
1515  encoding_idx = counter_idx + sizeof (int16_t);
1516  data_idx = encoding_idx + sizeof (int8_t);
1517  buffer[encoding_idx] = encoding;
1518  packet_length = 1 + IV_LENGTH + sizeof (int16_t) + sizeof (int16_t) + sizeof (int16_t) + 1 + len;
1519  } else {
1520  data_idx = counter_idx + sizeof (int16_t);
1521  packet_length = 1 + IV_LENGTH + sizeof (int16_t) + sizeof (int16_t) + sizeof (int16_t) + len;
1522  }
1523  uint8_t tag_idx = data_idx + len;
1524 
1525  if (!data) {
1526  DEBUG_ERROR ("Downlink message buffer empty");
1527  return false;
1528  }
1529  if (len > MAX_MESSAGE_LENGTH - 25) {
1530  DEBUG_ERROR ("Downlink message too long: %d bytes", len);
1531  return false;
1532  }
1533 
1534  if (!memcmp (node->getMacAddress (), BROADCAST_ADDRESS, ENIGMAIOT_ADDR_LEN)) {
1535  DEBUG_DBG ("Encoding broadcast message");
1536  broadcast = true;
1537  }
1538 
1539  if (controlData == control_message_type::USERDATA_GET) {
1540  buffer[0] = (uint8_t)DOWNSTREAM_DATA_GET;
1541  } else if (controlData == control_message_type::USERDATA_SET) {
1542  buffer[0] = (uint8_t)DOWNSTREAM_DATA_SET;
1543  } else {
1544  buffer[0] = (uint8_t)DOWNSTREAM_CTRL_DATA;
1545  }
1546 
1547  if (broadcast) {
1548  buffer[0] = buffer[0] | 0x80; // Mark message as broadcast
1549  DEBUG_DBG ("Broadcast message. Type: 0x%X", buffer[0]);
1550  }
1551 
1552  CryptModule::random (buffer + iv_idx, IV_LENGTH);
1553 
1554  DEBUG_VERBOSE ("IV: %s", printHexBuffer (buffer + iv_idx, IV_LENGTH));
1555 
1556  memcpy (buffer + nodeId_idx, &nodeId, sizeof (uint16_t));
1557 
1558  if (useCounter) {
1559  if (!broadcast) {
1560  counter = node->getLastDownlinkMsgCounter () + 1;
1561  node->setLastDownlinkMsgCounter (counter);
1562  } else {
1563  counter = nodelist.getLastBroadcastMsgCounter () + 1;
1565  }
1566  } else {
1567  counter = (uint16_t)(Crypto.random ());
1568  }
1569  DEBUG_INFO ("Downlink message #%d", counter);
1570 
1571  memcpy (buffer + counter_idx, &counter, sizeof (uint16_t));
1572 
1573  memcpy (buffer + data_idx, data, len);
1574 
1575  DEBUG_VERBOSE ("Data: %s", printHexBuffer (buffer + data_idx, len));
1576 
1577  memcpy (buffer + length_idx, &packet_length, sizeof (uint16_t));
1578 
1579  DEBUG_VERBOSE ("Downlink message: %s", printHexBuffer (buffer, packet_length));
1580  DEBUG_VERBOSE ("Message length: %d bytes", packet_length);
1581 
1582  //uint8_t* crypt_buf = buffer + length_idx;
1583 
1584  //size_t cryptLen = packet_length - length_idx;
1585 
1586  const uint8_t addDataLen = 1 + IV_LENGTH;
1587  uint8_t aad[AAD_LENGTH + addDataLen];
1588 
1589  memcpy (aad, buffer, addDataLen); // Copy message upto iv
1590 
1591  // Copy 8 last bytes from Node Key
1592  memcpy (aad + addDataLen, node->getEncriptionKey () + KEY_LENGTH - AAD_LENGTH, AAD_LENGTH);
1593 
1594  if (!CryptModule::encryptBuffer (buffer + length_idx, packet_length - addDataLen, // Encrypt from length
1595  buffer + iv_idx, IV_LENGTH,
1596  node->getEncriptionKey (), KEY_LENGTH - AAD_LENGTH, // Use first 24 bytes of node key
1597  aad, sizeof (aad), buffer + tag_idx, TAG_LENGTH)) {
1598  DEBUG_ERROR ("Error during encryption");
1599  return false;
1600  }
1601 
1602  //DEBUG_WARN ("Encryption key: %s", printHexBuffer (node->getEncriptionKey (), KEY_LENGTH));
1603  DEBUG_VERBOSE ("Encrypted downlink message: %s", printHexBuffer (buffer, packet_length + TAG_LENGTH));
1604 
1605  if (node->getSleepy ()) { // Queue message if node may be sleeping
1606  if (controlData != control_message_type::OTA) {
1607  DEBUG_VERBOSE ("Node is sleepy. Queing message");
1608  memcpy (node->queuedMessage, buffer, packet_length + TAG_LENGTH);
1609  //node->queuedMessage = buffer;
1610  node->qMessageLength = packet_length + TAG_LENGTH;
1611  node->qMessagePending = true;
1612  return true;
1613  } else {
1614  DEBUG_ERROR ("OTA is only possible with non sleepy nodes. Configure it accordingly first");
1615  return false;
1616  }
1617  } else {
1618  DEBUG_INFO (" -------> DOWNLINK DATA");
1619  flashTx = true;
1620  return comm->send (node->getMacAddress (), buffer, packet_length + TAG_LENGTH) == 0;
1621  }
1622 }
1623 
1625  /*
1626  * --------------------------
1627  *| msgType (1) | reason (1) |
1628  * --------------------------
1629  */
1630 
1631  // TODO: Encrypt using network key, adding some random data.This is to avoid DoS attack.
1632  // I have to investigate if this may really work.
1633  // Other options:
1634  // - mark message using timestamp. May not work with gateways not connected to Internet.
1635  // - Adding a number calculated from node message (a byte should be sufficient).
1636  // For instance nth byte + 3. Most probable candidate
1637 
1638  struct __attribute__ ((packed, aligned (1))) {
1639  uint8_t msgType;
1640  uint8_t reason;
1641  } invalidateKey_msg;
1642 
1643 #define IKMSG_LEN sizeof(invalidateKey_msg)
1644 
1645  invalidateKey_msg.msgType = INVALIDATE_KEY; // Server hello message
1646 
1647  invalidateKey_msg.reason = reason;
1648 
1649  DEBUG_VERBOSE ("Invalidate Key message: %s", printHexBuffer ((uint8_t*)&invalidateKey_msg, IKMSG_LEN));
1650  DEBUG_INFO (" -------> INVALIDATE_KEY");
1652  uint8_t* mac = node->getMacAddress ();
1653  notifyNodeDisconnection (mac, reason);
1654  }
1655  int32_t error = comm->send (node->getMacAddress (), (uint8_t*)&invalidateKey_msg, IKMSG_LEN) == 0;
1656  node->reset ();
1657  return error;
1658 }
1659 
1660 bool EnigmaIOTGatewayClass::processClientHello (const uint8_t mac[ENIGMAIOT_ADDR_LEN], const uint8_t* buf, size_t count, Node* node) {
1661  /*
1662  * ------------------------------------------------------------------------------------------------------------
1663  *| msgType (1) | IV (12) | DH Kmaster (32) | Random (30 bits) | Broadcast (1 bit) | Sleepy (1 bit) | Tag (16) |
1664  * ------------------------------------------------------------------------------------------------------------
1665  */
1666 
1667  bool sleepyNode;
1668  bool broadcast;
1669 
1670  struct __attribute__ ((packed, aligned (1))) {
1671  uint8_t msgType;
1672  uint8_t iv[IV_LENGTH];
1673  uint8_t publicKey[KEY_LENGTH];
1674  uint32_t random;
1675  uint8_t tag[TAG_LENGTH];
1676  } clientHello_msg;
1677 
1678 #define CHMSG_LEN sizeof(clientHello_msg)
1679 
1680  if (count < CHMSG_LEN) {
1681  DEBUG_WARN ("Message too short");
1682  return false;
1683  }
1684 
1685  memcpy (&clientHello_msg, buf, count);
1686 
1687  const uint8_t addDataLen = CHMSG_LEN - TAG_LENGTH - sizeof (uint32_t) - KEY_LENGTH;
1688  uint8_t aad[AAD_LENGTH + addDataLen];
1689 
1690  memcpy (aad, (uint8_t*)&clientHello_msg, addDataLen); // Copy message upto iv
1691 
1692  // Copy 8 last bytes from NetworkKey
1693  memcpy (aad + addDataLen, gwConfig.networkKey + KEY_LENGTH - AAD_LENGTH, AAD_LENGTH);
1694 
1695  if (!CryptModule::decryptBuffer (clientHello_msg.publicKey, KEY_LENGTH + sizeof (uint32_t),
1696  clientHello_msg.iv, IV_LENGTH,
1697  gwConfig.networkKey, KEY_LENGTH - AAD_LENGTH, // Use first 24 bytes of network key
1698  aad, sizeof (aad), clientHello_msg.tag, TAG_LENGTH)) {
1699  DEBUG_ERROR ("Error during decryption");
1700  return false;
1701  }
1702 
1703  DEBUG_VERBOSE ("Decrypted Client Hello message: %s", printHexBuffer ((uint8_t*)&clientHello_msg, CHMSG_LEN - TAG_LENGTH));
1704 
1705  node->reset ();
1706 
1707  node->setEncryptionKey (clientHello_msg.publicKey);
1708 
1709  Crypto.getDH1 ();
1710  memcpy (myPublicKey, Crypto.getPubDHKey (), KEY_LENGTH);
1711 
1712  if (Crypto.getDH2 (node->getEncriptionKey ())) {
1713  CryptModule::getSHA256 (node->getEncriptionKey (), KEY_LENGTH);
1714 
1715  node->setKeyValid (true);
1716  node->setStatus (INIT);
1717  DEBUG_DBG ("Node key: %s", printHexBuffer (node->getEncriptionKey (), KEY_LENGTH));
1718  } else {
1720  char macstr[ENIGMAIOT_ADDR_LEN * 3];
1721  mac2str ((uint8_t*)mac, macstr);
1722  DEBUG_ERROR ("DH2 error with %s", macstr);
1723  return false;
1724  }
1725 
1726  sleepyNode = (clientHello_msg.random & 0x00000001U) == 1;
1727  node->setInitAsSleepy (sleepyNode);
1728  node->setSleepy (sleepyNode);
1729  DEBUG_VERBOSE ("This is a %s node", sleepyNode ? "sleepy" : "always awaken");
1730 
1731  broadcast = (clientHello_msg.random & 0x00000002U) == 2;
1732  node->enableBroadcast (broadcast);
1733  node->setBroadcastKeyRequested (broadcast);
1734  DEBUG_INFO ("This node has broadcast mode %s", broadcast ? "enabled" : "disabled");
1735 
1736  return true;
1737 }
1738 
1739 bool EnigmaIOTGatewayClass::processClockRequest (const uint8_t mac[ENIGMAIOT_ADDR_LEN], const uint8_t* buf, size_t count, Node* node) {
1740  /*
1741  * ---------------------------------------------------------
1742  *| msgType (1) | IV (12) | Counter (2) | T1 (8) | Tag (16) |
1743  * ---------------------------------------------------------
1744  */
1745  struct timeval tv;
1746  //struct timezone tz;
1747 
1748  struct __attribute__ ((packed, aligned (1))) {
1749  uint8_t msgType;
1750  uint8_t iv[IV_LENGTH];
1751  uint16_t counter;
1752  int64_t t1;
1753  uint8_t tag[TAG_LENGTH];
1754  } clockRequest_msg;
1755  uint16_t counter;
1756 
1757  const unsigned int CRMSG_LEN = sizeof (clockRequest_msg);
1758 
1759  if (count < CRMSG_LEN) {
1760  DEBUG_WARN ("Message too short");
1761  return false;
1762  }
1763 
1764  node->setTimeSyncEnabled ();
1765 
1766  // Get current time. If Gateway is synchronized to NTP server it sends real world time.
1767  gettimeofday (&tv, NULL);
1768  int64_t t2 = tv.tv_sec;
1769  t2 *= 1000000L;
1770  t2 += tv.tv_usec;
1771 
1772 
1773  //CryptModule::random (clockRequest_msg.iv, IV_LENGTH);
1774 
1775  memcpy (&clockRequest_msg, buf, count);
1776 
1777  DEBUG_VERBOSE ("IV: %s", printHexBuffer (clockRequest_msg.iv, IV_LENGTH));
1778 
1779  const uint8_t addDataLen = 1 + IV_LENGTH;
1780  uint8_t aad[AAD_LENGTH + addDataLen];
1781 
1782  memcpy (aad, buf, addDataLen); // Copy message upto iv
1783 
1784  // Copy 8 last bytes from NetworkKey
1785  memcpy (aad + addDataLen, node->getEncriptionKey () + KEY_LENGTH - AAD_LENGTH, AAD_LENGTH);
1786 
1787  //uint8_t packetLen = count - TAG_LENGTH;
1788 
1789  if (!CryptModule::decryptBuffer ((uint8_t*)&(clockRequest_msg.counter), CRMSG_LEN - IV_LENGTH - TAG_LENGTH - 1, // Decrypt from counter, 10 bytes
1790  clockRequest_msg.iv, IV_LENGTH,
1791  node->getEncriptionKey (), KEY_LENGTH - AAD_LENGTH, // Use first 24 bytes of network key
1792  aad, sizeof (aad), clockRequest_msg.tag, TAG_LENGTH)) {
1793  DEBUG_ERROR ("Error during decryption");
1794  return false;
1795  }
1796 
1797  DEBUG_VERBOSE ("Decripted Clock Request message: %s", printHexBuffer ((uint8_t*)&clockRequest_msg, count - TAG_LENGTH));
1798 
1799  memcpy (&counter, &(clockRequest_msg.counter), sizeof (uint16_t));
1800  DEBUG_INFO ("Node Id %d. Control message #%d", node->getNodeId (), counter);
1801  if (useCounter) {
1802  if (counter > node->getLastControlCounter ()) {
1803  DEBUG_INFO ("Accepted");
1804  node->setLastControlCounter (counter);
1805  } else {
1806  DEBUG_WARN ("Control message rejected");
1807  return false;
1808  }
1809  }
1810 
1811  //node->t1 = clockRequest_msg.t1;
1812 
1813  //node->t2 = time_us;
1814 
1815  DEBUG_DBG ("T1: %llu", clockRequest_msg.t1);
1816  DEBUG_DBG ("T2: %llu", t2);
1817  DEBUG_VERBOSE ("Clock Request message: %s", printHexBuffer ((uint8_t*)&clockRequest_msg, CRMSG_LEN - TAG_LENGTH));
1818 
1819  return clockResponse (node, clockRequest_msg.t1, t2);
1820 }
1821 
1822 bool EnigmaIOTGatewayClass::clockResponse (Node* node, uint64_t t1, uint64_t t2) {
1823  struct timeval tv;
1824  //struct timezone tz;
1825 
1826  struct __attribute__ ((packed, aligned (1))) {
1827  uint8_t msgType;
1828  uint8_t iv[IV_LENGTH];
1829  uint16_t counter;
1830  int64_t t1;
1831  int64_t t2;
1832  int64_t t3;
1833  uint8_t tag[TAG_LENGTH];
1834  } clockResponse_msg;
1835 
1836  uint16_t counter;
1837 
1838  const unsigned int CRSMSG_LEN = sizeof (clockResponse_msg);
1839 
1840  clockResponse_msg.msgType = CLOCK_RESPONSE;
1841 
1842  if (useCounter) {
1843  counter = node->getLastDownlinkMsgCounter () + 1;
1844  node->setLastDownlinkMsgCounter (counter);
1845  } else {
1846  counter = (uint16_t)(Crypto.random ());
1847  }
1848  DEBUG_INFO ("Downlink message #%d", counter);
1849 
1850  memcpy (&(clockResponse_msg.counter), &counter, sizeof (uint16_t));
1851 
1852  memcpy (&(clockResponse_msg.t1), &t1, sizeof (int64_t));
1853 
1854  memcpy (&(clockResponse_msg.t2), &t2, sizeof (int64_t));
1855 
1856  // Get current time. If Gateway is synchronized to NTP server it sends real world time.
1857  gettimeofday (&tv, NULL);
1858  int64_t t3 = tv.tv_sec;
1859  t3 *= 1000000L;
1860  t3 += tv.tv_usec;
1861 
1862  memcpy (&(clockResponse_msg.t3), &t3, sizeof (int64_t));
1863 
1864  DEBUG_VERBOSE ("Clock Response message: %s", printHexBuffer ((uint8_t*)&clockResponse_msg, CRSMSG_LEN - TAG_LENGTH));
1865 
1866 #ifdef DEBUG_ESP_PORT
1867  char mac[ENIGMAIOT_ADDR_LEN * 3];
1868  mac2str (node->getMacAddress (), mac);
1869 #endif
1870  DEBUG_DBG ("T1: %llu", t1);
1871  DEBUG_DBG ("T2: %llu", t2);
1872  DEBUG_DBG ("T3: %llu", t3);
1873 
1874  const uint8_t addDataLen = 1 + IV_LENGTH;
1875  uint8_t aad[AAD_LENGTH + addDataLen];
1876 
1877  memcpy (aad, (uint8_t*)&clockResponse_msg, addDataLen); // Copy message upto iv
1878 
1879  // Copy 8 last bytes from NetworkKey
1880  memcpy (aad + addDataLen, node->getEncriptionKey () + KEY_LENGTH - AAD_LENGTH, AAD_LENGTH);
1881 
1882  if (!CryptModule::encryptBuffer ((uint8_t*)&(clockResponse_msg.counter), CRSMSG_LEN - IV_LENGTH - TAG_LENGTH - 1, // Encrypt only from counter, 18 bytes
1883  clockResponse_msg.iv, IV_LENGTH,
1884  node->getEncriptionKey (), KEY_LENGTH - AAD_LENGTH, // Use first 24 bytes of network key
1885  aad, sizeof (aad), clockResponse_msg.tag, TAG_LENGTH)) {
1886  DEBUG_ERROR ("Error during encryption");
1887  return false;
1888  }
1889 
1890  DEBUG_VERBOSE ("Encrypted Clock Response message: %s", printHexBuffer ((uint8_t*)&clockResponse_msg, CRSMSG_LEN));
1891 
1892  DEBUG_INFO (" -------> CLOCK RESPONSE");
1893  if (comm->send (node->getMacAddress (), (uint8_t*)&clockResponse_msg, CRSMSG_LEN) == 0) {
1894  DEBUG_INFO ("Clock Response message sent to %s", mac);
1895  return true;
1896  } else {
1898  DEBUG_ERROR ("Error sending Clock Response message to %s", mac);
1899  return false;
1900  }
1901 }
1902 
1904 
1905  DEBUG_DBG ("Send broadcast key to " MACSTR, MAC2STR (node->getMacAddress ()));
1906  return sendDownstream (node->getMacAddress (), NULL, 0, control_message_type_t::BRCAST_KEY);
1907 
1908 }
1909 
1910 bool EnigmaIOTGatewayClass::serverHello (const uint8_t* key, Node* node) {
1911  /*
1912  * -----------------------------------------------------------------------------
1913  *| msgType (1) | IV (12) | DH Kslave (32) | NodeID (2) | Random (4) | Tag (16) |
1914  * -----------------------------------------------------------------------------
1915  */
1916 
1917  struct __attribute__ ((packed, aligned (1))) {
1918  uint8_t msgType;
1919  uint8_t iv[IV_LENGTH];
1920  uint8_t publicKey[KEY_LENGTH];
1921  uint16_t nodeId;
1922  uint32_t random;
1923  uint8_t tag[TAG_LENGTH];
1924  } serverHello_msg;
1925 
1926 #define SHMSG_LEN sizeof(serverHello_msg)
1927 
1928  uint32_t random;
1929 
1930  if (!key) {
1931  DEBUG_ERROR ("NULL key");
1932  return false;
1933  }
1934 
1935  serverHello_msg.msgType = SERVER_HELLO; // Server hello message
1936 
1937  CryptModule::random (serverHello_msg.iv, IV_LENGTH);
1938 
1939  DEBUG_VERBOSE ("IV: %s", printHexBuffer (serverHello_msg.iv, IV_LENGTH));
1940 
1941  for (int i = 0; i < KEY_LENGTH; i++) {
1942  serverHello_msg.publicKey[i] = key[i];
1943  }
1944 
1945  uint16_t nodeId = node->getNodeId ();
1946  memcpy (&(serverHello_msg.nodeId), &nodeId, sizeof (uint16_t));
1947 
1948  random = Crypto.random ();
1949  memcpy (&(serverHello_msg.random), &random, RANDOM_LENGTH);
1950 
1951  DEBUG_VERBOSE ("Server Hello message: %s", printHexBuffer ((uint8_t*)&serverHello_msg, SHMSG_LEN - TAG_LENGTH));
1952 
1953  const uint8_t addDataLen = SHMSG_LEN - TAG_LENGTH - sizeof (uint32_t) - sizeof (uint16_t) - KEY_LENGTH;
1954  uint8_t aad[AAD_LENGTH + addDataLen];
1955 
1956  memcpy (aad, (uint8_t*)&serverHello_msg, addDataLen); // Copy message upto iv
1957 
1958  // Copy 8 last bytes from NetworkKey
1959  memcpy (aad + addDataLen, gwConfig.networkKey + KEY_LENGTH - AAD_LENGTH, AAD_LENGTH);
1960 
1961  if (!CryptModule::encryptBuffer (serverHello_msg.publicKey, KEY_LENGTH + sizeof (uint16_t) + sizeof (uint32_t), // Encrypt from public key
1962  serverHello_msg.iv, IV_LENGTH,
1963  gwConfig.networkKey, KEY_LENGTH - AAD_LENGTH, // Use first 24 bytes of network key
1964  aad, sizeof (aad), serverHello_msg.tag, TAG_LENGTH)) {
1965  DEBUG_ERROR ("Error during encryption");
1966  return false;
1967  }
1968 
1969  DEBUG_VERBOSE ("Encrypted Server Hello message: %s", printHexBuffer ((uint8_t*)&serverHello_msg, SHMSG_LEN));
1970 
1971  flashTx = true;
1972 
1973 #ifdef DEBUG_ESP_PORT
1974  char mac[ENIGMAIOT_ADDR_LEN * 3];
1975  mac2str (node->getMacAddress (), mac);
1976 #endif
1977  DEBUG_INFO (" -------> SERVER_HELLO");
1978  if (comm->send (node->getMacAddress (), (uint8_t*)&serverHello_msg, SHMSG_LEN) == 0) {
1979  DEBUG_INFO ("Server Hello message sent to %s", mac);
1980  return true;
1981  } else {
1983  DEBUG_ERROR ("Error sending Server Hello message to %s", mac);
1984  return false;
1985  }
1986 }
1987 
1988 #if SUPPORT_HA_DISCOVERY
1989 bool EnigmaIOTGatewayClass::sendHADiscoveryJSON (uint8_t* address, uint8_t* data, size_t len, const char* networkName, const char* nodeName) {
1990  DynamicJsonDocument inputJSON (1024);
1991  const int jsonBufferSize = 1024;
1992  char jsonStringBuffer[jsonBufferSize];
1993  haDeviceType_t deviceType;
1994 
1995  DeserializationError result = deserializeMsgPack (inputJSON, data, len);
1996 
1997  if (result != DeserializationError::Ok) {
1998  DEBUG_WARN ("Error decoding HA discovery message: %s", result.c_str ());
1999  return false;
2000  }
2001 
2002  DEBUG_DBG ("Entity name: %s", nodeName ? nodeName : mac2str (address));
2003 
2004  if (inputJSON.containsKey (ha_device_type)) {
2005  deviceType = inputJSON[ha_device_type];
2006  DEBUG_DBG ("Device Type: %d", deviceType);
2007  } else {
2008  DEBUG_WARN ("Device type error");
2009  return false;
2010  }
2011 
2012  String topic = HAEntity::getDiscoveryTopic (HA_DISCOVERY_PREFIX, nodeName ? nodeName : mac2str(address), deviceType, inputJSON.containsKey (ha_name_sufix) ? inputJSON[ha_name_sufix] : (const char *) NULL);
2013 
2014  size_t jsonStrLen;
2015 
2016  switch (deviceType) {
2017  case BINARY_SENSOR:
2018  jsonStrLen = HABinarySensor::getDiscoveryJson (jsonStringBuffer, jsonBufferSize, nodeName ? nodeName : mac2str (address), networkName, &inputJSON);
2019  break;
2020  case SENSOR:
2021  jsonStrLen = HASensor::getDiscoveryJson (jsonStringBuffer, jsonBufferSize, nodeName ? nodeName : mac2str (address), networkName, &inputJSON);
2022  break;
2023  case COVER:
2024  jsonStrLen = HACover::getDiscoveryJson (jsonStringBuffer, jsonBufferSize, nodeName ? nodeName : mac2str (address), networkName, &inputJSON);
2025  break;
2026  case SWITCH:
2027  jsonStrLen = HASwitch::getDiscoveryJson (jsonStringBuffer, jsonBufferSize, nodeName ? nodeName : mac2str (address), networkName, &inputJSON);
2028  break;
2029  case DEVICE_TRIGGER:
2030  jsonStrLen = HATrigger::getDiscoveryJson (jsonStringBuffer, jsonBufferSize, nodeName ? nodeName : mac2str (address), networkName, &inputJSON);
2031  break;
2032  default:
2033  jsonStringBuffer[0] = 0;
2034  jsonStrLen = 0;
2035  DEBUG_WARN ("Device is not supported for HomeAssistant discovery: %d", deviceType);
2036  return false;
2037  break;
2038  }
2039 
2040  DEBUG_INFO ("%s : %s", topic.c_str (), jsonStringBuffer);
2041  if (notifyHADiscovery) {
2042  notifyHADiscovery (topic.c_str (), jsonStringBuffer, jsonStrLen);
2043  }
2044 
2045  return true;
2046 
2047 }
2048 #endif // SUPPORT_HA_DISCOVERY
2049 
2051 
Comms_halClass::onDataRcvd
virtual void onDataRcvd(comms_hal_rcvd_data dataRcvd)=0
Attach a callback function to be run on every received message.
EnigmaIOTGatewayClass::flashRx
volatile bool flashRx
true if Rx LED should flash
Definition: EnigmaIOTGateway.h:124
DISCONNECT_ON_DATA_ERROR
static const bool DISCONNECT_ON_DATA_ERROR
Activates node invalidation in case of data error.
Definition: EnigmaIoTconfigAdvanced.h:33
CryptModule::decryptBuffer
static bool decryptBuffer(const uint8_t *data, size_t length, const uint8_t *iv, uint8_t ivlen, const uint8_t *key, uint8_t keylen, const uint8_t *aad, uint8_t aadLen, const uint8_t *tag, uint8_t tagLen)
Decrypts a buffer using a shared key.
Definition: cryptModule.cpp:52
CLOCK_RESPONSE
@ CLOCK_RESPONSE
Definition: EnigmaIOTGateway.h:51
HACover::getDiscoveryJson
static size_t getDiscoveryJson(char *buffer, size_t buflen, const char *nodeName, const char *networkName, DynamicJsonDocument *inputJSON)
Allows Gateway to get Home Assistant discovery message using Cover template.
Definition: haCover.cpp:114
EnigmaIOTGatewayClass::invalidateKey
bool invalidateKey(Node *node, gwInvalidateReason_t reason)
Creates an InvalidateKey message and sned it. This trigger a new key agreement to start on related no...
Definition: EnigmaIOTGateway.cpp:1624
CryptModule::getPubDHKey
uint8_t * getPubDHKey()
Gets own public key used on Diffie Hellman algorithm.
Definition: cryptModule.h:136
OTAongoing
bool OTAongoing
Definition: EnigmaIOTGateway.cpp:38
EnigmaIOTGatewayClass::useCounter
bool useCounter
true if counter is used to check data messages order
Definition: EnigmaIOTGateway.h:139
IDENTIFY
@ IDENTIFY
Definition: NodeList.h:57
EnigmaIOTGatewayClass::doResetConfig
static void doResetConfig(void)
Activates a flag that signals that configuration has to be saved.
Definition: EnigmaIOTGateway.cpp:47
CHMSG_LEN
#define CHMSG_LEN
COVER
@ COVER
Definition: haEntity.h:58
buildSetName
bool buildSetName(uint8_t *data, size_t &dataLen, const uint8_t *inputData, size_t inputLen)
Definition: EnigmaIOTGateway.cpp:135
buildSetResetConfig
bool buildSetResetConfig(uint8_t *data, size_t &dataLen, const uint8_t *inputData, size_t inputLen)
Definition: EnigmaIOTGateway.cpp:151
EnigmaIOTGatewayClass::myPublicKey
uint8_t myPublicKey[KEY_LENGTH]
Temporary public key store used during key agreement.
Definition: EnigmaIOTGateway.h:122
EnigmaIOTGatewayClass::addInputMsgQueue
bool addInputMsgQueue(const uint8_t *addr, const uint8_t *msg, size_t len)
Add message to input queue.
Definition: EnigmaIOTGateway.cpp:792
CryptModule::getDH1
void getDH1()
Starts first stage of Diffie Hellman key agreement algorithm.
Definition: cryptModule.cpp:141
BINARY_SENSOR
@ BINARY_SENSOR
Definition: haEntity.h:56
SLEEP_SET
@ SLEEP_SET
Definition: NodeList.h:55
EnigmaIOTGatewayClass::rxLedOnTime
unsigned long rxLedOnTime
Flash duration for Rx LED.
Definition: EnigmaIOTGateway.h:131
RESTART_NODE
@ RESTART_NODE
Definition: NodeList.h:65
NodeList::getNodeFromName
Node * getNodeFromName(const char *name)
Gets node that correspond with given node name.
Definition: NodeList.cpp:174
EnigmaIOTRingBuffer< msg_queue_item_t >
TAG_LENGTH
const uint8_t TAG_LENGTH
Authentication tag length. For Poly1305 it is always 16.
Definition: EnigmaIoTconfigAdvanced.h:72
USERDATA_SET
@ USERDATA_SET
Definition: NodeList.h:71
ENIGMAIOT_ADDR_LEN
static const size_t ENIGMAIOT_ADDR_LEN
Address size. Mac address = 6 bytes.
Definition: EnigmaIoTconfigAdvanced.h:23
Comms_halClass::onDataSent
virtual void onDataSent(comms_hal_sent_data dataRcvd)=0
Attach a callback function to be run after sending a message to receive its status.
cryptModule.h
Crypto library that implements EnigmaIoT encryption, decryption and key agreement fuctions.
NODE_NAME_SET
@ NODE_NAME_SET
Definition: EnigmaIOTGateway.h:52
EnigmaIOTGatewayClass
Main gateway class. Manages communication with nodes and sends data to upper layer.
Definition: EnigmaIOTGateway.h:120
NodeList::printToSerial
void printToSerial(Stream *port)
Dumps node list data to a Stream object.
Definition: NodeList.cpp:340
memstr
const void * memstr(const void *str, size_t str_size, const char *target, size_t target_size)
Definition: EnigmaIOTGateway.cpp:73
MAX_NODE_INACTIVITY
static const unsigned int MAX_NODE_INACTIVITY
After this time (in ms) a node is marked as gone. Setting this to 0 means imfinite.
Definition: EnigmaIoTconfig.h:26
EnigmaIOTGatewayClass::processControlMessage
bool processControlMessage(const uint8_t mac[ENIGMAIOT_ADDR_LEN], uint8_t *buf, size_t count, Node *node)
Processes control message from node.
Definition: EnigmaIOTGateway.cpp:1250
MAX_MESSAGE_LENGTH
static const uint8_t MAX_MESSAGE_LENGTH
Maximum payload size on ESP-NOW.
Definition: EnigmaIoTconfigAdvanced.h:21
Comms_halClass::begin
virtual void begin(uint8_t *gateway, uint8_t channel, peerType_t peerType=COMM_NODE)=0
Setup communication environment and establish the connection from node to gateway.
SWITCH
@ SWITCH
Definition: haEntity.h:67
buildOtaMsg
bool buildOtaMsg(uint8_t *data, size_t &dataLen, const uint8_t *inputData, size_t inputLen)
Definition: EnigmaIOTGateway.cpp:239
EnigmaIOTRingBuffer::size
int size()
Returns actual number of elements that buffer holds.
Definition: EnigmaIOTRingBuffer.h:54
GwAPI
GatewayAPI GwAPI
API instance.
Definition: GatewayAPI.cpp:391
NODE_NAME_LENGTH
static const uint8_t NODE_NAME_LENGTH
Maximum number of characters of node name.
Definition: EnigmaIoTconfigAdvanced.h:25
initWiFi
void initWiFi(uint8_t channel, const char *networkName, const char *networkKey, uint8_t role)
Initalizes WiFi interfaces on ESP8266 or ESP32.
Definition: helperFunctions.cpp:32
EnigmaIOTGatewayClass::tempBuffer
msg_queue_item_t tempBuffer
Temporary storage for input message got from buffer.
Definition: EnigmaIOTGateway.h:145
EnigmaIOTRingBuffer::push
bool push(Telement *item)
Adds a new item to buffer, deleting older element if it is full.
Definition: EnigmaIOTRingBuffer.h:73
NodeList::initBroadcastNode
void initBroadcastNode()
Init broadcast node data.
Definition: NodeList.cpp:161
EnigmaIOTGatewayClass::manageMessage
void manageMessage(const uint8_t *mac, uint8_t *buf, uint8_t count)
Process every received message.
Definition: EnigmaIOTGateway.cpp:935
EnigmaIOTGatewayClass::comm
Comms_halClass * comm
Instance of physical communication layer.
Definition: EnigmaIOTGateway.h:127
EnigmaIOTGatewayClass::rx_cb
static void rx_cb(uint8_t *mac_addr, uint8_t *data, uint8_t len)
Function that will be called anytime this gateway receives a message.
Definition: EnigmaIOTGateway.cpp:849
NodeList::getNewNode
Node * getNewNode(const uint8_t *mac)
Finds a node that correspond with given address of creates a new one if it does not exist.
Definition: NodeList.cpp:324
EnigmaIOTGatewayClass::txled
int8_t txled
I/O pin to connect a led that flashes when gateway transmits data.
Definition: EnigmaIOTGateway.h:128
EnigmaIOTGatewayClass::configWiFiManager
bool configWiFiManager()
Starts configuration AP and web server and gets settings from it.
Definition: EnigmaIOTGateway.cpp:494
EnigmaIOTGatewayClass::processDataMessage
bool processDataMessage(const uint8_t mac[ENIGMAIOT_ADDR_LEN], uint8_t *buf, size_t count, Node *node, bool encrypted=true)
Processes data message from node.
Definition: EnigmaIOTGateway.cpp:1377
CryptModule::encryptBuffer
static bool encryptBuffer(const uint8_t *data, size_t length, const uint8_t *iv, uint8_t ivlen, const uint8_t *key, uint8_t keylen, const uint8_t *aad, uint8_t aadLen, const uint8_t *tag, uint8_t tagLen)
Decrypts a buffer using a shared key.
Definition: cryptModule.cpp:86
EnigmaIOTGatewayClass::server
AsyncWebServer * server
WebServer that holds configuration portal.
Definition: EnigmaIOTGateway.h:149
Crypto
CryptModule Crypto
Singleton Crypto class instance.
Definition: cryptModule.cpp:167
haEntity.h
Defines an entity for Home Assistant autodiscovery.
BRCAST_KEY
@ BRCAST_KEY
Definition: NodeList.h:67
gateway_config_t::networkKey
uint8_t networkKey[KEY_LENGTH]
Definition: EnigmaIOTGateway.h:106
buildSetSleep
bool buildSetSleep(uint8_t *data, size_t &dataLen, const uint8_t *inputData, size_t inputLen)
Definition: EnigmaIOTGateway.cpp:333
NodeList::getNodeFromMAC
Node * getNodeFromMAC(const uint8_t *mac)
Gets node that correspond with given address.
Definition: NodeList.cpp:142
EnigmaIOTGatewayClass::notifyData
onGwDataRx_t notifyData
Callback function that will be invoked when data is received from a node.
Definition: EnigmaIOTGateway.h:132
RAW
@ RAW
Definition: EnigmaIOTGateway.h:62
EnigmaIOTGatewayClass::input_queue
EnigmaIOTRingBuffer< msg_queue_item_t > * input_queue
Input messages buffer. It acts as a FIFO queue.
Definition: EnigmaIOTGateway.h:147
haDeviceType_t
haDeviceType_t
Definition: haEntity.h:53
EnigmaIOTGatewayClass::notifyHADiscovery
onHADiscovery_t notifyHADiscovery
Callback function that will be invoked when HomeAssistant discovery message is received from a node.
Definition: EnigmaIOTGateway.h:134
EnigmaIOTGatewayClass::sendBroadcastKey
bool sendBroadcastKey(Node *node)
Sends broadcast key to node if it has requested it explicitly or it has notified during handshake.
Definition: EnigmaIOTGateway.cpp:1903
LED_OFF
#define LED_OFF
Definition: enigmaiot_led_flasher.cpp:40
OTA
@ OTA
Definition: NodeList.h:68
NodeList::incLastBroadcastMsgCounter
void incLastBroadcastMsgCounter()
Increments last broadcast message counter stata by one.
Definition: NodeList.h:627
NETWORK_NAME_LENGTH
static const uint8_t NETWORK_NAME_LENGTH
Maximum number of characters of network name.
Definition: EnigmaIoTconfigAdvanced.h:24
haSwitch.h
Home Assistant switch integration.
buildGetSleep
bool buildGetSleep(uint8_t *data, size_t &dataLen, const uint8_t *inputData, size_t inputLen)
Definition: EnigmaIOTGateway.cpp:95
ENIGMAIOT
@ ENIGMAIOT
Definition: EnigmaIOTGateway.h:69
RESET
@ RESET
Definition: NodeList.h:58
DOWNSTREAM_DATA_GET
@ DOWNSTREAM_DATA_GET
Definition: EnigmaIOTGateway.h:44
printHexBuffer
char * printHexBuffer(const uint8_t *buffer, uint16_t len)
Debug helper function that generates a string that represent a buffer hexadecimal values.
Definition: helperFunctions.cpp:16
buildSetIdentify
bool buildSetIdentify(uint8_t *data, size_t &dataLen, const uint8_t *inputData, size_t inputLen)
Definition: EnigmaIOTGateway.cpp:105
EnigmaIOTGatewayClass::setRxLed
void setRxLed(uint8_t led, time_t onTime=FLASH_LED_TIME)
Sets a LED to be flashed every time a message is received.
Definition: EnigmaIOTGateway.cpp:66
EnigmaIOTGatewayClass::notifyWiFiManagerExit
onWiFiManagerExit_t notifyWiFiManagerExit
Function called when configuration portal exits.
Definition: EnigmaIOTGateway.h:152
DOWNSTREAM_DATA_SET
@ DOWNSTREAM_DATA_SET
Definition: EnigmaIOTGateway.h:42
EnigmaIOTGatewayClass::handle
void handle()
This method should be called periodically for instance inside loop() function. It is used for interna...
Definition: EnigmaIOTGateway.cpp:867
NodeList::getLastBroadcastMsgCounter
uint16_t getLastBroadcastMsgCounter()
Ask for last broadcast message counter state.
Definition: NodeList.h:620
RSSI_GET
@ RSSI_GET
Definition: NodeList.h:60
NodeList::unregisterNode
bool unregisterNode(uint16_t nodeId)
Frees up a node and marks it as available.
Definition: NodeList.cpp:258
DEBUG_ESP_PORT
#define DEBUG_ESP_PORT
Stream to output debug info. It will normally be Serial
Definition: EnigmaIoTconfig.h:58
msg_queue_item_t::addr
uint8_t addr[ENIGMAIOT_ADDR_LEN]
Definition: EnigmaIOTGateway.h:111
INIT
@ INIT
Definition: NodeList.h:25
HAEntity::getDiscoveryTopic
static String getDiscoveryTopic(const char *hassPrefix, const char *nodeName, haDeviceType_t entityType, const char *nameSuffix=NULL)
Allows Gateway to get discovery message MQTT topic.
Definition: haEntity.h:277
DOWNSTREAM_CTRL_DATA
@ DOWNSTREAM_CTRL_DATA
Definition: EnigmaIOTGateway.h:47
EnigmaIOTGatewayClass::notifyNodeDisconnection
onNodeDisconnected_t notifyNodeDisconnection
Callback function that will be invoked when a node gets disconnected.
Definition: EnigmaIOTGateway.h:137
EnigmaIOTGatewayClass::processUnencryptedDataMessage
bool processUnencryptedDataMessage(const uint8_t mac[ENIGMAIOT_ADDR_LEN], uint8_t *buf, size_t count, Node *node)
Processes unencrypted data message from node.
Definition: EnigmaIOTGateway.cpp:1324
EnigmaIOTRingBuffer::empty
bool empty()
Checks if buffer is empty.
Definition: EnigmaIOTRingBuffer.h:66
CryptModule::getSHA256
static uint8_t * getSHA256(uint8_t *buffer, uint8_t length)
Generates a SHA256 hash from input.
Definition: cryptModule.cpp:20
buildSendBrcastKey
bool buildSendBrcastKey(uint8_t *data, size_t &dataLen, const uint8_t *key, size_t keyLen)
Definition: EnigmaIOTGateway.cpp:171
ha_name_sufix
constexpr auto ha_name_sufix
Definition: haEntity.h:47
EnigmaIOTGatewayClass::setTxLed
void setTxLed(uint8_t led, time_t onTime=FLASH_LED_TIME)
Sets a LED to be flashed every time a message is transmitted.
Definition: EnigmaIOTGateway.cpp:59
SLEEP_GET
@ SLEEP_GET
Definition: NodeList.h:54
CONTROL_DATA
@ CONTROL_DATA
Definition: EnigmaIOTGateway.h:46
OTA_GW_TIMEOUT
static const int OTA_GW_TIMEOUT
OTA mode timeout. In OTA mode all data messages are ignored.
Definition: EnigmaIoTconfigAdvanced.h:31
EnigmaIOTRingBuffer::pop
bool pop()
Deletes older item from buffer, if buffer is not empty.
Definition: EnigmaIOTRingBuffer.h:106
AAD_LENGTH
const uint8_t AAD_LENGTH
Number of bytes from last part of key that will be used for additional authenticated data.
Definition: EnigmaIoTconfigAdvanced.h:73
lastOTAmsg
time_t lastOTAmsg
Definition: EnigmaIOTGateway.cpp:39
haSensor.h
Home Assistant sensor integration.
NodeList::getBroadcastNode
Node * getBroadcastNode()
Gets broadcas node instance.
Definition: NodeList.h:607
SENSOR_DATA
@ SENSOR_DATA
Definition: EnigmaIOTGateway.h:39
SHMSG_LEN
#define SHMSG_LEN
EnigmaIOTRingBuffer::front
Telement * front()
Gets a pointer to older item in buffer, if buffer is not empty.
Definition: EnigmaIOTRingBuffer.h:125
haCover.h
Home Assistant cover and shade integration.
Node::setEncryptionKey
void setEncryptionKey(const uint8_t *key)
Sets encryption key.
Definition: NodeList.cpp:11
Comms_halClass::send
virtual int32_t send(uint8_t *da, uint8_t *data, int len)=0
Sends data to the other peer.
GatewayAPI::begin
void begin()
Starts REST API web server.
Definition: GatewayAPI.cpp:28
Node::getEncriptionKey
uint8_t * getEncriptionKey()
Gets Node encryption key.
Definition: NodeList.h:173
USERDATA_GET
@ USERDATA_GET
Definition: NodeList.h:70
NUM_NODES
static const int NUM_NODES
Maximum number of nodes that this gateway can handle.
Definition: EnigmaIoTconfig.h:32
EnigmaIOTGatewayClass::processClockRequest
bool processClockRequest(const uint8_t mac[ENIGMAIOT_ADDR_LEN], const uint8_t *buf, size_t count, Node *node)
Starts clock sync procedure from node to gateway.
Definition: EnigmaIOTGateway.cpp:1739
CryptModule::random
static uint32_t random()
Gets a random number.
Definition: cryptModule.cpp:119
getNextNumber
int getNextNumber(char *&data, size_t &len)
Definition: EnigmaIOTGateway.cpp:184
msg_queue_item_t::data
uint8_t data[MAX_MESSAGE_LENGTH]
Definition: EnigmaIOTGateway.h:112
gatewayPayloadEncoding_t
gatewayPayloadEncoding_t
Definition: EnigmaIOTGateway.h:61
gateway_config_t::channel
uint8_t channel
Definition: EnigmaIOTGateway.h:105
CONFIG_FILE
const char CONFIG_FILE[]
Definition: EnigmaIOTGateway.cpp:35
EnigmaIOTGatewayClass::getTotalPackets
uint32_t getTotalPackets(uint8_t *address)
Gets total packets sent by node that has a specific address.
Definition: EnigmaIOTGateway.cpp:1470
HABinarySensor::getDiscoveryJson
static size_t getDiscoveryJson(char *buffer, size_t buflen, const char *nodeName, const char *networkName, DynamicJsonDocument *inputJSON)
Allows Gateway to get Home Assistant discovery message using Binary Sensor template.
Definition: haBinarySensor.cpp:78
EnigmaIOTGatewayClass::flashTx
bool flashTx
true if Tx LED should flash
Definition: EnigmaIOTGateway.h:123
EnigmaIOTGatewayClass::dns
DNSServer * dns
DNS server used by configuration portal.
Definition: EnigmaIOTGateway.h:150
msg_queue_item_t
Definition: EnigmaIOTGateway.h:110
EnigmaIOTGatewayClass::getStatus
void getStatus(uint8_t *mac_addr, uint8_t status)
Functrion to debug send status.
Definition: EnigmaIOTGateway.cpp:858
EnigmaIOTGatewayClass::getShouldSave
bool getShouldSave()
Gets flag that indicates if configuration should be saved.
Definition: EnigmaIOTGateway.cpp:55
EnigmaIOTGatewayClass::nodelist
NodeList nodelist
Node database that keeps status and shared keys.
Definition: EnigmaIOTGateway.h:126
EnigmaIOTGatewayClass::clockResponse
bool clockResponse(Node *node, uint64_t t1, uint64_t t2)
Returns timestaps needed so that node can calculate time difference.
Definition: EnigmaIOTGateway.cpp:1822
msg_queue_item_t::len
size_t len
Definition: EnigmaIOTGateway.h:113
RANDOM_LENGTH
const uint8_t RANDOM_LENGTH
Length of random number generator values.
Definition: cryptModule.h:27
EnigmaIOTGateway
EnigmaIOTGatewayClass EnigmaIOTGateway
Definition: EnigmaIOTGateway.cpp:2050
HASwitch::getDiscoveryJson
static size_t getDiscoveryJson(char *buffer, size_t buflen, const char *nodeName, const char *networkName, DynamicJsonDocument *inputJSON)
Allows Gateway to get Home Assistant discovery message using Switch template.
Definition: haSwitch.cpp:85
KEY_LENGTH
const uint8_t KEY_LENGTH
Key length used by selected crypto algorythm. The only tested value is 32. Change it only if you know...
Definition: EnigmaIoTconfigAdvanced.h:70
EnigmaIOTGatewayClass::node
node_t node
temporary store to keep node data while processing a message
Definition: EnigmaIOTGateway.h:125
EnigmaIOTGatewayClass::txLedOnTime
unsigned long txLedOnTime
Flash duration for Tx LED.
Definition: EnigmaIOTGateway.h:130
ha_device_type
constexpr auto ha_device_type
Definition: haEntity.h:23
haTrigger.h
Home Assistant trigger integration.
EnigmaIOTGatewayClass::doSave
static void doSave(void)
Activates a flag that signals that configuration has to be saved.
Definition: EnigmaIOTGateway.cpp:42
BROADCAST_ADDRESS
static const uint8_t BROADCAST_ADDRESS[]
Broadcast address.
Definition: EnigmaIoTconfigAdvanced.h:26
EnigmaIOTGatewayClass::notifyWiFiManagerStarted
simpleEventHandler_t notifyWiFiManagerStarted
Function called when configuration portal is started.
Definition: EnigmaIOTGateway.h:153
MAX_KEY_VALIDITY
static const unsigned int MAX_KEY_VALIDITY
After this time (in ms) a node is unregistered. Setting this to 0 means imfinite.
Definition: EnigmaIoTconfig.h:25
MACSTR
#define MACSTR
Definition: helperFunctions.cpp:83
Node
Class definition for a single sensor Node.
Definition: NodeList.h:109
shouldSave
bool shouldSave
Definition: EnigmaIOTGateway.cpp:37
SENSOR
@ SENSOR
Definition: haEntity.h:66
NodeList::checkNodeName
int8_t checkNodeName(const char *name, const uint8_t *address)
Check Node name for duplicate.
Definition: NodeList.cpp:201
HASensor::getDiscoveryJson
static size_t getDiscoveryJson(char *buffer, size_t buflen, const char *nodeName, const char *networkName, DynamicJsonDocument *inputJSON)
Allows Gateway to get Home Assistant discovery message using Sensor template.
Definition: haSensor.cpp:65
buildGetVersion
bool buildGetVersion(uint8_t *data, size_t &dataLen, const uint8_t *inputData, size_t inputLen)
Definition: EnigmaIOTGateway.cpp:85
EnigmaIOTGatewayClass::gwConfig
gateway_config_t gwConfig
Gateway specific configuration to be stored on flash memory.
Definition: EnigmaIOTGateway.h:140
KEY_EXPIRED
@ KEY_EXPIRED
Definition: EnigmaIOTGateway.h:81
EnigmaIOTGatewayClass::processClientHello
bool processClientHello(const uint8_t mac[ENIGMAIOT_ADDR_LEN], const uint8_t *buf, size_t count, Node *node)
Gets a buffer containing a ClientHello message and process it. This carries node public key to be use...
Definition: EnigmaIOTGateway.cpp:1660
EnigmaIOTGatewayClass::tx_cb
static void tx_cb(uint8_t *mac_addr, uint8_t status)
Function that will be called anytime this gateway sends a message to indicate status result of sendin...
Definition: EnigmaIOTGateway.cpp:854
EnigmaIOTGatewayClass::saveFlashData
bool saveFlashData()
Saves configuration to flash memory.
Definition: EnigmaIOTGateway.cpp:692
COMM_GATEWAY
@ COMM_GATEWAY
Definition: Comms_hal.h:25
EnigmaIOTGatewayClass::rxled
int8_t rxled
I/O pin to connect a led that flashes when gateway receives data.
Definition: EnigmaIOTGateway.h:129
VERSION
@ VERSION
Definition: NodeList.h:52
HATrigger::getDiscoveryJson
static size_t getDiscoveryJson(char *buffer, size_t buflen, const char *nodeName, const char *networkName, DynamicJsonDocument *inputJSON)
Allows Gateway to get Home Assistant discovery message using Trigger template.
Definition: haTrigger.cpp:39
HA_DISCOVERY_MESSAGE
@ HA_DISCOVERY_MESSAGE
Definition: EnigmaIOTGateway.h:49
EnigmaIOTGatewayClass::processNodeNameSet
bool processNodeNameSet(const uint8_t mac[ENIGMAIOT_ADDR_LEN], uint8_t *buf, size_t count, Node *node)
Processes new node name request fromn node.
Definition: EnigmaIOTGateway.cpp:1175
buildGetRSSI
bool buildGetRSSI(uint8_t *data, size_t &dataLen, const uint8_t *inputData, size_t inputLen)
Definition: EnigmaIOTGateway.cpp:115
haBinarySensor.h
Home Assistant binary sensor integration.
NAME_GET
@ NAME_GET
Definition: NodeList.h:62
data
@ data
Definition: GwOutput_generic.h:23
EnigmaIOTGatewayClass::downstreamDataMessage
bool downstreamDataMessage(Node *node, const uint8_t *data, size_t len, control_message_type_t controlData, gatewayPayloadEncoding_t encoding=ENIGMAIOT)
Builds, encrypts and sends a DownstreamData message.
Definition: EnigmaIOTGateway.cpp:1489
WRONG_DATA
@ WRONG_DATA
Definition: EnigmaIOTGateway.h:79
REGISTERED
@ REGISTERED
Definition: NodeList.h:28
SERVER_HELLO
@ SERVER_HELLO
Definition: EnigmaIOTGateway.h:57
MAX_INPUT_QUEUE_SIZE
static const int MAX_INPUT_QUEUE_SIZE
Input queue size for EnigmaIOT messages. Acts as a buffer to be able to handle messages during high l...
Definition: EnigmaIoTconfig.h:30
EnigmaIOTGatewayClass::sendDownstream
bool sendDownstream(uint8_t *mac, const uint8_t *data, size_t len, control_message_type_t controlData, gatewayPayloadEncoding_t payload_type=RAW, char *nodeName=NULL)
Starts a downstream data message transmission.
Definition: EnigmaIOTGateway.cpp:364
CryptModule::getDH2
bool getDH2(const uint8_t *remotePubKey)
Starts second stage of Diffie Hellman key agreement algorithm and calculate shares key.
Definition: cryptModule.cpp:148
EnigmaIOTGatewayClass::getErrorPackets
uint32_t getErrorPackets(uint8_t *address)
Gets number of errored packets of node that has a specific address.
Definition: EnigmaIOTGateway.cpp:1476
CLOCK_REQUEST
@ CLOCK_REQUEST
Definition: EnigmaIOTGateway.h:50
EnigmaIOTGatewayClass::notifyNewNode
onNewNode_t notifyNewNode
Callback function that will be invoked when a new node is connected.
Definition: EnigmaIOTGateway.h:136
EnigmaIOTGatewayClass::getPacketsHour
double getPacketsHour(uint8_t *address)
Gets packet rate sent by node that has a specific address, in packets per hour.
Definition: EnigmaIOTGateway.cpp:1482
IKMSG_LEN
#define IKMSG_LEN
UNREGISTERED_NODE
@ UNREGISTERED_NODE
Definition: EnigmaIOTGateway.h:80
NODE_NAME_RESULT
@ NODE_NAME_RESULT
Definition: EnigmaIOTGateway.h:53
buildRestartNode
bool buildRestartNode(uint8_t *data, size_t &dataLen, const uint8_t *inputData, size_t inputLen)
Definition: EnigmaIOTGateway.cpp:161
mac2str
char * mac2str(const uint8_t *mac, char *extBuffer)
Debug helper function that generates a string that represent a MAC address.
Definition: helperFunctions.cpp:85
helperFunctions.h
Auxiliary function definition.
NodeList::getNodeFromID
Node * getNodeFromID(uint16_t nodeId)
Gets node that correspond with given nodeId.
Definition: NodeList.cpp:135
IV_LENGTH
const uint8_t IV_LENGTH
Initalization vector length used by selected crypto algorythm.
Definition: EnigmaIoTconfigAdvanced.h:71
HA_DISCOVERY_PREFIX
static const char HA_DISCOVERY_PREFIX[]
Used to build HomeAssistant discovery message topic.
Definition: EnigmaIoTconfigAdvanced.h:41
EnigmaIOTGateway.h
Library to build a gateway for EnigmaIoT system.
gwInvalidateReason_t
gwInvalidateReason_t
Key invalidation reason definition.
Definition: EnigmaIOTGateway.h:75
EnigmaIOTGatewayClass::serverHello
bool serverHello(const uint8_t *key, Node *node)
Build a ServerHello message and send it to node.
Definition: EnigmaIOTGateway.cpp:1910
EnigmaIOTGatewayClass::loadFlashData
bool loadFlashData()
Loads configuration from flash memory.
Definition: EnigmaIOTGateway.cpp:611
EnigmaIoTUpdate.sleepyNode
bool sleepyNode
Definition: EnigmaIoTUpdate.py:13
CLIENT_HELLO
@ CLIENT_HELLO
Definition: EnigmaIOTGateway.h:56
DEVICE_TRIGGER
@ DEVICE_TRIGGER
Definition: haEntity.h:60
control_message_type_t
enum control_message_type control_message_type_t
LED_ON
#define LED_ON
Definition: enigmaiot_led_flasher.cpp:39
EnigmaIOTGatewayClass::nodeNameSetRespose
bool nodeNameSetRespose(Node *node, int8_t error)
Send back set name response.
Definition: EnigmaIOTGateway.cpp:1106
buildGetName
bool buildGetName(uint8_t *data, size_t &dataLen, const uint8_t *inputData, size_t inputLen)
Definition: EnigmaIOTGateway.cpp:125
NAME_SET
@ NAME_SET
Definition: NodeList.h:64
EnigmaIOTGatewayClass::getPER
double getPER(uint8_t *address)
Gets packet error rate of node that has a specific address.
Definition: EnigmaIOTGateway.cpp:1460
isHexChar
bool isHexChar(char c)
Definition: EnigmaIOTGateway.cpp:230
EnigmaIOTGatewayClass::plainNetKey
char plainNetKey[KEY_LENGTH]
Definition: EnigmaIOTGateway.h:141
EnigmaIOTGatewayClass::begin
void begin(Comms_halClass *comm, uint8_t *networkKey=NULL, bool useDataCounter=true)
Initalizes communication basic data and starts accepting node registration.
Definition: EnigmaIOTGateway.cpp:736
gateway_config_t::networkName
char networkName[NETWORK_NAME_LENGTH]
Definition: EnigmaIOTGateway.h:107
INVALIDATE_KEY
@ INVALIDATE_KEY
Definition: EnigmaIOTGateway.h:58
SLEEP_ANS
@ SLEEP_ANS
Definition: NodeList.h:56
EnigmaIOTGatewayClass::wifiManager
AsyncWiFiManager * wifiManager
Wifi configuration portal.
Definition: EnigmaIOTGateway.h:151
status
@ status
Definition: GwOutput_generic.h:25
EnigmaIOTGatewayClass::getInputMsgQueue
msg_queue_item_t * getInputMsgQueue(msg_queue_item_t *buffer)
Gets next item in the queue.
Definition: EnigmaIOTGateway.cpp:815
Comms_halClass
Interface for communication subsystem abstraction layer definition.
Definition: Comms_hal.h:41
EnigmaIOTGatewayClass::sendHADiscoveryJSON
bool sendHADiscoveryJSON(uint8_t *address, uint8_t *data, size_t len, const char *networkName, const char *nodeName)
Sends a Home Assistant discovery message after receiving it from node.
Definition: EnigmaIOTGateway.cpp:1989
UNENCRYPTED_NODE_DATA
@ UNENCRYPTED_NODE_DATA
Definition: EnigmaIOTGateway.h:41
EnigmaIOTGatewayClass::popInputMsgQueue
void popInputMsgQueue()
Deletes next item in the queue.
Definition: EnigmaIOTGateway.cpp:843