From 8d02f9dbf6ffe409eadab80cdc4e4c19d669a189 Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Tue, 18 Nov 2025 14:03:44 +0100 Subject: [PATCH 01/26] Initial reworking of the communicaiton layer spec --- .../discovery-pairing-authentication.md | 210 +++++++++++------- 1 file changed, 125 insertions(+), 85 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 2d60d02..2b6f9de 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -3,28 +3,32 @@ title: The Specification sidebar_position: 2 --- -## Introduction +# Introduction s2-ws-json is a WebSocket and JSON based protocol specification implementing the EN50491-12-2 "S2" standard for home and building energy management. -This specification addresses everything needed to created a secure and interoperable implementation of the S2 standard. The communication layer concerns the discovery, the pairing, the application layer communication protocol, the authentication, the message data model and the serialization. +This specification addresses everything needed to created a secure and interoperable implementation of the S2 standard. The communication layer concerns the discovery, the pairing, the application layer communication protocol, the authentication, the message data model and the serialization. It was specifically designed to support multiple deployment scenario's and to give a relative consistent user experience throughout these different scenario's. -The protocol is designed to specify communication between two devices, a resource (e.g. a heat pump or EV charger) and (home) energy management system. It is worth noting that, while this specification focuses on describing the interaction between two components, a (home) energy management is likely to be communicating with multiple resources at the same time. +The protocol is designed to specify communication between two devices, a resource (e.g. a heat pump or EV charger) and a (home) energy management system. It is worth noting that, while this specification focuses on describing the interaction between two components, a (home) energy management is likely to be communicating with multiple resources at the same time. -## List of abbreviations +# List of abbreviations |Abbreviation | Meaning |---|---| -|S2| European standard on Energy Flexibility EN50491-12-2| -|RM| Resource Manager | -|CEM| Customer Energy Manager | +| S2 | European standard on Energy Flexibility EN50491-12-2| +| RM | Resource Manager | +| CEM | Customer Energy Manager | +| LAN | Local Area Network (i.e. a local network, typically contstrained to the building) | +| WAN | Wide Area Network (i.e. the public internet) | -## Background +# Background -### Requirements +## Requirements The communication layer meets the following requirements: -The Customer Energy Manager (CEM) and Resource Manager (RM) are logical concepts within the S2 architecture, therefore the S2 standard does not make any assumptions on how and where the CEM and RM are deployed in a real life situation. In practice, the CEM could be deployed on a local gateway or as a server in the cloud, while the RM could be part of the device itself, deployed on an add-on module or in the cloud as well. This means that the S2 communication layer **MUST** be able to deal with multiple scenarios that are depicted in the figure below. +The Customer Energy Manager (CEM) and Resource Manager (RM) are logical concepts within the S2 architecture, therefore the S2 standard does not make any assumptions on how and where the CEM and RM are deployed in a real life situation. In practice, the CEM could be deployed on a local gateway in a LAN or as a server somewhere on the internet (WAN), while the RM could be part of the device itself, deployed on an add-on module or on the internet as well. This means that the S2 communication layer **MUST** be able to deal with multiple scenarios that are depicted in the figure below. + +*TODO update image with LAN/WAN terminology* ![Deployment_options](/img/communication-layer/deployment-options.png) @@ -38,14 +42,14 @@ In addition to - and partly because of - supporting the various deployment optio - Implementation of the communication layer **MUST** be based on a widely accepted technology and must be relatively easy to implement. - The pairing process **SHOULD** support extensibility for other application layer communication protocols. -### Technical decisions +## Technical decisions Given the requirements, this specification is build on the following high-level technical choices: Application layer communication protocol: WebSocket Secure with bearer token authentication. Pairing: Custom HTTP API specified in OpenAPI. -Discovery: DNS-SD in combination with a central registry. +Discovery: DNS-SD (within a LAN) in combination with a central registry (for WAN deployments). Serialization: json. @@ -61,33 +65,31 @@ This specification uses the concepts that are defined below. **S2 node (S2Node)** -Refers to either a CEM or a RM as defined in EN 50491-12-1. S2 communication between two S2 nodes can only be established if one of the S2 nodes is a CEM and the other a RM. These S2 nodes must also have the same end user. - -**S2 node UI (S2NodeUI)** +Refers to an instance of either a CEM or a RM as defined in EN 50491-12-1 and implementing this specification. S2 communication between two S2 nodes can only be established if one of the S2 nodes is a CEM and the other a RM. These S2 nodes must also have the same end user. -A user interface through which an end user can interact with a S2 node. Interaction between the end user and the user interface must be secure, but this is out of scope for this specification. Examples of a user interface are a web interface, an app or a physical interface (HMI) on a device. +**S2 endpoint** -**S2 server node (S2ServerNode)** +TODO -A S2 node that implemented a WebSocket Secure server that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as a S2 client node. +**S2 node UI (S2NodeUI)** -**S2 client node (S2ClientNode)** +A user interface through which an end user can interact with an S2 node. Interaction between the end user and the user interface must be secure, but this is out of scope for this specification. Examples of a user interface are a web interface, an app or a physical interface (HMI) on a device. -A S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as a S2 server node. +**S2 pairing server (S2PairingServer)** -**S2 server node UI (S2ServerNodeUI)** +An S2 node that implemented a WebSocket Secure server that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 client node. -This is the S2 node UI of a S2 server node. Through this user interface the end user should be able to obtain the WebSocket Secure URI and configure or generate a token to be used by a S2 client node. +**S2 pairing client (S2PairingClient)** -**S2 client node UI (S2ClientNodeUI)** +A S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 server node. -This is the S2 node UI of a S2 client node. Through this user interface the end user must be able to retrieve a token (unless the S2 client node has a fixed string that must be configured as a token on the S2 server node UI) and configure the WebSocket Secure URI of an S2 server node that this S2 client node wants to establish S2 communication with. +**S2 communication server (S2CommunicationServer)** -**S2 server node REST API (S2ServerNodeRestAPI)** +An S2 node that implemented a WebSocket Secure server that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 client node. -The S2 server node features a REST API that facilitates the pairing process of a S2 client node and a S2 server node. It is also plays a role in the initiation of a WebSocket Secure connection between the client and server node. +**S2 communication client (S2CommunicationClient)** -The implementation of this REST API **MUST** follow the OpenAPI definition that can be found [here.](https://github.com/flexiblepower/s2-ws-json/blob/e238819dfb8bc0310be61c372294d6eaa316c51e/s2-pairing/s2-pairing-openapi-spec.yml) +A S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 server node. **S2 server node endpoint (S2ServerNodeWSSEndpoint)** @@ -101,62 +103,96 @@ This is used to denote the endpoint of both the REST API client and the WebSocke A person or entity that manages S2 nodes. For the purpose of this specification it is assumed that there is already a trust relationship in place between this person and the S2 nodes. The means that the way the trust relationship has been established is out of scope for this specification. -# Mapping the CEM and RM to S2 Server and Client Nodes +**Pairing token** + +TODO + +**Authentication token** + +TODO + +# Architecture + +## Deployment of S2 nodes + +TODO uitleggen LAN/WAN + +## Pairing and unpairing from the perspective of the end user +The end user can take the initiative to *pair* a CEM instance with a RM instance. In order to pair a CEM with an RM two things must happen: + +1. The CEM and the RM must be able to find each other on an IP-based network +2. The end user must retrieve a *pairing token* from either the CEM or the RM (depending on the specific situation) and enter this token in the other S2 node + +If pairing is performed sucessfully, the CEM and RM instances should esteblish a connection with each other and communicate through S2. If the connection is interrupted, the instances must automatically try to reestablish the connection. + +Once a CEM is paired, the user has to possibility to command the CEM or the RM to *unpair*. After unpairing the CEM and RM instances can no longer communicate through S2. + +## Working of pairing and communication + +TODO Uitleggen http server/client, openapi, websockets, certificaten + +## Types of S2 eindpoint implementations + +TODO uitleggen user environment en endpoints (plaatje) + +## Mapping the CEM and RM to HTTP server or client The CEM and RM roles defined by the S2 protocol are distinct from the Server and Client roles of the S2 pairing process. The following rules apply to determine whether the RM or CEM acts as a Client or Server in the pairing process. -* If a connection is set up between a cloud/WAN node and a local node, the cloud/WAN node must act as a S2 Server Node, and the local node must act as a S2 Client Node. -* If a connection is set up between two nodes that are similarly deployed (i.e. both in cloud/WAN, or both local), the CEM must act as a S2 Server Node, and the RM must act as a S2 Client Node. +* If a connection is set up between a cloud/WAN node and a local node, the cloud/WAN node must act as an S2 Server Node, and the local node must act as an S2 Client Node. +* If a connection is set up between two nodes that are similarly deployed (i.e. both in cloud/WAN, or both local), the CEM must act as an S2 Server Node, and the RM must act as an S2 Client Node. There are four scenarios for CEM and RM deployment, and applying the rules above yields the following: | CEM deployment | RM deployment | CEM acts as | RM acts as | |----------------|---------------|-------------|------------| -| cloud/WAN | cloud/WAN | S2 Server Node | S2 Client Node | -| cloud/WAN | local | S2 Server Node | S2 Client Node | -| local | cloud/WAN | S2 Client Node | S2 Server Node | -| local | local | S2 Server Node | S2 Client Node | +| WAN | WAN | S2 communication server | S2 communication client | +| WAN | LAN | S2 communication server | S2 communication client | +| LAN | WAN | S2 communication client | S2 communication server | +| LAN | LAN | S2 communication server | S2 communication client | + +Note: A device developed solely for use as an RM in a local setup will never function as an S2 communication server. Similarly, a device designed exclusively for use as a CEM in a WAN deployment will never operate as an S2 communication client. + -Note: A device developed solely for use as an RM in a local setup will never function as an S2 Server Node. Similarly, a device designed exclusively for use as a CEM in a cloud/WAN deployment will never operate as an S2 Client Node. -## Hostname of S2 Server Nodes -In the case of S2 Server Nodes in LAN, the Node **MUST** advertise itself using **hostnames** rather than IP addresses and S2 Client Nodes need to identify and remember the Server using this hostname, not just the IP address as this may be subject to change. The hostname of a node **MUST** be stable and unique. + +# The pairing process + +The pairing process is based on the trust relation that the end user has with both the CEM and the RM. That trust relation is out of scope for this specification and is up to CEM and RM providers to implement. Given the deployment scenario, it [follows](# Mapping the CEM and RM to S2 Server and Client Nodes) which of the RM and the CEM is server and which is the client in the pairing process. + +A CEM can be paired with multiple RM's a the same time. A RM can only be paired with one CEM at a time. An S2 node is always available for pairing. When a RM that is already is paired with an CEM is paired with another CEM, the initial pairing is automatically unpaired. When a CEM and a RM are paired when they already are paired, they stay paired. ## Discovery In order to ease the pairing process, which is specified below, the discovery process provides a way for nodes to find each other without requiring a user to know the pairing endpoint of the other node. In other words, the discovery process is a way to provide an S2 Node with the URL of another node which is needed to start the pairing process. Alternatively, it is always possible to initiate the pairing by manually providing the URL by the end user. > NOTE: the discovery process specification is work in progress and will be updated soon. -### Cloud-cloud +### WAN-WAN Both the S2 RM and CEM run in the cloud (for example communicating with the device via a manufacturer specific protocol). Discovery of the other node by lookup in a central registry. > NOTE: how the API of the registry will look like will be published soon -### Cloud-local +### WAN-LAN + A hybrid scenario where either the RM or CEM is deployed locally and the other in the cloud. Discovery of the cloud node by lookup in a central registry or DNS-SD in case the node is also present on the LAN. > NOTE: the DNS-SD service specification will be published soon -### Local-local +### LAN-LAN A LAN scenario where both RM and CEM are running on the same local network. Discovery through DNS-SD. > NOTE: the DNS-SD service specification will be published soon -## The pairing process - -The pairing process is based on the trust relation that the end user has with both the CEM and the RM. That trust relation is out of scope for this specification and is up to CEM and RM providers to implement. Given the deployment scenario, it [follows](# Mapping the CEM and RM to S2 Server and Client Nodes) which of the RM and the CEM is server and which is the client in the pairing process. - -The pairing process can either be initiated from the client or from the server. +## The pairing token -### Initiate pairing from the client +TODO -#### Pairing with a discovered Server +## Pairing interaction -The user visits the S2ClientNodeUI and the S2ServerNode has been discovered (so the S2ServerNode base URL is known) by the the S2ClientNode per [discovery](#discovery) as specified above. The S2ClientNode does a preparePairing HTTP request to let the S2ServerNode know that there is a S2ClientNode that wants to pair. It is up to the S2ServerNode implementation to decide what to do with this signal. It can be used to display a pop-up with the pairing token in its UI to improve the user experience. It must be implemented by the client, but only when there is a clear distinction between the moment perparePairing is called and when requestPairing is called. When preparePairing is called, it is not guaranteed that a call to pairingRequest or cancelPreparePairing will follow so it is recommended to put a time-out on showing the pairing token in the S2ServerNodeUI. +The user visits the S2ClientNodeUI and the S2ServerNode has been discovered (so the S2ServerNode base URL is known) by the the S2ClientNode per [discovery](#discovery) as specified above. The S2ClientNode does a preparePairing HTTP request to let the S2ServerNode know that there is an S2ClientNode that wants to pair. It is up to the S2ServerNode implementation to decide what to do with this signal. It can be used to display a pop-up with the pairing token in its UI to improve the user experience. It must be implemented by the client, but only when there is a clear distinction between the moment perparePairing is called and when requestPairing is called. When preparePairing is called, it is not guaranteed that a call to pairingRequest or cancelPreparePairing will follow so it is recommended to put a time-out on showing the pairing token in the S2ServerNodeUI. The S2ServerNodeUI **can** show the S2ServerNode URL to (also) allow clients that did not discover the server to connect but it does not need to because there is a client that already knows the S2ServerNode URL and that wants to pair. -#### Pairing with server manually If the S2ServerNode was not discovered, the process is as follows: ![image](/img/communication-layer/pairing-manually.png) @@ -196,30 +232,21 @@ S2ServerNodeRestAPI-->S2ClientNodeEndpoint: 6. HTTPS pairingResponse 5. The server check if the pairingToken in the requestPairing call matches the token that it displays to the end user. 6. The pairingResponse informs the S2ClientNode whether the pairing was successful or not. -### Initiate pairing from the server +## Challenge response +TODO -> NOTE: the specification for initiating the pairing from the server will be published soon +## LAN-LAN specific extentions of the pairing process +TODO -## The unpairing process -Unpairing can either be initiated by S2Node that runs the CEM or the RM. - -As the pairing and authorization process is based on a client/server model, the following applies to the unpairing process. - -If the client node takes the initiative to unpair, it first **should** close any active connections it has and subsequently call the unpair HTTP API endpoint of the server using its access token. Upon receiving the unpair call from a client, the S2 server node **must** remove all security information related to the paired node. After the response has been received by the client, the client **must** remove all security information of the server node. - -If the server takes the initiative to unpair, first it **must** remove all security information related to client node. After that, it **should** send a S2 [SessionRequest](/model-reference/Common/SessionRequest/) message with type [RECONNECT](/model-reference/Common/SessionRequestType/) to the client. The next initiateConnection HTTP API request **must** fail with the 'Unknown node ID' response to let the client know that it is not paired anymore. - -Client and server **can** keep other (non-security) information for user experience purposes. - -## S2 Connections +# S2 Connection After two nodes have been paired, the nodes exchange S2 messages over a secure connection. The following mechanism **must** be used to initiate a secure connection between two S2 nodes. Client authentication is based on a one-time use communication token that needs to be renewed every time a new S2 session is created. -### Initiation +## Initiation ![connection initiation](/img/communication-layer/connection-initiation.png) @@ -288,7 +315,7 @@ a. Since the server implicitly acknowledged the new accessToken, all pairs of ac b. If the client is not able to remove the old tokens (e.g. because the storage device or the DBMS is not available) it can continue working as normal. -### WebSocket based communication +## WebSocket based communication This section specifies how to use WebSocket Secure as the S2-over-TCP/IP application layer protocol. @@ -296,20 +323,20 @@ The WebSocket client **must** run on the S2ClientNode and the WebSocket server o The choice for a WebSocket as application layer communication protocol has the advantage that the session concept is intrinsically introduced with the communication protocol. All S2 communication happens in the context of a (stateful) S2 session which is catered for by the WebSocket session. So, the S2 session matches the WebSocket session. -#### Authentication +### Authentication For each S2 WebSocket session the client **must** authenticate itself using the commToken in the authorization header of the websocket connection request, following [RFC 6750 - The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://datatracker.ietf.org/doc/html/rfc6750). -#### Encrypted connection (WSS) +### Encrypted connection (WSS) Communication over the WebSocket endpoint **must** be encrypted following [RFC 6455 The WebSocket Protocol](https://datatracker.ietf.org/doc/html/rfc6455). S2 **MUST NOT** be sent over unencrypted channels. Therefore a wss connection (the URL starting with wss://) must be used. -#### Compression +### Compression The WebSocket Protocol ([RFC6455](https://datatracker.ietf.org/doc/html/rfc6455)) has an extension for compression: [**RFC 7692**](https://datatracker.ietf.org/doc/html/rfc7692.html) implementing so called per-message-deflate compression. https://datatracker.ietf.org/doc/html/rfc7692 RFC 7692 is widely supported by WebSocket libraries and and we are exchanging JSON plain text messages, it is expected to save a large amount of data. Therefore, implementations of S2 WebSockets **SHOULD** support RFC 7692 and **SHOULD** enable it whenever possible. -#### Keepalive & heartbeat (ping / pong) +### Keepalive & heartbeat (ping / pong) WebSockets by default have a **keepalive** and a **heartbeat mechanism**. Keepalive is designed to keep the connection open while heartbeat is designed to check the latency and check the connection is still working. This means that periodically a ping frame is sent to the server (endpoint) and in response a pong frame is sent. @@ -318,6 +345,25 @@ In order to reduce network traffic, S2 WebSocket implementations **SHOULD** not [https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2%5C%5C) + +### Termination + +A S2 session can be terminated in different ways: + +* In case an S2 node unexpectedly becomes unavailable, the WebSocket connection **CAN** timeout. This will cause an S2 session to be terminated. More details about the timeout can be found [in the heartbeat section](#keepalive--heartbeat-ping--pong) +* an S2 node **CAN** terminate the S2 session by sending the S2 terminate message, including an optional earliest time that the session can be restored. The other S2 node can take this into account in planning and (in the case of a client) deciding when to attempt to reconnect. +* After two S2 nodes have unpaired, the S2 WebSocket connection **MUST** be terminated immediately. + +### Reconnection strategy + +Once an S2 session is terminated it cannot be resumed and if further communication is required, a new session needs to be started. an S2 client node may try to establish a WebSocket connection. + +An exponential back-off strategy **SHOULD** be used, increasing the time between reconnection attempts at every failed attempt. If a reconnection time was included in any termination, S2 client nodes are permitted to make an attempt to reconnect before this time. At the specified time the delay between reconnection attempts **SHOULD** be reset. + +## Communication - JSON messages + +The S2 standard has been encoded into a JSON schema and asyncapi specification, for details see: [JSON protocol specification for S2 WebSockets](https://github.com/flexiblepower/s2-ws-json) + ### State of communication ![State of Communication](/img/communication-layer/state-of-communication.png) @@ -355,25 +401,23 @@ WebSocketDisconnected --> [*] | ControlType FRBC activated | FRBC.Instruction
SelectControlType
SessionRequest
ReceptionStatus | FRBC.ActuatorStatus
FRBC.FillLevelTargetProfile
FRBC.LeakageBehaviour
FRBC.StorageStatus
FRBC.SystemDescription
FRBC.UsageForecast
FRBC.TimerStatus
RevokeObject
InstructionStatusUpdate ResourceManagerDetails
PowerMeasurement
PowerForecast
SessionRequest
ReceptionStatus | | ControlType DDBC activated | DDBC.Instruction
SelectControlType
SessionRequest
ReceptionStatus | DDBC.ActuatorStatus
DDBC.AverageDemandRateForecast
DDBC.SystemDescription
DDBC.TimerStatus
RevokeObject
InstructionStatusUpdate
ResourceManagerDetails
PowerMeasurement PowerForecast
SessionRequest
ReceptionStatus | -### Termination +# The unpairing process -A S2 session can be terminated in different ways: +Unpairing can either be initiated by S2Node that runs the CEM or the RM. -* In case a S2 node unexpectedly becomes unavailable, the WebSocket connection **CAN** timeout. This will cause an S2 session to be terminated. More details about the timeout can be found [in the heartbeat section](#keepalive--heartbeat-ping--pong) -* A S2 node **CAN** terminate the S2 session by sending the S2 terminate message, including an optional earliest time that the session can be restored. The other S2 node can take this into account in planning and (in the case of a client) deciding when to attempt to reconnect. -* After two S2 nodes have unpaired, the S2 WebSocket connection **MUST** be terminated immediately. +As the pairing and authorization process is based on a client/server model, the following applies to the unpairing process. -### Reconnection strategy +If the client node takes the initiative to unpair, it first **should** close any active connections it has and subsequently call the unpair HTTP API endpoint of the server using its access token. Upon receiving the unpair call from a client, the S2 server node **must** remove all security information related to the paired node. After the response has been received by the client, the client **must** remove all security information of the server node. -Once a S2 session is terminated it cannot be resumed and if further communication is required, a new session needs to be started. A S2 client node may try to establish a WebSocket connection. +If the server takes the initiative to unpair, first it **must** remove all security information related to client node. After that, it **should** send an S2 [SessionRequest](/model-reference/Common/SessionRequest/) message with type [RECONNECT](/model-reference/Common/SessionRequestType/) to the client. The next initiateConnection HTTP API request **must** fail with the 'Unknown node ID' response to let the client know that it is not paired anymore. -An exponential back-off strategy **SHOULD** be used, increasing the time between reconnection attempts at every failed attempt. If a reconnection time was included in any termination, S2 client nodes are permitted to make an attempt to reconnect before this time. At the specified time the delay between reconnection attempts **SHOULD** be reset. +Client and server **can** keep other (non-security) information for user experience purposes. -## Security +# Security Please refer to an extensive description of the security specifications to [Security considerations](./security-considerations.md). -### Certificates +## Certificates For each S2 connection the server authenticates using a certificate. The cloud implementation certificates **MUST** be PKI certificates which are not self-signed. Only local servers can use a self-signed CA certificate, which is used to sign a server certificate. If the S2 protocol is used in a local-local configuration, the server **CAN** use a self-signed CA certificate. In this case, the pairingInfo **MUST** include the first 9 bytes, encodes as 12 base64 encoded characters, of the fingerprint of this self-signed CA certificate and the client **MUST** check this fingerprint. @@ -382,10 +426,6 @@ Note that all communication use TLS. This is further explained in [Security cons The server certificates **MUST** be exchanged and validated during the initiation of the connection (REST and WSS). This is default usage of most networking libraries. -### Cipher suites - -Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. - -## Communication - JSON messages +## Cipher suites -The S2 standard has been encoded into a JSON schema and asyncapi specification, for details see: [JSON protocol specification for S2 WebSockets](https://github.com/flexiblepower/s2-ws-json) \ No newline at end of file +Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. \ No newline at end of file From 4712716a51aaa426a635f6ed51186f33147c9d13 Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Thu, 20 Nov 2025 14:14:04 +0100 Subject: [PATCH 02/26] Improved communication layer specification, still wip --- .../discovery-pairing-authentication.md | 134 ++++++++++++++---- .../deployment_options.png | Bin 0 -> 82997 bytes .../communication-layer/pairing_direction.png | Bin 0 -> 64843 bytes .../powerpoint_img_source.pptx | Bin 0 -> 164764 bytes 4 files changed, 103 insertions(+), 31 deletions(-) create mode 100644 website/static/img/communication-layer/deployment_options.png create mode 100644 website/static/img/communication-layer/pairing_direction.png create mode 100644 website/static/img/communication-layer/powerpoint_img_source.pptx diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 2b6f9de..7731782 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -21,19 +21,19 @@ The protocol is designed to specify communication between two devices, a resourc | LAN | Local Area Network (i.e. a local network, typically contstrained to the building) | | WAN | Wide Area Network (i.e. the public internet) | -# Background +# Background (informative) ## Requirements +TODO beter uitleggen wat doelen van deze oplossing zijn (bijv. afweging univormiteit vs. complexiteit) + The communication layer meets the following requirements: The Customer Energy Manager (CEM) and Resource Manager (RM) are logical concepts within the S2 architecture, therefore the S2 standard does not make any assumptions on how and where the CEM and RM are deployed in a real life situation. In practice, the CEM could be deployed on a local gateway in a LAN or as a server somewhere on the internet (WAN), while the RM could be part of the device itself, deployed on an add-on module or on the internet as well. This means that the S2 communication layer **MUST** be able to deal with multiple scenarios that are depicted in the figure below. -*TODO update image with LAN/WAN terminology* - -![Deployment_options](/img/communication-layer/deployment-options.png) - In addition to - and partly because of - supporting the various deployment options, the S2 communication layer has the following generic requirements: +TODO cleanup + - Support for full duplex communication. Both sides **MUST** be able to send and receive data simultaneously. - Communication **MUST** be IP based. - Communication **MUST** be encrypted. @@ -41,6 +41,9 @@ In addition to - and partly because of - supporting the various deployment optio - Communication **MUST** work without additional firewall configuration by the end user. - Implementation of the communication layer **MUST** be based on a widely accepted technology and must be relatively easy to implement. - The pairing process **SHOULD** support extensibility for other application layer communication protocols. +- Provide a relatively consistent user experince regardless of the deployment of the S2 node +- Run a local RM on a device with constrained hardware +- A RM could not have a UI ## Technical decisions Given the requirements, this specification is build on the following high-level technical choices: @@ -81,7 +84,7 @@ An S2 node that implemented a WebSocket Secure server that can be used for estab **S2 pairing client (S2PairingClient)** -A S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 server node. +An S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 server node. **S2 communication server (S2CommunicationServer)** @@ -89,15 +92,7 @@ An S2 node that implemented a WebSocket Secure server that can be used for estab **S2 communication client (S2CommunicationClient)** -A S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 server node. - -**S2 server node endpoint (S2ServerNodeWSSEndpoint)** - -This is the endpoint of the WebSocket Secure server which can be addressed through its corresponding URL. - -**S2 client node endpoint (S2ClientNodeEndpoint)** - -This is used to denote the endpoint of both the REST API client and the WebSocket Secure client. +An S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 server node. **End user (EndUser)** @@ -111,32 +106,109 @@ TODO TODO -# Architecture +**Pairing attempt** + +The process of pairing two S2 nodes. The process can be completed successfully or unsuccessfully. + +**Iniator S2 Node** + +TODO + +**Responder S2 Node** + +TODO + +**End user environment** + +TODO + + + +# Architecture (informative) +This section explains the over architecture and deployment options for CEM and RM instances. ## Deployment of S2 nodes -TODO uitleggen LAN/WAN +This specification is concerned with connecting an instance of a CEM with an instance of a RM. Either of these instances are refered to as *S2 nodes*, which either have the CEM *role* or the RM *role*. Obivously, it is only possible to pair an S2 node with the CEM role to an S2 node with the RM role. + +S2 Nodes can be deployed locally within the LAN, or somewhere on a server in the WAN. Although their deploymend doesn't significantly affect the working of these S2 Nodes, there are some key differences between these types of deployment. + +* **WAN** S2 nodes are typically part of a large application that run on many servers and/or on some kind of cloud computing platform. A single application usually serves many users. Each user could have one or multiple S2 nodes. This could for example be a cloud-based energy management system that can connect to many devices. It could also be a cloud enviroment of a device manufacturer that hosts the RM instances in the cloud. An end user could own multiple devices from this manufacturer, thus the application could host multipse RM instances for this particual user. We call a group of S2 nodes that a single user can manage within one application an *end user environment*. It is also possible that an end user environment contains both CEM and RM instances. The user interface is typically a web interface or a smartphone app. +* **LAN** S2 nodes are typically part of an application that runs on an embedded computer device somewhere in the building. Such a device could be a physical energy management system, an energy flexible device such as a home battery, heat pump or EV charger, or a gateway device which connects to an energy flexibel device through some kind of protocol. Often an application will only host a single S2 node, but it is also possible that an application hosts multiple S2 nodes. A device could function completely on its own, but it cloud also be connected to an internet based application of the manufacturer. The user interface could be a physical human-machine interface on the device, but also be a smartphone app that connects directly to the device (e.g. via bluetooth), or a smartphone app or web interface that connects to an internet based application of the manufacturer. For energy flexible devices, it is assumed that they could also have no user interface at all, or that they are very constrained when it comes to comuting power. It is assumed that a CEM always has a user interface. + +![Deployment_options](/img/communication-layer/deployment_options.png) + +There are three types of S2 connections between S2 nodes possible: + +* **WAN-WAN**: A connection between two S2 nodes deployed in a WAN. Connecting between them is straitforward and can be done based on URLs, based on DNS domain names. It is possible to rely on common TLS certificates thanks to a public key infrastructure. +* **WAN-LAN**: A connection between a LAN deplayed S2 node a WAN deployed S2 node. Since there is almost always a firewall and/or NAT between these two, it is assumed that it is only possible to set up a connection from the LAN to the WAN; not the other way around. Connecting from the LAN S2 node to the WAN S2 node can be done based on a URL, and common TLS certificates can be used thanks to public key infrastructure. +* **LAN-LAN**: A connection between two LAN deployed S2 nodes. It is assumed that in this situation we cannot rely an internet connection, making it impossible to rely on a public key infrastructure for certificates. That is why for this type of connection self-signed TLS certificates are used. Connections are made based on hostnames that are resolved to IP-adresses using Multicast DNS (mDNS), since IP-adresses are not guarenteed to be stable. Discovering another node could be done using DNS Service Discovery (DNS-SD). ## Pairing and unpairing from the perspective of the end user -The end user can take the initiative to *pair* a CEM instance with a RM instance. In order to pair a CEM with an RM two things must happen: +The end user can take the initiative to *pair* a CEM instance with a RM instance. This process has to be started with one of the S2 nodes. Which node this is depends on the deployment and implementation decisions of the S2 node, but ideally it could be either one. The S2 node however needs to have a user interface. We'll call the S2 node that user uses to start the pairing proces the *initator*. We'll call the other S2 node the *responder*. -1. The CEM and the RM must be able to find each other on an IP-based network -2. The end user must retrieve a *pairing token* from either the CEM or the RM (depending on the specific situation) and enter this token in the other S2 node +The first step of pairing is esteblishing a connection from the iniator S2 node to the responder S2 node. This can be done in serveral ways (for more details see [Discovery](#discovery)). +* If the responder S2 node is deployed in the WAN, this could either be done by manually entering the URL of the pairing endpoint, or finding the endpoint through the S2 pairing endpoint registry. The latter provides a more user friendly way of retrieving the URL of the pairing endpoint. +* If both S2 nodes are deployed in the LAN however, connecting can also be done by manually entering an URL of the pairing endpoint of the responder S2 node, or by finding the responder S2 node through a process based on DNS-SD. This way the user only has to select the desired S2 node to connect to from a list of S2 nodes which were discovered in the LAN. Again, the latter is a more user friendly approach. -If pairing is performed sucessfully, the CEM and RM instances should esteblish a connection with each other and communicate through S2. If the connection is interrupted, the instances must automatically try to reestablish the connection. +The second step is entering the pairing token of the responder S2 node. This is a means for the end user to confirm that these two S2 nodes are allowed to send control signals through S2 to each other. The pairing token can be obtained from the responder S2 node. The pairing token is a (seemingly) random string of characters. This pairing token is typically displayed somewhere in the user interface of the other S2 node. We recommend to use a dynamic token which expires after 5 minutes. However, if the S2 node has a physical precense in the building and doesn't have user interface, there is also the option to have a static pairing token which can be printed on the device. -Once a CEM is paired, the user has to possibility to command the CEM or the RM to *unpair*. After unpairing the CEM and RM instances can no longer communicate through S2. +Optionally, the initiator S2 node can send a signal to the responder S2 node to indicate that the end user has started the pairing process and has selected the responder S2 node. This could trigger the user interface of the responder S2 node to proactively show the pairing token (e.g. through a pop-up) to improve the user experince. -## Working of pairing and communication +Once the pairing token is known to the initiator S2 node, the pairing process is started. It is esteblished that both S2 nodes are compatible and it is verified that the entered pairing token is correct. Pairing could either fail or succeed. + +If pairing is performed sucessfully, the CEM and RM instances should esteblish a connection with each other and communicate through S2. If the connection is interrupted, the instances will automatically try to reestablish the connection. + +Once a CEM is paired, the user has to possibility to command either of the S2 nodes to *unpair*. After unpairing the CEM and RM instances can no longer communicate through S2 (unless the end user pairs them again). + +TODO plaatje invoegen + +
+Image generated using the following PlantUML code: + +``` +@startuml +actor EndUser as e +participant InitiatorS2Node as i +participant ResponderS2Node as r + +e->i: Provide identity of ResponderS2Node (e.g. URL) +r->e: Provide pairing token +e->i: Provide pairing token +i->r: Attempt pairing +r->i: Pairing result (success or failure) +i->e: Pairing result (success or failure) +@enduml +``` +
-TODO Uitleggen http server/client, openapi, websockets, certificaten +## Pairing details for different deployments -## Types of S2 eindpoint implementations +The pairing process is based on HTTP REST calls. That means that for every pairing attempt, one S2 node behaves as the HTTP server, and one HTTP node behaves as the pairing client. The logical solution would be to make the initiator S2 node the HTTP client and the responder S2 node the HTTP server. After all, it is the HTTP client that takes the initiative to contact the HTTP server. The HTTP server cannot take the initiative to contact the HTTP client. + +The objective is to have all S2 nodes be able to be the initiator S2 node, as well as the responder S2 Node. This is necessery to provide a consistent user experience. The end user might not be aware which S2 node is deployed in the LAN or in the WAN, and then it might be confusing that his energy management system both provides S2 pairing tokens and asks for S2 pairing tokens. + +If every S2 node must be able to be the initator S2 node in centain situations, and the responder S2 node in other situations, and the easiest solution is to implement teh initiator as HTTP client and the responder as HTTP server, you might come to the conclusion that every S2 node needs to be able to behave both as an HTTP server and as a HTTP client. + +* WAN initiator S2 node and LAN responder S2 node: TODO +* LAN initiator RM and LAN responder RM: TODO + + +![Pairing_direction](/img/communication-layer/pairing_direction.png) + + +## Types of S2 applications TODO uitleggen user environment en endpoints (plaatje) +## Working of pairing and communication + +TODO Uitleggen http server/client, openapi, websockets, certificaten, pairing proxies + ## Mapping the CEM and RM to HTTP server or client +TODO check + The CEM and RM roles defined by the S2 protocol are distinct from the Server and Client roles of the S2 pairing process. The following rules apply to determine whether the RM or CEM acts as a Client or Server in the pairing process. * If a connection is set up between a cloud/WAN node and a local node, the cloud/WAN node must act as an S2 Server Node, and the local node must act as an S2 Client Node. @@ -156,7 +228,7 @@ Note: A device developed solely for use as an RM in a local setup will never fun -# The pairing process +# Pairing process (normative) The pairing process is based on the trust relation that the end user has with both the CEM and the RM. That trust relation is out of scope for this specification and is up to CEM and RM providers to implement. Given the deployment scenario, it [follows](# Mapping the CEM and RM to S2 Server and Client Nodes) which of the RM and the CEM is server and which is the client in the pairing process. @@ -185,7 +257,7 @@ A LAN scenario where both RM and CEM are running on the same local network. Disc ## The pairing token -TODO +TODO uitleggen geldigheidsduur en format van token ## Pairing interaction @@ -239,7 +311,7 @@ TODO TODO -# S2 Connection +# S2 Connection (normative) After two nodes have been paired, the nodes exchange S2 messages over a secure connection. @@ -348,7 +420,7 @@ In order to reduce network traffic, S2 WebSocket implementations **SHOULD** not ### Termination -A S2 session can be terminated in different ways: +An S2 session can be terminated in different ways: * In case an S2 node unexpectedly becomes unavailable, the WebSocket connection **CAN** timeout. This will cause an S2 session to be terminated. More details about the timeout can be found [in the heartbeat section](#keepalive--heartbeat-ping--pong) * an S2 node **CAN** terminate the S2 session by sending the S2 terminate message, including an optional earliest time that the session can be restored. The other S2 node can take this into account in planning and (in the case of a client) deciding when to attempt to reconnect. @@ -401,7 +473,7 @@ WebSocketDisconnected --> [*] | ControlType FRBC activated | FRBC.Instruction
SelectControlType
SessionRequest
ReceptionStatus | FRBC.ActuatorStatus
FRBC.FillLevelTargetProfile
FRBC.LeakageBehaviour
FRBC.StorageStatus
FRBC.SystemDescription
FRBC.UsageForecast
FRBC.TimerStatus
RevokeObject
InstructionStatusUpdate ResourceManagerDetails
PowerMeasurement
PowerForecast
SessionRequest
ReceptionStatus | | ControlType DDBC activated | DDBC.Instruction
SelectControlType
SessionRequest
ReceptionStatus | DDBC.ActuatorStatus
DDBC.AverageDemandRateForecast
DDBC.SystemDescription
DDBC.TimerStatus
RevokeObject
InstructionStatusUpdate
ResourceManagerDetails
PowerMeasurement PowerForecast
SessionRequest
ReceptionStatus | -# The unpairing process +# Unpairing process (normative) Unpairing can either be initiated by S2Node that runs the CEM or the RM. @@ -413,7 +485,7 @@ If the server takes the initiative to unpair, first it **must** remove all secur Client and server **can** keep other (non-security) information for user experience purposes. -# Security +# Security (normative) Please refer to an extensive description of the security specifications to [Security considerations](./security-considerations.md). diff --git a/website/static/img/communication-layer/deployment_options.png b/website/static/img/communication-layer/deployment_options.png new file mode 100644 index 0000000000000000000000000000000000000000..aed59fcd5233f5dde7892f92c24071e0bddf745c GIT binary patch literal 82997 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%0A8}U-ab;{ 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 EKWdZcdH?_b literal 0 HcmV?d00001 diff --git a/website/static/img/communication-layer/pairing_direction.png b/website/static/img/communication-layer/pairing_direction.png new file mode 100644 index 0000000000000000000000000000000000000000..9c9344fa34b274e38d5ba55c7fa6e1c9e848a100 GIT binary patch literal 64843 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Ðq+Wu4+r>mU-Z@Yqd)HwA3Ya&NUilPwp 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 z zb*MsB^S1sd4UH2%erzIpH;N{4(Kt)U@Wt7(tJtD@9j(#*{a$L=c_zcs8RFKmFi~)+ zYNn1ZC)7Jyc0r-HjCF{x*z~^704YlOn;5WWA94N}QkW}_AP8O6WfojRzwV*EQu~!|H9E{q?8?rCYejt-+mXn zTVN)=3#T34JDu?UCdiYUad)|6LAhrvQii*){?$_8xIVBruJA&47@&D=cQ^xVRA+Ry zJJQAvDc>+V1k2!xqw2D>A%-R{M>98)j+Jowsx!RsvNXV0Bs6tSnjMCauS&L{%m)_` z{V~CF=j2W@av#oqMER_+?RoQqxnS=6v48*8l77V8|1Zw2R1LExp6nEX!YvT(aq|KF zpe_qhGVs?1&F;tL2nM=AkGd`Tm~-4$PGIQ@cGHuO9`ww%XZ65Zbb3?n@(froLN0`o zW&}+-nT=DxdZi#OyL@xb{aI3M=^*>dct}0)HuI?v^qcVE;^TSeT4nG%Hu_Pm53`5) zF3diy9A57BEl6Hq9tyYi3Hu4PwD(U#B9gfKe5{uOjTLP^>uTJ4WnQ=a%3Qp0I4yQ` zAP`~d0G;NESS3(5z7OK8(T&`)sZ!0xKe5bX0x$4OW1b1`B$A@2Yb>J8Jd>POVTrIE z8*gefC9B72l#+G2wwua73LD&DpZsa#nfc7;G6{>Z?LdSKMZ@`P#s(;7z39G4#>BA9 z#G@;-nPUWq>p78MXcHi=0QF}MxMhcDwo1$aFv+bqWZQgiATj`&VU^g8z7>An8g5zCxo|no!gRJSbRn+2x>PBE-Mt3SKmmo z;F?9)j&18VVl>(d6~I4l2Za=RJXqWkp|@$A@03aX6Wr#jR_vvz2EO#VGG$RQvlnIK z)YMho|7lMt4UqRa4rOb7iHmE!gp5sGP%4(KA0&>9hQVQK|8Ki!>P|D4F1HwX9b&x} zL%rB7#SKmBlU;*B{dzvtZz21nnxqWh5N|xUx62x* zX8mRuZtMIKkKH{pZ}5#QvirAjkCP_Q)eYXxov*8|Q$NU^{(Pr!tCRRpcs`uM=9*RN z*!G*HW>i)kV2aOcu~I@d$A6@AR_N>ZRVL(yMq4%=#xJ;@ZY{7IBOMVw7V6z zNk<5jS=3Zd>=`fzVx>FluCt*GUbkv|*6iLm{=09_+-vS6?$?A&| zVYJd{%3gqoM~S9L7)3d>iRjT{b#`k6+!{FnTI|?g5wcaH9M2w^_Wv;UP!xMQ=L(Qg z{-=irZ2$l4s7LfSMKYcO5li5TE{Ee4E7AWL82p!y{h#BBe;yMylP8c`SBbDqzY1~~ zrvbvVwc8U^^yuAJEmvU(fe;YENcG>L{y)Vh|KEo3zl%=(t7zzd?5%$YK2G>&A|+gp z0N*QBwb9IUBwM4pMhdXNN!{UjG27-lrqsZPdpI1s{R^vX*ECL@QILpI*i|wECUpmr zS?x=pRL|u_SM##_K!hfnaR3Hv`=iOLQ=R=VTG0+ibTy8P%mDq761@%ou%#v$fe?NB z_p7ZpFC5!jtA6ZmEFWH@b+yoL3c%bgosYP5j}u1eXaRwq0qSa!OC!@s((iLThTN@r zfP1#~5U7kW9L_|bKp@z6`s)=!7-1Wc6%GowB~lULaK~Bdl@c4I8_W~~NAhI9vr_a}^12!nk8vGKbPGxc>fD>j8=}so-LURF=}LqiBzbY~)(5t>H1NPu2JX z8%|Mk}nkHq9|jZYDf+7a%Vq!flYQr%%A~#*hUqAe{BaAYwMsCRF+J zH1Q%FzE6gTR)@zrd?JQHi7!TbK%HIhmpwn4T~tXmL47c1EbEs#b6gtdwX8TiKx4y~ zsD5v^@+bNbr@1BLLB2y#>pT2lOYw7`EIQX~o{Ra**~A@sKiF~j9fxN3s2&@G(~u7R zIt6cbTc1<*@!Nh62jd#b9U!IzRh+ezd{dI7o6}Jnp#yLI=2G?hi$O0pzIt=0$70=5 zY)Y9J-_dUH&;yQAP73ye6D_t>=jhhPU=Q0a5xfbUv~y0&`Y7^)a5mQa*wPoSbqUy$ z!V2bHOW@J{!Wl``Y%k|Gs`q~4RS4I3jW`Y&Z<~xDH14q;lkXP1ZADdK)*9i! zz05fb<2)LOT^2DhWZU}ZP5Q*(8~qjN@mMqK{xuBb zg={FFIKsS2uP_fn8!{m|{gqsVb?kDErGur;f2<6w^i4pBod+ddwS3Y&0h@f!#?RD# zoK1cE59besFuO5%3S9j2zjJ2=_j?wB+B~D3^(%)(JpkJ%zvwvZ56|Jl&M@x z61{{~@so(W#?QijHh7d(K;wRuz#sPS?cgI>OLEzEk{v-qj`GQA557tbj`w?a!QBGX zSwsOYLHFPfI;0golXwo&_fsPxGOK5x7Lp^!FECPwJ;ZY6U^Yn4)1`2vb<{h!(-#X} z60S7~Si`56CQ^bt+LVATKmq)j79LlXQR{+R(nvAQHL)|VkHt~|wRIDvY&)$T7|$A` zX9K~(c9}K7ee*Lbt&Zk=^ESU*WW8l+6z6$szo`$L%Aj`7K2=Bjt2sV+hI0@--0@T9 ztd`7Cd(%6JL?Bme2__U2#l$UmgP8rq-DO22@lE2JW5CuGz2!1WV( zK9KHx)>h}T?Pk)HytPD-rfpQ>Ov|@

j@3884-O*(<;mBE{i^HjD$44`9Pbej9q> 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

J^7J(^dZLaj0wn_6f;J?|XImz-i%xX-(9nG!s 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 literal 0 HcmV?d00001 From e22a40c78bd34b7fd5ba8da7e8cef74f67283386 Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Mon, 24 Nov 2025 12:13:25 +0100 Subject: [PATCH 03/26] Expanded architecture section --- .../discovery-pairing-authentication.md | 125 ++++++++++++------ .../pairing_process_user.png | Bin 0 -> 11890 bytes 2 files changed, 86 insertions(+), 39 deletions(-) create mode 100644 website/static/img/communication-layer/pairing_process_user.png diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 7731782..d1d0d7a 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -15,10 +15,13 @@ The protocol is designed to specify communication between two devices, a resourc |Abbreviation | Meaning |---|---| -| S2 | European standard on Energy Flexibility EN50491-12-2| -| RM | Resource Manager | | CEM | Customer Energy Manager | +| HTTP | HyperText Transfer Protocol | | LAN | Local Area Network (i.e. a local network, typically contstrained to the building) | +| NAT | Network Address Translation | +| REST | Representational state transfer | +| RM | Resource Manager | +| S2 | European standard on Energy Flexibility EN50491-12-2| | WAN | Wide Area Network (i.e. the public internet) | # Background (informative) @@ -66,7 +69,7 @@ For the long answer, please refer to [this page](why-not-oauth.md). This specification uses the concepts that are defined below. -**S2 node (S2Node)** +**S2 node** Refers to an instance of either a CEM or a RM as defined in EN 50491-12-1 and implementing this specification. S2 communication between two S2 nodes can only be established if one of the S2 nodes is a CEM and the other a RM. These S2 nodes must also have the same end user. @@ -74,27 +77,27 @@ Refers to an instance of either a CEM or a RM as defined in EN 50491-12-1 and im TODO -**S2 node UI (S2NodeUI)** +**S2 node UI** A user interface through which an end user can interact with an S2 node. Interaction between the end user and the user interface must be secure, but this is out of scope for this specification. Examples of a user interface are a web interface, an app or a physical interface (HMI) on a device. -**S2 pairing server (S2PairingServer)** +**S2 pairing server** -An S2 node that implemented a WebSocket Secure server that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 client node. +TODO An S2 node that implemented a WebSocket Secure server that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 client node. -**S2 pairing client (S2PairingClient)** +**S2 pairing client** -An S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 server node. +TODO An S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 server node. -**S2 communication server (S2CommunicationServer)** +**S2 communication server** -An S2 node that implemented a WebSocket Secure server that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 client node. +TODO An S2 node that implemented a WebSocket Secure server that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 client node. -**S2 communication client (S2CommunicationClient)** +**S2 communication client** -An S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 server node. +TODO An S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 server node. -**End user (EndUser)** +**End user** A person or entity that manages S2 nodes. For the purpose of this specification it is assumed that there is already a trust relationship in place between this person and the S2 nodes. The means that the way the trust relationship has been established is out of scope for this specification. @@ -145,11 +148,12 @@ There are three types of S2 connections between S2 nodes possible: * **LAN-LAN**: A connection between two LAN deployed S2 nodes. It is assumed that in this situation we cannot rely an internet connection, making it impossible to rely on a public key infrastructure for certificates. That is why for this type of connection self-signed TLS certificates are used. Connections are made based on hostnames that are resolved to IP-adresses using Multicast DNS (mDNS), since IP-adresses are not guarenteed to be stable. Discovering another node could be done using DNS Service Discovery (DNS-SD). ## Pairing and unpairing from the perspective of the end user -The end user can take the initiative to *pair* a CEM instance with a RM instance. This process has to be started with one of the S2 nodes. Which node this is depends on the deployment and implementation decisions of the S2 node, but ideally it could be either one. The S2 node however needs to have a user interface. We'll call the S2 node that user uses to start the pairing proces the *initator*. We'll call the other S2 node the *responder*. +The end user can take the initiative to *pair* a single CEM instance with a single RM instance. This process has to be started with one of the S2 nodes. Which node this is depends on the deployment and implementation decisions of the S2 node, but ideally it could be either one. The S2 node however needs to have a user interface. We'll call the S2 node that user uses to start the pairing proces the *initator*. We'll call the other S2 node the *responder*. -The first step of pairing is esteblishing a connection from the iniator S2 node to the responder S2 node. This can be done in serveral ways (for more details see [Discovery](#discovery)). -* If the responder S2 node is deployed in the WAN, this could either be done by manually entering the URL of the pairing endpoint, or finding the endpoint through the S2 pairing endpoint registry. The latter provides a more user friendly way of retrieving the URL of the pairing endpoint. -* If both S2 nodes are deployed in the LAN however, connecting can also be done by manually entering an URL of the pairing endpoint of the responder S2 node, or by finding the responder S2 node through a process based on DNS-SD. This way the user only has to select the desired S2 node to connect to from a list of S2 nodes which were discovered in the LAN. Again, the latter is a more user friendly approach. +The first step of pairing is esteblishing a connection from the iniator S2 node to the responder S2 node. This can be done in serveral ways: +* Enter the address manually. +* If the responder S2 node is deployed in the WAN, the URL could be retrieved through a registry. The end user would have to select the type of S2 node from a list of known s2 node services in its region. +* If both S2 nodes are deployed in the LAN however, s2 nodes can be automatically be detected. The end user would have to select the S2 node from a list of automatically discovered S2 nodes. The second step is entering the pairing token of the responder S2 node. This is a means for the end user to confirm that these two S2 nodes are allowed to send control signals through S2 to each other. The pairing token can be obtained from the responder S2 node. The pairing token is a (seemingly) random string of characters. This pairing token is typically displayed somewhere in the user interface of the other S2 node. We recommend to use a dynamic token which expires after 5 minutes. However, if the S2 node has a physical precense in the building and doesn't have user interface, there is also the option to have a static pairing token which can be printed on the device. @@ -161,53 +165,92 @@ If pairing is performed sucessfully, the CEM and RM instances should esteblish a Once a CEM is paired, the user has to possibility to command either of the S2 nodes to *unpair*. After unpairing the CEM and RM instances can no longer communicate through S2 (unless the end user pairs them again). -TODO plaatje invoegen +![Pairing_process_user](/img/communication-layer/pairing_process_user.png)

Image generated using the following PlantUML code: ``` @startuml -actor EndUser as e -participant InitiatorS2Node as i -participant ResponderS2Node as r - -e->i: Provide identity of ResponderS2Node (e.g. URL) -r->e: Provide pairing token -e->i: Provide pairing token +participant "Initiator S2 node" as i +participant "Initiator S2 node UI" as iui +actor "End user" as e +participant "Responder S2 node UI" as rui +participant "Responder S2 node" as r + +e->iui: Provide identity of Responder S2 node (e.g. URL) +rui->e: Retrieve pairing token +e->iui: Provide pairing token i->r: Attempt pairing r->i: Pairing result (success or failure) -i->e: Pairing result (success or failure) +iui->e: Pairing result (success or failure) @enduml ```
-## Pairing details for different deployments +## The S2 node and the S2 endpoint -The pairing process is based on HTTP REST calls. That means that for every pairing attempt, one S2 node behaves as the HTTP server, and one HTTP node behaves as the pairing client. The logical solution would be to make the initiator S2 node the HTTP client and the responder S2 node the HTTP server. After all, it is the HTTP client that takes the initiative to contact the HTTP server. The HTTP server cannot take the initiative to contact the HTTP client. +TODO: Begrip endpoint ergens uitleggen -The objective is to have all S2 nodes be able to be the initiator S2 node, as well as the responder S2 Node. This is necessery to provide a consistent user experience. The end user might not be aware which S2 node is deployed in the LAN or in the WAN, and then it might be confusing that his energy management system both provides S2 pairing tokens and asks for S2 pairing tokens. +## Used technology for pairing and communication -If every S2 node must be able to be the initator S2 node in centain situations, and the responder S2 node in other situations, and the easiest solution is to implement teh initiator as HTTP client and the responder as HTTP server, you might come to the conclusion that every S2 node needs to be able to behave both as an HTTP server and as a HTTP client. +An S2 connection basically consists of four steps: discovery, pairing, communication and unpairing. For these steps different types of technology are used. -* WAN initiator S2 node and LAN responder S2 node: TODO -* LAN initiator RM and LAN responder RM: TODO +On of the main techologies the process relies on is HTTP REST. All interactions based on HTTP are formally described in OpenAPI specification files. [OpenAPI](https://swagger.io/specification/) is a formal language for specifying HTTP based API's. It can be used to generate reference documentation for developers, as well as stub code for many programming languages. +### Discovery -![Pairing_direction](/img/communication-layer/pairing_direction.png) +The first step is finding the responder S2 node from the initaitor S2 node. In principle this is done based on the URL of the responder S2 node. However, to improve user experince, two systems exist to find this URL in a more user frindely manner. For more details see [Discovery](#discovery). + +* If the responder S2 node is deployed in the WAN, the end user can find the endpoint through the S2 pairing endpoint registry. This would result in a list of vendors that offer S2 nodes. +* If both S2 nodes are deployed in the LAN however, the responder S2 can be detected automatically through a process based on DNS-SD. This way the user only has to select the desired S2 node to connect to from a list of S2 nodes which were discovered in the LAN. + +### Pairing + +The pairing process itself is completely based on HTTP rest. One S2 node behaves as the HTTP server, and the other as the HTTP client. This process is described in an OpenAPI file. The process consists of multiple steps. If the pairing process is completed successfully, the S2 nodes will agree on an authentication token. This token is used to initiate communication or to unpair. + +We'll refer to the endpoint that behaves as the HTTP server during the pairing process as the *S2 pairing server*, and the client as the *S2 pairing client*. + +Pairing interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN deployments self-signed certificates are used. + +### Communication + +Communication is setting up the actual session, where S2 messages are being exchanged. +The process always starts with HTTP based communication, but then is handed over to a protocol which supports a two-way messages based communication channel. Currently the only protocol that is being used is WebSockets, but there are plans to add other options in the future. The HTTP interface is also specified in an OpenAPI file, together with the unpairing process. -## Types of S2 applications +We'll refer to the endpoint that behaves as the HTTP server during the communication process as the *S2 communication server*, and the client as the *S2 communication client*. -TODO uitleggen user environment en endpoints (plaatje) +It should be noted that pairing and communication are two seperete HTTP interfaces, that don't have to be used in the same way. It could be that an S2 Node is an S2 pairing client, but then becomes an S2 communication server. This depends on the deployment of the s2 Nodes (see [Pairing details for different deployments](#pairing-details-for-different-deployments)). -## Working of pairing and communication +Communication interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN deployments self-signed certificates are used. -TODO Uitleggen http server/client, openapi, websockets, certificaten, pairing proxies +After the HTTP interaction a WebSocket is esteblished. The S2 communication server is always the WebSocket server. This server must use the same TLS certificate as the HTTP server. + +### Unpairing + +Either S2 node can take the initiative to unpair from the other S2 node. This is done using the same HTTP OpenAPI specification and the same HTTP server and client as the communication. The details for unpairing differ depending if it is the S2 communication server or if it is the S2 communication client that initiates the unpairing process. + +## Pairing details for different deployments + +As explained, the pairing process is based on HTTP REST calls. That means that for every pairing attempt, one S2 node behaves as the HTTP server, and one HTTP node behaves as the pairing client. The logical solution would be to make the initiator S2 node the HTTP client and the responder S2 node the HTTP server. After all, it is the HTTP client that takes the initiative to contact the HTTP server. The HTTP server cannot take the initiative to contact the HTTP client. + +The objective is to have all S2 nodes be able to be the initiator S2 node, as well as the responder S2 Node. This is necessery to provide a consistent user experience. The end user might not be aware which S2 node is deployed in the LAN or in the WAN, and then it might be confusing that, for example, his energy management system both provides S2 pairing tokens and asks for S2 pairing tokens. + +If every S2 node must be able to be the initator S2 node in certain situations, and the responder S2 node in other situations, and the easiest solution is to implement the initiator as HTTP client and the responder as HTTP server, you might come to the conclusion that every S2 node needs to be able to behave both as an HTTP server and as an HTTP client. + +There are however two situations where this is not possible: + +* **WAN initiator S2 node and LAN responder S2 node**: Since the LAN is usually shielded from the WAN through a firewall or NAT, it is assumed that it is not possible to approach a LAN HTTP server from a WAN client. This specifications offers two approaches to this problem: + * Accept this limitaiton and not allow the WAN S2 node to be the initiator S2 node. Pairing can only be performed when the LAN S2 node is the initiator S2 node and the WAN S2 node is the responder S2 node. Special care must be taken to explain this to the end user. + * Many modern devices or EMS systems are connected to a cloud backend managed by the OEM. If this is the case, it is possible to implement the pairing HTTP server in the cloud, even though the S2 node itself is in the WAN. If the pairing is performed successfully in the OEM backend, the result of the pairing must be communicated to the S2 node via the existing connection between device/EMS and the OEM backend. +* **LAN initiator RM and LAN responder RM**: Since one of the requiremets is that a LAN RM instance can be implemented on restricted hardware, and a TLS enabled HTTP server is for more memory intensive than an HTTP client, there is an option to implement a LAN RM instance purely as an HTTP server. A long-polling mechanism is available to indicate to the HTTP Server that the S2 node is available for pairing. This mechanism is also used to initiate the pairing process from the HTTP server. In other words: in this specific situation the initiator S2 node behaves as the HTTP server, and the responder S2 node only has to be an HTTP client. + +![Pairing_direction](/img/communication-layer/pairing_direction.png) ## Mapping the CEM and RM to HTTP server or client -TODO check +TODO update The CEM and RM roles defined by the S2 protocol are distinct from the Server and Client roles of the S2 pairing process. The following rules apply to determine whether the RM or CEM acts as a Client or Server in the pairing process. @@ -500,4 +543,8 @@ The server certificates **MUST** be exchanged and validated during the initiatio ## Cipher suites -Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. \ No newline at end of file +Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. + +# Versioning of specification files + +TODO \ No newline at end of file diff --git a/website/static/img/communication-layer/pairing_process_user.png b/website/static/img/communication-layer/pairing_process_user.png new file mode 100644 index 0000000000000000000000000000000000000000..365dd11f9c90cbb66afe62aff3dbd57be62e02ee GIT binary patch literal 11890 zcmbVyWmsIxmUaUHf&`b~A-D$!1PB^}ySux)OB37)(zph9cMI-88+Q%vZqwwPd(VAl zzWMP@{n<~|uC;2FE_qjothC4*M0`X50Psdk^s77o05c5$KpDJ-fwUZ#8`wZT3$l`m zLXa;!{A(m6Bve#1Y;0^?+_#WIO#F_FjEs_!nu&>tf#Jiam%{b=GaoPS7ZDK=VPSDm zQAr62SvfgnMMX7LRV^JIBLf2ieSK3K8yhn-TS&39b9HqEdU^T#_yh(9h6D$PhKGZq zq7q|bV!nS*N=V2|Nl8ge{E?lVotasfpI=$N?un+nbtt+uH|w zdU`rLfA#fEj*gD@_k)LqW@lz*CMH&uj$g{j<>lq` z^YfQ8aSzD!XdQ)A9Sv-3-7Jla9RVUn)<*Vvjz)&>^xfW>Iy%}qa4|92TIyLlI$2pV z8rWDl{~9ELtdyRaqN?M++5u1yKCY<|(J~g$%xFEww^)8uJk(0&G#+RR=3&Z(rZXOk zL`JqQRphY@-uID8H0xPB{`||wt+%myX!{iL0fiTN;j}~05eTv0v3K;qFwUIpKjAX@ z-icCCF~(HR$(e?82E847ZrP{>hGf~NZbYnz-1%|q_3CH(m)+6QgS?IL# z#QfZhk~5?>WEye>zzpcdoPG_WkZFOj^Vm#Tv`{ML{YHgOPuFPLTP#9kGGP!Lz|Y!E z--X7XP+6<^HHtxSXZ=|(sQ>oVWBA9IQqDQv)NdHhK@AdmN z)#SdtQ!Hw|txFWJ9XEN+2}|URDYV4ET3Z`nv`LomQzU?!s8xqpcbLtsFSl$`a^_rT zb*=4D5}NK}9=>G9*k8lrzcwVGEXUCm$1?8Wy|IMeSA_RQiSJ09q2J*!X8J%M9}`CNR~Fd@6h+O3IvG4m0RZFg ziE97=pOtqdP{D++EdcM~JMn$`qhG57GDFv;#kg@a#bHZf8hk6@F1yOfnJU&7j&`xx zO&NZ|`m+@fSX+cfFV7+9?dt1{nftId)WKbL+Gy!b32pDOlhhEdB6s3X4GqTW8M)J# za{k1Fki_bTGM6xmF`5eeM1QHKo#%namoj0YM9`))rC*&NDk;BKN3(sU_lAFyUlj(8 z^QAd4#tdYVTmoN1@f5MTJ5E;%BlXL#8C%XouC3r?@bGH4`aGU5J{^4 zAG!Yr5!&?sxha#GR+GXM;U|(mn`yX<$_gw4lxx`tI~s)ekn)YP3bjXHB{bC}F~n z`wGQhYb>GExy##O^cE-EFkfKtdQQnlAIhnbK zp{gU>LRM-ox5`mVr>)JuPm>f5Qr%iA(KEt{YW(tYUBQ1Yj;!*gs##DieZ%_)=Xr1}I_??a?oh*yR-}yEv`Kj`FEfML0IPgR)FS^d zc~;llQSDKmq}>9rS@MWXPdh$ul6ZnA=Ax%oUfXMVy zOEkfW$LFE@5c|d*(eI0g1pVGT$6EhZm(=$ip9sN1J?IUSrq7Z0wkf86q15Wo8Sxe^ zd$WHVcf!m5o_HP?Q(=fIf3*Lyv>R(~g(fB)FP@sF5d0{Ay+urOp8q(7t537$=u1kT zM){S?$2cJhZUls0`qgOnowxRrrAvy8R1HUU5Q?Z)phhhskr}cDSq$*Ln4I? zlLUpr?)Z)|KZB&{s6?Hi`<<}Vc+u5_JY60sL)!3mNf&%|%Hix{p<{*I!dKT{nn{Kd z`unAuRPYI(f-74=FT!j$8%^?bNdGO2c1r9?*=J_^8a`SE6zANj=Jyky`o?5#c{zn< z-)4}Kp{jKE8XpzP@L0Eb;T&_8KfK~k+$UmJ$xA@1qs$*z9o0{FveUb8ykxZNQVydO z7Ap)rozTa2sAXm#lAPZ4Q>|O3-P_vG-y(M@ou^b0pEP?P)n|o>`;``z2Yr6DE!UV)~5aM%4gMM+3Sa= zDi`<|Q{#9sQrFt1k<%ija?W~AQ^s~mlWb0?y1aFUvSjPret+!ej@Cxu>~j&H4*I74 zu!zF#?CLwWYL7J1&Y@T)g_n(h-i^V@;Yw5uzi`t`o-?<@*vRdn?G->G%YgRkBzxo+ zU7=cx4Nz;9N`y}dvS~gt-VDSbC{whuY5nh`%CxGGt}$}t1uC9y(w!DdN{Gd_G^EIw zAN%Ho{8ZE?T9E*IXU3!9POPKXB^Zk8_{s3FF(@(HhsM4YVQ@!)XBZ&ct^Iy5rDKJ? zrDOMDdWUUD@+$n5Ved>8O{>(bSts9e11WoALuU_PnA*ZZvjljBA>|0+DmjhZ!@|1h zn%315ouctQQ;#(6E&mNCL#~Bwt0;S91PEdD8a8HSpkmX=TDX6QFL__D7GoV+CSC^N z*3wm|slHMLXknpVq`!$(CmbGxqZRHKU%CE6c5aDJr@hMb^k}^OYr014xrvJi^FnP& zJwm+(DJaxYmusFfp!7T^uXU+OF?*@%HeI)PPOK_r8^@#Ew{P72Y$W>(hGt=E&)Drj zRig1}PcKz6nYaL?4cgdhQ~Po?Kwt;H!_s21dz_1staoI6+D@Sgv2XdNCbG$Jjn_j> zQ>)Ki^IQL*7FpV=1k?V>{&DcV@G%Jt&QDFv)>P#Ma?a<#tx>U2C*f&aiB{IYvYfT) zT>Qg$sq0r7BatynAM2+*u;av{mgl0TSvqkidOM6^c@1ub(L%orT7@E&e%%uN+`fSn z66Y*b@dMQ*v3b1}wYA&TY+QWSf)rY%QnBHHcbZC`n@6e3ol$F<)IiP7Y)$ZCLf|1X z_0HW?k&|o(2l4dcP5gn?$v_e>&3*p$?q@0MsWLE_o#y1Mf4GmQx{o=xw&d_|ES%+y zi$qkq?4#YDuU13K$7WII$wl>J5Ue|4X>rB&8?mUNkq^XvJ2e=Uyg{@CKP4cizSQ3j z2Trhq0>$NyiR70PYHUF5?oq8T*D`5esNQ4qtj!ary;MJfp%tlz^|KCeICzdVwhcvE zZXsvBiD$35_GMeTma1ygb-@SZb5agrttvl`D&UB^##g!%=wE zf5jB5HvPh2?o8-L4Z02R(AP2Fe|^VzmoE8=3QzA%iQ!Qg)5JBf{3c<$2SAL8`ldBr zv+#mRa3=^sh09H%ztHizrMnvf1F)zF`<0%InOwbe_9D(^1~t7>2Z5Z? zc>nnQ&7pTN#I|8-5|ieh+n(~X`$688YMk>edu~u>sCqN?xxERF!PiDrH+MJ$t9ZXM zi$cKr3+?2+z&i^<(ucOh>R}eLuysUSVZSgGN_r8CVFNtkhK!zs8=w-VHQy6s^!P!X zF^M-W37X0cE=4>^GO3HsLn?J&`Oja}k_CEN2(CGX;~z{U#~>Qr3AS_Ofs+h!RjXKeUcHMY>-?x*xmbxYkmjdGX<%r@ov>_vkXz6NiLoIH4hJkhRE${va`Trepi^ zsIoViW((kV{VB%rpN5<#;=bnCGd$am-$BVs;t{gXApJNzQ?x%c%!9~)RfHFN_*(_s zJA2)K|9~)4Pz|z&pAVj&<>?1C6P@uD_B-ey@kw^?izlpZFmw7nSc`1+bRJPyO=h$_ z;M31q$9!)EEfMZgF3xXvm<$Mq?&!D=V>AtmUNVr!C^Ztaw6v#F)-2%+*46ppXFbs! zR>Yp9p>kiZS9S&A866{frtM-x&47^4nu+KZ{ov>GkjjB(l4XaK14H#8=@SHByg9|= ziLE}$KP(mW!Od)WR1vfjY#tp9W`vJ^q9l4HrfN*gLw9Er`;fipgT}N2_ckWsATn<5 z@V0C={Dz2XlVz`4(`_x)9Z)BMyS{miEaA^G$~r8m={92_&oT%i$xmu%+g<0o3My8@ z<*bt_DoousxP(?8aZZV2iyi#sWt2SnOjp*joG5$tyJyP0sHgb(b)Qrsrs&qSVrYH+ zm*PPXp107bAz9{Rr+QA5EexuM!-3-V%qtpT(wyc!-voIRP>)1wa63Im;_Tyf53mO_ zKx43Dgz-`DSntON9v)!RIjO_Wc&_h5TkHFo3l*BwGx4osl>QB*J4I6(`4Z*RO=>N1 zPbp&KNL5A;CAv(oq$N9zd0WA(Wq-dmfl;k_X`*t3^5RXDr7Gr?4*5{42hb^5Pd2`o zxgww<4=$sVD{f^I<;QezqW6VK-~)$!Sc~V=$fj?iofSW?acg>mbhz&!FlwE>G2{jruw+vOZC~G=F`?wZa+IA(z{pAYZ3N#XMF~`6o8>P&%`vpU+ z2Y0$W5?ZZsij;V=P-65B%%j1elDm=3*J3S(Jf&>xPfuhbkMaJWFjYWSvsr`S-!szJ zf^*Z(vSe5eNwu`*$3g8(!@;D?gWvV_l({>$Z)V4ENJ%z5#5C9NN!9GKTt6m{<`0UP z)1+J=F|(-GX*Wlfg?(eEVK4&`@GjmS-Iq3(Xv1oxGhxs)e5HX&N;8Mbg$NT_Bm(wu znN7>c=ns1yS{^G>2{i+o_DDUy&iF`Wo6qXcF;Y}C2;QA|N|3FqC3?AoX8CFUs8ZEu zZ$1?INtjfM$s6|Rd~dBHoIZQB+~rUuM_RWicdxTZSf=ozK#ZTyeDHcDwAV zTyn=#;tc>XcR!jK*znSC_mfr4xNs|VT~`_?rOV>x4QCD?jrp$4L`5iv6wuri`5P% zdtUvz;H|X#XjQlU8Y*otv+TN*l27te|eE zu)9nq*xBn_Lw^!BK}CQh9yyM>{(MU5Rz{ofz3QE)#z6f{8~#m*jL3f0bnyuZlblQX zQyMu%*i|7g2kdvV)9x)JoSQMQmpXuUo^t*%_^ z>wzzG!n!{0{CkH32r8vnZ4isOXA~HmTN@;f=02gXR({#(h+HaQQTK)aV&TQwbHyCl zX)#a!ot`lgAJyPMGqay&RXr7>SL^^!fkS;DoVov9d5z#U3uYbDJWtP>*hmt^OzG9` zpjc=j*^=Z|GZVZfEmac`DRM=7U;b09M| z)BI<1E1NpwH27aJkqvdf%88fj4vX14vQUr1jrPPpRkkOU@fO)ea@Qh0a&Z~xKo}hf z2CGTf3GSwxZ~M1qFUNvu9PEOcfEg4%_&-RdJGXIUc==L968A&y-z4nvb!80|e%`(u z?oeKjpYf3o&@jozsF9qK>Fj}Fqlq2WD?;H45shNmUs$9S^P@utdt(gN<*64ui+dsN z`+Yd-C{5V&*;Nss3&Be0oSk;|uTn^ln4s3tplV-Qq>08L{j}HY`O2vhtp>0v7T1;h zVb0Qbuf5C<@8k-%Rftj<|UtfuV_{gOG_~t@=y}3OG;( zwS9^cs?>{)^h_PECsScFNGN46pL<1l1HUpA+`{7113*P=FaqdnI=i2h`?@l(g;w8{ zd?QJ;f>G)d7(lWG{Vr?@${MJbb;4=s2u+wgFUn3-mOV%Ms0yG}+UNDx=MMv^5uy4m z6MXZNGtLw^a4zm)mz7d#1=K2zbd9A-*x`^LJe{T1W?HkF)&ffI(1>{lmu>C2b6!>k zf2(o$3r(o^KU9~B;J>M&ZnMh1c4&ToYXuikU7+Ii8Wm%sd*JkY`cUtwkfHAxTH^2uz-($qLhDGWiS1v4wTLPJDFqMHm&tiUdWl#Fh{Z zEuq{hMG3D3AzU+kCXpgCBagF^s+Hw1uA?^Jff;ReJ@~G3X^Pd-GUxfxDJNRCjt%@} zEpd+avHqB+e+x@S80;+f$v%z;|2oY4*PSF$F;2t>r&n27Fu)G5NCVVdEOYiO<@#d20I*8_k2qwjeapFXvCENk%Rq3X>OEBxDeq7y0qe`vjSs1~%Krk-_W6 z7`O!?aDQ|`eMRp1_MH@wQdlqLf6nXS`D!<;>F|g7CN%{JR=CdCzmP05NxB)+I@7$l z;4SB%*zs$%_mD|APLhgP5TJ4}FMfRCD7Sg8(@vE=0YZ@^g6eVXzIeX9?9!-%zQqq> z|1tY1hLz=C>m&I25lHQl`Ck0Wr`eckY};JrlQuV|f(Sl>k%j^T!~TPC`rc;HPF!= z&vAn^QJ5@Ci=u$L@{bb^Nz9Wgiy-fY)6{f5e=M|@*P42p#UifF1DI}zK9gA;?5-M? zzYghi3=C<2BlAuWODFQ*Sv=}Mj+Hx}C zXRd)ZXZU>=1?7^v33$?H}FjXrVX^A z09O6m>xb)t$ct(7fRA-AU1l`~A&X~^VgS9rtz%MC4&!5NIYR8!l|LtkQ`Y2ZYCx0U z3?h;ECPN0px6W#ud5le-YdRM9;!;O#^fI-fNw5>OJuR=UEVod{GQ}}> zwrB`IksKu<@9RyfqrNmdG0pZ{IooOC!LYn#P-)^GXP3)lgt()mxbs6#!l85`1BL`~ z`k`L{(E1+f-yLbnh^rpE^J7nrN8O_UXb6bpl~v9T4VR&kH(1lQa<4Mw?3s)NXCx-| zc+`XJ`DLhu#!=at*$%#J6&$s4qdmzPXT6X6fwQ2HK|(_#nhu>k?={d4l@Kcm2yu;0uC&XaDbK5T1}#!0*ql zAofY_{TUR54>3bt0bD2QDwY#dG(Jd6&c-OEE9UKWCIAnze+{0KJew_sXSd|vJ59gl zAFHv7n?qUAng9Lh*Oo4(hKs*?fk_(W1oPd+fOk)GjrcbvvBX4<)Eo=%@pY6wlqn`J zQK=QB!Ct%9u>hk<&7eZdSzLg6Id_k!%;g$c&tuCCEilJZ+i7j10U-rhDEG^uR*|vj zP;LwDCTCY~cfiI+%unXzf$CB*fo|^B9W@|Z8Fb-nh?9QjSX1{*BTljghpg=O^=eQ9 zglpb& zfEQ}cu-BZMY`*+nFXs?zt_VXPY)Woc1J~Wf&yRGNGDJ_RfzzP21$N)s<@N09GQ0(= z(ld(M8IiiF$C8p|i0mJmvvIN|BqxZTGlyklsOB4$jhil7g7HskpsIgSV_wm8{>I#E zpKy+1ai|_Fz8swNym4hN$Z4KP;`vfD>V(^-ed<3OBfzM#R_jum3_~#!lB_}wg2c!) znf9~BYt)wTKdc;|x8-A*!`aA@O#ySCGW=6`x-?7AAbY4t!h2-K-7(tTf7!PZ%X|r( z*1DmDxF;oeM?Ba=Ud$z}bl`C6Mpw4-?zDU$)xbRtHXD9K3Plr}?$Nj7dqkhQ)%sd( z@5ht)+wjgV+R;5w{@n?-m^@0ie$~~lxmbtETD(`hc&sfF;NTx|n(EpJ%fD{ggtN`6 zLvs#C${**pYA~()xbOo4r`2TyW*0drTdVN5{*RM3r>$8Pmrf4+i9O?nKc($uyk> z==jTx!`(1WYTO+WD|y>TWPs=%$Yb&NsVW7Hh7a|Q1lou9;i}@CrX;A{ooY$!h+(mF z9N#DNbay`4Q7e%aHy~(fWOG^B_aQGpi|=*5d6 zGKz1yP8hh@AA6urN|zH>{PV~3MkGl}25G^|>H--C;JW_2I)3FI2nFEoh(FQ)5 zeF&&3EyNK|GBEy`_*Kp7>7xw~HNc7;{%)ZxrsKUOVj|jWTfG!V{9e`7hQ=~KcOXsHF$T<&p6H zt{yP;+GQokn%Tw}S&{G#Nre?;2H8oO*g^4%$50+EwOa89Hk%T} zi=+A=ko)Gr8W$2heR0kHL~nbjSphy&90>6Lp_N3aMvUM*zy0CCVJy$|ccAw#)9eLx z7j(1M(k@X{FUrHPDCEE^OAb@Q87eh9m5M9n9*aldp+jQ0;&0~ivH+&39+=loEW)>w z-{*hCNysl&?)7Jigg?NjY%Wb%qSr>uFz7kjh9KUZf*Nh>(8u4LI+^x#%omJ+!lreC znHbYsnb*4k6kg1>3`b`_WaR1&4NA`07+KYfvKaVWkPV6#I^r*TVuu96>~P-3>(~Z7 z;IBMSHlCav`AxmuwV@xl{r(C416q6q+?v2CDKwT}1)TJ7?$BpLdOAi1&tRPic^%XU zT9eJiFQn;h9WkwMcsVDjjGixeiGm5BD|}h$R=d~akIsHBw!H;g;4&VYIs+TYPPbg_ z4l_b564btxv>vp_KjA`1tg7rZu*OhqYv7b4pD5eHL;JL@i1Bz9i6{vsCpRBl;x&6F zoZ^G^TFAXQhM5*I?Sh_>%njtVkRb`?Ok^sO%R+!?ucS%Rfckl zSStDIy_Bq~1K`H_aTV_##J{dz_r$n93QKey{h@F)nfmusuKF$_IGum7$#*5$E12BD zfC2b1Rm5%`_o{d^yeg9{f41VeF6PdWi&OAvfMQ_xhE0XJ&*=i-!9dA;IDW?HrQzw- zenJv1_GM>6%6@%-J)bxnlj3ws@xZFmvhHm1jGoh_5;Vv_e@r#Fm7WrjLdfM<7~k}z zmI|%DY_-~$@BP3Q`9s;wHFFu`Z%@bI{@djRWSUeAmK&ka5=PWF><=p;`5L0GeOqPqp%Fjts88TH`nfgyrMSdDTHO%>xIQM zjcx;Og8RE8Y75~Ejzg3lzNLS#>c_*@TD>&j3hOV1eBl9fi90^W2sB~McR{<+XVcZ? zQ*m3Dr^CO>pRbY<8$BeOO5P(kPcD^@qG#aBEDnQqDdO3wCR1SLy7u;5Wu9(%X71^0 z)q@4XlYb~3=H@C(3aWDjddzpuOoTb&4|*$*-*u$Cz)V`Njh%>OSJLo#IT-P&J4%0T{lMnp}1U@R)78+);GdMejz=O>s%jOibKV$tzM!}So6X6;tm1kO*lo!nyAGiPg*!vRptQM-_LQdD^qf>;EH-vb;Tq! z;*n-No0bKE#e6q4R4r33S_df7PJG)nO~I=B6`OqJye}UC*$c>3GNu}^PS!YR zMI;v4ANUkCh?--mP@J-LYQV5{R~%%=V&_JGarrfx zbVh4SO!KdXy|X)YsJuveI%99L5v#=V^KwQ z&GM7XnrhZ6`Q$bBYrqNMuWUU(EwX^DTDxL2WLKN8%vUEqTuy zLAOJa4QwPU!*-_E;HlRB&pC+y8=+}9qDY8|6&1wVi8wvlYtEzqFhL@_q&Rsj3P`0Y zF!P9=_#ZoaWkYiU*?r@o76nxO;ATzWx@Vw_`AkWFVncE|NsdI5)Nj_&aci(v;UFo1 zZ`#>P8KrDa1;U^hU(z@gAGL4Eh%gz!|Ti{!4OtlNzE9nB|T-suney` zUeZ)6Jww<^s}th=;$NlmtNJy~9*wBVv$QS(!Op{}Ea?Hdnw@Um{~CPci6OUUZ@6=di&0DdU7d{q58_hNtv jLgc^l4gOjGC%y;dD9~drPah;J8XzVl{k80iuJ3;VMZ8tf literal 0 HcmV?d00001 From 158730e7d43eb5fc59a8f42155a1e44874892c8f Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Tue, 25 Nov 2025 14:00:05 +0100 Subject: [PATCH 04/26] Expandend informative and normative sections --- .../discovery-pairing-authentication.md | 240 +++++++++++++----- .../powerpoint_img_source.pptx | Bin 164764 -> 164772 bytes 2 files changed, 181 insertions(+), 59 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index d1d0d7a..859be06 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -67,6 +67,8 @@ For the long answer, please refer to [this page](why-not-oauth.md). # List of definitions +TODO: omzetten naar tabel + This specification uses the concepts that are defined below. **S2 node** @@ -105,6 +107,12 @@ A person or entity that manages S2 nodes. For the purpose of this specification TODO +**Pairing S2 node ID** + +**S2 Node ID** + +**Pairing code** + **Authentication token** TODO @@ -125,6 +133,9 @@ TODO TODO +**S2 pairing endpoint registry** + + # Architecture (informative) @@ -144,7 +155,7 @@ S2 Nodes can be deployed locally within the LAN, or somewhere on a server in the There are three types of S2 connections between S2 nodes possible: * **WAN-WAN**: A connection between two S2 nodes deployed in a WAN. Connecting between them is straitforward and can be done based on URLs, based on DNS domain names. It is possible to rely on common TLS certificates thanks to a public key infrastructure. -* **WAN-LAN**: A connection between a LAN deplayed S2 node a WAN deployed S2 node. Since there is almost always a firewall and/or NAT between these two, it is assumed that it is only possible to set up a connection from the LAN to the WAN; not the other way around. Connecting from the LAN S2 node to the WAN S2 node can be done based on a URL, and common TLS certificates can be used thanks to public key infrastructure. +* **WAN-LAN**: A connection between a LAN deployed S2 node a WAN deployed S2 node. Since there is almost always a firewall and/or NAT between these two, it is assumed that it is only possible to set up a connection from the LAN to the WAN; not the other way around. Connecting from the LAN S2 node to the WAN S2 node can be done based on a URL, and common TLS certificates can be used thanks to public key infrastructure. * **LAN-LAN**: A connection between two LAN deployed S2 nodes. It is assumed that in this situation we cannot rely an internet connection, making it impossible to rely on a public key infrastructure for certificates. That is why for this type of connection self-signed TLS certificates are used. Connections are made based on hostnames that are resolved to IP-adresses using Multicast DNS (mDNS), since IP-adresses are not guarenteed to be stable. Discovering another node could be done using DNS Service Discovery (DNS-SD). ## Pairing and unpairing from the perspective of the end user @@ -155,11 +166,11 @@ The first step of pairing is esteblishing a connection from the iniator S2 node * If the responder S2 node is deployed in the WAN, the URL could be retrieved through a registry. The end user would have to select the type of S2 node from a list of known s2 node services in its region. * If both S2 nodes are deployed in the LAN however, s2 nodes can be automatically be detected. The end user would have to select the S2 node from a list of automatically discovered S2 nodes. -The second step is entering the pairing token of the responder S2 node. This is a means for the end user to confirm that these two S2 nodes are allowed to send control signals through S2 to each other. The pairing token can be obtained from the responder S2 node. The pairing token is a (seemingly) random string of characters. This pairing token is typically displayed somewhere in the user interface of the other S2 node. We recommend to use a dynamic token which expires after 5 minutes. However, if the S2 node has a physical precense in the building and doesn't have user interface, there is also the option to have a static pairing token which can be printed on the device. +The second step is entering the pairing code of the responder S2 node. This is a means for the end user to confirm that these two S2 nodes are allowed to send control signals through S2 to each other. The pairing code can be obtained from the responder S2 node. The pairing code is a (seemingly) random string of characters. This pairing code is typically displayed somewhere in the user interface of the other S2 node. We recommend to use a dynamic token which expires after 5 minutes. However, if the S2 node has a physical precense in the building and doesn't have user interface, there is also the option to have a static pairing code which can be printed on the device. -Optionally, the initiator S2 node can send a signal to the responder S2 node to indicate that the end user has started the pairing process and has selected the responder S2 node. This could trigger the user interface of the responder S2 node to proactively show the pairing token (e.g. through a pop-up) to improve the user experince. +Optionally, the initiator S2 node can send a signal to the responder S2 node to indicate that the end user has started the pairing process and has selected the responder S2 node. This could trigger the user interface of the responder S2 node to proactively show the pairing code (e.g. through a pop-up) to improve the user experince. -Once the pairing token is known to the initiator S2 node, the pairing process is started. It is esteblished that both S2 nodes are compatible and it is verified that the entered pairing token is correct. Pairing could either fail or succeed. +Once the pairing code is known to the initiator S2 node, the pairing process is started. It is esteblished that both S2 nodes are compatible and it is verified that the entered pairing code is correct. Pairing could either fail or succeed. If pairing is performed sucessfully, the CEM and RM instances should esteblish a connection with each other and communicate through S2. If the connection is interrupted, the instances will automatically try to reestablish the connection. @@ -179,8 +190,8 @@ participant "Responder S2 node UI" as rui participant "Responder S2 node" as r e->iui: Provide identity of Responder S2 node (e.g. URL) -rui->e: Retrieve pairing token -e->iui: Provide pairing token +rui->e: Retrieve pairing code +e->iui: Provide pairing code i->r: Attempt pairing r->i: Pairing result (success or failure) iui->e: Pairing result (success or failure) @@ -190,7 +201,11 @@ iui->e: Pairing result (success or failure) ## The S2 node and the S2 endpoint -TODO: Begrip endpoint ergens uitleggen +Within this protocol we make a clear distinction between two types of identities: the one of the *S2 endpoint* and the one of the *S2 node* itself. + +An S2 node is an instance of a CEM or a RM. It is either a specific energy management system or service, or the representative of a physical energy flexible device, such as a heat pump or a home battery. It is typically easily recognized by the end user by its brand, device type, model name or maybe even a user given name. + +The S2 endpoint is basically the application that hosts the S2 nodes. In a LAN deployment an S2 endpoint might only host one S2 node, and these identities may seem very similar. But in a WAN deployment, an S2 endpoint could host all kinds of different types of S2 nodes. It could for example be that a certain S2 endpoint hosts RM instances for several brands of devices. Therefore an S2 endpoint needs its own identity, which can be recognized by the end user. ## Used technology for pairing and communication @@ -203,7 +218,7 @@ On of the main techologies the process relies on is HTTP REST. All interactions The first step is finding the responder S2 node from the initaitor S2 node. In principle this is done based on the URL of the responder S2 node. However, to improve user experince, two systems exist to find this URL in a more user frindely manner. For more details see [Discovery](#discovery). * If the responder S2 node is deployed in the WAN, the end user can find the endpoint through the S2 pairing endpoint registry. This would result in a list of vendors that offer S2 nodes. -* If both S2 nodes are deployed in the LAN however, the responder S2 can be detected automatically through a process based on DNS-SD. This way the user only has to select the desired S2 node to connect to from a list of S2 nodes which were discovered in the LAN. +* If both S2 nodes are deployed in the LAN however, the responder S2 can be detected automatically through a process based on DNS-SD. This way the user only has to select the desired S2 node to connect to from a list of S2 nodes which were discovered in the LAN. This process can also be used when an S2 node is deployed in the WAN, but the device also has a presence in the LAN. ### Pairing @@ -235,7 +250,7 @@ Either S2 node can take the initiative to unpair from the other S2 node. This is As explained, the pairing process is based on HTTP REST calls. That means that for every pairing attempt, one S2 node behaves as the HTTP server, and one HTTP node behaves as the pairing client. The logical solution would be to make the initiator S2 node the HTTP client and the responder S2 node the HTTP server. After all, it is the HTTP client that takes the initiative to contact the HTTP server. The HTTP server cannot take the initiative to contact the HTTP client. -The objective is to have all S2 nodes be able to be the initiator S2 node, as well as the responder S2 Node. This is necessery to provide a consistent user experience. The end user might not be aware which S2 node is deployed in the LAN or in the WAN, and then it might be confusing that, for example, his energy management system both provides S2 pairing tokens and asks for S2 pairing tokens. +The objective is to have all S2 nodes be able to be the initiator S2 node, as well as the responder S2 Node. This is necessery to provide a consistent user experience. The end user might not be aware which S2 node is deployed in the LAN or in the WAN, and then it might be confusing that, for example, his energy management system both provides S2 pairing codes and asks for S2 pairing codes. If every S2 node must be able to be the initator S2 node in certain situations, and the responder S2 node in other situations, and the easiest solution is to implement the initiator as HTTP client and the responder as HTTP server, you might come to the conclusion that every S2 node needs to be able to behave both as an HTTP server and as an HTTP client. @@ -248,36 +263,32 @@ There are however two situations where this is not possible: ![Pairing_direction](/img/communication-layer/pairing_direction.png) -## Mapping the CEM and RM to HTTP server or client -TODO update - -The CEM and RM roles defined by the S2 protocol are distinct from the Server and Client roles of the S2 pairing process. The following rules apply to determine whether the RM or CEM acts as a Client or Server in the pairing process. -* If a connection is set up between a cloud/WAN node and a local node, the cloud/WAN node must act as an S2 Server Node, and the local node must act as an S2 Client Node. -* If a connection is set up between two nodes that are similarly deployed (i.e. both in cloud/WAN, or both local), the CEM must act as an S2 Server Node, and the RM must act as an S2 Client Node. -There are four scenarios for CEM and RM deployment, and applying the rules above yields the following: -| CEM deployment | RM deployment | CEM acts as | RM acts as | -|----------------|---------------|-------------|------------| -| WAN | WAN | S2 communication server | S2 communication client | -| WAN | LAN | S2 communication server | S2 communication client | -| LAN | WAN | S2 communication client | S2 communication server | -| LAN | LAN | S2 communication server | S2 communication client | +# Pairing process (normative) -Note: A device developed solely for use as an RM in a local setup will never function as an S2 communication server. Similarly, a device designed exclusively for use as a CEM in a WAN deployment will never operate as an S2 communication client. +The pairing process is based on the trust relation that the end user has with both the CEM and the RM instances. That trust relation is out of scope for this specification and is up to CEM and RM providers to implement. +For each pairing attempt, one S2 endpoint must be the HTTP server, while the other is the HTTP client. +| Initiator S2 node | Responder S2 node | Responder is exclusively RM? | Initiator HTTP role | Responder HTTP role | WAN S2 pairing server | Remark | +| ----------------- | ----------------- | ---------------------------- | ------------------- | ------------------- | --------------------- | ------ | +| WAN | WAN | Doesn't matter | Client | Server | n/a | | +| WAN | LAN | Doesn't matter | Client | n/a | Server | The WAN S2 pairing server for LAN deployment is optional | +| LAN | WAN | Doesn't matter | Client | Server | n/a | | +| LAN | LAN | Yes | Server | Client | n/a | Through long-polling | +| LAN | LAN | No | Client | Server | n/a | | +A CEM can be paired with multiple RM's a the same time. A RM can only be paired with one CEM at a time. An S2 node is always available for pairing. When a RM that is already is paired with an CEM is paired with another CEM, the initial pairing is automatically unpaired. When a CEM and a RM are paired when they already are paired, they stay paired. -# Pairing process (normative) +TODO: OpenAPI is altijd leading -The pairing process is based on the trust relation that the end user has with both the CEM and the RM. That trust relation is out of scope for this specification and is up to CEM and RM providers to implement. Given the deployment scenario, it [follows](# Mapping the CEM and RM to S2 Server and Client Nodes) which of the RM and the CEM is server and which is the client in the pairing process. +## Discovery -A CEM can be paired with multiple RM's a the same time. A RM can only be paired with one CEM at a time. An S2 node is always available for pairing. When a RM that is already is paired with an CEM is paired with another CEM, the initial pairing is automatically unpaired. When a CEM and a RM are paired when they already are paired, they stay paired. +TODO -## Discovery In order to ease the pairing process, which is specified below, the discovery process provides a way for nodes to find each other without requiring a user to know the pairing endpoint of the other node. In other words, the discovery process is a way to provide an S2 Node with the URL of another node which is needed to start the pairing process. Alternatively, it is always possible to initiate the pairing by manually providing the URL by the end user. > NOTE: the discovery process specification is work in progress and will be updated soon. @@ -298,17 +309,42 @@ A LAN scenario where both RM and CEM are running on the same local network. Disc > NOTE: the DNS-SD service specification will be published soon -## The pairing token +## The pairing token, the pairing S2 node ID and the pairing code -TODO uitleggen geldigheidsduur en format van token +The pairing token is a random string of characters that is generated by the responder S2 node. It is a secret which is transfered by the end user to the initiator S2 node, and then is verified during the pairing process. Since there will be many cases where the end user has to manully type in the pairing token, the pairing token has to be short enough to make it easy for the end user to type in, but long enough to make it secure. The pairing token **must** be generated by a cryptographically secure pseudorandom number generator. The pairing token **must** use a rondom combination of lower case and upper case letters and numbers, and **must** contain at least 6 characters -## Pairing interaction +The pairing token is typically dynamically generated when the user requests the pairing token at the user interface of the responder S2 node. Dynamically generated pairing tokens **must** expire after a duration; five minutes is the recommended duration. However, energy flexible devices that do not have a user interface are allowed to have a static pairing token, that for example can be printed somewhere on the physical device. Static pairing tokens do not expire. Static pairing tokens **should** be longer than 6 characters. + +An S2 endpoint can host multiple S2 nodes. When attempting to pair a certain S2 node, the S2 endpoint needs to know exactly which of its S2 nodes this pairing attempt is aimed at. S2 nodes are uniquely identified with thair S2 node ID. Since this S2 node ID is a UUID, it is pretty long and cumbersome to type in. That is why an S2 endpoint can assign its nodes a `pairing S2 node ID`. This is an identifier that is intented to be short, and only unique within the context of this particular S2 endpoint. Pairing S2 node IDs could be assigned by the S2 endpoint whenever new S2 nodes are created, but also could be generated dynamically only when someone is attempting to pair to this S2 node. This way, pairing S2 node IDs have a short live, and can be reused by other S2 nodes at other moments. This allows to use shorter pairing S2 node ID's. Pairing S2 node IDs are a string of characters, which may include lower case letters, upper case letters and numbers. Pairing S2 node IDs are ideally as short as possible (at least one character), but should of course be long enough to allow the S2 endpoint to uniquely identify an S2 node. When an S2 endpoint only contains one S2 node, there is no need for a pairing S2 node ID. + +Although the pairing token and the pairing S2 node ID are two seperate strings, which are treated as completely different in the pairing process, they are presented together to the user as one string: the *pairing code*. The pairing code is simply the pairing S2 node ID, followed by a dash ('-'), followed by the pairing token. When there is no pairing S2 node ID, the pairing code is simply identical to the pairing token. + +``` +When no pairing S2 node ID is used (i.e. the S2 endpoint only contains one S2 node): + pairing code = [pairing token] +When a pairing S2 node ID is used: + pairing code = [pairing S2 node ID]-[pairing token] +``` + +The pairing code allows us to transfer two pieces of information by only bothering the end user once. Due to its format the initiator S2 node can easily excract the pairing S2 node ID and the pairing token from the pairing code by splitting the string at the dash. + +## Pre-pairing interaction + +> This section is only applicable for LAN-LAN pairing + +TODO checken/herschijven The user visits the S2ClientNodeUI and the S2ServerNode has been discovered (so the S2ServerNode base URL is known) by the the S2ClientNode per [discovery](#discovery) as specified above. The S2ClientNode does a preparePairing HTTP request to let the S2ServerNode know that there is an S2ClientNode that wants to pair. It is up to the S2ServerNode implementation to decide what to do with this signal. It can be used to display a pop-up with the pairing token in its UI to improve the user experience. It must be implemented by the client, but only when there is a clear distinction between the moment perparePairing is called and when requestPairing is called. When preparePairing is called, it is not guaranteed that a call to pairingRequest or cancelPreparePairing will follow so it is recommended to put a time-out on showing the pairing token in the S2ServerNodeUI. -The S2ServerNodeUI **can** show the S2ServerNode URL to (also) allow clients that did not discover the server to connect but it does not need to because there is a client that already knows the S2ServerNode URL and that wants to pair. +## Long-polling + +> This section is only applicable for LAN-LAN pairing -If the S2ServerNode was not discovered, the process is as follows: +TODO long-polling + +## Pairing interaction + +TODO update image ![image](/img/communication-layer/pairing-manually.png) @@ -317,41 +353,108 @@ If the S2ServerNode was not discovered, the process is as follows: ``` @startuml -participant S2ClientNodeEndpoint -participant S2ClientNodeUI -participant EndUser -participant S2ServerNodeUI -participant S2ServerNodeRestAPI +participant "HTTP Client" as Client +participant "HTTP Server" as Server + +'compatability check +Client->Server: 1. POST /requestPairing +activate Server +Server->Server: 2. Calculate clientHmacChallengeResponse +Server-->Client: 3. Response status 200 +deactivate Server + +Client->Client: 4. HTTP Client checks clientHmacChallengeResponse + +Note over Client: HTTP Client now trusts HTTP Server + +Client->Client: 5. Calculate serverHmacChallengeResponse + +alt Pairing server is Communication Server + Client->Server: 6A. POST /requestConnectionDetails + activate Server + Server->Server: 7A. HTTP Server checks serverHmacChallengeResponse + Note over Server: HTTP Server now trusts HTTP Client + Server-->Client: 8A. Response status 200 + deactivate Server +else Pairing server is Communication Client + Client->Server: 6B. POST /postConnectionDetails + activate Server + Server->Server: 7B. HTTP Server checks serverHmacChallengeResponse + Note over Server: HTTP Server now trusts HTTP Client + Server-->Client: 8B. Response status 204 + deactivate Server +end + +Client->Server++: 9. POST /finalizePairing +Server-->Client: 10. Response status 204 +@enduml +``` + -note over S2ServerNodeRestAPI, S2ServerNodeUI: S2ServerNode -note over S2ClientNodeEndpoint, S2ClientNodeUI: S2ClientNode +### 1. POST /requestPairing +TODO uitleg -EndUser->S2ServerNodeUI: 1. Request pairingInfo -note over S2ServerNodeUI: The pairing token that is part\nof the pairingInfo object expires\nafter 5 minutes -S2ServerNodeUI-->EndUser: 2. Provide pairingInfo +### 2. Calculate clientHmacChallengeResponse -EndUser->S2ClientNodeUI: 3. Provide pairingInfo +### 3. Response status 200 -S2ClientNodeEndpoint->S2ServerNodeRestAPI: 4. HTTPS requestPairing(pairingRequest) -S2ServerNodeRestAPI->S2ServerNodeRestAPI: 5. Check pairing token -S2ServerNodeRestAPI-->S2ClientNodeEndpoint: 6. HTTPS pairingResponse -@enduml -``` - +### 4. HTTP Client checks clientHmacChallengeResponse +### 5. Calculate serverHmacChallengeResponse -1. The end user visits the S2ServerNodeUI to retrieve the pairingInfo, i.e. the server URL and the pairingToken. It needs this information to enter it at the S2ClientNodeUI such that the client can initiate the pairing. -2. The S2ServerNodeUI shows the pairingInfo. A JSON object that specifies the information in the pairingInfo is defined in the [s2-over-ip-pairing OpenAPI definition](https://github.com/flexiblepower/s2-ws-json/blob/e238819dfb8bc0310be61c372294d6eaa316c51e/s2-pairing/s2-pairing-openapi-spec.yml). The schema for the pairingInfo is not associated with a REST path as this information is obtained from a User Interface and not by means of a REST call. -3. The end user submits the pairingInfo in the S2ClientNodeUI. -4. The client performs the requestPairing call as defined in the [s2-over-ip-pairing OpenAPI definition](https://github.com/flexiblepower/s2-ws-json/blob/e238819dfb8bc0310be61c372294d6eaa316c51e/s2-pairing/s2-pairing-openapi-spec.yml). -5. The server check if the pairingToken in the requestPairing call matches the token that it displays to the end user. -6. The pairingResponse informs the S2ClientNode whether the pairing was successful or not. +### 6A. POST /requestConnectionDetails -## Challenge response -TODO +### 7A. HTTP Server checks serverHmacChallengeResponse -## LAN-LAN specific extentions of the pairing process -TODO +### 8A. Response status 200 + +### 6B. POST /postConnectionDetails + +### 7B. HTTP Server checks serverHmacChallengeResponse + +### 8B. Response status 204 + +### 9. POST /finalizePairing + +### 10. Response status 204 +TODO CA certificaat opslaan + + +## Challenge response process + +This protocol uses a two-way challenge response process to verify that both S2 Nodes have the same pairing token. For this process it doesn't matter which S2 node has generated the pairing token and which S2 node has the pairing token that was entered by the end user. The reason a two-way challenge response process is used to verify the authentication token is that it allows to esteblish trust without having to expose the authentication token before this trust is estiblished. Both a challenge and a response are binary data, which are encoded using Base64. + +The challenge that is generated by the HTTP Client is called the `clientHmacChallenge`. The response to this challenge, generated by the HTTP Server, is called the `clientHmacChallengeResponse`. The challenge that is generated by the HTTP Server is called the `serverHmacChallenge`. The response to this challenge, generated by the HTTP Client, is called the `serverHmacChallengeResponse`. + +TODO minimale lengtes toevoegen + +A challenge is a nonce; a random string of bytes. It **must** be generated by a cryptographically secure pseudorandom number generator. The response is calculated based on the function described below. Both the generater of the challenge and the receiver of the challenge calculate the response based several input parameters. Since both S2 nodes should have the same input, both S2 nodes should calculate the same response. The S2 Node that received the challenge sends it back to the S2 node that generated the challenge. Now the S2 node that generated the challenge simply has to check if the received response is identical to the expected response that he calculated himself. + +The algorithm to calculate the response is based on the HMAC (hash-based message authentication code) function. This function has a *key* and a *message* as arguments. Most programming languages have a function or library availabable that provides HMAC functions. + +The HMAC function itself uses a crypographic hash function for its calcualtions. Since cryptographic hash functions might contain vulnarabilities, this protocol uses a simple crypographic hash function selection mechanism. The HTTP client sends with the requestPairing HTTP request a list of supported hash functions. In the response the HTTP server indicates which hash function it has selected from this list. This function **must** be used for all response calculations during het pairing attempt. Currently there is only one hash function available (SHA256), but other options might be added in the future. + +It order to avoid man-in-the-middle attacks when using self-signed certificates, the SHA256 fingerprint of the server TLS certificate can also be used as input for calculating the response. + +The exact function to calculate the response depends on the deployment of the S2 nodes. + +When both S2 nodes have a LAN deployment: + +```R = HMAC(C, T || F)``` + +When at least one S2 node has a WAN deployment: + +```R = HMAC(C, T)``` + +Where: +| Symbol | meaning | +| ------ | ------- | +| `R` | Response +| `HMAC` | HMAC function with the selected cryptographic hash function | +| `C` | Challenge | +| `T` | Pairing token | +| `F` | SHA256 fingerpint of the TLS server certificate of the HTTP server | +| `\|\|` | concatenation | # S2 Connection (normative) @@ -360,6 +463,25 @@ After two nodes have been paired, the nodes exchange S2 messages over a secure c The following mechanism **must** be used to initiate a secure connection between two S2 nodes. Client authentication is based on a one-time use communication token that needs to be renewed every time a new S2 session is created. +## Mapping the CEM and RM to HTTP server or client + +TODO update, veplaatsen naar normatieve gedeelte + +The CEM and RM roles defined by the S2 protocol are distinct from the Server and Client roles of the S2 pairing process. The following rules apply to determine whether the RM or CEM acts as a Client or Server in the pairing process. + +* If a connection is set up between a cloud/WAN node and a local node, the cloud/WAN node must act as an S2 Server Node, and the local node must act as an S2 Client Node. +* If a connection is set up between two nodes that are similarly deployed (i.e. both in cloud/WAN, or both local), the CEM must act as an S2 Server Node, and the RM must act as an S2 Client Node. + +There are four scenarios for CEM and RM deployment, and applying the rules above yields the following: + +| CEM deployment | RM deployment | CEM acts as | RM acts as | +|----------------|---------------|-------------|------------| +| WAN | WAN | S2 communication server | S2 communication client | +| WAN | LAN | S2 communication server | S2 communication client | +| LAN | WAN | S2 communication client | S2 communication server | +| LAN | LAN | S2 communication server | S2 communication client | + +Note: A device developed solely for use as an RM in a local setup will never function as an S2 communication server. Similarly, a device designed exclusively for use as a CEM in a WAN deployment will never operate as an S2 communication client. ## Initiation diff --git a/website/static/img/communication-layer/powerpoint_img_source.pptx b/website/static/img/communication-layer/powerpoint_img_source.pptx index 164316c9ef7ed53ae7a02b4b485e8f738fdc5576..ee15a09971cc07c39e2cdf222eeed44da3e5db7f 100644 GIT binary patch delta 12798 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 V>Gc0|{e-xE0#U#+xW7>W{||>YwQ&Fd delta 12723 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 From 4c49365ebc1979abb1fb5d3d71cea024e83138a4 Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Thu, 27 Nov 2025 14:00:30 +0100 Subject: [PATCH 05/26] Further expanded the specification --- .../discovery-pairing-authentication.md | 241 +++++++++++------- .../pairing_http_process.png | Bin 0 -> 27317 bytes 2 files changed, 142 insertions(+), 99 deletions(-) create mode 100644 website/static/img/communication-layer/pairing_http_process.png diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 859be06..bf5cf16 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -17,11 +17,12 @@ The protocol is designed to specify communication between two devices, a resourc |---|---| | CEM | Customer Energy Manager | | HTTP | HyperText Transfer Protocol | -| LAN | Local Area Network (i.e. a local network, typically contstrained to the building) | +| LAN | Local Area Network (i.e. a local network, typically constrained to the building) | | NAT | Network Address Translation | | REST | Representational state transfer | | RM | Resource Manager | -| S2 | European standard on Energy Flexibility EN50491-12-2| +| S2 | European standard on Energy Flexibility EN50491-12-2 | +| UUID | Universally Unique IDentifier (see [RFC 9562](https://www.rfc-editor.org/rfc/rfc9562)) | | WAN | Wide Area Network (i.e. the public internet) | # Background (informative) @@ -65,77 +66,32 @@ The short answer is: oAuth is mainly designed for accessing protected resources For the long answer, please refer to [this page](why-not-oauth.md). -# List of definitions +# Terms and definitions (normative) -TODO: omzetten naar tabel +TODO waar handig linkjes toevoegen naar sections met meer uitleg This specification uses the concepts that are defined below. -**S2 node** - -Refers to an instance of either a CEM or a RM as defined in EN 50491-12-1 and implementing this specification. S2 communication between two S2 nodes can only be established if one of the S2 nodes is a CEM and the other a RM. These S2 nodes must also have the same end user. - -**S2 endpoint** - -TODO - -**S2 node UI** - -A user interface through which an end user can interact with an S2 node. Interaction between the end user and the user interface must be secure, but this is out of scope for this specification. Examples of a user interface are a web interface, an app or a physical interface (HMI) on a device. - -**S2 pairing server** - -TODO An S2 node that implemented a WebSocket Secure server that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 client node. - -**S2 pairing client** - -TODO An S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 server node. - -**S2 communication server** - -TODO An S2 node that implemented a WebSocket Secure server that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 client node. - -**S2 communication client** - -TODO An S2 Node that implemented a WebSocket Secure client that can be used for establishing S2 communication between itself and another S2 node. The other S2 node must act as an S2 server node. - -**End user** - -A person or entity that manages S2 nodes. For the purpose of this specification it is assumed that there is already a trust relationship in place between this person and the S2 nodes. The means that the way the trust relationship has been established is out of scope for this specification. - -**Pairing token** - -TODO - -**Pairing S2 node ID** - -**S2 Node ID** - -**Pairing code** - -**Authentication token** - -TODO - -**Pairing attempt** - -The process of pairing two S2 nodes. The process can be completed successfully or unsuccessfully. - -**Iniator S2 Node** - -TODO - -**Responder S2 Node** - -TODO - -**End user environment** - -TODO - -**S2 pairing endpoint registry** - - +| Term | Definition | +| --- | --- | +| Authentication token | A token that is used for setting up an S2 connection. | +| End user | A person or entity that manages S2 nodes. For the purpose of this specification it is assumed that there is already a trust relationship in place between this person and the S2 nodes. The means that the way the trust relationship has been established is out of scope for this specification. | +| End user environment | An restricted area within an application that contains all the S2 nodes that belong to the end user. | +| Initiator S2 node | The S2 node that that takes the initiative to pair with a responder S2 node. This is typically the S2 node from which the user initiates the pairing process. It is the counterpart of the responder S2 node. | +| Pairing attempt | The process of pairing two S2 nodes. The process can be completed successfully or unsuccessfully. | +| Pairing code | The pairing code is the string of characters the end user has to copy from the responder S2 node user interface to the initiator S2 node user interface, in order to pair the two S2 nodes. The pairing code consist of a pairing token and when required a pairing S2 node ID. | +| Pairing S2 node ID | A short identifier for an S2 node, which is unique in the context of a single S2 endpoint. | +| Pairing token | A secret string of characters, which acts as a proof of the trust relationship between the end user and an S2 node | +| Responder S2 Node | The S2 node that that responds to a request to pair. This is the S2 node that issued the pairing code. It is the counterpart of the initiator S2 node. | +| S2 endpoint | A service which can handle pairing requests or initiates pairing requests itself. An S2 endpoint can represent on S2 node, but could also represent many. | +| S2 communication client | The S2 nodes which behaves as the HTTP client when initiating an S2 connection. | +| S2 communication server | The S2 nodes which behaves as the HTTP server when initiating an S2 connection. | +| S2 pairing client | The 2S endpoint which behaves as the HTTP client when pairing with an S2 node. | +| S2 pairing endpoint registry | The central registry that keeps track of publicly available S2 pairing servers. | +| S2 pairing server | The 2S endpoint which behaves as the HTTP client when pairing with an S2 node. | +| S2 node | Refers to an instance of either a CEM or a RM as defined in EN 50491-12-1 and implementing this specification. S2 communication between two S2 nodes can only be established if one of the S2 nodes is a CEM and the other a RM. These S2 nodes must also have the same end user. | +| S2 node ID | A globally unique identifier for an S2 node in the UUID format. | +| User interface | A user interface through which an end user can interact with an S2 node. Interaction between the end user and the user interface must be secure, but this is out of scope for this specification. Examples of a user interface are a web interface, an app or a physical interface (HMI) on a device. | # Architecture (informative) @@ -143,12 +99,12 @@ This section explains the over architecture and deployment options for CEM and R ## Deployment of S2 nodes -This specification is concerned with connecting an instance of a CEM with an instance of a RM. Either of these instances are refered to as *S2 nodes*, which either have the CEM *role* or the RM *role*. Obivously, it is only possible to pair an S2 node with the CEM role to an S2 node with the RM role. +This specification is concerned with connecting an instance of a CEM with an instance of a RM. Either of these instances are referred to as *S2 nodes*, which either have the CEM *role* or the RM *role*. Obviously, it is only possible to pair an S2 node with the CEM role to an S2 node with the RM role. -S2 Nodes can be deployed locally within the LAN, or somewhere on a server in the WAN. Although their deploymend doesn't significantly affect the working of these S2 Nodes, there are some key differences between these types of deployment. +S2 Nodes can be deployed locally within the LAN, or somewhere on a server in the WAN. Although their deployment doesn't significantly affect the working of these S2 Nodes, there are some key differences between these types of deployment. -* **WAN** S2 nodes are typically part of a large application that run on many servers and/or on some kind of cloud computing platform. A single application usually serves many users. Each user could have one or multiple S2 nodes. This could for example be a cloud-based energy management system that can connect to many devices. It could also be a cloud enviroment of a device manufacturer that hosts the RM instances in the cloud. An end user could own multiple devices from this manufacturer, thus the application could host multipse RM instances for this particual user. We call a group of S2 nodes that a single user can manage within one application an *end user environment*. It is also possible that an end user environment contains both CEM and RM instances. The user interface is typically a web interface or a smartphone app. -* **LAN** S2 nodes are typically part of an application that runs on an embedded computer device somewhere in the building. Such a device could be a physical energy management system, an energy flexible device such as a home battery, heat pump or EV charger, or a gateway device which connects to an energy flexibel device through some kind of protocol. Often an application will only host a single S2 node, but it is also possible that an application hosts multiple S2 nodes. A device could function completely on its own, but it cloud also be connected to an internet based application of the manufacturer. The user interface could be a physical human-machine interface on the device, but also be a smartphone app that connects directly to the device (e.g. via bluetooth), or a smartphone app or web interface that connects to an internet based application of the manufacturer. For energy flexible devices, it is assumed that they could also have no user interface at all, or that they are very constrained when it comes to comuting power. It is assumed that a CEM always has a user interface. +* **WAN** S2 nodes are typically part of a large application that run on many servers and/or on some kind of cloud computing platform. A single application usually serves many users. Each user could have one or multiple S2 nodes. This could for example be a cloud-based energy management system that can connect to many devices. It could also be a cloud environment of a device manufacturer that hosts the RM instances in the cloud. An end user could own multiple devices from this manufacturer, thus the application could host multiple RM instances for this particular user. We call a group of S2 nodes that a single user can manage within one application an *end user environment*. It is also possible that an end user environment contains both CEM and RM instances. The user interface is typically a web interface or a smartphone app. +* **LAN** S2 nodes are typically part of an application that runs on an embedded computer device somewhere in the building. Such a device could be a physical energy management system, an energy flexible device such as a home battery, heat pump or EV charger, or a gateway device which connects to an energy flexible device through some kind of protocol. Often an application will only host a single S2 node, but it is also possible that an application hosts multiple S2 nodes. A device could function completely on its own, but it cloud also be connected to an internet based application of the manufacturer. The user interface could be a physical human-machine interface on the device, but also be a smartphone app that connects directly to the device (e.g. via bluetooth), or a smartphone app or web interface that connects to an internet based application of the manufacturer. For energy flexible devices, it is assumed that they could also have no user interface at all, or that they are very constrained when it comes to computing power. It is assumed that a CEM always has a user interface. ![Deployment_options](/img/communication-layer/deployment_options.png) @@ -215,7 +171,7 @@ On of the main techologies the process relies on is HTTP REST. All interactions ### Discovery -The first step is finding the responder S2 node from the initaitor S2 node. In principle this is done based on the URL of the responder S2 node. However, to improve user experince, two systems exist to find this URL in a more user frindely manner. For more details see [Discovery](#discovery). +The first step is finding the responder S2 node from the initiator S2 node. In principle this is done based on the URL of the responder S2 node. However, to improve user experience, two systems exist to find this URL in a more user friendly manner. For more details see [Discovery](#discovery). * If the responder S2 node is deployed in the WAN, the end user can find the endpoint through the S2 pairing endpoint registry. This would result in a list of vendors that offer S2 nodes. * If both S2 nodes are deployed in the LAN however, the responder S2 can be detected automatically through a process based on DNS-SD. This way the user only has to select the desired S2 node to connect to from a list of S2 nodes which were discovered in the LAN. This process can also be used when an S2 node is deployed in the WAN, but the device also has a presence in the LAN. @@ -240,7 +196,7 @@ It should be noted that pairing and communication are two seperete HTTP interfac Communication interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN deployments self-signed certificates are used. -After the HTTP interaction a WebSocket is esteblished. The S2 communication server is always the WebSocket server. This server must use the same TLS certificate as the HTTP server. +After the HTTP interaction a WebSocket is established. The S2 communication server is always the WebSocket server. This server must use the same TLS certificate as the HTTP server. ### Unpairing @@ -250,16 +206,16 @@ Either S2 node can take the initiative to unpair from the other S2 node. This is As explained, the pairing process is based on HTTP REST calls. That means that for every pairing attempt, one S2 node behaves as the HTTP server, and one HTTP node behaves as the pairing client. The logical solution would be to make the initiator S2 node the HTTP client and the responder S2 node the HTTP server. After all, it is the HTTP client that takes the initiative to contact the HTTP server. The HTTP server cannot take the initiative to contact the HTTP client. -The objective is to have all S2 nodes be able to be the initiator S2 node, as well as the responder S2 Node. This is necessery to provide a consistent user experience. The end user might not be aware which S2 node is deployed in the LAN or in the WAN, and then it might be confusing that, for example, his energy management system both provides S2 pairing codes and asks for S2 pairing codes. +The objective is to have all S2 nodes be able to be the initiator S2 node, as well as the responder S2 Node. This is necessary to provide a consistent user experience. The end user might not be aware which S2 node is deployed in the LAN or in the WAN, and then it might be confusing that, for example, his energy management system both provides S2 pairing codes and asks for S2 pairing codes. -If every S2 node must be able to be the initator S2 node in certain situations, and the responder S2 node in other situations, and the easiest solution is to implement the initiator as HTTP client and the responder as HTTP server, you might come to the conclusion that every S2 node needs to be able to behave both as an HTTP server and as an HTTP client. +If every S2 node must be able to be the initiator S2 node in certain situations, and the responder S2 node in other situations, and the easiest solution is to implement the initiator as HTTP client and the responder as HTTP server, you might come to the conclusion that every S2 node needs to be able to behave both as an HTTP server and as an HTTP client. There are however two situations where this is not possible: * **WAN initiator S2 node and LAN responder S2 node**: Since the LAN is usually shielded from the WAN through a firewall or NAT, it is assumed that it is not possible to approach a LAN HTTP server from a WAN client. This specifications offers two approaches to this problem: - * Accept this limitaiton and not allow the WAN S2 node to be the initiator S2 node. Pairing can only be performed when the LAN S2 node is the initiator S2 node and the WAN S2 node is the responder S2 node. Special care must be taken to explain this to the end user. + * Accept this limitation and not allow the WAN S2 node to be the initiator S2 node. Pairing can only be performed when the LAN S2 node is the initiator S2 node and the WAN S2 node is the responder S2 node. Special care must be taken to explain this to the end user. * Many modern devices or EMS systems are connected to a cloud backend managed by the OEM. If this is the case, it is possible to implement the pairing HTTP server in the cloud, even though the S2 node itself is in the WAN. If the pairing is performed successfully in the OEM backend, the result of the pairing must be communicated to the S2 node via the existing connection between device/EMS and the OEM backend. -* **LAN initiator RM and LAN responder RM**: Since one of the requiremets is that a LAN RM instance can be implemented on restricted hardware, and a TLS enabled HTTP server is for more memory intensive than an HTTP client, there is an option to implement a LAN RM instance purely as an HTTP server. A long-polling mechanism is available to indicate to the HTTP Server that the S2 node is available for pairing. This mechanism is also used to initiate the pairing process from the HTTP server. In other words: in this specific situation the initiator S2 node behaves as the HTTP server, and the responder S2 node only has to be an HTTP client. +* **LAN initiator RM and LAN responder RM**: Since one of the requirements is that a LAN RM instance can be implemented on restricted hardware, and a TLS enabled HTTP server is for more memory intensive than an HTTP client, there is an option to implement a LAN RM instance purely as an HTTP server. A long-polling mechanism is available to indicate to the HTTP Server that the S2 node is available for pairing. This mechanism is also used to initiate the pairing process from the HTTP server. In other words: in this specific situation the initiator S2 node behaves as the HTTP server, and the responder S2 node only has to be an HTTP client. ![Pairing_direction](/img/communication-layer/pairing_direction.png) @@ -311,13 +267,13 @@ A LAN scenario where both RM and CEM are running on the same local network. Disc ## The pairing token, the pairing S2 node ID and the pairing code -The pairing token is a random string of characters that is generated by the responder S2 node. It is a secret which is transfered by the end user to the initiator S2 node, and then is verified during the pairing process. Since there will be many cases where the end user has to manully type in the pairing token, the pairing token has to be short enough to make it easy for the end user to type in, but long enough to make it secure. The pairing token **must** be generated by a cryptographically secure pseudorandom number generator. The pairing token **must** use a rondom combination of lower case and upper case letters and numbers, and **must** contain at least 6 characters +The pairing token is a random string of characters that is generated by the responder S2 node. It is a secret which is transferred by the end user to the initiator S2 node, and then is verified during the pairing process. Since there will be many cases where the end user has to manually type in the pairing token, the pairing token has to be short enough to make it easy for the end user to type in, but long enough to make it secure. The pairing token **must** be generated by a cryptographically secure pseudorandom number generator. The pairing token **must** use a random combination of lower case and upper case letters and numbers, and **must** contain at least 6 characters The pairing token is typically dynamically generated when the user requests the pairing token at the user interface of the responder S2 node. Dynamically generated pairing tokens **must** expire after a duration; five minutes is the recommended duration. However, energy flexible devices that do not have a user interface are allowed to have a static pairing token, that for example can be printed somewhere on the physical device. Static pairing tokens do not expire. Static pairing tokens **should** be longer than 6 characters. -An S2 endpoint can host multiple S2 nodes. When attempting to pair a certain S2 node, the S2 endpoint needs to know exactly which of its S2 nodes this pairing attempt is aimed at. S2 nodes are uniquely identified with thair S2 node ID. Since this S2 node ID is a UUID, it is pretty long and cumbersome to type in. That is why an S2 endpoint can assign its nodes a `pairing S2 node ID`. This is an identifier that is intented to be short, and only unique within the context of this particular S2 endpoint. Pairing S2 node IDs could be assigned by the S2 endpoint whenever new S2 nodes are created, but also could be generated dynamically only when someone is attempting to pair to this S2 node. This way, pairing S2 node IDs have a short live, and can be reused by other S2 nodes at other moments. This allows to use shorter pairing S2 node ID's. Pairing S2 node IDs are a string of characters, which may include lower case letters, upper case letters and numbers. Pairing S2 node IDs are ideally as short as possible (at least one character), but should of course be long enough to allow the S2 endpoint to uniquely identify an S2 node. When an S2 endpoint only contains one S2 node, there is no need for a pairing S2 node ID. +An S2 endpoint can host multiple S2 nodes. When attempting to pair a certain S2 node, the S2 endpoint needs to know exactly which of its S2 nodes this pairing attempt is aimed at. S2 nodes are uniquely identified with their S2 node ID. Since this S2 node ID is a UUID, it is pretty long and cumbersome to type in. That is why an S2 endpoint can assign its nodes a `pairing S2 node ID`. This is an identifier that is intended to be short, and only unique within the context of this particular S2 endpoint. Pairing S2 node IDs could be assigned by the S2 endpoint whenever new S2 nodes are created, but also could be generated dynamically only when someone is attempting to pair to this S2 node. This way, pairing S2 node IDs have a short live, and can be reused by other S2 nodes at other moments. This allows to use shorter pairing S2 node ID's. Pairing S2 node IDs are a string of characters, which may include lower case letters, upper case letters and numbers. Pairing S2 node IDs are ideally as short as possible (at least one character), but should of course be long enough to allow the S2 endpoint to uniquely identify an S2 node. When an S2 endpoint only contains one S2 node, there is no need for a pairing S2 node ID. -Although the pairing token and the pairing S2 node ID are two seperate strings, which are treated as completely different in the pairing process, they are presented together to the user as one string: the *pairing code*. The pairing code is simply the pairing S2 node ID, followed by a dash ('-'), followed by the pairing token. When there is no pairing S2 node ID, the pairing code is simply identical to the pairing token. +Although the pairing token and the pairing S2 node ID are two separate strings, which are treated as completely different in the pairing process, they are presented together to the user as one string: the *pairing code*. The pairing code is simply the pairing S2 node ID, followed by a dash ('-'), followed by the pairing token. When there is no pairing S2 node ID, the pairing code is simply identical to the pairing token. ``` When no pairing S2 node ID is used (i.e. the S2 endpoint only contains one S2 node): @@ -326,7 +282,7 @@ When a pairing S2 node ID is used: pairing code = [pairing S2 node ID]-[pairing token] ``` -The pairing code allows us to transfer two pieces of information by only bothering the end user once. Due to its format the initiator S2 node can easily excract the pairing S2 node ID and the pairing token from the pairing code by splitting the string at the dash. +The pairing code allows us to transfer two pieces of information by only bothering the end user once. Due to its format the initiator S2 node can easily extract the pairing S2 node ID and the pairing token from the pairing code by splitting the string at the dash. ## Pre-pairing interaction @@ -334,7 +290,7 @@ The pairing code allows us to transfer two pieces of information by only botheri TODO checken/herschijven -The user visits the S2ClientNodeUI and the S2ServerNode has been discovered (so the S2ServerNode base URL is known) by the the S2ClientNode per [discovery](#discovery) as specified above. The S2ClientNode does a preparePairing HTTP request to let the S2ServerNode know that there is an S2ClientNode that wants to pair. It is up to the S2ServerNode implementation to decide what to do with this signal. It can be used to display a pop-up with the pairing token in its UI to improve the user experience. It must be implemented by the client, but only when there is a clear distinction between the moment perparePairing is called and when requestPairing is called. When preparePairing is called, it is not guaranteed that a call to pairingRequest or cancelPreparePairing will follow so it is recommended to put a time-out on showing the pairing token in the S2ServerNodeUI. +The user visits the S2ClientNodeUI and the S2ServerNode has been discovered (so the S2ServerNode base URL is known) by the the S2ClientNode per [discovery](#discovery) as specified above. The S2ClientNode does a preparePairing HTTP request to let the S2ServerNode know that there is an S2ClientNode that wants to pair. It is up to the S2ServerNode implementation to decide what to do with this signal. It can be used to display a pop-up with the pairing token in its UI to improve the user experience. It must be implemented by the client, but only when there is a clear distinction between the moment preparePairing is called and when requestPairing is called. When preparePairing is called, it is not guaranteed that a call to pairingRequest or cancelPreparePairing will follow so it is recommended to put a time-out on showing the pairing token in the S2ServerNodeUI. ## Long-polling @@ -344,9 +300,9 @@ TODO long-polling ## Pairing interaction -TODO update image +The pairing process itself consists of several HTTP interactions between client and server. The image below depicts a successful pairing process between two S2 nodes. -![image](/img/communication-layer/pairing-manually.png) +![image](/img/communication-layer/pairing_http_process.png)

Image generated using the following PlantUML code: @@ -391,60 +347,147 @@ Server-->Client: 10. Response status 204 ```
+### 0. Precondition + +Before an S2 node can be paired, it needs to things. + +1. The HTTP server and the HTTP client can only start with a pairing request when they are fully initialized and have all the details of the S2 nodes it represents available. +2. Both S2 nodes must have a pairing token available. Either because they issued this token themselves, or because the end user has provided it through the user interface. + +> Note: The initiator S2 node could be the HTTP server or the HTTP client + +If the HTTP client does not fulfill these preconditions, it **cannot** send the first HTTP request of the pairing process. + ### 1. POST /requestPairing -TODO uitleg +In the first POST request the client provides the server with same information about itself. The main purpose of this is to check if these two S2 nodes are compatible. + +The client sends the following information (for full details see the OpenAPI specification file): + +| Information | Description | +| --- | --- | +| `s2ClientNodeDescription` | Information about the S2 node that wants to pair, such as brand, logo and type. Important fields include `id` (the S2 node ID) and `role` of the initiator S2 node | +| `s2ClientEndpointDescription` | Information about the client S2 endpoint. An important field is the deployment. | +| `pairingS2NodeID` | The pairing S2 node ID of the node that is being targeted (this field can be omitted if the endpoint only represents one S2 node) | +| `supportedCommunicationProtocols` | List of supported communications protocols of the client (**must** always contain WebSockets) | +| `supportedS2MessageVersions` | List of supported S2 message versions by the client | +| `supportedHmacHashingAlgorithms` | List of supported hashing algorithms for the challenge response function (currently only `SHA256` is supported and **must** be present) | +| `clientHmacChallenge` | The challenge of the client for the challenge response process (see [Challenge response process](#challenge-response-process) | +| `forcePairing` | Indicate if the S2 nodes must pair, even though they (currently) do not support the same S2 message versions (this could in the future be solved with a software update) | + +The server **must** perform the checks in the table below to make sure that it can proceed with this request. If one of these checks fail, the server should respond with an HTTP status 400 and a `PairingResponseErrorMessage`. The contents of the `additionalInfo` field is supposed the be helpful and up to the implementer. + +| Check | Type of `PairingResponseErrorMessage` when check fails | Can be ignored when `forcePairing` is true ?| +| --- | --- | --- | +| Is the request properly formatted and does it follow the schema? | `Parsing error` | No | +| Does it recognize the `pairingS2NodeID`? | `S2Node not found` | No | +| Are the S2 endpoint and S2 node ready for pairing? | `Other` | No | +| If no `pairingS2NodeID` provided, does this endpoint indeed only represent one S2 node? | `No S2Node provided` | No | +| Does the targeted S2 node have a different role than the initiator S2 node (i.e. you cannot pair two RM's or two CEM's)? | `Invalid combination of roles` | No | +| Does the server accept any of the provided hashing algorithms for the challenge response process? | `Incompatible HMAC hashing algorithms` | No | +| Is there overlap between the S2 message versions? | `Incompatible S2 message versions` | Yes | +| If the targeted S2 node on the HTTP server is the initiator S2 node, did the end user provide a valid pairing token? | `No valid pairingToken on PairingServer` | No | +| If the targeted S2 node on the HTTP server is the responder S2 node, does the S2 node have a pairing token which has not expired? | `No valid pairingToken on PairingServer` | No | + +> Note: If the S2 node that is being paired is an RM which is already paired, the pairing process proceeds. When the paring process is finished successfully the existing pairing relation must be unpaired. + +> Note: If the targeted S2 node is already paired with the initiator S2 node, the pairing process proceeds. When the paring process is finished successfully the existing pairing relation is maintained. + +> Note: This is the only step where it is checked if the pairing code has expired. If the pairing token expires after this step, but during the pairing process, the pairing process will continue. A pairing attempt is limited to 30 seconds. + +If no checks fail the server **should** proceed to the next step. ### 2. Calculate clientHmacChallengeResponse +The server selects an hashing algorithm for the challenge response function from the list that was provided by the client. This has to be a hashing algorithm that the server considers secure. The server calculates a response to the provided `clientHmacChallenge`. For details see [Challenge response process](#challenge-response-process). ### 3. Response status 200 +In order to formulate a response, the server **must** generate a `pairingAttemptId`. This is an identifier that **must** be generated by a cryptographically secure pseudorandom number generator and encoded using Base64. This identifier is used to keep track of all the HTTP interactions during the pairing attempt, and **must** be provided by the HTTP client as a header with all subsequent interactions. A pairing attempt **must** be completed within 30 seconds, or else the server **must** assume the pairing attempt has failed. + +The server responds with the following information (for full details see the OpenAPI specification file): + +| Information | Description | +| --- | --- | +| `pairingAttemptId` | The generated identifier for this pairing attempt | +| `s2ServerNodeDescription` | Information about the S2 node that is being targeted, such as brand, logo and type. Important fields include `id` (the S2 node ID) and `role` of the responder S2 node | +| `s2ServerEndpointDescription` | Information about the server S2 endpoint. An important field is the deployment. | +| `selectedHmacHashingAlgorithm` | The hashing algorithm for the challenge response function as selected in step 2 | +| `clientHmacChallengeResponse` | The response to the challenge provided by the HTTP client as calculated in step 2 | +| `serverHmacChallenge` | The challenge created by the HTTP server for the challenge response process (see [Challenge response process](#challenge-response-process).) | + +The client **must** perform the following checks of this data. + +| Check | How to proceed if check fails | +| --- | --- | +| Can the contents of the response be parsed? | Do not proceed with the pairing attempt | +| Is the response formatted according to the schema? | call `/finalizePairing` where `success` is `false` if `pairingAttemptId` is available | +| Is the role of the S2 node at the server compatible? | call `/finalizePairing` where `success` is `false` | + +If no checks fail the server **should** proceed to the next step. ### 4. HTTP Client checks clientHmacChallengeResponse +The HTTP client checks the `clientHmacChallengeResponse` provided by the HTTP server in step 3. It does that by calculating the response itself, and checking if the results is identical to the `clientHmacChallengeResponse`. + +If the result is identical, the client **should** proceed to the next step. If the result is not identical, the client **must** stop the pairing attempt. It **must** attempt to inform the HTTP server of this by doing an HTTP request to `finalizePairing` where the value of `success` must be `false`. ### 5. Calculate serverHmacChallengeResponse +The HTTP client calculates a response to the provided `serverHmacChallenge` using the hashing algorithm as indicated in the `selectedHmacHashingAlgorithm`. For details see [Challenge response process](#challenge-response-process). + +From hereon the process branches into two scenario's, depending on if the HTTP client will be the communication client or the communication server. See [Mapping the CEM and RM to communication server or client](#mapping-the-cem-and-rm-to-communication-server-or-client) for which s2 node will perform which role for communication. + +If the HTTP server will be the communication *server* steps 6A, 7A and 8A **should** follow. If the HTTP server will be the communications *client* steps 6B, 7B en 8B **should** follow. ### 6A. POST /requestConnectionDetails +TODO ### 7A. HTTP Server checks serverHmacChallengeResponse +TODO ### 8A. Response status 200 +TODO ### 6B. POST /postConnectionDetails +TODO ### 7B. HTTP Server checks serverHmacChallengeResponse +TODO ### 8B. Response status 204 +TODO ### 9. POST /finalizePairing +TODO ### 10. Response status 204 -TODO CA certificaat opslaan +TODO CA certificaat opslaan, unpairen indien al gepaired + +### Interruption of the process +TODO maximale duur van het proces is 30 seconden, als een van de twee zo lang niets hoort is de pairingattempt voorbij. ## Challenge response process -This protocol uses a two-way challenge response process to verify that both S2 Nodes have the same pairing token. For this process it doesn't matter which S2 node has generated the pairing token and which S2 node has the pairing token that was entered by the end user. The reason a two-way challenge response process is used to verify the authentication token is that it allows to esteblish trust without having to expose the authentication token before this trust is estiblished. Both a challenge and a response are binary data, which are encoded using Base64. +This protocol uses a two-way challenge response process to verify that both S2 Nodes have the same pairing token. For this process it doesn't matter which S2 node has generated the pairing token and which S2 node has the pairing token that was entered by the end user. The reason a two-way challenge response process is used to verify the authentication token is that it allows to establish trust without having to expose the authentication token before this trust is established. Both a challenge and a response are binary data, which are encoded using Base64. The challenge that is generated by the HTTP Client is called the `clientHmacChallenge`. The response to this challenge, generated by the HTTP Server, is called the `clientHmacChallengeResponse`. The challenge that is generated by the HTTP Server is called the `serverHmacChallenge`. The response to this challenge, generated by the HTTP Client, is called the `serverHmacChallengeResponse`. TODO minimale lengtes toevoegen -A challenge is a nonce; a random string of bytes. It **must** be generated by a cryptographically secure pseudorandom number generator. The response is calculated based on the function described below. Both the generater of the challenge and the receiver of the challenge calculate the response based several input parameters. Since both S2 nodes should have the same input, both S2 nodes should calculate the same response. The S2 Node that received the challenge sends it back to the S2 node that generated the challenge. Now the S2 node that generated the challenge simply has to check if the received response is identical to the expected response that he calculated himself. +A challenge is a nonce; a random string of bytes. It **must** be generated by a cryptographically secure pseudorandom number generator. The response is calculated based on the function described below. Both the generator of the challenge and the receiver of the challenge calculate the response based several input parameters. Since both S2 nodes should have the same input, both S2 nodes should calculate the same response. The S2 Node that received the challenge sends it back to the S2 node that generated the challenge. Now the S2 node that generated the challenge simply has to check if the received response is identical to the expected response that he calculated himself. -The algorithm to calculate the response is based on the HMAC (hash-based message authentication code) function. This function has a *key* and a *message* as arguments. Most programming languages have a function or library availabable that provides HMAC functions. +The algorithm to calculate the response is based on the HMAC (hash-based message authentication code) function. This function has a *key* and a *message* as arguments. Most programming languages have a function or library available that provides HMAC functions. -The HMAC function itself uses a crypographic hash function for its calcualtions. Since cryptographic hash functions might contain vulnarabilities, this protocol uses a simple crypographic hash function selection mechanism. The HTTP client sends with the requestPairing HTTP request a list of supported hash functions. In the response the HTTP server indicates which hash function it has selected from this list. This function **must** be used for all response calculations during het pairing attempt. Currently there is only one hash function available (SHA256), but other options might be added in the future. +The HMAC function itself uses a cryptographic hash function for its calculations. Since cryptographic hash functions might contain vulnerabilities, this protocol uses a simple cryptographic hash function selection mechanism. The HTTP client sends with the requestPairing HTTP request a list of supported hash functions. In the response the HTTP server indicates which hash function it has selected from this list. This function **must** be used for all response calculations during het pairing attempt. Currently there is only one hash function available (SHA256), but other options might be added in the future. It order to avoid man-in-the-middle attacks when using self-signed certificates, the SHA256 fingerprint of the server TLS certificate can also be used as input for calculating the response. The exact function to calculate the response depends on the deployment of the S2 nodes. +``` When both S2 nodes have a LAN deployment: - -```R = HMAC(C, T || F)``` + R = HMAC(C, T || F)``` When at least one S2 node has a WAN deployment: - -```R = HMAC(C, T)``` + R = HMAC(C, T) +``` Where: | Symbol | meaning | @@ -453,8 +496,8 @@ Where: | `HMAC` | HMAC function with the selected cryptographic hash function | | `C` | Challenge | | `T` | Pairing token | -| `F` | SHA256 fingerpint of the TLS server certificate of the HTTP server | -| `\|\|` | concatenation | +| `F` | SHA256 fingerprint of the TLS server certificate of the HTTP server | +| `\|\|` | Concatenation | # S2 Connection (normative) @@ -463,7 +506,7 @@ After two nodes have been paired, the nodes exchange S2 messages over a secure c The following mechanism **must** be used to initiate a secure connection between two S2 nodes. Client authentication is based on a one-time use communication token that needs to be renewed every time a new S2 session is created. -## Mapping the CEM and RM to HTTP server or client +## Mapping the CEM and RM to communication server or client TODO update, veplaatsen naar normatieve gedeelte diff --git a/website/static/img/communication-layer/pairing_http_process.png b/website/static/img/communication-layer/pairing_http_process.png new file mode 100644 index 0000000000000000000000000000000000000000..c25fe0c015c5630d809caf837712f5312dc57486 GIT binary patch literal 27317 zcmagFbyyrtzch@y6Wrb1CAho0ySux)EF`$Q1qlwpB|va@_W;4&z9si_pYzIjuW$dD zYa4dEXSQdm>sM7Bt)w7{2!{&?0s?|4EhVM`0sYI0}lL4jeQn02ncARlAM}2 zun7$f0|NsM2M33Of`s+)!p6nL!p6oXBqSy##;2m9qM)FrrY2)zVq#!mW@Dr0`grm3 z^Rx5w^9u-wNJE$jOPw$;rveDk&+6tEx(=s;a7~Y3t}H7#Jw)=;#<4 znwy$xSXyeEnwpxJSlQa@J31OUIyzcgJGr=+IypHxJ9`BNI{1IQ!a_n_f`WoVLSiDL zJffmKW8?he;zAP>6XW7i5);!C0scwJfvIVsNl8h`$yu3M5xIFWSy@@>>4mwu<%LD@ zC8dC(qN4o#in6lrW#!4`70ETV=@k_fm6c7^)vdL48Fk+?>g($p8@rpEds|xb+S&`c zySv-k2D`g|b@!C^^p*__Rt*jg_VrEt{5dl;Ts=HeGcnmPIXU_3*W&p2^5j(G)O6GQ zLfhiv;>^tE;^O||(vOwZuJw(c)z#IdrGt%))6K2Eo!!BM!{NQXz3uJuot=xrqme)7 zlP9OsfByVAKfgG?n7X;0yS~1@e^`8bUw?mpS2NPB1WpO9o4A&nsiTv(oweqrJz(FCyUV;##X|x&8Zh5Kv%0UfE}< zhK?)Dp9VKyX=Leha9L5-$elKa;sk-L{aQ2eHHD)zR`v#|p zjkF!^y)m}QXyWN$XTc`_AhYBzo9P)Dw@d94a{77zQNEE&I3@R8%k=y7*Rrq`Z;WD06xhi{$HV6a3bbjxVTlR;Q%8API3o*PH!mW5tnW7JWNxIEhQUimK-I74ev> ze}rrQ<*RF`k%~LuH;$Zf)3j1>E74j7XBAhW=2ba!op>t-n5U|$wgrXdoD+p8T&tl! zE>`CXHtE-J4*AN?b2TI+{W?sHG}{pHnN^dZ=jJ^_5IGy z!FFqn>)mRU+xx2t@1Oi#q`!x!U4pN0Es6(USXz0{B2gr(EvnE5FZ&A4vM2Vx*eZs+ zZ*7D1ULZ^kAE&t?=q!hj(6@0y8fpn9u(1}}!g*@==!3QJ{F=V7Z3S6$8HcA}OSE(o zT!v*_i5K7kJ$em8xD3|0Z`PMBO|5ByIB1xRmt`$=F+cw0T|^GP?-z^Z=_nHbrXHiq zxRGp>2LfUdE-fak=CyL#iR`8}-**4LC2c`z29+!)#i|qqChQ6=f-;AfzVnQ$a{m;~o4g6`}=Qmrmu;=n^|5(*gH2_-NZkG_7aZk4|}Eq5F*OaszM zbqF8$1Y;au7Ze$j$4hAT!fW1fZtIp$V&p?KuL$0g;k0vBQv6URU)uTY~Pd8Qbefi{Uq?#C+h3^hZ>TV?8JWlkUjs z6KXRFC<}+?&V2gBtcQ-zi!c*GoCIj;{cYHe1z|kBAHy~DhHy}iZg830SKtwb z<9Z*dm~Xt>9D7sxF6wsJb08Sz2#??WSj)yE@*OrA(SCDHAB1P|4zNyjqA1Zbh|crn zo_vzkSD!^Dj1DJ2=}A!U*PL$?9hbvC%tIhF+qRTtZfWKMsi?Rc8*|2n?)fr7eYHDQ z+?cNrT;B!8#?-uu$F;RqTbq3Jnr zXfoY%gJHSD#JIqDu)Nza+Zk?QOOD7J@)H>AHlWs8cJ@Fn3110;*90Dy&X41Pk8Gt; z6aTXAo9{967}1=xEQG89b`@;E8^`g-HKBD`vP_CP8-@9s-g9gJ zqQfX`+e8vcP`t0nfu}t$d~m-wsP(e*vnF9sgCskpwEVbzdKfry-J=)C1ffDRo~FY@ zc+nSnl0_a?2xb=OkdvXjck_Yq2kT!W6GU(Bt}pScE*zBiDKA*>-IVDBN7ZtNL;ORD z+SrXQ4WqiA#cs}oa>Qs^E#n=>yeCg~n6B+o=!ZmbUi~Sv5E>O+{a8z=QUIZ}|w*To6tpFu) zdysOFG<_3rg7V52c0pK7LQIR}rOEDSZ}Y}07_;F>@W$|TpGeR)dWwt#F1DYGUEYr0 z86|ljIeE_@&aPQ6WP~rXCzf}#hCbU8*v9*n>;&0g0a?@Vr#B81RJwQ z&+Ql~y%}-H0AqSEj0xr61F&sCLx}sg`^hnlW`wSd*=DU-C2t)142J?W!g+sl3YOYBoY!4%-dKh@u{65JnWWp&%BK~D(>s6oRjKHN` zryRddP$SujL5v4%KLJB~25;2yhjoA7m8XbKokKL!XemhBR?Y&=d)wqyB6i<_YrZ-(hUM#H! z`xo_8|bLrrfQf zRW9eWAiQ*5Pu&{SFS|wP{3sPF551q%&T{Kg6P)Wx-<_E7a1eUSa$XYoeAG{5^K)=+ zWQL4IJKg5J&=n%9|0~GWYMDBG8%g-PS}>^^?=bb<7ZJG&5@p9EY*Fokb|o&uy;Qy}<&*?LK`9}c{L+MwE~URaC1L*NMhbvTtsVUq z`4|6dx!Q10ib$!CcQ*#Ebnr>xav5jPD&jtcr)`ceQ^#=c-N979<%K}x7L0Hqq^c%a z&n8lU;7Ajzzq_zJ#d73I&hEuIdG-8RduIidfGRD|jNl%qO;3g%n34jmN+OtFf%S#= z@dy_7a`9cW5V}`lIBew2_4Av>os4h<2mM&EjEFj*(HkeXVZm@7Zx7NV%~hXA7IdNm zADPRdtttL}SS^N*tJ4gAX}#{77lWB0h}Oe~PWo?UJX|i<)C9j8qoK^XvJ%FGY)|yZ zQ&(eB-L_+lRsVhZZ$-Tz$l%)Kyo%LCejUUccYTIU*o>2}#s)R*JzOO)RI0c$HM3PH zTk<<@hz3%u23Hp?w|=7X^M|%$Zx^pX8ii>f)`rol-nF?;za27Tu3c&o{G~>%6=8iZ z0%gSF%viL#i~BC-t#Ohel*4{KJ(3mr4|Fu^ny7^&vr^x>m#;=HGTQ}=RS zT_E@D9noXE>00#_242G&t#5(UX2t4H@Zvx$Dv?rSW2A2WQaHSdJstGX2#(w285H1DpbVZ9$Pn_bU`@Wa+h4MxMkhCpg<&w z23yd**2rB7t=^O%miiX_!G3^GOkp7m+7BZr(x{9&K=8kB|7l;GkoOpli)DRAhDgD^ z3-Ir&dG>al&;1=3uaO6;;%1sX9pC&53+rh$YbET6c181PM(TTERa&4kP4X=>^t5NIxQvDv)tL;4NXDj2UFQ-d2!MN?F!R|w}Mbt!&t3Q1)t{pJdJ z?Imk`m?ZlwQ7X0r5AlqIs*VD!=;0;1fj`r<9C(qNuC*^%4MOB!y}{Gaw#!xgb5qWO zPI**fU^TnoOHWdk`753@nq*Un(k)(vIA(9snfZ(Jx}~(gs4k|J#3%mpBoHRJqa#tc zt7^DYsp&W-h;uv+hbfx7s9*f(VkAVd@a(T89_x`fw9Me97@3He2HJZ-)WWTTkD*|| zi}5raIi#?{sF^R?(&BaPkqTIs_B*4Sk}eHjsCjzN`3?UP
lYCj;PHk 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 Date: Mon, 1 Dec 2025 09:54:02 +0100 Subject: [PATCH 06/26] Minor improvements to the spec --- .../discovery-pairing-authentication.md | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index bf5cf16..1e19b2d 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -45,7 +45,7 @@ TODO cleanup - Communication **MUST** work without additional firewall configuration by the end user. - Implementation of the communication layer **MUST** be based on a widely accepted technology and must be relatively easy to implement. - The pairing process **SHOULD** support extensibility for other application layer communication protocols. -- Provide a relatively consistent user experince regardless of the deployment of the S2 node +- Provide a relatively consistent user experience regardless of the deployment of the S2 node - Run a local RM on a device with constrained hardware - A RM could not have a UI @@ -110,25 +110,25 @@ S2 Nodes can be deployed locally within the LAN, or somewhere on a server in the There are three types of S2 connections between S2 nodes possible: -* **WAN-WAN**: A connection between two S2 nodes deployed in a WAN. Connecting between them is straitforward and can be done based on URLs, based on DNS domain names. It is possible to rely on common TLS certificates thanks to a public key infrastructure. +* **WAN-WAN**: A connection between two S2 nodes deployed in a WAN. Connecting between them is straightforward and can be done based on URLs, based on DNS domain names. It is possible to rely on common TLS certificates thanks to a public key infrastructure. * **WAN-LAN**: A connection between a LAN deployed S2 node a WAN deployed S2 node. Since there is almost always a firewall and/or NAT between these two, it is assumed that it is only possible to set up a connection from the LAN to the WAN; not the other way around. Connecting from the LAN S2 node to the WAN S2 node can be done based on a URL, and common TLS certificates can be used thanks to public key infrastructure. -* **LAN-LAN**: A connection between two LAN deployed S2 nodes. It is assumed that in this situation we cannot rely an internet connection, making it impossible to rely on a public key infrastructure for certificates. That is why for this type of connection self-signed TLS certificates are used. Connections are made based on hostnames that are resolved to IP-adresses using Multicast DNS (mDNS), since IP-adresses are not guarenteed to be stable. Discovering another node could be done using DNS Service Discovery (DNS-SD). +* **LAN-LAN**: A connection between two LAN deployed S2 nodes. It is assumed that in this situation we cannot rely an internet connection, making it impossible to rely on a public key infrastructure for certificates. That is why for this type of connection self-signed TLS certificates are used. Connections are made based on hostnames that are resolved to IP-addresses using Multicast DNS (mDNS), since IP-addresses are not guaranteed to be stable. Discovering another node could be done using DNS Service Discovery (DNS-SD). ## Pairing and unpairing from the perspective of the end user -The end user can take the initiative to *pair* a single CEM instance with a single RM instance. This process has to be started with one of the S2 nodes. Which node this is depends on the deployment and implementation decisions of the S2 node, but ideally it could be either one. The S2 node however needs to have a user interface. We'll call the S2 node that user uses to start the pairing proces the *initator*. We'll call the other S2 node the *responder*. +The end user can take the initiative to *pair* a single CEM instance with a single RM instance. This process has to be started with one of the S2 nodes. Which node this is depends on the deployment and implementation decisions of the S2 node, but ideally it could be either one. The S2 node however needs to have a user interface. We'll call the S2 node that user uses to start the pairing process the *initiator*. We'll call the other S2 node the *responder*. -The first step of pairing is esteblishing a connection from the iniator S2 node to the responder S2 node. This can be done in serveral ways: +The first step of pairing is establishing a connection from the initiator S2 node to the responder S2 node. This can be done in several ways: * Enter the address manually. * If the responder S2 node is deployed in the WAN, the URL could be retrieved through a registry. The end user would have to select the type of S2 node from a list of known s2 node services in its region. * If both S2 nodes are deployed in the LAN however, s2 nodes can be automatically be detected. The end user would have to select the S2 node from a list of automatically discovered S2 nodes. -The second step is entering the pairing code of the responder S2 node. This is a means for the end user to confirm that these two S2 nodes are allowed to send control signals through S2 to each other. The pairing code can be obtained from the responder S2 node. The pairing code is a (seemingly) random string of characters. This pairing code is typically displayed somewhere in the user interface of the other S2 node. We recommend to use a dynamic token which expires after 5 minutes. However, if the S2 node has a physical precense in the building and doesn't have user interface, there is also the option to have a static pairing code which can be printed on the device. +The second step is entering the pairing code of the responder S2 node. This is a means for the end user to confirm that these two S2 nodes are allowed to send control signals through S2 to each other. The pairing code can be obtained from the responder S2 node. The pairing code is a (seemingly) random string of characters. This pairing code is typically displayed somewhere in the user interface of the other S2 node. We recommend to use a dynamic token which expires after 5 minutes. However, if the S2 node has a physical presence in the building and doesn't have user interface, there is also the option to have a static pairing code which can be printed on the device. -Optionally, the initiator S2 node can send a signal to the responder S2 node to indicate that the end user has started the pairing process and has selected the responder S2 node. This could trigger the user interface of the responder S2 node to proactively show the pairing code (e.g. through a pop-up) to improve the user experince. +Optionally, the initiator S2 node can send a signal to the responder S2 node to indicate that the end user has started the pairing process and has selected the responder S2 node. This could trigger the user interface of the responder S2 node to proactively show the pairing code (e.g. through a pop-up) to improve the user experience. -Once the pairing code is known to the initiator S2 node, the pairing process is started. It is esteblished that both S2 nodes are compatible and it is verified that the entered pairing code is correct. Pairing could either fail or succeed. +Once the pairing code is known to the initiator S2 node, the pairing process is started. It is established that both S2 nodes are compatible and it is verified that the entered pairing code is correct. Pairing could either fail or succeed. -If pairing is performed sucessfully, the CEM and RM instances should esteblish a connection with each other and communicate through S2. If the connection is interrupted, the instances will automatically try to reestablish the connection. +If pairing is performed successfully, the CEM and RM instances should establish a connection with each other and communicate through S2. If the connection is interrupted, the instances will automatically try to reestablish the connection. Once a CEM is paired, the user has to possibility to command either of the S2 nodes to *unpair*. After unpairing the CEM and RM instances can no longer communicate through S2 (unless the end user pairs them again). @@ -167,7 +167,7 @@ The S2 endpoint is basically the application that hosts the S2 nodes. In a LAN d An S2 connection basically consists of four steps: discovery, pairing, communication and unpairing. For these steps different types of technology are used. -On of the main techologies the process relies on is HTTP REST. All interactions based on HTTP are formally described in OpenAPI specification files. [OpenAPI](https://swagger.io/specification/) is a formal language for specifying HTTP based API's. It can be used to generate reference documentation for developers, as well as stub code for many programming languages. +On of the main technologies the process relies on is HTTP REST. All interactions based on HTTP are formally described in OpenAPI specification files. [OpenAPI](https://swagger.io/specification/) is a formal language for specifying HTTP based API's. It can be used to generate reference documentation for developers, as well as stub code for many programming languages. ### Discovery @@ -192,9 +192,9 @@ The process always starts with HTTP based communication, but then is handed over We'll refer to the endpoint that behaves as the HTTP server during the communication process as the *S2 communication server*, and the client as the *S2 communication client*. -It should be noted that pairing and communication are two seperete HTTP interfaces, that don't have to be used in the same way. It could be that an S2 Node is an S2 pairing client, but then becomes an S2 communication server. This depends on the deployment of the s2 Nodes (see [Pairing details for different deployments](#pairing-details-for-different-deployments)). +It should be noted that pairing and communication are two separate HTTP interfaces, that don't have to be used in the same way. It could be that an S2 Node is an S2 pairing client, but then becomes an S2 communication server. This depends on the deployment of the s2 Nodes (see [Pairing details for different deployments](#pairing-details-for-different-deployments)). -Communication interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN deployments self-signed certificates are used. +Communication interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN-LAN deployments self-signed certificates are used. After the HTTP interaction a WebSocket is established. The S2 communication server is always the WebSocket server. This server must use the same TLS certificate as the HTTP server. @@ -241,6 +241,10 @@ A CEM can be paired with multiple RM's a the same time. A RM can only be paired TODO: OpenAPI is altijd leading +## Addressing S2 nodes + +TODO DNS names WAN, mDNS hostname LAN + ## Discovery TODO @@ -267,7 +271,7 @@ A LAN scenario where both RM and CEM are running on the same local network. Disc ## The pairing token, the pairing S2 node ID and the pairing code -The pairing token is a random string of characters that is generated by the responder S2 node. It is a secret which is transferred by the end user to the initiator S2 node, and then is verified during the pairing process. Since there will be many cases where the end user has to manually type in the pairing token, the pairing token has to be short enough to make it easy for the end user to type in, but long enough to make it secure. The pairing token **must** be generated by a cryptographically secure pseudorandom number generator. The pairing token **must** use a random combination of lower case and upper case letters and numbers, and **must** contain at least 6 characters +The pairing token is a random string of characters that is generated by the responder S2 node. It is a secret which is transferred by the end user to the initiator S2 node, and then is verified during the pairing process. Since there will be many cases where the end user has to manually type in the pairing token, the pairing token has to be short enough to make it easy for the end user to type in, but long enough to make it secure. The pairing token **must** be generated by a cryptographically secure pseudorandom number generator. The pairing token **must** be random binary data with a length of at least 9 bytes, and is encoded using Base64 before it is presented to the end user (9 bytes is equal to 12 characters when encoded with Base64). The pairing token is typically dynamically generated when the user requests the pairing token at the user interface of the responder S2 node. Dynamically generated pairing tokens **must** expire after a duration; five minutes is the recommended duration. However, energy flexible devices that do not have a user interface are allowed to have a static pairing token, that for example can be printed somewhere on the physical device. Static pairing tokens do not expire. Static pairing tokens **should** be longer than 6 characters. @@ -277,9 +281,9 @@ Although the pairing token and the pairing S2 node ID are two separate strings, ``` When no pairing S2 node ID is used (i.e. the S2 endpoint only contains one S2 node): - pairing code = [pairing token] + [pairing code] = [pairing token] When a pairing S2 node ID is used: - pairing code = [pairing S2 node ID]-[pairing token] + [pairing code] = [pairing S2 node ID]-[pairing token] ``` The pairing code allows us to transfer two pieces of information by only bothering the end user once. Due to its format the initiator S2 node can easily extract the pairing S2 node ID and the pairing token from the pairing code by splitting the string at the dash. @@ -465,7 +469,7 @@ TODO maximale duur van het proces is 30 seconden, als een van de twee zo lang ni ## Challenge response process -This protocol uses a two-way challenge response process to verify that both S2 Nodes have the same pairing token. For this process it doesn't matter which S2 node has generated the pairing token and which S2 node has the pairing token that was entered by the end user. The reason a two-way challenge response process is used to verify the authentication token is that it allows to establish trust without having to expose the authentication token before this trust is established. Both a challenge and a response are binary data, which are encoded using Base64. +This protocol uses a two-way challenge response process to verify that both S2 Nodes have the same pairing token. For this process it doesn't matter which S2 node has generated the pairing token and which S2 node has the pairing token that was entered by the end user. The reason a two-way challenge response process is used to verify the pairing token is that it allows to establish trust without having to expose the pairing token before this trust is established. Both a challenge and a response are binary data, which are encoded using Base64. The challenge that is generated by the HTTP Client is called the `clientHmacChallenge`. The response to this challenge, generated by the HTTP Server, is called the `clientHmacChallengeResponse`. The challenge that is generated by the HTTP Server is called the `serverHmacChallenge`. The response to this challenge, generated by the HTTP Client, is called the `serverHmacChallengeResponse`. @@ -477,13 +481,15 @@ The algorithm to calculate the response is based on the HMAC (hash-based message The HMAC function itself uses a cryptographic hash function for its calculations. Since cryptographic hash functions might contain vulnerabilities, this protocol uses a simple cryptographic hash function selection mechanism. The HTTP client sends with the requestPairing HTTP request a list of supported hash functions. In the response the HTTP server indicates which hash function it has selected from this list. This function **must** be used for all response calculations during het pairing attempt. Currently there is only one hash function available (SHA256), but other options might be added in the future. -It order to avoid man-in-the-middle attacks when using self-signed certificates, the SHA256 fingerprint of the server TLS certificate can also be used as input for calculating the response. +It order to avoid man-in-the-middle attacks when using self-signed certificates, the SHA256 fingerprint of the TLS *server certificate* can also be used as input for calculating the response. + +> Note that the pairing token is encoded using Base64, so it must also be decoded using Base64 before it can be used in the challenge response function. The exact function to calculate the response depends on the deployment of the S2 nodes. ``` When both S2 nodes have a LAN deployment: - R = HMAC(C, T || F)``` + R = HMAC(C, T || F) When at least one S2 node has a WAN deployment: R = HMAC(C, T) From b4591d6f52075d249820f0c8a7eec5d9ed9a6ded Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Tue, 2 Dec 2025 14:16:31 +0100 Subject: [PATCH 07/26] Further expandend the specification --- .../discovery-pairing-authentication.md | 249 ++++++++++++------ .../pairing_http_process.png | Bin 27317 -> 33134 bytes 2 files changed, 170 insertions(+), 79 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 1e19b2d..ec2aa9e 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -16,8 +16,11 @@ The protocol is designed to specify communication between two devices, a resourc |Abbreviation | Meaning |---|---| | CEM | Customer Energy Manager | +| DNS | Domain Name System | +| DNS-SD | DNS Service Discovery | | HTTP | HyperText Transfer Protocol | | LAN | Local Area Network (i.e. a local network, typically constrained to the building) | +| mDNS | Multicast DNS | | NAT | Network Address Translation | | REST | Representational state transfer | | RM | Resource Manager | @@ -74,7 +77,7 @@ This specification uses the concepts that are defined below. | Term | Definition | | --- | --- | -| Authentication token | A token that is used for setting up an S2 connection. | +| Access token | A token that is used for setting up an S2 connection. It random binary data and must be generated by a cryptographically secure pseudorandom number generator and have a minimum length of 32 bytes. It is encoded using Base64. | | End user | A person or entity that manages S2 nodes. For the purpose of this specification it is assumed that there is already a trust relationship in place between this person and the S2 nodes. The means that the way the trust relationship has been established is out of scope for this specification. | | End user environment | An restricted area within an application that contains all the S2 nodes that belong to the end user. | | Initiator S2 node | The S2 node that that takes the initiative to pair with a responder S2 node. This is typically the S2 node from which the user initiates the pairing process. It is the counterpart of the responder S2 node. | @@ -83,7 +86,7 @@ This specification uses the concepts that are defined below. | Pairing S2 node ID | A short identifier for an S2 node, which is unique in the context of a single S2 endpoint. | | Pairing token | A secret string of characters, which acts as a proof of the trust relationship between the end user and an S2 node | | Responder S2 Node | The S2 node that that responds to a request to pair. This is the S2 node that issued the pairing code. It is the counterpart of the initiator S2 node. | -| S2 endpoint | A service which can handle pairing requests or initiates pairing requests itself. An S2 endpoint can represent on S2 node, but could also represent many. | +| S2 endpoint | A service which can handle pairing requests or initiates pairing requests itself. An S2 endpoint can represent one S2 node, but could also represent many. | | S2 communication client | The S2 nodes which behaves as the HTTP client when initiating an S2 connection. | | S2 communication server | The S2 nodes which behaves as the HTTP server when initiating an S2 connection. | | S2 pairing client | The 2S endpoint which behaves as the HTTP client when pairing with an S2 node. | @@ -178,7 +181,7 @@ The first step is finding the responder S2 node from the initiator S2 node. In p ### Pairing -The pairing process itself is completely based on HTTP rest. One S2 node behaves as the HTTP server, and the other as the HTTP client. This process is described in an OpenAPI file. The process consists of multiple steps. If the pairing process is completed successfully, the S2 nodes will agree on an authentication token. This token is used to initiate communication or to unpair. +The pairing process itself is completely based on HTTP rest. One S2 node behaves as the HTTP server, and the other as the HTTP client. This process is described in an OpenAPI file. The process consists of multiple steps. If the pairing process is completed successfully, the S2 nodes will agree on an access token. This token is used to initiate communication or to unpair. We'll refer to the endpoint that behaves as the HTTP server during the pairing process as the *S2 pairing server*, and the client as the *S2 pairing client*. @@ -220,8 +223,35 @@ There are however two situations where this is not possible: ![Pairing_direction](/img/communication-layer/pairing_direction.png) +# Formal specification and versioning (normative) +This document servers as an overall specification of the S2 over IP protocol. However, where possible, the protocol has been specified in a formal specification language in order to minimize possible interpretation and allow tooling to assist the implementation of the specification. Since many details are better described in these formal specification files, they are not described in this document. Where the formal specification files and this document overlap, the formal specification file is leading. +| Part of specification | Description | Specification format | +| --- | --- | --- | +| S2 pairing API | HTTP based interaction to pair two S2 nodes | OpenAPI file | +| S2 connection API | HTTP based interaction set up a communication channel for S2 messages between two S2 nodes | OpenAPI file | +| S2 message structure | The types of S2 messages that can be exchanges between S2 nodes | JSON schema files | + +## Versioning of OpenAPI files +The S2 pairing API and the S2 connection API are formally defined as an OpenAPI file. To accommodate future changes to these APIs, the OpenAPI files are versioned. Versioning is done using a `major.minor` scheme. + +The minor version is increased when backwards compatible changes are made. Be aware that we consider adding items to certain lists of enums (e.g. the list of supported hash functions) backwards compatible. Other examples of backwards compatible changes are additional properties of JSON files or added operations. + +The major version is increased when non-backwards compatible changes are made. + +The major version of the API is embedded in the base URI of the API as `/v[major]` (e.g. `/v1`). HTTP server and HTTP clients can decide to implement several major version of the API in parallel to increase interoperability. In that case server must server all version on the same base URI (e.g. `https://hostname.local/pairing/v1/...` and `https://hostname.local/pairing/v2/...`). The server **must** always (even when it only supports one major version of the API) serve an index (e.g. `https://hostname.local/pairing/`) which returns a JSON array with all supported versions as they are defined as port of the URI (e.g. `["v1", "v2"]`). + +## Versioning of S2 message schemas + +TODO + +## Addressing S2 endpoints (normative) +The URI of the pairing and connection API are used in the discovery process, pairing process and connection process, as wel as the basis for TLS certificates. + +For WAN deployed S2 endpoints, the URI **must** be based on a DNS domain name. + +For LAN deployed S2 endpoint, the URI **must** be based on an mDNS alias or hostname (e.g. `hostname.local`). It is important that these names are *unique* and *stable*. Unique since there could be multiple instance within the same LAN, and stable because if it changes, the S2 endpoint cannot be found by other S2 endpoints. It should also be noted that the alias used by DNS-SD, and is presented to the end user. It recommended to choose a name that the end user should recognize and an element for the end user to make a distinction between two devices of the same type, such as a serial number. # Pairing process (normative) @@ -239,12 +269,6 @@ For each pairing attempt, one S2 endpoint must be the HTTP server, while the oth A CEM can be paired with multiple RM's a the same time. A RM can only be paired with one CEM at a time. An S2 node is always available for pairing. When a RM that is already is paired with an CEM is paired with another CEM, the initial pairing is automatically unpaired. When a CEM and a RM are paired when they already are paired, they stay paired. -TODO: OpenAPI is altijd leading - -## Addressing S2 nodes - -TODO DNS names WAN, mDNS hostname LAN - ## Discovery TODO @@ -288,6 +312,48 @@ When a pairing S2 node ID is used: The pairing code allows us to transfer two pieces of information by only bothering the end user once. Due to its format the initiator S2 node can easily extract the pairing S2 node ID and the pairing token from the pairing code by splitting the string at the dash. +## Self-signed certificates +If the HTTP server is deployed in the LAN, the HTTP server will use a self-signed certificate. This means that the HTTP client **must** be configured to accept self-signed certificates during the pairing process. Since the pairing process consists out of several HTTP requests, the HTTP client **must** check that for every request the same self-signed certificate is used by the HTTP server. If this is not the case, the HTTP client **cannot** proceed with the request. + +## Challenge response process + +This protocol uses a two-way challenge response process to verify that both S2 nodes have the same pairing token. For this process it doesn't matter which S2 node has issued the pairing token and which S2 node has the pairing token that was entered by the end user. The reason a two-way challenge response process is used to verify the pairing token is that it allows to establish trust without having to expose the pairing token. Both a challenge and a response are binary data, which are encoded using Base64. + +The challenge that is generated by the HTTP Client is called the `clientHmacChallenge`. The response to this challenge, generated by the HTTP Server, is called the `clientHmacChallengeResponse`. The challenge that is generated by the HTTP Server is called the `serverHmacChallenge`. The response to this challenge, generated by the HTTP Client, is called the `serverHmacChallengeResponse`. + +TODO minimale lengtes toevoegen + +A challenge is a nonce; a random binary data. It **must** be generated by a cryptographically secure pseudorandom number generator. The response is calculated based on the function described below. Both the generator of the challenge and the receiver of the challenge calculate the response based several input parameters. Since both S2 nodes should have the same input, both S2 nodes should calculate the same response. The S2 Node that received the challenge sends it back to the S2 node that generated the challenge. Now the S2 node that generated the challenge simply has to check if the received response is identical to the expected response that he calculated himself. + +The algorithm to calculate the response is based on the HMAC (hash-based message authentication code) function. This function has a *key* and a *message* as arguments. Most programming languages have a function or library available that provides HMAC functions. + +The HMAC function itself uses a cryptographic hash function for its calculations. Since cryptographic hash functions might contain vulnerabilities, this protocol uses a simple selection mechanism for the cryptographic hash function. The HTTP client sends with the requestPairing HTTP request a list of supported hash functions. In the response the HTTP server indicates which hash function it has selected from this list. This function **must** be used for all response calculations during het pairing attempt. Currently there is only one hash function available (SHA256), but other options might be added in the future. + +It order to avoid man-in-the-middle attacks when using self-signed certificates, the SHA256 fingerprint of the TLS *server certificate* can also be used as input for calculating the response. + +> Note that the pairing token is encoded using Base64, so it must also be decoded using Base64 before it can be used in the challenge response function. + +The exact function to calculate the response depends on the deployment of the S2 nodes. + +``` +When both S2 nodes have a LAN deployment: + R = HMAC(C, T || F) + +When at least one S2 node has a WAN deployment: + R = HMAC(C, T) +``` + +Where: +| Symbol | meaning | +| ------ | ------- | +| `R` | Response +| `HMAC` | HMAC function with the selected cryptographic hash function | +| `C` | Challenge | +| `T` | Pairing token | +| `F` | SHA256 fingerprint of the TLS server certificate of the HTTP server | +| `\|\|` | Concatenation | + + ## Pre-pairing interaction > This section is only applicable for LAN-LAN pairing @@ -316,37 +382,47 @@ The pairing process itself consists of several HTTP interactions between client participant "HTTP Client" as Client participant "HTTP Server" as Server -'compatability check -Client->Server: 1. POST /requestPairing +'select version of pairing API +Client->Server++: 1. GET / (index containing pairing API versions) +Server-->Client: 2. Response status 200 +deactivate Server +Client->Client: 3. Decide pairing version + +'compatibility check +Client->Server: 4. POST /[version]/requestPairing activate Server -Server->Server: 2. Calculate clientHmacChallengeResponse -Server-->Client: 3. Response status 200 +Server->Server: 5. Calculate clientHmacChallengeResponse +Server-->Client: 6. Response status 200 deactivate Server -Client->Client: 4. HTTP Client checks clientHmacChallengeResponse +Client->Client: 7. HTTP Client checks clientHmacChallengeResponse Note over Client: HTTP Client now trusts HTTP Server -Client->Client: 5. Calculate serverHmacChallengeResponse +Client->Client: 8. Calculate serverHmacChallengeResponse alt Pairing server is Communication Server - Client->Server: 6A. POST /requestConnectionDetails + Client->Server: 9A. POST /[version]/requestConnectionDetails activate Server - Server->Server: 7A. HTTP Server checks serverHmacChallengeResponse + Server->Server: 10A. HTTP Server checks serverHmacChallengeResponse Note over Server: HTTP Server now trusts HTTP Client - Server-->Client: 8A. Response status 200 + Server-->Client: 11A. Response status 200 deactivate Server else Pairing server is Communication Client - Client->Server: 6B. POST /postConnectionDetails + Client->Server: 9B. POST /[version]/postConnectionDetails activate Server - Server->Server: 7B. HTTP Server checks serverHmacChallengeResponse + Server->Server: 10B. HTTP Server checks serverHmacChallengeResponse Note over Server: HTTP Server now trusts HTTP Client - Server-->Client: 8B. Response status 204 + Server-->Client: 11B. Response status 204 deactivate Server end -Client->Server++: 9. POST /finalizePairing -Server-->Client: 10. Response status 204 +Client->Server++: 12. POST /[version]/finalizePairing +Server-->Client: 13. Response status 204 +deactivate Server + +Note over Client, Server: Pairing finalized + @enduml ``` @@ -356,13 +432,25 @@ Server-->Client: 10. Response status 204 Before an S2 node can be paired, it needs to things. 1. The HTTP server and the HTTP client can only start with a pairing request when they are fully initialized and have all the details of the S2 nodes it represents available. -2. Both S2 nodes must have a pairing token available. Either because they issued this token themselves, or because the end user has provided it through the user interface. +2. The HTTP client must have the base URL of the pairing API (e.g. `https://hostname.local/pairing/`) +3. Both S2 nodes must have a pairing token available. Either because they issued this token themselves, or because the end user has provided it through the user interface. > Note: The initiator S2 node could be the HTTP server or the HTTP client If the HTTP client does not fulfill these preconditions, it **cannot** send the first HTTP request of the pairing process. -### 1. POST /requestPairing +### 1. GET / (index containing pairing API versions) +Since the HTTP client does not know which major versions of the pairing API are implemented by the server, it must first do a GET request to the index (e.g. `https://hostname.local/pairing/`). + +### 2. Response status 200 +The server responds with a list of implement major versions of the pairing API. It is formatted as a JSON array contains all the supported version of the pairing API (e.g. `["v1"]`). + +If the HTTP client does not support any of the provided versions, it means that the two S2 endpoints are not compatible, and that pairing is not possible. + +### 3. Decide pairing version +From the provided list of major versions of the pairing API, the HTTP client must select one that is implement by the HTTP client itself (typically the highest supported version). + +### 4. POST /[version]/requestPairing In the first POST request the client provides the server with same information about itself. The main purpose of this is to check if these two S2 nodes are compatible. The client sends the following information (for full details see the OpenAPI specification file): @@ -400,10 +488,10 @@ The server **must** perform the checks in the table below to make sure that it c If no checks fail the server **should** proceed to the next step. -### 2. Calculate clientHmacChallengeResponse +### 5. Calculate clientHmacChallengeResponse The server selects an hashing algorithm for the challenge response function from the list that was provided by the client. This has to be a hashing algorithm that the server considers secure. The server calculates a response to the provided `clientHmacChallenge`. For details see [Challenge response process](#challenge-response-process). -### 3. Response status 200 +### 6. Response status 200 In order to formulate a response, the server **must** generate a `pairingAttemptId`. This is an identifier that **must** be generated by a cryptographically secure pseudorandom number generator and encoded using Base64. This identifier is used to keep track of all the HTTP interactions during the pairing attempt, and **must** be provided by the HTTP client as a header with all subsequent interactions. A pairing attempt **must** be completed within 30 seconds, or else the server **must** assume the pairing attempt has failed. The server responds with the following information (for full details see the OpenAPI specification file): @@ -427,84 +515,89 @@ The client **must** perform the following checks of this data. If no checks fail the server **should** proceed to the next step. -### 4. HTTP Client checks clientHmacChallengeResponse -The HTTP client checks the `clientHmacChallengeResponse` provided by the HTTP server in step 3. It does that by calculating the response itself, and checking if the results is identical to the `clientHmacChallengeResponse`. +### 7. HTTP Client checks clientHmacChallengeResponse +The HTTP client checks the `clientHmacChallengeResponse` provided by the HTTP server in step 6. It does that by calculating the response itself, and checking if the results is identical to the `clientHmacChallengeResponse`. If the result is identical, the client **should** proceed to the next step. If the result is not identical, the client **must** stop the pairing attempt. It **must** attempt to inform the HTTP server of this by doing an HTTP request to `finalizePairing` where the value of `success` must be `false`. -### 5. Calculate serverHmacChallengeResponse +### 8. Calculate serverHmacChallengeResponse The HTTP client calculates a response to the provided `serverHmacChallenge` using the hashing algorithm as indicated in the `selectedHmacHashingAlgorithm`. For details see [Challenge response process](#challenge-response-process). From hereon the process branches into two scenario's, depending on if the HTTP client will be the communication client or the communication server. See [Mapping the CEM and RM to communication server or client](#mapping-the-cem-and-rm-to-communication-server-or-client) for which s2 node will perform which role for communication. -If the HTTP server will be the communication *server* steps 6A, 7A and 8A **should** follow. If the HTTP server will be the communications *client* steps 6B, 7B en 8B **should** follow. +If the HTTP server will be the communication *server* steps 9A, 10A and 11A **should** follow. If the HTTP server will be the communications *client* steps 9B, 10B en 11B **should** follow. -### 6A. POST /requestConnectionDetails -TODO +### 9A. POST /[version]/requestConnectionDetails +> Note: The `pairingAttemptId` must be provided through a header for this HTTP request -### 7A. HTTP Server checks serverHmacChallengeResponse -TODO +The HTTP client makes a request for the connection details. This request also serves as a way to send the HTTP server the `serverHmacChallengeResponse` calculated in step 8. -### 8A. Response status 200 -TODO +If the `pairingAttemptId` is not recognized by the server (or has expired), the server **must** respond with status code 401. -### 6B. POST /postConnectionDetails -TODO +If the request was not understood by the server for any other reason, the server **must** respond with status 400. -### 7B. HTTP Server checks serverHmacChallengeResponse -TODO +### 10A. HTTP Server checks serverHmacChallengeResponse +The HTTP server checks the `serverHmacChallengeResponse` provided by the HTTP client in step 9A. It does that by calculating the response itself, and checking if the results is identical to the `serverHmacChallengeResponse`. -### 8B. Response status 204 -TODO +If the result is identical, the server **should** proceed to the next step. If the result is not identical, the client **must** stop the pairing attempt by responding with HTTP status code 403. The `pairingAttemptId` cannot be used by the HTTP client anymore. If the HTTP client wants to make another attempt, it **must** start with again step 1 or step 4. -### 9. POST /finalizePairing -TODO +### 11A. Response status 200 +The server **must** generates an access token for the HTTP client. The access is random binary data and **must** be generated by a cryptographically secure pseudorandom number generator and **must** have a minimum length of 32 bytes. It is encoded using Base64. The access token **cannot** be used by the initiator S2 node until the pairing process is completed. -### 10. Response status 204 -TODO CA certificaat opslaan, unpairen indien al gepaired +The server responds with two pieces of information: -### Interruption of the process -TODO maximale duur van het proces is 30 seconden, als een van de twee zo lang niets hoort is de pairingattempt voorbij. +| Information | Description | +| --- | --- | +| `initiateConnectionUrl` | The base URI for the S2 connection process (does not include the version number) | +| `accessToken` | The access token that was generated for this S2 node | +If the response is understood and properly formatted, the HTTP client **should** proceed to the next step. Otherwise the HTTP client **must** stop the pairing attempt. It **must** attempt to inform the HTTP server of this by doing an HTTP request to `finalizePairing` where the value of `success` must be `false`. -## Challenge response process +### 9B. POST /[version]/postConnectionDetails +> Note: The `pairingAttemptId` must be provided through a header for this HTTP request -This protocol uses a two-way challenge response process to verify that both S2 Nodes have the same pairing token. For this process it doesn't matter which S2 node has generated the pairing token and which S2 node has the pairing token that was entered by the end user. The reason a two-way challenge response process is used to verify the pairing token is that it allows to establish trust without having to expose the pairing token before this trust is established. Both a challenge and a response are binary data, which are encoded using Base64. +The HTTP sends the connection details to the HTTP server. This request also serves as a way to send the HTTP server the `serverHmacChallengeResponse` calculated in step 8. -The challenge that is generated by the HTTP Client is called the `clientHmacChallenge`. The response to this challenge, generated by the HTTP Server, is called the `clientHmacChallengeResponse`. The challenge that is generated by the HTTP Server is called the `serverHmacChallenge`. The response to this challenge, generated by the HTTP Client, is called the `serverHmacChallengeResponse`. -TODO minimale lengtes toevoegen +| Information | Description | +| --- | --- | +| `serverHmacChallengeResponse` | The response for the challenge responce process | +| `initiateConnectionUrl` | The base URI for the S2 connection process (does not include the version number) | +| `accessToken` | The access token that was generated for this S2 node | -A challenge is a nonce; a random string of bytes. It **must** be generated by a cryptographically secure pseudorandom number generator. The response is calculated based on the function described below. Both the generator of the challenge and the receiver of the challenge calculate the response based several input parameters. Since both S2 nodes should have the same input, both S2 nodes should calculate the same response. The S2 Node that received the challenge sends it back to the S2 node that generated the challenge. Now the S2 node that generated the challenge simply has to check if the received response is identical to the expected response that he calculated himself. +If the `pairingAttemptId` is not recognized by the server (or has expired), the server **must** respond with status code 401. -The algorithm to calculate the response is based on the HMAC (hash-based message authentication code) function. This function has a *key* and a *message* as arguments. Most programming languages have a function or library available that provides HMAC functions. +If the request was not understood by the server for any other reason, the server **must** respond with status 400. -The HMAC function itself uses a cryptographic hash function for its calculations. Since cryptographic hash functions might contain vulnerabilities, this protocol uses a simple cryptographic hash function selection mechanism. The HTTP client sends with the requestPairing HTTP request a list of supported hash functions. In the response the HTTP server indicates which hash function it has selected from this list. This function **must** be used for all response calculations during het pairing attempt. Currently there is only one hash function available (SHA256), but other options might be added in the future. +### 10B. HTTP Server checks serverHmacChallengeResponse +The HTTP server checks the `serverHmacChallengeResponse` provided by the HTTP client in step 9A. It does that by calculating the response itself, and checking if the results is identical to the `serverHmacChallengeResponse`. -It order to avoid man-in-the-middle attacks when using self-signed certificates, the SHA256 fingerprint of the TLS *server certificate* can also be used as input for calculating the response. +If the result is identical, the server **should** proceed to the next step. If the result is not identical, the client **must** stop the pairing attempt by responding with HTTP status code 403. The `pairingAttemptId` cannot be used by the HTTP client anymore. If the HTTP client wants to make another attempt, it **must** start again with step 1 or step 4. -> Note that the pairing token is encoded using Base64, so it must also be decoded using Base64 before it can be used in the challenge response function. +### 11B. Response status 204 +The server confirms it has accepted the response and received the connection details by responding with HTTP status 204. -The exact function to calculate the response depends on the deployment of the S2 nodes. +### 12. POST /[version]/finalizePairing +> Note: The `pairingAttemptId` must be provided through a header for this HTTP request -``` -When both S2 nodes have a LAN deployment: - R = HMAC(C, T || F) +If all interaction has been successful until this point, the HTTP client **must** do a request to finalize the pairing attempt. The provided value for `success` **must** be `true`. -When at least one S2 node has a WAN deployment: - R = HMAC(C, T) -``` +If the `pairingAttemptId` is not recognized by the server (or has expired), the server **must** respond with status code 401. -Where: -| Symbol | meaning | -| ------ | ------- | -| `R` | Response -| `HMAC` | HMAC function with the selected cryptographic hash function | -| `C` | Challenge | -| `T` | Pairing token | -| `F` | SHA256 fingerprint of the TLS server certificate of the HTTP server | -| `\|\|` | Concatenation | +If the request was not understood by the server for any other reason, the server **must** respond with status 400. + +Receiving a `/finalizePairing` request marks the completion of the pairing attempt for the HTTP server. If the HTTP server issued an access token during this pairing attempt, it can now be used by an S2 communication client to set up an S2 connection. The `pairingAttemptId` can no longer be used by the HTTP client. + +### 13. Response status 204 +To confirm the successful completion of the paring attempt, the HTTP server responds to the client with HTTP status code 204. This response marks the completion of the pairing attempt for teh HTTP client. If the HTTP client issued an access token during this pairing attempt, it can now be used by an S2 communication client to set up an S2 connection. The `pairingAttemptId` can no longer be used by the HTTP client. +If the HTTP server was using a self-signed certificate, the HTTP client can now store the self-signed root CA certificate. The client **must** check that this is the CA certificate that is used for all future interaction with this S2 endpoint. The HTTP server is allowed to use a new self-signed server certificate, as long as it is signed by the self-signed CA certificate that was used during the pairing process. + +### Interruption of the process +A pairing attempt has a maximum duration of 30 seconds. That means that once a `pairingAttemptId` has been issued, this `pairingAttemptId` cannot be used after 30 seconds since it was issued. From the perspective of the HTTP server, any pairing attempt that is not completed in 30 seconds (with success or not) is considered a failed attempt. From the perspective of the HTTP client, if the server does not respond within 30 seconds since it received the `pairingAttemptId`, it must consider the pairing attempt as failed. If the HTTP client wants to make another attempt, it should start again at step 1 or step 4. + +### Wrong interactions +If the server receives wrong HTTP request (e.g. `/postConnectionDetails` while it was expecting `/requestConnectionDetails`) or when it receives the requests in the wrong order (e.g. `/finalizePairing` with `success` = `true` before calling `/requestConnectionDetails`) it **must** respond with an status 400 and consider the pairing attempt as failed. The only exception is receiving the same request twice. # S2 Connection (normative) @@ -701,6 +794,8 @@ Client and server **can** keep other (non-security) information for user experie # Security (normative) +TODO: nog iets opschrijven over bescherming tegen DOS aanvallen + Please refer to an extensive description of the security specifications to [Security considerations](./security-considerations.md). ## Certificates @@ -714,8 +809,4 @@ The server certificates **MUST** be exchanged and validated during the initiatio ## Cipher suites -Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. - -# Versioning of specification files - -TODO \ No newline at end of file +Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. \ No newline at end of file diff --git a/website/static/img/communication-layer/pairing_http_process.png b/website/static/img/communication-layer/pairing_http_process.png index c25fe0c015c5630d809caf837712f5312dc57486..773488ac9912f9885c55762e06355dc7ea5f529c 100644 GIT binary patch literal 33134 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 E1F=|E1^@s6 literal 27317 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 Date: Thu, 4 Dec 2025 13:34:21 +0100 Subject: [PATCH 08/26] Made some changes to the specification --- .../discovery-pairing-authentication.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index ec2aa9e..476efc1 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -257,16 +257,19 @@ For LAN deployed S2 endpoint, the URI **must** be based on an mDNS alias or host The pairing process is based on the trust relation that the end user has with both the CEM and the RM instances. That trust relation is out of scope for this specification and is up to CEM and RM providers to implement. -For each pairing attempt, one S2 endpoint must be the HTTP server, while the other is the HTTP client. +For each pairing attempt, one S2 endpoint must be the HTTP server, while the other is the HTTP client. The table below defines which S2 endpoint is het HTTP server or client is which situation. | Initiator S2 node | Responder S2 node | Responder is exclusively RM? | Initiator HTTP role | Responder HTTP role | WAN S2 pairing server | Remark | | ----------------- | ----------------- | ---------------------------- | ------------------- | ------------------- | --------------------- | ------ | | WAN | WAN | Doesn't matter | Client | Server | n/a | | | WAN | LAN | Doesn't matter | Client | n/a | Server | The WAN S2 pairing server for LAN deployment is optional | | LAN | WAN | Doesn't matter | Client | Server | n/a | | -| LAN | LAN | Yes | Server | Client | n/a | Through long-polling | +| LAN | LAN | Yes | Server | Client | n/a | Through long-polling. Decision to implement the server or the client is up to the RM endpoint. | +| LAN | LAN | Yes | Client | Server | n/a | Alternative for long-polling. Decision to implement the server or the client is up to the RM endpoint. | | LAN | LAN | No | Client | Server | n/a | | +> A LAN deployed RM implementation can choose if it implements the HTTP server, or that it implements the HTTP client and uses long-polling. This feature exists to accommodate RM implementations with constrained hardware. + A CEM can be paired with multiple RM's a the same time. A RM can only be paired with one CEM at a time. An S2 node is always available for pairing. When a RM that is already is paired with an CEM is paired with another CEM, the initial pairing is automatically unpaired. When a CEM and a RM are paired when they already are paired, they stay paired. ## Discovery @@ -321,9 +324,7 @@ This protocol uses a two-way challenge response process to verify that both S2 n The challenge that is generated by the HTTP Client is called the `clientHmacChallenge`. The response to this challenge, generated by the HTTP Server, is called the `clientHmacChallengeResponse`. The challenge that is generated by the HTTP Server is called the `serverHmacChallenge`. The response to this challenge, generated by the HTTP Client, is called the `serverHmacChallengeResponse`. -TODO minimale lengtes toevoegen - -A challenge is a nonce; a random binary data. It **must** be generated by a cryptographically secure pseudorandom number generator. The response is calculated based on the function described below. Both the generator of the challenge and the receiver of the challenge calculate the response based several input parameters. Since both S2 nodes should have the same input, both S2 nodes should calculate the same response. The S2 Node that received the challenge sends it back to the S2 node that generated the challenge. Now the S2 node that generated the challenge simply has to check if the received response is identical to the expected response that he calculated himself. +A challenge is a nonce; a random binary data. It **must** be generated by a cryptographically secure pseudorandom number generator and it **must** have a minimal length of 32 bytes. The response is calculated based on the function described below. Both the generator of the challenge and the receiver of the challenge calculate the response based several input parameters. Since both S2 nodes should have the same input, both S2 nodes should calculate the same response. The S2 Node that received the challenge sends it back to the S2 node that generated the challenge. Now the S2 node that generated the challenge simply has to check if the received response is identical to the expected response that he calculated himself. The algorithm to calculate the response is based on the HMAC (hash-based message authentication code) function. This function has a *key* and a *message* as arguments. Most programming languages have a function or library available that provides HMAC functions. @@ -476,6 +477,7 @@ The server **must** perform the checks in the table below to make sure that it c | If no `pairingS2NodeID` provided, does this endpoint indeed only represent one S2 node? | `No S2Node provided` | No | | Does the targeted S2 node have a different role than the initiator S2 node (i.e. you cannot pair two RM's or two CEM's)? | `Invalid combination of roles` | No | | Does the server accept any of the provided hashing algorithms for the challenge response process? | `Incompatible HMAC hashing algorithms` | No | +| Is there overlap between the communication protocols? | `Incompatible communication protocols` | Yes | | Is there overlap between the S2 message versions? | `Incompatible S2 message versions` | Yes | | If the targeted S2 node on the HTTP server is the initiator S2 node, did the end user provide a valid pairing token? | `No valid pairingToken on PairingServer` | No | | If the targeted S2 node on the HTTP server is the responder S2 node, does the S2 node have a pairing token which has not expired? | `No valid pairingToken on PairingServer` | No | @@ -484,7 +486,7 @@ The server **must** perform the checks in the table below to make sure that it c > Note: If the targeted S2 node is already paired with the initiator S2 node, the pairing process proceeds. When the paring process is finished successfully the existing pairing relation is maintained. -> Note: This is the only step where it is checked if the pairing code has expired. If the pairing token expires after this step, but during the pairing process, the pairing process will continue. A pairing attempt is limited to 30 seconds. +> Note: This is the only step where it is checked if the pairing code has expired. If the pairing token expires after this step, but during the pairing process, the pairing process will continue. A pairing attempt is limited to 15 seconds. If no checks fail the server **should** proceed to the next step. @@ -492,7 +494,7 @@ If no checks fail the server **should** proceed to the next step. The server selects an hashing algorithm for the challenge response function from the list that was provided by the client. This has to be a hashing algorithm that the server considers secure. The server calculates a response to the provided `clientHmacChallenge`. For details see [Challenge response process](#challenge-response-process). ### 6. Response status 200 -In order to formulate a response, the server **must** generate a `pairingAttemptId`. This is an identifier that **must** be generated by a cryptographically secure pseudorandom number generator and encoded using Base64. This identifier is used to keep track of all the HTTP interactions during the pairing attempt, and **must** be provided by the HTTP client as a header with all subsequent interactions. A pairing attempt **must** be completed within 30 seconds, or else the server **must** assume the pairing attempt has failed. +In order to formulate a response, the server **must** generate a `pairingAttemptId`. This is an identifier that **must** be generated by a cryptographically secure pseudorandom number generator and encoded using Base64. This identifier is used to keep track of all the HTTP interactions during the pairing attempt, and **must** be provided by the HTTP client as a header with all subsequent interactions. A pairing attempt **must** be completed within 15 seconds, or else the server **must** assume the pairing attempt has failed. The server responds with the following information (for full details see the OpenAPI specification file): @@ -594,7 +596,7 @@ To confirm the successful completion of the paring attempt, the HTTP server resp If the HTTP server was using a self-signed certificate, the HTTP client can now store the self-signed root CA certificate. The client **must** check that this is the CA certificate that is used for all future interaction with this S2 endpoint. The HTTP server is allowed to use a new self-signed server certificate, as long as it is signed by the self-signed CA certificate that was used during the pairing process. ### Interruption of the process -A pairing attempt has a maximum duration of 30 seconds. That means that once a `pairingAttemptId` has been issued, this `pairingAttemptId` cannot be used after 30 seconds since it was issued. From the perspective of the HTTP server, any pairing attempt that is not completed in 30 seconds (with success or not) is considered a failed attempt. From the perspective of the HTTP client, if the server does not respond within 30 seconds since it received the `pairingAttemptId`, it must consider the pairing attempt as failed. If the HTTP client wants to make another attempt, it should start again at step 1 or step 4. +A pairing attempt has a maximum duration of 15 seconds. That means that once a `pairingAttemptId` has been issued, this `pairingAttemptId` cannot be used after 15 seconds since it was issued. From the perspective of the HTTP server, any pairing attempt that is not completed in 15 seconds (with success or not) is considered a failed attempt. From the perspective of the HTTP client, if the server does not respond within 15 seconds since it received the `pairingAttemptId`, it must consider the pairing attempt as failed. If the HTTP client wants to make another attempt, it should start again at step 1 or step 4. ### Wrong interactions If the server receives wrong HTTP request (e.g. `/postConnectionDetails` while it was expecting `/requestConnectionDetails`) or when it receives the requests in the wrong order (e.g. `/finalizePairing` with `success` = `true` before calling `/requestConnectionDetails`) it **must** respond with an status 400 and consider the pairing attempt as failed. The only exception is receiving the same request twice. From e1d7f7da6d7d2759b4b9a6643fc9aa43e474a82b Mon Sep 17 00:00:00 2001 From: Gerben Broenink Date: Mon, 8 Dec 2025 15:43:50 +0100 Subject: [PATCH 09/26] added security requirements --- .../discovery-pairing-authentication.md | 104 +++++++++++++++++- 1 file changed, 101 insertions(+), 3 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 476efc1..9eb6f68 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -63,12 +63,65 @@ Discovery: DNS-SD (within a LAN) in combination with a central registry (for WAN Serialization: json. + **Why not oAuth 2.0?** The short answer is: oAuth is mainly designed for accessing protected resources in the cloud and since the S2 CEM and RM would also need to be able to pair on a local network (even without requiring internet access) oAuth 2.0 is simply not a good fit. We have identified a way to make it work but since it is such non-typical way, we choose not to use oAuth 2.0. For the long answer, please refer to [this page](why-not-oauth.md). + + +## Security requirements + +The described protocol, ensures the following 4 requirements: + +1. Mutual authentication +2. Integrity of communication +3. Confidentiality of communication +4. Forward secrecy + +There is one guarantee that explicitly is not given by this protocol: + +5. Non-repudiation + + + +### 1. Mutual authentication + +The mutual authentication is based on the trust relation between the user and the Client/Server. Since it is assumed that the user already had a trust relation with both of them, this existing trust can be used for mutual authentication between the client and the server. Note that the this communication is not part of the S2 protocol. + +The enduser requests an url, certificate fingerprint and token from the server, and gives these to the client. Based on these data, the client can connect to the server, using a TLS connection, check the certificate and authenticate himself with the token. Note that if the server uses a self-signed certificate, it will also give a certificate fingerprint to the user. The client needs to use this fingerprint to verify the certificate in the TLS connection. + +### 2. Integrity of communication + +Using TLS will ensure the integrity of the data. + +### 3. Confidentiality of communication + +Using TLS will ensure the confidentiality of the data. + +### 4. Forward secrecy + +Using TLS1.3 will ensure the forward secrecy of the data. + +### 5. Non-repudiation + +Non-repudiation is not guaranteed in this protocol. Individual messages are not signed by anyone and as a result both parties could deny sending a specific request. However, while no legal proof is given, since integrity and authenticity is guaranteed by TLS, each party always knows for sure which party made what statement. + +### Remaining risk + +There are two remaining vulnerable situations for the described protocol. In this section both will be explained. + +### Self signed certificates + +In the case that a local RM and a local SEM communication, it is not possible to generate a PKI-certificate that can be publicly validated. As a result, S2 accepts in **ONLY** this situation self-signed certificates. The risk for spoofing attacks are mitigated by sharing the certificate fingerprint and pinning the self signed certificate at the client side. As a result, the client can check for all future connections whether or not it is connected with the same server. + +### Trust relations between the end-user and the Client/Server + +The entire trust model of S2 is based on the fact that there is already a trust relation between the end-user and the client/server. If these clients/servers do not use adequate security mechanisms, it might be possible to attack the S2 system as well. + + # Terms and definitions (normative) TODO waar handig linkjes toevoegen naar sections met meer uitleg @@ -315,8 +368,53 @@ When a pairing S2 node ID is used: The pairing code allows us to transfer two pieces of information by only bothering the end user once. Due to its format the initiator S2 node can easily extract the pairing S2 node ID and the pairing token from the pairing code by splitting the string at the dash. -## Self-signed certificates -If the HTTP server is deployed in the LAN, the HTTP server will use a self-signed certificate. This means that the HTTP client **must** be configured to accept self-signed certificates during the pairing process. Since the pairing process consists out of several HTTP requests, the HTTP client **must** check that for every request the same self-signed certificate is used by the HTTP server. If this is not the case, the HTTP client **cannot** proceed with the request. + +## Certificates + +There are two possible types of certificates. The first option is a public server certificate, that is part of the public PKI infrastructure, (indirectly) signed by a public root CA. This protocol allows local servers to use a self signed CA certificate to sign its local server certificate. This is needed because a local server is not able to get a certificate from a public PKI infrastructure. + +In the following image, the difference is shown. On the left a public root CA that's publicly known and trusted, on the right, a self signed root certificate, that's unknown and it's trustworthiness has to be achieved in another way. + +![image.png](/img/communication-layer/certificate-chains.png) + +
+Image generated using the following PlantUML code: + +``` +@startuml +struct PublicRootCA +struct PublicIntermediateCA +struct PublicServerCertificate + +PublicRootCA --> PublicIntermediateCA +PublicIntermediateCA --> PublicServerCertificate + + +struct SelfSignedCA +struct LocalServerCertificate + +SelfSignedCA --> SelfSignedCA +SelfSignedCA --> LocalServerCertificate +@enduml +``` +
+ + +### Trusting a self signed root certificate + +The self signed root certificate is by default not trusted. However during the pairing phase, the server with the self signed root certificate will share part of the root's certificate fingerprint as part of the pairing token, via a second channel. This will enable the client to verify the self signed root certificate, and create trust. From this moment on, the client will store the complete fingerprint of the self signed root certificate, and use it to verify the server certificate for all future connections. + +Note that the `preparePairing` and `cancelPreparePairing` endpoints can be called before the pairing has happened. So in the case the server is running on a LAN (and thus uses self-signed certificates), the client can skip the certificate validation steps on those endpoint. This means that the HTTP client **must** be configured to accept self-signed certificates during the pairing process. Since the pairing process consists out of several HTTP requests, the HTTP client **must** check that for every request the same self-signed certificate is used by the HTTP server. If this is not the case, the HTTP client **cannot** proceed with the request. + + + +### Updating the certificates + +A server can update its certificate. When a cloud server updates it's certificate, it **MUST** be signed by a CA, so a client can check it's validity. A server **SHOULD** update its server certificate at least once every 6 months. + +If the server is in local-local mode, and uses a self-signed CA certificate, the CA certificate **SHOULD** be created with a validity period which is long enough for the expected lifetime of the server. If the used crypto for the the CA certificate is broken, or the lifetime of the server is longer than the validity of the certificate, the server **MUST** create a new self-signed CA certificate and all clients need to be paired again. Like cloud servers, a local server **SHOULD** update its server certificate at least once every 6 months. + + ## Challenge response process @@ -811,4 +909,4 @@ The server certificates **MUST** be exchanged and validated during the initiatio ## Cipher suites -Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. \ No newline at end of file +Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. From 6cd533e1b22dcd34d590e5283b657a58665cf1fb Mon Sep 17 00:00:00 2001 From: Gerben Broenink Date: Thu, 11 Dec 2025 09:17:47 +0100 Subject: [PATCH 10/26] changed pairing token lenght requirements --- .../discovery-pairing-authentication.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 9eb6f68..fda6a91 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -87,25 +87,25 @@ There is one guarantee that explicitly is not given by this protocol: -### 1. Mutual authentication +### 1. Mutual authentication (guaranteed) The mutual authentication is based on the trust relation between the user and the Client/Server. Since it is assumed that the user already had a trust relation with both of them, this existing trust can be used for mutual authentication between the client and the server. Note that the this communication is not part of the S2 protocol. The enduser requests an url, certificate fingerprint and token from the server, and gives these to the client. Based on these data, the client can connect to the server, using a TLS connection, check the certificate and authenticate himself with the token. Note that if the server uses a self-signed certificate, it will also give a certificate fingerprint to the user. The client needs to use this fingerprint to verify the certificate in the TLS connection. -### 2. Integrity of communication +### 2. Integrity of communication (guaranteed) Using TLS will ensure the integrity of the data. -### 3. Confidentiality of communication +### 3. Confidentiality of communication (guaranteed) Using TLS will ensure the confidentiality of the data. -### 4. Forward secrecy +### 4. Forward secrecy (guaranteed) Using TLS1.3 will ensure the forward secrecy of the data. -### 5. Non-repudiation +### 5. Non-repudiation (NOT guaranteed) Non-repudiation is not guaranteed in this protocol. Individual messages are not signed by anyone and as a result both parties could deny sending a specific request. However, while no legal proof is given, since integrity and authenticity is guaranteed by TLS, each party always knows for sure which party made what statement. @@ -113,15 +113,14 @@ Non-repudiation is not guaranteed in this protocol. Individual messages are not There are two remaining vulnerable situations for the described protocol. In this section both will be explained. -### Self signed certificates +#### Self signed certificates -In the case that a local RM and a local SEM communication, it is not possible to generate a PKI-certificate that can be publicly validated. As a result, S2 accepts in **ONLY** this situation self-signed certificates. The risk for spoofing attacks are mitigated by sharing the certificate fingerprint and pinning the self signed certificate at the client side. As a result, the client can check for all future connections whether or not it is connected with the same server. +In the case that a local RM and a local SEM communication, it is not possible to generate a PKI-certificate that can be publicly validated. As a result, S2 accepts in **ONLY** this situation self-signed certificates. The risk for spoofing attacks are mitigated by sharing the certificate fingerprint and pinning the self signed certificate at the client side. In these situations, part of the certificate fingerprint will also be shared in the pairing token. As a result, the client can check for all connections whether or not it is connected with the correct server. -### Trust relations between the end-user and the Client/Server +#### Trust relations between the end-user and the Client/Server The entire trust model of S2 is based on the fact that there is already a trust relation between the end-user and the client/server. If these clients/servers do not use adequate security mechanisms, it might be possible to attack the S2 system as well. - # Terms and definitions (normative) TODO waar handig linkjes toevoegen naar sections met meer uitleg @@ -353,7 +352,7 @@ A LAN scenario where both RM and CEM are running on the same local network. Disc The pairing token is a random string of characters that is generated by the responder S2 node. It is a secret which is transferred by the end user to the initiator S2 node, and then is verified during the pairing process. Since there will be many cases where the end user has to manually type in the pairing token, the pairing token has to be short enough to make it easy for the end user to type in, but long enough to make it secure. The pairing token **must** be generated by a cryptographically secure pseudorandom number generator. The pairing token **must** be random binary data with a length of at least 9 bytes, and is encoded using Base64 before it is presented to the end user (9 bytes is equal to 12 characters when encoded with Base64). -The pairing token is typically dynamically generated when the user requests the pairing token at the user interface of the responder S2 node. Dynamically generated pairing tokens **must** expire after a duration; five minutes is the recommended duration. However, energy flexible devices that do not have a user interface are allowed to have a static pairing token, that for example can be printed somewhere on the physical device. Static pairing tokens do not expire. Static pairing tokens **should** be longer than 6 characters. +The pairing token is typically dynamically generated when the user requests the pairing token at the user interface of the responder S2 node. Dynamically generated pairing tokens **must** expire after a duration; five minutes is the recommended duration. However, energy flexible devices that do not have a user interface are allowed to have a static pairing token, that for example can be printed somewhere on the physical device. Static pairing tokens do not expire. Static pairing tokens **should** be longer than 12 (base64) characters. An S2 endpoint can host multiple S2 nodes. When attempting to pair a certain S2 node, the S2 endpoint needs to know exactly which of its S2 nodes this pairing attempt is aimed at. S2 nodes are uniquely identified with their S2 node ID. Since this S2 node ID is a UUID, it is pretty long and cumbersome to type in. That is why an S2 endpoint can assign its nodes a `pairing S2 node ID`. This is an identifier that is intended to be short, and only unique within the context of this particular S2 endpoint. Pairing S2 node IDs could be assigned by the S2 endpoint whenever new S2 nodes are created, but also could be generated dynamically only when someone is attempting to pair to this S2 node. This way, pairing S2 node IDs have a short live, and can be reused by other S2 nodes at other moments. This allows to use shorter pairing S2 node ID's. Pairing S2 node IDs are a string of characters, which may include lower case letters, upper case letters and numbers. Pairing S2 node IDs are ideally as short as possible (at least one character), but should of course be long enough to allow the S2 endpoint to uniquely identify an S2 node. When an S2 endpoint only contains one S2 node, there is no need for a pairing S2 node ID. From 4b32d5d3d33e8862dc9de57c380ee69d5a7f99ff Mon Sep 17 00:00:00 2001 From: Gerben Broenink Date: Thu, 11 Dec 2025 14:18:31 +0100 Subject: [PATCH 11/26] added security checks to all communication --- .../discovery-pairing-authentication.md | 84 ++++++++++++++++--- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index fda6a91..3ce73e9 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -91,7 +91,7 @@ There is one guarantee that explicitly is not given by this protocol: The mutual authentication is based on the trust relation between the user and the Client/Server. Since it is assumed that the user already had a trust relation with both of them, this existing trust can be used for mutual authentication between the client and the server. Note that the this communication is not part of the S2 protocol. -The enduser requests an url, certificate fingerprint and token from the server, and gives these to the client. Based on these data, the client can connect to the server, using a TLS connection, check the certificate and authenticate himself with the token. Note that if the server uses a self-signed certificate, it will also give a certificate fingerprint to the user. The client needs to use this fingerprint to verify the certificate in the TLS connection. +The enduser requests an url, and token from the server, and gives these to the client. Based on these data, the client can connect to the server, using a TLS connection, check the certificate and authenticate himself with the token. Note that if the server uses a self-signed certificate, the fingerprint will be shared during the pairing phase, so it can be verified by the client. ### 2. Integrity of communication (guaranteed) @@ -115,7 +115,7 @@ There are two remaining vulnerable situations for the described protocol. In thi #### Self signed certificates -In the case that a local RM and a local SEM communication, it is not possible to generate a PKI-certificate that can be publicly validated. As a result, S2 accepts in **ONLY** this situation self-signed certificates. The risk for spoofing attacks are mitigated by sharing the certificate fingerprint and pinning the self signed certificate at the client side. In these situations, part of the certificate fingerprint will also be shared in the pairing token. As a result, the client can check for all connections whether or not it is connected with the correct server. +In the case that a local RM and a local SEM communicate, it is not possible to generate a PKI-certificate that can be publicly validated. As a result, S2 accepts, **ONLY** in this situation, self-signed certificates. The risk for spoofing attacks are mitigated by sharing the fingerprint during the pairing phase and pinning the self signed certificate at the client side. In these situations, part of the certificate fingerprint will also be shared in the pairing token. As a result, the client can check for all connections whether or not it is connected with the correct server. #### Trust relations between the end-user and the Client/Server @@ -173,7 +173,7 @@ There are three types of S2 connections between S2 nodes possible: The end user can take the initiative to *pair* a single CEM instance with a single RM instance. This process has to be started with one of the S2 nodes. Which node this is depends on the deployment and implementation decisions of the S2 node, but ideally it could be either one. The S2 node however needs to have a user interface. We'll call the S2 node that user uses to start the pairing process the *initiator*. We'll call the other S2 node the *responder*. The first step of pairing is establishing a connection from the initiator S2 node to the responder S2 node. This can be done in several ways: -* Enter the address manually. +* Enter the responder S2 node address manually at the initiator S2 node. * If the responder S2 node is deployed in the WAN, the URL could be retrieved through a registry. The end user would have to select the type of S2 node from a list of known s2 node services in its region. * If both S2 nodes are deployed in the LAN however, s2 nodes can be automatically be detected. The end user would have to select the S2 node from a list of automatically discovered S2 nodes. @@ -233,11 +233,11 @@ The first step is finding the responder S2 node from the initiator S2 node. In p ### Pairing -The pairing process itself is completely based on HTTP rest. One S2 node behaves as the HTTP server, and the other as the HTTP client. This process is described in an OpenAPI file. The process consists of multiple steps. If the pairing process is completed successfully, the S2 nodes will agree on an access token. This token is used to initiate communication or to unpair. +The pairing process itself is completely based on HTTP REST. One S2 node behaves as the HTTP server, and the other as the HTTP client. This process is described in an OpenAPI file. The process consists of multiple steps. If the pairing process is completed successfully, the S2 nodes will agree on an access token. This token is used to initiate communication or to unpair. We'll refer to the endpoint that behaves as the HTTP server during the pairing process as the *S2 pairing server*, and the client as the *S2 pairing client*. -Pairing interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN deployments self-signed certificates are used. +Pairing interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN deployments self-signed certificates are used. For more information about the use of self-signed certificates, check [Trusting a self signed root certificate](###Trusting-a-self-signed-root-certificate) ### Communication @@ -249,7 +249,7 @@ We'll refer to the endpoint that behaves as the HTTP server during the communica It should be noted that pairing and communication are two separate HTTP interfaces, that don't have to be used in the same way. It could be that an S2 Node is an S2 pairing client, but then becomes an S2 communication server. This depends on the deployment of the s2 Nodes (see [Pairing details for different deployments](#pairing-details-for-different-deployments)). -Communication interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN-LAN deployments self-signed certificates are used. +Communication interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN-LAN deployments self-signed certificates are used. For more information about the use of self-signed certificates, check [Trusting a self signed root certificate](###Trusting-a-self-signed-root-certificate) After the HTTP interaction a WebSocket is established. The S2 communication server is always the WebSocket server. This server must use the same TLS certificate as the HTTP server. @@ -257,6 +257,9 @@ After the HTTP interaction a WebSocket is established. The S2 communication serv Either S2 node can take the initiative to unpair from the other S2 node. This is done using the same HTTP OpenAPI specification and the same HTTP server and client as the communication. The details for unpairing differ depending if it is the S2 communication server or if it is the S2 communication client that initiates the unpairing process. +Unpairing interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN-LAN deployments self-signed certificates are used. For more information about the use of self-signed certificates, check [Trusting a self signed root certificate](###Trusting-a-self-signed-root-certificate) + + ## Pairing details for different deployments As explained, the pairing process is based on HTTP REST calls. That means that for every pairing attempt, one S2 node behaves as the HTTP server, and one HTTP node behaves as the pairing client. The logical solution would be to make the initiator S2 node the HTTP client and the responder S2 node the HTTP server. After all, it is the HTTP client that takes the initiative to contact the HTTP server. The HTTP server cannot take the initiative to contact the HTTP client. @@ -270,7 +273,7 @@ There are however two situations where this is not possible: * **WAN initiator S2 node and LAN responder S2 node**: Since the LAN is usually shielded from the WAN through a firewall or NAT, it is assumed that it is not possible to approach a LAN HTTP server from a WAN client. This specifications offers two approaches to this problem: * Accept this limitation and not allow the WAN S2 node to be the initiator S2 node. Pairing can only be performed when the LAN S2 node is the initiator S2 node and the WAN S2 node is the responder S2 node. Special care must be taken to explain this to the end user. * Many modern devices or EMS systems are connected to a cloud backend managed by the OEM. If this is the case, it is possible to implement the pairing HTTP server in the cloud, even though the S2 node itself is in the WAN. If the pairing is performed successfully in the OEM backend, the result of the pairing must be communicated to the S2 node via the existing connection between device/EMS and the OEM backend. -* **LAN initiator RM and LAN responder RM**: Since one of the requirements is that a LAN RM instance can be implemented on restricted hardware, and a TLS enabled HTTP server is for more memory intensive than an HTTP client, there is an option to implement a LAN RM instance purely as an HTTP server. A long-polling mechanism is available to indicate to the HTTP Server that the S2 node is available for pairing. This mechanism is also used to initiate the pairing process from the HTTP server. In other words: in this specific situation the initiator S2 node behaves as the HTTP server, and the responder S2 node only has to be an HTTP client. +* **LAN initiator RM and LAN responder RM**: Since one of the requirements is that a LAN RM instance can be implemented on restricted hardware, and a TLS enabled HTTP server is far more memory intensive than an HTTP client, there is an option to implement a LAN RM instance purely as an HTTP server. A long-polling mechanism is available to indicate to the HTTP Server that the S2 node is available for pairing. This mechanism is also used to initiate the pairing process from the HTTP server. In other words: in this specific situation the initiator S2 node behaves as the HTTP server, and the responder S2 node only has to be an HTTP client. ![Pairing_direction](/img/communication-layer/pairing_direction.png) @@ -401,7 +404,7 @@ SelfSignedCA --> LocalServerCertificate ### Trusting a self signed root certificate -The self signed root certificate is by default not trusted. However during the pairing phase, the server with the self signed root certificate will share part of the root's certificate fingerprint as part of the pairing token, via a second channel. This will enable the client to verify the self signed root certificate, and create trust. From this moment on, the client will store the complete fingerprint of the self signed root certificate, and use it to verify the server certificate for all future connections. +The self signed root certificate is by default not trusted. However during the pairing phase, the server with the self signed root certificate will share the fingerprint of the certificate during the pairing phase as part of the challenge. This will enable the client to verify the self signed root certificate, and create trust. From this moment on, the client will store the complete fingerprint of the self signed root certificate, and use it to verify the server certificate for all future connections. Note that the `preparePairing` and `cancelPreparePairing` endpoints can be called before the pairing has happened. So in the case the server is running on a LAN (and thus uses self-signed certificates), the client can skip the certificate validation steps on those endpoint. This means that the HTTP client **must** be configured to accept self-signed certificates during the pairing process. Since the pairing process consists out of several HTTP requests, the HTTP client **must** check that for every request the same self-signed certificate is used by the HTTP server. If this is not the case, the HTTP client **cannot** proceed with the request. @@ -540,6 +543,15 @@ If the HTTP client does not fulfill these preconditions, it **cannot** send the ### 1. GET / (index containing pairing API versions) Since the HTTP client does not know which major versions of the pairing API are implemented by the server, it must first do a GET request to the index (e.g. `https://hostname.local/pairing/`). +The client **must** perform the following checks during this request: +| Check | How to proceed if check fails | +| --- | --- | +| Check certificate | Pairing is failed | +| If self signed certificate, check is server is local | Pairing is failed | +| Store fingerprint of certificate for later check | | + +If no checks fail the client **should** proceed to the next step. + ### 2. Response status 200 The server responds with a list of implement major versions of the pairing API. It is formatted as a JSON array contains all the supported version of the pairing API (e.g. `["v1"]`). @@ -564,6 +576,16 @@ The client sends the following information (for full details see the OpenAPI spe | `clientHmacChallenge` | The challenge of the client for the challenge response process (see [Challenge response process](#challenge-response-process) | | `forcePairing` | Indicate if the S2 nodes must pair, even though they (currently) do not support the same S2 message versions (this could in the future be solved with a software update) | +The client **must** perform the following checks during this request: +| Check | How to proceed if check fails | +| --- | --- | +| Check certificate | Pairing is failed | +| If self signed certificate, check is server is local | Pairing is failed | +| Check if same fingerprint is used as previous request | Pairing is failed | + +If no checks fail the client **should** proceed to the next step. + + The server **must** perform the checks in the table below to make sure that it can proceed with this request. If one of these checks fail, the server should respond with an HTTP status 400 and a `PairingResponseErrorMessage`. The contents of the `additionalInfo` field is supposed the be helpful and up to the implementer. | Check | Type of `PairingResponseErrorMessage` when check fails | Can be ignored when `forcePairing` is true ?| @@ -619,6 +641,9 @@ The HTTP client checks the `clientHmacChallengeResponse` provided by the HTTP se If the result is identical, the client **should** proceed to the next step. If the result is not identical, the client **must** stop the pairing attempt. It **must** attempt to inform the HTTP server of this by doing an HTTP request to `finalizePairing` where the value of `success` must be `false`. +Note that in case of a local server, the certificate fingerprint is part of the challenge. So if the challenge succeeds, the certificate fingerprint is correct, and the certificate can be trusted. The client **must** pin this certificate, and trust this certificate for future use. + + ### 8. Calculate serverHmacChallengeResponse The HTTP client calculates a response to the provided `serverHmacChallenge` using the hashing algorithm as indicated in the `selectedHmacHashingAlgorithm`. For details see [Challenge response process](#challenge-response-process). @@ -635,6 +660,16 @@ If the `pairingAttemptId` is not recognized by the server (or has expired), the If the request was not understood by the server for any other reason, the server **must** respond with status 400. +The client **must** perform the following checks during this request: +| Check | How to proceed if check fails | +| --- | --- | +| Check certificate | Pairing is failed | +| If self signed certificate, check is server is local | Pairing is failed | +| Check if certificate is pinned | Pairing is failed | + +If no checks fail the client **should** proceed to the next step. + + ### 10A. HTTP Server checks serverHmacChallengeResponse The HTTP server checks the `serverHmacChallengeResponse` provided by the HTTP client in step 9A. It does that by calculating the response itself, and checking if the results is identical to the `serverHmacChallengeResponse`. @@ -664,9 +699,22 @@ The HTTP sends the connection details to the HTTP server. This request also serv | `initiateConnectionUrl` | The base URI for the S2 connection process (does not include the version number) | | `accessToken` | The access token that was generated for this S2 node | -If the `pairingAttemptId` is not recognized by the server (or has expired), the server **must** respond with status code 401. +The client **must** perform the following checks during this request: +| Check | How to proceed if check fails | +| --- | --- | +| Check certificate | Pairing is failed | +| If self signed certificate, check is server is local | Pairing is failed | +| Check if certificate is pinned | Pairing is failed | -If the request was not understood by the server for any other reason, the server **must** respond with status 400. +If no checks fail the client **should** proceed to the next step. + +The server **must** perform the following checks during this request: +| Check | How to proceed if check fails | +| --- | --- | +| `pairingAttemptId` is recognized | Respond with status code 401 | +| Request could not be parsed correctly | Respond with status code 400 | + +If no checks fail the server **should** proceed to the next step. ### 10B. HTTP Server checks serverHmacChallengeResponse The HTTP server checks the `serverHmacChallengeResponse` provided by the HTTP client in step 9A. It does that by calculating the response itself, and checking if the results is identical to the `serverHmacChallengeResponse`. @@ -681,9 +729,21 @@ The server confirms it has accepted the response and received the connection det If all interaction has been successful until this point, the HTTP client **must** do a request to finalize the pairing attempt. The provided value for `success` **must** be `true`. -If the `pairingAttemptId` is not recognized by the server (or has expired), the server **must** respond with status code 401. +The client **must** perform the following checks during this request: +| Check | How to proceed if check fails | +| --- | --- | +| Check certificate | Pairing is failed | +| If self signed certificate, check is server is local | Pairing is failed | +| Check if certificate is pinned | Pairing is failed | +If no checks fail the client **should** proceed to the next step. -If the request was not understood by the server for any other reason, the server **must** respond with status 400. +The server **must** perform the following checks during this request: +| Check | How to proceed if check fails | +| --- | --- | +| The `pairingAttemptId` is correctly recognized | respond with status code 401 | +| The request is not understood for any other reason | respond with status code 400 | +| Check if certificate is pinned | Pairing is failed | +If no checks fail the server **should** proceed to the next step. Receiving a `/finalizePairing` request marks the completion of the pairing attempt for the HTTP server. If the HTTP server issued an access token during this pairing attempt, it can now be used by an S2 communication client to set up an S2 connection. The `pairingAttemptId` can no longer be used by the HTTP client. From 88fca3337551cf189fef66a9c83dc8d885fbde1f Mon Sep 17 00:00:00 2001 From: Gerben Broenink Date: Thu, 11 Dec 2025 16:14:18 +0100 Subject: [PATCH 12/26] fixed layout error --- .../discovery-pairing-authentication.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 3ce73e9..53fa74a 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -544,6 +544,7 @@ If the HTTP client does not fulfill these preconditions, it **cannot** send the Since the HTTP client does not know which major versions of the pairing API are implemented by the server, it must first do a GET request to the index (e.g. `https://hostname.local/pairing/`). The client **must** perform the following checks during this request: + | Check | How to proceed if check fails | | --- | --- | | Check certificate | Pairing is failed | @@ -577,6 +578,7 @@ The client sends the following information (for full details see the OpenAPI spe | `forcePairing` | Indicate if the S2 nodes must pair, even though they (currently) do not support the same S2 message versions (this could in the future be solved with a software update) | The client **must** perform the following checks during this request: + | Check | How to proceed if check fails | | --- | --- | | Check certificate | Pairing is failed | @@ -661,6 +663,7 @@ If the `pairingAttemptId` is not recognized by the server (or has expired), the If the request was not understood by the server for any other reason, the server **must** respond with status 400. The client **must** perform the following checks during this request: + | Check | How to proceed if check fails | | --- | --- | | Check certificate | Pairing is failed | @@ -700,6 +703,7 @@ The HTTP sends the connection details to the HTTP server. This request also serv | `accessToken` | The access token that was generated for this S2 node | The client **must** perform the following checks during this request: + | Check | How to proceed if check fails | | --- | --- | | Check certificate | Pairing is failed | @@ -709,6 +713,7 @@ The client **must** perform the following checks during this request: If no checks fail the client **should** proceed to the next step. The server **must** perform the following checks during this request: + | Check | How to proceed if check fails | | --- | --- | | `pairingAttemptId` is recognized | Respond with status code 401 | @@ -730,19 +735,23 @@ The server confirms it has accepted the response and received the connection det If all interaction has been successful until this point, the HTTP client **must** do a request to finalize the pairing attempt. The provided value for `success` **must** be `true`. The client **must** perform the following checks during this request: + | Check | How to proceed if check fails | | --- | --- | | Check certificate | Pairing is failed | | If self signed certificate, check is server is local | Pairing is failed | | Check if certificate is pinned | Pairing is failed | + If no checks fail the client **should** proceed to the next step. The server **must** perform the following checks during this request: + | Check | How to proceed if check fails | | --- | --- | | The `pairingAttemptId` is correctly recognized | respond with status code 401 | | The request is not understood for any other reason | respond with status code 400 | | Check if certificate is pinned | Pairing is failed | + If no checks fail the server **should** proceed to the next step. Receiving a `/finalizePairing` request marks the completion of the pairing attempt for the HTTP server. If the HTTP server issued an access token during this pairing attempt, it can now be used by an S2 communication client to set up an S2 connection. The `pairingAttemptId` can no longer be used by the HTTP client. From 7bde26eb80ce5c60fde045422d9c13c6e1f8297a Mon Sep 17 00:00:00 2001 From: Gerben Broenink Date: Thu, 11 Dec 2025 16:18:40 +0100 Subject: [PATCH 13/26] removed wrong check in checklist --- .../docs/communication-layer/discovery-pairing-authentication.md | 1 - 1 file changed, 1 deletion(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 53fa74a..10622e0 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -750,7 +750,6 @@ The server **must** perform the following checks during this request: | --- | --- | | The `pairingAttemptId` is correctly recognized | respond with status code 401 | | The request is not understood for any other reason | respond with status code 400 | -| Check if certificate is pinned | Pairing is failed | If no checks fail the server **should** proceed to the next step. From 246cfb4142de16ec32332bf95c1d2886e4ba9674 Mon Sep 17 00:00:00 2001 From: Gerben Broenink Date: Tue, 16 Dec 2025 10:04:01 +0100 Subject: [PATCH 14/26] added explicit cybersecurity check in communication description --- .../discovery-pairing-authentication.md | 77 +++++++++++++++---- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 10622e0..15cdc33 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -547,8 +547,8 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Pairing is failed | -| If self signed certificate, check is server is local | Pairing is failed | +| Check certificate | Pairing is failed, do not proceed with the pairing attempt | +| If self signed certificate, check is server is local | Pairing is failed, do not proceed with the pairing attempt | | Store fingerprint of certificate for later check | | If no checks fail the client **should** proceed to the next step. @@ -581,9 +581,9 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Pairing is failed | -| If self signed certificate, check is server is local | Pairing is failed | -| Check if same fingerprint is used as previous request | Pairing is failed | +| Check certificate | Pairing is failed, do not proceed with the pairing attempt | +| If self signed certificate, check is server is local | Pairing is failed, do not proceed with the pairing attempt | +| Check if same fingerprint is used as previous request | Pairing is failed, do not proceed with the pairing attempt | If no checks fail the client **should** proceed to the next step. @@ -666,9 +666,9 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Pairing is failed | -| If self signed certificate, check is server is local | Pairing is failed | -| Check if certificate is pinned | Pairing is failed | +| Check certificate | Pairing is failed, do not proceed with the pairing attempt | +| If self signed certificate, check is server is local | Pairing is failed, do not proceed with the pairing attempt | +| Check if certificate is pinned | Pairing is failed, do not proceed with the pairing attempt | If no checks fail the client **should** proceed to the next step. @@ -706,9 +706,9 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Pairing is failed | -| If self signed certificate, check is server is local | Pairing is failed | -| Check if certificate is pinned | Pairing is failed | +| Check certificate | Pairing is failed, do not proceed with the pairing attempt | +| If self signed certificate, check is server is local | Pairing is failed, do not proceed with the pairing attempt | +| Check if certificate is pinned | Pairing is failed, do not proceed with the pairing attempt | If no checks fail the client **should** proceed to the next step. @@ -738,9 +738,9 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Pairing is failed | -| If self signed certificate, check is server is local | Pairing is failed | -| Check if certificate is pinned | Pairing is failed | +| Check certificate | Pairing is failed, do not proceed with the pairing attempt | +| If self signed certificate, check is server is local | Pairing is failed, do not proceed with the pairing attempt | +| Check if certificate is pinned | Pairing is failed, do not proceed with the pairing attempt | If no checks fail the client **should** proceed to the next step. @@ -818,11 +818,34 @@ Client -> Client : 8. accept new accessToken The client has a list of pairs of accessTokens and commTokens. After the initial initiateConnection call, the list always contains at least one pair (initially there is only an accessToken as result of the pairing process). It uses one of the accessTokens to make an authorized request to retrieve a commToken and new accessToken form the server. +The client **must** perform the following checks during this request: + +| Check | How to proceed if check fails | +| --- | --- | +| Check certificate | Initiation is failed, do not proceed with the initiation attempt | +| If self signed certificate, check is server is local | Initiation is failed, do not proceed with the initiation attempt | +| Check if certificate is pinned | Initiation is failed, do not proceed with the initiation attempt | + +If no checks fail the client **should** proceed to the next step. + + + + **2. generate new token pair** For each paired NodeId the server saves an active accessToken and active commToken. In addition to that, the server also has a list for pending tokens. This list contains entries, each consisting of an accessToken, a commToken, a NodeId and a timestamp. The server checks the provided accessToken from the client with the active accessToken it has saved for this node. If the provided accessToken was valid, the server generates a new accessToken and commToken and saves this together with the S2NodeId and the current time as in entry in the list of pending tokens. +The server **must** perform the following checks during this request: + +| Check | How to proceed if check fails | +| --- | --- | +| Verify `accessToken` | respond with status code ??? | + + +If no checks fail the server **should** proceed to the next step. + + **3. initiateConnection response** a. If the provided accessToken was valid, the server sends the generated pending accessToken, commToken and connectionUrl to the client. @@ -839,6 +862,16 @@ b. If the client was not able to persist the new accessToken (e.g. because the s The client initiates the S2 session, and provides the commToken. +The client **must** perform the following checks during this request: + +| Check | How to proceed if check fails | +| --- | --- | +| Check certificate | Initiation is failed, do not proceed with the initiation attempt | +| If self signed certificate, check is server is local | Initiation is failed, do not proceed with the initiation attempt | +| Check if certificate is pinned | Initiation is failed, do not proceed with the initiation attempt | + +If no checks fail the client **should** proceed to the next step. + **6. Activate new accessToken for this NodeId** This step is the implicit acknowledgement to the server that the client has saved the accessToken successfully. @@ -849,6 +882,11 @@ b. If the provided commToken is not in the list of pending tokens, the server mu c. If the server is not able to active the new tokens (e.g. because the storage device or the DBMS is not available), the server must not accept the connection and responds with an error code. +| Check | How to proceed if check fails | +| --- | --- | +| Verify `commToken` | respond with status code ??? | + + **7. Successful S2 session?** a. The S2 session is successfully opened. This is an implicit acknowledgement that both server and the client are in possession of the new tokens. For the server it is an acknowledgement that is has activated the new access token successfully and for the client it means that it did correctly receive and persist the new accessToken. @@ -869,6 +907,17 @@ The WebSocket client **must** run on the S2ClientNode and the WebSocket server o The choice for a WebSocket as application layer communication protocol has the advantage that the session concept is intrinsically introduced with the communication protocol. All S2 communication happens in the context of a (stateful) S2 session which is catered for by the WebSocket session. So, the S2 session matches the WebSocket session. +The client **must** perform the following checks during this request: + +| Check | How to proceed if check fails | +| --- | --- | +| Check certificate | Websocket connection failed, do not proceed with the connection attempt | +| If self signed certificate, check is server is local | Websocket connection failed, do not proceed with the connection attempt | +| Check if certificate is pinned | Websocket connection failed, do not proceed with the connection attempt | + +If no checks fail the client **should** proceed to the next step. + + ### Authentication For each S2 WebSocket session the client **must** authenticate itself using the commToken in the authorization header of the websocket connection request, following [RFC 6750 - The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://datatracker.ietf.org/doc/html/rfc6750). From 0b042a9040212f3aa898af132443fd80ae9b0749 Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Tue, 6 Jan 2026 13:59:18 +0100 Subject: [PATCH 15/26] Expanded S2 connection initiation section --- .../discovery-pairing-authentication.md | 163 ++++++++++++------ 1 file changed, 109 insertions(+), 54 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 15cdc33..002f9b0 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -115,7 +115,7 @@ There are two remaining vulnerable situations for the described protocol. In thi #### Self signed certificates -In the case that a local RM and a local SEM communicate, it is not possible to generate a PKI-certificate that can be publicly validated. As a result, S2 accepts, **ONLY** in this situation, self-signed certificates. The risk for spoofing attacks are mitigated by sharing the fingerprint during the pairing phase and pinning the self signed certificate at the client side. In these situations, part of the certificate fingerprint will also be shared in the pairing token. As a result, the client can check for all connections whether or not it is connected with the correct server. +In the case that a local RM and a local CEM communicate, it is not in every situation possible to generate a PKI-certificate that can be publicly validated. As a result, S2 accepts, **ONLY** in this situation, self-signed certificates. The risk for spoofing attacks are mitigated by including the certificate fingerprint in the challenge-response process as part of the pairing process, and pinning the self signed CA certificate at the client side. As a result, the client can check for all connections whether or not it is connected with the correct server. #### Trust relations between the end-user and the Client/Server @@ -251,7 +251,7 @@ It should be noted that pairing and communication are two separate HTTP interfac Communication interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN-LAN deployments self-signed certificates are used. For more information about the use of self-signed certificates, check [Trusting a self signed root certificate](###Trusting-a-self-signed-root-certificate) -After the HTTP interaction a WebSocket is established. The S2 communication server is always the WebSocket server. This server must use the same TLS certificate as the HTTP server. +After the HTTP interaction a WebSocket is established (other transport protocols will be added in the future). The S2 communication server is always the WebSocket server. This server must use the same TLS certificate as the HTTP server. ### Unpairing @@ -571,7 +571,7 @@ The client sends the following information (for full details see the OpenAPI spe | `s2ClientNodeDescription` | Information about the S2 node that wants to pair, such as brand, logo and type. Important fields include `id` (the S2 node ID) and `role` of the initiator S2 node | | `s2ClientEndpointDescription` | Information about the client S2 endpoint. An important field is the deployment. | | `pairingS2NodeID` | The pairing S2 node ID of the node that is being targeted (this field can be omitted if the endpoint only represents one S2 node) | -| `supportedCommunicationProtocols` | List of supported communications protocols of the client (**must** always contain WebSockets) | +| `supportedCommunicationProtocols` | List of supported communications protocols of the client | | `supportedS2MessageVersions` | List of supported S2 message versions by the client | | `supportedHmacHashingAlgorithms` | List of supported hashing algorithms for the challenge response function (currently only `SHA256` is supported and **must** be present) | | `clientHmacChallenge` | The challenge of the client for the challenge response process (see [Challenge response process](#challenge-response-process) | @@ -770,16 +770,14 @@ If the server receives wrong HTTP request (e.g. `/postConnectionDetails` while i After two nodes have been paired, the nodes exchange S2 messages over a secure connection. -The following mechanism **must** be used to initiate a secure connection between two S2 nodes. Client authentication is based on a one-time use communication token that needs to be renewed every time a new S2 session is created. +The following mechanism **must** be used to initiate a secure connection between two S2 nodes. Client authentication is based on a one-time use communication token that needs to be renewed every time a new S2 session is created. The S2 communication client will always attempt to set up an S2 connecting with the S2 communication server when there is no connection. For more details see [Reconnection strategy](#reconnection-strategy). ## Mapping the CEM and RM to communication server or client -TODO update, veplaatsen naar normatieve gedeelte +The CEM and RM roles defined by the S2 protocol are distinct from the Server and Client roles of the S2 pairing process. The following rules apply to determine whether the RM or CEM acts as a client or server for the communication initialization. -The CEM and RM roles defined by the S2 protocol are distinct from the Server and Client roles of the S2 pairing process. The following rules apply to determine whether the RM or CEM acts as a Client or Server in the pairing process. - -* If a connection is set up between a cloud/WAN node and a local node, the cloud/WAN node must act as an S2 Server Node, and the local node must act as an S2 Client Node. -* If a connection is set up between two nodes that are similarly deployed (i.e. both in cloud/WAN, or both local), the CEM must act as an S2 Server Node, and the RM must act as an S2 Client Node. +* If a connection is set up between a WAN S2 node and a LAN S2 node, the WAN S2 node must act as an S2 communication server, and the local node must act as an S2 communication client. +* If a connection is set up between two nodes that are similarly deployed (i.e. both in WAN, or both in LAN), the CEM must act as an S2 communication server, and the RM must act as an S2 communication client. There are four scenarios for CEM and RM deployment, and applying the rules above yields the following: @@ -790,9 +788,11 @@ There are four scenarios for CEM and RM deployment, and applying the rules above | LAN | WAN | S2 communication client | S2 communication server | | LAN | LAN | S2 communication server | S2 communication client | -Note: A device developed solely for use as an RM in a local setup will never function as an S2 communication server. Similarly, a device designed exclusively for use as a CEM in a WAN deployment will never operate as an S2 communication client. +> Note: A device developed solely for use as an RM in a LAN setup will never function as an S2 communication server. + +## Connection initiation -## Initiation +During the pairing process an `accessToken` is generated by the S2 node which will be the S2 communication server and sent to the S2 node that will be the S2 communication client. This `accessToken` can be used by the S2 communication client to set up a session with the S2 communication server for exchanging S2 messages. Each time a new connection is made the `accessToken` will be renewed. In other words, an `accessToken` can only be used once to set up a connection. The S2 communication server will generate a new `accessToken` and sends it to the S2 communication client. Since this `accessToken` is the only means to connect two S2 nodes once they are paired, the connection initiation process makes sure that both S2 nodes confirm that they have successfully persisted the new `accessToken` before invalidating the old `accessToken`. ![connection initiation](/img/communication-layer/connection-initiation.png) @@ -801,99 +801,154 @@ Note: A device developed solely for use as an RM in a local setup will never fun ``` @startuml -Client -> Server : 1. initiateConnection(s2ClientNodeId, accessToken,\n supportedCommunicationProtocols, supportedS2Versions) -Server -> Server : 2. check protocol version, generate new token pair -Server -> Client : 3. initiateConnectionResponse(s2ServerNodeId,\npendingAccessToken, commToken, connectionUrl,\nselectedCommunicationProtocol, selectedS2Version) -Client -> Client : 4. store new accessToken -Client -> Server : 5. setUpConnection(commToken) -Server -> Server : 6. activate new accessToken for this NodeId -Client <-> Server : 7. Successful S2 session -Client -> Client : 8. accept new accessToken +participant "HTTP Client" as Client +participant "HTTP Server" as Server + +Client->Server++: 1. GET / (index containing pairing API versions) +Server-->Client: 2. Response status 200 +deactivate Server +Client->Client: 3. Decide pairing version + +Client->Server++: 4. POST /[version]/initiateConnection +Server->Server: 5. Generate new pending accessToken +Server-->Client--: 6. Response status 200 +Client->Client: 7. Store pending accessToken +Client->Server++: 8. POST /[version]/confirmAccessToken +Server->Server: 9. Activate new accessToken for this S2 node ID +Server-->Client--: 10. Response status 200 +Client -> Client : 11. Accept new accessToken @enduml ``` -**1. initiateConnection** +### 0. Precondition + +Before an S2 node can be paired, it needs to things. + +1. The HTTP server and the HTTP client can only start with a communication request when they are fully initialized and have all the details of the S2 nodes it represents available. +2. The HTTP client must have the base URL of the connection API (e.g. `https://hostname.local/connection/`) +3. The two S2 nodes must have been paired successfully and must have an accessToken for this pairing + +If the HTTP client does not fulfill these preconditions, it **cannot** send the first HTTP request of the connection process. -The client has a list of pairs of accessTokens and commTokens. After the initial initiateConnection call, the list always contains at least one pair (initially there is only an accessToken as result of the pairing process). It uses one of the accessTokens to make an authorized request to retrieve a commToken and new accessToken form the server. +### 1. GET / (index containing communication API versions) +Since the HTTP client does not know which major versions of the communication API are implemented by the server, it must first do a GET request to the index (e.g. `https://hostname.local/pairing/`). The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Initiation is failed, do not proceed with the initiation attempt | -| If self signed certificate, check is server is local | Initiation is failed, do not proceed with the initiation attempt | -| Check if certificate is pinned | Initiation is failed, do not proceed with the initiation attempt | +| Check certificate | Do not proceed with connection, try again later | +| If self signed certificate, check if server is local | Do not proceed with connection, try again later | If no checks fail the client **should** proceed to the next step. +### 2. Response status 200 +The server responds with a list of implement major versions of the pairing API. It is formatted as a JSON array contains all the supported version of the pairing API (e.g. `["v1"]`). +If the HTTP client does not support any of the provided versions, it means that the two S2 endpoints are not compatible, and that connection is not possible. +### 3. Decide communication API version +From the provided list of major versions of the communication API, the HTTP client must select one that is implement by the HTTP client itself (typically the highest supported version). -**2. generate new token pair** +### 4. POST /[version]/initiateConnection -For each paired NodeId the server saves an active accessToken and active commToken. In addition to that, the server also has a list for pending tokens. This list contains entries, each consisting of an accessToken, a commToken, a NodeId and a timestamp. -The server checks the provided accessToken from the client with the active accessToken it has saved for this node. If the provided accessToken was valid, the server generates a new accessToken and commToken and saves this together with the S2NodeId and the current time as in entry in the list of pending tokens. +Since there are situations in which the client cannot know for sure which `accessToken` the S2 communication server uses for this pairing, the S2 communication client must keep a persisted list of `accessTokens` (which will typically contain only one `accessToken`). -The server **must** perform the following checks during this request: +The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Verify `accessToken` | respond with status code ??? | - +| Check certificate | Initiation is failed, do not proceed with the initiation attempt | +| If self signed certificate, check is server is local | Initiation is failed, do not proceed with the initiation attempt | +| Check if certificate is pinned | Initiation is failed, do not proceed with the initiation attempt | -If no checks fail the server **should** proceed to the next step. +If no checks fail the client **should** proceed to the next step. +The client sends the following information (for full details see the OpenAPI specification file). In addition, the `accessToken` is sent through a header. -**3. initiateConnection response** +| Information | Description | +| --- | --- | +| `s2ClientNodeId` | The S2 node ID of the S2 communications client that wants to connect to the server. | +| `s2ServerNodeId` | The S2 node ID of the S2 communications server that the client wants to connect to. | +| `s2ClientNodeDescription` | Information about the S2 node, such as brand, logo and type. This only needs to be provided if the S2 communication client wants to update this information, otherwise the S2 communication server will assumer the stored information is still valid. | +| `s2ClientEndpointDescription` | Information about the client S2 endpoint. This only needs to be provided if the S2 communication client wants to update this information, otherwise the S2 communication server will assumer the stored information is still valid. | +| `supportedCommunicationProtocols` | List of supported communications protocols of the client | +| `supportedS2MessageVersions` | List of supported S2 message versions by the client | -a. If the provided accessToken was valid, the server sends the generated pending accessToken, commToken and connectionUrl to the client. +The server **must** perform the checks in the table below to make sure that it can proceed with this request. If one of these checks fail, the server should respond with an HTTP status 400 and a `CommunicationDetailsErrorMessage` or with HTTP status 401. The contents of the `additionalInfo` field of the `CommunicationDetailsErrorMessage` is supposed the be helpful and up to the implementer. -b. If the provided accesToken was not valid, the server responds with an error. The process terminates. If the client has multiple accessTokens stored, it can retry with another one. +| Check | Response | What should the client do with this message? | +| --- | --- | --- | +| Is the request properly formatted and does it follow the schema? | `CommunicationDetailsErrorMessage` with errorMessage `Parsing error` | Retry later | +| Was this S2 node ID paired with this S2 node, but was it unpaired? | `CommunicationDetailsErrorMessage` with errorMessage `No longer paired` | Do not retry, inform end user | +| Is this `s2ClientNodeId` paired with the `s2ServerNodeId`? | Status code 401 | Do not retry, inform end user | +| Is the `s2ServerNodeId` known? | Status code 401 | Do not retry, inform end user | +| Is this the correct `accessToken` for this S2 node ID? | Status code 401 | Do not retry, inform end user | +| Is there overlap between the communication protocols? | `CommunicationDetailsErrorMessage` with errorMessage `Incompatible communication protocols` | Retry later | +| Is there overlap between the S2 message versions? | `CommunicationDetailsErrorMessage` with errorMessage `Incompatible S2 message versions` | Retry later | +| Are the S2 endpoint and S2 node ready for pairing? | `CommunicationDetailsErrorMessage` with errorMessage `Other` | Retry later | -**4. store new accessToken** +### 5. Generate new pending `accessToken` -a. The client has a list of accessTokens. It adds the new accessToken to the list, but does not yet remove the old one. +For each paired S2 node the server saves an active `accessToken`. In addition to that, the server also has a list for pending `accessToken`s, that were generated but not yet confirmed by the client. This list contains entries, each consisting of an `accessToken`, the S2 node IDs of the client and server S2 nodes and a timestamp. -b. If the client was not able to persist the new accessToken (e.g. because the storage device or the DBMS is not available) the client does not proceed with the process. Once the client is able to persist accessTokens again, it can retry to set up a connection from the start. +The server generates a new `accessToken` and saves this together with the S2 node ID and the current time as in entry in the list of pending tokens. The `accessToken` **must** be generated by a cryptographically secure pseudorandom number generator. -**5. setUpConnection(commToken)** +### 6. Response status 200 -The client initiates the S2 session, and provides the commToken. +In the request the client supplied a list of supported communication protocols and S2 messages versions. The server must select one of the options that were provided by the client. -The client **must** perform the following checks during this request: +The server sends the following information (for full details see the OpenAPI specification file). -| Check | How to proceed if check fails | +| Information | Description | | --- | --- | -| Check certificate | Initiation is failed, do not proceed with the initiation attempt | -| If self signed certificate, check is server is local | Initiation is failed, do not proceed with the initiation attempt | -| Check if certificate is pinned | Initiation is failed, do not proceed with the initiation attempt | +| `selectedCommunicationProtocol` | The communication protocol that was selected by the server | +| `selectedS2MessageVersion` | The S2 message version that was selected by the server | +| `accessToken` | The newly generated pending `accessToken` | +| `serverS2NodeDescription` | Information about the S2 node at the server, such as brand, logo and type. This only needs to be provided if the S2 communication server wants to update this information, otherwise the S2 communication client will assumer the stored information is still valid. | +| `serverS2EndpointDescription` | Information about the server S2 endpoint. This only needs to be provided if the S2 communication server wants to update this information, otherwise the S2 communication client will assumer the stored information is still valid. | -If no checks fail the client **should** proceed to the next step. - -**6. Activate new accessToken for this NodeId** +The client **must** perform the checks in the table below to make sure that it can proceed with this request. -This step is the implicit acknowledgement to the server that the client has saved the accessToken successfully. +| Check | What should the client do? | +| --- | --- | +| Is the request properly formatted and does it follow the schema? | Do not proceed and try again later with step 1 | +| Was the selected S2 message version offered in the request? | Do not proceed and try again later with step 1 | +| Was the selected communication protocol offered in the request? | Do not proceed and try again later with step 1 | -a. If the provided commToken is in the list pending tokens, and the token was generated not more than 30 seconds ago, the server replaces the associated accessToken for the associated NodeId as the active token. Also, the entry is removed from the list of pending tokens. +### 7. Store pending accessToken +It client adds the pending `accessToken` to its list of `accessTokens`, but does not yet remove the old one. If the client is not able to persist the pending `accessToken` (e.g. because the storage device or the DBMS is not available), the client does not proceed with the process. Once the client is able to persist `accessTokens` again, it can retry to set up a connection starting with step 1. -b. If the provided commToken is not in the list of pending tokens, the server must not accept the connection and respond with an error code. +### 8. POST /[version]/confirmAccessToken +The client confirms to the server that it has successfully persisted the pending `accessToken`. The **pending** `accessToken` is provided through the header of the request. -c. If the server is not able to active the new tokens (e.g. because the storage device or the DBMS is not available), the server must not accept the connection and responds with an error code. +The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Verify `commToken` | respond with status code ??? | +| Check certificate | Do not proceed with connection, try again later | +| If self signed certificate, check if server is local | Do not proceed with connection, try again later | +| Check if certificate is pinned | Do not proceed with connection, try again later | + +If no checks fail the client **should** proceed. + +### 9. Activate new `accessToken` for this S2 node ID + +If the provided `accessToken` is in the list pending `accessToken`s, and the token was generated not more than 30 seconds ago, the server now makes the pending `accessToken` the active `accessToken` for this pairing of S2 nodes. Also, the entry is removed from the list of pending `accessToken`s. + +If the provided `accessToken` is not in the list of pending `accessTokens`s, the server must not accept the connection and respond with status 401. The client can try again later starting at step 1. +If the server is not able to active the new `accessToken` (e.g. because the storage device or the DBMS is not available), the server must not accept the connection and responds with an error code 500. The client can try again later starting at step 1. -**7. Successful S2 session?** +## 7. Successful S2 session? a. The S2 session is successfully opened. This is an implicit acknowledgement that both server and the client are in possession of the new tokens. For the server it is an acknowledgement that is has activated the new access token successfully and for the client it means that it did correctly receive and persist the new accessToken. b. If for any reason the S2 session is not set up successfully, the process terminates. The client can restart the process from the beginning when desired. -**8. Remove old tokens** +## 8. Remove old tokens a. Since the server implicitly acknowledged the new accessToken, all pairs of accessToken and commToken which do not contain the used commToken can be removed. From b62ddc39d31c01b904e1d0b08633a8adcb2d1e70 Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Fri, 9 Jan 2026 11:34:27 +0100 Subject: [PATCH 16/26] Further expanded pairing documentation --- .../discovery-pairing-authentication.md | 93 +++++++++--------- .../connection-initiation.png | Bin 31557 -> 15217 bytes 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 002f9b0..4c3f97a 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -67,10 +67,8 @@ Serialization: json. **Why not oAuth 2.0?** The short answer is: oAuth is mainly designed for accessing protected resources in the cloud and since the S2 CEM and RM would also need to be able to pair on a local network (even without requiring internet access) oAuth 2.0 is simply not a good fit. We have identified a way to make it work but since it is such non-typical way, we choose not to use oAuth 2.0. -For the long answer, please refer to [this page](why-not-oauth.md). - - +For the long answer, please refer to [this page](why-not-oauth.md). ## Security requirements @@ -123,21 +121,19 @@ The entire trust model of S2 is based on the fact that there is already a trust # Terms and definitions (normative) -TODO waar handig linkjes toevoegen naar sections met meer uitleg - This specification uses the concepts that are defined below. | Term | Definition | | --- | --- | -| Access token | A token that is used for setting up an S2 connection. It random binary data and must be generated by a cryptographically secure pseudorandom number generator and have a minimum length of 32 bytes. It is encoded using Base64. | +| Access token | A token that is used for setting up an S2 connection. It random binary data and must be generated by a cryptographically secure pseudorandom number generator and have a minimum length of 32 bytes. It is encoded using Base64. Also see [11A. Response status 200](#11a-response-status-200). | | End user | A person or entity that manages S2 nodes. For the purpose of this specification it is assumed that there is already a trust relationship in place between this person and the S2 nodes. The means that the way the trust relationship has been established is out of scope for this specification. | -| End user environment | An restricted area within an application that contains all the S2 nodes that belong to the end user. | -| Initiator S2 node | The S2 node that that takes the initiative to pair with a responder S2 node. This is typically the S2 node from which the user initiates the pairing process. It is the counterpart of the responder S2 node. | +| End user environment | An restricted area within an application that contains all the S2 nodes that belong to the end user. Also see [Deployment of S2 nodes](#deployment-of-s2-nodes). | +| Initiator S2 node | The S2 node that that takes the initiative to pair with a responder S2 node. This is typically the S2 node from which the user initiates the pairing process. It is the counterpart of the responder S2 node. Also soo [Pairing and unpairing from the perspective of the end user](#pairing-and-unpairing-from-the-perspective-of-the-end-user). | | Pairing attempt | The process of pairing two S2 nodes. The process can be completed successfully or unsuccessfully. | -| Pairing code | The pairing code is the string of characters the end user has to copy from the responder S2 node user interface to the initiator S2 node user interface, in order to pair the two S2 nodes. The pairing code consist of a pairing token and when required a pairing S2 node ID. | -| Pairing S2 node ID | A short identifier for an S2 node, which is unique in the context of a single S2 endpoint. | -| Pairing token | A secret string of characters, which acts as a proof of the trust relationship between the end user and an S2 node | -| Responder S2 Node | The S2 node that that responds to a request to pair. This is the S2 node that issued the pairing code. It is the counterpart of the initiator S2 node. | +| Pairing code | The pairing code is the string of characters the end user has to copy from the responder S2 node user interface to the initiator S2 node user interface, in order to pair the two S2 nodes. The pairing code consist of a pairing token and when required a pairing S2 node ID. Also see [The pairing token, the pairing S2 node ID and the pairing code](#the-pairing-token-the-pairing-s2-node-id-and-the-pairing-code). | +| Pairing S2 node ID | A short identifier for an S2 node, which is unique in the context of a single S2 endpoint. Also see [The pairing token, the pairing S2 node ID and the pairing code](#the-pairing-token-the-pairing-s2-node-id-and-the-pairing-code). | +| Pairing token | A secret string of characters, which acts as a proof of the trust relationship between the end user and an S2 node. Also see [The pairing token, the pairing S2 node ID and the pairing code](#the-pairing-token-the-pairing-s2-node-id-and-the-pairing-code). | +| Responder S2 Node | The S2 node that that responds to a request to pair. This is the S2 node that issued the pairing code. It is the counterpart of the initiator S2 node. Also soo [Pairing and unpairing from the perspective of the end user](#pairing-and-unpairing-from-the-perspective-of-the-end-user). | | S2 endpoint | A service which can handle pairing requests or initiates pairing requests itself. An S2 endpoint can represent one S2 node, but could also represent many. | | S2 communication client | The S2 nodes which behaves as the HTTP client when initiating an S2 connection. | | S2 communication server | The S2 nodes which behaves as the HTTP server when initiating an S2 connection. | @@ -548,7 +544,7 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | | Check certificate | Pairing is failed, do not proceed with the pairing attempt | -| If self signed certificate, check is server is local | Pairing is failed, do not proceed with the pairing attempt | +| If self signed certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | | Store fingerprint of certificate for later check | | If no checks fail the client **should** proceed to the next step. @@ -568,8 +564,8 @@ The client sends the following information (for full details see the OpenAPI spe | Information | Description | | --- | --- | -| `s2ClientNodeDescription` | Information about the S2 node that wants to pair, such as brand, logo and type. Important fields include `id` (the S2 node ID) and `role` of the initiator S2 node | -| `s2ClientEndpointDescription` | Information about the client S2 endpoint. An important field is the deployment. | +| `clientS2NodeDescription` | Information about the S2 node that wants to pair, such as brand, logo and type. Important fields include `id` (the S2 node ID) and `role` of the initiator S2 node | +| `clientS2EndpointDescription` | Information about the client S2 endpoint. An important field is the deployment. | | `pairingS2NodeID` | The pairing S2 node ID of the node that is being targeted (this field can be omitted if the endpoint only represents one S2 node) | | `supportedCommunicationProtocols` | List of supported communications protocols of the client | | `supportedS2MessageVersions` | List of supported S2 message versions by the client | @@ -582,7 +578,7 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | | Check certificate | Pairing is failed, do not proceed with the pairing attempt | -| If self signed certificate, check is server is local | Pairing is failed, do not proceed with the pairing attempt | +| If self signed certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | | Check if same fingerprint is used as previous request | Pairing is failed, do not proceed with the pairing attempt | If no checks fail the client **should** proceed to the next step. @@ -622,8 +618,8 @@ The server responds with the following information (for full details see the Ope | Information | Description | | --- | --- | | `pairingAttemptId` | The generated identifier for this pairing attempt | -| `s2ServerNodeDescription` | Information about the S2 node that is being targeted, such as brand, logo and type. Important fields include `id` (the S2 node ID) and `role` of the responder S2 node | -| `s2ServerEndpointDescription` | Information about the server S2 endpoint. An important field is the deployment. | +| `serverS2NodeDescription` | Information about the S2 node that is being targeted, such as brand, logo and type. Important fields include `id` (the S2 node ID) and `role` of the responder S2 node | +| `serverS2EndpointDescription` | Information about the server S2 endpoint. An important field is the deployment. | | `selectedHmacHashingAlgorithm` | The hashing algorithm for the challenge response function as selected in step 2 | | `clientHmacChallengeResponse` | The response to the challenge provided by the HTTP client as calculated in step 2 | | `serverHmacChallenge` | The challenge created by the HTTP server for the challenge response process (see [Challenge response process](#challenge-response-process).) | @@ -667,7 +663,7 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | | Check certificate | Pairing is failed, do not proceed with the pairing attempt | -| If self signed certificate, check is server is local | Pairing is failed, do not proceed with the pairing attempt | +| If self signed certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | | Check if certificate is pinned | Pairing is failed, do not proceed with the pairing attempt | If no checks fail the client **should** proceed to the next step. @@ -707,7 +703,7 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | | Check certificate | Pairing is failed, do not proceed with the pairing attempt | -| If self signed certificate, check is server is local | Pairing is failed, do not proceed with the pairing attempt | +| If self signed certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | | Check if certificate is pinned | Pairing is failed, do not proceed with the pairing attempt | If no checks fail the client **should** proceed to the next step. @@ -739,7 +735,7 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | | Check certificate | Pairing is failed, do not proceed with the pairing attempt | -| If self signed certificate, check is server is local | Pairing is failed, do not proceed with the pairing attempt | +| If self signed certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | | Check if certificate is pinned | Pairing is failed, do not proceed with the pairing attempt | If no checks fail the client **should** proceed to the next step. @@ -763,7 +759,7 @@ If the HTTP server was using a self-signed certificate, the HTTP client can now ### Interruption of the process A pairing attempt has a maximum duration of 15 seconds. That means that once a `pairingAttemptId` has been issued, this `pairingAttemptId` cannot be used after 15 seconds since it was issued. From the perspective of the HTTP server, any pairing attempt that is not completed in 15 seconds (with success or not) is considered a failed attempt. From the perspective of the HTTP client, if the server does not respond within 15 seconds since it received the `pairingAttemptId`, it must consider the pairing attempt as failed. If the HTTP client wants to make another attempt, it should start again at step 1 or step 4. -### Wrong interactions +### Invalid interactions If the server receives wrong HTTP request (e.g. `/postConnectionDetails` while it was expecting `/requestConnectionDetails`) or when it receives the requests in the wrong order (e.g. `/finalizePairing` with `success` = `true` before calling `/requestConnectionDetails`) it **must** respond with an status 400 and consider the pairing attempt as failed. The only exception is receiving the same request twice. # S2 Connection (normative) @@ -816,7 +812,7 @@ Client->Client: 7. Store pending accessToken Client->Server++: 8. POST /[version]/confirmAccessToken Server->Server: 9. Activate new accessToken for this S2 node ID Server-->Client--: 10. Response status 200 -Client -> Client : 11. Accept new accessToken +Client -> Client : 11. Remove old accessToken @enduml ``` @@ -861,7 +857,7 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | | Check certificate | Initiation is failed, do not proceed with the initiation attempt | -| If self signed certificate, check is server is local | Initiation is failed, do not proceed with the initiation attempt | +| If self signed certificate, check if server is local | Initiation is failed, do not proceed with the initiation attempt | | Check if certificate is pinned | Initiation is failed, do not proceed with the initiation attempt | If no checks fail the client **should** proceed to the next step. @@ -870,10 +866,10 @@ The client sends the following information (for full details see the OpenAPI spe | Information | Description | | --- | --- | -| `s2ClientNodeId` | The S2 node ID of the S2 communications client that wants to connect to the server. | -| `s2ServerNodeId` | The S2 node ID of the S2 communications server that the client wants to connect to. | -| `s2ClientNodeDescription` | Information about the S2 node, such as brand, logo and type. This only needs to be provided if the S2 communication client wants to update this information, otherwise the S2 communication server will assumer the stored information is still valid. | -| `s2ClientEndpointDescription` | Information about the client S2 endpoint. This only needs to be provided if the S2 communication client wants to update this information, otherwise the S2 communication server will assumer the stored information is still valid. | +| `clientS2NodeId` | The S2 node ID of the S2 communications client that wants to connect to the server. | +| `serverS2NodeId` | The S2 node ID of the S2 communications server that the client wants to connect to. | +| `clientS2NodeDescription` | Information about the S2 node, such as brand, logo and type. This only needs to be provided if the S2 communication client wants to update this information, otherwise the S2 communication server will assumer the stored information is still valid. | +| `clientS2EndpointDescription` | Information about the client S2 endpoint. This only needs to be provided if the S2 communication client wants to update this information, otherwise the S2 communication server will assumer the stored information is still valid. | | `supportedCommunicationProtocols` | List of supported communications protocols of the client | | `supportedS2MessageVersions` | List of supported S2 message versions by the client | @@ -883,9 +879,9 @@ The server **must** perform the checks in the table below to make sure that it c | --- | --- | --- | | Is the request properly formatted and does it follow the schema? | `CommunicationDetailsErrorMessage` with errorMessage `Parsing error` | Retry later | | Was this S2 node ID paired with this S2 node, but was it unpaired? | `CommunicationDetailsErrorMessage` with errorMessage `No longer paired` | Do not retry, inform end user | -| Is this `s2ClientNodeId` paired with the `s2ServerNodeId`? | Status code 401 | Do not retry, inform end user | -| Is the `s2ServerNodeId` known? | Status code 401 | Do not retry, inform end user | -| Is this the correct `accessToken` for this S2 node ID? | Status code 401 | Do not retry, inform end user | +| Is this `clientS2NodeId` paired with the `serverS2NodeId`? | Status code 401 | Try with other `accessToken` if possible. Otherwise do not retry, inform end user | +| Is the `serverS2NodeId` known? | Status code 401 | Try with other `accessToken` if possible. Otherwise do not retry, inform end user | +| Is this the correct `accessToken` for this S2 node ID? | Status code 401 | Try with other `accessToken` if possible. Otherwise do not retry, inform end user | | Is there overlap between the communication protocols? | `CommunicationDetailsErrorMessage` with errorMessage `Incompatible communication protocols` | Retry later | | Is there overlap between the S2 message versions? | `CommunicationDetailsErrorMessage` with errorMessage `Incompatible S2 message versions` | Retry later | | Are the S2 endpoint and S2 node ready for pairing? | `CommunicationDetailsErrorMessage` with errorMessage `Other` | Retry later | @@ -936,29 +932,32 @@ If no checks fail the client **should** proceed. ### 9. Activate new `accessToken` for this S2 node ID -If the provided `accessToken` is in the list pending `accessToken`s, and the token was generated not more than 30 seconds ago, the server now makes the pending `accessToken` the active `accessToken` for this pairing of S2 nodes. Also, the entry is removed from the list of pending `accessToken`s. +If the provided `accessToken` is in the list pending `accessToken`s, and the token was generated not more than **15 seconds** ago, the server now makes the pending `accessToken` the active `accessToken` for this pairing of S2 nodes (thereby invalidating the old `accessToken`). Also, the entry is removed from the list of pending `accessToken`s. If the provided `accessToken` is not in the list of pending `accessTokens`s, the server must not accept the connection and respond with status 401. The client can try again later starting at step 1. If the server is not able to active the new `accessToken` (e.g. because the storage device or the DBMS is not available), the server must not accept the connection and responds with an error code 500. The client can try again later starting at step 1. -## 7. Successful S2 session? +### 10. Response status 200 -a. The S2 session is successfully opened. This is an implicit acknowledgement that both server and the client are in possession of the new tokens. For the server it is an acknowledgement that is has activated the new access token successfully and for the client it means that it did correctly receive and persist the new accessToken. +The S2 communication server sends the details and credentials to open a socket for communicating the S2 messages. The exact contents of this message depend on the selected communication protocol. In any case it will be a JSON object containing the field `communicationProtocol`. The presence on other fields will depend on the value of the `communicationProtocol` field. -b. If for any reason the S2 session is not set up successfully, the process terminates. The client can restart the process from the beginning when desired. +If the response is not understood by the S2 communication client, the client **should** retry later. -## 8. Remove old tokens +### 11. Remove old accessToken -a. Since the server implicitly acknowledged the new accessToken, all pairs of accessToken and commToken which do not contain the used commToken can be removed. +Step 10 functions as a confirmation to the S2 communication client that the S2 communication server has activated the new `accessToken` for this pairing. The old `accessToken` cannot be used anymore, so the S2 communication client must remove the old `accessToken` from the list of `accessToken`s. -b. If the client is not able to remove the old tokens (e.g. because the storage device or the DBMS is not available) it can continue working as normal. +### Interruption of the process +Once the S2 communication server has generated a new pending `accessToken`, it must be confirmed within 15 seconds by the S2 communication client. If this doesn't happen, a client will have to start the process from step 1 (or step 4) again. + +If the S2 communication client doesn't receive a response to confirming the new `accessToken` (step 10), it does not know if the server has activated the new `accessToken`, or if the old `accessToken` is still in place. It now has (at least) two `accessToken`s in its list, and does not know for certain which one is activate at the S2 communication server. If it ## WebSocket based communication This section specifies how to use WebSocket Secure as the S2-over-TCP/IP application layer protocol. -The WebSocket client **must** run on the S2ClientNode and the WebSocket server on the S2ServerNode. +The WebSocket client **must** run on the S2 communication client and the WebSocket server on the S2 communication server. The choice for a WebSocket as application layer communication protocol has the advantage that the session concept is intrinsically introduced with the communication protocol. All S2 communication happens in the context of a (stateful) S2 session which is catered for by the WebSocket session. So, the S2 session matches the WebSocket session. @@ -967,7 +966,7 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | | Check certificate | Websocket connection failed, do not proceed with the connection attempt | -| If self signed certificate, check is server is local | Websocket connection failed, do not proceed with the connection attempt | +| If self signed certificate, check if server is local | Websocket connection failed, do not proceed with the connection attempt | | Check if certificate is pinned | Websocket connection failed, do not proceed with the connection attempt | If no checks fail the client **should** proceed to the next step. @@ -981,7 +980,7 @@ For each S2 WebSocket session the client **must** authenticate itself using the Communication over the WebSocket endpoint **must** be encrypted following [RFC 6455 The WebSocket Protocol](https://datatracker.ietf.org/doc/html/rfc6455). S2 **MUST NOT** be sent over unencrypted channels. Therefore a wss connection (the URL starting with wss://) must be used. ### Compression - + The WebSocket Protocol ([RFC6455](https://datatracker.ietf.org/doc/html/rfc6455)) has an extension for compression: [**RFC 7692**](https://datatracker.ietf.org/doc/html/rfc7692.html) implementing so called per-message-deflate compression. https://datatracker.ietf.org/doc/html/rfc7692 RFC 7692 is widely supported by WebSocket libraries and and we are exchanging JSON plain text messages, it is expected to save a large amount of data. Therefore, implementations of S2 WebSockets **SHOULD** support RFC 7692 and **SHOULD** enable it whenever possible. @@ -1053,15 +1052,17 @@ WebSocketDisconnected --> [*] # Unpairing process (normative) -Unpairing can either be initiated by S2Node that runs the CEM or the RM. +Unpairing can be initiated by either S2 node, and **should** only be done when instructed by the end user. The S2 node that did not take the initiative to unpair **should** try to inform the end user that the S2 node is no longer paired. + +## Unpairing by the S2 communication client -As the pairing and authorization process is based on a client/server model, the following applies to the unpairing process. +If the S2 communication client takes the initiative to unpair, it first **should** close the S2 communication session regarding this pairing (if there is one). Then it **must** call the `/[version]/unpair` HTTP API endpoint of the server using its `accessToken` (after discovering the API version at the server, the same way as steps 1 to 3 as mentioned in [Connection initiation](#connection-initiation)). Upon receiving the unpair call from a client, the S2 server node **must** remove all security information related to this pairing. Then the client **must** remove all security information of the S2 communication server related to this pairing. -If the client node takes the initiative to unpair, it first **should** close any active connections it has and subsequently call the unpair HTTP API endpoint of the server using its access token. Upon receiving the unpair call from a client, the S2 server node **must** remove all security information related to the paired node. After the response has been received by the client, the client **must** remove all security information of the server node. +## Unpairing by the S2 communication server -If the server takes the initiative to unpair, first it **must** remove all security information related to client node. After that, it **should** send an S2 [SessionRequest](/model-reference/Common/SessionRequest/) message with type [RECONNECT](/model-reference/Common/SessionRequestType/) to the client. The next initiateConnection HTTP API request **must** fail with the 'Unknown node ID' response to let the client know that it is not paired anymore. +If the server takes the initiative to unpair, first it **must** remove all security information related to client node. After that, if there is an active S2 session, it **should** send an S2 [SessionRequest](/model-reference/Common/SessionRequest/) message with type [RECONNECT](/model-reference/Common/SessionRequestType/) to the client. The next `/[version]/initiateConnection` HTTP API request **must** fail with the `No longer paired` response to let the client know that it is not paired anymore. -Client and server **can** keep other (non-security) information for user experience purposes. +Client and server **can** keep other (non-security) information for, for example, user experience purposes. # Security (normative) diff --git a/website/static/img/communication-layer/connection-initiation.png b/website/static/img/communication-layer/connection-initiation.png index 91258e38556adf5361f647e26e0caaaa31390a5f..3192ccc4ad8b30c5c5a00b82f4e38562a5b336ad 100644 GIT binary patch literal 15217 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
6@Y(~IH`R9F*6 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 From 48660feb78016909e3d534135e8b19228938a57e Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Mon, 12 Jan 2026 11:10:21 +0100 Subject: [PATCH 17/26] Made TODO's a bit nicer, so the document is ready for a first review --- .../discovery-pairing-authentication.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 4c3f97a..58bea96 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -31,7 +31,8 @@ The protocol is designed to specify communication between two devices, a resourc # Background (informative) ## Requirements -TODO beter uitleggen wat doelen van deze oplossing zijn (bijv. afweging univormiteit vs. complexiteit) + +> TODO: This section needs to be rewritten to better explain the design goals The communication layer meets the following requirements: @@ -39,8 +40,6 @@ The Customer Energy Manager (CEM) and Resource Manager (RM) are logical concepts In addition to - and partly because of - supporting the various deployment options, the S2 communication layer has the following generic requirements: -TODO cleanup - - Support for full duplex communication. Both sides **MUST** be able to send and receive data simultaneously. - Communication **MUST** be IP based. - Communication **MUST** be encrypted. @@ -293,9 +292,7 @@ The major version is increased when non-backwards compatible changes are made. The major version of the API is embedded in the base URI of the API as `/v[major]` (e.g. `/v1`). HTTP server and HTTP clients can decide to implement several major version of the API in parallel to increase interoperability. In that case server must server all version on the same base URI (e.g. `https://hostname.local/pairing/v1/...` and `https://hostname.local/pairing/v2/...`). The server **must** always (even when it only supports one major version of the API) serve an index (e.g. `https://hostname.local/pairing/`) which returns a JSON array with all supported versions as they are defined as port of the URI (e.g. `["v1", "v2"]`). -## Versioning of S2 message schemas - -TODO +> TODO: Versioning of S2 JSON Schema's needs to be explained ## Addressing S2 endpoints (normative) The URI of the pairing and connection API are used in the discovery process, pairing process and connection process, as wel as the basis for TLS certificates. @@ -325,7 +322,7 @@ A CEM can be paired with multiple RM's a the same time. A RM can only be paired ## Discovery -TODO +> TODO: This section still notes to be expanded In order to ease the pairing process, which is specified below, the discovery process provides a way for nodes to find each other without requiring a user to know the pairing endpoint of the other node. In other words, the discovery process is a way to provide an S2 Node with the URL of another node which is needed to start the pairing process. Alternatively, it is always possible to initiate the pairing by manually providing the URL by the end user. @@ -455,7 +452,7 @@ Where: > This section is only applicable for LAN-LAN pairing -TODO checken/herschijven +> TODO: This section needs to be improved The user visits the S2ClientNodeUI and the S2ServerNode has been discovered (so the S2ServerNode base URL is known) by the the S2ClientNode per [discovery](#discovery) as specified above. The S2ClientNode does a preparePairing HTTP request to let the S2ServerNode know that there is an S2ClientNode that wants to pair. It is up to the S2ServerNode implementation to decide what to do with this signal. It can be used to display a pop-up with the pairing token in its UI to improve the user experience. It must be implemented by the client, but only when there is a clear distinction between the moment preparePairing is called and when requestPairing is called. When preparePairing is called, it is not guaranteed that a call to pairingRequest or cancelPreparePairing will follow so it is recommended to put a time-out on showing the pairing token in the S2ServerNodeUI. @@ -463,7 +460,7 @@ The user visits the S2ClientNodeUI and the S2ServerNode has been discovered (so > This section is only applicable for LAN-LAN pairing -TODO long-polling +> TODO: Long-polling still needs to be explained ## Pairing interaction @@ -1066,7 +1063,7 @@ Client and server **can** keep other (non-security) information for, for example # Security (normative) -TODO: nog iets opschrijven over bescherming tegen DOS aanvallen +> TODO: This section needs to be expanded to explain measures agains ddos Please refer to an extensive description of the security specifications to [Security considerations](./security-considerations.md). @@ -1081,4 +1078,4 @@ The server certificates **MUST** be exchanged and validated during the initiatio ## Cipher suites -Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. +Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. \ No newline at end of file From f20331b02f6fb0214f01ec4a85bd8c69b0343e15 Mon Sep 17 00:00:00 2001 From: Jorrit Nutma Date: Tue, 13 Jan 2026 15:41:17 +0100 Subject: [PATCH 18/26] Update URI references to URL in documentation --- .../discovery-pairing-authentication.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 58bea96..d402c28 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -290,16 +290,16 @@ The minor version is increased when backwards compatible changes are made. Be aw The major version is increased when non-backwards compatible changes are made. -The major version of the API is embedded in the base URI of the API as `/v[major]` (e.g. `/v1`). HTTP server and HTTP clients can decide to implement several major version of the API in parallel to increase interoperability. In that case server must server all version on the same base URI (e.g. `https://hostname.local/pairing/v1/...` and `https://hostname.local/pairing/v2/...`). The server **must** always (even when it only supports one major version of the API) serve an index (e.g. `https://hostname.local/pairing/`) which returns a JSON array with all supported versions as they are defined as port of the URI (e.g. `["v1", "v2"]`). +The major version of the API is embedded in the base URL of the API as `/v[major]` (e.g. `/v1`). HTTP server and HTTP clients can decide to implement several major version of the API in parallel to increase interoperability. In that case server must server all version on the same base URL (e.g. `https://hostname.local/pairing/v1/...` and `https://hostname.local/pairing/v2/...`). The server **must** always (even when it only supports one major version of the API) serve an index (e.g. `https://hostname.local/pairing/`) which returns a JSON array with all supported versions as they are defined as port of the URL (e.g. `["v1", "v2"]`). > TODO: Versioning of S2 JSON Schema's needs to be explained ## Addressing S2 endpoints (normative) -The URI of the pairing and connection API are used in the discovery process, pairing process and connection process, as wel as the basis for TLS certificates. +The URL of the pairing and connection API are used in the discovery process, pairing process and connection process, as wel as the basis for TLS certificates. -For WAN deployed S2 endpoints, the URI **must** be based on a DNS domain name. +For WAN deployed S2 endpoints, the URL **must** be based on a DNS domain name. -For LAN deployed S2 endpoint, the URI **must** be based on an mDNS alias or hostname (e.g. `hostname.local`). It is important that these names are *unique* and *stable*. Unique since there could be multiple instance within the same LAN, and stable because if it changes, the S2 endpoint cannot be found by other S2 endpoints. It should also be noted that the alias used by DNS-SD, and is presented to the end user. It recommended to choose a name that the end user should recognize and an element for the end user to make a distinction between two devices of the same type, such as a serial number. +For LAN deployed S2 endpoint, the URL **must** be based on an mDNS alias or hostname (e.g. `hostname.local`). It is important that these names are *unique* and *stable*. Unique since there could be multiple instance within the same LAN, and stable because if it changes, the S2 endpoint cannot be found by other S2 endpoints. It should also be noted that the alias used by DNS-SD, and is presented to the end user. It recommended to choose a name that the end user should recognize and an element for the end user to make a distinction between two devices of the same type, such as a serial number. # Pairing process (normative) @@ -678,7 +678,7 @@ The server responds with two pieces of information: | Information | Description | | --- | --- | -| `initiateConnectionUrl` | The base URI for the S2 connection process (does not include the version number) | +| `initiateConnectionUrl` | The base URL for the S2 connection process (does not include the version number) | | `accessToken` | The access token that was generated for this S2 node | If the response is understood and properly formatted, the HTTP client **should** proceed to the next step. Otherwise the HTTP client **must** stop the pairing attempt. It **must** attempt to inform the HTTP server of this by doing an HTTP request to `finalizePairing` where the value of `success` must be `false`. @@ -692,7 +692,7 @@ The HTTP sends the connection details to the HTTP server. This request also serv | Information | Description | | --- | --- | | `serverHmacChallengeResponse` | The response for the challenge responce process | -| `initiateConnectionUrl` | The base URI for the S2 connection process (does not include the version number) | +| `initiateConnectionUrl` | The base URL for the S2 connection process (does not include the version number) | | `accessToken` | The access token that was generated for this S2 node | The client **must** perform the following checks during this request: @@ -1078,4 +1078,4 @@ The server certificates **MUST** be exchanged and validated during the initiatio ## Cipher suites -Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. \ No newline at end of file +Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. From a156187a7290cfcfac4e6298fce5e02de67625f0 Mon Sep 17 00:00:00 2001 From: Jorrit Nutma Date: Tue, 13 Jan 2026 18:14:02 +0100 Subject: [PATCH 19/26] Use TLS more often to indicate more explicitly the certificate use + fixed self-signed typos + improved proper use of the term CA --- .../discovery-pairing-authentication.md | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 58bea96..d245c8a 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -110,9 +110,9 @@ Non-repudiation is not guaranteed in this protocol. Individual messages are not There are two remaining vulnerable situations for the described protocol. In this section both will be explained. -#### Self signed certificates +#### self-signed certificates -In the case that a local RM and a local CEM communicate, it is not in every situation possible to generate a PKI-certificate that can be publicly validated. As a result, S2 accepts, **ONLY** in this situation, self-signed certificates. The risk for spoofing attacks are mitigated by including the certificate fingerprint in the challenge-response process as part of the pairing process, and pinning the self signed CA certificate at the client side. As a result, the client can check for all connections whether or not it is connected with the correct server. +In the case that a local RM and a local CEM communicate, it is not in every situation possible to generate a PKI-certificate that can be publicly validated. As a result, S2 accepts, **ONLY** in this situation, self-signed certificates. The risk for spoofing attacks are mitigated by including the certificate fingerprint in the challenge-response process as part of the pairing process, and pinning the self-signed CA certificate at the client side. As a result, the client can check for all connections whether or not it is connected with the correct server. #### Trust relations between the end-user and the Client/Server @@ -160,9 +160,9 @@ S2 Nodes can be deployed locally within the LAN, or somewhere on a server in the There are three types of S2 connections between S2 nodes possible: -* **WAN-WAN**: A connection between two S2 nodes deployed in a WAN. Connecting between them is straightforward and can be done based on URLs, based on DNS domain names. It is possible to rely on common TLS certificates thanks to a public key infrastructure. +* **WAN-WAN**: A connection between two S2 nodes deployed in a WAN. Connecting between them is straightforward and can be done based on URLs, based on DNS domain names. It is possible to rely on TLS certificates that can be validated thanks to a public key infrastructure. * **WAN-LAN**: A connection between a LAN deployed S2 node a WAN deployed S2 node. Since there is almost always a firewall and/or NAT between these two, it is assumed that it is only possible to set up a connection from the LAN to the WAN; not the other way around. Connecting from the LAN S2 node to the WAN S2 node can be done based on a URL, and common TLS certificates can be used thanks to public key infrastructure. -* **LAN-LAN**: A connection between two LAN deployed S2 nodes. It is assumed that in this situation we cannot rely an internet connection, making it impossible to rely on a public key infrastructure for certificates. That is why for this type of connection self-signed TLS certificates are used. Connections are made based on hostnames that are resolved to IP-addresses using Multicast DNS (mDNS), since IP-addresses are not guaranteed to be stable. Discovering another node could be done using DNS Service Discovery (DNS-SD). +* **LAN-LAN**: A connection between two LAN deployed S2 nodes. It is assumed that in this situation we cannot rely an internet connection, making it impossible to rely on a public key infrastructure for TLS certificates. That is why for this type of connection self-signed TLS certificates are used. Connections are made based on hostnames that are resolved to IP-addresses using Multicast DNS (mDNS), since IP-addresses are not guaranteed to be stable. Discovering another node could be done using DNS Service Discovery (DNS-SD). ## Pairing and unpairing from the perspective of the end user The end user can take the initiative to *pair* a single CEM instance with a single RM instance. This process has to be started with one of the S2 nodes. Which node this is depends on the deployment and implementation decisions of the S2 node, but ideally it could be either one. The S2 node however needs to have a user interface. We'll call the S2 node that user uses to start the pairing process the *initiator*. We'll call the other S2 node the *responder*. @@ -232,7 +232,7 @@ The pairing process itself is completely based on HTTP REST. One S2 node behaves We'll refer to the endpoint that behaves as the HTTP server during the pairing process as the *S2 pairing server*, and the client as the *S2 pairing client*. -Pairing interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN deployments self-signed certificates are used. For more information about the use of self-signed certificates, check [Trusting a self signed root certificate](###Trusting-a-self-signed-root-certificate) +Pairing interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN deployments self-signed certificates are used. For more information about the use of self-signed certificates, check [Trusting a self-signed root certificate](###Trusting-a-self-signed-root-certificate) ### Communication @@ -244,7 +244,7 @@ We'll refer to the endpoint that behaves as the HTTP server during the communica It should be noted that pairing and communication are two separate HTTP interfaces, that don't have to be used in the same way. It could be that an S2 Node is an S2 pairing client, but then becomes an S2 communication server. This depends on the deployment of the s2 Nodes (see [Pairing details for different deployments](#pairing-details-for-different-deployments)). -Communication interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN-LAN deployments self-signed certificates are used. For more information about the use of self-signed certificates, check [Trusting a self signed root certificate](###Trusting-a-self-signed-root-certificate) +Communication interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN-LAN deployments self-signed certificates are used. For more information about the use of self-signed certificates, check [Trusting a self-signed root certificate](###Trusting-a-self-signed-root-certificate) After the HTTP interaction a WebSocket is established (other transport protocols will be added in the future). The S2 communication server is always the WebSocket server. This server must use the same TLS certificate as the HTTP server. @@ -252,7 +252,7 @@ After the HTTP interaction a WebSocket is established (other transport protocols Either S2 node can take the initiative to unpair from the other S2 node. This is done using the same HTTP OpenAPI specification and the same HTTP server and client as the communication. The details for unpairing differ depending if it is the S2 communication server or if it is the S2 communication client that initiates the unpairing process. -Unpairing interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN-LAN deployments self-signed certificates are used. For more information about the use of self-signed certificates, check [Trusting a self signed root certificate](###Trusting-a-self-signed-root-certificate) +Unpairing interaction is always TLS based (i.e. HTTPS is used). For WAN deployments, normal certificates (signed by a Certificate Authority) are being used. For LAN-LAN deployments self-signed certificates are used. For more information about the use of self-signed certificates, check [Trusting a self-signed root certificate](###Trusting-a-self-signed-root-certificate) ## Pairing details for different deployments @@ -364,11 +364,11 @@ When a pairing S2 node ID is used: The pairing code allows us to transfer two pieces of information by only bothering the end user once. Due to its format the initiator S2 node can easily extract the pairing S2 node ID and the pairing token from the pairing code by splitting the string at the dash. -## Certificates +## TLS Certificates -There are two possible types of certificates. The first option is a public server certificate, that is part of the public PKI infrastructure, (indirectly) signed by a public root CA. This protocol allows local servers to use a self signed CA certificate to sign its local server certificate. This is needed because a local server is not able to get a certificate from a public PKI infrastructure. +There are two possible types of certificates for TLS communication. The first option is using a public server certificate, that is created through a Public Key Infrastructure (PKI) and thus signed by a public CA. The other option (only applicable to LAN servers) is to use a self-signed certificate. The latter is needed because a LAN server is not able to obtain a certificate that has been issued by a CA for its local domain. -In the following image, the difference is shown. On the left a public root CA that's publicly known and trusted, on the right, a self signed root certificate, that's unknown and it's trustworthiness has to be achieved in another way. +In the following image, the difference is shown. On the left a public root CA that is publicly known and trusted, on the right, a self-signed root certificate, that is unknown and its trustworthiness has to be achieved in another way. ![image.png](/img/communication-layer/certificate-chains.png) @@ -395,17 +395,17 @@ SelfSignedCA --> LocalServerCertificate -### Trusting a self signed root certificate +### Trusting a self-signed root certificate -The self signed root certificate is by default not trusted. However during the pairing phase, the server with the self signed root certificate will share the fingerprint of the certificate during the pairing phase as part of the challenge. This will enable the client to verify the self signed root certificate, and create trust. From this moment on, the client will store the complete fingerprint of the self signed root certificate, and use it to verify the server certificate for all future connections. +The self-signed root certificate is by default not trusted. However during the pairing phase, the server with the self-signed root certificate will share the fingerprint of the certificate during the pairing phase as part of the challenge. This will enable the client to verify the self-signed root certificate, and create trust. From this moment on, the client will store the complete fingerprint of the self-signed root certificate, and use it to verify the server certificate for all future connections. -Note that the `preparePairing` and `cancelPreparePairing` endpoints can be called before the pairing has happened. So in the case the server is running on a LAN (and thus uses self-signed certificates), the client can skip the certificate validation steps on those endpoint. This means that the HTTP client **must** be configured to accept self-signed certificates during the pairing process. Since the pairing process consists out of several HTTP requests, the HTTP client **must** check that for every request the same self-signed certificate is used by the HTTP server. If this is not the case, the HTTP client **cannot** proceed with the request. +Note that the `preparePairing` and `cancelPreparePairing` endpoints can be called before the pairing has happened. So in the case the server is running on a LAN (and thus uses self-signed certificates), the client can skip the certificate validation steps on those endpoint. This means that the HTTP client **must** be configured to accept self-signed certificates during the pairing process. Since the pairing process consists of several HTTP requests, the HTTP client **must** check that for every request the same self-signed certificate is used by the HTTP server. If this is not the case, the HTTP client **cannot** proceed with the request. ### Updating the certificates -A server can update its certificate. When a cloud server updates it's certificate, it **MUST** be signed by a CA, so a client can check it's validity. A server **SHOULD** update its server certificate at least once every 6 months. +A server can update its certificate. When a cloud server updates its certificate, it **MUST** be signed by a CA, so a client can check its validity. A server **SHOULD** update its server certificate at least once every 6 months. If the server is in local-local mode, and uses a self-signed CA certificate, the CA certificate **SHOULD** be created with a validity period which is long enough for the expected lifetime of the server. If the used crypto for the the CA certificate is broken, or the lifetime of the server is longer than the validity of the certificate, the server **MUST** create a new self-signed CA certificate and all clients need to be paired again. Like cloud servers, a local server **SHOULD** update its server certificate at least once every 6 months. @@ -540,9 +540,9 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Pairing is failed, do not proceed with the pairing attempt | -| If self signed certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | -| Store fingerprint of certificate for later check | | +| Check TLS certificate | Pairing is failed, do not proceed with the pairing attempt | +| If self-signed TLS certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | +| Store fingerprint of TLS certificate for later check | | If no checks fail the client **should** proceed to the next step. @@ -574,8 +574,8 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Pairing is failed, do not proceed with the pairing attempt | -| If self signed certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | +| Check TLS certificate | Pairing is failed, do not proceed with the pairing attempt | +| If self-signed TLS certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | | Check if same fingerprint is used as previous request | Pairing is failed, do not proceed with the pairing attempt | If no checks fail the client **should** proceed to the next step. @@ -636,7 +636,7 @@ The HTTP client checks the `clientHmacChallengeResponse` provided by the HTTP se If the result is identical, the client **should** proceed to the next step. If the result is not identical, the client **must** stop the pairing attempt. It **must** attempt to inform the HTTP server of this by doing an HTTP request to `finalizePairing` where the value of `success` must be `false`. -Note that in case of a local server, the certificate fingerprint is part of the challenge. So if the challenge succeeds, the certificate fingerprint is correct, and the certificate can be trusted. The client **must** pin this certificate, and trust this certificate for future use. +Note that in case of a local server, the TLS certificate fingerprint is part of the challenge. So if the challenge succeeds, the certificate fingerprint is correct, and the certificate can be trusted. The client **must** pin this certificate, and trust this certificate for future use. ### 8. Calculate serverHmacChallengeResponse @@ -659,9 +659,9 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Pairing is failed, do not proceed with the pairing attempt | -| If self signed certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | -| Check if certificate is pinned | Pairing is failed, do not proceed with the pairing attempt | +| Check TLS certificate | Pairing is failed, do not proceed with the pairing attempt | +| If self-signed TLS certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | +| Check if TLS certificate is pinned | Pairing is failed, do not proceed with the pairing attempt | If no checks fail the client **should** proceed to the next step. @@ -699,9 +699,9 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Pairing is failed, do not proceed with the pairing attempt | -| If self signed certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | -| Check if certificate is pinned | Pairing is failed, do not proceed with the pairing attempt | +| Check TLS certificate | Pairing is failed, do not proceed with the pairing attempt | +| If self-signed TLS certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | +| Check if TLS certificate is pinned | Pairing is failed, do not proceed with the pairing attempt | If no checks fail the client **should** proceed to the next step. @@ -731,9 +731,9 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Pairing is failed, do not proceed with the pairing attempt | -| If self signed certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | -| Check if certificate is pinned | Pairing is failed, do not proceed with the pairing attempt | +| Check TLS certificate | Pairing is failed, do not proceed with the pairing attempt | +| If self-signed TLS certificate, check if server is local | Pairing is failed, do not proceed with the pairing attempt | +| Check if TLS certificate is pinned | Pairing is failed, do not proceed with the pairing attempt | If no checks fail the client **should** proceed to the next step. @@ -751,7 +751,7 @@ Receiving a `/finalizePairing` request marks the completion of the pairing attem ### 13. Response status 204 To confirm the successful completion of the paring attempt, the HTTP server responds to the client with HTTP status code 204. This response marks the completion of the pairing attempt for teh HTTP client. If the HTTP client issued an access token during this pairing attempt, it can now be used by an S2 communication client to set up an S2 connection. The `pairingAttemptId` can no longer be used by the HTTP client. -If the HTTP server was using a self-signed certificate, the HTTP client can now store the self-signed root CA certificate. The client **must** check that this is the CA certificate that is used for all future interaction with this S2 endpoint. The HTTP server is allowed to use a new self-signed server certificate, as long as it is signed by the self-signed CA certificate that was used during the pairing process. +If the HTTP server was using a self-signed TLS certificate, the HTTP client can now store the self-signed root certificate. The client **must** check that this is the CA certificate that is used for all future interaction with this S2 endpoint. The HTTP server is allowed to use a new self-signed server certificate, as long as it is signed by the self-signed CA certificate that was used during the pairing process. ### Interruption of the process A pairing attempt has a maximum duration of 15 seconds. That means that once a `pairingAttemptId` has been issued, this `pairingAttemptId` cannot be used after 15 seconds since it was issued. From the perspective of the HTTP server, any pairing attempt that is not completed in 15 seconds (with success or not) is considered a failed attempt. From the perspective of the HTTP client, if the server does not respond within 15 seconds since it received the `pairingAttemptId`, it must consider the pairing attempt as failed. If the HTTP client wants to make another attempt, it should start again at step 1 or step 4. @@ -832,8 +832,8 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Do not proceed with connection, try again later | -| If self signed certificate, check if server is local | Do not proceed with connection, try again later | +| Check TLS certificate | Do not proceed with connection, try again later | +| If self-signed TLS certificate, check if server is local | Do not proceed with connection, try again later | If no checks fail the client **should** proceed to the next step. @@ -853,9 +853,9 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Initiation is failed, do not proceed with the initiation attempt | -| If self signed certificate, check if server is local | Initiation is failed, do not proceed with the initiation attempt | -| Check if certificate is pinned | Initiation is failed, do not proceed with the initiation attempt | +| Check TLS certificate | Initiation is failed, do not proceed with the initiation attempt | +| If self-signed TLS certificate, check if server is local | Initiation is failed, do not proceed with the initiation attempt | +| Check if TLS certificate is pinned | Initiation is failed, do not proceed with the initiation attempt | If no checks fail the client **should** proceed to the next step. @@ -921,9 +921,9 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Do not proceed with connection, try again later | -| If self signed certificate, check if server is local | Do not proceed with connection, try again later | -| Check if certificate is pinned | Do not proceed with connection, try again later | +| Check TLS certificate | Do not proceed with connection, try again later | +| If self-signed TLS certificate, check if server is local | Do not proceed with connection, try again later | +| Check if TLS certificate is pinned | Do not proceed with connection, try again later | If no checks fail the client **should** proceed. @@ -962,9 +962,9 @@ The client **must** perform the following checks during this request: | Check | How to proceed if check fails | | --- | --- | -| Check certificate | Websocket connection failed, do not proceed with the connection attempt | -| If self signed certificate, check if server is local | Websocket connection failed, do not proceed with the connection attempt | -| Check if certificate is pinned | Websocket connection failed, do not proceed with the connection attempt | +| Check TLS certificate | Websocket connection failed, do not proceed with the connection attempt | +| If self-signed TLS certificate, check if server is local | Websocket connection failed, do not proceed with the connection attempt | +| Check if TLS certificate is pinned | Websocket connection failed, do not proceed with the connection attempt | If no checks fail the client **should** proceed to the next step. @@ -1069,12 +1069,12 @@ Please refer to an extensive description of the security specifications to [Secu ## Certificates -For each S2 connection the server authenticates using a certificate. The cloud implementation certificates **MUST** be PKI certificates which are not self-signed. Only local servers can use a self-signed CA certificate, which is used to sign a server certificate. -If the S2 protocol is used in a local-local configuration, the server **CAN** use a self-signed CA certificate. In this case, the pairingInfo **MUST** include the first 9 bytes, encodes as 12 base64 encoded characters, of the fingerprint of this self-signed CA certificate and the client **MUST** check this fingerprint. +For each S2 connection the server authenticates using a TLS certificate. The cloud implementation certificates **MUST** be PKI certificates which are not self-signed. Only local servers can use a self-signed root certificate, which is used to sign a server certificate. +If the S2 protocol is used in a local-local configuration, the server **CAN** use a self-signed root certificate. In this case, the pairingInfo **MUST** include the first 9 bytes, encodes as 12 base64 encoded characters, of the fingerprint of this self-signed CA certificate and the client **MUST** check this fingerprint. -Note that all communication use TLS. This is further explained in [Security considerations](./security-considerations.md). +Note that all communication uses TLS. This is further explained in [Security considerations](./security-considerations.md). -The server certificates **MUST** be exchanged and validated during the initiation of the connection (REST and WSS). This is default usage of most networking libraries. +The server certificates **MUST** be exchanged and validated during the initiation of the connection (HTTPS and WSS). This is default usage of most networking libraries. ## Cipher suites From 97346d51be8ebe95e709f352790bd36d1d2bec06 Mon Sep 17 00:00:00 2001 From: Jorrit Nutma Date: Wed, 14 Jan 2026 10:50:54 +0100 Subject: [PATCH 20/26] Correct definitions and typos in discovery pairing doc (#55) * Correct definitions and typos in discovery pairing doc * solved several typos --- .../discovery-pairing-authentication.md | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 58bea96..26dc94e 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -88,7 +88,7 @@ There is one guarantee that explicitly is not given by this protocol: The mutual authentication is based on the trust relation between the user and the Client/Server. Since it is assumed that the user already had a trust relation with both of them, this existing trust can be used for mutual authentication between the client and the server. Note that the this communication is not part of the S2 protocol. -The enduser requests an url, and token from the server, and gives these to the client. Based on these data, the client can connect to the server, using a TLS connection, check the certificate and authenticate himself with the token. Note that if the server uses a self-signed certificate, the fingerprint will be shared during the pairing phase, so it can be verified by the client. +The end user requests an url, and token from the server, and gives these to the client. Based on these data, the client can connect to the server, using a TLS connection, check the certificate and authenticate himself with the token. Note that if the server uses a self-signed certificate, the fingerprint will be shared during the pairing phase, so it can be verified by the client. ### 2. Integrity of communication (guaranteed) @@ -124,28 +124,28 @@ This specification uses the concepts that are defined below. | Term | Definition | | --- | --- | -| Access token | A token that is used for setting up an S2 connection. It random binary data and must be generated by a cryptographically secure pseudorandom number generator and have a minimum length of 32 bytes. It is encoded using Base64. Also see [11A. Response status 200](#11a-response-status-200). | -| End user | A person or entity that manages S2 nodes. For the purpose of this specification it is assumed that there is already a trust relationship in place between this person and the S2 nodes. The means that the way the trust relationship has been established is out of scope for this specification. | -| End user environment | An restricted area within an application that contains all the S2 nodes that belong to the end user. Also see [Deployment of S2 nodes](#deployment-of-s2-nodes). | -| Initiator S2 node | The S2 node that that takes the initiative to pair with a responder S2 node. This is typically the S2 node from which the user initiates the pairing process. It is the counterpart of the responder S2 node. Also soo [Pairing and unpairing from the perspective of the end user](#pairing-and-unpairing-from-the-perspective-of-the-end-user). | +| Access token | A token that is used for setting up an S2 connection. It is random binary data and must be generated by a cryptographically secure pseudorandom number generator and have a minimum length of 32 bytes. It is encoded using Base64. Also see [11A. Response status 200](#11a-response-status-200). | +| End user | A person or entity that manages S2 nodes. For the purpose of this specification it is assumed that there is already a trust relationship in place between this person and the S2 nodes. This means that the way the trust relationship has been established is out of scope for this specification. | +| End user environment | A restricted area within an application that contains all the S2 nodes that belong to the end user. Also see [Deployment of S2 nodes](#deployment-of-s2-nodes). | +| Initiator S2 node | The S2 node that takes the initiative to pair with a responder S2 node. This is typically the S2 node from which the user initiates the pairing process. It is the counterpart of the responder S2 node. Also see [Pairing and unpairing from the perspective of the end user](#pairing-and-unpairing-from-the-perspective-of-the-end-user). | | Pairing attempt | The process of pairing two S2 nodes. The process can be completed successfully or unsuccessfully. | -| Pairing code | The pairing code is the string of characters the end user has to copy from the responder S2 node user interface to the initiator S2 node user interface, in order to pair the two S2 nodes. The pairing code consist of a pairing token and when required a pairing S2 node ID. Also see [The pairing token, the pairing S2 node ID and the pairing code](#the-pairing-token-the-pairing-s2-node-id-and-the-pairing-code). | +| Pairing code | The pairing code is the string of characters the end user has to copy from the responder S2 node user interface to the initiator S2 node user interface, in order to pair the two S2 nodes. The pairing code consists of a pairing token and if required a pairing S2 node ID. Also see [The pairing token, the pairing S2 node ID and the pairing code](#the-pairing-token-the-pairing-s2-node-id-and-the-pairing-code). | | Pairing S2 node ID | A short identifier for an S2 node, which is unique in the context of a single S2 endpoint. Also see [The pairing token, the pairing S2 node ID and the pairing code](#the-pairing-token-the-pairing-s2-node-id-and-the-pairing-code). | | Pairing token | A secret string of characters, which acts as a proof of the trust relationship between the end user and an S2 node. Also see [The pairing token, the pairing S2 node ID and the pairing code](#the-pairing-token-the-pairing-s2-node-id-and-the-pairing-code). | -| Responder S2 Node | The S2 node that that responds to a request to pair. This is the S2 node that issued the pairing code. It is the counterpart of the initiator S2 node. Also soo [Pairing and unpairing from the perspective of the end user](#pairing-and-unpairing-from-the-perspective-of-the-end-user). | +| Responder S2 Node | The S2 node that responds to a request to pair. This is the S2 node that issued the pairing code. It is the counterpart of the initiator S2 node. Also see [Pairing and unpairing from the perspective of the end user](#pairing-and-unpairing-from-the-perspective-of-the-end-user). | | S2 endpoint | A service which can handle pairing requests or initiates pairing requests itself. An S2 endpoint can represent one S2 node, but could also represent many. | | S2 communication client | The S2 nodes which behaves as the HTTP client when initiating an S2 connection. | | S2 communication server | The S2 nodes which behaves as the HTTP server when initiating an S2 connection. | -| S2 pairing client | The 2S endpoint which behaves as the HTTP client when pairing with an S2 node. | +| S2 pairing client | The S2 endpoint which behaves as the HTTP client when pairing with an S2 node. | | S2 pairing endpoint registry | The central registry that keeps track of publicly available S2 pairing servers. | -| S2 pairing server | The 2S endpoint which behaves as the HTTP client when pairing with an S2 node. | +| S2 pairing server | The S2 endpoint which behaves as the HTTP server when pairing with an S2 node. | | S2 node | Refers to an instance of either a CEM or a RM as defined in EN 50491-12-1 and implementing this specification. S2 communication between two S2 nodes can only be established if one of the S2 nodes is a CEM and the other a RM. These S2 nodes must also have the same end user. | | S2 node ID | A globally unique identifier for an S2 node in the UUID format. | -| User interface | A user interface through which an end user can interact with an S2 node. Interaction between the end user and the user interface must be secure, but this is out of scope for this specification. Examples of a user interface are a web interface, an app or a physical interface (HMI) on a device. | +| User interface | A user interface through which an end user can interact with an S2 node. Interactions between the end user and the user interface must be secure, but this is out of scope for this specification. Examples of a user interface are a web interface, an app or a physical interface (HMI) on a device. | # Architecture (informative) -This section explains the over architecture and deployment options for CEM and RM instances. +This section explains the overall architecture and deployment options for CEM and RM instances. ## Deployment of S2 nodes @@ -268,14 +268,14 @@ There are however two situations where this is not possible: * **WAN initiator S2 node and LAN responder S2 node**: Since the LAN is usually shielded from the WAN through a firewall or NAT, it is assumed that it is not possible to approach a LAN HTTP server from a WAN client. This specifications offers two approaches to this problem: * Accept this limitation and not allow the WAN S2 node to be the initiator S2 node. Pairing can only be performed when the LAN S2 node is the initiator S2 node and the WAN S2 node is the responder S2 node. Special care must be taken to explain this to the end user. * Many modern devices or EMS systems are connected to a cloud backend managed by the OEM. If this is the case, it is possible to implement the pairing HTTP server in the cloud, even though the S2 node itself is in the WAN. If the pairing is performed successfully in the OEM backend, the result of the pairing must be communicated to the S2 node via the existing connection between device/EMS and the OEM backend. -* **LAN initiator RM and LAN responder RM**: Since one of the requirements is that a LAN RM instance can be implemented on restricted hardware, and a TLS enabled HTTP server is far more memory intensive than an HTTP client, there is an option to implement a LAN RM instance purely as an HTTP server. A long-polling mechanism is available to indicate to the HTTP Server that the S2 node is available for pairing. This mechanism is also used to initiate the pairing process from the HTTP server. In other words: in this specific situation the initiator S2 node behaves as the HTTP server, and the responder S2 node only has to be an HTTP client. +* **LAN initiator RM and LAN responder RM**: Since one of the requirements is that a LAN RM instance can be implemented on restricted hardware, and a TLS enabled HTTP server is far more memory intensive than an HTTP client, there is an option to implement a LAN RM instance purely as an HTTP client. A long-polling mechanism is available to indicate to the HTTP Server that the S2 node is available for pairing. This mechanism is also used to initiate the pairing process from the HTTP server. In other words: in this specific situation the initiator S2 node behaves as the HTTP server, and the responder S2 node only has to be an HTTP client. ![Pairing_direction](/img/communication-layer/pairing_direction.png) # Formal specification and versioning (normative) -This document servers as an overall specification of the S2 over IP protocol. However, where possible, the protocol has been specified in a formal specification language in order to minimize possible interpretation and allow tooling to assist the implementation of the specification. Since many details are better described in these formal specification files, they are not described in this document. Where the formal specification files and this document overlap, the formal specification file is leading. +This document serves as an overall specification of the S2 over IP protocol. However, where possible, the protocol has been specified in a formal specification language in order to minimize possible interpretation and allow tooling to assist the implementation of the specification. Since many details are better described in these formal specification files, they are not described in this document. Where the formal specification files and this document overlap, the formal specification file is leading. | Part of specification | Description | Specification format | | --- | --- | --- | @@ -523,7 +523,7 @@ Note over Client, Server: Pairing finalized ### 0. Precondition -Before an S2 node can be paired, it needs to things. +Before two S2 node can be paired, the following preconditions must be met. 1. The HTTP server and the HTTP client can only start with a pairing request when they are fully initialized and have all the details of the S2 nodes it represents available. 2. The HTTP client must have the base URL of the pairing API (e.g. `https://hostname.local/pairing/`) @@ -669,10 +669,10 @@ If no checks fail the client **should** proceed to the next step. ### 10A. HTTP Server checks serverHmacChallengeResponse The HTTP server checks the `serverHmacChallengeResponse` provided by the HTTP client in step 9A. It does that by calculating the response itself, and checking if the results is identical to the `serverHmacChallengeResponse`. -If the result is identical, the server **should** proceed to the next step. If the result is not identical, the client **must** stop the pairing attempt by responding with HTTP status code 403. The `pairingAttemptId` cannot be used by the HTTP client anymore. If the HTTP client wants to make another attempt, it **must** start with again step 1 or step 4. +If the result is identical, the server **should** proceed to the next step. If the result is not identical, the server **must** stop the pairing attempt by responding with HTTP status code 403. The `pairingAttemptId` cannot be used by the HTTP client anymore. If the HTTP client wants to make another attempt, it **must** start again at step 1 or step 4. ### 11A. Response status 200 -The server **must** generates an access token for the HTTP client. The access is random binary data and **must** be generated by a cryptographically secure pseudorandom number generator and **must** have a minimum length of 32 bytes. It is encoded using Base64. The access token **cannot** be used by the initiator S2 node until the pairing process is completed. +The server **must** generates an access token for the HTTP client. The access token is random binary data and **must** be generated by a cryptographically secure pseudorandom number generator and **must** have a minimum length of 32 bytes. It is encoded using Base64. The access token **cannot** be used by the initiator S2 node until the pairing process is completed. The server responds with two pieces of information: @@ -691,7 +691,7 @@ The HTTP sends the connection details to the HTTP server. This request also serv | Information | Description | | --- | --- | -| `serverHmacChallengeResponse` | The response for the challenge responce process | +| `serverHmacChallengeResponse` | The response for the challenge response process | | `initiateConnectionUrl` | The base URI for the S2 connection process (does not include the version number) | | `accessToken` | The access token that was generated for this S2 node | @@ -717,7 +717,7 @@ If no checks fail the server **should** proceed to the next step. ### 10B. HTTP Server checks serverHmacChallengeResponse The HTTP server checks the `serverHmacChallengeResponse` provided by the HTTP client in step 9A. It does that by calculating the response itself, and checking if the results is identical to the `serverHmacChallengeResponse`. -If the result is identical, the server **should** proceed to the next step. If the result is not identical, the client **must** stop the pairing attempt by responding with HTTP status code 403. The `pairingAttemptId` cannot be used by the HTTP client anymore. If the HTTP client wants to make another attempt, it **must** start again with step 1 or step 4. +If the result is identical, the server **should** proceed to the next step. If the result is not identical, the client **must** stop the pairing attempt by responding with HTTP status code 403. The `pairingAttemptId` cannot be used by the HTTP client anymore. If the HTTP client wants to make another attempt, it **must** start again at step 1 or step 4. ### 11B. Response status 204 The server confirms it has accepted the response and received the connection details by responding with HTTP status 204. @@ -749,7 +749,7 @@ If no checks fail the server **should** proceed to the next step. Receiving a `/finalizePairing` request marks the completion of the pairing attempt for the HTTP server. If the HTTP server issued an access token during this pairing attempt, it can now be used by an S2 communication client to set up an S2 connection. The `pairingAttemptId` can no longer be used by the HTTP client. ### 13. Response status 204 -To confirm the successful completion of the paring attempt, the HTTP server responds to the client with HTTP status code 204. This response marks the completion of the pairing attempt for teh HTTP client. If the HTTP client issued an access token during this pairing attempt, it can now be used by an S2 communication client to set up an S2 connection. The `pairingAttemptId` can no longer be used by the HTTP client. +To confirm the successful completion of the pairing attempt, the HTTP server responds to the client with HTTP status code 204. This response marks the completion of the pairing attempt for the HTTP client. If the HTTP client issued an access token during this pairing attempt, it can now be used by an S2 communication client to set up an S2 connection. The `pairingAttemptId` can no longer be used by the HTTP client. If the HTTP server was using a self-signed certificate, the HTTP client can now store the self-signed root CA certificate. The client **must** check that this is the CA certificate that is used for all future interaction with this S2 endpoint. The HTTP server is allowed to use a new self-signed server certificate, as long as it is signed by the self-signed CA certificate that was used during the pairing process. @@ -757,7 +757,7 @@ If the HTTP server was using a self-signed certificate, the HTTP client can now A pairing attempt has a maximum duration of 15 seconds. That means that once a `pairingAttemptId` has been issued, this `pairingAttemptId` cannot be used after 15 seconds since it was issued. From the perspective of the HTTP server, any pairing attempt that is not completed in 15 seconds (with success or not) is considered a failed attempt. From the perspective of the HTTP client, if the server does not respond within 15 seconds since it received the `pairingAttemptId`, it must consider the pairing attempt as failed. If the HTTP client wants to make another attempt, it should start again at step 1 or step 4. ### Invalid interactions -If the server receives wrong HTTP request (e.g. `/postConnectionDetails` while it was expecting `/requestConnectionDetails`) or when it receives the requests in the wrong order (e.g. `/finalizePairing` with `success` = `true` before calling `/requestConnectionDetails`) it **must** respond with an status 400 and consider the pairing attempt as failed. The only exception is receiving the same request twice. +If the server receives a wrong HTTP request (e.g. `/postConnectionDetails` while it was expecting `/requestConnectionDetails`) or when it receives the requests in the wrong order (e.g. `/finalizePairing` with `success` = `true` before calling `/requestConnectionDetails`) it **must** respond with a status 400 and consider the pairing attempt as failed. The only exception is receiving the same request twice. # S2 Connection (normative) @@ -767,7 +767,7 @@ The following mechanism **must** be used to initiate a secure connection between ## Mapping the CEM and RM to communication server or client -The CEM and RM roles defined by the S2 protocol are distinct from the Server and Client roles of the S2 pairing process. The following rules apply to determine whether the RM or CEM acts as a client or server for the communication initialization. +The CEM and RM roles defined by the S2 protocol are distinct from the server and client roles of the S2 pairing process. The following rules apply to determine whether the RM or CEM acts as a client or server for the communication initialization. * If a connection is set up between a WAN S2 node and a LAN S2 node, the WAN S2 node must act as an S2 communication server, and the local node must act as an S2 communication client. * If a connection is set up between two nodes that are similarly deployed (i.e. both in WAN, or both in LAN), the CEM must act as an S2 communication server, and the RM must act as an S2 communication client. @@ -817,7 +817,7 @@ Client -> Client : 11. Remove old accessToken ### 0. Precondition -Before an S2 node can be paired, it needs to things. +Before an S2 node can initiate a connection, it needs three things. 1. The HTTP server and the HTTP client can only start with a communication request when they are fully initialized and have all the details of the S2 nodes it represents available. 2. The HTTP client must have the base URL of the connection API (e.g. `https://hostname.local/connection/`) @@ -986,7 +986,7 @@ RFC 7692 is widely supported by WebSocket libraries and and we are exchanging JS WebSockets by default have a **keepalive** and a **heartbeat mechanism**. Keepalive is designed to keep the connection open while heartbeat is designed to check the latency and check the connection is still working. This means that periodically a ping frame is sent to the server (endpoint) and in response a pong frame is sent. -In order to reduce network traffic, S2 WebSocket implementations **SHOULD** not send ping frames more often than every 50 seconds. Ping & poing frames are control frames and **MAY** include payload of maximum 125 bytes. +In order to reduce network traffic, S2 WebSocket implementations **SHOULD** not send ping frames more often than every 50 seconds. Ping & pong frames are control frames and **MAY** include payload of maximum 125 bytes. [https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2%5C%5C) @@ -1042,7 +1042,7 @@ WebSocketDisconnected --> [*] | --- | --- | --- | | WebSocket Connected | SelectControlType
SessionRequest
ReceptionStatus | ResourceManagerDetails
PowerMeasurement
PowerForecast
SessionRequest
ReceptionStatus | | ControlType PEBC activated | PEBC.Instruction
SelectControlType
SessionRequest
ReceptionStatus | PEBC.EnergyConstraint
PEBC.PowerConstraint
RevokeObject InstructionStatusUpdate
ResourceManagerDetails
PowerMeasurement
PowerForecast
SessionRequest
ReceptionStatus | -| ControlType PPBC activated | PPBC.EndInterrptionInstruction
PPBC.ScheduleInstruction
PPBC.StartInterruptionInstruction
SelectControlType
SessionRequest
ReceptionStatus | PPBC.PowerProfileDefinition
PPBC.PowerPorfileStatus
RevokeObject
InstructionStatusUpdate
ResourceManagerDetails
PowerMeasurement
PowerForecast
SessionRequest
ReceptionStatus | +| ControlType PPBC activated | PPBC.EndInterruptionInstruction
PPBC.ScheduleInstruction
PPBC.StartInterruptionInstruction
SelectControlType
SessionRequest
ReceptionStatus | PPBC.PowerProfileDefinition
PPBC.PowerPorfileStatus
RevokeObject
InstructionStatusUpdate
ResourceManagerDetails
PowerMeasurement
PowerForecast
SessionRequest
ReceptionStatus | | ControlType OMBC activated | OMBC.Instruction
SelectControlType
SessionRequest
ReceptionStatus | OMBC.Status
OMBC.SystemDescription
OMBC.TimerStatus RevokeObject
InstructionStatusUpdate
ResourceManagerDetails
PowerMeasurement
PowerForecast
SessionRequest
ReceptionStatus | | ControlType FRBC activated | FRBC.Instruction
SelectControlType
SessionRequest
ReceptionStatus | FRBC.ActuatorStatus
FRBC.FillLevelTargetProfile
FRBC.LeakageBehaviour
FRBC.StorageStatus
FRBC.SystemDescription
FRBC.UsageForecast
FRBC.TimerStatus
RevokeObject
InstructionStatusUpdate ResourceManagerDetails
PowerMeasurement
PowerForecast
SessionRequest
ReceptionStatus | | ControlType DDBC activated | DDBC.Instruction
SelectControlType
SessionRequest
ReceptionStatus | DDBC.ActuatorStatus
DDBC.AverageDemandRateForecast
DDBC.SystemDescription
DDBC.TimerStatus
RevokeObject
InstructionStatusUpdate
ResourceManagerDetails
PowerMeasurement PowerForecast
SessionRequest
ReceptionStatus | @@ -1063,7 +1063,7 @@ Client and server **can** keep other (non-security) information for, for example # Security (normative) -> TODO: This section needs to be expanded to explain measures agains ddos +> TODO: This section needs to be expanded to explain measures against ddos Please refer to an extensive description of the security specifications to [Security considerations](./security-considerations.md). @@ -1078,4 +1078,4 @@ The server certificates **MUST** be exchanged and validated during the initiatio ## Cipher suites -Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. \ No newline at end of file +Security levels of cipher suites will change over time. To stay secure, the used cipher suites should be updates regularly and adhere to regular updates. All S2Nodes **MUST** follow **ONLY** the accepted crypto libraries as defined in [Accepted crypto algorithms](./accepted-crypto.md). This list will be kept up-to-date. When changes are made to the list of accepted crypto libraries, all S2Nodes **MUST** follow these changes within half a year. From a8391ce673b6fa51a45ca826260c0b16b10504bb Mon Sep 17 00:00:00 2001 From: Dr Maurice Hendrix Date: Mon, 26 Jan 2026 10:53:44 +0100 Subject: [PATCH 21/26] Fix image paths in discovery-pairing-authentication.md Updated relative image paths to make sure pictures show up in github. --- .../discovery-pairing-authentication.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 26dc94e..a707a15 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -156,7 +156,7 @@ S2 Nodes can be deployed locally within the LAN, or somewhere on a server in the * **WAN** S2 nodes are typically part of a large application that run on many servers and/or on some kind of cloud computing platform. A single application usually serves many users. Each user could have one or multiple S2 nodes. This could for example be a cloud-based energy management system that can connect to many devices. It could also be a cloud environment of a device manufacturer that hosts the RM instances in the cloud. An end user could own multiple devices from this manufacturer, thus the application could host multiple RM instances for this particular user. We call a group of S2 nodes that a single user can manage within one application an *end user environment*. It is also possible that an end user environment contains both CEM and RM instances. The user interface is typically a web interface or a smartphone app. * **LAN** S2 nodes are typically part of an application that runs on an embedded computer device somewhere in the building. Such a device could be a physical energy management system, an energy flexible device such as a home battery, heat pump or EV charger, or a gateway device which connects to an energy flexible device through some kind of protocol. Often an application will only host a single S2 node, but it is also possible that an application hosts multiple S2 nodes. A device could function completely on its own, but it cloud also be connected to an internet based application of the manufacturer. The user interface could be a physical human-machine interface on the device, but also be a smartphone app that connects directly to the device (e.g. via bluetooth), or a smartphone app or web interface that connects to an internet based application of the manufacturer. For energy flexible devices, it is assumed that they could also have no user interface at all, or that they are very constrained when it comes to computing power. It is assumed that a CEM always has a user interface. -![Deployment_options](/img/communication-layer/deployment_options.png) +![Deployment_options](../../static/img/communication-layer/deployment_options.png) There are three types of S2 connections between S2 nodes possible: @@ -182,7 +182,7 @@ If pairing is performed successfully, the CEM and RM instances should establish Once a CEM is paired, the user has to possibility to command either of the S2 nodes to *unpair*. After unpairing the CEM and RM instances can no longer communicate through S2 (unless the end user pairs them again). -![Pairing_process_user](/img/communication-layer/pairing_process_user.png) +![Pairing_process_user](../../static/img/communication-layer/pairing_process_user.png)
Image generated using the following PlantUML code: @@ -270,7 +270,7 @@ There are however two situations where this is not possible: * Many modern devices or EMS systems are connected to a cloud backend managed by the OEM. If this is the case, it is possible to implement the pairing HTTP server in the cloud, even though the S2 node itself is in the WAN. If the pairing is performed successfully in the OEM backend, the result of the pairing must be communicated to the S2 node via the existing connection between device/EMS and the OEM backend. * **LAN initiator RM and LAN responder RM**: Since one of the requirements is that a LAN RM instance can be implemented on restricted hardware, and a TLS enabled HTTP server is far more memory intensive than an HTTP client, there is an option to implement a LAN RM instance purely as an HTTP client. A long-polling mechanism is available to indicate to the HTTP Server that the S2 node is available for pairing. This mechanism is also used to initiate the pairing process from the HTTP server. In other words: in this specific situation the initiator S2 node behaves as the HTTP server, and the responder S2 node only has to be an HTTP client. -![Pairing_direction](/img/communication-layer/pairing_direction.png) +![Pairing_direction](../../static/img/communication-layer/pairing_direction.png) # Formal specification and versioning (normative) @@ -370,7 +370,7 @@ There are two possible types of certificates. The first option is a public serve In the following image, the difference is shown. On the left a public root CA that's publicly known and trusted, on the right, a self signed root certificate, that's unknown and it's trustworthiness has to be achieved in another way. -![image.png](/img/communication-layer/certificate-chains.png) +![image.png](../../static/img/communication-layer/certificate-chains.png)
Image generated using the following PlantUML code: @@ -466,7 +466,7 @@ The user visits the S2ClientNodeUI and the S2ServerNode has been discovered (so The pairing process itself consists of several HTTP interactions between client and server. The image below depicts a successful pairing process between two S2 nodes. -![image](/img/communication-layer/pairing_http_process.png) +![image](../../static/img/communication-layer/pairing_http_process.png)
Image generated using the following PlantUML code: @@ -787,7 +787,7 @@ There are four scenarios for CEM and RM deployment, and applying the rules above During the pairing process an `accessToken` is generated by the S2 node which will be the S2 communication server and sent to the S2 node that will be the S2 communication client. This `accessToken` can be used by the S2 communication client to set up a session with the S2 communication server for exchanging S2 messages. Each time a new connection is made the `accessToken` will be renewed. In other words, an `accessToken` can only be used once to set up a connection. The S2 communication server will generate a new `accessToken` and sends it to the S2 communication client. Since this `accessToken` is the only means to connect two S2 nodes once they are paired, the connection initiation process makes sure that both S2 nodes confirm that they have successfully persisted the new `accessToken` before invalidating the old `accessToken`. -![connection initiation](/img/communication-layer/connection-initiation.png) +![connection initiation](../../static/img/communication-layer/connection-initiation.png)
Image generated using the following PlantUML code: @@ -1012,7 +1012,7 @@ The S2 standard has been encoded into a JSON schema and asyncapi specification, ### State of communication -![State of Communication](/img/communication-layer/state-of-communication.png) +![State of Communication](../../static/img/communication-layer/state-of-communication.png)
Image generated using the following PlantUML code: From e90443ae58a7be1bee4955b96bf64b6c2c8d62a6 Mon Sep 17 00:00:00 2001 From: MauriceHendrix Date: Mon, 26 Jan 2026 16:48:48 +0100 Subject: [PATCH 22/26] also adjusted links for resu of docs --- website/docs/communication-layer/security-considerations.md | 4 ++-- website/docs/communication-layer/why-not-oauth.md | 2 +- website/docs/examples/ev.md | 2 +- website/docs/examples/heat-pump.md | 2 +- website/docs/examples/pv.md | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/website/docs/communication-layer/security-considerations.md b/website/docs/communication-layer/security-considerations.md index 3592b5c..f0271d3 100644 --- a/website/docs/communication-layer/security-considerations.md +++ b/website/docs/communication-layer/security-considerations.md @@ -22,7 +22,7 @@ In the image below, the relevant security communication is visualized. Note that The following sequence diagram focuses on validation of certificates and tokens. Please refer to the other sequence diagrams that contain more details about the pairing and connection initiation. -![image](/img/communication-layer/pairing-security.png) +![image](../../static/img/communication-layer/pairing-security.png)
Image generated using the following PlantUML code: @@ -135,7 +135,7 @@ There are two possible types of certificates. The first option is a public serve In the following image, the difference is shown. On the left a public root CA that's publicly known and trusted, on the right, a self signed root certificate, that's unknown and it's trustworthiness has to be achieved in another way. -![image.png](/img/communication-layer/certificate-chains.png) +(../../static/imgge.png](/img/communication-layer/certificate-chains.png)
Image generated using the following PlantUML code: diff --git a/website/docs/communication-layer/why-not-oauth.md b/website/docs/communication-layer/why-not-oauth.md index 55e49b1..6859cfd 100644 --- a/website/docs/communication-layer/why-not-oauth.md +++ b/website/docs/communication-layer/why-not-oauth.md @@ -12,7 +12,7 @@ Given our requirements on the pairing and authentication process, the following The pairing and authentication process based on OAuth 2.0 would be specified as follows: -![image](/img/communication-layer/oauth2-s2.png) +![image](../../static/img/communication-layer/oauth2-s2.png)
Image generated using the following PlantUML code: diff --git a/website/docs/examples/ev.md b/website/docs/examples/ev.md index b8c97c8..0b1c0fb 100644 --- a/website/docs/examples/ev.md +++ b/website/docs/examples/ev.md @@ -3,7 +3,7 @@ title: "Example: Electric Vehicle" hide_title: true --- -![image not found](/img/example-ev-header.png) +![image not found](../../static/img/example-ev-header.png) This page serves as a guide for implementing a S2 RM for an electric vehicle (charge point), or to give a CEM developer a better understanding of what to expect when controlling the energy flexibility of electric vehicles. It provides some example S2 messages for a (fictional) EV as well. diff --git a/website/docs/examples/heat-pump.md b/website/docs/examples/heat-pump.md index 9a269a6..e4f0847 100644 --- a/website/docs/examples/heat-pump.md +++ b/website/docs/examples/heat-pump.md @@ -3,7 +3,7 @@ title: "Example: Heat Pump" hide_title: true --- -![image not found](/img/example-heat-pumps-header.png) +![image not found](../../static/img/example-heat-pumps-header.png) This page serves as a guide for implementing an S2 RM for a heat pump, or to give a CEM developer a better understanding of what to expect when controlling the energy flexibility of a heat pump. It provides some example S2 messages for same (fictional) heat pumps. diff --git a/website/docs/examples/pv.md b/website/docs/examples/pv.md index 07c251b..e7eba0b 100644 --- a/website/docs/examples/pv.md +++ b/website/docs/examples/pv.md @@ -3,7 +3,7 @@ title: "Example: PV Installation" hide_title: true --- -![image not found](/img/example-pv-header.png) +![image not found](../../static/img/example-pv-header.png) This page serves as a guide for implementing an S2 RM for a PV installation. It provides example S2 messages that help developers to understand how to interact with PV installations and what kind of flexibility they can offer. From 84dfb424384faa04fe47b0e86b16858696bd04c4 Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Mon, 2 Feb 2026 14:47:09 +0100 Subject: [PATCH 23/26] Added specification for DNS-SD --- .../discovery-pairing-authentication.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 26dc94e..4c720c5 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -344,6 +344,58 @@ A LAN scenario where both RM and CEM are running on the same local network. Disc > NOTE: the DNS-SD service specification will be published soon +### DNS-SD based discovery +DNS-SD is used for automatically discover S2 nodes from an S2 node that is deployed in the LAN. This method can be used in three ways. + +* To discover another S2 node that is deployed in the LAN, which is the responder S2 node +* To advertise a [long polling endpoint](#long-polling) so other initiator S2 nodes in the LAN could connect to this S2 node +* To discover an S2 node of which the RM is deployed in the WAN, but that also has a presence in the LAN. + +S2 uses the service type `s2emp` (short for S2 Energy Management Protocol) and exclusively uses tcp, since it is an HTTP based protocol. S2 uses the following DNS-SD values: + +| DNS-SD property | Value for S2 | +| --- | --- | +| Service type | `_s2emp` | +| Protocol | `_tcp` | +| Port | No fixed port, decision is up to the implementation | +| Subtypes | `_cem` and `_rm` | +| Service name | Identical to the hostname (see [here](#addressing-s2-endpoints-normative) for more details) + +An S2 endpoint deployed in the LAN **should** publish it service through DNS-SD once it is ready for pairing, and until it shuts down. + +A device has a presence in the LAN (e.g. a heat pump which is connected to the internet via the end user's WiFi), but has its RM deployed in the WAN, can still use DNS-SD to advertise its S2 endpoint to any local S2 nodes (e.g. a physical energy management device containing an CEM). In this case the device doesn't have to offer any actual service in the LAN; it merely uses the TXT record to advertise its WAN S2 endpoint URL. An arbitrary port may be specified. + +Two DNS-SD subtypes are used for S2 endpoints. Subtypes can be used to filter services. + + * `_cem` is used when the S2 endpoint contains one or more CEM S2 node + * `_rm` is used when the S2 endpoint contains one or more RM S2 node + * `_cem` and `_rm` are both used when the S2 endpoint contains both CEM and RM S2 nodes + +S2 uses the following TXT records when registering for services. In the table below, M indicates a mandatory value and O indicates on optional value. + +| Record name | M/O | Description +| --- | --- | --- | +| `txtver` | M | Version of this specification of usage of the TXT record. **Must** be the literal value `1` for this version | +| `e_name` | O | The name of this endpoint (identical to the `name` property in the `S2EndpointDescription` object as defined in de OpenAPI specification) | +| `e_logoUrl` | O | The logoUrl of this endpoint (identical to the `logoUrl` property in the `S2EndpointDescription` object as defined in de OpenAPI specification) | +| `deployment` | M | **Must** be the literal value `LAN` or `WAN` (identical to the `logoUrl` property in the `S2EndpointDescription` object as defined in de OpenAPI specification) | +| `pairingUrl` | O | The base URL of the pairing API of this S2 endpoint, excluding the version name but including the last slash (e.g. `https://hostname.local/pairing/`). If no value is provided, a `longpollingUrl` **must** be provided. +| `longpollingUrl` | O | The base URL of the pairing API of this S2 endpoint on which the longpolling feature is implemented. The URL should be provided excluding the version name but including the last slash (e.g. `https://hostname.local/pairing/`). Only needs to be provided when longpolling is supported. Can only be provided if the value for `deployment` is equal to `LAN`. + +> Note: It is mandatory to provide a value for at least one of the properties `pairingUrl` and `longpollingUrl`. Providing both is also possible. + +The receiver of the service description **must** use the URL provided in the TXT records; not the hostname or IP-address and port associated with the service registry. + +> Note: You may have noticed that the full URL of the endpoint is used in the TXT records, even though the endpoint and port are already exposed by DNS-SD itself. This is done to avoid any problems with TLS certificates, which are pinned to a certain domain name. When using a WAN S2 endpoint, the full URL needs to be specified as well, since no local service is actually being offered. + +> Scanning for S2 endpoints could for example be done using the following [avahi](https://avahi.org/) command: +> +> `avahi-browse -r _s2emp._tcp` +> +> Registering an S2 endpoint could for example be done using the folling avahi command: +> +> `avahi-publish-service -s "EVSE1038" _s2emp._tcp 443 "txtvers=1" "e_name=brand" "deployment=LAN" "pairingUrl=https://EVSE1038.local:443/pairing/" --sub _rm._sub._s2emp._tcp` + ## The pairing token, the pairing S2 node ID and the pairing code The pairing token is a random string of characters that is generated by the responder S2 node. It is a secret which is transferred by the end user to the initiator S2 node, and then is verified during the pairing process. Since there will be many cases where the end user has to manually type in the pairing token, the pairing token has to be short enough to make it easy for the end user to type in, but long enough to make it secure. The pairing token **must** be generated by a cryptographically secure pseudorandom number generator. The pairing token **must** be random binary data with a length of at least 9 bytes, and is encoded using Base64 before it is presented to the end user (9 bytes is equal to 12 characters when encoded with Base64). From 8e7904b8a814ecfd1d3d0759b4a5d3c17e53ead6 Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Fri, 27 Feb 2026 09:33:35 +0100 Subject: [PATCH 24/26] Added regex patterns for pairing token and code --- .../discovery-pairing-authentication.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 7ae7a2d..67ec76a 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -348,10 +348,22 @@ A LAN scenario where both RM and CEM are running on the same local network. Disc The pairing token is a random string of characters that is generated by the responder S2 node. It is a secret which is transferred by the end user to the initiator S2 node, and then is verified during the pairing process. Since there will be many cases where the end user has to manually type in the pairing token, the pairing token has to be short enough to make it easy for the end user to type in, but long enough to make it secure. The pairing token **must** be generated by a cryptographically secure pseudorandom number generator. The pairing token **must** be random binary data with a length of at least 9 bytes, and is encoded using Base64 before it is presented to the end user (9 bytes is equal to 12 characters when encoded with Base64). +The **pairing token** can be validated with the following regular expression: + +``` +^(?:[A-Za-z0-9+\/]{4}){2,}(?:[A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}={2})$ +``` + The pairing token is typically dynamically generated when the user requests the pairing token at the user interface of the responder S2 node. Dynamically generated pairing tokens **must** expire after a duration; five minutes is the recommended duration. However, energy flexible devices that do not have a user interface are allowed to have a static pairing token, that for example can be printed somewhere on the physical device. Static pairing tokens do not expire. Static pairing tokens **should** be longer than 12 (base64) characters. An S2 endpoint can host multiple S2 nodes. When attempting to pair a certain S2 node, the S2 endpoint needs to know exactly which of its S2 nodes this pairing attempt is aimed at. S2 nodes are uniquely identified with their S2 node ID. Since this S2 node ID is a UUID, it is pretty long and cumbersome to type in. That is why an S2 endpoint can assign its nodes a `pairing S2 node ID`. This is an identifier that is intended to be short, and only unique within the context of this particular S2 endpoint. Pairing S2 node IDs could be assigned by the S2 endpoint whenever new S2 nodes are created, but also could be generated dynamically only when someone is attempting to pair to this S2 node. This way, pairing S2 node IDs have a short live, and can be reused by other S2 nodes at other moments. This allows to use shorter pairing S2 node ID's. Pairing S2 node IDs are a string of characters, which may include lower case letters, upper case letters and numbers. Pairing S2 node IDs are ideally as short as possible (at least one character), but should of course be long enough to allow the S2 endpoint to uniquely identify an S2 node. When an S2 endpoint only contains one S2 node, there is no need for a pairing S2 node ID. +The **pairing S2 node ID** can be validated with the following regular expression: + +``` +^[0-9a-zA-Z]+$ +``` + Although the pairing token and the pairing S2 node ID are two separate strings, which are treated as completely different in the pairing process, they are presented together to the user as one string: the *pairing code*. The pairing code is simply the pairing S2 node ID, followed by a dash ('-'), followed by the pairing token. When there is no pairing S2 node ID, the pairing code is simply identical to the pairing token. ``` @@ -361,6 +373,12 @@ When a pairing S2 node ID is used: [pairing code] = [pairing S2 node ID]-[pairing token] ``` +Alternatively, the **pairing code** can be validated with the following regular expression: + +``` +^(?:[0-9a-zA-Z]+-)?(?:[A-Za-z0-9+\/]{4}){2,}(?:[A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}={2})$ +``` + The pairing code allows us to transfer two pieces of information by only bothering the end user once. Due to its format the initiator S2 node can easily extract the pairing S2 node ID and the pairing token from the pairing code by splitting the string at the dash. From 2cded42654bc93b78b96b2f390767535886aa947 Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Fri, 27 Feb 2026 14:14:45 +0100 Subject: [PATCH 25/26] Wrote the section on long-polling --- .../discovery-pairing-authentication.md | 41 +++- .../long-polling_activity_diagram.drawio | 209 ++++++++++++++++++ .../long-polling_activity_diagram.png | Bin 0 -> 49717 bytes 3 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 website/static/img/communication-layer/long-polling_activity_diagram.drawio create mode 100644 website/static/img/communication-layer/long-polling_activity_diagram.png diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index ba1cccf..9937b62 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -512,7 +512,46 @@ The user visits the S2ClientNodeUI and the S2ServerNode has been discovered (so > This section is only applicable for LAN-LAN pairing -> TODO: Long-polling still needs to be explained +The long-polling feature is intended for responder S2 nodes that only implement the HTTP client for pairing. It can be used by the responder S2 node (which implements the HTTP server) to notify it wants to pair. Long-polling can only be initiated by pairing endpoints that exclusively host RM S2 nodes and do not implement a pairing server. + +> Informative: Long-polling is a technique that allows the server to send signals to the client without a significant delay, and without relying on additional technologies such as Websockets or Server-Sent Events. The common alternative is polling, where the client sends a request on a regular interval; let's say every 30 seconds. Polling creates a delay from the perspective of the server. If the server wants to send something to the client, it has to wait until the client contacts the server; which in the worst case 30 seconds. With long-polling the server doesn't immediately respond the the request (a hanging HTTP request). It responds immediately when the server wants to client to do something, or just before the request would time out. After receiving the response from the server the client immediately opens a new request to allow the server to send signals the client again. + +The long-polling feature fulfills the following functionality: +* Make the existence of the client known to server, together with the S2 nodes IDs of the S2 nodes that are represented by the client endpoint +* Send the `S2NodeDescription` and `S2EndpointDescription` of S2 nodes represented by the client when requested by the server +* Send a prepare pairing signal or cancel prepare pairing from the server to the client for a particular S2 node ID +* Send the signal from the server to the client to initiate pairing for a particular S2 node ID +* Send an error message from the client to the server when pairing cannot be performed + +A client capable of long-polling **should** initiates long-polling when it encounters a S2 endpoint through DNS-SD that indicates that is available for long-polling requests. When the S2 endpoint represents zero S2 nodes the client **cannot** attempt long-polling. When the S2 endpoint advertisement itself, or only its long-polling indication disappears from DNS-SD the client **should** stop the long-polling process for that server. The client **must** also stop when it is no longer capable of pairing. + +The server **must** always respond within 25 seconds after receiving the request. The client **must** use a request time-out of at least 30 seconds. + +> TODO: Move the OpenAPI version selection process to its own section so we don't have to explain it every time + +The client starts the process by doing a POST request to the `/waitForPairing` path. For full normative details see the OpenAPI specification files. The request body contains a list of objects. The client **must** always provide a for each S2 node ID it represents. The items in the list have a mandatory property `clientS2NodeId` and optional parameters `clientS2NodeDescription`, `clientS2EndpointDescription`. The client should only provide values for these properties when requested by the server. The object also contains the optional property `errorMessage`, which only should be used when an error has occurred before pairing. + +When the server wants to client to immediately do a new request, it responds with status code 204. When it wants the client to do something, it responds with status 200 and a response body containing a list. This list contains an object only for S2 node IDs represented by the client, that the server wants to do something with. This object contains the mandatory properties `clientS2NodeId` and `action`. The `action` property is an enumeration indicating an action the server wants to execute for a specific S2 node. The possible action values are `sendS2NodeDescription`, `preparePairing`, `cancelPreparePairing` and `requestPairing`. + +The table below indicates how the client should respond to the requests of the server. Note that the server could send multiple actions (for different S2 node IDs) in the same response. The server **cannot** provide multiple objects for the same S2 node ID in one response. + +| Status code | Value `action` | Pairing code entered? | What should the client do | What should the client include in the next request body | +| --- | --- | --- | --- | --- | +| 204 | n/a | n/a | Only send a next request | Only the `clientS2NodeId` | +| 200 | `sendS2NodeDescription` | n/a | Only send the next request | The `clientS2NodeId`, `clientS2NodeDescription` and `clientS2EndpointDescription` | +| 200 | `preparePairing` | n/a | Prepare pairing for the mentioned S2NodeId and send the next request | Only the `clientS2NodeId` | n/a | +| 200 | `cancelPreparePairing` | n/a | Cancel prepare pairing for the mentioned S2NodeId and send the next request | Only the `clientS2NodeId` | +| 200 | `requestPairing` | Yes | Initiate the pairing for the mentioned S2NodeId and send the next request | Only the `clientS2NodeId` | +| 200 | `requestPairing` | No | Initiate the pairing for the mentioned S2NodeId and send the next request | Only the `clientS2NodeId`, and for the associated object provide an `errorMessage` with value `NoValidTokenOnPairingClient` | +| 400 | n/a | n/a | Stop long-polling until next time long-polling is advertised through DNS-SD | n/a | +| 401 | n/a | n/a | Stop long-polling | n/a | +| 500 | n/a | n/a | Wait before trying to send the next request | Only the `clientS2NodeId`| + +When the server sends the `requestPairing` action, the end user must have already entered the pairing code (which was generated by the server) in the UI of the client. If this is not the case, the client **must** perform a new request with an `errorMessage` containing the value `NoValidTokenOnPairingClient` in the object associated with the S2 node ID of the S2 node should have attempted to pair. + +The activity diagram below summarizes the complete long-polling process from teh perspective of the client. + +![image](../../static/img/communication-layer/long-polling_activity_diagram.png) ## Pairing interaction diff --git a/website/static/img/communication-layer/long-polling_activity_diagram.drawio b/website/static/img/communication-layer/long-polling_activity_diagram.drawio new file mode 100644 index 0000000..8d1f50d --- /dev/null +++ b/website/static/img/communication-layer/long-polling_activity_diagram.drawio @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/static/img/communication-layer/long-polling_activity_diagram.png b/website/static/img/communication-layer/long-polling_activity_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..4d376e879e2cc58e9a92ad93be2f81e4461077cc GIT binary patch literal 49717 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

aUFU#cUy8z-?!EH>Xv*`` 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 literal 0 HcmV?d00001 From 731a5ea09b74c041f21496115c9a5a6add748b0d Mon Sep 17 00:00:00 2001 From: Wilco Wijbrandi Date: Fri, 6 Mar 2026 14:43:22 +0100 Subject: [PATCH 26/26] Improved long-polling description based on review --- .../discovery-pairing-authentication.md | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/website/docs/communication-layer/discovery-pairing-authentication.md b/website/docs/communication-layer/discovery-pairing-authentication.md index 9937b62..4f312e6 100644 --- a/website/docs/communication-layer/discovery-pairing-authentication.md +++ b/website/docs/communication-layer/discovery-pairing-authentication.md @@ -400,7 +400,7 @@ The receiver of the service description **must** use the URL provided in the TXT The pairing token is a random string of characters that is generated by the responder S2 node. It is a secret which is transferred by the end user to the initiator S2 node, and then is verified during the pairing process. Since there will be many cases where the end user has to manually type in the pairing token, the pairing token has to be short enough to make it easy for the end user to type in, but long enough to make it secure. The pairing token **must** be generated by a cryptographically secure pseudorandom number generator. The pairing token **must** be random binary data with a length of at least 9 bytes, and is encoded using Base64 before it is presented to the end user (9 bytes is equal to 12 characters when encoded with Base64). -The pairing token is typically dynamically generated when the user requests the pairing token at the user interface of the responder S2 node. Dynamically generated pairing tokens **must** expire after a duration; five minutes is the recommended duration. However, energy flexible devices that do not have a user interface are allowed to have a static pairing token, that for example can be printed somewhere on the physical device. Static pairing tokens do not expire. Static pairing tokens **should** be longer than 12 (base64) characters. +The pairing token is typically dynamically generated when the user requests the pairing token at the user interface of the responder S2 node. Dynamically generated pairing tokens **must** expire after a duration; five minutes is the recommended duration. However, energy flexible devices that do not have a user interface are allowed to have a static pairing token, that for example can be printed somewhere on the physical device. Static pairing tokens do not expire. An S2 endpoint can host multiple S2 nodes. When attempting to pair a certain S2 node, the S2 endpoint needs to know exactly which of its S2 nodes this pairing attempt is aimed at. S2 nodes are uniquely identified with their S2 node ID. Since this S2 node ID is a UUID, it is pretty long and cumbersome to type in. That is why an S2 endpoint can assign its nodes a `pairing S2 node ID`. This is an identifier that is intended to be short, and only unique within the context of this particular S2 endpoint. Pairing S2 node IDs could be assigned by the S2 endpoint whenever new S2 nodes are created, but also could be generated dynamically only when someone is attempting to pair to this S2 node. This way, pairing S2 node IDs have a short live, and can be reused by other S2 nodes at other moments. This allows to use shorter pairing S2 node ID's. Pairing S2 node IDs are a string of characters, which may include lower case letters, upper case letters and numbers. Pairing S2 node IDs are ideally as short as possible (at least one character), but should of course be long enough to allow the S2 endpoint to uniquely identify an S2 node. When an S2 endpoint only contains one S2 node, there is no need for a pairing S2 node ID. @@ -512,9 +512,16 @@ The user visits the S2ClientNodeUI and the S2ServerNode has been discovered (so > This section is only applicable for LAN-LAN pairing -The long-polling feature is intended for responder S2 nodes that only implement the HTTP client for pairing. It can be used by the responder S2 node (which implements the HTTP server) to notify it wants to pair. Long-polling can only be initiated by pairing endpoints that exclusively host RM S2 nodes and do not implement a pairing server. +The long-polling feature is intended to support S2 endpoints that only want to implement an HTTP client, and not an HTTP server. Typically this is because the S2 endpoint runs on constrained hardware. An S2 endpoint is only allowed to only implement the client if it exclusively hosts S2 nodes that have the RM role. -> Informative: Long-polling is a technique that allows the server to send signals to the client without a significant delay, and without relying on additional technologies such as Websockets or Server-Sent Events. The common alternative is polling, where the client sends a request on a regular interval; let's say every 30 seconds. Polling creates a delay from the perspective of the server. If the server wants to send something to the client, it has to wait until the client contacts the server; which in the worst case 30 seconds. With long-polling the server doesn't immediately respond the the request (a hanging HTTP request). It responds immediately when the server wants to client to do something, or just before the request would time out. After receiving the response from the server the client immediately opens a new request to allow the server to send signals the client again. +Imagine we have S2 endpoints, one only hosting a CEM S2 node, and one only hosting a RM S2 node. The RM runs on constrained hardware, and only implements the HTTP client. We have two situations: + +1. **The RM is the initiator S2 node and the CEM is the responder S2 node**: The CEM issues a pairing code, the end users enters the pairing code in the UI of the RM. The RM endpoint (HTTP client) then sends a normal HTTP request to the CEM endpoint (HTTP server) to initiate pairing. The normal pairing process can be used, and long-polling is not required. +2. **The CEM is the initiator S2 node and the RM is the responder S2 node**: The RM issues a pairing code (a dynamic pairing code through its UI, or a static pairing code for example through a sticker on the hardware), the end user enters the pairing code in the UI of the CEM. Now the CEM endpoint (HTTP server) cannot use the normal pairing process, since it has no way to contact the RM endpoint (HTTP client). + +For the second situation the long-polling feature can be used. It can be used by the initiator S2 node (the HTTP server) to notify the responder S2 node (the HTTP client) it wants to pair. + +> Informative: Long-polling is a technique that allows the server to send signals to the client without a significant delay, and without relying on additional technologies such as Websockets or Server-Sent Events. The common alternative is polling, where the client sends a request on a regular interval; let's say every 30 seconds. Polling creates a delay from the perspective of the server. If the server wants to send something to the client, it has to wait until the client contacts the server; which in the worst case 30 seconds. With long-polling the server doesn't immediately respond the the request (a hanging HTTP request). It responds immediately when the server wants the client to do something, or just before the request would time out. After receiving the response from the server the client immediately opens a new request to allow the server to send signals the client again. The long-polling feature fulfills the following functionality: * Make the existence of the client known to server, together with the S2 nodes IDs of the S2 nodes that are represented by the client endpoint @@ -529,9 +536,9 @@ The server **must** always respond within 25 seconds after receiving the request > TODO: Move the OpenAPI version selection process to its own section so we don't have to explain it every time -The client starts the process by doing a POST request to the `/waitForPairing` path. For full normative details see the OpenAPI specification files. The request body contains a list of objects. The client **must** always provide a for each S2 node ID it represents. The items in the list have a mandatory property `clientS2NodeId` and optional parameters `clientS2NodeDescription`, `clientS2EndpointDescription`. The client should only provide values for these properties when requested by the server. The object also contains the optional property `errorMessage`, which only should be used when an error has occurred before pairing. +The client starts the process by doing a POST request to the `/waitForPairing` path. For full normative details see the OpenAPI specification files. The request body contains a list of objects. The client **must** always provide an object for each S2 node ID it represents. The items in the list have a mandatory property `clientS2NodeId` and optional parameters `clientS2NodeDescription`, `clientS2EndpointDescription`. The client should only provide values for these properties when requested by the server. The object also contains the optional property `errorMessage`, which only should be used when an error has occurred before pairing. -When the server wants to client to immediately do a new request, it responds with status code 204. When it wants the client to do something, it responds with status 200 and a response body containing a list. This list contains an object only for S2 node IDs represented by the client, that the server wants to do something with. This object contains the mandatory properties `clientS2NodeId` and `action`. The `action` property is an enumeration indicating an action the server wants to execute for a specific S2 node. The possible action values are `sendS2NodeDescription`, `preparePairing`, `cancelPreparePairing` and `requestPairing`. +When the server wants the client to immediately do a new request, it responds with status code 204. When it wants the client to do something, it responds with status 200 and a response body containing a list. This list contains an object only for S2 node IDs represented by the client, that the server wants to do something with. This object contains the mandatory properties `clientS2NodeId` and `action`. The `action` property is an enumeration indicating an action the server wants to execute for a specific S2 node. The possible action values are `sendS2NodeDescription`, `preparePairing`, `cancelPreparePairing` and `requestPairing`. The table below indicates how the client should respond to the requests of the server. Note that the server could send multiple actions (for different S2 node IDs) in the same response. The server **cannot** provide multiple objects for the same S2 node ID in one response. @@ -544,10 +551,12 @@ The table below indicates how the client should respond to the requests of the s | 200 | `requestPairing` | Yes | Initiate the pairing for the mentioned S2NodeId and send the next request | Only the `clientS2NodeId` | | 200 | `requestPairing` | No | Initiate the pairing for the mentioned S2NodeId and send the next request | Only the `clientS2NodeId`, and for the associated object provide an `errorMessage` with value `NoValidTokenOnPairingClient` | | 400 | n/a | n/a | Stop long-polling until next time long-polling is advertised through DNS-SD | n/a | -| 401 | n/a | n/a | Stop long-polling | n/a | +| 401 | n/a | n/a | Stop long-polling, do not attempt long-polling with this node again | n/a | | 500 | n/a | n/a | Wait before trying to send the next request | Only the `clientS2NodeId`| -When the server sends the `requestPairing` action, the end user must have already entered the pairing code (which was generated by the server) in the UI of the client. If this is not the case, the client **must** perform a new request with an `errorMessage` containing the value `NoValidTokenOnPairingClient` in the object associated with the S2 node ID of the S2 node should have attempted to pair. +2. **The CEM is the initiator S2 node and the RM is the responder S2 node**: The RM issues a pairing code (a dynamic pairing code through its UI, or a static pairing code for example through a sticker on the hardware), the end user inters the pairing code in the UI of the CEM. Now the CEM endpoint (HTTP server) cannot use the normal pairing process, since it has no way to contact the RM endpoint (HTTP client). + +When the server sends the `requestPairing` action, the S2 node on the client must already have issued a pairing token. If the S2 node uses a dynamic pairing code, it could be the case that the pairing code has expired, or that no pairing code has been issued in the first place. In that case the client **must** perform a new request with an `errorMessage` containing the value `NoValidTokenOnPairingClient` in the object associated with the S2 node ID of the S2 node should have attempted to pair. The activity diagram below summarizes the complete long-polling process from teh perspective of the client.