Skip to content

venaychawda/SecureJTAG

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Secure JTAG

An open-source, spec-driven implementation of Secure JTAG authentication and access control for automotive embedded systems — running in pure Python simulation today, targeting real hardware tomorrow.

Phase 1 Python License

▶ Live Demo


What Is This?

Modern automotive ECUs are required by ISO/SAE 21434 and UNECE R155 to protect their JTAG debug ports in production. An attacker with unrestricted JTAG access can dump firmware, extract cryptographic keys, and bypass secure boot — a direct threat to vehicle safety and OEM intellectual property.

This lab implements Secure JTAG: a challenge-response authentication gate that controls who can open the debug port, at what privilege level, and for how long. The design mirrors the debug access control architectures used in production automotive SoCs such as Renesas RH850, NXP S32K3, and STMicroelectronics SPC5xx / STM32H7.

Two delivery phases

Phase 1 — Simulation Phase 2 — Hardware
Status ✅ Complete — All tests GREEN Planned
Platform Pure Python, any OS STM32F407G-DISC1 + SparkFun ATECC608A
Crypto Software ECDSA-P256 (cryptography lib) ATECC608A hardware secure element
Entropy secrets.token_bytes() ATECC608A TRNG
Transport In-process function calls JSON-RPC over localhost TCP (port 9000)
Requirements Python 3.10+ + pyOCD, CryptoAuthLib, OpenOCD

Architecture — Two-Process Trust Boundary

The design enforces a two-process trust boundary that mirrors the hardware isolation between an ECU's application core and its dedicated debug security controller:

┌─────────────────────────────────────────────────────────────────────┐
│  Process 1 — Software Side  (UNTRUSTED — mirrors ECU application FW) │
│                                                                       │
│   sim/protocol/client.py      ← JSON-RPC client                      │
│   sim/core/auth_engine.py     ← challenge/response coordination       │
│   sim/core/session_manager.py ← session token tracking                │
│   sim/core/access_policy.py   ← RBAC permission enforcement           │
│   sim/core/audit_log.py       ← tamper-evident hash-chained log       │
└──────────────────────────────┬──────────────────────────────────────┘
                                │  JSON-RPC only — no shared memory
                                │  localhost TCP  port 9000
                                │
┌──────────────────────────────┴──────────────────────────────────────┐
│  Process 2 — Secure Debug Controller  (TRUSTED — mirrors HW root)    │
│                                                                       │
│   sim/protocol/server.py               ← JSON-RPC dispatcher          │
│   sim/hal/atecc_base.py                ← abstract crypto interface     │
│   sim/hal/atecc_simulator.py           ← Phase 1 software shim        │
│   sim/hal/atecc_device.py              ← Phase 2 real chip (locked)   │
│   sim/core/secure_debug_controller.py ← JTAG state machine + policy   │
│   sim/core/lockout_manager.py          ← brute-force protection        │
│   sim/core/tamper_monitor.py           ← tamper detection & response   │
│   sim/core/lifecycle_manager.py        ← DEVELOPMENT→DECOMMISSION      │
└─────────────────────────────────────────────────────────────────────┘

The golden rule: Process 1 has zero access to key material, lock state, or security counters except through defined RPC protocol messages. This models the bus isolation between an ECU host CPU and a real HSM or secure element.


Feature Coverage — Phase 1 (20 Test Cases, 53 Requirements)

