Security & Signing

created by Anticimex , updated by petewill

The signing driver provides a generic API for various signing backends to offer signing of MySensors messages.

Signing support created by Patrick "Anticimex" Fallberg

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 authenticity. 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 replay attack. Authenticity permits Bob to determine if Alice is the true sender of a message.

alicenfriends.png

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 integrity 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. Mallory would in this case be a man-in-the-middle attacker. Integrity permits Bob to verify that the messages received from Alice has not been tampered with. This is achieved by adding a signature to the message, which Bob can inspect to validate that Alice is the author.

alicenfriends2.png

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, replay attacks would be very hard to prevent. One way of doing this is to increment some counter on the sender side and include it in the signature. This is however predictable. 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. 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 nonce or salt.

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 pre-shared key (PSK).

If Eve or Mallory are really sophisticated, he/she might use a delayed replay attack. 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 here. 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:

alicenbob.png

The benefits for MySensors to support this are obvious. Nobody wants others to be able to control or manipulate any actuators in their home.

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 not address these issues. Encryption is needed to prevent this. 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 integrity 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 integrity and authenticity while at the same time keeping the current message routing mechanisms intact and therefore leave the matter of secrecy to be implemented in the “physical” transport layer. With the integrity and authenticity 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. 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. 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. This is however not covered by this implementation.

How this is done

There exist many forms of message signature solutions to combat Eve and Mallory. 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. A common scheme for message signing (authenticity and integrity) is implemented using HMAC which in combination with a strong hash function provides a very strong level of protection. The Atmel ATSHA204A 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 cryptocurrency would immediately be rendered worthless. The ATSHA device also contain a random number generator (RNG) which enables the generation of a good nonce, as in, non-predictable. 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 Arduino pseudo-random generator) 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.

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 the additional security layer signing provides, you pick the backend of your choice in your sketch. Currently, two compatible backends are supported; MY_SIGNING_ATSHA204 (hardware backed) and MY_SIGNING_SOFT (software backed). You can either enable these globally in MyConfig.h or in your sketch for sketch specific/local use.

Firstly, you need to make sure to pick a backend to use as described above.

//#define MY_SIGNING_SOFT
#define MY_SIGNING_ATSHA204

#include <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.

You also need to decide if the node (or gateway) in question require and verify signatures in addition to calculating them. 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. Just set the flag MY_SIGNING_REQUEST_SIGNATURES and the node will inform the gateway that it expects the gateway to sign all messages sent to the node. If this is set in a gateway, it will NOT force all nodes to sign messages to it. It will only require signatures from nodes that in turn require signatures. 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 signerPresentation(). A node can have three "states" with respect to signing:

  1. Node does not support signing in any way (neither MY_SIGNING_ATSHA204 nor MY_SIGNING_SOFT is set)
  2. Node does support signing but don't require messages sent to it to be signed (MY_SIGNING_REQUEST_SIGNATURES is not set)
  3. Node does support signing and require messages sent to it to be signed (MY_SIGNING_REQUEST_SIGNATURES is set)

Secondly, you need to verify the configuration for the backend. 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 match your personal build. The setting is defined using MY_SIGNING_ATSHA204_PIN and the default is to use pin A3. Similar to picking your backend, this can also be set in your sketch:

#define MY_SIGNING_ATSHA204
#define MY_SIGNING_ATSHA204_PIN 4
#define MY_SIGNING_REQUEST_SIGNATURES

#include <MySensors.h>

For the software backed signingbackend, an unconnected analog pin is required to set a random seed for the pseudo-random generator. 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 MY_SIGNING_SOFT_RANDOMSEED_PIN and the default is to use pin A7. The same configuration possibilities exist as with the other configuration options.

Thirdly, if you use the software backend, you need to personalize the node (see personalization).

#define MY_SIGNING_SOFT
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
#define MY_SIGNING_REQUEST_SIGNATURES

#include <MySensors.h>

An example of a node that require signatures is available in SecureActuator.ino.

If you use the “real” ATSHA204A, before any signing operations can be done, the device needs to be personalized. 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 SecurityPersonalizer.ino Note that you also need to do personalization for software signing, but then the values are stored in EEPROM (described below).

The process of personalizing the ATSHA204A involves

  • Writing and locking chip configuration
  • (optionally) Generating and (mandatory) writing HMAC key
  • (optionally) Locking data sections

First execute the sketch without modifications to make sure communications with the device can be established. It is highly recommended that the first time this is done, a device with serial debug possibilities is used. When this has been confirmed, it is time to decide what type of personalization is desired. There are a few options here. Firstly, enable LOCK_CONFIGURATION to allow the sketch to lock the chip configuration. The sketch will write the default settings to the chip because these are fine for our purposes. This also enables the RNG which is required to allow the sketch to automatically generate a PSK if this is desired. Now it is possible to execute the sketch to lock the configuration and enable the RNG. Next step is to decide if a new key should be generated or an existing key should be stored to the device. This is determined using USER_KEY, which, if defined, will use the data in the variable user_key_data. If USER_KEY is disabled, the RNG will be used to generate a key. This key obviously need to be made available to you so you can use it in other devices in the network, and this key is therefore also printed on the serial console when it has been generated. The key (generated or provided) will be written to the device unless SKIP_KEY_STORAGE is set. As long as the data zone is kept unlocked the key can be replaced at any time. However, Atmel suggests the data region to be locked for maximum security. On the other hand, they also claim that the key is not readable from the device even if the data zone remains unlocked so the need for locking the data region is optional for MySensors usage. For devices that does not have serial debug possibilities, it is possible to set SKIP_UART_CONFIRMATION, but it is required to set USER_KEY if this option is enabled since a generated and potentially unknown key could be written to the device and thus rendering it useless (if the data zone is also locked). For devices with serial debug possibilities it is recommended to not use SKIP_UART_CONFIRMATION as the sketch without that setting will ask user to send a ‘space’ character on the serial terminal before any locking operations are executed as an additional confirmation that this irreversible operation is done. However, if a number of nodes are to undergo personalization, this option can be enabled to streamline the personalization. This is a condensed description of settings to fully personalize and lock down a set of sensors (and gateways):

  • Pick a “master” device with serial debug port.
  • Set the following sketch configuration of the personalizer:
    1. Enable LOCK_CONFIGURATION
    2. Disable LOCK_DATA
    3. Enable SKIP_KEY_STORAGE
    4. Disable SKIP_UART_CONFIRMATION
    5. Disable USER_KEY
  • Execute the sketch on the “master” device to obtain a randomized key. Save this key to a secure location and keep it confidential so that you can retrieve it if you need to personalize more devices later on.
  • Now reconfigure the sketch with these settings:
    1. Enable LOCK_CONFIGURATION
    2. Enable LOCK_DATA (if you are sure you do not need to replace/revoke the key, this is the most secure option to protect from key readout according to Atmel, but they also claim that key is not readable even if data region remains unlocked from the slot we are using)
    3. Disable SKIP_KEY_STORAGE
    4. Enable SKIP_UART_CONFIRMATION
    5. Enable USER_KEY
  • Put the saved key in the user_key_data variable.
  • Now execute the sketch on all devices you want to personalize with this secret key.

For software signing the personalization procedure looks slightly different.

The personalizer also takes care of generation and storage of the AES key used for RF traffic encryption. See the software signing personalization flow for how to manage it.

That’s it. Personalization is done and the device is ready to execute signing operations which are valid only on your personal network.

If a node does require signing, any unsigned message sent to the node will be rejected. This also applies to the gateway. However, the difference is that the gateway will only require signed messages from nodes it knows in turn require signed messages. A node can also inform a different node that it expects to receive signed messages from it. This is done by transmitting an internal message of type I_SIGNING_PRESENTATION and provide flags as payload that inform the receiver of the signing preferences of the sender. All nodes and gateways in a network maintain a table where the signing preferences of all nodes are stored. This is also stored in EEPROM so if the gateway reboots, the nodes does not have to retransmit a signing presentation to the gateway for the gateway to realize that the node expect signed messages. Also, the nodes that do not require signed messages will also inform gateway of this, so if you reprogram a node to stop require signing, the gateway will adhere to this as soon as the new node has presented itself to the gateway.

The following sequence diagram illustrate how messages are passed in a MySensors network with respect to signing:

signingsequence.png

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 in your sketch. Depending on chosen backend, do personalization or key configurations and set MY_SIGNING_REQUEST_SIGNATURES on the node that 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, Eve can eavesdrop, but not do anything about, your messages).

Wiring

Wiring is not complicated and although we recommend using a 1k pullup resistor some people have found signing will work without it. enter image description here

Note: if you change the default pin (A3) make sure you use a pin that supports input/output (ex. A6 & A7 on a Pro Mini are input only pins).

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:

  • You have a node inside your garage (therefore secure and out of reach from prying eyes) that controls your garage door motor. 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 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 will not accept them.

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 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 not wanting to replace the PSK. 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 remove it from the nodes whitelist, and it will no longer be able to communicate signed messages to the door node.

In case you want to be able to "whitelist" trusted nodes (in order to be able to revoke them in case they are lost) you also need to take note of the serial number of the ATSHA device 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. The whitelist is stored on the node that require signatures. When a received message is verified, the serial of the sender is looked up in a list stored on the receiving node, and the corresponding serial stored in the list for that sender is then included in the signature verification process. The list is stored as the value of the flag that enables whitelisting, MY_SIGNING_NODE_WHITELISTING.

Whitelisting is achieved by 'salting' the signature with some node-unique information known to the receiver. In the case of ATSHA204A this is the unique serial number programmed into the circuit. This unique number is never transmitted over the air in clear text, so Eve will not be able to figure out a "trusted" serial by snooping on the traffic. 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.

Whitelisting is an optional alternative because it adds some code and configuration options which might not be desirable for every user. So if you want the ability to use whitelists, you need to enable MY_SIGNING_NODE_WHITELISTING. The whitelist is provided as value of the flag that enable it as follows (example is a node that require signing as well):

