Signing improvements package (#871)
* Signing support with MAX_PAYLOAD>32 Support in the signing backends to handle configurations where MAX_PAYLOAD is more than 32 bytes. Fixes #748 * Support big message signatures Support for signing messages larger than 32 bytes. This is not fully testable with the current version of the library but the change would add theoretical support for signing any sized messages. Fixes #749 * Have gw properly indicate whitelisting preferences If the gateway hold a signing whitelist, inform all nodes of this requirement and not just nodes that hold whitelists themselves. Fixes #806 * Harden security The flag MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL has been removed. Gateway will now request signatures from everyone by default. In addition to this, no nodes in the network will allow clearing of signing/whitelisting preferences by OTA commands by default. If the old behaviour is needed (suitable for gradual signing roll out and development purposes) a new flag, MY_SIGNING_WEAK_SECURITY can be set. Fixes #807 * Signing debug messages rewritten All signing related debug has been rewritten to better match other core debug printouts. Also, backends are updated to have a smaller implementation delta. * Simplified signing option implemented Enable by MY_SIGNING_SIMPLE_PASSWD. Signing, and signing requirements will be enabled, as will encryption. Whitelisting is optional. The value provided to MY_SIGNING_SIMPLE_PASSWD is used as HMAC and AES key. Whitelists use the first 8 bytes of the password and the ninth byte from the node ID as serial. Password is required to be at least 8 characters wide. It is zero padded if it is not long enough to cover the AES (16 bytes) and HMAC (32 bytes) keys. It is not recommended to have a password shorter than 32 bytes, and there is no need for a password longer than 32 bytes. * Add documentation section for signing debug * Add nRF5 encryption to simple signing option * Restructure MySigning documentation * Minor fixes for Linux
This commit is contained in:
committed by
Patrick Fallberg
parent
4c0661e410
commit
28c4f3f19e
@@ -0,0 +1,8 @@
|
|||||||
|
-IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino
|
||||||
|
-IC:\Program Files (x86)\Arduino\hardware\tools\avr\avr\include
|
||||||
|
-Idrivers\Linux
|
||||||
|
-Idrivers\ATSHA204
|
||||||
|
-Icore
|
||||||
|
-DMY_SIGNING_ATSHA204
|
||||||
|
-DMY_SIGNING_SOFT
|
||||||
|
-DARDUINO_ARCH_AVR
|
||||||
@@ -2,3 +2,4 @@
|
|||||||
drivers/Linux
|
drivers/Linux
|
||||||
drivers/AES
|
drivers/AES
|
||||||
drivers/BCM
|
drivers/BCM
|
||||||
|
core
|
||||||
|
|||||||
+49
-13
@@ -400,17 +400,44 @@
|
|||||||
* Message Signing Settings
|
* Message Signing Settings
|
||||||
***********************************/
|
***********************************/
|
||||||
/**
|
/**
|
||||||
* @def MY_DEBUG_VERBOSE_SIGNING
|
* @def MY_DEBUG_VERBOSE_SIGNING
|
||||||
* @brief Flag for verbose debug prints related to signing. Requires DEBUG to be enabled.
|
* @brief 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!
|
* This will add even more to the size of the final sketch!
|
||||||
*/
|
*/
|
||||||
//#define MY_DEBUG_VERBOSE_SIGNING
|
//#define MY_DEBUG_VERBOSE_SIGNING
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def MY_SIGNING_SIMPLE_PASSWD
|
||||||
|
* @brief Enables SW backed signing functionality in library and uses provided password as key.
|
||||||
|
*
|
||||||
|
* This flag will enable signing, signature requests and encryption. It has to be identical on ALL
|
||||||
|
* nodes in the network.<br>
|
||||||
|
* Whitelisting is supported and serial will be the first 8 characters of the password, the ninth
|
||||||
|
* character will be the node ID (to make each node have a unique serial).<br>
|
||||||
|
* As with the regular signing modes, whitelisting is only activated if a whitelist is specified in
|
||||||
|
* the sketch.
|
||||||
|
* No personalization is required for this mode.<br>
|
||||||
|
* It is allowed to set @ref MY_SIGNING_WEAK_SECURITY for deployment purposes in this mode as it is
|
||||||
|
* with the regular software and ATSHA204A based modes.<br>
|
||||||
|
* If the provided password is shorter than the size of the HMAC or AES key, it will be null-padded
|
||||||
|
* to accomodate the key size in question. A 32 character password is the maximum length. Any
|
||||||
|
* password longer than that will be truncated.
|
||||||
|
*/
|
||||||
|
//#define MY_SIGNING_SIMPLE_PASSWD "MyInsecurePassword"
|
||||||
|
#if defined(MY_SIGNING_SIMPLE_PASSWD)
|
||||||
|
#define MY_SIGNING_SOFT
|
||||||
|
#define MY_SIGNING_REQUEST_SIGNATURES
|
||||||
|
#define MY_RF24_ENABLE_ENCRYPTION
|
||||||
|
#define MY_RFM69_ENABLE_ENCRYPTION
|
||||||
|
#define MY_NRF5_ESB_ENABLE_ENCRYPTION
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def MY_SIGNING_ATSHA204
|
* @def MY_SIGNING_ATSHA204
|
||||||
* @brief Enables HW backed signing functionality in library.
|
* @brief Enables HW backed signing functionality in library.
|
||||||
*
|
*
|
||||||
* For any signing related functionality to be included, this define or @ref MY_SIGNING_SOFT has to be enabled.
|
* For any signing related functionality to be included, this define or @ref MY_SIGNING_SOFT has to
|
||||||
|
* be enabled.
|
||||||
*/
|
*/
|
||||||
//#define MY_SIGNING_ATSHA204
|
//#define MY_SIGNING_ATSHA204
|
||||||
|
|
||||||
@@ -418,7 +445,8 @@
|
|||||||
* @def MY_SIGNING_SOFT
|
* @def MY_SIGNING_SOFT
|
||||||
* @brief Enables SW backed signing functionality in library.
|
* @brief Enables SW backed signing functionality in library.
|
||||||
*
|
*
|
||||||
* For any signing related functionality to be included, this define or @ref MY_SIGNING_ATSHA204 has to be enabled.
|
* For any signing related functionality to be included, this define or @ref MY_SIGNING_ATSHA204 has
|
||||||
|
* to be enabled.
|
||||||
*/
|
*/
|
||||||
//#define MY_SIGNING_SOFT
|
//#define MY_SIGNING_SOFT
|
||||||
|
|
||||||
@@ -432,14 +460,21 @@
|
|||||||
//#define MY_SIGNING_REQUEST_SIGNATURES
|
//#define MY_SIGNING_REQUEST_SIGNATURES
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL
|
* @def MY_SIGNING_WEAK_SECURITY
|
||||||
* @brief Enable this to have gateway require all nodes in the network to sign messages sent to it.
|
* @brief Enable this to permit downgrade of security preferences and relaxed gateway signing
|
||||||
* @ref MY_SIGNING_REQUEST_SIGNATURES must also be set.
|
* requirements.
|
||||||
*
|
*
|
||||||
* Use this for maximum security, but be aware that every single node will have to be personalized before they can be used.
|
* Use this for evaluating security. It allows for gradual introduction of signing requirements in
|
||||||
* Note that if this is enabled, and whitelisting is also enabled, whitelisting will also be in effect for all nodes.
|
* a network. Nodes that present themselves as not requiering signing or whitelisting will be
|
||||||
|
* cleared of this requirement at the receiving end. A gateway which require signatures will only do
|
||||||
|
* so from nodes that in turn require signatures.<br>
|
||||||
|
* When not set, any node that has presented themselves as a node that require signatures or
|
||||||
|
* whitelisting, will be permanently remembered as doing so at the receiver until EEPROM is cleared
|
||||||
|
* or the receiver is reconfigured with this flag set or has signing disabled alltogether.
|
||||||
|
*
|
||||||
|
* @warning This flag when set will weaken security significantly
|
||||||
*/
|
*/
|
||||||
//#define MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL
|
//#define MY_SIGNING_WEAK_SECURITY
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def MY_VERIFICATION_TIMEOUT_MS
|
* @def MY_VERIFICATION_TIMEOUT_MS
|
||||||
@@ -1294,10 +1329,11 @@
|
|||||||
#define MY_INDICATION_HANDLER
|
#define MY_INDICATION_HANDLER
|
||||||
#define MY_DISABLE_REMOTE_RESET
|
#define MY_DISABLE_REMOTE_RESET
|
||||||
// signing
|
// signing
|
||||||
|
#define MY_SIGNING_SIMPLE_PASSWD
|
||||||
#define MY_SIGNING_ATSHA204
|
#define MY_SIGNING_ATSHA204
|
||||||
#define MY_SIGNING_SOFT
|
#define MY_SIGNING_SOFT
|
||||||
#define MY_SIGNING_REQUEST_SIGNATURES
|
#define MY_SIGNING_REQUEST_SIGNATURES
|
||||||
#define MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL
|
#define MY_SIGNING_WEAK_SECURITY
|
||||||
#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}}
|
#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}}
|
||||||
#define MY_DEBUG_VERBOSE_SIGNING
|
#define MY_DEBUG_VERBOSE_SIGNING
|
||||||
// RS485
|
// RS485
|
||||||
|
|||||||
@@ -91,15 +91,16 @@ MySensors options:
|
|||||||
--my-leds-rx-pin=<PIN> Receive LED pin.
|
--my-leds-rx-pin=<PIN> Receive LED pin.
|
||||||
--my-leds-tx-pin=<PIN> Transmit LED pin.
|
--my-leds-tx-pin=<PIN> Transmit LED pin.
|
||||||
--my-leds-blinking-inverse Inverse the blinking feature.
|
--my-leds-blinking-inverse Inverse the blinking feature.
|
||||||
--my-signing=[none|software]
|
--my-signing=[none|software|password]
|
||||||
Message signing. [none]
|
Message signing. [none]
|
||||||
--my-signing-debug Enable signing related debug.
|
--my-signing-debug Enable signing related debug.
|
||||||
--my-signing-request-signatures
|
--my-signing-request-signatures
|
||||||
Enable signature request from nodes that in turn requested
|
Enable signature request from nodes that in turn requested
|
||||||
gateway signature.
|
gateway signature.
|
||||||
--my-signing-request-gw-signatures-from-all
|
--my-signing-weak_security Enable this to permit downgrade of security preferences and
|
||||||
Require all nodes in the network to sign messages sent to the
|
relaxed gateway signing requirements.
|
||||||
gateway.
|
--my-signing-password=<PASSWORD>
|
||||||
|
If you are using password as the signature type, set your password here.
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
@@ -459,9 +460,11 @@ for opt do
|
|||||||
--my-signing-request-signatures*)
|
--my-signing-request-signatures*)
|
||||||
signing_request_signatures=true
|
signing_request_signatures=true
|
||||||
;;
|
;;
|
||||||
--my-signing-request-gw-signatures-from-all*)
|
--my-signing-weak_security*)
|
||||||
signing_request_signatures=true
|
CPPFLAGS="-DMY_SIGNING_WEAK_SECURITY $CPPFLAGS"
|
||||||
CPPFLAGS="-DMY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL $CPPFLAGS"
|
;;
|
||||||
|
--my-signing-password=*)
|
||||||
|
signing_password=${optarg}
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "[WARNING] Unknown option detected:$opt, ignored"
|
echo "[WARNING] Unknown option detected:$opt, ignored"
|
||||||
@@ -571,8 +574,14 @@ elif [[ ${signing} == "software" ]]; then
|
|||||||
if [[ ${signing_request_signatures} == true ]]; then
|
if [[ ${signing_request_signatures} == true ]]; then
|
||||||
CPPFLAGS="-DMY_SIGNING_REQUEST_SIGNATURES $CPPFLAGS"
|
CPPFLAGS="-DMY_SIGNING_REQUEST_SIGNATURES $CPPFLAGS"
|
||||||
fi
|
fi
|
||||||
|
elif [[ ${signing} == "password" ]]; then
|
||||||
|
if [ -z "${signing_password}" ]; then
|
||||||
|
die "You need to set the password for signing with --my-signing-password= option" 6
|
||||||
|
fi
|
||||||
|
echo "Simplified signing using password"
|
||||||
|
CPPFLAGS="-DMY_SIGNING_SIMPLE_PASSWD=\\\"${signing_password}\\\" $CPPFLAGS"
|
||||||
else
|
else
|
||||||
die "Invalid message signing option." 4
|
die "Invalid signing type." 7
|
||||||
fi
|
fi
|
||||||
|
|
||||||
LDFLAGS="-pthread $LDFLAGS"
|
LDFLAGS="-pthread $LDFLAGS"
|
||||||
|
|||||||
@@ -118,7 +118,6 @@ void _begin(void)
|
|||||||
ledsInit();
|
ledsInit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
(void)signerValidatePersonalization();
|
|
||||||
signerInit();
|
signerInit();
|
||||||
|
|
||||||
// Read latest received controller configuration from EEPROM
|
// Read latest received controller configuration from EEPROM
|
||||||
|
|||||||
+140
-110
@@ -30,11 +30,9 @@
|
|||||||
#define SIGN_DEBUG(x,...)
|
#define SIGN_DEBUG(x,...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(MY_SIGNING_REQUEST_SIGNATURES) && (!defined(MY_SIGNING_ATSHA204) && !defined(MY_SIGNING_SOFT))
|
#if defined(MY_SIGNING_REQUEST_SIGNATURES) &&\
|
||||||
#error You have to pick either MY_SIGNING_ATSHA204 or MY_SIGNING_SOFT in order to require signatures!
|
(!defined(MY_SIGNING_ATSHA204) && !defined(MY_SIGNING_SOFT))
|
||||||
#endif
|
#error You have to pick either MY_SIGNING_ATSHA204 or MY_SIGNING_SOFT to reqire signatures!
|
||||||
#if defined(MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL) && !defined(MY_SIGNING_REQUEST_SIGNATURES)
|
|
||||||
#error You have to require signatures if you want to require signatures from all (also enable MY_SIGNING_REQUEST_SIGNATURES in your gateway)
|
|
||||||
#endif
|
#endif
|
||||||
#if defined(MY_SIGNING_SOFT) && defined(MY_SIGNING_ATSHA204)
|
#if defined(MY_SIGNING_SOFT) && defined(MY_SIGNING_ATSHA204)
|
||||||
#error You have to pick one and only one signing backend
|
#error You have to pick one and only one signing backend
|
||||||
@@ -56,10 +54,14 @@ enum { SIGN_WAITING_FOR_NONCE = 0, SIGN_OK = 1 };
|
|||||||
// Macros for manipulating signing requirement tables
|
// Macros for manipulating signing requirement tables
|
||||||
#define DO_SIGN(node) (~_doSign[node>>3]&(1<<node%8))
|
#define DO_SIGN(node) (~_doSign[node>>3]&(1<<node%8))
|
||||||
#define SET_SIGN(node) (_doSign[node>>3]&=~(1<<node%8))
|
#define SET_SIGN(node) (_doSign[node>>3]&=~(1<<node%8))
|
||||||
|
#if defined(MY_SIGNING_WEAK_SECURITY)
|
||||||
#define CLEAR_SIGN(node) (_doSign[node>>3]|=(1<<node%8))
|
#define CLEAR_SIGN(node) (_doSign[node>>3]|=(1<<node%8))
|
||||||
|
#endif
|
||||||
#define DO_WHITELIST(node) (~_doWhitelist[node>>3]&(1<<node%8))
|
#define DO_WHITELIST(node) (~_doWhitelist[node>>3]&(1<<node%8))
|
||||||
#define SET_WHITELIST(node) (_doWhitelist[node>>3]&=~(1<<node%8))
|
#define SET_WHITELIST(node) (_doWhitelist[node>>3]&=~(1<<node%8))
|
||||||
|
#if defined(MY_SIGNING_WEAK_SECURITY)
|
||||||
#define CLEAR_WHITELIST(node) (_doWhitelist[node>>3]|=(1<<node%8))
|
#define CLEAR_WHITELIST(node) (_doWhitelist[node>>3]|=(1<<node%8))
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(MY_SIGNING_SOFT)
|
#if defined(MY_SIGNING_SOFT)
|
||||||
extern bool signerAtsha204SoftInit(void);
|
extern bool signerAtsha204SoftInit(void);
|
||||||
@@ -88,6 +90,7 @@ extern bool signerAtsha204SignMsg(MyMessage &msg);
|
|||||||
#define signerBackendVerifyMsg signerAtsha204VerifyMsg
|
#define signerBackendVerifyMsg signerAtsha204VerifyMsg
|
||||||
#define signerBackendSignMsg signerAtsha204SignMsg
|
#define signerBackendSignMsg signerAtsha204SignMsg
|
||||||
#endif
|
#endif
|
||||||
|
static bool signerValidatePersonalization(void);
|
||||||
static bool skipSign(MyMessage &msg);
|
static bool skipSign(MyMessage &msg);
|
||||||
#else // not MY_SIGNING_FEATURE
|
#else // not MY_SIGNING_FEATURE
|
||||||
#define signerBackendCheckTimer() true
|
#define signerBackendCheckTimer() true
|
||||||
@@ -100,6 +103,8 @@ static bool signerInternalProcessNonceResponse(MyMessage &msg);
|
|||||||
void signerInit(void)
|
void signerInit(void)
|
||||||
{
|
{
|
||||||
#if defined(MY_SIGNING_FEATURE)
|
#if defined(MY_SIGNING_FEATURE)
|
||||||
|
(void)signerValidatePersonalization();
|
||||||
|
|
||||||
// Read out the signing requirements from EEPROM
|
// Read out the signing requirements from EEPROM
|
||||||
hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS,
|
hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS,
|
||||||
sizeof(_doSign));
|
sizeof(_doSign));
|
||||||
@@ -108,33 +113,12 @@ void signerInit(void)
|
|||||||
hwReadConfigBlock((void*)_doWhitelist, (void*)EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS,
|
hwReadConfigBlock((void*)_doWhitelist, (void*)EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS,
|
||||||
sizeof(_doWhitelist));
|
sizeof(_doWhitelist));
|
||||||
|
|
||||||
signerBackendInit();
|
if (!signerBackendInit()) {
|
||||||
#endif
|
SIGN_DEBUG(PSTR("!SGN:INI:BND FAIL\n"));
|
||||||
}
|
|
||||||
|
|
||||||
bool signerValidatePersonalization(void)
|
|
||||||
{
|
|
||||||
uint8_t buffer[32];
|
|
||||||
uint8_t* hash;
|
|
||||||
uint8_t checksum;
|
|
||||||
signerSha256Init();
|
|
||||||
hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
|
|
||||||
signerSha256Update(buffer, 32);
|
|
||||||
hwReadConfigBlock((void*)buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
|
||||||
signerSha256Update(buffer, 16);
|
|
||||||
hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
|
|
||||||
signerSha256Update(buffer, 9);
|
|
||||||
hash = signerSha256Final();
|
|
||||||
hwReadConfigBlock((void*)&checksum, (void*)EEPROM_PERSONALIZATION_CHECKSUM_ADDRESS, 1);
|
|
||||||
if (checksum != hash[0]) {
|
|
||||||
SIGN_DEBUG(PSTR("WARNING! Personalization data in EEPROM is tampered!\n"));
|
|
||||||
SIGN_DEBUG(PSTR("If you are sure that the personalization data in EEPROM is ok,\n"));
|
|
||||||
SIGN_DEBUG(
|
|
||||||
PSTR("execute SecurityPersonalizer.ino unmodified to get a new checksum stored.\n"));
|
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
return true;
|
SIGN_DEBUG(PSTR("SGN:INI:BND OK\n"));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void signerPresentation(MyMessage &msg, uint8_t destination)
|
void signerPresentation(MyMessage &msg, uint8_t destination)
|
||||||
@@ -143,19 +127,25 @@ void signerPresentation(MyMessage &msg, uint8_t destination)
|
|||||||
|
|
||||||
#if defined(MY_SIGNING_REQUEST_SIGNATURES)
|
#if defined(MY_SIGNING_REQUEST_SIGNATURES)
|
||||||
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES;
|
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES;
|
||||||
SIGN_DEBUG(PSTR("Signing required\n"));
|
SIGN_DEBUG(PSTR("SGN:PRE:SGN REQ\n")); // Signing required
|
||||||
|
#else
|
||||||
|
SIGN_DEBUG(PSTR("SGN:PRE:SGN NREQ\n")); // Signing not required
|
||||||
#endif
|
#endif
|
||||||
#if defined(MY_SIGNING_NODE_WHITELISTING)
|
#if defined(MY_SIGNING_NODE_WHITELISTING)
|
||||||
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_WHITELISTING;
|
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_WHITELISTING;
|
||||||
SIGN_DEBUG(PSTR("Whitelisting required\n"));
|
SIGN_DEBUG(PSTR("SGN:PRE:WHI REQ\n")); // Whitelisting required
|
||||||
|
#else
|
||||||
|
SIGN_DEBUG(PSTR("SGN:PRE:WHI NREQ\n")); // Whitelisting not required
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!_sendRoute(msg)) {
|
if (!_sendRoute(msg)) {
|
||||||
SIGN_DEBUG(PSTR("Failed to transmit signing presentation!\n"));
|
SIGN_DEBUG(PSTR("!SGN:PRE:XMT,TO=%d FAIL\n"), destination); // Failed to transmit presentation!
|
||||||
|
} else {
|
||||||
|
SIGN_DEBUG(PSTR("SGN:PRE:XMT,TO=%d\n"), destination); // Transmitted signing presentation!
|
||||||
}
|
}
|
||||||
|
|
||||||
if (destination == GATEWAY_ADDRESS) {
|
if (destination == GATEWAY_ADDRESS) {
|
||||||
SIGN_DEBUG(PSTR("Waiting for GW to send signing preferences...\n"));
|
SIGN_DEBUG(PSTR("SGN:PRE:WAIT GW\n")); // Waiting for GW to send signing preferences...
|
||||||
wait(2000, C_INTERNAL, I_SIGNING_PRESENTATION);
|
wait(2000, C_INTERNAL, I_SIGNING_PRESENTATION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,31 +189,33 @@ bool signerSignMsg(MyMessage &msg)
|
|||||||
_signingNonceStatus=SIGN_WAITING_FOR_NONCE;
|
_signingNonceStatus=SIGN_WAITING_FOR_NONCE;
|
||||||
if (!_sendRoute(build(_msgSign, msg.destination, msg.sensor, C_INTERNAL,
|
if (!_sendRoute(build(_msgSign, msg.destination, msg.sensor, C_INTERNAL,
|
||||||
I_NONCE_REQUEST).set(""))) {
|
I_NONCE_REQUEST).set(""))) {
|
||||||
SIGN_DEBUG(PSTR("Failed to transmit nonce request!\n"));
|
SIGN_DEBUG(PSTR("!SGN:SGN:NCE REQ,TO=%d FAIL\n"),
|
||||||
|
msg.destination); // Failed to transmit nonce request!
|
||||||
ret = false;
|
ret = false;
|
||||||
} else {
|
} else {
|
||||||
SIGN_DEBUG(PSTR("Nonce requested from %d. Waiting...\n"), msg.destination);
|
SIGN_DEBUG(PSTR("SGN:SGN:NCE REQ,TO=%d\n"), msg.destination); // Nonce requested
|
||||||
// We have to wait for the nonce to arrive before we can sign our original message
|
// 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
|
// Other messages could come in-between. We trust _process() takes care of them
|
||||||
unsigned long enter = hwMillis();
|
unsigned long enter = hwMillis();
|
||||||
_msgSign = msg; // Copy the message to sign since message buffer might be touched in _process()
|
_msgSign = msg; // Copy the message to sign since buffer might be touched in _process()
|
||||||
while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS &&
|
while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS &&
|
||||||
_signingNonceStatus==SIGN_WAITING_FOR_NONCE) {
|
_signingNonceStatus==SIGN_WAITING_FOR_NONCE) {
|
||||||
_process();
|
_process();
|
||||||
}
|
}
|
||||||
if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) {
|
if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) {
|
||||||
SIGN_DEBUG(PSTR("Timeout waiting for nonce!\n"));
|
SIGN_DEBUG(PSTR("!SGN:SGN:NCE TMO\n")); // Timeout waiting for nonce!
|
||||||
ret = false;
|
ret = false;
|
||||||
} else {
|
} else {
|
||||||
if (_signingNonceStatus == SIGN_OK) {
|
if (_signingNonceStatus == SIGN_OK) {
|
||||||
// process() received a nonce and signerProcessInternal successfully signed the message
|
// process() received a nonce and signerProcessInternal successfully signed the message
|
||||||
msg = _msgSign; // Write the signed message back
|
msg = _msgSign; // Write the signed message back
|
||||||
SIGN_DEBUG(PSTR("Message to send has been signed\n"));
|
SIGN_DEBUG(PSTR("SGN:SGN:SGN\n")); // Message to send has been signed
|
||||||
ret = true;
|
ret = true;
|
||||||
// After this point, only the 'last' member of the message structure is allowed to be altered if the
|
// After this point, only the 'last' member of the message structure is allowed to be
|
||||||
// message has been signed, or signature will become invalid and the message rejected by the receiver
|
// altered if the message has been signed, or signature will become invalid and the
|
||||||
|
// message rejected by the receiver
|
||||||
} else {
|
} else {
|
||||||
SIGN_DEBUG(PSTR("Message to send could not be signed!\n"));
|
SIGN_DEBUG(PSTR("!SGN:SGN:SGN FAIL\n")); // Message to send could not be signed!
|
||||||
ret = false;
|
ret = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,12 +223,11 @@ bool signerSignMsg(MyMessage &msg)
|
|||||||
}
|
}
|
||||||
} else if (getNodeId() == msg.sender) {
|
} else if (getNodeId() == msg.sender) {
|
||||||
mSetSigned(msg, 0); // Message is not supposed to be signed, make sure it is marked unsigned
|
mSetSigned(msg, 0); // Message is not supposed to be signed, make sure it is marked unsigned
|
||||||
SIGN_DEBUG(PSTR("Will not sign message for destination %d as it does not require it\n"),
|
SIGN_DEBUG(PSTR("SGN:SGN:NREQ=%d\n"), msg.destination); // Do not sign message as it is not req
|
||||||
msg.destination);
|
|
||||||
ret = true;
|
ret = true;
|
||||||
} else {
|
} else {
|
||||||
SIGN_DEBUG(PSTR("Will not sign message since it was from %d and we are %d\n"), msg.sender,
|
SIGN_DEBUG(PSTR("SGN:SGN:%d!=%d NUS\n"), msg.sender,
|
||||||
getNodeId());
|
getNodeId()); // Will not sign message since it was from someone else
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -255,22 +246,24 @@ bool signerVerifyMsg(MyMessage &msg)
|
|||||||
#if defined(MY_SIGNING_FEATURE) && defined(MY_SIGNING_REQUEST_SIGNATURES)
|
#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 (or just a strict gw)
|
// If we are a node, or we are a gateway and the sender require signatures (or just a strict gw)
|
||||||
// and we are the destination...
|
// and we are the destination...
|
||||||
#if defined(MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL)
|
#if defined(MY_SIGNING_WEAK_SECURITY)
|
||||||
if (msg.destination == getNodeId()) {
|
|
||||||
#else
|
|
||||||
if ((!MY_IS_GATEWAY || DO_SIGN(msg.sender)) && msg.destination == getNodeId()) {
|
if ((!MY_IS_GATEWAY || DO_SIGN(msg.sender)) && msg.destination == getNodeId()) {
|
||||||
|
#else
|
||||||
|
if (msg.destination == getNodeId()) {
|
||||||
#endif
|
#endif
|
||||||
// Internal messages of certain types are not verified
|
// Internal messages of certain types are not verified
|
||||||
if (skipSign(msg)) {
|
if (skipSign(msg)) {
|
||||||
verificationResult = true;
|
verificationResult = true;
|
||||||
} else if (!mGetSigned(msg)) {
|
} else if (!mGetSigned(msg)) {
|
||||||
// Got unsigned message that should have been signed
|
// Got unsigned message that should have been signed
|
||||||
SIGN_DEBUG(PSTR("Message is not signed, but it should have been!\n"));
|
SIGN_DEBUG(PSTR("!SGN:VER:NSG\n")); // Message is not signed, but it should have been!
|
||||||
verificationResult = false;
|
verificationResult = false;
|
||||||
} else {
|
} else {
|
||||||
if (!signerBackendVerifyMsg(msg)) {
|
if (!signerBackendVerifyMsg(msg)) {
|
||||||
SIGN_DEBUG(PSTR("Signature verification failed!\n"));
|
SIGN_DEBUG(PSTR("!SGN:VER:FAIL\n")); // Signature verification failed!
|
||||||
verificationResult = false;
|
verificationResult = false;
|
||||||
|
} else {
|
||||||
|
SIGN_DEBUG(PSTR("SGN:VER:OK\n"));
|
||||||
}
|
}
|
||||||
#if defined(MY_NODE_LOCK_FEATURE)
|
#if defined(MY_NODE_LOCK_FEATURE)
|
||||||
if (verificationResult) {
|
if (verificationResult) {
|
||||||
@@ -279,10 +272,9 @@ bool signerVerifyMsg(MyMessage &msg)
|
|||||||
nof_failed_verifications = 0;
|
nof_failed_verifications = 0;
|
||||||
} else {
|
} else {
|
||||||
nof_failed_verifications++;
|
nof_failed_verifications++;
|
||||||
SIGN_DEBUG(PSTR("Failed verification attempts left until lockdown: %d\n"),
|
SIGN_DEBUG(PSTR("SGN:VER:LEFT=%d\n"), MY_NODE_LOCK_COUNTER_MAX-nof_failed_verifications);
|
||||||
MY_NODE_LOCK_COUNTER_MAX-nof_failed_verifications);
|
|
||||||
if (nof_failed_verifications >= MY_NODE_LOCK_COUNTER_MAX) {
|
if (nof_failed_verifications >= MY_NODE_LOCK_COUNTER_MAX) {
|
||||||
_nodeLock("TMFV"); //Too many failed verifications
|
_nodeLock("TMFV"); // Too many failed verifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -349,30 +341,57 @@ int signerMemcmp(const void* a, const void* b, size_t sz)
|
|||||||
|
|
||||||
#if defined(MY_SIGNING_FEATURE)
|
#if defined(MY_SIGNING_FEATURE)
|
||||||
// Helper function to centralize signing/verification exceptions
|
// Helper function to centralize signing/verification exceptions
|
||||||
|
static bool signerValidatePersonalization(void)
|
||||||
|
{
|
||||||
|
#ifdef MY_SIGNING_SIMPLE_PASSWD
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
uint8_t buffer[32];
|
||||||
|
uint8_t* hash;
|
||||||
|
uint8_t checksum;
|
||||||
|
signerSha256Init();
|
||||||
|
hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
|
||||||
|
signerSha256Update(buffer, 32);
|
||||||
|
hwReadConfigBlock((void*)buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
||||||
|
signerSha256Update(buffer, 16);
|
||||||
|
hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
|
||||||
|
signerSha256Update(buffer, 9);
|
||||||
|
hash = signerSha256Final();
|
||||||
|
hwReadConfigBlock((void*)&checksum, (void*)EEPROM_PERSONALIZATION_CHECKSUM_ADDRESS, 1);
|
||||||
|
if (checksum != hash[0]) {
|
||||||
|
SIGN_DEBUG(PSTR("!SGN:PER:TAMPERED\n"));
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
SIGN_DEBUG(PSTR("SGN:PER:OK\n"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static bool skipSign(MyMessage &msg)
|
static bool skipSign(MyMessage &msg)
|
||||||
{
|
{
|
||||||
bool ret;
|
bool ret = false;
|
||||||
if (mGetAck(msg)) {
|
if (mGetAck(msg)) {
|
||||||
SIGN_DEBUG(PSTR("Skipping security for ACK on command %d type %d\n"), mGetCommand(msg), msg.type);
|
|
||||||
ret = true;
|
ret = true;
|
||||||
} else if (mGetCommand(msg) == C_INTERNAL &&
|
} else if (mGetCommand(msg) == C_INTERNAL &&
|
||||||
(msg.type == I_NONCE_REQUEST || msg.type == I_NONCE_RESPONSE ||
|
(msg.type == I_SIGNING_PRESENTATION ||
|
||||||
msg.type == I_SIGNING_PRESENTATION ||
|
msg.type == I_REGISTRATION_REQUEST ||
|
||||||
|
msg.type == I_NONCE_REQUEST || msg.type == I_NONCE_RESPONSE ||
|
||||||
msg.type == I_ID_REQUEST || msg.type == I_ID_RESPONSE ||
|
msg.type == I_ID_REQUEST || msg.type == I_ID_RESPONSE ||
|
||||||
msg.type == I_FIND_PARENT_REQUEST || msg.type == I_FIND_PARENT_RESPONSE ||
|
msg.type == I_FIND_PARENT_REQUEST || msg.type == I_FIND_PARENT_RESPONSE ||
|
||||||
msg.type == I_HEARTBEAT_REQUEST || msg.type == I_HEARTBEAT_RESPONSE ||
|
msg.type == I_HEARTBEAT_REQUEST || msg.type == I_HEARTBEAT_RESPONSE ||
|
||||||
msg.type == I_PING || msg.type == I_PONG ||
|
msg.type == I_PING || msg.type == I_PONG ||
|
||||||
msg.type == I_REGISTRATION_REQUEST || msg.type == I_DISCOVER_REQUEST ||
|
msg.type == I_DISCOVER_REQUEST || msg.type == I_DISCOVER_RESPONSE )) {
|
||||||
msg.type == I_DISCOVER_RESPONSE )) {
|
|
||||||
SIGN_DEBUG(PSTR("Skipping security for command %d type %d\n"), mGetCommand(msg), msg.type);
|
|
||||||
ret = true;
|
ret = true;
|
||||||
} else if (mGetCommand(msg) == C_STREAM &&
|
} else if (mGetCommand(msg) == C_STREAM &&
|
||||||
(msg.type == ST_FIRMWARE_REQUEST || msg.type == ST_FIRMWARE_RESPONSE ||
|
(msg.type == ST_SOUND ||
|
||||||
msg.type == ST_SOUND || msg.type == ST_IMAGE)) {
|
msg.type == ST_IMAGE ||
|
||||||
SIGN_DEBUG(PSTR("Skipping security for command %d type %d\n"), mGetCommand(msg), msg.type);
|
msg.type == ST_FIRMWARE_REQUEST || msg.type == ST_FIRMWARE_RESPONSE )) {
|
||||||
ret = true;
|
ret = true;
|
||||||
} else {
|
}
|
||||||
ret = false;
|
if (ret) {
|
||||||
|
SIGN_DEBUG(PSTR("SGN:SKP:%s CMD=%d,TYPE=%d\n"), mGetAck(msg) ? "ACK" : "MSG", mGetCommand(msg),
|
||||||
|
msg.type); //Skip signing/verification of this message
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -395,27 +414,41 @@ static bool signerInternalProcessPresentation(MyMessage &msg)
|
|||||||
const uint8_t sender = msg.sender;
|
const uint8_t sender = msg.sender;
|
||||||
#if defined(MY_SIGNING_FEATURE)
|
#if defined(MY_SIGNING_FEATURE)
|
||||||
if (msg.data[0] != SIGNING_PRESENTATION_VERSION_1) {
|
if (msg.data[0] != SIGNING_PRESENTATION_VERSION_1) {
|
||||||
SIGN_DEBUG(PSTR("Unsupported signing presentation version (%d)!\n"), msg.data[0]);
|
SIGN_DEBUG(PSTR("!SGN:PRE:VER=%d\n"), msg.data[0]); // Unsupported signing presentation version
|
||||||
return true; // Just drop this presentation message
|
return true; // Just drop this presentation message
|
||||||
}
|
}
|
||||||
// We only handle version 1 here...
|
// We only handle version 1 here...
|
||||||
if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_SIGNATURES) {
|
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
|
// 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"), sender);
|
SIGN_DEBUG(PSTR("SGN:PRE:SGN REQ,FROM=%d\n"), sender); // Node require signatures
|
||||||
SET_SIGN(sender);
|
SET_SIGN(sender);
|
||||||
} else {
|
} else {
|
||||||
// We received an indicator that the sender does not require us to sign all messages we send to it
|
#if defined(MY_SIGNING_WEAK_SECURITY)
|
||||||
SIGN_DEBUG(PSTR("Mark node %d as one that do not require signed messages\n"), sender);
|
// We received an indicator that the sender does not require us to sign messages we send to it
|
||||||
|
SIGN_DEBUG(PSTR("SGN:PRE:SGN NREQ,FROM=%d\n"), sender); // Node does not require signatures
|
||||||
CLEAR_SIGN(sender);
|
CLEAR_SIGN(sender);
|
||||||
|
#else
|
||||||
|
if (DO_SIGN(sender)) {
|
||||||
|
SIGN_DEBUG(PSTR("!SGN:PRE:SGN NREQ,FROM=%d REJ\n"),
|
||||||
|
sender); // Node does not require signatures but used to do so
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_WHITELISTING) {
|
if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_WHITELISTING) {
|
||||||
// We received an indicator that the sender require us to salt signatures with serial
|
// 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"), sender);
|
SIGN_DEBUG(PSTR("SGN:PRE:WHI REQ,FROM=%d\n"), sender); // Node require whitelisting
|
||||||
SET_WHITELIST(sender);
|
SET_WHITELIST(sender);
|
||||||
} else {
|
} else {
|
||||||
// We received an indicator that the sender does not require us to sign all messages we send to it
|
#if defined(MY_SIGNING_WEAK_SECURITY)
|
||||||
SIGN_DEBUG(PSTR("Mark node %d as one that do not require whitelisting\n"), sender);
|
// We received an indicator that the sender does not require us to sign messages we send to it
|
||||||
|
SIGN_DEBUG(PSTR("SGN:PRE:WHI NREQ,FROM=%d\n"), sender); // Node does not require whitelisting
|
||||||
CLEAR_WHITELIST(sender);
|
CLEAR_WHITELIST(sender);
|
||||||
|
#else
|
||||||
|
if (DO_WHITELIST(sender)) {
|
||||||
|
SIGN_DEBUG(PSTR("!SGN:PRE:WHI NREQ,FROM=%d REJ\n"),
|
||||||
|
sender); // Node does not require whitelisting but used to do so
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save updated tables
|
// Save updated tables
|
||||||
@@ -429,35 +462,33 @@ static bool signerInternalProcessPresentation(MyMessage &msg)
|
|||||||
#if defined(MY_GATEWAY_FEATURE)
|
#if defined(MY_GATEWAY_FEATURE)
|
||||||
prepareSigningPresentation(msg, sender);
|
prepareSigningPresentation(msg, sender);
|
||||||
#if defined(MY_SIGNING_REQUEST_SIGNATURES)
|
#if defined(MY_SIGNING_REQUEST_SIGNATURES)
|
||||||
#if defined(MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL)
|
#if defined(MY_SIGNING_WEAK_SECURITY)
|
||||||
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES;
|
|
||||||
#else
|
|
||||||
if (DO_SIGN(sender)) {
|
if (DO_SIGN(sender)) {
|
||||||
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES;
|
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES;
|
||||||
#endif
|
#endif
|
||||||
#endif // MY_SIGNING_REQUEST_SIGNATURES
|
#endif // MY_SIGNING_REQUEST_SIGNATURES
|
||||||
#if defined(MY_SIGNING_NODE_WHITELISTING)
|
#if defined(MY_SIGNING_NODE_WHITELISTING)
|
||||||
#if defined(MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL)
|
|
||||||
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_WHITELISTING;
|
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_WHITELISTING;
|
||||||
#else
|
|
||||||
if (DO_WHITELIST(sender)) {
|
|
||||||
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_WHITELISTING;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif // MY_SIGNING_NODE_WHITELISTING
|
#endif // MY_SIGNING_NODE_WHITELISTING
|
||||||
if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_SIGNATURES) {
|
if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_SIGNATURES) {
|
||||||
SIGN_DEBUG(PSTR("Informing node %d that we require signatures\n"), sender);
|
SIGN_DEBUG(PSTR("SGN:PRE:SGN REQ,TO=%d\n"), sender); // Inform node that we require signatures
|
||||||
} else {
|
} else {
|
||||||
SIGN_DEBUG(PSTR("Informing node %d that we do not require signatures\n"), sender);
|
SIGN_DEBUG(PSTR("SGN:PRE:SGN NREQ,TO=%d\n"),
|
||||||
|
sender); // Inform node that we do not require signatures
|
||||||
}
|
}
|
||||||
if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_WHITELISTING) {
|
if (msg.data[1] & SIGNING_PRESENTATION_REQUIRE_WHITELISTING) {
|
||||||
SIGN_DEBUG(PSTR("Informing node %d that we require whitelisting\n"), sender);
|
SIGN_DEBUG(PSTR("SGN:PRE:WHI REQ,TO=%d\n"), sender); // Inform node that we require whitelisting
|
||||||
} else {
|
} else {
|
||||||
SIGN_DEBUG(PSTR("Informing node %d that we do not require whitelisting\n"), sender);
|
SIGN_DEBUG(PSTR("SGN:PRE:WHI NREQ,TO=%d\n"),
|
||||||
|
sender); // Inform node that we do not require whitelisting
|
||||||
}
|
}
|
||||||
if (!_sendRoute(msg)) {
|
if (!_sendRoute(msg)) {
|
||||||
SIGN_DEBUG(PSTR("Failed to transmit signing presentation!\n"));
|
SIGN_DEBUG(PSTR("!SGN:PRE:XMT,TO=%d FAIL\n"), sender); // Failed to transmit signing presentation!
|
||||||
|
} else {
|
||||||
|
SIGN_DEBUG(PSTR("SGN:PRE:XMT,TO=%d\n"), sender);
|
||||||
}
|
}
|
||||||
#endif // MY_GATEWAY_FEATURE
|
#endif // MY_GATEWAY_FEATURE
|
||||||
#else // not MY_SIGNING_FEATURE
|
#else // not MY_SIGNING_FEATURE
|
||||||
@@ -465,18 +496,20 @@ static bool signerInternalProcessPresentation(MyMessage &msg)
|
|||||||
// If we act as gateway and do not have the signing feature and receive a signing request we still
|
// 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
|
// need to do make sure the requester does not believe the gateway still require signatures
|
||||||
prepareSigningPresentation(msg, sender);
|
prepareSigningPresentation(msg, sender);
|
||||||
SIGN_DEBUG(
|
SIGN_DEBUG(PSTR("SGN:PRE:NSUP,TO=%d\n"), sender); // Informing node that we do not support signing
|
||||||
PSTR("Informing node %d that we do not require signatures because we do not support it\n"),
|
|
||||||
sender);
|
|
||||||
if (!_sendRoute(msg)) {
|
if (!_sendRoute(msg)) {
|
||||||
SIGN_DEBUG(PSTR("Failed to transmit signing presentation!\n"));
|
SIGN_DEBUG(PSTR("!SGN:PRE:XMT,TO=%d FAIL\n"), sender); // Failed to transmit signing presentation!
|
||||||
|
} else {
|
||||||
|
SIGN_DEBUG(PSTR("SGN:PRE:XMT,TO=%d\n"), sender);
|
||||||
}
|
}
|
||||||
#else // not MY_GATEWAY_FEATURE
|
#else // not MY_GATEWAY_FEATURE
|
||||||
// If we act as a node and do not have the signing feature then we just silently drop any signing
|
// If we act as a node and do not have the signing feature then we just silently drop any signing
|
||||||
// presentation messages received
|
// presentation messages received
|
||||||
(void)msg;
|
(void)msg;
|
||||||
(void)sender;
|
(void)sender;
|
||||||
SIGN_DEBUG(PSTR("Received signing presentation, but signing is not supported (message ignored)\n"));
|
// Received signing presentation, but signing is not supported (message ignored)
|
||||||
|
SIGN_DEBUG(
|
||||||
|
PSTR("SGN:PRE:NSUP\n"));
|
||||||
#endif // not MY_GATEWAY_FEATURE
|
#endif // not MY_GATEWAY_FEATURE
|
||||||
#endif // not MY_SIGNING_FEATURE
|
#endif // not MY_SIGNING_FEATURE
|
||||||
return true; // No need to further process I_SIGNING_PRESENTATION
|
return true; // No need to further process I_SIGNING_PRESENTATION
|
||||||
@@ -488,24 +521,25 @@ static bool signerInternalProcessNonceRequest(MyMessage &msg)
|
|||||||
#if defined(MY_SIGNING_FEATURE)
|
#if defined(MY_SIGNING_FEATURE)
|
||||||
#if defined(MY_NODE_LOCK_FEATURE)
|
#if defined(MY_NODE_LOCK_FEATURE)
|
||||||
nof_nonce_requests++;
|
nof_nonce_requests++;
|
||||||
SIGN_DEBUG(PSTR("Nonce requests left until lockdown: %d\n"),
|
SIGN_DEBUG(PSTR("SGN:NCE:LEFT=%d\n"),
|
||||||
MY_NODE_LOCK_COUNTER_MAX-nof_nonce_requests);
|
MY_NODE_LOCK_COUNTER_MAX-nof_nonce_requests); // Nonce requests left until lockdown
|
||||||
if (nof_nonce_requests >= MY_NODE_LOCK_COUNTER_MAX) {
|
if (nof_nonce_requests >= MY_NODE_LOCK_COUNTER_MAX) {
|
||||||
_nodeLock("TMNR"); //Too many nonces requested
|
_nodeLock("TMNR"); // Too many nonces requested
|
||||||
}
|
}
|
||||||
#endif // MY_NODE_LOCK_FEATURE
|
#endif // MY_NODE_LOCK_FEATURE
|
||||||
if (signerBackendGetNonce(msg)) {
|
if (signerBackendGetNonce(msg)) {
|
||||||
if (!_sendRoute(build(msg, msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_NONCE_RESPONSE))) {
|
if (!_sendRoute(build(msg, msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_NONCE_RESPONSE))) {
|
||||||
SIGN_DEBUG(PSTR("Failed to transmit nonce!\n"));
|
SIGN_DEBUG(PSTR("!SGN:NCE:XMT,TO=%d FAIL\n"), msg.sender); // Failed to transmit nonce!
|
||||||
} else {
|
} else {
|
||||||
SIGN_DEBUG(PSTR("Transmitted nonce\n"));
|
SIGN_DEBUG(PSTR("SGN:NCE:XMT,TO=%d\n"), msg.sender);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
SIGN_DEBUG(PSTR("Failed to generate nonce!\n"));
|
SIGN_DEBUG(PSTR("!SGN:NCE:GEN\n")); // Failed to generate nonce!
|
||||||
}
|
}
|
||||||
#else // not MY_SIGNING_FEATURE
|
#else // not MY_SIGNING_FEATURE
|
||||||
(void)msg;
|
(void)msg;
|
||||||
SIGN_DEBUG(PSTR("Received nonce request, but signing is not supported (message ignored)\n"));
|
SIGN_DEBUG(
|
||||||
|
PSTR("SGN:NCE:NSUP (DROPPED)\n")); // Received nonce request/response without signing support
|
||||||
#endif // MY_SIGNING_FEATURE
|
#endif // MY_SIGNING_FEATURE
|
||||||
return true; // No need to further process I_NONCE_REQUEST
|
return true; // No need to further process I_NONCE_REQUEST
|
||||||
}
|
}
|
||||||
@@ -515,24 +549,20 @@ static bool signerInternalProcessNonceResponse(MyMessage &msg)
|
|||||||
{
|
{
|
||||||
#if defined(MY_SIGNING_FEATURE)
|
#if defined(MY_SIGNING_FEATURE)
|
||||||
// Proceed with signing if nonce has been received
|
// Proceed with signing if nonce has been received
|
||||||
SIGN_DEBUG(PSTR("Nonce received from %d.\n"), msg.sender);
|
SIGN_DEBUG(PSTR("SGN:NCE:FROM=%d\n"), msg.sender);
|
||||||
if (msg.sender != _msgSign.destination) {
|
if (msg.sender != _msgSign.destination) {
|
||||||
SIGN_DEBUG(PSTR("Nonce did not come from the destination (%d) of the message to be signed! "
|
SIGN_DEBUG(PSTR("SGN:NCE:%d!=%d (DROPPED)\n"), _msgSign.destination, msg.sender);
|
||||||
"It came from %d.\n"), _msgSign.destination, msg.sender);
|
|
||||||
SIGN_DEBUG(PSTR("Silently discarding this nonce\n"));
|
|
||||||
} else {
|
} else {
|
||||||
SIGN_DEBUG(PSTR("Proceeding with signing...\n"));
|
|
||||||
signerBackendPutNonce(msg);
|
signerBackendPutNonce(msg);
|
||||||
if (!signerBackendSignMsg(_msgSign)) {
|
if (signerBackendSignMsg(_msgSign)) {
|
||||||
SIGN_DEBUG(PSTR("Failed to sign message!\n"));
|
// _msgSign now contains the signed message pending transmission
|
||||||
} else {
|
_signingNonceStatus = SIGN_OK;
|
||||||
SIGN_DEBUG(PSTR("Message signed\n"));
|
|
||||||
_signingNonceStatus = SIGN_OK; // _msgSign now contains the signed message pending transmission
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
(void)msg;
|
(void)msg;
|
||||||
SIGN_DEBUG(PSTR("Received nonce response, but signing is not supported (message ignored)\n"));
|
SIGN_DEBUG(
|
||||||
|
PSTR("SGN:NCE:NSUP (DROPPED)\n")); // Received nonce request/response without signing support
|
||||||
#endif
|
#endif
|
||||||
return true; // No need to further process I_NONCE_RESPONSE
|
return true; // No need to further process I_NONCE_RESPONSE
|
||||||
}
|
}
|
||||||
|
|||||||
+471
-255
@@ -24,150 +24,82 @@
|
|||||||
* @brief The signing driver provides a generic API for various signing backends to offer
|
* @brief The signing driver provides a generic API for various signing backends to offer
|
||||||
* signing of MySensors messages.
|
* signing of MySensors messages.
|
||||||
*
|
*
|
||||||
* Signing support created by Patrick "Anticimex" Fallberg <patrick@fallberg.net>
|
* Signing support created by Patrick "Anticimex" Fallberg (<patrick@fallberg.net>)
|
||||||
*
|
*
|
||||||
* @section MySigninggrpbackground Background and concepts
|
* @section MySigninggrptoc Table of contents
|
||||||
*
|
*
|
||||||
* Suppose two participants, Alice and Bob, wants to exchange a message. Alice sends a message to Bob. In MySensors “language” Alice
|
* @ref MySigninggrphowuse <br>
|
||||||
* could be a gateway and Bob an actuator (light swich, electronic lock, etc). But to be generic, we will substitute the term
|
* @ref MySigningwhitelisting <br>
|
||||||
* “gateway” with Alice and a “node” with Bob (although the reverse relationship is also supported).
|
* @ref MySigninglimitations <br>
|
||||||
*
|
* @ref MySigningusecases <br>
|
||||||
* Alice sends a message to Bob. This message can be heard by anyone who wants to listen (and also by anyone that is within “hearing”
|
* @ref MySigningtechnical <br>
|
||||||
* distance). Normally, this is perhaps not a big issue. Nothing Alice says to Bob may be secret or sensitive in any way.
|
* @ref MySigninggrpbackground <br>
|
||||||
* However, sometimes (or perhaps always) Bob want to be sure that the message Bob receives actually came from Alice. In cryptography,
|
* @ref MySigninggrphow <br>
|
||||||
* this is known as <i>authenticity</i>. Bob needs some way of determining that the message is authentic from Alice, when Bob receives it.
|
* @ref MySigninggrpencryption <br>
|
||||||
* This prevents an eavesdropper, Eve, to trick Bob into thinking it was Alice that sent a message Eve in fact transmitted. Bob also needs
|
|
||||||
* to know how to determine if the message has been repeated. Eve could record a message sent by Alice that Bob accepted and then send
|
|
||||||
* the same message again. Eve could also in some way prevent Bob from receiving the message and delay it in order to permit the message
|
|
||||||
* to arrive to Bob at a time Eve chooses, instead of Alice. Such an attack is known as a <b>replay attack</b>.<br>
|
|
||||||
* <i>Authenticity</i> permits Bob to determine if Alice is the true sender of a message.
|
|
||||||
* @image html MySigning/alicenfriends.png
|
|
||||||
* @image latex MySigning/alicenfriends.eps "Alice with Eve and Bob" width=6cm
|
|
||||||
*
|
|
||||||
* It can also be interesting for Bob to know that the message Alice sent has not been tampered with in any way. This is the
|
|
||||||
* <i>integrity</i> of the message. We now introduce Mallory, who could be intercepting the communication between Alice and Bob and
|
|
||||||
* replace some parts of the message but keeping the parts that authenticate the message. That way, Bob still trusts Alice to be the
|
|
||||||
* source, but the contents of the message was not the content Alice sent. Bob needs to be able to determine that the contents of the
|
|
||||||
* message was not altered after Alice sent it.<br>
|
|
||||||
* Mallory would in this case be a <b>man-in-the-middle</b> attacker.<br>
|
|
||||||
* <i>Integrity</i> permits Bob to verify that the messages received from Alice has not been tampered with.<br>
|
|
||||||
* This is achieved by adding a <i>signature</i> to the message, which Bob can inspect to validate that Alice is the author.
|
|
||||||
* @image html MySigning/alicenfriends2.png
|
|
||||||
* @image latex MySigning/alicenfriends2.eps "Alice with Mallory and Bob" width=9cm
|
|
||||||
*
|
|
||||||
* The signing scheme used, needs to address both these attack scenarios. Neither Eve nor Mallory must be permitted to interfere with the
|
|
||||||
* message exchange between Alice and Bob.
|
|
||||||
*
|
|
||||||
* The key challenge to implementing a secure signing scheme is to ensure that every signature is different, even if the message is not.
|
|
||||||
* If not, <b>replay attacks</b> would be very hard to prevent.<br>
|
|
||||||
* One way of doing this is to increment some counter on the sender side and include it in the signature. This is however predictable.<br>
|
|
||||||
* A better option would be to introduce a random number to the signature. That way, it is impossible to predict what the signature will be.
|
|
||||||
* The problem is, that also makes it impossible for the receiver (Bob) to verify that the signature is valid.<br>
|
|
||||||
* A solution to this is to let Bob generate the random number, keep it in memory and send it to Alice. Alice can then use the random number
|
|
||||||
* in the signature calculation and send the signed message back to Bob who can validate the signature with the random number used.
|
|
||||||
* This random number is in cryptography known as a <a href="https://en.wikipedia.org/wiki/Cryptographic_nonce">nonce</a> or
|
|
||||||
* <a href="https://en.wikipedia.org/wiki/Salt_%28cryptography%29">salt</a>.
|
|
||||||
*
|
|
||||||
* However, Mallory might be eavesdropping on the communication and snoop up the nonce in order to generate a new valid signature for a
|
|
||||||
* different message. To counter this, both Alice and Bob keep a secret that only they know. This secret is never transmitted over the air,
|
|
||||||
* nor is it revealed to anybody. This secret is known as a <a href="https://en.wikipedia.org/wiki/Pre-shared_key"> pre-shared key</a> (PSK).
|
|
||||||
*
|
|
||||||
* If Eve or Mallory are really sophisticated, he/she might use a <b>delayed replay attack</b>. This can be done by allowing Bob to transmit
|
|
||||||
* a nonce to Alice. But when Alice transmits the uniquely signed message, Mallory prevents Bob from receiving it, to a point when Mallory
|
|
||||||
* decides Bob should receive it. An example of such an attack is described
|
|
||||||
* <a href="http://spencerwhyte.blogspot.se/2014/03/delay-attack-jam-intercept-and-replay.html">here</a>.<br>
|
|
||||||
* This needs to be addressed as well, and one way of doing this is to have Bob keep track of time between a transmitted nonce and a signed
|
|
||||||
* message to verify. If Bob is asked for a nonce, Bob knows that a signed message is going to arrive “soon”. Bob can then decide that if the
|
|
||||||
* signed message does not arrive within a predefined timeframe, Bob throws away the generated nonce and thus makes it impossible to verify
|
|
||||||
* the message if it arrives late.
|
|
||||||
*
|
|
||||||
* The flow can be described like this:
|
|
||||||
* @image html MySigning/alicenbob.png
|
|
||||||
* @image latex MySigning/alicenbob.eps "Alice and Bob" width=8cm
|
|
||||||
* The benefits for MySensors to support this are obvious. Nobody wants others to be able to control or manipulate any actuators in their home.
|
|
||||||
*
|
|
||||||
* @section MySigninggrpencryption Why encryption is not part of this
|
|
||||||
*
|
|
||||||
* Well, some could be uncomfortable with somebody being able to snoop temperatures, motion or the state changes of locks in the environment.
|
|
||||||
* Signing does <b>not</b> address these issues. Encryption is needed to prevent this.<br>
|
|
||||||
* It is my personal standpoint that encryption should not be part of the MySensors “protocol”. The reason is that a gateway and a node does
|
|
||||||
* not really care about messages being readable or not by “others”. It makes more sense that such guarantees are provided by the underlying
|
|
||||||
* transmission layer (RF solution in this case). It is the information transmitted over the air that needs to be secret (if user so desires).
|
|
||||||
* The “trust” level on the other hand needs to go all the way into the sketches (who might have different requirements of trust depending on
|
|
||||||
* the message participant), and for this reason, it is more important (and less complicated) to ensure authenticity and <i>integrity</i> at
|
|
||||||
* protocol-level as message contents is still readable throughout the protocol stack. But as soon as the message leaves the “stack” it can be
|
|
||||||
* scramble into “garbage” when transmitted over the air and then reassembled by a receiving node before being fed in “the clear” up the stack
|
|
||||||
* at the receiving end.
|
|
||||||
*
|
|
||||||
* There are methods and possibilities to provide encryption also in software, but if this is done, it is my recommendation that this is done
|
|
||||||
* after integrity- and authentication information has been provided to the message (if this is desired). Integrity and authentication is of
|
|
||||||
* course not mandatory and some might be happy with only having encryption to cover their need for security. I, however, have only focused on
|
|
||||||
* <i>integrity</i> and <i>authenticity</i> while at the same time keeping the current message routing mechanisms intact and therefore leave
|
|
||||||
* the matter of <i>secrecy</i> to be implemented in the “physical” transport layer. With the <i>integrity</i> and <i>authenticity</i> handled
|
|
||||||
* in the protocol it ought to be enough for a simple encryption (nonce-less AES with a PSK for instance) on the message as it is sent to the
|
|
||||||
* RF backend. Atmel does provide such circuits as well but I have not investigated the matter further as it given the current size of the
|
|
||||||
* ethernet gateway sketch is close to the size limit on an Arduino Nano, so it will be difficult to fit this into some existing gateway
|
|
||||||
* designs.<br>
|
|
||||||
* Also it is worth to consider that the state of a lock can just as readily be determined by simply looking at the door in question or
|
|
||||||
* attempting to open it, so obfuscating this information will not necessarily deter an attacker in any way.<br>
|
|
||||||
* Nevertheless, I do acknowledge that people find the fact that all information is sent “in the clear” even if it require some technical
|
|
||||||
* effort for an intruder to obtain and inspect this information. So I do encourage the use of encrypting transport layers.<br>
|
|
||||||
* This is however not covered by this implementation.
|
|
||||||
*
|
|
||||||
* @section MySigninggrphow How this is done
|
|
||||||
*
|
|
||||||
* There exist many forms of message signature solutions to combat Eve and Mallory.<br>
|
|
||||||
* Most of these solutions are quite complex in term of computations, so I elected to use an algorithm that an external circuit is able to
|
|
||||||
* process. This has the added benefit of protecting any keys and intermediate data used for calculating the signature so that even if
|
|
||||||
* someone were to actually steal a sensor and disassembled it, they would not be able to extract the keys and other information from the
|
|
||||||
* device.<br>
|
|
||||||
* A common scheme for message signing (authenticity and integrity) is implemented using
|
|
||||||
* <a href="http://en.wikipedia.org/wiki/Hash-based_message_authentication_code">HMAC</a> which in combination with a strong
|
|
||||||
* <a href="http://en.wikipedia.org/wiki/Hash_function"> hash function</a> provides a very strong level of protection.<br>
|
|
||||||
* The <a href="http://www.atmel.com/devices/ATSHA204A.aspx">Atmel ATSHA204A</a> is a low-cost, low-voltage/current circuit that provides
|
|
||||||
* HMAC calculation capabilities with SHA256 hashing which is a (currently) virtually unbreakable combination. If SHA256 were to be hacked,
|
|
||||||
* a certain <a href="http://en.wikipedia.org/wiki/Bitcoin">cryptocurrency</a> would immediately be rendered worthless.<br>
|
|
||||||
* The ATSHA device also contain a random number generator (RNG) which enables the generation of a good nonce, as in,
|
|
||||||
* <i>non-predictable</i>.<br>
|
|
||||||
* As I acknowledge that some might not want to use an additional external circuit, I have also implemented a software version of the ATSHA
|
|
||||||
* device, capable of generating the same signatures as the ATSHA device does. Because it is pure-software however, it does not provide as
|
|
||||||
* good nonces (it uses the <a href="http://arduino.cc/en/reference/random">Arduino pseudo-random generator</a>) and the HMAC key is stored
|
|
||||||
* in SW and is therefore readable if the memory is dumped. It also naturally claims more flash space due to the more complex software. But
|
|
||||||
* for indoor sensors/actuators this might be good enough for most people.
|
|
||||||
*
|
*
|
||||||
* @section MySigninggrphowuse How to use this
|
* @section MySigninggrphowuse How to use this
|
||||||
*
|
*
|
||||||
* Before we begin with the details, I just want to emphasize that signing is completely optional and not enabled by default. If you do want
|
* Before we begin with the details, I just want to emphasize that signing is completely optional
|
||||||
* the additional security layer signing provides, you pick the backend of your choise in your sketch. Currently, two compatible backends are
|
* and not enabled by default.
|
||||||
* supported; @ref MY_SIGNING_ATSHA204 (hardware backed) and @ref MY_SIGNING_SOFT (software backed). You can either enable these globally in
|
|
||||||
* @ref MyConfig.h or in your sketch for sketch specific/local use.
|
|
||||||
*
|
*
|
||||||
* <b>Firstly</b>, you need to make sure to pick a backend to use as described above.
|
* If you do want the additional security layer signing provides, you pick the backend of your
|
||||||
|
* choise in your sketch. Currently, two compatible backends are supported; @ref MY_SIGNING_ATSHA204
|
||||||
|
* (hardware backed) and @ref MY_SIGNING_SOFT (software backed). There also exist a simplified
|
||||||
|
* variant (@ref MY_SIGNING_SIMPLE_PASSWD) of the software backend which only require one setting
|
||||||
|
* to activate.
|
||||||
|
*
|
||||||
|
* To use signng, you need to perform three major steps which are described below.
|
||||||
|
*
|
||||||
|
* <b>Firstly</b>, you need to make sure to pick a backend to use.
|
||||||
* @code{.cpp}
|
* @code{.cpp}
|
||||||
* //#define MY_SIGNING_SOFT
|
* //#define MY_SIGNING_SOFT
|
||||||
|
* //#define MY_SIGNING_SIMPLE_PASSWD
|
||||||
* #define MY_SIGNING_ATSHA204
|
* #define MY_SIGNING_ATSHA204
|
||||||
* #include <MySensors.h>
|
* #include <MySensors.h>
|
||||||
* ...
|
* ...
|
||||||
* @endcode
|
* @endcode
|
||||||
* Make sure to set the define before the inclusion of MySensors.h.
|
* Make sure to set the define before the inclusion of MySensors.h.
|
||||||
* It is legal to mix hardware- and software-based backends in a network. They work together.
|
* It is ok to mix @ref MY_SIGNING_SOFT and @ref MY_SIGNING_ATSHA204 in a network.
|
||||||
|
* They are fully compatible. It is however not recommended to use @ref MY_SIGNING_SOFT on nodes
|
||||||
|
* that are publicly accessible (more on that later).
|
||||||
*
|
*
|
||||||
* You also need to decide if the node (or gateway) in question require and verify signatures in addition to calculating them.
|
* If you use @ref MY_SIGNING_SOFT or @ref MY_SIGNING_ATSHA204 you also need to decide if the node
|
||||||
* This has to be set by at least one of the node in a "pair" or nobody will actually start calculating a signature for a message.
|
* (or gateway) in question require messages to be signed in addition to the ability to generate
|
||||||
* Just set the flag @ref MY_SIGNING_REQUEST_SIGNATURES and the node will inform the gateway that it expects the gateway to sign all
|
* signatures for other nodes.
|
||||||
* messages sent to the node. If this is set in a gateway, it will @b NOT force all nodes to sign messages to it. It will only require
|
* This has to be set by at least one of the nodes in a "pair" or nobody will actually start
|
||||||
* signatures from nodes that in turn require signatures. If it is desired that the gateway should require signatures from all nodes,
|
* calculating a signature for a message.
|
||||||
* @ref MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL can be set in the gateway sketch.<br>
|
* Just set the flag @ref MY_SIGNING_REQUEST_SIGNATURES and the node will inform the gateway that it
|
||||||
* If you want to have two nodes communicate securely directly with each other, the nodes that require signatures must send a presentation
|
* expects the gateway to sign all messages sent to the node. Note that when set in a gateway, the
|
||||||
* message to all nodes it expect signed messages from (only the gateway is informed automatically). See @ref signerPresentation().<br>
|
* gateway will require ALL nodes in the network to sign messages.
|
||||||
|
* If this behaviour is undesired, enable the flag @ref MY_SIGNING_WEAK_SECURITY which will allow
|
||||||
|
* the gateway to only require signatures from nodes that in turn require signatures. It will also
|
||||||
|
* allow the gateway (and all nodes) to "downgrade" security by clearing the signing/whitelisting
|
||||||
|
* requirements (whitelisting is described later on in the @ref MySigningwhitelisting section) in
|
||||||
|
* the EEPROM if a node presents itself as not having any security requirements.
|
||||||
|
* If @ref MY_SIGNING_WEAK_SECURITY is not set, any node that has presented itself with
|
||||||
|
* signing/whitelisting requirements will be permanently marked as such by the receiver
|
||||||
|
* (typically the gateway). The only way then to reset/revert this requirement is to clear the
|
||||||
|
* EEPROM at the receiver (or disable @ref MY_SIGNING_REQUEST_SIGNATURES, but the preference will be
|
||||||
|
* remembered if the request flag is re-enabled before EEPROM is cleared).<br>
|
||||||
|
* If you want to have two nodes communicate securely directly with each other, the nodes that
|
||||||
|
* require signatures must send a presentation message to all nodes it expect signed messages from
|
||||||
|
* (only the gateway is informed automatically). See @ref signerPresentation().<br>
|
||||||
* A node can have three "states" with respect to signing:
|
* A node can have three "states" with respect to signing:
|
||||||
* 1. Node does not support signing in any way (neither @ref MY_SIGNING_ATSHA204 nor @ref MY_SIGNING_SOFT is set)
|
* 1. Node does not support signing in any way (neither @ref MY_SIGNING_ATSHA204,
|
||||||
* 2. Node does support signing but don't require messages sent to it to be signed (@ref MY_SIGNING_REQUEST_SIGNATURES is not set)
|
* @ref MY_SIGNING_SOFT nor @ref MY_SIGNING_SIMPLE_PASSWD is set)
|
||||||
* 3. Node does support signing and require messages sent to it to be signed (@ref MY_SIGNING_REQUEST_SIGNATURES is set)
|
* 2. Node does support signing but don't require messages sent to it to be signed (neither
|
||||||
|
* @ref MY_SIGNING_REQUEST_SIGNATURES nor @ref MY_SIGNING_SIMPLE_PASSWD is set)
|
||||||
|
* 3. Node does support signing and require messages sent to it to be signed (@ref
|
||||||
|
* MY_SIGNING_SOFT or @ref MY_SIGNING_ATSHA204 together with @ref MY_SIGNING_REQUEST_SIGNATURES or
|
||||||
|
* @ref MY_SIGNING_SIMPLE_PASSWD are set)
|
||||||
*
|
*
|
||||||
* <b>Secondly</b>, you need to verify the configuration for the backend.<br>
|
* <b>Secondly</b>, you need to verify the configuration for the backend.<br>
|
||||||
* For hardware backed signing it is the pin the device is connected to. In MyConfig.h there are defaults which you might need to adjust to
|
* For hardware backed signing it is the pin the device is connected to. In MyConfig.h there are
|
||||||
* match your personal build. The setting is defined using @ref MY_SIGNING_ATSHA204_PIN and the default is to use pin A3.<br>
|
* defaults which you might need to adjust to match your personal build. The setting is defined
|
||||||
|
* using @ref MY_SIGNING_ATSHA204_PIN.<br>
|
||||||
|
* If you use an official MySensors board (like the SenseBender GW) you do not need to set the pin,
|
||||||
|
* this is configured automatically by the Arduino board definition files.
|
||||||
|
*
|
||||||
* Similar to picking your backend, this can also be set in your sketch:
|
* Similar to picking your backend, this can also be set in your sketch:
|
||||||
* @code{.cpp}
|
* @code{.cpp}
|
||||||
* #define MY_SIGNING_ATSHA204
|
* #define MY_SIGNING_ATSHA204
|
||||||
@@ -176,12 +108,14 @@
|
|||||||
* #include <MySensors.h>
|
* #include <MySensors.h>
|
||||||
* ...
|
* ...
|
||||||
* @endcode
|
* @endcode
|
||||||
* For the software backed signingbackend, an unconnected analog pin is required to set a random seed for the pseudo-random generator.
|
* For the software backed signing backend, an unconnected analog pin is required on boards that
|
||||||
* It is important that the pin is floating, or the output of the pseudo-random generator will be predictable, and thus compromise the
|
* does not provide a hardware based random generator unit to set a random seed for the
|
||||||
* signatures. The setting is defined using @ref MY_SIGNING_SOFT_RANDOMSEED_PIN and the default is to use pin A7. The same configuration
|
* pseudo-random generator.
|
||||||
* possibilities exist as with the other configuration options.
|
* It is important that the pin is floating, or the output of the pseudo-random generator will be
|
||||||
|
* predictable, and thus compromise the signatures. The setting is defined using
|
||||||
|
* @ref MY_SIGNING_SOFT_RANDOMSEED_PIN. The same configuration possibilities exist as with the other
|
||||||
|
* configuration options.
|
||||||
*
|
*
|
||||||
* <b>Thirdly</b>, if you use the software backend, you need to personalize the node (see @ref personalization).
|
|
||||||
* @code{.cpp}
|
* @code{.cpp}
|
||||||
* #define MY_SIGNING_SOFT
|
* #define MY_SIGNING_SOFT
|
||||||
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
||||||
@@ -192,10 +126,16 @@
|
|||||||
*
|
*
|
||||||
* An example of a node that require signatures is available in @ref SecureActuator.ino.
|
* An example of a node that require signatures is available in @ref SecureActuator.ino.
|
||||||
*
|
*
|
||||||
* @anchor personalization If you use the “real” ATSHA204A, before any signing operations can be done, the device needs to be <i>personalized</i>.
|
* <b>Thirdly</b>, if you use a signing backend and you don't use @ref MY_SIGNING_SIMPLE_PASSWD, you
|
||||||
* This can be a daunting process as it involves irreversibly writing configurations to the device, which cannot be undone. I have however tried
|
* need to personalize the node.
|
||||||
* 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.
|
* @anchor personalization If you use the ATSHA204A (@ref MY_SIGNING_ATSHA204), 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 SecurityPersonalizer.ino
|
||||||
|
* Note that you also need to do personalization for @ref MY_SIGNING_SOFT, but then the values are
|
||||||
|
* stored in EEPROM.
|
||||||
*
|
*
|
||||||
* To personalize a ATSHA204A do the following procedure:
|
* To personalize a ATSHA204A do the following procedure:
|
||||||
* 1. Enable @ref GENERATE_KEYS_ATSHA204A<br>
|
* 1. Enable @ref GENERATE_KEYS_ATSHA204A<br>
|
||||||
@@ -227,66 +167,84 @@
|
|||||||
*
|
*
|
||||||
* When you have personalized your first device after step 2 above, you can run the same sketch on
|
* When you have personalized your first device after step 2 above, you can run the same sketch on
|
||||||
* all devices in your network that needs to be personalized in a compatible manner. Pick
|
* all devices in your network that needs to be personalized in a compatible manner. Pick
|
||||||
* @ref PERSONALIZE_ATSHA204A or @ref PERSONALIZE_SOFT as needed by the hardware.
|
* @ref PERSONALIZE_ATSHA204A or @ref PERSONALIZE_SOFT as needed by the hardware. When the
|
||||||
|
* personalization has finished, you just program the sketch you plan to use (with the appropriate
|
||||||
|
* signing flags set).
|
||||||
*
|
*
|
||||||
* If a node does require signing, any unsigned message sent to the node will be rejected.<br>
|
* You are now set and ready to use message signing in your network.
|
||||||
* This also applies to the gateway. However, the difference is that the gateway will only require signed messages from nodes it knows in turn
|
* As of now, the following restrictions will be applied to your nodes:
|
||||||
* require signed messages (unless @ref MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL is set).<br>
|
* * If a node does require signing, any unsigned message sent to the node will be rejected.
|
||||||
* 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
|
* This also applies to the gateway.
|
||||||
* of type @ref I_SIGNING_PRESENTATION and provide flags as payload that inform the receiver of the signing preferences of the sender.<br>
|
* * Your radio communication quality is expected to work fine (if any NACK happen on a signing
|
||||||
* 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
|
* related message, it will fail, and enabeling signing will put maximum strain on your RF link as
|
||||||
* 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
|
* maximum sized packets are transmitted in the network). See @ref MySigningTroubleshootinggrp.
|
||||||
* expect signed messages.<br>
|
* * All nodes and gateways in a network maintain a table where the signing preferences of all nodes
|
||||||
* 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,
|
* are stored. This is also stored in EEPROM so if a node or gateway reboots, the other nodes does
|
||||||
* the gateway will adhere to this as soon as the new node has presented itself to the gateway. Note however, that if the gateway sets
|
* not have to retransmit a signing presentation to the node for the node to start expecting signed
|
||||||
* @ref MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL a node that does not support signing will be unable to send any data to the gateway.
|
* messages from other nodes.<br>
|
||||||
*
|
* * By default, the signing preferences are not "downgradeable". That is, any node that at any
|
||||||
* The following sequence diagram illustrate how messages are passed in a MySensors network with respect to signing:
|
* point in time has indicated a signing requirement will not be able to revert this requirement at
|
||||||
* @image html MySigning/signingsequence.png
|
* the receiving end (except by manual erase of the EEPROM).<br>
|
||||||
* @image latex MySigning/signingsequence.eps "Signing sequence diagram" width=9cm
|
* If you for some reason need to be able to downgrade the security requirements, you can set
|
||||||
*
|
* @ref MY_SIGNING_WEAK_SECURITY at the receiver to allow it to downgrade the security expectations
|
||||||
* None of this activity is “visible” to you (as the sensor sketch implementor). All you need to do is to set your preferences in MyConfig.h or
|
* of the node in question.<br>
|
||||||
* in your sketch. Depending on chosen backend, do personalization or key configurations and set @ref MY_SIGNING_REQUEST_SIGNATURES on the node that
|
* You then need to reset your transmitting node, to force it to transmit updated signing
|
||||||
* you want protected. That is enough to enable protection from both Eve and Mallory in your network (although if you do not also enable encryption,
|
* preferences.
|
||||||
* Eve can eavesdrop, but not do anything about, your messages).
|
|
||||||
*
|
*
|
||||||
* @section MySigningwhitelisting Whitelisting and node revocation
|
* @section MySigningwhitelisting Whitelisting and node revocation
|
||||||
*
|
*
|
||||||
* Consider the situation when you have set up your secure topology. We use the remotely operated garage door as an example:
|
* Consider the situation when you have set up your secure topology. We use the remotely operated
|
||||||
* - You have a node inside your garage (therefore secure and out of reach from prying eyes) that controls your garage door motor.\n
|
* garage door as an example:
|
||||||
* This node requires signing since you do not want an unauthorized person sending it orders to open the door.
|
* * You have a node inside your garage (considered physically inaccessible) that controls your
|
||||||
* - You have a keyfob node with a signing backend that uses the same PSK as your door opener node.
|
* garage door motor.<br>
|
||||||
|
* This node requires signing since you do not want an unauthorized person sending it orders to
|
||||||
|
* open the door.
|
||||||
|
* * You have a keyfob node with a signing backend that uses the same PSK as your door opener node.
|
||||||
*
|
*
|
||||||
* In this setup, your keyfob can securely transmit messages to your door node since the keyfob will sign the messages it sends and the door node
|
* In this setup, your keyfob can securely transmit messages to your door node since the keyfob will
|
||||||
* will verify that these were sent from a trusted node (since it used the correct PSK). If the keyfob does not sign the messages, the door node
|
* sign the messages it sends and the door node will verify that these were sent from a trusted node
|
||||||
* will not accept them.
|
* (since it used the correct PSK). If the keyfob does not sign the messages, the door node
|
||||||
|
* will not accept them. Optionally, your keyfob sends a signed message to your gateway (which
|
||||||
|
* require signatures) and the gateway in turn sends a signed message to your garage door.
|
||||||
*
|
*
|
||||||
* One day your keyfob gets stolen or you lost it or it simply broke down.
|
* One day your keyfob gets stolen or you lost it or it simply broke down.
|
||||||
*
|
*
|
||||||
* You now end up with a problem; you need some way of telling your door node that the keyfob in question cannot be trusted any more. Furthermore,
|
* You now end up with a problem; you need some way of telling your door node that the keyfob in
|
||||||
* you maybe locked the data region in your door nodes ATSHA device and is not able to revoke/change your PSK, or you have some other reason for
|
* question cannot be trusted any more. You could now repersonalize all your node to switch to a
|
||||||
* not wanting to replace the PSK. How do you make sure that the "rogue" keyfob can be removed from the "trusted chain"?
|
* different PSK but this obviously is a hassle. How do you make sure that the "rogue" keyfob can be
|
||||||
|
* removed from the "trusted chain"?
|
||||||
*
|
*
|
||||||
* The answer to this is whitelisting. You let your door node keep a whitelist of all nodes it trusts. If you stop trusting a particular node, you
|
* The answer to this is whitelisting. You let your door node keep a whitelist of all nodes it
|
||||||
* remove it from the nodes whitelist, and it will no longer be able to communicate signed messages to the door node.
|
* trusts. If you stop trusting a particular node, you remove it from the nodes whitelist
|
||||||
|
* (by uploading a new sketch), and it will no longer be able to communicate signed messages to the
|
||||||
|
* door node.
|
||||||
*
|
*
|
||||||
* In case you want to be able to "whitelist" trusted nodes (in order to be able to revoke them in case they are lost) you also need to take note
|
* In case you want to be able to "whitelist" trusted nodes (in order to be able to revoke them in
|
||||||
* 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
|
* case they are lost) you also need to take note 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>
|
* 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
|
* The whitelist is stored on the node that require signatures. When a received message is
|
||||||
* list stored on the receiving node, and the corresponding serial stored in the list for that sender is then included in the signature verification
|
* verified, the serial of the sender is looked up in a list stored on the receiving node, and the
|
||||||
* process. The list is stored as the value of the flag that enables whitelisting, @ref MY_SIGNING_NODE_WHITELISTING.<br>
|
* 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>
|
||||||
*
|
*
|
||||||
* Whitelisting is achieved by 'salting' the signature with some node-unique information known to the receiver. In the case of ATSHA204A this is the
|
* Whitelisting is achieved by 'salting' the signature with some node-unique information known to
|
||||||
* 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
|
* the receiver. In the case of @ref MY_SIGNING_ATSHA204 this is the unique serial number programmed
|
||||||
* figure out a "trusted" serial by snooping on the traffic.<br>
|
* into the circuit. This unique number is never transmitted over the air in clear text, so Eve will
|
||||||
* Instead the value is hashed together with the senders NodeId into the HMAC signature to produce the final signature. The receiver will then take
|
* not be able to figure out a "trusted" serial by snooping on the traffic.<br>
|
||||||
* 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
|
* Instead the value is hashed together with the senders NodeId into the HMAC signature to produce
|
||||||
|
* the final signature. The receiver will then take the originating NodeId of the signed message and
|
||||||
|
* do the corresponding calculation with the serial it has stored in it's whitelist if it finds a
|
||||||
* matching entry in it's whitelist.
|
* matching entry in it's whitelist.
|
||||||
*
|
*
|
||||||
* Whitelisting is an optional alternative because it adds some code and configuration options which might not be desirable for every user. So if
|
* Whitelisting is an optional alternative because it adds some code and configuration options which
|
||||||
* you want the ability to use whitelists, you need to enable @ref MY_SIGNING_NODE_WHITELISTING.<br>
|
* might not be desirable for every user. So if you want the ability to use whitelists, you need to
|
||||||
* The whitelist is provided as value of the flag that enable it as follows (example is a node that require signing as well):
|
* enable @ref MY_SIGNING_NODE_WHITELISTING. You need to remember that the gateway will remember if
|
||||||
|
* a node has presented it with a whitelisting requirement as described above, if you at some point
|
||||||
|
* decide to remove the whitelist requirement.<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}
|
* @code{.cpp}
|
||||||
* #define MY_SIGNING_ATSHA204
|
* #define MY_SIGNING_ATSHA204
|
||||||
* #define MY_SIGNING_REQUEST_SIGNATURES
|
* #define MY_SIGNING_REQUEST_SIGNATURES
|
||||||
@@ -294,8 +252,9 @@
|
|||||||
* #include <MySensors.h>
|
* #include <MySensors.h>
|
||||||
* ...
|
* ...
|
||||||
* @endcode
|
* @endcode
|
||||||
* In this example, there are two nodes in the whitelist; the gateway, and a separate node that communicates directly with this node (with signed
|
* In this example, there are two nodes in the whitelist; the gateway, and a separate node that
|
||||||
* messages). You do not need to do anything special for the sending nodes, apart from making sure they support signing.
|
* communicates directly with this node (with signed 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. Example:
|
* The "soft" backend of course also support whitelisting. Example:
|
||||||
* @code{.cpp}
|
* @code{.cpp}
|
||||||
@@ -307,65 +266,45 @@
|
|||||||
* ...
|
* ...
|
||||||
* @endcode
|
* @endcode
|
||||||
*
|
*
|
||||||
* For a node that should transmit whitelisted messages but not receive whitelisted messages, you do not need any special configurations:
|
* For a node that should transmit whitelisted messages but not receive whitelisted messages, you do
|
||||||
|
* not need any special configurations:
|
||||||
* @code{.cpp}
|
* @code{.cpp}
|
||||||
* #define MY_SIGNING_SOFT
|
* #define MY_SIGNING_SOFT
|
||||||
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
||||||
* @endcode
|
* @endcode
|
||||||
* Remember that you always need to select a signing backend for all nodes that communicate to a node that require whitelisting.
|
* Remember that you always need to select a signing backend for all nodes that communicate to a
|
||||||
* Also, note that a node that use whitelisting will not accept messages from nodes that are not present in it's whitelist.
|
* node that require whitelisting. Also, note that a node that use whitelisting will not accept
|
||||||
* And you have to personalize all nodes that use signing with a common HMAC key but different serial numbers (ATSHA204A always has unique serials).
|
* 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
|
||||||
* @section MySigningtechnical The technical stuff
|
* serial numbers (@ref MY_SIGNING_ATSHA204 always has unique serials).
|
||||||
*
|
|
||||||
* How are the messages actually affected by the signing?<br>
|
|
||||||
* The following illustration shows what part of the message is signed, and where the signature is stored:
|
|
||||||
* @image html MySigning/signingillustrated1.png
|
|
||||||
* @image latex MySigning/signingillustrated1.eps "Message structure" width=11cm
|
|
||||||
*
|
|
||||||
* The first byte of the header is not covered by the signature, because in the network, this byte is used to track hops in the network and therefore
|
|
||||||
* might change if the message is passing a relay node. So it cannot be part of the signature, or the signature would be invalid when it arrives to
|
|
||||||
* its destination. The signature also carries a byte with a signing identifier to prevent false results from accidental mixing of incompatible
|
|
||||||
* signing backends in the network. Thus, the maximum size for a payload is 29-7 bytes. Larger payloads are not possible to sign. Another thing to
|
|
||||||
* consider is that the strength of the signature is inversely proportional to the payload size.
|
|
||||||
*
|
|
||||||
* As for the software backend, it turns out that the ATSHA does not do “vanilla” HMAC processing. Fortunately, Atmel has documented exactly how
|
|
||||||
* the circuit processes the data and hashes thus making it possible to generate signatures that are identical to signatures generated by the circuit.
|
|
||||||
*
|
|
||||||
* The signatures are calculates in the following way:
|
|
||||||
* @image html MySigning/signingillustrated2.png
|
|
||||||
* @image latex MySigning/signingillustrated2.eps "HMAC processing" width=10cm
|
|
||||||
*
|
|
||||||
* 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_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.
|
|
||||||
*
|
*
|
||||||
* @section MySigninglimitations Known limitations
|
* @section MySigninglimitations Known limitations
|
||||||
*
|
*
|
||||||
* Due to the limiting factor of our cheapest Arduino nodes, the use of diversified keys is not implemented. That mean that all nodes in your
|
* Due to the limiting factor of our cheapest Arduino nodes, the use of diversified keys is not
|
||||||
* network share the same PSK (at least the ones that are supposed to exchange signed data). It is important to understand the implications of
|
* implemented. That mean that all nodes in your network share the same PSK (at least the ones that
|
||||||
|
* are supposed to exchange signed data). It is important to understand the implications of
|
||||||
* this, and that is covered in the "Typical use cases" chapter below.<br>
|
* this, and that is covered in the "Typical use cases" chapter below.<br>
|
||||||
* Also be reminded that the strength of the signature is inversely proportional to the size of the message. The larger the message, the weaker
|
* Most importantly, if you use @ref MY_SIGNING_SOFT your PSK will be stored in EEPROM and will
|
||||||
* the signature.
|
* therefore be accessible by anyone with physical access to your node. Therefore it is <b>NOT</b>
|
||||||
|
* recommended to use @ref MY_SIGNING_SOFT on nodes that are placed in a public place or worn on
|
||||||
|
* on your person (like a keyfob).<br>
|
||||||
|
* Also be reminded that the strength of the signature is inversely proportional to the size of the
|
||||||
|
* message. The larger the message, the weaker the signature.
|
||||||
*
|
*
|
||||||
* @section MySigningusecases Typical use cases
|
* @section MySigningusecases Typical use cases
|
||||||
*
|
*
|
||||||
* "Securely located" in this context mean a node which is not physically publicly accessible. Typically at least your gateway.<br>
|
* "Securely located" in this context mean a node which is not physically publicly accessible.
|
||||||
* "Public" in this context mean a node that is located outside your "trusted environment". This includes sensors located outdoors, keyfobs etc.
|
* Typically at least your gateway.<br>
|
||||||
|
* "Public" in this context mean a node that is located outside your "trusted environment". This
|
||||||
|
* includes sensors located outdoors, keyfobs etc.
|
||||||
*
|
*
|
||||||
* @subsection MySigninglock Securely located lock
|
* @subsection MySigninglock Securely located lock
|
||||||
*
|
*
|
||||||
* You have a securely located gateway and a lock somewhere inside your "trusted environment" (e.g. inside your house door, the door to your
|
* You have a securely located gateway and a lock somewhere inside your "trusted environment" (e.g.
|
||||||
* dungeon or similar).<br>
|
* inside your house door, the door to your dungeon or similar).<br>
|
||||||
* You should then keep the data section of your gateway and your lock node unlocked. Locking the data (and therefore the PSK) will require you
|
* You need to make your node require signed messages but you do not necessarily need to make your
|
||||||
* to replace at least the signing circuit in case you need to revoke the PSK because some other node in your network gets compromised.<br>
|
* gateway require signed messsages (unless you are concerned that someone might spoof the lock
|
||||||
* You need to make your node require signed messages but you do not necessarily need to make your gateway require signed messsages (unless you
|
* status of your lock).<br>
|
||||||
* are concerned that someone might spoof the lock status of your lock).<br>
|
|
||||||
* Configuration example for the secure lock node:<br>
|
* Configuration example for the secure lock node:<br>
|
||||||
* @code{.cpp}
|
* @code{.cpp}
|
||||||
* #define MY_SIGNING_ATSHA204
|
* #define MY_SIGNING_ATSHA204
|
||||||
@@ -373,18 +312,20 @@
|
|||||||
* #include <MySensors.h>
|
* #include <MySensors.h>
|
||||||
* ...
|
* ...
|
||||||
* @endcode
|
* @endcode
|
||||||
* If you do also want your gateway to require signatures from your lock you just enable the same (or similar if using software signing) settings
|
* If you do also want your gateway to require signatures from your lock you just enable the same
|
||||||
* in the gateway.
|
* (or similar if using software signing) settings in the gateway.
|
||||||
*
|
*
|
||||||
* @subsection MySigningpatio Patio motion sensor
|
* @subsection MySigningpatio Patio motion sensor
|
||||||
*
|
*
|
||||||
* Your gateway is securely located inside your house, but your motion sensor is located outside your house. You have for some reason elected
|
* Your gateway is securely located inside your house, but your motion sensor is located outside
|
||||||
* that this node should sign the messages it send to your gateway. You should lock the data (PSK) in this node then, because if someone were
|
* your house. You have for some reason elected that this node should sign the messages it send to
|
||||||
* to steal your patio motion sensor, they could rewrite the firmware and spoof your gateway to use it to transmit a correctly signed message
|
* your gateway. You should lock the data (PSK) in this node then, because if someone were to steal
|
||||||
* to your secure lock inside your house. But if you revoke your gateway (and lock) PSK the outside sensor cannot be used for this anymore.
|
* your patio motion sensor, they could rewrite the firmware and spoof your gateway to use it to
|
||||||
* Nor can it be changed in order to do it in the future. You can also use whitelisting to revoke your lost node.<br>
|
* transmit a correctly signed message to your secure lock inside your house. But if you revoke your
|
||||||
* 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,
|
* gateway (and lock) PSK the outside sensor cannot be used for this anymore. Nor can it be changed
|
||||||
* encryption is a better alternative.<br>
|
* 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:<br>
|
* Configuration example for a motion sensor:<br>
|
||||||
* @code{.cpp}
|
* @code{.cpp}
|
||||||
* #define MY_SIGNING_SOFT
|
* #define MY_SIGNING_SOFT
|
||||||
@@ -394,7 +335,8 @@
|
|||||||
* ...
|
* ...
|
||||||
* @endcode
|
* @endcode
|
||||||
*
|
*
|
||||||
* The gateway needs to be configured with a whitelist (and it has to have an entry for all nodes that send and/or require signed messages):<br>
|
* The gateway needs to be configured with a whitelist (and it has to have an entry for all nodes
|
||||||
|
* that send and/or require signed messages):<br>
|
||||||
* @code{.cpp}
|
* @code{.cpp}
|
||||||
* #define MY_SIGNING_SOFT
|
* #define MY_SIGNING_SOFT
|
||||||
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
||||||
@@ -406,17 +348,21 @@
|
|||||||
|
|
||||||
* @subsection MySigningkeyfob Keyfob for garage door opener
|
* @subsection MySigningkeyfob Keyfob for garage door opener
|
||||||
*
|
*
|
||||||
* Perhaps the most typical usecase for signed messages. Your keyfob should be totally locked down. If the garage door opener is secured
|
* Perhaps the most typical usecase for signed messages. Your keyfob should be totally locked down.
|
||||||
* (and it should be) it can be unlocked. That way, if you loose your keyfob, you can revoke the PSK in both the opener and your gateway,
|
* If the garage door opener is secured (and it should be) it can be unlocked. That way, if you
|
||||||
* thus rendering the keyfob useless without having to replace your nodes. You can also use whitelisting to revoke your lost keyfob.<br>
|
* loose your keyfob, you can revoke the PSK in both the opener and your gateway,
|
||||||
* Configuration example for the keyfob (keyfob will only transmit to another node and not receive anything):<br>
|
* thus rendering the keyfob useless without having to replace your nodes. You can also use
|
||||||
|
* whitelisting to revoke your lost keyfob.<br>
|
||||||
|
* Configuration example for the keyfob (keyfob will only transmit to another node and not receive
|
||||||
|
* anything):<br>
|
||||||
* @code{.cpp}
|
* @code{.cpp}
|
||||||
* #define MY_SIGNING_ATSHA204
|
* #define MY_SIGNING_ATSHA204
|
||||||
* #include <MySensors.h>
|
* #include <MySensors.h>
|
||||||
* ...
|
* ...
|
||||||
* @endcode
|
* @endcode
|
||||||
*
|
*
|
||||||
* Configuration example for the door controller node (should require signing from anyone who wants to control it):<br>
|
* Configuration example for the door controller node (should require signing from anyone who wants
|
||||||
|
* to control it):<br>
|
||||||
* @code{.cpp}
|
* @code{.cpp}
|
||||||
* #define MY_SIGNING_SOFT
|
* #define MY_SIGNING_SOFT
|
||||||
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
||||||
@@ -430,6 +376,200 @@
|
|||||||
*
|
*
|
||||||
* - @ref SecureActuator.ino
|
* - @ref SecureActuator.ino
|
||||||
* - @ref SecurityPersonalizer.ino
|
* - @ref SecurityPersonalizer.ino
|
||||||
|
*
|
||||||
|
* @section MySigningtechnical The technical stuff
|
||||||
|
*
|
||||||
|
* The following sequence diagram illustrate how messages are passed in a MySensors network with
|
||||||
|
* respect to signing:
|
||||||
|
* @image html MySigning/signingsequence.png
|
||||||
|
* @image latex MySigning/signingsequence.eps "Signing sequence diagram" width=9cm
|
||||||
|
*
|
||||||
|
* None of this activity is “visible” to you (as the sensor sketch implementor). All you need to do
|
||||||
|
* is to set your preferences in your sketch and personalize accordingly.
|
||||||
|
* That is enough to enable protection from both Eve and Mallory in your network
|
||||||
|
* although if you do not also enable encryption, Eve can eavesdrop, but not do anything about,
|
||||||
|
* your messages (except possibly preventing them from arriving). @ref MY_SIGNING_SIMPLE_PASSWD also
|
||||||
|
* enable encryption autmatically.
|
||||||
|
*
|
||||||
|
* How are the messages actually affected by the signing?<br>
|
||||||
|
* The following illustration shows what part of the message is signed, and where the signature is
|
||||||
|
* stored:
|
||||||
|
* @image html MySigning/signingillustrated1.png
|
||||||
|
* @image latex MySigning/signingillustrated1.eps "Message structure" width=11cm
|
||||||
|
*
|
||||||
|
* The first byte of the header is not covered by the signature, because in the network, this byte
|
||||||
|
* is used to track hops in the network and therefore might change if the message is passing a relay
|
||||||
|
* node. So it cannot be part of the signature, or the signature would be invalid when it arrives to
|
||||||
|
* its destination. The signature also carries a byte with a signing identifier to prevent false
|
||||||
|
* results from accidental mixing of incompatible signing backends in the network. Thus, the maximum
|
||||||
|
* size for a payload is 29-7 bytes. Larger payloads are not possible to sign at the moment. Another
|
||||||
|
* thing to consider is that the strength of the signature is inversely proportional to the payload
|
||||||
|
* size.
|
||||||
|
*
|
||||||
|
* As for the software backend, it turns out that the ATSHA does not do “vanilla” HMAC processing.
|
||||||
|
* Fortunately, Atmel has documented exactly how the circuit processes the data and hashes thus
|
||||||
|
* making it possible to generate signatures that are identical to signatures generated by the
|
||||||
|
* circuit.
|
||||||
|
*
|
||||||
|
* The signatures are calculates in the following way:
|
||||||
|
* @image html MySigning/signingillustrated2.png
|
||||||
|
* @image latex MySigning/signingillustrated2.eps "HMAC processing" width=10cm
|
||||||
|
*
|
||||||
|
* Exactly how this is done can be reviewed in the source for the software backend
|
||||||
|
* (MySigningAtsha204Soft.cpp) 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_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.
|
||||||
|
*
|
||||||
|
* @section MySigninggrpbackground Background and concepts
|
||||||
|
*
|
||||||
|
* Suppose two participants, Alice and Bob, wants to exchange a message. Alice sends a message to
|
||||||
|
* Bob. In MySensors “language” Alice could be a gateway and Bob an actuator (light swich,
|
||||||
|
* electronic lock, etc). But to be generic, we will substitute the term “gateway” with Alice and a
|
||||||
|
* “node” with Bob (although the reverse relationship is also supported).
|
||||||
|
*
|
||||||
|
* Alice sends a message to Bob. This message can be heard by anyone who wants to listen (and also
|
||||||
|
* by anyone that is within “hearing” distance). Normally, this is perhaps not a big issue. Nothing
|
||||||
|
* Alice says to Bob may be secret or sensitive in any way. However, sometimes (or perhaps always)
|
||||||
|
* Bob want to be sure that the message Bob receives actually came from Alice. In cryptography,
|
||||||
|
* this is known as <i>authenticity</i>. Bob needs some way of determining that the message is
|
||||||
|
* authentic from Alice, when Bob receives it. This prevents an eavesdropper, Eve, to trick Bob into
|
||||||
|
* thinking it was Alice that sent a message Eve in fact transmitted. Bob also needs to know how to
|
||||||
|
* determine if the message has been repeated. Eve could record a message sent by Alice that Bob
|
||||||
|
* accepted and then send the same message again. Eve could also in some way prevent Bob from
|
||||||
|
* receiving the message and delay it in order to permit the message to arrive to Bob at a time Eve
|
||||||
|
* chooses, instead of Alice. Such an attack is known as a <b>replay attack</b>.<br>
|
||||||
|
* <i>Authenticity</i> permits Bob to determine if Alice is the true sender of a message.
|
||||||
|
* @image html MySigning/alicenfriends.png
|
||||||
|
* @image latex MySigning/alicenfriends.eps "Alice with Eve and Bob" width=6cm
|
||||||
|
*
|
||||||
|
* It can also be interesting for Bob to know that the message Alice sent has not been tampered with
|
||||||
|
* in any way. This is the <i>integrity</i> of the message. We now introduce Mallory, who could be
|
||||||
|
* intercepting the communication between Alice and Bob and replace some parts of the message but
|
||||||
|
* keeping the parts that authenticate the message. That way, Bob still trusts Alice to be the
|
||||||
|
* source, but the contents of the message was not the content Alice sent. Bob needs to be able to
|
||||||
|
* determine that the contents of the message was not altered after Alice sent it.<br>
|
||||||
|
* Mallory would in this case be a <b>man-in-the-middle</b> attacker.<br>
|
||||||
|
* <i>Integrity</i> permits Bob to verify that the messages received from Alice has not been
|
||||||
|
* tampered with.<br>
|
||||||
|
* This is achieved by adding a <i>signature</i> to the message, which Bob can inspect to validate
|
||||||
|
* that Alice is the author.
|
||||||
|
* @image html MySigning/alicenfriends2.png
|
||||||
|
* @image latex MySigning/alicenfriends2.eps "Alice with Mallory and Bob" width=9cm
|
||||||
|
*
|
||||||
|
* The signing scheme used, needs to address both these attack scenarios. Neither Eve nor Mallory
|
||||||
|
* must be permitted to interfere with the message exchange between Alice and Bob.
|
||||||
|
*
|
||||||
|
* The key challenge to implementing a secure signing scheme is to ensure that every signature is
|
||||||
|
* different, even if the message is not. If not, <b>replay attacks</b> would be very hard to
|
||||||
|
* prevent.<br>
|
||||||
|
* One way of doing this is to increment some counter on the sender side and include it in the
|
||||||
|
* signature. This is however predictable.<br>
|
||||||
|
* A better option would be to introduce a random number to the signature. That way, it is
|
||||||
|
* impossible to predict what the signature will be. The problem is, that also makes it impossible
|
||||||
|
* for the receiver (Bob) to verify that the signature is valid.<br>
|
||||||
|
* A solution to this is to let Bob generate the random number, keep it in memory and send it to
|
||||||
|
* Alice. Alice can then use the random number in the signature calculation and send the signed
|
||||||
|
* message back to Bob who can validate the signature with the random number used.
|
||||||
|
* This random number is in cryptography known as a
|
||||||
|
* <a href="https://en.wikipedia.org/wiki/Cryptographic_nonce">nonce</a> or
|
||||||
|
* <a href="https://en.wikipedia.org/wiki/Salt_%28cryptography%29">salt</a>.
|
||||||
|
*
|
||||||
|
* However, Mallory might be eavesdropping on the communication and snoop up the nonce in order to
|
||||||
|
* generate a new valid signature for a different message. To counter this, both Alice and Bob keep
|
||||||
|
* a secret that only they know. This secret is never transmitted over the air,
|
||||||
|
* nor is it revealed to anybody. This secret is known as a
|
||||||
|
* <a href="https://en.wikipedia.org/wiki/Pre-shared_key"> pre-shared key</a> (PSK).
|
||||||
|
*
|
||||||
|
* If Eve or Mallory are really sophisticated, he/she might use a <b>delayed replay attack</b>.
|
||||||
|
* This can be done by allowing Bob to transmit a nonce to Alice. But when Alice transmits the
|
||||||
|
* uniquely signed message, Mallory prevents Bob from receiving it, to a point when Mallory
|
||||||
|
* decides Bob should receive it. An example of such an attack is described
|
||||||
|
* <a href="http://spencerwhyte.blogspot.se/2014/03/delay-attack-jam-intercept-and-replay.html">
|
||||||
|
* here</a>.<br>
|
||||||
|
* This needs to be addressed as well, and one way of doing this is to have Bob keep track of time
|
||||||
|
* between a transmitted nonce and a signed message to verify. If Bob is asked for a nonce, Bob
|
||||||
|
* knows that a signed message is going to arrive “soon”. Bob can then decide that if the signed
|
||||||
|
* message does not arrive within a predefined timeframe, Bob throws away the generated nonce and
|
||||||
|
* thus makes it impossible to verify the message if it arrives late.
|
||||||
|
*
|
||||||
|
* The flow can be described like this:
|
||||||
|
* @image html MySigning/alicenbob.png
|
||||||
|
* @image latex MySigning/alicenbob.eps "Alice and Bob" width=8cm
|
||||||
|
* The benefits for MySensors to support this are obvious. Nobody wants others to be able to control
|
||||||
|
* or manipulate any actuators in their home.
|
||||||
|
*
|
||||||
|
* @section MySigninggrphow How this is done
|
||||||
|
*
|
||||||
|
* There exist many forms of message signature solutions to combat Eve and Mallory.<br>
|
||||||
|
* Most of these solutions are quite complex in term of computations, so I elected to use an
|
||||||
|
* algorithm that an external circuit is able to process. This has the added benefit of protecting
|
||||||
|
* any keys and intermediate data used for calculating the signature so that even if someone were to
|
||||||
|
* actually steal a sensor and disassembled it, they would not be able to extract the keys and other
|
||||||
|
* information from the device.<br>
|
||||||
|
* A common scheme for message signing (authenticity and integrity) is implemented using
|
||||||
|
* <a href="http://en.wikipedia.org/wiki/Hash-based_message_authentication_code">HMAC</a> which in
|
||||||
|
* combination with a strong <a href="http://en.wikipedia.org/wiki/Hash_function"> hash function</a>
|
||||||
|
* provides a very strong level of protection.<br>
|
||||||
|
* The <a href="http://www.atmel.com/devices/ATSHA204A.aspx">Atmel ATSHA204A</a> is a low-cost,
|
||||||
|
* low-voltage/current circuit that provides HMAC calculation capabilities with SHA256 hashing which
|
||||||
|
* is a (currently) virtually unbreakable combination. If SHA256 were to be hacked, a certain
|
||||||
|
* <a href="http://en.wikipedia.org/wiki/Bitcoin">cryptocurrency</a> would immediately be rendered
|
||||||
|
* worthless.<br>
|
||||||
|
* The ATSHA device also contain a random number generator (RNG) which enables the generation of a
|
||||||
|
* good nonce, as in, <i>non-predictable</i>.<br>
|
||||||
|
* As I acknowledge that some might not want to use an additional external circuit, I have also
|
||||||
|
* implemented a software version of the ATSHA device, capable of generating the same signatures as
|
||||||
|
* the ATSHA device does. Because it is pure-software however, it does not provide as good nonces
|
||||||
|
* (it uses the <a href="http://arduino.cc/en/reference/random">Arduino pseudo-random generator</a>)
|
||||||
|
* and the HMAC key is stored in SW and is therefore readable if the memory is dumped. It also
|
||||||
|
* naturally claims more flash space due to the more complex software. But for indoor
|
||||||
|
* sensors/actuators this might be good enough for most people.
|
||||||
|
*
|
||||||
|
* @section MySigninggrpencryption Why encryption is not part of this
|
||||||
|
*
|
||||||
|
* Well, some could be uncomfortable with somebody being able to snoop temperatures, motion or the
|
||||||
|
* state changes of locks in the environment.
|
||||||
|
* Signing does <b>not</b> address these issues. Encryption is needed to prevent this.<br>
|
||||||
|
* It is my personal standpoint that encryption should not be part of the MySensors “protocol”. The
|
||||||
|
* reason is that a gateway and a node does not really care about messages being readable or not by
|
||||||
|
* “others”. It makes more sense that such guarantees are provided by the underlying transmission
|
||||||
|
* layer (RF solution in this case). It is the information transmitted over the air that needs to be
|
||||||
|
* secret (if user so desires). The “trust” level on the other hand needs to go all the way into the
|
||||||
|
* sketches (who might have different requirements of trust depending on the message participant),
|
||||||
|
* and for this reason, it is more important (and less complicated) to ensure authenticity and
|
||||||
|
* <i>integrity</i> at protocol-level as message contents is still readable throughout the protocol
|
||||||
|
* stack. But as soon as the message leaves the “stack” it can be scramble into “garbage” when
|
||||||
|
* transmitted over the air and then reassembled by a receiving node before being fed in “the clear”
|
||||||
|
* up the stack at the receiving end.
|
||||||
|
*
|
||||||
|
* There are methods and possibilities to provide encryption also in software, but if this is done,
|
||||||
|
* it is my recommendation that this is done after integrity- and authentication information has
|
||||||
|
* been provided to the message (if this is desired). Integrity and authentication is of course not
|
||||||
|
* mandatory and some might be happy with only having encryption to cover their need for security.
|
||||||
|
* I, however, have only focused on <i>integrity</i> and <i>authenticity</i> while at the same time
|
||||||
|
* keeping the current message routing mechanisms intact and therefore leave the matter of
|
||||||
|
* <i>secrecy</i> to be implemented in the “physical” transport layer. With the <i>integrity</i> and
|
||||||
|
* <i>authenticity</i> handled in the protocol it ought to be enough for a simple encryption
|
||||||
|
* (nonce-less AES with a PSK for instance) on the message as it is sent to the RF backend. Atmel
|
||||||
|
* does provide such circuits as well but I have not investigated the matter further as it given the
|
||||||
|
* current size of the ethernet gateway sketch is close to the size limit on an Arduino Nano, so it
|
||||||
|
* will be difficult to fit this into some existing gateway designs.<br>
|
||||||
|
* Also it is worth to consider that the state of a lock can just as readily be determined by simply
|
||||||
|
* looking at the door in question or attempting to open it, so obfuscating this information will
|
||||||
|
* not necessarily deter an attacker in any way.<br>
|
||||||
|
* Nevertheless, I do acknowledge that people find the fact that all information is sent “in the
|
||||||
|
* clear” even if it require some technical effort for an intruder to obtain and inspect this
|
||||||
|
* information. So I do encourage the use of encrypting transport layers.<br>
|
||||||
|
* This is however not covered by this implementation.<br>
|
||||||
|
* This might change in the future as more powerful platforms emerge which permit more complex
|
||||||
|
* security schemes and better hardware acceleration.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -462,16 +602,6 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
void signerInit(void);
|
void signerInit(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Validates personalization data
|
|
||||||
*
|
|
||||||
* This function checks that the personalization details are in order.
|
|
||||||
* \n@b Usage: This fuction should be called before any signing related operations take place.
|
|
||||||
*
|
|
||||||
* @returns @c true if personalization data is valid.
|
|
||||||
*/
|
|
||||||
bool signerValidatePersonalization(void);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Does signing specific presentation for a node.
|
* @brief Does signing specific presentation for a node.
|
||||||
*
|
*
|
||||||
@@ -605,8 +735,94 @@ int signerMemcmp(const void* a, const void* b, size_t sz);
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
/** @}*/
|
/** @}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @defgroup MySigningTroubleshootinggrp Troubleshooting
|
* @defgroup MySigningDebugMessages Signing related debug messages
|
||||||
|
* @ingroup MySigninggrp
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* MySigning-related log messages, format: [!]SYSTEM:SUB SYSTEM:MESSAGE
|
||||||
|
* - [!] Exclamation mark is prepended in case of error or warning
|
||||||
|
* - SYSTEM:
|
||||||
|
* - <b>SGN</b> messages emitted by MySigning
|
||||||
|
* - SUB SYSTEMS:
|
||||||
|
* - SGN:<b>INI</b> from @ref signerInit
|
||||||
|
* - SGN:<b>PER</b> from @ref signerInit
|
||||||
|
* - SGN:<b>PRE</b> from @ref signerPresentation
|
||||||
|
* - SGN:<b>SGN</b> from @ref signerSignMsg
|
||||||
|
* - SGN:<b>VER</b> from @ref signerVerifyMsg
|
||||||
|
* - SGN:<b>SKP</b> from @ref signerSignMsg or @ref signerVerifyMsg (skipSign)
|
||||||
|
* - SGN:<b>NCE</b> from @ref signerProcessInternal (signerInternalProcessNonceRequest)
|
||||||
|
* - SGN:<b>BND</b> from the signing backends
|
||||||
|
*
|
||||||
|
* MySigning debug log messages:
|
||||||
|
*
|
||||||
|
* |E| SYS | SUB | Message | Comment
|
||||||
|
* |-|-----|-----|--------------------------|----------------------------------------------------------------------------
|
||||||
|
* | | SGN | INI | BND OK | Backend has initialized ok
|
||||||
|
* |!| SGN | INI | BND FAIL | Backend has not initialized ok
|
||||||
|
* | | SGN | PER | OK | Personalization data is ok
|
||||||
|
* |!| SGN | PER | TAMPERED | Personalization data has been tampered
|
||||||
|
* | | SGN | PRE | SGN REQ | Signing required
|
||||||
|
* | | SGN | PRE | SGN REQ,TO='node' | Tell 'node' that we require signing
|
||||||
|
* | | SGN | PRE | SGN REQ,FROM='node' | Node 'node' require signing
|
||||||
|
* | | SGN | PRE | SGN NREQ | Signing not required
|
||||||
|
* | | SGN | PRE | SGN REQ,TO='node' | Tell 'node' that we do not require signing
|
||||||
|
* | | SGN | PRE | SGN NREQ,FROM='node' | Node 'node' does not require signing
|
||||||
|
* |!| SGN | PRE | SGN NREQ,FROM='node' REJ | Node 'node' does not require signing but used to (requirement remain unchanged)
|
||||||
|
* | | SGN | PRE | WHI REQ | Whitelisting required
|
||||||
|
* | | SGN | PRE | WHI REQ;TO='node' | Tell 'node' that we require whitelisting
|
||||||
|
* | | SGN | PRE | WHI REQ,FROM='node' | Node 'node' require whitelisting
|
||||||
|
* | | SGN | PRE | WHI NREQ | Whitelisting not required
|
||||||
|
* | | SGN | PRE | WHI NREQ,TO='node' | Tell 'node' that we do not require whitelisting
|
||||||
|
* | | SGN | PRE | WHI NREQ,FROM='node' | Node 'node' does not require whitelisting
|
||||||
|
* |!| SGN | PRE | WHI NREQ,FROM='node' REJ | Node 'node' does not require whitelisting but used to (requirement remain unchanged)
|
||||||
|
* | | SGN | PRE | XMT,TO='node' | Presentation data transmitted to 'node'
|
||||||
|
* |!| SGN | PRE | XMT,TO='node' FAIL | Presentation data not properly transmitted to 'node'
|
||||||
|
* | | SGN | PRE | WAIT GW | Waiting for gateway presentation data
|
||||||
|
* |!| SGN | PRE | VER='version' | Presentation version 'version' is not supported
|
||||||
|
* | | SGN | PRE | NSUP | Received signing presentation but signing is not supported
|
||||||
|
* | | SGN | PRE | NSUP,TO='node' | Informing 'node' that we do not support signing
|
||||||
|
* | | SGN | SGN | NCE REQ,TO='node' | Nonce request transmitted to 'node'
|
||||||
|
* |!| SGN | SGN | NCE REQ,TO='node' FAIL | Nonce request not properly transmitted to 'node'
|
||||||
|
* |!| SGN | SGN | NCE TMO | Timeout waiting for nonce
|
||||||
|
* | | SGN | SGN | SGN | Message signed
|
||||||
|
* |!| SGN | SGN | SGN FAIL | Message failed to be signed
|
||||||
|
* | | SGN | SGN | NREQ='node' | 'node' does not require signed messages
|
||||||
|
* | | SGN | SGN | 'sender'!='us' NUS | Will not sign because 'sender' is not 'us' (repeater)
|
||||||
|
* |!| SGN | VER | NSG | Message was not signed, but it should have been
|
||||||
|
* |!| SGN | VER | FAIL | Verification failed
|
||||||
|
* | | SGN | VER | OK | Verification succeeded
|
||||||
|
* | | SGN | VER | LEFT='number' | 'number' of failed verifications left in a row before node is locked
|
||||||
|
* | | SGN | SKP | MSG CMD='cmd',TYPE='type'| Message with command 'cmd' and type 'type' does not need to be signed
|
||||||
|
* | | SGN | SKP | ACK CMD='cmd',TYPE='type'| ACK messages does not need to be signed
|
||||||
|
* | | SGN | NCE | LEFT='number' | 'number' of nonce requests between successful verifications left before node is locked
|
||||||
|
* | | SGN | NCE | XMT,TO='node' | Nonce data transmitted to 'node'
|
||||||
|
* |!| SGN | NCE | XMT,TO='node' FAIL | Nonce data not properly transmitted to 'node'
|
||||||
|
* |!| SGN | NCE | GEN | Failed to generate nonce
|
||||||
|
* | | SGN | NCE | NSUP (DROPPED) | Ignored nonce/request for nonce (signing not supported)
|
||||||
|
* | | SGN | NCE | FROM='node' | Received nonce from 'node'
|
||||||
|
* | | SGN | NCE | 'sender'!='dst' (DROPPED)| Ignoring nonce as it did not come from the desgination of the message to sign
|
||||||
|
* |!| SGN | BND | INIT FAIL | Failed to initialize signing backend
|
||||||
|
* |!| SGN | BND | PWD<8 | Signing password too short
|
||||||
|
* |!| SGN | BND | PER | Backend not personalized
|
||||||
|
* |!| SGN | BND | SER | Could not get device unique serial from backend
|
||||||
|
* |!| SGN | BND | TMR | Backend timed out
|
||||||
|
* |!| SGN | BND | SIG,SIZE,'message'>'max' | Refusing to sign 'message' because it is bigger than 'max' allowed size
|
||||||
|
* | | SGN | BND | SIG WHI,ID='id' | Salting message with our 'id'
|
||||||
|
* | | SGN | BND | SIG WHI,SERIAL='serial' | Salting message with our 'serial'
|
||||||
|
* |!| SGN | BND | VER ONGOING | Verification failed, no ongoing session
|
||||||
|
* |!| SGN | BND | VER,IDENT='identifier' | Verification failed, 'identifier' is unknown
|
||||||
|
* | | SGN | BND | VER WHI,ID='sender' | 'sender' found in whitelist
|
||||||
|
* | | SGN | BND | VER WHI,SERIAL='serial' | Expecting 'serial' for this sender
|
||||||
|
* |!| SGN | BND | VER WHI,ID='id' MISSING | 'id' not found in whitelist
|
||||||
|
* | | SGN | BND | NONCE='nonce' | Calculating signature using 'nonce'
|
||||||
|
* | | SGN | BND | HMAC='hmac' | Calculated signature is 'hmac'
|
||||||
|
*/
|
||||||
|
/** @}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup MySigningTroubleshootinggrp Signing troubleshooting
|
||||||
* @ingroup MySigninggrp
|
* @ingroup MySigninggrp
|
||||||
* @{
|
* @{
|
||||||
*
|
*
|
||||||
|
|||||||
+166
-203
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||||
* between your home built sensors/actuators and HA controller of choice.
|
* between your home built sensors/actuators and HA controller of choice.
|
||||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||||
@@ -27,34 +27,57 @@
|
|||||||
|
|
||||||
#include "MySigning.h"
|
#include "MySigning.h"
|
||||||
|
|
||||||
|
#ifdef MY_SIGNING_ATSHA204
|
||||||
#define SIGNING_IDENTIFIER (1) //HMAC-SHA256
|
#define SIGNING_IDENTIFIER (1) //HMAC-SHA256
|
||||||
|
|
||||||
#if defined(MY_DEBUG_VERBOSE_SIGNING)
|
#if defined(MY_DEBUG_VERBOSE_SIGNING)
|
||||||
#define SIGN_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__)
|
#define SIGN_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__)
|
||||||
|
static char printStr[65];
|
||||||
|
static char i2h(uint8_t i)
|
||||||
|
{
|
||||||
|
uint8_t k = i & 0x0F;
|
||||||
|
if (k <= 9) {
|
||||||
|
return '0' + k;
|
||||||
|
} else {
|
||||||
|
return 'A' + k - 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void buf2str(const uint8_t* buf, size_t sz)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
if (sz > 32) {
|
||||||
|
sz = 32; //clamp to 32 bytes
|
||||||
|
}
|
||||||
|
for (i = 0; i < sz; i++) {
|
||||||
|
printStr[i * 2] = i2h(buf[i] >> 4);
|
||||||
|
printStr[(i * 2) + 1] = i2h(buf[i]);
|
||||||
|
}
|
||||||
|
printStr[sz * 2] = '\0';
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
#define SIGN_DEBUG(x,...)
|
#define SIGN_DEBUG(x,...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Define MY_DEBUG_VERBOSE_SIGNING in your sketch to enable signing backend debugprints
|
static unsigned long _signing_timestamp;
|
||||||
|
static bool _signing_verification_ongoing = false;
|
||||||
unsigned long _signing_timestamp;
|
static uint8_t _signing_verifying_nonce[32+9+1];
|
||||||
bool _signing_verification_ongoing = false;
|
static uint8_t _signing_signing_nonce[32+9+1];
|
||||||
uint8_t _signing_verifying_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1];
|
static uint8_t _signing_temp_message[SHA_MSG_SIZE];
|
||||||
uint8_t _signing_signing_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1];
|
static uint8_t _signing_rx_buffer[SHA204_RSP_SIZE_MAX];
|
||||||
uint8_t _signing_temp_message[SHA_MSG_SIZE];
|
static uint8_t _signing_tx_buffer[SHA204_CMD_SIZE_MAX];
|
||||||
uint8_t _signing_rx_buffer[SHA204_RSP_SIZE_MAX];
|
static uint8_t* const _signing_hmac = &_signing_rx_buffer[SHA204_BUFFER_POS_DATA];
|
||||||
uint8_t _signing_tx_buffer[SHA204_CMD_SIZE_MAX];
|
static uint8_t _signing_node_serial_info[9];
|
||||||
extern uint8_t _doWhitelist[32];
|
|
||||||
|
|
||||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||||
const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
|
static const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void signerCalculateSignature(MyMessage &msg, bool signing);
|
|
||||||
static uint8_t* signerSha256(const uint8_t* data, size_t sz);
|
|
||||||
|
|
||||||
static bool init_ok = false;
|
static bool init_ok = false;
|
||||||
|
|
||||||
|
static void signerCalculateSignature(MyMessage &msg, bool signing);
|
||||||
|
static uint8_t* signerAtsha204AHmac(const uint8_t* nonce, const uint8_t* data);
|
||||||
|
static uint8_t* signerSha256(const uint8_t* data, size_t sz);
|
||||||
|
|
||||||
bool signerAtsha204Init(void)
|
bool signerAtsha204Init(void)
|
||||||
{
|
{
|
||||||
init_ok = true;
|
init_ok = true;
|
||||||
@@ -64,28 +87,41 @@ bool signerAtsha204Init(void)
|
|||||||
// Read the configuration lock flag to determine if device is personalized or not
|
// Read the configuration lock flag to determine if device is personalized or not
|
||||||
if (atsha204_read(_signing_tx_buffer, _signing_rx_buffer,
|
if (atsha204_read(_signing_tx_buffer, _signing_rx_buffer,
|
||||||
SHA204_ZONE_CONFIG, 0x15<<2) != SHA204_SUCCESS) {
|
SHA204_ZONE_CONFIG, 0x15<<2) != SHA204_SUCCESS) {
|
||||||
SIGN_DEBUG(PSTR("Could not read ATSHA204A lock config, refusing to use backend\n"));
|
SIGN_DEBUG(PSTR("!SGN:BND:INIT FAIL\n")); //Could not read ATSHA204A lock config
|
||||||
init_ok = false;
|
init_ok = false;
|
||||||
} else if (_signing_rx_buffer[SHA204_BUFFER_POS_DATA+3] != 0x00) {
|
} else if (_signing_rx_buffer[SHA204_BUFFER_POS_DATA+3] != 0x00) {
|
||||||
SIGN_DEBUG(PSTR("ATSHA204A not personalized, refusing to use backend\n"));
|
SIGN_DEBUG(PSTR("!SGN:BND:PER\n")); //ATSHA204A not personalized
|
||||||
init_ok = false;
|
init_ok = false;
|
||||||
}
|
}
|
||||||
|
if (init_ok) {
|
||||||
|
// Get and cache the serial of the ATSHA204A
|
||||||
|
if (atsha204_getSerialNumber(_signing_node_serial_info) != SHA204_SUCCESS) {
|
||||||
|
SIGN_DEBUG(PSTR("!SGN:BND:SER\n")); //Could not get ATSHA204A serial
|
||||||
|
init_ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return init_ok;
|
return init_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool signerAtsha204CheckTimer(void)
|
bool signerAtsha204CheckTimer(void)
|
||||||
{
|
{
|
||||||
if (!init_ok) {
|
if (!init_ok) {
|
||||||
SIGN_DEBUG(PSTR("Backend not initialized\n"));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (_signing_verification_ongoing) {
|
if (_signing_verification_ongoing) {
|
||||||
if (hwMillis() < _signing_timestamp ||
|
unsigned long time_now = hwMillis();
|
||||||
hwMillis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
|
// If timestamp is taken so late a rollover can take place during the timeout,
|
||||||
SIGN_DEBUG(PSTR("Verification timeout\n"));
|
// offset both timestamp and current time to make sure no rollover takes place during the
|
||||||
|
// timeout
|
||||||
|
if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < _signing_timestamp) {
|
||||||
|
_signing_timestamp += MY_VERIFICATION_TIMEOUT_MS;
|
||||||
|
time_now += MY_VERIFICATION_TIMEOUT_MS;
|
||||||
|
}
|
||||||
|
if (time_now > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
|
||||||
|
SIGN_DEBUG(PSTR("!SGN:BND:TMR\n")); //Verification timeout
|
||||||
// Purge nonce
|
// Purge nonce
|
||||||
memset(_signing_signing_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
|
memset(_signing_signing_nonce, 0xAA, 32);
|
||||||
memset(_signing_verifying_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
|
memset(_signing_verifying_nonce, 0xAA, 32);
|
||||||
_signing_verification_ongoing = false;
|
_signing_verification_ongoing = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -96,60 +132,57 @@ bool signerAtsha204CheckTimer(void)
|
|||||||
bool signerAtsha204GetNonce(MyMessage &msg)
|
bool signerAtsha204GetNonce(MyMessage &msg)
|
||||||
{
|
{
|
||||||
if (!init_ok) {
|
if (!init_ok) {
|
||||||
SIGN_DEBUG(PSTR("Backend not initialized\n"));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SIGN_DEBUG(PSTR("Signing backend: ATSHA204\n"));
|
|
||||||
// Generate random number for use as nonce
|
// We used a basic whitening technique that XORs each byte in a 32byte random value with current
|
||||||
// We used a basic whitening technique that XORs each byte in a 32byte random value with current hwMillis() counter
|
// hwMillis() counter. This 32-byte random value is then hashed (SHA256) to produce the resulting
|
||||||
// This 32-byte random value is then hashed (SHA256) to produce the resulting nonce
|
// nonce
|
||||||
(void)atsha204_wakeup(_signing_temp_message);
|
(void)atsha204_wakeup(_signing_temp_message);
|
||||||
if (atsha204_execute(SHA204_RANDOM, RANDOM_SEED_UPDATE, 0, 0, NULL,
|
if (atsha204_execute(SHA204_RANDOM, RANDOM_SEED_UPDATE, 0, 0, NULL,
|
||||||
RANDOM_COUNT, _signing_tx_buffer, RANDOM_RSP_SIZE, _signing_rx_buffer) != SHA204_SUCCESS) {
|
RANDOM_COUNT, _signing_tx_buffer, RANDOM_RSP_SIZE, _signing_rx_buffer) !=
|
||||||
SIGN_DEBUG(PSTR("Failed to generate nonce\n"));
|
SHA204_SUCCESS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 32; i++) {
|
for (int i = 0; i < 32; i++) {
|
||||||
_signing_verifying_nonce[i] = _signing_rx_buffer[SHA204_BUFFER_POS_DATA+i] ^ (hwMillis()&0xFF);
|
_signing_verifying_nonce[i] = _signing_rx_buffer[SHA204_BUFFER_POS_DATA+i] ^ (hwMillis()&0xFF);
|
||||||
}
|
}
|
||||||
memcpy(_signing_verifying_nonce, signerSha256(_signing_verifying_nonce, 32), MAX_PAYLOAD);
|
memcpy(_signing_verifying_nonce, signerSha256(_signing_verifying_nonce, 32),
|
||||||
|
min(MAX_PAYLOAD, 32));
|
||||||
|
|
||||||
atsha204_idle(); // We just idle the chip now since we expect to use it soon when the signed message arrives
|
// We just idle the chip now since we expect to use it soon when the signed message arrives
|
||||||
|
atsha204_idle();
|
||||||
|
|
||||||
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
if (MAX_PAYLOAD < 32) {
|
||||||
memset(&_signing_verifying_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_verifying_nonce)-MAX_PAYLOAD);
|
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
||||||
|
memset(&_signing_verifying_nonce[MAX_PAYLOAD], 0xAA, 32-MAX_PAYLOAD);
|
||||||
|
}
|
||||||
|
|
||||||
// Transfer the first part of the nonce to the message
|
// Transfer the first part of the nonce to the message
|
||||||
msg.set(_signing_verifying_nonce, MAX_PAYLOAD);
|
msg.set(_signing_verifying_nonce, min(MAX_PAYLOAD, 32));
|
||||||
_signing_verification_ongoing = true;
|
_signing_verification_ongoing = true;
|
||||||
_signing_timestamp = hwMillis(); // Set timestamp to determine when to purge nonce
|
_signing_timestamp = hwMillis(); // Set timestamp to determine when to purge nonce
|
||||||
// Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover)
|
|
||||||
// Note that if message is "too" quick, and arrives before turnover, it will be rejected
|
|
||||||
// but this is consider such a rare case that it is accepted and rejects are 'safe'
|
|
||||||
if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < hwMillis()) {
|
|
||||||
_signing_timestamp = 0;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void signerAtsha204PutNonce(MyMessage &msg)
|
void signerAtsha204PutNonce(MyMessage &msg)
|
||||||
{
|
{
|
||||||
if (!init_ok) {
|
if (!init_ok) {
|
||||||
SIGN_DEBUG(PSTR("Backend not initialized\n"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SIGN_DEBUG(PSTR("Signing backend: ATSHA204\n"));
|
|
||||||
|
|
||||||
memcpy(_signing_signing_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
|
memcpy(_signing_signing_nonce, (uint8_t*)msg.getCustom(), min(MAX_PAYLOAD, 32));
|
||||||
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
if (MAX_PAYLOAD < 32) {
|
||||||
memset(&_signing_signing_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_signing_nonce)-MAX_PAYLOAD);
|
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
||||||
|
memset(&_signing_signing_nonce[MAX_PAYLOAD], 0xAA, 32-MAX_PAYLOAD);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool signerAtsha204SignMsg(MyMessage &msg)
|
bool signerAtsha204SignMsg(MyMessage &msg)
|
||||||
{
|
{
|
||||||
// If we cannot fit any signature in the message, refuse to sign it
|
// If we cannot fit any signature in the message, refuse to sign it
|
||||||
if (mGetLength(msg) > MAX_PAYLOAD-2) {
|
if (mGetLength(msg) > MAX_PAYLOAD-2) {
|
||||||
SIGN_DEBUG(PSTR("Message too large\n"));
|
SIGN_DEBUG(PSTR("!SGN:BND:SIG,SIZE,%d>%d\n"), mGetLength(msg), MAX_PAYLOAD-2); //Message too large
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,27 +192,27 @@ bool signerAtsha204SignMsg(MyMessage &msg)
|
|||||||
|
|
||||||
if (DO_WHITELIST(msg.destination)) {
|
if (DO_WHITELIST(msg.destination)) {
|
||||||
// Salt the signature with the senders nodeId and the unique serial of the ATSHA device
|
// Salt the signature with the senders nodeId and the unique serial of the ATSHA device
|
||||||
memcpy(_signing_signing_nonce, &_signing_rx_buffer[SHA204_BUFFER_POS_DATA],
|
// We can reuse the nonce buffer now since it is no longer needed
|
||||||
32); // We can reuse the nonce buffer now since it is no longer needed
|
memcpy(_signing_signing_nonce, _signing_hmac, 32);
|
||||||
_signing_signing_nonce[32] = msg.sender;
|
_signing_signing_nonce[32] = msg.sender;
|
||||||
atsha204_getSerialNumber(&_signing_signing_nonce[33]);
|
memcpy(&_signing_signing_nonce[33], _signing_node_serial_info, 9);
|
||||||
(void)signerSha256(_signing_signing_nonce,
|
// We can 'void' sha256 because the hash is already put in the correct place
|
||||||
32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
|
(void)signerSha256(_signing_signing_nonce, 32+1+9);
|
||||||
SIGN_DEBUG(PSTR("Signature salted with serial: %02X%02X%02X%02X%02X%02X%02X%02X%02X\n"),
|
SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,ID=%d\n"), msg.sender);
|
||||||
_signing_signing_nonce[33], _signing_signing_nonce[34], _signing_signing_nonce[35],
|
#ifdef MY_DEBUG_VERBOSE_SIGNING
|
||||||
_signing_signing_nonce[36], _signing_signing_nonce[37], _signing_signing_nonce[38],
|
buf2str(_signing_node_serial_info, 9);
|
||||||
_signing_signing_nonce[39], _signing_signing_nonce[40], _signing_signing_nonce[41]);
|
SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,SERIAL=%s\n"), printStr);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put device back to sleep
|
// Put device back to sleep
|
||||||
atsha204_sleep();
|
atsha204_sleep();
|
||||||
|
|
||||||
// Overwrite the first byte in the signature with the signing identifier
|
// Overwrite the first byte in the signature with the signing identifier
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
|
_signing_hmac[0] = SIGNING_IDENTIFIER;
|
||||||
|
|
||||||
// Transfer as much signature data as the remaining space in the message permits
|
// Transfer as much signature data as the remaining space in the message permits
|
||||||
memcpy(&msg.data[mGetLength(msg)], &_signing_rx_buffer[SHA204_BUFFER_POS_DATA],
|
memcpy(&msg.data[mGetLength(msg)], _signing_hmac, min(MAX_PAYLOAD-mGetLength(msg), 32));
|
||||||
MAX_PAYLOAD-mGetLength(msg));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -187,7 +220,7 @@ bool signerAtsha204SignMsg(MyMessage &msg)
|
|||||||
bool signerAtsha204VerifyMsg(MyMessage &msg)
|
bool signerAtsha204VerifyMsg(MyMessage &msg)
|
||||||
{
|
{
|
||||||
if (!_signing_verification_ongoing) {
|
if (!_signing_verification_ongoing) {
|
||||||
SIGN_DEBUG(PSTR("No active verification session\n"));
|
SIGN_DEBUG(PSTR("!SGN:BND:VER ONGOING\n"));
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// Make sure we have not expired
|
// Make sure we have not expired
|
||||||
@@ -198,7 +231,7 @@ bool signerAtsha204VerifyMsg(MyMessage &msg)
|
|||||||
_signing_verification_ongoing = false;
|
_signing_verification_ongoing = false;
|
||||||
|
|
||||||
if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
|
if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
|
||||||
SIGN_DEBUG(PSTR("Incorrect signing identifier\n"));
|
SIGN_DEBUG(PSTR("!SGN:BND:VER,IDENT=%d\n"), msg.data[mGetLength(msg)]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,18 +242,22 @@ bool signerAtsha204VerifyMsg(MyMessage &msg)
|
|||||||
size_t j;
|
size_t j;
|
||||||
for (j=0; j < NUM_OF(_signing_whitelist); j++) {
|
for (j=0; j < NUM_OF(_signing_whitelist); j++) {
|
||||||
if (_signing_whitelist[j].nodeId == msg.sender) {
|
if (_signing_whitelist[j].nodeId == msg.sender) {
|
||||||
SIGN_DEBUG(PSTR("Sender found in whitelist\n"));
|
// We can reuse the nonce buffer now since it is no longer needed
|
||||||
memcpy(_signing_verifying_nonce, &_signing_rx_buffer[SHA204_BUFFER_POS_DATA],
|
memcpy(_signing_verifying_nonce, _signing_hmac, 32);
|
||||||
32); // We can reuse the nonce buffer now since it is no longer needed
|
|
||||||
_signing_verifying_nonce[32] = msg.sender;
|
_signing_verifying_nonce[32] = msg.sender;
|
||||||
memcpy(&_signing_verifying_nonce[33], _signing_whitelist[j].serial, SHA204_SERIAL_SZ);
|
memcpy(&_signing_verifying_nonce[33], _signing_whitelist[j].serial, 9);
|
||||||
(void)signerSha256(_signing_verifying_nonce,
|
// We can 'void' sha256 because the hash is already put in the correct place
|
||||||
32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
|
(void)signerSha256(_signing_verifying_nonce, 32+1+9);
|
||||||
|
SIGN_DEBUG(PSTR("SGN:BND:VER WHI,ID=%d\n"), msg.sender);
|
||||||
|
#ifdef MY_DEBUG_VERBOSE_SIGNING
|
||||||
|
buf2str(_signing_whitelist[j].serial, 9);
|
||||||
|
SIGN_DEBUG(PSTR("SGN:BND:VER WHI,SERIAL=%s\n"), printStr);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (j == NUM_OF(_signing_whitelist)) {
|
if (j == NUM_OF(_signing_whitelist)) {
|
||||||
SIGN_DEBUG(PSTR("Sender not found in whitelist, message rejected!\n"));
|
SIGN_DEBUG(PSTR("!SGN:BND:VER WHI,ID=%d MISSING\n"), msg.sender);
|
||||||
// Put device back to sleep
|
// Put device back to sleep
|
||||||
atsha204_sleep();
|
atsha204_sleep();
|
||||||
return false;
|
return false;
|
||||||
@@ -231,134 +268,96 @@ bool signerAtsha204VerifyMsg(MyMessage &msg)
|
|||||||
atsha204_sleep();
|
atsha204_sleep();
|
||||||
|
|
||||||
// Overwrite the first byte in the signature with the signing identifier
|
// Overwrite the first byte in the signature with the signing identifier
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
|
_signing_hmac[0] = SIGNING_IDENTIFIER;
|
||||||
|
|
||||||
// Compare the caluclated signature with the provided signature
|
// Compare the caluclated signature with the provided signature
|
||||||
if (signerMemcmp(&msg.data[mGetLength(msg)], &_signing_rx_buffer[SHA204_BUFFER_POS_DATA],
|
if (signerMemcmp(&msg.data[mGetLength(msg)], _signing_hmac,
|
||||||
MAX_PAYLOAD-mGetLength(msg))) {
|
min(MAX_PAYLOAD-mGetLength(msg), 32))) {
|
||||||
SIGN_DEBUG(PSTR("Signature bad\n"));
|
|
||||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
|
||||||
SIGN_DEBUG(PSTR("Is the sender whitelisted and serial correct?\n"));
|
|
||||||
#endif
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
SIGN_DEBUG(PSTR("Signature OK\n"));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to calculate signature of msg (returned in _signing_rx_buffer[SHA204_BUFFER_POS_DATA])
|
// Helper to calculate signature of msg (returned in _signing_rx_buffer[SHA204_BUFFER_POS_DATA])
|
||||||
|
// (=_signing_hmac)
|
||||||
static void signerCalculateSignature(MyMessage &msg, bool signing)
|
static void signerCalculateSignature(MyMessage &msg, bool signing)
|
||||||
{
|
{
|
||||||
(void)atsha204_wakeup(_signing_temp_message);
|
// Signature is calculated on everything expect the first byte in the header
|
||||||
memset(_signing_temp_message, 0, 32);
|
uint16_t bytes_left = mGetLength(msg)+HEADER_SIZE-1;
|
||||||
memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE],
|
int16_t current_pos = 1-(int16_t)HEADER_SIZE; // Start at the second byte in the header
|
||||||
MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
|
uint8_t* nonce = signing ? _signing_signing_nonce : _signing_verifying_nonce;
|
||||||
|
|
||||||
|
#ifdef MY_DEBUG_VERBOSE_SIGNING
|
||||||
|
buf2str(nonce, 32);
|
||||||
|
SIGN_DEBUG(PSTR("SGN:BND:NONCE=%s\n"), printStr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (bytes_left) {
|
||||||
|
uint16_t bytes_to_include = min(bytes_left, 32);
|
||||||
|
|
||||||
|
(void)atsha204_wakeup(_signing_temp_message); // Issue wakeup to reset watchdog
|
||||||
|
memset(_signing_temp_message, 0, 32);
|
||||||
|
memcpy(_signing_temp_message, (uint8_t*)&msg.data[current_pos], bytes_to_include);
|
||||||
|
|
||||||
|
// We can 'void' signerAtsha204AHmac because the HMAC is already put in the correct place
|
||||||
|
(void)signerAtsha204AHmac(nonce, _signing_temp_message);
|
||||||
|
// Purge nonce when used
|
||||||
|
memset(nonce, 0xAA, 32);
|
||||||
|
|
||||||
|
bytes_left -= bytes_to_include;
|
||||||
|
current_pos += bytes_to_include;
|
||||||
|
|
||||||
|
if (bytes_left > 0) {
|
||||||
|
// We will do another pass, use current HMAC as nonce for the next HMAC
|
||||||
|
memcpy(nonce, _signing_hmac, 32);
|
||||||
|
atsha204_idle(); // Idle the chip to allow the wakeup call to reset the watchdog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef MY_DEBUG_VERBOSE_SIGNING
|
||||||
|
buf2str(_signing_hmac, 32);
|
||||||
|
SIGN_DEBUG(PSTR("SGN:BND:HMAC=%s\n"), printStr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to calculate a ATSHA204A specific HMAC-SHA256 using provided 32 byte nonce and data
|
||||||
|
// (zero padded to 32 bytes)
|
||||||
|
// The pointer to the HMAC is returned, but the HMAC is also stored in
|
||||||
|
// _signing_rx_buffer[SHA204_BUFFER_POS_DATA] (=_signing_hmac)
|
||||||
|
static uint8_t* signerAtsha204AHmac(const uint8_t* nonce, const uint8_t* data)
|
||||||
|
{
|
||||||
// Program the data to sign into the ATSHA204
|
// Program the data to sign into the ATSHA204
|
||||||
SIGN_DEBUG(PSTR("Current nonce: "
|
|
||||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"
|
|
||||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n"),
|
|
||||||
signing ? _signing_signing_nonce[0] : _signing_verifying_nonce[0],
|
|
||||||
signing ? _signing_signing_nonce[1] : _signing_verifying_nonce[1],
|
|
||||||
signing ? _signing_signing_nonce[2] : _signing_verifying_nonce[2],
|
|
||||||
signing ? _signing_signing_nonce[3] : _signing_verifying_nonce[3],
|
|
||||||
signing ? _signing_signing_nonce[4] : _signing_verifying_nonce[4],
|
|
||||||
signing ? _signing_signing_nonce[5] : _signing_verifying_nonce[5],
|
|
||||||
signing ? _signing_signing_nonce[6] : _signing_verifying_nonce[6],
|
|
||||||
signing ? _signing_signing_nonce[7] : _signing_verifying_nonce[7],
|
|
||||||
signing ? _signing_signing_nonce[8] : _signing_verifying_nonce[8],
|
|
||||||
signing ? _signing_signing_nonce[9] : _signing_verifying_nonce[9],
|
|
||||||
signing ? _signing_signing_nonce[10] : _signing_verifying_nonce[10],
|
|
||||||
signing ? _signing_signing_nonce[11] : _signing_verifying_nonce[11],
|
|
||||||
signing ? _signing_signing_nonce[12] : _signing_verifying_nonce[12],
|
|
||||||
signing ? _signing_signing_nonce[13] : _signing_verifying_nonce[13],
|
|
||||||
signing ? _signing_signing_nonce[14] : _signing_verifying_nonce[14],
|
|
||||||
signing ? _signing_signing_nonce[15] : _signing_verifying_nonce[15],
|
|
||||||
signing ? _signing_signing_nonce[16] : _signing_verifying_nonce[16],
|
|
||||||
signing ? _signing_signing_nonce[17] : _signing_verifying_nonce[17],
|
|
||||||
signing ? _signing_signing_nonce[18] : _signing_verifying_nonce[18],
|
|
||||||
signing ? _signing_signing_nonce[19] : _signing_verifying_nonce[19],
|
|
||||||
signing ? _signing_signing_nonce[20] : _signing_verifying_nonce[20],
|
|
||||||
signing ? _signing_signing_nonce[21] : _signing_verifying_nonce[21],
|
|
||||||
signing ? _signing_signing_nonce[22] : _signing_verifying_nonce[22],
|
|
||||||
signing ? _signing_signing_nonce[23] : _signing_verifying_nonce[23],
|
|
||||||
signing ? _signing_signing_nonce[24] : _signing_verifying_nonce[24],
|
|
||||||
signing ? _signing_signing_nonce[25] : _signing_verifying_nonce[25],
|
|
||||||
signing ? _signing_signing_nonce[26] : _signing_verifying_nonce[26],
|
|
||||||
signing ? _signing_signing_nonce[27] : _signing_verifying_nonce[27],
|
|
||||||
signing ? _signing_signing_nonce[28] : _signing_verifying_nonce[28],
|
|
||||||
signing ? _signing_signing_nonce[29] : _signing_verifying_nonce[29],
|
|
||||||
signing ? _signing_signing_nonce[30] : _signing_verifying_nonce[30],
|
|
||||||
signing ? _signing_signing_nonce[31] : _signing_verifying_nonce[31]
|
|
||||||
);
|
|
||||||
(void)atsha204_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32,
|
(void)atsha204_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32,
|
||||||
_signing_temp_message,
|
(uint8_t*)data,
|
||||||
WRITE_COUNT_LONG, _signing_tx_buffer, WRITE_RSP_SIZE, _signing_rx_buffer);
|
WRITE_COUNT_LONG, _signing_tx_buffer, WRITE_RSP_SIZE, _signing_rx_buffer);
|
||||||
|
|
||||||
// Program the nonce to use for the signature (has to be done just before GENDIG due to chip limitations)
|
// Program the nonce to use for the signature (has to be done just before GENDIG
|
||||||
(void)atsha204_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, NONCE_NUMIN_SIZE_PASSTHROUGH,
|
// due to chip limitations)
|
||||||
signing ? _signing_signing_nonce : _signing_verifying_nonce,
|
(void)atsha204_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, 32, (uint8_t*)nonce,
|
||||||
NONCE_COUNT_LONG, _signing_tx_buffer, NONCE_RSP_SIZE_SHORT, _signing_rx_buffer);
|
NONCE_COUNT_LONG, _signing_tx_buffer, NONCE_RSP_SIZE_SHORT,
|
||||||
|
_signing_rx_buffer);
|
||||||
// Purge nonce when used
|
|
||||||
memset(signing ? _signing_signing_nonce : _signing_verifying_nonce, 0x00,
|
|
||||||
NONCE_NUMIN_SIZE_PASSTHROUGH);
|
|
||||||
|
|
||||||
// Generate digest of data and nonce
|
// Generate digest of data and nonce
|
||||||
(void)atsha204_execute(SHA204_GENDIG, GENDIG_ZONE_DATA, 8, 0, NULL,
|
(void)atsha204_execute(SHA204_GENDIG, GENDIG_ZONE_DATA, 8, 0, NULL,
|
||||||
GENDIG_COUNT_DATA, _signing_tx_buffer, GENDIG_RSP_SIZE, _signing_rx_buffer);
|
GENDIG_COUNT_DATA, _signing_tx_buffer, GENDIG_RSP_SIZE,
|
||||||
|
_signing_rx_buffer);
|
||||||
|
|
||||||
// Calculate HMAC of message+nonce digest and secret key
|
// Calculate HMAC of message+nonce digest and secret key
|
||||||
(void)atsha204_execute(SHA204_HMAC, HMAC_MODE_SOURCE_FLAG_MATCH, 0, 0, NULL,
|
(void)atsha204_execute(SHA204_HMAC, HMAC_MODE_SOURCE_FLAG_MATCH, 0, 0, NULL,
|
||||||
HMAC_COUNT, _signing_tx_buffer, HMAC_RSP_SIZE, _signing_rx_buffer);
|
HMAC_COUNT, _signing_tx_buffer, HMAC_RSP_SIZE, _signing_rx_buffer);
|
||||||
|
return &_signing_rx_buffer[SHA204_BUFFER_POS_DATA];
|
||||||
SIGN_DEBUG(PSTR("HMAC: "
|
|
||||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"
|
|
||||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n"),
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+0],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+1],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+2],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+3],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+4],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+5],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+6],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+7],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+8],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+9],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+10],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+11],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+12],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+13],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+14],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+15],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+16],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+17],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+18],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+19],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+20],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+21],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+22],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+23],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+24],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+25],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+26],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+27],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+28],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+29],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+30],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+31]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to calculate a generic SHA256 digest of provided buffer (only supports one block)
|
// Helper to calculate a generic SHA256 digest of provided buffer (only supports one block)
|
||||||
// The pointer to the hash is returned, but the hash is also stored in _signing_rx_buffer[SHA204_BUFFER_POS_DATA])
|
// The pointer to the hash is returned, but the hash is also stored in
|
||||||
|
// _signing_rx_buffer[SHA204_BUFFER_POS_DATA])
|
||||||
static uint8_t* signerSha256(const uint8_t* data, size_t sz)
|
static uint8_t* signerSha256(const uint8_t* data, size_t sz)
|
||||||
{
|
{
|
||||||
// Initiate SHA256 calculator
|
// Initiate SHA256 calculator
|
||||||
(void)atsha204_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL,
|
(void)atsha204_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL,
|
||||||
SHA_COUNT_SHORT, _signing_tx_buffer, SHA_RSP_SIZE_SHORT, _signing_rx_buffer);
|
SHA_COUNT_SHORT, _signing_tx_buffer, SHA_RSP_SIZE_SHORT,
|
||||||
|
_signing_rx_buffer);
|
||||||
|
|
||||||
// Calculate a hash
|
// Calculate a hash
|
||||||
memset(_signing_temp_message, 0x00, SHA_MSG_SIZE);
|
memset(_signing_temp_message, 0x00, SHA_MSG_SIZE);
|
||||||
@@ -369,42 +368,6 @@ static uint8_t* signerSha256(const uint8_t* data, size_t sz)
|
|||||||
_signing_temp_message[SHA_MSG_SIZE-1] = (sz << 3);
|
_signing_temp_message[SHA_MSG_SIZE-1] = (sz << 3);
|
||||||
(void)atsha204_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, _signing_temp_message,
|
(void)atsha204_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, _signing_temp_message,
|
||||||
SHA_COUNT_LONG, _signing_tx_buffer, SHA_RSP_SIZE_LONG, _signing_rx_buffer);
|
SHA_COUNT_LONG, _signing_tx_buffer, SHA_RSP_SIZE_LONG, _signing_rx_buffer);
|
||||||
|
|
||||||
SIGN_DEBUG(PSTR("SHA256: "
|
|
||||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"
|
|
||||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n"),
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+0],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+1],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+2],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+3],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+4],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+5],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+6],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+7],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+8],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+9],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+10],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+11],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+12],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+13],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+14],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+15],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+16],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+17],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+18],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+19],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+20],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+21],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+22],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+23],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+24],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+25],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+26],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+27],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+28],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+29],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+30],
|
|
||||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA+31]
|
|
||||||
);
|
|
||||||
return &_signing_rx_buffer[SHA204_BUFFER_POS_DATA];
|
return &_signing_rx_buffer[SHA204_BUFFER_POS_DATA];
|
||||||
}
|
}
|
||||||
|
#endif //MY_SIGNING_ATSHA204
|
||||||
|
|||||||
+165
-130
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||||
* between your home built sensors/actuators and HA controller of choice.
|
* between your home built sensors/actuators and HA controller of choice.
|
||||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||||
@@ -23,44 +23,66 @@
|
|||||||
* ATSHA204 emulated signing backend. The emulated ATSHA204 implementation offers pseudo random
|
* ATSHA204 emulated signing backend. The emulated ATSHA204 implementation offers pseudo random
|
||||||
* number generation and HMAC-SHA256 authentication compatible with a "physical" ATSHA204.
|
* number generation and HMAC-SHA256 authentication compatible with a "physical" ATSHA204.
|
||||||
* NOTE: Key is stored in clear text in the Arduino firmware. Therefore, the use of this back-end
|
* NOTE: Key is stored in clear text in the Arduino firmware. Therefore, the use of this back-end
|
||||||
* could compromise the key used in the signed message infrastructure if device is lost and its memory
|
* could compromise the key used in the signed message infrastructure if device is lost and its
|
||||||
* dumped.
|
* memory dumped.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MySigning.h"
|
#include "MySigning.h"
|
||||||
#include "drivers/ATSHA204/sha256.h"
|
|
||||||
|
|
||||||
|
#ifdef MY_SIGNING_SOFT
|
||||||
|
#include "drivers/ATSHA204/sha256.h"
|
||||||
#define SIGNING_IDENTIFIER (1) //HMAC-SHA256
|
#define SIGNING_IDENTIFIER (1) //HMAC-SHA256
|
||||||
|
|
||||||
#if defined(MY_DEBUG_VERBOSE_SIGNING)
|
#if defined(MY_DEBUG_VERBOSE_SIGNING)
|
||||||
#define SIGN_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__)
|
#define SIGN_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__)
|
||||||
|
static char printStr[65];
|
||||||
|
static char i2h(uint8_t i)
|
||||||
|
{
|
||||||
|
uint8_t k = i & 0x0F;
|
||||||
|
if (k <= 9) {
|
||||||
|
return '0' + k;
|
||||||
|
} else {
|
||||||
|
return 'A' + k - 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void buf2str(const uint8_t* buf, size_t sz)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
if (sz > 32) {
|
||||||
|
sz = 32; //clamp to 32 bytes
|
||||||
|
}
|
||||||
|
for (i = 0; i < sz; i++) {
|
||||||
|
printStr[i * 2] = i2h(buf[i] >> 4);
|
||||||
|
printStr[(i * 2) + 1] = i2h(buf[i]);
|
||||||
|
}
|
||||||
|
printStr[sz * 2] = '\0';
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
#define SIGN_DEBUG(x,...)
|
#define SIGN_DEBUG(x,...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Define MY_DEBUG_VERBOSE_SIGNING in your sketch to enable signing backend debugprints
|
|
||||||
|
|
||||||
Sha256Class _signing_sha256;
|
Sha256Class _signing_sha256;
|
||||||
unsigned long _signing_timestamp;
|
static unsigned long _signing_timestamp;
|
||||||
bool _signing_verification_ongoing = false;
|
static bool _signing_verification_ongoing = false;
|
||||||
uint8_t _signing_verifying_nonce[32];
|
static uint8_t _signing_verifying_nonce[32+9+1];
|
||||||
uint8_t _signing_signing_nonce[32];
|
static uint8_t _signing_signing_nonce[32+9+1];
|
||||||
uint8_t _signing_temp_message[32];
|
static uint8_t _signing_temp_message[32];
|
||||||
static uint8_t _signing_hmac_key[32];
|
static uint8_t _signing_hmac_key[32];
|
||||||
uint8_t _signing_hmac[32];
|
static uint8_t _signing_hmac[32];
|
||||||
extern uint8_t _doWhitelist[32];
|
|
||||||
|
|
||||||
static uint8_t _signing_node_serial_info[9];
|
static uint8_t _signing_node_serial_info[9];
|
||||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||||
const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
|
static const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void signerCalculateSignature(MyMessage &msg, bool signing);
|
|
||||||
|
|
||||||
static bool init_ok = false;
|
static bool init_ok = false;
|
||||||
static const uint8_t reset_serial[9] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
static const uint8_t reset_serial[9] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
|
||||||
|
static void signerCalculateSignature(MyMessage &msg, bool signing);
|
||||||
|
static uint8_t* signerAtsha204AHmac(const uint8_t* nonce, const uint8_t* data);
|
||||||
|
static uint8_t* signerSha256(const uint8_t* data, size_t sz);
|
||||||
|
|
||||||
bool signerAtsha204SoftInit(void)
|
bool signerAtsha204SoftInit(void)
|
||||||
{
|
{
|
||||||
init_ok = true;
|
init_ok = true;
|
||||||
@@ -68,8 +90,22 @@ bool signerAtsha204SoftInit(void)
|
|||||||
// initialize pseudo-RNG
|
// initialize pseudo-RNG
|
||||||
hwRandomNumberInit();
|
hwRandomNumberInit();
|
||||||
// Set secrets
|
// Set secrets
|
||||||
|
#ifdef MY_SIGNING_SIMPLE_PASSWD
|
||||||
|
if (strnlen(MY_SIGNING_SIMPLE_PASSWD, 32) < 8) {
|
||||||
|
SIGN_DEBUG(PSTR("!SGN:BND:PWD<8\n")); //Password is too short to be acceptable
|
||||||
|
init_ok = false;
|
||||||
|
} else {
|
||||||
|
memset(_signing_hmac_key, 0, 32);
|
||||||
|
memcpy(_signing_hmac_key, MY_SIGNING_SIMPLE_PASSWD, strnlen(MY_SIGNING_SIMPLE_PASSWD, 32));
|
||||||
|
memset(_signing_node_serial_info, 0, 9);
|
||||||
|
memcpy(_signing_node_serial_info, MY_SIGNING_SIMPLE_PASSWD,
|
||||||
|
strnlen(MY_SIGNING_SIMPLE_PASSWD, 8));
|
||||||
|
_signing_node_serial_info[8] = getNodeId();
|
||||||
|
}
|
||||||
|
#else
|
||||||
hwReadConfigBlock((void*)_signing_hmac_key, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
|
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);
|
hwReadConfigBlock((void*)_signing_node_serial_info, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
|
||||||
|
#endif
|
||||||
if (!memcmp(_signing_node_serial_info, reset_serial, 9)) {
|
if (!memcmp(_signing_node_serial_info, reset_serial, 9)) {
|
||||||
unique_id_t uniqueID;
|
unique_id_t uniqueID;
|
||||||
// There is no serial, attempt to get unique ID for serial instead
|
// There is no serial, attempt to get unique ID for serial instead
|
||||||
@@ -84,13 +120,19 @@ bool signerAtsha204SoftInit(void)
|
|||||||
bool signerAtsha204SoftCheckTimer(void)
|
bool signerAtsha204SoftCheckTimer(void)
|
||||||
{
|
{
|
||||||
if (!init_ok) {
|
if (!init_ok) {
|
||||||
SIGN_DEBUG(PSTR("Backend not initialized\n"));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (_signing_verification_ongoing) {
|
if (_signing_verification_ongoing) {
|
||||||
if (hwMillis() < _signing_timestamp ||
|
unsigned long time_now = hwMillis();
|
||||||
hwMillis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
|
// If timestamp is taken so late a rollover can take place during the timeout,
|
||||||
SIGN_DEBUG(PSTR("Verification timeout\n"));
|
// offset both timestamp and current time to make sure no rollover takes place during the
|
||||||
|
// timeout
|
||||||
|
if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < _signing_timestamp) {
|
||||||
|
_signing_timestamp += MY_VERIFICATION_TIMEOUT_MS;
|
||||||
|
time_now += MY_VERIFICATION_TIMEOUT_MS;
|
||||||
|
}
|
||||||
|
if (time_now > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
|
||||||
|
SIGN_DEBUG(PSTR("!SGN:BND:TMR\n")); //Verification timeout
|
||||||
// Purge nonce
|
// Purge nonce
|
||||||
memset(_signing_signing_nonce, 0xAA, 32);
|
memset(_signing_signing_nonce, 0xAA, 32);
|
||||||
memset(_signing_verifying_nonce, 0xAA, 32);
|
memset(_signing_verifying_nonce, 0xAA, 32);
|
||||||
@@ -104,29 +146,29 @@ bool signerAtsha204SoftCheckTimer(void)
|
|||||||
bool signerAtsha204SoftGetNonce(MyMessage &msg)
|
bool signerAtsha204SoftGetNonce(MyMessage &msg)
|
||||||
{
|
{
|
||||||
if (!init_ok) {
|
if (!init_ok) {
|
||||||
SIGN_DEBUG(PSTR("Backend not initialized\n"));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SIGN_DEBUG(PSTR("Signing backend: ATSHA204Soft\n"));
|
|
||||||
|
|
||||||
#ifdef MY_HW_HAS_GETRANDOM
|
#ifdef MY_HW_HAS_GETRANDOM
|
||||||
// Try to get MAX_PAYLOAD random bytes
|
// Try to get MAX_PAYLOAD (or 32) random bytes
|
||||||
while (hwGetentropy(&_signing_verifying_nonce, MAX_PAYLOAD) != MAX_PAYLOAD);
|
while (hwGetentropy(&_signing_verifying_nonce, MIN(MAX_PAYLOAD, 32)) != MIN(MAX_PAYLOAD, 32));
|
||||||
#else
|
#else
|
||||||
// We used a basic whitening technique that XORs a random byte with the current hwMillis() counter and then the byte is
|
// We used a basic whitening technique that XORs a random byte with the current hwMillis() counter
|
||||||
// hashed (SHA256) to produce the resulting nonce
|
// and then the byte is hashed (SHA256) to produce the resulting nonce
|
||||||
_signing_sha256.init();
|
_signing_sha256.init();
|
||||||
for (int i = 0; i < 32; i++) {
|
for (int i = 0; i < 32; i++) {
|
||||||
_signing_sha256.write(random(256) ^ (hwMillis()&0xFF));
|
_signing_sha256.write(random(256) ^ (hwMillis()&0xFF));
|
||||||
}
|
}
|
||||||
memcpy(_signing_verifying_nonce, _signing_sha256.result(), MAX_PAYLOAD);
|
memcpy(_signing_verifying_nonce, _signing_sha256.result(), MIN(MAX_PAYLOAD, 32));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
if (MAX_PAYLOAD < 32) {
|
||||||
memset(&_signing_verifying_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_verifying_nonce)-MAX_PAYLOAD);
|
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
||||||
|
memset(&_signing_verifying_nonce[MAX_PAYLOAD], 0xAA, 32-MAX_PAYLOAD);
|
||||||
|
}
|
||||||
|
|
||||||
// Transfer the first part of the nonce to the message
|
// Transfer the first part of the nonce to the message
|
||||||
msg.set(_signing_verifying_nonce, MAX_PAYLOAD);
|
msg.set(_signing_verifying_nonce, MIN(MAX_PAYLOAD, 32));
|
||||||
_signing_verification_ongoing = true;
|
_signing_verification_ongoing = true;
|
||||||
_signing_timestamp = hwMillis(); // Set timestamp to determine when to purge nonce
|
_signing_timestamp = hwMillis(); // Set timestamp to determine when to purge nonce
|
||||||
// Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover)
|
// Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover)
|
||||||
@@ -141,20 +183,21 @@ bool signerAtsha204SoftGetNonce(MyMessage &msg)
|
|||||||
void signerAtsha204SoftPutNonce(MyMessage &msg)
|
void signerAtsha204SoftPutNonce(MyMessage &msg)
|
||||||
{
|
{
|
||||||
if (!init_ok) {
|
if (!init_ok) {
|
||||||
SIGN_DEBUG(PSTR("Backend not initialized\n"));
|
return;
|
||||||
}
|
}
|
||||||
SIGN_DEBUG(PSTR("Signing backend: ATSHA204Soft\n"));
|
|
||||||
|
|
||||||
memcpy(_signing_signing_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
|
memcpy(_signing_signing_nonce, (uint8_t*)msg.getCustom(), MIN(MAX_PAYLOAD, 32));
|
||||||
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
if (MAX_PAYLOAD < 32) {
|
||||||
memset(&_signing_signing_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_signing_nonce)-MAX_PAYLOAD);
|
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
||||||
|
memset(&_signing_signing_nonce[MAX_PAYLOAD], 0xAA, 32-MAX_PAYLOAD);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool signerAtsha204SoftSignMsg(MyMessage &msg)
|
bool signerAtsha204SoftSignMsg(MyMessage &msg)
|
||||||
{
|
{
|
||||||
// If we cannot fit any signature in the message, refuse to sign it
|
// If we cannot fit any signature in the message, refuse to sign it
|
||||||
if (mGetLength(msg) > MAX_PAYLOAD-2) {
|
if (mGetLength(msg) > MAX_PAYLOAD-2) {
|
||||||
SIGN_DEBUG(PSTR("Message too large\n"));
|
SIGN_DEBUG(PSTR("!SGN:BND:SIG,SIZE,%d>%d\n"), mGetLength(msg), MAX_PAYLOAD-2); //Message too large
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,27 +206,24 @@ bool signerAtsha204SoftSignMsg(MyMessage &msg)
|
|||||||
signerCalculateSignature(msg, true);
|
signerCalculateSignature(msg, true);
|
||||||
|
|
||||||
if (DO_WHITELIST(msg.destination)) {
|
if (DO_WHITELIST(msg.destination)) {
|
||||||
// Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided
|
// Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has
|
||||||
_signing_sha256.init();
|
// provided. We can reuse the nonce buffer now since it is no longer needed
|
||||||
for (int i=0; i<32; i++) {
|
memcpy(_signing_signing_nonce, _signing_hmac, 32);
|
||||||
_signing_sha256.write(_signing_hmac[i]);
|
_signing_signing_nonce[32] = msg.sender;
|
||||||
}
|
memcpy(&_signing_signing_nonce[33], _signing_node_serial_info, 9);
|
||||||
_signing_sha256.write(msg.sender);
|
memcpy(_signing_hmac, signerSha256(_signing_signing_nonce, 32+1+9), 32);
|
||||||
for (int i=0; i<SHA204_SERIAL_SZ; i++) {
|
SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,ID=%d\n"), msg.sender);
|
||||||
_signing_sha256.write(_signing_node_serial_info[i]);
|
#ifdef MY_DEBUG_VERBOSE_SIGNING
|
||||||
}
|
buf2str(_signing_node_serial_info, 9);
|
||||||
memcpy(_signing_hmac, _signing_sha256.result(), 32);
|
SIGN_DEBUG(PSTR("SGN:BND:SIG WHI,SERIAL=%s\n"), printStr);
|
||||||
SIGN_DEBUG(PSTR("Signature salted with serial: %02X%02X%02X%02X%02X%02X%02X%02X%02X\n"),
|
#endif
|
||||||
_signing_node_serial_info[0], _signing_node_serial_info[1], _signing_node_serial_info[2],
|
|
||||||
_signing_node_serial_info[3], _signing_node_serial_info[4], _signing_node_serial_info[5],
|
|
||||||
_signing_node_serial_info[6], _signing_node_serial_info[7], _signing_node_serial_info[8]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overwrite the first byte in the signature with the signing identifier
|
// Overwrite the first byte in the signature with the signing identifier
|
||||||
_signing_hmac[0] = SIGNING_IDENTIFIER;
|
_signing_hmac[0] = SIGNING_IDENTIFIER;
|
||||||
|
|
||||||
// Transfer as much signature data as the remaining space in the message permits
|
// Transfer as much signature data as the remaining space in the message permits
|
||||||
memcpy(&msg.data[mGetLength(msg)], _signing_hmac, MAX_PAYLOAD-mGetLength(msg));
|
memcpy(&msg.data[mGetLength(msg)], _signing_hmac, MIN(MAX_PAYLOAD-mGetLength(msg), 32));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -191,7 +231,7 @@ bool signerAtsha204SoftSignMsg(MyMessage &msg)
|
|||||||
bool signerAtsha204SoftVerifyMsg(MyMessage &msg)
|
bool signerAtsha204SoftVerifyMsg(MyMessage &msg)
|
||||||
{
|
{
|
||||||
if (!_signing_verification_ongoing) {
|
if (!_signing_verification_ongoing) {
|
||||||
SIGN_DEBUG(PSTR("No active verification session\n"));
|
SIGN_DEBUG(PSTR("!SGN:BND:VER ONGOING\n"));
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// Make sure we have not expired
|
// Make sure we have not expired
|
||||||
@@ -202,7 +242,7 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg)
|
|||||||
_signing_verification_ongoing = false;
|
_signing_verification_ongoing = false;
|
||||||
|
|
||||||
if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
|
if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
|
||||||
SIGN_DEBUG(PSTR("Incorrect signing identifier\n"));
|
SIGN_DEBUG(PSTR("!SGN:BND:VER,IDENT=%d\n"), msg.data[mGetLength(msg)]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,21 +253,21 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg)
|
|||||||
size_t j;
|
size_t j;
|
||||||
for (j=0; j < NUM_OF(_signing_whitelist); j++) {
|
for (j=0; j < NUM_OF(_signing_whitelist); j++) {
|
||||||
if (_signing_whitelist[j].nodeId == msg.sender) {
|
if (_signing_whitelist[j].nodeId == msg.sender) {
|
||||||
SIGN_DEBUG(PSTR("Sender found in whitelist\n"));
|
// We can reuse the nonce buffer now since it is no longer needed
|
||||||
_signing_sha256.init();
|
memcpy(_signing_verifying_nonce, _signing_hmac, 32);
|
||||||
for (int i=0; i<32; i++) {
|
_signing_verifying_nonce[32] = msg.sender;
|
||||||
_signing_sha256.write(_signing_hmac[i]);
|
memcpy(&_signing_verifying_nonce[33], _signing_whitelist[j].serial, 9);
|
||||||
}
|
memcpy(_signing_hmac, signerSha256(_signing_verifying_nonce, 32+1+9), 32);
|
||||||
_signing_sha256.write(msg.sender);
|
SIGN_DEBUG(PSTR("SGN:BND:VER WHI,ID=%d\n"), msg.sender);
|
||||||
for (int i=0; i<SHA204_SERIAL_SZ; i++) {
|
#ifdef MY_DEBUG_VERBOSE_SIGNING
|
||||||
_signing_sha256.write(_signing_whitelist[j].serial[i]);
|
buf2str(_signing_whitelist[j].serial, 9);
|
||||||
}
|
SIGN_DEBUG(PSTR("SGN:BND:VER WHI,SERIAL=%s\n"), printStr);
|
||||||
memcpy(_signing_hmac, _signing_sha256.result(), 32);
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (j == NUM_OF(_signing_whitelist)) {
|
if (j == NUM_OF(_signing_whitelist)) {
|
||||||
SIGN_DEBUG(PSTR("Sender not found in whitelist, message rejected!\n"));
|
SIGN_DEBUG(PSTR("!SGN:BND:VER WHI,ID=%d MISSING\n"), msg.sender);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -236,62 +276,58 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg)
|
|||||||
_signing_hmac[0] = SIGNING_IDENTIFIER;
|
_signing_hmac[0] = SIGNING_IDENTIFIER;
|
||||||
|
|
||||||
// Compare the caluclated signature with the provided signature
|
// Compare the caluclated signature with the provided signature
|
||||||
if (signerMemcmp(&msg.data[mGetLength(msg)], _signing_hmac, MAX_PAYLOAD-mGetLength(msg))) {
|
if (signerMemcmp(&msg.data[mGetLength(msg)], _signing_hmac,
|
||||||
SIGN_DEBUG(PSTR("Signature bad\n"));
|
MIN(MAX_PAYLOAD-mGetLength(msg), 32))) {
|
||||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
|
||||||
SIGN_DEBUG(PSTR("Is the sender whitelisted and serial correct?\n"));
|
|
||||||
#endif
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
SIGN_DEBUG(PSTR("Signature OK\n"));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to calculate signature of msg (returned in hmac)
|
// Helper to calculate signature of msg (returned in _signing_hmac)
|
||||||
static void signerCalculateSignature(MyMessage &msg, bool signing)
|
static void signerCalculateSignature(MyMessage &msg, bool signing)
|
||||||
{
|
{
|
||||||
memset(_signing_temp_message, 0, 32);
|
// Signature is calculated on everything expect the first byte in the header
|
||||||
memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE],
|
uint16_t bytes_left = mGetLength(msg)+HEADER_SIZE-1;
|
||||||
MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
|
int16_t current_pos = 1-(int16_t)HEADER_SIZE; // Start at the second byte in the header
|
||||||
SIGN_DEBUG(PSTR("Current nonce: "
|
uint8_t* nonce = signing ? _signing_signing_nonce : _signing_verifying_nonce;
|
||||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"
|
|
||||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n"),
|
|
||||||
signing ? _signing_signing_nonce[0] : _signing_verifying_nonce[0],
|
|
||||||
signing ? _signing_signing_nonce[1] : _signing_verifying_nonce[1],
|
|
||||||
signing ? _signing_signing_nonce[2] : _signing_verifying_nonce[2],
|
|
||||||
signing ? _signing_signing_nonce[3] : _signing_verifying_nonce[3],
|
|
||||||
signing ? _signing_signing_nonce[4] : _signing_verifying_nonce[4],
|
|
||||||
signing ? _signing_signing_nonce[5] : _signing_verifying_nonce[5],
|
|
||||||
signing ? _signing_signing_nonce[6] : _signing_verifying_nonce[6],
|
|
||||||
signing ? _signing_signing_nonce[7] : _signing_verifying_nonce[7],
|
|
||||||
signing ? _signing_signing_nonce[8] : _signing_verifying_nonce[8],
|
|
||||||
signing ? _signing_signing_nonce[9] : _signing_verifying_nonce[9],
|
|
||||||
signing ? _signing_signing_nonce[10] : _signing_verifying_nonce[10],
|
|
||||||
signing ? _signing_signing_nonce[11] : _signing_verifying_nonce[11],
|
|
||||||
signing ? _signing_signing_nonce[12] : _signing_verifying_nonce[12],
|
|
||||||
signing ? _signing_signing_nonce[13] : _signing_verifying_nonce[13],
|
|
||||||
signing ? _signing_signing_nonce[14] : _signing_verifying_nonce[14],
|
|
||||||
signing ? _signing_signing_nonce[15] : _signing_verifying_nonce[15],
|
|
||||||
signing ? _signing_signing_nonce[16] : _signing_verifying_nonce[16],
|
|
||||||
signing ? _signing_signing_nonce[17] : _signing_verifying_nonce[17],
|
|
||||||
signing ? _signing_signing_nonce[18] : _signing_verifying_nonce[18],
|
|
||||||
signing ? _signing_signing_nonce[19] : _signing_verifying_nonce[19],
|
|
||||||
signing ? _signing_signing_nonce[20] : _signing_verifying_nonce[20],
|
|
||||||
signing ? _signing_signing_nonce[21] : _signing_verifying_nonce[21],
|
|
||||||
signing ? _signing_signing_nonce[22] : _signing_verifying_nonce[22],
|
|
||||||
signing ? _signing_signing_nonce[23] : _signing_verifying_nonce[23],
|
|
||||||
signing ? _signing_signing_nonce[24] : _signing_verifying_nonce[24],
|
|
||||||
signing ? _signing_signing_nonce[25] : _signing_verifying_nonce[25],
|
|
||||||
signing ? _signing_signing_nonce[26] : _signing_verifying_nonce[26],
|
|
||||||
signing ? _signing_signing_nonce[27] : _signing_verifying_nonce[27],
|
|
||||||
signing ? _signing_signing_nonce[28] : _signing_verifying_nonce[28],
|
|
||||||
signing ? _signing_signing_nonce[29] : _signing_verifying_nonce[29],
|
|
||||||
signing ? _signing_signing_nonce[30] : _signing_verifying_nonce[30],
|
|
||||||
signing ? _signing_signing_nonce[31] : _signing_verifying_nonce[31]
|
|
||||||
);
|
|
||||||
|
|
||||||
|
#ifdef MY_DEBUG_VERBOSE_SIGNING
|
||||||
|
buf2str(nonce, 32);
|
||||||
|
SIGN_DEBUG(PSTR("SGN:BND:NONCE=%s\n"), printStr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (bytes_left) {
|
||||||
|
uint16_t bytes_to_include = MIN(bytes_left, 32);
|
||||||
|
|
||||||
|
memset(_signing_temp_message, 0, 32);
|
||||||
|
memcpy(_signing_temp_message, (uint8_t*)&msg.data[current_pos], bytes_to_include);
|
||||||
|
|
||||||
|
// We can 'void' signerAtsha204AHmac because the HMAC is already put in the correct place
|
||||||
|
(void)signerAtsha204AHmac(nonce, _signing_temp_message);
|
||||||
|
// Purge nonce when used
|
||||||
|
memset(nonce, 0xAA, 32);
|
||||||
|
|
||||||
|
bytes_left -= bytes_to_include;
|
||||||
|
current_pos += bytes_to_include;
|
||||||
|
|
||||||
|
if (bytes_left > 0) {
|
||||||
|
// We will do another pass, use current HMAC as nonce for the next HMAC
|
||||||
|
memcpy(nonce, _signing_hmac, 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef MY_DEBUG_VERBOSE_SIGNING
|
||||||
|
buf2str(_signing_hmac, 32);
|
||||||
|
SIGN_DEBUG(PSTR("SGN:BND:HMAC=%s\n"), printStr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to calculate a ATSHA204A specific HMAC-SHA256 using provided 32 byte nonce and data
|
||||||
|
// (zero padded to 32 bytes)
|
||||||
|
// The pointer to the HMAC is returned, but the HMAC is also stored in _signing_hmac
|
||||||
|
static uint8_t* signerAtsha204AHmac(const uint8_t* nonce, const uint8_t* data)
|
||||||
|
{
|
||||||
// ATSHA204 calculates the HMAC with a PSK and a SHA256 digest of the following data:
|
// ATSHA204 calculates the HMAC with a PSK and a SHA256 digest of the following data:
|
||||||
// 32 bytes zeroes
|
// 32 bytes zeroes
|
||||||
// 32 bytes digest,
|
// 32 bytes digest,
|
||||||
@@ -317,7 +353,7 @@ static void signerCalculateSignature(MyMessage &msg, bool signing)
|
|||||||
// Calculate message digest first
|
// Calculate message digest first
|
||||||
_signing_sha256.init();
|
_signing_sha256.init();
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<32; i++) {
|
||||||
_signing_sha256.write(_signing_temp_message[i]);
|
_signing_sha256.write(data[i]);
|
||||||
}
|
}
|
||||||
_signing_sha256.write(0x15); // OPCODE
|
_signing_sha256.write(0x15); // OPCODE
|
||||||
_signing_sha256.write(0x02); // param1
|
_signing_sha256.write(0x02); // param1
|
||||||
@@ -330,19 +366,17 @@ static void signerCalculateSignature(MyMessage &msg, bool signing)
|
|||||||
_signing_sha256.write(0x00);
|
_signing_sha256.write(0x00);
|
||||||
}
|
}
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<32; i++) {
|
||||||
_signing_sha256.write(signing ? _signing_signing_nonce[i] : _signing_verifying_nonce[i]);
|
_signing_sha256.write(nonce[i]);
|
||||||
}
|
}
|
||||||
// Purge nonce when used
|
memcpy(_signing_hmac, _signing_sha256.result(), 32);
|
||||||
memset(signing ? _signing_signing_nonce : _signing_verifying_nonce, 0xAA, 32);
|
|
||||||
memcpy(_signing_temp_message, _signing_sha256.result(), 32);
|
|
||||||
|
|
||||||
// Feed "message" to HMAC calculator
|
// Feed "message" to HMAC calculator
|
||||||
_signing_sha256.initHmac(_signing_hmac_key,32); // Set the key to use
|
_signing_sha256.initHmac(_signing_hmac_key, 32); // Set the key to use
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<32; i++) {
|
||||||
_signing_sha256.write(0x00); // 32 bytes zeroes
|
_signing_sha256.write(0x00); // 32 bytes zeroes
|
||||||
}
|
}
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<32; i++) {
|
||||||
_signing_sha256.write(_signing_temp_message[i]); // 32 bytes digest
|
_signing_sha256.write(_signing_hmac[i]); // 32 bytes digest
|
||||||
}
|
}
|
||||||
_signing_sha256.write(0x11); // OPCODE
|
_signing_sha256.write(0x11); // OPCODE
|
||||||
_signing_sha256.write(0x04); // Mode
|
_signing_sha256.write(0x04); // Mode
|
||||||
@@ -362,16 +396,17 @@ static void signerCalculateSignature(MyMessage &msg, bool signing)
|
|||||||
}
|
}
|
||||||
|
|
||||||
memcpy(_signing_hmac, _signing_sha256.resultHmac(), 32);
|
memcpy(_signing_hmac, _signing_sha256.resultHmac(), 32);
|
||||||
|
return _signing_hmac;
|
||||||
SIGN_DEBUG(PSTR("HMAC: "
|
|
||||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"
|
|
||||||
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n"),
|
|
||||||
_signing_hmac[0], _signing_hmac[1], _signing_hmac[2], _signing_hmac[3],
|
|
||||||
_signing_hmac[4], _signing_hmac[5], _signing_hmac[6], _signing_hmac[7],
|
|
||||||
_signing_hmac[8], _signing_hmac[9], _signing_hmac[10], _signing_hmac[11],
|
|
||||||
_signing_hmac[12], _signing_hmac[13], _signing_hmac[14], _signing_hmac[15],
|
|
||||||
_signing_hmac[16], _signing_hmac[17], _signing_hmac[18], _signing_hmac[19],
|
|
||||||
_signing_hmac[20], _signing_hmac[21], _signing_hmac[22], _signing_hmac[23],
|
|
||||||
_signing_hmac[24], _signing_hmac[25], _signing_hmac[26], _signing_hmac[27],
|
|
||||||
_signing_hmac[28], _signing_hmac[29], _signing_hmac[30], _signing_hmac[31]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper to calculate a generic SHA256 digest of provided buffer
|
||||||
|
// The pointer to the hash is returned
|
||||||
|
static uint8_t* signerSha256(const uint8_t* data, size_t sz)
|
||||||
|
{
|
||||||
|
_signing_sha256.init();
|
||||||
|
for (size_t i=0; i<sz; i++) {
|
||||||
|
_signing_sha256.write(data[i]);
|
||||||
|
}
|
||||||
|
return _signing_sha256.result();
|
||||||
|
}
|
||||||
|
#endif //MY_SIGNING_SOFT
|
||||||
|
|||||||
@@ -658,7 +658,8 @@ void transportProcessMessage(void)
|
|||||||
|
|
||||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:READ,%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
|
TRANSPORT_DEBUG(PSTR("TSF:MSG:READ,%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
|
||||||
sender, last, destination, _msg.sensor, command, type, mGetPayloadType(_msg), msgLength,
|
sender, last, destination, _msg.sensor, command, type, mGetPayloadType(_msg), msgLength,
|
||||||
mGetSigned(_msg), _msg.getString(_convBuf));
|
mGetSigned(_msg), ((command == C_INTERNAL &&
|
||||||
|
type == I_NONCE_RESPONSE) ? "<NONCE>" : _msg.getString(_convBuf)));
|
||||||
|
|
||||||
// Reject payloads with incorrect length
|
// Reject payloads with incorrect length
|
||||||
if (payloadLength != expectedMessageLength) {
|
if (payloadLength != expectedMessageLength) {
|
||||||
@@ -993,7 +994,8 @@ bool transportSendWrite(const uint8_t to, MyMessage &message)
|
|||||||
mGetPayloadType(message), mGetLength(message), mGetSigned(message),
|
mGetPayloadType(message), mGetLength(message), mGetSigned(message),
|
||||||
_transportSM.failedUplinkTransmissions,
|
_transportSM.failedUplinkTransmissions,
|
||||||
(result ? "OK" : "NACK"),
|
(result ? "OK" : "NACK"),
|
||||||
message.getString(_convBuf));
|
((mGetCommand(message) == C_INTERNAL &&
|
||||||
|
message.type == I_NONCE_RESPONSE) ? "<NONCE>" : message.getString(_convBuf)));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef ATSHA204_H
|
#ifndef ATSHA204_H
|
||||||
#define ATSHA204_H
|
#define ATSHA204_H
|
||||||
#if !DOXYGEN
|
#if !DOXYGEN
|
||||||
#include "Arduino.h"
|
#include <Arduino.h>
|
||||||
|
|
||||||
/* This is a scaled down variant of the ATSHA204 library, tweaked to meet the specific needs of the MySensors library. */
|
/* This is a scaled down variant of the ATSHA204 library, tweaked to meet the specific needs of the MySensors library. */
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file RFM69.h
|
* @file RFM69_new.h
|
||||||
*
|
*
|
||||||
* @defgroup RFM69Newgrp RFM69New
|
* @defgroup RFM69Newgrp RFM69New
|
||||||
* @ingroup internals
|
* @ingroup internals
|
||||||
@@ -618,4 +618,4 @@ LOCAL void RFM69_listenModeGetDurations(uint32_t &rxDuration, uint32_t &idleDura
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** @}*/
|
/** @}*/
|
||||||
|
|||||||
@@ -63,7 +63,9 @@
|
|||||||
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 //!< Unconnected analog pin for random seed
|
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 //!< Unconnected analog pin for random seed
|
||||||
|
|
||||||
// SETTINGS FOR MY_SIGNING_ATSHA204
|
// SETTINGS FOR MY_SIGNING_ATSHA204
|
||||||
|
#ifndef MY_SIGNING_ATSHA204_PIN
|
||||||
#define MY_SIGNING_ATSHA204_PIN 17 //!< A3 - pin where ATSHA204 is attached
|
#define MY_SIGNING_ATSHA204_PIN 17 //!< A3 - pin where ATSHA204 is attached
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <MySensors.h>
|
#include <MySensors.h>
|
||||||
|
|
||||||
|
|||||||
@@ -24,9 +24,6 @@
|
|||||||
|
|
||||||
#include <stddef.h> // data type definitions
|
#include <stddef.h> // data type definitions
|
||||||
|
|
||||||
/** \todo Use same values for same meanings for SHA204 and AES132.
|
|
||||||
* */
|
|
||||||
|
|
||||||
#define SHA204_SUCCESS ((uint8_t) 0x00) //!< Function succeeded.
|
#define SHA204_SUCCESS ((uint8_t) 0x00) //!< Function succeeded.
|
||||||
#define SHA204_PARSE_ERROR ((uint8_t) 0xD2) //!< response status byte indicates parsing error
|
#define SHA204_PARSE_ERROR ((uint8_t) 0xD2) //!< response status byte indicates parsing error
|
||||||
#define SHA204_CMD_FAIL ((uint8_t) 0xD3) //!< response status byte indicates command execution error
|
#define SHA204_CMD_FAIL ((uint8_t) 0xD3) //!< response status byte indicates command execution error
|
||||||
|
|||||||
@@ -61,11 +61,11 @@
|
|||||||
//#define MY_SIGNING_SOFT
|
//#define MY_SIGNING_SOFT
|
||||||
// Enable signing related debug
|
// Enable signing related debug
|
||||||
//#define MY_DEBUG_VERBOSE_SIGNING
|
//#define MY_DEBUG_VERBOSE_SIGNING
|
||||||
// Enable this to request signatures from nodes that in turn request signatures from gateway
|
// Enable this to request signatures
|
||||||
//#define MY_SIGNING_REQUEST_SIGNATURES
|
//#define MY_SIGNING_REQUEST_SIGNATURES
|
||||||
// Enable this to have gateway require all nodes in the network to sign messages sent to it
|
// Enable this to to weaken security for gradual deployment purpose
|
||||||
// Note: MY_SIGNING_REQUEST_SIGNATURES must also be set
|
// (see signing documentation for details)
|
||||||
//#define MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL
|
//#define MY_SIGNING_WEAK_SECURITY
|
||||||
|
|
||||||
// Enables RF24 encryption (all nodes and gateway must have this enabled, and all must be
|
// Enables RF24 encryption (all nodes and gateway must have this enabled, and all must be
|
||||||
// personalized with the same AES key)
|
// personalized with the same AES key)
|
||||||
|
|||||||
@@ -38,6 +38,9 @@
|
|||||||
#define MY_SERIALDEVICE Serial
|
#define MY_SERIALDEVICE Serial
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MIN(a,b) min(a,b)
|
||||||
|
#define MAX(a,b) max(a,b)
|
||||||
|
|
||||||
// Define these as macros to save valuable space
|
// Define these as macros to save valuable space
|
||||||
#define hwDigitalWrite(__pin, __value) digitalWriteFast(__pin, __value)
|
#define hwDigitalWrite(__pin, __value) digitalWriteFast(__pin, __value)
|
||||||
#define hwDigitalRead(__pin) digitalReadFast(__pin)
|
#define hwDigitalRead(__pin) digitalReadFast(__pin)
|
||||||
|
|||||||
@@ -31,6 +31,8 @@
|
|||||||
|
|
||||||
#define min(a,b) ((a)<(b)?(a):(b))
|
#define min(a,b) ((a)<(b)?(a):(b))
|
||||||
#define max(a,b) ((a)>(b)?(a):(b))
|
#define max(a,b) ((a)>(b)?(a):(b))
|
||||||
|
#define MIN(a,b) min(a,b)
|
||||||
|
#define MAX(a,b) max(a,b)
|
||||||
|
|
||||||
#define EEPROM_size (1024)
|
#define EEPROM_size (1024)
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ SerialSimulator Serial = SerialSimulator();
|
|||||||
#define MY_SERIALDEVICE Serial
|
#define MY_SERIALDEVICE Serial
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MIN(a,b) ((a)<(b)?(a):(b))
|
||||||
|
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||||
|
|
||||||
// Define these as macros (do nothing)
|
// Define these as macros (do nothing)
|
||||||
#define hwWatchdogReset()
|
#define hwWatchdogReset()
|
||||||
#define hwReboot()
|
#define hwReboot()
|
||||||
|
|||||||
@@ -45,6 +45,9 @@
|
|||||||
#define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__)
|
#define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__)
|
||||||
#define printf_P printf
|
#define printf_P printf
|
||||||
|
|
||||||
|
#define MIN(a,b) min(a,b)
|
||||||
|
#define MAX(a,b) max(a,b)
|
||||||
|
|
||||||
// Define these as macros to save valuable space
|
// Define these as macros to save valuable space
|
||||||
#define hwDigitalWrite(__pin, __value) digitalWrite(__pin, __value)
|
#define hwDigitalWrite(__pin, __value) digitalWrite(__pin, __value)
|
||||||
#define hwDigitalRead(__pin) digitalRead(__pin)
|
#define hwDigitalRead(__pin) digitalRead(__pin)
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ extEEPROM eep(MY_EXT_EEPROM_SIZE, 1, MY_EXT_EEPROM_PAGE_SIZE,
|
|||||||
|
|
||||||
#define min(a,b) ((a)<(b)?(a):(b))
|
#define min(a,b) ((a)<(b)?(a):(b))
|
||||||
#define max(a,b) ((a)>(b)?(a):(b))
|
#define max(a,b) ((a)>(b)?(a):(b))
|
||||||
|
#define MIN(a,b) min(a,b)
|
||||||
|
#define MAX(a,b) max(a,b)
|
||||||
#define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__)
|
#define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__)
|
||||||
|
|
||||||
// Define these as macros to save valuable space
|
// Define these as macros to save valuable space
|
||||||
|
|||||||
@@ -42,6 +42,9 @@
|
|||||||
#define MY_SERIALDEVICE Serial
|
#define MY_SERIALDEVICE Serial
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MIN(a,b) min(a,b)
|
||||||
|
#define MAX(a,b) max(a,b)
|
||||||
|
|
||||||
#ifndef digitalPinToInterrupt
|
#ifndef digitalPinToInterrupt
|
||||||
#define digitalPinToInterrupt(__pin) (__pin)
|
#define digitalPinToInterrupt(__pin) (__pin)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -50,6 +50,9 @@
|
|||||||
#define SIM_SCGC6_RNGA ((uint32_t)0x00000200)
|
#define SIM_SCGC6_RNGA ((uint32_t)0x00000200)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MIN(a,b) min(a,b)
|
||||||
|
#define MAX(a,b) max(a,b)
|
||||||
|
|
||||||
// Define these as macros to save valuable space
|
// Define these as macros to save valuable space
|
||||||
#define hwDigitalWrite(__pin, __value) digitalWriteFast(__pin, __value)
|
#define hwDigitalWrite(__pin, __value) digitalWriteFast(__pin, __value)
|
||||||
#define hwDigitalRead(__pin) digitalReadFast(__pin)
|
#define hwDigitalRead(__pin) digitalReadFast(__pin)
|
||||||
|
|||||||
@@ -273,7 +273,12 @@ void print_aes_key(uint8_t *key_ptr = NULL)
|
|||||||
uint8_t key[16];
|
uint8_t key[16];
|
||||||
|
|
||||||
if (key_ptr == NULL) {
|
if (key_ptr == NULL) {
|
||||||
|
#ifdef MY_SIGNING_SIMPLE_PASSWD
|
||||||
|
memset(key, 0, 16);
|
||||||
|
memcpy(key, MY_SIGNING_SIMPLE_PASSWD, strnlen(MY_SIGNING_SIMPLE_PASSWD, 16));
|
||||||
|
#else
|
||||||
hwReadConfigBlock(&key, reinterpret_cast<void*>EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
hwReadConfigBlock(&key, reinterpret_cast<void*>EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
||||||
|
#endif
|
||||||
key_ptr = key;
|
key_ptr = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,12 @@ uint8_t _psk[16];
|
|||||||
bool transportInit(void)
|
bool transportInit(void)
|
||||||
{
|
{
|
||||||
#if defined(MY_NRF5_ESB_ENABLE_ENCRYPTION)
|
#if defined(MY_NRF5_ESB_ENABLE_ENCRYPTION)
|
||||||
hwReadConfigBlock((void *)_psk, (void *)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
#ifdef MY_SIGNING_SIMPLE_PASSWD
|
||||||
|
memset(_psk, 0, 16);
|
||||||
|
memcpy(_psk, MY_SIGNING_SIMPLE_PASSWD, strnlen(MY_SIGNING_SIMPLE_PASSWD, 16));
|
||||||
|
#else
|
||||||
|
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
||||||
|
#endif
|
||||||
// set up AES-key
|
// set up AES-key
|
||||||
_aes.set_key(_psk, 16);
|
_aes.set_key(_psk, 16);
|
||||||
// Make sure it is purged from memory when set
|
// Make sure it is purged from memory when set
|
||||||
|
|||||||
@@ -68,7 +68,12 @@ uint8_t _psk[16];
|
|||||||
bool transportInit(void)
|
bool transportInit(void)
|
||||||
{
|
{
|
||||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||||
|
#ifdef MY_SIGNING_SIMPLE_PASSWD
|
||||||
|
memset(_psk, 0, 16);
|
||||||
|
memcpy(_psk, MY_SIGNING_SIMPLE_PASSWD, strnlen(MY_SIGNING_SIMPLE_PASSWD, 16));
|
||||||
|
#else
|
||||||
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
||||||
|
#endif
|
||||||
//set up AES-key
|
//set up AES-key
|
||||||
_aes.set_key(_psk, 16);
|
_aes.set_key(_psk, 16);
|
||||||
// Make sure it is purged from memory when set
|
// Make sure it is purged from memory when set
|
||||||
|
|||||||
@@ -33,7 +33,12 @@ bool transportInit(void)
|
|||||||
|
|
||||||
#ifdef MY_RFM69_ENABLE_ENCRYPTION
|
#ifdef MY_RFM69_ENABLE_ENCRYPTION
|
||||||
uint8_t _psk[16];
|
uint8_t _psk[16];
|
||||||
|
#ifdef MY_SIGNING_SIMPLE_PASSWD
|
||||||
|
memset(_psk, 0, 16);
|
||||||
|
memcpy(_psk, MY_SIGNING_SIMPLE_PASSWD, strnlen(MY_SIGNING_SIMPLE_PASSWD, 16));
|
||||||
|
#else
|
||||||
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
||||||
|
#endif
|
||||||
RFM69_encrypt((const char*)_psk);
|
RFM69_encrypt((const char*)_psk);
|
||||||
(void)memset(_psk, 0, 16); // Make sure it is purged from memory when set
|
(void)memset(_psk, 0, 16); // Make sure it is purged from memory when set
|
||||||
#endif
|
#endif
|
||||||
@@ -163,7 +168,12 @@ bool transportInit(void)
|
|||||||
if (_radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID)) {
|
if (_radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID)) {
|
||||||
#ifdef MY_RFM69_ENABLE_ENCRYPTION
|
#ifdef MY_RFM69_ENABLE_ENCRYPTION
|
||||||
uint8_t _psk[16];
|
uint8_t _psk[16];
|
||||||
|
#ifdef MY_SIGNING_SIMPLE_PASSWD
|
||||||
|
memset(_psk, 0, 16);
|
||||||
|
memcpy(_psk, MY_SIGNING_SIMPLE_PASSWD, strnlen(MY_SIGNING_SIMPLE_PASSWD, 16));
|
||||||
|
#else
|
||||||
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
||||||
|
#endif
|
||||||
_radio.encrypt((const char*)_psk);
|
_radio.encrypt((const char*)_psk);
|
||||||
(void)memset(_psk, 0, 16); // Make sure it is purged from memory when set
|
(void)memset(_psk, 0, 16); // Make sure it is purged from memory when set
|
||||||
#endif
|
#endif
|
||||||
@@ -262,4 +272,4 @@ int16_t transportGetTxPowerLevel(void)
|
|||||||
return INVALID_LEVEL;
|
return INVALID_LEVEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+2
-1
@@ -77,6 +77,7 @@ MY_INCLUSION_MODE_DURATION LITERAL1
|
|||||||
|
|
||||||
# Signing
|
# Signing
|
||||||
MY_DEBUG_VERBOSE_SIGNING LITERAL1
|
MY_DEBUG_VERBOSE_SIGNING LITERAL1
|
||||||
|
MY_SIGNING_SIMPLE_PASSWD LITERAL1
|
||||||
MY_SIGNING_ATSHA204 LITERAL1
|
MY_SIGNING_ATSHA204 LITERAL1
|
||||||
MY_SIGNING_SOFT LITERAL1
|
MY_SIGNING_SOFT LITERAL1
|
||||||
MY_VERIFICATION_TIMEOUT_MS LITERAL1
|
MY_VERIFICATION_TIMEOUT_MS LITERAL1
|
||||||
@@ -84,7 +85,7 @@ MY_SIGNING_NODE_WHITELISTING LITERAL1
|
|||||||
MY_SIGNING_ATSHA204_PIN LITERAL1
|
MY_SIGNING_ATSHA204_PIN LITERAL1
|
||||||
MY_SIGNING_SOFT_RANDOMSEED_PIN LITERAL1
|
MY_SIGNING_SOFT_RANDOMSEED_PIN LITERAL1
|
||||||
MY_SIGNING_REQUEST_SIGNATURES LITERAL1
|
MY_SIGNING_REQUEST_SIGNATURES LITERAL1
|
||||||
MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL LITERAL1
|
MY_SIGNING_WEAK_SECURITY LITERAL1
|
||||||
MY_NODE_LOCK_FEATURE LITERAL1
|
MY_NODE_LOCK_FEATURE LITERAL1
|
||||||
MY_NODE_UNLOCK_PIN LITERAL1
|
MY_NODE_UNLOCK_PIN LITERAL1
|
||||||
MY_NODE_LOCK_COUNTER_MAX LITERAL1
|
MY_NODE_LOCK_COUNTER_MAX LITERAL1
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
// EasyClangComplete is a clang based plugin that provides autocompletion features.
|
// EasyClangComplete is a clang based plugin that provides autocompletion features.
|
||||||
// Installation instructions is available here: https://github.com/niosus/EasyClangComplete
|
// Installation instructions is available here: https://github.com/niosus/EasyClangComplete
|
||||||
"ecc_errors_on_save": false, // disable clang error checking since it is hard to figure out the standard library locations and this gives many false positives
|
|
||||||
|
|
||||||
// MySensors core library uses some coding style rules. Artistic Style can be used to format the code according to these rules
|
// MySensors core library uses some coding style rules. Artistic Style can be used to format the code according to these rules
|
||||||
// The below settings enable automatic formatting of the code upon saving in Sublime Text
|
// The below settings enable automatic formatting of the code upon saving in Sublime Text
|
||||||
@@ -55,7 +54,11 @@
|
|||||||
"cppcheck":
|
"cppcheck":
|
||||||
{
|
{
|
||||||
"@disable": false,
|
"@disable": false,
|
||||||
"args": ["--force", "--library=avr"],
|
"args": ["--force", "--library=avr",
|
||||||
|
"--suppressions-list=$project_path/../../.mystools/cppcheck/config/suppressions.cfg",
|
||||||
|
"-I$project_path/../../", "-I$project_path/../../core",
|
||||||
|
"-DMY_SIGNING_NODE_WHITELISTING",
|
||||||
|
"-DMY_SIGNING_SIMPLE_PASSWD"],
|
||||||
"enable": "style,information",
|
"enable": "style,information",
|
||||||
"excludes": [],
|
"excludes": [],
|
||||||
"std": []
|
"std": []
|
||||||
|
|||||||
Reference in New Issue
Block a user