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<