#define MY_SIGNING_ATSHA204
#define MY_SIGNING_REQUEST_SIGNATURES
#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}},{.nodeId = 2,.serial = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09}}}

#include <MySensors.h>

In this example, there are two nodes in the whitelist; the gateway, and a separate node that communicates directly with this node (with signed messages). 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:

#define MY_SIGNING_SOFT
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
#define MY_SIGNING_REQUEST_SIGNATURES
#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}},{.nodeId = 2,.serial = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09}}}

#include <MySensors.h>

For a node that should transmit whitelisted messages but not receive whitelisted messages, you do not need any special configurations:

#define MY_SIGNING_SOFT
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7

Remember that you always need to select a signing backend for all nodes that communicate to a node that require whitelisting. Also, note that a node that use whitelisting will not accept messages from nodes that are not present in it's whitelist. And you have to personalize all nodes that use signing with a common HMAC key but different serial numbers (ATSHA204A always has unique serials).

The technical stuff

How are the messages actually affected by the signing? The following illustration shows what part of the message is signed, and where the signature is stored:

signingillustrated1.png

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:

signingillustrated2.png

Exactly how this is done can be reviewd in the source for the ATSHA204SOFT backend and the ATSHA204A datasheet. In the MySensors protocol, the following internal messagetypes handles signature requirements and nonce requests: I_SIGNING_PRESENTATION I_NONCE_REQUEST I_NONCE_RESPONSE

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.

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 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. 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.

Typical use cases

"Securely located" in this context mean a node which is not physically publicly accessible. Typically at least your gateway. "Public" in this context mean a node that is located outside your "trusted environment". This includes sensors located outdoors, keyfobs etc.

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 dungeon or similar). 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 to replace at least the signing circuit in case you need to revoke the PSK because some other node in your network gets compromised. You need to make your node require signed messages but you do not necessarily need to make your gateway require signed messsages (unless you are concerned that someone might spoof the lock status of your lock). Configuration example for the secure lock node:

#define MY_SIGNING_ATSHA204
#define MY_SIGNING_REQUEST_SIGNATURES

#include <MySensors.h>

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 in the gateway.

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 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 to steal your patio motion sensor, they could rewrite the firmware and spoof your gateway to use it to transmit a correctly signed message 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. Nor can it be changed in order to do it in the future. You can also use whitelisting to revoke your lost node. 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. Configuration example for a motion sensor:

#define MY_SIGNING_SOFT
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
#define MY_SIGNING_REQUEST_SIGNATURES

#include <MySensors.h>

The gateway needs to configured with a whitelist (and it have to have an entry for all nodes that send and/or require signed messages):

#define MY_SIGNING_SOFT
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
#define MY_SIGNING_REQUEST_SIGNATURES
#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = MOTION_SENSOR_ID,.serial = {0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78}}}

