This commit is contained in:
Evgeny @ SimpleX Chat committed 2026-10-02 21:18:35 +00:00
1 parent d6f0da197a
commit b4e6bc49aa
4 files changed
+39 -33

No files matched your search

+3 -2
View File
@@ -1145,7 +1145,7 @@ Having received the `RRES` response from the destination router, proxy router wi
```abnf
proxyResponse = %s"PRES" SP [responseNonce] <encrypted padded(forwardedResponse, 16226)>
responseNonce = 24*24 OCTET ; from v23
responseNonce = %s"0" / (%s"1" 24*24 OCTET) ; from v23
forwardedResponse = *OCTET ; client-encrypted SMP response, decrypted by client using per-command DH secret
```
@@ -1173,8 +1173,9 @@ The shared secret for encrypting transmission bodies between proxy router and de
```abnf
relayResponse = %s"RRES" SP <encrypted(responseTransmission)>
responseTransmission = fwdCorrId forwardedResponse
responseTransmission = fwdCorrId [fwdResponseNonce] forwardedResponse
; fwdCorrId and forwardedResponse defined above in RFWD section
fwdResponseNonce = 24*24 OCTET ; when fwdSmpVersion is 23 or higher
```
### Short link commands
+9 -13
View File
@@ -142,7 +142,6 @@ import Control.Monad.Trans.Except
import Crypto.Random (ChaChaDRG)
import qualified Data.Aeson.TH as J
import qualified Data.Attoparsec.ByteString.Char8 as A
import Data.Bifunctor (first)
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Base64 as B64
@@ -163,7 +162,7 @@ import Numeric.Natural
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Encoding
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, sumTypeJSON)
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, parseAll, sumTypeJSON)
import Simplex.Messaging.Protocol
import Simplex.Messaging.Protocol.Types
import Simplex.Messaging.Server.QueueStore.QueueInfo
@@ -1116,10 +1115,10 @@ deleteSMPQueues = okSMPCommands DEL
-- send PRXY :: SMPServer -> Maybe BasicAuth -> Command Sender
-- receives PKEY :: SessionId -> X.CertificateChain -> X.SignedExact X.PubKey -> BrokerMsg
connectSMPProxiedRelay :: SMPClient -> NetworkRequestMode -> SMPServer -> Maybe BasicAuth -> ExceptT SMPClientError IO ProxiedRelay
connectSMPProxiedRelay c@ProtocolClient {client_ = PClient {tcpConnectTimeout, tcpTimeout}} nm relayServ@ProtocolServer {port = relayPort, keyHash = C.KeyHash kh} proxyAuth =
connectSMPProxiedRelay c@ProtocolClient {thParams = THandleParams {thServerVRange}, client_ = PClient {tcpConnectTimeout, tcpTimeout}} nm relayServ@ProtocolServer {port = relayPort, keyHash = C.KeyHash kh} proxyAuth =
sendProtocolCommand_ c nm Nothing tOut Nothing NoEntity (Cmd SProxiedClient (PRXY relayServ proxyAuth)) >>= \case
PKEY sId vr (CertChainPubKey chain key) ->
case supportedClientSMPRelayVRange `compatibleVersion` vr of
case thServerVRange `compatibleVersion` vr of
Nothing -> throwE $ transportErr TEVersion
Just (Compatible v) -> do
relayKey <- liftEitherWith (const $ transportErr $ TEHandshake IDENTITY) =<< liftIO (runExceptT $ validateRelay chain key)
@@ -1170,7 +1169,7 @@ instance StrEncoding ProxyClientError where
-- consider how to process slow responses - is it handled somehow locally or delegated to the caller
-- this method is used in the client
-- sends PFWD :: C.PublicKeyX25519 -> EncTransmission -> Command Sender
-- receives PRES :: EncResponse -> BrokerMsg -- proxy to client
-- receives PRES :: Maybe C.CbNonce -> EncResponse -> BrokerMsg -- proxy to client
-- When client sends message via proxy, there may be one successful scenario and 9 error scenarios
-- as shown below (WTF stands for unexpected response, ??? for response that failed to parse).
@@ -1234,12 +1233,9 @@ proxySMPCommand c@ProtocolClient {thParams = proxyThParams, client_ = PClient {c
let tOut = Just $ 2 * netTimeoutInt tcpTimeout nm
tryE (sendProtocolCommand_ c nm (Just nonce) tOut Nothing (EntityId sessionId) (Cmd SProxiedClient (PFWD v cmdPubKey et))) >>= \case
Right r -> case r of
PRES (EncResponse er) -> do
PRES nonce_ (EncResponse er) -> do
-- server interaction errors are thrown directly
let (respNonce, er')
| v >= fwdNoncesSMPVersion = first C.cbNonce $ B.splitAt 24 er
| otherwise = (C.reverseNonce nonce, er)
t' <- liftEitherWith PCECryptoError $ C.cbDecrypt cmdSecret respNonce er'
t' <- liftEitherWith PCECryptoError $ C.cbDecrypt cmdSecret (fromMaybe (C.reverseNonce nonce) nonce_) er
case tParse serverThParams t' of
t'' :| [] -> case tDecodeClient serverThParams t'' of
(_, _, cmd) -> case cmd of
@@ -1260,7 +1256,7 @@ proxySMPCommand c@ProtocolClient {thParams = proxyThParams, client_ = PClient {c
-- receives RRES :: EncFwdResponse -> BrokerMsg
-- proxy should send PRES to the client with EncResponse
-- Always uses background timeout mode
forwardSMPTransmission :: SMPClient -> CorrId -> VersionSMP -> C.PublicKeyX25519 -> EncTransmission -> ExceptT SMPClientError IO EncResponse
forwardSMPTransmission :: SMPClient -> CorrId -> VersionSMP -> C.PublicKeyX25519 -> EncTransmission -> ExceptT SMPClientError IO (Maybe C.CbNonce, EncResponse)
forwardSMPTransmission c@ProtocolClient {thParams, client_ = PClient {clientCorrId = g}} fwdCorrId fwdVersion fwdKey fwdTransmission = do
-- prepare params
sessSecret <- case thAuth thParams of
@@ -1275,8 +1271,8 @@ forwardSMPTransmission c@ProtocolClient {thParams, client_ = PClient {clientCorr
RRES (EncFwdResponse efr) -> do
-- unwrap
r' <- liftEitherWith PCECryptoError $ C.cbDecryptNoPad sessSecret (C.reverseNonce nonce) efr
FwdResponse {fwdCorrId = _, fwdResponse} <- liftEitherWith (const $ PCEResponseError BLOCK) $ smpDecode r'
pure fwdResponse
FwdResponse {fwdCorrId = _, fwdNonce, fwdResponse} <- liftEitherWith (const $ PCEResponseError BLOCK) $ parseAll (fwdResponseP fwdVersion) r'
pure (fwdNonce, fwdResponse)
r -> throwE $ unexpectedResponse r
-- get queue information - always sent interactively
+20 -9
View File
@@ -170,6 +170,8 @@ module Simplex.Messaging.Protocol
EncTransmission (..),
encTransmissionNonce,
FwdResponse (..),
encodeFwdResponse,
fwdResponseP,
FwdTransmission (..),
NameRecord (..),
MsgFlags (..),
@@ -744,7 +746,7 @@ data BrokerMsg where
-- Should include certificate chain
PKEY :: SessionId -> VersionRangeSMP -> CertChainPubKey -> BrokerMsg -- TLS-signed server key for proxy shared secret and initial sender key
RRES :: EncFwdResponse -> BrokerMsg -- relay to proxy
PRES :: EncResponse -> BrokerMsg -- proxy to client
PRES :: Maybe C.CbNonce -> EncResponse -> BrokerMsg -- proxy to client
END :: BrokerMsg
ENDS :: Int64 -> IdsHash -> BrokerMsg
DELD :: BrokerMsg
@@ -767,15 +769,20 @@ newtype EncFwdResponse = EncFwdResponse ByteString
data FwdResponse = FwdResponse
{ fwdCorrId :: CorrId,
fwdNonce :: Maybe C.CbNonce,
fwdResponse :: EncResponse
}
instance Encoding FwdResponse where
smpEncode FwdResponse {fwdCorrId = CorrId corrId, fwdResponse = EncResponse t} =
smpEncode (corrId, Tail t)
smpP = do
(corrId, Tail t) <- smpP
pure FwdResponse {fwdCorrId = CorrId corrId, fwdResponse = EncResponse t}
encodeFwdResponse :: FwdResponse -> ByteString
encodeFwdResponse FwdResponse {fwdCorrId = CorrId corrId, fwdNonce, fwdResponse = EncResponse t} =
smpEncode corrId <> maybe "" smpEncode fwdNonce <> smpEncode (Tail t)
fwdResponseP :: VersionSMP -> Parser FwdResponse
fwdResponseP v = do
corrId <- smpP
fwdNonce <- if v >= fwdNoncesSMPVersion then Just <$> smpP else pure Nothing
Tail t <- smpP
pure FwdResponse {fwdCorrId = CorrId corrId, fwdNonce, fwdResponse = EncResponse t}
newtype EncResponse = EncResponse ByteString
deriving (Eq, Show)
@@ -1992,7 +1999,9 @@ instance ProtocolEncoding SMPVersion ErrorType BrokerMsg where
NMSG nmsgNonce encNMsgMeta -> e (NMSG_, ' ', nmsgNonce, encNMsgMeta)
PKEY sid vr certKey -> e (PKEY_, ' ', sid, vr, certKey)
RRES (EncFwdResponse encBlock) -> e (RRES_, ' ', Tail encBlock)
PRES (EncResponse encBlock) -> e (PRES_, ' ', Tail encBlock)
PRES nonce_ (EncResponse encBlock)
| v >= fwdNoncesSMPVersion -> e (PRES_, ' ', nonce_, Tail encBlock)
| otherwise -> e (PRES_, ' ', Tail encBlock)
END -> e END_
ENDS n idsHash -> serviceResp ENDS_ n idsHash
DELD -> e DELD_
@@ -2048,7 +2057,9 @@ instance ProtocolEncoding SMPVersion ErrorType BrokerMsg where
NMSG_ -> NMSG <$> _smpP <*> smpP
PKEY_ -> PKEY <$> _smpP <*> smpP <*> smpP
RRES_ -> RRES <$> (EncFwdResponse . unTail <$> _smpP)
PRES_ -> PRES <$> (EncResponse . unTail <$> _smpP)
PRES_
| v >= fwdNoncesSMPVersion -> PRES <$> _smpP <*> (EncResponse . unTail <$> smpP)
| otherwise -> PRES Nothing . EncResponse . unTail <$> _smpP
END_ -> pure END
ENDS_ -> serviceRespP ENDS
DELD_ -> pure DELD
+7 -9
View File
@@ -1462,7 +1462,7 @@ client
inc own pRequests
forkProxiedCmd $ do
liftIO (runExceptT (forwardSMPTransmission smp corrId fwdV pubKey encBlock) `E.catches` clientHandlers) >>= \case
Right r -> PRES r <$ inc own pSuccesses
Right (nonce_, r) -> PRES nonce_ r <$ inc own pSuccesses
Left e -> ERR (smpProxyError e) <$ case e of
PCEProtocolError {} -> inc own pSuccesses
_ -> inc own pErrorsOther
@@ -2144,15 +2144,13 @@ client
TBError _ _ : _ -> throwE BLOCK
TBTransmission b' _ : _ -> pure b'
TBTransmissions b' _ _ : _ -> pure b'
let encrypt nonce = liftEitherWith (const BLOCK) $ C.cbEncrypt clientSecret nonce r' paddedProxiedTLength
r2 <-
fwdNonce <-
if fwdVersion >= fwdNoncesSMPVersion
then do
nonce <- atomically . C.randomCbNonce =<< asks random
EncResponse . (smpEncode nonce <>) <$> encrypt nonce
else EncResponse <$> encrypt (C.reverseNonce clientNonce)
let fr = FwdResponse {fwdCorrId, fwdResponse = r2}
pure $ RRES $ EncFwdResponse $ C.cbEncryptNoPad sessSecret (C.reverseNonce proxyNonce) (smpEncode fr)
then Just <$> (atomically . C.randomCbNonce =<< asks random)
else pure Nothing
r2 <- liftEitherWith (const BLOCK) $ EncResponse <$> C.cbEncrypt clientSecret (fromMaybe (C.reverseNonce clientNonce) fwdNonce) r' paddedProxiedTLength
let fr = FwdResponse {fwdCorrId, fwdNonce, fwdResponse = r2}
pure $ RRES $ EncFwdResponse $ C.cbEncryptNoPad sessSecret (C.reverseNonce proxyNonce) (encodeFwdResponse fr)
-- the inner response, or Nothing if forked (RSLV).
r_ <- lift (rejectOrVerify clntThAuth t') >>= \case
-- rejectOrVerify filters allowed commands, no need to repeat it here.