diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index 0b4d806..61351af 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -16,16 +16,31 @@ 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. #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/MySensor.cpp b/libraries/MySensors/MySensor.cpp index c058e2e..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,19 +198,47 @@ 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 || (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; } // 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 @@ -313,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) && @@ -334,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'; @@ -392,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)); @@ -406,7 +451,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 @@ -420,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; @@ -524,27 +570,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..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); @@ -257,7 +265,6 @@ class MySensor void setupNode(); void findParentNode(); uint8_t crc8Message(MyMessage &message); - bool sign(MyMessage &message); }; #endif diff --git a/libraries/MySensors/MySigningAtsha204.cpp b/libraries/MySensors/MySigningAtsha204.cpp index 1effefc..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; } @@ -92,35 +96,39 @@ 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; + 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 + DEBUG_SIGNING_PRINTLN(F("SWS")); // SWS = Signature whitelist 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)); @@ -130,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 @@ -141,77 +149,97 @@ 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)); + 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 - // Get signature of message - if (!calculateSignature(msg)) { - DEBUG_ATSHA_PRINTLN("Failed to calculate signature"); - 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_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); + (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)); + DEBUG_SIGNING_PRINTBUF(F("SNOK:"), &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); // SNOK = Signature bad +#ifdef MY_SECURE_NODE_WHITELISTING + 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))); - // 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) { - 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(); - // Overwrite the first byte in the signature with the signing identifier - rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER; + DEBUG_SIGNING_PRINTBUF(F("HMAC:"), &rx_buffer[SHA204_BUFFER_POS_DATA], 32); +} - 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 (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 + (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); + 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_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_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 8850b2c..1ebf386 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,11 +41,16 @@ private: atsha204Class atsha204; unsigned long timestamp; bool verification_ongoing; - uint8_t current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH]; - uint8_t temp_message[32]; + 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); + 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/MySigningAtsha204Soft.cpp b/libraries/MySensors/MySigningAtsha204Soft.cpp index 3ac6f38..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,29 +37,46 @@ 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, 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() { - // 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 @@ -76,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; @@ -87,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; } @@ -104,18 +116,26 @@ 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 + Sha256.init(); + for (int i=0; i<32; i++) Sha256.write(hmac[i]); + 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/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/SecureActuator/SecureActuator.ino b/libraries/MySensors/examples/SecureActuator/SecureActuator.ino index d249f29..a3ee2bc 100644 --- a/libraries/MySensors/examples/SecureActuator/SecureActuator.ino +++ b/libraries/MySensors/examples/SecureActuator/SecureActuator.ino @@ -1,10 +1,17 @@ // Example sketch showing how to securely control locks. // This example will remember lock state even after power failure. -#include +#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,40 @@ #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; // 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); +#else +#error 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, 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/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); } 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) diff --git a/libraries/MySensors/utility/ATSHA204.cpp b/libraries/MySensors/utility/ATSHA204.cpp index da63668..c03ae7c 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); } +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) + { + 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; +} + /* SWI bit bang functions */ void atsha204Class::swi_set_signal_pin(uint8_t is_high) @@ -184,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) @@ -261,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 @@ -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) { @@ -447,12 +492,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..3864289 100644 --- a/libraries/MySensors/utility/ATSHA204.h +++ b/libraries/MySensors/utility/ATSHA204.h @@ -59,6 +59,8 @@ #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 // packet size definitions @@ -122,6 +124,22 @@ #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 +#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 +160,10 @@ #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 // command timing definitions for minimum execution times (ms) @@ -149,6 +171,8 @@ #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)) // command timing definitions for maximum execution times (ms) @@ -156,6 +180,8 @@ #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)) /* from sha204_config.h */ @@ -172,7 +198,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 @@ -181,6 +207,19 @@ #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 + +#define SHA204_SERIAL_SZ 9 // The number of bytes the serial number consists of + class atsha204Class { private: @@ -193,14 +232,16 @@ 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); + 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); + void getSerialNumber(uint8_t *response); }; #endif 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;