Merge branch 'development' into patch-2
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "MyHwESP8266.h"
|
||||
|
||||
#include <EEPROM.h>
|
||||
|
||||
/*
|
||||
int8_t pinIntTrigger = 0;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/** @}*/
|
||||
@@ -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)));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+342
-35
@@ -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 */
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user