Castell · SkyV2X

ETSI TS 103 097 · cert format

Every C-ITS cert.
Every signed CAM. This shape.

ETSI TS 103 097 specifies the certificate format and the SignedData / EncryptedData wrappers every C-ITS message uses. It's the layer between IEEE 1609.2 cryptography and the application messages above. Castell signs to it.

Anatomy of a TS 103 097 signed message SignedData ts 103 097 §5 · outer wrapper hashAlgorithm sha256 tbsData to-be-signed payload + headerInfo payload.unsecuredData <CAM bytes> · application message bytes headerInfo.psid 36 CAM service headerInfo.generationTime 706970579606066 microseconds since 2004-01-01 headerInfo.generationLocation { lat, lon } optional, ITS reference frame headerInfo.requestedCertificate absent cert-by-value not requested signer CHOICE digest 1A2B3C4D5E6F7081 HashedId8 of the AT certificate (example) signature CHOICE ecdsaNistP256Signature r = 0x8f c3 ad ... 1c s = 0x4e b7 a2 ... 9d
A signed CAM, taken apart. SignedData → tbsData → signer → signature.

What ETSI TS 103 097 specifies

TS 103 097 is the wire-level cryptographic contract for C-ITS. It defines:

The spec is built on top of IEEE 1609.2 cryptographic primitives. Where 1609.2 defines "how do you sign with ECDSA on curve X", TS 103 097 defines "what does a signed CAM actually look like on the wire, and what does its issuer cert look like".

Explicit vs implicit certificates

TS 103 097 inherits two cert flavours from 1609.2:

Explicit certs

The public key is carried directly in the cert body, as a PublicVerificationKey CHOICE with the curve and the encoded point. Verification is the straightforward ECDSA flow: hash the to-be-signed bytes, run ecdsa-verify with the carried key, accept or reject.

Implicit certs

The cert body carries a reconstructionValue instead of a key. The actual public key is derived by combining the reconstruction value with the issuer's public key — an ECQV (Elliptic Curve Qu-Vanstone) reconstruction. Implicit certs are smaller on the wire (no full pubkey, just a single point) at the cost of an extra reconstruction step before any signature can be verified.

Castell issues Explicit only. Implicit support is not implemented. This is a deliberate scope choice: Explicit certs are sufficient for the integrator workflows the testbed serves, and Implicit reconstruction adds significant complexity for marginal wire savings. The choice also dodges the IEEE 1609.2 P-384 implicit constraint — see the multi-curve page for that specific edge case.

How Castell signs to TS 103 097

Wire version

Castell declares conformance to TS 103 097 v1.3.1 cert format COER. v1.3.1 is the wire-stable cert encoding currently produced by the deployed ITS-G5 ecosystem. Newer versions (v2.1.1, v2.2.1) add fields and CHOICE entries but retain backward compatibility for the v1.3.1 subset; pinning to v1.3.1 means anything in the wild can parse Castell's certs without spec version negotiation.

What gets signed

Every artefact Castell publishes is signed end-to-end:

PSID and SSP — what each cert can sign

Every cert carries an appPermissions field enumerating the PSIDs (Provider Service Identifiers) the cert is authorized to claim, and the SSP (Service-Specific Permissions) constraining what within each PSID. A signed CAM must reference a PSID for which the signing cert holds permission; receivers reject signatures whose PSID isn't covered.

Castell's AT-2 release (v0.6.1, see changelog) widened the AA's certIssue permission set to cover all Day-1 services — CAM, DENM, SPATEM, MAPEM, IVIM, SREM, SSEM, VAM, CPM, GN-MGMT. Stacks running e.g. VAM (PSID 638) can now request an AT from Castell without hitting strict subset-chain validation rejections.

generationTime and the freshness window

Every signed message carries generationTime. A receiver rejects signatures whose generationTime falls outside an acceptable window from local time — typically ±5 minutes. Castell enforces this on incoming requests too: see Castell v0.8.3 in the changelog for the default window and the rationale.

Decoding TS 103 097 messages

Castell exposes a couple of debug endpoints that parse incoming TS 103 097 wire bytes and return their structure as JSON. They are deliberately public so any integrator can sanity-check what their stack is producing:

# Decode an EC request you just built — see what the wire looks like
curl -X POST https://pki.skyv2x.com/v221/ec-request-decode \
  -H 'Content-Type: application/octet-stream' \
  --data-binary @your-ec-request.coer

# Same for AT request (v2.2.1 strict decode)
curl -X POST https://pki.skyv2x.com/v221/at-request-decode \
  -H 'Content-Type: application/octet-stream' \
  --data-binary @your-at-request.coer

The decoded JSON exposes every CHOICE tag, every option field, the signer reference and the PSIDs/SSPs the signing cert holds. It's the fastest way to debug a v2.2.1 conformance failure without standing up your own parser.

Related material