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<