smp-server: reject corrId nonces of wrong size

This commit is contained in:
shum
2026-10-02 11:06:54 +00:00
parent 053e83b704
commit 4b2ca9364e
4 changed files with 116 additions and 26 deletions
+48 -1
View File
@@ -26,7 +26,7 @@ import qualified Data.List.NonEmpty as L
import Data.Time.Clock (getCurrentTime)
import SMPAgentClient
import SMPClient
import ServerTests (decryptMsgV3, sendRecv)
import ServerTests (decryptMsgV3, sendRecv, wrongSizeCorrIds)
import Simplex.Messaging.Agent hiding (createConnection, joinConnection, sendMessage)
import qualified Simplex.Messaging.Agent as A
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..))
@@ -36,11 +36,13 @@ import Simplex.Messaging.Client
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Crypto.Ratchet (pattern PQSupportOn)
import qualified Simplex.Messaging.Crypto.Ratchet as CR
import Simplex.Messaging.Encoding (smpEncode)
import Simplex.Messaging.Protocol (EncRcvMsgBody (..), MsgBody, QueueReqData (..), RcvMessage (..), SubscriptionMode (..), maxMessageLength, noMsgFlags)
import qualified Simplex.Messaging.Protocol as SMP
import Simplex.Messaging.Server.Env.STM (AStoreType (..), ServerConfig (..))
import Simplex.Messaging.Server.MsgStore.Types (SQSType (..))
import Simplex.Messaging.Transport
import qualified Simplex.Messaging.Transport.Client as TC
import Simplex.Messaging.Util (bshow, tshow)
import Simplex.Messaging.Version (mkVersionRange)
import System.FilePath (splitExtensions)
@@ -58,6 +60,9 @@ smpProxyTests = do
describe "server configuration" $ do
it "refuses proxy handshake unless enabled" testNoProxy
it "checks basic auth in proxy requests" testProxyAuth
describe "corrId used as nonce" $ do
it "proxy rejects PFWD with corrId of wrong size" testPFWDCorrIdSize
it "relay rejects RFWD with corrId of wrong size" testRFWDCorrIdSize
describe "relay reconnection" $ do
it "recovers when unresponsive relay restarts (control, no disconnect)" $ \_ ->
testProxyRecoversWithoutDisconnect
@@ -441,6 +446,48 @@ testProxyAuth msType = do
where
proxyCfgAuth = updateCfg (proxyCfgMS msType) $ \cfg_ -> cfg_ {newQueueBasicAuth = Just "correct"}
testPFWDCorrIdSize :: AStoreType -> IO ()
testPFWDCorrIdSize msType =
withSmpServerConfigOn (transport @TLS) (proxyCfgMS msType) testPort $ \_ ->
testSMPClient_ "127.0.0.1" testPort supportedServerSMPRelayVRange Nothing $ \(th@THandle {params} :: THandleSMP TLS 'TClient) -> do
g <- C.newRandom
(cmdKey, _ :: C.PrivateKeyX25519) <- atomically $ C.generateKeyPair g
let pfwd corrId = do
(_, _, reply) <- sendRecv th (Nothing, corrId, SMP.EntityId "unknown session", SMP.PFWD (thVersion params) cmdKey $ SMP.EncTransmission "invalid")
pure reply
corrId <- atomically $ C.randomBytes 24 g
pfwd corrId `shouldReturn` Right (SMP.ERR $ SMP.PROXY SMP.NO_SESSION)
forM_ (wrongSizeCorrIds corrId) $ \badCorrId ->
pfwd badCorrId `shouldReturn` Right (SMP.ERR $ SMP.CMD SMP.SYNTAX)
testRFWDCorrIdSize :: AStoreType -> IO ()
testRFWDCorrIdSize msType =
withSmpServerConfigOn (transport @TLS) (cfgMS msType) testPort2 $ \_ ->
testProxyModeClient $ \th@THandle {params = THandleParams {thVersion, thAuth}} -> do
Just THAuthClient {sessSecret = Just sessSecret} <- pure thAuth
g <- C.newRandom
(fwdKey, _ :: C.PrivateKeyX25519) <- atomically $ C.generateKeyPair g
let rfwd corrId fwdCorrId = do
let fwdT = SMP.FwdTransmission {fwdCorrId = SMP.CorrId fwdCorrId, fwdVersion = thVersion, fwdKey, fwdTransmission = SMP.EncTransmission "invalid"}
eft = SMP.EncFwdTransmission $ C.cbEncryptNoPad sessSecret (C.cbNonce corrId) (smpEncode fwdT)
(_, _, reply) <- sendRecv th (Nothing, corrId, NoEntity, SMP.RFWD eft)
pure reply
corrId <- atomically $ C.randomBytes 24 g
fwdCorrId <- atomically $ C.randomBytes 24 g
rfwd corrId fwdCorrId `shouldReturn` Right (SMP.ERR SMP.CRYPTO)
forM_ (wrongSizeCorrIds corrId) $ \badCorrId -> do
rfwd badCorrId fwdCorrId `shouldReturn` Right (SMP.ERR $ SMP.CMD SMP.SYNTAX)
rfwd corrId badCorrId `shouldReturn` Right (SMP.ERR $ SMP.CMD SMP.SYNTAX)
-- Connect to the relay on testPort2 the same way the proxy does, with the session secret.
testProxyModeClient :: (THandleSMP TLS 'TClient -> IO a) -> IO a
testProxyModeClient client = do
g <- C.newRandom
ks <- atomically $ C.generateKeyPair g
let tcConfig = TC.defaultTransportClientConfig {TC.clientALPN = Just alpnSupportedSMPHandshakes}
TC.runTransportClient tcConfig Nothing "127.0.0.1" testPort2 (Just testKeyHash) $ \h ->
runExceptT (smpClientHandshake h (Just ks) testKeyHash supportedClientSMPRelayVRange True Nothing) >>= either (fail . show) client
-- Connect a sender client to the proxy and request a relay session to testSMPServer2 (PRXY).
-- On success the reply is PKEY; otherwise it is the proxy error for the relay connection.
requestRelaySession :: IO (Either SMP.ErrorType SMP.BrokerMsg)