diff --git a/MySensors.h b/MySensors.h index 0a7b4af..c65fa4e 100644 --- a/MySensors.h +++ b/MySensors.h @@ -1,4 +1,4 @@ -/* +/* * The MySensors Arduino library handles the wireless radio link and protocol * between your home built sensors/actuators and HA controller of choice. * The sensors forms a self healing radio network with optional repeaters. Each @@ -145,7 +145,10 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs // SIGNING #if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT) -#define MY_SIGNING_FEATURE //!< MY_SIGNING_FEATURE +#define MY_SIGNING_FEATURE //!< MY_SIGNING_FEATURE +#endif +#if defined(MY_RF24_ENABLE_ENCRYPTION) || defined(MY_RFM69_ENABLE_ENCRYPTION) || defined(MY_NRF5_ESB_ENABLE_ENCRYPTION) +#define MY_ENCRYPTION_FEATURE //!< MY_ENCRYPTION_FEATURE #endif #include "core/MySigning.cpp" #include "drivers/ATSHA204/sha256.cpp" diff --git a/core/MySigning.cpp b/core/MySigning.cpp index 3302363..60743a1 100644 --- a/core/MySigning.cpp +++ b/core/MySigning.cpp @@ -38,10 +38,11 @@ #error You have to pick one and only one signing backend #endif #ifdef MY_SIGNING_FEATURE -uint8_t _doSign[32]; // Bitfield indicating which sensors require signed communication -uint8_t _doWhitelist[32]; // Bitfield indicating which sensors require serial salted signatures -MyMessage _msgSign; // Buffer for message to sign. -uint8_t _signingNonceStatus; +static uint8_t _doSign[32]; // Bitfield indicating which sensors require signed communication +static uint8_t _doWhitelist[32]; // Bitfield indicating which sensors require salted signatures +static MyMessage _msgSign; // Buffer for message to sign. +static uint8_t _signingNonceStatus; +static bool stateValid = false; #ifdef MY_NODE_LOCK_FEATURE static uint8_t nof_nonce_requests = 0; @@ -98,9 +99,25 @@ static void prepareSigningPresentation(MyMessage &msg, uint8_t destination); static bool signerInternalProcessPresentation(MyMessage &msg); static bool signerInternalProcessNonceRequest(MyMessage &msg); static bool signerInternalProcessNonceResponse(MyMessage &msg); +#if defined(MY_ENCRYPTION_FEATURE) || defined(MY_SIGNING_FEATURE) +static bool signerInternalValidatePersonalization(void); +#endif void signerInit(void) { +#if defined(MY_SIGNING_FEATURE) + stateValid = true; +#endif +#if defined (MY_ENCRYPTION_FEATURE) || defined (MY_SIGNING_FEATURE) + if (!signerInternalValidatePersonalization()) { + SIGN_DEBUG(PSTR("!SGN:PER:TAMPERED\n")); +#if defined(MY_SIGNING_FEATURE) + stateValid = false; +#endif + } else { + SIGN_DEBUG(PSTR("SGN:PER:OK\n")); + } +#endif #if defined(MY_SIGNING_FEATURE) // Read out the signing requirements from EEPROM hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, @@ -182,38 +199,44 @@ bool signerSignMsg(MyMessage &msg) if (skipSign(msg)) { ret = true; } else { - // Send nonce-request - _signingNonceStatus=SIGN_WAITING_FOR_NONCE; - if (!_sendRoute(build(_msgSign, msg.destination, msg.sensor, C_INTERNAL, - I_NONCE_REQUEST).set(""))) { - SIGN_DEBUG(PSTR("!SGN:SGN:NCE REQ,TO=%d FAIL\n"), - msg.destination); // Failed to transmit nonce request! + // Before starting, validate that our state is good, or signing will fail + if (!stateValid) { + SIGN_DEBUG(PSTR("!SGN:SGN:STATE\n")); // Signing system is not in a valid state ret = false; } else { - SIGN_DEBUG(PSTR("SGN:SGN:NCE REQ,TO=%d\n"), msg.destination); // Nonce requested - // We have to wait for the nonce to arrive before we can sign our original message - // Other messages could come in-between. We trust _process() takes care of them - unsigned long enter = hwMillis(); - _msgSign = msg; // Copy the message to sign since buffer might be touched in _process() - while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS && - _signingNonceStatus==SIGN_WAITING_FOR_NONCE) { - _process(); - } - if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) { - SIGN_DEBUG(PSTR("!SGN:SGN:NCE TMO\n")); // Timeout waiting for nonce! + // Send nonce-request + _signingNonceStatus=SIGN_WAITING_FOR_NONCE; + if (!_sendRoute(build(_msgSign, msg.destination, msg.sensor, C_INTERNAL, + I_NONCE_REQUEST).set(""))) { + SIGN_DEBUG(PSTR("!SGN:SGN:NCE REQ,TO=%d FAIL\n"), + msg.destination); // Failed to transmit nonce request! ret = false; } else { - if (_signingNonceStatus == SIGN_OK) { - // process() received a nonce and signerProcessInternal successfully signed the message - msg = _msgSign; // Write the signed message back - SIGN_DEBUG(PSTR("SGN:SGN:SGN\n")); // Message to send has been signed - ret = true; - // 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 { - SIGN_DEBUG(PSTR("!SGN:SGN:SGN FAIL\n")); // Message to send could not be signed! + SIGN_DEBUG(PSTR("SGN:SGN:NCE REQ,TO=%d\n"), msg.destination); // Nonce requested + // We have to wait for the nonce to arrive before we can sign our original message + // Other messages could come in-between. We trust _process() takes care of them + unsigned long enter = hwMillis(); + _msgSign = msg; // Copy the message to sign since buffer might be touched in _process() + while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS && + _signingNonceStatus==SIGN_WAITING_FOR_NONCE) { + _process(); + } + if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) { + SIGN_DEBUG(PSTR("!SGN:SGN:NCE TMO\n")); // Timeout waiting for nonce! ret = false; + } else { + if (_signingNonceStatus == SIGN_OK) { + // process() received a nonce and signerProcessInternal successfully signed the message + msg = _msgSign; // Write the signed message back + SIGN_DEBUG(PSTR("SGN:SGN:SGN\n")); // Message to send has been signed + ret = true; + // 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 { + SIGN_DEBUG(PSTR("!SGN:SGN:SGN FAIL\n")); // Message to send could not be signed! + ret = false; + } } } } @@ -256,11 +279,17 @@ bool signerVerifyMsg(MyMessage &msg) SIGN_DEBUG(PSTR("!SGN:VER:NSG\n")); // Message is not signed, but it should have been! verificationResult = false; } else { - if (!signerBackendVerifyMsg(msg)) { - SIGN_DEBUG(PSTR("!SGN:VER:FAIL\n")); // Signature verification failed! + // Before starting, validate that our state is good, or signing will fail + if (!stateValid) { + SIGN_DEBUG(PSTR("!SGN:VER:STATE\n")); // Signing system is not in a valid state verificationResult = false; } else { - SIGN_DEBUG(PSTR("SGN:VER:OK\n")); + if (!signerBackendVerifyMsg(msg)) { + SIGN_DEBUG(PSTR("!SGN:VER:FAIL\n")); // Signature verification failed! + verificationResult = false; + } else { + SIGN_DEBUG(PSTR("SGN:VER:OK\n")); + } } #if defined(MY_NODE_LOCK_FEATURE) if (verificationResult) { @@ -533,3 +562,32 @@ static bool signerInternalProcessNonceResponse(MyMessage &msg) #endif return true; // No need to further process I_NONCE_RESPONSE } + +#if defined(MY_ENCRYPTION_FEATURE) || defined(MY_SIGNING_FEATURE) +static bool signerInternalValidatePersonalization(void) +{ +#ifdef __linux__ + // Personalization on linux is a bit more crude + return true; +#else + uint8_t buffer[32]; + uint8_t* hash; + uint8_t checksum; + + signerSha256Init(); + hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32); + signerSha256Update(buffer, 32); + hwReadConfigBlock((void*)buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16); + signerSha256Update(buffer, 16); + hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9); + signerSha256Update(buffer, 9); + hash = signerSha256Final(); + hwReadConfigBlock((void*)&checksum, (void*)EEPROM_PERSONALIZATION_CHECKSUM_ADDRESS, 1); + if (checksum != hash[0]) { + return false; + } else { + return true; + } +#endif +} +#endif diff --git a/core/MySigning.h b/core/MySigning.h index 05334d2..d6c1015 100644 --- a/core/MySigning.h +++ b/core/MySigning.h @@ -790,10 +790,12 @@ int signerMemcmp(const void* a, const void* b, size_t sz); * |!| SGN | SGN | SGN FAIL | Message failed to be signed * | | SGN | SGN | NREQ='node' | 'node' does not require signed messages * | | SGN | SGN | 'sender'!='us' NUS | Will not sign because 'sender' is not 'us' (repeater) + * |!| SGN | SGN | STATE | Security system in a invalid state (personalization data tampered) * |!| SGN | VER | NSG | Message was not signed, but it should have been * |!| SGN | VER | FAIL | Verification failed * | | SGN | VER | OK | Verification succeeded * | | SGN | VER | LEFT='number' | 'number' of failed verifications left in a row before node is locked + * |!| SGN | VER | STATE | Security system in a invalid state (personalization data tampered) * | | SGN | SKP | MSG CMD='cmd',TYPE='type'| Message with command 'cmd' and type 'type' does not need to be signed * | | SGN | SKP | ACK CMD='cmd',TYPE='type'| ACK messages does not need to be signed * | | SGN | NCE | LEFT='number' | 'number' of nonce requests between successful verifications left before node is locked diff --git a/core/MySigningAtsha204Soft.cpp b/core/MySigningAtsha204Soft.cpp index 2f25f4d..7f67c93 100644 --- a/core/MySigningAtsha204Soft.cpp +++ b/core/MySigningAtsha204Soft.cpp @@ -103,33 +103,6 @@ bool signerAtsha204SoftInit(void) _signing_node_serial_info[8] = getNodeId(); } #else - uint8_t buffer[32]; - uint8_t* hash; - uint8_t checksum; - - _signing_sha256.init(); - hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32); - for (int i = 0; i < 32; i++) { - _signing_sha256.write(buffer[i]); - } - hwReadConfigBlock((void*)buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16); - for (int i = 0; i < 16; i++) { - _signing_sha256.write(buffer[i]); - } - hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9); - for (int i = 0; i < 9; i++) { - _signing_sha256.write(buffer[i]); - } - hash = _signing_sha256.result(); - hwReadConfigBlock((void*)&checksum, (void*)EEPROM_PERSONALIZATION_CHECKSUM_ADDRESS, 1); - if (checksum != hash[0]) { - SIGN_DEBUG(PSTR("!SGN:PER:TAMPERED\n")); - init_ok = false; - } else { - SIGN_DEBUG(PSTR("SGN:PER:OK\n")); - init_ok = true; - } - if (init_ok) { hwReadConfigBlock((void*)_signing_hmac_key, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32); hwReadConfigBlock((void*)_signing_node_serial_info, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);