IoT Cryptography: “Good Enough”
The goal is to use cryptography well enough to understand it and design with it. This article skips heavy mathematics and focuses on the concepts, parameters, and common mistakes that actually matter.
Core Concepts
- Confidentiality: prevent data from being read without permission
- Integrity: prevent data from being changed silently
- Identity: know who is talking to whom
- Symmetric keys: one key encrypts and decrypts, such as AES or ChaCha20-Poly1305
- Asymmetric keys: public/private key pairs, such as ECDSA, Ed25519, ECDH, or X25519
- AEAD: authenticated encryption with associated data, combining encryption and authentication in one step
- MAC: message authentication code, which authenticates but does not encrypt
- KDF: key derivation function, which turns raw secrets into usable keys
- Key formats: SPKI for public keys, PKCS8 for private keys, DER for binary encoding, PEM for text encoding
One Picture
[A private key + B public key] --(ECDH/X25519)--> [shared secret S]
[S] --(HKDF salt, info, L)--> [symmetric key K]
[message M, AAD] --(AEAD: K, nonce)--> [C || tag]
[M] --(HMAC/CMAC: K)--> [tag]
[M] --(signature: private key)--> [signature]
- AEAD always produces ciphertext plus tag, and the tag must be checked before decryption
- MACs and signatures do not encrypt data
- A shared secret is not a final key; it must go through a KDF first
Parameter Cheat Sheet
- Nonce / IV:
- GCM: 12 bytes
- ChaCha20-Poly1305: 12 bytes
- CCM: commonly 13 bytes
- CBC: 16 bytes
- Key sizes:
- AES: 16 / 24 / 32 bytes
- ChaCha20-Poly1305: 32 bytes
- Signature encodings:
- ECDSA: DER or raw
(r || s)form - Ed25519: raw form
- ECDSA: DER or raw
- Key containers:
- SPKI for public keys
- PKCS8 for private keys
When to Use What
- Constrained devices or wireless protocols: AES-CCM for hardware acceleration, or ChaCha20-Poly1305 for software-friendly performance
- Web and general-purpose systems: AES-GCM or ChaCha20-Poly1305
- Integrity only: HMAC-SHA256 or AES-CMAC
- Firmware signing or long messages: Ed25519 or ECDSA P-256
- Session establishment: ECDH P-256 or X25519, then HKDF, then AEAD
Attack and Defense Notes
- Never reuse an AEAD nonce under the same key
- CBC does not authenticate data, so it must be paired with a MAC or replaced by AEAD
- Key exchange must be signed to prevent man-in-the-middle attacks
Quick Start
You can verify these operations quickly in the CryptoBox web page:
- Symmetric: AES-GCM / CCM / CBC, ChaCha20-Poly1305
- Hash: SHA-256
- MAC: HMAC-SHA256, AES-CMAC
- KDF: HKDF-SHA256, PBKDF2-SHA256
- Sign / Verify: ECDSA P-256, Ed25519
- Key Exchange: ECDH P-256, X25519
- X.509: certificate parsing and conversion
- Codec: text / hex / base64 / PEM / DER conversion
- Random: random number generation
Use HEX by default. If a test vector is given in Base64, convert it to HEX first.
Hash
SHA-256
Principle
- Compresses input of arbitrary length into a fixed 32-byte digest. A small change in the input produces a large change in the output.
- It can tell you whether data changed, but not who changed it.
[message bytes] --(SHA-256)--> [32-byte digest]
Hands-On
- Page: Hash
- Input (HEX):
68656c6c6f("hello") - Click Compute
- Expected output (HEX):
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
Message Authentication Code
HMAC-SHA256
Principle
- A hash-based MAC: it proves that the sender knows the secret key and that the message was not altered.
- Intuition: two rounds of hashing, with the key mixed in on the inner pass and again on the outer pass.
tag = H( K ⊕ opad || H( K ⊕ ipad || msg ) )
Hands-On
- Page: MAC -> HMAC-SHA256
- Key (HEX):
0102030405060708090a0b0c0d0e0f1011121314 - Input (HEX):
48656c6c6f("Hello") - Click Compute
- Expected tag (HEX):
6a14dddf21c1cda3ed32da7461cecad54717d31dcfcdc9be4a11d25086df696f
AES-CMAC
Principle
- A MAC built on AES. It derives subkeys K1 and K2, adjusts the last block, and then aggregates with AES-CBC-style processing.
[msg] --(split / pad)--> [M1..Mn]
last = (full block? K1 : K2) ⊕ Mn
tag = the last 16 bytes of AES-CBC-Encrypt(key, IV=0, M1..M{n-1}, last)
Hands-On
- Page: MAC -> AES-CMAC
- Key (HEX):
2b7e151628aed2a6abf7158809cf4f3c - Input (HEX):
6bc1bee22e409f96e93d7e117393172a - Click Compute
- Output:
070a16b46b4d4144f79bdd9dd04a287c
Symmetric Encryption and AEAD
AES-GCM
Principle
- Uses counter-mode encryption plus GHASH authentication. The nonce must be 12 bytes, and it must never be reused with the same key.
key K, nonce N
C = CTR_Encrypt(K, N, P)
tag = GHASH(K, AAD, C)
output = C || tag
Hands-On
Page: Symmetric -> AES-GCM
Input (HEX):
00112233445566778899aabbccddeeff0001020304050607Key (HEX):
000102030405060708090a0b0c0d0e0fNonce (HEX):
000102030405060708090a0b(12 bytes)AAD (HEX, optional):
0c0d0e0f101112131415161718191a1bClick Encrypt
Output (HEX, ciphertext||tag):
937d85fd224e9123c34bcb31fa7e9ef7b32718e557e8fbf14c987e199c7d4d165cb8826e52c2d1f0
ChaCha20-Poly1305
Principle
- ChaCha20 provides encryption, and Poly1305 provides authentication. It fits software implementations and constrained systems without AES acceleration.
C = ChaCha20_Encrypt(key, nonce, P)
tag = Poly1305(key', AAD || C)
output = C || tag
Hands-On
- Page: Symmetric -> ChaCha20-Poly1305
- Input (HEX):
0102030405060708 - Key (HEX):
0000...00(32 bytes of zero) - Nonce (HEX):
0000...00(12 bytes of zero) - Click Encrypt
- Output (HEX, ciphertext||tag):
9e05e4ba50573f7216b4569116f74ad2ff14a1f5698d0dda
AES-CCM
Principle
- CCM combines CTR encryption with CBC-MAC authentication. It is common in embedded and wireless systems.
tag = CBC-MAC(key, B0 || AAD' || PT')
C = CTR_Encrypt(key, nonce, P)
output = C || tag
Hands-On
- Page: Symmetric -> AES-CCM
- Input (HEX):
00112233445566778899aabbccddeeff - Key (HEX):
00000000000000000000000000000000 - Nonce (HEX):
0f0e0d0c0b0a09080706050403(13 bytes) - AAD (HEX):
aabbccdd - Tag Length:
8(64 bits) - Click Encrypt
- Output (HEX, ciphertext||tag):
0b351eb48bc68b348aca2cd428d4864dedec3573620d52d5
AES-CBC (PKCS7/NoPadding)
Principle
- CBC is a block-chaining mode: each plaintext block is XORed with the previous ciphertext block before encryption. By default it uses PKCS7 padding;
NoPaddingrequires 16-byte alignment. CBC does not provide integrity by itself, so AEAD is preferred. - AES is a block cipher with a fixed 128-bit block size, so you must pad the input or make it a multiple of 16 bytes.
P0 = AES-Decrypt(K, C0) ⊕ IV
Pi = AES-Decrypt(K, Ci) ⊕ C{i-1}
Hands-On
- Page: Symmetric -> AES-CBC
- Input:
00112233445566778899aabbccddeeff - Key:
000102030405060708090a0b0c0d0e0f - IV:
0f0e0d0c0b0a09080706050403020100 - Click Encrypt
- Then switch to
NoPaddingif your input is already 16-byte aligned
Key Derivation (KDF)
HKDF-SHA256
Principle
- HKDF extracts and expands key material. It turns a raw secret, such as an ECDH/X25519 shared secret, into a usable symmetric key.
OKM = HKDF( IKM, salt, info, L )
Hands-On
- Page: KDF -> HKDF-SHA256
- Input (HEX):
000102030405060708090a0b0c0d0e0f - Salt (HEX): leave empty
- Info (HEX): leave empty
- Length:
32 - Click Derive
- OKM (HEX):
37ad29109f43265287804b674e2653d0a513718907f97fca97c95bded8104bbf
PBKDF2-SHA256
Principle
- Password stretching: repeated HMAC iterations significantly increase brute-force cost. The salt should be random and must not be reused.
DK = PBKDF2( password, salt, iterations, length )
Hands-On
- Page: KDF -> PBKDF2-SHA256
- Password (HEX):
70617373776f7264("password") - Salt (HEX):
73616c74("salt") - Iterations:
4096 - Length:
32 - Click Derive
- DK (HEX):
c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a
Sign / Verify
Principle
- Sign with the private key and verify with the public key. ECDSA (Elliptic Curve Digital Signature Algorithm) commonly uses DER or RAW encoding for
(r, s), while Ed25519 has a simpler interface.
sig = Sign(privateKey, H(message))
ok = Verify(publicKey, H(message), sig)
ECDSA-P256 (DER/RAW)
Principle
- Uses the private key to authenticate a message in a non-repudiable way; the public key verifies it. ECDSA produces two large integers,
rands, and common encodings are DER and RAW (r || s, 64 bytes).
Hands-On
Page: Sign / Verify -> ECDSA-P256
Message (HEX):
616263("abc")Public Key (SPKI, HEX):
3059301306072a8648ce3d020106082a8648ce3d030107034200047a593180860c4037c83c12749845c8ee1424dd297fadcb895e358255d2c7d2b2a8ca25580f2626fe579062ff1b99ff91c24a0da06fb32b5be20148c9249f5650Sig Encoding:
DERSignature (HEX):
30440220334d7ae5ce7dfa1f69c24acd19601442bda82b4c3dd0a26890f0053066325f72022046cfb76d8f55b0da16e3f82f34ea7264abc85305333ce6db49f48e9c4587d3a9Click Verify
Verify result: success
Ed25519
Principle
- A modern curve signature with a direct interface and good performance. Unlike ECDSA, there is no DER/RAW encoding split to worry about.
Hands-On
Page: Sign / Verify -> Ed25519
Message (HEX):
68656c6c6f("hello")Public Key (SPKI, HEX):
302a300506032b657003210003a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8Signature (HEX):
e1a7fca94a835127885b99e2eba733d6ee5bf5dc463ed8385eb6f1dcaa1117c0f151750a10f46f5b3796a91203578f702c85c67c334b5689a516284d499f710fClick Verify
Verify result: success
Key Exchange
Principle
- Both sides use “my private key + your public key” to derive the same shared secret. That secret must then be expanded through HKDF (HMAC-based Key Derivation Function) into a session key.
S = ECDH(a_priv, b_pub) = ECDH(b_priv, a_pub)
or S = X25519(a_priv, b_pub) = X25519(b_priv, a_pub)
ECDH-P256
Principle
- A uses a private key and B uses a public key, and both independently compute the same shared secret. The secret is not used directly for encryption; it must be expanded with HKDF into an AEAD key.
Hands-On
Page: Key Exchange -> ECDH-P256
A private key (PKCS8.DER):
308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420009f3d9f3d9f3d9f3d9f3d9f3d9f3d9f3d9f3d9f3d9f3d9f3d9f3d9f3d9f3d9fa1440342000408fb02eb998e991b5a615279ac576b5524fcd6b5a26e685aa0c96e88867625a59a106cd6a63087ff2a27c31c392647d18cc5fa8ba9f196e60a04eace8de6f38dB public key (SPKI.DER):
3059301306072a8648ce3d020106082a8648ce3d030107034200045bc3633e112acf5d0abb90f486776ba3500c19b5d095d7ea7163c7846f61f10cce93315c6c3b4dbfee3557a6677f58721c14a18bafc1fe253678e32b7a8337bfClick Derive
Shared Secret (HEX):
22a91d018252d0169da3bcb269c2e3852f08b27d2ef5e6c27e4f2ecd4b8e7463
X25519
Principle
- A modern key-exchange curve with a direct interface and good performance. Like ECDH, it outputs a shared secret, which must go through HKDF -> AEAD before it can be used for encryption.
Hands-On
Page: Key Exchange -> X25519
A private key (PKCS8.DER):
302e020100300506032b656e04220420000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1fB public key (SPKI.DER):
302a300506032b656e03210087968c1c1642bd0600f6ad869b88f92c9623d0dfc44f01deffe21c9add3dca5fClick Derive
Shared Secret (HEX):
dae0079aea6e6d02ca215a60d5d8f6689c3ed6009d41882b9181ff2481d9e27a
Certificates and Key Formats
X.509 Certificate
- A certificate is the carrier for a public key, identity, validity period, and extensions (X.509 v3).
- Public keys are usually stored in SPKI, and private keys are usually stored in PKCS8. The encoding can be DER (binary) or PEM (text).
[certificate] = { public key (SPKI), subject / issuer, validity, extensions, signature }
DER(bytes) ⇄ PEM(text wrapper)
Hands-On
Page: X.509
Paste the PEM below into the input area:
-----BEGIN CERTIFICATE----- MIICjTCCAjSgAwIBAgIUQ6eSZ5V2VDhwWNpPYbkmhbNO6JkwCgYIKoZIzj0EAwIw gYQxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlq aW5nMQwwCgYDVQQKDANJb1QxDDAKBgNVBAsMA0RldjEUMBIGA1UEAwwLZXhhbXBs ZS5jb20xHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wHhcNMjUwODEw MTcwODE3WhcNMzUwODA4MTcwODE3WjCBhDELMAkGA1UEBhMCQ04xEDAOBgNVBAgM B0JlaWppbmcxEDAOBgNVBAcMB0JlaWppbmcxDDAKBgNVBAoMA0lvVDEMMAoGA1UE CwwDRGV2MRQwEgYDVQQDDAtleGFtcGxlLmNvbTEfMB0GCSqGSIb3DQEJARYQdGVz dEBleGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCE1SJx84gOs MdT3I7UGLlrKZpHSuRc2mnoeepHkWGfo72iiLr5DJoxCyJJrxsnNvYQEhBg6vsum JyHMX6mVylGjgYEwfzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIFoDAdBgNVHSUEFjAU BggrBgEFBQcDAQYIKwYBBQUHAwIwJwYDVR0RBCAwHoILZXhhbXBsZS5jb22CD3d3 dy5leGFtcGxlLmNvbTAdBgNVHQ4EFgQU8+X7PvzqGxEGxEaCcjN5Evp6NC0wCgYI KoZIzj0EAwIDRwAwRAIgO5/F8Mu2KSP3zzxhp6n9HgZT0LIvOjXbMILZF94arY0C IG8cF+T8wn1VyGgbwqFvFLy36qufJ2TgeL/l4mkrj9az -----END CERTIFICATE-----Click Parse and Convert
Parsed text includes:
Public-Key: (256 bit),Subject/Issuer,Validity, extension OIDs, and moreConvert can round-trip between PEM and DER-HEX
Codec Conversion
- Data is bytes; display and exchange need encodings. Hex/Base64 are string representations of bytes, and PEM is a Base64 wrapper around DER (binary).
bytes ⇄ hex / base64
DER(bytes) ⇄ PEM("-----BEGIN ..." + base64 + "-----END ...")
Hands-On
- Page: Codec
- Convert Base64 PKCS8/SPKI to HEX before pasting into other pages
- Common flow: Base64 -> HEX, or HEX -> Base64
Random Numbers (RNG)
- CSPRNGs (cryptographically secure pseudo-random number generators) are used to generate keys, nonces, and salts.
- Length must match the purpose: keys (16/24/32 bytes), GCM/ChaCha20-Poly1305 nonces (12 bytes), CCM nonces (13 bytes).
Random(n) -> [n random bytes] -> (key / nonce / salt)
Hands-On
- Page: Random
- Choose 16 / 32 / 64 bytes, click Generate, and paste the output into other pages as needed
Glossary
| Abbreviation | Full Name | Description |
|---|---|---|
| AEAD | Authenticated Encryption with Associated Data | Authenticated encryption with associated data, providing both encryption and authentication |
| MAC | Message Authentication Code | A message authentication code used to verify integrity and authenticity |
| HMAC | Hash-based Message Authentication Code | A MAC built on a hash function |
| KDF | Key Derivation Function | A function that derives working keys from raw key material |
| HKDF | HMAC-based Key Derivation Function | A key derivation function based on HMAC |
| PBKDF2 | Password-Based Key Derivation Function 2 | Version 2 of the password-based key derivation function |
| AES | Advanced Encryption Standard | A symmetric encryption standard |
| GCM | Galois/Counter Mode | A Galois/Counter mode, one AEAD construction |
| CCM | Counter with CBC-MAC | Counter mode with CBC-MAC, another AEAD construction |
| CBC | Cipher Block Chaining | A classic block-cipher mode |
| CTR | Counter Mode | A block-cipher mode that turns a block cipher into a stream-like construction |
| GHASH | Galois Hash Function | The hash function used by AES-GCM |
| SHA | Secure Hash Algorithm | A family of secure hash algorithms |
| ECDSA | Elliptic Curve Digital Signature Algorithm | Elliptic-curve digital signature algorithm |
| ECDH | Elliptic Curve Diffie-Hellman | Elliptic-curve Diffie-Hellman key exchange |
| SPKI | Subject Public Key Info | Subject public key information, a public-key container format |
| PKCS8 | Private-Key Information Syntax Standard | Private-key information syntax standard, a private-key container format |
| DER | Distinguished Encoding Rules | A binary encoding format |
| PEM | Privacy-Enhanced Mail | A text encoding format based on Base64 |
| RSA | Rivest-Shamir-Adleman | A public-key algorithm family |
| ChaCha20 | Chaotic Cipher 20 | A stream cipher |
| Poly1305 | Polynomial Evaluation in a 1305-bit finite field | A one-time authenticator, often used with ChaCha20 |
| Ed25519 | Edwards Curve 25519 | A signature scheme based on the Edwards 25519 curve |
| X.509 | ITU-T standard | A public-key certificate standard |
| X25519 | Edwards Curve 25519 | A key-exchange algorithm based on Curve25519 |
| CSPRNG | Cryptographically Secure Pseudo-Random Number Generator | A cryptographically secure pseudo-random number generator |
| MITM | Man-in-the-Middle | An attack in which the attacker secretly relays data between two parties |