bool signerAtsha204SoftSignMsg(MyMessage &msg) {
	// If we cannot fit any signature in the message, refuse to sign it
	if (mGetLength(msg) > MAX_PAYLOAD-2) {
		DEBUG_SIGNING_PRINTBUF(F("Message too large"), NULL, 0);
		return false; 
	}

	// Calculate signature of message
	mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated
	signerCalculateSignature(msg);

	if (DO_WHITELIST(msg.destination)) {
		// Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided
		_signing_sha256.init();
		for (int i=0; i<32; i++) _signing_sha256.write(_signing_hmac[i]);
		_signing_sha256.write(msg.sender);
		for (int i=0; i<SHA204_SERIAL_SZ; i++) _signing_sha256.write(_signing_node_serial_info[i]);
		memcpy(_signing_hmac, _signing_sha256.result(), 32);
		DEBUG_SIGNING_PRINTBUF(F("SHA256: "), _signing_hmac, 32);
		DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0);
	}

	// Overwrite the first byte in the signature with the signing identifier
	_signing_hmac[0] = SIGNING_IDENTIFIER;

	// Transfer as much signature data as the remaining space in the message permits
	memcpy(&msg.data[mGetLength(msg)], _signing_hmac, MAX_PAYLOAD-mGetLength(msg));
	DEBUG_SIGNING_PRINTBUF(F("Signature in message: "), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg));

	return true;
}
bool signerAtsha204SoftVerifyMsg(MyMessage &msg) {
	if (!_signing_verification_ongoing) {
		DEBUG_SIGNING_PRINTBUF(F("No active verification session"), NULL, 0);
		return false; 
	} else {
		// Make sure we have not expired
		if (!signerCheckTimer()) {
			return false; 
		}

		_signing_verification_ongoing = false;

		if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
			DEBUG_SIGNING_PRINTBUF(F("Incorrect signing identifier"), NULL, 0);
			return false; 
		}

		// Get signature of message
		DEBUG_SIGNING_PRINTBUF(F("Signature in message: "), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg));
		signerCalculateSignature(msg);

#ifdef MY_SIGNING_NODE_WHITELISTING
		// Look up the senders nodeId in our whitelist and salt the signature with that data
		size_t j;
		for (j=0; j < NUM_OF(_signing_whitelist); j++) {
			if (_signing_whitelist[j].nodeId == msg.sender) {
				DEBUG_SIGNING_PRINTBUF(F("Sender found in whitelist"), NULL, 0);
				_signing_sha256.init();
				for (int i=0; i<32; i++) _signing_sha256.write(_signing_hmac[i]);
				_signing_sha256.write(msg.sender);
				for (int i=0; i<SHA204_SERIAL_SZ; i++) _signing_sha256.write(_signing_whitelist[j].serial[i]);
				memcpy(_signing_hmac, _signing_sha256.result(), 32);
				DEBUG_SIGNING_PRINTBUF(F("SHA256: "), _signing_hmac, 32);
				break;
			}
		}
		if (j == NUM_OF(_signing_whitelist)) {
			DEBUG_SIGNING_PRINTBUF(F("Sender not found in whitelist, message rejected!"), NULL, 0);
			return false;
		}
