Fixed: Software signing
This commit is contained in:
@@ -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
|
||||
|
||||
/**********************************
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(¤t_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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user