From eb7fcae31bab8323c77abfd7d703f20396845a3a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 22 Nov 2021 08:52:39 +0000 Subject: [PATCH] update transport protocol to use TLS (#204) * update transport protocol to use TLS * typos * s/serverKeyHash/serverIdentity/ Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com> * Update protocol/simplex-messaging.md Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com> * corrections Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com> --- protocol/simplex-messaging.md | 109 +++++++++++++++++----------------- 1 file changed, 56 insertions(+), 53 deletions(-) diff --git a/protocol/simplex-messaging.md b/protocol/simplex-messaging.md index 7818773d6..a1b4ff5e9 100644 --- a/protocol/simplex-messaging.md +++ b/protocol/simplex-messaging.md @@ -72,7 +72,7 @@ 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. @@ -84,12 +84,15 @@ The [ABNF][8] syntax of the message is: ```abnf queueInfo = %s"smp::" smpServer "::" queueId "::" encryptionKey -smpServer = srvHost [":" port] ["#" serverKeyHash] +smpServer = srvHost [":" port] "#" serverIdentity srvHost = ; RFC1123, RFC5891 port = 1*DIGIT -serverKeyHash = encoded +serverIdentity = encoded queueId = encoded -encryptionKey = %s"rsa:" x509encoded ; the recipient's RSA public key for sender to encrypt messages +encryptionKey = encryptionScheme ":" x509encoded ; the recipient's RSA public key for sender to encrypt messages +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. x509encoded = encoded = ``` @@ -98,7 +101,7 @@ encoded = `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)). +`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)). Encryption keys are encoded using [X509][11] specification. @@ -272,7 +275,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. @@ -348,16 +351,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 +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 = pong / queueIds / message / notifierId / messageNotification / +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 = ``` @@ -367,21 +372,20 @@ 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" ``` ### Recipient commands @@ -394,7 +398,9 @@ This command is sent by the recipient to the SMP server to create a new queue. T ```abnf create = %s"NEW" SP recipientKey -recipientKey = %s"rsa:" x509encoded ; the recipient's RSA public key for this queue +recipientKey = signatureScheme ":" x509encoded ; the recipient's public key to verify commands for this queue +signatureScheme = %s"rsa" | %s"ed25519" | %s"ed448" ; "rsa" means deprecated RSA-PSS signature scheme, + ; it must not be used for the new queues. x509encoded = ``` @@ -410,7 +416,7 @@ This response should be sent with empty queue ID (the second part of the transmi 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 `recipientKey` that was passed in the transmission – this verifies that the client has the private key that will be used to sign subsequent commands for this queue. #### Subscribe to queue @@ -430,7 +436,7 @@ This command is sent by the recipient to the server to add sender's key to the q ```abnf secure = %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 public key to verify SEND command for this queue ``` `senderKey` is received from the sender as part of the first message - see [Send Message](#send-message) command. @@ -443,7 +449,7 @@ This command is sent by the recipient to the server to add notifier's key to the ```abnf enableNotifications = %s"NKEY" SP notifierKey -notifierKey = %s"rsa:" x509encoded ; the notifier's RSA public key for this queue +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: @@ -528,7 +534,7 @@ 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 ``` @@ -570,7 +576,7 @@ 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) #### Notifier queue ID response @@ -639,52 +645,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 @@ -697,3 +698,5 @@ 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