Fixed: Software signing

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