Skip to content

About

C# .NET ISO/IEC 15118 Conformance & Interoperability Test Suite

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Repository files navigation

ISO/IEC 15118 Conformance & Interoperability Test Suite

The conformance and interoperability tests for the WWCP_ISO15118 ISO 15118 stack — its EXI codec, its EV↔EVSE state machines, its TLS and PKI, its Plug & Charge. It is carried here as a submodule of a submodule, inside EVSimulatorApp; this repository is the harness that proves it behaves the way the standard and the independent stacks in the field expect.

The point of separating the two: the app can be built and shipped on its own, and the thing that judges it — the corpus of recorded frames, the loopback E2Es, the live cross-checks against Josev and EVerest — lives beside it rather than inside it, so "does our stack interoperate" is a question this repository answers and the app does not have to carry. Here is the answer.

What is here

ISO15118ConformanceTests.slnx
├─ ISO15118ConformanceTests.Simulation/   the conformance suite proper
│   ├─ Interop/     live cross-checks vs Josev, EVerest, EVDriveFlow, TuxEVSE (all [Explicit])
│   ├─ E2E/         full-stack loopback sessions (SLAC→SDP→TLS→SAP→-2/-20) to SessionStop
│   ├─ StateMachines/, Discovery/, Framing/, Metering/, Sap/, Slac/, Timing/, Transport/
│   ├─ Vectors/     the recorded session corpus the offline tests replay
│   └─ Traces/      the replay and recording machinery behind it
├─ ISO15118ConformanceTests.Pqc/          post-quantum-crypto experiment tests (ML-KEM/ML-DSA)
└─ libs/EVSimulatorApp/                   the stack under test, as a submodule
    ├─ …/WWCP_ISO15118_EXI_Tests/         the stack's codec tests — the byte-exact cbV2G and Josev
    │                                     oracle, carried into this solution so the offline run
    │                                     judges against a foreign encoder and not only ourselves
    └─ …/WWCP_ISO15118_Session_Tests/     and its transport unit tests, carried for the same reason:
                                          the offline gate runs here, so a test that runs only in
                                          the app's solution is one this repository cannot vouch for

Two oracles judge the bytes offline, and only one of them is independent of us: cbV2G generated the vector corpus and shares a generator lineage with EVerest and tux-evse, while EXIficient — Josev's codec — does not. Since 2026-08-07 it is driven over both halves of the corpus: tools/interop-v2gdecoder/ for -2 and DIN, tools/interop-exificient/ for -20, where it found six frames a second codec cannot read at all.

The implementation is not here. Since 2026-08-08 the codec and the session state machines and the CLI live together in libs/EVSimulatorApp/libs/WWCP_ISO15118/ — that submodule is the ISO 15118 stack, libs/EVSimulatorApp/ above it is the apps and the language ports, and this repository is the evidence. Read WWCP_ISO15118's own README for how the stack works; this one is about how it is held to account.

The interop matrix — who we test against, and what happened

Four independent stacks sit on the other end of the live cross-checks (JosevInteropTests, EverestInteropTests, EvDriveFlowInteropTests, TuxEvseInteropTests; all [Explicit]). They are worth having because they differ — each brings a different EXI lineage and a different kind of evidence:

Josev EVerest eVDriveFlow tux-evse
Who SwitchEV (Python) LF Energy — the stack on real chargers EDF Lab IoT.bzh (Rust)
Their station, for our EV→ runs their SECC — EXIficient EvseV2G · Evse15118D20 · IsoMux — cbV2G (OpenV2G in the 2023 image) (details) their SECC — OpenEXI/Nagasena a responder replaying a captured Audi
Their EV, for our ←SECC runs their EVCC — EXIficient PyEvJosev — EVerest's fork of Josev, so EXIficient again (details) their EV — OpenEXI their injector, replaying captured cars (an Audi, a VW) (details)
Versions met current 2023.10.0 · 2025.10.0 · 2026.02.1 (source build) 60249c3 v0.1 image · main fc51088 (source build)
Directions EV→ ←SECC throughout EV→ throughout · ←SECC only where it adds something (details) EV→ ←SECC EV→ ←SECC

The Ours column is our own C# stack against itself — a loopback E2E with both peers ours, which is what runs in the offline suite and what every counterparty column is measured against. It says the scenario exists and is guarded here; it says nothing about conformance, because both ends share our assumptions. That is the whole reason for the columns to its right. Sessions recorded from the live runs replay offline as part of the suite too, so the matrix does not rot silently when the code moves. Evidence per cell lives under docs/interop-runs/; the run-notes README explains how to read one.