#endif

		// Overwrite the first byte in the signature with the signing identifier
		_signing_hmac[0] = SIGNING_IDENTIFIER;

		// Compare the caluclated signature with the provided signature
		if (memcmp(&msg.data[mGetLength(msg)], _signing_hmac, MAX_PAYLOAD-mGetLength(msg))) {
			DEBUG_SIGNING_PRINTBUF(F("Signature bad: "), _signing_hmac, MAX_PAYLOAD-mGetLength(msg));
#ifdef MY_SIGNING_NODE_WHITELISTING
			DEBUG_SIGNING_PRINTBUF(F("Is the sender whitelisted and serial correct?"), NULL, 0);
#endif
			return false; 
		} else {
			DEBUG_SIGNING_PRINTBUF(F("Signature OK"), NULL, 0);
			return true;
		}
	}
}
Esempio n. 3
0
boolean MySensor::sendWrite(uint8_t next, MyMessage &message, bool broadcast) {
	uint8_t length = mGetLength(message);
	message.last = nc.nodeId;
	mSetVersion(message, PROTOCOL_VERSION);
	// Make sure radio has powered up
	RF24::powerUp();
	RF24::stopListening();
	RF24::openWritingPipe(TO_ADDR(next));
	bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast);
	RF24::startListening();

	debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,st=%s:%s\n"),
			message.sender,message.last, next, message.destination, message.sensor, mGetCommand(message), message.type, mGetPayloadType(message), mGetLength(message), ok?"ok":"fail", message.getString(convBuf));

	return ok;
}
Esempio n. 4
0
boolean MySensor::process() {
	uint8_t pipe;
	boolean available = RF24::available(&pipe);

	if (!available || pipe>6)
		return false;

	uint8_t len = RF24::getDynamicPayloadSize();
	RF24::read(&msg, len);
	RF24::writeAckPayload(pipe,&pipe, 1 );

	// Add string termination, good if we later would want to print it.
	msg.data[mGetLength(msg)] = '\0';
	debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d:%s\n"),
				msg.sender, msg.last, msg.destination,  msg.sensor, mGetCommand(msg), msg.type, mGetPayloadType(msg), mGetLength(msg), msg.getString(convBuf));

	if(!(mGetVersion(msg) == PROTOCOL_VERSION)) {
		debug(PSTR("version mismatch\n"));
		return false;
	}

	uint8_t command = mGetCommand(msg);
	uint8_t type = msg.type;
	uint8_t sender = msg.sender;
	uint8_t last = msg.last;
	uint8_t destination = msg.destination;

	if (repeaterMode && command == C_INTERNAL && type == I_FIND_PARENT) {
		// Relaying nodes should always answer ping messages
		// Wait a random delay of 0-2 seconds to minimize collision
		// between ping ack messages from other relaying nodes
		delay(millis() & 0x3ff);
		sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(nc.distance), true);
		return false;
	} else if (destination == nc.nodeId) {
		// Check if sender requests an ack back.
		if (mGetRequestAck(msg)) {
			// Copy message
			ack = msg;
			mSetRequestAck(ack,false); // Reply without ack flag (otherwise we would end up in an eternal loop)
			mSetAck(ack,true);
			ack.sender = nc.nodeId;
			ack.destination = msg.sender;
			sendRoute(ack);
		}

		// This message is addressed to this node
		if (repeaterMode && last != nc.parentNodeId) {
			// Message is from one of the child nodes. Add it to routing table.
			addChildRoute(sender, last);
		}

		if (command == C_INTERNAL) {
			if (type == I_FIND_PARENT_RESPONSE && !isGateway) {
				// We've received a reply to a FIND_PARENT message. Check if the distance is
				// shorter than we already have.
				uint8_t distance = msg.getByte();
				if (distance<nc.distance-1) {
					// Found a neighbor closer to GW than previously found
					nc.distance = distance + 1;
					nc.parentNodeId = msg.sender;
					eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, nc.parentNodeId);
					eeprom_write_byte((uint8_t*)EEPROM_DISTANCE_ADDRESS, nc.distance);
					debug(PSTR("new parent=%d, d=%d\n"), nc.parentNodeId, nc.distance);
				}
				return false;
			} else if (sender == GATEWAY_ADDRESS) {
				bool isMetric;

				if (type == I_REBOOT) {
					wdt_enable(WDTO_15MS);
					for (;;);
				} else if (type == I_ID_RESPONSE) {
					if (nc.nodeId == AUTO) {
						nc.nodeId = msg.getByte();
						// Write id to EEPROM
						if (nc.nodeId == AUTO) {
							// sensor net gateway will return max id if all sensor id are taken
							debug(PSTR("full\n"));
							while (1); // Wait here. Nothing else we can do...
						} else {
							RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
							eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, nc.nodeId);
						}
						debug(PSTR("id=%d\n"), nc.nodeId);
					}
				} else if (type == I_CONFIG) {
					// Pick up configuration from controller (currently only metric/imperial)
					// and store it in eeprom if changed
					isMetric = msg.getByte() == 'M' ;
					if (cc.isMetric != isMetric) {
						cc.isMetric = isMetric;
						eeprom_write_byte((uint8_t*)EEPROM_CONTROLLER_CONFIG_ADDRESS, isMetric);
						//eeprom_write_block((const void*)&cc, (uint8_t*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
					}
				} else if (type == I_CHILDREN) {
					if (repeaterMode && msg.getByte() == 'C') {
						// Clears child relay data for this node
						debug(PSTR("rd=clear\n"));
						for (uint8_t i=0;i< sizeof(childNodeTable); i++) {
							removeChildRoute(i);
						}
						sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set(""));
					}
				} else if (type == I_TIME) {
					if (timeCallback != NULL) {
						// Deliver time to callback
						timeCallback(msg.getULong());
					}
				}
				return false;
			}
		}
		// Call incoming message callback if available
		if (msgCallback != NULL) {
			msgCallback(msg);
		}
		// Return true if message was addressed for this node...
		return true;
	} else if (repeaterMode && pipe == CURRENT_NODE_PIPE) {
		// We should try to relay this message to another node

		uint8_t route = getChildRoute(msg.destination);
		if (route>0 && route<255) {
			// This message should be forwarded to a child node. If we send message
			// to this nodes pipe then all children will receive it because the are
			// all listening to this nodes pipe.
			//
			//    +----B
			//  -A
			//    +----C------D
			//
			//  We're node C, Message comes from A and has destination D
			//
			// lookup route in table and send message there
			sendWrite(route, msg);
		} else  {
			// A message comes from a child node and we have no
			// route for it.
			//
			//    +----B
			//  -A
			//    +----C------D    <-- Message comes from D
			//
			//     We're node C
			//
			// Message should be passed to node A (this nodes relay)

			// This message should be routed back towards sensor net gateway
			sendWrite(nc.parentNodeId, msg);
			// Add this child to our "routing table" if it not already exist
			addChildRoute(sender, last);
		}
	}
	return false;
}
Esempio n. 5
0
static boolean sendWrite(MyMessage message) {
	return write(nc.parentNodeId, message.array, HEADER_SIZE + mGetLength(message), (message.destination == BROADCAST_ADDRESS));
}
Esempio n. 6
0
void transportProcessMessage(void)
{
	// Manage signing timeout
	(void)signerCheckTimer();
	// receive message
	setIndication(INDICATION_RX);
	uint8_t payloadLength = transportReceive((uint8_t *)&_msg);
	// get message length and limit size

	const uint8_t msgLength = min(mGetLength(_msg), (uint8_t)MAX_PAYLOAD);
	// calculate expected length
	const uint8_t expectedMessageLength = HEADER_SIZE + (mGetSigned(_msg) ? MAX_PAYLOAD : msgLength);
#if defined(MY_RF24_ENABLE_ENCRYPTION)
	// payload length = a multiple of blocksize length for decrypted messages, i.e. cannot be used for payload length check
	payloadLength = expectedMessageLength;
#endif
	const uint8_t command = mGetCommand(_msg);
	const uint8_t type = _msg.type;
	const uint8_t sender = _msg.sender;
	const uint8_t last = _msg.last;
	const uint8_t destination = _msg.destination;

	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,
	                mGetSigned(_msg), _msg.getString(_convBuf));

	// Reject payloads with incorrect length
	if (payloadLength != expectedMessageLength) {
		setIndication(INDICATION_ERR_LENGTH);
		TRANSPORT_DEBUG(PSTR("!TSF:MSG:LEN,%d!=%d\n"), payloadLength,
		                expectedMessageLength); // invalid payload length
		return;
	}

	// Reject messages with incorrect protocol version
	if (mGetVersion(_msg) != PROTOCOL_VERSION) {
		setIndication(INDICATION_ERR_VERSION);
		TRANSPORT_DEBUG(PSTR("!TSF:MSG:PVER,%d=%d\n"), mGetVersion(_msg),
		                PROTOCOL_VERSION);	// protocol version mismatch
		return;
	}

	// Reject messages that do not pass verification
	if (!signerVerifyMsg(_msg)) {
		setIndication(INDICATION_ERR_SIGN);
		TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN VERIFY FAIL\n"));
		return;
	}

	// update routing table if msg not from parent