Area Test Cases Key behaviours verified
Lock state TC-01, TC-02, TC-03 LOCKED on power-up, any reset, and during early boot
Challenge generation TC-04 Fresh 32-byte nonce per attempt, ECDSA-P256
Authentication TC-05, TC-06 Valid ECDSA sig → session token; invalid sig → reject + audit
Replay protection TC-07 Spent-nonce set blocks replayed challenge-response pairs
Lockout TC-08, TC-09 5 failures → 1-hour lockout; persists across power cycle
RBAC TC-10, TC-11, TC-12 READ_ONLY / DIAGNOSTICS / ENGINEERING; PROTECTED_OPS always denied
Session timeout TC-13 5-minute TTL; expired token → LOCKED + audit entry
Relock TC-14 Explicit relock zeroizes session and volatile material
Tamper response TC-15, TC-16 Any tamper → LOCKED + zeroize + TAMPER_DETECTED audit
Fault injection TC-17 Malformed sigs, oversized params, concurrent threads — no state corruption
Permanent disable TC-18 One-way DECOMMISSION brick — irreversible gate on all ops
Audit log TC-19 Hash-chained entries, FIFO eviction, no-clear API
Concurrency TC-20 Single active session enforced under concurrent authentication

Running Phase 1 — Simulation Mode

Prerequisites

  • Python 3.10 or newer
  • Git

Quick Start (Windows — one-click)

Double-click launch.bat in the project root. It will:

  1. Create the .venv virtual environment if it doesn't already exist
  2. Install (or update) all Phase 1 dependencies from requirements.txt
  3. Start the real backend — the Secure Debug Controller (sim/protocol/server.py, port 9000) and the browser WebSocket bridge (sim/protocol/ws_bridge.py, port 8765) — each in its own titled window
  4. Open the live Secure JTAG Debug Monitor dashboard in your default browser, already connected to that real backend
  5. Leave you in a terminal window with the virtual environment already activated, ready to run pytest or the reference CLI client

No manual setup required — this is the fastest way for a new user to get the lab running. To stop the backend, close the two titled windows it opens.

launch.bat

Raspberry Pi / hardware-only packages (RPi.GPIO, smbus2, cryptoauthlib) are marked Linux-only in requirements.txt and are automatically skipped on Windows — Phase 1 needs none of them.

1. Clone and set up a virtual environment (manual / macOS / Linux)

git clone https://github.com/venaychawda/SecureJTAG.git
cd SecureJTAG

python -m venv .venv

# Windows
.venv\Scripts\activate

# macOS / Linux
source .venv/bin/activate

pip install -r requirements.txt

2. Run the full Phase 1 test suite

pytest tests/ -v --tb=short

Expected output — all tests GREEN:

tests/test_TC01_power_up_lock.py        PASSED
tests/test_TC02_reset_lock.py           PASSED
tests/test_TC03_early_boot_lock.py      PASSED
tests/test_TC04_challenge_generation.py PASSED
tests/test_TC05_valid_auth.py           PASSED
tests/test_TC06_invalid_auth.py         PASSED
tests/test_TC07_replay_protection.py    PASSED
tests/test_TC08_lockout.py              PASSED
tests/test_TC09_lockout_persistence.py  PASSED
tests/test_TC10_rbac_limited.py         PASSED
tests/test_TC11_rbac_engineer.py        PASSED
tests/test_TC12_secure_asset_protection.py PASSED
tests/test_TC13_session_timeout.py      PASSED
tests/test_TC14_relock_reset.py         PASSED
tests/test_TC15_tamper_response.py      PASSED
tests/test_TC16_zeroization.py          PASSED
tests/test_TC17_fault_injection.py      PASSED
tests/test_TC18_permanent_disable.py    PASSED
tests/test_TC19_audit_log.py            PASSED
tests/test_TC20_concurrent_sessions.py  PASSED

20 passed

3. Run a single test case

pytest tests/test_TC05_valid_auth.py -v

4. Run with coverage

pytest tests/ -v --cov=sim --cov-report=term-missing

5. Run the two-process interactive simulation

Start the Secure Debug Controller and software client in separate terminals to observe the JSON-RPC trust boundary live:

# Terminal 1 — Secure Debug Controller (trusted process)
python -m sim.protocol.server

# Terminal 2 — Software side (untrusted process)
python -m sim.protocol.client --role DIAGNOSTICS

