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
***********************************/
// 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_SOFT
@@ -168,21 +168,26 @@
// 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
// 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
#ifndef MY_ATSHA204_PIN
#define MY_ATSHA204_PIN 17 // A3 - pin where ATSHA204 is attached
// Atsha204 default pin setting
#ifndef MY_SIGNING_ATSHA204_PIN
#define MY_SIGNING_ATSHA204_PIN 17 // A3 - pin where ATSHA204 is attached
#endif
// MySigningAtsha204Soft default settings
#ifndef MY_RANDOMSEED_PIN
#define MY_RANDOMSEED_PIN 7 // A7 - Pin used for random generation (do not connect anything to this)
// Pin used for random generation in soft signing (do not connect anything to this when enabled)
#ifndef MY_SIGNING_SOFT_RANDOMSEED_PIN
#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
// Key to use for HMAC calculation in MySigningAtsha204Soft (32 bytes)
#ifndef MY_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
#ifndef MY_SIGNING_SOFT_HMAC_KEY
#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
/**********************************
+28 -32
View File
@@ -25,6 +25,7 @@
// #pragma message(VAR_NAME_VALUE(MY_GATEWAY_FEATURE))
#include "MyConfig.h"
#include "core/MySensorCore.h"
#if defined(MY_GATEWAY_SERIAL) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_ENC28J60)
#define MY_GATEWAY_FEATURE
@@ -60,16 +61,19 @@
// SIGNING
#if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT)
#include "MySigning.cpp"
// SIGNING COMMON FUNCTIONS
#include "core/MySigning.cpp"
#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)
#include "MySigningAtsha204.cpp"
#include "core/MySigningAtsha204.cpp"
#include "drivers/ATSHA204/ATSHA204.cpp"
MySigningAtsha204 signer;
#elif defined(MY_SIGNING_SOFT)
#include "MySigningAtsha204Soft.cpp"
#include "core/MySigningAtsha204Soft.cpp"
#include "drivers/ATSHA204/sha256.cpp"
MySigningAtsha204Soft signer;
#endif
#endif
@@ -144,31 +148,6 @@
#undef MY_GATEWAY_FEATURE
#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
#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69)
@@ -188,6 +167,9 @@
SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID);
#endif
#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)
#include "drivers/RF24/RF24.cpp"
#include "core/MyTransportNRF24.cpp"
@@ -197,6 +179,20 @@
#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"
@@ -209,10 +205,10 @@ extern void loop();
// Initialize library and handle sketch functions like we want to
int main() {
init(); // Init Arduino
begin(); // Startup MySensors library
_begin(); // Startup MySensors library
setup(); // Call sketch setup
while(1) {
process(); // Process incoming data
_process(); // Process incoming data
loop(); // Call sketch loop
if (serialEventRun) serialEventRun();
}
@@ -21,8 +21,8 @@
#include "MyProtocolDefault.h"
#include "MyTransport.h"
char fmtBuffer[MAX_FORMAT_BUFFER_LENGTH];
char convBuffer[MAX_PAYLOAD*2+1];
char _fmtBuffer[MAX_FORMAT_BUFFER_LENGTH];
char _convBuffer[MAX_PAYLOAD*2+1];
bool protocolParse(MyMessage &message, char *inputString) {
char *str, *p, *value=NULL;
@@ -91,8 +91,8 @@ bool protocolParse(MyMessage &message, char *inputString) {
}
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));
return fmtBuffer;
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;
}
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).
#ifdef MY_DEBUG
char convBuf[MAX_PAYLOAD*2+1];
char _convBuf[MAX_PAYLOAD*2+1];
#endif
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 process() {
void _process() {
hwWatchdogReset();
#if defined (MY_LEDS_BLINKING_FEATURE)
ledsProcess();
#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;
}
void begin() {
void _begin() {
#if defined(MY_DISABLED_SERIAL)
hwInit();
#endif
@@ -165,7 +166,7 @@ ControllerConfig getConfig() {
}
boolean sendRoute(MyMessage &message) {
boolean _sendRoute(MyMessage &message) {
#if defined(MY_GATEWAY_FEATURE)
if (message.destination == _nc.nodeId) {
// 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;
mSetCommand(message,C_SET);
mSetRequestAck(message,enableAck);
return sendRoute(message);
return _sendRoute(message);
}
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) {
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) {
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) {
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) {
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) {
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)) {
_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
void processInternalMessages() {
void _processInternalMessages() {
bool isMetric;
uint8_t type = _msg.type;
@@ -250,7 +251,7 @@ void processInternalMessages() {
hwWriteConfig(EEPROM_DISTANCE_ADDRESS, 0xFF);
// Find parent node
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
}
@@ -266,7 +267,7 @@ uint8_t loadState(uint8_t pos) {
void wait(unsigned long ms) {
unsigned long enter = hwMillis();
while (hwMillis() - enter < ms) {
process();
_process();
}
}
@@ -274,7 +275,8 @@ void sleep(unsigned long ms) {
#if defined(MY_OTA_FIRMWARE_FEATURE)
if (_fwUpdateOngoing) {
// Do not sleep node while fw update is ongoing
process();
_process();
return;
} else {
#endif
#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 (_fwUpdateOngoing) {
// Do not sleep node while fw update is ongoing
process();
return false;
} else {
#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 (_fwUpdateOngoing) {
// Do not sleep node while fw update is ongoing
process();
return -1;
} else {
#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.
*/
void dataCallback(void (* msgCallback)(const MyMessage &)=NULL);
void setIncomingCallback(void (* msgCallback)(const MyMessage &)=NULL);
/**
* Return this nodes id.
@@ -175,11 +175,6 @@ void saveState(uint8_t pos, uint8_t value);
*/
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 ********/
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 MyMessage _msg; // Buffer for incoming messages.
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.
#ifdef MY_DEBUG
extern char convBuf[MAX_PAYLOAD*2+1];
extern char _convBuf[MAX_PAYLOAD*2+1];
#endif
// Inline function and macros
-8
View File
@@ -23,11 +23,3 @@
uint16_t _doSign[16]; // Bitfield indicating which sensors require signed communication
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 "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
#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)))
#define NUM_OF(x) (sizeof(x)/sizeof(x[0]))
class MySigning
{
public:
// MySigning constructor.
// Different parameters would be needed depending on signing backend (e.g. pins connected etc.).
// Keeping these parameters as #define's in MyConfig to streamline the driver interface.
// @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);
// 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.
// Returns false if subsystem is busy processing an ongoing signing operation.
// If successful, this marks the start of a signing operation at the receiving side so
// implementation is expected to do any necessary initializations within this call.
bool signerGetNonce(MyMessage &msg);
// Returns the current requestSignature setting
bool requestSignatures();
// Check timeout of verification session.
// 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.
// All space in message payload buffer is used for signing identifier and nonce.
// Returns false if subsystem is busy processing an ongoing signing operation.
// If successful, this marks the start of a signing operation at the receiving side so
// implementation is expected to do any necessary initializations within this call.
virtual bool getNonce(MyMessage &msg) = 0;
// Get nonce from provided message and store for signing operations.
// 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 sending side so
// implementation is expected to do any necessary initializations within this call.
bool signerPutNonce(MyMessage &msg);
// Check timeout of verification session.
// 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.
virtual bool checkTimer(void) = 0;
// Signs provided message. All remaining space in message payload buffer is used for signing
// 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.
bool signerSignMsg(MyMessage &msg);
// Get nonce from provided message and store for signing operations.
// 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 sending side so
// implementation is expected to do any necessary initializations within this call.
virtual bool putNonce(MyMessage &msg) = 0;
// Signs provided message. All remaining space in message payload buffer is used for signing
// 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;
};
// 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.
bool signerVerifyMsg(MyMessage &msg);
#endif
+24 -5
View File
@@ -34,6 +34,25 @@
// Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected)
//#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
#define DEBUG_SIGNING_PRINTLN(args) Serial.println(args)
#else
@@ -60,14 +79,14 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf,
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,
#endif
uint8_t atshaPin)
:
MySigning(requestSignatures),
atsha204(atshaPin),
#ifdef MY_SECURE_NODE_WHITELISTING
#ifdef MY_SIGNING_NODE_WHITELISTING
whitelist(the_whitelist),
whitlist_sz(nof_whitelist_entries),
#endif
@@ -142,7 +161,7 @@ bool MySigningAtsha204::signMsg(MyMessage &msg) {
mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated
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
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;
@@ -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
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
for (int j=0; j < whitlist_sz; j++) {
if (whitelist[j].nodeId == msg.sender) {
@@ -200,7 +219,7 @@ bool MySigningAtsha204::verifyMsg(MyMessage &msg) {
// Compare the caluclated signature with the provided signature
if (memcmp(&msg.data[mGetLength(msg)], &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg))) {
DEBUG_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?
#endif
return false;
+2 -18
View File
@@ -33,7 +33,7 @@
#include "drivers/ATSHA204/ATSHA204.h"
#include <stdint.h>
#ifdef MY_SECURE_NODE_WHITELISTING
#ifdef MY_SIGNING_NODE_WHITELISTING
typedef struct {
uint8_t nodeId;
uint8_t serial[SHA204_SERIAL_SZ];
@@ -47,29 +47,13 @@ class MySigningAtsha204 : public MySigning
{
public:
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);
bool signMsg(MyMessage &msg);
bool verifyMsg(MyMessage &msg);
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
@@ -35,6 +35,20 @@
#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)
//#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)
#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,
#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) {
bool signerGetNonce(MyMessage &msg) {
// 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
// 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++) {
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
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
current_nonce[0] = SIGNING_IDENTIFIER;
_signing_current_nonce[0] = SIGNING_IDENTIFIER;
// Transfer the first part of the nonce to the message
msg.set(current_nonce, MAX_PAYLOAD);
verification_ongoing = true;
timestamp = millis(); // Set timestamp to determine when to purge nonce
msg.set(_signing_current_nonce, MAX_PAYLOAD);
_signing_verification_ongoing = true;
_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)
// 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'
if (timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) timestamp = 0;
if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) _signing_timestamp = 0;
return true;
}
bool MySigningAtsha204Soft::checkTimer() {
if (verification_ongoing) {
if (millis() < timestamp || millis() > timestamp + MY_VERIFICATION_TIMEOUT_MS) {
bool signerCheckTimer() {
if (_signing_verification_ongoing) {
if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
DEBUG_SIGNING_PRINTLN(F("VT")); // VT = Verification timeout
// Purge nonce
memset(current_nonce, 0xAA, 32);
verification_ongoing = false;
memset(_signing_current_nonce, 0xAA, 32);
_signing_verification_ongoing = false;
return false;
}
}
return true;
}
bool MySigningAtsha204Soft::putNonce(MyMessage &msg) {
bool signerPutNonce(MyMessage &msg) {
if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) {
DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier
return false;
}
memcpy(current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
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 (mGetLength(msg) > MAX_PAYLOAD-2) {
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
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
Sha256.init();
for (int i=0; i<32; i++) Sha256.write(hmac[i]);
Sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) Sha256.write(node_serial_info[i]);
memcpy(hmac, Sha256.result(), 32);
_signing_sha256.init();
for (int i=0; i<32; i++) _signing_sha256.write(_signing_hmac[i]);
_signing_sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) _signing_sha256.write(_signing_node_serial_info[i]);
memcpy(_signing_hmac, _signing_sha256.result(), 32);
DEBUG_SIGNING_PRINTLN(F("SWS")); // SWS = Signature whitelist salted
#endif
// 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
memcpy(&msg.data[mGetLength(msg)], hmac, MAX_PAYLOAD-mGetLength(msg));
memcpy(&msg.data[mGetLength(msg)], _signing_hmac, MAX_PAYLOAD-mGetLength(msg));
return true;
}
bool MySigningAtsha204Soft::verifyMsg(MyMessage &msg) {
if (!verification_ongoing) {
bool signerVerifyMsg(MyMessage &msg) {
if (!_signing_verification_ongoing) {
DEBUG_SIGNING_PRINTLN(F("NAVS")); // NAVS = No active verification session
return false;
} else {
// Make sure we have not expired
if (!checkTimer()) {
if (!signerCheckTimer()) {
return false;
}
verification_ongoing = false;
_signing_verification_ongoing = false;
if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier
@@ -186,30 +179,30 @@ bool MySigningAtsha204Soft::verifyMsg(MyMessage &msg) {
// Get signature of 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
for (int j=0; j < whitlist_sz; j++) {
if (whitelist[j].nodeId == msg.sender) {
for (int j=0; j < NUM_OF(_signing_whitelist); j++) {
if (_signing_whitelist[j].nodeId == msg.sender) {
DEBUG_SIGNING_PRINTLN(F("SIW")); // SIW = Sender found in whitelist
Sha256.init();
for (int i=0; i<32; i++) Sha256.write(hmac[i]);
Sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) Sha256.write(whitelist[j].serial[i]);
memcpy(hmac, Sha256.result(), 32);
_signing_sha256.init();
for (int i=0; i<32; i++) _signing_sha256.write(_signing_hmac[i]);
_signing_sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) _signing_sha256.write(_signing_whitelist[j].serial[i]);
memcpy(_signing_hmac, _signing_sha256.result(), 32);
break;
}
}
#endif
// 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
if (memcmp(&msg.data[mGetLength(msg)], hmac, MAX_PAYLOAD-mGetLength(msg))) {
DEBUG_SIGNING_PRINTBUF(F("SNOK:"), hmac, MAX_PAYLOAD-mGetLength(msg)); // SNOK = Signature bad
#ifdef MY_SECURE_NODE_WHITELISTING
if (memcmp(&msg.data[mGetLength(msg)], _signing_hmac, MAX_PAYLOAD-mGetLength(msg))) {
DEBUG_SIGNING_PRINTBUF(F("SNOK:"), _signing_hmac, MAX_PAYLOAD-mGetLength(msg)); // SNOK = Signature bad
#ifdef MY_SIGNING_NODE_WHITELISTING
DEBUG_SIGNING_PRINTLN(F("W?")); // W? = Is the sender whitelisted?
#endif
return false;
@@ -221,11 +214,11 @@ bool MySigningAtsha204Soft::verifyMsg(MyMessage &msg) {
}
// Helper to calculate signature of msg (returned in hmac)
void MySigningAtsha204Soft::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)));
void signerCalculateSignature(MyMessage &msg) {
memset(_signing_temp_message, 0, 32);
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("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:
// 32 bytes zeroes
@@ -250,37 +243,37 @@ void MySigningAtsha204Soft::calculateSignature(MyMessage &msg) {
// 32 bytes nonce
// Calculate message digest first
Sha256.init();
for (int i=0; i<32; i++) Sha256.write(temp_message[i]);
Sha256.write(0x15); // OPCODE
Sha256.write(0x02); // param1
Sha256.write(0x08); // param2(1)
Sha256.write(0x00); // param2(2)
Sha256.write(0xEE); // SN[8]
Sha256.write(0x01); // SN[0]
Sha256.write(0x23); // SN[1]
for (int i=0; i<25; i++) Sha256.write(0x00);
for (int i=0; i<32; i++) Sha256.write(current_nonce[i]);
_signing_sha256.init();
for (int i=0; i<32; i++) _signing_sha256.write(_signing_temp_message[i]);
_signing_sha256.write(0x15); // OPCODE
_signing_sha256.write(0x02); // param1
_signing_sha256.write(0x08); // param2(1)
_signing_sha256.write(0x00); // param2(2)
_signing_sha256.write(0xEE); // SN[8]
_signing_sha256.write(0x01); // SN[0]
_signing_sha256.write(0x23); // SN[1]
for (int i=0; i<25; i++) _signing_sha256.write(0x00);
for (int i=0; i<32; i++) _signing_sha256.write(_signing_current_nonce[i]);
// Purge nonce when used
memset(current_nonce, 0xAA, 32);
memcpy(temp_message, Sha256.result(), 32);
memset(_signing_current_nonce, 0xAA, 32);
memcpy(_signing_temp_message, _signing_sha256.result(), 32);
// Feed "message" to HMAC calculator
Sha256.initHmac(hmacKey,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++) Sha256.write(temp_message[i]); // 32 bytes digest
Sha256.write(0x11); // OPCODE
Sha256.write(0x04); // Mode
Sha256.write(0x00); // SlotID(1)
Sha256.write(0x00); // SlotID(2)
for (int i=0; i<11; i++) Sha256.write(0x00); // 11 bytes zeroes
Sha256.write(0xEE); // SN[8]
for (int i=0; i<4; i++) Sha256.write(0x00); // 4 bytes zeroes
Sha256.write(0x01); // SN[0]
Sha256.write(0x23); // SN[1]
for (int i=0; i<2; i++) Sha256.write(0x00); // 2 bytes zeroes
_signing_sha256.initHmac(_signing_hmac_key,32); // Set the key to use
for (int i=0; i<32; i++) _signing_sha256.write(0x00); // 32 bytes zeroes
for (int i=0; i<32; i++) _signing_sha256.write(_signing_temp_message[i]); // 32 bytes digest
_signing_sha256.write(0x11); // OPCODE
_signing_sha256.write(0x04); // Mode
_signing_sha256.write(0x00); // SlotID(1)
_signing_sha256.write(0x00); // SlotID(2)
for (int i=0; i<11; i++) _signing_sha256.write(0x00); // 11 bytes zeroes
_signing_sha256.write(0xEE); // SN[8]
for (int i=0; i<4; i++) _signing_sha256.write(0x00); // 4 bytes zeroes
_signing_sha256.write(0x01); // SN[0]
_signing_sha256.write(0x23); // SN[1]
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 <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.
// It is designed to work fully compliant with nodes using ATSHA204 in the network
// 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 sampled from an analog pin. This pin should unconnected in the hardware.
// The pin is selected using MY_RANDOMSEED_PIN in MyConfig.h.
class MySigningAtsha204Soft : public MySigning
{
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
};
// The pin is selected using MY_SIGNING_SOFT_RANDOMSEED_PIN in MyConfig.h.
void signerCalculateSignature(MyMessage &msg);
#endif
+141 -129
View File
@@ -23,6 +23,9 @@
bool _autoFindParent;
uint8_t _failedTransmissions;
#ifdef MY_SIGNING_FEATURE
bool _signingNonceStatus;
#endif
#ifdef MY_OTA_FIRMWARE_FEATURE
NodeFirmwareConfig _fc;
@@ -60,14 +63,14 @@ inline void transportProcess() {
firmwareRequest->type = _fc.type;
firmwareRequest->version = _fc.version;
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
return;
}
#ifdef MY_SIGNING_FEATURE
(void)signer.checkTimer(); // Manage signing timeout
(void)signerCheckTimer(); // Manage signing timeout
#endif
uint8_t len = transportReceive((uint8_t *)&_msg);
@@ -77,29 +80,31 @@ inline void transportProcess() {
#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) &&
(mGetCommand(_msg) != C_INTERNAL ||
(_msg.type != I_GET_NONCE_RESPONSE && _msg.type != I_GET_NONCE && _msg.type != I_REQUEST_SIGNING &&
_msg.type != I_ID_REQUEST && _msg.type != I_ID_RESPONSE &&
_msg.type != I_FIND_PARENT && _msg.type != I_FIND_PARENT_RESPONSE))) {
if (!mGetSigned(_msg)) {
// Got unsigned message that should have been signed
debug(PSTR("no sign\n"));
ledBlinkErr(1);
return;
#ifdef MY_SIGNING_REQUEST_SIGNATURES
if (_msg.destination == _nc.nodeId && mGetLength(_msg) && !mGetAck(_msg) &&
(mGetCommand(_msg) != C_INTERNAL ||
(_msg.type != I_GET_NONCE_RESPONSE && _msg.type != I_GET_NONCE && _msg.type != I_REQUEST_SIGNING &&
_msg.type != I_ID_REQUEST && _msg.type != I_ID_RESPONSE &&
_msg.type != I_FIND_PARENT && _msg.type != I_FIND_PARENT_RESPONSE))) {
if (!mGetSigned(_msg)) {
// Got unsigned message that should have been signed
debug(PSTR("no sign\n"));
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)) {
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.
_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"),
_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
if(!(mGetVersion(_msg) == PROTOCOL_VERSION)) {
@@ -132,7 +137,7 @@ inline void transportProcess() {
mSetAck(_msgTmp,true);
_msgTmp.sender = _nc.nodeId;
_msgTmp.destination = _msg.sender;
sendRoute(_msgTmp);
_sendRoute(_msgTmp);
}
if (command == C_INTERNAL) {
@@ -158,10 +163,10 @@ inline void transportProcess() {
return;
#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));
if (signerGetNonce(_msg)) {
_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) {
if (_msg.getBool()) {
// 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
// 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
if (MY_GATEWAY_FEATURE) {
if (signer.requestSignatures() && DO_SIGN(_msg.sender))
sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true));
else
sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false));
}
#if defined (MY_GATEWAY_FEATURE)
#ifdef MY_SIGNING_REQUEST_SIGNATURES
if (DO_SIGN(_msg.sender))
_sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true));
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
} 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)
#endif
} else if (sender == GATEWAY_ADDRESS) {
@@ -199,7 +212,7 @@ inline void transportProcess() {
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, _nc.nodeId);
debug(PSTR("id=%d\n"), _nc.nodeId);
} else {
processInternalMessages();
_processInternalMessages();
}
return;
}
@@ -292,7 +305,7 @@ inline void transportProcess() {
}
} else if (to == _nc.nodeId) {
// We should try to relay this message to another node
sendRoute(_msg);
_sendRoute(_msg);
}
}
#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"),
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;
}
@@ -356,103 +369,101 @@ boolean transportSendRoute(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(message) &&
(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))) {
bool signOk = false;
// Send nonce-request
if (!sendRoute(build(_msgTmp, _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 = 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;
}
// 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) &&
(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))) {
bool signing = SIGN_WAITING_FOR_NONCE;
// Send nonce-request
if (!_sendRoute(build(_msgTmp, _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 = 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
if (dest == GATEWAY_ADDRESS || !MY_IS_REPEATER) {
// Store this address in routing table (if repeater)
#if defined (MY_IS_REPEATER)
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
#if !defined(MY_REPEATER_FEATURE)
// None repeating node... We can only send to our parent
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);
} 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
#else
if (dest == GATEWAY_ADDRESS) {
// Store this address in routing table (if repeater)
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) {
// Failure when sending to parent node. The parent node might be down and we
// need to find another route to gateway.
@@ -484,12 +495,13 @@ void transportSetupNode() {
if (_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()));
// If we do require signing, wait for the gateway to tell us how it prefer us to transmit our messages
if (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
wait(2000);
}
#else
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false));
#endif
#endif
// Send presentation for this radio node
@@ -501,7 +513,7 @@ void transportSetupNode() {
// Send a configuration exchange request to controller
// Node sends parent node. Controller answers with latest node configuration
// 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(2000);
@@ -516,7 +528,7 @@ void transportSetupNode() {
// add bootloader information
reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION;
_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
+3
View File
@@ -41,6 +41,9 @@
// Search for a new parent node after this many transmission failures
#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
typedef struct {
@@ -29,7 +29,9 @@
//#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_OTA_FIRMWARE_FEATURE
@@ -25,79 +25,62 @@
* Example sketch showing how to securely control locks.
* 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 <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 NOF_LOCKS 1 // Total number of attached locks
#define LOCK_LOCK 1 // GPIO value to write to lock 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()
{
// 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
gw.sendSketchInfo("Secure Lock", "1.0");
sendSketchInfo("Secure Lock", "1.0");
// Fetch lock status
for (int lock=1, pin=LOCK_1; lock<=NOF_LOCKS;lock++, pin++) {
// 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
pinMode(pin, OUTPUT);
// 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()
{
// Alway process incoming messages whenever possible
gw.process();
}
void incomingMessage(const MyMessage &message) {
@@ -106,7 +89,7 @@ void incomingMessage(const MyMessage &message) {
// Change relay state
digitalWrite(message.sensor-1+LOCK_1, message.getBool()?LOCK_LOCK:LOCK_UNLOCK);
// Store state in eeprom
gw.saveState(message.sensor, message.getBool());
saveState(message.sensor, message.getBool());
// Write some debug info
Serial.print("Incoming change for lock:");
Serial.print(message.sensor);