reduce SMP envelope sizes (#284)

This commit is contained in:
Evgeny Poberezkin
2022-01-11 21:01:21 +00:00
committed by GitHub
parent 083d39be22
commit 657c9b1b37
5 changed files with 53 additions and 41 deletions
+35 -32
View File
@@ -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:
+3 -2
View File
@@ -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}
+5 -2
View File
@@ -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)
+2 -2
View File
@@ -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)
+8 -3
View File
@@ -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