diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index d47f22a..bbbabdb 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -35,13 +35,22 @@ // final sketch but is helpful to see what is actually is happening during development //#define MY_DEBUG -// Enable MY_DEBUG_VERBOSE flag for verbose debug prints. Requires DEBUG to be enabled. +// Enable MY_DEBUG_VERBOSE flag for verbose debug prints related to RF24 radio. +// Requires DEBUG to be enabled. // This will add even more to the size of the final sketch! //#define MY_DEBUG_VERBOSE +// Enable MY_DEBUG_VERBOSE_SIGNING flag for verbose debug prints related to signing. +// Requires DEBUG to be enabled. +// This will add even more to the size of the final sketch! +//#define MY_DEBUG_VERBOSE_SIGNING + // Enable this in sketch if you want to use TX(1), RX(0) as normal I/O pin //#define MY_DISABLED_SERIAL +// Enable MY_CORE_ONLY flag if you want to use core functions without loading the framework +//#define MY_CORE_ONLY + // Turn off debug if serial pins is used for other stuff #ifdef MY_DISABLED_SERIAL #undef MY_DEBUG @@ -280,25 +289,6 @@ #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 #endif -/** - * @def MY_SIGNING_SOFT_SERIAL - * @brief Serial number for SW signing enabled node - * - * Set the soft_serial value to an arbitrary value for proper security. - * It should be random and unique for each node. - */ -#ifndef MY_SIGNING_SOFT_SERIAL -#define MY_SIGNING_SOFT_SERIAL 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09 -#endif - -/** - * @def MY_SIGNING_SOFT_HMAC_KEY - * @brief Key to use for HMAC calculation in MySigningAtsha204Soft (32 bytes) - */ -#ifndef MY_SIGNING_SOFT_HMAC_KEY -#define MY_SIGNING_SOFT_HMAC_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -#endif - /********************************** * RS485 Driver Defaults ***********************************/ @@ -315,14 +305,9 @@ * NRF24L01 Driver Defaults ***********************************/ -// Enables RF24 encryption (all nodes and gateway must have this enabled) +// Enables RF24 encryption (all nodes and gateway must have this enabled, and all must be personalized with the same AES key) //#define MY_RF24_ENABLE_ENCRYPTION -// Default encryption key. Override in sketch if needed. -#ifndef MY_RF24_ENCRYPTKEY -#define MY_RF24_ENCRYPTKEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x11,0x12,0x13,0x14,0x15,0x16 -#endif - // Default pin settings. Override in sketch if needed. #ifndef MY_RF24_CE_PIN #if defined(ARDUINO_ARCH_ESP8266) @@ -409,11 +394,8 @@ #endif #endif -// Enable this for encryption of packets +// Enables RFM69 encryption (all nodes and gateway must have this enabled, and all must be personalized with the same AES key) //#define MY_RFM69_ENABLE_ENCRYPTION -#ifndef MY_RFM69_ENCRYPTKEY -#define MY_RFM69_ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes! -#endif /************************************** * Ethernet Gateway Transport Defaults diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index d795655..2833f5a 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -48,8 +48,10 @@ //#undef F //#define F(x) (x) #include "core/MyHwESP8266.cpp" - // For ESP8266, we always enable gateway feature - #define MY_GATEWAY_ESP8266 + // Enable gateway feature + #if !defined(MY_CORE_ONLY) + #define MY_GATEWAY_ESP8266 + #endif #elif defined(ARDUINO_ARCH_AVR) #include "core/MyHwATMega328.cpp" #elif defined(ARDUINO_ARCH_SAMD) @@ -93,9 +95,12 @@ // SIGNING #if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT) - // SIGNING COMMON FUNCTIONS - #include "core/MySigning.cpp" #define MY_SIGNING_FEATURE +#endif +#include "core/MySigning.cpp" +#include "drivers/ATSHA204/sha256.cpp" +#if defined(MY_SIGNING_FEATURE) + // SIGNING COMMON FUNCTIONS #if defined(MY_SIGNING_ATSHA204) && defined(MY_SIGNING_SOFT) #error Only one signing engine can be activated #endif @@ -105,7 +110,6 @@ #include "drivers/ATSHA204/ATSHA204.cpp" #elif defined(MY_SIGNING_SOFT) #include "core/MySigningAtsha204Soft.cpp" - #include "drivers/ATSHA204/sha256.cpp" #endif #endif @@ -219,22 +223,24 @@ #undef MY_INCLUSION_BUTTON_FEATURE #endif -#if !defined(MY_GATEWAY_FEATURE) && !defined(MY_RADIO_FEATURE) - #error No forward link or gateway feature activated. This means nowhere to send messages! Pretty pointless. +#if !defined(MY_CORE_ONLY) + #if !defined(MY_GATEWAY_FEATURE) && !defined(MY_RADIO_FEATURE) + #error No forward link or gateway feature activated. This means nowhere to send messages! Pretty pointless. + #endif #endif - #include "core/MyCapabilities.h" #include "core/MyMessage.cpp" #include "core/MySensorCore.cpp" #include -#if defined(MY_GATEWAY_ESP8266) - #include "core/MyMainESP8266.cpp" -#else - #include "core/MyMainDefault.cpp" +#if !defined(MY_CORE_ONLY) + #if defined(MY_GATEWAY_ESP8266) + #include "core/MyMainESP8266.cpp" + #else + #include "core/MyMainDefault.cpp" + #endif #endif - #endif diff --git a/libraries/MySensors/core/MyEepromAddresses.h b/libraries/MySensors/core/MyEepromAddresses.h new file mode 100644 index 0000000..510126f --- /dev/null +++ b/libraries/MySensors/core/MyEepromAddresses.h @@ -0,0 +1,43 @@ +/** + * 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-2015 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. + */ + +#ifndef MyEepromAddresses_h +#define MyEepromAddresses_h +// EEPROM start address for mysensors library data +#define EEPROM_START 0 +// EEPROM location of node id +#define EEPROM_NODE_ID_ADDRESS EEPROM_START +// EEPROM location of parent id +#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1) +// EEPROM location of distance to gateway +#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1) +#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes. +#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller) +#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24) +#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS+2) +#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS+2) +#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS+2) +#define EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2) +#define EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS+32) +#define EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS (EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS+32) // This is set with SecurityPersonalizer.ino +#define EEPROM_SIGNING_SOFT_SERIAL_ADDRESS (EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS+32) // This is set with SecurityPersonalizer.ino +#define EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS (EEPROM_SIGNING_SOFT_SERIAL_ADDRESS+9) // This is set with SecurityPersonalizer.ino +#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS+16) // First free address for sketch static configuration + +#endif diff --git a/libraries/MySensors/core/MyHwESP8266.cpp b/libraries/MySensors/core/MyHwESP8266.cpp index 946210a..cec567f 100644 --- a/libraries/MySensors/core/MyHwESP8266.cpp +++ b/libraries/MySensors/core/MyHwESP8266.cpp @@ -18,7 +18,7 @@ */ #include "MyHwESP8266.h" - +#include /* int8_t pinIntTrigger = 0; diff --git a/libraries/MySensors/core/MyMessage.h b/libraries/MySensors/core/MyMessage.h index c40f995..3ed57ed 100644 --- a/libraries/MySensors/core/MyMessage.h +++ b/libraries/MySensors/core/MyMessage.h @@ -163,9 +163,9 @@ typedef enum { I_INCLUSION_MODE, I_CONFIG, I_FIND_PARENT, I_FIND_PARENT_RESPONSE, I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION, I_REBOOT, I_GATEWAY_READY, - I_REQUEST_SIGNING, /*!< Indicates sender require signed messages (bool param) */ - I_GET_NONCE, /*!< Request for a nonce */ - I_GET_NONCE_RESPONSE, /*!< Payload is nonce data */ + I_SIGNING_PRESENTATION, /*!< Provides signing related preferences (first byte is preference version) */ + I_NONCE_REQUEST, /*!< Request for a nonce */ + I_NONCE_RESPONSE, /*!< Payload is nonce data */ I_HEARTBEAT, I_PRESENTATION, I_DISCOVER, I_DISCOVER_RESPONSE, I_HEARTBEAT_RESPONSE } mysensor_internal; diff --git a/libraries/MySensors/core/MyPrivateConfig.h.sample b/libraries/MySensors/core/MyPrivateConfig.h.sample index 3ac1175..099fd36 100644 --- a/libraries/MySensors/core/MyPrivateConfig.h.sample +++ b/libraries/MySensors/core/MyPrivateConfig.h.sample @@ -40,12 +40,6 @@ // Pin used for random generation in soft signing (do not connect anything to this when enabled) #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 // A7 - -// Set the soft_serial value to an arbitrary value for proper security -#define MY_SIGNING_SOFT_SERIAL 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09 - -// Key to use for HMAC calculation in MySigningAtsha204Soft (32 bytes) -#define MY_SIGNING_SOFT_HMAC_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 - // Enable node whitelisting //#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}} @@ -55,9 +49,6 @@ // Enables RF24 encryption (all nodes and gateway must have this enabled) //#define MY_RF24_ENABLE_ENCRYPTION -// Default encryption key -#define MY_RF24_ENCRYPTKEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x11,0x12,0x13,0x14,0x15,0x16 - /********************************** * RFM69 Driver Defaults ***********************************/ @@ -66,6 +57,5 @@ // Enable this for encryption of packets //#define MY_RFM69_ENABLE_ENCRYPTION -#define MY_RFM69_ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes! #endif diff --git a/libraries/MySensors/core/MySensorCore.cpp b/libraries/MySensors/core/MySensorCore.cpp index 809a32e..9fea04d 100644 --- a/libraries/MySensors/core/MySensorCore.cpp +++ b/libraries/MySensors/core/MySensorCore.cpp @@ -62,15 +62,12 @@ static inline bool isValidParent( const uint8_t parent ) { void _begin() { #if !defined(MY_DISABLED_SERIAL) hwInit(); - #endif - - #if defined(MY_SIGNING_SOFT) - // initialize pseudo-RNG - randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN)); #endif debug(PSTR("Starting " MY_NODE_TYPE " (" MY_CAPABILIIES ", " LIBRARY_VERSION ")\n")); + signerInit(); + #if defined(MY_RADIO_FEATURE) _failedTransmissions = 0; @@ -107,11 +104,6 @@ void _begin() { #endif - #if defined(MY_SIGNING_FEATURE) - // Read out the signing requirements from EEPROM - hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign)); - #endif - #if defined(MY_LEDS_BLINKING_FEATURE) ledsInit(); #endif @@ -190,6 +182,9 @@ ControllerConfig getConfig() { boolean _sendRoute(MyMessage &message) { // increment heartbeat counter _heartbeat++; + #if defined(MY_CORE_ONLY) + (void)message; + #endif #if defined(MY_GATEWAY_FEATURE) if (message.destination == _nc.nodeId) { // This is a message sent from a sensor attached on the gateway node. diff --git a/libraries/MySensors/core/MySensorCore.h b/libraries/MySensors/core/MySensorCore.h index 4d2c92e..1991673 100644 --- a/libraries/MySensors/core/MySensorCore.h +++ b/libraries/MySensors/core/MySensorCore.h @@ -23,6 +23,7 @@ #include "Version.h" // Auto generated by bot #include "MyConfig.h" +#include "MyEepromAddresses.h" #include "MyMessage.h" #ifdef MY_OTA_FIRMWARE_FEATURE #include "drivers/SPIFlash/SPIFlash.h" @@ -37,23 +38,6 @@ #define debug(x,...) #endif -// EEPROM start address for mysensors library data -#define EEPROM_START 0 -// EEPROM location of node id -#define EEPROM_NODE_ID_ADDRESS EEPROM_START -// EEPROM location of parent id -#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1) -// EEPROM location of distance to gateway -#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1) -#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes. -#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller) -#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24) -#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS+2) -#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS+2) -#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS+2) -#define EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2) -#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS+32) // First free address for sketch static configuration - // This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data). #define GATEWAY_ADDRESS ((uint8_t)0) // Node child is always created/presented when a node is started diff --git a/libraries/MySensors/core/MySigning.cpp b/libraries/MySensors/core/MySigning.cpp index ff5e3d1..689b374 100644 --- a/libraries/MySensors/core/MySigning.cpp +++ b/libraries/MySensors/core/MySigning.cpp @@ -18,8 +18,363 @@ */ #include "MySigning.h" +#include "drivers/ATSHA204/sha256.h" +#define SIGNING_PRESENTATION_VERSION_1 1 +#define SIGNING_PRESENTATION_REQUIRE_SIGNATURES (1 << 0) +#define SIGNING_PRESENTATION_REQUIRE_WHITELISTING (1 << 1) -uint8_t _doSign[32]; // Bitfield indicating which sensors require signed communication -MyMessage _msgSign; // Buffer for message to sign. +#if defined(MY_DEBUG_VERBOSE_SIGNING) +#define SIGN_DEBUG(x,...) debug(x, ##__VA_ARGS__) +#else +#define SIGN_DEBUG(x,...) +#endif +#if defined(MY_SIGNING_REQUEST_SIGNATURES) && (!defined(MY_SIGNING_ATSHA204) && !defined(MY_SIGNING_SOFT)) +#error You have to pick either MY_SIGNING_ATSHA204 or MY_SIGNING_SOFT in order to require signatures! +#endif +#ifdef MY_SIGNING_FEATURE +uint8_t _doSign[32]; // Bitfield indicating which sensors require signed communication +uint8_t _doWhitelist[32]; // Bitfield indicating which sensors require serial salted signatures +MyMessage _msgSign; // Buffer for message to sign. +uint8_t _signingNonceStatus; + +// Status when waiting for signing nonce in signerProcessInternal +enum { SIGN_WAITING_FOR_NONCE = 0, SIGN_OK = 1 }; + +// Macros for manipulating signing requirement table +#define DO_SIGN(node) (~_doSign[node>>3]&(1<>3]&=~(1<>3]|=(1< MY_VERIFICATION_TIMEOUT_MS) { + SIGN_DEBUG(PSTR("Timeout waiting for nonce!\n")); + return false; + } + if (_signingNonceStatus == SIGN_OK) { + // process() received a nonce and signerProcessInternal successfully signed the message + msg = _msgSign; // Write the signed message back + SIGN_DEBUG(PSTR("Message to send has been signed\n")); + } else { + SIGN_DEBUG(PSTR("Message to send could not be signed!\n")); + return false; + } + // After this point, only the 'last' member of the message structure is allowed to be altered if the + // message has been signed, or signature will become invalid and the message rejected by the receiver + } + } else if (_nc.nodeId == msg.sender) { + mSetSigned(msg, 0); // Message is not supposed to be signed, make sure it is marked unsigned + } +#else + (void)msg; +#endif // MY_SIGNING_FEATURE + return true; +} + +bool signerVerifyMsg(MyMessage &msg) { + bool verificationResult = true; + // Before processing message, reject unsigned messages if signing is required and check signature + // (if it is signed and addressed to us) + // Note that we do not care at all about any signature found if we do not require signing +#if defined(MY_SIGNING_FEATURE) && defined(MY_SIGNING_REQUEST_SIGNATURES) + // If we are a node, or we are a gateway and the sender require signatures + // and we are the destination... + if ((!MY_IS_GATEWAY || DO_SIGN(msg.sender)) && msg.destination == _nc.nodeId) { + // Internal messages of certain types are not verified + if (skipSign(msg)) { + verificationResult = true; + } + else if (!mGetSigned(msg)) { + // Got unsigned message that should have been signed + SIGN_DEBUG(PSTR("Message is not signed, but it should have been!\n")); + verificationResult = false; + } else { +#if defined(MY_SIGNING_SOFT) + verificationResult = signerAtsha204SoftVerifyMsg(msg); +#endif +#if defined(MY_SIGNING_ATSHA204) + verificationResult = signerAtsha204VerifyMsg(msg); +#endif + if (!verificationResult) { + SIGN_DEBUG(PSTR("Signature verification failed!\n")); + } + mSetSigned(msg,0); // Clear the sign-flag now as verification is completed + } + } +#else + (void)msg; +#endif // MY_SIGNING_REQUEST_SIGNATURES + return verificationResult; +} + +static uint8_t sha256_hash[32]; +Sha256Class _soft_sha256; + +void signerSha256Init(void) { + memset(sha256_hash, 0, 32); + _soft_sha256.init(); +} + +void signerSha256Update(const uint8_t* data, size_t sz) { + for (size_t i = 0; i < sz; i++) { + _soft_sha256.write(data[i]); + } +} + +uint8_t* signerSha256Final(void) { + memcpy(sha256_hash, _soft_sha256.result(), 32); + return sha256_hash; +} diff --git a/libraries/MySensors/core/MySigning.h b/libraries/MySensors/core/MySigning.h index 0c66096..68d76a8 100644 --- a/libraries/MySensors/core/MySigning.h +++ b/libraries/MySensors/core/MySigning.h @@ -178,14 +178,10 @@ * signatures. The setting is defined using @ref MY_SIGNING_SOFT_RANDOMSEED_PIN and the default is to use pin A7. The same configuration * possibilities exist as with the other configuration options. * - * Thirdly, if you use the software backend, you need to create/set a HMAC key to use. This key is 32 bytes wide and should - * be an arbitrarily chosen value. A string is OK, but as this key never needs to be “parsed” a completely random number is recommended. The - * key is stored in software and can be set "globally" in @ref MyConfig.h or locally in your sketch. The define is @ref MY_SIGNING_SOFT_HMAC_KEY. + * Thirdly, if you use the software backend, you need to personalize the node (see @ref perzonalization). * @code{.cpp} * #define MY_SIGNING_SOFT - * #define MY_SIGNING_SOFT_SERIAL 0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78 * #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 - * #define MY_SIGNING_SOFT_HMAC_KEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0,0x11,0x22 * #define MY_SIGNING_REQUEST_SIGNATURES * #include * ... @@ -195,7 +191,8 @@ * * @anchor perzonalization If you use the “real” ATSHA204A, before any signing operations can be done, the device needs to be personalized. * This can be a daunting process as it involves irreversibly writing configurations to the device, which cannot be undone. I have however tried - * to simplify the process as much as possibly by creating a helper-sketch specifically for this purpose in @ref Sha204Personalizer.ino + * to simplify the process as much as possibly by creating a helper-sketch specifically for this purpose in @ref SecurityPersonalizer.ino + * Note that you also need to do personalization for software signing, but then the values are stored in EEPROM (described below). * * The process of personalizing the ATSHA204A involves * - Writing and locking chip configuration @@ -211,15 +208,15 @@ * if this is desired.
* Now it is possible to execute the sketch to lock the configuration and enable the RNG.
* Next step is to decide if a new key should be generated or an existing key should be stored to the device. This is determined using - * @ref USER_KEY_DATA, which, if defined, will use the data in the variable user_key_data.
- * If @ref USER_KEY_DATA is disabled, the RNG will be used to generate a key. This key obviously need to be made available to you so you can use + * @ref USER_KEY, which, if defined, will use the data in the variable user_key_data.
+ * If @ref USER_KEY is disabled, the RNG will be used to generate a key. This key obviously need to be made available to you so you can use * it in other devices in the network, and this key is therefore also printed on the serial console when it has been generated.
* The key (generated or provided) will be written to the device unless @ref SKIP_KEY_STORAGE is set. As long as the data zone is kept unlocked * the key can be replaced at any time. However, Atmel suggests the data region to be locked for maximum security. On the other hand, they also * claim that the key is not readable from the device even if the data zone remains unlocked so the need for locking the data region is optional * for MySensors usage.
* For devices that does not have serial debug possibilities, it is possible to set @ref SKIP_UART_CONFIRMATION, but it is required to set - * @ref USER_KEY_DATA if this option is enabled since a generated and potentially unknown key could be written to the device and thus rendering + * @ref USER_KEY if this option is enabled since a generated and potentially unknown key could be written to the device and thus rendering * it useless (if the data zone is also locked).
* For devices with serial debug possibilities it is recommended to not use @ref SKIP_UART_CONFIRMATION as the sketch without that setting will * ask user to send a ‘space’ character on the serial terminal before any locking operations are executed as an additional confirmation that @@ -232,7 +229,7 @@ * -# Disable @ref LOCK_DATA * -# Enable @ref SKIP_KEY_STORAGE * -# Disable @ref SKIP_UART_CONFIRMATION - * -# Disable @ref USER_KEY_DATA + * -# Disable @ref USER_KEY * - Execute the sketch on the “master” device to obtain a randomized key. Save this key to a secure location and keep it confidential so that\n you can retrieve it if you need to personalize more devices later on. * - Now reconfigure the sketch with these settings: @@ -241,19 +238,38 @@ * according to Atmel, but they also claim that key is not readable even if data region remains unlocked from the slot we are using) * -# Disable @ref SKIP_KEY_STORAGE * -# Enable @ref SKIP_UART_CONFIRMATION - * -# Enable @ref USER_KEY_DATA + * -# Enable @ref USER_KEY * - Put the saved key in the user_key_data variable. * - Now execute the sketch on all devices you want to personalize with this secret key. * + * For software signing the personalization procedure looks slightly different.
+ * - If you do not have the ATSHA204A device and need to generate random keys: + * -# Enable @ref USE_SOFT_SIGNING + * - If you do have the ATSHA204A and want to use it to generate random keys for soft signing: + * -# Disable @ref USE_SOFT_SIGNING + * - If you want to review existing EEPROM configuration to determine if anything needs to be updated: + * -# Make sure to disable any ATSHA204A update features if you use it (enable @ref SKIP_KEY_STORAGE, disable @ref LOCK_CONFIGURATION and @ref LOCK_DATA) + * -# Disable @ref STORE_SOFT_KEY + * -# Disable @ref STORE_SOFT_SERIAL + * -# Disable @ref STORE_AES_KEY + * - Execute the sketch and the EEPROM configuration will be printed on the serial console. + * - Decide what you want to update and enable the corresponding flags (@ref STORE_SOFT_KEY, @ref STORE_SOFT_SERIAL, @ref STORE_AES_KEY). + * - If you have existing keys and want to use them instead of having the values randomized: + * - Enable the corresponding flag (@ref USER_SOFT_KEY, @ref USER_SOFT_SERIAL, @ref USER_AES_KEY) and also set the appropriate values + * (@ref user_soft_key_data, @ref user_soft_serial, @ref user_aes_key) + * + * The personalizer also takes care of generation and storage of the AES key used for RF traffic encryption. + * See the software signing personalization flow for how to manage it. + * * That’s it. Personalization is done and the device is ready to execute signing operations which are valid only on your personal network. * * If a node does require signing, any unsigned message sent to the node will be rejected.
* This also applies to the gateway. However, the difference is that the gateway will only require signed messages from nodes it knows in turn * require signed messages.
* A node can also inform a different node that it expects to receive signed messages from it. This is done by transmitting an internal message - * of type @ref I_REQUEST_SIGNING and provide a boolean for payload, set to @c true.
+ * of type @ref I_SIGNING_PRESENTATION and provide flags as payload that inform the receiver of the signing preferences of the sender.
* All nodes and gateways in a network maintain a table where the signing preferences of all nodes are stored. This is also stored in EEPROM so - * if the gateway reboots, the nodes does not have to retransmit a signing request to the gateway for the gateway to realize that the node + * if the gateway reboots, the nodes does not have to retransmit a signing presentation to the gateway for the gateway to realize that the node * expect signed messages.
* Also, the nodes that do not require signed messages will also inform gateway of this, so if you reprogram a node to stop require signing, * the gateway will adhere to this as soon as the new node has presented itself to the gateway. @@ -288,13 +304,11 @@ * remove it from the nodes whitelist, and it will no longer be able to communicate signed messages to the door node. * * In case you want to be able to "whitelist" trusted nodes (in order to be able to revoke them in case they are lost) you also need to take note - * of the serial number of the ATSHA device. This is unique for each device. The serial number is printed in a copy+paste friendly format by the - * personalizer for this purpose. For the software backend, it is defined by @ref MY_SIGNING_SOFT_SERIAL. You should keep this value unique for each - * node in the network.
+ * of the serial number of the ATSHA device or the software value stored in EEPROM. This is unique for each device. The serial number is printed + * in a copy+paste friendly format by the personalizer for this purpose.
* The whitelist is stored on the node that require signatures. When a received message is verified, the serial of the sender is looked up in a * list stored on the receiving node, and the corresponding serial stored in the list for that sender is then included in the signature verification * process. The list is stored as the value of the flag that enables whitelisting, @ref MY_SIGNING_NODE_WHITELISTING.
- * For whitelisting to work, the sending node also needs to have whitelisting enabled.
* * Whitelisting is achieved by 'salting' the signature with some node-unique information known to the receiver. In the case of ATSHA204A this is the * unique serial number programmed into the circuit. This unique number is never transmitted over the air in clear text, so Eve will not be able to @@ -303,8 +317,8 @@ * the originating NodeId of the signed message and do the corresponding calculation with the serial it has stored in it's whitelist if it finds a * matching entry in it's whitelist. * - * Whitelisting is an optional alternative because it adds some code which might not be desirable for every user. So if you want the ability to - * provide and use whitelists, as well as transmitting to a node with a whitelist, you need to enable @ref MY_SIGNING_NODE_WHITELISTING.
+ * Whitelisting is an optional alternative because it adds some code and configuration options which might not be desirable for every user. So if + * you want the ability to use whitelists, you need to enable @ref MY_SIGNING_NODE_WHITELISTING.
* The whitelist is provided as value of the flag that enable it as follows (example is a node that require signing as well): * @code{.cpp} * #define MY_SIGNING_ATSHA204 @@ -314,31 +328,26 @@ * ... * @endcode * In this example, there are two nodes in the whitelist; the gateway, and a separate node that communicates directly with this node (with signed - * messages). + * messages). You do not need to do anything special for the sending nodes, apart from making sure they support signing. * - * The "soft" backend of course also support whitelisting. However, since it does not contain a unique identifier, you have to provide an additional - * setting (@ref MY_SIGNING_SOFT_SERIAL) when you enable whitelisting as illustrated in this example: + * The "soft" backend of course also support whitelisting. Example: * @code{.cpp} * #define MY_SIGNING_SOFT - * #define MY_SIGNING_SOFT_SERIAL 0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78 * #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 - * #define MY_SIGNING_SOFT_HMAC_KEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0,0x11,0x22 * #define MY_SIGNING_REQUEST_SIGNATURES * #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}},{.nodeId = 2,.serial = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09}}} * #include * ... * @endcode * - * For a node that should transmit whitelisted messages but not receive whitelisted messages, you can set the whitelist flag as follows: + * For a node that should transmit whitelisted messages but not receive whitelisted messages, you do not need any special configurations: * @code{.cpp} - * #define MY_SIGNING_NODE_WHITELISTING {} + * #define MY_SIGNING_SOFT + * #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 * @endcode - * Remember that for the software backend, you still need to set @ref MY_SIGNING_SOFT_SERIAL (a default is set in MyConfig.h if you do not provide one - * in your sketch. - * - * It is important to emphasize that you do not have to provide a whitelist that has entries for all nodes that transmit signed messages to the node - * in question. You only need to have entries for the nodes that in turn have enabled @ref MY_SIGNING_NODE_WHITELISTING. Nodes that does not have - * this option enabled can still transmit "regular" signed messages as long as they do not match a NodeId in the receivers whitelist. + * Remember that you always need to select a signing backend for all nodes that communicate to a node that require whitelisting. + * Also, note that a node that use whitelisting will not accept messages from nodes that are not present in it's whitelist. + * And you have to personalize all nodes that use signing with a common HMAC key but different serial numbers (ATSHA204A always has unique serials). * * @section MySigningtechnical The technical stuff * @@ -363,9 +372,9 @@ * Exactly how this is done can be reviewd in the source for the ATSHA204SOFT backend and the ATSHA204A * datasheet. * In the MySensors protocol, the following internal messagetypes handles signature requirements and nonce requests:
- * @ref I_REQUEST_SIGNING
- * @ref I_GET_NONCE
- * @ref I_GET_NONCE_RESPONSE
+ * @ref I_SIGNING_PRESENTATION
+ * @ref I_NONCE_REQUEST
+ * @ref I_NONCE_RESPONSE
* * Also, the version field in the header has been reduced from 3 to 2 bits in order to fit a single bit to indicate that a message is signed. * @@ -409,14 +418,11 @@ * Nor can it be changed in order to do it in the future. You can also use whitelisting to revoke your lost node.
* This is an unlikely use case because there really is no reason to sign sensor values. If you for some reason want to obfuscate sensor data, * encryption is a better alternative.
- * Configuration example for a motion sensor with whitelisting (it has to have the gateway whitelisted since the gateway will send signed messages to the node):
+ * Configuration example for a motion sensor:
* @code{.cpp} * #define MY_SIGNING_SOFT - * #define MY_SIGNING_SOFT_SERIAL 0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78 * #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 - * #define MY_SIGNING_SOFT_HMAC_KEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0,0x11,0x22 * #define MY_SIGNING_REQUEST_SIGNATURES - * #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}}} * #include * ... * @endcode @@ -424,9 +430,7 @@ * The gateway needs to configured with a whitelist (and it have to have an entry for all nodes that send and/or require signed messages):
* @code{.cpp} * #define MY_SIGNING_SOFT - * #define MY_SIGNING_SOFT_SERIAL 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88 * #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 - * #define MY_SIGNING_SOFT_HMAC_KEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0,0x11,0x22 * #define MY_SIGNING_REQUEST_SIGNATURES * #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = MOTION_SENSOR_ID,.serial = {0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78}}} * #include @@ -441,16 +445,14 @@ * Configuration example for the keyfob (keyfob will only transmit to another node and not receive anything):
* @code{.cpp} * #define MY_SIGNING_ATSHA204 - * #define MY_SIGNING_NODE_WHITELISTING {} * #include * ... * @endcode + * * Configuration example for the door controller node (should require signing from anyone who wants to control it):
* @code{.cpp} * #define MY_SIGNING_SOFT - * #define MY_SIGNING_SOFT_SERIAL 0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78 * #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 - * #define MY_SIGNING_SOFT_HMAC_KEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0,0x11,0x22 * #define MY_SIGNING_REQUEST_SIGNATURES * #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}},{.nodeId = KEYFOB_ID,.serial = {}}} * #include @@ -460,7 +462,7 @@ * @subsection MySigningsketches Relevant sketches * * - @ref SecureActuator.ino - * - @ref Sha204Personalizer.ino + * - @ref SecurityPersonalizer.ino */ /** @@ -484,29 +486,50 @@ typedef struct { } whitelist_entry_t; #endif -// Macros for manipulating signing requirement table -/** @brief Return 'true' if provided node ID is requiring signed messages */ -#define DO_SIGN(node) (~_doSign[node>>3]&(1<<(node%8))) -/** @brief Mark provided node ID to require signed messages in table */ -#define SET_SIGN(node) (_doSign[node>>3]&=~(1<<(node%8))) -/** @brief Mark provided node ID to not require signed messages in table */ -#define CLEAR_SIGN(node) (_doSign[node>>3]|=(1<<(node%8))) /** @brief Helper macro to determine the number of elements in a array */ #define NUM_OF(x) (sizeof(x)/sizeof(x[0])) +/** @brief Helper macro to determine if node require serial salted signatures */ +#define DO_WHITELIST(node) (~_doWhitelist[node>>3]&(1<>3]&=~(1<>3]|=(1< - * Returns @c false if subsystem is busy processing an ongoing signing operation.
- * If successful, this marks the start of a signing operation at the receiving side so - * implementation is expected to do any necessary initializations within this call. - * \n@b Usage: This function is typically called as action when receiving a @ref I_GET_NONCE message. - * - * @param msg The message to put the nonce in. - * @returns @c true if nonce is successfully put in provided message. + * This function makes sure that the internal states of the signing infrastructure + * is set to a known initial state. + * \n@b Usage: This fuction should be called before any signing related operations take place. */ -bool signerGetNonce(MyMessage &msg); +void signerInit(void); + +/** + * @brief Does signing specific presentation for a node. + * + * This function makes sure any signing related presentation info is shared with the gateway. + * The presentation of the gateways signing preferences is done in @ref signerProcessInternal(). + * \n@b Usage: This function should be called by the presentation routine of the mysensors library. + * There is no need to call this directly from a sketch. + * + * @param msg Message buffer to use. + */ +void signerPresentation(MyMessage &msg); + +/** + * @brief Manages internal signing message handshaking. + * + * This function takes care of signing related message handshaknig such as nonce exchange. + * \n@b Usage: This function should be called by the incoming message handler before any further message + * processing is performed on internal messages. This function should only be called for @ref C_INTERNAL class + * messages. + * + * @param msg Message buffer to process. + * @returns @c true if caller should stop further message processing. + */ +bool signerProcessInternal(MyMessage &msg); /** * @brief Check timeout of verification session. @@ -525,7 +548,7 @@ bool signerCheckTimer(void); * Returns @c false if signing identifier found in message is not supported by the used backend.
* If successful, this marks the start of a signing operation at the sending side so * implementation is expected to do any necessary initializations within this call. - * \n@b Usage: This function is typically called as action when receiving a @ref I_GET_NONCE_RESPONSE + * \n@b Usage: This function is typically called as action when receiving a @ref I_NONCE_RESPONSE * message. * * @param msg The message to get the nonce from. @@ -537,13 +560,13 @@ bool signerPutNonce(MyMessage &msg); * @brief Signs provided message. All remaining space in message payload buffer is used for * signing identifier and signature. * - * Nonce used for signature calculation is the one generated previously using @ref signerGetNonce().
+ * Nonce used for signature calculation is the one generated previously within @ref signerProcessInternal().
* Nonce will be cleared when this function is called to prevent re-use of nonce.
* Returns @c false if subsystem is busy processing an ongoing signing operation.
* Returns @c false if not two bytes or more of free payload space is left in provided message.
* This ends a signing operation at the sending side so implementation is expected to do any * deinitializations and enter a power saving state within this call. - * \n@b Usage: This function is typically called as action when receiving a @ref I_GET_NONCE_RESPONSE + * \n@b Usage: This function is typically called as action when receiving a @ref I_NONCE_RESPONSE * message and after @ref signerPutNonce() has successfully been executed. * * @param msg The message to sign. @@ -568,5 +591,32 @@ bool signerSignMsg(MyMessage &msg); */ bool signerVerifyMsg(MyMessage &msg); +/** + * @brief Initialize a hash calculation session. + * + * Any ongoing calculation session will be terminated/reset. + */ +void signerSha256Init(void); + +/** + * @brief Add data to an ongoing hash calculation session. + * + * More data can be added by repeated calls to this function. + * + * @param data Buffer with data to add. + * @param sz Size of data buffer. + */ +void signerSha256Update(const uint8_t* data, size_t sz); + +/** + * @brief Finalize an ongong hash calculation session and return the hash. + * + * The returned hash size is always 32 bytes. Buffer can be assumed to be valid + * until @ref signerSha256Init() is called again. + * + * @returns Buffer with 32-byte hash. + */ +uint8_t* signerSha256Final(void); + #endif /** @}*/ \ No newline at end of file diff --git a/libraries/MySensors/core/MySigningAtsha204.cpp b/libraries/MySensors/core/MySigningAtsha204.cpp index 218c22f..61a1293 100644 --- a/libraries/MySensors/core/MySigningAtsha204.cpp +++ b/libraries/MySensors/core/MySigningAtsha204.cpp @@ -29,7 +29,7 @@ #define SIGNING_IDENTIFIER (1) -// Define MY_DEBUG in your sketch to enable signing backend debugprints +// Define MY_DEBUG_VERBOSE_SIGNING in your sketch to enable signing backend debugprints ATSHA204Class atsha204(MY_SIGNING_ATSHA204_PIN); unsigned long _signing_timestamp; @@ -38,16 +38,17 @@ uint8_t _signing_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1]; uint8_t _signing_temp_message[SHA_MSG_SIZE]; uint8_t _singning_rx_buffer[SHA204_RSP_SIZE_MAX]; uint8_t _singning_tx_buffer[SHA204_CMD_SIZE_MAX]; +extern uint8_t _doWhitelist[32]; #ifdef MY_SIGNING_NODE_WHITELISTING const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING; #endif -void signerCalculateSignature(MyMessage &msg); -uint8_t* signerSha256(const uint8_t* data, size_t sz); +static void signerCalculateSignature(MyMessage &msg); +static uint8_t* signerSha256(const uint8_t* data, size_t sz); -#ifdef MY_DEBUG +#ifdef MY_DEBUG_VERBOSE_SIGNING static char i2h(uint8_t i) { uint8_t k = i & 0x0F; @@ -57,8 +58,7 @@ static char i2h(uint8_t i) return 'A' + k - 10; } -static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) -{ +static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) { static char printBuffer[300]; #ifdef MY_GATEWAY_FEATURE // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) @@ -86,7 +86,23 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, #define DEBUG_SIGNING_PRINTBUF(str, buf, sz) #endif -bool signerGetNonce(MyMessage &msg) { +void signerAtsha204Init(void) { +} + +bool signerAtsha204CheckTimer(void) { + if (_signing_verification_ongoing) { + if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) { + DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0); + // Purge nonce + memset(_signing_current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH); + _signing_verification_ongoing = false; + return false; + } + } + return true; +} + +bool signerAtsha204GetNonce(MyMessage &msg) { DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0); // Generate random number for use as nonce // We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value @@ -104,9 +120,6 @@ bool signerGetNonce(MyMessage &msg) { // We set the part of the 32-byte nonce that does not fit into a message to 0xAA memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD); - // Replace the first byte in the nonce with our signing identifier - _signing_current_nonce[0] = SIGNING_IDENTIFIER; - // Transfer the first part of the nonce to the message msg.set(_signing_current_nonce, MAX_PAYLOAD); _signing_verification_ongoing = true; @@ -118,33 +131,15 @@ bool signerGetNonce(MyMessage &msg) { return true; } -bool signerCheckTimer() { - if (_signing_verification_ongoing) { - if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) { - DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0); - // Purge nonce - memset(_signing_current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH); - _signing_verification_ongoing = false; - return false; - } - } - return true; -} - -bool signerPutNonce(MyMessage &msg) { +void signerAtsha204PutNonce(MyMessage &msg) { DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0); - if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) { - DEBUG_SIGNING_PRINTBUF(F("Incorrect signing identifier"), NULL, 0); - return false; - } memcpy(_signing_current_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_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD); - return true; } -bool signerSignMsg(MyMessage &msg) { +bool signerAtsha204SignMsg(MyMessage &msg) { // If we cannot fit any signature in the message, refuse to sign it if (mGetLength(msg) > MAX_PAYLOAD-2) { DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0); @@ -155,14 +150,14 @@ bool signerSignMsg(MyMessage &msg) { mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated signerCalculateSignature(msg); -#ifdef MY_SIGNING_NODE_WHITELISTING - // Salt the signature with the senders nodeId and the unique serial of the ATSHA device - memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed - _signing_current_nonce[32] = msg.sender; - atsha204.getSerialNumber(&_signing_current_nonce[33]); - (void)signerSha256(_signing_current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place - DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0); -#endif + if (DO_WHITELIST(msg.destination)) { + // Salt the signature with the senders nodeId and the unique serial of the ATSHA device + memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed + _signing_current_nonce[32] = msg.sender; + atsha204.getSerialNumber(&_signing_current_nonce[33]); + (void)signerSha256(_signing_current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place + DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0); + } // Overwrite the first byte in the signature with the signing identifier _singning_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER; @@ -174,7 +169,7 @@ bool signerSignMsg(MyMessage &msg) { return true; } -bool signerVerifyMsg(MyMessage &msg) { +bool signerAtsha204VerifyMsg(MyMessage &msg) { if (!_signing_verification_ongoing) { DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0); return false; @@ -196,7 +191,8 @@ bool signerVerifyMsg(MyMessage &msg) { #ifdef MY_SIGNING_NODE_WHITELISTING // Look up the senders nodeId in our whitelist and salt the signature with that data - for (int j=0; j < NUM_OF(_signing_whitelist); j++) { + size_t j; + for (j=0; j < NUM_OF(_signing_whitelist); j++) { if (_signing_whitelist[j].nodeId == msg.sender) { DEBUG_SIGNING_PRINTBUF(F("Sender found in whitelist"), NULL, 0); memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed @@ -206,6 +202,10 @@ bool signerVerifyMsg(MyMessage &msg) { break; } } + if (j == NUM_OF(_signing_whitelist)) { + DEBUG_SIGNING_PRINTBUF(F("Sender not found in whitelist, message rejected!"), NULL, 0); + return false; + } #endif // Overwrite the first byte in the signature with the signing identifier @@ -226,7 +226,7 @@ bool signerVerifyMsg(MyMessage &msg) { } // Helper to calculate signature of msg (returned in _singning_rx_buffer[SHA204_BUFFER_POS_DATA]) -void signerCalculateSignature(MyMessage &msg) { +static void signerCalculateSignature(MyMessage &msg) { memset(_signing_temp_message, 0, 32); memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); @@ -259,7 +259,7 @@ void signerCalculateSignature(MyMessage &msg) { // Helper to calculate a generic SHA256 digest of provided buffer (only supports one block) // The pointer to the hash is returned, but the hash is also stored in _singning_rx_buffer[SHA204_BUFFER_POS_DATA]) -uint8_t* signerSha256(const uint8_t* data, size_t sz) { +static uint8_t* signerSha256(const uint8_t* data, size_t sz) { // Initiate SHA256 calculator (void)atsha204.sha204m_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL, SHA_COUNT_SHORT, _singning_tx_buffer, SHA_RSP_SIZE_SHORT, _singning_rx_buffer); diff --git a/libraries/MySensors/core/MySigningAtsha204Soft.cpp b/libraries/MySensors/core/MySigningAtsha204Soft.cpp index c4152f6..037905a 100644 --- a/libraries/MySensors/core/MySigningAtsha204Soft.cpp +++ b/libraries/MySensors/core/MySigningAtsha204Soft.cpp @@ -33,24 +33,25 @@ #define SIGNING_IDENTIFIER (1) -// Define MY_DEBUG in your sketch to enable signing backend debugprints +// 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_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH]; uint8_t _signing_temp_message[32]; -static uint8_t _signing_hmac_key[32] = { MY_SIGNING_SOFT_HMAC_KEY }; +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 uint8_t _signing_node_serial_info[SHA204_SERIAL_SZ] = {MY_SIGNING_SOFT_SERIAL}; const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING; #endif -void signerCalculateSignature(MyMessage &msg); +static void signerCalculateSignature(MyMessage &msg); -#ifdef MY_DEBUG +#ifdef MY_DEBUG_VERBOSE_SIGNING static char i2h(uint8_t i) { uint8_t k = i & 0x0F; @@ -60,8 +61,7 @@ static char i2h(uint8_t i) return 'A' + k - 10; } -static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) -{ +static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) { static char printBuffer[300]; #ifdef MY_GATEWAY_FEATURE // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) @@ -89,7 +89,28 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, #define DEBUG_SIGNING_PRINTBUF(str, buf, sz) #endif -bool signerGetNonce(MyMessage &msg) { +void signerAtsha204SoftInit(void) { + // initialize pseudo-RNG + randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN)); + // 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); +} + +bool signerAtsha204SoftCheckTimer(void) { + if (_signing_verification_ongoing) { + if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) { + DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0); + // Purge nonce + memset(_signing_current_nonce, 0xAA, 32); + _signing_verification_ongoing = false; + return false; + } + } + return true; +} + +bool signerAtsha204SoftGetNonce(MyMessage &msg) { DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0); // We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value @@ -104,9 +125,6 @@ bool signerGetNonce(MyMessage &msg) { // We set the part of the 32-byte nonce that does not fit into a message to 0xAA memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD); - // Replace the first byte in the nonce with our signing identifier - _signing_current_nonce[0] = SIGNING_IDENTIFIER; - // Transfer the first part of the nonce to the message msg.set(_signing_current_nonce, MAX_PAYLOAD); _signing_verification_ongoing = true; @@ -118,33 +136,15 @@ bool signerGetNonce(MyMessage &msg) { return true; } -bool signerCheckTimer() { - if (_signing_verification_ongoing) { - if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) { - DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0); - // Purge nonce - memset(_signing_current_nonce, 0xAA, 32); - _signing_verification_ongoing = false; - return false; - } - } - return true; -} - -bool signerPutNonce(MyMessage &msg) { +void signerAtsha204SoftPutNonce(MyMessage &msg) { DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0); - if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) { - DEBUG_SIGNING_PRINTBUF(F("Incorrect signing identifier"), NULL, 0); - return false; - } memcpy(_signing_current_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_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD); - return true; } -bool signerSignMsg(MyMessage &msg) { +bool signerAtsha204SoftSignMsg(MyMessage &msg) { // If we cannot fit any signature in the message, refuse to sign it if (mGetLength(msg) > MAX_PAYLOAD-2) { DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0); @@ -155,16 +155,16 @@ bool signerSignMsg(MyMessage &msg) { mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated signerCalculateSignature(msg); -#ifdef MY_SIGNING_NODE_WHITELISTING - // 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 MY_VERIFICATION_TIMEOUT_MS) { - debug(PSTR("nonce tmo\n")); - ledBlinkErr(1); - return false; - } - if (_signingNonceStatus == SIGN_OK) { - message = _msgSign; // Write the signed message back - } else { - debug(PSTR("sign fail\n")); - ledBlinkErr(1); - return false; - } - // After this point, only the 'last' member of the message structure is allowed to be altered if the message has been signed, - // or signature will become invalid and the message rejected by the receiver - } else if (_nc.nodeId == message.sender) { - mSetSigned(message, 0); // Message is not supposed to be signed, make sure it is marked unsigned - } - #endif + if (!signerSignMsg(message)) { + debug(PSTR("sign fail\n")); + ledBlinkErr(1); + } #if !defined(MY_REPEATER_FEATURE) @@ -552,19 +452,8 @@ void transportPresentNode() { // Present node and request config #ifndef MY_GATEWAY_FEATURE if (_nc.nodeId != AUTO) { - #ifdef MY_SIGNING_FEATURE - // Notify gateway (and possibly controller) about the signing preferences of this node - #if defined(MY_SIGNING_REQUEST_SIGNATURES) - _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true)); - // If we do require signing, wait for the gateway to tell us how it prefer us to transmit our messages - wait(2000); - #else - _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false)); - #endif - #else - // We do not support signing, make sure gateway knows this - _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false)); - #endif + // Send signing preferences for this node + signerPresentation(_msg); // Send presentation for this radio node #ifdef MY_REPEATER_FEATURE diff --git a/libraries/MySensors/core/MyTransport.h b/libraries/MySensors/core/MyTransport.h index 18be525..e23869e 100644 --- a/libraries/MySensors/core/MyTransport.h +++ b/libraries/MySensors/core/MyTransport.h @@ -41,9 +41,6 @@ // Search for a new parent node after this many transmission failures #define SEARCH_FAILURES 5 -// Status when waiting for signing nonce in process -enum { SIGN_WAITING_FOR_NONCE = 0, SIGN_OK = 1 }; - // FW config structure, stored in eeprom typedef struct { diff --git a/libraries/MySensors/core/MyTransportNRF24.cpp b/libraries/MySensors/core/MyTransportNRF24.cpp index 1a62766..3d2ac1d 100644 --- a/libraries/MySensors/core/MyTransportNRF24.cpp +++ b/libraries/MySensors/core/MyTransportNRF24.cpp @@ -38,7 +38,7 @@ uint8_t _address; #if defined(MY_RF24_ENABLE_ENCRYPTION) AES _aes; uint8_t _dataenc[32] = {0}; - uint8_t _psk[] = { MY_RF24_ENCRYPTKEY }; + uint8_t _psk[16]; #endif bool transportInit() { @@ -65,7 +65,9 @@ bool transportInit() { #if defined(MY_RF24_ENABLE_ENCRYPTION) + hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16); _aes.set_key(_psk, 16); //set up AES-key + memset(_psk, 0, 16); // Make sure it is purged from memory when set #endif //_rf24.printDetails(); diff --git a/libraries/MySensors/core/MyTransportRFM69.cpp b/libraries/MySensors/core/MyTransportRFM69.cpp index f6ded5f..35305ce 100644 --- a/libraries/MySensors/core/MyTransportRFM69.cpp +++ b/libraries/MySensors/core/MyTransportRFM69.cpp @@ -30,7 +30,10 @@ bool transportInit() { // Start up the radio library (_address will be set later by the MySensors library) if (_radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID)) { #ifdef MY_RFM69_ENABLE_ENCRYPTION - _radio.encrypt(RFM69_ENCRYPTKEY); + uint8_t _psk[16]; + hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16); + _radio.encrypt(_psk); + memset(_psk, 0, 16); // Make sure it is purged from memory when set #endif return true; } diff --git a/libraries/MySensors/core/Version.h b/libraries/MySensors/core/Version.h index db9db44..fce3bc7 100644 --- a/libraries/MySensors/core/Version.h +++ b/libraries/MySensors/core/Version.h @@ -6,6 +6,6 @@ #ifndef Version_h #define Version_h -#define LIBRARY_VERSION "1.6.0-beta" +#define LIBRARY_VERSION "2.0.0-beta" #endif diff --git a/libraries/MySensors/drivers/AES/AES.h b/libraries/MySensors/drivers/AES/AES.h index a37a1e5..1d92c8c 100644 --- a/libraries/MySensors/drivers/AES/AES.h +++ b/libraries/MySensors/drivers/AES/AES.h @@ -35,7 +35,7 @@ /* code was modified by george spanos * 16/12/14 */ - +/** AES class */ class AES { public: @@ -351,7 +351,7 @@ class AES * @defgroup aeslib AES library for Arduino and Raspberry pi * @ingroup internals * - * @section Goals design Goals + * @section AesGoals design Goals * * This library is AESigned to be... * @li Fast and efficient. @@ -393,10 +393,10 @@ class AES * cd examples_Rpi
* sudo ./\