#if defined(MY_REPEATER_FEATURE)
#if !defined(MY_GATEWAY_FEATURE)
	if (last != _transportConfig.parentNodeId) {
#else
	// GW doesn't have parent
	{
#endif
		// Message is from one of the child nodes and not sent from this node. Add it to routing table.
		if (sender != _transportConfig.nodeId)
		{
			transportSetRoute(sender, last);
		}
	}
#endif // MY_REPEATER_FEATURE

	// set message received flag
	_transportSM.msgReceived = true;

	// Is message addressed to this node?
	if (destination == _transportConfig.nodeId) {
		// prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes)
		mSetLength(_msg, min(mGetLength(_msg),(uint8_t)MAX_PAYLOAD));
		// null terminate data
		_msg.data[msgLength] = 0u;
		// Check if sender requests an ack back.
		if (mGetRequestAck(_msg)) {
			TRANSPORT_DEBUG(PSTR("TSF:MSG:ACK REQ\n"));	// ACK requested
			_msgTmp = _msg;	// Copy message
			mSetRequestAck(_msgTmp,
			               false); // Reply without ack flag (otherwise we would end up in an eternal loop)
			mSetAck(_msgTmp, true); // set ACK flag
			_msgTmp.sender = _transportConfig.nodeId;
			_msgTmp.destination = sender;
			// send ACK, use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply
			(void)transportSendRoute(_msgTmp);
		}
		if(!mGetAck(_msg)) {
			// only process if not ACK
			if (command == C_INTERNAL) {
				// Process signing related internal messages
				if (signerProcessInternal(_msg)) {
					return; // Signer processing indicated no further action needed
				}
#if !defined(MY_GATEWAY_FEATURE)
				if (type == I_ID_RESPONSE) {
#if (MY_NODE_ID == AUTO)
					// only active if node ID dynamic
					(void)transportAssignNodeID(_msg.getByte());
#endif
					return; // no further processing required
				}
				if (type == I_FIND_PARENT_RESPONSE) {
#if !defined(MY_GATEWAY_FEATURE) && !defined(MY_PARENT_NODE_IS_STATIC)
					if (_transportSM.findingParentNode) {	// only process if find parent active
						// Reply to a I_FIND_PARENT_REQUEST message. Check if the distance is shorter than we already have.
						uint8_t distance = _msg.getByte();
						if (isValidDistance(distance)) {
							distance++;	// Distance to gateway is one more for us w.r.t. parent
							// update settings if distance shorter or preferred parent found
							if (((isValidDistance(distance) && distance < _transportConfig.distanceGW) || (!_autoFindParent &&
							        sender == (uint8_t)MY_PARENT_NODE_ID)) && !_transportSM.preferredParentFound) {
								// Found a neighbor closer to GW than previously found
								if (!_autoFindParent && sender == (uint8_t)MY_PARENT_NODE_ID) {
									_transportSM.preferredParentFound = true;
									TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR PREF\n"));	// find parent, preferred parent found
								}
								_transportConfig.distanceGW = distance;
								_transportConfig.parentNodeId = sender;
								TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR OK,ID=%d,D=%d\n"), _transportConfig.parentNodeId,
								                _transportConfig.distanceGW);
							}
						}
					} else {
						TRANSPORT_DEBUG(PSTR("!TSF:MSG:FPAR INACTIVE\n"));	// find parent response received, but inactive
					}
					return;
#endif
				}
#endif
				// general
				if (type == I_PING) {
					TRANSPORT_DEBUG(PSTR("TSF:MSG:PINGED,ID=%d,HP=%d\n"), sender, _msg.getByte()); // node pinged
#if defined(MY_GATEWAY_FEATURE) && (F_CPU>16000000)
					// delay for fast GW and slow nodes
					delay(5);
#endif
					(void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL,
					                                  I_PONG).set((uint8_t)1));
					return; // no further processing required
				}
				if (type == I_PONG) {
					if (_transportSM.pingActive) {
						_transportSM.pingActive = false;
						_transportSM.pingResponse = _msg.getByte();
						TRANSPORT_DEBUG(PSTR("TSF:MSG:PONG RECV,HP=%d\n"), _transportSM.pingResponse); // pong received
					} else {
						TRANSPORT_DEBUG(PSTR("!TSF:MSG:PONG RECV,INACTIVE\n")); // pong received, but !pingActive
					}
					return; // no further processing required
				}
				if (_processInternalMessages()) {
					return; // no further processing required
				}
			} else if (command == C_STREAM) {
#if defined(MY_OTA_FIRMWARE_FEATURE)
				if(firmwareOTAUpdateProcess()) {
					return; // OTA FW update processing indicated no further action needed
				}
#endif
			}
		} else {
			TRANSPORT_DEBUG(
			    PSTR("TSF:MSG:ACK\n")); // received message is ACK, no internal processing, handover to msg callback
		}