#include <MySensors.h>

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 (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, thus rendering the keyfob useless without having to replace your nodes. You can also use whitelisting to revoke your lost keyfob. Configuration example for the keyfob (keyfob will only transmit to another node and not receive anything):

#define MY_SIGNING_ATSHA204

#include <MySensors.h>

Configuration example for the door controller node (should require signing from anyone who wants to control it):

#define MY_SIGNING_SOFT
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
#define MY_SIGNING_REQUEST_SIGNATURES
#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}},{.nodeId = KEYFOB_ID,.serial = {<FROM ATSHA ON KEYFOB>}}}

#include <MySensors.h>

Relevant sketches

/mysensors/MySensors/examples/SecureActuator/SecureActuator.ino
Last updated by henrikekblad, 25 Dec 2016, "Refactor signing backend (#690)"

/*
 * The MySensors Arduino library handles the wireless radio link and protocol
 * between your home built sensors/actuators and HA controller of choice.
 * The sensors forms a self healing radio network with optional repeaters. Each
 * repeater and gateway builds a routing tables in EEPROM which keeps track of the
 * network topology allowing messages to be routed to nodes.
 *
 * Created by Henrik Ekblad <[email protected]>
 * Copyright (C) 2013-2015 Sensnology AB
 * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
 *
 * Documentation: http://www.mysensors.org
 * Support Forum: http://forum.mysensors.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 *******************************
 */
/**
 * @ingroup MySigninggrp
 * @{
 * @file SecureActuator.ino
 * @brief Example sketch showing how to securely control locks.
 *
 * This example will remember lock state even after power failure.
 *
 * REVISION HISTORY
 *  - See git log (git log libraries/MySensors/examples/SecureActuator/SecureActuator.ino)
 */

/**
 * @example SecureActuator.ino
 * This example implements a secure actuator in the form of a IO controlled electrical lock.<br>
 * Multiple locks are supported as long as they are on subsequent IO pin indices. The first lock pin
 * is defined by @ref LOCK_1. The number of locks is controlled by @ref NOF_LOCKS .<br>
 * The sketch will require incoming messages to be signed and the use of signing backend is selected
 * by @ref MY_SIGNING_ATSHA204 or @ref MY_SIGNING_SOFT. Hard or soft ATSHA204 signing is supported.<br>
 * Whitelisting can be enabled through @ref MY_SIGNING_NODE_WHITELISTING in which case a single entry
 * is provided in this example which typically should map to the gateway of the network.
 */

#define MY_DEBUG //!< Enable debug prints to serial monitor
#define MY_DEBUG_VERBOSE_SIGNING //!< Enable signing related debug prints to serial monitor
#define MY_NODE_LOCK_FEATURE //!< Enable lockdown of node if suspicious activity is detected

// Enable and select radio type attached
#define MY_RADIO_NRF24 //!< NRF24L01 radio driver
//#define MY_RADIO_RFM69 //!< RFM69 radio driver

// Select soft/hardware signing method
#define MY_SIGNING_SOFT //!< Software signing
//#define MY_SIGNING_ATSHA204 //!< Hardware signing using ATSHA204A

// Enable node whitelisting
//#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}}
// Enable this if you want destination node to sign all messages sent to this node.
#define MY_SIGNING_REQUEST_SIGNATURES


// SETTINGS FOR MY_SIGNING_SOFT
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 //!< Unconnected analog pin for random seed

// SETTINGS FOR MY_SIGNING_ATSHA204
#define MY_SIGNING_ATSHA204_PIN 17 //!< A3 - pin where ATSHA204 is attached

#include <MySensors.h>


#define LOCK_1  3     //!< Arduino Digital I/O pin number for first lock (second on pin+1 etc)
#define NOF_LOCKS 1   //!< Total number of attached locks
#define LOCK_LOCK 1   //!< GPIO value to write to lock attached lock
#define LOCK_UNLOCK 0 //!< GPIO value to write to unlock attached lock

void setup()
{
    for (int lock=1, pin=LOCK_1; lock<=NOF_LOCKS; lock++, pin++) {
        // Set lock pins in output mode
        pinMode(pin, OUTPUT);
        // Set lock to last known state (using eeprom storage)
        digitalWrite(pin, loadState(lock)?LOCK_LOCK:LOCK_UNLOCK);
    }
}

void presentation()
{
    // Send the sketch version information to the gateway and Controller
    sendSketchInfo("Secure Lock", "1.0");

    // Fetch lock status
    for (int lock=1, pin=LOCK_1; lock<=NOF_LOCKS; lock++, pin++) {
        // Register all locks to gw (they will be created as child devices)
        present(lock, S_LOCK, "SecureActuator", false);
    }
}

/** @brief Sketch execution code */
void loop()
{
}

/**
 * @brief Incoming message handler
 *
 * @param message The message to handle.
 */
void receive(const MyMessage &message)
{
    // We only expect one type of message from controller. But we better check anyway.
    // And acks are not accepted as control messages
    if (message.type==V_LOCK_STATUS && message.sensor<=NOF_LOCKS && !mGetAck(message)) {
        // Change relay state
        digitalWrite(message.sensor-1+LOCK_1, message.getBool()?LOCK_LOCK:LOCK_UNLOCK);
        // Store state in eeprom
        saveState(message.sensor, message.getBool());
        // Write some debug info
        Serial.print("Incoming change for lock:");
        Serial.print(message.sensor);
        Serial.print(", New status: ");
        Serial.println(message.getBool());
    }
}
/** @}*/

/mysensors/MySensors/examples/SecurityPersonalizer/SecurityPersonalizer.ino
Last updated by web-flow, 2 Apr 2017, "Security personalizer rewritten (#794)"

/*
 * The MySensors Arduino library handles the wireless radio link and protocol
 * between your home built sensors/actuators and HA controller of choice.
 * The sensors forms a self healing radio network with optional repeaters. Each
 * repeater and gateway builds a routing tables in EEPROM which keeps track of the
 * network topology allowing messages to be routed to nodes.
 *
 * Created by Henrik Ekblad <[email protected]>
 * Copyright (C) 2013-2015 Sensnology AB
 * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
 *
 * Documentation: http://www.mysensors.org
 * Support Forum: http://forum.mysensors.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 *******************************
 */
/**
 * @ingroup MySigninggrp
 * @{
 * @file SecurityPersonalizer.ino
 * @brief Security personalization sketch
 *
 * REVISION HISTORY
 *  - See git log (git log libraries/MySensors/examples/SecurityPersonalizer/SecurityPersonalizer.ino)
 */

/**
 * @example SecurityPersonalizer.ino
 * This sketch will personalize either none-volatile memory or ATSHA204A for security functions
 * available in the MySensors library.<br>
 * Details on personalization procedure is given in @ref personalization.<br>
 * This sketch will when executed without modifications also print a guided workflow on the UART.
 */

#include "sha204_library.h"
#include "sha204_lib_return_codes.h"
/** @brief Make use of the MySensors framework without invoking the entire system */
#define MY_CORE_ONLY
#include <MySensors.h>

/************************************ User defined key data ***************************************/

/** @brief The user-defined HMAC key to use unless @ref GENERATE_HMAC_KEY is set */
#define MY_HMAC_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00

/** @brief The user-defined AES key to store in EEPROM unless @ref GENERATE_AES_KEY is set */
#define MY_AES_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00

/** @brief The user-defined soft serial to use for soft signing unless @ref GENERATE_SOFT_SERIAL is set */
#define MY_SOFT_SERIAL 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF

/***************************** Flags for guided personalization flow ******************************/

/**
 * @def GENERATE_KEYS_ATSHA204A
 * @brief Default settings for generating keys using ATSHA204A
 *
 * @note The generated keys displayed in the serial log with this setting needs to be written down
 *       and transferred to all nodes this gateway will communicate with. This is mandatory for ALL
 *       nodes for encryption (AES key). For signing (HMAC key) it is only required for nodes that
 *       use signing. Typically you set the values for @ref MY_HMAC_KEY and @ref MY_AES_KEY.
 */
//#define GENERATE_KEYS_ATSHA204A

/**
 * @def GENERATE_KEYS_SOFT
 * @brief Default settings for generating keys using software
 *
 * @b Important<br>
 * You will need to ensure @ref MY_SIGNING_SOFT_RANDOMSEED_PIN is set to an unconnected analog pin
 * in order to provide entropy to the software RNG.
 *
 * @note The generated keys displayed in the serial log with this setting needs to be written down
 *       and transferred to all nodes this gateway will communicate with. This is mandatory for ALL
 *       nodes for encryption (AES key). For signing (HMAC key) it is only required for nodes that
 *       use signing. Typically you set the values for @ref MY_HMAC_KEY and @ref MY_AES_KEY.
 */
//#define GENERATE_KEYS_SOFT

/**
 * @def PERSONALIZE_ATSHA204A
 * @brief Default settings for personalizing an ATSHA204A
 *
 * It is assumed that you have updated @ref MY_HMAC_KEY and @ref MY_AES_KEY with the keys displayed
 * when executing this sketch with @ref GENERATE_KEYS_ATSHA204A or @ref GENERATE_KEYS_SOFT defined.
 */
//#define PERSONALIZE_ATSHA204A

/**
 * @def PERSONALIZE_SOFT
 * @brief Default settings for personalizing EEPROM for software signing
 *
 * It is assumed that you have updated @ref MY_HMAC_KEY and @ref MY_AES_KEY with the keys displayed
 * when executing this sketch with @ref GENERATE_KEYS_ATSHA204A or @ref GENERATE_KEYS_SOFT defined.
 */
//#define PERSONALIZE_SOFT

/**
 * @def PERSONALIZE_SOFT_RANDOM_SERIAL
 * @brief This is an alternative to @ref PERSONALIZE_SOFT which will also store a randomly generated
 * serial to EEPROM in addition to the actions performed by @ref PERSONALIZE_SOFT. Take note of the
 * generated soft serial as it will be needed if you plan to use whitelisting. It should be
 * unique for each node.
 *
 * @note This is only needed for targets that lack unique device IDs. The sketch will inform you if
 *       there is a need for generating a random serial or not. Check the "Hardware security
 *       peripherals" listing. If a target has a unique device ID and a serial in EEPROM, the serial
 *       in EEPROM will be used. If erased (replaced with FF:es) the unique device ID will be used
 *       instead.
 */
//#define PERSONALIZE_SOFT_RANDOM_SERIAL

/*************************** The settings below are for advanced users ****************************/
/**
 * @def USE_SOFT_SIGNING
 * @brief Uncomment this to generate keys by software and store them to EEPROM instead of ATSHA204A
 */
//#define USE_SOFT_SIGNING

/**
 * @def LOCK_ATSHA204A_CONFIGURATION
 * @brief Uncomment this to enable locking the ATSHA204A configuration zone
 *
 * It is still possible to change the key, and this also enable random key generation.
 * @warning BE AWARE THAT THIS PREVENTS ANY FUTURE CONFIGURATION CHANGE TO THE CHIP
 */
//#define LOCK_ATSHA204A_CONFIGURATION

/**
 * @def SKIP_UART_CONFIRMATION
 * @brief Uncomment this for boards that lack UART
 *
 * This will disable additional confirmation for actions that are non-reversible.
 *
 * @b Important<br> For ATSHA204A, no confirmation will be required for locking any zones with this
 * configuration! Also, if you generate keys on a board without UART, you have no way of determining
 * what the key is unless it is stored in EEPROM.
 */
//#define SKIP_UART_CONFIRMATION

/**
 * @def GENERATE_HMAC_KEY
 * @brief Uncomment this to generate a random HMAC key using ATSHA204A or software depending on
 *        @ref USE_SOFT_SIGNING
 * @note If not enabled, key defined by @ref MY_HMAC_KEY will be used instead.
 */
//#define GENERATE_HMAC_KEY

/**
 * @def STORE_HMAC_KEY
 * @brief Uncomment this to store HMAC key to ATSHA204A or EEPROM depending on @ref USE_SOFT_SIGNING
 */
//#define STORE_HMAC_KEY

/**
 * @def GENERATE_AES_KEY
 * @brief Uncomment this to generate a random AES key using ATSHA204A or software depending on
 * @ref USE_SOFT_SIGNING
 * @note If not enabled, key defined by @ref MY_AES_KEY will be used instead.
 */
//#define GENERATE_AES_KEY

/**
 * @def STORE_AES_KEY
 * @brief Uncomment this to store AES key to EEPROM
 */
//#define STORE_AES_KEY

/**
 * @def GENERATE_SOFT_SERIAL
 * @brief Uncomment this to generate a random serial number for software signing
 * @note If not enabled, serial defined by @ref MY_SOFT_SERIAL will be used instead.
 */
//#define GENERATE_SOFT_SERIAL

/**
 * @def STORE_SOFT_SERIAL
 * @brief Uncomment this to store the serial number to EEPROM
 */
//#define STORE_SOFT_SERIAL

/**
 * @def PRINT_DETAILED_ATSHA204A_CONFIG
 * @brief Uncomment to print the detailed ATSHA204A configuration
 */
//#define PRINT_DETAILED_ATSHA204A_CONFIG

/**
 * @def RESET_EEPROM_PERSONALIZATION
 * @brief Uncomment to reset the personalization data in EEPROM to 0xFF:es
 */
//#define RESET_EEPROM_PERSONALIZATION

/********************* Guided mode flag configurations (don't change these) ***********************/
#ifdef GENERATE_KEYS_ATSHA204A
#define LOCK_ATSHA204A_CONFIGURATION // We have to lock configuration to enable random number generation
#define GENERATE_HMAC_KEY // Generate random HMAC key
#define GENERATE_AES_KEY // Generate random AES key
#define SKIP_UART_CONFIRMATION // This is an automated mode
#endif

#ifdef GENERATE_KEYS_SOFT
#define USE_SOFT_SIGNING // Use software backend
#define GENERATE_HMAC_KEY // Generate random HMAC key
#define GENERATE_AES_KEY // Generate random AES key
#define SKIP_UART_CONFIRMATION // This is an automated mode
#endif

#ifdef PERSONALIZE_ATSHA204A
#define LOCK_ATSHA204A_CONFIGURATION // We have to lock configuration to enable random number generation
#define STORE_HMAC_KEY // Store the HMAC key
#define STORE_AES_KEY // Store the AES key
#define SKIP_UART_CONFIRMATION // This is an automated mode
#endif

#ifdef PERSONALIZE_SOFT_RANDOM_SERIAL
#define GENERATE_SOFT_SERIAL // Generate a soft serial number
#define PERSONALIZE_SOFT // Do the rest as PERSONALIZE_SOFT
#endif

#ifdef PERSONALIZE_SOFT
#define USE_SOFT_SIGNING // Use software backend
#define STORE_HMAC_KEY // Store the HMAC key
#define STORE_AES_KEY // Store the AES key
#define STORE_SOFT_SERIAL // Store the soft serial number
#define SKIP_UART_CONFIRMATION // This is an automated mode
#endif

#if defined(GENERATE_HMAC_KEY) || defined(GENERATE_AES_KEY) || defined(GENERATE_SOFT_SERIAL)
#define GENERATE_SOMETHING
#endif

/********************************** Preprocessor sanitychecks *************************************/
#if defined(GENERATE_SOFT_SERIAL) && !defined(USE_SOFT_SIGNING)
#error Cannot generate soft serial using ATSHA204A, use USE_SOFT_SINGING for this
#endif
#if defined(STORE_SOFT_SERIAL) && !defined(USE_SOFT_SIGNING)
#error Cannot store soft serial to ATSHA204A, use USE_SOFT_SINGING for this
#endif
#if defined(PRINT_DETAILED_ATSHA204A_CONFIG) && defined(USE_SOFT_SIGNING)
#error Cannot print ATSHA204A config using software signing flag, disable USE_SOFT_SINGING for this
#endif
#if defined(LOCK_ATSHA204A_CONFIGURATION) && defined(USE_SOFT_SIGNING)
#error Cannot lock ATSHA204A config using software signing flag, disable USE_SOFT_SINGING for this
#endif
#ifdef GENERATE_KEYS_ATSHA204A
#ifdef USE_SOFT_SIGNING
#error You cannot select soft signing if you want to generate keys using ATSHA204A
#endif
#ifdef STORE_HMAC_KEY
#error Disable STORE_SOFT_KEY, you should not store keys in this mode
#endif
#ifdef STORE_SOFT_SERIAL
#error Disable STORE_SOFT_SERIAL, you should not store serial in this mode
#endif
#ifdef STORE_AES_KEY
#error Disable STORE_AES_KEY, you should not store keys in this mode
#endif
#if defined(GENERATE_KEYS_SOFT) ||\
        defined (PERSONALIZE_ATSHA204A) ||\
        defined (PERSONALIZE_SOFT) ||\
        defined (PERSONALIZE_SOFT_RANDOM_SERIAL)
#error You can not enable GENERATE_KEYS_ATSHA204A together with other guided modes
#endif
#endif // GENERATE_KEYS_ATSHA204A
#ifdef GENERATE_KEYS_SOFT
#ifdef STORE_HMAC_KEY
#error Disable STORE_SOFT_KEY, you should not store keys in this mode
#endif
#ifdef STORE_SOFT_SERIAL
#error Disable STORE_SOFT_SERIAL, you should not store serial in this mode
#endif
#ifdef STORE_AES_KEY
#error Disable STORE_AES_KEY, you should not store keys in this mode
#endif
#ifndef MY_SIGNING_SOFT_RANDOMSEED_PIN
#error You have to set MY_SIGNING_SOFT_RANDOMSEED_PIN to a suitable value in this mode
#endif
#if defined(GENERATE_KEYS_ATSHA204A) ||\
        defined (PERSONALIZE_ATSHA204A) ||\
        defined (PERSONALIZE_SOFT) ||\
        defined (PERSONALIZE_SOFT_RANDOM_SERIAL)
#error You can not enable GENERATE_KEYS_SOFT together with other guided modes
#endif
#endif // GENERATE_KEYS_SOFT
#ifdef PERSONALIZE_ATSHA204A
#ifdef USE_SOFT_SIGNING
#error You cannot select soft signing if you want to personalize an ATSHA204A
#endif
#if defined(GENERATE_KEYS_ATSHA204A) ||\
        defined (GENERATE_KEYS_SOFT) ||\
        defined (PERSONALIZE_SOFT) ||\
        defined (PERSONALIZE_SOFT_RANDOM_SERIAL)
#error You can not enable PERSONALIZE_ATSHA204A together with other guided modes
#endif
#ifdef RESET_EEPROM_PERSONALIZATION
#error You cannot reset EEPROM personalization when personalizing a device
#endif
#endif // PERSONALIZE_ATSHA204A
#ifdef PERSONALIZE_SOFT
#if defined(GENERATE_KEYS_ATSHA204A) ||\
        defined (GENERATE_KEYS_SOFT) ||\
        defined (PERSONALIZE_ATSHA204A)
#error You can not enable PERSONALIZE_SOFT together with other guided modes
#endif
#ifdef RESET_EEPROM_PERSONALIZATION
#error You cannot reset EEPROM personalization when personalizing a device
#endif
#endif // PERSONALIZE_SOFT
#ifdef PERSONALIZE_SOFT_RANDOM_SERIAL
#if defined(GENERATE_KEYS_SOFT) ||\
        defined (PERSONALIZE_ATSHA204A) ||\
        defined (GENERATE_KEYS_ATSHA204A)
#error You can only enable one of the guided modes at a time
#endif
#ifdef RESET_EEPROM_PERSONALIZATION
#error You cannot reset EEPROM personalization when personalizing a device
#endif
#endif // PERSONALIZE_SOFT_RANDOM_SERIAL

#if !defined(GENERATE_KEYS_ATSHA204A) &&\
        !defined(GENERATE_KEYS_SOFT) &&\
        !defined(PERSONALIZE_ATSHA204A) &&\
        !defined(PERSONALIZE_SOFT) &&\
        !defined(PERSONALIZE_SOFT_RANDOM_SERIAL) &&\
        !defined(USE_SOFT_SIGNING) &&\
        !defined(LOCK_ATSHA204A_CONFIGURATION) &&\
        !defined(SKIP_UART_CONFIRMATION) &&\
        !defined(GENERATE_HMAC_KEY) &&\
        !defined(STORE_HMAC_KEY) &&\
        !defined(GENERATE_SOFT_SERIAL) &&\
        !defined(STORE_SOFT_SERIAL) &&\
        !defined(GENERATE_AES_KEY) &&\
        !defined(STORE_AES_KEY) &&\
        !defined(PRINT_DETAILED_ATSHA204A_CONFIG) &&\
        !defined(RESET_EEPROM_PERSONALIZATION)
/** @brief Set when there are no config flags defined */
#define NO_SETTINGS_DEFINED
#endif

#if defined(GENERATE_KEYS_ATSHA204A) ||\
        defined(GENERATE_KEYS_SOFT) ||\
        defined(PERSONALIZE_ATSHA204A) ||\
        defined(PERSONALIZE_SOFT) ||\
        defined(PERSONALIZE_SOFT_RANDOM_SERIAL)
/** @brief Set when there is a guided mode flag defined */
#define GUIDED_MODE
#endif

/************************************* Function declarations ***************************************/
static void halt(bool success);
#ifdef GENERATE_SOMETHING
static bool generate_random_data(uint8_t* data, size_t sz);
#endif
static void generate_keys(void);
static void store_keys(void);
static void print_hex_buffer(uint8_t* data, size_t sz);
static void print_c_friendly_hex_buffer(uint8_t* data, size_t sz);
#ifdef STORE_HMAC_KEY
static bool store_hmac_key_data(uint8_t* data, size_t sz);
#endif
#ifdef STORE_AES_KEY
static bool store_aes_key_data(uint8_t* data, size_t sz);
#endif
#ifdef STORE_SOFT_SERIAL
static bool store_soft_serial_data(uint8_t* data, size_t sz);
#endif
#ifndef USE_SOFT_SIGNING
static void init_atsha204a_state(void);
#ifdef LOCK_ATSHA204A_CONFIGURATION
static void    lock_atsha204a_config(void);
static uint16_t write_atsha204a_config_and_get_crc(void);
#endif
static bool get_atsha204a_serial(uint8_t* data);
#ifdef STORE_HMAC_KEY
static bool write_atsha204a_key(uint8_t* key);
#endif
#endif // not USE_SOFT_SIGNING
static void print_greeting(void);
static void print_ending(void);
static void    probe_and_print_peripherals(void);
static void print_eeprom_data(void);
static void print_whitelisting_entry(void);
#ifdef PRINT_DETAILED_ATSHA204A_CONFIG
static void dump_detailed_atsha204a_configuration(void);
#endif
#ifdef RESET_EEPROM_PERSONALIZATION
static void reset_eeprom(void);
#endif
static void write_eeprom_checksum(void);

/**************************************** File local data *****************************************/
#if defined(GENERATE_HMAC_KEY) || defined(STORE_HMAC_KEY)
/** @brief The data to store as HAMC key in ATSHA204A or EEPROM */
static uint8_t user_hmac_key[32] = {MY_HMAC_KEY};
#endif

#if defined(GENERATE_SOFT_SERIAL) || defined(STORE_SOFT_SERIAL)
/** @brief The data to store as soft serial in EEPROM */
static uint8_t user_soft_serial[9] = {MY_SOFT_SERIAL};
#endif

#if defined(GENERATE_AES_KEY) || defined(STORE_AES_KEY)
/* @brief The data to store as AES key in EEPROM */
static uint8_t user_aes_key[16] = {MY_AES_KEY};
#endif

#ifndef USE_SOFT_SIGNING
const int sha204Pin = MY_SIGNING_ATSHA204_PIN; //!< The IO pin to use for ATSHA204A
atsha204Class sha204(sha204Pin);
static uint8_t tx_buffer[SHA204_CMD_SIZE_MAX];
static uint8_t rx_buffer[SHA204_RSP_SIZE_MAX];
static uint8_t ret_code;
static uint8_t lockConfig = 0;
static uint8_t lockValue = 0;
#endif
static bool has_device_unique_id = false;
static const uint8_t reset_buffer[32] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

/******************************************* Functions ********************************************/

/** @brief Sketch setup code (all personalization is done here as it is a run-once sketch) */
void setup()
{
    // Delay startup a bit for serial consoles to catch up
    unsigned long enter = hwMillis();
    while (hwMillis() - enter < (unsigned long)500);
#ifdef USE_SOFT_SIGNING
    // initialize pseudo-RNG
    randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN));
