Open Charging Technology · Whitepapers

EVQI Transport: Multi-Channel Communication between Charging Stations and CSMS

Version 0.1, Draft, 2026-10-03 CC-BY-SA 4.0 · OpenChargingTechnology/Whitepapers · reference implementations: Vanaheimr/Hermod (transport), OpenChargingCloud/WWCP_OCPP (OCPP)

This document describes the transport layer of EVQI – "Unified data exchange for electrical Vehicle charging", funded by the NLnet NGI0 Commons Fund. EVQI aims at an open, real-time communication and validation layer with cryptographic integrity anchors, metrological correctness and low-latency communication. It is a concept and a work plan, not yet a specification.

It elaborates the outline in Resilient, high-available and pooled parallel Connections and builds on TLS Client/Server Configuration for OCPP v2.x, Metrological CBOR and OCPP 2.x Firmware Updates with Software Separation.


Contents

  1. Summary
  2. Starting point
  3. Transport options: HTTP/1.x, HTTP/2, HTTP/3
  4. WebTransport
  5. Concept: channel sessions (IChannelSession / IChannel)
  6. Connection setup and discovery via DNS (HTTPS RR, SRV)
  7. Security: role certificates, multiple client certificates, privileged channels
  8. Message representation and message signatures: Metrological CBOR and COSE
  9. Reconnect policies
  10. Resource and denial-of-service protection
  11. Mapping to OCPP 1.6 and OCPP 2.x
  12. Implementation status
  13. Work plan
  14. Open questions and decisions
  15. References

0. Summary

The problem. OCPP-J knows exactly one WebSocket connection per charging station, and on it each side may have only one CALL outstanding at a time. Everything shares this connection: small real-time data, transactions, configuration, certificates and firmware commands. Large transfers (logs, firmware) therefore run over separate HTTP or FTP URLs today, with credentials in the URL and without uniform authorization or logging, or they clog the only channel. The OCPP 2.1 specification admits as much: frequent large SEND messages "may cause a delay for other messages" (Part 4, §4.2.4).

The proposal. A channel session (IChannelSession) bundles several named channels (IChannel) between one charging station and one CSMS. Every channel is a complete OCPP-J WebSocket (RFC 6455 framing) with its own priority and its own CALL slot. It runs over one of three bindings:

Binding A channel is Transport connections per station
HTTP/1.x its own TCP+TLS connection (RFC 6455) one per channel
HTTP/2 its own stream via Extended CONNECT (RFC 8441) one per role/certificate domain
HTTP/3 its own QUIC stream via Extended CONNECT (RFC 9220) one per role/certificate domain

WebTransport is treated as a separate research track (chapter 3): structurally the best fit, but still a draft and not OCPP-J compatible.

The load-bearing decisions:

  1. The control channel is today's OCPP-J WebSocket, unchanged. Everything else is used only after a capability negotiation, which keeps the system fully backward compatible.
  2. The synchronicity rule is per connection (Part 4, §4.1.1: "this rule is per OCPP-J connection"). Every channel therefore has its own outstanding CALL. Message IDs already have to be unique across all connections of the same charging station identity (§4.1.4): the specification already allows for several connections per identity.
  3. Privileges are bound to the TLS connection. HTTP/2 (RFC 8740) and QUIC (RFC 9001, §4.4) forbid client authentication after the handshake. A channel with elevated privileges therefore always needs its own TLS connection with its own role certificate. That is intended: firmware updates run over an explicit, time-limited on-demand channel, not over the everyday WebSocket, and they are logged separately and tamper-evidently.
  4. Messages are signed by default. Future OCPP messages use Metrological CBOR as their default representation and carry COSE signatures (RFC 9052). Justified exceptions are allowed. The role in the connection's certificate is therefore only an upper bound: a message is executed only if both the connection's role and the role of the message's signer permit it (chapter 7).
  5. Reconnects and resources are governed per session. The control channel follows the OCPP back-off rule exactly (§5.4). Other channels reconnect afterwards and only when needed, privileged ones never automatically. The server holds budgets per identity and per node and sheds bulk channels first under load.
  6. Discovery via DNS: HTTPS RR (RFC 9460) first, then SRV (RFC 2782), then A/AAAA. The TLS certificate is always checked against the configured name, never against the DNS target.

The initial questions, answered briefly:


1. Starting point

1.1 OCPP-J today

The following points are normative (OCPP 2.1 Edition 2, Part 4; OCPP-J 1.6):

Topic Rule Source
Roles charging station = WebSocket client, CSMS = server; the station keeps the connection open all the time 2.1 Part 4 §3, §3.1
Identity identity as the last path segment of the connection URL; the CSMS is RECOMMENDED to check it against the credentials §3.1.1
Versions negotiated via Sec-WebSocket-Protocol (ocpp1.6, ocpp2.0.1, ocpp2.1, registered with IANA) §3.1.2
Synchronicity at most one outstanding CALL per sender, per OCPP-J connection (2.1: SHALL NOT, 1.6: SHOULD NOT) §4.1.1
Message IDs unique across every WebSocket connection using the same charging station identity §4.1.4
SEND (new in 2.1) unconfirmed, may be sent while a CALL is outstanding; warning about delaying other messages §4.2.4
Reconnect 2.0.1/2.1: increasing back-off with a random part (RetryBackOffWaitMinimum, RetryBackOffRandomRange, RetryBackOffRepeatTimes); 1.6: no back-off rule 2.1 §5.4, §8; 1.6J §5.4
Local Controller one WebSocket connection to the CSMS per connected station 2.1 §6.2
Signed messages (2.1) JWS-wrapped messages with action <Action>-Signed 2.1 §7
File transfer firmware download and log upload via URLs (FileTransferProtocols: FTP, FTPS, HTTP, HTTPS, SFTP); log upload by HTTP PUT, Basic Auth credentials inside the URL; no standardized resume (2.1, N01.FR.27) 2.1 Part 2, L01/N01
Security profiles 1 (Basic without TLS), 2 (TLS + Basic), 3 (mTLS); one profile per port is allowed 2.1 Part 2 A; 1.6 Security WP §2
Certificates station: CN = serial number; CSMS: CN = FQDN; Extended Key Usage should not be used (ISO 15118) 2.1 Part 2, A00.FR.5xx
Firmware authenticity firmware signature via a separate manufacturer PKI (Firmware Signing Certificate) 2.1 Part 2 A, L01

