From 316dc7b320c3d42fb236cdeab1e0c1e41e52bf00 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Thu, 2 Dec 2021 18:42:13 +0000 Subject: [PATCH] merge protocol changes from v5 (#211) --- protocol/agent-protocol.md | 180 +++++++++++++-------- protocol/simplex-messaging.md | 286 +++++++++++++++++++++++----------- 2 files changed, 313 insertions(+), 153 deletions(-) diff --git a/protocol/agent-protocol.md b/protocol/agent-protocol.md index 9d80beef0..00dbc873b 100644 --- a/protocol/agent-protocol.md +++ b/protocol/agent-protocol.md @@ -24,71 +24,68 @@ - [END notification](#end-notification) - [OFF command](#off-command) - [DEL command](#del-command) -- [Connection invitation](#connection-invitation) +- [Connection request](#connection-request) ## Abstract The purpose of SMP agent protocol is to define the syntax and the semantics of communications between the client and the agent that connects to [SMP](./simplex-messaging.md) servers. It provides: -- convenient protocol to create and manage bi-directional (duplex) connections between the users of SMP agents consisting of two (or more) separate unidirectional (simplex) SMP queues, abstracting away multiple steps required to establish bi-directional connections and any information about the servers location from the users of the protocol. -- management of E2E encryption between SMP agents, generating ephemeral RSA keys for each connection. -- SMP command authentication on SMP servers, generating ephemeral RSA keys for each SMP queue. -- TCP transport handshake and encryption with SMP servers. +- protocol to create and manage bi-directional (duplex) connections between the users of SMP agents consisting of two (or more) separate unidirectional (simplex) SMP queues, abstracting away multiple steps required to establish bi-directional connections and any information about the servers location from the users of the agent protocol. +- management of E2E encryption between SMP agents, generating ephemeral asymmetric keys for each connection. +- SMP command authentication on SMP servers, generating ephemeral keys for each SMP queue. +- TCP/TLS transport handshake with SMP servers. - validation of message integrity. -SMP agent protocols provides no encryption or any security on the client side - it is assumed that the agent is executed in the trusted and secure environment. - -The future versions of this protocol could provide: -- managing redundant SMP queues with more than 1 queue in each direction. -- managing simple symmetric groups as a foundation for chat groups and device synchronization. -- agent cluster - synchronizing states of multiple agents. -- secure "synchronous" streams with symmetric message encryption and connection-level authentication (requires extending [SMP protocol](./simplex-messaging.md)) - it can be used, e.g., for file transfers. +SMP agent protocol provides no encryption or security on the client side - it is assumed that the agent is executed in the trusted and secure environment, in one of three ways: +- via TCP network using secure connection. +- via local port (when the agent runs on the same device as a separate process). +- via agent library, when the agent logic is included directly into the client application - [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) uses this approach. ## SMP agent -SMP agent is a client-side process or library that communicates via SMP servers using [simplex messaging protocol (SMP)](./simplex-messaging.md) with other SMP agents according to the commands received from its users. This protocol is a middle layer in SMP protocols stack (above SMP protocol but below any application level protocol) - it is intended to be used by client-side applications that need secure asynchronous bi-directional communication channels ("connections"). +SMP agents communicate with each other via SMP servers using [simplex messaging protocol (SMP)](./simplex-messaging.md) according to the commands received from its users. This protocol is a middle layer in SimpleX protocols (above SMP protocol but below any application level protocol) - it is intended to be used by client-side applications that need secure asynchronous bi-directional communication channels ("connections"). -The agent must have a persistent storage to manage the states of known connections and of the client-side information of two SMP queues that each connection consists of, and also the buffer of the most recent messages. The number of the messages that should be stored is implementation specific, depending on the error management approach that the agent implements; at the very least the agent must store the hash and id of the last received message. +The agent must have a persistent storage to manage the states of known connections and of the client-side information of SMP queues that each connection consists of, and also the buffer of the most recent sent and received messages. The number of the messages that should be stored is implementation specific, depending on the error management approach that the agent implements; at the very least the agent must store the hashes and IDs of the last received and sent messages. ## SMP servers management -SMP agent protocol commands do not contain SMP servers that the agent will use to establish the connections between their users. The servers are part of the agent configuration and can be dynamically added and removed by the agent implementation: +SMP agent protocol commands do not contain the addresses of the SMP servers that the agent will use to create and use the connections (excluding the server address in queue URIs used in JOIN command). The list of the servers is a part of the agent configuration and can be dynamically changed by the agent implementation: - by the client applications via any API that is outside of scope of this protocol. -- by the agents themselves based on servers availability and latency. +- by the agents themselves based on availability and latency of the configured servers. ## SMP agent protocol components SMP agent protocol has 3 main parts: -- the syntax and semantics of messages that SMP agents exchange between each other in order to: - - negotiate establishing unidirectional (simplex) encrypted queues on SMP server(s) +- the syntax and semantics of the messages that SMP agents exchange with each other in order to: + - negotiate establishing unidirectional (simplex) encrypted queues on SMP servers. - exchange client messages and delivery notifications, providing sequential message IDs and message integrity (by including the hash of the previous message). -- the syntax and semantics of the commands (a higher level interface than SMP protocol) that are sent over TCP or other sequential protocol by agent clients to the agents. This protocol allows to create and manage multiple connections, each consisting of two simplex SMP queues. -- the syntax and semantics of the message that the clients of SMP agents should send out-of-band (as pre-shared "invitation" including SMP server, queue ID and encryption key) to ensure [E2E encryption][1] the integrity of SMP queues and protection against active attacks ([MITM attacks][2]). +- the syntax and semantics of the commands that are sent by the agent clients to the agents. This protocol allows to create and manage multiple connections, each consisting of two or more SMP queues. +- the syntax and semantics of the message that the clients of SMP agents should send out-of-band (as pre-shared "invitation" including queue URIs) to protect [E2E encryption][1] from active attacks ([MITM attacks][2]). ## Duplex connection procedure ![Duplex connection procedure](./diagrams/duplex-messaging/duplex-creating.svg) -The procedure of establishing a duplex connection is explained on the example of Alice and Bob creating a bi-directional connection comprised of two unidirectional (simplex) queues, using SMP agents (A and B) to facilitate it, and two different SMP servers (which could be the same server). It is shown on the diagram above and has these steps: +The procedure of establishing a duplex connection is explained on the example of Alice and Bob creating a bi-directional connection consisting of two unidirectional (simplex) queues, using SMP agents (A and B) to facilitate it, and two different SMP servers (which could be the same server). It is shown on the diagram above and has these steps: -1. Alice requests the new connection from the SMP agent A using `NEW` command. -2. Agent A creates an SMP connection on the server (using [SMP protocol](./simplex-messaging.md)) and responds to Alice with the invitation that contains queue information and the encryption key Bob's agent B should use. The invitation format is described in [Connection invitation](#connection-invitation). -3. Alice sends the invitation to Bob via any secure channel they have (out-of-band message). -4. Bob sends `JOIN` command with the invitation as a parameter to agent B to accept the connection. -5. Establishing Alice's SMP connection (with SMP protocol commands): - - Agent B sends an "SMP confirmation" to the SMP queue specified in the invitation - SMP confirmation is an unauthenticated message with an ephemeral key that will be used to authenticate Bob's commands to the queue, as described in SMP protocol, and Bob's info. - - Agent A receives the SMP confirmation containing Bob's key and info. +1. Alice requests the new connection from the SMP agent A using SMP NEW command. +2. Agent A creates an SMP connection on the server (using [SMP protocol](./simplex-messaging.md)) and responds to Alice with the invitation that contains queue information and the encryption key Bob's agent B should use. The invitation format is described in [Connection request](#connection-request). +3. Alice sends the [connection request](#connection-request) to Bob via any secure channel (out-of-band message). +4. Bob sends `JOIN` command with the connection request as a parameter to agent B to accept the connection. +5. Establishing Alice's SMP queue (with SMP protocol commands): + - Agent B sends an "SMP confirmation" with SMP SEND command to the SMP queue specified in the connection request - SMP confirmation is an unauthenticated message with an ephemeral key that will be used to authenticate Bob's commands to the queue, as described in SMP protocol, and Bob's info (profile, public key for E2E encryption, etc.). This message is encrypted using key passed in the connection request (or with the derived key, in which case public key for key derivation should be sent in clear text). + - Agent A receives the SMP confirmation containing Bob's key and info as SMP MSG. - Agent A notifies Alice sending REQ notification with Bob's info. - Alice accepts connection request with ACPT command. - - Agent A secures the queue. + - Agent A secures the queue with SMP KEY command. - Agent B tries sending authenticated SMP SEND command with agent `HELLO` message until it succeeds. Once it succeeds, Bob's agent "knows" the queue is secured. 6. Agent B creates a new SMP queue on the server. 7. Establish Bob's SMP queue: - - Agent B sends `REPLY` message with the invitation to this 2nd queue to Alice's agent (via the 1st queue). - - Agent A, having received this `REPLY` message, sends unauthenticated message to SMP queue with Alice agent's ephemeral key that will be used to authenticate Alice's commands to the queue, as described in SMP protocol, and Alice's info. - - Bob's agent receives the key and Alice's information and secures the queue. + - Agent B sends `REPLY` message (SMP SEND command) with the connection request to this 2nd queue to Alice's agent (via the 1st queue) - this connection request SHOULD use "simplex" URI scheme. + - Agent A, having received `REPLY` message, sends unauthenticated message (SMP SEND) to SMP queue with Alice agent's ephemeral key that will be used to authenticate Alice's commands to the queue, as described in SMP protocol, and Alice's info. + - Bob's agent receives the key and Alice's information and secures the queue (SMP KEY). - Bob's agent sends the notification `INFO` with Alice's information to Bob. - Alice's agent keeps sending `HELLO` message until it succeeds. 8. Agents A and B notify Alice and Bob that connection is established. @@ -97,17 +94,21 @@ The procedure of establishing a duplex connection is explained on the example of At this point the duplex connection between Alice and Bob is established, they can use `SEND` command to send messages. The diagram also shows how the connection status changes for both parties, where the first part is the status of the SMP queue to receive messages, and the second part - the status of the queue to send messages. -The most communication happens between the agents and servers, from the point of view of Alice and Bob they have only 3 steps to do: +The most communication happens between the agents and servers, from the point of view of Alice and Bob there are 4 steps (not including notifications): 1. Alice requests a new connection with `NEW` command and receives the invitation. -2. Alice passes invitation out-of-band to Bob. -3. Bob accepts the connection by sending `JOIN` command with the invitation to his agent. +2. Alice passes connection request out-of-band to Bob. +3. Bob accepts the connection with `JOIN` command with the connection request to his agent. +4. Alice accepts the connection with `ACPT` command. +5. Both parties receive `CON` notification once duplex connection is established. + +Clients SHOULD support establishing duplex connection asynchronously (when parties are intermittently offline) by persisting intermediate states and resuming SMP queue subscriptions. ## Communication between SMP agents -SMP agents communicate via SMP servers managing creation, deletion and operations of SMP queues. +To establish duplex connections and to send messages on behalf of their clients, SMP agents communicate via SMP servers. -Agents can use SMP message client body (the part of the SMP message after header - see [SMP protocol](./simplex-messaging.md)) to transmit agent client messages and exchange messages between each other. +Agents use SMP message client body (the part of the SMP message after header - see [SMP protocol](./simplex-messaging.md)) to transmit agent client messages and exchange messages between each other. Each SMP message client body, once decrypted, contains 3 parts (one of them may include binary message body), as defined by `decryptedSmpMessageBody` syntax: @@ -115,8 +116,9 @@ Each SMP message client body, once decrypted, contains 3 parts (one of them may - `agentMessage` - a command/message to the other SMP agent: - to establish the connection with two SMP queues (`helloMsg`, `replyQueueMsg`) - to send and to acknowledge user messages (`clientMsg`, `acknowledgeMsg`) - - to notify another agent about queue deletion (`deleteQueueMsg`) -- `msgPadding` - an optional message padding to make all SMP messages have consistent size as an additional privacy protection measure. + - to manage SMP queue rotation (`newQueueMessage`, `deleteQueueMsg`) + - to manage encryption key rotation (TODO) +- `msgPadding` - an optional message padding to make all SMP messages have constant size, to prevent servers from observing the actual message size. ### Messages between SMP agents @@ -130,7 +132,9 @@ agentTimestamp = ; RFC3339 previousMsgHash = encoded encoded = -agentMessage = helloMsg / replyQueueMsg / deleteQueueMsg / clientMsg / acknowledgeMsg +agentMessage = helloMsg / replyQueueMsg / + clientMsg / acknowledgeMsg / + newQueueMessage / deleteQueueMsg msgPadding = *OCTET ; optional random bytes to get messages to the same size (as defined in SMP message size) @@ -138,19 +142,18 @@ helloMsg = %s"HELLO" SP signatureVerificationKey [SP %s"NO_ACK"] ; NO_ACK means that acknowledgements to client messages will NOT be sent in this connection by the agent that sent `HELLO` message. signatureVerificationKey = encoded -replyQueueMsg = %s"REPLY" SP ; `queueInfo` is the same as in out-of-band message, see SMP protocol +replyQueueMsg = %s"REPLY" SP connectionRequest ; `connectionRequest` is defined below ; this message can only be sent by the second connection party -deleteQueueMsg = %s"DEL" ; notification that recipient queue will be deleted -; no need to notify the other party about suspending queue separately, as suspended and deleted queues are the same to the sender -; NOT SUPPORTED with the current implementation - clientMsg = %s"MSG" SP size CRLF clientMsgBody CRLF ; CRLF is in addition to CRLF in decryptedSmpMessageBody size = 1*DIGIT clientMsgBody = *OCTET -acknowledgeMsg = %s"ACK" SP agentMsgId SP ackStatus -; NOT SUPPORTED with the current implementation +acknowledgeMsg = %s"ACK" SP agentMsgId SP msgHash SP ackStatus +; NOT SUPPORTED in the current implementation + +msgHash = encoded +; base64 encoded hash of the received message ackStatus = %s"OK" / ackError @@ -160,26 +163,47 @@ ackErrorType = ackUnknownMsg / ackProhibitedMsg / ackSyntaxErr ackUnknownMsg = %s"UNKNOWN" -ackProhibitedMsg = %s"PROHIBITED" ; e.g. "HELLO" or "REPLY" +ackProhibitedMsg = %s"PROHIBITED" ; unexpected message e.g. "HELLO" or "REPLY" ackSyntaxErr = %s"SYNTAX" SP syntaxErrCode syntaxErrCode = 1*DIGIT ; TODO + +newQueueMsg = %s"NEW" SP queueURI +; this message can be sent by any party to add SMP queue to the connection. +; NOT SUPPORTED in the current implementation + +deleteQueueMsg = %s"DEL" SP queueURI +; notification that the queue with passed URI will be deleted +; no need to notify the other party about suspending queue separately, as suspended and deleted queues are indistinguishable to the sender +; NOT SUPPORTED in the current implementation ``` #### HELLO message -This is the first message that both agents send after the respective SMP queue is secured by the receiving agent (see diagram). It contains the verification key that the sender will use to cryptographically sign the messages. +This is the first message that both agents send after the respective SMP queue is secured by the receiving agent (see diagram). It MAY contain the public key that the recipient would use to verify messages signed by the sender. -Sending agent might need to retry sending HELLO message, as it would not have any other confirmation that the queue is secured other than the success of sending this message with the signed SEND command of SMP protocol. +Sending agent might need to retry sending HELLO message, as it would not have any other confirmation that the queue is secured other than the success of sending this message with the signed SMP SEND command. #### REPLY message -This is the message that is sent by the agent that received an out-of-band invitation to pass the invitation to the reply SMP queue to the agent that originated the connection (see diagram). +This is the message that is sent by the agent that received an out-of-band connection request to pass the connection request for the reply SMP queues to the agent that originated the connection (see diagram). #### MSG message This is the agent envelope used to send client messages once the connection is established. Do not confuse it with the MSG response from SMP server to the agent and MSG response from SMP agent to the client that are sent in different contexts. +#### ACK message + +This message is sent to confirm the client message reception. It includes received message number, message hash and the reception status. + +#### NEW message + +This message is sent to add an additional SMP queue to the connection. Unlike REPLY message it can be sent at any time. + +#### DEL message + +This message is sent to notify that the queue with passed URI will be deleted - having received this message, the receiving agent should no longer send messages to this queue. In case it was the only queue in the connection to which the agent could send the messages, it MAY also delete the reply queue(s) in the connection. + ## SMP agent commands This part describes the transmissions between users and client-side SMP agents: commands that the users send to create and operate duplex connections and SMP agent responses and messages they deliver. @@ -204,7 +228,7 @@ agentMsg = invitation / connRequest / connInfo / connected / unsubscribed / conn newCmd = %s"NEW" [SP %s"NO_ACK"] ; response is `invitation` or `error` ; NO_ACK parameter currently not supported -invitation = %s"INV" SP ; `queueInfo` is the same as in out-of-band message, see SMP protocol +invitation = %s"INV" SP ; `connectionRequest` is defined below connRequest = %s"REQ" SP confirmationId SP msgBody ; msgBody here is any binary information identifying connection request @@ -231,8 +255,8 @@ connDown = %s"DOWN" connUp = %s"UP" ; restored connection -joinCmd = %s"JOIN" SP [SP %s"NO_REPLY"] [SP %s"NO_ACK"] -; `queueInfo` is the same as in out-of-band message, see SMP protocol +joinCmd = %s"JOIN" SP [SP %s"NO_REPLY"] [SP %s"NO_ACK"] +; `connectionRequest` is defined below ; response is `connected` or `error` suspendCmd = %s"OFF" ; can be sent by either party, response `ok` or `error` @@ -275,9 +299,12 @@ previousMsgId = agentMsgId acknowledgeCmd = %s"ACK" SP agentMsgId ; ID assigned by receiving agent (in MSG "R") -received = %s"RCVD" SP agentMsgId ; ID assigned by sending agent (in SENT response) +received = %s"RCVD" SP agentMsgId SP msgIntegrity +; ID assigned by sending agent (in SENT response) ; currently not implemented +msgStatus = ok | error + ok = %s"OK" error = %s"ERR" SP @@ -287,13 +314,13 @@ error = %s"ERR" SP #### NEW command and INV response -`NEW` command is used to create a connection and an invitation to be sent out-of-band to another protocol user (the joining party). It should be used by the client of the agent that initiates creating a duplex connection (the initiating party). +`NEW` command is used to create a connection and a connection request to be sent out-of-band to another protocol user (the joining party). It should be used by the client of the agent that initiates creating a duplex connection (the initiating party). `INV` response is sent by the agent to the client of the initiating party. #### JOIN command -It is used to create a connection and accept the invitation received out-of-band. It should be used by the client of the agent that accepts the connection (the joining party). +It is used to create a connection and accept the connection request received out-of-band. It should be used by the client of the agent that accepts the connection (the joining party). #### REQ notification and ACPT command @@ -311,17 +338,17 @@ Once the connection is established and ready to accept client messages, both age This command can be used by the client to resume receiving messages from the connection that was created in another TCP/client session. Agent response to this command can be `OK` or `ERR` in case connection does not exist (or can only be used to send connections - e.g. when the reply queue was not created). -#### SEND command and MID, SENT and MERR responses +#### SEND command and MID, SENT, RCVD and MERR responses `SEND` command is used by the client to send messages. -`MID` notification with the message ID (the sequential message number that includes both sent and received messages in the connection) is sent to the client to confirm that the message is accepted by the agent, before it is sent to the SMP server. +`MID` response with the message ID (the sequential message number that includes both sent and received messages in the connection) is sent to the client to confirm that the message is accepted by the agent, before it is sent to the SMP server. -`SENT` response is sent by the agent to confirm that the message was delivered to the SMP server. This notification contains the same message ID as `MID` notification. `SENT` notification, depending on network availability, can be sent at any time later, potentially in the next client session. +`SENT` notification is sent by the agent to confirm that the message was delivered to at least one of SMP servers. This notification contains the same message ID as `MID` notification. `SENT` notification, depending on network availability, can be sent at any time later, potentially in the next client session. -In case of the failure to send the message for any other reason than network connection or message queue quota - e.g. authentication error (`ERR AUTH`) or syntax error (`ERR CMD error`), the agent will send to the client `MERR` notification with the message ID, and this message delivery will no longer be attempted. +`RCVD` notification is sent by the agent when it receives `ACK` message from the receiving agent. This notification contains reception status, only one successful notification will be sent, and multiple error notifications will be sent in case `ACK` had error status. -In case of client disconnecting from the agent, the pending messages will not be sent until the client re-connects to the agent and subscribes to the connection that has pending messages. +In case of the failure to send the message for any other reason than network connection or message queue quota - e.g. authentication error (`ERR AUTH`) or syntax error (`ERR CMD error`), the agent will send to the client `MERR` notification with the message ID, and this message delivery will no longer be attempted to this SMP queue. #### MSG notification @@ -348,11 +375,34 @@ It is used to suspend the receiving SMP queue - sender will no longer be able to It is used to delete the connection and all messages in it, as well as the receiving SMP queue and all messages in it that were remaining on the server. Agent response to this command can be `OK` or `ERR`. This command is irreversible. -## Connection invitation +## Connection request -Connection invitation `queueInfo` is generated by SMP agent in response to `newCmd` command (`"NEW"`), used by another party user with `joinCmd` command (`"JOIN"`), and then another invitation is sent by the agent in `replyQueueMsg` and used by the first party agent to connect to the reply queue (the second part of the process is invisible to the users). +Connection request `connectionRequest` is generated by SMP agent in response to `newCmd` command (`"NEW"`), used by another party user with `joinCmd` command (`"JOIN"`), and then another connection request is sent by the agent in `replyQueueMsg` and used by the first party agent to connect to the reply queue (the second part of the process is invisible to the users). -See SMP protocol [out-of-band messages](./simplex-messaging.md#out-of-band-messages) for connection invitation syntax. +Connection request syntax: + +``` +connectionRequest = connectionProtocol "/" action "#/?smp=" smpQueues "&e2e=" e2eEncryption +action = %s"connect" +connectionProtocol = (%s"https://" clientAppServer) | %s"simplex:" +clientAppServer = hostname [ ":" port ] +; client app server, e.g. simplex.chat +e2eEncryption = encryptionScheme ":" publicKey +encryptionScheme = %s"rsa" ; end-to-end encryption and key exchange protocols, + ; the current hybrid encryption scheme (RSA-OAEP/AES-256-GCM-SHA256) + ; will be replaced with double ratchet protocol and DH key exchange. +publicKey = +smpQueues = smpQueue [ "," 1*smpQueue ] ; SMP queues for the connection +smpQueue = +``` + +All parameters are passed via URI hash to avoid sending them to the server (in case "https" scheme is used) - they can be used by the client-side code and processed by the client application. Parameters `smp` and `e2e` can be present in any order, any unknown additional parameters SHOULD be ignored. + +`clientAppServer` is not an SMP server - it is a server that shows the instruction on how to download the client app that will connect using this connection request. This server can also host a mobile or desktop app manifest so that this link is opened directly in the app if it is installed on the device. + +"simplex" URI scheme in `connectionProtocol` can be used instead of client app server, to connect without creating any web traffic. Client apps MUST support this URI scheme. + +See SMP protocol [out-of-band messages](./simplex-messaging.md#out-of-band-messages) for syntax of `queueURI`. [1]: https://en.wikipedia.org/wiki/End-to-end_encryption [2]: https://en.wikipedia.org/wiki/Man-in-the-middle_attack diff --git a/protocol/simplex-messaging.md b/protocol/simplex-messaging.md index 884ae98ec..1ae222b1d 100644 --- a/protocol/simplex-messaging.md +++ b/protocol/simplex-messaging.md @@ -7,11 +7,13 @@ - [SMP Model](#smp-model) - [Out-of-band messages](#out-of-band-messages) - [Simplex queue](#simplex-queue) +- [SMP queue URI](#smp-queue-uri) - [SMP procedure](#smp-procedure) - [SMP qualities and features](#smp-qualities-and-features) - [Cryptographic algorithms](#cryptographic-algorithms) - [Simplex queue IDs](#simplex-queue-ids) -- [Server privacy requirements](#server-privacy-requirements) +- [Server security requirements](#server-security-requirements) +- [Message delivery notifications](#message-delivery-notifications) - [SMP commands](#smp-commands) - [Correlating responses with commands](#correlating-responses-with-commands) - [Command authentication](#command-authentication) @@ -20,14 +22,19 @@ - [Create queue command](#create-queue-command) - [Subscribe to queue](#subscribe-to-queue) - [Secure queue command](#secure-queue-command) + - [Enable notifications command](#enable-notifications-command) - [Acknowledge message delivery](#acknowledge-message-delivery) - [Suspend queue](#suspend-queue) - [Delete queue](#delete-queue) - [Sender commands](#sender-commands) - [Send message](#send-message) + - [Notifier commands](#notifier-commands) + - [Subscribe to queue notifications](#subscribe-to-queue-notifications) - [Server messages](#server-messages) - [Queue IDs response](#queue-ids-response) - [Deliver queue message](#deliver-queue-message) + - [Notifier queue ID response](#notifier-queue-id-response) + - [Deliver message notification](#deliver-message-notification) - [Subscription END notification](#subscription-end-notification) - [Error responses](#error-responses) - [OK response](#ok-response) @@ -66,37 +73,15 @@ The SMP model has three communication participants: the recipient, the message b SMP server manages multiple "simplex queues" - data records on the server that identify communication channels from the senders to the recipients. The same communicating party that is the sender in one queue, can be the recipient in another - without exposing this fact to the server. -The queue record consists of 2 unique random IDs generated by the server, one for the recipient and another for the sender, and 2 keys to authenticate the recipient and the sender respectively, provided by the client. The users of SMP protocol must use a unique key for each queue, to avoid the possibility of aggregating and analysing their queues in case SMP server is compromised. +The queue record consists of 2 unique random IDs generated by the server, one for the recipient and another for the sender, and 2 keys to authenticate the recipient and the sender respectively, provided by the client. The users of SMP protocol must use a unique key for each queue, to avoid the possibility of aggregating and analyzing their queues in case SMP server is compromised. Creating and using the queue requires sending commands to the SMP server from the recipient and the sender - they are described in detail in [SMP commands](#smp-commands) section. ## Out-of-band messages -The out-of-band message with the queue information is sent via some trusted alternative channel from the recipient to the sender. This message is used to share the encryption (a.k.a. "public") key that the sender will use to encrypt the messages (to be decrypted by the recipient), sender queue ID, server hostname and any other information necessary to establish secure encrypted connection with SMP server (see [Appendix A](#appendix-a) for SMP transport protocol). +The out-of-band message with the queue information is sent via some trusted alternative channel from the recipient to the sender. This message is used to share one or several [queue URIs](#smp-queue-uri) that parties can use to establish the initial connection, the encryption scheme and, it can include the public key(s) for end-to-end encryption. -The [ABNF][8] syntax of the message is: - -```abnf -queueInfo = %s"smp::" smpServer "::" queueId "::" encryptionKey -smpServer = srvHost [":" port] ["#" serverKeyHash] -srvHost = ; RFC1123, RFC5891 -port = 1*DIGIT -serverKeyHash = encoded -queueId = encoded -encryptionKey = %s"rsa:" x509encoded ; the recipient's RSA public key for sender to encrypt messages -x509encoded = -encoded = -``` - -`hostname` can be IP address or domain name, as defined in RFC 1123, section 2.1. - -`port` is optional, the default TCP port for SMP protocol is 5223. - -`serverKeyHash` is an optional hash of the server transport key used during transport handshake (see [Appendix A](#appendix-a)). - -Encryption keys are encoded using [X509][11] specification. - -Defining the approach to out-of-band message passing is out of scope of this protocol. +The approach to out-of-band message passing and their syntax should be defined in application-level protocols. ## Simplex queue @@ -124,6 +109,29 @@ Queue is defined by recipient ID `RID` and sender ID `SID`, unique for the serve The protocol uses different IDs for sender and recipient in order to provide an additional privacy by preventing the correlation of senders and recipients commands sent over the network - in case the encrypted transport is compromised, it would still be difficult to correlate senders and recipients without access to the queue records on the server. +## SMP queue URI + +The SMP queue URIs MUST include server identity, queue hostname, an optional port, sender queue ID and the public key that the clients must use to verify responses. Server identity is used to establish secure connection protected from MITM attack with SMP server (see [Appendix A](#appendix-a) for SMP transport protocol). + +The [ABNF][8] syntax of the queue URI is: + +```abnf +queueURI = %s"smp://" smpServer "/" queueId "#" serverSignaturePublicKey +; serverSignaturePublicKey syntax is defined below +smpServer = serverIdentity "@" srvHost [":" port] +srvHost = ; RFC1123, RFC5891 +port = 1*DIGIT +serverIdentity = base64url +queueId = base64url +base64url = ; RFC4648, section 5 +``` + +`hostname` can be IP address or domain name, as defined in RFC 1123, section 2.1. + +`port` is optional, the default TCP port for SMP protocol is 5223. + +`serverIdentity` is a required hash of the server certificate SPKI block (without line breaks, header and footer) used by the client to validate server certificate during transport handshake (see [Appendix A](#appendix-a)) + ## SMP procedure The SMP procedure of creating a simplex queue on SMP server is explained using participants Alice (the recipient) who wants to receive messages from Bob (the sender). @@ -266,7 +274,7 @@ Simplex Messaging Protocol: - One unique "public" key is used by the servers to authenticate requests to send the messages into the queue, and another unique "public" key - to retrieve the messages from the queue. "Unique" here means that each "public" key is used only for one queue and is not used for any other context - effectively, this key is not public and does not represent any participant identity. - - Both recipient and sender "public" keys are provided to the server by the queue recipient. "Public" key `RK` is provided when the queue is created, public key `SK` is proviced when the queue is secured. + - Both recipient and sender "public" keys are provided to the server by the queue recipient. "Public" key `RK` is provided when the queue is created, public key `SK` is provided when the queue is secured. - The "public" keys known to the server and used to authenticate commands from the participants are unrelated to the keys used to encrypt and decrypt the messages - the latter keys are also unique per each queue but they are only known to participants, not to the servers. @@ -274,29 +282,36 @@ Simplex Messaging Protocol: ## Cryptographic algorithms -Simplex messaging clients need to cryptographically sign commands for the following operations: +Simplex messaging clients and servers must cryptographically sign commands, responses and messages for the following operations: - With the recipient's key `RK` (server to verify): - create the queue (`NEW`) - subscribe to queue (`SUB`) - secure the queue (`KEY`) + - enable queue notifications (`NKEY`) - acknowledge received messages (`ACK`) - suspend the queue (`OFF`) - delete the queue (`DEL`) - With the sender's key `SK` (server to verify): - send messages (`SEND`) +- With the optional notifier's key: + - subscribe to message notifications (`NSUB`) +- With the server's key (for recipient and sender to verify) + - queue IDs response (`IDS`) + - notifier queue ID response (`NID`) + - delivered messages (`MSG`) + - `OK` and `ERR` responses (excluding error responses not related to a queue) -To sign and verify commands, clients and servers MUST use RSA-PSS algorithm defined in [RFC3447][2]. +To sign/verify transmissions clients and servers MUST use Ed25519 or Ed448 algorithm defined in [RFC8709][15]. -To optionally sign and verify messages, clients SHOULD use RSA-PSS algorithm. +To encrypt/decrypt message bodies delivered to the recipients, servers/clients MUST use x25519 or x448 algorithm defined in [RFC8709][15] to derive the shared secret (TODO encryption scheme). -To encrypt and decrypt messages, clients and servers SHOULD use RSA-OAEP algorithm defined in [RFC3447][2]. +Clients MUST encrypt message bodies sent via SMP servers - the protocol for this end-to-end encryption should be chosen by the clients using SMP protocol. The reasons to use these algorithms: -- They are supported by WebCrypto API. -- They are more widely supported than ECC algorithms. -- They are newer versions than RSA-PKCS1-v1_5 encryption and signature schemes. +- Faster operation than RSA algorithms. +- DH key exchange provides forward secrecy. Future versions of the protocol may allow different cryptographic algorithms. @@ -319,6 +334,18 @@ Simplex messaging server implementations MUST NOT create, store or send to any o - Any other information that may compromise privacy or [forward secrecy][4] of communication between clients using simplex messaging servers. +## Message delivery notifications + +Supporting message delivery while the client mobile app is not running requires sending push notifications with the device token. All alternative mechanisms for background message delivery are unreliable, particularly on iOS platform. Obviously, supporting push notification delivery by simply subscribing to messages would reduce meta-data privacy as it allows to see all queues that a given device uses. + +To protect the privacy of the recipients, there are several commands in SMP protocol that allow enabling and subscribing to message notifications from SMP queues, using separate set of "notifier keys" and via separate queue IDs - as long as SMP server is not compromised, these notifier queue IDs cannot be correlated with recipient or sender queue IDs. + +The clients can optionally instruct a dedicated push notification server to subscribe to notifications and deliver push notifications to the device, which can then retrieve the messages in the background and send local notifications to the user - this is out of scope of SMP protocol. The commands that SMP protocol provides to allow it: + +- `enableNotifications` (`"NKEY"`) with `notifierId` (`"NID"`) response - see [Enable notifications command](#enable-notifications-command). +- `subscribeNotifications` (`"NSUB"`) - see [Subscribe to queue notifications](#subscribe-to-queue-notifications). +- `messageNotification` (`"NMSG"`) - see [Deliver message notification](#deliver-message-notification). + ## SMP commands Commands syntax below is provided using [ABNF][8] with [case-sensitive strings extension][8a]. @@ -327,14 +354,18 @@ Each transmission between the client and the server must have this format/syntax ```abnf transmission = [signature] SP signed SP pad ; pad to the fixed block size -signed = [corrId] SP [queueId] SP cmd -cmd = ping / recipientCmd / send / serverMsg -recipientCmd = create / subscribe / secure / acknowledge / suspend / delete -serverMsg = pong / queueIds / message / unsubscribed / ok / error +signed = sessionIdentifier SP [corrId] SP [queueId] SP cmd ; corrId is required in client commands and server responses, + ; corrId is empty in server notifications. +cmd = ping / recipientCmd / send / subscribeNotifications / serverMsg +recipientCmd = create / subscribe / secure / enableNotifications / + acknowledge / suspend / delete +serverMsg = queueIds / message / notifierId / messageNotification / + unsubscribed / ok / error corrId = 1*(%x21-7F) ; any characters other than control/whitespace queueId = encoded ; empty queue ID is used with "create" command signature = encoded -; empty signature can be used with "create", "send" and "ping" commands and server messages +; empty signature can be used with "send" before the queue is secured with secure command +; signature is always empty with "ping" and "serverMsg" encoded = ``` @@ -344,23 +375,24 @@ The syntax of specific commands and responses is defined below. ### Correlating responses with commands -The server should send `queueIds`, `error` and `ok` responses in the same order within each queue ID as the commands received in the transport connection, so that they can be correlated by the clients. To simplify correlation of commands and responses, the server should use the same `corrId` in the response as in the command sent by the client. +The server should send `queueIds`, `error` and `ok` responses in the same order within each queue ID as the commands received in the transport connection, so that they can be correlated by the clients. To simplify correlation of commands and responses, the server must use the same `corrId` in the response as in the command sent by the client. If the transport connection is closed before some responses are sent, these responses should be discarded. ### Command authentication -SMP servers must authenticate all transmissions (excluding `ping` and `send` commands) by verifying the provided signatures. Command signature should be generated by applying RSA-PSS algorithm to the `signed` block of the transmission using the key associated with the queue ID (sender's or recipient's, depending on which queue ID is used). +SMP servers must authenticate all transmissions (excluding `ping` and initial `send` commands) by verifying the client signatures. Command signature should be generated by applying the algorithm specified for the queue to the `signed` block of the transmission, using the key associated with the queue ID (recipient's, sender's or notifier's, depending on which queue ID is used). ### Keep-alive command -To keep the transport connection alive and to generate noise traffic the clients should use `ping` command to which the server responds with `pong` response. This command should be sent unsigned and without queue ID. +To keep the transport connection alive and to generate noise traffic the clients should use `ping` command to which the server responds with `ok` response. This command should be sent unsigned and without queue ID. ```abnf ping = %s"PING" -pong = %s"PONG" ``` +This command is always send unsigned. + ### Recipient commands Sending any of the commands in this section (other than `create`, that is sent without queue ID) is only allowed with recipient's ID (`RID`). If sender's ID is used the server must respond with `"ERR AUTH"` response (see [Error responses](#error-responses)). @@ -370,24 +402,45 @@ Sending any of the commands in this section (other than `create`, that is sent w This command is sent by the recipient to the SMP server to create a new queue. The syntax is: ```abnf -create = %s"NEW" SP recipientKey -recipientKey = %s"rsa:" x509encoded ; the recipient's RSA public key for this queue +create = %s"NEW" SP recipientSignaturePublicKey SP recipientDhPublicKey +recipientSignaturePublicKey = signaturePublicKey +; the recipient's public key to verify commands for this queue + +signaturePublicKey = signatureScheme ":" x509encoded +signatureScheme = %s"rsa" | %s"ed25519" | %s"ed448" +; "rsa" means deprecated RSA-PSS signature scheme, +; it must not be used for the new queues. + +recipientDhPublicKey = dhPublicKey +dhPublicKey = encryptionScheme ":" x509encoded +; the recipient's key for DH exchange to derive the secret +; that the server will use to encrypt delivered message bodies + +encryptionScheme = %s"x25519" | %s"x448" +; TODO change to define the encryption scheme, e.g. "crypto_box" + x509encoded = ``` -If the queue is created successfully, the server must send `queueIds` response with the recipient's and sender's queue IDs: +If the queue is created successfully, the server must send `queueIds` response with the recipient's and sender's queue IDs and public keys to sign all responses and messages and to encrypt delivered message bodies: ```abnf queueIds = %s"IDS" SP recipientId SP senderId + SP serverSignaturePublicKey SP serverDhPublicKey +serverSignaturePublicKey = signatureKey +; the server's public key to verify responses and messages for this queue +serverDhPublicKey = dhPublicKey +; the server's key for DH exchange to derive the secret +; that the server will use to encrypt delivered message bodies to the recipient recipientId = encoded senderId = encoded ``` -This response should be sent with empty queue ID (the second part of the transmission). - Once the queue is created, the recipient gets automatically subscribed to receive the messages from that queue, until the transport connection is closed. The `subscribe` command is needed only to start receiving the messages from the existing queue when the new transport connection is opened. -NEW `transmission` must be signed using the `recipientKey` that was passed in the transmission. +`NEW` transmission MUST be signed using the private part of the `recipientSignaturePublicKey` – this verifies that the client has the private key that will be used to sign subsequent commands for this queue. + +`IDS` response transmission MUST be sent signed with `serverSignaturePublicKey` – this verifies that the server has the private key that will be used to sign subsequent responses and messages for this queue. This response should be sent with empty queue ID (the third part of the transmission). #### Subscribe to queue @@ -401,19 +454,42 @@ If subscription is successful the server must respond with the first available m The first message will be delivered either immediately or as soon as it is available; to receive the following message the recipient must acknowledge the reception of the message (see [Acknowledge message delivery](#acknowledge-message-delivery)). +This transmission and its response MUST be signed. + #### Secure queue command This command is sent by the recipient to the server to add sender's key to the queue: ```abnf -secure = %s"KEY" SP senderKey -senderKey = %s"rsa:" x509encoded ; the sender's RSA public key for this queue +secure = %s"KEY" SP senderSignaturePublicKey +senderSignaturePublicKey = signaturePublicKey +; the sender's key to verify SEND commands for this queue ``` `senderKey` is received from the sender as part of the first message - see [Send Message](#send-message) command. Once the queue is secured only signed messages can be sent to it. +#### Enable notifications command + +This command is sent by the recipient to the server to add notifier's key to the queue, to allow push notifications server to receive notifications when the message arrives, via a separate queue ID, without receiving message content. + +```abnf +enableNotifications = %s"NKEY" SP notifierKey +notifierKey = signatureScheme ":" x509encoded ; the notifier's public key public key to verify NSUB command for this queue +``` + +The server will respond with `notifierId` response if notifications were enabled and the notifier's key was successfully added to the queue: + +```abnf +notifierId = %s"NID" SP notifierId +recipientId = encoded +``` + +This response is sent with the recipient's queue ID (the third part of the transmission). + +To receive the message notifications, `subscribeNotifications` command ("NSUB") must be sent signed with the notifier's key. + #### Acknowledge message delivery The recipient should send the acknowledgement of message delivery once the message was stored in the client, to notify the server that the message should be deleted: @@ -485,12 +561,26 @@ The body should be encrypted with the recipient's "public" key (`EK`); once decr decryptedBody = [clientHeader] CRLF clientBody CRLF clientHeader = senderKeyMsg senderKeyMsg = %s"KEY" SP senderKey -senderKey = %s"rsa:" x509encoded ; the sender's RSA public key for this queue +senderKey = signatureScheme ":" x509encoded ; the sender's public key to sign SEND commands for this queue clientBody = *OCTET ``` `clientHeader` in the initial unsigned message is used to transmit sender's server key and can be used in the future revisions of SMP protocol for other purposes. +### Notifier commands + +#### Subscribe to queue notifications + +The push notifications server (notifier) must use this command to start receiving message notifications from the queue: + +```abnf +subscribeNotifications = %s"NSUB" +``` + +If subscription is successful the server must respond with `ok` response if no messages are available. The notifier will be receiving the message notifications from this queue until the transport connection is closed or until another transport connection subscribes to notifications from the same simplex queue - in this case the first subscription should be cancelled and [subscription END notification](#subscription-end-notification) delivered. + +The first message notification will be delivered either immediately or as soon as the message is available. + ### Server messages #### Queue IDs response @@ -504,7 +594,9 @@ See its syntax in [Create queue command](#create-queue-command) The server must deliver messages to all subscribed simplex queues on the currently open transport connection. The syntax for the message delivery is: ```abnf -message = %s"MSG" SP msgId SP timestamp SP size SP msgBody SP +message = %s"MSG" SP encryptedMessage +encryptedMessage = +sentMessage = msgId SP timestamp SP size SP msgBody SP msgId = encoded timestamp = ``` @@ -513,7 +605,27 @@ timestamp = `timestamp` - the UTC time when the server received the message from the sender, must be in date-time format defined by [RFC 3339][10] -`binaryMsg` - see syntax in [Send message](#send-message) +`msgBody` - see syntax in [Send message](#send-message) + +When server delivers the messages to the recipient, message body should be encrypted with the secret derived from DH exchange using the keys passed during the queue creation and returned with `queueIds` response. + +This is done to prevent the possibility of correlation of incoming and outgoing traffic of SMP server inside transport protocol. + +#### Notifier queue ID response + +Server must respond with this message when queue notifications are enabled. + +See its syntax in [Enable notifications command](#enable-notifications-command) + +#### Deliver message notification + +The server must deliver message notifications to all simplex queues that were subscribed with `subscribeNotifications` command ("NSUB") on the currently open transport connection. The syntax for the message notification delivery is: + +```abnf +messageNotification = %s"NMSG" +``` + +Message notification does not contain any message data or meta-data, it only notifies that the message is available. #### Subscription END notification @@ -566,52 +678,47 @@ ok = %s"OK" Both the recipient and the sender can use TCP or some other, possibly higher level, transport protocol to communicate with the server. The default TCP port for SMP server is 5223. -By default, the client and server should use the protocol presented below, that does not depend on a centralized certificate authority. +For scenarios when meta-data privacy is critical, it is recommended that clients: +- communicating over Tor network, +- establish a separate connection for each SMP queue, +- send noise traffic (using PING command). -Transport is encrypted with [AEAD-GCM][12] protocol with two random symmetric AES 256-bit keys and two random base IVs that will be agreed during the handshake. Both client and the server should maintain two 32-bit word counters, one for the sent and one for the received messages. The IV for each message should be computed by xor-ing the sequential message counter, starting from 0, with the first 32 bits of agreed base IV (the number is encoded in network byte order). +In addition to that, the servers can be deployed as Tor onion services. -To establish the session keys and base IVs, the server should have an asymmetric key pair generated during server deployment and unknown to the clients. The users should know the key hash (256 bits) in advance in order to be able to validate the server public key during transport connection handshake. +The transport protocol should provide the following: +- server authentication (by matching server certificate hash with `serverIdentity`), +- forward secrecy (by encrypting the traffic using ephemeral keys agreed during transport handshake), +- integrity (preventing data modification by the attacker without detection), +- unique channel binding (`sessionIdentifier`) to include in the signed part of SMP transmissions. -The handshake sequence is the following: +By default, the client and server communicate using [TLS 1.3 protocol][13] restricted to: +- TLS_AES_256_GCM_SHA384 cypher suite, +- ed25519 and ed448 EdDSA algorithms for signatures, +- x25519 and x448 ECDHE groups for key exchange. +- servers must send only one self-signed certificate in the handshake, clients must abort the connection in case more than one certificate is sent. +- server and client TLS configuration should not allow resuming the sessions. -1. Once the connection is established, the server sends the `server_header` followed by its public RSA key encoded in X509 binary (not base-64 encoded) format to the client. -2. The client compares the SHA256 hash of the received key with the hash it already has (e.g. received as part of connection invitation or as SMP server configuration). If the hash does not match, the client must terminate the connection. -3. If the hash is the same, the client should generate two random symmetric 256-bit AES keys and two base IVs that will be used as session keys/IVs by the client and the server. -4. The client then should create the `client_handshake` block and send it to the server, encrypted using [RSA-OAEP][2] scheme with the server public key: `rsa-encrypt(client_handshake)`. `snd_aes_key` and `snd_base_iv` will be used by the client to encrypt **sent** messages and by the server to decrypt them, `rcv_aes_key` and `rcv_base_iv` will be used by the client to decrypt **received** messages and by the server to encrypt them. `client_handshake` also contains `block_size` and reserved `protocol` blocks (see syntax). -5. The server should decrypt the received AES keys and base IVs with its private RSA key. -6. In case of successful decryption, the server should send encrypted welcome block (`encrypted_welcome_block`) that contains SMP protocol version supported by the server. +During TLS handshake the client must validate that the hash of the server certificate SPKI block is equal to the `serverIdentity` the client received as part of SMP server address; if the server identity does not match the client must abort the connection. -All the subsequent data, both from the client and from the server, should be sent padded to the fixed agreed block size, encrypted with symmetric AES keys and base IVs (incremented by counters on both sides), that were sent by the client during the handshake. If the application needs to transmit a larger message, it should be broken down into fragments. +Once TLS handshake is complete, client and server will exchange blocks of fixed size (16384 bytes). -Handshake blocks sent by the client and the server have this syntax: +The first block sent by the client should be `clientHello` and the server should respond with `serverHello`: ```abnf -server_header = block_size protocol key_size -block_size = 4*4(OCTET) ; 4-byte block size sent by the server -protocol = 2*2(%x00) ; 0, reserved -key_size = 2*2(OCTET) ; the size of the encoded key in bytes (binary encoded in X509 standard) +clientHello = SP smpVersion SP reserved pad +serverHello = sessionIdentifier SP smpVersion SP reserved pad +sessionIdentifier = ; unique session identifier derived from transport connection handshake + ; it should be included in all SMP transmissions sent in this transport connection. -client_handshake = client_block_size protocol snd_aes_key snd_base_iv rcv_aes_key rcv_base_iv -client_block_size = 4*4(OCTET) ; 4-byte block size sent by the client, -; to confirm or override the block size sent by the server -client_protocol = 2*2(%x00) ; 0, reserved -snd_aes_key = 32*32(OCTET) -snd_base_iv = 16*16(OCTET) -rcv_aes_key = 32*32(OCTET) -rcv_base_iv = 16*16(OCTET) - -transport_block = aes_body_auth_tag aes_encrypted_body -; size is sent by server during handshake, usually 4096 bytes -aes_body_auth_tag = 16*16(OCTET) -aes_encrypted_body = 1*OCTET - -encrypted_welcome_block = transport_block -welcome_block = smp_version SP pad ; decrypt(encrypted_welcome_block) -smp_version = %s"v" 1*DIGIT "." 1*DIGIT "." 1*DIGIT ["-" 1*ALPHA "." 1*DIGIT] ; in semver format - ; for example: v123.456.789-alpha.7 +smpVersion = %s"SMP v" 1*DIGIT "." 1*DIGIT "." 1*DIGIT ; semver format, the version in this document is v0.5.0 +reserved = pad = 1*OCTET ``` +For TLS 1.3 transport client should assert that `sessionIdentifier` is equal to `tls-unique` channel binding defined in [RFC 5929][14] (TLS Finished message struct); we pass it in `serverHello` block to allow communication over some other transport protocol. + +The communication party (client or server) that has the lower protocol version should assume that this version will be supported by another party, the party with the higher protocol version should abort the connection in case they cannot support the lower version. + [1]: https://en.wikipedia.org/wiki/Man-in-the-middle_attack [2]: https://en.wikipedia.org/wiki/End-to-end_encryption [3]: https://en.wikipedia.org/wiki/QR_code @@ -624,3 +731,6 @@ pad = 1*OCTET [10]: https://tools.ietf.org/html/rfc3339 [11]: https://tools.ietf.org/html/rfc5280 [12]: https://tools.ietf.org/html/rfc7714 +[13]: https://datatracker.ietf.org/doc/html/rfc8446 +[14]: https://datatracker.ietf.org/doc/html/rfc5929#section-3 +[15]: https://www.rfc-editor.org/rfc/rfc8709.html