#endif

    Serial.begin(MY_BAUD_RATE);
    while(!Serial); // For USB enabled devices, wait for USB enumeration before continuing
    hwInit();

    print_greeting();

#ifndef USE_SOFT_SIGNING
    init_atsha204a_state();
    // Lock configuration now if requested to enable RNG in ATSHA
#ifdef LOCK_ATSHA204A_CONFIGURATION
    lock_atsha204a_config();
#endif
#endif
    // Generate the requested keys (if any)
    generate_keys();

#ifdef RESET_EEPROM_PERSONALIZATION
    // If requested, reset EEPROM before storing keys
    reset_eeprom();
#endif

    // Store the keys (if configured to do so)
    store_keys();

    // Write a checksum on the EEPROM data
    write_eeprom_checksum();

    // Print current EEPROM
    print_eeprom_data();
    print_whitelisting_entry();
    Serial.println();

    print_ending();
    halt(true);
}

/** @brief Sketch execution code (unused) */
void loop()
{
}

/** @brief Print a notice and halt the execution */
static void halt(bool success)
{
    Serial.println();
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                                  Execution result                                  |"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    if (success) {
        Serial.println(F(
                           "| SUCCESS                                                                            |"));
    } else {
        Serial.print(F(
                         "| FAILURE "));
#ifdef USE_SOFT_SIGNING
        Serial.println(F("                                                                           |"));
#else
        if (ret_code != SHA204_SUCCESS) {
            Serial.print(F("(last ATSHA204A return code: 0x"));
            if (ret_code < 0x10) {
                Serial.print('0'); // Because Serial.print does not 0-pad HEX
            }
            Serial.print(ret_code, HEX);
            Serial.println(F(")                                         |"));
        } else {
            Serial.println(F("                                                                           |"));
        }
#endif
    }
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    while(1) {
        doYield();
    }
}

#ifdef GENERATE_SOMETHING
/**
 * @brief Generate random data of specified size and store in provided buffer
 * @param data Buffer to store bytes in (buffer is assumed to be of required size)
 * @param sz Number of random bytes to generate
 * @returns false if failed
 */
