Files
Ashin Walpola d107534eb2 Keep vanetza-idf in obu-firmware, so a plain clone builds the firmware
obu-firmware builds against the vanetza-idf C-ITS library, which until now
came from the colleague's microbu-esp32c5 tree beside the repository and was
not tracked here, so a clone of this repository could not build the firmware
it ships. The library alone is now part of obu-firmware, as
obu-firmware/external/vanetza-idf: their external/vanetza-idf at commit
cf4b99f, unchanged (9775 files; see its PROVENANCE.md). CMake takes it from
there by default; -DVANETZA_IDF_DIR still points the build elsewhere.

The rest of the colleague's tree (their own VAM firmware, PKI tooling,
station-link Python tools, the V2X2MAP bridge) stays out of this repository
and gitignored; nothing is pushed to their repository. NOTES.md, docs/06,
TODO.md and the pcap verifier's usage line point at the new location.
2026-09-24 10:56:05 +02:00

5.3 KiB

independent-verifier-01: signed CAM/DENM checked by c-its

An independent implementation (c-its, Rust, https://github.com/TheEnbyperor/c-its, commit e3bb3b82480d6df4237e2a8c35ea0dd7eade25b4 of 2026-09-07; rasn/rasn-its codecs, RustCrypto p256) verifies what the host SUT signs with a lab credential chain issued by vidf_issue. Nothing of the verification is ours: c-its decodes the GeoNetworking and TS 103 097 structures with its own ASN.1 modules, checks the message signature, every certificate signature up to the root it is given, the chain lengths, the end-entity permissions against the issuers' certIssuePermissions and finally the CAM/DENM content against the ticket's SSP (security_authorized).

Material (public parts only)

File Content
LAB_RCA.oer self-signed root, vidf_issue root from a throwaway P-256 key (openssl ecparam -genkey), CPOC-shaped certIssuePermissions (minChainLength 2, eeType app+enrol; second group psid 623 013E/FFC1)
LAB_AA.oer AA under the root, appPermissions psid 623 ssp 0130
LAB_AT.oer standard ticket: psid 36 (01FFFC), 37 (01FFFFFF), 141 (no SSP), 638 (01)
LAB_AT_NOMGMT.oer the same without psid 141
sut-standard.pcap, sut-nomgmt.pcap three CAM carriers (certificate, digest, certificate) and one DENM carrier (GBC) each, recorded with tools/capture_pcap.py
c-its-standard.jsonl, c-its-nomgmt.jsonl c-its-pcap output, one JSON object per frame

Private keys (*.vkey, the PEM) are not part of the evidence. The lab root is not the project's root certificate; the procedure is the one the project root would go through (vidf_issue root --key <encrypted PEM>, pass phrase prompted by OpenSSL, never on the command line).

Commands

vidf_issue root      --key lab-root.pem --name "vanetza-idf lab root" --id LAB_RCA --out chain
vidf_issue authority --issuer chain/LAB_RCA.oer --issuer-key lab-root.pem --name "vanetza-idf lab AA" --id LAB_AA --out chain
vidf_issue ticket    --issuer chain/LAB_AA.oer --issuer-key chain/LAB_AA.vkey --id LAB_AT \
                     --permission 36:01FFFC --permission 37:01FFFFFF --permission 141 --permission 638:01 --out chain
vidf_issue ticket    ... --id LAB_AT_NOMGMT --permission 36:01FFFC --permission 37:01FFFFFF --permission 638:01 --out chain
python tools/capture_pcap.py --sut vidf_sut --pool chain --root LAB_RCA --aa LAB_AA --at LAB_AT --out sut-standard.pcap
python tools/capture_pcap.py --sut vidf_sut --pool chain --root LAB_RCA --aa LAB_AA --at LAB_AT_NOMGMT --out sut-nomgmt.pcap
mkdir ctl && cp chain/LAB_RCA.oer ctl/root-LAB_RCA.oer && cp chain/LAB_AA.oer ctl/aa-LAB_AA.oer
RUST_LOG=info c-its-pcap sut-standard.pcap > c-its-standard.jsonl
RUST_LOG=info c-its-pcap sut-nomgmt.pcap   > c-its-nomgmt.jsonl

c-its was built on Windows with cargo build --release --features build-binary --bin c-its-pcap (cargo 1.98.1). Its ieee80211 dependency enables defmt by default, which needs an embedded logger and does not link on a host; the local checkout sets ieee80211 = { version = "0.5.9", default-features = false, features = ["crypto"] } (a build change, no functional change).

Result

Capture Frames Message signature Certificate signatures (root, AA, AT) Chain lengths / eeType ee_permissions_valid validated_chain security_authorized
sut-nomgmt.pcap 3 CAM + 1 DENM verifies (4/4) verify (4/4) root [2,2] app+enrol, AA [1,1] app true chain 0 (root → AA → AT) true for the CAMs and the DENM
sut-standard.pcap 3 CAM + 1 DENM verifies (4/4) verify (4/4) as above false none false

The digest-signed CAM (second frame) is resolved by c-its from the certificate it saw in the first frame; the DENM carries the certificate (TS 103 097 clause 7.1.2) and the generationLocation.

The difference between the two captures is psid 141 (GN-MGMT) alone: the standard ticket lists it without an SSP, which IEEE Std 1609.2 permits and the ETSI test-suite tickets do as well (CERT_IUT_A_AT: <ssp aid="GN-MGMT"> empty), and which is consistent with an issuer range all. c-its maps an appPermissions entry without SSP to AppPermission::Unsupported (src/security/perms.rs, _ => Self::Unsupported), and CertSubjectAuthorization::authorizes returns false for every unsupported entry (src/security/certs.rs), so c-its cannot validate the permissions of any ticket that carries GN-MGMT that way. The second capture removes the entry to show that everything else c-its checks passes.

What this found on our side

The first attempt (not retained) used the previous lab profile: root and AA with the default chain length (1) and a bitmap range 00/FF for GN-MGMT, the AA with the end-entity SSP 01C0 for psid 623. c-its verified all signatures but reported ee_permissions_valid: false because a root whose certIssuePermissions permit a chain of length 1 does not authorise a ticket two certificates below it (IEEE Std 1609.2 6.4.28). The lab issuer and the test pool now follow the EU CCMS CPOC Protocol Release 3.0 root profile (minChainLength 2, eeType app+enrol, the two psid 623 groups) and TS 102 941 V2.2.1 Table B.6 for the CA-side SSPs; the library's own verifier gained the chain consistency checks it lacked (docs/idf/validation.md, "Chain consistency").