From ff015dd3730d892400a0e7c7a72c6858e3b2f7c6 Mon Sep 17 00:00:00 2001 From: Patrick Fallberg Date: Sat, 2 Jan 2016 14:55:41 +0100 Subject: [PATCH] Signing whitelisting rewritten Whitelisting is now supported by all nodes that support signing, while only nodes that define a whitelist with MY_SIGNING_NODE_WHITELISTING will check signatures against a whitelist. This allows a single node in a network to have a whitelist without require all nodes that communicate it to be configured for whitelisting. The node will publish its whitelisting preferences to the gateway together with its signing preferences. In other words; whitelisting is now working as signing requirements does. A node that use whitelisting will reject any message (signed or unsigned) from nodes not present in its whitelist. Fixes #279 --- libraries/MySensors/core/MyMessage.h | 6 +- libraries/MySensors/core/MySensorCore.h | 3 +- libraries/MySensors/core/MySigning.cpp | 125 ++++++++++++------ libraries/MySensors/core/MySigning.h | 44 +++--- .../MySensors/core/MySigningAtsha204.cpp | 24 ++-- .../MySensors/core/MySigningAtsha204Soft.cpp | 30 +++-- 6 files changed, 149 insertions(+), 83 deletions(-) diff --git a/libraries/MySensors/core/MyMessage.h b/libraries/MySensors/core/MyMessage.h index a81073a..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_NONCE_REQUEST, /*!< Request for a nonce */ - I_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/MySensorCore.h b/libraries/MySensors/core/MySensorCore.h index 4d2c92e..6400373 100644 --- a/libraries/MySensors/core/MySensorCore.h +++ b/libraries/MySensors/core/MySensorCore.h @@ -52,7 +52,8 @@ #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 +#define EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS+32) +#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_WHITELIST_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) diff --git a/libraries/MySensors/core/MySigning.cpp b/libraries/MySensors/core/MySigning.cpp index 6e95b4a..7d297e2 100644 --- a/libraries/MySensors/core/MySigning.cpp +++ b/libraries/MySensors/core/MySigning.cpp @@ -20,6 +20,10 @@ #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) + #if defined(MY_DEBUG_VERBOSE) #define SIGN_DEBUG(x,...) debug(x, ##__VA_ARGS__) #else @@ -30,8 +34,9 @@ #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 -MyMessage _msgSign; // Buffer for message to sign. +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 @@ -72,7 +77,7 @@ static bool skipSign(MyMessage &msg) { SIGN_DEBUG(PSTR("Skipping security for ACK on command %d type %d\n"), mGetCommand(msg), msg.type); return true; } else if (mGetCommand(msg) == C_INTERNAL && - (msg.type == I_NONCE_REQUEST || msg.type == I_NONCE_RESPONSE || msg.type == I_REQUEST_SIGNING || + (msg.type == I_NONCE_REQUEST || msg.type == I_NONCE_RESPONSE || msg.type == I_SIGNING_PRESENTATION || msg.type == I_ID_REQUEST || msg.type == I_ID_RESPONSE || msg.type == I_FIND_PARENT || msg.type == I_FIND_PARENT_RESPONSE || msg.type == I_HEARTBEAT || msg.type == I_HEARTBEAT_RESPONSE)) { @@ -89,10 +94,24 @@ static bool skipSign(MyMessage &msg) { } #endif // MY_SIGNING_FEATURE +// Helper to prepare a signing presentation message +static void prepareSigningPresentation(MyMessage &msg, uint8_t destination) { + // Only supports version 1 for now + build(msg, _nc.nodeId, destination, NODE_SENSOR_ID, C_INTERNAL, I_SIGNING_PRESENTATION, false).set(""); + mSetLength(msg, 2); + msg.data[0] = SIGNING_PRESENTATION_VERSION_1; + msg.data[1] = 0; +} + void signerInit(void) { #if defined(MY_SIGNING_FEATURE) // Read out the signing requirements from EEPROM - hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign)); + hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, + sizeof(_doSign)); + + // Read out the whitelist requirements from EEPROM + hwReadConfigBlock((void*)_doWhitelist, (void*)EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS, + sizeof(_doWhitelist)); #if defined(MY_SIGNING_SOFT) // initialize pseudo-RNG @@ -102,21 +121,23 @@ void signerInit(void) { } void signerPresentation(MyMessage &msg) { -#ifdef MY_SIGNING_FEATURE + prepareSigningPresentation(msg, GATEWAY_ADDRESS); + #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 - SIGN_DEBUG(PSTR("Request for signing sent, waiting for GW response...\n")); - wait(2000); -#else - _sendRoute(build(msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, - C_INTERNAL, I_REQUEST_SIGNING, false).set(false)); + msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES; + SIGN_DEBUG(PSTR("Signing required\n")); #endif -#else - // Ww 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)); +#if defined(MY_SIGNING_NODE_WHITELISTING) + msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_WHITELISTING; + SIGN_DEBUG(PSTR("Whitelisting required\n")); +#endif + + _sendRoute(msg); + +#if defined(MY_SIGNING_FEATURE) + // If we do support signing, wait for the gateway to tell us how it prefer us to transmit our messages + SIGN_DEBUG(PSTR("Waiting for GW to send signing preferences...\n")); + wait(2000); #endif } @@ -125,12 +146,12 @@ bool signerProcessInternal(MyMessage &msg) { #if !defined(MY_SIGNING_FEATURE) && defined(MY_GATEWAY_FEATURE) // If we act as gateway and do not have the signing feature and receive a signing request we still // need to do make sure the requester does not believe the gateway still require signatures - if (msg.type == I_REQUEST_SIGNING) { - _sendRoute(build(msg, _nc.nodeId, msg.sender, NODE_SENSOR_ID, - C_INTERNAL, I_REQUEST_SIGNING, false).set((uint8_t)false)); + if (msg.type == I_SIGNING_PRESENTATION) { + prepareSigningPresentation(msg, msg.sender); SIGN_DEBUG(PSTR("Informing node %d that we do not require signatures because we do not support it\n"), msg.sender); - return true; // No need to further process I_REQUEST_SIGNING in this case + _sendRoute(msg); + return true; // No need to further process I_SIGNING_PRESENTATION in this case } #elif defined(MY_SIGNING_FEATURE) if (msg.type == I_NONCE_REQUEST) { @@ -147,8 +168,14 @@ bool signerProcessInternal(MyMessage &msg) { SIGN_DEBUG(PSTR("Failed to generate nonce!\n")); } return true; // No need to further process I_NONCE_REQUEST - } else if (msg.type == I_REQUEST_SIGNING) { - if (msg.getBool()) { + } else if (msg.type == I_SIGNING_PRESENTATION) { + if (msg.data[0] != SIGNING_PRESENTATION_VERSION_1) { + SIGN_DEBUG(PSTR("Unsupported signing presentation version (%d)!\n"), msg.data[0]); + return true; // Just drop this presentation message + } + + // We only handle version 1 here... + if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_SIGNATURES) { // We received an indicator that the sender require us to sign all messages we send to it SIGN_DEBUG(PSTR("Mark node %d as one that require signed messages\n"), msg.sender); SET_SIGN(msg.sender); @@ -157,32 +184,52 @@ bool signerProcessInternal(MyMessage &msg) { SIGN_DEBUG(PSTR("Mark node %d as one that do not require signed messages\n"), msg.sender); CLEAR_SIGN(msg.sender); } - // Save updated table - hwWriteConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign)); + + if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_WHITELISTING) { + // We received an indicator that the sender require us to salt signatures with serial + SIGN_DEBUG(PSTR("Mark node %d as one that require whitelisting\n"), msg.sender); + SET_WHITELIST(msg.sender); + } else { + // We received an indicator that the sender does not require us to sign all messages we send to it + SIGN_DEBUG(PSTR("Mark node %d as one that do not require whitelisting\n"), msg.sender); + CLEAR_WHITELIST(msg.sender); + } + + // Save updated tables + hwWriteConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, + sizeof(_doSign)); + hwWriteConfigBlock((void*)_doWhitelist, (void*)EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS, + sizeof(_doWhitelist)); // Inform sender about our preference if we are a gateway, but only require signing if the sender // required signing // We do not want a gateway to require signing from all nodes in a network just because it wants one node // to sign it's messages -#if defined (MY_GATEWAY_FEATURE) -#ifdef MY_SIGNING_REQUEST_SIGNATURES +#if defined(MY_GATEWAY_FEATURE) + prepareSigningPresentation(msg, msg.sender); +#if defined(MY_SIGNING_REQUEST_SIGNATURES) if (DO_SIGN(msg.sender)) { - _sendRoute(build(msg, _nc.nodeId, msg.sender, NODE_SENSOR_ID, - C_INTERNAL, I_REQUEST_SIGNING, false).set((uint8_t)true)); - SIGN_DEBUG(PSTR("Informing node %d that we require signatures\n"), msg.sender); + msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES; } - else { - _sendRoute(build(msg, _nc.nodeId, msg.sender, NODE_SENSOR_ID, - C_INTERNAL, I_REQUEST_SIGNING, false).set((uint8_t)false)); +#endif +#if defined(MY_SIGNING_NODE_WHITELISTING) + if (DO_WHITELIST(msg.sender)) { + msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_WHITELISTING; + } +#endif + if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_SIGNATURES) { + SIGN_DEBUG(PSTR("Informing node %d that we require signatures\n"), msg.sender); + } else { SIGN_DEBUG(PSTR("Informing node %d that we do not require signatures\n"), msg.sender); } -#else // no MY_SIGNING_REQUEST_SIGNATURES - _sendRoute(build(msg, _nc.nodeId, msg.sender, NODE_SENSOR_ID, - C_INTERNAL, I_REQUEST_SIGNING, false).set((uint8_t)false)); - SIGN_DEBUG(PSTR("Informing node %d that we do not require signatures\n"), msg.sender); -#endif + if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_WHITELISTING) { + SIGN_DEBUG(PSTR("Informing node %d that we require whitelisting\n"), msg.sender); + } else { + SIGN_DEBUG(PSTR("Informing node %d that we do not require whitelisting\n"), msg.sender); + } + _sendRoute(msg); #endif // MY_GATEWAY_FEATURE - return true; // No need to further process I_REQUEST_SIGNING + return true; // No need to further process I_SIGNING_PRESENTATION } else if (msg.type == I_NONCE_RESPONSE) { // Proceed with signing if nonce has been received SIGN_DEBUG(PSTR("Nonce received from %d. Proceeding with signing...\n"), msg.sender); diff --git a/libraries/MySensors/core/MySigning.h b/libraries/MySensors/core/MySigning.h index 4f69c81..10de162 100644 --- a/libraries/MySensors/core/MySigning.h +++ b/libraries/MySensors/core/MySigning.h @@ -251,9 +251,9 @@ * 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. @@ -294,7 +294,6 @@ * 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 +302,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,10 +313,10 @@ * ... * @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: + * setting (@ref MY_SIGNING_SOFT_SERIAL) for all nodes that communicate to a node which has whitelisting enabled. Example: * @code{.cpp} * #define MY_SIGNING_SOFT * #define MY_SIGNING_SOFT_SERIAL 0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78 @@ -329,16 +328,16 @@ * ... * @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 except to define + * a unique serial if using the software backend: * @code{.cpp} - * #define MY_SIGNING_NODE_WHITELISTING {} + * #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 * @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. * * @section MySigningtechnical The technical stuff * @@ -363,7 +362,7 @@ * 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_SIGNING_PRESENTATION
* @ref I_NONCE_REQUEST
* @ref I_NONCE_RESPONSE
* @@ -409,14 +408,13 @@ * 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 @@ -441,10 +439,10 @@ * 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 @@ -487,6 +485,14 @@ typedef struct { /** @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<