Protect MQTT telemetry with authenticated encryption
Encrypt one fictional telemetry message before publishing and prove that tamper, wrong context, replay, and wrong-key inputs fail without releasing plaintext.

Shield Shelly: I want you to test the protected message's boundary and prove every unsafe path rejects before plaintext is trusted.
Predict the reading, then compare it with the measurement.
Python 3 in your browser (JupyterLite)
Python · no installEncrypt one fictional telemetry message before publishing and prove that tamper, wrong context, replay, and wrong-key inputs fail without releasing plaintext.
Open the notebook in your browser and run each Python cell; no install or account is needed.
Three ways to run: use JupyterLite here with no install; run main.py locally from the downloadable lab folder; or open the same notebook in Google Colab.
Steps
Step 1
- Do
- Run `python3 main.py --step 1` to generate a local AES-256-GCM key and inspect only its boundary metadata.
- You will see
- The `Local key boundary` table shows AES-256-GCM, `key length 32 bytes`, `key output hidden`, and the broker-safe fields.
- Why it matters
- The visible metadata proves the key's size and local boundary while keeping the secret itself out of logs and traffic.

Step 1 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 2
- Do
- Run `python3 main.py --step 2` online to encrypt the fictional reading locally and publish its protected packet to the random broker topic.
- You will see
- The output places the fictional plaintext beside truncated ciphertext, prints the received `topic payload`, then shows the authenticated decrypted plaintext.
- Why it matters
- The side-by-side forms and actual broker packet show that routing succeeds while object security hides plaintext from the broker.

Step 2 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 3
- Do
- Run `python3 main.py --step 3` to authenticate the topic and schema as associated data while decrypting the packet.
- You will see
- The authentication view shows plaintext, truncated ciphertext, `AAD topic=iotclass/pump-7/telemetry;schema=1`, `Tag check VALID`, and the released message.
- Why it matters
- Showing all three inputs and the valid-tag result demonstrates how visible routing context is authenticated with encrypted content.

Step 3 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 4
- Do
- Run `python3 main.py --step 4` to flip the first ciphertext byte before tag verification.
- You will see
- The original and altered ciphertext differ in the first hex byte beside `Rule broken: authentication tag must match every ciphertext byte.` and `plaintext: NOT RELEASED`.
- Why it matters
- The before/after bytes connect a concrete mutation to failed authentication and prove the receiver fails closed.

Step 4 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 5
- Do
- Run `python3 main.py --step 5` to verify the pump-7 packet with associated data naming pump-8.
- You will see
- The unchanged ciphertext appears beside expected pump-7 AAD and supplied pump-8 AAD, followed by `Tag check: INVALID; plaintext: NOT RELEASED`.
- Why it matters
- Comparing the two contexts shows that associated data is authenticated even though it is not encrypted.

Step 5 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 6
- Do
- Run `python3 main.py --step 6` to submit message ID pump-7:42 twice to the receiver replay window.
- You will see
- The replay table shows arrival 1 for `pump-7:42` as ACCEPT and arrival 2 for the same ID as REJECT_REPLAY.
- Why it matters
- The two submissions expose the receiver state that AEAD itself cannot provide and make the freshness rule reproducible.

Step 6 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 7
- Do
- Run `python3 main.py --step 7` to generate 1,000 96-bit nonces for one bounded test key scope.
- You will see
- The check lists AES-GCM, 96-bit nonce width, one-key scope, sample size 1000, and `unique count 1000` before the no-reuse rule.
- Why it matters
- The scope and sample rows explain what the bounded uniqueness observation does and does not prove about production nonce allocation.

Step 7 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 8
- Do
- Run `python3 main.py --step 8` to decrypt a valid packet with a different generated key.
- You will see
- The truncated ciphertext appears beside hidden key A and hidden key B, then `Tag check: INVALID; plaintext: NOT RELEASED`.
- Why it matters
- The named-but-hidden key boundaries demonstrate wrong-key failure without leaking either secret into diagnostics.

Step 8 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 9
- Do
- Run `python3 main.py --step 9` and review the expected receiver decision for every tested path.
- You will see
- The release matrix shows valid as `ACCEPT RELEASED` while tamper, context, replay, and wrong key each read `REJECT NOT RELEASED`.
- Why it matters
- The row-by-row matrix consolidates the demonstrated paths into reviewable release evidence for the claimed fail-closed boundary.

Step 9 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
Chapter checks
These questions refer to the chapter’s examples. Use the return links to review their answers.
A vendor says sensor readings are end-to-end secure because each device uses TLS to the cloud message broker, which then forwards the readings to other services. What is the strongest correction?
Return to the chapter’s knowledge checkTelemetry must stay confidential from the cloud message broker, which only needs to route it. The team secures every device-to-broker link with TLS. What is the correct review conclusion?
Return to the chapter’s knowledge checkA device uses a fixed long-term key to derive the same session key for every TLS connection, to save computation. Years of recorded ciphertext are later exposed when that long-term key leaks. Why is this worse than a design that uses ephemeral key agreement per session?
Return to the chapter’s knowledge check