Device communication designs often say “encrypt it with AES”. That is too vague.
The real questions are: only encrypt, or also authenticate? Can tampering be detected? Is replay handled? Where does nonce come from? Can the device reuse nonce after power loss?
Modern protocols usually use AEAD instead of raw encryption.
AEAD = Authenticated Encryption with Associated Data
= encryption + integrity authentication + optional authenticated cleartext context
Common AEAD algorithms include:
- AES-GCM
- AES-CCM
- ChaCha20-Poly1305
AEAD is a strong default for IoT and network protocols, but it has a hard boundary: under the same key, nonce must not repeat.
Symmetric Encryption Provides Confidentiality With a Shared Key
Symmetric encryption uses the same key to encrypt and decrypt:
plaintext + key -> ciphertext
ciphertext + key -> plaintext
It fits scenarios where device and server already share a session key or device key.
But encryption alone only provides confidentiality. If an attacker cannot read the content, that does not mean they cannot modify the ciphertext.
Traditional encryption without authentication can lead to:
- ciphertext changes producing modified plaintext
- padding oracle style protocol attacks
- receiver unable to confirm integrity
- corruption appearing only at the business layer
Modern designs should not manually combine “encryption plus checksum”. Use AEAD when possible.
AEAD Protects Ciphertext and Associated Data
AEAD usually takes:
key
nonce
plaintext
AAD
and outputs:
ciphertext
tag
Where:
plaintextis encryptedAADis not encrypted but is authenticatedtagverifies that ciphertext and AAD were not modified
AAD is useful for protocol fields that must remain visible but must not be modified:
- device_id
- message_type
- protocol_version
- sequence_number
- topic
- header flags
The receiver must verify the tag before trusting decrypted data. If tag verification fails, plaintext should not be handed to business logic.
nonce Is Not Secret, but Must Be Unique
nonce is sometimes called IV. It usually does not need to be secret, but it must satisfy the algorithm’s requirements.
For many AEAD modes, the most important rule is:
under the same key, nonce must not repeat
Repeating nonce is not “slightly weaker”. It can be catastrophic.
For stream-cipher-like constructions, the same key and nonce produce the same keystream. Encrypting two plaintexts under the same keystream leaks relationships between plaintexts.
For AES-GCM-like modes, nonce reuse can also damage authentication security and enable tag forgery.
So nonce is not random decoration. It is protocol state.
Power Loss Makes nonce Design Hard
Servers can maintain monotonic counters more easily. Embedded devices have harder constraints:
- power loss can roll back RAM counters
- flash counter writes cause wear and power-loss consistency problems
- RTC may be inaccurate or reset
- factory reset may clear state
- concurrent senders may race for the same counter
- multiple channels may share one key
If a device builds nonce from (key, counter) but keeps counter only in RAM, rebooting from zero reuses nonce under the same key.
This kind of bug often does not look like “encryption failure”. It silently destroys the security assumptions.
Common nonce Designs
Several patterns are common.
First, monotonic counter:
nonce = fixed_prefix || counter
It avoids collisions if the counter never rolls back or races. The hard parts are persistence and power-loss consistency.
Second, random nonce:
nonce = random(96 bits)
It avoids persistent counters if randomness is strong and collision probability is acceptable. The hard part is small-device entropy, especially early at boot.
Third, device-unique prefix plus counter:
nonce = device_or_session_prefix || message_counter
This helps with multiple devices or sessions, but prefix and key scope must match, and counter still must not roll back.
Fourth, session key plus per-session counter:
long_term_key -> key exchange / KDF -> session_key
session_key + per-session counter -> AEAD nonce
This is common in security protocols. The long-term key does not encrypt bulk data directly. Each session derives a fresh key, and nonce only needs to be unique within the session.
AAD Is Cleartext but Authenticated
AAD is often misread as unimportant metadata.
It is not encrypted, so attackers can see it. But it is included in tag computation, so attackers cannot silently modify it.
Typical use:
AAD = device_id || message_type || sequence_number
plaintext = sensor_payload
The receiver can verify that this ciphertext is bound to this device, message type, and sequence number.
If sequence number is not in AAD, attackers may not decrypt payload, but they may be able to reorder or swap headers. Business context becomes polluted.
AEAD Does Not Automatically Prevent Replay
AEAD detects modification of ciphertext and AAD, but it does not automatically reject old valid messages.
If an attacker records:
nonce, AAD, ciphertext, tag
and later sends the exact same tuple, the tag is still valid. AEAD cannot know by itself that the message was used before.
Replay protection usually needs protocol state:
- monotonic sequence numbers
- sliding windows
- timestamps with tolerance windows
- session IDs
- challenge/response
- server-side tracking of recent nonce or counter values
Nonce uniqueness and replay protection must be designed together. Nonce uniqueness is an encryption safety requirement. Rejecting old messages is a protocol-state requirement.
Tag Failure Means Stop
During AEAD decryption, tag failure means authentication failed.
Avoid:
- handing partial plaintext to business logic after tag failure
- returning detailed differences that create an oracle
- retrying with the same nonce and different plaintext
- logging sensitive plaintext or key material
- truncating tags too aggressively
The tag is the authentication result, not a checksum. Failure means ciphertext, AAD, nonce, or key does not match. Business logic should not guess.
When AEAD Is Not the Right Tool
Most new designs should prefer AEAD, but there are exceptions:
- public file checksum: hash is enough
- authentication without encryption: HMAC/CMAC may fit better
- protocol already specifies a construction: follow the protocol
- hardware supports only a limited mode: evaluate authentication and nonce separately
- key wrapping, disk encryption, and similar domains: use dedicated modes and constraints
Do not choose AES-CBC plus your own HMAC only because “it uses AES”, unless ordering, padding, error behavior, and compatibility requirements are fully understood. New protocols should usually use mature AEAD.
Debugging Order
For encrypted device communication failures, reboot-only issues, or server tag errors, inspect:
is AEAD used
-> what is the key scope
-> is nonce unique under the same key
-> can nonce roll back after reboot
-> can concurrent senders reuse a counter
-> does AAD cover critical headers
-> does the receiver check replay
-> is tag failure immediately rejected
-> is randomness available early at boot
If the problem appears only after power loss, factory reset, fleet deployment, or long runtime, focus on nonce persistence, key rotation, and counter boundaries.
The Boundary to Remember
AEAD is an excellent default for modern device communication because it puts encryption and authentication into one clear interface.
But AEAD security is not automatic just because AES-GCM was called. It depends on key, nonce, AAD, tag, and replay state being used correctly together.
Under the same key, nonce must not repeat. AAD should cover unencrypted context that must not change. Tag failure must stop processing. Replay protection belongs to protocol state. For devices, the hardest part is often not the algorithm, but keeping nonce unique across power loss, reboot, concurrency, and long-term operation.