server.py listens on 127.0.0.1:9000 and speaks the exact JSON-RPC message contract in sim/protocol/messages.py. On first run it generates a demo ECDSA-P256 debug-tool key pair (sim/protocol/.debug_identity.json, git-ignored, never committed) and enrols the public half into the controller — this stands in for the out-of-band certificate provisioning a real debug tool would receive before ever touching a vehicle's JTAG port. client.py reads that same identity to sign challenges for real, over the real socket.


Secure JTAG Debug Monitor — Two Dashboards

This repo ships two HTML dashboards that look alike but work very differently. Pick the right one for what you're demonstrating.

docs/index.html — offline visual simulation

A self-contained mockup: all state (lock status, role, TTL, audit log) lives in a JavaScript variable in the page itself. It never talks to the Python backend, needs no server, and works by double-clicking the file — which is exactly why it's what GitHub Pages hosts as the public Live Demo. It's useful for a quick, zero-setup walkthrough of the state machine's shape, but it does not exercise any real code in sim/.

docs/index.html

Double-click the file or drag it into a browser tab. Local-only — not wired to a backend.

docs/SecureJTAG.html — real server-client app (local only)

A genuine client of the real backend: every button click sends an actual JSON-RPC message over a real socket to the actual SecureDebugController running in sim/protocol/server.py, and every response you see (session state, RBAC decisions, audit log entries) was computed by the real Python code the pytest suite tests. It performs real ECDSA-P256 signing in the browser via the native Web Crypto API, using the private key handed to it once by the WebSocket bridge.

Browser (docs/SecureJTAG.html)
   │  WebSocket  ws://127.0.0.1:8765
   ▼
sim/protocol/ws_bridge.py   (relays JSON-RPC; hands the browser its local debug-tool identity)
   │  TCP JSON-RPC  127.0.0.1:9000
   ▼
sim/protocol/server.py  →  SecureDebugController  →  ATECCSimulator

Browsers cannot open raw TCP sockets — only WebSocket/HTTP — so ws_bridge.py exists purely to relay WebSocket frames to the real TCP JSON-RPC server byte-for-byte. It also plays the role of "the debug engineer's laptop": it reads the locally-provisioned private key from disk and gives it to the page once per connection, over a small bridge-local message that never touches the real P1↔P2 wire and isn't part of the documented message contract.

Requires the backend running (launch.bat starts it automatically, or manually):

python -m sim.protocol.server       # Terminal 1 — Process 2, port 9000
python -m sim.protocol.ws_bridge    # Terminal 2 — browser bridge, port 8765

Then open docs/SecureJTAG.html. Because it depends on a locally running Python backend, it is not part of the GitHub Pages live demo — it only works when run from a local clone.

What both dashboards show

  • JTAG lock/unlock state, active session role, TTL, lifecycle
  • Authenticate with a valid signature, or a deliberately bogus key to see AUTH_FAIL
  • Relock, tamper trigger, lifecycle transitions, permanent disable
  • RBAC-gated debug operations (try HALT_CPU as READ_ONLY — denied; as ENGINEERING — permitted)
  • Live audit log stream

Hardware Setup — Phase 2 - NEXT MILESTONE

Why This low cost Setup Is Equivalent to Automotive ECU Secure JTAG Learning

Production automotive ECUs — such as the Renesas RH850/U2A, NXP S32K3, and STM32H7 — implement Secure JTAG inside dedicated hardware security modules (HSMs). That silicon is expensive, export-controlled, and requires NDA access. This lab replaces it with a ~$25 discovery board and an ~$8 breakout — and the security model is architecturally identical:

