04 · Cybersecurity Testing for Vehicles (ISO 21434)¶
Connected ECUs — telematics units, Ethernet-based domain controllers, anything reachable from outside the vehicle's trusted boundary — carry a threat model functional and safety testing don't cover. ISO 21434 governs automotive cybersecurity engineering; this module covers its testing-relevant pieces: TARA-driven test scoping, the UDS security access mechanism, and how penetration-testing thinking applies to an ECU test bench.
About this module
No physical penetration-testing toolchain or live vehicle network was used to produce this content. The TARA model, UDS security access flow, and CAPL sketches below reflect documented ISO 21434 and UDS (ISO 14229) standards — actual penetration testing against a real vehicle network requires specific authorization and specialized tooling well beyond this course's scope.
TARA: the cybersecurity equivalent of the FMEA¶
Threat Analysis and Risk Assessment (TARA) is ISO 21434's structured process for identifying threats and rating their risk — the cybersecurity parallel to the FMEA that feeds Level 3 Module 6's fault matrix. A TARA identifies:
| TARA element | Example |
|---|---|
| Asset | The ECU's ability to control braking torque |
| Threat scenario | An attacker on the vehicle's Ethernet/CAN network sends a spoofed braking-torque request |
| Attack path | Compromised infotainment unit → gateway → braking ECU, if the gateway doesn't segment traffic |
| Impact rating | Safety, financial, operational, privacy — same S/E/C-style structured rating spirit as ASIL |
| Cybersecurity Assurance Level (CAL) | Drives how much testing rigor the corresponding requirement needs, the security analog of ASIL |
Just as Level 3 Module 4 tied test rigor to ASIL, cybersecurity test rigor should tie to CAL — a low-CAL informational ECU doesn't need the same penetration-testing depth as a high-CAL gateway or braking ECU.
UDS security access: the mechanism test engineers touch most¶
Most ECUs restrict sensitive diagnostic services (calibration writes,
reflashing, certain control commands) behind a Security Access
(UDS service 0x27) seed/key exchange:
1. Tester requests a Security Access "request seed" for a given level.
2. ECU responds with a pseudo-random seed.
3. Tester computes a key from the seed using an algorithm (often a
proprietary or cryptographic function shared out-of-band with the
supplier — never guessable from the protocol alone).
4. Tester sends the computed key back.
5. ECU verifies it; on success, restricted services unlock for that
session. On repeated failure, the ECU should enforce an escalating
lockout delay.
CAPL testing of the security access mechanism¶
// Illustrative -- CAPL UDS security access flow. The actual key
// algorithm is supplier-specific and provided separately; this
// example uses a placeholder function.
testcase tc_SecurityAccess_CorrectKeyUnlocks()
{
testCaseTitle("[SEC-REQ-101] Valid key unlocks restricted diagnostic services");
DiagRequest seedReq = new DiagSecurityAccessRequestSeed(0x03);
byte seed[4], key[4];
DiagSendRequest(seedReq);
testWaitForDiagResponse(seedReq, 1000);
DiagGetResponseData(seedReq, seed, 4);
ComputeSecurityKey(seed, key); // supplier-provided algorithm, not shown
DiagRequest keyReq = new DiagSecurityAccessSendKey(0x03, key);
DiagSendRequest(keyReq);
testWaitForDiagResponse(keyReq, 1000);
testStepCheck("security access granted", DiagGetLastResponseType(keyReq) == POSITIVE_RESPONSE);
}
testcase tc_SecurityAccess_LockoutAfterRepeatedFailures()
{
testCaseTitle("[SEC-REQ-102] ECU enforces escalating lockout after repeated invalid keys");
int i;
byte badKey[4] = {0x00, 0x00, 0x00, 0x00};
for (i = 0; i < 3; i++)
{
DiagRequest seedReq = new DiagSecurityAccessRequestSeed(0x03);
DiagSendRequest(seedReq);
testWaitForDiagResponse(seedReq, 1000);
DiagRequest keyReq = new DiagSecurityAccessSendKey(0x03, badKey);
DiagSendRequest(keyReq);
testWaitForDiagResponse(keyReq, 1000);
testStepCheck("invalid key correctly rejected", DiagGetLastResponseType(keyReq) == NEGATIVE_RESPONSE);
}
// A 4th attempt, per most UDS security implementations, should now
// be rejected immediately with a "required time delay not expired"
// negative response code, without even issuing a new seed.
DiagRequest lockedReq = new DiagSecurityAccessRequestSeed(0x03);
DiagSendRequest(lockedReq);
testWaitForDiagResponse(lockedReq, 1000);
testStepCheck("lockout delay enforced on 4th attempt",
DiagGetNegativeResponseCode(lockedReq) == NRC_REQUIRED_TIME_DELAY_NOT_EXPIRED);
}
tc_SecurityAccess_LockoutAfterRepeatedFailures matters as much as
the positive-path test — a security mechanism with no lockout, or a
lockout an attacker can trivially reset (e.g., by power-cycling the
ECU), fails its actual security purpose even if the seed/key exchange
itself is cryptographically sound.
Penetration-testing thinking, adapted to a bus-level test bench¶
A test engineer without a dedicated red-team toolchain can still apply adversarial thinking within the CAPL/CANoe skillset already built:
| Adversarial question | How to test it with existing tooling |
|---|---|
| Can an unauthorized node spoof a message from a trusted sender? | Send the exact frame ID/payload from an unauthorized simulated node and check whether the receiving ECU has any sender-authentication mechanism (e.g., a MAC/counter in the payload) that should reject it |
| Does a malformed diagnostic request crash or hang the ECU rather than rejecting it cleanly? | Fuzz UDS request payloads with malformed lengths/subfunctions (see Level 2's DTC module for well-formed request structure) and confirm the ECU always returns a clean negative response, never becomes unresponsive |
| Is a security-relevant DTC actually logged when an attack is attempted? | Verify a failed security access attempt sets an expected security-event DTC, not just a silent rejection |
Where this module's coverage ends¶
Genuine automotive penetration testing — network protocol fuzzing at scale, side-channel/timing attacks on cryptographic implementations, firmware reverse engineering — requires specialized tooling, training, and typically a dedicated red-team function with explicit authorization. This module's scope is limited to the adversarial testing habits a functional/HIL test engineer can and should apply within their existing CAPL/CANoe skillset — recognizing that boundary honestly is itself part of professional practice, the same way Level 3 Module 9 drew an honest line around SOTIF's Area 3 scenario discovery.
Cheat sheet¶
| Concept | Key point |
|---|---|
| TARA | Cybersecurity's FMEA-equivalent — threats, attack paths, CAL ratings drive test rigor |
| CAL | The security analog of ASIL — higher CAL needs deeper testing |
| UDS Security Access (0x27) | Seed/key exchange gating sensitive diagnostic services |
| Lockout testing | As important as key-validation testing — a resettable/absent lockout defeats the mechanism |
| Adversarial habits within existing tooling | Spoofing, malformed-input, and logging checks are reachable with CAPL; deep pentesting is not |
How It Actually Works¶
Why the seed can't just be a fixed value, and what "pseudo-random"
actually has to mean here. If DiagSecurityAccessRequestSeed
returned the same seed every time (or a seed following a predictable
sequence, like an incrementing counter), an attacker could compute the
key once — by any means, including a single successful legitimate
unlock captured off the bus — and replay that exact key indefinitely
without ever needing to know the underlying key-derivation algorithm.
A correctly implemented UDS 0x27 seed generator uses a
cryptographically adequate random or pseudo-random source (commonly
seeded from a hardware RNG or a counter mixed with an unpredictable
runtime value) specifically so that observing N successful seed/key
exchanges gives an attacker no useful information for predicting the
N+1th seed. tc_SecurityAccess_CorrectKeyUnlocks as written only
proves the happy path works with one valid seed/key pair — it does
not, by itself, prove seed unpredictability, which is why a real
security test plan adds a dedicated statistical or repeated-sampling
check on seed values (comparing many captured seeds for detectable
patterns) as a separate testcase from the functional unlock check.
Why the lockout has to be measured in ECU-persistent time, not
session time. A naive lockout implementation might track failed
attempts in RAM and reset the counter on any ECU reset or power cycle
— which defeats the mechanism against exactly the attacker model it
exists for, since an attacker with physical or network access to
power-cycle the ECU (or trigger a reset via an unrelated diagnostic
service) gets unlimited retry attempts, just three at a time. A
correct implementation persists the failed-attempt count and the
lockout-expiry timestamp in non-volatile memory (EEPROM/flash) or
derives the delay from a monotonic counter unaffected by reset, so the
required time delay (UDS NRC 0x37, "requiredTimeDelayNotExpired")
continues counting across a reset. This is exactly the mechanism
Exercise 1 is pointing at — the lockout test is incomplete without
a reset-survival check, because a lockout that survives inspection but
not a power cycle looks identical to a correct one in the base test.
Why TARA's attack-path column is the thing that actually drives which ECU gets deep security testing. A naive risk model might rate an ECU's cybersecurity test depth purely by what that ECU itself controls (a braking ECU gets deep testing, an infotainment unit doesn't). But ISO 21434's attack-path concept means an asset's actual exposure depends on everything reachable before it in the network topology — the TARA example's attack path (infotainment → gateway → braking ECU) means the infotainment unit's own security testing depth should be driven partly by what it can reach downstream, not just by its own low intrinsic asset value, because a gateway that fails to enforce message-origin segmentation turns a low-value, externally-reachable ECU into the practical entry point for compromising a high-CAL one. This is the mechanical reason gateway segmentation testing (confirming the gateway actually drops or rejects out-of-profile messages from the infotainment side rather than merely relaying everything) is one of the highest-leverage single tests in a vehicle network's whole cybersecurity test suite.
Exercise¶
tc_SecurityAccess_LockoutAfterRepeatedFailureschecks a 4th attempt is rejected. Extend it to check that power-cycling the ECU between the 3rd and 4th attempt does NOT reset the lockout counter — write the testcase structure (the actual power-cycle mechanism depends on rig capability, so describe what HIL primitive from Level 3 Module 1 you'd need).- Design a spoofing test for a message that includes a rolling counter or MAC field intended to prevent replay — what exactly would you send, and what response from the ECU would indicate the anti-replay mechanism is actually working versus merely present in the DBC/A2L definition but unenforced?
- Explain, referencing the CAL concept, why a low-CAL courtesy-light ECU and a high-CAL telematics gateway should not receive the same security test depth, and sketch what a CAL-based test-tiering table (parallel to Level 3 Module 4's ASIL-based rigor table) would look like.