static bool generate_random_data(uint8_t* data, size_t sz)
{
#ifdef USE_SOFT_SIGNING
    for (size_t i = 0; i < sz; i++) {
        data[i] = random(256) ^ micros();
        unsigned long enter = hwMillis();
        while (hwMillis() - enter < (unsigned long)2);
    }
    return true;
#else
    ret_code = sha204.sha204m_random(tx_buffer, rx_buffer, RANDOM_SEED_UPDATE);
    if ((ret_code != SHA204_SUCCESS) || (lockConfig != 0x00)) {
        return false;
    } else {
        memcpy(data, rx_buffer+SHA204_BUFFER_POS_DATA, sz);
        return true;
    }
#endif // not USE_SOFT_SIGNING
}
#endif // GENERATE_SOMETHING

/** @brief Generate keys depending on configuration */
static void generate_keys(void)
{
#ifdef GENERATE_SOMETHING
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                                   Key generation                                   |"));
    Serial.println(
        F("+--------+--------+------------------------------------------------------------------+"));
    Serial.println(
        F("| Key ID | Status | Key                                                              |"));
    Serial.println(
        F("+--------+--------+------------------------------------------------------------------+"));
#endif
#ifdef GENERATE_HMAC_KEY
    Serial.print(F("| HMAC   | "));
    if (!generate_random_data(user_hmac_key, 32)) {
        memset(user_hmac_key, 0xFF, 32);
        Serial.print(F("FAILED | "));
    } else {
        Serial.print(F("OK     | "));
    }
    print_hex_buffer(user_hmac_key, 32);
    Serial.println(F(" |"));
#endif
#ifdef GENERATE_AES_KEY
    Serial.print(F("| AES    | "));
    if (!generate_random_data(user_aes_key, 16)) {
        memset(user_aes_key, 0xFF, 16);
        Serial.print(F("FAILED | "));
    } else {
        Serial.print(F("OK     | "));
    }
    print_hex_buffer(user_aes_key, 16);
    Serial.println(F("                                 |"));
#endif
#ifdef GENERATE_SOFT_SERIAL
    Serial.print(F("| SERIAL | "));
    if (has_device_unique_id) {
        Serial.println(F("N/A    | MCU has a unique serial which will be used instead.              |"));
    } else {
        if (!generate_random_data(user_soft_serial, 9)) {
            memset(user_soft_serial, 0xFF, 9);
            Serial.print(F("FAILED | "));
        } else {
            Serial.print(F("OK     | "));
        }
        print_hex_buffer(user_soft_serial, 9);
        Serial.println(F("                                               |"));
    }
#endif
#if defined(GENERATE_SOMETHING) && !defined(PERSONALIZE_SOFT_RANDOM_SERIAL)
    Serial.println(
        F("+--------+--------+------------------------------------------------------------------+"));
    Serial.println();
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                                  Key copy section                                  |"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
#ifdef GENERATE_HMAC_KEY
    Serial.print(F("#define MY_HMAC_KEY "));
    print_c_friendly_hex_buffer(user_hmac_key, 32);
    Serial.println();
#endif
#ifdef GENERATE_AES_KEY
    Serial.print(F("#define MY_AES_KEY "));
    print_c_friendly_hex_buffer(user_aes_key, 16);
    Serial.println();
#endif
#ifdef GENERATE_SOFT_SERIAL
    Serial.print(F("#define MY_SOFT_SERIAL "));
    print_c_friendly_hex_buffer(user_soft_serial, 9);
    Serial.println();
#endif
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println();
#elif defined(PERSONALIZE_SOFT_RANDOM_SERIAL)
    Serial.println(
        F("+--------+--------+------------------------------------------------------------------+"));
    Serial.println();
#endif
}

