Merge branch 'development' into patch-2

This commit is contained in:
Kolbi
2016-01-13 08:44:47 +01:00
26 changed files with 1035 additions and 426 deletions
+12 -30
View File
@@ -35,13 +35,22 @@
// final sketch but is helpful to see what is actually is happening during development // final sketch but is helpful to see what is actually is happening during development
//#define MY_DEBUG //#define MY_DEBUG
// Enable MY_DEBUG_VERBOSE flag for verbose debug prints. Requires DEBUG to be enabled. // Enable MY_DEBUG_VERBOSE flag for verbose debug prints related to RF24 radio.
// Requires DEBUG to be enabled.
// This will add even more to the size of the final sketch! // This will add even more to the size of the final sketch!
//#define MY_DEBUG_VERBOSE //#define MY_DEBUG_VERBOSE
// Enable MY_DEBUG_VERBOSE_SIGNING flag for verbose debug prints related to signing.
// Requires DEBUG to be enabled.
// This will add even more to the size of the final sketch!
//#define MY_DEBUG_VERBOSE_SIGNING
// Enable this in sketch if you want to use TX(1), RX(0) as normal I/O pin // Enable this in sketch if you want to use TX(1), RX(0) as normal I/O pin
//#define MY_DISABLED_SERIAL //#define MY_DISABLED_SERIAL
// Enable MY_CORE_ONLY flag if you want to use core functions without loading the framework
//#define MY_CORE_ONLY
// Turn off debug if serial pins is used for other stuff // Turn off debug if serial pins is used for other stuff
#ifdef MY_DISABLED_SERIAL #ifdef MY_DISABLED_SERIAL
#undef MY_DEBUG #undef MY_DEBUG
@@ -280,25 +289,6 @@
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
#endif #endif
/**
* @def MY_SIGNING_SOFT_SERIAL
* @brief Serial number for SW signing enabled node
*
* Set the soft_serial value to an arbitrary value for proper security.
* It should be random and unique for each node.
*/
#ifndef MY_SIGNING_SOFT_SERIAL
#define MY_SIGNING_SOFT_SERIAL 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09
#endif
/**
* @def MY_SIGNING_SOFT_HMAC_KEY
* @brief Key to use for HMAC calculation in MySigningAtsha204Soft (32 bytes)
*/
#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
/********************************** /**********************************
* RS485 Driver Defaults * RS485 Driver Defaults
***********************************/ ***********************************/
@@ -315,14 +305,9 @@
* NRF24L01 Driver Defaults * NRF24L01 Driver Defaults
***********************************/ ***********************************/
// Enables RF24 encryption (all nodes and gateway must have this enabled) // Enables RF24 encryption (all nodes and gateway must have this enabled, and all must be personalized with the same AES key)
//#define MY_RF24_ENABLE_ENCRYPTION //#define MY_RF24_ENABLE_ENCRYPTION
// Default encryption key. Override in sketch if needed.
#ifndef MY_RF24_ENCRYPTKEY
#define MY_RF24_ENCRYPTKEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x11,0x12,0x13,0x14,0x15,0x16
#endif
// Default pin settings. Override in sketch if needed. // Default pin settings. Override in sketch if needed.
#ifndef MY_RF24_CE_PIN #ifndef MY_RF24_CE_PIN
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
@@ -409,11 +394,8 @@
#endif #endif
#endif #endif
// Enable this for encryption of packets // Enables RFM69 encryption (all nodes and gateway must have this enabled, and all must be personalized with the same AES key)
//#define MY_RFM69_ENABLE_ENCRYPTION //#define MY_RFM69_ENABLE_ENCRYPTION
#ifndef MY_RFM69_ENCRYPTKEY
#define MY_RFM69_ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
#endif
/************************************** /**************************************
* Ethernet Gateway Transport Defaults * Ethernet Gateway Transport Defaults
+19 -13
View File
@@ -48,8 +48,10 @@
//#undef F //#undef F
//#define F(x) (x) //#define F(x) (x)
#include "core/MyHwESP8266.cpp" #include "core/MyHwESP8266.cpp"
// For ESP8266, we always enable gateway feature // Enable gateway feature
#define MY_GATEWAY_ESP8266 #if !defined(MY_CORE_ONLY)
#define MY_GATEWAY_ESP8266
#endif
#elif defined(ARDUINO_ARCH_AVR) #elif defined(ARDUINO_ARCH_AVR)
#include "core/MyHwATMega328.cpp" #include "core/MyHwATMega328.cpp"
#elif defined(ARDUINO_ARCH_SAMD) #elif defined(ARDUINO_ARCH_SAMD)
@@ -93,9 +95,12 @@
// SIGNING // SIGNING
#if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT) #if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT)
// SIGNING COMMON FUNCTIONS
#include "core/MySigning.cpp"
#define MY_SIGNING_FEATURE #define MY_SIGNING_FEATURE
#endif
#include "core/MySigning.cpp"
#include "drivers/ATSHA204/sha256.cpp"
#if defined(MY_SIGNING_FEATURE)
// SIGNING COMMON FUNCTIONS
#if defined(MY_SIGNING_ATSHA204) && defined(MY_SIGNING_SOFT) #if defined(MY_SIGNING_ATSHA204) && defined(MY_SIGNING_SOFT)
#error Only one signing engine can be activated #error Only one signing engine can be activated
#endif #endif
@@ -105,7 +110,6 @@
#include "drivers/ATSHA204/ATSHA204.cpp" #include "drivers/ATSHA204/ATSHA204.cpp"
#elif defined(MY_SIGNING_SOFT) #elif defined(MY_SIGNING_SOFT)
#include "core/MySigningAtsha204Soft.cpp" #include "core/MySigningAtsha204Soft.cpp"
#include "drivers/ATSHA204/sha256.cpp"
#endif #endif
#endif #endif
@@ -219,22 +223,24 @@
#undef MY_INCLUSION_BUTTON_FEATURE #undef MY_INCLUSION_BUTTON_FEATURE
#endif #endif
#if !defined(MY_GATEWAY_FEATURE) && !defined(MY_RADIO_FEATURE) #if !defined(MY_CORE_ONLY)
#error No forward link or gateway feature activated. This means nowhere to send messages! Pretty pointless. #if !defined(MY_GATEWAY_FEATURE) && !defined(MY_RADIO_FEATURE)
#error No forward link or gateway feature activated. This means nowhere to send messages! Pretty pointless.
#endif
#endif #endif
#include "core/MyCapabilities.h" #include "core/MyCapabilities.h"
#include "core/MyMessage.cpp" #include "core/MyMessage.cpp"
#include "core/MySensorCore.cpp" #include "core/MySensorCore.cpp"
#include <Arduino.h> #include <Arduino.h>
#if defined(MY_GATEWAY_ESP8266) #if !defined(MY_CORE_ONLY)
#include "core/MyMainESP8266.cpp" #if defined(MY_GATEWAY_ESP8266)
#else #include "core/MyMainESP8266.cpp"
#include "core/MyMainDefault.cpp" #else
#include "core/MyMainDefault.cpp"
#endif
#endif #endif
#endif #endif
@@ -0,0 +1,43 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#ifndef MyEepromAddresses_h
#define MyEepromAddresses_h
// EEPROM start address for mysensors library data
#define EEPROM_START 0
// EEPROM location of node id
#define EEPROM_NODE_ID_ADDRESS EEPROM_START
// EEPROM location of parent id
#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1)
// EEPROM location of distance to gateway
#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1)
#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes.
#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller)
#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24)
#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS+2)
#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS+2)
#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS+2)
#define EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2)
#define EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS+32)
#define EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS (EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS+32) // This is set with SecurityPersonalizer.ino
#define EEPROM_SIGNING_SOFT_SERIAL_ADDRESS (EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS+32) // This is set with SecurityPersonalizer.ino
#define EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS (EEPROM_SIGNING_SOFT_SERIAL_ADDRESS+9) // This is set with SecurityPersonalizer.ino
#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS+16) // First free address for sketch static configuration
#endif
+1 -1
View File
@@ -18,7 +18,7 @@
*/ */
#include "MyHwESP8266.h" #include "MyHwESP8266.h"
#include <EEPROM.h>
/* /*
int8_t pinIntTrigger = 0; int8_t pinIntTrigger = 0;
+3 -3
View File
@@ -163,9 +163,9 @@ typedef enum {
I_INCLUSION_MODE, I_CONFIG, I_FIND_PARENT, I_FIND_PARENT_RESPONSE, I_INCLUSION_MODE, I_CONFIG, I_FIND_PARENT, I_FIND_PARENT_RESPONSE,
I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION, I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION,
I_REBOOT, I_GATEWAY_READY, I_REBOOT, I_GATEWAY_READY,
I_REQUEST_SIGNING, /*!< Indicates sender require signed messages (bool param) */ I_SIGNING_PRESENTATION, /*!< Provides signing related preferences (first byte is preference version) */
I_GET_NONCE, /*!< Request for a nonce */ I_NONCE_REQUEST, /*!< Request for a nonce */
I_GET_NONCE_RESPONSE, /*!< Payload is nonce data */ I_NONCE_RESPONSE, /*!< Payload is nonce data */
I_HEARTBEAT, I_PRESENTATION, I_DISCOVER, I_DISCOVER_RESPONSE, I_HEARTBEAT_RESPONSE I_HEARTBEAT, I_PRESENTATION, I_DISCOVER, I_DISCOVER_RESPONSE, I_HEARTBEAT_RESPONSE
} mysensor_internal; } mysensor_internal;
@@ -40,12 +40,6 @@
// Pin used for random generation in soft signing (do not connect anything to this when enabled) // Pin used for random generation in soft signing (do not connect anything to this when enabled)
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 // A7 - #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 // A7 -
// Set the soft_serial value to an arbitrary value for proper security
#define MY_SIGNING_SOFT_SERIAL 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09
// Key to use for HMAC calculation in MySigningAtsha204Soft (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
// Enable node whitelisting // Enable node whitelisting
//#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}} //#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}}
@@ -55,9 +49,6 @@
// Enables RF24 encryption (all nodes and gateway must have this enabled) // Enables RF24 encryption (all nodes and gateway must have this enabled)
//#define MY_RF24_ENABLE_ENCRYPTION //#define MY_RF24_ENABLE_ENCRYPTION
// Default encryption key
#define MY_RF24_ENCRYPTKEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x11,0x12,0x13,0x14,0x15,0x16
/********************************** /**********************************
* RFM69 Driver Defaults * RFM69 Driver Defaults
***********************************/ ***********************************/
@@ -66,6 +57,5 @@
// Enable this for encryption of packets // Enable this for encryption of packets
//#define MY_RFM69_ENABLE_ENCRYPTION //#define MY_RFM69_ENABLE_ENCRYPTION
#define MY_RFM69_ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
#endif #endif
+5 -10
View File
@@ -62,15 +62,12 @@ static inline bool isValidParent( const uint8_t parent ) {
void _begin() { void _begin() {
#if !defined(MY_DISABLED_SERIAL) #if !defined(MY_DISABLED_SERIAL)
hwInit(); hwInit();
#endif
#if defined(MY_SIGNING_SOFT)
// initialize pseudo-RNG
randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN));
#endif #endif
debug(PSTR("Starting " MY_NODE_TYPE " (" MY_CAPABILIIES ", " LIBRARY_VERSION ")\n")); debug(PSTR("Starting " MY_NODE_TYPE " (" MY_CAPABILIIES ", " LIBRARY_VERSION ")\n"));
signerInit();
#if defined(MY_RADIO_FEATURE) #if defined(MY_RADIO_FEATURE)
_failedTransmissions = 0; _failedTransmissions = 0;
@@ -107,11 +104,6 @@ void _begin() {
#endif #endif
#if defined(MY_SIGNING_FEATURE)
// Read out the signing requirements from EEPROM
hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign));
#endif
#if defined(MY_LEDS_BLINKING_FEATURE) #if defined(MY_LEDS_BLINKING_FEATURE)
ledsInit(); ledsInit();
#endif #endif
@@ -190,6 +182,9 @@ ControllerConfig getConfig() {
boolean _sendRoute(MyMessage &message) { boolean _sendRoute(MyMessage &message) {
// increment heartbeat counter // increment heartbeat counter
_heartbeat++; _heartbeat++;
#if defined(MY_CORE_ONLY)
(void)message;
#endif
#if defined(MY_GATEWAY_FEATURE) #if defined(MY_GATEWAY_FEATURE)
if (message.destination == _nc.nodeId) { if (message.destination == _nc.nodeId) {
// This is a message sent from a sensor attached on the gateway node. // This is a message sent from a sensor attached on the gateway node.
+1 -17
View File
@@ -23,6 +23,7 @@
#include "Version.h" // Auto generated by bot #include "Version.h" // Auto generated by bot
#include "MyConfig.h" #include "MyConfig.h"
#include "MyEepromAddresses.h"
#include "MyMessage.h" #include "MyMessage.h"
#ifdef MY_OTA_FIRMWARE_FEATURE #ifdef MY_OTA_FIRMWARE_FEATURE
#include "drivers/SPIFlash/SPIFlash.h" #include "drivers/SPIFlash/SPIFlash.h"
@@ -37,23 +38,6 @@
#define debug(x,...) #define debug(x,...)
#endif #endif
// EEPROM start address for mysensors library data
#define EEPROM_START 0
// EEPROM location of node id
#define EEPROM_NODE_ID_ADDRESS EEPROM_START
// EEPROM location of parent id
#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1)
// EEPROM location of distance to gateway
#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1)
#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes.
#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller)
#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24)
#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS+2)
#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS+2)
#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS+2)
#define EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2)
#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS+32) // First free address for sketch static configuration
// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data). // This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data).
#define GATEWAY_ADDRESS ((uint8_t)0) #define GATEWAY_ADDRESS ((uint8_t)0)
// Node child is always created/presented when a node is started // Node child is always created/presented when a node is started
+357 -2
View File
@@ -18,8 +18,363 @@
*/ */
#include "MySigning.h" #include "MySigning.h"
#include "drivers/ATSHA204/sha256.h"
#define SIGNING_PRESENTATION_VERSION_1 1
#define SIGNING_PRESENTATION_REQUIRE_SIGNATURES (1 << 0)
#define SIGNING_PRESENTATION_REQUIRE_WHITELISTING (1 << 1)
uint8_t _doSign[32]; // Bitfield indicating which sensors require signed communication #if defined(MY_DEBUG_VERBOSE_SIGNING)
MyMessage _msgSign; // Buffer for message to sign. #define SIGN_DEBUG(x,...) debug(x, ##__VA_ARGS__)
#else
#define SIGN_DEBUG(x,...)
#endif
#if defined(MY_SIGNING_REQUEST_SIGNATURES) && (!defined(MY_SIGNING_ATSHA204) && !defined(MY_SIGNING_SOFT))
#error You have to pick either MY_SIGNING_ATSHA204 or MY_SIGNING_SOFT in order to require signatures!
#endif
#ifdef MY_SIGNING_FEATURE
uint8_t _doSign[32]; // Bitfield indicating which sensors require signed communication
uint8_t _doWhitelist[32]; // Bitfield indicating which sensors require serial salted signatures
MyMessage _msgSign; // Buffer for message to sign.
uint8_t _signingNonceStatus;
// Status when waiting for signing nonce in signerProcessInternal
enum { SIGN_WAITING_FOR_NONCE = 0, SIGN_OK = 1 };
// Macros for manipulating signing requirement table
#define DO_SIGN(node) (~_doSign[node>>3]&(1<<node%8))
#define SET_SIGN(node) (_doSign[node>>3]&=~(1<<node%8))
#define CLEAR_SIGN(node) (_doSign[node>>3]|=(1<<node%8))
/*
* 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.
* This function is typically called as action when receiving a I_NONCE_REQUEST message.
*/
#if defined(MY_SIGNING_SOFT)
extern void signerAtsha204SoftInit(void);
extern bool signerAtsha204SoftCheckTimer(void);
extern bool signerAtsha204SoftGetNonce(MyMessage &msg);
extern void signerAtsha204SoftPutNonce(MyMessage &msg);
extern bool signerAtsha204SoftVerifyMsg(MyMessage &msg);
extern bool signerAtsha204SoftSignMsg(MyMessage &msg);
#endif
#if defined(MY_SIGNING_ATSHA204)
extern void signerAtsha204Init(void);
extern bool signerAtsha204CheckTimer(void);
extern bool signerAtsha204GetNonce(MyMessage &msg);
extern void signerAtsha204PutNonce(MyMessage &msg);
extern bool signerAtsha204VerifyMsg(MyMessage &msg);
extern bool signerAtsha204SignMsg(MyMessage &msg);
#endif
// Helper function to centralize signing/verification exceptions
static bool skipSign(MyMessage &msg) {
if mGetAck(msg) {
SIGN_DEBUG(PSTR("Skipping security for ACK on command %d type %d\n"), mGetCommand(msg), msg.type);
return true;
} else if (mGetCommand(msg) == C_INTERNAL &&
(msg.type == I_NONCE_REQUEST || msg.type == I_NONCE_RESPONSE || msg.type == I_SIGNING_PRESENTATION ||
msg.type == I_ID_REQUEST || msg.type == I_ID_RESPONSE ||
msg.type == I_FIND_PARENT || msg.type == I_FIND_PARENT_RESPONSE ||
msg.type == I_HEARTBEAT || msg.type == I_HEARTBEAT_RESPONSE)) {
SIGN_DEBUG(PSTR("Skipping security for command %d type %d\n"), mGetCommand(msg), msg.type);
return true;
} else if (mGetCommand(msg) == C_STREAM &&
(msg.type == ST_FIRMWARE_REQUEST || msg.type == ST_FIRMWARE_RESPONSE ||
msg.type == ST_SOUND || msg.type == ST_IMAGE)) {
SIGN_DEBUG(PSTR("Skipping security for command %d type %d\n"), mGetCommand(msg), msg.type);
return true;
} else {
return false;
}
}
#endif // MY_SIGNING_FEATURE
// Helper to prepare a signing presentation message
static void prepareSigningPresentation(MyMessage &msg, uint8_t destination) {
// Only supports version 1 for now
build(msg, _nc.nodeId, destination, NODE_SENSOR_ID, C_INTERNAL, I_SIGNING_PRESENTATION, false).set("");
mSetLength(msg, 2);
msg.data[0] = SIGNING_PRESENTATION_VERSION_1;
msg.data[1] = 0;
}
void signerInit(void) {
#if defined(MY_SIGNING_FEATURE)
// Read out the signing requirements from EEPROM
hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS,
sizeof(_doSign));
// Read out the whitelist requirements from EEPROM
hwReadConfigBlock((void*)_doWhitelist, (void*)EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS,
sizeof(_doWhitelist));
#if defined(MY_SIGNING_SOFT)
signerAtsha204SoftInit();
#endif
#if defined(MY_SIGNING_ATSHA204)
signerAtsha204Init();
#endif
#endif
}
void signerPresentation(MyMessage &msg) {
prepareSigningPresentation(msg, GATEWAY_ADDRESS);
#if defined(MY_SIGNING_REQUEST_SIGNATURES)
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES;
SIGN_DEBUG(PSTR("Signing required\n"));
#endif
#if defined(MY_SIGNING_NODE_WHITELISTING)
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_WHITELISTING;
SIGN_DEBUG(PSTR("Whitelisting required\n"));
#endif
_sendRoute(msg);
#if defined(MY_SIGNING_FEATURE)
// If we do support signing, wait for the gateway to tell us how it prefer us to transmit our messages
SIGN_DEBUG(PSTR("Waiting for GW to send signing preferences...\n"));
wait(2000);
#endif
}
bool signerProcessInternal(MyMessage &msg) {
if (mGetCommand(msg) == C_INTERNAL) {
#if !defined(MY_SIGNING_FEATURE) && defined(MY_GATEWAY_FEATURE)
// If we act as gateway and do not have the signing feature and receive a signing request we still
// need to do make sure the requester does not believe the gateway still require signatures
if (msg.type == I_SIGNING_PRESENTATION) {
prepareSigningPresentation(msg, msg.sender);
SIGN_DEBUG(PSTR("Informing node %d that we do not require signatures because we do not support it\n"),
msg.sender);
_sendRoute(msg);
return true; // No need to further process I_SIGNING_PRESENTATION in this case
}
#elif defined(MY_SIGNING_FEATURE)
if (msg.type == I_NONCE_REQUEST) {
#if defined(MY_SIGNING_SOFT)
if (signerAtsha204SoftGetNonce(msg)) {
#endif
#if defined(MY_SIGNING_ATSHA204)
if (signerAtsha204GetNonce(msg)) {
#endif
SIGN_DEBUG(PSTR("Transmittng nonce\n"));
_sendRoute(build(msg, _nc.nodeId, msg.sender, NODE_SENSOR_ID,
C_INTERNAL, I_NONCE_RESPONSE, false));
} else {
SIGN_DEBUG(PSTR("Failed to generate nonce!\n"));
}
return true; // No need to further process I_NONCE_REQUEST
} else if (msg.type == I_SIGNING_PRESENTATION) {
if (msg.data[0] != SIGNING_PRESENTATION_VERSION_1) {
SIGN_DEBUG(PSTR("Unsupported signing presentation version (%d)!\n"), msg.data[0]);
return true; // Just drop this presentation message
}
// We only handle version 1 here...
if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_SIGNATURES) {
// We received an indicator that the sender require us to sign all messages we send to it
SIGN_DEBUG(PSTR("Mark node %d as one that require signed messages\n"), msg.sender);
SET_SIGN(msg.sender);
} else {
// We received an indicator that the sender does not require us to sign all messages we send to it
SIGN_DEBUG(PSTR("Mark node %d as one that do not require signed messages\n"), msg.sender);
CLEAR_SIGN(msg.sender);
}
if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_WHITELISTING) {
// We received an indicator that the sender require us to salt signatures with serial
SIGN_DEBUG(PSTR("Mark node %d as one that require whitelisting\n"), msg.sender);
SET_WHITELIST(msg.sender);
} else {
// We received an indicator that the sender does not require us to sign all messages we send to it
SIGN_DEBUG(PSTR("Mark node %d as one that do not require whitelisting\n"), msg.sender);
CLEAR_WHITELIST(msg.sender);
}
// Save updated tables
hwWriteConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS,
sizeof(_doSign));
hwWriteConfigBlock((void*)_doWhitelist, (void*)EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS,
sizeof(_doWhitelist));
// Inform sender about our preference if we are a gateway, but only require signing if the sender
// required signing
// We do not want a gateway to require signing from all nodes in a network just because it wants one node
// to sign it's messages
#if defined(MY_GATEWAY_FEATURE)
prepareSigningPresentation(msg, msg.sender);
#if defined(MY_SIGNING_REQUEST_SIGNATURES)
if (DO_SIGN(msg.sender)) {
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES;
}
#endif
#if defined(MY_SIGNING_NODE_WHITELISTING)
if (DO_WHITELIST(msg.sender)) {
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_WHITELISTING;
}
#endif
if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_SIGNATURES) {
SIGN_DEBUG(PSTR("Informing node %d that we require signatures\n"), msg.sender);
} else {
SIGN_DEBUG(PSTR("Informing node %d that we do not require signatures\n"), msg.sender);
}
if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_WHITELISTING) {
SIGN_DEBUG(PSTR("Informing node %d that we require whitelisting\n"), msg.sender);
} else {
SIGN_DEBUG(PSTR("Informing node %d that we do not require whitelisting\n"), msg.sender);
}
_sendRoute(msg);
#endif // MY_GATEWAY_FEATURE
return true; // No need to further process I_SIGNING_PRESENTATION
} else if (msg.type == I_NONCE_RESPONSE) {
// Proceed with signing if nonce has been received
SIGN_DEBUG(PSTR("Nonce received from %d. Proceeding with signing...\n"), msg.sender);
if (msg.sender != _msgSign.destination) {
SIGN_DEBUG(PSTR("Nonce did not come from the destination (%d) of the message to be signed! "
"It came from %d.\n"), _msgSign.destination, msg.sender);
SIGN_DEBUG(PSTR("Silently discarding this nonce\n"));
return true; // No need to further process I_NONCE_RESPONSE
}
#if defined(MY_SIGNING_SOFT)
signerAtsha204SoftPutNonce(msg);
#endif
#if defined(MY_SIGNING_ATSHA204)
signerAtsha204PutNonce(msg);
#endif
#if defined(MY_SIGNING_SOFT)
if (!signerAtsha204SoftSignMsg(_msgSign)) {
#endif
#if defined(MY_SIGNING_ATSHA204)
if (!signerAtsha204SignMsg(_msgSign)) {
#endif
SIGN_DEBUG(PSTR("Failed to sign message!\n"));
} else {
SIGN_DEBUG(PSTR("Message signed\n"));
_signingNonceStatus = SIGN_OK; // _msgSign now contains the signed message pending transmission
}
return true; // No need to further process I_NONCE_RESPONSE
}
#endif // MY_SIGNING_FEATURE
}
return false;
}
bool signerCheckTimer(void) {
#if defined(MY_SIGNING_SOFT)
return signerAtsha204SoftCheckTimer();
#elif defined(MY_SIGNING_ATSHA204)
return signerAtsha204CheckTimer();
#else
return true; // Without a configured backend, we always give "positive" results
#endif
}
bool signerSignMsg(MyMessage &msg) {
#if defined(MY_SIGNING_FEATURE)
// If destination is known to require signed messages and we are the sender,
// sign this message unless it is a handshake message
if (DO_SIGN(msg.destination) && msg.sender == _nc.nodeId) {
if (skipSign(msg)) {
return true;
} else {
// Send nonce-request
_signingNonceStatus=SIGN_WAITING_FOR_NONCE;
if (!_sendRoute(build(_msgTmp, _nc.nodeId, msg.destination, msg.sensor,
C_INTERNAL, I_NONCE_REQUEST, false).set(""))) {
SIGN_DEBUG(PSTR("Failed to transmit nonce request!\n"));
return false;
}
SIGN_DEBUG(PSTR("Nonce requested from %d. Waiting...\n"), msg.destination);
// We have to wait for the nonce to arrive before we can sign our original message
// Other messages could come in-between. We trust _process() takes care of them
unsigned long enter = hwMillis();
_msgSign = msg; // Copy the message to sign since 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) {
SIGN_DEBUG(PSTR("Timeout waiting for nonce!\n"));
return false;
}
if (_signingNonceStatus == SIGN_OK) {
// process() received a nonce and signerProcessInternal successfully signed the message
msg = _msgSign; // Write the signed message back
SIGN_DEBUG(PSTR("Message to send has been signed\n"));
} else {
SIGN_DEBUG(PSTR("Message to send could not be signed!\n"));
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 if (_nc.nodeId == msg.sender) {
mSetSigned(msg, 0); // Message is not supposed to be signed, make sure it is marked unsigned
}
#else
(void)msg;
#endif // MY_SIGNING_FEATURE
return true;
}
bool signerVerifyMsg(MyMessage &msg) {
bool verificationResult = true;
// 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
#if defined(MY_SIGNING_FEATURE) && defined(MY_SIGNING_REQUEST_SIGNATURES)
// If we are a node, or we are a gateway and the sender require signatures
// and we are the destination...
if ((!MY_IS_GATEWAY || DO_SIGN(msg.sender)) && msg.destination == _nc.nodeId) {
// Internal messages of certain types are not verified
if (skipSign(msg)) {
verificationResult = true;
}
else if (!mGetSigned(msg)) {
// Got unsigned message that should have been signed
SIGN_DEBUG(PSTR("Message is not signed, but it should have been!\n"));
verificationResult = false;
} else {
#if defined(MY_SIGNING_SOFT)
verificationResult = signerAtsha204SoftVerifyMsg(msg);
#endif
#if defined(MY_SIGNING_ATSHA204)
verificationResult = signerAtsha204VerifyMsg(msg);
#endif
if (!verificationResult) {
SIGN_DEBUG(PSTR("Signature verification failed!\n"));
}
mSetSigned(msg,0); // Clear the sign-flag now as verification is completed
}
}
#else
(void)msg;
#endif // MY_SIGNING_REQUEST_SIGNATURES
return verificationResult;
}
static uint8_t sha256_hash[32];
Sha256Class _soft_sha256;
void signerSha256Init(void) {
memset(sha256_hash, 0, 32);
_soft_sha256.init();
}
void signerSha256Update(const uint8_t* data, size_t sz) {
for (size_t i = 0; i < sz; i++) {
_soft_sha256.write(data[i]);
}
}
uint8_t* signerSha256Final(void) {
memcpy(sha256_hash, _soft_sha256.result(), 32);
return sha256_hash;
}
+115 -65
View File
@@ -178,14 +178,10 @@
* signatures. The setting is defined using @ref MY_SIGNING_SOFT_RANDOMSEED_PIN and the default is to use pin A7. The same configuration * signatures. The setting is defined using @ref MY_SIGNING_SOFT_RANDOMSEED_PIN and the default is to use pin A7. The same configuration
* possibilities exist as with the other configuration options. * possibilities exist as with the other configuration options.
* *
* <b>Thirdly</b>, if you use the software backend, you need to create/set a HMAC key to use. This key is 32 bytes wide and should * <b>Thirdly</b>, if you use the software backend, you need to personalize the node (see @ref perzonalization).
* be an arbitrarily chosen value. A string is OK, but as this key never needs to be “parsed” a completely random number is recommended. The
* key is stored in software and can be set "globally" in @ref MyConfig.h or locally in your sketch. The define is @ref MY_SIGNING_SOFT_HMAC_KEY.
* @code{.cpp} * @code{.cpp}
* #define MY_SIGNING_SOFT * #define MY_SIGNING_SOFT
* #define MY_SIGNING_SOFT_SERIAL 0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 * #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
* #define MY_SIGNING_SOFT_HMAC_KEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0,0x11,0x22
* #define MY_SIGNING_REQUEST_SIGNATURES * #define MY_SIGNING_REQUEST_SIGNATURES
* #include <MySensor.h> * #include <MySensor.h>
* ... * ...
@@ -195,7 +191,8 @@
* *
* @anchor perzonalization If you use the “real” ATSHA204A, before any signing operations can be done, the device needs to be <i>personalized</i>. * @anchor perzonalization If you use the “real” ATSHA204A, before any signing operations can be done, the device needs to be <i>personalized</i>.
* This can be a daunting process as it involves irreversibly writing configurations to the device, which cannot be undone. I have however tried * This can be a daunting process as it involves irreversibly writing configurations to the device, which cannot be undone. I have however tried
* to simplify the process as much as possibly by creating a helper-sketch specifically for this purpose in @ref Sha204Personalizer.ino * to simplify the process as much as possibly by creating a helper-sketch specifically for this purpose in @ref SecurityPersonalizer.ino
* Note that you also need to do personalization for software signing, but then the values are stored in EEPROM (described below).
* *
* The process of personalizing the ATSHA204A involves * The process of personalizing the ATSHA204A involves
* - Writing and locking chip configuration * - Writing and locking chip configuration
@@ -211,15 +208,15 @@
* if this is desired.<br> * if this is desired.<br>
* Now it is possible to execute the sketch to lock the configuration and enable the RNG.<br> * Now it is possible to execute the sketch to lock the configuration and enable the RNG.<br>
* Next step is to decide if a new key should be generated or an existing key should be stored to the device. This is determined using * Next step is to decide if a new key should be generated or an existing key should be stored to the device. This is determined using
* @ref USER_KEY_DATA, which, if defined, will use the data in the variable user_key_data.<br> * @ref USER_KEY, which, if defined, will use the data in the variable user_key_data.<br>
* If @ref USER_KEY_DATA is disabled, the RNG will be used to generate a key. This key obviously need to be made available to you so you can use * If @ref USER_KEY is disabled, the RNG will be used to generate a key. This key obviously need to be made available to you so you can use
* it in other devices in the network, and this key is therefore also printed on the serial console when it has been generated.<br> * it in other devices in the network, and this key is therefore also printed on the serial console when it has been generated.<br>
* The key (generated or provided) will be written to the device unless @ref SKIP_KEY_STORAGE is set. As long as the data zone is kept unlocked * The key (generated or provided) will be written to the device unless @ref SKIP_KEY_STORAGE is set. As long as the data zone is kept unlocked
* the key can be replaced at any time. However, Atmel suggests the data region to be locked for maximum security. On the other hand, they also * the key can be replaced at any time. However, Atmel suggests the data region to be locked for maximum security. On the other hand, they also
* claim that the key is not readable from the device even if the data zone remains unlocked so the need for locking the data region is optional * claim that the key is not readable from the device even if the data zone remains unlocked so the need for locking the data region is optional
* for MySensors usage.<br> * for MySensors usage.<br>
* For devices that does not have serial debug possibilities, it is possible to set @ref SKIP_UART_CONFIRMATION, but it is required to set * For devices that does not have serial debug possibilities, it is possible to set @ref SKIP_UART_CONFIRMATION, but it is required to set
* @ref USER_KEY_DATA if this option is enabled since a generated and potentially unknown key could be written to the device and thus rendering * @ref USER_KEY if this option is enabled since a generated and potentially unknown key could be written to the device and thus rendering
* it useless (if the data zone is also locked).<br> * it useless (if the data zone is also locked).<br>
* For devices with serial debug possibilities it is recommended to not use @ref SKIP_UART_CONFIRMATION as the sketch without that setting will * For devices with serial debug possibilities it is recommended to not use @ref SKIP_UART_CONFIRMATION as the sketch without that setting will
* ask user to send a ‘space’ character on the serial terminal before any locking operations are executed as an additional confirmation that * ask user to send a ‘space’ character on the serial terminal before any locking operations are executed as an additional confirmation that
@@ -232,7 +229,7 @@
* -# Disable @ref LOCK_DATA * -# Disable @ref LOCK_DATA
* -# Enable @ref SKIP_KEY_STORAGE * -# Enable @ref SKIP_KEY_STORAGE
* -# Disable @ref SKIP_UART_CONFIRMATION * -# Disable @ref SKIP_UART_CONFIRMATION
* -# Disable @ref USER_KEY_DATA * -# Disable @ref USER_KEY
* - Execute the sketch on the “master” device to obtain a randomized key. Save this key to a secure location and keep it confidential so that\n * - Execute the sketch on the “master” device to obtain a randomized key. Save this key to a secure location and keep it confidential so that\n
you can retrieve it if you need to personalize more devices later on. you can retrieve it if you need to personalize more devices later on.
* - Now reconfigure the sketch with these settings: * - Now reconfigure the sketch with these settings:
@@ -241,19 +238,38 @@
* according to Atmel, but they also claim that key is not readable even if data region remains unlocked from the slot we are using) * according to Atmel, but they also claim that key is not readable even if data region remains unlocked from the slot we are using)
* -# Disable @ref SKIP_KEY_STORAGE * -# Disable @ref SKIP_KEY_STORAGE
* -# Enable @ref SKIP_UART_CONFIRMATION * -# Enable @ref SKIP_UART_CONFIRMATION
* -# Enable @ref USER_KEY_DATA * -# Enable @ref USER_KEY
* - Put the saved key in the user_key_data variable. * - Put the saved key in the user_key_data variable.
* - Now execute the sketch on all devices you want to personalize with this secret key. * - Now execute the sketch on all devices you want to personalize with this secret key.
* *
* For software signing the personalization procedure looks slightly different.<br>
* - If you do not have the ATSHA204A device and need to generate random keys:
* -# Enable @ref USE_SOFT_SIGNING
* - If you do have the ATSHA204A and want to use it to generate random keys for soft signing:
* -# Disable @ref USE_SOFT_SIGNING
* - If you want to review existing EEPROM configuration to determine if anything needs to be updated:
* -# Make sure to disable any ATSHA204A update features if you use it (enable @ref SKIP_KEY_STORAGE, disable @ref LOCK_CONFIGURATION and @ref LOCK_DATA)
* -# Disable @ref STORE_SOFT_KEY
* -# Disable @ref STORE_SOFT_SERIAL
* -# Disable @ref STORE_AES_KEY
* - Execute the sketch and the EEPROM configuration will be printed on the serial console.
* - Decide what you want to update and enable the corresponding flags (@ref STORE_SOFT_KEY, @ref STORE_SOFT_SERIAL, @ref STORE_AES_KEY).
* - If you have existing keys and want to use them instead of having the values randomized:
* - Enable the corresponding flag (@ref USER_SOFT_KEY, @ref USER_SOFT_SERIAL, @ref USER_AES_KEY) and also set the appropriate values
* (@ref user_soft_key_data, @ref user_soft_serial, @ref user_aes_key)
*
* The personalizer also takes care of generation and storage of the AES key used for RF traffic encryption.
* See the software signing personalization flow for how to manage it.
*
* That’s it. Personalization is done and the device is ready to execute signing operations which are valid only on your personal network. * That’s it. Personalization is done and the device is ready to execute signing operations which are valid only on your personal network.
* *
* If a node does require signing, any unsigned message sent to the node will be rejected.<br> * If a node does require signing, any unsigned message sent to the node will be rejected.<br>
* This also applies to the gateway. However, the difference is that the gateway will only require signed messages from nodes it knows in turn * This also applies to the gateway. However, the difference is that the gateway will only require signed messages from nodes it knows in turn
* require signed messages.<br> * require signed messages.<br>
* A node can also inform a different node that it expects to receive signed messages from it. This is done by transmitting an internal message * A node can also inform a different node that it expects to receive signed messages from it. This is done by transmitting an internal message
* of type @ref I_REQUEST_SIGNING and provide a boolean for payload, set to @c true.<br> * of type @ref I_SIGNING_PRESENTATION and provide flags as payload that inform the receiver of the signing preferences of the sender.<br>
* All nodes and gateways in a network maintain a table where the signing preferences of all nodes are stored. This is also stored in EEPROM so * All nodes and gateways in a network maintain a table where the signing preferences of all nodes are stored. This is also stored in EEPROM so
* if the gateway reboots, the nodes does not have to retransmit a signing request to the gateway for the gateway to realize that the node * if the gateway reboots, the nodes does not have to retransmit a signing presentation to the gateway for the gateway to realize that the node
* expect signed messages.<br> * expect signed messages.<br>
* Also, the nodes that do not require signed messages will also inform gateway of this, so if you reprogram a node to stop require signing, * Also, the nodes that do not require signed messages will also inform gateway of this, so if you reprogram a node to stop require signing,
* the gateway will adhere to this as soon as the new node has presented itself to the gateway. * the gateway will adhere to this as soon as the new node has presented itself to the gateway.
@@ -288,13 +304,11 @@
* remove it from the nodes whitelist, and it will no longer be able to communicate signed messages to the door node. * remove it from the nodes whitelist, and it will no longer be able to communicate signed messages to the door node.
* *
* In case you want to be able to "whitelist" trusted nodes (in order to be able to revoke them in case they are lost) you also need to take note * In case you want to be able to "whitelist" trusted nodes (in order to be able to revoke them in case they are lost) you also need to take note
* of the serial number of the ATSHA device. This is unique for each device. The serial number is printed in a copy+paste friendly format by the * of the serial number of the ATSHA device or the software value stored in EEPROM. This is unique for each device. The serial number is printed
* personalizer for this purpose. For the software backend, it is defined by @ref MY_SIGNING_SOFT_SERIAL. You should keep this value unique for each * in a copy+paste friendly format by the personalizer for this purpose.<br>
* node in the network.<br>
* The whitelist is stored on the node that require signatures. When a received message is verified, the serial of the sender is looked up in a * The whitelist is stored on the node that require signatures. When a received message is verified, the serial of the sender is looked up in a
* list stored on the receiving node, and the corresponding serial stored in the list for that sender is then included in the signature verification * list stored on the receiving node, and the corresponding serial stored in the list for that sender is then included in the signature verification
* process. The list is stored as the value of the flag that enables whitelisting, @ref MY_SIGNING_NODE_WHITELISTING.<br> * process. The list is stored as the value of the flag that enables whitelisting, @ref MY_SIGNING_NODE_WHITELISTING.<br>
* For whitelisting to work, the sending node also needs to have whitelisting enabled.<br>
* *
* Whitelisting is achieved by 'salting' the signature with some node-unique information known to the receiver. In the case of ATSHA204A this is the * Whitelisting is achieved by 'salting' the signature with some node-unique information known to the receiver. In the case of ATSHA204A this is the
* unique serial number programmed into the circuit. This unique number is never transmitted over the air in clear text, so Eve will not be able to * unique serial number programmed into the circuit. This unique number is never transmitted over the air in clear text, so Eve will not be able to
@@ -303,8 +317,8 @@
* the originating NodeId of the signed message and do the corresponding calculation with the serial it has stored in it's whitelist if it finds a * the originating NodeId of the signed message and do the corresponding calculation with the serial it has stored in it's whitelist if it finds a
* matching entry in it's whitelist. * matching entry in it's whitelist.
* *
* Whitelisting is an optional alternative because it adds some code which might not be desirable for every user. So if you want the ability to * Whitelisting is an optional alternative because it adds some code and configuration options which might not be desirable for every user. So if
* provide and use whitelists, as well as transmitting to a node with a whitelist, you need to enable @ref MY_SIGNING_NODE_WHITELISTING.<br> * you want the ability to use whitelists, you need to enable @ref MY_SIGNING_NODE_WHITELISTING.<br>
* The whitelist is provided as value of the flag that enable it as follows (example is a node that require signing as well): * The whitelist is provided as value of the flag that enable it as follows (example is a node that require signing as well):
* @code{.cpp} * @code{.cpp}
* #define MY_SIGNING_ATSHA204 * #define MY_SIGNING_ATSHA204
@@ -314,31 +328,26 @@
* ... * ...
* @endcode * @endcode
* In this example, there are two nodes in the whitelist; the gateway, and a separate node that communicates directly with this node (with signed * In this example, there are two nodes in the whitelist; the gateway, and a separate node that communicates directly with this node (with signed
* messages). * messages). You do not need to do anything special for the sending nodes, apart from making sure they support signing.
* *
* The "soft" backend of course also support whitelisting. However, since it does not contain a unique identifier, you have to provide an additional * The "soft" backend of course also support whitelisting. Example:
* setting (@ref MY_SIGNING_SOFT_SERIAL) when you enable whitelisting as illustrated in this example:
* @code{.cpp} * @code{.cpp}
* #define MY_SIGNING_SOFT * #define MY_SIGNING_SOFT
* #define MY_SIGNING_SOFT_SERIAL 0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 * #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
* #define MY_SIGNING_SOFT_HMAC_KEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0,0x11,0x22
* #define MY_SIGNING_REQUEST_SIGNATURES * #define MY_SIGNING_REQUEST_SIGNATURES
* #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}},{.nodeId = 2,.serial = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09}}} * #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}},{.nodeId = 2,.serial = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09}}}
* #include <MySensor.h> * #include <MySensor.h>
* ... * ...
* @endcode * @endcode
* *
* For a node that should transmit whitelisted messages but not receive whitelisted messages, you can set the whitelist flag as follows: * For a node that should transmit whitelisted messages but not receive whitelisted messages, you do not need any special configurations:
* @code{.cpp} * @code{.cpp}
* #define MY_SIGNING_NODE_WHITELISTING {} * #define MY_SIGNING_SOFT
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
* @endcode * @endcode
* Remember that for the software backend, you still need to set @ref MY_SIGNING_SOFT_SERIAL (a default is set in MyConfig.h if you do not provide one * Remember that you always need to select a signing backend for all nodes that communicate to a node that require whitelisting.
* in your sketch. * Also, note that a node that use whitelisting will not accept messages from nodes that are not present in it's whitelist.
* * And you have to personalize all nodes that use signing with a common HMAC key but different serial numbers (ATSHA204A always has unique serials).
* It is important to emphasize that you do not have to provide a whitelist that has entries for all nodes that transmit signed messages to the node
* in question. You only need to have entries for the nodes that in turn have enabled @ref MY_SIGNING_NODE_WHITELISTING. Nodes that does not have
* this option enabled can still transmit "regular" signed messages as long as they do not match a NodeId in the receivers whitelist.
* *
* @section MySigningtechnical The technical stuff * @section MySigningtechnical The technical stuff
* *
@@ -363,9 +372,9 @@
* Exactly how this is done can be reviewd in the source for the ATSHA204SOFT backend and the ATSHA204A * Exactly how this is done can be reviewd in the source for the ATSHA204SOFT backend and the ATSHA204A
* <a href="http://www.atmel.com/Images/Atmel-8885-CryptoAuth-ATSHA204A-Datasheet.pdf">datasheet</a>. * <a href="http://www.atmel.com/Images/Atmel-8885-CryptoAuth-ATSHA204A-Datasheet.pdf">datasheet</a>.
* In the MySensors protocol, the following internal messagetypes handles signature requirements and nonce requests:<br> * In the MySensors protocol, the following internal messagetypes handles signature requirements and nonce requests:<br>
* @ref I_REQUEST_SIGNING <br> * @ref I_SIGNING_PRESENTATION <br>
* @ref I_GET_NONCE <br> * @ref I_NONCE_REQUEST <br>
* @ref I_GET_NONCE_RESPONSE <br> * @ref I_NONCE_RESPONSE <br>
* *
* Also, the version field in the header has been reduced from 3 to 2 bits in order to fit a single bit to indicate that a message is signed. * Also, the version field in the header has been reduced from 3 to 2 bits in order to fit a single bit to indicate that a message is signed.
* *
@@ -409,14 +418,11 @@
* Nor can it be changed in order to do it in the future. You can also use whitelisting to revoke your lost node.<br> * Nor can it be changed in order to do it in the future. You can also use whitelisting to revoke your lost node.<br>
* This is an unlikely use case because there really is no reason to sign sensor values. If you for some reason want to obfuscate sensor data, * This is an unlikely use case because there really is no reason to sign sensor values. If you for some reason want to obfuscate sensor data,
* encryption is a better alternative.<br> * encryption is a better alternative.<br>
* Configuration example for a motion sensor with whitelisting (it has to have the gateway whitelisted since the gateway will send signed messages to the node):<br> * Configuration example for a motion sensor:<br>
* @code{.cpp} * @code{.cpp}
* #define MY_SIGNING_SOFT * #define MY_SIGNING_SOFT
* #define MY_SIGNING_SOFT_SERIAL 0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 * #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
* #define MY_SIGNING_SOFT_HMAC_KEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0,0x11,0x22
* #define MY_SIGNING_REQUEST_SIGNATURES * #define MY_SIGNING_REQUEST_SIGNATURES
* #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}}}
* #include <MySensor.h> * #include <MySensor.h>
* ... * ...
* @endcode * @endcode
@@ -424,9 +430,7 @@
* The gateway needs to configured with a whitelist (and it have to have an entry for all nodes that send and/or require signed messages):<br> * The gateway needs to configured with a whitelist (and it have to have an entry for all nodes that send and/or require signed messages):<br>
* @code{.cpp} * @code{.cpp}
* #define MY_SIGNING_SOFT * #define MY_SIGNING_SOFT
* #define MY_SIGNING_SOFT_SERIAL 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 * #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
* #define MY_SIGNING_SOFT_HMAC_KEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0,0x11,0x22
* #define MY_SIGNING_REQUEST_SIGNATURES * #define MY_SIGNING_REQUEST_SIGNATURES
* #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = MOTION_SENSOR_ID,.serial = {0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78}}} * #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = MOTION_SENSOR_ID,.serial = {0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78}}}
* #include <MySensor.h> * #include <MySensor.h>
@@ -441,16 +445,14 @@
* Configuration example for the keyfob (keyfob will only transmit to another node and not receive anything):<br> * Configuration example for the keyfob (keyfob will only transmit to another node and not receive anything):<br>
* @code{.cpp} * @code{.cpp}
* #define MY_SIGNING_ATSHA204 * #define MY_SIGNING_ATSHA204
* #define MY_SIGNING_NODE_WHITELISTING {}
* #include <MySensor.h> * #include <MySensor.h>
* ... * ...
* @endcode * @endcode
*
* Configuration example for the door controller node (should require signing from anyone who wants to control it):<br> * Configuration example for the door controller node (should require signing from anyone who wants to control it):<br>
* @code{.cpp} * @code{.cpp}
* #define MY_SIGNING_SOFT * #define MY_SIGNING_SOFT
* #define MY_SIGNING_SOFT_SERIAL 0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 * #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
* #define MY_SIGNING_SOFT_HMAC_KEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0,0x11,0x22
* #define MY_SIGNING_REQUEST_SIGNATURES * #define MY_SIGNING_REQUEST_SIGNATURES
* #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}},{.nodeId = KEYFOB_ID,.serial = {<FROM ATSHA ON KEYFOB>}}} * #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}},{.nodeId = KEYFOB_ID,.serial = {<FROM ATSHA ON KEYFOB>}}}
* #include <MySensor.h> * #include <MySensor.h>
@@ -460,7 +462,7 @@
* @subsection MySigningsketches Relevant sketches * @subsection MySigningsketches Relevant sketches
* *
* - @ref SecureActuator.ino * - @ref SecureActuator.ino
* - @ref Sha204Personalizer.ino * - @ref SecurityPersonalizer.ino
*/ */
/** /**
@@ -484,29 +486,50 @@ typedef struct {
} whitelist_entry_t; } whitelist_entry_t;
#endif #endif
// Macros for manipulating signing requirement table
/** @brief Return 'true' if provided node ID is requiring signed messages */
#define DO_SIGN(node) (~_doSign[node>>3]&(1<<(node%8)))
/** @brief Mark provided node ID to require signed messages in table */
#define SET_SIGN(node) (_doSign[node>>3]&=~(1<<(node%8)))
/** @brief Mark provided node ID to not require signed messages in table */
#define CLEAR_SIGN(node) (_doSign[node>>3]|=(1<<(node%8)))
/** @brief Helper macro to determine the number of elements in a array */ /** @brief Helper macro to determine the number of elements in a array */
#define NUM_OF(x) (sizeof(x)/sizeof(x[0])) #define NUM_OF(x) (sizeof(x)/sizeof(x[0]))
/** @brief Helper macro to determine if node require serial salted signatures */
#define DO_WHITELIST(node) (~_doWhitelist[node>>3]&(1<<node%8))
/** @brief Helper macro to set that node require serial salted signatures */
#define SET_WHITELIST(node) (_doWhitelist[node>>3]&=~(1<<node%8))
/** @brief Helper macro to set that node does not require serial salted signatures */
#define CLEAR_WHITELIST(node) (_doWhitelist[node>>3]|=(1<<node%8))
/** /**
* @brief Stores signing identifier and a new nonce in provided message for signing operations. * @brief Initializes signing infrastructure and associated backend.
* *
* All space in message payload buffer is used for signing identifier and nonce.<br> * This function makes sure that the internal states of the signing infrastructure
* Returns @c false if subsystem is busy processing an ongoing signing operation.<br> * is set to a known initial state.
* If successful, this marks the start of a signing operation at the receiving side so * \n@b Usage: This fuction should be called before any signing related operations take place.
* implementation is expected to do any necessary initializations within this call.
* \n@b Usage: This function is typically called as action when receiving a @ref I_GET_NONCE message.
*
* @param msg The message to put the nonce in.
* @returns @c true if nonce is successfully put in provided message.
*/ */
bool signerGetNonce(MyMessage &msg); void signerInit(void);
/**
* @brief Does signing specific presentation for a node.
*
* This function makes sure any signing related presentation info is shared with the gateway.
* The presentation of the gateways signing preferences is done in @ref signerProcessInternal().
* \n@b Usage: This function should be called by the presentation routine of the mysensors library.
* There is no need to call this directly from a sketch.
*
* @param msg Message buffer to use.
*/
void signerPresentation(MyMessage &msg);
/**
* @brief Manages internal signing message handshaking.
*
* This function takes care of signing related message handshaknig such as nonce exchange.
* \n@b Usage: This function should be called by the incoming message handler before any further message
* processing is performed on internal messages. This function should only be called for @ref C_INTERNAL class
* messages.
*
* @param msg Message buffer to process.
* @returns @c true if caller should stop further message processing.
*/
bool signerProcessInternal(MyMessage &msg);
/** /**
* @brief Check timeout of verification session. * @brief Check timeout of verification session.
@@ -525,7 +548,7 @@ bool signerCheckTimer(void);
* Returns @c false if signing identifier found in message is not supported by the used backend.<br> * Returns @c false if signing identifier found in message is not supported by the used backend.<br>
* If successful, this marks the start of a signing operation at the sending side so * If successful, this marks the start of a signing operation at the sending side so
* implementation is expected to do any necessary initializations within this call. * implementation is expected to do any necessary initializations within this call.
* \n@b Usage: This function is typically called as action when receiving a @ref I_GET_NONCE_RESPONSE * \n@b Usage: This function is typically called as action when receiving a @ref I_NONCE_RESPONSE
* message. * message.
* *
* @param msg The message to get the nonce from. * @param msg The message to get the nonce from.
@@ -537,13 +560,13 @@ bool signerPutNonce(MyMessage &msg);
* @brief Signs provided message. All remaining space in message payload buffer is used for * @brief Signs provided message. All remaining space in message payload buffer is used for
* signing identifier and signature. * signing identifier and signature.
* *
* Nonce used for signature calculation is the one generated previously using @ref signerGetNonce().<br> * Nonce used for signature calculation is the one generated previously within @ref signerProcessInternal().<br>
* Nonce will be cleared when this function is called to prevent re-use of nonce.<br> * Nonce will be cleared when this function is called to prevent re-use of nonce.<br>
* Returns @c false if subsystem is busy processing an ongoing signing operation.<br> * Returns @c false if subsystem is busy processing an ongoing signing operation.<br>
* Returns @c false if not two bytes or more of free payload space is left in provided message.<br> * Returns @c false if not two bytes or more of free payload space is left in provided message.<br>
* This ends a signing operation at the sending side so implementation is expected to do any * 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. * deinitializations and enter a power saving state within this call.
* \n@b Usage: This function is typically called as action when receiving a @ref I_GET_NONCE_RESPONSE * \n@b Usage: This function is typically called as action when receiving a @ref I_NONCE_RESPONSE
* message and after @ref signerPutNonce() has successfully been executed. * message and after @ref signerPutNonce() has successfully been executed.
* *
* @param msg The message to sign. * @param msg The message to sign.
@@ -568,5 +591,32 @@ bool signerSignMsg(MyMessage &msg);
*/ */
bool signerVerifyMsg(MyMessage &msg); bool signerVerifyMsg(MyMessage &msg);
/**
* @brief Initialize a hash calculation session.
*
* Any ongoing calculation session will be terminated/reset.
*/
void signerSha256Init(void);
/**
* @brief Add data to an ongoing hash calculation session.
*
* More data can be added by repeated calls to this function.
*
* @param data Buffer with data to add.
* @param sz Size of data buffer.
*/
void signerSha256Update(const uint8_t* data, size_t sz);
/**
* @brief Finalize an ongong hash calculation session and return the hash.
*
* The returned hash size is always 32 bytes. Buffer can be assumed to be valid
* until @ref signerSha256Init() is called again.
*
* @returns Buffer with 32-byte hash.
*/
uint8_t* signerSha256Final(void);
#endif #endif
/** @}*/ /** @}*/
+42 -42
View File
@@ -29,7 +29,7 @@
#define SIGNING_IDENTIFIER (1) #define SIGNING_IDENTIFIER (1)
// Define MY_DEBUG in your sketch to enable signing backend debugprints // Define MY_DEBUG_VERBOSE_SIGNING in your sketch to enable signing backend debugprints
ATSHA204Class atsha204(MY_SIGNING_ATSHA204_PIN); ATSHA204Class atsha204(MY_SIGNING_ATSHA204_PIN);
unsigned long _signing_timestamp; unsigned long _signing_timestamp;
@@ -38,16 +38,17 @@ uint8_t _signing_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1];
uint8_t _signing_temp_message[SHA_MSG_SIZE]; uint8_t _signing_temp_message[SHA_MSG_SIZE];
uint8_t _singning_rx_buffer[SHA204_RSP_SIZE_MAX]; uint8_t _singning_rx_buffer[SHA204_RSP_SIZE_MAX];
uint8_t _singning_tx_buffer[SHA204_CMD_SIZE_MAX]; uint8_t _singning_tx_buffer[SHA204_CMD_SIZE_MAX];
extern uint8_t _doWhitelist[32];
#ifdef MY_SIGNING_NODE_WHITELISTING #ifdef MY_SIGNING_NODE_WHITELISTING
const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING; const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
#endif #endif
void signerCalculateSignature(MyMessage &msg); static void signerCalculateSignature(MyMessage &msg);
uint8_t* signerSha256(const uint8_t* data, size_t sz); static uint8_t* signerSha256(const uint8_t* data, size_t sz);
#ifdef MY_DEBUG #ifdef MY_DEBUG_VERBOSE_SIGNING
static char i2h(uint8_t i) static char i2h(uint8_t i)
{ {
uint8_t k = i & 0x0F; uint8_t k = i & 0x0F;
@@ -57,8 +58,7 @@ static char i2h(uint8_t i)
return 'A' + k - 10; return 'A' + k - 10;
} }
static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) {
{
static char printBuffer[300]; static char printBuffer[300];
#ifdef MY_GATEWAY_FEATURE #ifdef MY_GATEWAY_FEATURE
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
@@ -86,7 +86,23 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf,
#define DEBUG_SIGNING_PRINTBUF(str, buf, sz) #define DEBUG_SIGNING_PRINTBUF(str, buf, sz)
#endif #endif
bool signerGetNonce(MyMessage &msg) { void signerAtsha204Init(void) {
}
bool signerAtsha204CheckTimer(void) {
if (_signing_verification_ongoing) {
if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0);
// Purge nonce
memset(_signing_current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
_signing_verification_ongoing = false;
return false;
}
}
return true;
}
bool signerAtsha204GetNonce(MyMessage &msg) {
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0);
// Generate random number for use as nonce // Generate random number for use as nonce
// We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value // We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value
@@ -104,9 +120,6 @@ bool signerGetNonce(MyMessage &msg) {
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA // We set the part of the 32-byte nonce that does not fit into a message to 0xAA
memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_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
_signing_current_nonce[0] = SIGNING_IDENTIFIER;
// Transfer the first part of the nonce to the message // Transfer the first part of the nonce to the message
msg.set(_signing_current_nonce, MAX_PAYLOAD); msg.set(_signing_current_nonce, MAX_PAYLOAD);
_signing_verification_ongoing = true; _signing_verification_ongoing = true;
@@ -118,33 +131,15 @@ bool signerGetNonce(MyMessage &msg) {
return true; return true;
} }
bool signerCheckTimer() { void signerAtsha204PutNonce(MyMessage &msg) {
if (_signing_verification_ongoing) {
if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0);
// Purge nonce
memset(_signing_current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
_signing_verification_ongoing = false;
return false;
}
}
return true;
}
bool signerPutNonce(MyMessage &msg) {
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0);
if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) {
DEBUG_SIGNING_PRINTBUF(F("Incorrect signing identifier"), NULL, 0);
return false;
}
memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD); memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA // We set the part of the 32-byte nonce that does not fit into a message to 0xAA
memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD); memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD);
return true;
} }
bool signerSignMsg(MyMessage &msg) { bool signerAtsha204SignMsg(MyMessage &msg) {
// If we cannot fit any signature in the message, refuse to sign it // If we cannot fit any signature in the message, refuse to sign it
if (mGetLength(msg) > MAX_PAYLOAD-2) { if (mGetLength(msg) > MAX_PAYLOAD-2) {
DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0);
@@ -155,14 +150,14 @@ bool signerSignMsg(MyMessage &msg) {
mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated
signerCalculateSignature(msg); signerCalculateSignature(msg);
#ifdef MY_SIGNING_NODE_WHITELISTING if (DO_WHITELIST(msg.destination)) {
// Salt the signature with the senders nodeId and the unique serial of the ATSHA device // Salt the signature with the senders nodeId and the unique serial of the ATSHA device
memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
_signing_current_nonce[32] = msg.sender; _signing_current_nonce[32] = msg.sender;
atsha204.getSerialNumber(&_signing_current_nonce[33]); atsha204.getSerialNumber(&_signing_current_nonce[33]);
(void)signerSha256(_signing_current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place (void)signerSha256(_signing_current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0);
#endif }
// Overwrite the first byte in the signature with the signing identifier // Overwrite the first byte in the signature with the signing identifier
_singning_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER; _singning_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
@@ -174,7 +169,7 @@ bool signerSignMsg(MyMessage &msg) {
return true; return true;
} }
bool signerVerifyMsg(MyMessage &msg) { bool signerAtsha204VerifyMsg(MyMessage &msg) {
if (!_signing_verification_ongoing) { if (!_signing_verification_ongoing) {
DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0);
return false; return false;
@@ -196,7 +191,8 @@ bool signerVerifyMsg(MyMessage &msg) {
#ifdef MY_SIGNING_NODE_WHITELISTING #ifdef MY_SIGNING_NODE_WHITELISTING
// Look up the senders nodeId in our whitelist and salt the signature with that data // Look up the senders nodeId in our whitelist and salt the signature with that data
for (int j=0; j < NUM_OF(_signing_whitelist); j++) { size_t j;
for (j=0; j < NUM_OF(_signing_whitelist); j++) {
if (_signing_whitelist[j].nodeId == msg.sender) { if (_signing_whitelist[j].nodeId == msg.sender) {
DEBUG_SIGNING_PRINTBUF(F("Sender found in whitelist"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Sender found in whitelist"), NULL, 0);
memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
@@ -206,6 +202,10 @@ bool signerVerifyMsg(MyMessage &msg) {
break; break;
} }
} }
if (j == NUM_OF(_signing_whitelist)) {
DEBUG_SIGNING_PRINTBUF(F("Sender not found in whitelist, message rejected!"), NULL, 0);
return false;
}
#endif #endif
// Overwrite the first byte in the signature with the signing identifier // Overwrite the first byte in the signature with the signing identifier
@@ -226,7 +226,7 @@ bool signerVerifyMsg(MyMessage &msg) {
} }
// Helper to calculate signature of msg (returned in _singning_rx_buffer[SHA204_BUFFER_POS_DATA]) // Helper to calculate signature of msg (returned in _singning_rx_buffer[SHA204_BUFFER_POS_DATA])
void signerCalculateSignature(MyMessage &msg) { static void signerCalculateSignature(MyMessage &msg) {
memset(_signing_temp_message, 0, 32); 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))); memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
@@ -259,7 +259,7 @@ void signerCalculateSignature(MyMessage &msg) {
// Helper to calculate a generic SHA256 digest of provided buffer (only supports one block) // Helper to calculate a generic SHA256 digest of provided buffer (only supports one block)
// The pointer to the hash is returned, but the hash is also stored in _singning_rx_buffer[SHA204_BUFFER_POS_DATA]) // The pointer to the hash is returned, but the hash is also stored in _singning_rx_buffer[SHA204_BUFFER_POS_DATA])
uint8_t* signerSha256(const uint8_t* data, size_t sz) { static uint8_t* signerSha256(const uint8_t* data, size_t sz) {
// Initiate SHA256 calculator // Initiate SHA256 calculator
(void)atsha204.sha204m_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL, (void)atsha204.sha204m_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL,
SHA_COUNT_SHORT, _singning_tx_buffer, SHA_RSP_SIZE_SHORT, _singning_rx_buffer); SHA_COUNT_SHORT, _singning_tx_buffer, SHA_RSP_SIZE_SHORT, _singning_rx_buffer);
@@ -33,24 +33,25 @@
#define SIGNING_IDENTIFIER (1) #define SIGNING_IDENTIFIER (1)
// Define MY_DEBUG in your sketch to enable signing backend debugprints // Define MY_DEBUG_VERBOSE_SIGNING in your sketch to enable signing backend debugprints
Sha256Class _signing_sha256; Sha256Class _signing_sha256;
unsigned long _signing_timestamp; unsigned long _signing_timestamp;
bool _signing_verification_ongoing = false; bool _signing_verification_ongoing = false;
uint8_t _signing_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH]; uint8_t _signing_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH];
uint8_t _signing_temp_message[32]; uint8_t _signing_temp_message[32];
static uint8_t _signing_hmac_key[32] = { MY_SIGNING_SOFT_HMAC_KEY }; static uint8_t _signing_hmac_key[32];
uint8_t _signing_hmac[32]; uint8_t _signing_hmac[32];
extern uint8_t _doWhitelist[32];
static uint8_t _signing_node_serial_info[9];
#ifdef MY_SIGNING_NODE_WHITELISTING #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; const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
#endif #endif
void signerCalculateSignature(MyMessage &msg); static void signerCalculateSignature(MyMessage &msg);
#ifdef MY_DEBUG #ifdef MY_DEBUG_VERBOSE_SIGNING
static char i2h(uint8_t i) static char i2h(uint8_t i)
{ {
uint8_t k = i & 0x0F; uint8_t k = i & 0x0F;
@@ -60,8 +61,7 @@ static char i2h(uint8_t i)
return 'A' + k - 10; return 'A' + k - 10;
} }
static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) {
{
static char printBuffer[300]; static char printBuffer[300];
#ifdef MY_GATEWAY_FEATURE #ifdef MY_GATEWAY_FEATURE
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
@@ -89,7 +89,28 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf,
#define DEBUG_SIGNING_PRINTBUF(str, buf, sz) #define DEBUG_SIGNING_PRINTBUF(str, buf, sz)
#endif #endif
bool signerGetNonce(MyMessage &msg) { void signerAtsha204SoftInit(void) {
// initialize pseudo-RNG
randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN));
// Set secrets
hwReadConfigBlock((void*)_signing_hmac_key, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
hwReadConfigBlock((void*)_signing_node_serial_info, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
}
bool signerAtsha204SoftCheckTimer(void) {
if (_signing_verification_ongoing) {
if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0);
// Purge nonce
memset(_signing_current_nonce, 0xAA, 32);
_signing_verification_ongoing = false;
return false;
}
}
return true;
}
bool signerAtsha204SoftGetNonce(MyMessage &msg) {
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0);
// We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value // We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value
@@ -104,9 +125,6 @@ bool signerGetNonce(MyMessage &msg) {
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA // We set the part of the 32-byte nonce that does not fit into a message to 0xAA
memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_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
_signing_current_nonce[0] = SIGNING_IDENTIFIER;
// Transfer the first part of the nonce to the message // Transfer the first part of the nonce to the message
msg.set(_signing_current_nonce, MAX_PAYLOAD); msg.set(_signing_current_nonce, MAX_PAYLOAD);
_signing_verification_ongoing = true; _signing_verification_ongoing = true;
@@ -118,33 +136,15 @@ bool signerGetNonce(MyMessage &msg) {
return true; return true;
} }
bool signerCheckTimer() { void signerAtsha204SoftPutNonce(MyMessage &msg) {
if (_signing_verification_ongoing) {
if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0);
// Purge nonce
memset(_signing_current_nonce, 0xAA, 32);
_signing_verification_ongoing = false;
return false;
}
}
return true;
}
bool signerPutNonce(MyMessage &msg) {
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0);
if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) {
DEBUG_SIGNING_PRINTBUF(F("Incorrect signing identifier"), NULL, 0);
return false;
}
memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD); memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA // We set the part of the 32-byte nonce that does not fit into a message to 0xAA
memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD); memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD);
return true;
} }
bool signerSignMsg(MyMessage &msg) { bool signerAtsha204SoftSignMsg(MyMessage &msg) {
// If we cannot fit any signature in the message, refuse to sign it // If we cannot fit any signature in the message, refuse to sign it
if (mGetLength(msg) > MAX_PAYLOAD-2) { if (mGetLength(msg) > MAX_PAYLOAD-2) {
DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0);
@@ -155,16 +155,16 @@ bool signerSignMsg(MyMessage &msg) {
mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated
signerCalculateSignature(msg); signerCalculateSignature(msg);
#ifdef MY_SIGNING_NODE_WHITELISTING if (DO_WHITELIST(msg.destination)) {
// Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided // Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided
_signing_sha256.init(); _signing_sha256.init();
for (int i=0; i<32; i++) _signing_sha256.write(_signing_hmac[i]); for (int i=0; i<32; i++) _signing_sha256.write(_signing_hmac[i]);
_signing_sha256.write(msg.sender); _signing_sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) _signing_sha256.write(_signing_node_serial_info[i]); for (int i=0; i<SHA204_SERIAL_SZ; i++) _signing_sha256.write(_signing_node_serial_info[i]);
memcpy(_signing_hmac, _signing_sha256.result(), 32); memcpy(_signing_hmac, _signing_sha256.result(), 32);
DEBUG_SIGNING_PRINTBUF(F("SHA256: "), _signing_hmac, 32); DEBUG_SIGNING_PRINTBUF(F("SHA256: "), _signing_hmac, 32);
DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0);
#endif }
// Overwrite the first byte in the signature with the signing identifier // Overwrite the first byte in the signature with the signing identifier
_signing_hmac[0] = SIGNING_IDENTIFIER; _signing_hmac[0] = SIGNING_IDENTIFIER;
@@ -176,7 +176,7 @@ bool signerSignMsg(MyMessage &msg) {
return true; return true;
} }
bool signerVerifyMsg(MyMessage &msg) { bool signerAtsha204SoftVerifyMsg(MyMessage &msg) {
if (!_signing_verification_ongoing) { if (!_signing_verification_ongoing) {
DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0);
return false; return false;
@@ -199,7 +199,8 @@ bool signerVerifyMsg(MyMessage &msg) {
#ifdef MY_SIGNING_NODE_WHITELISTING #ifdef MY_SIGNING_NODE_WHITELISTING
// Look up the senders nodeId in our whitelist and salt the signature with that data // Look up the senders nodeId in our whitelist and salt the signature with that data
for (int j=0; j < NUM_OF(_signing_whitelist); j++) { size_t j;
for (j=0; j < NUM_OF(_signing_whitelist); j++) {
if (_signing_whitelist[j].nodeId == msg.sender) { if (_signing_whitelist[j].nodeId == msg.sender) {
DEBUG_SIGNING_PRINTBUF(F("Sender found in whitelist"), NULL, 0); DEBUG_SIGNING_PRINTBUF(F("Sender found in whitelist"), NULL, 0);
_signing_sha256.init(); _signing_sha256.init();
@@ -211,6 +212,10 @@ bool signerVerifyMsg(MyMessage &msg) {
break; break;
} }
} }
if (j == NUM_OF(_signing_whitelist)) {
DEBUG_SIGNING_PRINTBUF(F("Sender not found in whitelist, message rejected!"), NULL, 0);
return false;
}
#endif #endif
// Overwrite the first byte in the signature with the signing identifier // Overwrite the first byte in the signature with the signing identifier
@@ -231,7 +236,7 @@ bool signerVerifyMsg(MyMessage &msg) {
} }
// Helper to calculate signature of msg (returned in hmac) // Helper to calculate signature of msg (returned in hmac)
void signerCalculateSignature(MyMessage &msg) { static void signerCalculateSignature(MyMessage &msg) {
memset(_signing_temp_message, 0, 32); 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))); memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
DEBUG_SIGNING_PRINTBUF(F("Message to process: "), (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); DEBUG_SIGNING_PRINTBUF(F("Message to process: "), (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
+16 -127
View File
@@ -23,10 +23,6 @@
bool _autoFindParent; bool _autoFindParent;
uint8_t _failedTransmissions; uint8_t _failedTransmissions;
#ifdef MY_SIGNING_FEATURE
uint8_t _signingNonceStatus;
#endif
#ifdef MY_OTA_FIRMWARE_FEATURE #ifdef MY_OTA_FIRMWARE_FEATURE
SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID); SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID);
NodeFirmwareConfig _fc; NodeFirmwareConfig _fc;
@@ -70,9 +66,7 @@ inline void transportProcess() {
return; return;
} }
#ifdef MY_SIGNING_FEATURE
(void)signerCheckTimer(); // Manage signing timeout (void)signerCheckTimer(); // Manage signing timeout
#endif
uint8_t payloadLength = transportReceive((uint8_t *)&_msg); uint8_t payloadLength = transportReceive((uint8_t *)&_msg);
(void)payloadLength; //until somebody makes use of it (void)payloadLength; //until somebody makes use of it
@@ -86,34 +80,12 @@ inline void transportProcess() {
uint8_t destination = _msg.destination; uint8_t destination = _msg.destination;
#ifdef MY_SIGNING_FEATURE // Reject massages that do not pass verification
// Before processing message, reject unsigned messages if signing is required and check signature (if it is signed and addressed to us) if (!signerVerifyMsg(_msg)) {
// 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) debug(PSTR("verify fail\n"));
#ifdef MY_SIGNING_REQUEST_SIGNATURES ledBlinkErr(1);
return;
if ((!MY_IS_GATEWAY || DO_SIGN(sender)) && }
destination == _nc.nodeId &&
!mGetAck(_msg) &&
(mGetCommand(_msg) != C_INTERNAL ||
(type != I_GET_NONCE_RESPONSE && type != I_GET_NONCE && type != I_REQUEST_SIGNING &&
type != I_ID_REQUEST && type != I_ID_RESPONSE &&
type != I_FIND_PARENT && type != I_FIND_PARENT_RESPONSE &&
type != I_HEARTBEAT && type != I_HEARTBEAT_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!
}
}
#endif
#endif
if (destination == _nc.nodeId) { if (destination == _nc.nodeId) {
debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"), debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
@@ -138,7 +110,6 @@ inline void transportProcess() {
if (destination == _nc.nodeId) { if (destination == _nc.nodeId) {
// This message is addressed to this node // This message is addressed to this node
mSetSigned(_msg,0); // Clear the sign-flag now as verification is completed
// prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes) // prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes)
mSetLength(_msg, min(mGetLength(_msg),MAX_PAYLOAD)); mSetLength(_msg, min(mGetLength(_msg),MAX_PAYLOAD));
// null terminate data // null terminate data
@@ -163,6 +134,10 @@ inline void transportProcess() {
} }
if (command == C_INTERNAL) { if (command == C_INTERNAL) {
// Process signing related internal messages
if (signerProcessInternal(_msg)) {
return; // Signer processing indicated no further action needed
}
if (type == I_FIND_PARENT_RESPONSE) { if (type == I_FIND_PARENT_RESPONSE) {
if (_autoFindParent) { if (_autoFindParent) {
// We've received a reply to a FIND_PARENT message. Check if the distance is // We've received a reply to a FIND_PARENT message. Check if the distance is
@@ -183,44 +158,6 @@ inline void transportProcess() {
} }
} }
return; return;
#ifdef MY_SIGNING_FEATURE
} else if (type == I_GET_NONCE) {
if (signerGetNonce(_msg)) {
_sendRoute(build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_GET_NONCE_RESPONSE, false));
}
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
SET_SIGN(sender);
} else {
// We received an indicator that the sender does not require us to sign all messages we send to it
CLEAR_SIGN(sender);
}
// Save updated table
hwWriteConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign));
// 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 defined (MY_GATEWAY_FEATURE)
#ifdef MY_SIGNING_REQUEST_SIGNATURES
if (DO_SIGN(sender))
_sendRoute(build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set((uint8_t)true));
else
_sendRoute(build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set((uint8_t)false));
#else
_sendRoute(build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set((uint8_t)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) { } else if (sender == GATEWAY_ADDRESS) {
if (type == I_ID_RESPONSE && _nc.nodeId == AUTO) { if (type == I_ID_RESPONSE && _nc.nodeId == AUTO) {
_nc.nodeId = _msg.getByte(); _nc.nodeId = _msg.getByte();
@@ -414,47 +351,10 @@ boolean transportSendRoute(MyMessage &message) {
mSetVersion(message, PROTOCOL_VERSION); mSetVersion(message, PROTOCOL_VERSION);
#ifdef MY_SIGNING_FEATURE if (!signerSignMsg(message)) {
uint8_t type = message.type; debug(PSTR("sign fail\n"));
// 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 ledBlinkErr(1);
if (DO_SIGN(message.destination) && message.sender == _nc.nodeId && !mGetAck(message) && }
(mGetCommand(message) != C_INTERNAL ||
(type != I_GET_NONCE && type != I_GET_NONCE_RESPONSE && type != I_REQUEST_SIGNING &&
type != I_ID_REQUEST && type != I_ID_RESPONSE &&
type != I_FIND_PARENT && type != I_FIND_PARENT_RESPONSE &&
type != I_HEARTBEAT && type != I_HEARTBEAT_RESPONSE))) {
// Send nonce-request
_signingNonceStatus=SIGN_WAITING_FOR_NONCE;
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 if (_nc.nodeId == message.sender) {
mSetSigned(message, 0); // Message is not supposed to be signed, make sure it is marked unsigned
}
#endif
#if !defined(MY_REPEATER_FEATURE) #if !defined(MY_REPEATER_FEATURE)
@@ -552,19 +452,8 @@ void transportPresentNode() {
// Present node and request config // Present node and request config
#ifndef MY_GATEWAY_FEATURE #ifndef MY_GATEWAY_FEATURE
if (_nc.nodeId != AUTO) { if (_nc.nodeId != AUTO) {
#ifdef MY_SIGNING_FEATURE // Send signing preferences for this node
// Notify gateway (and possibly controller) about the signing preferences of this node signerPresentation(_msg);
#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
#else
// We do not support signing, make sure gateway knows this
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false));
#endif
// Send presentation for this radio node // Send presentation for this radio node
#ifdef MY_REPEATER_FEATURE #ifdef MY_REPEATER_FEATURE
-3
View File
@@ -41,9 +41,6 @@
// Search for a new parent node after this many transmission failures // Search for a new parent node after this many transmission failures
#define SEARCH_FAILURES 5 #define SEARCH_FAILURES 5
// Status when waiting for signing nonce in process
enum { SIGN_WAITING_FOR_NONCE = 0, SIGN_OK = 1 };
// FW config structure, stored in eeprom // FW config structure, stored in eeprom
typedef struct { typedef struct {
@@ -38,7 +38,7 @@ uint8_t _address;
#if defined(MY_RF24_ENABLE_ENCRYPTION) #if defined(MY_RF24_ENABLE_ENCRYPTION)
AES _aes; AES _aes;
uint8_t _dataenc[32] = {0}; uint8_t _dataenc[32] = {0};
uint8_t _psk[] = { MY_RF24_ENCRYPTKEY }; uint8_t _psk[16];
#endif #endif
bool transportInit() { bool transportInit() {
@@ -65,7 +65,9 @@ bool transportInit() {
#if defined(MY_RF24_ENABLE_ENCRYPTION) #if defined(MY_RF24_ENABLE_ENCRYPTION)
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
_aes.set_key(_psk, 16); //set up AES-key _aes.set_key(_psk, 16); //set up AES-key
memset(_psk, 0, 16); // Make sure it is purged from memory when set
#endif #endif
//_rf24.printDetails(); //_rf24.printDetails();
@@ -30,7 +30,10 @@ bool transportInit() {
// Start up the radio library (_address will be set later by the MySensors library) // Start up the radio library (_address will be set later by the MySensors library)
if (_radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID)) { if (_radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID)) {
#ifdef MY_RFM69_ENABLE_ENCRYPTION #ifdef MY_RFM69_ENABLE_ENCRYPTION
_radio.encrypt(RFM69_ENCRYPTKEY); uint8_t _psk[16];
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
_radio.encrypt(_psk);
memset(_psk, 0, 16); // Make sure it is purged from memory when set
#endif #endif
return true; return true;
} }
+1 -1
View File
@@ -6,6 +6,6 @@
#ifndef Version_h #ifndef Version_h
#define Version_h #define Version_h
#define LIBRARY_VERSION "1.6.0-beta" #define LIBRARY_VERSION "2.0.0-beta"
#endif #endif
+5 -5
View File
@@ -35,7 +35,7 @@
/* code was modified by george spanos <spaniakos@gmail.com> /* code was modified by george spanos <spaniakos@gmail.com>
* 16/12/14 * 16/12/14
*/ */
/** AES class */
class AES class AES
{ {
public: public:
@@ -351,7 +351,7 @@ class AES
* @defgroup aeslib AES library for Arduino and Raspberry pi * @defgroup aeslib AES library for Arduino and Raspberry pi
* @ingroup internals * @ingroup internals
* *
* @section Goals design Goals * @section AesGoals design Goals
* *
* This library is AESigned to be... * This library is AESigned to be...
* @li Fast and efficient. * @li Fast and efficient.
@@ -393,10 +393,10 @@ class AES
* cd examples_Rpi<br /> * cd examples_Rpi<br />
* sudo ./\<sketch\><br /><br /> * sudo ./\<sketch\><br /><br />
* *
* @section News News * @section AesNews News
* *
* If issues are discovered with the documentation, please report them <a href="https://github.com/spaniakos/spaniakos.github.io/issues"> here</a> * If issues are discovered with the documentation, please report them <a href="https://github.com/spaniakos/spaniakos.github.io/issues"> here</a>
* @section Useful Useful References * @section AesUseful Useful References
* *
* Please refer to: * Please refer to:
* *
@@ -405,7 +405,7 @@ class AES
* @li <a href="https://github.com/spaniakos/AES/"><b>Source Code</b></a> * @li <a href="https://github.com/spaniakos/AES/"><b>Source Code</b></a>
* @li <a href="http://spaniakos.github.io/">All spaniakos Documentation Main Page</a> * @li <a href="http://spaniakos.github.io/">All spaniakos Documentation Main Page</a>
* *
* @section Board_Support Board Support * @section AesBoard_Support Board Support
* *
* Most standard Arduino based boards are supported: * Most standard Arduino based boards are supported:
* - Arduino * - Arduino
@@ -199,7 +199,9 @@
#define SHA204_COMMAND_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) //! maximum command delay #define SHA204_COMMAND_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) //! maximum command delay
#define SHA204_CMD_SIZE_MIN ((uint8_t) 7) //! minimum number of bytes in command (from count byte to second CRC byte) #define SHA204_CMD_SIZE_MIN ((uint8_t) 7) //! minimum number of bytes in command (from count byte to second CRC byte)
#ifndef SHA204_CMD_SIZE_MAX
#define SHA204_CMD_SIZE_MAX ((uint8_t) SHA_COUNT_LONG) //! maximum size of command packet (SHA) #define SHA204_CMD_SIZE_MAX ((uint8_t) SHA_COUNT_LONG) //! maximum size of command packet (SHA)
#endif
#define SHA204_CRC_SIZE ((uint8_t) 2) //! number of CRC bytes #define SHA204_CRC_SIZE ((uint8_t) 2) //! number of CRC bytes
#define SHA204_BUFFER_POS_STATUS (1) //! buffer index of status byte in status response #define SHA204_BUFFER_POS_STATUS (1) //! buffer index of status byte in status response
#define SHA204_BUFFER_POS_DATA (1) //! buffer index of first data byte in data response #define SHA204_BUFFER_POS_DATA (1) //! buffer index of first data byte in data response
@@ -23,7 +23,7 @@ class Sha256Class
void initHmac(const uint8_t* secret, int secretLength); void initHmac(const uint8_t* secret, int secretLength);
uint8_t* result(void); uint8_t* result(void);
uint8_t* resultHmac(void); uint8_t* resultHmac(void);
virtual void write(uint8_t); void write(uint8_t);
private: private:
void pad(); void pad();
void addUncounted(uint8_t data); void addUncounted(uint8_t data);
@@ -23,16 +23,18 @@
* This sketch clears radioId, relayId and other routing information in EEPROM back to factory default * This sketch clears radioId, relayId and other routing information in EEPROM back to factory default
* *
*/ */
// load core modules only
#define MY_CORE_ONLY
#include <MyConfig.h>
#include <EEPROM.h> #include <EEPROM.h>
#include <MySensor.h>
void setup() void setup()
{ {
Serial.begin(MY_BAUD_RATE); Serial.begin(MY_BAUD_RATE);
Serial.println("Started clearing. Please wait..."); Serial.println("Started clearing. Please wait...");
for (int i=0;i<512;i++) { for (int i=0;i<EEPROM_LOCAL_CONFIG_ADDRESS;i++) {
EEPROM.write(i, 0xff); hwWriteConfig(i,0xFF);
} }
Serial.println("Clearing done. You're ready to go!"); Serial.println("Clearing done. You're ready to go!");
} }
@@ -37,12 +37,12 @@
* is defined by @ref LOCK_1. The number of locks is controlled by @ref NOF_LOCKS .<br> * is defined by @ref LOCK_1. The number of locks is controlled by @ref NOF_LOCKS .<br>
* The sketch will require incoming messages to be signed and the use of signing backend is selected * The sketch will require incoming messages to be signed and the use of signing backend is selected
* by @ref MY_SIGNING_ATSHA204 or @ref MY_SIGNING_SOFT. Hard or soft ATSHA204 signing is supported.<br> * by @ref MY_SIGNING_ATSHA204 or @ref MY_SIGNING_SOFT. Hard or soft ATSHA204 signing is supported.<br>
* If soft signing is chosen, make sure to use a correct HMAC key (see @ref MY_SIGNING_SOFT_HMAC_KEY).<br>
* Whitelisting can be enabled through @ref MY_SIGNING_NODE_WHITELISTING in which case a single entry * Whitelisting can be enabled through @ref MY_SIGNING_NODE_WHITELISTING in which case a single entry
* is provided in this example which typically should map to the gateway of the network. * is provided in this example which typically should map to the gateway of the network.
*/ */
#define MY_DEBUG //!< Enable debug prints to serial monitor #define MY_DEBUG //!< Enable debug prints to serial monitor
#define MY_DEBUG_VERBOSE_SIGNING //!< Enable signing related debug prints to serial monitor
// Enable and select radio type attached // Enable and select radio type attached
#define MY_RADIO_NRF24 //!< NRF24L01 radio driver #define MY_RADIO_NRF24 //!< NRF24L01 radio driver
@@ -59,11 +59,6 @@
// SETTINGS FOR MY_SIGNING_SOFT // 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 //!< Unconnected analog pin for random seed #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 //!< Unconnected analog pin for random seed
// SETTINGS FOR MY_SIGNING_ATSHA204 // SETTINGS FOR MY_SIGNING_ATSHA204
@@ -21,16 +21,20 @@
/** /**
* @ingroup MySigninggrp * @ingroup MySigninggrp
* @{ * @{
* @file Sha204Personalizer.ino * @file SecurityPersonalizer.ino
* @brief SHA204 personalization sketch * @brief Security personalization sketch
* *
* REVISION HISTORY * REVISION HISTORY
* - See git log (git log libraries/MySensors/examples/Sha204Personalizer/Sha204Personalizer.ino) * - See git log (git log libraries/MySensors/examples/SecurityPersonalizer/SecurityPersonalizer.ino)
*/ */
/** /**
* @example Sha204Personalizer.ino * @example SecurityPersonalizer.ino
* This sketch will write factory default settings to the configuration zone * This sketch will personalize either none-volatile memory or ATSHA204A for security functions
* available in the MySensors library.
*
* For ATSHA204A:
* It will write factory default settings to the configuration zone
* and then lock it.<br> * and then lock it.<br>
* It will then either<br> * It will then either<br>
* -# Generate a random value to use as a key which will be stored in * -# Generate a random value to use as a key which will be stored in
@@ -45,13 +49,41 @@
* turn on the locking. Furthermore, user have to send a SPACE character on serial * turn on the locking. Furthermore, user have to send a SPACE character on serial
* console when prompted to do any locking. On boards that does not provide UART * console when prompted to do any locking. On boards that does not provide UART
* input it is possible to configure the sketch to skip this confirmation. * input it is possible to configure the sketch to skip this confirmation.
* Default settings use ATSHA204 on @ref ATSHA204_PIN. * Default settings use ATSHA204A on @ref MY_SIGNING_ATSHA204_PIN.
*
* For Soft signing:
* It will<br>
* -# Generate a random value to use as a key which will be stored in EEPROM.
* The key is printed on UART (115200) in clear text for the user to be ablle to
* use it as a user-supplied key in other personalization executions where the same
* key is needed.
* -# Use a user-supplied value to use as a key which will be stored in EEPROM.
* -# Generate a random value to use as a serial number which will be stored in EEPROM.
* The serial number is printed on UART (115200) in clear text for the user to be ablle to
* use it as a user-supplied serial number in other personalization executions where the
* serial is needed (typically for a whitelist).
* -# Use a user-supplied value to use as a serial which will be stored in EEPROM.
*
* For Encryption support:
* -# Generate a random value to use as a AES key which will be stored in EEPROM.
* The AES key is printed on UART (115200) in clear text for the user to be ablle to
* use it as a user-supplied AES key in other personalization executions where the
* AES key is needed (typically for RF encryption).
* -# Use a user-supplied value to use as a AES key which will be stored in EEPROM.
*
* Personalizing EEPROM or ATSHA204A still require the appropriate configuration of the
* library to actually have an effect. There is no problem personalizing EEPROM and
* ATSHA204A at the same time. There is however a security risk with using the same
* data for EEPROM and ATSHA204A so it is recommended to use different serial and HMAC
* keys on the same device for ATSHA204A vs soft signing settings.
* *
* Details on personalization procedure is given in @ref perzonalization. * Details on personalization procedure is given in @ref perzonalization.
*/ */
#include <sha204_library.h> #include <sha204_library.h>
#include <sha204_lib_return_codes.h> #include <sha204_lib_return_codes.h>
#define MY_CORE_ONLY
#include <MySensor.h>
// Doxygen specific constructs, not included when built normally // Doxygen specific constructs, not included when built normally
// This is used to enable disabled macros/definitions to be included in the documentation as well. // This is used to enable disabled macros/definitions to be included in the documentation as well.
@@ -59,12 +91,17 @@
#define LOCK_CONFIGURATION #define LOCK_CONFIGURATION
#define LOCK_DATA #define LOCK_DATA
#define SKIP_KEY_STORAGE #define SKIP_KEY_STORAGE
#define USER_KEY_DATA #define USER_KEY
#define SKIP_UART_CONFIRMATION #define SKIP_UART_CONFIRMATION
#define USE_SOFT_SIGNING
#define STORE_SOFT_KEY
#define USER_SOFT_KEY
#define STORE_SOFT_SERIAL
#define USER_SOFT_SERIAL
#define STORE_AES_KEY
#define USER_AES_KEY
#endif #endif
#define ATSHA204_PIN 17 //!< The pin the ATSHA204 is connected on (A3).
/** /**
* @def LOCK_CONFIGURATION * @def LOCK_CONFIGURATION
* @brief Uncomment this to enable locking the configuration zone. * @brief Uncomment this to enable locking the configuration zone.
@@ -89,13 +126,13 @@
* @def SKIP_KEY_STORAGE * @def SKIP_KEY_STORAGE
* @brief Uncomment this to skip key storage (typically once key has been written once) * @brief Uncomment this to skip key storage (typically once key has been written once)
*/ */
//#define SKIP_KEY_STORAGE #define SKIP_KEY_STORAGE
/** /**
* @def USER_KEY_DATA * @def USER_KEY
* @brief Uncomment this to skip key generation and use @ref user_key_data as key instead. * @brief Uncomment this to skip key generation and use @ref user_key_data as key instead.
*/ */
//#define USER_KEY_DATA //#define USER_KEY
/** /**
* @def SKIP_UART_CONFIRMATION * @def SKIP_UART_CONFIRMATION
@@ -106,19 +143,84 @@
*/ */
//#define SKIP_UART_CONFIRMATION //#define SKIP_UART_CONFIRMATION
#if defined(SKIP_UART_CONFIRMATION) && !defined(USER_KEY_DATA) /**
#error You have to define USER_KEY_DATA for boards that does not have UART * @def USE_SOFT_SIGNING
* @brief Uncomment this to store data to EEPROM instead of ATSHA204A
*/
//#define USE_SOFT_SIGNING
/**
* @def STORE_SOFT_KEY
* @brief Uncomment this to store soft HMAC key to EEPROM
*/
//#define STORE_SOFT_KEY
/**
* @def USER_SOFT_KEY
* @brief Uncomment this to skip soft HMAC key generation and use @ref user_soft_key_data as HMAC key instead.
*/
//#define USER_SOFT_KEY
/**
* @def STORE_SOFT_SERIAL
* @brief Uncomment this to store soft serial to EEPROM
*/
//#define STORE_SOFT_SERIAL
/**
* @def USER_SOFT_SERIAL
* @brief Uncomment this to skip soft serial generation and use @ref user_soft_serial as serial instead.
*/
//#define USER_SOFT_SERIAL
/**
* @def STORE_AES_KEY
* @brief Uncomment this to store AES key to EEPROM
*/
//#define STORE_AES_KEY
/**
* @def USER_AES_KEY
* @brief Uncomment this to skip AES key generation and use @ref user_aes_key as key instead.
*/
//#define USER_AES_KEY
#if defined(SKIP_UART_CONFIRMATION) && !defined(USER_KEY)
#error You have to define USER_KEY for boards that does not have UART
#endif #endif
#ifdef USER_KEY_DATA #ifdef USER_KEY
/** @brief The user-defined HMAC key to use for personalization */ /** @brief The user-defined HMAC key to use for personalization */
#define MY_HMAC_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 #define MY_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
/** @brief The data to store in key slot 0 */ /** @brief The data to store in key slot 0 */
const uint8_t user_key_data[32] = {MY_HMAC_KEY}; const uint8_t user_key_data[32] = {MY_HMAC_KEY};
#endif #endif
const int sha204Pin = ATSHA204_PIN; //!< The IO pin to use for ATSHA204 #ifdef USER_SOFT_KEY
/** @brief The user-defined soft HMAC key to use for EEPROM personalization */
#define MY_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
/** @brief The data to store as soft HMAC key in EEPROM */
const uint8_t user_soft_key_data[32] = {MY_SOFT_HMAC_KEY};
#endif
#ifdef USER_SOFT_SERIAL
/** @brief The user-defined soft serial to use for EEPROM personalization */
#define MY_SOFT_SERIAL 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
/** @brief The data to store as soft serial in EEPROM */
const uint8_t user_soft_serial[9] = {MY_SOFT_SERIAL};
#endif
#ifdef USER_AES_KEY
/** @brief The user-defined AES key to use for EEPROM personalization */
#define MY_AES_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
/** @brief The data to store as AES key in EEPROM */
const uint8_t user_aes_key[16] = {MY_AES_KEY};
#endif
#ifndef USE_SOFT_SIGNING
const int sha204Pin = MY_SIGNING_ATSHA204_PIN; //!< The IO pin to use for ATSHA204A
atsha204Class sha204(sha204Pin); atsha204Class sha204(sha204Pin);
#endif
/** @brief Print a error notice and halt the execution */ /** @brief Print a error notice and halt the execution */
void halt() void halt()
@@ -127,6 +229,7 @@ void halt()
while(1); while(1);
} }
#ifndef USE_SOFT_SIGNING
/** /**
* @brief Write default configuration and return CRC of the configuration bits * @brief Write default configuration and return CRC of the configuration bits
* @returns CRC over the configuration bits * @returns CRC over the configuration bits
@@ -275,14 +378,53 @@ void write_key(uint8_t* key)
halt(); halt();
} }
} }
#endif // not USE_SOFT_SIGNING
/** @brief Dump current configuration to UART */ /** @brief Dump current configuration to UART */
void dump_configuration() void dump_configuration()
{ {
uint8_t buffer[32];
#ifndef USE_SOFT_SIGNING
Serial.println(F("EEPROM DATA:"));
#endif
hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
Serial.print(F("SOFT_HMAC_KEY | "));
for (int j=0; j<32; j++)
{
if (buffer[j] < 0x10)
{
Serial.print('0'); // Because Serial.print does not 0-pad HEX
}
Serial.print(buffer[j], HEX);
}
Serial.println();
hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
Serial.print(F("SOFT_SERIAL | "));
for (int j=0; j<9; j++)
{
if (buffer[j] < 0x10)
{
Serial.print('0'); // Because Serial.print does not 0-pad HEX
}
Serial.print(buffer[j], HEX);
}
Serial.println();
hwReadConfigBlock((void*)buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
Serial.print(F("AES_KEY | "));
for (int j=0; j<16; j++)
{
if (buffer[j] < 0x10)
{
Serial.print('0'); // Because Serial.print does not 0-pad HEX
}
Serial.print(buffer[j], HEX);
}
Serial.println();
#ifndef USE_SOFT_SIGNING
uint8_t tx_buffer[SHA204_CMD_SIZE_MAX]; uint8_t tx_buffer[SHA204_CMD_SIZE_MAX];
uint8_t rx_buffer[SHA204_RSP_SIZE_MAX]; uint8_t rx_buffer[SHA204_RSP_SIZE_MAX];
uint8_t ret_code; uint8_t ret_code;
Serial.println(F("ATSHA204A DATA:"));
for (int i=0; i < 88; i += 4) for (int i=0; i < 88; i += 4)
{ {
ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, i); ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, i);
@@ -712,25 +854,36 @@ void dump_configuration()
Serial.println(); Serial.println();
} }
} }
#endif // not USE_SOFT_SIGNING
} }
/** @brief Sketch setup code */ /** @brief Sketch setup code */
void setup() void setup()
{ {
// Delay startup a bit for serial consoles to catch up
unsigned long enter = hwMillis();
while (hwMillis() - enter < (unsigned long)500);
#ifndef USE_SOFT_SIGNING
uint8_t tx_buffer[SHA204_CMD_SIZE_MAX]; uint8_t tx_buffer[SHA204_CMD_SIZE_MAX];
uint8_t rx_buffer[SHA204_RSP_SIZE_MAX]; uint8_t rx_buffer[SHA204_RSP_SIZE_MAX];
uint8_t key[32];
uint8_t ret_code; uint8_t ret_code;
uint8_t lockConfig = 0; uint8_t lockConfig = 0;
uint8_t lockValue = 0; uint8_t lockValue = 0;
uint16_t crc; uint16_t crc;
(void)crc; (void)crc;
#else
// initialize pseudo-RNG
randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN));
#endif
uint8_t key[32];
(void)key;
Serial.begin(115200); Serial.begin(115200);
Serial.println(F("ATSHA204 personalization sketch for MySensors usage.")); Serial.println(F("Personalization sketch for MySensors usage."));
Serial.println(F("----------------------------------------------------")); Serial.println(F("-------------------------------------------"));
#ifndef USE_SOFT_SIGNING
// Wake device before starting operations // Wake device before starting operations
ret_code = sha204.sha204c_wakeup(rx_buffer); ret_code = sha204.sha204c_wakeup(rx_buffer);
if (ret_code != SHA204_SUCCESS) if (ret_code != SHA204_SUCCESS)
@@ -738,7 +891,171 @@ void setup()
Serial.print(F("Failed to wake device. Response: ")); Serial.println(ret_code, HEX); Serial.print(F("Failed to wake device. Response: ")); Serial.println(ret_code, HEX);
halt(); halt();
} }
// Read out lock config bits to determine if locking is possible
ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, 0x15<<2);
if (ret_code != SHA204_SUCCESS)
{
Serial.print(F("Failed to determine device lock status. Response: ")); Serial.println(ret_code, HEX);
halt();
}
else
{
lockConfig = rx_buffer[SHA204_BUFFER_POS_DATA+3];
lockValue = rx_buffer[SHA204_BUFFER_POS_DATA+2];
}
#endif
#ifdef STORE_SOFT_KEY
#ifdef USER_SOFT_KEY
memcpy(key, user_soft_key_data, 32);
Serial.println(F("Using this user supplied soft HMAC key:"));
#else
// Retrieve random value to use as soft HMAC key
#ifdef USE_SOFT_SIGNING
for (int i = 0; i < 32; i++) {
key[i] = random(256) ^ micros();
unsigned long enter = hwMillis();
while (hwMillis() - enter < (unsigned long)2);
}
Serial.println(F("This value will be stored in EEPROM as soft HMAC key:"));
#else
ret_code = sha204.sha204m_random(tx_buffer, rx_buffer, RANDOM_SEED_UPDATE);
if (ret_code != SHA204_SUCCESS)
{
Serial.print(F("Random key generation failed. Response: ")); Serial.println(ret_code, HEX);
halt();
}
else
{
memcpy(key, rx_buffer+SHA204_BUFFER_POS_DATA, 32);
}
if (lockConfig == 0x00)
{
Serial.println(F("This value will be stored in EEPROM as soft HMAC key:"));
}
else
{
Serial.println(F("Key is not randomized (configuration not locked):"));
}
#endif // not USE_SOFT_SIGNING
#endif // not USER_SOFT_KEY
Serial.print("#define MY_SOFT_HMAC_KEY ");
for (int i=0; i<32; i++)
{
Serial.print("0x");
if (key[i] < 0x10)
{
Serial.print('0'); // Because Serial.print does not 0-pad HEX
}
Serial.print(key[i], HEX);
if (i < 31) Serial.print(',');
}
Serial.println();
hwWriteConfigBlock((void*)key, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
#endif // STORE_SOFT_KEY
#ifdef STORE_SOFT_SERIAL
#ifdef USER_SOFT_SERIAL
memcpy(key, user_soft_serial, 9);
Serial.println(F("Using this user supplied soft serial:"));
#else
// Retrieve random value to use as serial
#ifdef USE_SOFT_SIGNING
for (int i = 0; i < 9; i++) {
key[i] = random(256) ^ micros();
unsigned long enter = hwMillis();
while (hwMillis() - enter < (unsigned long)2);
}
Serial.println(F("This value will be stored in EEPROM as soft serial:"));
#else
ret_code = sha204.sha204m_random(tx_buffer, rx_buffer, RANDOM_SEED_UPDATE);
if (ret_code != SHA204_SUCCESS)
{
Serial.print(F("Random serial generation failed. Response: ")); Serial.println(ret_code, HEX);
halt();
}
else
{
memcpy(key, rx_buffer+SHA204_BUFFER_POS_DATA, 9);
}
if (lockConfig == 0x00)
{
Serial.println(F("This value will be stored in EEPROM as soft serial:"));
}
else
{
Serial.println(F("Serial is not randomized (configuration not locked):"));
}
#endif // not USE_SOFT_SIGNING
#endif // not USER_SOFT_SERIAL
Serial.print("#define MY_SOFT_SERIAL ");
for (int i=0; i<9; i++)
{
Serial.print("0x");
if (key[i] < 0x10)
{
Serial.print('0'); // Because Serial.print does not 0-pad HEX
}
Serial.print(key[i], HEX);
if (i < 8) Serial.print(',');
}
Serial.println();
hwWriteConfigBlock((void*)key, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
#endif // STORE_SOFT_SERIAL
#ifdef STORE_AES_KEY
#ifdef USER_AES_KEY
memcpy(key, user_aes_key, 16);
Serial.println(F("Using this user supplied AES key:"));
#else
// Retrieve random value to use as key
#ifdef USE_SOFT_SIGNING
for (int i = 0; i < 16; i++) {
key[i] = random(256) ^ micros();
unsigned long enter = hwMillis();
while (hwMillis() - enter < (unsigned long)2);
}
Serial.println(F("This key will be stored in EEPROM as AES key:"));
#else
ret_code = sha204.sha204m_random(tx_buffer, rx_buffer, RANDOM_SEED_UPDATE);
if (ret_code != SHA204_SUCCESS)
{
Serial.print(F("Random key generation failed. Response: ")); Serial.println(ret_code, HEX);
halt();
}
else
{
memcpy(key, rx_buffer+SHA204_BUFFER_POS_DATA, 32);
}
if (lockConfig == 0x00)
{
Serial.println(F("This key will be stored in EEPROM as AES key:"));
}
else
{
Serial.println(F("Key is not randomized (configuration not locked):"));
}
#endif // not USE_SOFT_SIGNING
#endif // not USER_AES_KEY
Serial.print("#define MY_AES_KEY ");
for (int i=0; i<16; i++)
{
Serial.print("0x");
if (key[i] < 0x10)
{
Serial.print('0'); // Because Serial.print does not 0-pad HEX
}
Serial.print(key[i], HEX);
if (i < 15) Serial.print(',');
}
Serial.println();
hwWriteConfigBlock((void*)key, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
#endif // STORE_AES_KEY
#ifdef USE_SOFT_SIGNING
Serial.println(F("EEPROM configuration:"));
dump_configuration();
#else
// Output device revision on console // Output device revision on console
ret_code = sha204.sha204m_dev_rev(tx_buffer, rx_buffer); ret_code = sha204.sha204m_dev_rev(tx_buffer, rx_buffer);
if (ret_code != SHA204_SUCCESS) if (ret_code != SHA204_SUCCESS)
@@ -794,19 +1111,6 @@ void setup()
Serial.println(); Serial.println();
} }
// Read out lock config bits to determine if locking is possible
ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, 0x15<<2);
if (ret_code != SHA204_SUCCESS)
{
Serial.print(F("Failed to determine device lock status. Response: ")); Serial.println(ret_code, HEX);
halt();
}
else
{
lockConfig = rx_buffer[SHA204_BUFFER_POS_DATA+3];
lockValue = rx_buffer[SHA204_BUFFER_POS_DATA+2];
}
if (lockConfig != 0x00) if (lockConfig != 0x00)
{ {
// Write config and get CRC for the updated config // Write config and get CRC for the updated config
@@ -886,9 +1190,9 @@ void setup()
#ifdef SKIP_KEY_STORAGE #ifdef SKIP_KEY_STORAGE
Serial.println(F("Disable SKIP_KEY_STORAGE to store key.")); Serial.println(F("Disable SKIP_KEY_STORAGE to store key."));
#else #else
#ifdef USER_KEY_DATA #ifdef USER_KEY
memcpy(key, user_key_data, 32); memcpy(key, user_key_data, 32);
Serial.println(F("Using this user supplied key:")); Serial.println(F("Using this user supplied HMAC key:"));
#else #else
// Retrieve random value to use as key // Retrieve random value to use as key
ret_code = sha204.sha204m_random(tx_buffer, rx_buffer, RANDOM_SEED_UPDATE); ret_code = sha204.sha204m_random(tx_buffer, rx_buffer, RANDOM_SEED_UPDATE);
@@ -1022,9 +1326,11 @@ void setup()
{ {
Serial.println(F("Skipping OTP/data zone lock (zone already locked).")); Serial.println(F("Skipping OTP/data zone lock (zone already locked)."));
} }
#endif // not USE_SOFT_SIGNING
Serial.println(F("--------------------------------")); Serial.println(F("--------------------------------"));
Serial.println(F("Personalization is now complete.")); Serial.println(F("Personalization is now complete."));
#ifndef USE_SOFT_SIGNING
Serial.print(F("Configuration is ")); Serial.print(F("Configuration is "));
if (lockConfig == 0x00) if (lockConfig == 0x00)
{ {
@@ -1043,6 +1349,7 @@ void setup()
{ {
Serial.println("UNLOCKED"); Serial.println("UNLOCKED");
} }
#endif
} }
/** @brief Sketch execution code */ /** @brief Sketch execution code */
+4 -4
View File
@@ -31,6 +31,9 @@ smartSleep KEYWORD2
####################################### #######################################
AUTO LITERAL1 AUTO LITERAL1
MY_DEBUG LITERAL1 MY_DEBUG LITERAL1
MY_CORE_ONLY LITERAL1
MY_DEBUG_VERBOSE LITERAL1
MY_DEBUG_VERBOSE_SIGNING LITERAL1
MY_REPEATER_FEATURE LITERAL1 MY_REPEATER_FEATURE LITERAL1
MY_RADIO_NRF24 LITERAL1 MY_RADIO_NRF24 LITERAL1
MY_RADIO_RFM69 LITERAL1 MY_RADIO_RFM69 LITERAL1
@@ -57,10 +60,7 @@ MY_VERIFICATION_TIMEOUT_MS LITERAL1
MY_SIGNING_NODE_WHITELISTING LITERAL1 MY_SIGNING_NODE_WHITELISTING LITERAL1
MY_SIGNING_ATSHA204_PIN LITERAL1 MY_SIGNING_ATSHA204_PIN LITERAL1
MY_SIGNING_SOFT_RANDOMSEED_PIN LITERAL1 MY_SIGNING_SOFT_RANDOMSEED_PIN LITERAL1
MY_SIGNING_SOFT_SERIAL LITERAL1
MY_SIGNING_SOFT_HMAC_KEY LITERAL1
MY_RF24_ENABLE_ENCRYPTION LITERAL1 MY_RF24_ENABLE_ENCRYPTION LITERAL1
MY_RF24_ENCRYPTKEY LITERAL1
MY_RF24_CE_PIN LITERAL1 MY_RF24_CE_PIN LITERAL1
MY_RF24_CS_PIN LITERAL1 MY_RF24_CS_PIN LITERAL1
MY_RF24_PA_LEVEL LITERAL1 MY_RF24_PA_LEVEL LITERAL1
@@ -78,7 +78,7 @@ MY_RFM69_NETWORKID LITERAL1
MY_RF69_IRQ_PIN LITERAL1 MY_RF69_IRQ_PIN LITERAL1
MY_RF69_SPI_CS LITERAL1 MY_RF69_SPI_CS LITERAL1
MY_RF69_IRQ_NUM LITERAL1 MY_RF69_IRQ_NUM LITERAL1
MY_RFM69_ENCRYPTKEY LITERAL1 MY_RFM69_ENABLE_ENCRYPTION LITERAL1
MY_GATEWAY_SERIAL LITERAL1 MY_GATEWAY_SERIAL LITERAL1
MY_GATEWAY_W5100 LITERAL1 MY_GATEWAY_W5100 LITERAL1
MY_GATEWAY_ENC28J60 LITERAL1 MY_GATEWAY_ENC28J60 LITERAL1
+1 -1
View File
@@ -1,5 +1,5 @@
name=MySensors name=MySensors
version=1.6.0 version=2.0.0-beta
author=The MySensors Team author=The MySensors Team
maintainer=The MySensors Team maintainer=The MySensors Team
sentence=Home Automation Framework sentence=Home Automation Framework
+2
View File
@@ -259,7 +259,9 @@
#define SHA204_COMMAND_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) //! maximum command delay #define SHA204_COMMAND_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) //! maximum command delay
#define SHA204_CMD_SIZE_MIN ((uint8_t) 7) //! minimum number of bytes in command (from count byte to second CRC byte) #define SHA204_CMD_SIZE_MIN ((uint8_t) 7) //! minimum number of bytes in command (from count byte to second CRC byte)
#ifndef SHA204_CMD_SIZE_MAX
#define SHA204_CMD_SIZE_MAX ((uint8_t) 84) //! maximum size of command packet (CheckMac) #define SHA204_CMD_SIZE_MAX ((uint8_t) 84) //! maximum size of command packet (CheckMac)
#endif
#define SHA204_CRC_SIZE ((uint8_t) 2) //! number of CRC bytes #define SHA204_CRC_SIZE ((uint8_t) 2) //! number of CRC bytes
#define SHA204_BUFFER_POS_STATUS (1) //! buffer index of status byte in status response #define SHA204_BUFFER_POS_STATUS (1) //! buffer index of status byte in status response
#define SHA204_BUFFER_POS_DATA (1) //! buffer index of first data byte in data response #define SHA204_BUFFER_POS_DATA (1) //! buffer index of first data byte in data response