#if defined(MY_GATEWAY_FEATURE)
		// Hand over message to controller
		(void)gatewayTransportSend(_msg);
#endif
		// Call incoming message callback if available
		if (receive) {
			receive(_msg);
		}
	} else if (destination == BROADCAST_ADDRESS) {
		TRANSPORT_DEBUG(PSTR("TSF:MSG:BC\n"));	// broadcast msg
		if (command == C_INTERNAL) {
			if (isTransportReady()) {
				// only reply if node is fully operational
				if (type == I_FIND_PARENT_REQUEST) {
#if defined(MY_REPEATER_FEATURE)
					if (sender != _transportConfig.parentNodeId) {	// no circular reference
						TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR REQ,ID=%d\n"), sender);	// FPAR: find parent request
						// check if uplink functional - node can only be parent node if link to GW functional
						// this also prevents circular references in case GW ooo
						if (transportCheckUplink()) {
							_transportSM.lastUplinkCheck = hwMillis();
							TRANSPORT_DEBUG(PSTR("TSF:MSG:GWL OK\n")); // GW uplink ok
							// random delay minimizes collisions
							delay(hwMillis() & 0x3ff);
							(void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL,
							                                  I_FIND_PARENT_RESPONSE).set(_transportConfig.distanceGW));
						} else {
							TRANSPORT_DEBUG(PSTR("!TSF:MSG:GWL FAIL\n")); // GW uplink fail, do not respond to parent request
						}
					}
#endif
					return; // no further processing required, do not forward
				}
			} // isTransportReady
			if (type == I_FIND_PARENT_RESPONSE) {
				return;	// no further processing required, do not forward
			}
#if !defined(MY_GATEWAY_FEATURE)
			if (type == I_DISCOVER_REQUEST) {
				if (last == _transportConfig.parentNodeId) {
					// random wait to minimize collisions
					delay(hwMillis() & 0x3ff);
					(void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL,
					                                  I_DISCOVER_RESPONSE).set(_transportConfig.parentNodeId));
					// no return here (for fwd if repeater)
				}
			}