Automotive ECU This Lab Why it is equivalent
HSM / dedicated security core STM32F407G-DISC1 running secure_debug_controller.py in an isolated process Same trust boundary: application software cannot directly access HSM state
Hardware Secure Element (key store) SparkFun ATECC608A DEV-18077 The ATECC608A is the same Microchip IC family used as a companion HSM in automotive designs
Hardware TRNG ATECC608A internal TRNG NIST SP 800-90B compliant — same standard required by ISO 26262 and ISO/SAE 21434
ECDSA-P256 verification ATECC608A Sign/Verify commands Identical NIST P-256 curve and API used in production secure debug controllers
Non-volatile lockout counter ATECC608A monotonic counter Hardware-backed, survives power loss — prevents brute-force even across reboots
JTAG TAP controller hardware gate STM32 RDP (Readout Protection) bits + pyOCD TCK/TMS control Same principle: a hardware bit blocks the debug TAP regardless of software state
ISO/SAE 21434 audit trail Hash-chained AuditLog (tamper-evident, no-clear API) Implements the evidence-chain requirement from TARA artefacts
Role-based debug privileges READ_ONLY / DIAGNOSTICS / ENGINEERING RBAC Directly maps to OEM debug manifest role definitions
OEM lifecycle management DEVELOPMENT → PRODUCTION → SERVICE → DECOMMISSION Standard ECU lifecycle model per ISO/SAE 21434 Section 11
OEM challenge-response protocol JSON-RPC over TCP (replacing SPI/I²C physical bus) Same protocol semantics — only the transport medium differs

Regulatory context

ISO/SAE 21434 (Road Vehicles — Cybersecurity Engineering) and UNECE Regulation 155 require OEMs to document and test debug access control as part of the vehicle cybersecurity management system. The docs/traceability_matrix.md in this repo follows exactly the requirement → test case → verification evidence chain demanded by a TARA (Threat Analysis and Risk Assessment) artefact — giving learners a complete, regulation-aligned example at a fraction of the cost of proprietary toolchains.


Repository Structure

SecureJTAG/
├── sim/
│   ├── hal/
│   │   ├── atecc_base.py               # Abstract crypto interface (ABC)
│   │   ├── atecc_simulator.py          # Phase 1 — software crypto shim
│   │   └── atecc_device.py             # Phase 2 — real ATECC608A (locked)
│   ├── core/
│   │   ├── secure_debug_controller.py  # JTAG state machine + policy
│   │   ├── session_manager.py          # Session tokens and TTL
│   │   ├── access_policy.py            # RBAC permission tables
│   │   ├── audit_log.py                # Hash-chained tamper-evident log
│   │   ├── lockout_manager.py          # Brute-force counter + lockout
│   │   ├── tamper_monitor.py           # Tamper detection and response
│   │   └── lifecycle_manager.py        # Lifecycle state machine
│   ├── protocol/
│   │   ├── client.py                   # Process 1 — reference RPC client (CLI)
│   │   ├── server.py                   # Process 2 — RPC dispatcher + demo identity provisioning
│   │   ├── ws_bridge.py                # Browser WebSocket <-> TCP relay for SecureJTAG.html
│   │   ├── messages.py                 # Message schema constants
│   │   └── .debug_identity.json        # Generated on first server.py run — git-ignored
│   └── config/
│       ├── device_policy.json          # Policy constants (timeouts, roles)
│       └── lifecycle_states.json       # Valid state transitions
├── tests/
│   ├── conftest.py                     # Shared pytest fixtures
│   └── test_TC01_*.py … test_TC20_*.py # One file per TC-ID
├── specs/
│   └── requirements.csv               # Source of truth — READ ONLY (Added to .gitignore)
├── docs/
│   ├── traceability_matrix.md          # REQ → TC → status mapping
│   ├── index.html                      # Offline simulation dashboard (GitHub Pages live demo)
│   ├── SecureJTAG.html                 # Live dashboard — real server-client app (local only)
│   └── images/
│       └── hardware_wiring.png         # Phase 2 wiring photo (Phase 2)
├── firmware/                           # Phase 2 only (locked during Phase 1)
├── host/                               # Phase 2 only (locked during Phase 1)
├── requirements.txt
├── launch.bat                          # Windows one-click setup + dashboard launcher
└── README.md

Traceability

Artefact Location
Requirement → test case → status matrix docs/traceability_matrix.md

License

MIT


About

SecureJTAG Mini Project for Automotive Cybersecurity

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages