From dcd63f3aa27fe9b7a37fcdd92215dcf69ecd7b09 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Tue, 27 Jan 2026 00:31:55 +0000 Subject: [PATCH] docs: update protocol --- protocol/agent-protocol.md | 172 +++++++++++++++-- protocol/simplex-messaging.md | 351 ++++++++++++++++++++++++++++++---- protocol/xftp.md | 54 +++++- 3 files changed, 522 insertions(+), 55 deletions(-) diff --git a/protocol/agent-protocol.md b/protocol/agent-protocol.md index d8744f128..f7da11c00 100644 --- a/protocol/agent-protocol.md +++ b/protocol/agent-protocol.md @@ -1,4 +1,4 @@ -Version 5, 2024-06-22 +Version 7, 2025-01-24 # SMP agent protocol - duplex communication over SMP protocol @@ -9,6 +9,7 @@ Version 5, 2024-06-22 - [SMP servers management](#smp-servers-management) - [SMP agent protocol scope](#smp-agent-protocol-scope) - [Duplex connection procedure](#duplex-connection-procedure) +- [Fast duplex connection procedure](#fast-duplex-connection-procedure) - [Contact addresses](#contact-addresses) - [Communication between SMP agents](#communication-between-smp-agents) - [Message syntax](#messages-between-smp-agents) @@ -20,6 +21,14 @@ Version 5, 2024-06-22 - [Rotating messaging queue](#rotating-messaging-queue) - [End-to-end encryption](#end-to-end-encryption) - [Connection link: 1-time invitation and contact address](#connection-link-1-time-invitation-and-contact-address) + - [Full connection link syntax](#full-connection-link-syntax) + - [Short connection link syntax](#short-connection-link-syntax) +- [Short links](#short-links) + - [Short link structure](#short-link-structure) + - [Link key derivation](#link-key-derivation) + - [Link data encryption](#link-data-encryption) + - [Short link resolution](#short-link-resolution) + - [Link data management](#link-data-management) - [Appendix A: SMP agent API](#smp-agent-api) - [API functions](#api-functions) - [API events](#api-events) @@ -37,6 +46,16 @@ It provides: SMP agent API provides no security between the agent and the client - it is assumed that the agent is executed in the trusted and secure environment, via the 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. +This document describes SMP agent protocol version 7. The version history: + +- v1: initial version +- v2: duplex handshake - allows including reply queue(s) in the initial confirmation +- v3: ratchet sync - supports re-negotiating double ratchet encryption +- v4: delivery receipts - supports acknowledging message delivery to the sender +- v5: post-quantum - supports post-quantum key exchange in double ratchet (PQDR) +- v6: sender auth key - supports sender authentication key in confirmations +- v7: ratchet on confirmation - initializes double ratchet during confirmation + ## SMP agent SMP agents communicate with each other via SMP servers using [simplex messaging protocol (SMP)](./simplex-messaging.md) according to the API calls used by the client applications. 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"). @@ -152,7 +171,7 @@ These messages are encrypted with per-queue shared secret using NaCL crypto_box - `agentConfirmation` - used when confirming SMP queues, contains connection information encrypted with double ratchet. This envelope can only contain `agentConnInfo` or `agentConnInfoReply` encrypted with double ratchet. - `agentMsgEnvelope` - contains different agent messages encrypted with double ratchet, as defined in `agentMessage`. - `agentInvitation` - sent to SMP queue that is used as contact address, does not use double ratchet. -- `agentRatchetKey` - used to re-negotiate double ratchet encryption - can contain additional information in `agentRatchetKey`. +- `agentRatchetKey` - used to re-negotiate double ratchet encryption - can contain additional information in `agentRatchetInfo`. ```abnf decryptedSMPClientMessage = agentConfirmation / agentMsgEnvelope / agentInvitation / agentRatchetKey @@ -182,7 +201,7 @@ Decrypted SMP message client body can be one of 4 types: - `agentMessage` - all other agent messages. `agentMessage` contains these parts: -- `agentMsgHeader` - agent message header that contains sequential agent message ID for a particular SMP queue, agent timestamp (ISO8601) and the hash of the previous message. +- `agentMsgHeader` - agent message header that contains sequential agent message ID for a particular SMP queue and the hash of the previous message. - `aMessage` - a command/message to the other SMP agent: - to confirm the connection (`HELLO`). - to send and to confirm reception of user messages (`A_MSG`, `A_RCVD`). @@ -200,7 +219,9 @@ decryptedAgentMessage = agentConnInfo / agentConnInfoReply / agentRatchetInfo / agentConnInfo = %s"I" connInfo connInfo = *OCTET agentConnInfoReply = %s"D" smpQueues connInfo +smpQueues = length 1*newQueueInfo ; NonEmpty list of reply queues agentRatchetInfo = %s"R" ratchetInfo +ratchetInfo = *OCTET agentMessage = %s"M" agentMsgHeader aMessage msgPadding agentMsgHeader = agentMsgId prevMsgHash @@ -215,8 +236,10 @@ HELLO = %s"H" A_MSG = %s"M" userMsgBody userMsgBody = *OCTET -A_RCVD = %s"V" msgReceipt +A_RCVD = %s"V" msgReceipts +msgReceipts = length 1*msgReceipt ; NonEmpty list msgReceipt = agentMsgId msgHash rcptLength rcptInfo +msgHash = shortString EREADY = %s"E" agentMsgId @@ -224,14 +247,14 @@ A_QCONT = %s"QC" sndQueueAddr QADD = %s"QA" sndQueues sndQueues = length 1*(newQueueUri replacedSndQueue) -newQueueUri = clientVRange smpServer senderId dhPublicKey [sndSecure] +newQueueUri = clientVRange smpServer senderId dhPublicKey [queueMode] dhPublicKey = length x509encoded -sndSecure = "T" +queueMode = %s"M" / %s"C" ; M - messaging (sender can secure), C - contact replacedSndQueue = "0" / "1" sndQueueAddr QKEY = %s"QK" sndQueueKeys sndQueueKeys = length 1*(newQueueInfo senderKey) -newQueueInfo = version smpServer senderId dhPublicKey [sndSecure] +newQueueInfo = version smpServer senderId dhPublicKey [queueMode] senderKey = length x509encoded QUSE = %s"QU" sndQueuesReady @@ -270,7 +293,7 @@ This is the agent envelope used to send client messages once the connection is e #### A_RCVD message -This message is sent to confirm the client message reception. It includes received message number and message hash. +This message is sent to confirm the client message reception. It includes a list of message receipts, each containing the received message number, message hash and receipt info. #### EREADY message @@ -345,24 +368,22 @@ To summarize, the upgrade to DH+KEM secret happens in a sent message that has PQ Connection links are generated by SMP agent in response to `createConnection` api call, used by another party user with `joinConnection` api, and then another connection link is sent by the agent in `agentConnInfoReply` 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 link syntax: +### Full connection link syntax ``` -connectionLink = connectionScheme "/" connLinkType "#/?smp=" smpQueues "&e2e=" e2eEncryption +connectionLink = connectionScheme "/" connLinkType "#/?v=" versionRange "&smp=" smpQueues ["&e2e=" e2eEncryption] ["&data=" clientData] connLinkType = %s"invitation" / %s"contact" connectionScheme = (%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 +versionRange = 1*DIGIT / 1*DIGIT "-" 1*DIGIT ; agent version range +e2eEncryption = +smpQueues = smpQueue *(";" smpQueue) ; SMP queues for the connection (semicolon-separated) smpQueue = +clientData = ``` -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. +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 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 link. 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. @@ -370,6 +391,123 @@ All parameters are passed via URI hash to avoid sending them to the server (in c See SMP protocol [out-of-band messages](./simplex-messaging.md#out-of-band-messages) for syntax of `queueURI`. +### Short connection link syntax + +Short links provide a more compact representation by storing connection data on the server: + +``` +shortLink = shortLinkScheme "/" linkType "#" [linkId "/"] linkKey ["?" shortLinkParams] +shortLinkScheme = %s"simplex:" / (%s"https://" serverHost) +linkType = %s"i" / contactType ; i - invitation, or contact type +contactType = %s"a" / %s"c" / %s"g" / %s"r" ; a - contact, c - channel, g - group, r - relay +linkId = base64url ; only for invitation links +linkKey = base64url ; SHA3-256 hash of fixed data, used to decrypt link data +shortLinkParams = hostParam ["&" portParam] ["&" keyHashParam] +hostParam = %s"h=" hostList +hostList = host *("," host) +portParam = %s"p=" port +keyHashParam = %s"c=" base64url ; server certificate fingerprint +``` + +Contact types: +- `a` (CCTContact) - direct contact connection +- `c` (CCTChannel) - channel connection +- `g` (CCTGroup) - group connection +- `r` (CCTRelay) - relay connection + +Short links can use either the `simplex:` scheme or `https://` with a server hostname. When using the simplex scheme, server information is included in query parameters. + +## Short links + +Short links provide a compact representation of connection links by storing encrypted connection data on the SMP server. The link key in the URI fragment (after `#`) is never sent to the server, ensuring the server cannot decrypt the stored connection data. + +### Link key derivation + +The link key is derived from the fixed link data using SHA3-256 hash function: + +``` +linkKey = SHA3-256(fixedLinkData) +``` + +The fixed link data includes: +- Agent version range +- Root public key (Ed25519) for signing +- SMP queue connection request (server, queue IDs, encryption keys) +- Optional link entity ID + +For contact links, the link ID and encryption key are derived from the link key using HKDF: + +``` +(linkId, encryptionKey) = HKDF(info="SimpleXContactLink", key=linkKey, outputLen=56) +; linkId = first 24 bytes, encryptionKey = remaining 32 bytes +``` + +For invitation links, the link ID is stored separately (usually included in the URI), and only the encryption key is derived: + +``` +encryptionKey = HKDF(info="SimpleXInvLink", key=linkKey, outputLen=32) +``` + +### Link data encryption + +Link data stored on the server consists of two encrypted parts: fixed data and user data. Both are encrypted using NaCl secret_box (XSalsa20-Poly1305) with the derived encryption key: + +```abnf +queueLinkData = encFixedData encUserData +encFixedData = largeString ; encrypted padded(signedFixedData, 2008) +encUserData = largeString ; encrypted padded(signedUserData, 13784) + +signedFixedData = signature fixedData +signedUserData = signature userData +signature = length 64*64 OCTET ; Ed25519 signature + +fixedData = agentVersionRange rootKey linkConnReq [linkEntityId] +agentVersionRange = version version ; min and max agent protocol version +version = 2*2 OCTET +rootKey = length x509encoded ; Ed25519 public key +linkEntityId = shortString +userData = invitationLinkData / contactLinkData +invitationLinkData = %s"I" agentVersionRange connInfo +contactLinkData = %s"C" agentVersionRange userContactData +largeString = 2*2 OCTET *OCTET ; Word16 length prefix +length = 1*1 OCTET +shortString = length *OCTET +``` + +The fixed data is signed with the root key and its hash becomes the link key. The user data is signed either with the root key (for invitations) or with an owner key (for contact addresses). + +### Short link resolution + +When a user receives a short link, the agent resolves it as follows: + +1. Extract the link key from the URI fragment +2. Send `LGET` command to the SMP server with the link ID +3. Receive encrypted link data from the server +4. Decrypt the link data using the link key +5. Extract the full connection information (SMP queue URI, encryption keys, profile) +6. Proceed with the standard connection procedure using `joinConnection` + +For invitation links, the `LKEY` command is used to set the sender key when getting link data. Repeated `LKEY` would require using the same key. + +### Link data management + +The recipient who created the queue can manage the short link data: + +- **LSET** - Set or update the link data associated with a queue. This is used when creating a short link or updating the user data (e.g., profile changes). +- **LDEL** - Delete the link data from the server. This effectively invalidates the short link. + +Short links support different connection modes: +- **invitation** - One-time invitation links that can only be used once +- **contact** - Reusable contact address links that can be used multiple times + +For contact addresses, the link data includes additional information about the contact type: +- **contact** - Direct contact connection +- **channel** - Channel connection +- **group** - Group connection +- **relay** - Relay connection + +The agent maintains the link data and updates it when connection parameters change, ensuring short links remain valid and reflect current connection information. + ## Appendix A: SMP agent API The exact specification of agent library API and of the events that the agent sends to the client application is out of scope of the protocol specification. diff --git a/protocol/simplex-messaging.md b/protocol/simplex-messaging.md index 16e4e6606..5a077eff2 100644 --- a/protocol/simplex-messaging.md +++ b/protocol/simplex-messaging.md @@ -1,4 +1,4 @@ -Version 9, 2024-06-22 +Version 19, 2025-01-24 # Simplex Messaging Protocol (SMP) @@ -18,6 +18,10 @@ Version 9, 2024-06-22 - [Simplex queue IDs](#simplex-queue-ids) - [Server security requirements](#server-security-requirements) - [Message delivery notifications](#message-delivery-notifications) +- [Client services](#client-services) + - [Service roles](#service-roles) + - [Service certificates](#service-certificates) + - [Service subscriptions](#service-subscriptions) - [SMP Transmission and transport block structure](#smp-transmission-and-transport-block-structure) - [SMP commands](#smp-commands) - [Correlating responses with commands](#correlating-responses-with-commands) @@ -26,7 +30,11 @@ Version 9, 2024-06-22 - [Recipient commands](#recipient-commands) - [Create queue command](#create-queue-command) - [Subscribe to queue](#subscribe-to-queue) + - [Subscribe to multiple queues](#subscribe-to-multiple-queues) - [Secure queue by recipient](#secure-queue-by-recipient) + - [Set queue recipient keys](#set-queue-recipient-keys) + - [Set short link](#set-short-link) + - [Delete short link](#delete-short-link) - [Enable notifications command](#enable-notifications-command) - [Disable notifications command](#disable-notifications-command) - [Get message command](#get-message-command) @@ -41,12 +49,22 @@ Version 9, 2024-06-22 - [Request proxied session](#request-proxied-session) - [Send command via proxy](#send-command-via-proxy) - [Forward command to destination server](#forward-command-to-destination-server) + - [Short link commands](#short-link-commands) + - [Set link key](#set-link-key) + - [Get link data](#get-link-data) - [Notifier commands](#notifier-commands) - [Subscribe to queue notifications](#subscribe-to-queue-notifications) + - [Subscribe to multiple queue notifications](#subscribe-to-multiple-queue-notifications) - [Server messages](#server-messages) + - [Link response](#link-response) + - [Queue subscription response](#queue-subscription-response) + - [Service subscription response](#service-subscription-response) + - [All service messages received](#all-service-messages-received) - [Deliver queue message](#deliver-queue-message) - [Deliver message notification](#deliver-message-notification) - [Subscription END notification](#subscription-end-notification) + - [Service subscription END notification](#service-subscription-end-notification) + - [Queue deleted notification](#queue-deleted-notification) - [Error responses](#error-responses) - [OK response](#ok-response) - [Transport connection with the SMP server](#transport-connection-with-the-SMP-server) @@ -65,7 +83,26 @@ It's designed with the focus on communication security and integrity, under the It is designed as a low level protocol for other application protocols to solve the problem of secure and private message transmission, making [MITM attack][1] very difficult at any part of the message transmission system. -This document describes SMP protocol versions 6 and 7, the previous versions are discontinued. +This document describes SMP protocol version 19. Versions 1-5 are discontinued. The version history: + +- v1: binary protocol encoding +- v2: message flags (used to control notifications) +- v3: encrypt message timestamp and flags together with the body when delivered to recipient +- v4: support command batching +- v5: basic auth for SMP servers +- v6: allow creating queues without subscribing (current minimum version) +- v7: support authenticated encryption to verify senders' commands +- v8: SMP proxy for sender commands (PRXY, PFWD, RFWD, PKEY, PRES, RRES) +- v9: faster handshake with SKEY command for sender to secure queue +- v10: DELD event to subscriber when queue is deleted via another connection +- v11: additional encryption of transport blocks with forward secrecy +- v12: BLOCKED error for blocked queues +- v14: proxyServer handshake property to disable transport encryption between server and proxy +- v15: short links with associated data passed in NEW or LSET command +- v16: service certificates +- v17: create notification credentials with NEW command +- v18: support client notices in BLOCKED error +- v19: service subscriptions to messages (SUBS, SOKS, ENDS commands) ## Introduction @@ -395,13 +432,52 @@ To protect the privacy of the recipients, there are several commands in SMP prot 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). +- `enableNotifications` (`"NKEY"`) with `notifierIdResp` (`"NID"`) response - see [Enable notifications command](#enable-notifications-command). - `disableNotifications` (`"NDEL"`) - see [Disable notifications command](#disable-notifications-command). - `subscribeNotifications` (`"NSUB"`) - see [Subscribe to queue notifications](#subscribe-to-queue-notifications). - `messageNotification` (`"NMSG"`) - see [Deliver message notification](#deliver-message-notification). [`SEND` command](#send-message) includes the notification flag to instruct SMP server whether to send the notification - this flag is forwarded to the recipient inside encrypted envelope, together with the timestamp and the message body, so even if TLS is compromised this flag cannot be used for traffic correlation. +## Client services + +SMP protocol supports client services - high capacity clients that act as services. Client services allow scalable message and notification delivery services. + +### Service roles + +A client service can have one of two roles: + +- **Messaging** - Message receiver service that subscribes to and receives messages from multiple SMP queues with a single command. + +- **Notifications** - Notification service that subscribes to queue notifications and delivers push notifications to user devices. + +Service role is identified in the transport handshake and determines what commands the service is authorized to send. + +### Service certificates + +To send service commands, services should authenticate themselves to SMP servers using service certificates. This provides: + +- **Service identity** - The server assigns a unique service ID based on the service certificate, allowing associating multiple SMP queues with a service. +- **Subscription management** - Services can efficiently manage subscriptions across reconnections without re-subscribing to individual queues. +- **Rate limiting** - Servers can apply rate limits per service identity rather than per connection. + +Service certificates are included in the client handshake and verified by the server. The service receives a service ID in the handshake response, which is then used as entity ID in service transmissions. + +```abnf +clientHandshakeService = serviceRole serviceCertKey +serviceRole = %s"M" / %s"N" ; Messaging / Notifier +serviceCertKey = certChainPubKey +``` + +### Service subscriptions + +Services use batch subscription commands to subscribe to multiple queues: + +- **SUBS** - Subscribe to messages from all associated SMP queues at once. The service provides a count and hash of queue IDs, and receives `SOKS` response with the service ID. +- **NSUBS** - Subscribe to notifications from all associated SMP queues. Similar to SUBS. +- **SOKS** - Server response confirming batch subscription success. +- **ENDS** - Server notification when batch subscriptions are terminated (e.g., when another instance of service connects). + ## SMP Transmission and transport block structure Each transport block has a fixed size of 16384 bytes for traffic uniformity. @@ -455,15 +531,19 @@ Commands syntax below is provided using [ABNF][8] with [case-sensitive strings e ```abnf smpCommand = ping / recipientCmd / senderCommand / - proxyCommand / subscribeNotifications / serverMsg -recipientCmd = create / subscribe / rcvSecure / + proxyCommand / notifierCommand / linkCommand / serverMsg +recipientCmd = create / subscribe / subscribeMultiple / rcvSecure / recipientKeys / enableNotifications / disableNotifications / getMessage - acknowledge / suspend / delete / getQueueInfo + acknowledge / suspend / delete / getQueueInfo / setShortLink / deleteShortLink senderCommand = send / sndSecure -proxyCommand = proxySession / proxyCommand / relayCommand -serverMsg = queueIds / message / notifierId / messageNotification / - proxySessionKey / proxyResponse / relayResponse - unsubscribed / queueInfo/ ok / error +linkCommand = setLinkKey / getLinkData +proxyCommand = proxySession / proxyForward / relayForward +notifierCommand = subscribeNotifications / subscribeNotificationsMultiple +serverMsg = queueIds / linkResponse / serviceOk / serviceOkMultiple / + message / allReceived / notifierIdResp / messageNotification / + proxySessionKey / proxyResponse / relayResponse / + unsubscribed / serviceUnsubscribed / deleted / + queueInfo / ok / error / pong ``` The syntax of specific commands and responses is defined below. @@ -480,13 +560,14 @@ SMP servers must verify all transmissions (excluding `ping` and initial `send` c ### 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 `ok` 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 `pong` response. This command should be sent unsigned and without queue ID. ```abnf ping = %s"PING" +pong = %s"PONG" ``` -This command is always send unsigned. +This command is always sent unsigned. ### Recipient commands @@ -501,30 +582,54 @@ Servers SHOULD support basic auth with this command, to allow only server owners The syntax is: ```abnf -create = %s"NEW " recipientAuthPublicKey recipientDhPublicKey basicAuth subscribe sndSecure +create = %s"NEW " recipientAuthPublicKey recipientDhPublicKey optBasicAuth subscribeMode optQueueReqData optNtfCreds recipientAuthPublicKey = length x509encoded ; the recipient's Ed25519 or X25519 public key to verify commands for this queue recipientDhPublicKey = length x509encoded ; the recipient's Curve25519 key for DH exchange to derive the secret ; that the server will use to encrypt delivered message bodies ; using [NaCl crypto_box][16] encryption scheme (curve25519xsalsa20poly1305). -basicAuth = "0" / "1" shortString ; server password +optBasicAuth = %s"0" / (%s"1" shortString) ; optional server password subscribeMode = %s"S" / %s"C" ; S - create and subscribe, C - only create -sndSecure = %s"T" / %s"F" ; T - sender can secure the queue, from v9 +optQueueReqData = %s"0" / (%s"1" queueReqData) ; optional queue request data +queueReqData = queueReqMessaging / queueReqContact +queueReqMessaging = %s"M" optMessagingLinkData +queueReqContact = %s"C" optContactLinkData +optMessagingLinkData = %s"0" / (%s"1" senderId encFixedData encUserData) +optContactLinkData = %s"0" / (%s"1" linkId senderId encFixedData encUserData) +senderId = shortString ; first 24 bytes of SHA3-384(corrId) +linkId = shortString +encFixedData = largeString ; encrypted fixed link data +encUserData = largeString ; encrypted user data +optNtfCreds = %s"0" / (%s"1" ntfKey ntfDhKey) ; optional notification credentials +ntfKey = length x509encoded +ntfDhKey = length x509encoded x509encoded = +shortString = length *OCTET +largeString = length2 *OCTET length = 1*1 OCTET +length2 = 2*2 OCTET ; Word16, network byte order ``` If the queue is created successfully, the server must send `queueIds` response with the recipient's and sender's queue IDs and public key to encrypt delivered message bodies: ```abnf -queueIds = %s"IDS " recipientId senderId srvDhPublicKey sndSecure -serverDhPublicKey = length x509encoded +queueIds = %s"IDS " recipientId senderId srvDhPublicKey optQueueMode optLinkId optServiceId optServerNtfCreds +srvDhPublicKey = length x509encoded ; the server's Curve25519 key for DH exchange to derive the secret ; that the server will use to encrypt delivered message bodies to the recipient recipientId = shortString ; 16-24 bytes senderId = shortString ; 16-24 bytes +optQueueMode = %s"0" / (%s"1" queueMode) +queueMode = %s"M" / %s"C" ; M - messaging (sender can secure), C - contact +optLinkId = %s"0" / (%s"1" linkId) +linkId = shortString +optServiceId = %s"0" / (%s"1" serviceId) +serviceId = shortString +optServerNtfCreds = %s"0" / (%s"1" srvNtfId srvNtfDhKey) +srvNtfId = shortString +srvNtfDhKey = length x509encoded ``` Once the queue is created, depending on `subscribeMode` parameter of `NEW` command the recipient gets automatically subscribed to receive the messages from that queue, until the transport connection is closed. To start receiving the messages from the existing queue when the new transport connection is opened the client must use `subscribe` command. @@ -541,12 +646,24 @@ When the simplex queue was not created in the current transport connection, the subscribe = %s"SUB" ``` -If subscription is successful the server must respond with the first available message or with `ok` response if no messages are available. The recipient will continue receiving the messages from this queue until the transport connection is closed or until another transport connection subscribes to the same simplex queue - in this case the first subscription should be cancelled and [subscription END notification](#subscription-end-notification) delivered. +If subscription is successful the server must respond with the first available message or with [queue subscription response](#queue-subscription-response) (`SOK`) if no messages are available. The recipient will continue receiving the messages from this queue until the transport connection is closed or until another transport connection subscribes to the same simplex queue - in this case the first subscription should be cancelled and [subscription END notification](#subscription-end-notification) delivered. 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. +#### Subscribe to multiple queues + +This command is used by recipient services to subscribe to multiple queues at once: + +```abnf +subscribeMultiple = %s"SUBS " count idsHash +count = 8*8 OCTET ; Int64, network byte order (big-endian) +idsHash = 16*16 OCTET ; XOR of MD5 hashes of all queue IDs +``` + +The count and idsHash allow the server to detect subscription drift. The server responds with `serviceOkMultiple` (`SOKS`) response. + #### Secure queue by recipient This command is only used until v8 of SMP protocol. V9 uses [SKEY](#secure-queue-by-sender). @@ -565,6 +682,44 @@ Once the queue is secured only authorized messages can be sent to it. This command MUST be used in transmission with recipient queue ID. +#### Set queue recipient keys + +This command is used to set additional recipient keys to support shared management of the queue: + +```abnf +recipientKeys = %s"RKEY " recipientKeysList +recipientKeysList = count 1*recipientKey ; non-empty list +count = 1*1 OCTET ; number of keys (1-255) +recipientKey = length x509encoded +``` + +This command added to allow multiple group owners manage data of the same queue link. + +#### Set short link + +This command is used to associate a short link with the queue: + +```abnf +setShortLink = %s"LSET " linkId encFixedData encUserData +linkId = shortString +encFixedData = largeString ; encrypted fixed link data +encUserData = largeString ; encrypted user data (e.g., profile) +largeString = length2 *OCTET +length2 = 2*2 OCTET ; Word16, network byte order (big-endian) +``` + +The server responds with `OK` response if successful. + +#### Delete short link + +This command is used to remove a short link association from the queue: + +```abnf +deleteShortLink = %s"LDEL" +``` + +The server responds with `OK` or `ERR` + #### 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. @@ -580,10 +735,10 @@ recipientNotificationDhPublicKey = length x509encoded ; using [NaCl crypto_box][16] encryption scheme (curve25519xsalsa20poly1305). ``` -The server will respond with `notifierId` response if notifications were enabled and the notifier's key was successfully added to the queue: +The server will respond with `NID` response if notifications were enabled and the notifier's key was successfully added to the queue: ```abnf -notifierId = %s"NID " notifierId srvNotificationDhPublicKey +notifierIdResponse = %s"NID " notifierId srvNotificationDhPublicKey notifierId = shortString ; 16-24 bytes srvNotificationDhPublicKey = length x509encoded ; the server's Curve25519 key for DH exchange to derive the secret @@ -1001,6 +1156,35 @@ The shared secret for encrypting transmission bodies between proxy server and de relayResponse = %s"RRES" SP ``` +### Short link commands + +These commands are used by senders to access queues via short links (added in v8). + +#### Set link key + +This command is used to set the sender key and to get link data associated with a "messaging" queue: + +```abnf +setLinkKey = %s"LKEY " senderAuthPublicKey +senderAuthPublicKey = length x509encoded +``` + +The server secures the queue with the provided key and responds with `LNK` response containing the sender ID and encrypted link data. + +Once this command is used, the queue is secured, and the command can only be repeated with the same key. + +#### Get link data + +This command is used to retrieve the link data associated with a "contact" queue: + +```abnf +getLinkData = %s"LGET" +``` + +The server responds with `LNK` response containing the sender ID and encrypted link data. + +This command may be repeated multiple times. + ### Notifier commands #### Subscribe to queue notifications @@ -1011,16 +1195,69 @@ The push notifications server (notifier) must use this command to start receivin 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. +If subscription is successful the server must respond with [queue subscription response](#queue-subscription-response) (`SOK`). 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. +#### Subscribe to multiple queue notifications + +This command is used by notifier services to subscribe to multiple queues at once: + +```abnf +subscribeNotificationsMultiple = %s"NSUBS " count idsHash +count = 8*8 OCTET ; Int64, network byte order (big-endian) +idsHash = 16*16 OCTET ; XOR of MD5 hashes of all queue IDs +``` + +The server responds with `serviceOkMultiple` (`SOKS`) response. + ### Server messages This section includes server events and generic command responses used for several commands. The syntax for command-specific responses is shown together with the commands. +#### Link response + +Sent in response to `LKEY` and `LGET` commands: + +```abnf +linkResponse = %s"LNK " senderId encFixedData encUserData +senderId = shortString ; the sender ID for the queue +encFixedData = largeString ; encrypted fixed link data +encUserData = largeString ; encrypted user data +``` + +#### Queue subscription response + +Sent in response to `SUB` and `NSUB` commands: + +```abnf +serviceOk = %s"SOK " optServiceId +optServiceId = %s"0" / (%s"1" serviceId) +serviceId = shortString +``` + +If response contains `serviceId`, it means that queue is associated with the service. + +#### Service subscription response + +Sent in response to `SUBS` or `NSUBS` commands: + +```abnf +serviceOkMultiple = %s"SOKS " count idsHash +count = 8*8 OCTET ; Int64, network byte order (big-endian) +idsHash = 16*16 OCTET ; XOR of MD5 hashes of all subscribed queue IDs +``` + +#### All service messages received + +Sent to indicate all messages have been delivered from all queues associated with the service: + +```abnf +allReceived = %s"ALLS" +``` + #### Deliver queue 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. @@ -1077,6 +1314,24 @@ unsubscribed = %s"END" No further messages should be delivered to unsubscribed transport connection. +#### Service subscription END notification + +Sent when service subscription is terminated (can be sent when service re-connects): + +```abnf +serviceUnsubscribed = %s"ENDS " count idsHash +count = 8*8 OCTET ; Int64, network byte order (big-endian) +idsHash = 16*16 OCTET ; XOR of MD5 hashes of terminated queue IDs +``` + +#### Queue deleted notification + +Sent when a queue has been deleted via another connection: + +```abnf +deleted = %s"DELD" +``` + #### Error responses - incorrect block format, encoding or authorization size (`BLOCK`). @@ -1100,6 +1355,7 @@ No further messages should be delivered to unsubscribed transport connection. - `NETWORK` - network error. - `TIMEOUT` - command response timeout. - `HOST` - no compatible server host (e.g. onion when public is required, or vice versa) + - `NO_SERVICE` - service unavailable client-side. - `TRANSPORT` - handshake or other transport error: - `BLOCK` - error parsing transport block. - `VERSION` - incompatible client or server version. @@ -1111,25 +1367,42 @@ No further messages should be delivered to unsubscribed transport connection. - `IDENTITY` - incorrect server identity (certificate fingerprint does not match server address). - `BAD_AUTH` - incorrect or missing server credentials in handshake. - authentication error (`AUTH`) - incorrect authorization, unknown (or suspended) queue, sender's ID is used in place of recipient's and vice versa, and some other cases (see [Send message](#send-message) command). +- blocked entity error (`BLOCKED`) - the entity (queue or message) was blocked due to policy violation (added in v17). Contains blocking information: + - `reason` - blocking reason (`spam` or `content`). + - `notice` - optional client notice with additional information. +- service error (`SERVICE`) - service-related error. +- crypto error (`CRYPTO`) - cryptographic operation failed. - message queue quota exceeded error (`QUOTA`) - too many messages were sent to the message queue. Further messages can only be sent after the recipient retrieves the messages. +- store error (`STORE`) - server storage error with error message. +- message expired (`EXPIRED`) - message has expired. +- no message (`NO_MSG`) - no message available or message ID mismatch. - sent message is too large (> 16064) to be delivered (`LARGE_MSG`). - internal server error (`INTERNAL`). +- duplicate error (`DUPLICATE_`) - internal duplicate detection error (not returned by server). The syntax for error responses: ```abnf error = %s"ERR " errorType -errorType = %s"BLOCK" / %s"SESSION" / %s"CMD" SP cmdError / %s"PROXY" proxyError / - %s"AUTH" / %s"QUOTA" / %s"LARGE_MSG" / %s"INTERNAL" -cmdError = %s"SYNTAX" / %s"PROHIBITED" / %s"NO_AUTH" / %s"HAS_AUTH" / %s"NO_ENTITY" +errorType = %s"BLOCK" / %s"SESSION" / %s"CMD" SP cmdError / %s"PROXY" SP proxyError / + %s"AUTH" / %s"BLOCKED" SP blockingInfo / %s"SERVICE" / %s"CRYPTO" / + %s"QUOTA" / %s"STORE" SP storeError / %s"EXPIRED" / %s"NO_MSG" / + %s"LARGE_MSG" / %s"INTERNAL" / %s"DUPLICATE_" +cmdError = %s"UNKNOWN" / %s"SYNTAX" / %s"PROHIBITED" / %s"NO_AUTH" / %s"HAS_AUTH" / %s"NO_ENTITY" proxyError = %s"PROTOCOL" SP errorType / %s"BROKER" SP brokerError / %s"BASIC_AUTH" / %s"NO_SESSION" brokerError = %s"RESPONSE" SP shortString / %s"UNEXPECTED" SP shortString / - %s"NETWORK" / %s"TIMEOUT" / %s"HOST" / + %s"NETWORK" [SP networkError] / %s"TIMEOUT" / %s"HOST" / %s"NO_SERVICE" / %s"TRANSPORT" SP transportError +networkError = %s"CONNECT" SP shortString / %s"TLS" SP shortString / + %s"UNKNOWNCA" / %s"FAILED" / %s"TIMEOUT" / %s"SUBSCRIBE" SP shortString transportError = %s"BLOCK" / %s"VERSION" / %s"LARGE_MSG" / %s"SESSION" / %s"NO_AUTH" / %s"HANDSHAKE" SP handshakeError -handshakeError = %s"PARSE" / %s"IDENTITY" / %s"BAD_AUTH" +handshakeError = %s"PARSE" / %s"IDENTITY" / %s"BAD_AUTH" / %s"BAD_SERVICE" +blockingInfo = %s"reason=" blockingReason ["," %s"notice=" jsonNotice] +blockingReason = %s"spam" / %s"content" +jsonNotice = +storeError = *OCTET ``` Server implementations must aim to respond within the same time for each command in all cases when `"ERR AUTH"` response is required to prevent timing attacks (e.g., the server should verify authorization even when the queue does not exist on the server or the authorization of different type is sent, using any dummy key compatible with the used authorization). @@ -1218,7 +1491,7 @@ The first block sent by the server should be `paddedServerHello` and the client ```abnf paddedServerHello = -serverHello = smpVersionRange sessionIdentifier [serverCert signedServerKey] ignoredPart +serverHello = smpVersionRange sessionIdentifier [serverCertKey] ignoredPart smpVersionRange = minSmpVersion maxSmpVersion minSmpVersion = smpVersion maxSmpVersion = smpVersion @@ -1226,25 +1499,39 @@ sessionIdentifier = shortString ; unique session identifier derived from transport connection handshake ; it should be included in authorized part of all SMP transmissions sent in this transport connection, ; but it must not be sent as part of the transmission in the current protocol version. -serverCert = originalLength x509encoded -signedServerKey = originalLength x509encoded ; signed by server certificate +serverCertKey = certChain signedServerKey +certChain = count 1*cert ; 2-4 certificates +cert = originalLength x509encoded +signedServerKey = originalLength x509encoded ; X25519 key signed by server certificate paddedClientHello = -clientHello = smpVersion [clientKey] ignoredPart +clientHello = smpVersion keyHash [clientKey] proxyServer optClientService ignoredPart ; chosen SMP protocol version - it must be the maximum supported version ; within the range offered by the server -clientKey = length x509encoded +keyHash = shortString ; server identity - CA certificate fingerprint +clientKey = length x509encoded ; X25519 public key for session encryption - only present if needed +proxyServer = %s"T" / %s"F" ; true if connecting client is a proxy server +optClientService = %s"0" / (%s"1" clientService) ; optional service client credentials +clientService = serviceRole serviceCertKey +serviceRole = %s"M" / %s"N" ; Messaging / Notifier +serviceCertKey = certChain signedServiceKey +signedServiceKey = originalLength x509encoded ; Ed25519 key signed by service certificate smpVersion = 2*2OCTET ; Word16 version number originalLength = 2*2OCTET +count = 1*1OCTET ignoredPart = *OCTET pad = *OCTET ``` -`signedServerKey` is used to compute a shared secret to authorize client transmission - it is combined with the per-queue key that was used when the queue was created. +`signedServerKey` is used to compute a shared secret to authorize client transmissions - it is combined with the per-queue key that was used when the queue was created. `clientKey` is used only by SMP proxy server when it connects to the destination server to agree shared secret for the additional encryption layer, end user clients do not use this key. +`proxyServer` flag (v14+) disables additional transport encryption inside TLS for proxy connections, since proxy server connection already has additional encryption. + +`clientService` (v16+) provides long-term service client certificate for high-volume services using SMP server (chat relays, notification servers, high traffic bots). The server responds with a third handshake message containing the assigned service ID. + `ignoredPart` in handshake allows to add additional parameters in handshake without changing protocol version - the client and servers must ignore any extra bytes within the original block length. For TLS 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 (possibly, with another channel binding). diff --git a/protocol/xftp.md b/protocol/xftp.md index bd1d9e664..1a17524d7 100644 --- a/protocol/xftp.md +++ b/protocol/xftp.md @@ -1,4 +1,4 @@ -Version 2, 2024-06-22 +Version 3, 2025-01-24 # SimpleX File Transfer Protocol @@ -33,6 +33,7 @@ Version 2, 2024-06-22 - [File recipient commands](#file-recipient-commands) - [Download file chunk](#download-file-chunk) - [Acknowledge file chunk download](#acknowledge-file-chunk-download) + - [Error responses](#error-responses) - [Threat model](#threat-model) ## Abstract @@ -49,6 +50,12 @@ The objective of SimpleX File Transfer Protocol (XFTP) is to facilitate the secu XFTP is implemented as an application level protocol on top of HTTP2 and TLS. +This document describes XFTP protocol version 3. The version history: + +- v1: initial version +- v2: authenticated commands - added basic auth support for commands +- v3: blocked files - added BLOCKED error type for policy violations + The protocol describes the set of commands that senders and recipients can send to XFTP servers to create, upload, download and delete file chunks of several pre-defined sizes. XFTP servers SHOULD support chunks of 4 sizes: 64KB, 256KB, 1MB and 4MB (1KB = 1024 bytes, 1MB = 1024KB). The protocol is designed with the focus on meta-data privacy and security. While using TLS, the protocol does not rely on TLS security by using additional encryption to achieve that there are no identifiers or ciphertext in common in received and sent server traffic, frustrating traffic correlation even if TLS is compromised. @@ -283,7 +290,7 @@ XFTP server implementations MUST NOT create, store or send to any other servers: - binary-encoded commands sent as fixed-size padded block in the body of HTTP2 POST request, similar to SMP and notifications server protocol transmission encodings. - HTTP2 POST with a fixed size padded block body for file upload and download. -Block size - 4096 bytes (it would fit ~120 Ed25519 recipient keys). +Block size - 16384 bytes (it would fit ~350 Ed25519 recipient keys). The reasons to use HTTP2: @@ -320,12 +327,13 @@ Once TLS handshake is complete, client and server will exchange blocks of fixed ```abnf paddedServerHello = -serverHello = xftpVersionRange sessionIdentifier serverCert signedServerKey ignoredPart +serverHello = xftpVersionRange sessionIdentifier serverCerts signedServerKey ignoredPart xftpVersionRange = minXftpVersion maxXftpVersion minXftpVersion = xftpVersion maxXftpVersion = xftpVersion sessionIdentifier = shortString ; unique session identifier derived from transport connection handshake +serverCerts = length 1*serverCert ; NonEmpty list of certificates in chain serverCert = originalLength signedServerKey = originalLength ; signed by server certificate @@ -382,7 +390,7 @@ Commands syntax below is provided using ABNF with case-sensitive strings extensi xftpCommand = ping / senderCommand / recipientCmd / serverMsg senderCommand = register / add / put / delete recipientCmd = get / ack -serverMsg = pong / sndIds / rcvIds / ok / file +serverMsg = pong / sndIds / rcvIds / ok / file / error ``` The syntax of specific commands and responses is defined below. @@ -427,7 +435,7 @@ The syntax is: register = %s"FNEW " fileInfo rcvPublicAuthKeys basicAuth fileInfo = sndKey size digest sndKey = length x509encoded -size = 1*DIGIT +size = 4*4 OCTET ; Word32 big-endian digest = length *OCTET rcvPublicAuthKeys = length 1*rcvPublicAuthKey rcvPublicAuthKey = length x509encoded @@ -509,7 +517,7 @@ If requested file is successfully located, the server must send `file` response. ```abnf file = %s"FILE " sDhKey cbNonce sDhKey = length x509encoded -cbNonce = +cbNonce = 24*24 OCTET ; NaCl crypto_box nonce ``` Chunk is additionally encrypted on the way from the server to the recipient using a key agreed via ephemeral DH keys `rDhKey` and `sDhKey`, so there is no ciphertext in common between sent and received traffic inside TLS connection, in order to complicate traffic correlation attacks, if TLS is compromised. @@ -526,6 +534,40 @@ If file recipient ID is successfully deleted, the server must send `ok` response In current implementation of XFTP protocol in SimpleX Chat clients don't use FACK command. Files are automatically expired on servers after configured time interval. +### Error responses + +The server responds with `ERR` followed by the error type: + +```abnf +error = %s"ERR " errorType +errorType = %s"BLOCK" / %s"SESSION" / %s"HANDSHAKE" / + %s"CMD" SP cmdError / %s"AUTH" / %s"BLOCKED" SP blockingInfo / + %s"SIZE" / %s"QUOTA" / %s"DIGEST" / %s"CRYPTO" / + %s"NO_FILE" / %s"HAS_FILE" / %s"FILE_IO" / + %s"TIMEOUT" / %s"INTERNAL" +cmdError = %s"UNKNOWN" / %s"SYNTAX" / %s"PROHIBITED" / %s"NO_AUTH" / %s"HAS_AUTH" / %s"NO_ENTITY" +blockingInfo = %s"reason=" blockingReason ["," %s"notice=" jsonNotice] +blockingReason = %s"spam" / %s"content" +jsonNotice = *OCTET ; JSON-encoded notice object +``` + +Error types: +- `BLOCK` - incorrect block format, encoding or signature size. +- `SESSION` - incorrect session ID (TLS Finished message / tls-unique binding). +- `HANDSHAKE` - incorrect handshake command. +- `CMD` - command syntax errors (UNKNOWN, SYNTAX, PROHIBITED, NO_AUTH, HAS_AUTH, NO_ENTITY). +- `AUTH` - command authorization error - bad signature or non-existing file chunk. +- `BLOCKED` - file chunk was blocked due to policy violation (added in v3). Contains blocking reason and optional notice. +- `SIZE` - incorrect file size. +- `QUOTA` - storage quota exceeded. +- `DIGEST` - incorrect file digest. +- `CRYPTO` - file encryption/decryption failed. +- `NO_FILE` - no expected file body in request/response or no file on the server. +- `HAS_FILE` - unexpected file body. +- `FILE_IO` - file IO error. +- `TIMEOUT` - file sending or receiving timeout. +- `INTERNAL` - internal server error. + ## Threat model #### Global Assumptions