#endif
		}
		// controlled BC relay
#if defined(MY_REPEATER_FEATURE)
		// controlled BC repeating: forward only if message received from parent and sender not self to prevent circular fwds
		if(last == _transportConfig.parentNodeId && sender != _transportConfig.nodeId &&
		        isTransportReady()) {
			TRANSPORT_DEBUG(PSTR("TSF:MSG:FWD BC MSG\n")); // controlled broadcast msg forwarding
			(void)transportRouteMessage(_msg);
		}
#endif

		// Callback for BC, only for non-internal messages
		if (command != C_INTERNAL) {
#if !defined(MY_GATEWAY_FEATURE)
			// only proceed if message received from parent
			if (last != _transportConfig.parentNodeId) {
				return;
			}
#endif
#if defined(MY_GATEWAY_FEATURE)
			// Hand over message to controller
			(void)gatewayTransportSend(_msg);
#endif
			if (receive) {
				receive(_msg);
			}
		}

	} else {
		// msg not to us and not BC, relay msg
#if defined(MY_REPEATER_FEATURE)
		if (isTransportReady()) {
			TRANSPORT_DEBUG(PSTR("TSF:MSG:REL MSG\n"));	// relay msg
			if (command == C_INTERNAL) {
				if (type == I_PING || type == I_PONG) {
					uint8_t hopsCnt = _msg.getByte();
					if (hopsCnt != MAX_HOPS) {
						TRANSPORT_DEBUG(PSTR("TSF:MSG:REL PxNG,HP=%d\n"), hopsCnt);
						hopsCnt++;
						_msg.set(hopsCnt);
					}
				}
			}
			// Relay this message to another node
			(void)transportRouteMessage(_msg);
		}
#else
		TRANSPORT_DEBUG(PSTR("!TSF:MSG:REL MSG,NREP\n"));	// message relaying request, but not a repeater
#endif
	}
}

void transportInvokeSanityCheck(void)
{
	if (!transportSanityCheck()) {
		TRANSPORT_DEBUG(PSTR("!TSF:SAN:FAIL\n"));	// sanity check fail
		transportSwitchSM(stFailure);
	} else {
		TRANSPORT_DEBUG(PSTR("TSF:SAN:OK\n"));		// sanity check ok
	}
}

void transportProcessFIFO(void)
{
	if (!_transportSM.transportActive) {
		// transport not active, no further processing required
		return;
	}

#if defined(MY_TRANSPORT_SANITY_CHECK)
	if (hwMillis() - _lastSanityCheck > MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS) {
		_lastSanityCheck = hwMillis();
		transportInvokeSanityCheck();
	}
#endif

	uint8_t _processedMessages = MAX_SUBSEQ_MSGS;
	// process all msgs in FIFO or counter exit
	while (transportAvailable() && _processedMessages--) {
		transportProcessMessage();
	}
#if defined(MY_OTA_FIRMWARE_FEATURE)
	if (isTransportReady()) {
		// only process if transport ok
		firmwareOTAUpdateRequest();
	}
#endif
}

