From 4c4c9a8b39332516a2196490bb749933e3d1df59 Mon Sep 17 00:00:00 2001 From: Patrick Fallberg Date: Sat, 7 Mar 2015 14:47:06 +0100 Subject: [PATCH 01/11] Added SHA command to ATSHA204 backend driver The SHA command is available in ATSHA204A variants of the chip and can be used to calculate SHA256 digests of arbitrary data. This enables us to strengthen the quality of the signatures by salting signatures with node-specific data and improving the quality of random data. --- libraries/MySensors/MySigningAtsha204.cpp | 26 +++++++++++++++++++++++ libraries/MySensors/MySigningAtsha204.h | 3 ++- libraries/MySensors/utility/ATSHA204.cpp | 15 +++++++++---- libraries/MySensors/utility/ATSHA204.h | 16 +++++++++++++- 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/libraries/MySensors/MySigningAtsha204.cpp b/libraries/MySensors/MySigningAtsha204.cpp index 1effefc..146ce30 100644 --- a/libraries/MySensors/MySigningAtsha204.cpp +++ b/libraries/MySensors/MySigningAtsha204.cpp @@ -215,3 +215,29 @@ bool MySigningAtsha204::calculateSignature(MyMessage &msg) { DEBUG_ATSHA_PRINTBUF("HMAC:", &rx_buffer[SHA204_BUFFER_POS_DATA], 32); return true; // We return with the signature in rx_buffer[SHA204_BUFFER_POS_DATA] } + +// Helper to calculate a generic SHA256 digest of provided buffer (returned in rx_buffer[SHA204_BUFFER_POS_DATA]) (only supports one block) +uint8_t* MySigningAtsha204::sha256(const uint8_t* data, size_t sz) { + // Initiate SHA256 calculator + if (atsha204.sha204m_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL, + SHA_COUNT_SHORT, tx_buffer, SHA_RSP_SIZE_SHORT, rx_buffer) != SHA204_SUCCESS) { + DEBUG_ATSHA_PRINTLN("Failed to initiate sha256"); + return NULL; + } + + // Calculate a hash + memset(temp_message, 0x00, SHA_MSG_SIZE); + memcpy(temp_message, data, sz); + temp_message[sz] = 0x80; + // Write length data to the last bytes + temp_message[SHA_MSG_SIZE-2] = (sz >> 5); + temp_message[SHA_MSG_SIZE-1] = (sz << 3); + DEBUG_ATSHA_PRINTBUF("Data to hash (padded):", temp_message, SHA_MSG_SIZE); + if (atsha204.sha204m_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, temp_message, + SHA_COUNT_LONG, tx_buffer, SHA_RSP_SIZE_LONG, rx_buffer) != SHA204_SUCCESS) { + DEBUG_ATSHA_PRINTLN("Failed to calculate sha256"); + return NULL; + } + DEBUG_ATSHA_PRINTBUF("SHA:", &rx_buffer[SHA204_BUFFER_POS_DATA], 32); + return &rx_buffer[SHA204_BUFFER_POS_DATA]; +} diff --git a/libraries/MySensors/MySigningAtsha204.h b/libraries/MySensors/MySigningAtsha204.h index 8850b2c..d6b3465 100644 --- a/libraries/MySensors/MySigningAtsha204.h +++ b/libraries/MySensors/MySigningAtsha204.h @@ -31,10 +31,11 @@ private: unsigned long timestamp; bool verification_ongoing; uint8_t current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH]; - uint8_t temp_message[32]; + uint8_t temp_message[SHA_MSG_SIZE]; uint8_t rx_buffer[SHA204_RSP_SIZE_MAX]; uint8_t tx_buffer[SHA204_CMD_SIZE_MAX]; bool calculateSignature(MyMessage &msg); + uint8_t* sha256(const uint8_t* data, size_t sz); }; #endif diff --git a/libraries/MySensors/utility/ATSHA204.cpp b/libraries/MySensors/utility/ATSHA204.cpp index da63668..21ad514 100644 --- a/libraries/MySensors/utility/ATSHA204.cpp +++ b/libraries/MySensors/utility/ATSHA204.cpp @@ -447,12 +447,19 @@ uint8_t atsha204Class::sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t response_size = RANDOM_RSP_SIZE; break; - case SHA204_WRITE: - poll_delay = WRITE_DELAY; - poll_timeout = WRITE_EXEC_MAX - WRITE_DELAY; - response_size = WRITE_RSP_SIZE; + case SHA204_SHA: + poll_delay = SHA_DELAY; + poll_timeout = SHA_EXEC_MAX - SHA_DELAY; + response_size = param1 == SHA_INIT + ? SHA_RSP_SIZE_SHORT : SHA_RSP_SIZE_LONG; break; + case SHA204_WRITE: + poll_delay = WRITE_DELAY; + poll_timeout = WRITE_EXEC_MAX - WRITE_DELAY; + response_size = WRITE_RSP_SIZE; + break; + default: poll_delay = 0; poll_timeout = SHA204_COMMAND_EXEC_MAX; diff --git a/libraries/MySensors/utility/ATSHA204.h b/libraries/MySensors/utility/ATSHA204.h index cb2abc8..c2a057e 100644 --- a/libraries/MySensors/utility/ATSHA204.h +++ b/libraries/MySensors/utility/ATSHA204.h @@ -59,6 +59,7 @@ #define SHA204_HMAC ((uint8_t) 0x11) //!< HMAC command op-code #define SHA204_NONCE ((uint8_t) 0x16) //!< Nonce command op-code #define SHA204_RANDOM ((uint8_t) 0x1B) //!< Random command op-code +#define SHA204_SHA ((uint8_t) 0x47) //!< SHA command op-code #define SHA204_WRITE ((uint8_t) 0x12) //!< Write command op-code // packet size definitions @@ -122,6 +123,15 @@ #define RANDOM_SEED_UPDATE ((uint8_t) 0x00) //!< Random mode for automatic seed update #define RANDOM_NO_SEED_UPDATE ((uint8_t) 0x01) //!< Random mode for no seed update +// SHA command definitions +#define SHA_MODE_IDX SHA204_PARAM1_IDX //!< SHA command index for mode +#define SHA_PARAM2_IDX SHA204_PARAM2_IDX //!< SHA command index for 2. parameter +#define SHA_COUNT_SHORT SHA204_CMD_SIZE_MIN //!< SHA command packet size for init +#define SHA_COUNT_LONG (71) //!< SHA command packet size for calculation +#define SHA_MSG_SIZE (64) //!< SHA message data size +#define SHA_INIT ((uint8_t) 0x00) //!< SHA mode for init +#define SHA_CALC ((uint8_t) 0x01) //!< SHA mode for calculation + // Write command definitions #define WRITE_ZONE_IDX SHA204_PARAM1_IDX //!< Write command index for zone #define WRITE_ADDR_IDX SHA204_PARAM2_IDX //!< Write command index for address @@ -142,6 +152,8 @@ #define NONCE_RSP_SIZE_SHORT SHA204_RSP_SIZE_MIN //!< response size of Nonce command with mode[0:1] = 3 #define NONCE_RSP_SIZE_LONG SHA204_RSP_SIZE_MAX //!< response size of Nonce command #define RANDOM_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of Random command +#define SHA_RSP_SIZE_SHORT SHA204_RSP_SIZE_MIN //!< response size of SHA command with mode[0:1] = 0 +#define SHA_RSP_SIZE_LONG SHA204_RSP_SIZE_MAX //!< response size of SHA command #define WRITE_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of Write command // command timing definitions for minimum execution times (ms) @@ -149,6 +161,7 @@ #define HMAC_DELAY ((uint8_t) (27.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5)) #define NONCE_DELAY ((uint8_t) (22.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5)) #define RANDOM_DELAY ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5)) +#define SHA_DELAY ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5)) #define WRITE_DELAY ((uint8_t) ( 4.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5)) // command timing definitions for maximum execution times (ms) @@ -156,6 +169,7 @@ #define HMAC_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) #define NONCE_EXEC_MAX ((uint8_t) (60.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) #define RANDOM_EXEC_MAX ((uint8_t) (50.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) +#define SHA_EXEC_MAX ((uint8_t) (22.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) #define WRITE_EXEC_MAX ((uint8_t) (42.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) /* from sha204_config.h */ @@ -172,7 +186,7 @@ #define SHA204_COMMAND_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) //! maximum command delay #define SHA204_CMD_SIZE_MIN ((uint8_t) 7) //! minimum number of bytes in command (from count byte to second CRC byte) -#define SHA204_CMD_SIZE_MAX ((uint8_t) NONCE_COUNT_LONG) //! maximum size of command packet (Nonce) +#define SHA204_CMD_SIZE_MAX ((uint8_t) SHA_COUNT_LONG) //! maximum size of command packet (SHA) #define SHA204_CRC_SIZE ((uint8_t) 2) //! number of CRC bytes #define SHA204_BUFFER_POS_STATUS (1) //! buffer index of status byte in status response #define SHA204_BUFFER_POS_DATA (1) //! buffer index of first data byte in data response From a1024cd2cd8607db078bd2a2391fdf6d575da23f Mon Sep 17 00:00:00 2001 From: Patrick Fallberg Date: Mon, 9 Mar 2015 21:37:16 +0100 Subject: [PATCH 02/11] Added support to read the serial number from ATSHA204 The serial number can be used for whitelisting of nodes that are considered "trusted". --- libraries/MySensors/utility/ATSHA204.cpp | 45 ++++++++++++++++++++++++ libraries/MySensors/utility/ATSHA204.h | 25 +++++++++++++ 2 files changed, 70 insertions(+) diff --git a/libraries/MySensors/utility/ATSHA204.cpp b/libraries/MySensors/utility/ATSHA204.cpp index 21ad514..9991335 100644 --- a/libraries/MySensors/utility/ATSHA204.cpp +++ b/libraries/MySensors/utility/ATSHA204.cpp @@ -18,6 +18,34 @@ atsha204Class::atsha204Class(uint8_t pin) device_port_IN = portInputRegister(port); } +uint8_t atsha204Class::getSerialNumber(uint8_t * response) +{ + uint8_t readCommand[READ_COUNT]; + uint8_t readResponse[READ_4_RSP_SIZE]; + + /* read from bytes 0->3 of config zone */ + uint8_t returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN03); + if (!returnCode) // should return 0 if successful + { + for (int i=0; i<4; i++) // store bytes 0-3 into respones array + response[i] = readResponse[SHA204_BUFFER_POS_DATA+i]; + + /* read from bytes 8->11 of config zone */ + returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN47); + + for (int i=4; i<8; i++) // store bytes 4-7 of SN into response array + response[i] = readResponse[SHA204_BUFFER_POS_DATA+(i-4)]; + + if (!returnCode) + { /* Finally if last two reads were successful, read byte 8 of the SN */ + returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN8); + response[8] = readResponse[SHA204_BUFFER_POS_DATA]; // Byte 8 of SN should always be 0xEE + } + } + + return returnCode; +} + /* SWI bit bang functions */ void atsha204Class::swi_set_signal_pin(uint8_t is_high) @@ -412,6 +440,23 @@ uint8_t atsha204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_s /* Marshaling functions */ +uint8_t atsha204Class::sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address) +{ + uint8_t rx_size; + + address >>= 2; + + tx_buffer[SHA204_COUNT_IDX] = READ_COUNT; + tx_buffer[SHA204_OPCODE_IDX] = SHA204_READ; + tx_buffer[READ_ZONE_IDX] = zone; + tx_buffer[READ_ADDR_IDX] = (uint8_t) (address & SHA204_ADDRESS_MASK); + tx_buffer[READ_ADDR_IDX + 1] = 0; + + rx_size = (zone & SHA204_ZONE_COUNT_FLAG) ? READ_32_RSP_SIZE : READ_4_RSP_SIZE; + + return sha204c_send_and_receive(&tx_buffer[0], rx_size, &rx_buffer[0], READ_DELAY, READ_EXEC_MAX - READ_DELAY); +} + uint8_t atsha204Class::sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t param2, uint8_t datalen1, uint8_t *data1, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer) { diff --git a/libraries/MySensors/utility/ATSHA204.h b/libraries/MySensors/utility/ATSHA204.h index c2a057e..d29f884 100644 --- a/libraries/MySensors/utility/ATSHA204.h +++ b/libraries/MySensors/utility/ATSHA204.h @@ -59,6 +59,7 @@ #define SHA204_HMAC ((uint8_t) 0x11) //!< HMAC command op-code #define SHA204_NONCE ((uint8_t) 0x16) //!< Nonce command op-code #define SHA204_RANDOM ((uint8_t) 0x1B) //!< Random command op-code +#define SHA204_READ ((uint8_t) 0x02) //!< Read command op-code #define SHA204_SHA ((uint8_t) 0x47) //!< SHA command op-code #define SHA204_WRITE ((uint8_t) 0x12) //!< Write command op-code @@ -123,6 +124,13 @@ #define RANDOM_SEED_UPDATE ((uint8_t) 0x00) //!< Random mode for automatic seed update #define RANDOM_NO_SEED_UPDATE ((uint8_t) 0x01) //!< Random mode for no seed update +// Read command definitions +#define READ_ZONE_IDX SHA204_PARAM1_IDX //!< Read command index for zone +#define READ_ADDR_IDX SHA204_PARAM2_IDX //!< Read command index for address +#define READ_COUNT SHA204_CMD_SIZE_MIN //!< Read command packet size +#define READ_ZONE_MASK ((uint8_t) 0x83) //!< Read zone bits 2 to 6 are 0. +#define READ_ZONE_MODE_32_BYTES ((uint8_t) 0x80) //!< Read mode: 32 bytes + // SHA command definitions #define SHA_MODE_IDX SHA204_PARAM1_IDX //!< SHA command index for mode #define SHA_PARAM2_IDX SHA204_PARAM2_IDX //!< SHA command index for 2. parameter @@ -152,6 +160,8 @@ #define NONCE_RSP_SIZE_SHORT SHA204_RSP_SIZE_MIN //!< response size of Nonce command with mode[0:1] = 3 #define NONCE_RSP_SIZE_LONG SHA204_RSP_SIZE_MAX //!< response size of Nonce command #define RANDOM_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of Random command +#define READ_4_RSP_SIZE SHA204_RSP_SIZE_VAL //!< response size of Read command when reading 4 bytes +#define READ_32_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of Read command when reading 32 bytes #define SHA_RSP_SIZE_SHORT SHA204_RSP_SIZE_MIN //!< response size of SHA command with mode[0:1] = 0 #define SHA_RSP_SIZE_LONG SHA204_RSP_SIZE_MAX //!< response size of SHA command #define WRITE_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of Write command @@ -161,6 +171,7 @@ #define HMAC_DELAY ((uint8_t) (27.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5)) #define NONCE_DELAY ((uint8_t) (22.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5)) #define RANDOM_DELAY ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5)) +#define READ_DELAY ((uint8_t) ( 0.4 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5)) #define SHA_DELAY ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5)) #define WRITE_DELAY ((uint8_t) ( 4.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5)) @@ -169,6 +180,7 @@ #define HMAC_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) #define NONCE_EXEC_MAX ((uint8_t) (60.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) #define RANDOM_EXEC_MAX ((uint8_t) (50.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) +#define READ_EXEC_MAX ((uint8_t) ( 4.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) #define SHA_EXEC_MAX ((uint8_t) (22.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) #define WRITE_EXEC_MAX ((uint8_t) (42.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) @@ -195,6 +207,17 @@ #define SHA204_STATUS_BYTE_EXEC ((uint8_t) 0x0F) //! command execution error #define SHA204_STATUS_BYTE_COMM ((uint8_t) 0xFF) //! communication error +/* EEPROM Addresses */ +/* Configuration Zone */ +#define ADDRESS_SN03 0 // SN[0:3] are bytes 0->3 of configuration zone +#define ADDRESS_RevNum 4 // bytes 4->7 of config zone are RevNum +#define ADDRESS_SN47 8 // SN[4:7] are bytes 8->11 of config zone +#define ADDRESS_SN8 12 // SN[8] is byte 12 of config zone, should be 0xEE +#define ADDRESS_I2CEN 14 // I2C Enable, bit 0 represents I2C enable status +#define ADDRESS_I2CADD 16 // Defines I2C address of SHA204 +#define ADDRESS_OTPMODE 18 // Sets the One-time-programmable mode +#define ADDRESS_SELECTOR 19 // Controls writability of Selector + class atsha204Class { private: @@ -207,6 +230,7 @@ private: uint8_t swi_send_bytes(uint8_t count, uint8_t *buffer); uint8_t swi_send_byte(uint8_t value); uint8_t sha204p_receive_response(uint8_t size, uint8_t *response); + uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address); public: atsha204Class(uint8_t pin); // Constructor uint8_t sha204c_wakeup(uint8_t *response); @@ -215,6 +239,7 @@ public: uint8_t sha204c_resync(uint8_t size, uint8_t *response); uint8_t sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t param2, uint8_t datalen1, uint8_t *data1, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer); + uint8_t getSerialNumber(uint8_t *response); }; #endif From e35a01766bc8da7bb7790528e2ae5a48f58a11de Mon Sep 17 00:00:00 2001 From: Patrick Fallberg Date: Sun, 8 Mar 2015 21:17:34 +0100 Subject: [PATCH 03/11] Implemented basic whitening of nonce A simple (for performanc reasons) whitening scheme has been implemented for the ATSHA204 backeds that strengthens the nonce "randomness". --- libraries/MySensors/MySigningAtsha204.cpp | 52 +++++++++++++------ libraries/MySensors/MySigningAtsha204Soft.cpp | 17 ++++-- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/libraries/MySensors/MySigningAtsha204.cpp b/libraries/MySensors/MySigningAtsha204.cpp index 146ce30..2a966fa 100644 --- a/libraries/MySensors/MySigningAtsha204.cpp +++ b/libraries/MySensors/MySigningAtsha204.cpp @@ -42,31 +42,43 @@ MySigningAtsha204::MySigningAtsha204(bool requestSignatures, uint8_t atshaPin) atsha204(atshaPin), verification_ongoing(false) { - // We set the part of the 32-byte nonce that does not fit into a message to 0xAA - memset(current_nonce, 0xAA, sizeof(current_nonce)); } bool MySigningAtsha204::getNonce(MyMessage &msg) { - // Wake device before starting operations - if (atsha204.sha204c_wakeup(rx_buffer) != SHA204_SUCCESS) { - DEBUG_ATSHA_PRINTLN("Failed to wake device"); - return false; - } - // Generate random number for use as nonce - if (atsha204.sha204m_execute(SHA204_RANDOM, RANDOM_NO_SEED_UPDATE, 0, 0, NULL, - RANDOM_COUNT, tx_buffer, RANDOM_RSP_SIZE, rx_buffer) != SHA204_SUCCESS) { - DEBUG_ATSHA_PRINTLN("Failed to generate nonce"); - return false; + uint8_t* pRnd = NULL; + // 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 + for (int i = 0; i < 32; i++) { + // Wake device before starting operations + if (atsha204.sha204c_wakeup(rx_buffer) != SHA204_SUCCESS) { + DEBUG_ATSHA_PRINTLN("Failed to wake device"); + return false; + } + if (atsha204.sha204m_execute(SHA204_RANDOM, RANDOM_NO_SEED_UPDATE, 0, 0, NULL, + RANDOM_COUNT, tx_buffer, RANDOM_RSP_SIZE, rx_buffer) != SHA204_SUCCESS) { + DEBUG_ATSHA_PRINTLN("Failed to generate nonce"); + return false; + } + atsha204.sha204c_sleep(); + current_nonce[i] = rx_buffer[SHA204_BUFFER_POS_DATA]; } - - // We can put the device back to sleep as we will program the nonce once we do the verification + pRnd = sha256(current_nonce, 32); atsha204.sha204c_sleep(); - // Store nonce and replace the first byte in the nonce with our signing identifier - memcpy(current_nonce, &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD); + if (!pRnd) { + DEBUG_ATSHA_PRINTLN("Failed to hash random value"); + return false; + } + + memcpy(current_nonce, pRnd, 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); + + // Replace the first byte in the nonce with our signing identifier current_nonce[0] = SIGNING_IDENTIFIER; - + // Transfer the first part of the nonce to the message msg.set(current_nonce, MAX_PAYLOAD); verification_ongoing = true; @@ -218,6 +230,12 @@ bool MySigningAtsha204::calculateSignature(MyMessage &msg) { // Helper to calculate a generic SHA256 digest of provided buffer (returned in rx_buffer[SHA204_BUFFER_POS_DATA]) (only supports one block) uint8_t* MySigningAtsha204::sha256(const uint8_t* data, size_t sz) { + // Wake device before starting operations + if (atsha204.sha204c_wakeup(rx_buffer) != SHA204_SUCCESS) { + DEBUG_ATSHA_PRINTLN("Failed to wake device"); + return NULL; + } + // Initiate SHA256 calculator if (atsha204.sha204m_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL, SHA_COUNT_SHORT, tx_buffer, SHA_RSP_SIZE_SHORT, rx_buffer) != SHA204_SUCCESS) { diff --git a/libraries/MySensors/MySigningAtsha204Soft.cpp b/libraries/MySensors/MySigningAtsha204Soft.cpp index 3ac6f38..465cd93 100644 --- a/libraries/MySensors/MySigningAtsha204Soft.cpp +++ b/libraries/MySensors/MySigningAtsha204Soft.cpp @@ -49,17 +49,24 @@ MySigningAtsha204Soft::MySigningAtsha204Soft(bool requestSignatures, uint8_t ran Sha256() { - // We set the part of the 32-byte nonce that does not fit into a message to 0xAA - memset(current_nonce, 0xAA, sizeof(current_nonce)); } bool MySigningAtsha204Soft::getNonce(MyMessage &msg) { // Set randomseed randomSeed(analogRead(rndPin)); - for (int i = 0; i < MAX_PAYLOAD; i++) - { - current_nonce[i] = random(255); + + // 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(); + for (int i = 0; i < 32; i++) { + Sha256.write(random(255)); } + memcpy(current_nonce, 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); + + // Replace the first byte in the nonce with our signing identifier current_nonce[0] = SIGNING_IDENTIFIER; // Transfer the first part of the nonce to the message From cf31edcb810ff04b7a9c367d52fb91b07729086a Mon Sep 17 00:00:00 2001 From: Patrick Fallberg Date: Fri, 13 Mar 2015 18:52:47 +0100 Subject: [PATCH 04/11] New way of presenting ATSHA204 serial in personalizer The serial is now more copy+paste friendly for use in whitelists. --- .../Sha204Personalizer/Sha204Personalizer.ino | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/libraries/MySensors/examples/Sha204Personalizer/Sha204Personalizer.ino b/libraries/MySensors/examples/Sha204Personalizer/Sha204Personalizer.ino index 066c834..4b7017f 100644 --- a/libraries/MySensors/examples/Sha204Personalizer/Sha204Personalizer.ino +++ b/libraries/MySensors/examples/Sha204Personalizer/Sha204Personalizer.ino @@ -41,7 +41,7 @@ //#define SKIP_KEY_STORAGE // Uncomment this to skip key data storage (once configuration is locked, key -// will aways randomize ) +// will aways randomize) // Uncomment this to skip key generation and use 'user_key_data' as key instead. //#define USER_KEY_DATA @@ -703,6 +703,19 @@ void setup() else { Serial.print(F("Device serial: ")); + Serial.print('{'); + for (int i=0; i<9; i++) + { + Serial.print(F("0x")); + if (rx_buffer[i] < 0x10) + { + Serial.print('0'); // Because Serial.print does not 0-pad HEX + } + Serial.print(rx_buffer[i], HEX); + if (i < 8) Serial.print(','); + } + Serial.print('}'); + Serial.println(); for (int i=0; i<9; i++) { if (rx_buffer[i] < 0x10) From f295ef2961809777247b86471b5438e3fed74611 Mon Sep 17 00:00:00 2001 From: Patrick Fallberg Date: Fri, 13 Mar 2015 23:44:12 +0100 Subject: [PATCH 05/11] Whitelisting support in signing backend Whitelisting is enabled using MY_SECURE_NODE_WHITELISTING in MyConfig.h. The whitelist is provided in backend constructors. See the SecureActuator example for a typical usecase. --- libraries/MySensors/MyConfig.h | 12 ++++ libraries/MySensors/MySigningAtsha204.cpp | 68 +++++++++++++------ libraries/MySensors/MySigningAtsha204.h | 19 +++++- libraries/MySensors/MySigningAtsha204Soft.cpp | 53 +++++++++++++-- libraries/MySensors/MySigningAtsha204Soft.h | 21 +++++- .../SecureActuator/SecureActuator.ino | 37 ++++++++-- libraries/MySensors/utility/ATSHA204.h | 2 + 7 files changed, 177 insertions(+), 35 deletions(-) diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index 0b4d806..e68709a 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -21,11 +21,23 @@ typedef MyHwATMega328 MyHwDriver; // which might vary, especially in networks with many hops. 5s ought to be enough for anyone. #define MY_VERIFICATION_TIMEOUT_MS 5000 +// Enable to turn on whitelisting +// When enabled, a signing node will salt the signature with it's unique signature and nodeId. +// The verifying node will look up the sender in a local table of trusted nodes and +// do the corresponding salting in order to verify the signature. +// For this reason, if whitelisting is enabled on one of the nodes in a sign-verify pair, both +// nodes have to implement whitelisting for this to work. +// Note that a node can still transmit a non-salted message (i.e. have whitelisting disabled) +// to a node that has whitelisting enabled (assuming the receiver does not have a matching entry +// for the sender in it's whitelist) +//#define MY_SECURE_NODE_WHITELISTING + // MySigningAtsha204 default setting #define MY_ATSHA204_PIN 17 // A3 - pin where ATSHA204 is attached // MySigningAtsha204Soft default settings #define MY_RANDOMSEED_PIN 7 // A7 - Pin used for random generation (do not connect anything to this) + // Key to use for HMAC calculation in MySigningAtsha204Soft (32 bytes) #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 diff --git a/libraries/MySensors/MySigningAtsha204.cpp b/libraries/MySensors/MySigningAtsha204.cpp index 2a966fa..e9fcd42 100644 --- a/libraries/MySensors/MySigningAtsha204.cpp +++ b/libraries/MySensors/MySigningAtsha204.cpp @@ -36,10 +36,18 @@ static void DEBUG_ATSHA_PRINTBUF(char* str, uint8_t* buf, uint8_t sz) #endif -MySigningAtsha204::MySigningAtsha204(bool requestSignatures, uint8_t atshaPin) +MySigningAtsha204::MySigningAtsha204(bool requestSignatures, +#ifdef MY_SECURE_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 + whitelist(the_whitelist), + whitlist_sz(nof_whitelist_entries), +#endif verification_ongoing(false) { } @@ -50,21 +58,14 @@ bool MySigningAtsha204::getNonce(MyMessage &msg) { // 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 for (int i = 0; i < 32; i++) { - // Wake device before starting operations - if (atsha204.sha204c_wakeup(rx_buffer) != SHA204_SUCCESS) { - DEBUG_ATSHA_PRINTLN("Failed to wake device"); - return false; - } if (atsha204.sha204m_execute(SHA204_RANDOM, RANDOM_NO_SEED_UPDATE, 0, 0, NULL, RANDOM_COUNT, tx_buffer, RANDOM_RSP_SIZE, rx_buffer) != SHA204_SUCCESS) { DEBUG_ATSHA_PRINTLN("Failed to generate nonce"); return false; } - atsha204.sha204c_sleep(); current_nonce[i] = rx_buffer[SHA204_BUFFER_POS_DATA]; } pRnd = sha256(current_nonce, 32); - atsha204.sha204c_sleep(); if (!pRnd) { DEBUG_ATSHA_PRINTLN("Failed to hash random value"); @@ -134,6 +135,19 @@ bool MySigningAtsha204::signMsg(MyMessage &msg) { return false; } +#ifdef MY_SECURE_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; + (void)atsha204.getSerialNumber(¤t_nonce[33]); + (void)sha256(current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place + memset(current_nonce, 0xAA, NONCE_NUMIN_SIZE_PASSTHROUGH); + DEBUG_ATSHA_PRINTLN("Signature salted"); +#endif + + // Overwrite the first byte in the signature with the signing identifier + rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER; + // Transfer as much signature data as the remaining space in the message permits memcpy(&msg.data[mGetLength(msg)], &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); @@ -166,9 +180,29 @@ bool MySigningAtsha204::verifyMsg(MyMessage &msg) { return false; } +#ifdef MY_SECURE_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) { + DEBUG_ATSHA_PRINTLN("Sender found in whitelist"); + 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; + memcpy(¤t_nonce[33], whitelist[j].serial, SHA204_SERIAL_SZ); + (void)sha256(current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place + break; + } + } +#endif + + // Overwrite the first byte in the signature with the signing identifier + rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER; + // 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_ATSHA_PRINTBUF("Signature bad. Calculated signature:", &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); +#ifdef MY_SECURE_NODE_WHITELISTING + DEBUG_ATSHA_PRINTLN("Is the sender whitelisted?"); +#endif return false; } else { DEBUG_ATSHA_PRINTLN("Signature ok"); @@ -182,9 +216,6 @@ bool MySigningAtsha204::calculateSignature(MyMessage &msg) { memset(temp_message, 0, 32); memcpy(temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); - // Wake device before starting operations - if (atsha204.sha204c_wakeup(rx_buffer) != SHA204_SUCCESS) return false; - // Program the data to sign into the ATSHA204 if (atsha204.sha204m_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32, temp_message, WRITE_COUNT_LONG, tx_buffer, WRITE_RSP_SIZE, rx_buffer) != SHA204_SUCCESS) { @@ -221,21 +252,12 @@ bool MySigningAtsha204::calculateSignature(MyMessage &msg) { // Put device back to sleep atsha204.sha204c_sleep(); - // Overwrite the first byte in the signature with the signing identifier - rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER; - DEBUG_ATSHA_PRINTBUF("HMAC:", &rx_buffer[SHA204_BUFFER_POS_DATA], 32); return true; // We return with the signature in rx_buffer[SHA204_BUFFER_POS_DATA] } // Helper to calculate a generic SHA256 digest of provided buffer (returned in rx_buffer[SHA204_BUFFER_POS_DATA]) (only supports one block) uint8_t* MySigningAtsha204::sha256(const uint8_t* data, size_t sz) { - // Wake device before starting operations - if (atsha204.sha204c_wakeup(rx_buffer) != SHA204_SUCCESS) { - DEBUG_ATSHA_PRINTLN("Failed to wake device"); - return NULL; - } - // Initiate SHA256 calculator if (atsha204.sha204m_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL, SHA_COUNT_SHORT, tx_buffer, SHA_RSP_SIZE_SHORT, rx_buffer) != SHA204_SUCCESS) { @@ -250,12 +272,16 @@ uint8_t* MySigningAtsha204::sha256(const uint8_t* data, size_t sz) { // Write length data to the last bytes temp_message[SHA_MSG_SIZE-2] = (sz >> 5); temp_message[SHA_MSG_SIZE-1] = (sz << 3); - DEBUG_ATSHA_PRINTBUF("Data to hash (padded):", temp_message, SHA_MSG_SIZE); + DEBUG_ATSHA_PRINTBUF("Data to hash:", temp_message, SHA_MSG_SIZE); if (atsha204.sha204m_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, temp_message, SHA_COUNT_LONG, tx_buffer, SHA_RSP_SIZE_LONG, rx_buffer) != SHA204_SUCCESS) { DEBUG_ATSHA_PRINTLN("Failed to calculate sha256"); return NULL; } + + // Put device back to sleep + atsha204.sha204c_sleep(); + DEBUG_ATSHA_PRINTBUF("SHA:", &rx_buffer[SHA204_BUFFER_POS_DATA], 32); return &rx_buffer[SHA204_BUFFER_POS_DATA]; } diff --git a/libraries/MySensors/MySigningAtsha204.h b/libraries/MySensors/MySigningAtsha204.h index d6b3465..b96386a 100644 --- a/libraries/MySensors/MySigningAtsha204.h +++ b/libraries/MySensors/MySigningAtsha204.h @@ -14,13 +14,24 @@ #define SIGNING_IDENTIFIER (1) +#ifdef MY_SECURE_NODE_WHITELISTING +typedef struct { + uint8_t nodeId; + uint8_t serial[SHA204_SERIAL_SZ]; +} whitelist_entry_t; +#endif + // The ATSHA204 is capable of generating proper random numbers for nonce // and can calculate HMAC-SHA256 signatures. This is enterprise- // level of security and ought to implement the signing needs for anybody. class MySigningAtsha204 : public MySigning { public: - MySigningAtsha204(bool requestSignatures=true, uint8_t atshaPin = MY_ATSHA204_PIN); + 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); @@ -30,12 +41,16 @@ private: atsha204Class atsha204; unsigned long timestamp; bool verification_ongoing; - uint8_t current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH]; + 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]; bool 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/MySigningAtsha204Soft.cpp b/libraries/MySensors/MySigningAtsha204Soft.cpp index 465cd93..4d56cf1 100644 --- a/libraries/MySensors/MySigningAtsha204Soft.cpp +++ b/libraries/MySensors/MySigningAtsha204Soft.cpp @@ -40,11 +40,21 @@ static void DEBUG_ATSHASOFT_PRINTBUF(char* str, uint8_t* buf, uint8_t sz) #define DEBUG_ATSHASOFT_PRINTBUF(str, buf, sz) #endif -MySigningAtsha204Soft::MySigningAtsha204Soft(bool requestSignatures, uint8_t randomseedPin) +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), rndPin(randomseedPin), hmacKey({MY_HMAC_KEY}), +#ifdef MY_SECURE_NODE_WHITELISTING + whitlist_sz(nof_whitelist_entries), + whitelist(the_whitelist), + node_serial_info(the_serial), +#endif verification_ongoing(false), Sha256() @@ -124,6 +134,19 @@ bool MySigningAtsha204Soft::signMsg(MyMessage &msg) { return false; } +#ifdef MY_SECURE_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 +#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 @@ -12,13 +19,35 @@ #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_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; // Message signing driver -MySensor gw(radio, signer); +#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); void setup() -{ +{ // Initialize library and add callback for incoming messages (signing is required) - gw.begin(incomingMessage, AUTO, true, AUTO); + gw.begin(incomingMessage, AUTO); // Send the sketch version information to the gateway and Controller gw.sendSketchInfo("Secure Lock", "1.0"); diff --git a/libraries/MySensors/utility/ATSHA204.h b/libraries/MySensors/utility/ATSHA204.h index d29f884..f4499b4 100644 --- a/libraries/MySensors/utility/ATSHA204.h +++ b/libraries/MySensors/utility/ATSHA204.h @@ -218,6 +218,8 @@ #define ADDRESS_OTPMODE 18 // Sets the One-time-programmable mode #define ADDRESS_SELECTOR 19 // Controls writability of Selector +#define SHA204_SERIAL_SZ 9 // The number of bytes the serial number consists of + class atsha204Class { private: From 611ba0cf442d7d30c40fbf261335280ee5891593 Mon Sep 17 00:00:00 2001 From: Patrick Fallberg Date: Sat, 14 Mar 2015 12:20:02 +0100 Subject: [PATCH 06/11] Slimmed down memory footprint of signing backends --- libraries/MySensors/MySigningAtsha204.cpp | 136 ++++++------------ libraries/MySensors/MySigningAtsha204.h | 2 +- libraries/MySensors/MySigningAtsha204Soft.cpp | 62 ++++---- libraries/MySensors/MySigningAtsha204Soft.h | 2 +- libraries/MySensors/MySigningNone.cpp | 15 +- libraries/MySensors/utility/ATSHA204.cpp | 12 +- libraries/MySensors/utility/ATSHA204.h | 10 +- 7 files changed, 92 insertions(+), 147 deletions(-) diff --git a/libraries/MySensors/MySigningAtsha204.cpp b/libraries/MySensors/MySigningAtsha204.cpp index e9fcd42..07dca5f 100644 --- a/libraries/MySensors/MySigningAtsha204.cpp +++ b/libraries/MySensors/MySigningAtsha204.cpp @@ -8,18 +8,17 @@ #include "MySigningAtsha204.h" // Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected) -//#define DEBUG_ATSHA_SIGNING +//#define DEBUG_SIGNING -#ifdef DEBUG_ATSHA_SIGNING -#define DEBUG_ATSHA_PRINTLN(args) Serial.println(args) +#ifdef DEBUG_SIGNING +#define DEBUG_SIGNING_PRINTLN(args) Serial.println(args) #else -#define DEBUG_ATSHA_PRINTLN(args) +#define DEBUG_SIGNING_PRINTLN(args) #endif -#ifdef DEBUG_ATSHA_SIGNING -static void DEBUG_ATSHA_PRINTBUF(char* str, uint8_t* buf, uint8_t sz) +#ifdef DEBUG_SIGNING +static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) { - int i; Serial.println(str); for (int i=0; i timestamp + MY_VERIFICATION_TIMEOUT_MS) { - DEBUG_ATSHA_PRINTLN("Verification timeout"); + DEBUG_SIGNING_PRINTLN(F("VT")); // VT = Verification timeout // Purge nonce - memset(current_nonce, 0xAA, NONCE_NUMIN_SIZE_PASSTHROUGH); + memset(current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH); verification_ongoing = false; return false; } @@ -105,44 +96,35 @@ bool MySigningAtsha204::checkTimer() { } bool MySigningAtsha204::putNonce(MyMessage &msg) { - if (mGetLength(msg) != MAX_PAYLOAD) { - DEBUG_ATSHA_PRINTLN("Incoming nonce with incorrect size"); - return false; // We require as big nonce as possible - } - if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) { - DEBUG_ATSHA_PRINTLN("Incorrect signing identifier"); + DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier return false; } memcpy(current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD); + // We set the part of the 32-byte nonce that does not fit into a message to 0xAA + memset(¤t_nonce[MAX_PAYLOAD], 0xAA, sizeof(current_nonce)-MAX_PAYLOAD); return true; } bool MySigningAtsha204::signMsg(MyMessage &msg) { // If we cannot fit any signature in the message, refuse to sign it if (mGetLength(msg) > MAX_PAYLOAD-2) { - DEBUG_ATSHA_PRINTLN("Cannot fit any signature to this message"); + DEBUG_SIGNING_PRINTLN(F("MTOL")); // Message too large for signature to fit return false; } // Calculate signature of message mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated - DEBUG_ATSHA_PRINTBUF("Message to sign:", (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); - if (!calculateSignature(msg)) { - DEBUG_ATSHA_PRINTLN("Failed to calculate signature"); - mSetSigned(msg, 0); // make sure signing flag is truthful - return false; - } + calculateSignature(msg); #ifdef MY_SECURE_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; - (void)atsha204.getSerialNumber(¤t_nonce[33]); + atsha204.getSerialNumber(¤t_nonce[33]); (void)sha256(current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place - memset(current_nonce, 0xAA, NONCE_NUMIN_SIZE_PASSTHROUGH); - DEBUG_ATSHA_PRINTLN("Signature salted"); + DEBUG_SIGNING_PRINTLN(F("SWS")); // SWS = Signature whitelist salted #endif // Overwrite the first byte in the signature with the signing identifier @@ -156,7 +138,7 @@ bool MySigningAtsha204::signMsg(MyMessage &msg) { bool MySigningAtsha204::verifyMsg(MyMessage &msg) { if (!verification_ongoing) { - DEBUG_ATSHA_PRINTLN("No active verification session"); + DEBUG_SIGNING_PRINTLN(F("NAVS")); // NAVS = No active verification session return false; } else { // Make sure we have not expired @@ -167,24 +149,18 @@ bool MySigningAtsha204::verifyMsg(MyMessage &msg) { verification_ongoing = false; if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) { - DEBUG_ATSHA_PRINTLN("Incorrect signing identifier"); + DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier return false; } - DEBUG_ATSHA_PRINTBUF("Message to verify:", (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); - DEBUG_ATSHA_PRINTBUF("Signature in message:", (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg)); - - // Get signature of message - if (!calculateSignature(msg)) { - DEBUG_ATSHA_PRINTLN("Failed to calculate signature"); - return false; - } + 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 // 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) { - DEBUG_ATSHA_PRINTLN("Sender found in whitelist"); + DEBUG_SIGNING_PRINTLN(F("SIW")); // SIW = Sender found in whitelist 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; memcpy(¤t_nonce[33], whitelist[j].serial, SHA204_SERIAL_SZ); @@ -199,71 +175,56 @@ 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_ATSHA_PRINTBUF("Signature bad. Calculated signature:", &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 - DEBUG_ATSHA_PRINTLN("Is the sender whitelisted?"); + DEBUG_SIGNING_PRINTLN(F("W?")); // W? = Is the sender whitelisted? #endif return false; } else { - DEBUG_ATSHA_PRINTLN("Signature ok"); + DEBUG_SIGNING_PRINTLN(F("SOK")); // SOK = Signature OK return true; } } } // Helper to calculate signature of msg (returned in rx_buffer[SHA204_BUFFER_POS_DATA]) -bool MySigningAtsha204::calculateSignature(MyMessage &msg) { +void MySigningAtsha204::calculateSignature(MyMessage &msg) { memset(temp_message, 0, 32); memcpy(temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); // Program the data to sign into the ATSHA204 - if (atsha204.sha204m_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32, temp_message, - WRITE_COUNT_LONG, tx_buffer, WRITE_RSP_SIZE, rx_buffer) != SHA204_SUCCESS) { - DEBUG_ATSHA_PRINTLN("Failed to program data"); - return false; - } + DEBUG_SIGNING_PRINTBUF(F("MSG:"), (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); // MSG = Message to sign + DEBUG_SIGNING_PRINTBUF(F("CNC:"), current_nonce, 32); // CNC = Current nonce + (void)atsha204.sha204m_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32, temp_message, + WRITE_COUNT_LONG, tx_buffer, WRITE_RSP_SIZE, rx_buffer); // Program the nonce to use for the signature (has to be done just before GENDIG due to chip limitations) - if (atsha204.sha204m_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, NONCE_NUMIN_SIZE_PASSTHROUGH, current_nonce, - NONCE_COUNT_LONG, tx_buffer, NONCE_RSP_SIZE_SHORT, rx_buffer) != SHA204_SUCCESS) { - DEBUG_ATSHA_PRINTLN("Failed to program nonce"); - // Purge nonce when used - memset(current_nonce, 0xAA, NONCE_NUMIN_SIZE_PASSTHROUGH); - return false; - } + (void)atsha204.sha204m_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, NONCE_NUMIN_SIZE_PASSTHROUGH, current_nonce, + NONCE_COUNT_LONG, tx_buffer, NONCE_RSP_SIZE_SHORT, rx_buffer); // Purge nonce when used - memset(current_nonce, 0xAA, NONCE_NUMIN_SIZE_PASSTHROUGH); + memset(current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH); // Generate digest of data and nonce - if (atsha204.sha204m_execute(SHA204_GENDIG, GENDIG_ZONE_DATA, 8, 0, NULL, - GENDIG_COUNT_DATA, tx_buffer, GENDIG_RSP_SIZE, rx_buffer) != SHA204_SUCCESS) { - DEBUG_ATSHA_PRINTLN("Failed to generate digest"); - return false; - } + (void)atsha204.sha204m_execute(SHA204_GENDIG, GENDIG_ZONE_DATA, 8, 0, NULL, + GENDIG_COUNT_DATA, tx_buffer, GENDIG_RSP_SIZE, rx_buffer); // Calculate HMAC of message+nonce digest and secret key - if (atsha204.sha204m_execute(SHA204_HMAC, HMAC_MODE_SOURCE_FLAG_MATCH, 0, 0, NULL, - HMAC_COUNT, tx_buffer, HMAC_RSP_SIZE, rx_buffer) != SHA204_SUCCESS) { - DEBUG_ATSHA_PRINTLN("Failed to generate HMAC"); - return false; - } + (void)atsha204.sha204m_execute(SHA204_HMAC, HMAC_MODE_SOURCE_FLAG_MATCH, 0, 0, NULL, + HMAC_COUNT, tx_buffer, HMAC_RSP_SIZE, rx_buffer); // Put device back to sleep atsha204.sha204c_sleep(); - DEBUG_ATSHA_PRINTBUF("HMAC:", &rx_buffer[SHA204_BUFFER_POS_DATA], 32); - return true; // We return with the signature in rx_buffer[SHA204_BUFFER_POS_DATA] + DEBUG_SIGNING_PRINTBUF(F("HMAC:"), &rx_buffer[SHA204_BUFFER_POS_DATA], 32); } -// Helper to calculate a generic SHA256 digest of provided buffer (returned in rx_buffer[SHA204_BUFFER_POS_DATA]) (only supports one block) +// Helper to calculate a generic SHA256 digest of provided buffer (only supports one block) +// The pointer to the hash is returned, but the hash is also stored in rx_buffer[SHA204_BUFFER_POS_DATA]) uint8_t* MySigningAtsha204::sha256(const uint8_t* data, size_t sz) { // Initiate SHA256 calculator - if (atsha204.sha204m_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL, - SHA_COUNT_SHORT, tx_buffer, SHA_RSP_SIZE_SHORT, rx_buffer) != SHA204_SUCCESS) { - DEBUG_ATSHA_PRINTLN("Failed to initiate sha256"); - return NULL; - } + (void)atsha204.sha204m_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL, + SHA_COUNT_SHORT, tx_buffer, SHA_RSP_SIZE_SHORT, rx_buffer); // Calculate a hash memset(temp_message, 0x00, SHA_MSG_SIZE); @@ -272,16 +233,13 @@ uint8_t* MySigningAtsha204::sha256(const uint8_t* data, size_t sz) { // Write length data to the last bytes temp_message[SHA_MSG_SIZE-2] = (sz >> 5); temp_message[SHA_MSG_SIZE-1] = (sz << 3); - DEBUG_ATSHA_PRINTBUF("Data to hash:", temp_message, SHA_MSG_SIZE); - if (atsha204.sha204m_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, temp_message, - SHA_COUNT_LONG, tx_buffer, SHA_RSP_SIZE_LONG, rx_buffer) != SHA204_SUCCESS) { - DEBUG_ATSHA_PRINTLN("Failed to calculate sha256"); - return NULL; - } + DEBUG_SIGNING_PRINTBUF(F("DTH:"), temp_message, SHA_MSG_SIZE); // DTH = Data to hash + (void)atsha204.sha204m_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, temp_message, + SHA_COUNT_LONG, tx_buffer, SHA_RSP_SIZE_LONG, rx_buffer); // Put device back to sleep atsha204.sha204c_sleep(); - DEBUG_ATSHA_PRINTBUF("SHA:", &rx_buffer[SHA204_BUFFER_POS_DATA], 32); + DEBUG_SIGNING_PRINTBUF(F("SHA:"), &rx_buffer[SHA204_BUFFER_POS_DATA], 32); return &rx_buffer[SHA204_BUFFER_POS_DATA]; } diff --git a/libraries/MySensors/MySigningAtsha204.h b/libraries/MySensors/MySigningAtsha204.h index b96386a..1ebf386 100644 --- a/libraries/MySensors/MySigningAtsha204.h +++ b/libraries/MySensors/MySigningAtsha204.h @@ -45,7 +45,7 @@ private: uint8_t temp_message[SHA_MSG_SIZE]; uint8_t rx_buffer[SHA204_RSP_SIZE_MAX]; uint8_t tx_buffer[SHA204_CMD_SIZE_MAX]; - bool calculateSignature(MyMessage &msg); + void calculateSignature(MyMessage &msg); uint8_t* sha256(const uint8_t* data, size_t sz); #ifdef MY_SECURE_NODE_WHITELISTING uint8_t whitlist_sz; diff --git a/libraries/MySensors/MySigningAtsha204Soft.cpp b/libraries/MySensors/MySigningAtsha204Soft.cpp index 4d56cf1..d5aa8aa 100644 --- a/libraries/MySensors/MySigningAtsha204Soft.cpp +++ b/libraries/MySensors/MySigningAtsha204Soft.cpp @@ -13,16 +13,16 @@ #include "utility/sha256.h" // Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected) -//#define DEBUG_ATSHASOFT_SIGNING +//#define DEBUG_SIGNING -#ifdef DEBUG_ATSHASOFT_SIGNING -#define DEBUG_ATSHASOFT_PRINTLN(args) Serial.println(args) +#ifdef DEBUG_SIGNING +#define DEBUG_SIGNING_PRINTLN(args) Serial.println(args) #else -#define DEBUG_ATSHASOFT_PRINTLN(args) +#define DEBUG_SIGNING_PRINTLN(args) #endif -#ifdef DEBUG_ATSHASOFT_SIGNING -static void DEBUG_ATSHASOFT_PRINTBUF(char* str, uint8_t* buf, uint8_t sz) +#ifdef DEBUG_SIGNING +static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) { int i; Serial.println(str); @@ -37,7 +37,7 @@ static void DEBUG_ATSHASOFT_PRINTBUF(char* str, uint8_t* buf, uint8_t sz) Serial.println(); } #else -#define DEBUG_ATSHASOFT_PRINTBUF(str, buf, sz) +#define DEBUG_SIGNING_PRINTBUF(str, buf, sz) #endif MySigningAtsha204Soft::MySigningAtsha204Soft(bool requestSignatures, @@ -93,7 +93,7 @@ bool MySigningAtsha204Soft::getNonce(MyMessage &msg) { bool MySigningAtsha204Soft::checkTimer() { if (verification_ongoing) { if (millis() < timestamp || millis() > timestamp + MY_VERIFICATION_TIMEOUT_MS) { - DEBUG_ATSHASOFT_PRINTLN("Verification timeout"); + DEBUG_SIGNING_PRINTLN(F("VT")); // VT = Verification timeout // Purge nonce memset(current_nonce, 0xAA, 32); verification_ongoing = false; @@ -104,13 +104,8 @@ bool MySigningAtsha204Soft::checkTimer() { } bool MySigningAtsha204Soft::putNonce(MyMessage &msg) { - if (mGetLength(msg) != MAX_PAYLOAD) { - DEBUG_ATSHASOFT_PRINTLN("Incoming nonce with incorrect size"); - return false; // We require as big nonce as possible - } - if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) { - DEBUG_ATSHASOFT_PRINTLN("Incorrect signing identifier"); + DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier return false; } @@ -121,18 +116,13 @@ bool MySigningAtsha204Soft::putNonce(MyMessage &msg) { bool MySigningAtsha204Soft::signMsg(MyMessage &msg) { // If we cannot fit any signature in the message, refuse to sign it if (mGetLength(msg) > MAX_PAYLOAD-2) { - DEBUG_ATSHASOFT_PRINTLN("Cannot fit any signature to this message"); + DEBUG_SIGNING_PRINTLN(F("MTOL")); // Message too large for signature to fit return false; } // Calculate signature of message mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated - DEBUG_ATSHASOFT_PRINTBUF("Message to sign:", (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); - if (!calculateSignature(msg)) { - DEBUG_ATSHASOFT_PRINTLN("Failed to calculate signature"); - mSetSigned(msg, 0); // make sure signing flag is truthful - return false; - } + calculateSignature(msg); #ifdef MY_SECURE_NODE_WHITELISTING // Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided @@ -141,7 +131,7 @@ bool MySigningAtsha204Soft::signMsg(MyMessage &msg) { Sha256.write(msg.sender); for (int i=0; i */ +#include "MyConfig.h" #include "MySigning.h" #include "MySigningNone.h" // Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected) -//#define DEBUG_NONE_SIGNING +//#define DEBUG_SIGNING -#ifdef DEBUG_NONE_SIGNING -#define DEBUG_NONE_PRINTLN(args) Serial.println(args) +#ifdef DEBUG_SIGNING +#define DEBUG_SIGNING_PRINTLN(args) Serial.println(args) #else -#define DEBUG_NONE_PRINTLN(args) +#define DEBUG_SIGNING_PRINTLN(args) #endif -#ifdef DEBUG_NONE_SIGNING -static void DEBUG_NONE_PRINTBUF(char* str, uint8_t* buf, uint8_t sz) +#ifdef DEBUG_SIGNING +static void DEBUG_NONE_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) { int i; Serial.println(str); @@ -52,7 +53,7 @@ bool MySigningNone::putNonce(MyMessage &msg) { bool MySigningNone::signMsg(MyMessage &msg) { // If we cannot fit any signature in the message, refuse to sign it if (mGetLength(msg) > MAX_PAYLOAD-2) { - DEBUG_NONE_PRINTLN("Cannot fit any signature to this message"); + DEBUG_SIGNING_PRINTLN(F("MTOL")); // Message too large for signature to fit return false; } mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated diff --git a/libraries/MySensors/utility/ATSHA204.cpp b/libraries/MySensors/utility/ATSHA204.cpp index 9991335..c03ae7c 100644 --- a/libraries/MySensors/utility/ATSHA204.cpp +++ b/libraries/MySensors/utility/ATSHA204.cpp @@ -18,14 +18,14 @@ atsha204Class::atsha204Class(uint8_t pin) device_port_IN = portInputRegister(port); } -uint8_t atsha204Class::getSerialNumber(uint8_t * response) +void atsha204Class::getSerialNumber(uint8_t * response) { uint8_t readCommand[READ_COUNT]; uint8_t readResponse[READ_4_RSP_SIZE]; /* read from bytes 0->3 of config zone */ uint8_t returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN03); - if (!returnCode) // should return 0 if successful + if (!returnCode) { for (int i=0; i<4; i++) // store bytes 0-3 into respones array response[i] = readResponse[SHA204_BUFFER_POS_DATA+i]; @@ -43,7 +43,7 @@ uint8_t atsha204Class::getSerialNumber(uint8_t * response) } } - return returnCode; + return; } /* SWI bit bang functions */ @@ -212,9 +212,9 @@ uint8_t atsha204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer) /* Physical functions */ -uint8_t atsha204Class::sha204c_sleep() +void atsha204Class::sha204c_sleep() { - return swi_send_byte(SHA204_SWI_FLAG_SLEEP); + swi_send_byte(SHA204_SWI_FLAG_SLEEP); } uint8_t atsha204Class::sha204p_receive_response(uint8_t size, uint8_t *response) @@ -289,7 +289,7 @@ uint8_t atsha204Class::sha204c_resync(uint8_t size, uint8_t *response) // We lost communication. Send a Wake pulse and try // to receive a response (steps 2 and 3 of the // re-synchronization process). - (void) sha204c_sleep(); + sha204c_sleep(); ret_code = sha204c_wakeup(response); // Translate a return value of success into one diff --git a/libraries/MySensors/utility/ATSHA204.h b/libraries/MySensors/utility/ATSHA204.h index f4499b4..3864289 100644 --- a/libraries/MySensors/utility/ATSHA204.h +++ b/libraries/MySensors/utility/ATSHA204.h @@ -233,15 +233,15 @@ private: uint8_t swi_send_byte(uint8_t value); uint8_t sha204p_receive_response(uint8_t size, uint8_t *response); uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address); + uint8_t sha204c_wakeup(uint8_t *response); + uint8_t sha204c_resync(uint8_t size, uint8_t *response); + uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout); public: atsha204Class(uint8_t pin); // Constructor - uint8_t sha204c_wakeup(uint8_t *response); - uint8_t sha204c_sleep(); - uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout); - uint8_t sha204c_resync(uint8_t size, uint8_t *response); + void sha204c_sleep(); uint8_t sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t param2, uint8_t datalen1, uint8_t *data1, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer); - uint8_t getSerialNumber(uint8_t *response); + void getSerialNumber(uint8_t *response); }; #endif From 2d2eb74e0a9a4476845e9880fd809b0af2332f64 Mon Sep 17 00:00:00 2001 From: Patrick Fallberg Date: Sat, 14 Mar 2015 12:37:22 +0100 Subject: [PATCH 07/11] Fixed bug with storing signing table in EEPROM eeprom_updated_block() uses a non-string-standardised order of arguments. --- libraries/MySensors/MySensor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index c058e2e..644b3e1 100755 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -406,7 +406,7 @@ boolean MySensor::process() { CLEAR_SIGN(msg.sender); } // Save updated table - hw_writeConfigBlock((void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, (void*)doSign, sizeof(doSign)); + hw_writeConfigBlock((void*)doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(doSign)); // 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 From 7a2a5dad81e0a3ec30e6167d673d0c9d84f8c5c2 Mon Sep 17 00:00:00 2001 From: Patrick Fallberg Date: Sat, 14 Mar 2015 13:38:39 +0100 Subject: [PATCH 08/11] Message signing logic now integrated into sendRoute() --- libraries/MySensors/MySensor.cpp | 52 +++++++++++++++++--------------- libraries/MySensors/MySensor.h | 1 - 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 644b3e1..b92f118 100755 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -192,7 +192,33 @@ boolean MySensor::sendRoute(MyMessage &message) { (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))) { - if (!sign(message)) { + bool signOk = false; + // Send nonce-request + if (!sendRoute(build(tmpMsg, 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 = hw_millis(); + msgSign = message; // Copy the message to sign since message buffer might be touched in process() + while (hw_millis() - 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 (hw_millis() - enter > MY_VERIFICATION_TIMEOUT_MS) { + debug(PSTR("nonce tmo\n")); + return false; + } + if (!signOk) { debug(PSTR("sign fail\n")); return false; } @@ -524,27 +550,3 @@ int8_t MySensor::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, ui return hw.sleep(interrupt1, mode1, interrupt2, mode2, ms) ; } -bool MySensor::sign(MyMessage &message) { - if (!sendRoute(build(tmpMsg, nc.nodeId, message.destination, message.sensor, C_INTERNAL, I_GET_NONCE, false).set(""))) { - return false; - } else { - // 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 = hw_millis(); - msgSign = message; // Copy the message to sign since message buffer might be touched in process() - while (hw_millis() - enter < 5000) { - 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 - return true; - } - break; - } - } - } - } - return false; -} - diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 9a2ec57..9093183 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -257,7 +257,6 @@ class MySensor void setupNode(); void findParentNode(); uint8_t crc8Message(MyMessage &message); - bool sign(MyMessage &message); }; #endif From 70eac0423d0f8da9b1ba8b6eaa5c440d8cca4a60 Mon Sep 17 00:00:00 2001 From: Patrick Fallberg Date: Sat, 14 Mar 2015 13:53:11 +0100 Subject: [PATCH 09/11] Added config flag MY_SIGNING_FEATURE in MyConfig.h By disabeling MY_SIGNING_FEATURE all signing logic in the library will be removed. --- libraries/MySensors/MyConfig.h | 3 +++ libraries/MySensors/MySensor.cpp | 24 +++++++++++++++++-- libraries/MySensors/MySensor.h | 14 ++++++++--- .../SecureActuator/SecureActuator.ino | 5 ++++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index e68709a..61351af 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -16,6 +16,9 @@ typedef MyHwATMega328 MyHwDriver; /********************************** * Message Signing Settings ***********************************/ +// Disable to completly disable signing functionality in library +#define MY_SIGNING_FEATURE + // Define a suitable timeout for a signature verification session // Consider the turnaround from a nonce being generated to a signed message being received // which might vary, especially in networks with many hops. 5s ought to be enough for anyone. diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index b92f118..aa777d4 100755 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -13,10 +13,12 @@ #define DISTANCE_INVALID (0xFF) +#ifdef MY_SIGNING_FEATURE // 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))) +#endif // Inline function and macros static inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) { @@ -38,10 +40,16 @@ static inline bool isValidDistance( const uint8_t distance ) { } -MySensor::MySensor(MyTransport &_radio, MySigning &_signer, MyHw &_hw) +MySensor::MySensor(MyTransport &_radio, MyHw &_hw +#ifdef MY_SIGNING_FEATURE + , MySigning &_signer +#endif + ) : radio(_radio), +#ifdef MY_SIGNING_FEATURE signer(_signer), +#endif hw(_hw) { } @@ -63,8 +71,10 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b hw_readConfigBlock((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig)); // Read latest received controller configuration from EEPROM hw_readConfigBlock((void*)&cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig)); +#ifdef MY_SIGNING_FEATURE // Read out the signing requirements from EEPROM hw_readConfigBlock((void*)doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(doSign)); +#endif if (isGateway) { nc.distance = 0; @@ -128,6 +138,7 @@ void MySensor::setupNode() { // Present node and request config if (!isGateway && 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())); @@ -135,6 +146,7 @@ void MySensor::setupNode() { if (signer.requestSignatures()) { wait(2000); } +#endif // Send presentation for this radio node (attach present(NODE_SENSOR_ID, repeaterMode? S_ARDUINO_REPEATER_NODE : S_ARDUINO_NODE); @@ -186,6 +198,7 @@ boolean MySensor::sendRoute(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(msg) && (mGetCommand(message) != C_INTERNAL || @@ -225,6 +238,7 @@ boolean MySensor::sendRoute(MyMessage &message) { // 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 || !repeaterMode) { // If destination is the gateway or if we aren't a repeater, let @@ -339,10 +353,13 @@ boolean MySensor::process() { if (!radio.available(&to)) return false; +#ifdef MY_SIGNING_FEATURE (void)signer.checkTimer(); // Manage signing timeout - +#endif + uint8_t len = radio.receive((uint8_t *)&msg); +#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) && @@ -360,6 +377,7 @@ boolean MySensor::process() { return false; // This signed message has been tampered with! } } +#endif // Add string termination, good if we later would want to print it. msg.data[mGetLength(msg)] = '\0'; @@ -418,6 +436,7 @@ boolean MySensor::process() { } } return false; +#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)); @@ -446,6 +465,7 @@ boolean MySensor::process() { return false; // Signing request is an internal MySensor protocol message, no need to inform caller about this } else if (type == I_GET_NONCE_RESPONSE) { return true; // Just pass along nonce silently (no need to call callback for these) +#endif } else if (sender == GATEWAY_ADDRESS) { bool isMetric; diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 9093183..0d43da3 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -18,8 +18,10 @@ #include "MyTransport.h" #include "MyTransportNRF24.h" #include "MyParser.h" +#ifdef MY_SIGNING_FEATURE #include "MySigning.h" #include "MySigningNone.h" +#endif #include "MyMessage.h" #include #include @@ -73,7 +75,11 @@ class MySensor * Creates a new instance of Sensor class. * */ - MySensor(MyTransport &radio =*new MyTransportNRF24(), MySigning &signer=*new MySigningNone(), MyHw &hw=*new MyHwDriver()); + MySensor(MyTransport &radio =*new MyTransportNRF24(), MyHw &hw=*new MyHwDriver() +#ifdef MY_SIGNING_FEATURE + , MySigning &signer=*new MySigningNone() +#endif + ); /** * Begin operation of the MySensors library @@ -233,14 +239,16 @@ class MySensor bool repeaterMode; bool autoFindParent; bool isGateway; - uint16_t doSign[16]; // Bitfield indicating which sensors require signed communication MyMessage msg; // Buffer for incoming messages. MyMessage tmpMsg ; // Buffer for temporary messages (acks and nonces among others). +#ifdef MY_SIGNING_FEATURE + uint16_t doSign[16]; // Bitfield indicating which sensors require signed communication MyMessage msgSign; // Buffer for message to sign. + MySigning& signer; +#endif MyTransport& radio; - MySigning& signer; MyHw& hw; boolean sendWrite(uint8_t dest, MyMessage &message); diff --git a/libraries/MySensors/examples/SecureActuator/SecureActuator.ino b/libraries/MySensors/examples/SecureActuator/SecureActuator.ino index 297f6dd..a3ee2bc 100644 --- a/libraries/MySensors/examples/SecureActuator/SecureActuator.ino +++ b/libraries/MySensors/examples/SecureActuator/SecureActuator.ino @@ -20,6 +20,7 @@ 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 @@ -43,6 +44,10 @@ MySigningAtsha204 signer; #endif #endif MySensor gw(radio, hw, signer); +#else +#error SecureActuator cannot possibly be secure without signing enabled +MySensor gw(radio, hw); +#endif void setup() { From 6fd26880a2ba06ec9d3b1da67358bacd405fab12 Mon Sep 17 00:00:00 2001 From: Patrick Fallberg Date: Mon, 16 Mar 2015 22:45:39 +0100 Subject: [PATCH 10/11] Made "Inclusion started by button" a DEBUG message In other words, the message will only be printed if DEBUG flag is enabled. --- libraries/MySensors/examples/EthernetGateway/GatewayUtil.h | 2 ++ libraries/MySensors/examples/SerialGateway/GatewayUtil.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/libraries/MySensors/examples/EthernetGateway/GatewayUtil.h b/libraries/MySensors/examples/EthernetGateway/GatewayUtil.h index d564ea6..6f006df 100644 --- a/libraries/MySensors/examples/EthernetGateway/GatewayUtil.h +++ b/libraries/MySensors/examples/EthernetGateway/GatewayUtil.h @@ -91,7 +91,9 @@ void checkButtonTriggeredInclusion() { if (buttonTriggeredInclusion) { // Ok, someone pressed the inclusion button on the gateway // start inclusion mode for 1 munute. +#ifdef DEBUG serial(PSTR("0;0;%d;0;%d;Inclusion started by button.\n"), C_INTERNAL, I_LOG_MESSAGE); +#endif buttonTriggeredInclusion = false; setInclusionMode(true); } diff --git a/libraries/MySensors/examples/SerialGateway/GatewayUtil.h b/libraries/MySensors/examples/SerialGateway/GatewayUtil.h index d564ea6..6f006df 100644 --- a/libraries/MySensors/examples/SerialGateway/GatewayUtil.h +++ b/libraries/MySensors/examples/SerialGateway/GatewayUtil.h @@ -91,7 +91,9 @@ void checkButtonTriggeredInclusion() { if (buttonTriggeredInclusion) { // Ok, someone pressed the inclusion button on the gateway // start inclusion mode for 1 munute. +#ifdef DEBUG serial(PSTR("0;0;%d;0;%d;Inclusion started by button.\n"), C_INTERNAL, I_LOG_MESSAGE); +#endif buttonTriggeredInclusion = false; setInclusionMode(true); } From 6fb726675272738a4fbb2f369ea7bbe73b82023a Mon Sep 17 00:00:00 2001 From: Patrick Fallberg Date: Mon, 16 Mar 2015 22:53:27 +0100 Subject: [PATCH 11/11] Removed 'new' from PinChangeInt library As a consequence, only one pin is currently suppored. AddPin will not really add a new pin, just replace the configuration of the existing pin. The motivation for this limitation is that 'new' costs a lot of program memory. --- libraries/PinChangeInt/PinChangeInt.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/PinChangeInt/PinChangeInt.h b/libraries/PinChangeInt/PinChangeInt.h index 19be5a6..807b20d 100644 --- a/libraries/PinChangeInt/PinChangeInt.h +++ b/libraries/PinChangeInt/PinChangeInt.h @@ -234,6 +234,7 @@ protected: volatile uint8_t portFallingPins; volatile uint8_t lastPinView; PCintPin* firstPin; + PCintPin thePin; }; #ifndef LIBCALL_PINCHANGEINT // LIBCALL_PINCHANGEINT *********************************************** @@ -388,8 +389,9 @@ int8_t PCintPort::addPin(uint8_t arduinoPin, PCIntvoidFuncPtr userFunc, uint8_t } while (true); } - // Create pin p: fill in the data. - PCintPin* p=new PCintPin; + // Create pin p: fill in the data. NOTE: MySensors only support one pin due to size constraints + // TODO: Figure out a better way to solve this without relying on new/malloc + PCintPin* p = &thePin; //PCintPin* p=new PCintPin; if (p == NULL) return(-1); p->arduinoPin=arduinoPin; p->mode = mode;