Hi there,
Ok , so Looks like it’s common, I was surprised my colleagues agree
The combination of ECDH + AES encryption is a modern and secure design pattern, widely used in both consumer and industrial products for secure communication over constrained wireless channels like BLE.
From experience, here are the usual stumbling blocks:
1. Lack of Serial Output / Silent Failures
- Often due to stack overflow, malloc failures, or MbedTLS entropy/seeding issues.
- The Xiao nRF52840 has 256KB of RAM, but Arduino’s heap layout can be limiting.
Fix tip: Make sure they run
mbedtls_memory_buffer_alloc_init()
and avoid heap-heavy objects unless needed.
2. Missing Platform Functions
- Some MbedTLS features (like entropy collection or platform time) are stubbed or unimplemented in Arduino builds.
- ECDH requires seeding RNG properly, and if that’s broken, the keygen silently fails.
Fix tip: Explicitly seed using
mbedtls_ctr_drbg_seed()
withmbedtls_entropy_func
and a real entropy source.
3. MbedTLS not linked correctly
- Even though
mbedtls
compiles, not all features may be included in the build. - If they included
mbedtls/config.h
without proper flags (e.g., enablingMBEDTLS_ECDH_C
andMBEDTLS_BIGNUM_C
), the result can be undefined behavior or missing functions.
You must define config flags like:
#define MBEDTLS_ECDH_C
#define MBEDTLS_ECP_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_CTR_DRBG_C
Yes, it’s absolutely possible to:
- Perform ECDH (e.g., using Curve25519 or secp256r1),
- Derive a shared key,
- Encrypt/decrypt with AES using MbedTLS on the Xiao nRF52840.
But Arduino IDE + MbedTLS is a tough combo without debug support, so you may want to:
- Test smaller ECDH-only examples first,
- Print intermediate steps (
Serial.print()
), - Consider switching to Zephyr or nRF SDK later for more stability and easier debugging.
The flow is valid and secure in principle:
- ECDH → derive a shared secret between two BLE peers.
- Use that shared secret to derive a symmetric AES key.
- Use AES (e.g. AES-GCM or AES-128-CBC) to encrypt/decrypt the BLE data payload.
This is a classic hybrid encryption scheme: asymmetric key agreement (ECDH) + symmetric encryption (AES). It’s widely used in secure communications (e.g. TLS, Signal protocol).
here is an older lightweight example for the Xiao with a different POV.
mind the header , i.e. must use BSP 1.1.1 for it to run.
/*
* Xiao nRF52840 - ECDH Key Exchange + AES-128 Encryption Example
* Uses MbedTLS (included with Seeed nRF52 BSP 1.1.1+)
*/
#include <mbedtls/ecdh.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#include <mbedtls/aes.h>
void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println("Starting ECDH + AES example...");
// === Setup ===
mbedtls_ecdh_context ctx;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
const char *pers = "ecdh";
mbedtls_ecdh_init(&ctx);
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);
if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
(const unsigned char *)pers, strlen(pers)) != 0) {
Serial.println("DRBG seed failed!");
return;
}
// Load curve
mbedtls_ecp_group_load(&ctx.grp, MBEDTLS_ECP_DP_SECP256R1);
// Generate local keypair
if (mbedtls_ecdh_gen_public(&ctx.grp, &ctx.d, &ctx.Q,
mbedtls_ctr_drbg_random, &ctr_drbg) != 0) {
Serial.println("Keygen failed");
return;
}
// For testing, simulate peer public key with own public key
mbedtls_ecp_copy(&ctx.Qp, &ctx.Q);
// Compute shared secret
unsigned char shared_secret[32];
if (mbedtls_ecdh_compute_shared(&ctx.grp, &ctx.z, &ctx.Qp, &ctx.d,
mbedtls_ctr_drbg_random, &ctr_drbg) != 0) {
Serial.println("Shared secret calc failed!");
return;
}
mbedtls_mpi_write_binary(&ctx.z, shared_secret, 32);
Serial.print("Shared Secret: ");
for (int i = 0; i < 32; i++) Serial.printf("%02X", shared_secret[i]);
Serial.println();
// === AES encrypt with 128-bit key ===
mbedtls_aes_context aes;
mbedtls_aes_init(&aes);
const char *plaintext = "HelloBLE12345678"; // 16 bytes
uint8_t ciphertext[16];
mbedtls_aes_setkey_enc(&aes, shared_secret, 128); // Use first 16 bytes
mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, (const uint8_t *)plaintext, ciphertext);
Serial.print("Ciphertext: ");
for (int i = 0; i < 16; i++) Serial.printf("%02X", ciphertext[i]);
Serial.println();
mbedtls_aes_free(&aes);
mbedtls_ecdh_free(&ctx);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
Serial.println("Done.");
}
void loop() {
delay (50); // allows time for bootloader if a crash occurs.
}
HTH
GL PJ
I will say , the nRF54L15 is a better fit However since it’s not out yet You can add more flash or use the grove Expansion base and add more Flash as in the examples for that.