/** * The MySensors Arduino library handles the wireless radio link and protocol * between your home built sensors/actuators and HA controller of choice. * The sensors forms a self healing radio network with optional repeaters. Each * repeater and gateway builds a routing tables in EEPROM which keeps track of the * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad * Copyright (C) 2013-2017 Sensnology AB * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * * Documentation: http://www.mysensors.org * Support Forum: http://forum.mysensors.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * ******************************* * * DESCRIPTION * Signing support created by Patrick "Anticimex" Fallberg * ATSHA204 emulated signing backend. The emulated ATSHA204 implementation offers pseudo random * number generation and HMAC-SHA256 authentication compatible with a "physical" ATSHA204. * NOTE: Key is stored in clear text in the Arduino firmware. Therefore, the use of this back-end * could compromise the key used in the signed message infrastructure if device is lost and its memory * dumped. * */ #include "MySigning.h" #include "drivers/ATSHA204/sha256.h" #define SIGNING_IDENTIFIER (1) //HMAC-SHA256 #if defined(MY_DEBUG_VERBOSE_SIGNING) #define SIGN_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__) #else #define SIGN_DEBUG(x,...) #endif // Define MY_DEBUG_VERBOSE_SIGNING in your sketch to enable signing backend debugprints Sha256Class _signing_sha256; unsigned long _signing_timestamp; bool _signing_verification_ongoing = false; uint8_t _signing_verifying_nonce[32]; uint8_t _signing_signing_nonce[32]; uint8_t _signing_temp_message[32]; static uint8_t _signing_hmac_key[32]; uint8_t _signing_hmac[32]; extern uint8_t _doWhitelist[32]; static uint8_t _signing_node_serial_info[9]; #ifdef MY_SIGNING_NODE_WHITELISTING const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING; #endif static void signerCalculateSignature(MyMessage &msg, bool signing); static bool init_ok = false; static const uint8_t reset_serial[9] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; bool signerAtsha204SoftInit(void) { init_ok = true; // initialize pseudo-RNG hwRandomNumberInit(); // Set secrets hwReadConfigBlock((void*)_signing_hmac_key, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32); hwReadConfigBlock((void*)_signing_node_serial_info, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9); if (!memcmp(_signing_node_serial_info, reset_serial, 9)) { unique_id_t uniqueID; // There is no serial, attempt to get unique ID for serial instead if (hwUniqueID(&uniqueID)) { // There is a unique serial, use that memcpy(_signing_node_serial_info, uniqueID, 9); } } return init_ok; } bool signerAtsha204SoftCheckTimer(void) { if (!init_ok) { SIGN_DEBUG(PSTR("Backend not initialized\n")); return false; } if (_signing_verification_ongoing) { if (hwMillis() < _signing_timestamp || hwMillis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) { SIGN_DEBUG(PSTR("Verification timeout\n")); // Purge nonce memset(_signing_signing_nonce, 0xAA, 32); memset(_signing_verifying_nonce, 0xAA, 32); _signing_verification_ongoing = false; return false; } } return true; } bool signerAtsha204SoftGetNonce(MyMessage &msg) { if (!init_ok) { SIGN_DEBUG(PSTR("Backend not initialized\n")); return false; } SIGN_DEBUG(PSTR("Signing backend: ATSHA204Soft\n")); #ifdef MY_HW_HAS_GETRANDOM // Try to get MAX_PAYLOAD random bytes while (hwGetentropy(&_signing_verifying_nonce, MAX_PAYLOAD) != MAX_PAYLOAD); #else // We used a basic whitening technique that XORs a random byte with the current hwMillis() counter and then the byte is // hashed (SHA256) to produce the resulting nonce _signing_sha256.init(); for (int i = 0; i < 32; i++) { _signing_sha256.write(random(256) ^ (hwMillis()&0xFF)); } memcpy(_signing_verifying_nonce, _signing_sha256.result(), MAX_PAYLOAD); #endif // We set the part of the 32-byte nonce that does not fit into a message to 0xAA memset(&_signing_verifying_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_verifying_nonce)-MAX_PAYLOAD); // Transfer the first part of the nonce to the message msg.set(_signing_verifying_nonce, MAX_PAYLOAD); _signing_verification_ongoing = true; _signing_timestamp = hwMillis(); // Set timestamp to determine when to purge nonce // Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover) // Note that if message is "too" quick, and arrives before turnover, it will be rejected // but this is consider such a rare case that it is accepted and rejects are 'safe' if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < hwMillis()) { _signing_timestamp = 0; } return true; } void signerAtsha204SoftPutNonce(MyMessage &msg) { if (!init_ok) { SIGN_DEBUG(PSTR("Backend not initialized\n")); } SIGN_DEBUG(PSTR("Signing backend: ATSHA204Soft\n")); memcpy(_signing_signing_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD); // We set the part of the 32-byte nonce that does not fit into a message to 0xAA memset(&_signing_signing_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_signing_nonce)-MAX_PAYLOAD); } bool signerAtsha204SoftSignMsg(MyMessage &msg) { // If we cannot fit any signature in the message, refuse to sign it if (mGetLength(msg) > MAX_PAYLOAD-2) { SIGN_DEBUG(PSTR("Message too large\n")); return false; } // Calculate signature of message mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated signerCalculateSignature(msg, true); if (DO_WHITELIST(msg.destination)) { // Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided _signing_sha256.init(); for (int i=0; i<32; i++) { _signing_sha256.write(_signing_hmac[i]); } _signing_sha256.write(msg.sender); for (int i=0; i