* - * @section News News + * @section AesNews News * * If issues are discovered with the documentation, please report them here - * @section Useful Useful References + * @section AesUseful Useful References * * Please refer to: * @@ -405,7 +405,7 @@ class AES * @li Source Code * @li All spaniakos Documentation Main Page * - * @section Board_Support Board Support + * @section AesBoard_Support Board Support * * Most standard Arduino based boards are supported: * - Arduino diff --git a/libraries/MySensors/drivers/ATSHA204/ATSHA204.h b/libraries/MySensors/drivers/ATSHA204/ATSHA204.h index 59a9f0e..c7034bf 100644 --- a/libraries/MySensors/drivers/ATSHA204/ATSHA204.h +++ b/libraries/MySensors/drivers/ATSHA204/ATSHA204.h @@ -199,7 +199,9 @@ #define SHA204_COMMAND_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) //! maximum command delay #define SHA204_CMD_SIZE_MIN ((uint8_t) 7) //! minimum number of bytes in command (from count byte to second CRC byte) +#ifndef SHA204_CMD_SIZE_MAX #define SHA204_CMD_SIZE_MAX ((uint8_t) SHA_COUNT_LONG) //! maximum size of command packet (SHA) +#endif #define SHA204_CRC_SIZE ((uint8_t) 2) //! number of CRC bytes #define SHA204_BUFFER_POS_STATUS (1) //! buffer index of status byte in status response #define SHA204_BUFFER_POS_DATA (1) //! buffer index of first data byte in data response diff --git a/libraries/MySensors/drivers/ATSHA204/sha256.h b/libraries/MySensors/drivers/ATSHA204/sha256.h index c0e3ef9..730e11d 100644 --- a/libraries/MySensors/drivers/ATSHA204/sha256.h +++ b/libraries/MySensors/drivers/ATSHA204/sha256.h @@ -23,7 +23,7 @@ class Sha256Class void initHmac(const uint8_t* secret, int secretLength); uint8_t* result(void); uint8_t* resultHmac(void); - virtual void write(uint8_t); + void write(uint8_t); private: void pad(); void addUncounted(uint8_t data); diff --git a/libraries/MySensors/examples/ClearEepromConfig/ClearEepromConfig.ino b/libraries/MySensors/examples/ClearEepromConfig/ClearEepromConfig.ino index fffc4f9..272bf43 100644 --- a/libraries/MySensors/examples/ClearEepromConfig/ClearEepromConfig.ino +++ b/libraries/MySensors/examples/ClearEepromConfig/ClearEepromConfig.ino @@ -23,16 +23,18 @@ * This sketch clears radioId, relayId and other routing information in EEPROM back to factory default * */ +// load core modules only +#define MY_CORE_ONLY -#include #include +#include void setup() { Serial.begin(MY_BAUD_RATE); Serial.println("Started clearing. Please wait..."); - for (int i=0;i<512;i++) { - EEPROM.write(i, 0xff); + for (int i=0;i * The sketch will require incoming messages to be signed and the use of signing backend is selected * by @ref MY_SIGNING_ATSHA204 or @ref MY_SIGNING_SOFT. Hard or soft ATSHA204 signing is supported.
- * If soft signing is chosen, make sure to use a correct HMAC key (see @ref MY_SIGNING_SOFT_HMAC_KEY).
* Whitelisting can be enabled through @ref MY_SIGNING_NODE_WHITELISTING in which case a single entry * is provided in this example which typically should map to the gateway of the network. */ #define MY_DEBUG //!< Enable debug prints to serial monitor +#define MY_DEBUG_VERBOSE_SIGNING //!< Enable signing related debug prints to serial monitor // Enable and select radio type attached #define MY_RADIO_NRF24 //!< NRF24L01 radio driver @@ -59,11 +59,6 @@ // SETTINGS FOR MY_SIGNING_SOFT -/// Set the soft_serial value to an arbitrary value for proper security (9 bytes) -#define MY_SIGNING_SOFT_SERIAL 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09 -/// Key to use for HMAC calculation in soft signing (32 bytes) -#define MY_SIGNING_SOFT_HMAC_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 - #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 //!< Unconnected analog pin for random seed // SETTINGS FOR MY_SIGNING_ATSHA204 diff --git a/libraries/MySensors/examples/Sha204Personalizer/Sha204Personalizer.ino b/libraries/MySensors/examples/SecurityPersonalizer/SecurityPersonalizer.ino similarity index 73% rename from libraries/MySensors/examples/Sha204Personalizer/Sha204Personalizer.ino rename to libraries/MySensors/examples/SecurityPersonalizer/SecurityPersonalizer.ino index 0634283..43e25be 100644 --- a/libraries/MySensors/examples/Sha204Personalizer/Sha204Personalizer.ino +++ b/libraries/MySensors/examples/SecurityPersonalizer/SecurityPersonalizer.ino @@ -21,16 +21,20 @@ /** * @ingroup MySigninggrp * @{ - * @file Sha204Personalizer.ino - * @brief SHA204 personalization sketch + * @file SecurityPersonalizer.ino + * @brief Security personalization sketch * * REVISION HISTORY - * - See git log (git log libraries/MySensors/examples/Sha204Personalizer/Sha204Personalizer.ino) + * - See git log (git log libraries/MySensors/examples/SecurityPersonalizer/SecurityPersonalizer.ino) */ /** - * @example Sha204Personalizer.ino - * This sketch will write factory default settings to the configuration zone + * @example SecurityPersonalizer.ino + * This sketch will personalize either none-volatile memory or ATSHA204A for security functions + * available in the MySensors library. + * + * For ATSHA204A: + * It will write factory default settings to the configuration zone * and then lock it.
* It will then either
* -# Generate a random value to use as a key which will be stored in @@ -45,13 +49,41 @@ * turn on the locking. Furthermore, user have to send a SPACE character on serial * console when prompted to do any locking. On boards that does not provide UART * input it is possible to configure the sketch to skip this confirmation. - * Default settings use ATSHA204 on @ref ATSHA204_PIN. + * Default settings use ATSHA204A on @ref MY_SIGNING_ATSHA204_PIN. + * + * For Soft signing: + * It will
+ * -# Generate a random value to use as a key which will be stored in EEPROM. + * The key is printed on UART (115200) in clear text for the user to be ablle to + * use it as a user-supplied key in other personalization executions where the same + * key is needed. + * -# Use a user-supplied value to use as a key which will be stored in EEPROM. + * -# Generate a random value to use as a serial number which will be stored in EEPROM. + * The serial number is printed on UART (115200) in clear text for the user to be ablle to + * use it as a user-supplied serial number in other personalization executions where the + * serial is needed (typically for a whitelist). + * -# Use a user-supplied value to use as a serial which will be stored in EEPROM. + * + * For Encryption support: + * -# Generate a random value to use as a AES key which will be stored in EEPROM. + * The AES key is printed on UART (115200) in clear text for the user to be ablle to + * use it as a user-supplied AES key in other personalization executions where the + * AES key is needed (typically for RF encryption). + * -# Use a user-supplied value to use as a AES key which will be stored in EEPROM. + * + * Personalizing EEPROM or ATSHA204A still require the appropriate configuration of the + * library to actually have an effect. There is no problem personalizing EEPROM and + * ATSHA204A at the same time. There is however a security risk with using the same + * data for EEPROM and ATSHA204A so it is recommended to use different serial and HMAC + * keys on the same device for ATSHA204A vs soft signing settings. * * Details on personalization procedure is given in @ref perzonalization. */ #include #include +#define MY_CORE_ONLY +#include // Doxygen specific constructs, not included when built normally // This is used to enable disabled macros/definitions to be included in the documentation as well. @@ -59,12 +91,17 @@ #define LOCK_CONFIGURATION #define LOCK_DATA #define SKIP_KEY_STORAGE -#define USER_KEY_DATA +#define USER_KEY #define SKIP_UART_CONFIRMATION +#define USE_SOFT_SIGNING +#define STORE_SOFT_KEY +#define USER_SOFT_KEY +#define STORE_SOFT_SERIAL +#define USER_SOFT_SERIAL +#define STORE_AES_KEY +#define USER_AES_KEY #endif -#define ATSHA204_PIN 17 //!< The pin the ATSHA204 is connected on (A3). - /** * @def LOCK_CONFIGURATION * @brief Uncomment this to enable locking the configuration zone. @@ -89,13 +126,13 @@ * @def SKIP_KEY_STORAGE * @brief Uncomment this to skip key storage (typically once key has been written once) */ -//#define SKIP_KEY_STORAGE +#define SKIP_KEY_STORAGE /** - * @def USER_KEY_DATA + * @def USER_KEY * @brief Uncomment this to skip key generation and use @ref user_key_data as key instead. */ -//#define USER_KEY_DATA +//#define USER_KEY /** * @def SKIP_UART_CONFIRMATION @@ -106,19 +143,84 @@ */ //#define SKIP_UART_CONFIRMATION -#if defined(SKIP_UART_CONFIRMATION) && !defined(USER_KEY_DATA) -#error You have to define USER_KEY_DATA for boards that does not have UART +/** + * @def USE_SOFT_SIGNING + * @brief Uncomment this to store data to EEPROM instead of ATSHA204A + */ +//#define USE_SOFT_SIGNING + +/** + * @def STORE_SOFT_KEY + * @brief Uncomment this to store soft HMAC key to EEPROM + */ +//#define STORE_SOFT_KEY + +/** + * @def USER_SOFT_KEY + * @brief Uncomment this to skip soft HMAC key generation and use @ref user_soft_key_data as HMAC key instead. + */ +//#define USER_SOFT_KEY + +/** + * @def STORE_SOFT_SERIAL + * @brief Uncomment this to store soft serial to EEPROM + */ +//#define STORE_SOFT_SERIAL + +/** + * @def USER_SOFT_SERIAL + * @brief Uncomment this to skip soft serial generation and use @ref user_soft_serial as serial instead. + */ +//#define USER_SOFT_SERIAL + +/** + * @def STORE_AES_KEY + * @brief Uncomment this to store AES key to EEPROM + */ +//#define STORE_AES_KEY + +/** + * @def USER_AES_KEY + * @brief Uncomment this to skip AES key generation and use @ref user_aes_key as key instead. + */ +//#define USER_AES_KEY + +#if defined(SKIP_UART_CONFIRMATION) && !defined(USER_KEY) +#error You have to define USER_KEY for boards that does not have UART #endif -#ifdef USER_KEY_DATA +#ifdef USER_KEY /** @brief The user-defined HMAC key to use for personalization */ #define MY_HMAC_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 /** @brief The data to store in key slot 0 */ const uint8_t user_key_data[32] = {MY_HMAC_KEY}; #endif -const int sha204Pin = ATSHA204_PIN; //!< The IO pin to use for ATSHA204 +#ifdef USER_SOFT_KEY +/** @brief The user-defined soft HMAC key to use for EEPROM personalization */ +#define MY_SOFT_HMAC_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +/** @brief The data to store as soft HMAC key in EEPROM */ +const uint8_t user_soft_key_data[32] = {MY_SOFT_HMAC_KEY}; +#endif + +#ifdef USER_SOFT_SERIAL +/** @brief The user-defined soft serial to use for EEPROM personalization */ +#define MY_SOFT_SERIAL 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +/** @brief The data to store as soft serial in EEPROM */ +const uint8_t user_soft_serial[9] = {MY_SOFT_SERIAL}; +#endif + +#ifdef USER_AES_KEY +/** @brief The user-defined AES key to use for EEPROM personalization */ +#define MY_AES_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +/** @brief The data to store as AES key in EEPROM */ +const uint8_t user_aes_key[16] = {MY_AES_KEY}; +#endif + +#ifndef USE_SOFT_SIGNING +const int sha204Pin = MY_SIGNING_ATSHA204_PIN; //!< The IO pin to use for ATSHA204A atsha204Class sha204(sha204Pin); +#endif /** @brief Print a error notice and halt the execution */ void halt() @@ -127,6 +229,7 @@ void halt() while(1); } +#ifndef USE_SOFT_SIGNING /** * @brief Write default configuration and return CRC of the configuration bits * @returns CRC over the configuration bits @@ -275,14 +378,53 @@ void write_key(uint8_t* key) halt(); } } +#endif // not USE_SOFT_SIGNING /** @brief Dump current configuration to UART */ void dump_configuration() { + uint8_t buffer[32]; +#ifndef USE_SOFT_SIGNING + Serial.println(F("EEPROM DATA:")); +#endif + hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32); + Serial.print(F("SOFT_HMAC_KEY | ")); + for (int j=0; j<32; j++) + { + if (buffer[j] < 0x10) + { + Serial.print('0'); // Because Serial.print does not 0-pad HEX + } + Serial.print(buffer[j], HEX); + } + Serial.println(); + hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9); + Serial.print(F("SOFT_SERIAL | ")); + for (int j=0; j<9; j++) + { + if (buffer[j] < 0x10) + { + Serial.print('0'); // Because Serial.print does not 0-pad HEX + } + Serial.print(buffer[j], HEX); + } + Serial.println(); + hwReadConfigBlock((void*)buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16); + Serial.print(F("AES_KEY | ")); + for (int j=0; j<16; j++) + { + if (buffer[j] < 0x10) + { + Serial.print('0'); // Because Serial.print does not 0-pad HEX + } + Serial.print(buffer[j], HEX); + } + Serial.println(); +#ifndef USE_SOFT_SIGNING uint8_t tx_buffer[SHA204_CMD_SIZE_MAX]; uint8_t rx_buffer[SHA204_RSP_SIZE_MAX]; uint8_t ret_code; - + Serial.println(F("ATSHA204A DATA:")); for (int i=0; i < 88; i += 4) { ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, i); @@ -712,25 +854,36 @@ void dump_configuration() Serial.println(); } } +#endif // not USE_SOFT_SIGNING } /** @brief Sketch setup code */ void setup() { + // Delay startup a bit for serial consoles to catch up + unsigned long enter = hwMillis(); + while (hwMillis() - enter < (unsigned long)500); +#ifndef USE_SOFT_SIGNING uint8_t tx_buffer[SHA204_CMD_SIZE_MAX]; uint8_t rx_buffer[SHA204_RSP_SIZE_MAX]; - uint8_t key[32]; uint8_t ret_code; uint8_t lockConfig = 0; uint8_t lockValue = 0; uint16_t crc; (void)crc; +#else + // initialize pseudo-RNG + randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN)); +#endif + uint8_t key[32]; + (void)key; Serial.begin(115200); - Serial.println(F("ATSHA204 personalization sketch for MySensors usage.")); - Serial.println(F("----------------------------------------------------")); + Serial.println(F("Personalization sketch for MySensors usage.")); + Serial.println(F("-------------------------------------------")); +#ifndef USE_SOFT_SIGNING // Wake device before starting operations ret_code = sha204.sha204c_wakeup(rx_buffer); if (ret_code != SHA204_SUCCESS) @@ -738,7 +891,171 @@ void setup() Serial.print(F("Failed to wake device. Response: ")); Serial.println(ret_code, HEX); halt(); } + // Read out lock config bits to determine if locking is possible + ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, 0x15<<2); + if (ret_code != SHA204_SUCCESS) + { + Serial.print(F("Failed to determine device lock status. Response: ")); Serial.println(ret_code, HEX); + halt(); + } + else + { + lockConfig = rx_buffer[SHA204_BUFFER_POS_DATA+3]; + lockValue = rx_buffer[SHA204_BUFFER_POS_DATA+2]; + } +#endif +#ifdef STORE_SOFT_KEY +#ifdef USER_SOFT_KEY + memcpy(key, user_soft_key_data, 32); + Serial.println(F("Using this user supplied soft HMAC key:")); +#else + // Retrieve random value to use as soft HMAC key +#ifdef USE_SOFT_SIGNING + for (int i = 0; i < 32; i++) { + key[i] = random(256) ^ micros(); + unsigned long enter = hwMillis(); + while (hwMillis() - enter < (unsigned long)2); + } + Serial.println(F("This value will be stored in EEPROM as soft HMAC key:")); +#else + ret_code = sha204.sha204m_random(tx_buffer, rx_buffer, RANDOM_SEED_UPDATE); + if (ret_code != SHA204_SUCCESS) + { + Serial.print(F("Random key generation failed. Response: ")); Serial.println(ret_code, HEX); + halt(); + } + else + { + memcpy(key, rx_buffer+SHA204_BUFFER_POS_DATA, 32); + } + if (lockConfig == 0x00) + { + Serial.println(F("This value will be stored in EEPROM as soft HMAC key:")); + } + else + { + Serial.println(F("Key is not randomized (configuration not locked):")); + } +#endif // not USE_SOFT_SIGNING +#endif // not USER_SOFT_KEY + Serial.print("#define MY_SOFT_HMAC_KEY "); + for (int i=0; i<32; i++) + { + Serial.print("0x"); + if (key[i] < 0x10) + { + Serial.print('0'); // Because Serial.print does not 0-pad HEX + } + Serial.print(key[i], HEX); + if (i < 31) Serial.print(','); + } + Serial.println(); + hwWriteConfigBlock((void*)key, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32); +#endif // STORE_SOFT_KEY + +#ifdef STORE_SOFT_SERIAL +#ifdef USER_SOFT_SERIAL + memcpy(key, user_soft_serial, 9); + Serial.println(F("Using this user supplied soft serial:")); +#else + // Retrieve random value to use as serial +#ifdef USE_SOFT_SIGNING + for (int i = 0; i < 9; i++) { + key[i] = random(256) ^ micros(); + unsigned long enter = hwMillis(); + while (hwMillis() - enter < (unsigned long)2); + } + Serial.println(F("This value will be stored in EEPROM as soft serial:")); +#else + ret_code = sha204.sha204m_random(tx_buffer, rx_buffer, RANDOM_SEED_UPDATE); + if (ret_code != SHA204_SUCCESS) + { + Serial.print(F("Random serial generation failed. Response: ")); Serial.println(ret_code, HEX); + halt(); + } + else + { + memcpy(key, rx_buffer+SHA204_BUFFER_POS_DATA, 9); + } + if (lockConfig == 0x00) + { + Serial.println(F("This value will be stored in EEPROM as soft serial:")); + } + else + { + Serial.println(F("Serial is not randomized (configuration not locked):")); + } +#endif // not USE_SOFT_SIGNING +#endif // not USER_SOFT_SERIAL + Serial.print("#define MY_SOFT_SERIAL "); + for (int i=0; i<9; i++) + { + Serial.print("0x"); + if (key[i] < 0x10) + { + Serial.print('0'); // Because Serial.print does not 0-pad HEX + } + Serial.print(key[i], HEX); + if (i < 8) Serial.print(','); + } + Serial.println(); + hwWriteConfigBlock((void*)key, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9); +#endif // STORE_SOFT_SERIAL + +#ifdef STORE_AES_KEY +#ifdef USER_AES_KEY + memcpy(key, user_aes_key, 16); + Serial.println(F("Using this user supplied AES key:")); +#else + // Retrieve random value to use as key +#ifdef USE_SOFT_SIGNING + for (int i = 0; i < 16; i++) { + key[i] = random(256) ^ micros(); + unsigned long enter = hwMillis(); + while (hwMillis() - enter < (unsigned long)2); + } + Serial.println(F("This key will be stored in EEPROM as AES key:")); +#else + ret_code = sha204.sha204m_random(tx_buffer, rx_buffer, RANDOM_SEED_UPDATE); + if (ret_code != SHA204_SUCCESS) + { + Serial.print(F("Random key generation failed. Response: ")); Serial.println(ret_code, HEX); + halt(); + } + else + { + memcpy(key, rx_buffer+SHA204_BUFFER_POS_DATA, 32); + } + if (lockConfig == 0x00) + { + Serial.println(F("This key will be stored in EEPROM as AES key:")); + } + else + { + Serial.println(F("Key is not randomized (configuration not locked):")); + } +#endif // not USE_SOFT_SIGNING +#endif // not USER_AES_KEY + Serial.print("#define MY_AES_KEY "); + for (int i=0; i<16; i++) + { + Serial.print("0x"); + if (key[i] < 0x10) + { + Serial.print('0'); // Because Serial.print does not 0-pad HEX + } + Serial.print(key[i], HEX); + if (i < 15) Serial.print(','); + } + Serial.println(); + hwWriteConfigBlock((void*)key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16); +#endif // STORE_AES_KEY + +#ifdef USE_SOFT_SIGNING + Serial.println(F("EEPROM configuration:")); + dump_configuration(); +#else // Output device revision on console ret_code = sha204.sha204m_dev_rev(tx_buffer, rx_buffer); if (ret_code != SHA204_SUCCESS) @@ -794,19 +1111,6 @@ void setup() Serial.println(); } - // Read out lock config bits to determine if locking is possible - ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, 0x15<<2); - if (ret_code != SHA204_SUCCESS) - { - Serial.print(F("Failed to determine device lock status. Response: ")); Serial.println(ret_code, HEX); - halt(); - } - else - { - lockConfig = rx_buffer[SHA204_BUFFER_POS_DATA+3]; - lockValue = rx_buffer[SHA204_BUFFER_POS_DATA+2]; - } - if (lockConfig != 0x00) { // Write config and get CRC for the updated config @@ -886,9 +1190,9 @@ void setup() #ifdef SKIP_KEY_STORAGE Serial.println(F("Disable SKIP_KEY_STORAGE to store key.")); #else -#ifdef USER_KEY_DATA +#ifdef USER_KEY memcpy(key, user_key_data, 32); - Serial.println(F("Using this user supplied key:")); + Serial.println(F("Using this user supplied HMAC key:")); #else // Retrieve random value to use as key ret_code = sha204.sha204m_random(tx_buffer, rx_buffer, RANDOM_SEED_UPDATE); @@ -1022,9 +1326,11 @@ void setup() { Serial.println(F("Skipping OTP/data zone lock (zone already locked).")); } +#endif // not USE_SOFT_SIGNING Serial.println(F("--------------------------------")); Serial.println(F("Personalization is now complete.")); +#ifndef USE_SOFT_SIGNING Serial.print(F("Configuration is ")); if (lockConfig == 0x00) { @@ -1043,6 +1349,7 @@ void setup() { Serial.println("UNLOCKED"); } +#endif } /** @brief Sketch execution code */ diff --git a/libraries/MySensors/keywords.txt b/libraries/MySensors/keywords.txt index 7df3813..fc148be 100644 --- a/libraries/MySensors/keywords.txt +++ b/libraries/MySensors/keywords.txt @@ -31,6 +31,9 @@ smartSleep KEYWORD2 ####################################### AUTO LITERAL1 MY_DEBUG LITERAL1 +MY_CORE_ONLY LITERAL1 +MY_DEBUG_VERBOSE LITERAL1 +MY_DEBUG_VERBOSE_SIGNING LITERAL1 MY_REPEATER_FEATURE LITERAL1 MY_RADIO_NRF24 LITERAL1 MY_RADIO_RFM69 LITERAL1 @@ -57,10 +60,7 @@ MY_VERIFICATION_TIMEOUT_MS LITERAL1 MY_SIGNING_NODE_WHITELISTING LITERAL1 MY_SIGNING_ATSHA204_PIN LITERAL1 MY_SIGNING_SOFT_RANDOMSEED_PIN LITERAL1 -MY_SIGNING_SOFT_SERIAL LITERAL1 -MY_SIGNING_SOFT_HMAC_KEY LITERAL1 MY_RF24_ENABLE_ENCRYPTION LITERAL1 -MY_RF24_ENCRYPTKEY LITERAL1 MY_RF24_CE_PIN LITERAL1 MY_RF24_CS_PIN LITERAL1 MY_RF24_PA_LEVEL LITERAL1 @@ -78,7 +78,7 @@ MY_RFM69_NETWORKID LITERAL1 MY_RF69_IRQ_PIN LITERAL1 MY_RF69_SPI_CS LITERAL1 MY_RF69_IRQ_NUM LITERAL1 -MY_RFM69_ENCRYPTKEY LITERAL1 +MY_RFM69_ENABLE_ENCRYPTION LITERAL1 MY_GATEWAY_SERIAL LITERAL1 MY_GATEWAY_W5100 LITERAL1 MY_GATEWAY_ENC28J60 LITERAL1 diff --git a/libraries/MySensors/library.properties b/libraries/MySensors/library.properties index db72528..60d1ce7 100644 --- a/libraries/MySensors/library.properties +++ b/libraries/MySensors/library.properties @@ -1,5 +1,5 @@ name=MySensors -version=1.6.0 +version=2.0.0-beta author=The MySensors Team maintainer=The MySensors Team sentence=Home Automation Framework diff --git a/libraries/sha204/sha204_library.h b/libraries/sha204/sha204_library.h index fe12392..c22f9ed 100644 --- a/libraries/sha204/sha204_library.h +++ b/libraries/sha204/sha204_library.h @@ -259,7 +259,9 @@ #define SHA204_COMMAND_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) //! maximum command delay #define SHA204_CMD_SIZE_MIN ((uint8_t) 7) //! minimum number of bytes in command (from count byte to second CRC byte) +#ifndef SHA204_CMD_SIZE_MAX #define SHA204_CMD_SIZE_MAX ((uint8_t) 84) //! maximum size of command packet (CheckMac) +#endif #define SHA204_CRC_SIZE ((uint8_t) 2) //! number of CRC bytes #define SHA204_BUFFER_POS_STATUS (1) //! buffer index of status byte in status response #define SHA204_BUFFER_POS_DATA (1) //! buffer index of first data byte in data response