Chapter 17 — Cryptography concepts
Lesson map
Lesson 17 published. Map, example, EX049.
flowchart TD
N0["Learning outcomes"]
N1["17.1 Integrity first"]
N2["17.2 Replay"]
N3["17.3 Reading the run"]
N4["17.4 Operational costs"]
N5["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5
N5 --> EX["Worked example"]
EX --> Q["Exercise EX049"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["17.1 Integrity first"]
N2["17.2 Replay"]
N3["17.3 Reading the run"]
N4["17.4 Operational costs"]
N5["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> Q["EX049"]
[Learning outcomes]
|
v
[17.1 Integrity first]
|
v
[17.2 Replay]
|
v
[17.3 Reading the run]
|
v
[17.4 Operational costs]
|
v
[Synthesis]
|
v
[Worked example] --> [Exercise EX049]
Learning outcomes
You will (1) explain message authentication codes and why integrity often matters more than secrecy in OT; (2) use hmac and hashlib from the standard library; (3) prevent replay with counters; and (4) list the operational costs of cryptography on constrained devices.
17.1 Integrity first
A heat meter's reading is not secret to its owner, but a forged reading changes a bill and a forged control command changes a process. A message authentication code (MAC) lets a receiver who shares a secret key verify that a message came from a key holder and was not altered. HMAC with SHA-256 is a widely used construction, standardised in FIPS 198-1.
At LDHP the stakes are a bill and a boiler. A forged meter reading reduces one household's bill; a forged write to PLC-H's register 100 changes the heat supplied to every household. The HMAC lab uses meter readings because the integrity question is clearest there.
17.2 Replay
A valid message captured yesterday is still valid cryptographically today. Including a strictly increasing counter (or a timestamp checked against a window) inside the authenticated data lets the receiver reject repeats.
"""Lab 17: HMAC-protected meter readings with a replay counter (synthetic key)."""
import hmac, hashlib
KEY = b"synthetic-lab-key-not-for-use"
def tag(ctr, kwh): return hmac.new(KEY, f"{ctr}|{kwh}".encode(), hashlib.sha256).hexdigest()[:16]
last = 0
msgs = [(1, 120.5, tag(1, 120.5)), (2, 121.0, tag(2, 121.0)), (2, 121.0, tag(2, 121.0)), (3, 99.0, tag(3, 121.4))]
for ctr, kwh, t in msgs:
if not hmac.compare_digest(t, tag(ctr, kwh)): print(ctr, kwh, "REJECT bad tag")
elif ctr <= last: print(ctr, kwh, "REJECT replay")
else: last = ctr; print(ctr, kwh, "accept")
Run with python3 lab_hmac.py. Verified output:
1 120.5 accept
2 121.0 accept
2 121.0 REJECT replay
3 99.0 REJECT bad tag
In the HMAC lab, a reading accepted once with counter 2 is rejected when the same bytes arrive again, although its tag is correct; only the counter differs in meaning. For LDHP's meters, this blocks a tenant with radio equipment from replaying last month's lower reading, which is the abuse case written in Chapter 26.
17.3 Reading the run
Message 2 is accepted once and rejected the second time as a replay, even though its tag is correct. Message 3 carries a tag computed for 121.4 kWh but claims 99.0 kWh, so verification fails. Note hmac.compare_digest, which compares in constant time and avoids leaking how many leading characters matched.
For LDHP the second rejection is the tenant scenario: a captured lower reading replayed for next month's bill fails on its counter even though every byte of the tag is genuine. The third rejection is tampering in transit: a gateway or attacker that changes 121.4 kWh to 99.0 kWh without the meter's key cannot produce a matching tag.
17.4 Operational costs
Keys must be provisioned per device, rotated and revoked; a meter replaced after a fault needs a new key, and a shared fleet-wide key means one extracted key forges every meter. Constrained devices may lack hardware for secure key storage. ETSI EN 303 645 provision 5.4 asks that sensitive security parameters be stored securely and that hard-coded critical security parameters in device software are not used.
Maya's size estimate puts the radio cost at 1.5 MB per day across 4,000 meters; the key process is the larger cost.
Worked example (synthetic LDHP)
Maya computes the size overhead: a truncated 16-hex-character (64-bit) tag plus an 8-byte counter adds 16 bytes per reading. At one reading per hour per meter across 4,000 meters, that is 1.5 MB per day — trivial for the network, but the key-management process is the real cost.
Synthetic case — Lakeside District Heating Plant. The key in the lab is a placeholder string; never embed keys in code.
Errors and limits
Truncating MACs reduces forgery resistance; 64 bits is a common minimum for such designs. Hashing alone (without a key) provides no authentication. Encryption without integrity protection can be malleable.
Exercises
- EX049 — Detecting a counter reset. After a meter firmware update the counter restarts at 1. What does the lab's receiver do, and how should the design handle it? Rubric: 3 = rejects as replay; re-key or persist counter with a key-epoch field; 2 = behaviour only; 1 = accepts; 0 = none.
- EX050 — Why not plain SHA-256? Explain why appending SHA-256(reading) does not stop forgery. Rubric: 3 = no secret: anyone can recompute the hash for a forged value; 2 = partial; 1 = "SHA-256 is weak"; 0 = none.
- EX051 — Fleet-wide key risk. Describe the consequence of all 4,000 meters sharing one HMAC key. Rubric: 3 = extracting it from one meter forges readings for any meter; per-device keys limit blast radius; 2 = partial; 1 = vague; 0 = none.
Synthesis
For OT data, authenticated integrity with replay protection is the main cryptographic goal, and key management — not the algorithm — decides whether it works.
Continue studying — chapter 17 of 40
← Chapter 16 (draft) · Course chapter index · Chapter 18 (draft) →