From be39a421218a84183a7e81ef74bd8c7df42d4eb2 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Wed, 30 Sep 2026 07:16:52 +0100 Subject: [PATCH] core: limit the number of skipped keys during remote access (#7622) * core: limit the number of skipped keys during remote access * simplify * core: restrict body size for XRCP commands and responses, limit decompressed size (#7616) Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- src/Simplex/Chat/Remote/Types.hs | 3 +++ tests/RemoteTests.hs | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/Simplex/Chat/Remote/Types.hs b/src/Simplex/Chat/Remote/Types.hs index 746aa7f566..90ab3b3279 100644 --- a/src/Simplex/Chat/Remote/Types.hs +++ b/src/Simplex/Chat/Remote/Types.hs @@ -18,6 +18,7 @@ import Control.Monad (when) import qualified Data.Aeson.TH as J import Data.ByteString (ByteString) import Data.Int (Int64) +import qualified Data.Map.Strict as M import Data.Text (Text) import Data.Word (Word16, Word32) import Simplex.Chat.Remote.AppVersion @@ -73,8 +74,10 @@ getRemoteRcvKeys RemoteCrypto {rcvCounter, chainKeys = TSbChainKeys {rcvKey}, sk | otherwise = do -- prevCorrId < corrId writeTVar rcvCounter corrId skipKeys (prevCorrId + 1) + modifyTVar' skippedKeys $ \m -> M.drop (M.size m - maxSkippedKeys) m Right <$> getKeys maxSkip = 256 + maxSkippedKeys = 1024 getKeys = (,) <$> stateTVar rcvKey C.sbcHkdf <*> stateTVar rcvKey C.sbcHkdf skipKeys !cId = when (cId < corrId) $ do diff --git a/tests/RemoteTests.hs b/tests/RemoteTests.hs index 3b1cfa1cae..2ab4cae0d6 100644 --- a/tests/RemoteTests.hs +++ b/tests/RemoteTests.hs @@ -61,6 +61,23 @@ remoteTests = describe "Remote" $ do filter (not . sanitized) fileNames `shouldBe` [] it "sanitizes to a name with no directory components" $ \_ -> filter (not . bareName) fileNames `shouldBe` [] + describe "skipped keys" $ do + it "keeps the most recent skipped keys" $ \_ -> do + (_, pk) <- atomically . C.generateKeyPair =<< C.newRandom + let (ck, _) = C.sbcInit "" ("secret" :: B.ByteString) + sndCounter <- newTVarIO 0 + rcvCounter <- newTVarIO 0 + sndKey <- newTVarIO ck + rcvKey <- newTVarIO ck + skippedKeys <- newTVarIO M.empty + let rc = RemoteCrypto {sessionCode = "", sndCounter, rcvCounter, chainKeys = TSbChainKeys {sndKey, rcvKey}, skippedKeys, signatures = RSSign pk pk, compression = False} + receive corrId = eitherToMaybe <$> atomically (getRemoteRcvKeys rc corrId) + sent <- replicateM 1280 $ atomically $ getRemoteSndKeys rc + let sentKeys = M.fromList [(corrId, (cmdKN, fileKN)) | (corrId, cmdKN, fileKN) <- sent] + forM_ [256, 512 .. 1280] $ \corrId -> receive corrId `shouldReturn` M.lookup corrId sentKeys + M.size <$> readTVarIO skippedKeys `shouldReturn` 1024 + receive 251 `shouldReturn` Nothing + receive 252 `shouldReturn` M.lookup 252 sentKeys describe "body size limit" $ do it "rejects encrypted body above limit without reading it" $ \_ -> do rc <- testRemoteCrypto