Skip to content

Commit a99ce61

Browse files
authored
secure queue by sender via proxy (proxy SKEY command) (#1210)
* client: secure queue by sender via proxy (proxy SKEY command) * agent and server: proxy SKEY command
1 parent a6b542b commit a99ce61

File tree

4 files changed

+64
-39
lines changed

4 files changed

+64
-39
lines changed

src/Simplex/Messaging/Agent/Client.hs

+29-13
Original file line numberDiff line numberDiff line change
@@ -1026,7 +1026,27 @@ withSMPClient c q cmdStr action = do
10261026
withLogClient c tSess (queueId q) cmdStr $ action . connectedClient
10271027

10281028
sendOrProxySMPMessage :: AgentClient -> UserId -> SMPServer -> ByteString -> Maybe SMP.SndPrivateAuthKey -> SMP.SenderId -> MsgFlags -> SMP.MsgBody -> AM (Maybe SMPServer)
1029-
sendOrProxySMPMessage c userId destSrv cmdStr spKey_ senderId msgFlags msg = do
1029+
sendOrProxySMPMessage c userId destSrv cmdStr spKey_ senderId msgFlags msg =
1030+
sendOrProxySMPCommand c userId destSrv cmdStr senderId sendViaProxy sendDirectly
1031+
where
1032+
sendViaProxy smp proxySess = do
1033+
atomically $ incSMPServerStat c userId destSrv sentViaProxyAttempts
1034+
atomically $ incSMPServerStat c userId (protocolClientServer' smp) sentProxiedAttempts
1035+
proxySMPMessage smp proxySess spKey_ senderId msgFlags msg
1036+
sendDirectly smp = do
1037+
atomically $ incSMPServerStat c userId destSrv sentDirectAttempts
1038+
sendSMPMessage smp spKey_ senderId msgFlags msg
1039+
1040+
sendOrProxySMPCommand ::
1041+
AgentClient ->
1042+
UserId ->
1043+
SMPServer ->
1044+
ByteString ->
1045+
SMP.SenderId ->
1046+
(SMPClient -> ProxiedRelay -> ExceptT SMPClientError IO (Either ProxyClientError ())) ->
1047+
(SMPClient -> ExceptT SMPClientError IO ()) ->
1048+
AM (Maybe SMPServer)
1049+
sendOrProxySMPCommand c userId destSrv cmdStr senderId sendCmdViaProxy sendCmdDirectly = do
10301050
sess <- liftIO $ mkTransportSession c userId destSrv senderId
10311051
ifM (atomically shouldUseProxy) (sendViaProxy sess) (sendDirectly sess $> Nothing)
10321052
where
@@ -1048,10 +1068,7 @@ sendOrProxySMPMessage c userId destSrv cmdStr spKey_ senderId msgFlags msg = do
10481068
unknownServer = maybe True (all ((destSrv /=) . protoServer)) <$> TM.lookup userId (userServers c)
10491069
sendViaProxy destSess@(_, _, qId) = do
10501070
r <- tryAgentError . withProxySession c destSess senderId ("PFWD " <> cmdStr) $ \(SMPConnectedClient smp _, proxySess) -> do
1051-
r' <- liftClient SMP (clientServer smp) $ do
1052-
atomically $ incSMPServerStat c userId destSrv sentViaProxyAttempts
1053-
atomically $ incSMPServerStat c userId (protocolClientServer' smp) sentProxiedAttempts
1054-
proxySMPMessage smp proxySess spKey_ senderId msgFlags msg
1071+
r' <- liftClient SMP (clientServer smp) $ sendCmdViaProxy smp proxySess
10551072
case r' of
10561073
Right () -> pure . Just $ protocolClientServer' smp
10571074
Left proxyErr -> do
@@ -1089,11 +1106,7 @@ sendOrProxySMPMessage c userId destSrv cmdStr spKey_ senderId msgFlags msg = do
10891106
| otherwise -> throwE e
10901107
sendDirectly tSess =
10911108
withLogClient_ c tSess senderId ("SEND " <> cmdStr) $ \(SMPConnectedClient smp _) -> do
1092-
r <-
1093-
tryAgentError $
1094-
liftClient SMP (clientServer smp) $ do
1095-
atomically $ incSMPServerStat c userId destSrv sentDirectAttempts
1096-
sendSMPMessage smp spKey_ senderId msgFlags msg
1109+
r <- tryAgentError $ liftClient SMP (clientServer smp) $ sendCmdDirectly smp
10971110
case r of
10981111
Right () -> atomically $ incSMPServerStat c userId destSrv sentDirect
10991112
Left e -> throwE e
@@ -1536,9 +1549,12 @@ secureQueue c rq@RcvQueue {rcvId, rcvPrivateKey} senderKey =
15361549
secureSMPQueue smp rcvPrivateKey rcvId senderKey
15371550

15381551
secureSndQueue :: AgentClient -> SndQueue -> AM ()
1539-
secureSndQueue c sq@SndQueue {sndId, sndPrivateKey, sndPublicKey} =
1540-
withSMPClient c sq "SKEY <key>" $ \smp ->
1541-
secureSndSMPQueue smp sndPrivateKey sndId sndPublicKey
1552+
secureSndQueue c SndQueue {userId, server, sndId, sndPrivateKey, sndPublicKey} =
1553+
void $ sendOrProxySMPCommand c userId server "SKEY <key>" sndId secureViaProxy secureDirectly
1554+
where
1555+
-- TODO track statistics
1556+
secureViaProxy smp proxySess = proxySecureSndSMPQueue smp proxySess sndPrivateKey sndId sndPublicKey
1557+
secureDirectly smp = secureSndSMPQueue smp sndPrivateKey sndId sndPublicKey
15421558

15431559
enableQueueNotifications :: AgentClient -> RcvQueue -> SMP.NtfPublicAuthKey -> SMP.RcvNtfPublicDhKey -> AM (SMP.NotifierId, SMP.RcvNtfPublicDhKey)
15441560
enableQueueNotifications c rq@RcvQueue {rcvId, rcvPrivateKey} notifierKey rcvNtfPublicDhKey =

src/Simplex/Messaging/Client.hs

+18-10
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ module Simplex.Messaging.Client
4848
subscribeSMPQueuesNtfs,
4949
secureSMPQueue,
5050
secureSndSMPQueue,
51+
proxySecureSndSMPQueue,
5152
enableSMPQueueNotifications,
5253
disableSMPQueueNotifications,
5354
enableSMPQueuesNtfs,
@@ -59,7 +60,7 @@ module Simplex.Messaging.Client
5960
deleteSMPQueues,
6061
connectSMPProxiedRelay,
6162
proxySMPMessage,
62-
forwardSMPMessage,
63+
forwardSMPTransmission,
6364
getSMPQueueInfo,
6465
sendProtocolCommand,
6566

@@ -736,6 +737,10 @@ secureSndSMPQueue :: SMPClient -> SndPrivateAuthKey -> SenderId -> SndPublicAuth
736737
secureSndSMPQueue c spKey sId senderKey = okSMPCommand (SKEY senderKey) c spKey sId
737738
{-# INLINE secureSndSMPQueue #-}
738739

740+
proxySecureSndSMPQueue :: SMPClient -> ProxiedRelay -> SndPrivateAuthKey -> SenderId -> SndPublicAuthKey -> ExceptT SMPClientError IO (Either ProxyClientError ())
741+
proxySecureSndSMPQueue c proxiedRelay spKey sId senderKey = proxySMPCommand c proxiedRelay (Just spKey) sId (SKEY senderKey)
742+
{-# INLINE proxySecureSndSMPQueue #-}
743+
739744
-- | Enable notifications for the queue for push notifications server.
740745
--
741746
-- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#enable-notifications-command
@@ -776,6 +781,9 @@ sendSMPMessage c spKey sId flags msg =
776781
OK -> pure ()
777782
r -> throwE $ unexpectedResponse r
778783

784+
proxySMPMessage :: SMPClient -> ProxiedRelay -> Maybe SndPrivateAuthKey -> SenderId -> MsgFlags -> MsgBody -> ExceptT SMPClientError IO (Either ProxyClientError ())
785+
proxySMPMessage c proxiedRelay spKey sId flags msg = proxySMPCommand c proxiedRelay spKey sId (SEND flags msg)
786+
779787
-- | Acknowledge message delivery (server deletes the message).
780788
--
781789
-- https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#acknowledge-message-delivery
@@ -877,39 +885,39 @@ instance StrEncoding ProxyClientError where
877885
-- 8) PFWD(SEND) -> WTF -> ProxyUnexpectedResponse - client/proxy protocol logic
878886
-- 9) PFWD(SEND) -> ??? -> ProxyResponseError - client/proxy syntax
879887
--
880-
-- We report as proxySMPMessage error (ExceptT error) the errors of two kinds:
888+
-- We report as proxySMPCommand error (ExceptT error) the errors of two kinds:
881889
-- - protocol errors from the destination relay wrapped in PRES - to simplify processing of AUTH and QUOTA errors, in this case proxy is "transparent" for such errors (PCEProtocolError, PCEUnexpectedResponse, PCEResponseError)
882890
-- - other response/transport/connection errors from the client connected to proxy itself
883891
-- Other errors are reported in the function result as `Either ProxiedRelayError ()`, including
884892
-- - protocol errors from the client connected to proxy in ProxyClientError (PCEProtocolError, PCEUnexpectedResponse, PCEResponseError)
885893
-- - other errors from the client running on proxy and connected to relay in PREProxiedRelayError
886894

887-
proxySMPMessage ::
895+
-- This function proxies Sender commands that return OK or ERR
896+
proxySMPCommand ::
888897
SMPClient ->
889898
-- proxy session from PKEY
890899
ProxiedRelay ->
891900
-- message to deliver
892901
Maybe SndPrivateAuthKey ->
893902
SenderId ->
894-
MsgFlags ->
895-
MsgBody ->
903+
Command 'Sender ->
896904
ExceptT SMPClientError IO (Either ProxyClientError ())
897-
proxySMPMessage c@ProtocolClient {thParams = proxyThParams, client_ = PClient {clientCorrId = g, tcpTimeout}} (ProxiedRelay sessionId v serverKey) spKey sId flags msg = do
905+
proxySMPCommand c@ProtocolClient {thParams = proxyThParams, client_ = PClient {clientCorrId = g, tcpTimeout}} (ProxiedRelay sessionId v serverKey) spKey sId command = do
898906
-- prepare params
899907
let serverThAuth = (\ta -> ta {serverPeerPubKey = serverKey}) <$> thAuth proxyThParams
900908
serverThParams = smpTHParamsSetVersion v proxyThParams {sessionId, thAuth = serverThAuth}
901909
(cmdPubKey, cmdPrivKey) <- liftIO . atomically $ C.generateKeyPair @'C.X25519 g
902910
let cmdSecret = C.dh' serverKey cmdPrivKey
903911
nonce@(C.CbNonce corrId) <- liftIO . atomically $ C.randomCbNonce g
904912
-- encode
905-
let TransmissionForAuth {tForAuth, tToSend} = encodeTransmissionForAuth serverThParams (CorrId corrId, sId, Cmd SSender (SEND flags msg))
913+
let TransmissionForAuth {tForAuth, tToSend} = encodeTransmissionForAuth serverThParams (CorrId corrId, sId, Cmd SSender command)
906914
auth <- liftEitherWith PCETransportError $ authTransmission serverThAuth spKey nonce tForAuth
907915
b <- case batchTransmissions (batch serverThParams) (blockSize serverThParams) [Right (auth, tToSend)] of
908916
[] -> throwE $ PCETransportError TELargeMsg
909917
TBError e _ : _ -> throwE $ PCETransportError e
910918
TBTransmission s _ : _ -> pure s
911919
TBTransmissions s _ _ : _ -> pure s
912-
et <- liftEitherWith PCECryptoError $ EncTransmission <$> C.cbEncrypt cmdSecret nonce b paddedProxiedMsgLength
920+
et <- liftEitherWith PCECryptoError $ EncTransmission <$> C.cbEncrypt cmdSecret nonce b paddedProxiedTLength
913921
-- proxy interaction errors are wrapped
914922
let tOut = Just $ 2 * tcpTimeout
915923
tryE (sendProtocolCommand_ c (Just nonce) tOut Nothing sessionId (Cmd SProxiedClient (PFWD v cmdPubKey et))) >>= \case
@@ -937,8 +945,8 @@ proxySMPMessage c@ProtocolClient {thParams = proxyThParams, client_ = PClient {c
937945
-- sends RFWD :: EncFwdTransmission -> Command Sender
938946
-- receives RRES :: EncFwdResponse -> BrokerMsg
939947
-- proxy should send PRES to the client with EncResponse
940-
forwardSMPMessage :: SMPClient -> CorrId -> VersionSMP -> C.PublicKeyX25519 -> EncTransmission -> ExceptT SMPClientError IO EncResponse
941-
forwardSMPMessage c@ProtocolClient {thParams, client_ = PClient {clientCorrId = g}} fwdCorrId fwdVersion fwdKey fwdTransmission = do
948+
forwardSMPTransmission :: SMPClient -> CorrId -> VersionSMP -> C.PublicKeyX25519 -> EncTransmission -> ExceptT SMPClientError IO EncResponse
949+
forwardSMPTransmission c@ProtocolClient {thParams, client_ = PClient {clientCorrId = g}} fwdCorrId fwdVersion fwdKey fwdTransmission = do
942950
-- prepare params
943951
sessSecret <- case thAuth thParams of
944952
Nothing -> throwE $ PCETransportError TENoServerAuth

src/Simplex/Messaging/Protocol.hs

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ module Simplex.Messaging.Protocol
4343
( -- * SMP protocol parameters
4444
supportedSMPClientVRange,
4545
maxMessageLength,
46-
paddedProxiedMsgLength,
46+
paddedProxiedTLength,
4747
e2eEncConfirmationLength,
4848
e2eEncMessageLength,
4949

@@ -258,8 +258,8 @@ maxMessageLength v
258258
| v >= sendingProxySMPVersion = 16064 -- max 16067
259259
| otherwise = 16088 -- 16064 - always use this size to determine allowed ranges
260260

261-
paddedProxiedMsgLength :: Int
262-
paddedProxiedMsgLength = 16242 -- 16241 .. 16243
261+
paddedProxiedTLength :: Int
262+
paddedProxiedTLength = 16242 -- 16241 .. 16243
263263

264264
-- TODO v6.0 change to 16064
265265
type MaxMessageLen = 16088

src/Simplex/Messaging/Server.hs

+14-13
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ import GHC.Stats (getRTSStats)
7070
import GHC.TypeLits (KnownNat)
7171
import Network.Socket (ServiceName, Socket, socketToHandle)
7272
import Simplex.Messaging.Agent.Lock
73-
import Simplex.Messaging.Client (ProtocolClient (thParams), ProtocolClientError (..), SMPClient, SMPClientError, forwardSMPMessage, smpProxyError, temporaryClientError)
73+
import Simplex.Messaging.Client (ProtocolClient (thParams), ProtocolClientError (..), SMPClient, SMPClientError, forwardSMPTransmission, smpProxyError, temporaryClientError)
7474
import Simplex.Messaging.Client.Agent (OwnServer, SMPClientAgent (..), SMPClientAgentEvent (..), closeSMPClientAgent, getSMPServerClient'', isOwnServer, lookupSMPServerClient, getConnectedSMPServerClient)
7575
import qualified Simplex.Messaging.Crypto as C
7676
import Simplex.Messaging.Encoding
@@ -742,7 +742,7 @@ client thParams' clnt@Client {subscriptions, ntfSubscriptions, rcvQ, sndQ, sessi
742742
inc own pRequests
743743
if v >= sendingProxySMPVersion
744744
then forkProxiedCmd $ do
745-
liftIO (runExceptT (forwardSMPMessage smp corrId fwdV pubKey encBlock) `catch` (pure . Left . PCEIOError)) >>= \case
745+
liftIO (runExceptT (forwardSMPTransmission smp corrId fwdV pubKey encBlock) `catch` (pure . Left . PCEIOError)) >>= \case
746746
Right r -> PRES r <$ inc own pSuccesses
747747
Left e -> ERR (smpProxyError e) <$ case e of
748748
PCEProtocolError {} -> inc own pSuccesses
@@ -1095,24 +1095,21 @@ client thParams' clnt@Client {subscriptions, ntfSubscriptions, rcvQ, sndQ, sessi
10951095
t :| [] -> pure $ tDecodeParseValidate clntTHParams t
10961096
_ -> throwE BLOCK
10971097
let clntThAuth = Just $ THAuthServer {serverPrivKey, sessSecret' = Just clientSecret}
1098-
-- process forwarded SEND
1098+
-- process forwarded command
10991099
r <-
11001100
lift (rejectOrVerify clntThAuth t') >>= \case
11011101
Left r -> pure r
1102-
Right t''@(_, (corrId', entId', cmd')) -> case cmd' of
1103-
Cmd SSender SEND {} ->
1104-
-- Left will not be returned by processCommand, as only SEND command is allowed
1105-
fromMaybe (corrId', entId', ERR INTERNAL) <$> lift (processCommand t'')
1106-
_ ->
1107-
pure (corrId', entId', ERR $ CMD PROHIBITED)
1102+
-- rejectOrVerify filters allowed commands, no need to repeat it here.
1103+
-- INTERNAL is used because processCommand never returns Nothing for sender commands (could be extracted for better types).
1104+
Right t''@(_, (corrId', entId', _)) -> fromMaybe (corrId', entId', ERR INTERNAL) <$> lift (processCommand t'')
11081105
-- encode response
11091106
r' <- case batchTransmissions (batch clntTHParams) (blockSize clntTHParams) [Right (Nothing, encodeTransmission clntTHParams r)] of
11101107
[] -> throwE INTERNAL -- at least 1 item is guaranteed from NonEmpty/Right
11111108
TBError _ _ : _ -> throwE BLOCK
11121109
TBTransmission b' _ : _ -> pure b'
11131110
TBTransmissions b' _ _ : _ -> pure b'
11141111
-- encrypt to client
1115-
r2 <- liftEitherWith (const BLOCK) $ EncResponse <$> C.cbEncrypt clientSecret (C.reverseNonce clientNonce) r' paddedProxiedMsgLength
1112+
r2 <- liftEitherWith (const BLOCK) $ EncResponse <$> C.cbEncrypt clientSecret (C.reverseNonce clientNonce) r' paddedProxiedTLength
11161113
-- encrypt to proxy
11171114
let fr = FwdResponse {fwdCorrId, fwdResponse = r2}
11181115
r3 = EncFwdResponse $ C.cbEncryptNoPad sessSecret (C.reverseNonce proxyNonce) (smpEncode fr)
@@ -1124,13 +1121,17 @@ client thParams' clnt@Client {subscriptions, ntfSubscriptions, rcvQ, sndQ, sessi
11241121
rejectOrVerify clntThAuth (tAuth, authorized, (corrId', entId', cmdOrError)) =
11251122
case cmdOrError of
11261123
Left e -> pure $ Left (corrId', entId', ERR e)
1127-
Right cmd'@(Cmd SSender SEND {}) -> verified <$> verifyTransmission ((,C.cbNonce (bs corrId')) <$> clntThAuth) tAuth authorized entId' cmd'
1124+
Right cmd'
1125+
| allowed -> verified <$> verifyTransmission ((,C.cbNonce (bs corrId')) <$> clntThAuth) tAuth authorized entId' cmd'
1126+
| otherwise -> pure $ Left (corrId', entId', ERR $ CMD PROHIBITED)
11281127
where
1128+
allowed = case cmd' of
1129+
Cmd SSender SEND {} -> True
1130+
Cmd SSender (SKEY _) -> True
1131+
_ -> False
11291132
verified = \case
11301133
VRVerified qr -> Right (qr, (corrId', entId', cmd'))
11311134
VRFailed -> Left (corrId', entId', ERR AUTH)
1132-
Right _ -> pure $ Left (corrId', entId', ERR $ CMD PROHIBITED)
1133-
11341135
deliverMessage :: T.Text -> QueueRec -> RecipientId -> TVar Sub -> MsgQueue -> Maybe Message -> M (Transmission BrokerMsg)
11351136
deliverMessage name qr rId sub q msg_ = time (name <> " deliver") $ do
11361137
readTVarIO sub >>= \case

0 commit comments

Comments
 (0)