Merge pull request #377 from fallberg/lockdown

Node lock feature
This commit is contained in:
Henrik Ekblad
2016-01-30 14:45:10 +01:00
9 changed files with 182 additions and 3 deletions
+2 -1
View File
@@ -38,6 +38,7 @@
#define EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS (EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS+32) // This is set with SecurityPersonalizer.ino
#define EEPROM_SIGNING_SOFT_SERIAL_ADDRESS (EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS+32) // This is set with SecurityPersonalizer.ino
#define EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS (EEPROM_SIGNING_SOFT_SERIAL_ADDRESS+9) // This is set with SecurityPersonalizer.ino
#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS+16) // First free address for sketch static configuration
#define EEPROM_NODE_LOCK_COUNTER (EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS+16)
#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_NODE_LOCK_COUNTER+1) // First free address for sketch static configuration
#endif
+2 -1
View File
@@ -170,7 +170,8 @@ typedef enum {
I_SIGNING_PRESENTATION, //!< Provides signing related preferences (first byte is preference version)
I_NONCE_REQUEST, //!< Request for a nonce
I_NONCE_RESPONSE, //!< Payload is nonce data
I_HEARTBEAT, I_PRESENTATION, I_DISCOVER, I_DISCOVER_RESPONSE, I_HEARTBEAT_RESPONSE
I_HEARTBEAT, I_PRESENTATION, I_DISCOVER, I_DISCOVER_RESPONSE, I_HEARTBEAT_RESPONSE,
I_LOCKED //!< Node is locked (reason in string-payload)
} mysensor_internal;
+44
View File
@@ -152,6 +152,31 @@ void _begin() {
}
#endif
#ifdef MY_NODE_LOCK_FEATURE
// Check if node has been locked down
if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0) {
// Node is locked, check if unlock pin is asserted, else hang the node
pinMode(MY_NODE_UNLOCK_PIN, INPUT_PULLUP);
// Make a short delay so we are sure any large external nets are fully pulled
unsigned long enter = hwMillis();
while (hwMillis() - enter < 2);
if (digitalRead(MY_NODE_UNLOCK_PIN) == 0) {
// Pin is grounded, reset lock counter
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
// Disable pullup
pinMode(MY_NODE_UNLOCK_PIN, INPUT);
debug(PSTR("Node is unlocked.\n"));
} else {
// Disable pullup
pinMode(MY_NODE_UNLOCK_PIN, INPUT);
nodeLock("LDB"); //Locked during boot
}
} else if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0xFF) {
// Reset walue
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
}
#endif
// Call sketch setup
if (setup)
setup();
@@ -162,6 +187,7 @@ void _begin() {
#endif
if (presentation)
presentation();
debug(PSTR("Init complete, id=%d, parent=%d, distance=%d\n"), _nc.nodeId, _nc.parentNodeId, _nc.distance);
}
@@ -400,4 +426,22 @@ int8_t smartSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t
return ret;
}
#ifdef MY_NODE_LOCK_FEATURE
void nodeLock(const char* str) {
// Make sure EEPROM is updated to locked status
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, 0);
while (1) {
debug(PSTR("Node is locked. Ground pin %d and reset to unlock.\n"), MY_NODE_UNLOCK_PIN);
#if defined(MY_GATEWAY_ESP8266)
yield();
#endif
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID,
C_INTERNAL, I_LOCKED, false).set(str));
#if defined(MY_RADIO_FEATURE)
transportPowerDown();
#endif
(void)hwSleep((unsigned long)1000*60*30); // Sleep for 30 min before resending LOCKED message
}
}
#endif
+14
View File
@@ -202,6 +202,20 @@ int8_t smartSleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0);
int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms=0);
int8_t smartSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms=0);
#ifdef MY_NODE_LOCK_FEATURE
/**
* @ingroup MyLockgrp
* @ingroup internals
* @brief Lock a node and transmit provided message with 30m intervals
*
* This function is called if suspicious activity has exceeded the threshold (see
* @ref ATTACK_COUNTER_MAX). Unlocking with a normal Arduino bootloader require erasing the EEPROM
* while unlocking with a custom bootloader require holding @ref UNLOCK_PIN low during power on/reset.
*
* @param str The string to transmit.
*/
void nodeLock(const char* str);
#endif
/****** PRIVATE ********/
+25
View File
@@ -39,6 +39,11 @@ uint8_t _doWhitelist[32]; // Bitfield indicating which sensors require serial sa
MyMessage _msgSign; // Buffer for message to sign.
uint8_t _signingNonceStatus;
#ifdef MY_NODE_LOCK_FEATURE
static uint8_t nof_nonce_requests = 0;
static uint8_t nof_failed_verifications = 0;
#endif
// Status when waiting for signing nonce in signerProcessInternal
enum { SIGN_WAITING_FOR_NONCE = 0, SIGN_OK = 1 };
@@ -161,6 +166,13 @@ bool signerProcessInternal(MyMessage &msg) {
}
#elif defined(MY_SIGNING_FEATURE)
if (msg.type == I_NONCE_REQUEST) {
#if defined(MY_NODE_LOCK_FEATURE)
nof_nonce_requests++;
SIGN_DEBUG(PSTR("Nonce requests left until lockdown: %d\n"), MY_NODE_LOCK_COUNTER_MAX-nof_nonce_requests);
if (nof_nonce_requests >= MY_NODE_LOCK_COUNTER_MAX) {
nodeLock("TMNR"); //Too many nonces requested
}
#endif
#if defined(MY_SIGNING_SOFT)
if (signerAtsha204SoftGetNonce(msg)) {
#endif
@@ -353,6 +365,19 @@ bool signerVerifyMsg(MyMessage &msg) {
if (!verificationResult) {
SIGN_DEBUG(PSTR("Signature verification failed!\n"));
}
#if defined(MY_NODE_LOCK_FEATURE)
if (verificationResult) {
// On successful verification, clear lock counters
nof_nonce_requests = 0;
nof_failed_verifications = 0;
} else {
nof_failed_verifications++;
SIGN_DEBUG(PSTR("Failed verification attempts left until lockdown: %d\n"), MY_NODE_LOCK_COUNTER_MAX-nof_failed_verifications);
if (nof_failed_verifications >= MY_NODE_LOCK_COUNTER_MAX) {
nodeLock("TMFV"); //Too many failed verifications
}
}
#endif
mSetSigned(msg,0); // Clear the sign-flag now as verification is completed
}
}