bool transportSendWrite(const uint8_t to, MyMessage &message)
{
	message.last = _transportConfig.nodeId; // Update last
	// sign message if required
	if (!signerSignMsg(message)) {
		TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN FAIL\n"));
		setIndication(INDICATION_ERR_SIGN);
		return false;
	}

	// msg length changes if signed
	const uint8_t totalMsgLength = HEADER_SIZE + ( mGetSigned(message) ? MAX_PAYLOAD : mGetLength(
	                                   message) );

	// send
	setIndication(INDICATION_TX);
	bool result = transportSend(to, &message, min((uint8_t)MAX_MESSAGE_LENGTH, totalMsgLength));
	// broadcasting (workaround counterfeits)
	result |= (to == BROADCAST_ADDRESS);

	TRANSPORT_DEBUG(PSTR("%sTSF:MSG:SEND,%d-%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,ft=%d,st=%s:%s\n"),
	                (result ? "" : "!"), message.sender, message.last, to, message.destination, message.sensor,
	                mGetCommand(message), message.type,
	                mGetPayloadType(message), mGetLength(message), mGetSigned(message),
	                _transportSM.failedUplinkTransmissions, (result ? "OK" : "NACK"), message.getString(_convBuf));

	return result;
}
// Helper to calculate signature of msg (returned in hmac)
static void signerCalculateSignature(MyMessage &msg) {
	memset(_signing_temp_message, 0, 32);
	memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
	DEBUG_SIGNING_PRINTBUF(F("Message to process: "), (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
	DEBUG_SIGNING_PRINTBUF(F("Current nonce: "), _signing_current_nonce, 32);

	// ATSHA204 calculates the HMAC with a PSK and a SHA256 digest of the following data:
	// 32 bytes zeroes
	// 32 bytes digest,
	// 1 byte OPCODE (0x11)
	// 1 byte Mode (0x04)
	// 2 bytes SlotID (0x0000)
	// 11 bytes zeroes
	// SN[8] (0xEE)
	// 4 bytes zeroes
	// SN[0:1] (0x0123)
	// 2 bytes zeroes

	// The digest is calculated as a SHA256 digest of the following:
	// 32 bytes message
	// 1 byte OPCODE (0x15)
	// 1 byte param1 (0x02)
	// 2 bytes param2 (0x0800)
	// SN[8] (0xEE)
	// SN[0:1] (0x0123)
	// 25 bytes zeroes
	// 32 bytes nonce

	// Calculate message digest first
	_signing_sha256.init();
	for (int i=0; i<32; i++) _signing_sha256.write(_signing_temp_message[i]);
	_signing_sha256.write(0x15); // OPCODE
	_signing_sha256.write(0x02); // param1
	_signing_sha256.write(0x08); // param2(1)
	_signing_sha256.write(0x00); // param2(2)
	_signing_sha256.write(0xEE); // SN[8]
	_signing_sha256.write(0x01); // SN[0]
	_signing_sha256.write(0x23); // SN[1]
	for (int i=0; i<25; i++) _signing_sha256.write(0x00);
	for (int i=0; i<32; i++) _signing_sha256.write(_signing_current_nonce[i]);
	// Purge nonce when used
	memset(_signing_current_nonce, 0xAA, 32);
	memcpy(_signing_temp_message, _signing_sha256.result(), 32);

	// Feed "message" to HMAC calculator
	_signing_sha256.initHmac(_signing_hmac_key,32); // Set the key to use
	for (int i=0; i<32; i++) _signing_sha256.write(0x00); // 32 bytes zeroes
	for (int i=0; i<32; i++) _signing_sha256.write(_signing_temp_message[i]); // 32 bytes digest
	_signing_sha256.write(0x11); // OPCODE
	_signing_sha256.write(0x04); // Mode
	_signing_sha256.write(0x00); // SlotID(1)
	_signing_sha256.write(0x00); // SlotID(2)
	for (int i=0; i<11; i++) _signing_sha256.write(0x00); // 11 bytes zeroes
	_signing_sha256.write(0xEE); // SN[8]
	for (int i=0; i<4; i++) _signing_sha256.write(0x00); // 4 bytes zeroes
	_signing_sha256.write(0x01); // SN[0]
	_signing_sha256.write(0x23); // SN[1]
	for (int i=0; i<2; i++) _signing_sha256.write(0x00); // 2 bytes zeroes

	memcpy(_signing_hmac, _signing_sha256.resultHmac(), 32);

	DEBUG_SIGNING_PRINTBUF(F("HMAC: "), _signing_hmac, 32);
}