diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index dc014fc..f765a38 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -148,7 +148,7 @@ * Message Signing Settings ***********************************/ -// Enable one of these in sketch to use signing functionality +// Enable one of these in sketch to use the signing functionality //#define MY_SIGNING_ATSHA204 //#define MY_SIGNING_SOFT @@ -168,21 +168,26 @@ // Note that a node can still transmit a non-salted message (i.e. have white listing disabled) // to a node that has white listing enabled (assuming the receiver does not have a matching entry // for the sender in it's white list) -//#define MY_SECURE_NODE_WHITELISTING +//#define MY_SIGNING_NODE_WHITELIST {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}} -// MySigningAtsha204 default setting -#ifndef MY_ATSHA204_PIN -#define MY_ATSHA204_PIN 17 // A3 - pin where ATSHA204 is attached +// Atsha204 default pin setting +#ifndef MY_SIGNING_ATSHA204_PIN +#define MY_SIGNING_ATSHA204_PIN 17 // A3 - pin where ATSHA204 is attached #endif -// MySigningAtsha204Soft default settings -#ifndef MY_RANDOMSEED_PIN -#define MY_RANDOMSEED_PIN 7 // A7 - Pin used for random generation (do not connect anything to this) +// Pin used for random generation in soft signing (do not connect anything to this when enabled) +#ifndef MY_SIGNING_SOFT_RANDOMSEED_PIN +#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 // A7 - +#endif + +// Set the soft_serial value to an arbitrary value for proper security +#ifndef MY_SIGNING_SOFT_SERIAL +#define MY_SIGNING_SOFT_SERIAL 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09 #endif // Key to use for HMAC calculation in MySigningAtsha204Soft (32 bytes) -#ifndef MY_HMAC_KEY -#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 +#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 /********************************** diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 62951a9..151ab7e 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -25,6 +25,7 @@ // #pragma message(VAR_NAME_VALUE(MY_GATEWAY_FEATURE)) #include "MyConfig.h" +#include "core/MySensorCore.h" #if defined(MY_GATEWAY_SERIAL) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_ENC28J60) #define MY_GATEWAY_FEATURE @@ -60,16 +61,19 @@ // SIGNING #if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT) - #include "MySigning.cpp" + // SIGNING COMMON FUNCTIONS + #include "core/MySigning.cpp" #define MY_SIGNING_FEATURE + #if defined(MY_SIGNING_ATSHA204) && defined(MY_SIGNING_SOFT) + #error Only one signing engine can be activated + #endif + #if defined(MY_SIGNING_ATSHA204) - #include "MySigningAtsha204.cpp" + #include "core/MySigningAtsha204.cpp" #include "drivers/ATSHA204/ATSHA204.cpp" - MySigningAtsha204 signer; #elif defined(MY_SIGNING_SOFT) - #include "MySigningAtsha204Soft.cpp" + #include "core/MySigningAtsha204Soft.cpp" #include "drivers/ATSHA204/sha256.cpp" - MySigningAtsha204Soft signer; #endif #endif @@ -144,31 +148,6 @@ #undef MY_GATEWAY_FEATURE #endif -// Make sure to disable child features when parent feature is disabled -#if !defined(MY_RADIO_FEATURE) - #undef MY_OTA_FIRMWARE_FEATURE - #undef MY_REPEATER_FEATURE - #undef MY_SECURE_NODE_WHITELISTING - #undef MY_SIGNING_FEATURE -#endif - -#if !defined(MY_GATEWAY_FEATURE) - #undef MY_INCLUSION_MODE_FEATURE - #undef MY_INCLUSION_BUTTON_FEATURE -#endif - -// Defines used in code for if-statements -#if defined(MY_REPEATER_FEATURE) - #define MY_IS_REPEATER true -#else - #define MY_IS_REPEATER false -#endif - -#if defined(MY_GATEWAY_FEATURE) - #define MY_IS_GATEWAY true -#else - #define MY_IS_GATEWAY false -#endif // RADIO #if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) @@ -188,6 +167,9 @@ SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID); #endif #include "core/MyTransport.cpp" + #if defined(MY_RADIO_NRF24) && defined(MY_RADIO_RFM69) + #error Only one radio driver can be activated + #endif #if defined(MY_RADIO_NRF24) #include "drivers/RF24/RF24.cpp" #include "core/MyTransportNRF24.cpp" @@ -197,6 +179,20 @@ #endif #endif +// Make sure to disable child features when parent feature is disabled +#if !defined(MY_RADIO_FEATURE) + #undef MY_OTA_FIRMWARE_FEATURE + #undef MY_REPEATER_FEATURE + #undef MY_SIGNING_NODE_WHITELISTING + #undef MY_SIGNING_FEATURE +#endif + +#if !defined(MY_GATEWAY_FEATURE) + #undef MY_INCLUSION_MODE_FEATURE + #undef MY_INCLUSION_BUTTON_FEATURE +#endif + + #include "core/MyMessage.cpp" @@ -209,10 +205,10 @@ extern void loop(); // Initialize library and handle sketch functions like we want to int main() { init(); // Init Arduino - begin(); // Startup MySensors library + _begin(); // Startup MySensors library setup(); // Call sketch setup while(1) { - process(); // Process incoming data + _process(); // Process incoming data loop(); // Call sketch loop if (serialEventRun) serialEventRun(); } diff --git a/libraries/MySensors/core/MyProtocolDefault.cpp b/libraries/MySensors/core/MyProtocolDefault.cpp index 727b337..d4a54c1 100644 --- a/libraries/MySensors/core/MyProtocolDefault.cpp +++ b/libraries/MySensors/core/MyProtocolDefault.cpp @@ -21,8 +21,8 @@ #include "MyProtocolDefault.h" #include "MyTransport.h" -char fmtBuffer[MAX_FORMAT_BUFFER_LENGTH]; -char convBuffer[MAX_PAYLOAD*2+1]; +char _fmtBuffer[MAX_FORMAT_BUFFER_LENGTH]; +char _convBuffer[MAX_PAYLOAD*2+1]; bool protocolParse(MyMessage &message, char *inputString) { char *str, *p, *value=NULL; @@ -91,8 +91,8 @@ bool protocolParse(MyMessage &message, char *inputString) { } char * protocolFormat(MyMessage &message) { - snprintf_P(fmtBuffer, MAX_FORMAT_BUFFER_LENGTH, PSTR("%d;%d;%d;%d;%d;%s\n"), message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type, message.getString(convBuffer)); - return fmtBuffer; + snprintf_P(_fmtBuffer, MAX_FORMAT_BUFFER_LENGTH, PSTR("%d;%d;%d;%d;%d;%s\n"), message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type, message.getString(_convBuffer)); + return _fmtBuffer; } uint8_t protocolH2i(char c) { diff --git a/libraries/MySensors/core/MySensorCore.cpp b/libraries/MySensors/core/MySensorCore.cpp index da576b6..e05db79 100644 --- a/libraries/MySensors/core/MySensorCore.cpp +++ b/libraries/MySensors/core/MySensorCore.cpp @@ -25,7 +25,7 @@ MyMessage _msg; // Buffer for incoming messages. MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others). #ifdef MY_DEBUG - char convBuf[MAX_PAYLOAD*2+1]; + char _convBuf[MAX_PAYLOAD*2+1]; #endif uint16_t _heartbeat; @@ -33,9 +33,10 @@ void (*_timeCallback)(unsigned long); // Callback for requested time messages void (*_msgCallback)(const MyMessage &); // Callback for incoming messages from other nodes and gateway. -void process() { +void _process() { hwWatchdogReset(); + #if defined (MY_LEDS_BLINKING_FEATURE) ledsProcess(); #endif @@ -59,11 +60,11 @@ static inline bool isValidParent( const uint8_t parent ) { } -void dataCallback(void (*msgCallback)(const MyMessage &)) { +void setIncomingCallback(void (*msgCallback)(const MyMessage &)) { _msgCallback = msgCallback; } -void begin() { +void _begin() { #if defined(MY_DISABLED_SERIAL) hwInit(); #endif @@ -165,7 +166,7 @@ ControllerConfig getConfig() { } -boolean sendRoute(MyMessage &message) { +boolean _sendRoute(MyMessage &message) { #if defined(MY_GATEWAY_FEATURE) if (message.destination == _nc.nodeId) { // This is a message sent from a sensor attached on the gateway node. @@ -185,42 +186,42 @@ bool send(MyMessage &message, bool enableAck) { message.sender = _nc.nodeId; mSetCommand(message,C_SET); mSetRequestAck(message,enableAck); - return sendRoute(message); + return _sendRoute(message); } void sendBatteryLevel(uint8_t value, bool enableAck) { - sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value)); + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value)); } void sendHeartbeat(void) { - sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT, false).set(_heartbeat++)); + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT, false).set(_heartbeat++)); } void present(uint8_t childSensorId, uint8_t sensorType, const char *description, bool enableAck) { - sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(childSensorId==NODE_SENSOR_ID?LIBRARY_VERSION:description)); + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(childSensorId==NODE_SENSOR_ID?LIBRARY_VERSION:description)); } void sendSketchInfo(const char *name, const char *version, bool enableAck) { if (name != NULL) { - sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, enableAck).set(name)); + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, enableAck).set(name)); } if (version != NULL) { - sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, enableAck).set(version)); + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, enableAck).set(version)); } } void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { - sendRoute(build(_msg, _nc.nodeId, destination, childSensorId, C_REQ, variableType, false).set("")); + _sendRoute(build(_msg, _nc.nodeId, destination, childSensorId, C_REQ, variableType, false).set("")); } void requestTime(void (* timeCallback)(unsigned long)) { _timeCallback = timeCallback; - sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set("")); + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set("")); } // Message delivered through _msg -void processInternalMessages() { +void _processInternalMessages() { bool isMetric; uint8_t type = _msg.type; @@ -250,7 +251,7 @@ void processInternalMessages() { hwWriteConfig(EEPROM_DISTANCE_ADDRESS, 0xFF); // Find parent node transportFindParentNode(); - sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set("")); + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set("")); } #endif } @@ -266,7 +267,7 @@ uint8_t loadState(uint8_t pos) { void wait(unsigned long ms) { unsigned long enter = hwMillis(); while (hwMillis() - enter < ms) { - process(); + _process(); } } @@ -274,7 +275,8 @@ void sleep(unsigned long ms) { #if defined(MY_OTA_FIRMWARE_FEATURE) if (_fwUpdateOngoing) { // Do not sleep node while fw update is ongoing - process(); + _process(); + return; } else { #endif #if defined(MY_RADIO_FEATURE) @@ -290,7 +292,6 @@ bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) { #if defined(MY_OTA_FIRMWARE_FEATURE) if (_fwUpdateOngoing) { // Do not sleep node while fw update is ongoing - process(); return false; } else { #endif @@ -307,7 +308,6 @@ int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode #if defined(MY_OTA_FIRMWARE_FEATURE) if (_fwUpdateOngoing) { // Do not sleep node while fw update is ongoing - process(); return -1; } else { #endif diff --git a/libraries/MySensors/core/MySensorCore.h b/libraries/MySensors/core/MySensorCore.h index ce9ed5d..202019b 100644 --- a/libraries/MySensors/core/MySensorCore.h +++ b/libraries/MySensors/core/MySensorCore.h @@ -79,7 +79,7 @@ struct ControllerConfig { * * @param incomingMessageCallback Callback function for incoming messages from other nodes or controller and request responses. */ -void dataCallback(void (* msgCallback)(const MyMessage &)=NULL); +void setIncomingCallback(void (* msgCallback)(const MyMessage &)=NULL); /** * Return this nodes id. @@ -175,11 +175,6 @@ void saveState(uint8_t pos, uint8_t value); */ uint8_t loadState(uint8_t pos); -/** -* Returns the last received message -*/ -MyMessage& getLastMessage(void); - /** @@ -224,20 +219,20 @@ int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode /****** PRIVATE ********/ -void begin(); +void _begin(); -void process(void); +void _process(void); -void processInternalMessages(); +void _processInternalMessages(); -boolean sendRoute(MyMessage &message); +boolean _sendRoute(MyMessage &message); extern NodeConfig _nc; extern MyMessage _msg; // Buffer for incoming messages. extern MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others). extern void (*_msgCallback)(const MyMessage &); // Callback for incoming messages from other nodes and gateway. #ifdef MY_DEBUG - extern char convBuf[MAX_PAYLOAD*2+1]; + extern char _convBuf[MAX_PAYLOAD*2+1]; #endif // Inline function and macros diff --git a/libraries/MySensors/core/MySigning.cpp b/libraries/MySensors/core/MySigning.cpp index ad0d78c..e6350c8 100644 --- a/libraries/MySensors/core/MySigning.cpp +++ b/libraries/MySensors/core/MySigning.cpp @@ -23,11 +23,3 @@ uint16_t _doSign[16]; // Bitfield indicating which sensors require signed communication MyMessage _msgSign; // Buffer for message to sign. - -MySigning::MySigning(bool requestSignatures) : _requestSignatures(requestSignatures) { -} - - -bool MySigning::requestSignatures() { - return _requestSignatures; -} diff --git a/libraries/MySensors/core/MySigning.h b/libraries/MySensors/core/MySigning.h index 637b281..2e430c1 100644 --- a/libraries/MySensors/core/MySigning.h +++ b/libraries/MySensors/core/MySigning.h @@ -29,65 +29,57 @@ #include #include "MyMessage.h" +#include "drivers/ATSHA204/ATSHA204.h" +#ifdef MY_SIGNING_NODE_WHITELISTING +typedef struct { + uint8_t nodeId; + uint8_t serial[SHA204_SERIAL_SZ]; +} whitelist_entry_t; +#endif // Macros for manipulating signing requirement table #define DO_SIGN(node) (node == 0 ? (~_doSign[0]&1) : (~_doSign[node>>4]&(node%16))) #define SET_SIGN(node) (node == 0 ? (_doSign[0]&=~1) : (_doSign[node>>4]&=~(node%16))) #define CLEAR_SIGN(node) (node == 0 ? (_doSign[0]|=1) : (_doSign[node>>4]|=(node%16))) +#define NUM_OF(x) (sizeof(x)/sizeof(x[0])) -class MySigning -{ -public: - // MySigning constructor. - // Different parameters would be needed depending on signing backend (e.g. pins connected etc.). - // Keeping these parameters as #define's in MyConfig to streamline the driver interface. - // @param requestSignatures Set this to true if you want destination node to sign all messages sent to this node. Default is not to require signing. - MySigning(bool requestSignatures=false); +// Stores signing identifier and a new nonce in provided message for signing operations. +// All space in message payload buffer is used for signing identifier and nonce. +// Returns 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. +bool signerGetNonce(MyMessage &msg); - // Returns the current requestSignature setting - bool requestSignatures(); +// Check timeout of verification session. +// Nonce will be purged if it takes too long for a signed message to be sent to the receiver. +// This function should be called on regular intervals. +bool signerCheckTimer(void); - // Stores signing identifier and a new nonce in provided message for signing operations. - // All space in message payload buffer is used for signing identifier and nonce. - // Returns 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. - virtual bool getNonce(MyMessage &msg) = 0; +// Get nonce from provided message and store for signing operations. +// Returns false if subsystem is busy processing an ongoing signing operation. +// Returns 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. +bool signerPutNonce(MyMessage &msg); - // Check timeout of verification session. - // Nonce will be purged if it takes too long for a signed message to be sent to the receiver. - // This function should be called on regular intervals. - virtual bool checkTimer(void) = 0; +// 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 getNonce(). +// Nonce will be cleared when this function is called to prevent re-use of nonce. +// Returns false if subsystem is busy processing an ongoing signing operation. +// Returns 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. +bool signerSignMsg(MyMessage &msg); - // Get nonce from provided message and store for signing operations. - // Returns false if subsystem is busy processing an ongoing signing operation. - // Returns 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. - virtual bool putNonce(MyMessage &msg) = 0; - - // 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 getNonce(). - // Nonce will be cleared when this function is called to prevent re-use of nonce. - // Returns false if subsystem is busy processing an ongoing signing operation. - // Returns 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. - virtual bool signMsg(MyMessage &msg) = 0; - - // Verifies signature in provided message. - // Nonce used for verification is the one previously set using putNonce(). - // Nonce will be cleared when this function is called to prevent re-use of nonce. - // Returns false if subsystem is busy processing an ongoing signing operation. - // Returns false if signing identifier found in message is not supported by the used backend. - // This ends a signing operation at the receiving side so implementation is expected to do any - // deinitializations and enter a power saving state within this call. - virtual bool verifyMsg(MyMessage &msg) = 0; - - public: - bool _requestSignatures; -}; +// Verifies signature in provided message. +// Nonce used for verification is the one previously set using putNonce(). +// Nonce will be cleared when this function is called to prevent re-use of nonce. +// Returns false if subsystem is busy processing an ongoing signing operation. +// Returns false if signing identifier found in message is not supported by the used backend. +// This ends a signing operation at the receiving side so implementation is expected to do any +// deinitializations and enter a power saving state within this call. +bool signerVerifyMsg(MyMessage &msg); #endif diff --git a/libraries/MySensors/core/MySigningAtsha204.cpp b/libraries/MySensors/core/MySigningAtsha204.cpp index 06d1b45..4796388 100644 --- a/libraries/MySensors/core/MySigningAtsha204.cpp +++ b/libraries/MySensors/core/MySigningAtsha204.cpp @@ -34,6 +34,25 @@ // Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected) //#define DEBUG_SIGNING +uint8_t atshaPin=MY_ATSHA204_PIN); +ATSHA204Class atsha204; +unsigned long timestamp; +bool verification_ongoing; +uint8_t current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1]; +uint8_t temp_message[SHA_MSG_SIZE]; +uint8_t rx_buffer[SHA204_RSP_SIZE_MAX]; +uint8_t tx_buffer[SHA204_CMD_SIZE_MAX]; +void calculateSignature(MyMessage &msg); +uint8_t* sha256(const uint8_t* data, size_t sz); + +#ifdef MY_SIGNING_NODE_WHITELISTING + uint8_t nof_whitelist_entries=0; + const uint8_t whitelist_entry_t* the_whitelist=NULL, + uint8_t whitlist_sz; + const whitelist_entry_t* whitelist; +#endif + + #ifdef DEBUG_SIGNING #define DEBUG_SIGNING_PRINTLN(args) Serial.println(args) #else @@ -60,14 +79,14 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, MySigningAtsha204::MySigningAtsha204(bool requestSignatures, -#ifdef MY_SECURE_NODE_WHITELISTING +#ifdef MY_SIGNING_NODE_WHITELISTING uint8_t nof_whitelist_entries, const whitelist_entry_t* the_whitelist, #endif uint8_t atshaPin) : MySigning(requestSignatures), atsha204(atshaPin), -#ifdef MY_SECURE_NODE_WHITELISTING +#ifdef MY_SIGNING_NODE_WHITELISTING whitelist(the_whitelist), whitlist_sz(nof_whitelist_entries), #endif @@ -142,7 +161,7 @@ bool MySigningAtsha204::signMsg(MyMessage &msg) { mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated calculateSignature(msg); -#ifdef MY_SECURE_NODE_WHITELISTING +#ifdef MY_SIGNING_NODE_WHITELISTING // Salt the signature with the senders nodeId and the unique serial of the ATSHA device memcpy(current_nonce, &rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed current_nonce[32] = msg.sender; @@ -180,7 +199,7 @@ bool MySigningAtsha204::verifyMsg(MyMessage &msg) { DEBUG_SIGNING_PRINTBUF(F("SIM:"), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg)); // SIM = Signature in message calculateSignature(msg); // Get signature of message -#ifdef MY_SECURE_NODE_WHITELISTING +#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 < whitlist_sz; j++) { if (whitelist[j].nodeId == msg.sender) { @@ -200,7 +219,7 @@ bool MySigningAtsha204::verifyMsg(MyMessage &msg) { // Compare the caluclated signature with the provided signature if (memcmp(&msg.data[mGetLength(msg)], &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg))) { DEBUG_SIGNING_PRINTBUF(F("SNOK:"), &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); // SNOK = Signature bad -#ifdef MY_SECURE_NODE_WHITELISTING +#ifdef MY_SIGNING_NODE_WHITELISTING DEBUG_SIGNING_PRINTLN(F("W?")); // W? = Is the sender whitelisted? #endif return false; diff --git a/libraries/MySensors/core/MySigningAtsha204.h b/libraries/MySensors/core/MySigningAtsha204.h index 1242db7..fe353f7 100644 --- a/libraries/MySensors/core/MySigningAtsha204.h +++ b/libraries/MySensors/core/MySigningAtsha204.h @@ -33,7 +33,7 @@ #include "drivers/ATSHA204/ATSHA204.h" #include -#ifdef MY_SECURE_NODE_WHITELISTING +#ifdef MY_SIGNING_NODE_WHITELISTING typedef struct { uint8_t nodeId; uint8_t serial[SHA204_SERIAL_SZ]; @@ -47,29 +47,13 @@ class MySigningAtsha204 : public MySigning { public: MySigningAtsha204(bool requestSignatures=true, -#ifdef MY_SECURE_NODE_WHITELISTING - uint8_t nof_whitelist_entries=0, const whitelist_entry_t* the_whitelist=NULL, -#endif - uint8_t atshaPin=MY_ATSHA204_PIN); + bool getNonce(MyMessage &msg); bool checkTimer(void); bool putNonce(MyMessage &msg); bool signMsg(MyMessage &msg); bool verifyMsg(MyMessage &msg); private: - ATSHA204Class atsha204; - unsigned long timestamp; - bool verification_ongoing; - uint8_t current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1]; - uint8_t temp_message[SHA_MSG_SIZE]; - uint8_t rx_buffer[SHA204_RSP_SIZE_MAX]; - uint8_t tx_buffer[SHA204_CMD_SIZE_MAX]; - void calculateSignature(MyMessage &msg); - uint8_t* sha256(const uint8_t* data, size_t sz); -#ifdef MY_SECURE_NODE_WHITELISTING - uint8_t whitlist_sz; - const whitelist_entry_t* whitelist; -#endif }; #endif diff --git a/libraries/MySensors/core/MySigningAtsha204Soft.cpp b/libraries/MySensors/core/MySigningAtsha204Soft.cpp index fc52085..d5e8b09 100644 --- a/libraries/MySensors/core/MySigningAtsha204Soft.cpp +++ b/libraries/MySensors/core/MySigningAtsha204Soft.cpp @@ -35,6 +35,20 @@ #define SIGNING_IDENTIFIER (1) + +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 }; +uint8_t _signing_hmac[32]; + +#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 + // Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected) //#define DEBUG_SIGNING @@ -63,81 +77,60 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, #define DEBUG_SIGNING_PRINTBUF(str, buf, sz) #endif -// Initialize hmacKey from MyConfig.h (codebender didn't like static initialization in constructor) -uint8_t MySigningAtsha204Soft::hmacKey[32] = { MY_HMAC_KEY }; -MySigningAtsha204Soft::MySigningAtsha204Soft(bool requestSignatures, -#ifdef MY_SECURE_NODE_WHITELISTING - uint8_t nof_whitelist_entries, const whitelist_entry_t* the_whitelist, - const uint8_t* the_serial, -#endif - uint8_t randomseedPin) - : - MySigning(requestSignatures), -#ifdef MY_SECURE_NODE_WHITELISTING - whitlist_sz(nof_whitelist_entries), - whitelist(the_whitelist), - node_serial_info(the_serial), -#endif - Sha256(), - verification_ongoing(false), - rndPin(randomseedPin) -{ -} - -bool MySigningAtsha204Soft::getNonce(MyMessage &msg) { +bool signerGetNonce(MyMessage &msg) { // Set randomseed - randomSeed(analogRead(rndPin)); + randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN)); // We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value // This 32-byte random value is then hashed (SHA256) to produce the resulting nonce - Sha256.init(); + _signing_sha256.init(); for (int i = 0; i < 32; i++) { - Sha256.write(random(255)); + _signing_sha256.write(random(255)); } - memcpy(current_nonce, Sha256.result(), MAX_PAYLOAD); + memcpy(_signing_current_nonce, _signing_sha256.result(), MAX_PAYLOAD); // We set the part of the 32-byte nonce that does not fit into a message to 0xAA - memset(¤t_nonce[MAX_PAYLOAD], 0xAA, sizeof(current_nonce)-MAX_PAYLOAD); + memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD); // Replace the first byte in the nonce with our signing identifier - current_nonce[0] = SIGNING_IDENTIFIER; + _signing_current_nonce[0] = SIGNING_IDENTIFIER; // Transfer the first part of the nonce to the message - msg.set(current_nonce, MAX_PAYLOAD); - verification_ongoing = true; - timestamp = millis(); // Set timestamp to determine when to purge nonce + msg.set(_signing_current_nonce, MAX_PAYLOAD); + _signing_verification_ongoing = true; + _signing_timestamp = millis(); // Set timestamp to determine when to purge nonce // Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover) // Note that if message is "too" quick, and arrives before turnover, it will be rejected // but this is consider such a rare case that it is accepted and rejects are 'safe' - if (timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) timestamp = 0; + if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) _signing_timestamp = 0; return true; } -bool MySigningAtsha204Soft::checkTimer() { - if (verification_ongoing) { - if (millis() < timestamp || millis() > timestamp + MY_VERIFICATION_TIMEOUT_MS) { +bool signerCheckTimer() { + if (_signing_verification_ongoing) { + if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) { DEBUG_SIGNING_PRINTLN(F("VT")); // VT = Verification timeout // Purge nonce - memset(current_nonce, 0xAA, 32); - verification_ongoing = false; + memset(_signing_current_nonce, 0xAA, 32); + _signing_verification_ongoing = false; return false; } } return true; } -bool MySigningAtsha204Soft::putNonce(MyMessage &msg) { +bool signerPutNonce(MyMessage &msg) { if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) { DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier return false; } - memcpy(current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD); + memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD); return true; } -bool MySigningAtsha204Soft::signMsg(MyMessage &msg) { +bool signerSignMsg(MyMessage &msg) { // If we cannot fit any signature in the message, refuse to sign it if (mGetLength(msg) > MAX_PAYLOAD-2) { DEBUG_SIGNING_PRINTLN(F("MTOL")); // Message too large for signature to fit @@ -146,38 +139,38 @@ bool MySigningAtsha204Soft::signMsg(MyMessage &msg) { // Calculate signature of message mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated - calculateSignature(msg); + signerCalculateSignature(msg); -#ifdef MY_SECURE_NODE_WHITELISTING +#ifdef MY_SIGNING_NODE_WHITELISTING // Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided - Sha256.init(); - for (int i=0; i<32; i++) Sha256.write(hmac[i]); - Sha256.write(msg.sender); - for (int i=0; i -#ifdef MY_SECURE_NODE_WHITELISTING -typedef struct { - uint8_t nodeId; - uint8_t serial[SHA204_SERIAL_SZ]; -} whitelist_entry_t; -#endif + // This implementation is the pure software variant of the ATSHA204. // It is designed to work fully compliant with nodes using ATSHA204 in the network // and therefore uses the same signing identifier as ATSHA204. -// Because it is completly software based, the quality of the generated random numbers +// Because it is completely software based, the quality of the generated random numbers // is weaker though. Random numbers are generated using the Arduino library and seed // is sampled from an analog pin. This pin should unconnected in the hardware. -// The pin is selected using MY_RANDOMSEED_PIN in MyConfig.h. -class MySigningAtsha204Soft : public MySigning -{ -public: - MySigningAtsha204Soft(bool requestSignatures=true, -#ifdef MY_SECURE_NODE_WHITELISTING - uint8_t nof_whitelist_entries=0, const whitelist_entry_t* the_whitelist=NULL, - const uint8_t* the_serial=NULL, //SHA204_SERIAL_SZ sized buffer expected if provided -#endif - uint8_t randomseedPin=MY_RANDOMSEED_PIN); - bool getNonce(MyMessage &msg); - bool checkTimer(void); - bool putNonce(MyMessage &msg); - bool signMsg(MyMessage &msg); - bool verifyMsg(MyMessage &msg); -private: - Sha256Class Sha256; - unsigned long timestamp; - bool verification_ongoing; - uint8_t current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH]; - uint8_t temp_message[32]; - static uint8_t hmacKey[32]; - uint8_t rndPin; - uint8_t hmac[32]; - void calculateSignature(MyMessage &msg); -#ifdef MY_SECURE_NODE_WHITELISTING - uint8_t whitlist_sz; - const whitelist_entry_t* whitelist; - const uint8_t* node_serial_info; -#endif -}; +// The pin is selected using MY_SIGNING_SOFT_RANDOMSEED_PIN in MyConfig.h. + +void signerCalculateSignature(MyMessage &msg); + #endif diff --git a/libraries/MySensors/core/MyTransport.cpp b/libraries/MySensors/core/MyTransport.cpp index 309bc4d..626e9e8 100644 --- a/libraries/MySensors/core/MyTransport.cpp +++ b/libraries/MySensors/core/MyTransport.cpp @@ -23,6 +23,9 @@ bool _autoFindParent; uint8_t _failedTransmissions; +#ifdef MY_SIGNING_FEATURE + bool _signingNonceStatus; +#endif #ifdef MY_OTA_FIRMWARE_FEATURE NodeFirmwareConfig _fc; @@ -60,14 +63,14 @@ inline void transportProcess() { firmwareRequest->type = _fc.type; firmwareRequest->version = _fc.version; firmwareRequest->block = (_fwBlock - 1); - sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false)); + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false)); } #endif return; } #ifdef MY_SIGNING_FEATURE - (void)signer.checkTimer(); // Manage signing timeout + (void)signerCheckTimer(); // Manage signing timeout #endif uint8_t len = transportReceive((uint8_t *)&_msg); @@ -77,29 +80,31 @@ inline void transportProcess() { #ifdef MY_SIGNING_FEATURE // 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, nor do we care about ACKs (they are never signed) - if (signer.requestSignatures() && _msg.destination == _nc.nodeId && mGetLength(_msg) && !mGetAck(_msg) && - (mGetCommand(_msg) != C_INTERNAL || - (_msg.type != I_GET_NONCE_RESPONSE && _msg.type != I_GET_NONCE && _msg.type != I_REQUEST_SIGNING && - _msg.type != I_ID_REQUEST && _msg.type != I_ID_RESPONSE && - _msg.type != I_FIND_PARENT && _msg.type != I_FIND_PARENT_RESPONSE))) { - if (!mGetSigned(_msg)) { - // Got unsigned message that should have been signed - debug(PSTR("no sign\n")); - ledBlinkErr(1); - return; + #ifdef MY_SIGNING_REQUEST_SIGNATURES + if (_msg.destination == _nc.nodeId && mGetLength(_msg) && !mGetAck(_msg) && + (mGetCommand(_msg) != C_INTERNAL || + (_msg.type != I_GET_NONCE_RESPONSE && _msg.type != I_GET_NONCE && _msg.type != I_REQUEST_SIGNING && + _msg.type != I_ID_REQUEST && _msg.type != I_ID_RESPONSE && + _msg.type != I_FIND_PARENT && _msg.type != I_FIND_PARENT_RESPONSE))) { + if (!mGetSigned(_msg)) { + // Got unsigned message that should have been signed + debug(PSTR("no sign\n")); + ledBlinkErr(1); + return; + } + else if (!signerVerifyMsg(_msg)) { + debug(PSTR("verify fail\n")); + ledBlinkErr(1); + return; // This signed message has been tampered with! + } } - else if (!signer.verifyMsg(_msg)) { - debug(PSTR("verify fail\n")); - ledBlinkErr(1); - return; // This signed message has been tampered with! - } - } + #endif #endif // Add string termination, good if we later would want to print it. _msg.data[mGetLength(_msg)] = '\0'; debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"), - _msg.sender, _msg.last, _msg.destination, _msg.sensor, mGetCommand(_msg), _msg.type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(convBuf)); + _msg.sender, _msg.last, _msg.destination, _msg.sensor, mGetCommand(_msg), _msg.type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(_convBuf)); mSetSigned(_msg,0); // Clear the sign-flag now as verification (and debug printing) is completed if(!(mGetVersion(_msg) == PROTOCOL_VERSION)) { @@ -132,7 +137,7 @@ inline void transportProcess() { mSetAck(_msgTmp,true); _msgTmp.sender = _nc.nodeId; _msgTmp.destination = _msg.sender; - sendRoute(_msgTmp); + _sendRoute(_msgTmp); } if (command == C_INTERNAL) { @@ -158,10 +163,10 @@ inline void transportProcess() { return; #ifdef MY_SIGNING_FEATURE } else if (type == I_GET_NONCE) { - if (signer.getNonce(_msg)) { - sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_GET_NONCE_RESPONSE, false)); + if (signerGetNonce(_msg)) { + _sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_GET_NONCE_RESPONSE, false)); } - return false; // Nonce exchange is an internal MySensor protocol message, no need to inform caller about this + return; // Nonce exchange is an internal MySensor protocol message, no need to inform caller about this } else if (type == I_REQUEST_SIGNING) { if (_msg.getBool()) { // We received an indicator that the sender require us to sign all messages we send to it @@ -176,14 +181,22 @@ inline void transportProcess() { // Inform sender about our preference if we are a gateway, but only require signing if the sender required signing // We do not currently want a gateway to require signing from all nodes in a network just because it wants one node // to sign it's messages - if (MY_GATEWAY_FEATURE) { - if (signer.requestSignatures() && DO_SIGN(_msg.sender)) - sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true)); - else - sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false)); - } + #if defined (MY_GATEWAY_FEATURE) + #ifdef 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(true)); + else + _sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false)); + #else + _sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false)); + #endif + #endif return; // Signing request is an internal MySensor protocol message, no need to inform caller about this } else if (type == I_GET_NONCE_RESPONSE) { + // Proceed with signing if nonce has been received + if (signerPutNonce(_msg) && signerSignMsg(_msgSign)) { + _signingNonceStatus = SIGN_OK; + } return; // Just pass along nonce silently (no need to call callback for these) #endif } else if (sender == GATEWAY_ADDRESS) { @@ -199,7 +212,7 @@ inline void transportProcess() { hwWriteConfig(EEPROM_NODE_ID_ADDRESS, _nc.nodeId); debug(PSTR("id=%d\n"), _nc.nodeId); } else { - processInternalMessages(); + _processInternalMessages(); } return; } @@ -292,7 +305,7 @@ inline void transportProcess() { } } else if (to == _nc.nodeId) { // We should try to relay this message to another node - sendRoute(_msg); + _sendRoute(_msg); } } #endif @@ -327,7 +340,7 @@ boolean transportSendWrite(uint8_t to, MyMessage &message) { debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,st=%s:%s\n"), message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type, - mGetPayloadType(message), mGetLength(message), mGetSigned(message), to==BROADCAST_ADDRESS ? "bc" : (ok ? "ok":"fail"), message.getString(convBuf)); + mGetPayloadType(message), mGetLength(message), mGetSigned(message), to==BROADCAST_ADDRESS ? "bc" : (ok ? "ok":"fail"), message.getString(_convBuf)); return ok; } @@ -356,103 +369,101 @@ boolean transportSendRoute(MyMessage &message) { mSetVersion(message, PROTOCOL_VERSION); #ifdef MY_SIGNING_FEATURE - // If destination is known to require signed messages and we are the sender, sign this message unless it is an ACK or a handshake message - if (DO_SIGN(message.destination) && message.sender == _nc.nodeId && !mGetAck(message) && mGetLength(message) && - (mGetCommand(message) != C_INTERNAL || - (message.type != I_GET_NONCE && message.type != I_GET_NONCE_RESPONSE && message.type != I_REQUEST_SIGNING && - message.type != I_ID_REQUEST && message.type != I_ID_RESPONSE && - message.type != I_FIND_PARENT && message.type != I_FIND_PARENT_RESPONSE))) { - bool signOk = false; - // Send nonce-request - if (!sendRoute(build(_msgTmp, _nc.nodeId, message.destination, message.sensor, C_INTERNAL, I_GET_NONCE, false).set(""))) { - debug(PSTR("nonce tr err\n")); - return false; - } - // We have to wait for the nonce to arrive before we can sign our original message - // Other messages could come in-between. We trust process() takes care of them - unsigned long enter = hwMillis(); - _msgSign = message; // Copy the message to sign since message buffer might be touched in process() - while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS) { - if (process()) { - if (mGetCommand(getLastMessage()) == C_INTERNAL && getLastMessage().type == I_GET_NONCE_RESPONSE) { - // Proceed with signing if nonce has been received - if (signer.putNonce(getLastMessage()) && signer.signMsg(_msgSign)) { - message = _msgSign; // Write the signed message back - signOk = true; - } - break; - } + // If destination is known to require signed messages and we are the sender, sign this message unless it is an ACK or a handshake message + if (DO_SIGN(message.destination) && message.sender == _nc.nodeId && !mGetAck(message) && mGetLength(message) && + (mGetCommand(message) != C_INTERNAL || + (message.type != I_GET_NONCE && message.type != I_GET_NONCE_RESPONSE && message.type != I_REQUEST_SIGNING && + message.type != I_ID_REQUEST && message.type != I_ID_RESPONSE && + message.type != I_FIND_PARENT && message.type != I_FIND_PARENT_RESPONSE))) { + bool signing = SIGN_WAITING_FOR_NONCE; + // Send nonce-request + if (!_sendRoute(build(_msgTmp, _nc.nodeId, message.destination, message.sensor, C_INTERNAL, I_GET_NONCE, false).set(""))) { + debug(PSTR("nonce tr err\n")); + return false; } + // We have to wait for the nonce to arrive before we can sign our original message + // Other messages could come in-between. We trust process() takes care of them + unsigned long enter = hwMillis(); + _msgSign = message; // Copy the message to sign since message buffer might be touched in process() + + while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS && _signingNonceStatus==SIGN_WAITING_FOR_NONCE) { + _process(); + } + if (hwMillis() - enter > 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 { + mSetSigned(message, 0); // Message is not supposed to be signed, make sure it is marked unsigned } - if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) { - debug(PSTR("nonce tmo\n")); - ledBlinkErr(1); - return false; - } - if (!signOk) { - 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 { - mSetSigned(message, 0); // Message is not supposed to be signed, make sure it is marked unsigned - } #endif - if (dest == GATEWAY_ADDRESS || !MY_IS_REPEATER) { - // Store this address in routing table (if repeater) - #if defined (MY_IS_REPEATER) - hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last); - #endif - // If destination is the gateway or if we aren't a repeater, let - // our parent take care of the message + + #if !defined(MY_REPEATER_FEATURE) + // None repeating node... We can only send to our parent ok = transportSendWrite(_nc.parentNodeId, message); - } else { - // Relay the message - uint8_t route = hwReadConfig(EEPROM_ROUTES_ADDRESS+dest); - if (route > GATEWAY_ADDRESS && route < BROADCAST_ADDRESS) { - // This message should be forwarded to a child node. If we send message - // to this nodes pipe then all children will receive it because the are - // all listening to this nodes pipe. - // - // +----B - // -A - // +----C------D - // - // We're node C, Message comes from A and has destination D - // - // Message destination is not gateway and is in routing table for this node. - // Send it downstream - return transportSendWrite(route, message); - } else if (sender == GATEWAY_ADDRESS && dest == BROADCAST_ADDRESS) { - // Node has not yet received any id. We need to send it - // by doing a broadcast sending, - return transportSendWrite(BROADCAST_ADDRESS, message); - } else if (MY_IS_GATEWAY) { - // Destination isn't in our routing table and isn't a broadcast address - // Nothing to do here - return false; - } else { - // A message comes from a child node and we have no - // route for it. - // - // +----B - // -A - // +----C------D <-- Message comes from D - // - // We're node C - // - // Message should be passed to node A (this nodes relay) - - // This message should be routed back towards sensor net gateway - ok = transportSendWrite(_nc.parentNodeId, message); - // Add this child to our "routing table" if it not already exist + #else + if (dest == GATEWAY_ADDRESS) { + // Store this address in routing table (if repeater) hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last); - } - } + // If destination is the gateway or if we aren't a repeater, let + // our parent take care of the message + ok = transportSendWrite(_nc.parentNodeId, message); + } else { + // Relay the message + uint8_t route = hwReadConfig(EEPROM_ROUTES_ADDRESS+dest); + if (route > GATEWAY_ADDRESS && route < BROADCAST_ADDRESS) { + // This message should be forwarded to a child node. If we send message + // to this nodes pipe then all children will receive it because the are + // all listening to this nodes pipe. + // + // +----B + // -A + // +----C------D + // + // We're node C, Message comes from A and has destination D + // + // Message destination is not gateway and is in routing table for this node. + // Send it downstream + return transportSendWrite(route, message); + } else if (sender == GATEWAY_ADDRESS && dest == BROADCAST_ADDRESS) { + // Node has not yet received any id. We need to send it + // by doing a broadcast sending, + return transportSendWrite(BROADCAST_ADDRESS, message); + } + #if defined (MY_GATEWAY_FEATURE) + // Destination isn't in our routing table and isn't a broadcast address + // Nothing to do here + return false; + # else + // A message comes from a child node and we have no + // route for it. + // + // +----B + // -A + // +----C------D <-- Message comes from D + // + // We're node C + // + // Message should be passed to node A (this nodes relay) + // This message should be routed back towards sensor net gateway + ok = transportSendWrite(_nc.parentNodeId, message); + // Add this child to our "routing table" if it not already exist + hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last); + #endif + } + #endif if (!ok) { // Failure when sending to parent node. The parent node might be down and we // need to find another route to gateway. @@ -484,12 +495,13 @@ void transportSetupNode() { if (_nc.nodeId != AUTO) { #ifdef MY_SIGNING_FEATURE // Notify gateway (and possibly controller) about the signing preferences of this node - sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(signer.requestSignatures())); - - // If we do require signing, wait for the gateway to tell us how it prefer us to transmit our messages - if (signer.requestSignatures()) { + #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 #endif // Send presentation for this radio node @@ -501,7 +513,7 @@ void transportSetupNode() { // Send a configuration exchange request to controller // Node sends parent node. Controller answers with latest node configuration // which is picked up in process() - sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(_nc.parentNodeId)); + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(_nc.parentNodeId)); // Wait configuration reply. wait(2000); @@ -516,7 +528,7 @@ void transportSetupNode() { // add bootloader information reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION; _fwUpdateOngoing = false; - sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false)); + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false)); #endif } #endif diff --git a/libraries/MySensors/core/MyTransport.h b/libraries/MySensors/core/MyTransport.h index f72ea8f..827430b 100644 --- a/libraries/MySensors/core/MyTransport.h +++ b/libraries/MySensors/core/MyTransport.h @@ -41,6 +41,9 @@ // 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/examples/MotionSensor/MotionSensor.ino b/libraries/MySensors/examples/MotionSensor/MotionSensor.ino index d07b2a7..d48131c 100644 --- a/libraries/MySensors/examples/MotionSensor/MotionSensor.ino +++ b/libraries/MySensors/examples/MotionSensor/MotionSensor.ino @@ -29,7 +29,9 @@ //#define MY_DEBUG -#define MY_RADIO_NRF24 // MY_RADIO_RFM69 +// Enable and select radio type +#define MY_RADIO_NRF24 +//#define MY_RADIO_RFM69 //#define MY_SOFTSPI //#define MY_OTA_FIRMWARE_FEATURE diff --git a/libraries/MySensors/examples/SecureActuator/SecureActuator.ino b/libraries/MySensors/examples/SecureActuator/SecureActuator.ino index cb69354..76a80ea 100644 --- a/libraries/MySensors/examples/SecureActuator/SecureActuator.ino +++ b/libraries/MySensors/examples/SecureActuator/SecureActuator.ino @@ -25,79 +25,62 @@ * Example sketch showing how to securely control locks. * This example will remember lock state even after power failure. */ + +// Enable and select radio type +#define MY_RADIO_NRF24 +//#define MY_RADIO_RFM69 +// Select soft/hardware signing method +#define MY_SIGNING_SOFT // Software signing enabled +//#define MY_SIGNING_ATSHA204 // Hardware signing using ATSHA204A + +// Enable node whitelisting +#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}} +// Enable this if you want destination node to sign all messages sent to this node. Default is not to require signing. +#define MY_SIGNING_REQUEST_SIGNATURES + + +// 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 + +// SETTINGS FOR MY_SIGNING_ATSHA204 +#define MY_SIGNING_ATSHA204_PIN 17 // A3 - pin where ATSHA204 is attached -#define USE_SOFTWARE_ATSHA // Disable to use ATSHA204A circuit -#include -#include #include -#include -#ifdef USE_SOFTWARE_ATSHA -#include -#else -#include -#endif + #define LOCK_1 3 // Arduino Digital I/O pin number for first lock (second on pin+1 etc) #define NOF_LOCKS 1 // Total number of attached locks #define LOCK_LOCK 1 // GPIO value to write to lock attached lock #define LOCK_UNLOCK 0 // GPIO value to write to unlock attached lock -MyTransportNRF24 radio; // NRFRF24L01 radio driver -MyHwATMega328 hw; // Select AtMega328 hardware profile -#ifdef MY_SIGNING_FEATURE -#ifdef MY_SECURE_NODE_WHITELISTING -#ifdef USE_SOFTWARE_ATSHA -// Change the soft_serial value to an arbitrary value for proper security -uint8_t soft_serial[SHA204_SERIAL_SZ] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09}; -#endif -// We only use one whitelist entry (the gateway) -whitelist_entry_t node_whitelist[] = { - { .nodeId = GATEWAY_ADDRESS, - .serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01} } -}; -#ifdef USE_SOFTWARE_ATSHA -MySigningAtsha204Soft signer(true, 1, node_whitelist, soft_serial); // Message signing driver -#else -MySigningAtsha204 signer(true, 1, node_whitelist); -#endif -#else -#ifdef USE_SOFTWARE_ATSHA -MySigningAtsha204Soft signer; -#else -MySigningAtsha204 signer; -#endif -#endif -MySensor gw(radio, hw, signer); -#else -// WARNING! SecureActuator cannot possibly be secure without signing enabled -MySensor gw(radio, hw); -#endif void setup() { // Initialize library and add callback for incoming messages (signing is required) - gw.begin(incomingMessage, AUTO); + setIncomingCallback(incomingMessage); // Send the sketch version information to the gateway and Controller - gw.sendSketchInfo("Secure Lock", "1.0"); + sendSketchInfo("Secure Lock", "1.0"); // Fetch lock status for (int lock=1, pin=LOCK_1; lock<=NOF_LOCKS;lock++, pin++) { // Register all locks to gw (they will be created as child devices) - gw.present(lock, S_LOCK, "SecureActuator", false); + present(lock, S_LOCK, "SecureActuator", false); // Then set lock pins in output mode pinMode(pin, OUTPUT); // Set lock to last known state (using eeprom storage) - digitalWrite(pin, gw.loadState(lock)?LOCK_LOCK:LOCK_UNLOCK); + digitalWrite(pin, loadState(lock)?LOCK_LOCK:LOCK_UNLOCK); } } - void loop() { - // Alway process incoming messages whenever possible - gw.process(); } void incomingMessage(const MyMessage &message) { @@ -106,7 +89,7 @@ void incomingMessage(const MyMessage &message) { // Change relay state digitalWrite(message.sensor-1+LOCK_1, message.getBool()?LOCK_LOCK:LOCK_UNLOCK); // Store state in eeprom - gw.saveState(message.sensor, message.getBool()); + saveState(message.sensor, message.getBool()); // Write some debug info Serial.print("Incoming change for lock:"); Serial.print(message.sensor);