As IoT devices become more prevalent, so do the challenges of protecting them from cyber threats. Ensuring secure communication and safeguarding sensitive data are key points for developers building secure systems. While no single solution can guarantee complete security, the Arduino Uno WiFi Rev2 takes a significant step forward with its onboard ATECC608A HSM.
This dedicated secure hardware module simplifies implementing core security features like encryption, secure key storage, and device authentication.
In this article, we will explore the capabilities of the Arduino Uno WiFi Rev2 Cryptochip, how it enhances security, and how you can integrate it into your projects.
What is a Hardware Secure Module (HSM)
A Hardware Secure Module (HSM) is a dedicated security component designed to perform cryptographic operations in a protected environment. Unlike software-based security, which relies on the microcontroller’s processing power and memory, an HSM handles encryption, authentication, and key management independently, ensuring that sensitive data remains secure and resistant to attacks.

HSMs can be external or internal, depending on their integration within a system. External HSMs function as separate cryptographic chips, like the ATECC608A, which interfaces with a microcontroller via I2C. Internal HSMs, on the other hand, exist as dedicated security cores embedded inside modern microcontrollers, providing built-in cryptographic capabilities without requiring an external chip.
The ATECC608A, built into the Arduino Uno board, operates as an external HSM. It securely stores cryptographic keys in tamper-resistant memory and performs encryption tasks, preventing unauthorized access or key extraction. Offloading security operations from the microcontroller enhances protection while freeing up system resources.
Hardware Overview: The ATECC608A HSM on Arduino Uno
The ATECC608A is a dedicated cryptographic co-processor designed to enhance security in IoT applications. Unlike software-based encryption, which relies on the microcontroller’s processing power and can be vulnerable to attacks, this HSM handles encryption, authentication, and key storage in a tamper-resistant environment.

The key features of the ATECC608A are:
- Asymmetric Encryption – Supports Elliptic Curve Cryptography (ECC) for secure data exchange and authentication.
- Symmetric Encryption – Supports AES algorithm.
- Hashing algorithms – Support the creation of SHA256 digests.
- Secure Key Storage – Keeps cryptographic keys protected from unauthorized access, preventing exposure even if the device is compromised.
- Digital Signatures – Allows devices to sign messages securely, ensuring authenticity and preventing tampering.
- I2C Interface – Communicates through the classical I2C bus for simple integration.
Project setup: Preparing the Arduino Uno for the ATECC608A Integration
Before diving into the HSM’s capabilities, setting up the development environment for the Arduino Uno WiFi Rev2 is essential. For this particular setup, I will use the Arduino Uno WiFi Rev 2, a versatile board with built-in WiFi capabilities and well-suited for projects requiring robust connectivity and reliable performance. If you are interested in connecting this board to WiFi, you can refer to the following article.
Begin by downloading and installing the Arduino IDE from the official Arduino website. Ensure the IDE version is 1.8.13 or later to avoid compatibility issues with the board and libraries.
Once the IDE is installed, you must add support for the Arduino Uno WiFi Rev2. Open the IDE, navigate to the Boards Manager under the Tools menu, and search “Arduino megaAVR Boards.” Install this package to enable programming for the ATmega4809 microcontroller used by the board.

Next, install the necessary libraries to interact with the ATECC608A cryptochip. Open the Library Manager from the Tools menu, search for “ArduinoECCX08,” and install it. This library provides the functions required to initialize and utilize the Cryptochip.