Status: ✅ complete live session  ·  ◐ partial — the session ran only to the stated point, or it ran through and one property is not checkable against that counterparty; the cell says which  ·  ⛔ blocked by a counterparty defect or limitation  ·  ▢ not attempted yet  ·  — not applicable / not implemented on their side

Which side is ours — the arrow points the way the session is driven, and the label names our role:

EV→ our C# EVCC drives their station. The "forward" direction: we are the car.
←SECC their EV drives our C# SECC. The "reverse" direction: we are the charging station.
EV→ ←SECC both, in separate sessions.

Transport rows and application rows — the distinction that decides how two neighbouring cells relate. The TLS row of each table (TLS 1.2 (unilateral) for -2, Mutual TLS 1.3 for -20) measures the transport: the version, the prescribed cipher suites, who authenticates, and whether the peer's chain builds to a root we trust. Unilateral means only the station presents a certificate, which is what -2 prescribes; -20 wants both sides and has its own row. Every other row is the application layer riding on that transport. So Plug & Charge is not a second TLS measurement: -2 forbids Contract without TLS, so that row presupposes the transport row and measures the contract credential — PaymentDetails, the signed AuthorizationReq, the station's verdict. One session can be ✅ in the transport row and carry a second marker in the application row; read them as two questions, not two verdicts on one thing.

Two markers in one cell mean the scenario runs and something in it does not: the first names what works, the second what is missing. ✅ complete charge · ▢ nothing validates the contract is a session that ran end to end with one property nobody can check — not a failure, and not a clean pass either.


We as an EVCC (Electric Vehicle)

Our C# EVCC drives their station. Ours is the loopback, where the station is ours too.

ISO 15118-2

