Chapter 27 — OT, IoT and cyber-physical security: verification
Lesson map
Lesson 27 published. Map, example, EX079.
flowchart TD
N0["Learning outcomes"]
N1["27.1 Policies need tests"]
N2["27.2 Designing vectors"]
N3["27.3 Reading the result"]
N4["27.4 From lab to plant"]
N5["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5
N5 --> EX["Worked example"]
EX --> Q["Exercise EX079"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["27.1 Policies need tests"]
N2["27.2 Designing vectors"]
N3["27.3 Reading the result"]
N4["27.4 From lab to plant"]
N5["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> Q["EX079"]
[Learning outcomes]
|
v
[27.1 Policies need tests]
|
v
[27.2 Designing vectors]
|
v
[27.3 Reading the result]
|
v
[27.4 From lab to plant]
|
v
[Synthesis]
|
v
[Worked example] --> [Exercise EX079]
Learning outcomes
You will (1) write test vectors for an allow-list policy; (2) include boundary, negative and unknown-source cases; (3) run conformance tests offline before any deployment; and (4) explain why verification evidence is part of the IEC 62443 lifecycle.
27.1 Policies need tests
A firewall or protocol-aware allow-list is code, even when written as rules. Verification means showing, with recorded evidence, that the policy permits what operations need and blocks what it should. IEC 62443-4-1 (secure product development lifecycle) and the system integrator's commissioning activities both expect documented testing.
LDHP's policy for PLC-H is small enough to state in one line: HMI-1 may read with function code 3 and write registers 100–119 with function code 6, HIST-1 may read with function codes 3 and 4, and every other source and function is blocked. The lab turns that line into six vectors with expected results. A policy that has never been run against vectors like these is a guess about what the firewall does.
27.2 Designing vectors
Good vectors include: each permitted (source, function) pair; each boundary of a register range (first, last, one past last); a permitted source using a forbidden function; an unknown source; and a write function the plant never uses (16, write multiple). Expected results are written before running the test.
"""Lab 27: conformance test of a Modbus allow-list against a test vector table."""
ALLOW = {("HMI-1", 3), ("HMI-1", 6), ("HIST-1", 3), ("HIST-1", 4)}
WRITE_RANGE = range(100, 120)
def permit(src, fc, reg):
if (src, fc) not in ALLOW: return "BLOCK"
if fc == 6 and reg not in WRITE_RANGE: return "BLOCK"
return "PASS"
vectors = [("HMI-1", 3, 100, "PASS"), ("HMI-1", 6, 119, "PASS"), ("HMI-1", 6, 120, "BLOCK"),
("HIST-1", 6, 105, "BLOCK"), ("ENG-WS", 16, 100, "BLOCK"), ("HIST-1", 4, 10, "PASS")]
fails = 0
for src, fc, reg, want in vectors:
got = permit(src, fc, reg); fails += got != want
print(f"{src:6} fc={fc:2} reg={reg:3} want={want:5} got={got:5} {'ok' if got == want else 'FAIL'}")
print("failures:", fails, "of", len(vectors))
Run with python3 lab_allow.py. Verified output:
HMI-1 fc= 3 reg=100 want=PASS got=PASS ok
HMI-1 fc= 6 reg=119 want=PASS got=PASS ok
HMI-1 fc= 6 reg=120 want=BLOCK got=BLOCK ok
HIST-1 fc= 6 reg=105 want=BLOCK got=BLOCK ok
ENG-WS fc=16 reg=100 want=BLOCK got=BLOCK ok
HIST-1 fc= 4 reg= 10 want=PASS got=PASS ok
failures: 0 of 6
In the lab the six vectors are: HMI-1 reading register 100; HMI-1 writing 119, the last allowed register; HMI-1 writing 120, one past the end; HIST-1 writing 105; ENG-WS using function 16; and HIST-1 reading with function 4.
27.3 Reading the result
All six vectors match. Notice vector 3: register 120 is one past the end of range(100, 120), so it must block — the classic off-by-one boundary. Vector 4 confirms the historian cannot write even within the writable range. A clean run is evidence for these six cases only; it says nothing about function code 23 (read/write multiple) unless you add it.
Maya's colleague's change request shows how the vectors are maintained: extending HMI-1's writable range to include register 120 for a new pump speed setpoint meant changing vector 3's expected result to PASS, adding a new vector (HMI-1, 6, 121, BLOCK) and only then editing WRITE_RANGE.
27.4 From lab to plant
Before deploying a policy to LDHP's control-zone firewall, the same vectors are replayed in a bench environment with the actual device, in monitor mode first, and the result attached to the change ticket.
Monitor mode means the firewall logs what it would have blocked without blocking it. At LDHP that period should cover at least one full week, including the weekly report run and a maintenance window, because a legitimate source that writes only occasionally, such as EWS-1 during a logic change, would otherwise first appear as a blocked write in the middle of an operator's shift. The change ticket then carries the bench vector results, the monitor-mode log and the shift lead's agreement to enforce.
Worked example (synthetic LDHP)
A colleague proposes extending HMI-1's writable range to include register 120 (a new pump speed setpoint). Maya first changes the expected result of vector 3 to PASS, adds vector (HMI-1, 6, 121, BLOCK), then edits WRITE_RANGE to range(100, 121), and reruns. The test change is reviewed alongside the policy change.
Synthetic case — Lakeside District Heating Plant. Register numbers are invented.
Errors and limits
Tests written after the code tend to confirm what the code does, not what the plant needs. Offline conformance does not prove device performance under load.
Exercises
- EX079 — A missing function code. Add a vector for (HMI-1, 23, 105). What should it expect and what does the lab return? Rubric: 3 = expect BLOCK; lab returns BLOCK because (HMI-1, 23) not in ALLOW; 2 = one; 1 = PASS; 0 = none.
- EX080 — Boundary after the range change. After
WRITE_RANGE = range(100, 121), list the two boundary vectors you would keep. Rubric: 3 = reg 120 PASS and reg 121 BLOCK; 2 = one; 1 = 119 only; 0 = none. - EX081 — Writing expectations first. Why write expected results before running? Rubric: 3 = avoids confirming code behaviour; ties tests to operational requirement; 2 = partial; 1 = "good practice"; 0 = none.
Synthesis
Verification is a written list of expected decisions, including boundaries and unknowns, run before deployment and kept as evidence.
Continue studying — chapter 27 of 40
← Chapter 26 (draft) · Course chapter index · Chapter 28 (draft) →