#ifdef RESET_EEPROM_PERSONALIZATION
/** @brief Reset EEPROM */
static void    reset_eeprom(void)
{
    uint8_t validation_buffer[32];
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                                   EEPROM reset                                     |"));
    Serial.println(
        F("+--------+---------------------------------------------------------------------------+"));
    Serial.println(
        F("| Key ID | Status                                                                    |"));
    Serial.println(
        F("+--------+---------------------------------------------------------------------------+"));
    Serial.print(F("| HMAC   | "));
    hwWriteConfigBlock((void*)reset_buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
    // Validate data written
    hwReadConfigBlock((void*)validation_buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
    if (memcmp(validation_buffer, reset_buffer,    32) != 0) {
        Serial.println(F("FAILED                                                                    |"));
    } else {
        Serial.println(F("OK                                                                        |"));
    }
    Serial.print(F("| AES    | "));
    hwWriteConfigBlock((void*)reset_buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
    // Validate data written
    hwReadConfigBlock((void*)validation_buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
    if (memcmp(validation_buffer, reset_buffer,    16) != 0) {
        Serial.println(F("FAILED                                                                    |"));
    } else {
        Serial.println(F("OK                                                                        |"));
    }
    Serial.print(F("| SERIAL | "));
    hwWriteConfigBlock((void*)reset_buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
    // Validate data written
    hwReadConfigBlock((void*)validation_buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
    if (memcmp(validation_buffer, reset_buffer,    9) != 0) {
        Serial.println(F("FAILED                                                                    |"));
    } else {
        Serial.println(F("OK                                                                        |"));
    }
    Serial.println(
        F("+--------+---------------------------------------------------------------------------+"));
}
#endif // RESET_EEPROM_PERSONALIZATION

static void write_eeprom_checksum(void)
{
    uint8_t buffer[32];
    uint8_t* hash;
    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();
    hwWriteConfigBlock((void*)&hash[0], (void*)EEPROM_PERSONALIZATION_CHECKSUM_ADDRESS, 1);
}

/** @brief Store keys depending on configuration */
static void store_keys(void)
{
#if defined(STORE_HMAC_KEY) || defined(STORE_AES_KEY) || defined(STORE_SOFT_SERIAL)
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                                    Key storage                                     |"));
    Serial.println(
        F("+--------+--------+------------------------------------------------------------------+"));
    Serial.println(
        F("| Key ID | Status | Key                                                              |"));
    Serial.println(
        F("+--------+--------+------------------------------------------------------------------+"));
#endif
#ifdef STORE_HMAC_KEY
    Serial.print(F("| HMAC   | "));
    if (!store_hmac_key_data(user_hmac_key)) {
        Serial.print(F("FAILED | "));
    } else {
        Serial.print(F("OK     | "));
    }
    print_hex_buffer(user_hmac_key, 32);
    Serial.println(F(" |"));
#endif
#ifdef STORE_AES_KEY
    Serial.print(F("| AES    | "));
    if (!store_aes_key_data(user_aes_key)) {
        Serial.print(F("FAILED | "));
    } else {
        Serial.print(F("OK     | "));
    }
    print_hex_buffer(user_aes_key, 16);
    Serial.println(F("                                 |"));
#endif
#ifdef STORE_SOFT_SERIAL
    Serial.print(F("| SERIAL | "));
    if (has_device_unique_id) {
        memset(user_soft_serial, 0xFF, 9);
    }
    if (!store_soft_serial_data(user_soft_serial)) {
        Serial.print(F("FAILED | "));
    } else {
        Serial.print(F("OK     | "));
    }
    if (has_device_unique_id) {
        Serial.println(F("EEPROM reset. MCU has a unique serial which will be used instead.|"));
    } else {
        print_hex_buffer(user_soft_serial, 9);
        Serial.println(F("                                               |"));
    }
#endif
#if defined(STORE_HMAC_KEY) || defined(STORE_AES_KEY) || defined(STORE_SOFT_SERIAL)
    Serial.println(
        F("+--------+--------+------------------------------------------------------------------+"));
    Serial.println();
#endif
}

/**
 * @brief Print provided buffer as HEX data on serial console
 * @param data Buffer to print
 * @param sz Number of bytes to print
 */
static void print_hex_buffer(uint8_t* data, size_t sz)
{
    for (size_t i=0; i<sz; i++) {
        if (data[i] < 0x10) {
            Serial.print('0'); // Because Serial.print does not 0-pad HEX
        }
        Serial.print(data[i], HEX);
    }
}

/**
 * @brief Print provided buffer as C friendly copy+paste HEX data on serial console
 * @param data Buffer to print
 * @param sz Number of bytes to print
 */
static void print_c_friendly_hex_buffer(uint8_t* data, size_t sz)
{
    for (size_t i=0; i<sz; i++) {
        Serial.print("0x");
        if (data[i] < 0x10) {
            Serial.print('0'); // Because Serial.print does not 0-pad HEX
        }
        Serial.print(data[i], HEX);
        if (i < sz-1) {
            Serial.print(',');
        }
    }
}

#ifdef STORE_HMAC_KEY
/**
 * @brief Store the provided data as HMAC key to ATSHA204A or EEPROM depending on @ref USE_SOFT_SIGNING flag
 * @param data Key to store
 * @returns false if failed
 */
static bool store_hmac_key_data(uint8_t* data)
{
#ifdef USE_SOFT_SIGNING
    static uint8_t validation_buffer[32];
    hwWriteConfigBlock((void*)data, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
    // Validate data written
    hwReadConfigBlock((void*)validation_buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
    if (memcmp(validation_buffer, data,    32) != 0) {
        return false;
    } else {
        return true;
    }
#else
    // It will not be possible to write the key if the configuration zone is unlocked
    if (lockConfig == 0x00) {
        // Write the key to the appropriate slot in the data zone
        if (!write_atsha204a_key(data)) {
            return false;
        } else {
            return true;
        }
    } else {
        return false;
    }
#endif
}
#endif

#ifdef STORE_AES_KEY
/**
 * @brief Store the provided data as AES key to EEPROM
 * @param data Key to store
 * @returns false if failed
 */
static bool store_aes_key_data(uint8_t* data)
{
    static uint8_t validation_buffer[16];
    hwWriteConfigBlock((void*)data, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
    // Validate data written
    hwReadConfigBlock((void*)validation_buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
    if (memcmp(validation_buffer, data,    16) != 0) {
        return false;
    } else {
        return true;
    }
}
#endif // STORE_AES_KEY

#ifdef STORE_SOFT_SERIAL
/**
 * @brief Store the provided data as serial number to EEPROM
 * @param data Serial to store
 * @returns false if failed
 */
static bool store_soft_serial_data(uint8_t* data)
{
    static uint8_t validation_buffer[9];
    hwWriteConfigBlock((void*)data, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
    // Validate data written
    hwReadConfigBlock((void*)validation_buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
    if (memcmp(validation_buffer, data,    9) != 0) {
        return false;
    } else {
        return true;
    }
}
#endif // STORE_SOFT_SERIAL

#ifndef USE_SOFT_SIGNING
/**
 * @brief Initialize and read states in ATSHA204A
 */
static void init_atsha204a_state(void)
{
    // Read out lock config bits to determine if locking is possible
    ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, 0x15<<2);
    if (ret_code != SHA204_SUCCESS) {
        halt(false);
    } else {
        lockConfig = rx_buffer[SHA204_BUFFER_POS_DATA+3];
        lockValue = rx_buffer[SHA204_BUFFER_POS_DATA+2];
    }
}

#ifdef LOCK_ATSHA204A_CONFIGURATION
static void    lock_atsha204a_config(void)
{
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                           ATSHA204A configuration locking                          |"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    if (lockConfig != 0x00) {
        uint16_t crc;
        (void)crc;

        // Write config and get CRC for the updated config
        crc = write_atsha204a_config_and_get_crc();

        // List current configuration before attempting to lock
#ifdef PRINT_DETAILED_ATSHA204A_CONFIG
        Serial.println(
            F("|                             New ATSHA204A Configuration                            |"));
        dump_detailed_atsha204a_configuration();
#endif // PRINT_DETAILED_ATSHA204A_CONFIG

#ifndef SKIP_UART_CONFIRMATION
        // Purge serial input buffer
        while (Serial.available()) {
            Serial.read();
        }
        Serial.println(
            F("| * Send SPACE character now to lock the configuration...                            |"));

        while (Serial.available() == 0);
        if (Serial.read() == ' ')
#endif //not SKIP_UART_CONFIRMATION
        {
            Serial.print(F("| * Locking configuration..."));

            // Correct sequence, resync chip
            ret_code = sha204.sha204c_resync(SHA204_RSP_SIZE_MAX, rx_buffer);
            if (ret_code != SHA204_SUCCESS && ret_code != SHA204_RESYNC_WITH_WAKEUP) {
                Serial.println(
                    F("+------------------------------------------------------------------------------------+"));
                halt(false);
            }

            // Lock configuration zone
            ret_code = sha204.sha204m_execute(SHA204_LOCK, SHA204_ZONE_CONFIG,
                                              crc, 0, NULL, 0, NULL, 0, NULL,
                                              LOCK_COUNT, tx_buffer, LOCK_RSP_SIZE, rx_buffer);
            if (ret_code != SHA204_SUCCESS) {
                Serial.println(F("Failed                                                   |"));
                Serial.println(
                    F("+------------------------------------------------------------------------------------+"));
                halt(false);
            } else {
                Serial.println(F("Done                                                     |"));

                // Update lock flags after locking
                ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, 0x15<<2);
                if (ret_code != SHA204_SUCCESS) {
                    Serial.println(
                        F("+------------------------------------------------------------------------------------+"));
                    halt(false);
                } else {
                    lockConfig = rx_buffer[SHA204_BUFFER_POS_DATA+3];
                    lockValue = rx_buffer[SHA204_BUFFER_POS_DATA+2];
                }
            }
        }
#ifndef SKIP_UART_CONFIRMATION
        else {
            Serial.println(
                F("| * Unexpected answer. Skipping locking.                                             |"));
            Serial.println(
                F("+------------------------------------------------------------------------------------+"));
        }
#endif //not SKIP_UART_CONFIRMATION
    } else {
        Serial.println(
            F("| * Skipping configuration write and lock (configuration already locked).            |"));
        Serial.println(
            F("+------------------------------------------------------------------------------------+"));
    }
    Serial.println();
}

/**
 * @brief Write default configuration and return CRC of the configuration bits
 * @returns CRC over the configuration bits
 */
static uint16_t write_atsha204a_config_and_get_crc(void)
{
    uint16_t crc = 0;
    uint8_t config_word[4];

    // We will set default settings from datasheet on all slots. This means that we can use slot 0 for the key
    // as that slot will not be readable (key will therefore be secure) and slot 8 for the payload digest
    // calculationon as that slot can be written in clear text even when the datazone is locked.
    // Other settings which are not relevant are kept as is.

    for (int i=0; i < 88; i += 4) {
        bool do_write = true;
        if (i == 20) {
            config_word[0] = 0x8F;
            config_word[1] = 0x80;
            config_word[2] = 0x80;
            config_word[3] = 0xA1;
        } else if (i == 24) {
            config_word[0] = 0x82;
            config_word[1] = 0xE0;
            config_word[2] = 0xA3;
            config_word[3] = 0x60;
        } else if (i == 28) {
            config_word[0] = 0x94;
            config_word[1] = 0x40;
            config_word[2] = 0xA0;
            config_word[3] = 0x85;
        } else if (i == 32) {
            config_word[0] = 0x86;
            config_word[1] = 0x40;
            config_word[2] = 0x87;
            config_word[3] = 0x07;
        } else if (i == 36) {
            config_word[0] = 0x0F;
            config_word[1] = 0x00;
            config_word[2] = 0x89;
            config_word[3] = 0xF2;
        } else if (i == 40) {
            config_word[0] = 0x8A;
            config_word[1] = 0x7A;
            config_word[2] = 0x0B;
            config_word[3] = 0x8B;
        } else if (i == 44) {
            config_word[0] = 0x0C;
            config_word[1] = 0x4C;
            config_word[2] = 0xDD;
            config_word[3] = 0x4D;
        } else if (i == 48) {
            config_word[0] = 0xC2;
            config_word[1] = 0x42;
            config_word[2] = 0xAF;
            config_word[3] = 0x8F;
        } else if (i == 52 || i == 56 || i == 60 || i == 64) {
            config_word[0] = 0xFF;
            config_word[1] = 0x00;
            config_word[2] = 0xFF;
            config_word[3] = 0x00;
        } else if (i == 68 || i == 72 || i == 76 || i == 80) {
            config_word[0] = 0xFF;
            config_word[1] = 0xFF;
            config_word[2] = 0xFF;
            config_word[3] = 0xFF;
        } else {
            // All other configs are untouched
            ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, i);
            if (ret_code != SHA204_SUCCESS) {
                Serial.println(
                    F("+------------------------------------------------------------------------------------+"));
                halt(false);
            }
            // Set config_word to the read data
            config_word[0] = rx_buffer[SHA204_BUFFER_POS_DATA+0];
            config_word[1] = rx_buffer[SHA204_BUFFER_POS_DATA+1];
            config_word[2] = rx_buffer[SHA204_BUFFER_POS_DATA+2];
            config_word[3] = rx_buffer[SHA204_BUFFER_POS_DATA+3];
            do_write = false;
        }

        // Update crc with CRC for the current word
        crc = sha204.calculateAndUpdateCrc(4, config_word, crc);

        // Write config word
        if (do_write) {
            ret_code = sha204.sha204m_execute(SHA204_WRITE, SHA204_ZONE_CONFIG,
                                              i >> 2, 4, config_word, 0, NULL, 0, NULL,
                                              WRITE_COUNT_SHORT, tx_buffer, WRITE_RSP_SIZE, rx_buffer);
            if (ret_code != SHA204_SUCCESS) {
                Serial.println(
                    F("+------------------------------------------------------------------------------------+"));
                halt(false);
            }
        }
    }
    return crc;
}
#endif

static bool get_atsha204a_serial(uint8_t* data)
{
    ret_code = sha204.getSerialNumber(rx_buffer);
    if (ret_code != SHA204_SUCCESS) {
        return false;
    } else {
        memcpy(data, rx_buffer, 9);
        return true;
    }
}

#ifdef STORE_HMAC_KEY
/**
 * @brief Write provided key to slot 0
 * @param key The key data to write
 * @returns false if failed
 */
static bool write_atsha204a_key(uint8_t* key)
{
    // Write key to slot 0
    ret_code = sha204.sha204m_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG,
                                      0, SHA204_ZONE_ACCESS_32, key, 0, NULL, 0, NULL,
                                      WRITE_COUNT_LONG, tx_buffer, WRITE_RSP_SIZE, rx_buffer);
    if (ret_code != SHA204_SUCCESS) {
        return false;
    } else {
        return true;
    }
}
#endif // STORE_HMAC_KEY
#endif // not USE_SOFT_SIGNING

/** @brief Print a greeting on serial console */
static void print_greeting(void)
{
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                           MySensors security personalizer                          |"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println();
#ifdef NO_SETTINGS_DEFINED
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("| You are running without any configuration flags set.                               |"));
    Serial.println(
        F("| No changes will be made to ATSHA204A or EEPROM except for the EEPROM checksum      |"));
    Serial.println(
        F("| which will be updated.                                                             |"));
    Serial.println(
        F("|                                                                                    |"));
    Serial.println(
        F("| If you want to personalize your device, you have two options.                      |"));
    Serial.println(
        F("|                                                                                    |"));
    Serial.println(
        F("| 1. a. Enable either GENERATE_KEYS_ATSHA204A or GENERATE_KEYS_SOFT                  |"));
    Serial.println(
        F("|       This will generate keys for ATSHA204A or software signing.                   |"));
    Serial.println(
        F("|    b. Execute the sketch. You will be guided through the steps below under         |"));
    Serial.println(
        F("|       WHAT TO DO NEXT?                                                             |"));
    Serial.println(
        F("|    c. Copy the generated keys and replace the topmost definitions in this file.    |"));
    Serial.println(
        F("|    d. Save the sketch and then disable the flag you just enabled.                  |"));
    Serial.println(
        F("|    e. Enable PERSONALIZE_ATSHA204A to personalize the ATSHA204A device.            |"));
    Serial.println(
        F("|       or                                                                           |"));
    Serial.println(
        F("|       Enable PERSONALIZE_SOFT to personalize the EEPROM for software signing.      |"));
    Serial.println(
        F("|       If you want to use whitelisting you need to pick a unique serial number      |"));
    Serial.println(
        F("|       for each device you run the sketch on and fill in MY_SOFT_SERIAL.            |"));
    Serial.println(
        F("|       or                                                                           |"));
    Serial.println(
        F("|       Enable PERSONALIZE_SOFT_RANDOM_SERIAL to personalzie the EEPROM and          |"));
    Serial.println(
        F("|       include a new random serial number every time the sketch is executed.        |"));
    Serial.println(
        F("|       Take note of each saved serial number if you plan to use whitelisting.       |"));
    Serial.println(
        F("|    f. Execute the sketch on each device you want to personalize that is supposed   |"));
    Serial.println(
        F("|       to communicate securely.                                                     |"));
    Serial.println(
        F("|                                                                                    |"));
    Serial.println(
        F("| 2. Enable any configuration flag as you see fit.                                   |"));
    Serial.println(
        F("|    It is assumed that you know what you are doing.                                 |"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println();
#else
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                               Configuration settings                               |"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
#if defined(GENERATE_KEYS_ATSHA204A)
    Serial.println(
        F("| * Guided key generation for ATSHA204A using ATSHA024A                              |"));
#endif
#if defined(GENERATE_KEYS_SOFT)
    Serial.println(
        F("| * Guided key generation for EEPROM using software                                  |"));
#endif
#if defined(PERSONALIZE_ATSHA204A)
    Serial.println(
        F("| * Guided personalization/storage of keys in ATSHA204A                              |"));
#endif
#if defined(PERSONALIZE_SOFT)
    Serial.println(
        F("| * Guided personalization/storage of keys in EEPROM                                 |"));
#endif
#if defined(PERSONALIZE_SOFT_RANDOM_SERIAL)
    Serial.println(
        F("| * Guided storage and generation of random serial in EEPROM                         |"));
#endif
#if defined(USE_SOFT_SIGNING)
    Serial.println(
        F("| * Software based personalization (no ATSHA204A usage whatsoever)                   |"));
#else
    Serial.println(
        F("| * ATSHA204A based personalization                                                  |"));
#endif
#if defined(LOCK_ATSHA204A_CONFIGURATION)
    Serial.println(
        F("| * Will lock ATSHA204A configuration                                                |"));
#endif
#if defined(SKIP_UART_CONFIRMATION)
    Serial.println(
        F("| * Will not require any UART confirmations                                          |"));
#endif
#if defined(GENERATE_HMAC_KEY)
    Serial.print(
        F("| * Will generate HMAC key using "));
#if defined(USE_SOFT_SIGNING)
    Serial.println(
        F("software                                            |"));
#else
    Serial.println(
        F("ATSHA204A                                           |"));
#endif
#endif
#if defined(STORE_HMAC_KEY)
    Serial.print(F("| * Will store HMAC key to "));
#if defined(USE_SOFT_SIGNING)
    Serial.println(
        F("EEPROM                                                    |"));
#else
    Serial.println(
        F("ATSHA204A                                                 |"));
#endif
#endif
#if defined(GENERATE_AES_KEY)
    Serial.print(
        F("| * Will generate AES key using "));
#if defined(USE_SOFT_SIGNING)
    Serial.println(
        F("software                                             |"));
#else
    Serial.println(
        F("ATSHA204A                                            |"));
#endif
#endif
#if defined(STORE_AES_KEY)
    Serial.println(
        F("| * Will store AES key to EEPROM                                                     |"));
#endif
#if defined(GENERATE_SOFT_SERIAL)
    Serial.println(
        F("| * Will generate soft serial using software                                         |"));
#endif
#if defined(STORE_SOFT_SERIAL)
    Serial.println(
        F("| * Will store soft serial to EEPROM                                                 |"));
#endif
#if defined(PRINT_DETAILED_ATSHA204A_CONFIG)
    Serial.println(
        F("| * Will print detailed ATSHA204A configuration                                      |"));
#endif
#if defined(RESET_EEPROM_PERSONALIZATION)
    Serial.println(
        F("| * Will reset EEPROM personalization data                                           |"));
#endif
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println();
#endif // not NO_SETTINGS_DEFINED
    probe_and_print_peripherals();
}

static void print_ending(void)
{
#if defined(GUIDED_MODE) || defined(NO_SETTINGS_DEFINED)
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                                  WHAT TO DO NEXT?                                  |"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
#ifdef NO_SETTINGS_DEFINED
    Serial.println(
        F("| To proceed with the personalization, enable GENERATE_KEYS_ATSHA204A or             |"));
    Serial.println(
        F("| GENERATE_KEYS_SOFT depending on what type of signing backend you plan to use.      |"));
    Serial.println(
        F("| Both options will generate an AES key for encryption if you plan to use that.      |"));
    Serial.println(
        F("| Recompile, upload and run the sketch again for further instructions.               |"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
#endif
#ifdef GENERATE_KEYS_ATSHA204A
    Serial.println(
        F("| To proceed with the personalization, copy the keys shown in the Key copy section,  |"));
    Serial.println(
        F("| and replace the corresponding definitions in the top of the sketch, then disable   |"));
    Serial.println(
        F("| GENERATE_KEYS_ATSHA204A and enable PERSONALIZE_ATSHA204A.                          |"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
#endif
#ifdef GENERATE_KEYS_SOFT
    Serial.println(
        F("| To proceed with the personalization, copy the keys shown in the Key copy section,  |"));
    Serial.println(
        F("| and replace the corresponding definitions in the top of the sketch, then disable   |"));
    Serial.println(
        F("| GENERATE_KEYS_SOFT and enable PERSONALIZE_SOFT or PERSONALIZE_SOFT_RANDOM_SERIAL.  |"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
#endif
#if defined(PERSONALIZE_ATSHA204A) ||\
    defined(PERSONALIZE_SOFT) ||\
    defined(PERSONALIZE_SOFT_RANDOM_SERIAL)
    Serial.println(
        F("| This device has now been personalized. Run this sketch with its current settings   |"));
    Serial.println(
        F("| on all the devices in your network that have security enabled.                     |"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
#endif
#endif // GUIDED_MODE or NO_SETTINGS_DEFINED
}

static void    probe_and_print_peripherals(void)
{
    unique_id_t uniqueID;

    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                           Hardware security peripherals                            |"));
    Serial.println(
        F("+--------------+--------------+--------------+------------------------------+--------+"));
    Serial.println(
        F("| Device       | Status       | Revision     | Serial number                | Locked |"));
    Serial.println(
        F("+--------------+--------------+--------------+------------------------------+--------+"));
#if defined(ARDUINO_ARCH_AVR)
    Serial.print(F("| AVR          | DETECTED     | N/A          | "));
#elif defined(ARDUINO_ARCH_ESP8266)
    Serial.print(F("| ESP8266      | DETECTED     | N/A          | "));
#elif defined(ARDUINO_ARCH_SAMD)
    Serial.print(F("| SAMD         | DETECTED     | N/A          | "));
#elif defined(ARDUINO_ARCH_STM32F1)
    Serial.print(F("| STM32F1      | DETECTED     | N/A          | "));
#elif defined(__linux__)
    Serial.print(F("| Linux        | DETECTED     | N/A          | "));
#else
    Serial.print(F("| Unknown      | DETECTED     | N/A          | "));
#endif
    if (hwUniqueID(&uniqueID)) {
        has_device_unique_id = true;
        print_hex_buffer(uniqueID, 9);
        Serial.println(F("           | N/A    |"));
    } else {
        Serial.println(F("N/A (generation required)    | N/A    |"));
    }
    Serial.println(
        F("+--------------+--------------+--------------+------------------------------+--------+"));
#ifndef USE_SOFT_SIGNING
    Serial.print(F("| ATSHA204A    | "));
    ret_code = sha204.sha204c_wakeup(rx_buffer);
    if (ret_code != SHA204_SUCCESS) {
        ret_code = SHA204_SUCCESS; // Reset retcode to avoid false negative execution result
        Serial.println(F("NOT DETECTED | N/A          | N/A                          | N/A    |"));
    } else {
        uint8_t buffer[9];
        Serial.print(F("DETECTED     | "));
        ret_code = sha204.sha204m_dev_rev(tx_buffer, rx_buffer);
        if (ret_code != SHA204_SUCCESS) {
            Serial.print(F("FAILED       | "));
        } else {
            print_hex_buffer(&rx_buffer[SHA204_BUFFER_POS_DATA], 4);
            Serial.print(F("     | "));
        }
        if (!get_atsha204a_serial(buffer)) {
            memset(buffer, 0xFF, 9);
            Serial.print(F("FAILED                       | "));
        } else {
            print_hex_buffer(buffer, 9);
            Serial.print(F("           | "));
        }
        ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, 0x15<<2);
        if (ret_code != SHA204_SUCCESS) {
            Serial.println("FAILED |");
        } else {
            if (rx_buffer[SHA204_BUFFER_POS_DATA+3] == 0x00) {
                Serial.println("YES    |");
            } else {
                Serial.println("NO     |");
            }
        }
    }
    Serial.println(
        F("+--------------+--------------+--------------+------------------------------+--------+"));
#ifdef PRINT_DETAILED_ATSHA204A_CONFIG
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                          Current ATSHA204A Configuration                           |"));
    dump_detailed_atsha204a_configuration();
#endif
#endif // not USE_SOFT_SIGNING
    Serial.println();
}

static void print_eeprom_data(void)
{
    uint8_t buffer[32];

    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                                       EEPROM                                       |"));
    Serial.println(
        F("+--------+--------+------------------------------------------------------------------+"));
    Serial.println(
        F("| Key ID | Status | Key                                                              |"));
    Serial.println(
        F("+--------+--------+------------------------------------------------------------------+"));
    Serial.print(F("| HMAC   | "));
    hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
    if (!memcmp(buffer, reset_buffer, 32)) {
        Serial.print(F("RESET  | "));
    } else {
        Serial.print(F("OK     | "));
    }
    print_hex_buffer(buffer, 32);
    Serial.println(F(" |"));
    Serial.print(F("| AES    | "));
    hwReadConfigBlock((void*)buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
    if (!memcmp(buffer, reset_buffer, 16)) {
        Serial.print(F("RESET  | "));
    } else {
        Serial.print(F("OK     | "));
    }
    print_hex_buffer(buffer, 16);
    Serial.println(F("                                 |"));
    Serial.print(F("| SERIAL | "));
    hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
    if (!memcmp(buffer, reset_buffer, 9)) {
        if (has_device_unique_id) {
            Serial.println(F("N/A    | Device unique serial, not stored in EEPROM                       |"));
        } else {
            Serial.print(F("RESET  | "));
            print_hex_buffer(buffer, 9);
            Serial.println(F("                                               |"));
        }
    } else {
        Serial.print(F("OK     | "));
        print_hex_buffer(buffer, 9);
        Serial.println(F("                                               |"));
    }
    Serial.println(
        F("+--------+--------+------------------------------------------------------------------+"));
    Serial.println();
}

static void print_whitelisting_entry(void)
{
    uint8_t buffer[9];
#ifdef USE_SOFT_SIGNING
    unique_id_t uniqueID;
    hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
    if (!memcmp(buffer, reset_buffer, 9)) {
        // Serial reset in EEPROM, check for unique ID
        if (hwUniqueID(&uniqueID)) {
            memcpy(buffer, uniqueID, 9);
        }
    }
#else
    // If ATSHA204A is used, use that serial here
    if (!get_atsha204a_serial(buffer)) {
        memset(buffer, 0xFF, 9);
    }
#endif
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.println(
        F("|                      This nodes whitelist entry on other nodes                     |"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
    Serial.print(F("{.nodeId = <ID of this node>,.serial = {"));
    print_c_friendly_hex_buffer(buffer, 9);
    Serial.println(F("}}"));
    Serial.println(
        F("+------------------------------------------------------------------------------------+"));
}

#ifdef PRINT_DETAILED_ATSHA204A_CONFIG
/** @brief Dump current configuration to UART */
static void dump_detailed_atsha204a_configuration(void)
{
    Serial.println(
        F("+---------------------------------------------------------------++-------------------+"));
    Serial.println(
        F("|                           Fieldname                           ||       Data        |"));
    Serial.println(
        F("+-------------------------------+-------------------------------++---------+---------+"));
    for (int i=0; i < 88; i += 4) {
        ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, i);
        if (ret_code != SHA204_SUCCESS) {
            Serial.println(
                F("+------------------------------------------------------------------------------------+"));
            halt(false);
        }
        if (i == 0x00) {
            Serial.print(F("|            SN[0:1]            |            SN[2:3]            || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j == 1) {
                    Serial.print(F(" | "));
                } else if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+-------------------------------+-------------------------------++---------+---------+"));
        } else if (i == 0x04) {
            Serial.print(F("|                            Revnum                             || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+---------------------------------------------------------------++-------------------+"));
        } else if (i == 0x08) {
            Serial.print(F("|                            SN[4:7]                            || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+---------------+---------------+---------------+---------------++----+----+----+----+"));
        } else if (i == 0x0C) {
            Serial.print(F("|     SN[8]     |  Reserved13   |   I2CEnable   |  Reserved15   || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F(" | "));
                } else {
                    Serial.println(F(" |"));
                }
            }
            Serial.println(
                F("+---------------+---------------+---------------+---------------++----+----+----+----+"));
        } else if (i == 0x10) {
            Serial.print(F("|  I2CAddress   |  TempOffset   |    OTPmode    | SelectorMode  || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F(" | "));
                } else {
                    Serial.println(F(" |"));
                }
            }
            Serial.println(
                F("+---------------+---------------+---------------+---------------++----+----+----+----+"));
        } else if (i == 0x14) {
            Serial.print(F("|          SlotConfig00         |         SlotConfig01          || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j == 1) {
                    Serial.print(F(" | "));
                } else if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+-------------------------------+-------------------------------++---------+---------+"));
        } else if (i == 0x18) {
            Serial.print(F("|          SlotConfig02         |         SlotConfig03          || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j == 1) {
                    Serial.print(F(" | "));
                } else if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+-------------------------------+-------------------------------++---------+---------+"));
        } else if (i == 0x1C) {
            Serial.print(F("|          SlotConfig04         |         SlotConfig05          || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j == 1) {
                    Serial.print(F(" | "));
                } else if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+-------------------------------+-------------------------------++---------+---------+"));
        } else if (i == 0x20) {
            Serial.print(F("|          SlotConfig06         |         SlotConfig07          || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j == 1) {
                    Serial.print(F(" | "));
                } else if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+-------------------------------+-------------------------------++---------+---------+"));
        } else if (i == 0x24) {
            Serial.print(F("|          SlotConfig08         |         SlotConfig09          || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j == 1) {
                    Serial.print(F(" | "));
                } else if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+-------------------------------+-------------------------------++---------+---------+"));
        } else if (i == 0x28) {
            Serial.print(F("|          SlotConfig0A         |         SlotConfig0B          || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j == 1) {
                    Serial.print(F(" | "));
                } else if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+-------------------------------+-------------------------------++---------+---------+"));
        } else if (i == 0x2C) {
            Serial.print(F("|          SlotConfig0C         |         SlotConfig0D          || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j == 1) {
                    Serial.print(F(" | "));
                } else if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+-------------------------------+-------------------------------++---------+---------+"));
        } else if (i == 0x30) {
            Serial.print(F("|          SlotConfig0E         |         SlotConfig0F          || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j == 1) {
                    Serial.print(F(" | "));
                } else if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+---------------+---------------+---------------+---------------++----+----+----+----+"));
        } else if (i == 0x34) {
            Serial.print(F("|   UseFlag00   | UpdateCount00 |   UseFlag01   | UpdateCount01 || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F(" | "));
                } else {
                    Serial.println(F(" |"));
                }
            }
            Serial.println(
                F("+---------------+---------------+---------------+---------------++----+----+----+----+"));
        } else if (i == 0x38) {
            Serial.print(F("|   UseFlag02   | UpdateCount02 |   UseFlag03   | UpdateCount03 || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F(" | "));
                } else {
                    Serial.println(F(" |"));
                }
            }
            Serial.println(
                F("+---------------+---------------+---------------+---------------++----+----+----+----+"));
        } else if (i == 0x3C) {
            Serial.print(F("|   UseFlag04   | UpdateCount04 |   UseFlag05   | UpdateCount05 || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F(" | "));
                } else {
                    Serial.println(F(" |"));
                }
            }
            Serial.println(
                F("+---------------+---------------+---------------+---------------++----+----+----+----+"));
        } else if (i == 0x40) {
            Serial.print(F("|   UseFlag06   | UpdateCount06 |   UseFlag07   | UpdateCount07 || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F(" | "));
                } else {
                    Serial.println(F(" |"));
                }
            }
            Serial.println(
                F("+---------------+---------------+---------------+---------------++----+----+----+----+"));
        } else if (i == 0x44) {
            Serial.print(F("|                        LastKeyUse[0:3]                        || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+---------------------------------------------------------------++-------------------+"));
        } else if (i == 0x48) {
            Serial.print(F("|                        LastKeyUse[4:7]                        || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+---------------------------------------------------------------++-------------------+"));
        } else if (i == 0x4C) {
            Serial.print(F("|                        LastKeyUse[8:B]                        || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+---------------------------------------------------------------++-------------------+"));
        } else if (i == 0x50) {
            Serial.print(F("|                        LastKeyUse[C:F]                        || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F("   "));
                }
            }
            Serial.println(F(" |"));
            Serial.println(
                F("+---------------+---------------+---------------+---------------++----+----+----+----+"));
        } else if (i == 0x54) {
            Serial.print(F("|   UserExtra   |    Selector   |   LockValue   |  LockConfig   || "));
            for (int j=0; j<4; j++) {
                if (rx_buffer[SHA204_BUFFER_POS_DATA+j] < 0x10) {
                    Serial.print('0'); // Because Serial.print does not 0-pad HEX
                }
                Serial.print(rx_buffer[SHA204_BUFFER_POS_DATA+j], HEX);
                if (j < 3) {
                    Serial.print(F(" | "));
                } else {
                    Serial.println(F(" |"));
                }
            }
            Serial.println(
                F("+---------------+---------------+---------------+---------------++----+----+----+----+"));
        }
    }
}
#endif // PRINT_DETAILED_ATSHA204A_CONFIG

// Doxygen specific constructs, not included when built normally
// This is used to enable disabled macros/definitions to be included in the documentation as well.
#if DOXYGEN
#define GENERATE_KEYS_ATSHA204A
#define GENERATE_KEYS_SOFT
#define PERSONALIZE_ATSHA204A
#define PERSONALIZE_SOFT
#define PERSONALIZE_SOFT_RANDOM_SERIAL
#define USE_SOFT_SIGNING
#define LOCK_ATSHA204A_CONFIGURATION
#define SKIP_UART_CONFIRMATION
#define GENERATE_HMAC_KEY
#define STORE_HMAC_KEY
#define GENERATE_AES_KEY
#define STORE_AES_KEY
#define GENERATE_SOFT_SERIAL
#define STORE_SOFT_SERIAL
#define PRINT_DETAILED_ATSHA204A_CONFIG
#define RESET_EEPROM_PERSONALIZATION
#endif

Related Content

Comments