With the IDE and libraries installed, the setup process is complete. The following section will cover how to write and upload a sketch to initialize the Cryptochip and verify its functionality.
Working with the ATECC608A HSM: Initialization and Functionality
This section will guide you through the key steps for using the ATECC608A Cryptochip on the Arduino Uno, from initialization to demonstrating its practical functionality. Each part will include clear code examples and explanations to help you fully understand its capabilities.
Initializing the ATECC608A HSM on the Arduino Uno
Before using the ATECC608A, it must be initialized appropriately to ensure it operates correctly. The chip memory layout is divided into two key sections:
- Configuration Zone – Stores settings defining the HSM’s operation, including key permissions and I²C settings.
- Data Zone – Stores cryptographic keys and related security data.
The configuration zone must be set up first, as it defines how the data zone will function. Once the configuration is written and locked, you can generate and store cryptographic keys.
The complete ATECC608A datasheet is not publicly available, but fortunately, the ArduinoECCX08 library allows us to interact with the chip without needing to understand its internal details, making it easier to configure and use its security features effectively.
First of all, here follows the configuration we will store in the configuration zone, which has been copied from the library example code.
const byte ECCX08_CONFIGURATION[128] = {
// Read only - start
// SN[0:3]
0x01, 0x23, 0x00, 0x00,
// RevNum
0x00, 0x00, 0x50, 0x00,
// SN[4:8]
0x00, 0x00, 0x00, 0x00, 0x00,
// Reserved
0xC0,
// I2C_Enable
0x71,
// Reserved
0x00,
// Read only - end
// I2C_Address
0xC0,
// Reserved
0x00,
// OTPmode
0x55,
// ChipMode
0x00,
// SlotConfig
0x83, 0x20, // External Signatures | Internal Signatures | IsSecret | Write Configure Never, Default: 0x83, 0x20,
0x87, 0x20, // External Signatures | Internal Signatures | ECDH | IsSecret | Write Configure Never, Default: 0x87, 0x20,
0x87, 0x20, // External Signatures | Internal Signatures | ECDH | IsSecret | Write Configure Never, Default: 0x8F, 0x20,
0x87, 0x2F, // External Signatures | Internal Signatures | ECDH | IsSecret | WriteKey all slots | Write Configure Never, Default: 0xC4, 0x8F,
0x87, 0x2F, // External Signatures | Internal Signatures | ECDH | IsSecret | WriteKey all slots | Write Configure Never, Default: 0x8F, 0x8F,
0x8F, 0x8F,
0x9F, 0x8F,
0xAF, 0x8F,
0x87, 0x20, // External Signatures | Internal Signatures | ECDH | IsSecret | Write Configure Never, Default: 0x87, 0x20,
0x87, 0x20, // External Signatures | Internal Signatures | ECDH | IsSecret | Write Configure Never, Default: 0x8F, 0x20,
0x87, 0x20, // External Signatures | Internal Signatures | ECDH | IsSecret | Write Configure Never, Default: 0x87, 0x20,
0x87, 0x20, // External Signatures | Internal Signatures | ECDH | IsSecret | Write Configure Never, Default: 0x8F, 0x20,
0x87, 0x20, // External Signatures | Internal Signatures | ECDH | IsSecret | Write Configure Never, Default: 0x87, 0x20,
0x87, 0x20, // External Signatures | Internal Signatures | ECDH | IsSecret | Write Configure Never, Default: 0x8F, 0x20,
0x00, 0x00,
0xAF, 0x8F,
// Counter[0]
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
// Counter[1]
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
// UseLock
0x00,
// VolatileKey
0x00,
//Secure Boot
0x00, 0x00,
// KdflvLoc
0x00,
// KdflvStr
0x00, 0x00,
// Reserved
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Write via commands only - start
// UserExtra
0x00,
// Selector
0x00,
// LockValue
0x55,
// LockConfig
0x55,
// SlotLocked
0xFF, 0xFF,
// Write via commands only - end
// Chip Options
0x00, 0x00,
// X509format
0x00, 0x00, 0x00, 0x00,
// KeyConfig
0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x33, 0x00,
0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x33, 0x00,
0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x33, 0x00,
0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x1C, 0x00,
0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x1C, 0x00,
0x1C, 0x00, // Not ECC | SHA Key
0x1C, 0x00, // Not ECC | SHA Key
0x1C, 0x00, // Not ECC | SHA Key
0x3C, 0x00,
0x3C, 0x00,
0x38, 0x00,
0x38, 0x00,
0x3A, 0x00,
0x3A, 0x00,
0x3C, 0x00,
0x1C, 0x00
};
This is the configuration content. The configuration has many parameters, and I will not cover all of them. The most important ones are the I²C address, slot configuration, and key configuration, as they define how the HSM securely communicates and handles cryptographic keys.
The I²C address is set to 0xC0.
The slot configuration defines how key storage slots are used and secured. Slots configured with values like 0x87 0x20 allow internal and external signatures, meaning the stored key can be used for cryptographic signing operations. Slots with 0x87 0x2F also enable ECDH (Elliptic Curve Diffie-Hellman) key exchange, which securely generates shared secrets. The IsSecret flag ensures that private keys remain unreadable, even to the system using them. Some slots are write-protected, meaning they cannot be modified once initialized.
The key configuration determines the type of cryptographic keys each slot stores. Slots with values like 0x33 0x00 store P-256 ECC private and public keys, which are used for authentication, signing, and encryption. Other slots, configured with 0x1C 0x00, store SHA-based keys, meaning they are used for hashing or HMAC authentication instead of ECC operations.
The configuration needs now to be programmed into the HSM, and this can be achieved through the following sketch, where I will intentionally omit the configuration shown above:
#include <ArduinoECCX08.h>
void setup() {
Serial.begin(9600);
while (!Serial)
{
; // Wait for serial connection
}
if (!ECCX08.begin())
{
Serial.println("Error: HSM not detected!");
while (1); // Halt execution
}
Serial.println("ATECC608A initialized successfully!");
// Write the Cryptochip configuration
WriteConf();
}
void loop()
{
Serial.print("Random number: ");
Serial.println(ECCX08.random(65535));
delay(1000);
}
void WriteConf(void)
{
if (!ECCX08.locked())
{
Serial.println("ECCX08 is not yet locked. Proceeding with configuration writing");
if (!ECCX08.writeConfiguration(ECCX08_CONFIGURATION))
{
Serial.println("Writing ECCX08 configuration failed!");
while (1);
}
if (!ECCX08.lock(0))
{
Serial.println("Locking ECCX08 configuration failed!");
while (1);
}
Serial.println("ECCX08 locked successfully");
Serial.println();
}
}
The code begins by establishing a serial connection and initializing the ATECC608A HSM. If the HSM is not detected, execution halts to prevent further operations. Once the chip is successfully initialized, the WriteConf() function is called to configure it.
The WriteConf() function first checks whether the HSM is already locked. If it is not, the predefined configuration stored in the ECCX08_CONFIGURATION const is written to the chip. After successfully writing the configuration, the function proceeds to lock the configuration zone using ECCX08.lock(0), ensuring that the settings cannot be modified afterward. If any step in this process fails, execution stops to prevent an improper or incomplete configuration. After the setup is complete, the program enters the main loop, where it repeatedly generates a random number using ECCX08.random(65535). This random number is printed on the serial monitor every second. This feature leverages the Cryptochip’s built-in random number generator, demonstrating that the Cryptochip was correctly configured.
Generating our first Key with the ATECC608A HSM
Once the HSM is initialized, we can use the device’s real cryptography feature. Below is a function that generates an asymmetric elliptic curve private key.
int generatePrivateKey(int slot)
{
int retVal = 0;
byte publicKey[64];
if (!ECCX08.generatePrivateKey(slot, publicKey))
{
Serial.println("Failed to generate and store private key.");
}
else
{
Serial.println("Private key generated and securely stored.");
retVal = 1;
}
return retVal;
}
This function generates and securely stores a private key inside the ATECC608A Cryptochip. It attempts to create an ECC P-256 private key in slot 0 using the generatePrivateKey method. If the key generation fails, an error message is printed to the serial monitor. Since the HSM protects private keys internally, the generated key remains inaccessible, ensuring high security for cryptographic operations.
Signing a message with the ATECC608A HSM on Arduino Uno
We will now see how to sign a message on the ATECC608A HSM. You only need to specify the slot where the private key is stored (e.g., slot 0) and provide the data to be signed, as shown in the following code snippet:
int signMessage(const byte *message, byte *signature, int slot)
{
int retVal = 0;
// Perform the signing operation
if (!ECCX08.ecSign(slot, message, signature))
{
Serial.println("Failed to sign the message.");
}
else
{
Serial.println("Message signed successfully!");
Serial.print("Signature: ");
for (int i = 0; i < 64; i++) {
Serial.print(signature[i], HEX);
Serial.print(" ");
}
Serial.println();
retVal = 1;
}
return retVal;
}
The function calls ECCX08.ecSign
to sign the message with the key stored in the given slot. If the signing operation fails, the serial monitor prints an error message. If successful, it prints a success message and outputs the 64-byte signature in hexadecimal format. The function returns 1 if the signing is successful and 0
if it fails.
Verify our signature
Finally, we can show the code used to verify the signature. For this scope I create the following function.
int validateSignature(const byte *message, byte *signature, int slot)
{
byte publicKey[64];
int retVal = 0;
ECCX08.generatePublicKey(slot, publicKey);
if (ECCX08.ecdsaVerify(message, signature, publicKey))
{
Serial.println("Verified signature successfully.");
retVal = 1;
}
else
{
Serial.println("Failed to verify signature.");
}
return retVal;
}
The validateSignature
function verifies a digital signature using the ATECC608A cryptographic chip. It takes a message, a signature, and a slot index where the corresponding private key is stored. The function first generates the public key from the specified slot using ECCX08.generatePublicKey
. Then, it calls ECCX08.ecdsaVerify
to check if the provided signature is valid for the given message using the generated public key. If the verification is successful, it prints a success message and returns 1
; otherwise, it prints a failure message and returns 0
.
Testing the whole project
Now, we can test the complete example by uploading the following sketch to your Arduino board. This sketch combines all the previously discussed parts into a single program, without repeating the functions already defined above.
#include <ArduinoECCX08.h>
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("ECCX08 demonstration project");
Serial.println();
if (!ECCX08.begin()) {
Serial.println("Failed to communicate with the HSM");
while (1);
}
Serial.println("HSM initialized successfully!");
}
void loop()
{
const byte message[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
};
byte signature[64];
int slot = 0;
if (!generatePrivateKey(slot))
{
while(1);
}
if(!signMessage(message, signature, slot))
{
while(1);
}
if(!validateSignature(message, signature, slot))
{
while(1);
}
}
The following sketch initializes the ATECC608A cryptographic chip and performs a complete signing and verification process. In the setup
function, we establish the serial communication and check if the chip is properly detected. If initialization fails, the program halts. In the loop
function, a predefined 32-byte message is signed using a private key stored in the specified slot. The resulting signature is then validated using the corresponding public key. If any step fails, the program enters an infinite loop to indicate an error.
Upload the sketch to your Arduino board and run the example. The following output should appear in the serial monitor, confirming the successful execution of the signing and verification process:

Conclusion
The ATECC608A HSM on the Arduino Uno offers an effective way to add security to IoT projects. By combining hardware-based encryption, secure key storage, and authentication, it simplifies protecting sensitive data and ensuring trusted communication.
Now that you’ve learned how to set up and use this HSM, take the next step: experiment with its features and integrate them into your own IoT projects. Strengthen your devices and contribute to a more secure IoT ecosystem!