1.2 What hurts

  1. Head-of-line blocking on two levels. Everything shares one ordered byte stream, and each side has a single CALL slot. A large report (NotifyReport, NotifyMonitoringReport) or a stream of SEND messages delays time-critical control.
  2. Large files travel outside OCPP. Firmware and logs use separate HTTP/FTP servers. Credentials sit in URLs, authorization and logging are not uniform, there is no resume and no priority control, and every firewall rule is extra infrastructure.
  3. No least privilege. Whoever controls the one everyday connection (or holds the one client certificate) can do everything: load management, network profiles, certificates, firmware.
  4. Privileged operations are not logged separately. Calibration law demands traceability for updates of legally relevant software (section 6.7).
  5. The number of connections scales badly. A Local Controller with 20 stations holds 21 connections to the CSMS. After a CSMS restart all stations reconnect at the same time.
  6. Message-level authenticity is optional. Signed messages exist in OCPP 2.1 but are not required, so a TLS-terminating intermediary (Local Controller, load balancer) can read and alter everything it relays.

1.3 Requirements

ID Requirement
R1 Several parallel channels per station with different priorities (real-time, control, bulk)
R2 Bindings: several HTTP/1.x connections, WebSockets over HTTP/2 and over HTTP/3
R3 Evaluate WebTransport as a separate option
R4 One API across all bindings (IChannelSession / IChannel)
R5 Mapping to OCPP 1.6 and 2.x, backward compatible
R6 Automatic reconnect policies
R7 Bounded number of TCP connections; a node must not be taken down by a connection flood
R8 Connection setup via DNS SRV and HTTPS RR
R9 Several TLS client certificates between two hosts, with a role extension (RBAC) as in Modbus/TLS
R10 Privileged operations (e.g. firmware) only over explicit on-demand channels with elevated privileges and separate logging (metrological log)
R11 Metrological CBOR as the future default representation, COSE signatures on all messages by default

1.4 Non-goals


2. Transport options: HTTP/1.x, HTTP/2, HTTP/3

2.1 HTTP/1.x + RFC 6455: one connection per channel

Every channel is its own TCP+TLS connection with its own upgrade handshake.

2.2 HTTP/2 + RFC 8441: streams over one connection

Every channel is a stream opened with Extended CONNECT (:protocol = websocket); the RFC 6455 framing runs unchanged inside DATA frames.

2.3 HTTP/3 + RFC 9220: QUIC streams

Every channel is a bidirectional QUIC stream, again via Extended CONNECT.

2.4 Comparison

Property HTTP/1.x (N connections) HTTP/2 HTTP/3
Transport connections per station N (one per channel) 1 per certificate domain 1 per certificate domain
Cost of a new channel TCP + TLS handshake one CONNECT stream one CONNECT stream
Loss independence between channels yes no (TCP HOL) yes
Priority between channels only indirectly (throttling in the sender) RFC 9218, both directions (Hermod) QUIC sender urgency (Hermod)
A failure takes down only the channel all channels of the connection all channels of the connection (but survives NAT rebinding)
Middlebox traversal very good good (TLS + ALPN h2), check proxies medium (UDP)
Different client certificates per channel per connection per connection
Maturity in Hermod production implemented, client scheduling new reference implementation

Recommendation: all three bindings behind the same API. HTTP/1.x is the always-available fallback and the first production target. HTTP/2 becomes the default binding once the gaps of chapter 11 are closed. HTTP/3 is preferred where UDP gets through and the stack has been hardened.


3. WebTransport

3.1 What WebTransport is

WebTransport over HTTP/3 (draft-ietf-webtrans-http3; Hermod implements -13) opens a session via Extended CONNECT (:protocol = webtransport). Inside the session there are:

For environments that block UDP there is the draft WebTransport over HTTP/2 (draft-ietf-webtrans-http2). Hermod does not implement it.

3.2 What it would mean between charging station and CSMS

3.3 Limits

  1. Draft status. The wire format is still changing. Interoperability exists mainly between browsers and servers; embedded implementations are rare.
  2. No RFC 6455 framing. OCPP-J cannot be carried over unchanged. A binding of its own is needed, e.g. one message per bidirectional stream, or length-prefixed messages on a stream: a new OCPP transport profile and therefore standardization work.
  3. UDP filtering as with HTTP/3. The fallback would be WebTransport over HTTP/2, which Hermod lacks, or falling back to OCPP-J channels.
  4. Privileges: as with HTTP/3, one TLS identity per QUIC connection, shared by all sessions on it. Privileged operations still need a connection of their own.
  5. Gaps in Hermod: WebTransportStream does not expose SendUrgency. The session API is synchronous and polling-based (matching the deterministic core) and reachable only through the core, not through the Task facade Http3Client.

3.4 Assessment

OCPP-J channels (H1/H2/H3) WebTransport
OCPP compatibility immediate (channel = OCPP-J) new binding needed
Field readiness H1 today, H2 soon research
File transfers bulk channel with chunks ideal (one stream per transfer)
Server-initiated only on request via the control channel native
Unreliable telemetry no datagrams

Recommendation: EVQI stage 1 relies on OCPP-J channels. WebTransport is a research track (work package G): an "OCPP over WebTransport" prototype in Hermod, measurements against the channel variant and, if successful, a proposal to the Open Charge Alliance. The IChannel API is cut so that a WebTransport binding can implement it later (channel = bidirectional stream with message framing, channels accepted on both sides).


4. Concept: channel sessions (IChannelSession / IChannel)

4.1 Terms

Term Meaning
Station identity the OCPP identity (identifierString, at most 48 characters)
Channel session the logical association of one station identity with one CSMS endpoint; spans one or more transport connections; has a lifecycle and a reconnect policy
Channel named, bidirectional, message-oriented (RFC 6455 semantics); ordered and reliable within itself; its own CALL slot
Channel class Control, Realtime, Bulk, Privileged; determines priority, lifetime, role and reconnect behaviour
Binding HTTP/1.x, HTTP/2, HTTP/3 (later WebTransport)
Certificate domain the pair of endpoint and client certificate (= role); exactly one transport connection for HTTP/2 and HTTP/3; channels of different domains never share a connection

4.2 Channel classes

