Explain Public Key Cryptography: Describe how two mathematically related keys enable secure communication without pre-shared secrets
Apply RSA Encryption: Calculate RSA key generation parameters and select proper key sizes for IoT key exchange and signatures
Implement Diffie-Hellman: Configure DH key exchange to establish shared secrets over insecure channels
Evaluate Digital Signatures: Verify message authenticity and non-repudiation using public-key signature schemes
In 60 Seconds
Asymmetric encryption uses mathematically linked key pairs — a public key to encrypt and a private key to decrypt — enabling secure key exchange and digital signatures without sharing secrets in advance.
For Beginners: Public Key Cryptography
Asymmetric encryption uses two different keys: a public key (like your mailing address that anyone can know) and a private key (like the key to your mailbox that only you have). Anyone can encrypt a message with your public key, but only your private key can decrypt it. This solves the key-sharing problem of symmetric encryption.
Why it matters for IoT: When you set up a new smart device, it can use the server’s public key to securely establish a connection – no need to pre-share secrets during manufacturing.
Sensor Squad: The Magic Mailbox!
“I have a puzzle,” Sammy the Sensor said. “How can I send Max a secret message if we have never met and cannot safely share a key?”
Max the Microcontroller pulled out two keys. “With public key cryptography! I create a key PAIR – a public key and a private key. I share the public key with everyone, like posting my mailing address. But the private key stays secret with me, like the key to my mailbox. Anyone can drop a letter in, but only I can open it and read it.”
“The cool part is digital signatures work in reverse,” Lila the LED added. “When Max signs a message, he uses his PRIVATE key. Then anyone with his PUBLIC key can verify the signature is real. It proves the message truly came from Max and nobody changed it. It is like a wax seal that only Max’s ring can make, but anyone can check if the seal is genuine.”
“For IoT, we love Elliptic Curve Cryptography – ECC,” Bella the Battery said. “It gives the same security as RSA but with much smaller keys. A 256-bit ECC key is as strong as a 3072-bit RSA key! That means faster processing and less energy usage, which is perfect for tiny devices like us that need to save every drop of battery power.”
10.2 How Asymmetric Encryption Works
Asymmetric encryption (public-key cryptography) uses two related keys: a public key for encryption and a private key for decryption.
Figure 10.1: Asymmetric encryption: encrypt with public key, decrypt with private key
Mathematical Relationship:
Keys are mathematically related but computationally infeasible to derive one from the other
Encryption with public key -> decryption with private key
Signing with private key -> verification with public key
Advantages:
Solves key distribution problem
No secure channel needed for public key exchange
Digital signatures enable authentication
Non-repudiation support
Disadvantages:
100-1000x slower than symmetric encryption
Not suitable for bulk data encryption
Public keys need authentication (certificates)
Key loss means permanent data loss
10.3 RSA Cryptosystem
History: Developed by Rivest, Shamir, and Adleman, published in 1978.
Mathematical Intuition: Why RSA Works
RSA relies on a mathematical “one-way door” – operations that are easy in one direction but nearly impossible to reverse:
Direction
Difficulty
Example
Forward (easy)
Milliseconds
Multiply 2 large primes: 61 x 53 = 3,233
Backward (hard)
Years
Factor 3,233 into primes: ? x ? = 3,233
For small numbers, factoring is trivial. But RSA uses 617-digit numbers (2048 bits). Factoring those would take all the world’s computers billions of years.
The Elegant Trick:
You know a secret shortcut - If you chose the original primes (p and q), you can easily compute the private key
Attackers see only the product - They would need to factor the huge number to find your primes
Math guarantees it works - Euler’s theorem ensures that encrypting then decrypting returns the original message
The Catch: Quantum Computers
Future quantum computers could factor large numbers quickly using Shor’s algorithm. That’s why post-quantum cryptography is being developed. For now, RSA-2048+ remains secure against classical computers.
Step 2: Select public exponent - Choose \(e = 17\) (must be coprime with \(\phi(N) = 3120\)) - Verify: \(\gcd(17, 3120) = 1\) ✓
Step 3: Compute private exponent - Find \(d\) such that \(e \cdot d \equiv 1 \pmod{3120}\) - Using extended Euclidean algorithm: \(d = 2753\) - Verify: \((17 \times 2753) \mod 3120 = 46801 \mod 3120 = 1\) ✓
Result: Public key \((N=3233, e=17)\), Private key \((N=3233, d=2753)\). For RSA-2048 production keys, \(p\) and \(q\) are each 1024-bit primes (309 digits), making \(N\) a 617-digit number.
In practice: Factoring RSA-2048’s 617-digit \(N\) requires \(\approx 2^{112}\) operations (vs AES-128’s \(2^{128}\) brute force). Best known factoring algorithms take centuries on supercomputers – but Shor’s quantum algorithm could factor in polynomial time, driving post-quantum migration.
Recommended for new deployments (10+ year lifespan)
RSA-4096
~152-bit
Maximum security, rarely needed (very slow)
Never Use RSA-1024
RSA-1024 has been deprecated since 2010 and is considered insecure. Academic attacks have demonstrated factorization of 768-bit RSA (2009). Always use RSA-2048 or higher.
Try It: RSA Key Generation and Encryption
Objective: Generate an RSA key pair and use it to encrypt and decrypt a sensor reading.
from cryptography.hazmat.primitives.asymmetric import rsa, paddingfrom cryptography.hazmat.primitives import hashes, serialization# Step 1: Generate RSA-2048 key pairprivate_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)public_key = private_key.public_key()# Step 2: Encrypt a sensor command with the public keymessage =b"CMD:unlock_door,device_id=ESP32_001"ciphertext = public_key.encrypt( message, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ))print(f"Plaintext: {message.decode()}")print(f"Ciphertext: {ciphertext[:32].hex()}... ({len(ciphertext)} bytes)")# Step 3: Decrypt with the private keydecrypted = private_key.decrypt( ciphertext, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ))print(f"Decrypted: {decrypted.decode()}")print(f"Match: {message == decrypted}")
What to Observe:
The ciphertext is always 256 bytes (2048 bits) regardless of message length
Each encryption produces different ciphertext (OAEP adds randomness)
Only the private key can decrypt – the public key cannot reverse the operation
rsaenc_e = {functiongcd(a, b) { return b ===0? a :gcd(b, a % b); }for (let e =3; e < rsaenc_phi; e +=2) {if (gcd(e, rsaenc_phi) ===1) return e; }return3;}
Show code
rsaenc_d = {for (let d =2; d < rsaenc_phi; d++) {if ((d * rsaenc_e) % rsaenc_phi ===1) return d; }return0;}
sig_n =3233sig_e =17sig_d =2753sig_signVal = {// Sign: signature = hash^d mod nconst hashMod = sig_origHash % sig_n;functionmodPow(base, exp, mod) {let result =1n; base =BigInt(base) %BigInt(mod); exp =BigInt(exp); mod =BigInt(mod);while (exp >0n) {if (exp %2n ===1n) result = (result * base) % mod; exp = exp /2n; base = (base * base) % mod; }returnNumber(result); }returnmodPow(hashMod, sig_d, sig_n);}
Show code
sig_verifyVal = {// Verify: recovered = signature^e mod n, compare to hash of received messagefunctionmodPow(base, exp, mod) {let result =1n; base =BigInt(base) %BigInt(mod); exp =BigInt(exp); mod =BigInt(mod);while (exp >0n) {if (exp %2n ===1n) result = (result * base) % mod; exp = exp /2n; base = (base * base) % mod; }returnNumber(result); }returnmodPow(sig_signVal, sig_e, sig_n);}sig_tampHashMod = sig_tampHash % sig_nsig_verified = sig_verifyVal === sig_tampHashMod
Show code
html`<div style="background: #f8f9fa; padding: 1rem; border-radius: 8px; border-left: 4px solid #9B59B6;"><h4 style="color: #2C3E50; margin-top: 0;">Step 1: Hash the Message</h4><p><strong>Original hash:</strong> <code>${sig_origHash}</code> → hash mod n = <code>${sig_origHash % sig_n}</code></p><h4 style="color: #2C3E50;">Step 2: Sign with Private Key (d=${sig_d})</h4><p><strong>Signature</strong> = hash<sup>d</sup> mod n = ${sig_origHash % sig_n}<sup>${sig_d}</sup> mod ${sig_n} = <span style="color: #9B59B6; font-weight: bold;">${sig_signVal}</span></p><h4 style="color: #2C3E50;">Step 3: Verify with Public Key (e=${sig_e})</h4><p><strong>Recovered hash</strong> = signature<sup>e</sup> mod n = ${sig_signVal}<sup>${sig_e}</sup> mod ${sig_n} = <code>${sig_verifyVal}</code></p><p><strong>Tampered message hash:</strong> <code>${sig_tampHash}</code> → hash mod n = <code>${sig_tampHashMod}</code></p><h4 style="color: #2C3E50;">Step 4: Compare</h4><p><strong>Recovered hash:</strong> <code>${sig_verifyVal}</code> ${sig_verified ?'==':'!='} <strong>Received message hash:</strong> <code>${sig_tampHashMod}</code></p><p style="font-size: 1.1em; font-weight: bold; margin-bottom: 0; color: ${sig_verified ?'#16A085':'#E74C3C'};">${sig_verified ?'VERIFIED -- Signature is valid. Message is authentic and unmodified.':'REJECTED -- Signature verification FAILED! Message has been tampered with.'}</p></div>`
10.5 Diffie-Hellman Key Exchange
Interactive: Diffie-Hellman Key Exchange
Diffie-Hellman allows two parties to establish a shared secret over an insecure channel without transmitting the secret itself.
Figure 10.3: Diffie-Hellman: establishing shared secrets over insecure channels
Mathematical Foundation:
Given public \(p\) (prime) and \(g\) (generator):
Alice chooses secret \(a\), computes \(A = g^a \mod p\)
Bob chooses secret \(b\), computes \(B = g^b \mod p\)
They exchange \(A\) and \(B\) publicly
Alice computes \(s = B^a \mod p = g^{ab} \mod p\)
Bob computes \(s = A^b \mod p = g^{ab} \mod p\)
Both arrive at the same shared secret \(s = g^{ab} \mod p\).
Security: Even seeing p, g, A, and B, an attacker cannot compute s without solving the discrete logarithm problem (computationally infeasible for large primes).
Try It: Diffie-Hellman Key Exchange Simulation
Objective: Watch two IoT devices establish a shared secret over an insecure channel.
from cryptography.hazmat.primitives.asymmetric import dhfrom cryptography.hazmat.primitives.kdf.hkdf import HKDFfrom cryptography.hazmat.primitives import hashes# Generate shared parameters (published openly)parameters = dh.generate_parameters(generator=2, key_size=2048)# Device A generates its key pairdevice_a_private = parameters.generate_private_key()device_a_public = device_a_private.public_key()# Device B generates its key pairdevice_b_private = parameters.generate_private_key()device_b_public = device_b_private.public_key()# Exchange public keys over insecure channel# (An eavesdropper sees both public keys but cannot compute the shared secret)# Device A computes shared secret using B's public keyshared_a = device_a_private.exchange(device_b_public)# Device B computes shared secret using A's public keyshared_b = device_b_private.exchange(device_a_public)print(f"Device A shared secret: {shared_a[:16].hex()}...")print(f"Device B shared secret: {shared_b[:16].hex()}...")print(f"Secrets match: {shared_a == shared_b}")# Derive a usable AES key from the shared secretaes_key = HKDF( algorithm=hashes.SHA256(), length=32, salt=None, info=b"iot-session-key").derive(shared_a)print(f"\nDerived AES-256 key: {aes_key.hex()[:32]}...")print("Both devices now share an AES key for symmetric encryption!")
What to Observe:
Both devices independently compute the same shared secret
Only public values are exchanged – the private keys never leave the device
The raw shared secret is derived into a proper AES key using HKDF
Objective: Elliptic Curve Diffie-Hellman (ECDH) provides the same security as classical DH with dramatically smaller keys. A 256-bit ECC key offers security equivalent to a 3072-bit RSA key, making ECDH the preferred key exchange for resource-constrained IoT devices.
from cryptography.hazmat.primitives.asymmetric import ecfrom cryptography.hazmat.primitives.kdf.hkdf import HKDFfrom cryptography.hazmat.primitives import hashes, serializationimport time# ── Compare key sizes: RSA vs ECC ──print("=== Key Size Comparison (equivalent security) ===")sizes = [ ("RSA-2048 / ECC-224", "112-bit", 2048, 224), ("RSA-3072 / ECC-256", "128-bit", 3072, 256), ("RSA-7680 / ECC-384", "192-bit", 7680, 384), ("RSA-15360 / ECC-521", "256-bit", 15360, 521),]print(f" {'Match':<25}{'Security':<12}{'RSA bits':<10}{'ECC bits':<10}{'Ratio'}")for name, sec, rsa_bits, ecc_bits in sizes:print(f" {name:<25}{sec:<12}{rsa_bits:<10}{ecc_bits:<10}{rsa_bits/ecc_bits:.0f}x")# ── ECDH Key Exchange between two IoT devices ──print("\n=== ECDH Key Exchange (SECP256R1 / P-256) ===")# Device A (e.g., temperature sensor) generates ephemeral key pairstart = time.perf_counter()device_a_key = ec.generate_private_key(ec.SECP256R1())keygen_time = (time.perf_counter() - start) *1000device_a_public = device_a_key.public_key()# Device B (e.g., gateway) generates ephemeral key pairdevice_b_key = ec.generate_private_key(ec.SECP256R1())device_b_public = device_b_key.public_key()# Show public key sizes (what gets transmitted over the wire)a_pub_bytes = device_a_public.public_bytes( serialization.Encoding.X962, serialization.PublicFormat.CompressedPoint)print(f" Public key size (compressed): {len(a_pub_bytes)} bytes")print(f" Key generation time: {keygen_time:.1f} ms")# Each device computes the shared secret using the other's public keystart = time.perf_counter()shared_a = device_a_key.exchange(ec.ECDH(), device_b_public)exchange_time = (time.perf_counter() - start) *1000shared_b = device_b_key.exchange(ec.ECDH(), device_a_public)print(f" Key exchange time: {exchange_time:.1f} ms")print(f" Raw shared secret: {shared_a[:16].hex()}...")print(f" Secrets match: {shared_a == shared_b}")# Derive a usable AES-256 key from the raw shared secretaes_key = HKDF( algorithm=hashes.SHA256(), length=32, salt=None, info=b"iot-sensor-session-v1").derive(shared_a)print(f" Derived AES-256 key: {aes_key[:16].hex()}...")print(f"\n Both devices now share an AES key for symmetric encryption")print(f" Only 33 bytes were sent over the air (compressed public key)")
What to Observe:
ECC-256 provides 128-bit security with keys 12x smaller than RSA-3072 – critical for bandwidth-constrained IoT
The compressed public key is only 33 bytes, small enough for a single BLE advertisement packet
Key generation and exchange complete in milliseconds even on general-purpose hardware
The raw ECDH output is passed through HKDF to derive a proper AES key with domain separation
An eavesdropper seeing both public keys cannot compute the shared secret (Elliptic Curve Discrete Logarithm Problem)
Try It: ECC vs RSA Key Size and Bandwidth Explorer
In practice, IoT systems use both symmetric and asymmetric encryption together:
Figure 10.5: Hybrid encryption: asymmetric for key exchange, symmetric for data
Why Hybrid?
Asymmetric (RSA/ECDH): Securely exchange session keys (slow, but only once)
Symmetric (AES): Encrypt all data with session key (fast, continuous)
This is exactly how TLS/HTTPS works – and why it is the standard for IoT security.
10.8 Certificate Chains and Trust
Certificate chains establish trust hierarchies for authenticating IoT devices and servers.
Try It: Certificate Chain Verification
Objective: Verify a certificate chain like an IoT device validates a server’s identity during TLS handshake. This demonstrates Root CA -> Intermediate CA -> End-Entity certificate trust.
from cryptography import x509from cryptography.x509.oid import NameOIDfrom cryptography.hazmat.primitives import hashesfrom cryptography.hazmat.primitives.asymmetric import ecimport datetime# ── Step 1: Create a self-signed Root CA ──root_key = ec.generate_private_key(ec.SECP256R1())root_name = x509.Name([ x509.NameAttribute(NameOID.ORGANIZATION_NAME, "IoT Manufacturer CA"), x509.NameAttribute(NameOID.COMMON_NAME, "Root CA"),])root_cert = ( x509.CertificateBuilder() .subject_name(root_name) .issuer_name(root_name) .public_key(root_key.public_key()) .serial_number(x509.random_serial_number()) .not_valid_before(datetime.datetime.now(datetime.timezone.utc)) .not_valid_after(datetime.datetime.now(datetime.timezone.utc)+ datetime.timedelta(days=3650)) .add_extension(x509.BasicConstraints(ca=True, path_length=1), critical=True) .sign(root_key, hashes.SHA256()))print("=== Certificate Chain Verification ===")print(f" Root CA: {root_cert.subject.rfc4514_string()}")print(f" Valid: {root_cert.not_valid_before_utc.date()} to "f"{root_cert.not_valid_after_utc.date()}")# ── Step 2: Issue Intermediate CA signed by Root ──inter_key = ec.generate_private_key(ec.SECP256R1())inter_name = x509.Name([ x509.NameAttribute(NameOID.ORGANIZATION_NAME, "IoT Manufacturer CA"), x509.NameAttribute(NameOID.COMMON_NAME, "Device Signing CA"),])inter_cert = ( x509.CertificateBuilder() .subject_name(inter_name) .issuer_name(root_name) .public_key(inter_key.public_key()) .serial_number(x509.random_serial_number()) .not_valid_before(datetime.datetime.now(datetime.timezone.utc)) .not_valid_after(datetime.datetime.now(datetime.timezone.utc)+ datetime.timedelta(days=1825)) .add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True) .sign(root_key, hashes.SHA256()) # Signed by Root CA)print(f" Intermediate: {inter_cert.subject.rfc4514_string()}")# ── Step 3: Issue device certificate signed by Intermediate ──device_key = ec.generate_private_key(ec.SECP256R1())device_name = x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "ESP32-SENSOR-00142"),])device_cert = ( x509.CertificateBuilder() .subject_name(device_name) .issuer_name(inter_name) .public_key(device_key.public_key()) .serial_number(x509.random_serial_number()) .not_valid_before(datetime.datetime.now(datetime.timezone.utc)) .not_valid_after(datetime.datetime.now(datetime.timezone.utc)+ datetime.timedelta(days=365)) .sign(inter_key, hashes.SHA256()) # Signed by Intermediate)print(f" Device cert: {device_cert.subject.rfc4514_string()}")# ── Step 4: Verify the chain (device -> intermediate -> root) ──print("\n Verifying chain:")# Verify device cert was signed by intermediatetry: inter_key.public_key().verify( device_cert.signature, device_cert.tbs_certificate_bytes, ec.ECDSA(hashes.SHA256()) )print(" [OK] Device cert signed by Intermediate CA")exceptException:print(" [FAIL] Device cert signature invalid")# Verify intermediate cert was signed by roottry: root_key.public_key().verify( inter_cert.signature, inter_cert.tbs_certificate_bytes, ec.ECDSA(hashes.SHA256()) )print(" [OK] Intermediate CA signed by Root CA")exceptException:print(" [FAIL] Intermediate cert signature invalid")print(" [OK] Chain verified: Device -> Intermediate -> Root")print(f"\n Chain depth: 3 certificates")print(f" All using ECC P-256 (33-byte public keys)")
What to Observe:
The Root CA is self-signed (issuer == subject) and has ca=True – this is the trust anchor embedded in devices
Each certificate is signed by its parent’s private key, creating a verifiable chain
Verification only needs the parent’s public key – private keys stay in their respective HSMs
If the Intermediate CA key is compromised, revoke it without replacing the Root CA
All certificates use ECC P-256, keeping the entire chain under 1 KB – suitable for constrained devices
Try It: Certificate Chain Trust Simulator
Show code
viewof cert_rootName = Inputs.text({label:"Root CA name",value:"IoT Manufacturer Root CA",width:"100%"})viewof cert_intName = Inputs.text({label:"Intermediate CA name",value:"Device Signing CA",width:"100%"})viewof cert_devName = Inputs.text({label:"Device name",value:"ESP32-SENSOR-00142",width:"100%"})
Worked Example: Secure Firmware Update System with RSA-2048 Signatures
Scenario: You manufacture 10,000 smart locks deployed in hotels worldwide. You need to push a critical security patch that fixes a vulnerability in the Bluetooth pairing process. The firmware update must be cryptographically signed to prevent attackers from uploading malicious firmware.
Implementation:
Key Generation (performed once, secured in HSM):
# Generate manufacturer RSA-2048 key pairopenssl genrsa -out manufacturer_key.pem 2048openssl rsa -in manufacturer_key.pem -pubout-out manufacturer_pubkey.pem# Key sizes:# Private key: 2048 bits (256 bytes), stored in Hardware Security Module# Public key: 2048 bits, embedded in every lock's firmware during manufacturing
Signing the Firmware (performed by CI/CD during release):
Cloud server sends: firmware_v2.1.bin (128 KB) + firmware.sig (256 bytes)
Lock downloads both files
Lock verifies signature before writing to flash
If signature invalid -> reject update, log security event
If signature valid -> install update, reboot
Performance Measurements (ESP32 @ 240 MHz): - SHA-256 hashing: 128 KB firmware @ 15 MB/s = 8.5 ms - RSA-2048 signature verification: 42 ms - Total verification time: 50.5 ms - Power consumption: 0.08 mAh (negligible)
Security Analysis:
Attacker capabilities: Reverse-engineer one lock’s firmware -> extract public key
Attack scenario: Attacker creates malicious firmware, tries to sign with guessed private key
Why it fails: Factoring the 2048-bit RSA modulus requires 2^112 operations (impossible even with all computers on Earth)
Result: Only the manufacturer (possessing private key in HSM) can sign valid firmware
Real-World Impact: In 2019, a smart lock competitor used unsigned firmware updates. Attackers uploaded malicious firmware that disabled all locks, causing $12M in hotel lockout costs (4,500 hotels, emergency locksmith fees). With RSA-2048 signature verification, this attack is cryptographically impossible.
Cost-Benefit:
Implementation cost: 50 ms verification time (acceptable for background update)
Security benefit: Prevents $12M+ losses from malicious firmware
An IoT manufacturer wants to push signed firmware updates to 100,000 deployed devices. Which key should they use to SIGN the firmware, and which key should devices use to VERIFY it?
Options:
Sign with public key, verify with private key
Sign with manufacturer’s private key, verify with manufacturer’s public key embedded in devices
Use symmetric encryption – same key for sign and verify
Sign with each device’s private key, verify with device’s public key
Answer
Correct: B
The manufacturer signs firmware with their private key (kept secret). Each device has the manufacturer’s public key pre-installed and uses it to verify signatures. Since only the manufacturer has the private key, only they can create valid signatures. This is how secure boot and firmware verification work.
Question: Diffie-Hellman Security
During a Diffie-Hellman key exchange, an attacker intercepts the public values A and B exchanged between Alice and Bob over an insecure channel. Can the attacker compute the shared secret?
Options:
Yes, because A and B together reveal the shared secret
No, computing the shared secret from A and B requires solving the discrete logarithm problem
Yes, if they also know the public parameters p and g
No, but only if the channel uses TLS encryption
Answer
Correct: B
This is the mathematical foundation of Diffie-Hellman security. The attacker sees p, g, A (g^a mod p), and B (g^b mod p), but computing a or b from these values is the discrete logarithm problem – computationally infeasible for sufficiently large primes. The parameters p and g are intentionally public (published in RFC 3526). Only Alice and Bob can compute s = g^ab mod p.
Quiz: Public Key Cryptography
Decision Framework: RSA vs ECC for IoT Public Key Cryptography
Algorithm Comparison for Equivalent 128-bit Security Level:
Feature
RSA-3072
ECC P-256 (ECDSA)
Ed25519
Best For
Key Size
3072 bits (384 bytes)
256 bits (32 bytes)
256 bits (32 bytes)
ECC/Ed25519: 12x smaller
Signature Size
384 bytes
64 bytes
64 bytes
ECC/Ed25519: 6x smaller
Sign Speed
15 ms
8 ms
2 ms
Ed25519: 7x faster
Verify Speed
0.5 ms
16 ms
5 ms
RSA: fastest verify
TLS/DTLS Support
Full support
Full support
Full (TLS 1.3+)
All: good compatibility
FIPS Approved
Yes (FIPS 186-5)
Yes (FIPS 186-5)
Yes (FIPS 186-5, EdDSA)
All three: regulatory compliance
Quantum Resistance
None
None
None
All three vulnerable to Shor’s algorithm
Battery Impact (1000 ops)
2.5 mAh
1.3 mAh
0.4 mAh
Ed25519: lowest power
Decision Tree:
Is regulatory compliance required? (FIPS 140-2/3, Common Criteria)
YES -> Use ECDSA P-256 (NIST-approved) or RSA-3072 (if >128 KB RAM available)
NO -> Consider Ed25519 for better performance
What is the primary use case?
Firmware signatures (verify often, sign rarely) -> RSA-3072 (0.5 ms verify is fastest)
Wi-Fi, Ethernet (ample bandwidth) -> Any algorithm acceptable
Cellular (pay-per-byte) -> Ed25519 saves data costs (6x smaller signatures)
Is quantum computing a concern? (10+ year device lifespan)
YES -> Plan migration to ML-KEM and ML-DSA (NIST post-quantum standards)
NO -> Current algorithms sufficient
Best Practices:
Default recommendation: ECC P-256 (ECDSA) for device certificates and TLS/DTLS (widest compatibility)
Optimize for performance: Ed25519 for high-frequency signing operations (logging, telemetry)
Optimize for verification: RSA-3072 for firmware signatures (verify on device, sign on server)
Never use RSA-1024: Factored in academic research, considered broken
Common Mistake: Using the Same RSA Key Pair for Encryption AND Signing
What Developers Do Wrong: To simplify key management, developers generate one RSA-2048 key pair per device and use it for BOTH encrypting data (with public key) and signing firmware updates (with private key). This seems efficient – one key pair handles all cryptographic needs.
Why It Fails: Using the same RSA key for encryption and signing creates a cryptographic cross-protocol attack surface:
Mathematical relationship exploitation: RSA signatures and encryption use the same modular exponentiation operation. An attacker can sometimes trick a device into signing a specially crafted “message” that is actually an encrypted session key, allowing the attacker to decrypt communications.
Bleichenbacher’s attack variant: If the device accepts signature requests without validating the message format, an attacker can submit ciphertexts as “messages to sign.” The resulting signature leaks information about the private key exponent through padding oracle attacks.
Key compromise blast radius: If the signing key is compromised (e.g., through firmware reverse engineering), the encryption key is simultaneously compromised, exposing ALL encrypted data and future signatures.
Real-World Example: In 2017, a medical IoT device manufacturer used one RSA-2048 key pair per device for both encrypting patient vitals (data confidentiality) and signing control commands (authentication). Security researchers discovered they could: - Capture encrypted patient data from the device - Submit the ciphertext to the device’s “sign this command” API - The device signed the ciphertext (thinking it was a valid command) - Researchers used the signature to compute parts of the private key - After collecting 1,200 signatures, they recovered the full private key - Result: HIPAA breach affecting 23,000 patients, $1.2M fine
Correct Approach: Always use separate key pairs for different cryptographic purposes:
// CORRECT: Two distinct key pairsRSA_KeyPair encryption_keypair;// For encrypting session keysRSA_KeyPair signing_keypair;// For signing firmware/messages// Generate separate keys during manufacturinggenerate_rsa_keypair(&encryption_keypair,2048);generate_rsa_keypair(&signing_keypair,2048);// Use encryption key ONLY for encryptionencrypt_data(plaintext, encryption_keypair.public_key);// Use signing key ONLY for signaturessign_message(message, signing_keypair.private_key);
Even Better: Use Different Algorithms:
Encryption: Use ECDH (Elliptic Curve Diffie-Hellman) for key exchange -> derive AES session keys
Signing: Use ECDSA or Ed25519 for digital signatures
This eliminates the possibility of cross-protocol attacks because the mathematical operations are completely different.
The Cost: In the medical IoT breach, the manufacturer saved $0.15 per device by using one key pair instead of two. The HIPAA fine ($1.2M) and remediation costs ($3.8M) resulted in a net loss of $5M – a 33 million times cost increase from the $0.15 “savings” per device.
Key Takeaway: RSA key pairs are NOT multipurpose tools. One key = one purpose. Violating this principle creates mathematical attack vectors that skilled attackers WILL exploit.
Concept Relationships
Concept
Depends On
Enables
Trade-off
Public Key Cryptography
Mathematical one-way functions
Key exchange without pre-shared secrets
100-1000x slower than symmetric
RSA
Prime factorization difficulty
Encryption + signatures
Large keys (2048+ bits)
Diffie-Hellman
Discrete logarithm problem
Secure key agreement
Requires authentication to prevent MITM
Digital Signatures
Hash functions + asymmetric crypto
Authentication + non-repudiation
Larger than MAC tags
ECC
Elliptic curve discrete log
RSA-equivalent security, 10x smaller keys
Less widely understood than RSA
Hybrid Encryption
Both symmetric and asymmetric
Fast bulk encryption + secure key exchange
Implementation complexity
Common Confusion: “Use RSA for all IoT encryption” – NO. RSA is too slow for bulk data. Use RSA/ECC for key exchange, then AES for data.
Match the Asymmetric Cryptography Concept
Key Concepts
Public Key: The freely shareable component of an asymmetric key pair; used to encrypt data or verify digital signatures.
Private Key: The secret component of an asymmetric key pair; used to decrypt data or create digital signatures. Must never be shared.
RSA: A widely used asymmetric algorithm based on the difficulty of factoring large numbers; key sizes of 2048+ bits are recommended.
ECC (Elliptic Curve Cryptography): An asymmetric algorithm based on elliptic curve mathematics; provides equivalent security to RSA with much smaller key sizes (e.g., 256-bit ECC ≈ 3072-bit RSA).
Digital Signature: A cryptographic value computed with a private key that allows anyone with the corresponding public key to verify authenticity and non-repudiation.
Key Exchange: A protocol by which two parties derive a shared secret over an insecure channel without transmitting the secret directly (e.g., ECDH, Diffie-Hellman).
PKI (Public Key Infrastructure): The ecosystem of policies, software, and hardware used to create, manage, distribute, and revoke digital certificates built on asymmetric cryptography.
Order the Hybrid Encryption Steps
Place the steps of a TLS-style hybrid encryption session in correct order:
Common Pitfalls
1. Using Asymmetric Encryption for Bulk Data
RSA and ECC operations are computationally expensive — 100–1000x slower than AES. Encrypting large sensor data streams with RSA will overwhelm constrained processors. Use asymmetric crypto only for key exchange; encrypt bulk data with AES.
2. Generating Keys on Weak Entropy Sources
Key generation requires high-quality randomness. Embedded devices booting from cold often have predictable entropy (same boot time, same sensor readings). Use a hardware RNG or seed the PRNG from multiple sources before generating keys.
3. Storing Private Keys in Flash Without Protection
Private keys stored in plaintext in flash can be extracted via physical attacks or firmware dumps. Use secure elements, TrustZone, or encrypted key stores with a device-unique root key.
4. Ignoring Signature Verification Failures
Code that receives a signed message must check the verification result and reject invalid signatures. Ignoring or logging-only failures allows attackers to substitute arbitrary data.
Label the Diagram
💻 Code Challenge
10.10 Summary
Asymmetric encryption uses two keys: public (encrypt) and private (decrypt)
RSA is based on the difficulty of factoring large prime products – use RSA-2048 minimum
Diffie-Hellman enables shared secret establishment over insecure channels
Digital signatures prove authenticity: sign with private key, verify with public key
ECC provides equivalent security to RSA with 10x smaller keys (ideal for IoT)
Hybrid encryption combines both: asymmetric for key exchange, symmetric for bulk data
Never reuse the same key pair for encryption and signing – separate keys for separate purposes
10.11 What’s Next
Continue to Elliptic Curve Cryptography (ECC) to learn why ECC is the preferred choice for resource-constrained IoT devices, offering RSA-equivalent security with dramatically smaller keys and faster operations.