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
//#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!
//#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
//#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
#ifdef MY_DISABLED_SERIAL
#undef MY_DEBUG
@@ -280,25 +289,6 @@
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
#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
***********************************/
@@ -315,14 +305,9 @@
* 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
// 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.
#ifndef MY_RF24_CE_PIN
#if defined(ARDUINO_ARCH_ESP8266)
@@ -409,11 +394,8 @@
#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
#ifndef MY_RFM69_ENCRYPTKEY
#define MY_RFM69_ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
#endif
/**************************************
* Ethernet Gateway Transport Defaults
+19 -13
View File
@@ -48,8 +48,10 @@
//#undef F
//#define F(x) (x)
#include "core/MyHwESP8266.cpp"
// For ESP8266, we always enable gateway feature
#define MY_GATEWAY_ESP8266
// Enable gateway feature
#if !defined(MY_CORE_ONLY)
#define MY_GATEWAY_ESP8266
#endif
#elif defined(ARDUINO_ARCH_AVR)
#include "core/MyHwATMega328.cpp"
#elif defined(ARDUINO_ARCH_SAMD)
@@ -93,9 +95,12 @@
// SIGNING
#if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT)
// SIGNING COMMON FUNCTIONS
#include "core/MySigning.cpp"
#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)
#error Only one signing engine can be activated
#endif
@@ -105,7 +110,6 @@
#include "drivers/ATSHA204/ATSHA204.cpp"
#elif defined(MY_SIGNING_SOFT)
#include "core/MySigningAtsha204Soft.cpp"
#include "drivers/ATSHA204/sha256.cpp"
#endif
#endif
@@ -219,22 +223,24 @@
#undef MY_INCLUSION_BUTTON_FEATURE
#endif
#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.
#if !defined(MY_CORE_ONLY)
#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
#include "core/MyCapabilities.h"
#include "core/MyMessage.cpp"
#include "core/MySensorCore.cpp"
#include <Arduino.h>
#if defined(MY_GATEWAY_ESP8266)
#include "core/MyMainESP8266.cpp"
#else
#include "core/MyMainDefault.cpp"
#if !defined(MY_CORE_ONLY)
#if defined(MY_GATEWAY_ESP8266)
#include "core/MyMainESP8266.cpp"
#else
#include "core/MyMainDefault.cpp"
#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 <EEPROM.h>
/*
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_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION,
I_REBOOT, I_GATEWAY_READY,
I_REQUEST_SIGNING, /*!< Indicates sender require signed messages (bool param) */
I_GET_NONCE, /*!< Request for a nonce */
I_GET_NONCE_RESPONSE, /*!< Payload is nonce data */
I_SIGNING_PRESENTATION, /*!< Provides signing related preferences (first byte is preference version) */
I_NONCE_REQUEST, /*!< Request for a nonce */
I_NONCE_RESPONSE, /*!< Payload is nonce data */
I_HEARTBEAT, I_PRESENTATION, I_DISCOVER, I_DISCOVER_RESPONSE, I_HEARTBEAT_RESPONSE
} mysensor_internal;
@@ -40,12 +40,6 @@
// Pin used for random generation in soft signing (do not connect anything to this when enabled)
#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
//#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)
//#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
***********************************/
@@ -66,6 +57,5 @@
// Enable this for encryption of packets
//#define MY_RFM69_ENABLE_ENCRYPTION
#define MY_RFM69_ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
#endif
+5 -10
View File
@@ -62,15 +62,12 @@ static inline bool isValidParent( const uint8_t parent ) {
void _begin() {
#if !defined(MY_DISABLED_SERIAL)
hwInit();
#endif
#if defined(MY_SIGNING_SOFT)
// initialize pseudo-RNG
randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN));
#endif
debug(PSTR("Starting " MY_NODE_TYPE " (" MY_CAPABILIIES ", " LIBRARY_VERSION ")\n"));
signerInit();
#if defined(MY_RADIO_FEATURE)
_failedTransmissions = 0;
@@ -107,11 +104,6 @@ void _begin() {
#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)
ledsInit();
#endif
@@ -190,6 +182,9 @@ ControllerConfig getConfig() {
boolean _sendRoute(MyMessage &message) {
// increment heartbeat counter
_heartbeat++;
#if defined(MY_CORE_ONLY)
(void)message;
#endif
#if defined(MY_GATEWAY_FEATURE)
if (message.destination == _nc.nodeId) {
// 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 "MyConfig.h"
#include "MyEepromAddresses.h"
#include "MyMessage.h"
#ifdef MY_OTA_FIRMWARE_FEATURE
#include "drivers/SPIFlash/SPIFlash.h"
@@ -37,23 +38,6 @@
#define debug(x,...)
#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).
#define GATEWAY_ADDRESS ((uint8_t)0)
// Node child is always created/presented when a node is started
+357 -2
View File
@@ -18,8 +18,363 @@
*/
#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
MyMessage _msgSign; // Buffer for message to sign.
#if defined(MY_DEBUG_VERBOSE_SIGNING)
#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
* 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
* 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.
* <b>Thirdly</b>, if you use the software backend, you need to personalize the node (see @ref perzonalization).
* @code{.cpp}
* #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_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
* #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>.
* 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
* - Writing and locking chip configuration
@@ -211,15 +208,15 @@
* if this is desired.<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
* @ref USER_KEY_DATA, 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
* @ref USER_KEY, which, if defined, will use the data in the variable user_key_data.<br>
* 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>
* 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
* 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 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>
* 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
@@ -232,7 +229,7 @@
* -# Disable @ref LOCK_DATA
* -# Enable @ref SKIP_KEY_STORAGE
* -# 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
you can retrieve it if you need to personalize more devices later on.
* - 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)
* -# Disable @ref SKIP_KEY_STORAGE
* -# Enable @ref SKIP_UART_CONFIRMATION
* -# Enable @ref USER_KEY_DATA
* -# Enable @ref USER_KEY
* - 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.
*
* 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.
*
* 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
* 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
* 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
* 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>
* 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.
@@ -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.
*
* 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
* 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
* node in the network.<br>
* 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
* in a copy+paste friendly format by the personalizer for this purpose.<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
* 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>
* 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
* 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
* 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
* provide and use whitelists, as well as transmitting to a node with a whitelist, you need to enable @ref MY_SIGNING_NODE_WHITELISTING.<br>
* Whitelisting is an optional alternative because it adds some code and configuration options which might not be desirable for every user. So if
* 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):
* @code{.cpp}
* #define MY_SIGNING_ATSHA204
@@ -314,31 +328,26 @@
* ...
* @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
* 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
* setting (@ref MY_SIGNING_SOFT_SERIAL) when you enable whitelisting as illustrated in this example:
* The "soft" backend of course also support whitelisting. Example:
* @code{.cpp}
* #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_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_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>
* ...
* @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}
* #define MY_SIGNING_NODE_WHITELISTING {}
* #define MY_SIGNING_SOFT
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
* @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
* in your sketch.
*
* 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.
* Remember that you always need to select a signing backend for all nodes that communicate to a node that require whitelisting.
* 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).
*
* @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
* <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>
* @ref I_REQUEST_SIGNING <br>
* @ref I_GET_NONCE <br>
* @ref I_GET_NONCE_RESPONSE <br>
* @ref I_SIGNING_PRESENTATION <br>
* @ref I_NONCE_REQUEST <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.
*
@@ -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>
* 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>
* 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}
* #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_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_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}}}
* #include <MySensor.h>
* ...
* @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>
* @code{.cpp}
* #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_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_NODE_WHITELISTING {{.nodeId = MOTION_SENSOR_ID,.serial = {0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78}}}
* #include <MySensor.h>
@@ -441,16 +445,14 @@
* Configuration example for the keyfob (keyfob will only transmit to another node and not receive anything):<br>
* @code{.cpp}
* #define MY_SIGNING_ATSHA204
* #define MY_SIGNING_NODE_WHITELISTING {}
* #include <MySensor.h>
* ...
* @endcode
*
* Configuration example for the door controller node (should require signing from anyone who wants to control it):<br>
* @code{.cpp}
* #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_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_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>
@@ -460,7 +462,7 @@
* @subsection MySigningsketches Relevant sketches
*
* - @ref SecureActuator.ino
* - @ref Sha204Personalizer.ino
* - @ref SecurityPersonalizer.ino
*/
/**
@@ -484,29 +486,50 @@ typedef struct {
} whitelist_entry_t;
#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 */
#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>
* Returns @c false if subsystem is busy processing an ongoing signing operation.<br>
* 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.
* \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.
* This function makes sure that the internal states of the signing infrastructure
* is set to a known initial state.
* \n@b Usage: This fuction should be called before any signing related operations take place.
*/
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.
@@ -525,7 +548,7 @@ bool signerCheckTimer(void);
* 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
* 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.
*
* @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
* 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>
* 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>
* 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.
* \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.
*
* @param msg The message to sign.
@@ -568,5 +591,32 @@ bool signerSignMsg(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
/** @}*/
+42 -42
View File
@@ -29,7 +29,7 @@
#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);
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 _singning_rx_buffer[SHA204_RSP_SIZE_MAX];
uint8_t _singning_tx_buffer[SHA204_CMD_SIZE_MAX];
extern uint8_t _doWhitelist[32];
#ifdef MY_SIGNING_NODE_WHITELISTING
const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
#endif
void signerCalculateSignature(MyMessage &msg);
uint8_t* signerSha256(const uint8_t* data, size_t sz);
static void signerCalculateSignature(MyMessage &msg);
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)
{
uint8_t k = i & 0x0F;
@@ -57,8 +58,7 @@ static char i2h(uint8_t i)
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];
#ifdef MY_GATEWAY_FEATURE
// 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)
#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);
// 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
@@ -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
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
msg.set(_signing_current_nonce, MAX_PAYLOAD);
_signing_verification_ongoing = true;
@@ -118,33 +131,15 @@ bool signerGetNonce(MyMessage &msg) {
return true;
}
bool signerCheckTimer() {
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) {
void signerAtsha204PutNonce(MyMessage &msg) {
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);
// 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);
return true;
}
bool signerSignMsg(MyMessage &msg) {
bool signerAtsha204SignMsg(MyMessage &msg) {
// If we cannot fit any signature in the message, refuse to sign it
if (mGetLength(msg) > MAX_PAYLOAD-2) {
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
signerCalculateSignature(msg);
#ifdef MY_SIGNING_NODE_WHITELISTING
// 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
_signing_current_nonce[32] = msg.sender;
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
DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0);
#endif
if (DO_WHITELIST(msg.destination)) {
// 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
_signing_current_nonce[32] = msg.sender;
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
DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0);
}
// Overwrite the first byte in the signature with the signing identifier
_singning_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
@@ -174,7 +169,7 @@ bool signerSignMsg(MyMessage &msg) {
return true;
}
bool signerVerifyMsg(MyMessage &msg) {
bool signerAtsha204VerifyMsg(MyMessage &msg) {
if (!_signing_verification_ongoing) {
DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0);
return false;
@@ -196,7 +191,8 @@ bool signerVerifyMsg(MyMessage &msg) {
#ifdef MY_SIGNING_NODE_WHITELISTING
// Look up the senders nodeId in our whitelist and salt the signature with that data
for (int j=0; j < NUM_OF(_signing_whitelist); j++) {
size_t j;
for (j=0; j < NUM_OF(_signing_whitelist); j++) {
if (_signing_whitelist[j].nodeId == msg.sender) {
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
@@ -206,6 +202,10 @@ bool signerVerifyMsg(MyMessage &msg) {
break;
}
}
if (j == NUM_OF(_signing_whitelist)) {
DEBUG_SIGNING_PRINTBUF(F("Sender not found in whitelist, message rejected!"), NULL, 0);
return false;
}
#endif
// 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])
void signerCalculateSignature(MyMessage &msg) {
static void signerCalculateSignature(MyMessage &msg) {
memset(_signing_temp_message, 0, 32);
memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
@@ -259,7 +259,7 @@ void signerCalculateSignature(MyMessage &msg) {
// 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])
uint8_t* signerSha256(const uint8_t* data, size_t sz) {
static uint8_t* signerSha256(const uint8_t* data, size_t sz) {
// Initiate SHA256 calculator
(void)atsha204.sha204m_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL,
SHA_COUNT_SHORT, _singning_tx_buffer, SHA_RSP_SIZE_SHORT, _singning_rx_buffer);
@@ -33,24 +33,25 @@
#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;
unsigned long _signing_timestamp;
bool _signing_verification_ongoing = false;
uint8_t _signing_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH];
uint8_t _signing_temp_message[32];
static uint8_t _signing_hmac_key[32] = { MY_SIGNING_SOFT_HMAC_KEY };
static uint8_t _signing_hmac_key[32];
uint8_t _signing_hmac[32];
extern uint8_t _doWhitelist[32];
static uint8_t _signing_node_serial_info[9];
#ifdef MY_SIGNING_NODE_WHITELISTING
const uint8_t _signing_node_serial_info[SHA204_SERIAL_SZ] = {MY_SIGNING_SOFT_SERIAL};
const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
#endif
void signerCalculateSignature(MyMessage &msg);
static void signerCalculateSignature(MyMessage &msg);
#ifdef MY_DEBUG
#ifdef MY_DEBUG_VERBOSE_SIGNING
static char i2h(uint8_t i)
{
uint8_t k = i & 0x0F;
@@ -60,8 +61,7 @@ static char i2h(uint8_t i)
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];
#ifdef MY_GATEWAY_FEATURE
// 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)
#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);
// 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
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
msg.set(_signing_current_nonce, MAX_PAYLOAD);
_signing_verification_ongoing = true;
@@ -118,33 +136,15 @@ bool signerGetNonce(MyMessage &msg) {
return true;
}
bool signerCheckTimer() {
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) {
void signerAtsha204SoftPutNonce(MyMessage &msg) {
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);
// 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);
return true;
}
bool signerSignMsg(MyMessage &msg) {
bool signerAtsha204SoftSignMsg(MyMessage &msg) {
// If we cannot fit any signature in the message, refuse to sign it
if (mGetLength(msg) > MAX_PAYLOAD-2) {
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
signerCalculateSignature(msg);
#ifdef MY_SIGNING_NODE_WHITELISTING
// Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided
_signing_sha256.init();
for (int i=0; i<32; i++) _signing_sha256.write(_signing_hmac[i]);
_signing_sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) _signing_sha256.write(_signing_node_serial_info[i]);
memcpy(_signing_hmac, _signing_sha256.result(), 32);
DEBUG_SIGNING_PRINTBUF(F("SHA256: "), _signing_hmac, 32);
DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0);
#endif
if (DO_WHITELIST(msg.destination)) {
// Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided
_signing_sha256.init();
for (int i=0; i<32; i++) _signing_sha256.write(_signing_hmac[i]);
_signing_sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) _signing_sha256.write(_signing_node_serial_info[i]);
memcpy(_signing_hmac, _signing_sha256.result(), 32);
DEBUG_SIGNING_PRINTBUF(F("SHA256: "), _signing_hmac, 32);
DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0);
}
// Overwrite the first byte in the signature with the signing identifier
_signing_hmac[0] = SIGNING_IDENTIFIER;
@@ -176,7 +176,7 @@ bool signerSignMsg(MyMessage &msg) {
return true;
}
bool signerVerifyMsg(MyMessage &msg) {
bool signerAtsha204SoftVerifyMsg(MyMessage &msg) {
if (!_signing_verification_ongoing) {
DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0);
return false;
@@ -199,7 +199,8 @@ bool signerVerifyMsg(MyMessage &msg) {
#ifdef MY_SIGNING_NODE_WHITELISTING
// Look up the senders nodeId in our whitelist and salt the signature with that data
for (int j=0; j < NUM_OF(_signing_whitelist); j++) {
size_t j;
for (j=0; j < NUM_OF(_signing_whitelist); j++) {
if (_signing_whitelist[j].nodeId == msg.sender) {
DEBUG_SIGNING_PRINTBUF(F("Sender found in whitelist"), NULL, 0);
_signing_sha256.init();
@@ -211,6 +212,10 @@ bool signerVerifyMsg(MyMessage &msg) {
break;
}
}
if (j == NUM_OF(_signing_whitelist)) {
DEBUG_SIGNING_PRINTBUF(F("Sender not found in whitelist, message rejected!"), NULL, 0);
return false;
}
#endif
// 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)
void signerCalculateSignature(MyMessage &msg) {
static void signerCalculateSignature(MyMessage &msg) {
memset(_signing_temp_message, 0, 32);
memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
DEBUG_SIGNING_PRINTBUF(F("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;
uint8_t _failedTransmissions;
#ifdef MY_SIGNING_FEATURE
uint8_t _signingNonceStatus;
#endif
#ifdef MY_OTA_FIRMWARE_FEATURE
SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID);
NodeFirmwareConfig _fc;
@@ -70,9 +66,7 @@ inline void transportProcess() {
return;
}
#ifdef MY_SIGNING_FEATURE
(void)signerCheckTimer(); // Manage signing timeout
#endif
uint8_t payloadLength = transportReceive((uint8_t *)&_msg);
(void)payloadLength; //until somebody makes use of it
@@ -86,34 +80,12 @@ inline void transportProcess() {
uint8_t destination = _msg.destination;
#ifdef MY_SIGNING_FEATURE
// Before processing message, reject unsigned messages if signing is required and check signature (if it is signed and addressed to us)
// Note that we do not care at all about any signature found if we do not require signing, nor do we care about ACKs (they are never signed)
#ifdef MY_SIGNING_REQUEST_SIGNATURES
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
// Reject massages that do not pass verification
if (!signerVerifyMsg(_msg)) {
debug(PSTR("verify fail\n"));
ledBlinkErr(1);
return;
}
if (destination == _nc.nodeId) {
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) {
// 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)
mSetLength(_msg, min(mGetLength(_msg),MAX_PAYLOAD));
// null terminate data
@@ -163,6 +134,10 @@ inline void transportProcess() {
}
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 (_autoFindParent) {
// We've received a reply to a FIND_PARENT message. Check if the distance is
@@ -183,44 +158,6 @@ inline void transportProcess() {
}
}
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) {
if (type == I_ID_RESPONSE && _nc.nodeId == AUTO) {
_nc.nodeId = _msg.getByte();
@@ -414,47 +351,10 @@ boolean transportSendRoute(MyMessage &message) {
mSetVersion(message, PROTOCOL_VERSION);
#ifdef MY_SIGNING_FEATURE
uint8_t type = message.type;
// If destination is known to require signed messages and we are the sender, sign this message unless it is an ACK or a handshake message
if (DO_SIGN(message.destination) && message.sender == _nc.nodeId && !mGetAck(message) &&
(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 (!signerSignMsg(message)) {
debug(PSTR("sign fail\n"));
ledBlinkErr(1);
}
#if !defined(MY_REPEATER_FEATURE)
@@ -552,19 +452,8 @@ void transportPresentNode() {
// Present node and request config
#ifndef MY_GATEWAY_FEATURE
if (_nc.nodeId != AUTO) {
#ifdef MY_SIGNING_FEATURE
// Notify gateway (and possibly controller) about the signing preferences of this node
#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 signing preferences for this node
signerPresentation(_msg);
// Send presentation for this radio node
#ifdef MY_REPEATER_FEATURE
-3
View File
@@ -41,9 +41,6 @@
// Search for a new parent node after this many transmission failures
#define SEARCH_FAILURES 5
// Status when waiting for signing nonce in process
enum { SIGN_WAITING_FOR_NONCE = 0, SIGN_OK = 1 };
// FW config structure, stored in eeprom
typedef struct {
@@ -38,7 +38,7 @@ uint8_t _address;
#if defined(MY_RF24_ENABLE_ENCRYPTION)
AES _aes;
uint8_t _dataenc[32] = {0};
uint8_t _psk[] = { MY_RF24_ENCRYPTKEY };
uint8_t _psk[16];
#endif
bool transportInit() {
@@ -65,7 +65,9 @@ bool transportInit() {
#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
memset(_psk, 0, 16); // Make sure it is purged from memory when set
#endif
//_rf24.printDetails();
@@ -30,7 +30,10 @@ bool transportInit() {
// Start up the radio library (_address will be set later by the MySensors library)
if (_radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID)) {
#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
return true;
}
+1 -1
View File
@@ -6,6 +6,6 @@
#ifndef Version_h
#define Version_h
#define LIBRARY_VERSION "1.6.0-beta"
#define LIBRARY_VERSION "2.0.0-beta"
#endif
+5 -5
View File
@@ -35,7 +35,7 @@
/* code was modified by george spanos <spaniakos@gmail.com>
* 16/12/14
*/
/** AES class */
class AES
{
public:
@@ -351,7 +351,7 @@ class AES
* @defgroup aeslib AES library for Arduino and Raspberry pi
* @ingroup internals
*
* @section Goals design Goals
* @section AesGoals design Goals
*
* This library is AESigned to be...
* @li Fast and efficient.
@@ -393,10 +393,10 @@ class AES
* cd examples_Rpi<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>
* @section Useful Useful References
* @section AesUseful Useful References
*
* 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="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:
* - 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_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)
#endif
#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_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);
uint8_t* result(void);
uint8_t* resultHmac(void);
virtual void write(uint8_t);
void write(uint8_t);
private:
void pad();
void addUncounted(uint8_t data);
@@ -23,16 +23,18 @@
* 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 <MySensor.h>
void setup()
{
Serial.begin(MY_BAUD_RATE);
Serial.println("Started clearing. Please wait...");
for (int i=0;i<512;i++) {
EEPROM.write(i, 0xff);
for (int i=0;i<EEPROM_LOCAL_CONFIG_ADDRESS;i++) {
hwWriteConfig(i,0xFF);
}
Serial.println("Clearing done. You're ready to go!");
}
@@ -40,4 +42,4 @@ void setup()
void loop()
{
// Nothing to do here...
}
}
@@ -37,12 +37,12 @@
* 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
* 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
* 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_VERBOSE_SIGNING //!< Enable signing related debug prints to serial monitor
// Enable and select radio type attached
#define MY_RADIO_NRF24 //!< NRF24L01 radio driver
@@ -59,11 +59,6 @@
// 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
// SETTINGS FOR MY_SIGNING_ATSHA204
@@ -21,16 +21,20 @@
/**
* @ingroup MySigninggrp
* @{
* @file Sha204Personalizer.ino
* @brief SHA204 personalization sketch
* @file SecurityPersonalizer.ino
* @brief Security personalization sketch
*
* 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
* This sketch will write factory default settings to the configuration zone
* @example SecurityPersonalizer.ino
* 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>
* It will then either<br>
* -# 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
* 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.
* 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.
*/
#include <sha204_library.h>
#include <sha204_lib_return_codes.h>
#define MY_CORE_ONLY
#include <MySensor.h>
// Doxygen specific constructs, not included when built normally
// 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_DATA
#define SKIP_KEY_STORAGE
#define USER_KEY_DATA
#define USER_KEY
#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
#define ATSHA204_PIN 17 //!< The pin the ATSHA204 is connected on (A3).
/**
* @def LOCK_CONFIGURATION
* @brief Uncomment this to enable locking the configuration zone.
@@ -89,13 +126,13 @@
* @def SKIP_KEY_STORAGE
* @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.
*/
//#define USER_KEY_DATA
//#define USER_KEY
/**
* @def SKIP_UART_CONFIRMATION
@@ -106,19 +143,84 @@
*/
//#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
#ifdef USER_KEY_DATA
#ifdef USER_KEY
/** @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
/** @brief The data to store in key slot 0 */
const uint8_t user_key_data[32] = {MY_HMAC_KEY};
#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);
#endif
/** @brief Print a error notice and halt the execution */
void halt()
@@ -127,6 +229,7 @@ void halt()
while(1);
}
#ifndef USE_SOFT_SIGNING
/**
* @brief Write default configuration and return CRC of the configuration bits
* @returns CRC over the configuration bits
@@ -275,14 +378,53 @@ void write_key(uint8_t* key)
halt();
}
}
#endif // not USE_SOFT_SIGNING
/** @brief Dump current configuration to UART */
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 rx_buffer[SHA204_RSP_SIZE_MAX];
uint8_t ret_code;
Serial.println(F("ATSHA204A DATA:"));
for (int i=0; i < 88; i += 4)
{
ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, i);
@@ -712,25 +854,36 @@ void dump_configuration()
Serial.println();
}
}
#endif // not USE_SOFT_SIGNING
}
/** @brief Sketch setup code */
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 rx_buffer[SHA204_RSP_SIZE_MAX];
uint8_t key[32];
uint8_t ret_code;
uint8_t lockConfig = 0;
uint8_t lockValue = 0;
uint16_t 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.println(F("ATSHA204 personalization sketch for MySensors usage."));
Serial.println(F("----------------------------------------------------"));
Serial.println(F("Personalization sketch for MySensors usage."));
Serial.println(F("-------------------------------------------"));
#ifndef USE_SOFT_SIGNING
// Wake device before starting operations
ret_code = sha204.sha204c_wakeup(rx_buffer);
if (ret_code != SHA204_SUCCESS)
@@ -738,7 +891,171 @@ void setup()
Serial.print(F("Failed to wake device. Response: ")); Serial.println(ret_code, HEX);
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
ret_code = sha204.sha204m_dev_rev(tx_buffer, rx_buffer);
if (ret_code != SHA204_SUCCESS)
@@ -794,19 +1111,6 @@ void setup()
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)
{
// Write config and get CRC for the updated config
@@ -886,9 +1190,9 @@ void setup()
#ifdef SKIP_KEY_STORAGE
Serial.println(F("Disable SKIP_KEY_STORAGE to store key."));
#else
#ifdef USER_KEY_DATA
#ifdef USER_KEY
memcpy(key, user_key_data, 32);
Serial.println(F("Using this user supplied key:"));
Serial.println(F("Using this user supplied HMAC key:"));
#else
// Retrieve random value to use as key
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)."));
}
#endif // not USE_SOFT_SIGNING
Serial.println(F("--------------------------------"));
Serial.println(F("Personalization is now complete."));
#ifndef USE_SOFT_SIGNING
Serial.print(F("Configuration is "));
if (lockConfig == 0x00)
{
@@ -1043,6 +1349,7 @@ void setup()
{
Serial.println("UNLOCKED");
}
#endif
}
/** @brief Sketch execution code */
+4 -4
View File
@@ -31,6 +31,9 @@ smartSleep KEYWORD2
#######################################
AUTO LITERAL1
MY_DEBUG LITERAL1
MY_CORE_ONLY LITERAL1
MY_DEBUG_VERBOSE LITERAL1
MY_DEBUG_VERBOSE_SIGNING LITERAL1
MY_REPEATER_FEATURE LITERAL1
MY_RADIO_NRF24 LITERAL1
MY_RADIO_RFM69 LITERAL1
@@ -57,10 +60,7 @@ MY_VERIFICATION_TIMEOUT_MS LITERAL1
MY_SIGNING_NODE_WHITELISTING LITERAL1
MY_SIGNING_ATSHA204_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_ENCRYPTKEY LITERAL1
MY_RF24_CE_PIN LITERAL1
MY_RF24_CS_PIN LITERAL1
MY_RF24_PA_LEVEL LITERAL1
@@ -78,7 +78,7 @@ MY_RFM69_NETWORKID LITERAL1
MY_RF69_IRQ_PIN LITERAL1
MY_RF69_SPI_CS LITERAL1
MY_RF69_IRQ_NUM LITERAL1
MY_RFM69_ENCRYPTKEY LITERAL1
MY_RFM69_ENABLE_ENCRYPTION LITERAL1
MY_GATEWAY_SERIAL LITERAL1
MY_GATEWAY_W5100 LITERAL1
MY_GATEWAY_ENC28J60 LITERAL1
+1 -1
View File
@@ -1,5 +1,5 @@
name=MySensors
version=1.6.0
version=2.0.0-beta
author=The MySensors Team
maintainer=The MySensors Team
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_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)
#endif
#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_DATA (1) //! buffer index of first data byte in data response