Class Examples Lifetime Priority (RFC 9218) Role Reconnect
Control today's OCPP-J traffic: boot, heartbeat, status, transactions, remote control, charging profiles permanent u=1 operational always, OCPP §5.4
Realtime NotifyPeriodicEventStream (SEND), live power for load management, display permanent or on demand u=0 operational after the control channel
Bulk log upload, DataCollectorLog, large reports, local authorization lists on demand, idle timeout u=6, i operational or diagnostics only with pending work, with resume
Privileged firmware, security configuration, certificates, metrological parameters explicit, time-limited own connection (u=3) firmware, security-admin, metrology never automatically

One rule matters: messages that depend on each other's order stay on one channel. Transaction-related messages (TransactionEvent, or Start/Stop/MeterValues in 1.6) and the related status notifications belong to the control channel; billing-relevant meter values do not move to the real-time channel.

4.3 Specification (C# sketch, Hermod style)

namespace org.GraphDefined.Vanaheimr.Hermod.Channels
{

    public enum ChannelClass      { Control, Realtime, Bulk, Privileged }
    public enum TransportBinding  { HTTP1, HTTP2, HTTP3, WebTransport }
    public enum ChannelCloseScope { Channel, Connection, Session }

    /// <summary>RFC 9218: urgency 0 (highest) to 7, default 3.</summary>
    public readonly record struct ChannelPriority(Byte Urgency, Boolean Incremental);

    public sealed record ChannelOptions(String           Name,
                                        ChannelClass     Class,
                                        ChannelPriority  Priority,
                                        String?          Role            = null,   // selects the certificate domain
                                        String?          Representation  = null,   // "cbor" or "json"
                                        TimeSpan?        IdleTimeout     = null,
                                        TimeSpan?        MaxLifetime     = null,
                                        String?          Ticket          = null);  // Privileged only

    public sealed record ChannelCloseInfo(UInt16             Code,
                                          String?            Reason,
                                          ChannelCloseScope  Scope,          // what went down with it?
                                          Boolean            ClosedByPeer,
                                          TimeSpan?          RetryAfter);    // server hint, if any

    public interface IChannelSecurity
    {
        String?            LocalRole        { get; }
        String?            PeerRole         { get; }   // from the peer's certificate
        X509Certificate2?  PeerCertificate  { get; }
        Byte[]?            ExportKeyingMaterial(String Label, Int32 Length);   // where the TLS stack offers it
    }

    public interface IChannel : IAsyncDisposable
    {
        String                  Name            { get; }
        ChannelClass            Class           { get; }
        TransportBinding        Binding         { get; }
        String                  SubProtocol     { get; }   // e.g. "ocpp2.1"
        String                  Representation  { get; }   // "cbor" or "json"
        ChannelPriority         Priority        { get; }
        IChannelSecurity        Security        { get; }

        Task                    UpdatePriorityAsync (ChannelPriority Priority, CancellationToken CancellationToken = default);

        Task                    SendTextAsync       (String Text,                CancellationToken CancellationToken = default);
        Task                    SendBinaryAsync     (ReadOnlyMemory<Byte> Data,  CancellationToken CancellationToken = default);
        Task<ChannelMessage?>   ReceiveAsync        (CancellationToken CancellationToken = default);   // null = closed

        Task                    CloseAsync          (UInt16 Code, String Reason, CancellationToken CancellationToken = default);
        Task<ChannelCloseInfo>  Completion          { get; }
    }

    public interface IChannelSession : IAsyncDisposable
    {
        String                          SessionId     { get; }
        String                          PeerIdentity  { get; }
        ChannelSessionState             State         { get; }   // Connecting, Established, Degraded, Reconnecting, Closed
        IReadOnlyCollection<IChannel>   Channels      { get; }

        Task<IChannel>                  OpenChannelAsync    (ChannelOptions Options, CancellationToken CancellationToken = default);
        IAsyncEnumerable<IChannel>      AcceptChannelsAsync (CancellationToken CancellationToken = default);   // server; client only with WebTransport

        Task                            CloseAsync          (UInt16 Code, String Reason, CancellationToken CancellationToken = default);
        Task<ChannelCloseInfo>          Completion          { get; }
    }

}

Semantics:

4.4 Semantics per binding

HTTP/1.x HTTP/2 HTTP/3 WebTransport (later)
Channel = TCP+TLS connection + upgrade Extended CONNECT stream Extended CONNECT stream bidirectional stream in a session
Session = group of connections held together by a session token connection(s) per certificate domain + token as HTTP/2 WT session
Priority local throttling, otherwise TCP fairness RFC 9218 in both directions + PRIORITY_UPDATE QUIC sender urgency + PRIORITY_UPDATE stream urgency (missing in Hermod)
Loss on channel A disturbs channel B no yes no no
Server opens a channel only on request via the control channel as HTTP/1.x as HTTP/1.x native
Limits per connection – SETTINGS_MAX_CONCURRENT_STREAMS MAX_STREAMS WT stream limits

4.5 Discussion: where the abstraction leaks

  1. Different shared fate. With HTTP/1.x a channel dies alone; with HTTP/2 and HTTP/3 all channels of a connection die together. The API makes this visible through Scope instead of hiding it. With HTTP/1.x the session is a fiction that lives as long as the control channel does. If that fails, the server closes the session's other channels; otherwise orphaned channels remain.
  2. Priority is a hint, not a guarantee. HTTP/1.x cannot really prioritize between channels, it can only throttle the bulk channel. HTTP/2 can, until the next lost TCP segment. HTTP/3 does it best.
  3. Loss independence holds only between channels. Within a channel everything stays ordered and reliable; two transfers on the same bulk channel still block each other. Only WebTransport, with one stream per transfer, solves that.
  4. Server-initiated channels do not exist (except with WebTransport). The CSMS requests a channel via the control channel (OpenChannel, chapter 10), and the station opens it.
  5. Privileges bound to the connection. A channel with a different role needs a different connection. With HTTP/2 and HTTP/3 the goal "one connection per station" therefore holds per certificate domain: one connection in everyday operation, briefly two during a firmware update.
  6. Existing identity logic. Today's CSMS implementations commonly close all older connections when a new one with the same identity arrives ("newest wins"). An additional channel with the same identity would kill the control channel at such a peer. A station therefore opens extra channels only after a successful capability negotiation and only to the endpoints the CSMS announced, and "newest wins" in the CSMS becomes per channel.
  7. Binding choice and fallback are policy, not API. The order is HTTP/3 → HTTP/2 → HTTP/1.x. Candidates come from the ALPN of the HTTPS RR (chapter 5), from Alt-Svc (RFC 7838) or from earlier connections. The choice is remembered per network and re-checked periodically with jitter, so nothing oscillates.

4.6 Conventions on the wire

4.7 Lifecycle of a session

stateDiagram-v2
    [*] --> Connecting
    Connecting --> Established: control channel up + capabilities negotiated
    Connecting --> Reconnecting: failure
    Established --> Degraded: non-control channel lost
    Degraded --> Established: channel reopened or no longer needed
    Established --> Reconnecting: control channel lost
    Degraded --> Reconnecting: control channel lost
    Reconnecting --> Connecting: back-off elapsed
    Established --> Closed: CloseAsync / session replaced
    Reconnecting --> Closed: MaxAttempts / CloseAsync
    Closed --> [*]

If a session loses its control channel, the session as a whole is Reconnecting. All other channels are closed and reopened only after a new negotiation. A new session of the same identity replaces the old one completely, with all its channels.


5. Connection setup and discovery via DNS (HTTPS RR, SRV)

5.1 Goals

Load balancing and failover across several CSMS nodes, protocol selection (ALPN h3/h2/http/1.1), port and address hints and ECH, without changing the station's configuration.

5.2 Procedure

  1. The starting point is the configured OCPP endpoint URL. In OCPP 2.x it lives in the NetworkConnectionProfile slot (ocppCsmsUrl). Choosing between slots (NetworkConfigurationPriority) remains OCPP's job; discovery works within one slot.
  2. HTTPS RR (RFC 9460) for the host of the URL. For port 443 this is the host name itself, otherwise port prefix naming applies, _8443._https.csms.example.com (§9.1). RFC 9460 §9.6 explicitly applies to WebSocket as well. The ServiceMode records, sorted by SvcPriority, yield TargetName, alpn, port, ipv4hint/ipv6hint and ech; AliasMode points to another name.
  3. Without an HTTPS RR: SRV (RFC 2782) under _ocpp._tcp.<host>, as already proposed in TLS Configuration §1.39: priority, weighted random selection, failover to the next target. The service name "ocpp" is not registered with IANA (as of 2026-10-03); it should be registered per RFC 6335 before production use.
  4. A/AAAA with Happy Eyeballs (RFC 8305) across IPv6 and IPv4.
  5. TLS: SNI and certificate validation against the configured origin name, not against the DNS target (RFC 9460 §9.4: the TLS SNI indicates the origin, not the TargetName). The CSMS certificate carries the FQDN in its CN (OCPP A00.FR.510). A manipulated DNS can therefore only redirect the station to servers that hold a valid certificate for the configured name: an outage is possible, redirection to a foreign server is not.
  6. DNS transport: DoT/DoH to a trusted resolver, or DNSSEC validation where available.
  7. Robustness: if DNS is unreachable, stale records are used ("serve stale", RFC 8767), plus a last-known-good endpoint list. A DNS outage must not prevent reconnecting.
  8. Interplay with reconnects: failed targets rotate in priority order; after K failures across all targets the name is resolved again.

Privileged endpoints are not discovered via DNS. The CSMS announces their URL over the already authenticated control channel, and the station accepts it only if the server certificate there carries the matching role (chapter 6). Without DNSSEC, DNS is not authenticated and must not decide the path to elevated privileges.

5.3 Status in Hermod


6. Security: role certificates, multiple client certificates, privileged channels

6.1 Model: Modbus/TCP Security

The Modbus/TCP Security specification (Modbus.org) carries a client's role as an X.509 extension in its client certificate: OID 1.3.6.1.4.1.50316.802.1, value a DER UTF8String. The server derives from the role which function codes and registers are allowed. Hermod implements this in Hermod/Modbus/SunSpec:

On the client side there is one certificate per connection, and the role is chosen by picking the certificate file.

6.2 Transfer to EVQI

6.3 Role model (proposal)

Role May (excerpt) Channel class
evqi:operational everyday operation: boot, heartbeat, status, transactions, authorization, remote start/stop, charging profiles, reservations, TriggerMessage, GetVariables, periodic event streams Control, Realtime, Bulk
evqi:diagnostics GetLog uploads, DataCollectorLog, large reports, monitoring reports Bulk
evqi:security-admin InstallCertificate, DeleteCertificate, CertificateSigned, SetNetworkProfile, SetVariables on SecurityCtrlr/OCPPCommCtrlr/EVQICtrlr (1.6: ChangeConfiguration of security keys), signature policies and user roles Privileged
evqi:firmware UpdateFirmware / SignedUpdateFirmware, PublishFirmware / UnpublishFirmware, FirmwareStatusNotification, firmware transfer, for firmware components without regulatory classification Privileged
evqi:metrology parameters and software relevant to calibration law, including metrologically classified firmware components, retrieval of the metrological log Privileged

The split between evqi:firmware and evqi:metrology follows the firmware component model of Firmware Updates with Software Separation: a component's regulatory classification decides which role may update it.

Customer-related data (CustomerInformation) may deserve a role of its own for data protection reasons. The model is a proposal and is to be agreed with operators, manufacturers and PTB.

6.4 Certificate selection per channel

6.5 On-demand channel with elevated privileges: procedure

sequenceDiagram
    autonumber
    participant Op as Operator/back office
    participant CSMS as CSMS (control endpoint)
    participant OPS as CSMS (privileged endpoint)
    participant CS as Charging station

    Op->>CSMS: schedule firmware update (approvals, maintenance window)
    CSMS->>CSMS: create ticket (operation, role, validity, approvers)
    CSMS->>CS: OpenChannelRequest (class Privileged, role evqi:firmware,<br/>endpoint, ticket, valid until), COSE-signed
    CS->>CS: check policy (window, rate limit, signature)
    CS-->>CSMS: OpenChannelResponse (Accepted)
    CS->>OPS: TLS with firmware role certificate (key in secure element)
    OPS-->>CS: server certificate from the privileged sub-CA, role evqi:firmware
    CS->>OPS: WebSocket/CONNECT with EVQI-Ticket, EVQI-Session
    OPS->>OPS: check identity (CN = serial number) + role + ticket, audit entry
    OPS->>CS: UpdateFirmware, COSE-signed by the release signers (four eyes)
    CS->>OPS: firmware transfer in the channel (chunks, resume, SHA-256)
    CS->>OPS: FirmwareStatusNotification …
    CS->>CSMS: SecurityEventNotification (PrivilegedChannelOpened/Closed)
    OPS-->>CS: close (operation finished or ticket expired)

Rules:

  1. The request on the control channel is harmless in itself: it only asks for a connection. It is signed nevertheless (chapter 7), so that its origin from the CSMS is verifiable even across a Local Controller.
  2. The station executes privileged commands only on a privileged channel and only with a valid signature of an authorized signer (section 7.4). For the migration there is the station policy PrivilegedActionsOnControlChannel with the levels Allowed (legacy behaviour), AllowedAndLogged and Rejected (target, answered with CALLERROR SecurityError).
  3. No automatic reconnect. Within the ticket's validity a few retries are allowed (e.g. 3); after that a new ticket is required.
  4. The channel closes when the operation ends or the ticket expires, at the latest after MaxLifetime.
  5. The firmware signature of the manufacturer PKI remains the authenticity anchor of the image. The privileged channel and the message signatures govern who may trigger an update, and they log it. The mechanisms complement each other.

6.6 Local Controller and privileged channels

A Local Controller terminates TLS (OCPP 2.1 §6.5). If the privileged channel ran through it, it would itself need the firmware role, which is to be avoided. Two options:

Message signatures (chapter 7) keep the commands authentic in both cases, but only option (b) or (a) keeps the transport privileges away from the Local Controller. Decision open (chapter 13).

6.7 Separate logging (audit log and metrological log)


7. Message representation and message signatures: Metrological CBOR and COSE

7.1 Metrological CBOR as the default representation

Today OCPP-J messages are JSON text. EVQI makes CBOR (RFC 8949) in deterministic encoding (RFC 8949, §4.2.1) the future default representation of OCPP messages, with every measured value encoded as a Metrological CBOR value (CBOR tag 44252, registered with IANA on 2026-08-19).

7.2 COSE-signed OCPP messages

7.3 Signed by default, justified exceptions

All future OCPP messages are signed by default. Exceptions are allowed when they are justified, declared in a policy table (device model and capability negotiation) and still authorized and logged at the connection level. Examples of justified exceptions:

Exceptions never cover privileged operations: messages on privileged channels are always signed.

7.4 Certificate roles as an upper bound

Authorization works on two levels:

  1. Connection role: the role extension of the TLS certificate (chapter 6). It states what may happen on this channel at all: the upper bound.
  2. Signer role: the role of whoever signed the message: a person, a system or a device. It is bound to the signing key, e.g. by the same role extension (1.3.6.1.4.1.60483.1.1) in the signer's certificate, or by a signed role assignment. It states who actually authorized this message.

Effective permission = connection role ∩ signer role. A message is executed only if both permit it.

Consequences:


8. Reconnect policies

8.1 Starting point

8.2 Policy per channel class

Class When to reconnect Back-off Particulars
Control always exactly OCPP §5.4 (OCPPCommCtrlr variables; mirrored as configuration keys for 1.6) binding fallback H3 → H2 → H1 after N failures
Realtime once the control channel is up and negotiated, random delay 0–5 s like Control, capped skipped while no streams are configured
Bulk only while a transfer is pending like Control resume from the last acknowledged offset
Privileged never automatically at most 3 retries within the ticket's validity then a new ticket is required

8.3 Server hints and storm avoidance


9. Resource and denial-of-service protection

9.1 Connection budget

Scenario today HTTP/1.x binding HTTP/2 or HTTP/3 binding
10,000 stations, Control + Realtime permanent, 5 % with an active bulk transfer 10,000 20,500 10,000
plus 20 firmware updates in progress – +20 +20 (own certificate domain)
Local Controller with 20 stations, towards the CSMS 21 up to 41 (with Realtime) 1 (all station channels as streams) + 1 own channel

Per station:

The gain is largest for the Local Controller: its connections to the CSMS, one per station today (OCPP 2.1 §6.2), become one.

9.2 Limits on the station

9.3 Limits on the server (CSMS node)

Before authentication (cheap, per source):

Per-IP limits must be generous: behind a carrier-grade NAT many stations share one IPv4 address. The real limits apply per identity.

After authentication (per identity):

Limit Proposal
Sessions per identity 1 (a new session replaces the old one completely)
Channels per session and class Control 1, Realtime 1, Bulk 1, Privileged 1
Channel with a foreign session token reject (403)
CALLs in progress 1 per channel and direction (OCPP rule) + a cap on response queues
Message size Control 64 KiB, Realtime 16 KiB, bulk chunk 1 MiB (for the assembled message, not just the frame)
Message rate token bucket per channel
HTTP/2 MAX_CONCURRENT_STREAMS for station connections 8
Idle timeouts short for Bulk and Privileged; Control and Realtime kept alive by pings
Signature verification rate-limited per identity, verified before any expensive processing

Per node:

Authenticate before the upgrade. Reject before sending 101 or 200, so the cost asymmetry lies with the attacker.

9.4 Status in the code


10. Mapping to OCPP 1.6 and OCPP 2.x

10.1 Principles

ID Principle
P1 The control channel is plain OCPP-J. An EVQI station works with every CSMS, an EVQI CSMS with every station.
P2 Extra channels only after a successful capability negotiation, and only to announced endpoints.
P3 Every channel has full OCPP-J semantics: CALL/CALLRESULT/CALLERROR (2.1 also CALLRESULTERROR and SEND), its own CALL slot, IDs unique per identity.
P4 Per-channel authorization by class, connection role and signer role; a violation → CALLERROR SecurityError.
P5 Extensions are additive: in 1.6 via DataTransfer and configuration keys, in 2.x via DataTransfer, customData and a device model component, later as a proposal to the OCA.
P6 Messages are signed by default (COSE for CBOR, JWS for JSON); exceptions are declared.

10.2 Capability negotiation: ChannelSession

After every (re)connect of the control channel the station asks. Under §5.4 a BootNotification is sent only after changes, so it cannot serve this purpose.

[2, "a1b2c3", "DataTransfer", {
  "vendorId":  "org.openchargingcloud.evqi",
  "messageId": "ChannelSession",
  "data": {
    "bindings":        ["h3", "h2", "http/1.1"],
    "channelClasses":  ["Realtime", "Bulk", "Privileged"],
    "roles":           ["evqi:operational", "evqi:firmware"],
    "representations": ["cbor", "json"],
    "signatures":      ["COSE", "JWS"],
    "maxChannels":     4
  }
}]

The response contains:

If the CSMS does not know the message, it answers UnknownVendorId, and the station stays with one channel (P1).

10.3 OCPP 1.6 (OCPP-J 1.6 + Security Whitepaper Edition 3)

10.4 OCPP 2.0.1 and 2.1

10.5 Assignment of messages to channels

Message (2.x) 1.6 equivalent Channel Connection role
BootNotification, Heartbeat, StatusNotification same Control operational
TransactionEvent, MeterValues Start-/StopTransaction, MeterValues Control (ordering!) operational
Authorize, RequestStart-/StopTransaction Authorize, RemoteStart/Stop Control operational
Set-/Clear-/GetChargingProfile, GetCompositeSchedule same Control operational
ReserveNow, CancelReservation, UnlockConnector, ChangeAvailability, TriggerMessage same Control operational
GetVariables GetConfiguration Control operational
SetVariables (not security-relevant) ChangeConfiguration Control operational
Open/Adjust/ClosePeriodicEventStream – Control operational
NotifyPeriodicEventStream (SEND) – Realtime operational
GetLog → upload, LogStatusNotification GetDiagnostics/GetLog → upload request Control, data Bulk diagnostics
GetBaseReport → NotifyReport, GetMonitoringReport → NotifyMonitoringReport – request Control, reports Bulk diagnostics
SendLocalList same Bulk operational
CustomerInformation, NotifyCustomerInformation – Bulk own role (data protection)
UpdateFirmware, PublishFirmware, UnpublishFirmware, FirmwareStatusNotification (Signed)UpdateFirmware, (Signed)FirmwareStatusNotification Privileged firmware / metrology
InstallCertificate, DeleteCertificate, CertificateSigned, SetNetworkProfile, SetVariables (security) corresponding Security Whitepaper messages, ChangeConfiguration (security) Privileged security-admin
SecurityEventNotification same Control operational
SignCertificate (operational certificate) same Control operational
SignCertificate (role certificate) – Privileged security-admin

The column states the connection role, i.e. the upper bound. The required signer role per message is part of the authorization table (E3) and of the signature policy (E10).

10.6 Proposed OCPP extensions

No. Extension Purpose Priority
E1 ChannelSession (capabilities + session token + endpoints) prerequisite for everything else; protects legacy peers high
E2 OpenChannel (CSMS → CS), ChannelStatusNotification (CS → CSMS) request and monitor on-demand channels high
E3 per-channel and per-role authorization table in the device model (connection role and signer role) least privilege, auditable high
E4 role certificate profile (OID 1.3.6.1.4.1.60483.1.1) + SignCertificate for role certificates RBAC as in Modbus/TLS, short-lived certificates high
E5 in-band transfer via evqi: URIs (chunks, resume from offset, SHA-256) replaces FTP/HTTP with credentials in the URL medium
E6 log types EVQIAuditLog, MetrologicalLog; security events PrivilegedChannelOpened/Closed/Rejected, RoleViolation, SignatureViolation separate logging, retrievable medium
E7 reconnect parameters for 1.6; semantics of Retry-After and close code 1013 uniform reconnect behaviour medium
E8 discovery options in the NetworkConnectionProfile (HTTPS RR/SRV) DNS-based load balancing and failover low
E9 headers EVQI-Session, EVQI-Channel, EVQI-Ticket correlation on the wire high (part of E1/E2)
E10 CBOR representation with Metrological CBOR, COSE signatures, sign-by-default policy with declared exceptions exact measurement data, end-to-end authenticity high

The path to standardization: implement first as DataTransfer with a vendorId of its own and try it out at plugfests, then propose it to the Open Charge Alliance. E1 to E3 can be cut as an "OCPP Multi-Channel Profile" of their own, E10 as an "OCPP CBOR/COSE Profile".


11. Implementation status

As of: Hermod 22768a4a1; WWCP_OCPP 6d12a82, WWCP_Core a83151c, WWCP_Node 97d764b; Styx master. W-1 as of WWCP_Core 5efb78cc and WWCP_OCPP b13c9ffe.

11.1 Hermod and Styx: available

Area Status
HTTP/1.1 WebSocket production; reconnect policy with Retry-After; client certificate visible to server handlers (WebSocketServerConnection.ClientCertificate); client certificate selection via LocalCertificateSelector; SRV via DNSService
HTTP/2 Extended CONNECT in both roles; OpenWebSocketAsync(…, Priority); WebSocketConnection.UpdatePriorityAsync; RFC 9218 scheduling in both roles; flow control without mutual blocking; configurable connection window (4 MiB default)
HTTP/3 / QUIC Extended CONNECT, WebSockets, datagrams, WebTransport (draft -13) including server-initiated streams; urgency scheduling in the QUIC sender; path validation on NAT rebinding
DNS SRV/SVCB/HTTPS/TLSA, DoT, DoH, DNSSEC validator
PKI / RBAC PKIFactory with AdditionalExtension; Modbus/TLS roles (extension, extraction, authorization, audit hook)
CBOR / COSE (Styx) Metrological CBOR (tag 44252); COSE Sign1/Sign, countersignatures, Encrypt0/Encrypt, Mac0/Mac, keys and thumbprints, RFC 9864 algorithms, brainpool, ML-DSA

11.2 Hermod: gaps for EVQI

No. Gap
H-1 The HTTP/2 CONNECT handler does not see the client certificate, and RequireAuthentication does not apply to CONNECT. Without both, roles cannot be checked over HTTP/2 WebSockets.
H-2 H2/H3 WebSocketConnection: message-size and decompression limits (fix in progress), no ping API, no subprotocol
H-3 Three WebSocket framing implementations (H1, H2, H3); a transport-neutral framing is missing
H-4 HTTP/2 client: no subprotocol parameter; response headers of a refusal not available (no Retry-After); one client certificate without a selection callback; pool without tunnels and WebSockets; keepalive off by default (quiet channels die after 120 s at the server)
H-5 HTTP/2 server: no connection limit (total, per IP); MAX_CONCURRENT_STREAMS not configurable; a priority header in CONNECT responses does not change the server's own send order
H-6 Shared port for HTTP/1.1 and HTTP/2: the HTTP11Fallback receives no socket information, so WebSocketUpgrade answers 403 when OnValidateTCPConnection handlers exist
H-7 HTTP/3: Extended CONNECT and WebTransport only in the core, not in the Task facade; Http3Client IPv4 only; SendExtendedConnect sets no urgency; WebTransportStream without SendUrgency; Http3Tunnel without backpressure; keepalive off; WebTransport over HTTP/2 missing; stack not production-hardened
H-8 Reconnect: close code 1013 not treated specially; no additive (OCPP-conformant) jitter mode
H-9 DNS: no SRV failover and no fallback on an empty answer; HTTPS RR not used to choose the connection; SvcParams untyped (and wrongly encoded in the JSON path); no Happy Eyeballs, no serve-stale, no ECH
H-10 No audit log for TLS/WebSocket (only EventSources and the SSH audit); no TPM/PKCS#11 binding

11.3 WWCP_OCPP / WWCP_Core: available

11.4 WWCP_OCPP / WWCP_Core: gaps

No. Gap
W-1 Duplicate connections are handled per identity ("newest wins"), not per channel; WWCPWebSocketServer.Supersedes is the one place that decides which older connections a newer one replaces. Done in WWCP_Core b6b3b4e8: the identity is the last path segment, and the credentials (client certificate CN, else HTTP Basic username, else TOTP login) must name the same networking node or one allowed to connect as it (AllowToActFor, for a local controller per OCPP 2.1 Part 4 §6.2 and §6.5); otherwise 403 before the 101 where the server can check the credentials itself, and no registration where only the application can. A newer connection replaces the older ones in one step; a late close of an older connection does not take it down, and a connection that ends without a close frame is unregistered, in AOverlayWebSocketServer too since 5efb78cc. Passwords and one-time passwords no longer appear in the debug log, and credentials in the query string are gone. Still open: a station certificate's CN is its serial number (OCPP 2.1 Part 2 A00.FR.511), so a serial number other than the identity needs AllowToActFor; no test covers the certificate path over TLS.
W-2 Routing: all connections register with the same priority, so the last connection wins; the high-availability networking with several prioritized connections described in the overlay README is not implemented.
W-3 The OCPP 2.x reconnect back-off is not implemented (variables present, unread); SetNetworkProfile is a stub.
W-4 No limits per identity, no message rates, an unbounded table of outstanding requests.
W-5 Firmware and log transfer: the messages exist, the transfer code is missing (handlers are stubs); SecurityEventNotification is not sent automatically.
W-6 The own signature format lacks canonicalization and should give way to COSE/JWS; SecureDataTransfer should be replaced by COSE_Encrypt0 with an AEAD algorithm.
W-7 Roles for OCPP messages exist only in outline: the only enforcement is that SetVariables requires the role "admin" once roles are configured.
W-8 No CBOR serialization format for OCPP messages yet.

12. Work plan

Sizes: S ≈ days, M ≈ 1–3 weeks, L ≈ 1–2 months (one person, rough).

Phase 0: done (September/October 2026)

HTTP/2 client writer loop with RFC 9218 priority (Vanaheimr/Hermod#63); no mutual blocking for a stalled reader (Vanaheimr/Hermod#62) and client backpressure (Vanaheimr/Hermod#66); INITIAL_WINDOW_SIZE correction (Vanaheimr/Hermod#64); robustness on connection end, GOAWAY and transport close (Vanaheimr/Hermod#65, Vanaheimr/Hermod#67, Vanaheimr/Hermod#68, Vanaheimr/Hermod#69, Vanaheimr/Hermod#72, Vanaheimr/Hermod#75, Vanaheimr/Hermod#79); MAX_HEADER_LIST_SIZE (Vanaheimr/Hermod#71); bounded buffered responses (Vanaheimr/Hermod#70); content-length check (Vanaheimr/Hermod#76); Stream.Reset via CancelAsync (513797ae). Metrological CBOR registered with IANA (tag 44252), COSE in Styx.

Phase A: Hermod groundwork

WP Content Size Acceptance
A1 WebSocket framing: re-align the H2/H3 copy (in progress), then one transport-neutral framing for H1/H2/H3 M one framing, Autobahn suite green against every binding
A2 Harden H2/H3 WebSockets: limits for assembled messages and decompressed data (in progress), ping API, subprotocol (H-2) S tests with a deflate bomb and an overlong fragment chain
A3 HTTP/2 CONNECT: client certificate and authentication in the connect handler; priority override; Retry-After and headers in the client; configurable MAX_CONCURRENT_STREAMS; server connection limits; tunnel keepalive (H-1, H-4, H-5) M role check over an H2 WebSocket in a test; load test with a connection flood
A4 HTTP/3: Extended CONNECT/WebSocket in the Task facade, IPv6, urgency for CONNECT and WT streams, tunnel backpressure, keepalive (H-7) M channel tests over H3 with loss simulation
A5 Reconnect: close code 1013, additive OCPP jitter, helper for 503 + Retry-After when shedding load (H-8) S unit tests against the formula of OCPP §5.4
A6 DNS discovery: typed SvcParams, HTTPS RR → SRV → A/AAAA, wire in DNSSRVManager, Happy Eyeballs, serve-stale (H-9) M DNSConformanceTests scenarios; validation against the origin name
A7 Shared TLS port H1/H2 with full context for the fallback (H-6) S OnValidateTCPConnection behind ALPN too

Phase B: channel core (HTTP/1.x first)

WP Content Size Acceptance
B1 IChannelSession/IChannel, channel classes, ChannelCloseInfo, event adapter for WWCP M API review, tests against a fake binding
B2 HTTP/1.x binding: connection group, session token, lifecycle (control channel fails → session fails) M integration tests: channel loss, session replacement, reconnect
B3 Session policy: reconnect per class (section 8.2), station limits (section 9.2) S tests for connection count and back-off sequence

Phase C: HTTP/2 and HTTP/3 bindings

WP Content Size Acceptance
C1 HTTP/2 binding with a connection pool per certificate domain, no coalescing across domains M latency measurement: real-time message during a 100 MB bulk transfer
C2 HTTP/3 binding M as C1, additionally with packet loss (H2/H3 comparison)
C3 Binding choice and fallback (HTTPS RR ALPN, Alt-Svc, remembered choice, periodic re-check) S tests with blocked UDP and a proxy without RFC 8441

Phase D: security

WP Content Size Acceptance
D1 Role extension 1.3.6.1.4.1.60483.1.1, creation via PKIFactory, extraction following RoleExtractor, criticality behaviour tested S certificates for every role; rejection of the unknown critical extension by legacy validators demonstrated
D2 Certificate selection per domain (client), role check per channel and message (server), roles checked in both directions M negative tests: operational certificate at the privileged endpoint, role violation → SecurityError
D3 Privileged endpoint, ticket lifecycle, on-demand procedure (section 6.5) M end-to-end firmware update in a test network
D4 Audit log based on WWCP_Node SignedLog (both sides), storing COSE objects, exporter binding where available M chain verification; tampering test
D5 Key storage abstraction (TPM 2.0 / PKCS#11) for privileged keys L firmware role key not exportable

Phase S: representation and signatures

WP Content Size Acceptance
S1 CBOR representation of OCPP messages in WWCP (new serialization format, binary frames, negotiation per channel), measured values as Metrological CBOR M round trip JSON ↔ CBOR for all 2.1 messages; deterministic bytes
S2 COSE signing and verification in the OCPP adapter (Styx COSE): protected OCPP context, freshness, countersignatures M replay and cut-and-paste tests fail as intended
S3 Sign-by-default policy with declared exceptions; two-level authorization (connection role ∩ signer role) M table-driven tests per message and role
S4 Signer key and role management (signer certificates with the role extension, four eyes via COSE_Sign) M firmware update with two signatures end to end
S5 JSON gateway carrying COSE objects unchanged; JWS per OCPP 2.1 §7 on JSON channels S signature still verifies after the gateway

Phase E: OCPP mapping in WWCP

WP Content Size Acceptance
E1 "Newest wins" per channel (W-1); binding identity to credentials and keeping credentials out of logs are done (WWCP_Core b6b3b4e8) S two channels of one station coexist; a newer connection replaces only the one of its own channel
E2 Limits per identity, message rates, cap on outstanding requests (W-4) S load test with an abusive station
E3 OCPP 2.x back-off from OCPPCommCtrlr, mirrored for 1.6 (W-3) S conformance with §5.4
E4 ChannelSession, OpenChannel, ChannelStatusNotification via DataTransfer (1.6 and 2.x), EVQICtrlr M station and CSMS negotiate; legacy peers stay with one channel
E5 Per-channel authorization table (section 10.5) S table-driven tests
E6 Bulk transfer with evqi: URIs based on BinaryStreams (chunks, resume, hash); GetLog/GetDiagnostics M upload with an interruption and resumption
E7 Privileged firmware procedure including SignedUpdateFirmware/UpdateFirmware, security events, log retrieval M end to end between a charging station and a CSMS

Phase F: load and abuse tests

10,000 simulated stations; CSMS restart (storm); carrier-grade NAT scenario; slowloris on upgrade and CONNECT; deflate bombs; signature-verification floods; latency comparison of the bindings under loss. The result is limits for section 9.3 and an operations manual. Size: M.

Phase G: WebTransport (research)

An "OCPP over WebTransport" prototype: one stream per transfer, datagrams for ephemeral telemetry, server-initiated streams. Then a measurement against the channel variant and a decision on a proposal to the OCA. Size: L.

Phase H: standardization and documentation

Propose an "OCPP Multi-Channel Profile" (E1–E3, E9, then E4–E7) and an "OCPP CBOR/COSE Profile" (E10) to the OCA; register the service name ocpp (SRV) and possibly the URI scheme evqi: (provisionally per RFC 7595) with IANA; fix and publish the EVQI arc under PEN 60483; conformance tests in OCPPConformanceTests, HTTP2ConformanceTests and MCBORConformanceTests. Size: M, ongoing.

Milestones

Result Prerequisites
M1 Multi-channel over HTTP/1.x in a test network: Control + Realtime + Bulk, negotiation, reconnect A2, A5, B1–B3, E1–E5
M2 Privileged firmware channel with signed commands and audit log D1–D4, S2–S4, E6–E7
M3 CBOR representation with COSE signatures by default S1–S5
M4 HTTP/2 binding production-ready, Local Controller over one connection A1, A3, A7, C1, C3, F
M5 HTTP/3 binding and DNS discovery A4, A6, C2
M6 WebTransport prototype with assessment G

M1 and M2 are independent of each other and can run in parallel. M1 delivers the most benefit fastest because HTTP/1.x is already in production.


13. Open questions and decisions

  1. Channel endpoint: a path of its own (proposed) or the same path with headers? A path of its own protects against "newest wins" at legacy peers but requires an announcement.
  2. OID: does the proposed EVQI arc 1.3.6.1.4.1.60483.1 (section 6.2) fit the GraphDefined OID registry? One role per certificate (proposed) or a list of roles? Criticality as in section 6.2?
  3. Privileged channels behind a Local Controller: direct path or blind tunnel (section 6.6)?
  4. Session token: format (COSE, JWS?), lifetime, binding to the control connection.
  5. Short-lived role certificates per maintenance window, or long-lived ones in a secure element?
  6. Approval process for privileged operations (four eyes?): not part of the transport layer, but it has to be reflected in the ticket, in the signatures and in the log.
  7. Calibration law: which events are relevant to calibration law, and which log format does a conformity assessment accept? To be agreed with PTB.
  8. HTTP/3 in production: what hardening does Hermod's reference implementation need (load tests, fuzzing, interop) before stations use it?
  9. CBOR keys: text keys (OCPP JSON field names, proposed for version 1) or integer keys from a registry for further compactness?
  10. Signature exceptions: which message types may be exempt, and is a periodic signed checkpoint sufficient for high-frequency telemetry?
  11. Freshness: width of the timestamp window, and behaviour while time is not yet synchronized after a cold start.

14. References

OCPP (Open Charge Alliance):

IETF:

Further:

Related whitepapers in this repository:

Implementations:


License

© 2026 GraphDefined GmbH

This document is published under the Creative Commons Attribution Share Alike 4.0 International (CC-BY-SA 4.0). You are free to share, adapt, and build upon it, even for commercial purposes, as long as you give appropriate credit and distribute your work and contributions under the same license.

Verbatim quotations from the OCPP specifications remain © Open Charge Alliance and are subject to their license (CC BY-ND 4.0).