mirror of
https://github.com/simplex-chat/simplexmq.git
synced 2026-08-28 13:44:26 +00:00
reduce SMP envelope sizes (#284)
This commit is contained in:
@@ -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 = <binary X509 key encoding>
|
||||
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 = <encrypted padded(smpClientMessage, 15842)>
|
||||
sentMsgBody = <encrypted padded(smpClientMessage, 16030)>
|
||||
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 = <encrypt paddedSentMsgBody> ; server-encrypted padded sent msgBody
|
||||
paddedSentMsgBody = <padded(sentMsgBody, maxMessageLength + 2)> ; maxMessageLength = 15968
|
||||
paddedSentMsgBody = <padded(sentMsgBody, maxMessageLength + 2)> ; 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:
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user