http://docker:8085Each device gets a unique ML-DSA-44 keypair. The public key serves as the device identity. Lightweight enough for microcontrollers.
ML-DSA-44 (FIPS 204, Level 2)Challenge-response: server sends a challenge, device signs it with ML-DSA-44 private key, server verifies. Proves the device is genuine.
ML-DSA-44 (FIPS 204, Level 2)Device signs sensor data (temperature, humidity, etc.) before sending. The server verifies the signature to ensure data integrity and source authenticity.
ML-DSA-44 (FIPS 204, Level 2)Vendor signs firmware with ML-DSA-87 (highest security). Device verifies the signature before applying the update. Prevents malicious firmware injection.
ML-DSA-87 (FIPS 204, Level 5)This service demonstrates quantum-safe IoT device security — the same patterns you'll implement using the Qudo JNI provider in your device firmware and IoT gateway.
Device (ML-DSA-44): provider.generateKeyPair("ML-DSA-44") — smallest signatures for constrained devices. Used for registration, authentication, and telemetry signing.
Vendor (ML-DSA-87): provider.sign(firmware, vendorKey, "ML-DSA-87") — highest security for firmware. Devices verify with provider.verify() before applying updates.
/api/iot/register-device
Register a new IoT device
/api/iot/authenticate
Authenticate device (challenge-response)
/api/iot/send-telemetry
Send signed telemetry data
/api/iot/firmware-update
Verify and apply firmware update
/api/iot/devices
List all registered devices
/api/iot/health
Service health + device count
Secure IoT devices with quantum-safe authentication and firmware verification. Use this playground to try PQC device patterns, then use the Qudo JNI provider directly in your device firmware and IoT gateway.
IoT Device IoT Gateway / Server Vendor / OEM | | | |-- Register (get ML-DSA-44 keypair) --| | | | | |-- Auth: sign challenge ------>| | | (ML-DSA-44) | verify signature | | | | |-- Telemetry: sign data ------>| | | (ML-DSA-44) | verify, store | | | | | |<--- Firmware signed --------- | | | (ML-DSA-87 by vendor) | |<-- Push firmware update ----- | | | verify ML-DSA-87 signature | | | ACCEPTED / REJECTED | |
Run the complete PQC IoT lifecycle in sequence. Each step uses the output from the previous step.
Click to generate an ML-DSA-44 keypair and register a new device.
Challenge-response: the device signs a challenge with its ML-DSA-44 private key.
Device signs sensor data with ML-DSA-44. Server verifies authenticity.
Vendor signs firmware with ML-DSA-87 (Level 5). Device verifies before applying.
Replace classical device crypto with the Qudo JNI provider. Your MQTT/CoAP protocol stays the same — PQC changes are at the crypto layer only.
import com.qudo.crypto.QudoCrypto;
import com.qudo.crypto.QudoKeyPair;
QudoCrypto provider = QudoCrypto.create();
// ============================================================
// Device side: ML-DSA-44 (lightweight, constrained devices)
// ============================================================
// Registration: generate device identity
QudoKeyPair deviceKeys = provider.generateKeyPair("ML-DSA-44");
// Store deviceKeys.getPrivateKeyPem() securely on device
// Send deviceKeys.getPublicKeyPem() to server during registration
// Authentication: sign server challenge
byte[] challenge = receiveFromServer();
byte[] authSig = provider.sign(challenge, deviceKeys.getPrivateKeyPem(), "ML-DSA-44");
sendToServer(authSig);
// Telemetry: sign sensor data before sending
byte[] telemetry = "{\"temp\":22.5,\"humidity\":45}".getBytes();
byte[] telemetrySig = provider.sign(telemetry, deviceKeys.getPrivateKeyPem(), "ML-DSA-44");
sendToServer(telemetry, telemetrySig);
// ============================================================
// Vendor side: ML-DSA-87 (highest security for firmware)
// ============================================================
QudoKeyPair vendorKeys = provider.generateKeyPair("ML-DSA-87");
byte[] firmware = loadFirmwareBinary();
byte[] firmwareSig = provider.sign(firmware, vendorKeys.getPrivateKeyPem(), "ML-DSA-87");
// Distribute firmware + firmwareSig + vendorKeys.getPublicKeyPem()
// ============================================================
// Device side: verify firmware before applying
// ============================================================
boolean valid = provider.verify(firmware, firmwareSig, vendorPubKey, "ML-DSA-87");
if (valid) applyUpdate(firmware); // FIRMWARE_ACCEPTED
else rejectUpdate(); // FIRMWARE_REJECTED
| Component | Classical | PQC (Qudo JNI) |
|---|---|---|
| Device identity | ECDSA P-256 | provider.generateKeyPair("ML-DSA-44") |
| Authentication | ECDSA challenge-response | provider.sign(challenge, key, "ML-DSA-44") |
| Telemetry signing | HMAC-SHA256 / ECDSA | provider.sign(data, key, "ML-DSA-44") |
| Firmware signing | RSA-4096 / ECDSA P-384 | provider.sign(fw, vendorKey, "ML-DSA-87") |
| TLS transport | ECDHE / X25519 | X25519MLKEM768 (gateway NGINX config) |
| Protocol (MQTT/CoAP) | Same | Same — PQC is at crypto layer, not protocol |
| ML-DSA-44 (Device) | ML-DSA-87 (Firmware) | |
|---|---|---|
| Security Level | NIST Level 2 | NIST Level 5 |
| Signature Size | 2,420 bytes | 4,627 bytes |
| Public Key Size | 1,860 bytes | 3,595 bytes |
| Sign Latency | ~118ms | ~125ms |
| Use Case | Frequent, bandwidth-constrained | Infrequent, security-critical |
| Lifetime | Session-based auth | Must verify for 10-20 years |
| Operation | ML-DSA-44 | ECDSA P-256 | Impact |
|---|---|---|---|
| Key generation | ~50ms | ~5ms | One-time during provisioning |
| Sign telemetry | ~118ms | ~2ms | Per data packet (can batch) |
| Verify (server-side) | ~40ms | ~2ms | Server has more resources |
| Signature over wire | 2,420 bytes | 64 bytes | ~2.4KB extra per message |
Impact: MQTT has no hard message size limit, but CoAP has a ~1KB payload recommendation. LoRaWAN has a 51-222 byte limit.
Mitigation: For extremely constrained protocols, sign at the gateway level rather than the device. Or batch multiple readings into one signed payload.
Impact: If the ML-DSA-44 private key is extracted during manufacturing, the device identity is compromised.
Fix: Generate keys on-device (if the device has a secure element) or use a secure provisioning channel. Never transmit private keys over unencrypted connections.
Why: Firmware updates must remain verifiable for the entire device lifetime (10-20 years). ML-DSA-87 (Level 5) provides the highest security margin against future advances in both classical and quantum computing.
Impact: Many IoT devices don't have a reliable clock, so certificate validity dates are meaningless.
Fix: Use raw public key authentication (device stores vendor's ML-DSA-87 public key directly) rather than X.509 certificates for firmware verification.
A: ML-DSA-44 verification requires ~100KB RAM and ~200ms on a Cortex-M4 class MCU. Signing requires similar resources. For Cortex-M0 class devices (very low-end), consider signing at the gateway and using a simpler device authentication (e.g., pre-shared keys with AES-GCM).
A: Depends on the use case. For safety-critical data (medical devices, industrial control), sign every reading. For non-critical data (temperature sensors), sign batches of readings to reduce overhead. The signature covers whatever data you include — one reading or one hundred.
A: The vendor signs the firmware binary hash with ML-DSA-87. The signed hash is sent alongside the firmware. The device: 1) downloads firmware, 2) computes SHA-256 hash, 3) verifies the hash against the vendor's ML-DSA-87 signature, 4) applies only if FIRMWARE_ACCEPTED.
A: If the MQTT broker terminates TLS behind an NGINX gateway with X25519MLKEM768, all MQTT traffic gets quantum-safe transport automatically. The device signs telemetry at the application layer for end-to-end integrity regardless of the transport.
A: Yes. The server can verify both ECDSA and ML-DSA signatures. During migration, new devices use ML-DSA-44, existing devices keep ECDSA. The server checks the signature algorithm in the device record and verifies accordingly.
Analyze any endpoint's cryptographic configuration. Enter a host:port to scan its TLS setup and identify what's quantum-safe vs what needs migration.