Common Mistakes with AES on Microcontrollers
AES itself is not the problem. The cipher is sound and has resisted decades of analysis. What breaks encryption on embedded devices is how AES gets used: the mode, the IV, the key, the randomness, and the assumptions around them. These are the mistakes I find most often, and each one quietly defeats the protection while the code appears to work.
The Cipher Is Fine, the Usage Is Not
When an assessment finds broken encryption on a device, the flaw is almost never in AES. It is in the dozen decisions that surround the cipher, which mode, how the IV is chosen, where the key comes from, whether integrity is checked, and the mistakes are easy to introduce by reaching for a library default. The code compiles, the data comes back decrypted, and the device ships with encryption an attacker walks straight through. Here is the short list:
| Mistake | Why it breaks | Fix |
|---|---|---|
| ECB mode | Identical plaintext blocks leak as identical ciphertext | Use an authenticated mode |
| Reused or static IV | Repeats under one key; CTR reuse leaks plaintext | Fresh unique IV per message from real randomness |
| No integrity check | Bit-flips and splicing go undetected | Authenticated encryption with a tag |
| GCM nonce reuse | Recovers the auth subkey and enables forgery | A counter nonce that never repeats or rolls back |
| Keys from weak randomness | A 256-bit key from a 16-bit seed has 16 bits | Seed from a hardware RNG; gather entropy first |
| Key reused across purposes | One compromise loses everything at once | One key per purpose via a KDF |
Each row is a usage mistake, not a weakness in AES, and each undermines the protection while the code looks correct. The rest of this shows what each one looks like in real embedded code and how to fix it.
ECB Mode Leaks Structure
The simplest mode, ECB, encrypts identical plaintext blocks to identical ciphertext blocks, so any structure in the data, repeated values, patterns, fixed headers, survives as repeated patterns in the ciphertext. It is the mode a library hands you if you call the raw block function in a loop.
// the mistake: encrypting block by block in ECB
for (i = 0; i < len; i += 16)
aes_encrypt(key, plaintext + i, ciphertext + i); // ECB, leaks patterns The classic demonstration is encrypting an image in ECB and still seeing the picture, because identical regions encrypt identically. On a device, ECB over structured telemetry or configuration leaks far more than anyone intends.
Reused or Static IVs
Modes like CBC and CTR need a unique initialization vector per message. The IV need not be secret, but it must not repeat under the same key. A fixed IV compiled into the firmware, or one that resets to zero each boot, undermines confidentiality and, in CTR mode, can expose the key stream and the data outright.
// the mistake: a hardcoded IV reused for every message
uint8_t iv[16] = {0}; // same IV every time
aes_cbc_encrypt(key, iv, pt, ct, len);two messages with the same first block now produce the same first ciphertext block -> an observer learns when payloads repeat, and CTR-mode reuse can leak plaintext via xor of two ciphertexts
Hardcoded and reused IVs are one of the most common embedded crypto mistakes, because generating a fresh one each time takes a random source the device may not have set up. The fix is a unique IV per message from real randomness, stored or transmitted alongside the ciphertext.
No Integrity Check
Encryption hides data, it does not detect tampering. With a plain mode like CBC or CTR and no separate check, an attacker can flip bits in the ciphertext to cause controlled changes in the plaintext, or splice messages, without ever knowing the key.
// the mistake: encrypt-only, no authentication aes_ctr_encrypt(key, iv, pt, ct, len); // confidentiality only // an attacker who flips a ciphertext bit flips the same plaintext bit
This is why bit-flipping attacks work against unauthenticated encryption, and why a command channel protected with CTR alone can be manipulated into issuing different commands. Encryption without integrity solves half the problem and leaves the other half wide open.
Reach for Authenticated Encryption
The fix for the integrity gap is an authenticated mode that provides confidentiality and integrity together. AES-GCM is the common choice: it encrypts and produces an authentication tag, and decryption fails if the ciphertext or the associated data was altered.
// the fix: authenticated encryption with AES-GCM aes_gcm_encrypt(key, iv, aad, pt, ct, tag, len); // receiver: aes_gcm_decrypt(...) returns FAIL if tag mismatch -> reject
tampered ciphertext -> tag verification fails -> decryption rejected # the device never acts on data an attacker modified
Choosing AES-GCM by default avoids the common pattern of encrypting without any integrity check. But it is not a free pass: GCM fails badly if a nonce is reused under the same key, since an attacker who sees two messages with the same nonce can recover the authentication subkey and forge messages. On a constrained device, a counter-based nonce kept in non-volatile storage, one that never repeats and never rolls back, is often safer than a random one. Switching to GCM does not remove the IV problem, it changes its shape.
Keys from Weak Randomness
An AES key is only as strong as the randomness that produced it. A key derived from a predictable seed, a boot timer, a fixed value, or a poorly seeded PRNG, is guessable no matter how many bits it nominally has: a 256-bit key from a 16-bit seed has 16 bits of real strength.
// the mistake: keying from a predictable source srand(boot_time_ms()); // low-entropy, somewhat predictable seed for (i=0;i<32;i++) key[i]=rand(); // "256-bit" key, far less real entropy
Embedded devices are notoriously poor sources of randomness at boot, when they have no entropy yet, which is exactly when key generation often happens. Use a hardware random number generator if the part has one, gather entropy before generating keys, and never key from timers or counters.
Test the Randomness You Rely On
Because weak randomness silently weakens every key, it is worth testing a device’s random source rather than trusting it. Capturing a large sample and running it through a statistical test suite reveals bias and patterns that betray a poor generator.
# collect RNG output from the device and test it for bias
dieharder -a -f device_rng_sample.bin | grep -iE 'WEAK|FAILED'diehard_birthdays WEAK rgb_lagged_sum FAILED # the device's "random" output is biased -> keys are weaker than their bit length
A random source that fails statistical tests is producing keys an attacker can attack far below their nominal strength. Catching that on the bench, before the keys protect anything, is the difference between a 256-bit key and a 256-bit label on a guessable value.
Separate Keys, and Do Not Roll Your Own
Two more quiet mistakes round out the list. Using one key for everything, encryption, message authentication, session derivation, means one compromise loses everything at once and can create interactions that weaken all of them; the clean pattern is one key per purpose, derived from a master secret with a proper KDF so the keys are independent, which is a cheap call at startup.
And teams sometimes assemble their own scheme from AES plus a hash plus some glue, where the glue is where it breaks: encrypt-and-MAC versus encrypt-then-MAC ordering, a MAC that does not cover the IV, an unauthenticated length field. The reliable move is a vetted authenticated mode or library construction rather than composing one, since AES-GCM and ChaCha20-Poly1305 exist precisely so you do not have to get the composition right yourself. Even the convenient path of library defaults is not automatic, some default to ECB for the raw cipher and leave nonce management to you, so confirm the mode and the IV handling for the function you call.
What I Check in a Review
When I review embedded crypto, I am rarely looking for a flaw in AES itself; I am checking the mode, the IV or nonce handling, whether integrity is verified, and where the key and the randomness come from. Those four areas account for the overwhelming majority of real-world failures I find. A report that says the cipher is broken is rare and dramatic; a report that says you are using CBC with a hardcoded IV and no MAC is common and fixable, and it points the team straight at the change that turns decorative encryption into real protection.
Where This Fits
Reviewing how a device actually uses cryptography, not whether the algorithm is strong but whether the usage is sound, is a core part of a product security assessment. If you want your embedded crypto usage reviewed for these failure modes, that is the kind of work we do at Berkner Tech.



