EnigmaIOT  0.9.8
Secure sensor and gateway platform based on ESP8266 and ESP32
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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