Scenario Ours (C# loopback) Josev EVerest eVDriveFlow tux-evse
AC, EIM ✅ Iso2LoopbackTests ✅ ✅ plain TCP · ✅ over TLS 1.2 (details) — —
DC, EIM ✅ Iso2LoopbackTests ✅ ✅ (details) — ◐ stops at SessionSetup (details)
Plug & Charge ✅ Iso2LoopbackTests (signed auth + metering receipts) ✅ ✅ 30,16 kWh charged, 81 loops · ▢ nothing validates the contract itself (details) — —
Contract provisioning ✅ Iso2LoopbackTests — Install and Update, key unwrapped — not implemented ✅ Install, with our MO backend behind their station · ⛔ Update answers OK from an empty handler (details) — —
Pause / Resume ✅ Iso2LoopbackTests ✅ OK_OldSessionJoined — — —
Signed tariffs (SalesTariff) ✅ E2E ✅ their MO-signed tariff verified by us — — —
Renegotiation ✅ Iso2RenegotiationSequenceTests (AC + DC through CableCheck) ✅ AC, [V2G2-841] ⛔ their station fails its own cable check and goes Inoperative (details) — —
TLS 1.2 (unilateral) ✅ TlsLoopbackTests · ✅ trusted_ca_keys on the wire, [V2G2-651] (details) ✅ ✅ the prescribed suite, their full chain against the root alone (details) — —

ISO 15118-20

Scenario Ours (C# loopback) Josev EVerest eVDriveFlow tux-evse
DC, Scheduled, EIM ✅ Iso20LoopbackTests ✅ TCP + TLS ✅ ✅ 12 exchanges · ⛔ their SECC drops DC_ChargeLoop (details) —
DC, Dynamic ✅ Evcc20DynamicModeTests — ✅ — —
AC ✅ Iso20LoopbackTests — ✅ plain TCP · ✅ over mutual TLS 1.3 (details) — —
BPT, AC + DC (incl. Dynamic) ✅ Evcc20BidirectionalTests, Evcc20BptRankingTests — ✅ DC_BPT and AC_BPT, plain and over mutual TLS 1.3 (details) — —
Plug & Charge ✅ Iso20LoopbackTests ✅ — commented out on their side — they implement none (details) —
CertificateInstallation ✅ Iso20LoopbackTests — full roundtrip, contract key unwrapped — — — —
Pause / Resume ✅ Iso20LoopbackTests ⛔ their -20 session context stays empty (details) ✅ over mutual TLS, resuming at DcChargeParameterDiscovery (details) — —
Signed tariffs (AbsolutePriceSchedule) ✅ signature verified at the EV — — they send none, deliberately (details) — —
Mutual TLS 1.3 ✅ MutualTlsLoopbackTests, BcMutualTlsLoopbackTests ✅ ✅ our client on Windows (details) — —
SDP discovery ✅ FullStackLoopbackTests ✅ ✅ multicast and unicast — —
Multi-protocol SAP offer ✅ MultiProtocolSapTests — ✅ all four offer shapes · ⛔ over TLS it routes a -20 session onto TLS 1.2 (details) — —
WPT · ACDP ▢ codec only, independently judged (details) no independent stack implements these state machines

ISO 15118-20 MCS

Scenario Ours (C# loopback) Josev EVerest eVDriveFlow tux-evse
MCS ✅ Secc20McsTests — ✅ Scheduled and Dynamic — —
MCS_BPT ✅ Secc20McsTests (ranking + envelope) — ✅ service 9, our discharge limits read back (details) — —

We as a SECC (Charging Station / EVSE)

Their EV drives our C# SECC. Ours is the same loopback, seen from the station's side.

ISO 15118-2

Scenario Ours (C# loopback) Josev EVerest eVDriveFlow tux-evse
AC, EIM ✅ Iso2LoopbackTests ✅ ✅ their EV, EIM plain (details) — ✅ a real VW's route · ✅ two Porsche routes (details)
DC, EIM ✅ Iso2LoopbackTests ✅ — — ✅ the full captured-Audi session (details)
Plug & Charge ✅ Iso2LoopbackTests ✅ signed auth and signed metering receipt verified, chain at their MO root (details) ✅ their EV over TLS (details) — —
Signed tariffs (SalesTariff) ✅ Secc2TariffTests ✅ their EV consumed ours — — —
Renegotiation ✅ Iso2RenegotiationSequenceTests ✅ AC, [V2G2-841] — — —
TLS 1.2 (unilateral) ✅ TlsLoopbackTests ✅ their EV validates our chain against their V2G root (details) — — ⛔ their configs offer neither prescribed suite · ◐ unpinned, 4 exchanges (details)

ISO 15118-20

Scenario Ours (C# loopback) Josev EVerest eVDriveFlow tux-evse
DC, Scheduled, EIM ✅ Iso20LoopbackTests ✅ TCP + TLS — — —
DC, Dynamic ✅ Secc20DynamicModeTests ✅ their EV adopts the mode we offer (details) ✅ with a Scheduled control arm that switches with the offer (details) ✅ 15 exchanges into the charge loop (details) —
AC ✅ Iso20LoopbackTests ✅ TCP + TLS ✅ 56 exchanges, 44 charge loops — over mutual TLS 1.3 and in Dynamic (details) — —
BPT, AC + DC (incl. Dynamic) ✅ Secc20AcBptTests ✅ their EV selects service 6 / 5 ✅ AC_BPT and DC_BPT out of our catalogue, all four AC variants (details) ✅ DC_BPT, both envelopes crossed (details) —
Plug & Charge ✅ Iso20LoopbackTests (signed auth verified at SECC) ✅ contract chain at their MO root, TLS client chain at their OEM root (details) ✅ their EV's signed AuthorizationReq verified, chain at their MO root (details) — they implement none (details) —
CertificateInstallation ✅ Iso20LoopbackTests ✅ our signed response verified · ⛔ their handler ends at NotImplementedError ✅ their EV's real OEM chain, built against their OEM root · ⛔ the same wall (details) — —
Pause / Resume ✅ Iso20LoopbackTests — — — —
Signed tariffs (AbsolutePriceSchedule) ✅ Iso20LoopbackTests ✅ their AC EVCC consumed our signed schedule · ▢ nothing external verifies it (details) — — —
Renegotiation ✅ Secc20DynamicModeTests ✅ a real SessionStopReq(ServiceRenegotiation), [V2G20-1477] · ⛔ then drops the link (details) ✅ the same, in DC · ⛔ the same drop (details) — —
Mutual TLS 1.3 ✅ MutualTlsLoopbackTests ✅ their EV's client chain anchored at their OEM root · ⛔ the leaf is their OEM provisioning certificate, not a Vehicle one (chain, credential) ✅ their EV presents an OEM vehicle certificate (details) ✅ secp521r1 both ways (details) —
SDP discovery ✅ FullStackLoopbackTests ✅ ✅ their EV discovers the recording fixture (details) ✅ their EV found our SECC —

ISO 15118-20 MCS

Scenario Ours (C# loopback) Josev EVerest eVDriveFlow tux-evse
MCS ✅ Secc20McsTests — ✅ their EV picked service 8 out of our catalogue (details) — —
MCS_BPT ✅ Secc20McsTests — — — —

The background behind a cell — what the run showed, what it did not, and which report it produced — is one document per cell under docs/matrix/. The reasoning and the defects live on the counterparty's own page, linked under Deeper reading below.

Run

git submodule update --init --recursive
bash libs/EVSimulatorApp/libs/WWCP_ISO15118/tools/download-schemas.sh
dotnet test -c Release

The middle step is not optional. The source generators run at build time from the ISO schemas in the app's WWCP submodule (libs/EVSimulatorApp/libs/WWCP_ISO15118/**/Schemas/), and those schemas are ISO's — not redistributed here, so a fresh clone carries only a placeholder README.md in each Schemas/ and the build stops at EXIGEN001. Running the script is you accepting the ISO Customer Licence Agreement, which nobody can accept on your behalf; if you already have the files, SCHEMA_CACHE=<dir> bash …/download-schemas.sh lays that copy out instead of fetching — <dir> holding the iso-2/, iso-20/ and amd1/ directories the script would otherwise have created.

The offline run (dotnet test) needs no C toolchain, no Java and no network: the record-mode cross-checks re-encode Josev's captured EXIficient frames through our codec (WWCP_ISO15118_EXI_Tests), the session corpus under Vectors/ guards our own wire output against regression, the transport's own decisions are unit-tested in WWCP_ISO15118_Session_Tests, and the loopback E2Es run both peers in-process. 1 462 tests, all four assemblies green. The live cross-checks against a running Josev or EVerest are [Explicit] and stay out of the offline run — they need the other stack on the wire. What each of them has proven is the matrix above.

Deeper reading

docs/josev-cross-validation.md the independent codec (EXIficient), the counterparty with the most history here, and the only one that serves both roles well. Every -20 energy mode any independent stack implements, over TCP and TLS, plain and Plug & Charge, in both control modes.
docs/everest-cross-validation.md the independent charger, the thing a car in the field actually meets, and the counterparty that has found the most defects in this project; almost all of them share one of two shapes, which that page names. No unattempted cell left, the walls that remain named one at a time, and the drafts it produced indexed in docs/reports/ — which is the only place their number is kept, because carrying it here is how it went four out of date.
docs/evdriveflow-cross-validation.md the second independent codec (OpenEXI), and the highest yield per exchange here: one defect of ours that every other oracle was structurally blind to, and four of theirs. The wall that held all four of its capabilities turned out to be a closed file descriptor, not a state machine.
docs/tux-evse-cross-validation.md a replayer, not a codec: their scenarios come from packet captures, so what it offers is a real car's route and the only DIN 70121 material this project has seen. As a responder it answers the car in its recording and no other; as an injector at their HEAD it drove our SECC through the full captured-Audi DC session and a VW AC route — and reached the one arm of our state machine no self-consistent test had ever executed. Over TLS it produced the first external check of our TLS profile, and two findings drafted for them. Their Tesla DIN capture is unreadable to us past the handshake — and the handshake alone carried a vendor-proprietary protocol at priority 1, an offer shape nothing here could have written for itself.
docs/open-work.md the inverse of the matrix above: every cell that is not ✅, why, and who it waits on. The to-do list.
docs/interop-runs/ one write-up per live run: configuration, frame logs, divergences. History, not a to-do list — each note's Next section is a snapshot from that day, and later runs close items without editing it
docs/reports/ findings written up for the counterparty they belong to — forty-seven filings across six projects, each a draft for a person to send, with the reproduction that makes it confirmable
tools/interop-*/ how to bring each counterparty up and drive it — Josev · EVerest · eVDriveFlow · tux-evse
docs/assumed-values-sweep.md where our own assumptions replaced values the protocol supplies

The stack all of this tests — the EXI codec, the state machines, the TLS/PKI, the CLI — is documented in WWCP_ISO15118, and the apps built on it in EVSimulatorApp one level above it.

This repository is only the judge.

About

C# .NET ISO/IEC 15118 Conformance & Interoperability Test Suite

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages