Smart-Home am Beispiel der Präsenzerkennung im Raum Projektarbeit Lennart Heimbs, Johannes Krug, Sebastian Dohle und Kevin Holzschuh bei Prof. Oliver Hofmann SS2019
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MySigningAtsha204Soft.cpp 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /*
  2. * The MySensors Arduino library handles the wireless radio link and protocol
  3. * between your home built sensors/actuators and HA controller of choice.
  4. * The sensors forms a self healing radio network with optional repeaters. Each
  5. * repeater and gateway builds a routing tables in EEPROM which keeps track of the
  6. * network topology allowing messages to be routed to nodes.
  7. *
  8. * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
  9. * Copyright (C) 2013-2018 Sensnology AB
  10. * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
  11. *
  12. * Documentation: http://www.mysensors.org
  13. * Support Forum: http://forum.mysensors.org
  14. *
  15. * This program is free software; you can redistribute it and/or
  16. * modify it under the terms of the GNU General Public License
  17. * version 2 as published by the Free Software Foundation.
  18. *
  19. *******************************
  20. *
  21. * DESCRIPTION
  22. * Signing support created by Patrick "Anticimex" Fallberg <patrick@fallberg.net>
  23. * ATSHA204 emulated signing backend. The emulated ATSHA204 implementation offers pseudo random
  24. * number generation and HMAC-SHA256 authentication compatible with a "physical" ATSHA204.
  25. * NOTE: Key is stored in clear text in the Arduino firmware. Therefore, the use of this back-end
  26. * could compromise the key used in the signed message infrastructure if device is lost and its
  27. * memory dumped.
  28. *
  29. */
  30. #include "MySigning.h"
  31. #ifdef MY_SIGNING_SOFT
  32. #define SIGNING_IDENTIFIER (1) //HMAC-SHA256
  33. #if defined(MY_DEBUG_VERBOSE_SIGNING)
  34. #define SIGN_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__)
  35. static char printStr[65];
  36. static char i2h(const uint8_t i)
  37. {
  38. uint8_t k = i & 0x0F;
  39. if (k <= 9) {
  40. return '0' + k;
  41. } else {
  42. return 'A' + k - 10;
  43. }
  44. }
  45. static void buf2str(const uint8_t *buf, size_t sz)
  46. {
  47. if (sz > 32) {
  48. sz = 32; //clamp to 32 bytes
  49. }
  50. for (uint8_t i = 0; i < sz; i++) {
  51. printStr[i * 2] = i2h(buf[i] >> 4);
  52. printStr[(i * 2) + 1] = i2h(buf[i]);
  53. }
  54. printStr[sz * 2] = '\0';
  55. }
  56. #else
  57. #define SIGN_DEBUG(x,...)
  58. #endif
  59. static unsigned long _signing_timestamp;
  60. static bool _signing_verification_ongoing = false;
  61. static bool _signing_init_ok = false;
  62. static uint8_t _signing_verifying_nonce[32+9+1];
  63. static uint8_t _signing_nonce[32+9+1];
  64. static uint8_t _signing_hmac_key[SIZE_SIGNING_SOFT_HMAC_KEY];
  65. static uint8_t _signing_hmac[32];
  66. static uint8_t _signing_node_serial_info[SIZE_SIGNING_SOFT_SERIAL];
  67. #ifdef MY_SIGNING_NODE_WHITELISTING
  68. static const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
  69. #endif
  70. static void signerCalculateSignature(MyMessage &msg, const bool signing);
  71. static void signerAtsha204AHmac(uint8_t *dest, const uint8_t *nonce, const uint8_t *data);
  72. bool signerAtsha204SoftInit(void)
  73. {
  74. _signing_init_ok = true;
  75. // initialize pseudo-RNG
  76. hwRandomNumberInit();
  77. // Set secrets
  78. #ifdef MY_SIGNING_SIMPLE_PASSWD
  79. if (strnlen(MY_SIGNING_SIMPLE_PASSWD, 32) < 8) {
  80. SIGN_DEBUG(PSTR("!SGN:BND:PWD<8\n")); //Password is too short to be acceptable
  81. _signing_init_ok = false;
  82. } else {
  83. (void)memset((void *)_signing_hmac_key, 0x00, sizeof(_signing_hmac_key));
  84. (void)memcpy((void *)_signing_hmac_key, MY_SIGNING_SIMPLE_PASSWD, strnlen(MY_SIGNING_SIMPLE_PASSWD,
  85. 32));
  86. (void)memset((void *)_signing_node_serial_info, 0x00, sizeof(_signing_node_serial_info));
  87. (void)memcpy((void *)_signing_node_serial_info, MY_SIGNING_SIMPLE_PASSWD,
  88. strnlen(MY_SIGNING_SIMPLE_PASSWD, 8));
  89. _signing_node_serial_info[8] = getNodeId();
  90. }
  91. #else
  92. hwReadConfigBlock((void *)_signing_hmac_key, (void *)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS,
  93. SIZE_SIGNING_SOFT_HMAC_KEY);
  94. hwReadConfigBlock((void *)_signing_node_serial_info, (void *)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS,
  95. SIZE_SIGNING_SOFT_SERIAL);
  96. #endif
  97. uint16_t chk = 0;
  98. for (uint8_t i = 0; i < SIZE_SIGNING_SOFT_SERIAL; i++) {
  99. chk += _signing_node_serial_info[i];
  100. }
  101. if (chk==SIZE_SIGNING_SOFT_SERIAL *
  102. 0xFF) { // test if == { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
  103. unique_id_t uniqueID;
  104. // There is no serial, attempt to get unique ID for serial instead
  105. if (hwUniqueID(&uniqueID)) {
  106. // There is a unique serial, use that
  107. (void)memcpy((void *)_signing_node_serial_info, (const void *)uniqueID, SIZE_SIGNING_SOFT_SERIAL);
  108. }
  109. }
  110. return _signing_init_ok;
  111. }
  112. bool signerAtsha204SoftCheckTimer(void)
  113. {
  114. if (!_signing_init_ok) {
  115. return false;
  116. }
  117. if (_signing_verification_ongoing) {
  118. unsigned long time_now = hwMillis();
  119. // If timestamp is taken so late a rollover can take place during the timeout,
  120. // offset both timestamp and current time to make sure no rollover takes place during the
  121. // timeout
  122. if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < _signing_timestamp) {
  123. _signing_timestamp += MY_VERIFICATION_TIMEOUT_MS;
  124. time_now += MY_VERIFICATION_TIMEOUT_MS;
  125. }
  126. if (time_now > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
  127. SIGN_DEBUG(PSTR("!SGN:BND:TMR\n")); //Verification timeout
  128. // Purge nonce
  129. (void)memset((void *)_signing_nonce, 0xAA, sizeof(_signing_nonce));
  130. (void)memset((void *)_signing_verifying_nonce, 0xAA, sizeof(_signing_verifying_nonce));
  131. _signing_verification_ongoing = false;
  132. return false;
  133. }
  134. }
  135. return true;
  136. }
  137. bool signerAtsha204SoftGetNonce(MyMessage &msg)
  138. {
  139. if (!_signing_init_ok) {
  140. return false;
  141. }
  142. #ifdef MY_HW_HAS_GETENTROPY
  143. // Try to get MAX_PAYLOAD random bytes
  144. while (hwGetentropy(&_signing_verifying_nonce, MAX_PAYLOAD) != MAX_PAYLOAD);
  145. #else
  146. // We used a basic whitening technique that XORs a random byte with the current hwMillis() counter
  147. // and then the byte is hashed (SHA256) to produce the resulting nonce
  148. uint8_t randBuffer[32];
  149. for (uint8_t i = 0; i < sizeof(randBuffer); i++) {
  150. randBuffer[i] = random(256) ^ (hwMillis() & 0xFF);
  151. }
  152. SHA256(_signing_verifying_nonce, randBuffer, sizeof(randBuffer));
  153. #endif
  154. if (MAX_PAYLOAD < 32) {
  155. // We set the part of the 32-byte nonce that does not fit into a message to 0xAA
  156. (void)memset((void *)&_signing_verifying_nonce[MAX_PAYLOAD], 0xAA, 32-MAX_PAYLOAD);
  157. }
  158. // Transfer the first part of the nonce to the message
  159. msg.set(_signing_verifying_nonce, MIN(MAX_PAYLOAD, 32));
  160. _signing_verification_ongoing = true;
  161. _signing_timestamp = hwMillis(); // Set timestamp to determine when to purge nonce
  162. // Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover)
  163. // Note that if message is "too" quick, and arrives before turnover, it will be rejected
  164. // but this is consider such a rare case that it is accepted and rejects are 'safe'
  165. if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < hwMillis()) {
  166. _signing_timestamp = 0;
  167. }
  168. return true;
  169. }
  170. void signerAtsha204SoftPutNonce(MyMessage &msg)
  171. {
  172. if (!_signing_init_ok) {
  173. return;
  174. }
  175. (void)memcpy((void *)_signing_nonce, (const void *)msg.getCustom(), MIN(MAX_PAYLOAD, 32));
  176. if (MAX_PAYLOAD < 32) {
  177. // We set the part of the 32-byte nonce that does not fit into a message to 0xAA
  178. (void)memset((void *)&_signing_nonce[MAX_PAYLOAD], 0xAA, 32-MAX_PAYLOAD);
  179. }
  180. }
  181. bool signerAtsha204SoftSignMsg(MyMessage &msg)
  182. {
  183. // If we cannot fit any signature in the message, refuse to sign it
  184. if (mGetLength(msg) > MAX_PAYLOAD-2) {
  185. SIGN_DEBUG(PSTR("!SGN:BND:SIG,SIZE,%" PRIu8 ">%" PRIu8 "\n"), mGetLength(msg),
  186. MAX_PAYLOAD-2); //Message too large
  187. return false;
  188. }
  189. // Calculate signature of message
  190. mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated
  191. signerCalculateSignature(msg, true);
  192. if (DO_WHITELIST(msg.destination)) {
  193. // Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has
  194. // provided. We can reuse the nonce buffer now since it is no longer needed
  195. (void)memcpy((void *)_signing_nonce, (const void *)_signing_hmac, 32);
  196. _signing_nonce[32] = msg.sender;
  197. (void)memcpy((void *)&_signing_nonce[33], (const void *)_signing_node_serial_info, 9);
  198. SHA256(_signing_hmac, _signing_nonce, 32+1+9);
  199. SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,ID=%" PRIu8 "\n"), msg.sender);
  200. #ifdef MY_DEBUG_VERBOSE_SIGNING
  201. buf2str(_signing_node_serial_info, 9);
  202. SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,SERIAL=%s\n"), printStr);
  203. #endif
  204. }
  205. // Overwrite the first byte in the signature with the signing identifier
  206. _signing_hmac[0] = SIGNING_IDENTIFIER;
  207. // Transfer as much signature data as the remaining space in the message permits
  208. (void)memcpy((void *)&msg.data[mGetLength(msg)], (const void *)_signing_hmac,
  209. MIN(MAX_PAYLOAD-mGetLength(msg), 32));
  210. return true;
  211. }
  212. bool signerAtsha204SoftVerifyMsg(MyMessage &msg)
  213. {
  214. if (!_signing_verification_ongoing) {
  215. SIGN_DEBUG(PSTR("!SGN:BND:VER ONGOING\n"));
  216. return false;
  217. } else {
  218. // Make sure we have not expired
  219. if (!signerCheckTimer()) {
  220. return false;
  221. }
  222. _signing_verification_ongoing = false;
  223. if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
  224. SIGN_DEBUG(PSTR("!SGN:BND:VER,IDENT=%" PRIu8 "\n"), msg.data[mGetLength(msg)]);
  225. return false;
  226. }
  227. signerCalculateSignature(msg, false); // Get signature of message
  228. #ifdef MY_SIGNING_NODE_WHITELISTING
  229. // Look up the senders nodeId in our whitelist and salt the signature with that data
  230. size_t j;
  231. for (j = 0; j < NUM_OF(_signing_whitelist); j++) {
  232. if (_signing_whitelist[j].nodeId == msg.sender) {
  233. // We can reuse the nonce buffer now since it is no longer needed
  234. (void)memcpy((void *)_signing_verifying_nonce, (const void *)_signing_hmac, 32);
  235. _signing_verifying_nonce[32] = msg.sender;
  236. (void)memcpy((void *)&_signing_verifying_nonce[33], (const void *)_signing_whitelist[j].serial, 9);
  237. SHA256(_signing_hmac, _signing_verifying_nonce, 32+1+9);
  238. SIGN_DEBUG(PSTR("SGN:BND:VER WHI,ID=%" PRIu8 "\n"), msg.sender);
  239. #ifdef MY_DEBUG_VERBOSE_SIGNING
  240. buf2str(_signing_whitelist[j].serial, 9);
  241. SIGN_DEBUG(PSTR("SGN:BND:VER WHI,SERIAL=%s\n"), printStr);
  242. #endif
  243. break;
  244. }
  245. }
  246. if (j == NUM_OF(_signing_whitelist)) {
  247. SIGN_DEBUG(PSTR("!SGN:BND:VER WHI,ID=%" PRIu8 " MISSING\n"), msg.sender);
  248. return false;
  249. }
  250. #endif
  251. // Overwrite the first byte in the signature with the signing identifier
  252. _signing_hmac[0] = SIGNING_IDENTIFIER;
  253. // Compare the calculated signature with the provided signature
  254. if (signerMemcmp(&msg.data[mGetLength(msg)], _signing_hmac,
  255. MIN(MAX_PAYLOAD-mGetLength(msg), 32))) {
  256. return false;
  257. } else {
  258. return true;
  259. }
  260. }
  261. }
  262. // Helper to calculate signature of msg (returned in _signing_hmac)
  263. static void signerCalculateSignature(MyMessage &msg, const bool signing)
  264. {
  265. // Signature is calculated on everything expect the first byte in the header
  266. uint8_t bytes_left = mGetLength(msg)+HEADER_SIZE-1;
  267. int16_t current_pos = 1-(int16_t)HEADER_SIZE; // Start at the second byte in the header
  268. uint8_t* nonce = signing ? _signing_nonce : _signing_verifying_nonce;
  269. #ifdef MY_DEBUG_VERBOSE_SIGNING
  270. buf2str(nonce, 32);
  271. SIGN_DEBUG(PSTR("SGN:BND:NONCE=%s\n"), printStr);
  272. #endif
  273. uint8_t _signing_temp_message[32];
  274. while (bytes_left) {
  275. uint8_t bytes_to_include = MIN(bytes_left, 32);
  276. (void)memset((void *)_signing_temp_message, 0x00, sizeof(_signing_temp_message));
  277. (void)memcpy((void *)_signing_temp_message, (const void *)&msg.data[current_pos], bytes_to_include);
  278. signerAtsha204AHmac(_signing_hmac, nonce, _signing_temp_message);
  279. // Purge nonce when used
  280. (void)memset((void *)nonce, 0xAA, 32);
  281. bytes_left -= bytes_to_include;
  282. current_pos += bytes_to_include;
  283. if (bytes_left) {
  284. // We will do another pass, use current HMAC as nonce for the next HMAC
  285. (void)memcpy((void *)nonce, (const void *)_signing_hmac, 32);
  286. }
  287. }
  288. #ifdef MY_DEBUG_VERBOSE_SIGNING
  289. buf2str(_signing_hmac, 32);
  290. SIGN_DEBUG(PSTR("SGN:BND:HMAC=%s\n"), printStr);
  291. #endif
  292. }
  293. // Helper to calculate a ATSHA204A specific HMAC-SHA256 using provided 32 byte nonce and data
  294. // (zero padded to 32 bytes)
  295. // The pointer to the HMAC is returned, but the HMAC is also stored in _signing_hmac
  296. static void signerAtsha204AHmac(uint8_t *dest, const uint8_t *nonce, const uint8_t *data)
  297. {
  298. // ATSHA204 calculates the HMAC with a PSK and a SHA256 digest of the following data:
  299. // 32 bytes zeroes
  300. // 32 bytes digest,
  301. // 1 byte OPCODE (0x11)
  302. // 1 byte Mode (0x04)
  303. // 2 bytes SlotID (0x0000)
  304. // 11 bytes zeroes
  305. // SN[8] (0xEE)
  306. // 4 bytes zeroes
  307. // SN[0:1] (0x0123)
  308. // 2 bytes zeroes
  309. // The digest is calculated as a SHA256 digest of the following:
  310. // 32 bytes message
  311. // 1 byte OPCODE (0x15)
  312. // 1 byte param1 (0x02)
  313. // 2 bytes param2 (0x0800)
  314. // SN[8] (0xEE)
  315. // SN[0:1] (0x0123)
  316. // 25 bytes zeroes
  317. // 32 bytes nonce
  318. #if defined(MY_CRYPTO_SHA256_ASM)
  319. static uint8_t _signing_buffer[96]; // static for AVR ASM SHA256
  320. #else
  321. uint8_t _signing_buffer[96];
  322. #endif
  323. // Calculate message digest first
  324. (void)memset((void *)_signing_buffer, 0x00, sizeof(_signing_buffer));
  325. (void)memcpy((void *)_signing_buffer, (const void *)data, 32);
  326. _signing_buffer[0 + 32] = 0x15; // OPCODE
  327. _signing_buffer[1 + 32] = 0x02; // param1
  328. _signing_buffer[2 + 32] = 0x08; // param2(1)
  329. //_signing_buffer[3 + 32] = 0x00; // param2(2)
  330. _signing_buffer[4 + 32] = 0xEE; // SN[8]
  331. _signing_buffer[5 + 32] = 0x01; // SN[0]
  332. _signing_buffer[6 + 32] = 0x23; // SN[1]
  333. // _signing_buffer[7 + 32..31 + 32] => 0x00;
  334. (void)memcpy((void *)&_signing_buffer[64], (const void *)nonce, 32);
  335. SHA256(_signing_hmac, _signing_buffer, 96);
  336. // Feed "message" to HMAC calculator
  337. (void)memset((void *)_signing_buffer, 0x00, sizeof(_signing_buffer));
  338. (void)memcpy((void *)&_signing_buffer[32], (const void *)_signing_hmac, 32);
  339. _signing_buffer[0 + 64] = 0x11; // OPCODE
  340. _signing_buffer[1 + 64] = 0x04; // Mode
  341. //_signing_buffer[2 + 64] = 0x00; // SlotID(1)
  342. //_signing_buffer[3 + 64] = 0x00; // SlotID(2)
  343. //_signing_buffer[4 + 64..14 + 64] => 0x00; // 11 bytes zeroes
  344. _signing_buffer[15 + 64] = 0xEE; // SN[8]
  345. //_signing_buffer[16 + 64..19 + 64] => 0x00; // 4 bytes zeroes
  346. _signing_buffer[20 + 64] = 0x01;
  347. _signing_buffer[21 + 64] = 0x23;
  348. //_signing_buffer[22 + 64] = 0x00; // SN[0]
  349. //_signing_buffer[23 + 64] = 0x00; // SN[1]
  350. SHA256HMAC(dest, _signing_hmac_key, 32, _signing_buffer, 88);
  351. }
  352. #endif //MY_SIGNING_SOFT