Lab 02 — Anti-Tamper Integrity Self-Check + Attestation Model

Phase: 11 — Security & Licensing | Difficulty: ⭐⭐⭐⭐⭐ | Time: 5–8 hours Language: C (self-check) + Python (tamper tool + attestation model) | Hardware: none

Concept primer: ../WARMUP.md Ch. 6–8. Defensive scope; every technique stated with its defeat condition.

Run

make && ./protected             # runs + self-verifies its own code section
python3 tamper.py protected     # flips one byte in the code section -> protected_tampered
./protected_tampered            # self-check fires: tamper detected, refuses
python3 attestation.py          # models measure -> quote -> verify (the STRONG answer)

0. The mission

Two halves of the protection story (WARMUP Ch. 6, 8):

  1. Integrity self-check (protected.c): the program hashes its own .text (code) region at runtime and compares to an embedded expected digest; if a byte's been patched (e.g., to skip a license check), the hash differs and it refuses to run. This raises the cost of casual patching — and you'll state its defeat condition.
  2. Attestation model (attestation.py): models the qualitatively stronger approach — a hardware root of trust measures code, produces a hardware-signed quote, and an external verifier checks it. The point: the verifier is external and the key is in hardware, so it isn't defeated by patching software.

1. What the output shows

$ ./protected
[self-check] code digest matches embedded value — integrity OK
protected workload running: result = 1240

$ python3 tamper.py protected
patched 1 byte in the code region -> protected_tampered

$ ./protected_tampered
[self-check] code digest MISMATCH — binary has been tampered. Refusing to run.

$ python3 attestation.py
== self-check vs attestation ==
self-check : the checker is IN the binary the attacker controls -> patchable
attestation: measure -> quote (signed by a HARDWARE key) -> external verify
  known-good measurement  -> verifier ACCEPTS
  tampered measurement    -> verifier REJECTS
  forged quote (no hw key)-> verifier REJECTS (can't sign without the TPM/GPU key)
why stronger: the verifier is EXTERNAL and the signing key is in HARDWARE,
              so patching the software can't fake a valid quote.

2. Reading order

  1. protected.cself_check() hashes a marked code region and compares to EXPECTED_DIGEST; note the honest comment on how it's defeated (WARMUP Ch. 6).
  2. tamper.py — flips a byte in the code region to simulate a patch (the attacker's move against the Lab 01 license check).
  3. attestation.py — the measure/quote/verify flow and the side-by-side with the self-check (WARMUP Ch. 8).

3. The honest threat model (write it — the real deliverable)

In THREAT-MODEL.md, for each technique state what it raises the cost of and how it's defeated:

TechniqueRaises cost ofDefeated by
integrity self-checkcasual binary patchingpatching the checker / the expected digest
obfuscation (Ch. 7)locating/understanding the checka debugger + attacker-hours
anti-tamper responseautomated tamperingcalibration errors (false positives hurt honest users)
attestationfaking the software statea sound trust root is required; proves what loaded, not bug-freeness
confidential computingreading weights/data on hostTEE + attestation must gate decryption

The lesson (WARMUP Ch. 1): everything self-contained raises cost, never achieves impossibility; only a hardware root of trust (attestation, confidential computing) gives a real guarantee.

4. Extensions

  1. Redundant + obfuscated checks (WARMUP Ch. 6–7): add multiple self-checks in different functions; measure how much harder tampering gets (attacker must find them all).
  2. String/control-flow obfuscation experiment: obfuscate the check logic; reflect on the RE-cost increase vs the maintenance cost.
  3. Real attestation design doc: design a TPM measured-boot or NVIDIA H100 Confidential Computing attestation flow for gating model-weight decryption (WARMUP Ch. 8–9) — the production version of attestation.py.
  4. Encrypted weights at rest: weights decrypt only after a valid attestation (model the gate).

5. Common pitfalls

  1. Believing the self-check is "secure" — it's a cost-raiser; the checker is in the attacker's binary (WARMUP Ch. 1, 6). Always pair the claim with the defeat condition.
  2. Aggressive tamper response — self-destruction/false positives punish honest users (a hardware change, a debugger attached for support). Calibrate.
  3. Confusing self-attestation with attestation — software vouching for itself is not attestation; the verifier and key must be external/hardware.
  4. Over-obfuscating — kills performance/debuggability for marginal, bounded gain; target only high-value code.

6. What this lab proves about you

You can implement protection and threat-model it honestly — the rare combination that makes a security architecture credible to a regulated customer. You know where the line is between cost-raising (self-check, obfuscation) and real guarantees (attestation, confidential computing), which is exactly the judgment a Head of Engineering needs to architect — and to not oversell — a sovereign-deployment security story.