diff --git a/protocol/simplex-messaging.md b/protocol/simplex-messaging.md index 872c23faa..4930291ab 100644 --- a/protocol/simplex-messaging.md +++ b/protocol/simplex-messaging.md @@ -566,15 +566,15 @@ This command is sent to the server by the sender both to confirm the queue after ```abnf send = %s"SEND " smpEncMessage -smpEncMessage = smpPubHeader sentMsgBody ; message up to 15968 bytes +smpEncMessage = smpPubHeader sentMsgBody ; message up to 16078 bytes smpPubHeader = smpClientVersion ("1" senderPublicDhKey / "0") smpClientVersion = word16 senderPublicDhKey = length x509encoded ; sender's Curve25519 public key to agree DH secret for E2E encryption in this queue ; it is only sent in confirmation message x509encoded = -sentMsgBody = 15842*15842 OCTET -; E2E-encrypted smpClientMessage padded to 15842 bytes before encryption +sentMsgBody = 16030*16030 OCTET +; E2E-encrypted smpClientMessage padded to 16030 bytes before encryption word16 = 2*2 OCTET ``` @@ -593,14 +593,14 @@ Until the queue is secured, the server should accept any number of unsigned mess The body should be encrypted with the recipient's "public" key (`EK`); once decrypted it must have this format: ```abnf -sentMsgBody = +sentMsgBody = smpClientMessage = smpPrivHeader clientMsgBody smpPrivHeader = emptyHeader / smpConfirmationHeader emptyHeader = " " smpConfirmationHeader = %s"K" senderKey senderKey = length x509encoded ; the sender's Ed25519 or Ed448 public key to sign SEND commands for this queue -clientMsgBody = *OCTET ; up to 15784 in case of emptyHeader +clientMsgBody = *OCTET ; up to 16012 in case of emptyHeader ``` `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. @@ -610,19 +610,20 @@ SMP transmission structure for sent messages: ``` ------- transmission (= 16384 bytes) 2 | originalLength - 396- | signature sessionId corrId queueId %s"SEND" SP (1+114 + 1+32? + 1+32 + 1+24 + 4+1 = 210) - ....... smpEncMessage (= 15968 bytes = 16384 - 416 bytes) - 126- | smpPubHeader + 286- | signature sessionId corrId queueId %s"SEND" SP (1+114 + 1+32? + 1+32 + 1+24 + 4+1 = 210) + ....... smpEncMessage (= 16078 bytes = 16384 - 306 bytes) + 8- | smpPubHeader (for messages it is only version and '0' to mean "no DH key" = 3 bytes) 24 | nonce for smpClientMessage - ------- smpClientMessage (E2E encrypted, = 15802 bytes = 15968 - 166) + 16 | auth tag for smpClientMessage + ------- smpClientMessage (E2E encrypted, = 16030 bytes = 16078 - 48) 2 | originalLength - 16- | smpPrivHeader + 8- | smpPrivHeader ....... - | clientMsgBody (<= 15784 bytes = 15802 - 18) + | clientMsgBody (<= 16020 bytes = 16030 - 10) ....... 0+ | smpClientMessage pad ------- smpClientMessage end - 16 | auth tag for smpClientMessage + | ....... smpEncMessage end 18+ | transmission pad ------- transmission end @@ -633,42 +634,44 @@ SMP transmission structure for received messages: ``` ------- transmission (= 16384 bytes) 2 | originalLength - 396- | signature sessionId corrId queueId %s"MSG" SP msgId timestamp (1+114 + 1+32? + 1+32 + 1+24 + 3+1 + 24+1 + 8 = 243) - ------- serverEncryptedMsg (= 15970 bytes = 16384 - 414 bytes) + 286- | signature sessionId corrId queueId %s"MSG" SP msgId timestamp (1+114 + 1+32? + 1+32 + 1+24 + 3+1 + 24+1 + 8 = 243) + 16 | auth tag (msgId is used as nonce) + ------- serverEncryptedMsg (= 16080 bytes = 16384 - 304 bytes) 2 | originalLength - ....... smpEncMessage (= 15968 bytes = 15970 - 2 bytes) - 126- | smpPubHeader + ....... smpEncMessage (= 16078 bytes = 16080 - 2 bytes) + 8- | smpPubHeader (empty header for the message) 24 | nonce for smpClientMessage - ------- smpClientMessage (E2E encrypted, = 15802 bytes = 15968 - 166 bytes) + 16 | auth tag for smpClientMessage + ------- smpClientMessage (E2E encrypted, = 16030 bytes = 16078 - 48 bytes) 2 | originalLength - 16- | smpPrivHeader - ....... clientMsgBody (<= 15784 bytes = 15802 - 18) + 8- | smpPrivHeader (empty header for the message) + ....... clientMsgBody (<= 16020 bytes = 16030 - 10) -- TODO move internal structure to agent protocol - 16- | agentPublicHeader (the size is for user messages post handshake, without E2E X3DH keys) - ....... E2E double-ratchet encrypted (= 15768 bytes = 15784 - 16) - 96 | double-ratchet header + 8- | agentPublicHeader (the size is for user messages post handshake, without E2E X3DH keys - it is version and 'M' for the messages - 3 bytes in total) + ....... E2E double-ratchet encrypted (= 16012 bytes = 16020 - 8) + 88 | double-ratchet header (actual size is 69 bytes, the rest is reserved) 16 | double-ratchet header auth tag - 24 | double-ratchet header iv - ------- encrypted agent message (= 15616 bytes = 15768 - 152) + 16 | double-ratchet header iv + 16 | message auth tag (IV generated from chain ratchet) + ------- encrypted agent message (= 15876 bytes = 16012 - 136) 2 | originalLength - 122 (90) | agentHeader - 4 | %s"MSG" SP + 84 (41) | agentHeader (8 + 1+32) + 2 | %s"MM" ....... - | application message (<= 15488 bytes = 15616 - 128) + | application message (<= 15788 bytes = 15876 - 88) ....... 0+ | encrypted agent message pad ------- encrypted agent message end - 16 | auth tag (IV generated from chain ratchet) + | ....... E2E double-ratchet encrypted end | ....... clientMsgBody end 0+ | smpClientMessage pad ------- smpClientMessage end - 16 | auth tag for smpClientMessage + | ....... smpEncMessage end 0+ | serverEncryptedMsg pad ------- serverEncryptedMsg end - 16 | auth tag (msgId is used as nonce) 0+ | transmission pad ------- transmission end ``` @@ -702,7 +705,7 @@ The server must deliver messages to all subscribed simplex queues on the current ```abnf message = %s"MSG " msgId SP timestamp SP encryptedMsgBody encryptedMsgBody = ; server-encrypted padded sent msgBody -paddedSentMsgBody = ; maxMessageLength = 15968 +paddedSentMsgBody = ; maxMessageLength = 16078 msgId = length 24*24OCTET timestamp = 8*8OCTET ``` @@ -755,7 +758,7 @@ No further messages should be delivered to unsubscribed transport connection. - transmission has no required queue ID (`NO_QUEUE`) - authentication error (`AUTH`) - incorrect signature, 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). - 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. -- sent message is too large (> 15968) to be delivered (`LARGE_MSG`). +- sent message is too large (> 16078) to be delivered (`LARGE_MSG`). - internal server error (`INTERNAL`). The syntax for error responses: diff --git a/src/Simplex/Messaging/Agent/Client.hs b/src/Simplex/Messaging/Agent/Client.hs index 8cddbc5f4..6bf99c0f5 100644 --- a/src/Simplex/Messaging/Agent/Client.hs +++ b/src/Simplex/Messaging/Agent/Client.hs @@ -356,9 +356,10 @@ sendAgentMessage c sq@SndQueue {server, sndId, sndPrivateKey} agentMsg = agentCbEncrypt :: AgentMonad m => SndQueue -> Maybe C.PublicKeyX25519 -> ByteString -> m ByteString agentCbEncrypt SndQueue {e2eDhSecret} e2ePubKey msg = do cmNonce <- liftIO C.randomCbNonce + let paddedLen = maybe SMP.e2eEncMessageLength (const SMP.e2eEncConfirmationLength) e2ePubKey cmEncBody <- liftEither . first cryptoError $ - C.cbEncrypt e2eDhSecret cmNonce msg SMP.e2eEncMessageLength + C.cbEncrypt e2eDhSecret cmNonce msg paddedLen -- TODO per-queue client version let cmHeader = SMP.PubHeader (maxVersion SMP.smpClientVRange) e2ePubKey pure $ smpEncode SMP.ClientMsgEnvelope {cmHeader, cmNonce, cmEncBody} @@ -371,7 +372,7 @@ agentCbEncryptOnce dhRcvPubKey msg = do cmNonce <- liftIO C.randomCbNonce cmEncBody <- liftEither . first cryptoError $ - C.cbEncrypt e2eDhSecret cmNonce msg SMP.e2eEncMessageLength + C.cbEncrypt e2eDhSecret cmNonce msg SMP.e2eEncConfirmationLength -- TODO per-queue client version let cmHeader = SMP.PubHeader (maxVersion SMP.smpClientVRange) (Just dhSndPubKey) pure $ smpEncode SMP.ClientMsgEnvelope {cmHeader, cmNonce, cmEncBody} diff --git a/src/Simplex/Messaging/Agent/Protocol.hs b/src/Simplex/Messaging/Agent/Protocol.hs index d6f11dffd..b191e1aff 100644 --- a/src/Simplex/Messaging/Agent/Protocol.hs +++ b/src/Simplex/Messaging/Agent/Protocol.hs @@ -154,11 +154,14 @@ smpAgentVersion = 1 smpAgentVRange :: VersionRange smpAgentVRange = mkVersionRange 1 smpAgentVersion +-- it is shorter to allow all handshake headers, +-- including E2E (double-ratchet) parameters and +-- signing key of the sender for the server e2eEncConnInfoLength :: Int -e2eEncConnInfoLength = 14336 +e2eEncConnInfoLength = 14848 e2eEncUserMsgLength :: Int -e2eEncUserMsgLength = 15488 +e2eEncUserMsgLength = 15788 -- | Raw (unparsed) SMP agent protocol transmission. type ARawTransmission = (ByteString, ByteString, ByteString) diff --git a/src/Simplex/Messaging/Crypto/Ratchet.hs b/src/Simplex/Messaging/Crypto/Ratchet.hs index 4d967dd4f..b1043186f 100644 --- a/src/Simplex/Messaging/Crypto/Ratchet.hs +++ b/src/Simplex/Messaging/Crypto/Ratchet.hs @@ -264,9 +264,9 @@ data AMsgHeader AMsgHeader (SAlgorithm a) (MsgHeader a) -- to allow extension without increasing the size, the actual header length is: --- 81 = 2 (original size) + 2 + 1 + 68 (Ed448) + 4 + 4 +-- 69 = 2 (original size) + 2 + 1+56 (Curve448) + 4 + 4 paddedHeaderLen :: Int -paddedHeaderLen = 96 +paddedHeaderLen = 88 -- only used in tests to validate correct padding -- (2 bytes - version size, 1 byte - header size, not to have it fixed or version-dependent) diff --git a/src/Simplex/Messaging/Protocol.hs b/src/Simplex/Messaging/Protocol.hs index a9eb0f64a..0657c35c7 100644 --- a/src/Simplex/Messaging/Protocol.hs +++ b/src/Simplex/Messaging/Protocol.hs @@ -33,6 +33,7 @@ module Simplex.Messaging.Protocol smpClientVersion, smpClientVRange, maxMessageLength, + e2eEncConfirmationLength, e2eEncMessageLength, -- * SMP protocol types @@ -117,10 +118,14 @@ smpClientVRange :: VersionRange smpClientVRange = mkVersionRange 1 smpClientVersion maxMessageLength :: Int -maxMessageLength = 15968 +maxMessageLength = 16078 + +-- it is shorter to allow per-queue e2e encryption DH key in the "public" header +e2eEncConfirmationLength :: Int +e2eEncConfirmationLength = 15942 e2eEncMessageLength :: Int -e2eEncMessageLength = 15842 +e2eEncMessageLength = 16030 -- | SMP protocol clients data Party = Recipient | Sender | Notifier @@ -464,7 +469,7 @@ data ErrorType QUOTA | -- | ACK command is sent without message to be acknowledged NO_MSG - | -- | sent message is too large (> maxMessageLength = 15968 bytes) + | -- | sent message is too large (> maxMessageLength = 16078 bytes) LARGE_MSG | -- | internal server error INTERNAL