From c2eb680535d7336c2f92ef2c1b75f7de7069ccec Mon Sep 17 00:00:00 2001 From: Evgeny Date: Fri, 31 Jul 2026 09:43:19 +0100 Subject: [PATCH] clients, servers: drop support of SMP protocol versions before 01/2025 (#1837) * servers: drop support of versions before 01/2025 * fix agent tests * fix ntf tests * fix test * remove code for non-batched transmissions * fix batching test * fix ntf * comment --- src/Simplex/FileTransfer/Client.hs | 8 +- src/Simplex/FileTransfer/Server.hs | 2 +- src/Simplex/Messaging/Agent/Client.hs | 6 +- src/Simplex/Messaging/Client.hs | 29 +- .../Messaging/Notifications/Protocol.hs | 12 +- .../Messaging/Notifications/Transport.hs | 62 ++--- src/Simplex/Messaging/Protocol.hs | 72 ++--- src/Simplex/Messaging/Server.hs | 33 +-- src/Simplex/Messaging/Transport.hs | 144 +++------- tests/AgentTests/FunctionalAPITests.hs | 136 ++------- tests/AgentTests/NotificationTests.hs | 6 +- tests/CoreTests/BatchingTests.hs | 257 +++--------------- tests/NtfClient.hs | 20 +- tests/SMPAgentClient.hs | 5 +- tests/SMPClient.hs | 10 +- tests/SMPProxyTests.hs | 30 +- tests/ServerTests.hs | 17 +- 17 files changed, 199 insertions(+), 650 deletions(-) diff --git a/src/Simplex/FileTransfer/Client.hs b/src/Simplex/FileTransfer/Client.hs index 9d19d0492..952777798 100644 --- a/src/Simplex/FileTransfer/Client.hs +++ b/src/Simplex/FileTransfer/Client.hs @@ -109,12 +109,6 @@ data XFTPClientConfig = XFTPClientConfig clientALPN :: Maybe [ALPN] } -data XFTPChunkBody = XFTPChunkBody - { chunkSize :: Int, - chunkPart :: Int -> IO ByteString, - http2Body :: HTTP2Body - } - data XFTPChunkSpec = XFTPChunkSpec { filePath :: FilePath, chunkOffset :: Int64, @@ -147,7 +141,7 @@ getXFTPClient transportSession@(_, srv, _) config@XFTPClientConfig {clientALPN, let HTTP2Client {sessionId, sessionALPN} = http2Client v = VersionXFTP 1 thServerVRange = versionToRange v - thParams0 = THandleParams {sessionId, blockSize = xftpBlockSize, thVersion = v, thServerVRange, thAuth = Nothing, implySessId = False, encryptBlock = Nothing, batch = True, serviceAuth = False} + thParams0 = THandleParams {sessionId, blockSize = xftpBlockSize, thVersion = v, thServerVRange, thAuth = Nothing, implySessId = False, encryptBlock = Nothing, serviceAuth = False} logDebug $ "Client negotiated handshake protocol: " <> tshow sessionALPN thParams@THandleParams {thVersion} <- case sessionALPN of Just alpn diff --git a/src/Simplex/FileTransfer/Server.hs b/src/Simplex/FileTransfer/Server.hs index 3cd19e7c1..b04d5409d 100644 --- a/src/Simplex/FileTransfer/Server.hs +++ b/src/Simplex/FileTransfer/Server.hs @@ -157,7 +157,7 @@ xftpServer cfg@XFTPServerConfig {xftpPort, transportConfig, inactiveClientExpira reqBody <- getHTTP2Body r xftpBlockSize let v = VersionXFTP 1 thServerVRange = versionToRange v - thParams0 = THandleParams {sessionId, blockSize = xftpBlockSize, thVersion = v, thServerVRange, thAuth = Nothing, implySessId = False, encryptBlock = Nothing, batch = True, serviceAuth = False} + thParams0 = THandleParams {sessionId, blockSize = xftpBlockSize, thVersion = v, thServerVRange, thAuth = Nothing, implySessId = False, encryptBlock = Nothing, serviceAuth = False} req0 = XFTPTransportRequest {thParams = thParams0, request = r, reqBody, sendResponse, sniUsed, addCORS = addCORS'} flip runReaderT env $ case sessionALPN of Nothing -> processRequest req0 diff --git a/src/Simplex/Messaging/Agent/Client.hs b/src/Simplex/Messaging/Agent/Client.hs index bb4f1a950..37c9d61e9 100644 --- a/src/Simplex/Messaging/Agent/Client.hs +++ b/src/Simplex/Messaging/Agent/Client.hs @@ -308,7 +308,7 @@ import Simplex.Messaging.Session import Simplex.Messaging.SystemTime import Simplex.Messaging.TMap (TMap) import qualified Simplex.Messaging.TMap as TM -import Simplex.Messaging.Transport (HandshakeError (..), SMPServiceRole (..), SMPVersion, ServiceCredentials (..), SessionId, THClientService' (..), THandleAuth (..), THandleParams (sessionId, thAuth, thVersion), TransportError (..), TransportPeer (..), sndAuthKeySMPVersion, shortLinksSMPVersion, newNtfCredsSMPVersion) +import Simplex.Messaging.Transport (HandshakeError (..), SMPServiceRole (..), SMPVersion, ServiceCredentials (..), SessionId, THClientService' (..), THandleAuth (..), THandleParams (sessionId, thAuth, thVersion), TransportError (..), TransportPeer (..), shortLinksSMPVersion, newNtfCredsSMPVersion) import Simplex.Messaging.Transport.Client (TransportHost (..)) import Simplex.Messaging.Transport.Credentials import Simplex.Messaging.Util @@ -1504,9 +1504,7 @@ newRcvQueue_ c nm userId connId (ProtoServerWithAuth srv auth) vRange cqrd enabl if sndId == sndId' && lnkId == lnkId' then pure $ Just $ ShortLinkCreds lnkId linkKey privSigKey Nothing (fst d) else newErr "different sender or link IDs" - (_, Nothing) -> case linkId of - Nothing | v < sndAuthKeySMPVersion -> pure Nothing - _ -> newErr "unexpected link ID" + (_, Nothing) -> newErr "unexpected link ID" _ -> newErr "unexpected queue mode" where v = thVersion thParams' diff --git a/src/Simplex/Messaging/Client.hs b/src/Simplex/Messaging/Client.hs index dc811fa0b..e7a978075 100644 --- a/src/Simplex/Messaging/Client.hs +++ b/src/Simplex/Messaging/Client.hs @@ -227,9 +227,8 @@ smpClientStub g sessionId thVersion thAuth = do thServerVRange = supportedServerSMPRelayVRange, thAuth, blockSize = smpBlockSize, - implySessId = thVersion >= authCmdsSMPVersion, + implySessId = True, encryptBlock = Nothing, - batch = True, serviceAuth = thVersion >= serviceCertsSMPVersion }, sessionTs = ts, @@ -1110,17 +1109,15 @@ 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 - | thVersion (thParams c) >= sendingProxySMPVersion = - sendProtocolCommand_ c nm Nothing tOut Nothing NoEntity (Cmd SProxiedClient (PRXY relayServ proxyAuth)) >>= \case - PKEY sId vr (CertChainPubKey chain key) -> - case supportedClientSMPRelayVRange `compatibleVersion` vr of - Nothing -> throwE $ transportErr TEVersion - Just (Compatible v) -> do - relayKey <- liftEitherWith (const $ transportErr $ TEHandshake IDENTITY) =<< liftIO (runExceptT $ validateRelay chain key) - pure $ ProxiedRelay sId v proxyAuth relayKey - r -> throwE $ unexpectedResponse r - | otherwise = throwE $ PCETransportError TEVersion +connectSMPProxiedRelay c@ProtocolClient {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 + Nothing -> throwE $ transportErr TEVersion + Just (Compatible v) -> do + relayKey <- liftEitherWith (const $ transportErr $ TEHandshake IDENTITY) =<< liftIO (runExceptT $ validateRelay chain key) + pure $ ProxiedRelay sId v proxyAuth relayKey + r -> throwE $ unexpectedResponse r where tOut = Just $ netTimeoutInt tcpConnectTimeout nm + netTimeoutInt tcpTimeout nm transportErr = PCEProtocolError . PROXY . BROKER . TRANSPORT @@ -1351,7 +1348,7 @@ sendProtocolCommand c nm = sendProtocolCommand_ c nm Nothing Nothing -- -- Please note: if nonce is passed it is also used as a correlation ID sendProtocolCommand_ :: forall v err msg. Protocol v err msg => ProtocolClient v err msg -> NetworkRequestMode -> Maybe C.CbNonce -> Maybe Int -> Maybe C.APrivateAuthKey -> EntityId -> ProtoCommand msg -> ExceptT (ProtocolClientError err) IO msg -sendProtocolCommand_ c@ProtocolClient {client_ = PClient {sndQ}, thParams = THandleParams {batch, blockSize, serviceAuth}} nm nonce_ tOut pKey entId cmd = +sendProtocolCommand_ c@ProtocolClient {client_ = PClient {sndQ}, thParams = THandleParams {blockSize, serviceAuth}} nm nonce_ tOut pKey entId cmd = ExceptT $ uncurry sendRecv =<< mkTransmission_ c nonce_ (entId, pKey, cmd) where -- two separate "atomically" needed to avoid blocking @@ -1364,9 +1361,7 @@ sendProtocolCommand_ c@ProtocolClient {client_ = PClient {sndQ}, thParams = THan nonBlockingWriteTBQueue sndQ (Just r, s) response <$> getResponse c nm tOut r where - s - | batch = tEncodeBatch1 serviceAuth t - | otherwise = tEncode serviceAuth t + s = tEncodeBatch1 serviceAuth t nonBlockingWriteTBQueue :: TBQueue a -> a -> IO () nonBlockingWriteTBQueue q x = do diff --git a/src/Simplex/Messaging/Notifications/Protocol.hs b/src/Simplex/Messaging/Notifications/Protocol.hs index 45376a780..44f096f9b 100644 --- a/src/Simplex/Messaging/Notifications/Protocol.hs +++ b/src/Simplex/Messaging/Notifications/Protocol.hs @@ -61,7 +61,7 @@ import Simplex.Messaging.Agent.Store.DB (FromField (..), ToField (..), fromTextF import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Encoding import Simplex.Messaging.Encoding.String -import Simplex.Messaging.Notifications.Transport (NTFVersion, invalidReasonNTFVersion, ntfClientHandshake) +import Simplex.Messaging.Notifications.Transport (NTFVersion, ntfClientHandshake) import Simplex.Messaging.Protocol hiding (Command (..), CommandTag (..)) import Simplex.Messaging.Util (eitherToMaybe, (<$?>)) @@ -329,18 +329,12 @@ data NtfResponse instance ProtocolEncoding NTFVersion ErrorType NtfResponse where type Tag NtfResponse = NtfResponseTag - encodeProtocol v = \case + encodeProtocol _v = \case NRTknId entId dhKey -> e (NRTknId_, ' ', entId, dhKey) NRSubId entId -> e (NRSubId_, ' ', entId) NROk -> e NROk_ NRErr err -> e (NRErr_, ' ', err) - NRTkn stat -> e (NRTkn_, ' ', stat') - where - stat' - | v >= invalidReasonNTFVersion = stat - | otherwise = case stat of - NTInvalid _ -> NTInvalid Nothing - _ -> stat + NRTkn stat -> e (NRTkn_, ' ', stat) NRSub stat -> e (NRSub_, ' ', stat) NRPong -> e NRPong_ where diff --git a/src/Simplex/Messaging/Notifications/Transport.hs b/src/Simplex/Messaging/Notifications/Transport.hs index 3df1e53ea..6617ecba1 100644 --- a/src/Simplex/Messaging/Notifications/Transport.hs +++ b/src/Simplex/Messaging/Notifications/Transport.hs @@ -12,7 +12,6 @@ module Simplex.Messaging.Notifications.Transport VersionRangeNTF, pattern VersionNTF, THandleNTF, - invalidReasonNTFVersion, supportedClientNTFVRange, supportedServerNTFVRange, alpnSupportedNTFHandshakes, @@ -20,12 +19,8 @@ module Simplex.Messaging.Notifications.Transport ntfClientHandshake, ) where -import Control.Monad (forM) import Control.Monad.Except import Control.Monad.Trans.Except -import Data.Attoparsec.ByteString.Char8 (Parser) -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as B import Data.Word (Word16) import qualified Data.X509 as X import qualified Simplex.Messaging.Crypto as C @@ -50,13 +45,10 @@ pattern VersionNTF :: Word16 -> VersionNTF pattern VersionNTF v = Version v initialNTFVersion :: VersionNTF -initialNTFVersion = VersionNTF 1 +initialNTFVersion = VersionNTF 3 -authBatchCmdsNTFVersion :: VersionNTF -authBatchCmdsNTFVersion = VersionNTF 2 - -invalidReasonNTFVersion :: VersionNTF -invalidReasonNTFVersion = VersionNTF 3 +_invalidReasonNTFVersion :: VersionNTF +_invalidReasonNTFVersion = VersionNTF 3 currentClientNTFVersion :: VersionNTF currentClientNTFVersion = VersionNTF 3 @@ -67,9 +59,6 @@ currentServerNTFVersion = VersionNTF 3 supportedClientNTFVRange :: VersionRangeNTF supportedClientNTFVRange = mkVersionRange initialNTFVersion currentClientNTFVersion -legacyServerNTFVRange :: VersionRangeNTF -legacyServerNTFVRange = mkVersionRange initialNTFVersion initialNTFVersion - supportedServerNTFVRange :: VersionRangeNTF supportedServerNTFVRange = mkVersionRange initialNTFVersion currentServerNTFVersion @@ -82,7 +71,7 @@ data NtfServerHandshake = NtfServerHandshake { ntfVersionRange :: VersionRangeNTF, sessionId :: SessionId, -- pub key to agree shared secrets for command authorization and entity ID encryption. - authPubKey :: Maybe (X.SignedExact X.PubKey) + authPubKey :: X.SignedExact X.PubKey } data NtfClientHandshake = NtfClientHandshake @@ -94,25 +83,13 @@ data NtfClientHandshake = NtfClientHandshake instance Encoding NtfServerHandshake where smpEncode NtfServerHandshake {ntfVersionRange, sessionId, authPubKey} = - B.concat - [ smpEncode (ntfVersionRange, sessionId), - encodeAuthEncryptCmds (maxVersion ntfVersionRange) $ C.SignedObject <$> authPubKey - ] + smpEncode (ntfVersionRange, sessionId, C.SignedObject authPubKey) smpP = do (ntfVersionRange, sessionId) <- smpP - -- TODO drop SMP v6: remove special parser and make key non-optional - authPubKey <- authEncryptCmdsP (maxVersion ntfVersionRange) $ C.getSignedExact <$> smpP + authPubKey <- C.getSignedExact <$> smpP pure NtfServerHandshake {ntfVersionRange, sessionId, authPubKey} -encodeAuthEncryptCmds :: Encoding a => VersionNTF -> Maybe a -> ByteString -encodeAuthEncryptCmds v k - | v >= authBatchCmdsNTFVersion = maybe "" smpEncode k - | otherwise = "" - -authEncryptCmdsP :: VersionNTF -> Parser a -> Parser (Maybe a) -authEncryptCmdsP v p = if v >= authBatchCmdsNTFVersion then Just <$> p else pure Nothing - instance Encoding NtfClientHandshake where smpEncode NtfClientHandshake {ntfVersion, keyHash} = smpEncode (ntfVersion, keyHash) @@ -122,11 +99,10 @@ instance Encoding NtfClientHandshake where -- | Notifcations server transport handshake. ntfServerHandshake :: forall c. Transport c => C.APrivateSignKey -> c 'TServer -> C.KeyPairX25519 -> C.KeyHash -> VersionRangeNTF -> ExceptT TransportError IO (THandleNTF c 'TServer) -ntfServerHandshake serverSignKey c (k, pk) kh ntfVRange = do +ntfServerHandshake serverSignKey c (k, pk) kh ntfVersionRange = do let th@THandle {params = THandleParams {sessionId}} = ntfTHandle c - let sk = C.signX509 serverSignKey $ C.publicToX509 k - let ntfVersionRange = maybe legacyServerNTFVRange (const ntfVRange) $ getSessionALPN c - sendHandshake th $ NtfServerHandshake {sessionId, ntfVersionRange, authPubKey = Just sk} + authPubKey = C.signX509 serverSignKey $ C.publicToX509 k + sendHandshake th $ NtfServerHandshake {sessionId, ntfVersionRange, authPubKey} getHandshake th >>= \case NtfClientHandshake {ntfVersion = v, keyHash} | keyHash /= kh -> @@ -140,18 +116,18 @@ ntfServerHandshake serverSignKey c (k, pk) kh ntfVRange = do ntfClientHandshake :: forall c. Transport c => c 'TClient -> C.KeyHash -> VersionRangeNTF -> Bool -> Maybe (ServiceCredentials, C.KeyPairEd25519) -> ExceptT TransportError IO (THandleNTF c 'TClient) ntfClientHandshake c keyHash ntfVRange _proxyServer _serviceKeys = do let th@THandle {params = THandleParams {sessionId}} = ntfTHandle c - NtfServerHandshake {sessionId = sessId, ntfVersionRange, authPubKey = sk'} <- getHandshake th + NtfServerHandshake {sessionId = sessId, ntfVersionRange, authPubKey} <- getHandshake th if sessionId /= sessId then throwE TEBadSession else case ntfVersionRange `compatibleVRange` ntfVRange of Just (Compatible vr) -> do - ck_ <- forM sk' $ \signedKey -> liftEitherWith (const $ TEHandshake BAD_AUTH) $ do + ck <- liftEitherWith (const $ TEHandshake BAD_AUTH) $ do serverKey <- getServerVerifyKey c - pubKey <- C.verifyX509 serverKey signedKey - (,CertChainPubKey (getPeerCertChain c) signedKey) <$> C.x509ToPublic' pubKey + pubKey <- C.verifyX509 serverKey authPubKey + (,CertChainPubKey (getPeerCertChain c) authPubKey) <$> C.x509ToPublic' pubKey let v = maxVersion vr sendHandshake th $ NtfClientHandshake {ntfVersion = v, keyHash} - pure $ ntfThHandleClient th v vr ck_ + pure $ ntfThHandleClient th v vr ck Nothing -> throwE TEVersion ntfThHandleServer :: forall c. THandleNTF c 'TServer -> VersionNTF -> VersionRangeNTF -> C.PrivateKeyX25519 -> THandleNTF c 'TServer @@ -159,17 +135,16 @@ ntfThHandleServer th v vr pk = let thAuth = THAuthServer {serverPrivKey = pk, peerClientService = Nothing, sessSecret' = Nothing} in ntfThHandle_ th v vr (Just thAuth) -ntfThHandleClient :: forall c. THandleNTF c 'TClient -> VersionNTF -> VersionRangeNTF -> Maybe (C.PublicKeyX25519, CertChainPubKey) -> THandleNTF c 'TClient +ntfThHandleClient :: forall c. THandleNTF c 'TClient -> VersionNTF -> VersionRangeNTF -> (C.PublicKeyX25519, CertChainPubKey) -> THandleNTF c 'TClient ntfThHandleClient th v vr ck_ = - let thAuth = clientTHParams <$> ck_ + let thAuth = Just $ clientTHParams ck_ clientTHParams (k, ck) = THAuthClient {peerServerPubKey = k, peerServerCertKey = ck, clientService = Nothing, sessSecret = Nothing} in ntfThHandle_ th v vr thAuth ntfThHandle_ :: forall c p. THandleNTF c p -> VersionNTF -> VersionRangeNTF -> Maybe (THandleAuth p) -> THandleNTF c p ntfThHandle_ th@THandle {params} v vr thAuth = -- TODO drop SMP v6: make thAuth non-optional - let v3 = v >= authBatchCmdsNTFVersion - params' = params {thVersion = v, thServerVRange = vr, thAuth, implySessId = v3, batch = v3} + let params' = params {thVersion = v, thServerVRange = vr, thAuth} in (th :: THandleNTF c p) {params = params'} ntfTHandle :: Transport c => c p -> THandleNTF c p @@ -183,8 +158,7 @@ ntfTHandle c = THandle {connection = c, params} thVersion = v, thServerVRange = versionToRange v, thAuth = Nothing, - implySessId = False, + implySessId = True, encryptBlock = Nothing, - batch = False, serviceAuth = False } diff --git a/src/Simplex/Messaging/Protocol.hs b/src/Simplex/Messaging/Protocol.hs index 8730597f3..a28d78fe6 100644 --- a/src/Simplex/Messaging/Protocol.hs +++ b/src/Simplex/Messaging/Protocol.hs @@ -312,18 +312,13 @@ currentSMPClientVersion = VersionSMPC 4 supportedSMPClientVRange :: VersionRangeSMPC supportedSMPClientVRange = mkVersionRange initialSMPClientVersion currentSMPClientVersion --- TODO v6.0 remove dependency on version -maxMessageLength :: VersionSMP -> Int -maxMessageLength v - | v >= encryptedBlockSMPVersion = 16048 -- max 16048 - | v >= sendingProxySMPVersion = 16064 -- max 16067 - | otherwise = 16088 -- 16048 - always use this size to determine allowed ranges +maxMessageLength :: Int +maxMessageLength = 16048 -- max 16048 paddedProxiedTLength :: Int paddedProxiedTLength = 16226 -- 16225 .. 16227 --- TODO v7.0 change to 16048 -type MaxMessageLen = 16088 +type MaxMessageLen = 16048 -- 16 extra bytes: 8 for timestamp and 8 for flags (7 flags and the space, only 1 flag is currently used) type MaxRcvMessageLen = MaxMessageLen + 16 -- 16104, the padded size is 16106 @@ -1582,7 +1577,7 @@ data ErrorType STORE {storeErr :: Text} | -- | ACK command is sent without message to be acknowledged NO_MSG - | -- | sent message is too large (> maxMessageLength = 16088 bytes) + | -- | sent message is too large (> maxMessageLength = 16048 bytes) LARGE_MSG | -- | relay public key is expired EXPIRED @@ -1795,13 +1790,11 @@ instance PartyI p => ProtocolEncoding SMPVersion ErrorType (Command p) where type Tag (Command p) = CommandTag p encodeProtocol v = \case NEW NewQueueReq {rcvAuthKey = rKey, rcvDhKey = dhKey, auth_, subMode, queueReqData, ntfCreds} - | v >= newNtfCredsSMPVersion -> new <> e (auth_, subMode, queueReqData, ntfCreds) - | v >= shortLinksSMPVersion -> new <> e (auth_, subMode, queueReqData) - | v >= sndAuthKeySMPVersion -> new <> e (auth_, subMode, senderCanSecure (queueReqMode <$> queueReqData)) - | otherwise -> new <> auth <> e subMode + | v >= newNtfCredsSMPVersion -> new <> e (subMode, queueReqData, ntfCreds) + | v >= shortLinksSMPVersion -> new <> e (subMode, queueReqData) + | otherwise -> new <> e (subMode, senderCanSecure (queueReqMode <$> queueReqData)) where - new = e (NEW_, ' ', rKey, dhKey) - auth = maybe "" (e . ('A',)) auth_ + new = e (NEW_, ' ', rKey, dhKey, auth_) SUB -> e SUB_ SUBS n idsHash | v >= rcvServiceSMPVersion -> e (SUBS_, ' ', n, idsHash) @@ -1886,21 +1879,19 @@ instance ProtocolEncoding SMPVersion ErrorType Cmd where CT SCreator NEW_ -> Cmd SCreator <$> newCmd where newCmd - | v >= newNtfCredsSMPVersion = new smpP smpP smpP - | v >= shortLinksSMPVersion = new smpP smpP nothing - | v >= sndAuthKeySMPVersion = new smpP (qReq <$> smpP) nothing - | otherwise = new auth nothing nothing + | v >= newNtfCredsSMPVersion = new smpP smpP + | v >= shortLinksSMPVersion = new smpP nothing + | otherwise = new (qReq <$> smpP) nothing where nothing = pure Nothing - new p1 p2 p3 = NEW <$> do + new p2 p3 = NEW <$> do rcvAuthKey <- _smpP rcvDhKey <- smpP - auth_ <- p1 + auth_ <- smpP subMode <- smpP queueReqData <- p2 ntfCreds <- p3 pure NewQueueReq {rcvAuthKey, rcvDhKey, auth_, subMode, queueReqData, ntfCreds} - auth = optional (A.char 'A' *> smpP) qReq sndSecure = Just $ if sndSecure then QRMessaging Nothing else QRContact Nothing CT SRecipient tag -> Cmd SRecipient <$> case tag of @@ -1950,11 +1941,10 @@ instance ProtocolEncoding SMPVersion ErrorType BrokerMsg where type Tag BrokerMsg = BrokerMsgTag encodeProtocol v = \case IDS QIK {rcvId, sndId, rcvPublicDhKey = srvDh, queueMode, linkId, serviceId, serverNtfCreds} - | v >= newNtfCredsSMPVersion -> ids <> e queueMode <> e linkId <> e serviceId <> e serverNtfCreds - | v >= serviceCertsSMPVersion -> ids <> e queueMode <> e linkId <> e serviceId - | v >= shortLinksSMPVersion -> ids <> e queueMode <> e linkId - | v >= sndAuthKeySMPVersion -> ids <> e (senderCanSecure queueMode) - | otherwise -> ids + | v >= newNtfCredsSMPVersion -> ids <> e (queueMode, linkId, serviceId, serverNtfCreds) + | v >= serviceCertsSMPVersion -> ids <> e (queueMode, linkId, serviceId) + | v >= shortLinksSMPVersion -> ids <> e (queueMode, linkId) + | otherwise -> ids <> e (senderCanSecure queueMode) where ids = e (IDS_, ' ', rcvId, sndId, srvDh) LNK sId d -> e (LNK_, ' ', sId, d) @@ -1972,16 +1962,13 @@ instance ProtocolEncoding SMPVersion ErrorType BrokerMsg where PRES (EncResponse encBlock) -> e (PRES_, ' ', Tail encBlock) END -> e END_ ENDS n idsHash -> serviceResp ENDS_ n idsHash - DELD - | v >= deletedEventSMPVersion -> e DELD_ - | otherwise -> e END_ + DELD -> e DELD_ INFO info -> e (INFO_, ' ', info) OK -> e OK_ ERR err -> e (ERR_, ' ', err') where err' = case err of BLOCKED info - | v < blockedEntitySMPVersion -> AUTH | v < clientNoticesSMPVersion -> BLOCKED info {notice = Nothing} _ -> err PONG -> e PONG_ @@ -2004,8 +1991,7 @@ instance ProtocolEncoding SMPVersion ErrorType BrokerMsg where | v >= newNtfCredsSMPVersion -> ids smpP smpP smpP smpP | v >= serviceCertsSMPVersion -> ids smpP smpP smpP nothing | v >= shortLinksSMPVersion -> ids smpP smpP nothing nothing - | v >= sndAuthKeySMPVersion -> ids (qm <$> smpP) nothing nothing nothing - | otherwise -> ids nothing nothing nothing nothing + | otherwise -> ids (qm <$> smpP) nothing nothing nothing where qm sndSecure = Just $ if sndSecure then QMMessaging else QMContact nothing = pure Nothing @@ -2290,19 +2276,8 @@ batchTransmissions params = batchTransmissions' params . L.map (,()) -- | encodes and batches transmissions into blocks batchTransmissions' :: forall v p r. THandleParams v p -> NonEmpty (Either TransportError SentRawTransmission, r) -> [TransportBatch r] -batchTransmissions' THandleParams {batch, blockSize = bSize, serviceAuth} ts - | batch = batchTransmissions_ bSize $ L.map (first $ fmap $ tEncodeForBatch serviceAuth) ts - | otherwise = map mkBatch1 $ L.toList ts - where - mkBatch1 :: (Either TransportError SentRawTransmission, r) -> TransportBatch r - mkBatch1 (t_, r) = case t_ of - Left e -> TBError e r - Right t - -- 2 bytes are reserved for pad size - | B.length s <= bSize - 2 -> TBTransmission s r - | otherwise -> TBError TELargeMsg r - where - s = tEncode serviceAuth t +batchTransmissions' THandleParams {blockSize, serviceAuth} ts = + batchTransmissions_ blockSize $ L.map (first $ fmap $ tEncodeForBatch serviceAuth) ts -- | Pack encoded transmissions into batches batchTransmissions_ :: Int -> NonEmpty (Either TransportError ByteString, r) -> [TransportBatch r] @@ -2366,9 +2341,8 @@ tGetParse th@THandle {params} = eitherList (tParse params) <$> tGetBlock th {-# INLINE tGetParse #-} tParse :: THandleParams v p -> ByteString -> NonEmpty (Either TransportError RawTransmission) -tParse thParams@THandleParams {batch} s - | batch = eitherList (L.map (\(Large t) -> tParse1 t)) ts - | otherwise = [tParse1 s] +tParse thParams s = + eitherList (L.map (tParse1 . unLarge)) ts where tParse1 = parse (transmissionP thParams) TEBadBlock ts = parse smpP TEBadBlock s diff --git a/src/Simplex/Messaging/Server.hs b/src/Simplex/Messaging/Server.hs index 36ac471f9..617d43a36 100644 --- a/src/Simplex/Messaging/Server.hs +++ b/src/Simplex/Messaging/Server.hs @@ -1377,11 +1377,10 @@ client ms clnt@Client {clientId, rcvQ, sndQ, msgQ, clientTHParams = thParams'@THandleParams {sessionId}, procThreads} = do labelMyThread . B.unpack $ "client $" <> encode sessionId <> " commands" - let THandleParams {thVersion} = thParams' - clntServiceId = (\THClientService {serviceId} -> serviceId) <$> (peerClientService =<< thAuth thParams') + let clntServiceId = (\THClientService {serviceId} -> serviceId) <$> (peerClientService =<< thAuth thParams') process batchSubs t acc@(rs, msgs) = (maybe acc (\(!r, !msg_) -> (r : rs, maybe msgs (: msgs) msg_))) - <$> processCommand clntServiceId thVersion batchSubs t + <$> processCommand clntServiceId batchSubs t forever $ do batch <- atomically (readTBQueue rcvQ) batchSubs <- prepareBatchSubs clntServiceId batch @@ -1444,11 +1443,11 @@ client pure . ERR $ smpProxyError e where proxyResp smp = - let THandleParams {sessionId = srvSessId, thVersion, thServerVRange, thAuth} = thParams smp + let THandleParams {sessionId = srvSessId, thServerVRange, thAuth} = thParams smp in case compatibleVRange thServerVRange proxiedSMPRelayVRange of -- Cap the destination relay version range to prevent client version fingerprinting. -- See comment for proxiedSMPRelayVersion. - Just (Compatible vr) | thVersion >= sendingProxySMPVersion -> case thAuth of + Just (Compatible vr) -> case thAuth of Just THAuthClient {peerServerCertKey} -> PKEY srvSessId vr peerServerCertKey Nothing -> ERR $ transportErr TENoServerAuth _ -> ERR $ transportErr TEVersion @@ -1459,16 +1458,12 @@ client liftIO (lookupSMPServerClient a sessId) >>= \case Just (own, smp) -> do inc own pRequests - if v >= sendingProxySMPVersion - then forkProxiedCmd $ do - liftIO (runExceptT (forwardSMPTransmission smp corrId fwdV pubKey encBlock) `E.catches` clientHandlers) >>= \case - Right r -> PRES r <$ inc own pSuccesses - Left e -> ERR (smpProxyError e) <$ case e of - PCEProtocolError {} -> inc own pSuccesses - _ -> inc own pErrorsOther - else Just (ERR $ transportErr TEVersion) <$ inc own pErrorsCompat - where - THandleParams {thVersion = v} = thParams smp + forkProxiedCmd $ do + liftIO (runExceptT (forwardSMPTransmission smp corrId fwdV pubKey encBlock) `E.catches` clientHandlers) >>= \case + Right r -> PRES r <$ inc own pSuccesses + Left e -> ERR (smpProxyError e) <$ case e of + PCEProtocolError {} -> inc own pSuccesses + _ -> inc own pErrorsOther Nothing -> inc False pRequests >> inc False pErrorsConnect $> Just (ERR $ PROXY NO_SESSION) where forkProxiedCmd :: M s BrokerMsg -> M s (Maybe BrokerMsg) @@ -1512,8 +1507,8 @@ client mkIncProxyStats ps psOwn own sel = do incStat $ sel ps when own $ incStat $ sel psOwn - processCommand :: Maybe ServiceId -> VersionSMP -> Either ErrorType (Map RecipientId Message, Map RecipientId (Either ErrorType ()), Map RecipientId (Either ErrorType ())) -> VerifiedTransmission s -> M s (Maybe ResponseAndMessage) - processCommand clntServiceId clntVersion batchSubs (q_, (corrId, entId, cmd)) = case cmd of + processCommand :: Maybe ServiceId -> Either ErrorType (Map RecipientId Message, Map RecipientId (Either ErrorType ()), Map RecipientId (Either ErrorType ())) -> VerifiedTransmission s -> M s (Maybe ResponseAndMessage) + processCommand clntServiceId batchSubs (q_, (corrId, entId, cmd)) = case cmd of Cmd SProxiedClient command -> processProxiedCmd (corrId, entId, command) Cmd SSender command -> case command of SKEY k -> withQueue $ \q qr -> checkMode QMMessaging qr $ secureQueue_ q k @@ -1980,7 +1975,7 @@ client sendMessage :: MsgFlags -> MsgBody -> StoreQueue s -> QueueRec -> M s (Transmission BrokerMsg) sendMessage msgFlags msgBody q qr - | B.length msgBody > maxMessageLength clntVersion = do + | B.length msgBody > maxMessageLength = do stats <- asks serverStats incStat $ msgSentLarge stats pure $ err LARGE_MSG @@ -2157,7 +2152,7 @@ client either ERR id <$> runExceptT (encodeResp (corrId', entId', msg)) -- INTERNAL because processCommand never returns Nothing for sender commands; -- `fst` drops the empty message only returned for SUB. - _ -> Just . maybe (corrId', entId', ERR INTERNAL) fst <$> lift (processCommand Nothing fwdVersion (Right (M.empty, M.empty, M.empty)) t'') + _ -> Just . maybe (corrId', entId', ERR INTERNAL) fst <$> lift (processCommand Nothing (Right (M.empty, M.empty, M.empty)) t'') stats <- asks serverStats incStat $ pMsgFwdsRecv stats traverse encodeResp r_ diff --git a/src/Simplex/Messaging/Transport.hs b/src/Simplex/Messaging/Transport.hs index 51a2955eb..a899b0ad4 100644 --- a/src/Simplex/Messaging/Transport.hs +++ b/src/Simplex/Messaging/Transport.hs @@ -46,12 +46,6 @@ module Simplex.Messaging.Transport minServerSMPRelayVersion, currentClientSMPRelayVersion, currentServerSMPRelayVersion, - authCmdsSMPVersion, - sendingProxySMPVersion, - sndAuthKeySMPVersion, - deletedEventSMPVersion, - encryptedBlockSMPVersion, - blockedEntitySMPVersion, shortLinksSMPVersion, serviceCertsSMPVersion, newNtfCredsSMPVersion, @@ -115,7 +109,6 @@ import Control.Monad.Except import Control.Monad.IO.Class import Control.Monad.Trans.Except (throwE) import qualified Data.Aeson.TH as J -import Data.Attoparsec.ByteString.Char8 (Parser) import qualified Data.Attoparsec.ByteString.Char8 as A import Data.Bifunctor (first) import Data.Bitraversable (bimapM) @@ -185,29 +178,8 @@ type VersionRangeSMP = VersionRange SMPVersion pattern VersionSMP :: Word16 -> VersionSMP pattern VersionSMP v = Version v -_subModeSMPVersion :: VersionSMP -_subModeSMPVersion = VersionSMP 6 - -authCmdsSMPVersion :: VersionSMP -authCmdsSMPVersion = VersionSMP 7 - -sendingProxySMPVersion :: VersionSMP -sendingProxySMPVersion = VersionSMP 8 - -sndAuthKeySMPVersion :: VersionSMP -sndAuthKeySMPVersion = VersionSMP 9 - -deletedEventSMPVersion :: VersionSMP -deletedEventSMPVersion = VersionSMP 10 - -encryptedBlockSMPVersion :: VersionSMP -encryptedBlockSMPVersion = VersionSMP 11 - -blockedEntitySMPVersion :: VersionSMP -blockedEntitySMPVersion = VersionSMP 12 - -proxyServerHandshakeSMPVersion :: VersionSMP -proxyServerHandshakeSMPVersion = VersionSMP 14 +_proxyServerHandshakeSMPVersion :: VersionSMP +_proxyServerHandshakeSMPVersion = VersionSMP 14 shortLinksSMPVersion :: VersionSMP shortLinksSMPVersion = VersionSMP 15 @@ -228,17 +200,14 @@ namesSMPVersion :: VersionSMP namesSMPVersion = VersionSMP 20 minClientSMPRelayVersion :: VersionSMP -minClientSMPRelayVersion = VersionSMP 6 +minClientSMPRelayVersion = VersionSMP 14 minServerSMPRelayVersion :: VersionSMP -minServerSMPRelayVersion = VersionSMP 6 +minServerSMPRelayVersion = VersionSMP 14 currentClientSMPRelayVersion :: VersionSMP currentClientSMPRelayVersion = VersionSMP 20 -legacyServerSMPRelayVersion :: VersionSMP -legacyServerSMPRelayVersion = VersionSMP 6 - currentServerSMPRelayVersion :: VersionSMP currentServerSMPRelayVersion = VersionSMP 20 @@ -252,14 +221,10 @@ currentServerSMPRelayVersion = VersionSMP 20 proxiedSMPRelayVersion :: VersionSMP proxiedSMPRelayVersion = VersionSMP 20 --- minimal supported protocol version is 6 --- TODO remove code that supports sending commands without batching +-- minimal supported protocol version is 14 supportedClientSMPRelayVRange :: VersionRangeSMP supportedClientSMPRelayVRange = mkVersionRange minClientSMPRelayVersion currentClientSMPRelayVersion -legacyServerSMPRelayVRange :: VersionRangeSMP -legacyServerSMPRelayVRange = mkVersionRange minServerSMPRelayVersion legacyServerSMPRelayVersion - supportedServerSMPRelayVRange :: VersionRangeSMP supportedServerSMPRelayVRange = mkVersionRange minServerSMPRelayVersion currentServerSMPRelayVersion @@ -267,7 +232,7 @@ supportedProxyClientSMPRelayVRange :: VersionRangeSMP supportedProxyClientSMPRelayVRange = mkVersionRange minServerSMPRelayVersion currentServerSMPRelayVersion proxiedSMPRelayVRange :: VersionRangeSMP -proxiedSMPRelayVRange = mkVersionRange sendingProxySMPVersion proxiedSMPRelayVersion +proxiedSMPRelayVRange = mkVersionRange minServerSMPRelayVersion proxiedSMPRelayVersion alpnSupportedSMPHandshakes :: [ALPN] alpnSupportedSMPHandshakes = ["smp/1"] @@ -489,12 +454,10 @@ data THandleParams v p = THandleParams thAuth :: Maybe (THandleAuth p), -- | do NOT send session ID in transmission, but include it into signed message -- based on protocol version + -- This is True for SMP and NTF servers, and False for XFTP implySessId :: Bool, -- | keys for additional transport encryption encryptBlock :: Maybe TSbChainKeys, - -- | send multiple transmissions in a single block - -- based on protocol version - batch :: Bool, -- | include service signature (or '0' if it is absent), based on protocol version serviceAuth :: Bool } @@ -548,7 +511,7 @@ data SMPServerHandshake = SMPServerHandshake sessionId :: SessionId, -- pub key to agree shared secrets for command authorization and entity ID encryption. -- todo C.PublicKeyX25519 - authPubKey :: Maybe CertChainPubKey + authPubKey :: CertChainPubKey } -- This is the third handshake message that SMP server sends to services @@ -602,14 +565,13 @@ data SMPServiceRole = SRMessaging | SRNotifier | SRProxy deriving (Eq, Show) instance Encoding SMPClientHandshake where smpEncode SMPClientHandshake {smpVersion = v, keyHash, authPubKey, proxyServer, clientService} = smpEncode (v, keyHash) - <> encodeAuthEncryptCmds v authPubKey - <> ifHasProxy v (smpEncode proxyServer) "" + <> maybe "" smpEncode authPubKey + <> smpEncode proxyServer <> ifHasService v (smpEncode clientService) "" smpP = do (v, keyHash) <- smpP - -- TODO drop SMP v6: remove special parser and make key non-optional - authPubKey <- authEncryptCmdsP v smpP - proxyServer <- ifHasProxy v smpP (pure False) + authPubKey <- optional smpP + proxyServer <- smpP clientService <- ifHasService v smpP (pure Nothing) pure SMPClientHandshake {smpVersion = v, keyHash, authPubKey, proxyServer, clientService} @@ -632,21 +594,14 @@ instance Encoding SMPServiceRole where 'P' -> pure SRProxy _ -> fail "bad SMPServiceRole" -ifHasProxy :: VersionSMP -> a -> a -> a -ifHasProxy v a b = if v >= proxyServerHandshakeSMPVersion then a else b - ifHasService :: VersionSMP -> a -> a -> a ifHasService v a b = if v >= serviceCertsSMPVersion then a else b instance Encoding SMPServerHandshake where smpEncode SMPServerHandshake {smpVersionRange, sessionId, authPubKey} = - smpEncode (smpVersionRange, sessionId) <> auth - where - auth = encodeAuthEncryptCmds (maxVersion smpVersionRange) authPubKey + smpEncode (smpVersionRange, sessionId, authPubKey) smpP = do - (smpVersionRange, sessionId) <- smpP - -- TODO drop SMP v6: remove special parser and make key non-optional - authPubKey <- authEncryptCmdsP (maxVersion smpVersionRange) smpP + (smpVersionRange, sessionId, authPubKey) <- smpP pure SMPServerHandshake {smpVersionRange, sessionId, authPubKey} -- newtype for CertificateChain and a session key signed with this certificate @@ -663,14 +618,6 @@ instance Encoding CertChainPubKey where C.SignedObject signedPubKey <- smpP pure CertChainPubKey {certChain, signedPubKey} -encodeAuthEncryptCmds :: Encoding a => VersionSMP -> Maybe a -> ByteString -encodeAuthEncryptCmds v k - | v >= authCmdsSMPVersion = maybe "" smpEncode k - | otherwise = "" - -authEncryptCmdsP :: VersionSMP -> Parser a -> Parser (Maybe a) -authEncryptCmdsP v p = if v >= authCmdsSMPVersion then optional p else pure Nothing - instance Encoding SMPServerHandshakeResponse where smpEncode = \case SMPServerHandshakeResponse serviceId -> smpEncode ('R', serviceId) @@ -765,10 +712,9 @@ smpServerHandshake :: VersionRangeSMP -> (SMPServiceRole -> X.CertificateChain -> XV.Fingerprint -> ExceptT TransportError IO ServiceId) -> ExceptT TransportError IO (THandleSMP c 'TServer) -smpServerHandshake srvCert srvSignKey c (k, pk) kh smpVRange getService = do +smpServerHandshake srvCert srvSignKey c (k, pk) kh smpVersionRange getService = do let sk = C.signX509 srvSignKey $ C.publicToX509 k - smpVersionRange = maybe legacyServerSMPRelayVRange (const smpVRange) $ getSessionALPN c - sendHandshake th $ SMPServerHandshake {sessionId, smpVersionRange, authPubKey = Just (CertChainPubKey srvCert sk)} + sendHandshake th $ SMPServerHandshake {sessionId, smpVersionRange, authPubKey = CertChainPubKey srvCert sk} SMPClientHandshake {smpVersion = v, keyHash, authPubKey = k', proxyServer, clientService} <- getHandshake th when (keyHash /= kh) $ throwE $ TEHandshake IDENTITY case compatibleVRange' smpVersionRange v of @@ -800,35 +746,17 @@ smpServerHandshake srvCert srvSignKey c (k, pk) kh smpVRange getService = do -- -- See https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#appendix-a smpClientHandshake :: forall c. Transport c => c 'TClient -> Maybe C.KeyPairX25519 -> C.KeyHash -> VersionRangeSMP -> Bool -> Maybe (ServiceCredentials, C.KeyPairEd25519) -> ExceptT TransportError IO (THandleSMP c 'TClient) -smpClientHandshake c ks_ keyHash@(C.KeyHash kh) vRange proxyServer serviceKeys_ = do - SMPServerHandshake {sessionId = sessId, smpVersionRange, authPubKey} <- getHandshake th +smpClientHandshake c ks_ keyHash@(C.KeyHash kh) smpVRange proxyServer serviceKeys_ = do + SMPServerHandshake {sessionId = sessId, smpVersionRange, authPubKey = certKey@(CertChainPubKey chain exact)} <- getHandshake th when (sessionId /= sessId) $ throwE TEBadSession - -- Below logic downgrades version range in case the "client" is SMP proxy server and it is - -- connected to the destination server of the version 11 or older. - -- It disables transport encryption between SMP proxy and destination relay. - -- - -- Prior to version v6.3 the version between proxy and destination was capped at 8, - -- by mistake, which also disables transport encryption and the latest features. - -- - -- Transport encryption between proxy and destination breaks clients with version 10 or earlier, - -- because of a larger message size (see maxMessageLength). - -- - -- To summarize: - -- - proxy and relay version 12: the agreed version is 12, transport encryption disabled (see blockEncryption with proxyServer == True). - -- - proxy is v 12, relay is 11: the agreed version is 10, because of this logic, transport encryption is disabled. - let smpVRange = - if proxyServer && maxVersion smpVersionRange < proxyServerHandshakeSMPVersion - then vRange {maxVersion = max (minVersion vRange) deletedEventSMPVersion} - else vRange case smpVersionRange `compatibleVRange` smpVRange of Just (Compatible vr) -> do - ck_ <- forM authPubKey $ \certKey@(CertChainPubKey chain exact) -> - liftEitherWith (const $ TEHandshake BAD_AUTH) $ do - case chainIdCaCerts chain of - CCValid {idCert} | XV.Fingerprint kh == XV.getFingerprint idCert X.HashSHA256 -> pure () - _ -> throwError "bad certificate" - serverKey <- getServerVerifyKey c - (,certKey) <$> (C.x509ToPublic' =<< C.verifyX509 serverKey exact) + ck <- liftEitherWith (const $ TEHandshake BAD_AUTH) $ do + case chainIdCaCerts chain of + CCValid {idCert} | XV.Fingerprint kh == XV.getFingerprint idCert X.HashSHA256 -> pure () + _ -> throwError "bad certificate" + serverKey <- getServerVerifyKey c + (,certKey) <$> (C.x509ToPublic' =<< C.verifyX509 serverKey exact) let v = maxVersion vr serviceVersion ServiceCredentials {serviceRole} = if serviceRole == SRMessaging then rcvServiceSMPVersion else serviceCertsSMPVersion serviceKeys = case serviceKeys_ of @@ -838,7 +766,7 @@ smpClientHandshake c ks_ keyHash@(C.KeyHash kh) vRange proxyServer serviceKeys_ hs = SMPClientHandshake {smpVersion = v, keyHash, authPubKey = fst <$> ks_, proxyServer, clientService} sendHandshake th hs service <- mapM getClientService serviceKeys - liftIO $ smpTHandleClient th v vr (snd <$> ks_) ck_ proxyServer service + liftIO $ smpTHandleClient th v vr (snd <$> ks_) ck proxyServer service Nothing -> throwE TEVersion where th@THandle {params = THandleParams {sessionId}} = smpTHandle c @@ -855,17 +783,17 @@ smpClientHandshake c ks_ keyHash@(C.KeyHash kh) vRange proxyServer serviceKeys_ smpTHandleServer :: forall c. THandleSMP c 'TServer -> VersionSMP -> VersionRangeSMP -> C.PrivateKeyX25519 -> Maybe C.PublicKeyX25519 -> Bool -> Maybe THPeerClientService -> IO (THandleSMP c 'TServer) smpTHandleServer th v vr pk k_ proxyServer peerClientService = do let thAuth = Just THAuthServer {serverPrivKey = pk, peerClientService, sessSecret' = (`C.dh'` pk) <$!> k_} - be <- blockEncryption th v proxyServer thAuth + be <- blockEncryption th proxyServer thAuth pure $ smpTHandle_ th v vr thAuth $ uncurry TSbChainKeys <$> be -smpTHandleClient :: forall c. THandleSMP c 'TClient -> VersionSMP -> VersionRangeSMP -> Maybe C.PrivateKeyX25519 -> Maybe (C.PublicKeyX25519, CertChainPubKey) -> Bool -> Maybe THClientService -> IO (THandleSMP c 'TClient) -smpTHandleClient th v vr pk_ ck_ proxyServer clientService = do - let thAuth = clientTHParams <$!> ck_ - be <- blockEncryption th v proxyServer thAuth +smpTHandleClient :: forall c. THandleSMP c 'TClient -> VersionSMP -> VersionRangeSMP -> Maybe C.PrivateKeyX25519 -> (C.PublicKeyX25519, CertChainPubKey) -> Bool -> Maybe THClientService -> IO (THandleSMP c 'TClient) +smpTHandleClient th v vr pk_ (k, ck) proxyServer clientService = do + let thAuth = Just $! clientTHParams + be <- blockEncryption th proxyServer thAuth -- swap is needed to use client's sndKey as server's rcvKey and vice versa pure $ smpTHandle_ th v vr thAuth $ uncurry TSbChainKeys . swap <$> be where - clientTHParams (k, ck) = + clientTHParams = THAuthClient { peerServerPubKey = k, peerServerCertKey = forceCertChain ck, @@ -873,9 +801,9 @@ smpTHandleClient th v vr pk_ ck_ proxyServer clientService = do sessSecret = C.dh' k <$!> pk_ } -blockEncryption :: THandleSMP c p -> VersionSMP -> Bool -> Maybe (THandleAuth p) -> IO (Maybe (TVar C.SbChainKey, TVar C.SbChainKey)) -blockEncryption THandle {params = THandleParams {sessionId}} v proxyServer = \case - Just thAuth | not proxyServer && v >= encryptedBlockSMPVersion -> case thAuth of +blockEncryption :: THandleSMP c p -> Bool -> Maybe (THandleAuth p) -> IO (Maybe (TVar C.SbChainKey, TVar C.SbChainKey)) +blockEncryption THandle {params = THandleParams {sessionId}} proxyServer = \case + Just thAuth | not proxyServer -> case thAuth of THAuthClient {sessSecret} -> be sessSecret THAuthServer {sessSecret'} -> be sessSecret' _ -> pure Nothing @@ -892,7 +820,6 @@ smpTHandle_ th@THandle {params} v vr thAuth encryptBlock = { thVersion = v, thServerVRange = vr, thAuth, - implySessId = v >= authCmdsSMPVersion, encryptBlock, serviceAuth = v >= serviceCertsSMPVersion -- optional service signature will be encoded for all commands and responses } @@ -930,9 +857,8 @@ smpTHandle c = THandle {connection = c, params} thServerVRange = versionToRange v, thVersion = v, thAuth = Nothing, - implySessId = False, + implySessId = True, encryptBlock = Nothing, - batch = True, serviceAuth = False } diff --git a/tests/AgentTests/FunctionalAPITests.hs b/tests/AgentTests/FunctionalAPITests.hs index 7b95c71bf..30401143a 100644 --- a/tests/AgentTests/FunctionalAPITests.hs +++ b/tests/AgentTests/FunctionalAPITests.hs @@ -67,7 +67,7 @@ import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as B import Data.Either (isRight) import Data.Int (Int64) -import Data.List (find, isPrefixOf, isSuffixOf, nub) +import Data.List (find, isPrefixOf, isSuffixOf) import Data.List.NonEmpty (NonEmpty) import qualified Data.Map as M import Data.Maybe (isJust, isNothing) @@ -100,8 +100,7 @@ import Simplex.Messaging.Crypto.Ratchet (InitialKeys (..), PQEncryption (..), PQ import qualified Simplex.Messaging.Crypto.Ratchet as CR import Simplex.Messaging.Encoding import Simplex.Messaging.Encoding.String -import Simplex.Messaging.Notifications.Transport (NTFVersion, pattern VersionNTF) -import Simplex.Messaging.Protocol (BasicAuth, ErrorType (..), MsgBody, NetworkError (..), ProtocolServer (..), SubscriptionMode (..), initialSMPClientVersion, srvHostnamesSMPClientVersion, supportedSMPClientVRange) +import Simplex.Messaging.Protocol (BasicAuth, ErrorType (..), MsgBody, NetworkError (..), ProtocolServer (..), SubscriptionMode (..), supportedSMPClientVRange) import qualified Simplex.Messaging.Protocol as SMP import Simplex.Messaging.Protocol.Types import Simplex.Messaging.Server.Env.STM (AStoreType (..), ServerConfig (..), ServerStoreCfg (..), StorePaths (..)) @@ -109,7 +108,7 @@ import Simplex.Messaging.Server.Expiration import Simplex.Messaging.Server.MsgStore.Types (SMSType (..), SQSType (..)) import Simplex.Messaging.Server.QueueStore.QueueInfo import Simplex.Messaging.Server.StoreLog (StoreLogRecord (..)) -import Simplex.Messaging.Transport (ASrvTransport, SMPVersion, VersionSMP, authCmdsSMPVersion, currentServerSMPRelayVersion, minClientSMPRelayVersion, minServerSMPRelayVersion, sendingProxySMPVersion, sndAuthKeySMPVersion, alpnSupportedSMPHandshakes, supportedServerSMPRelayVRange) +import Simplex.Messaging.Transport (ASrvTransport, SMPVersion, VersionSMP, currentServerSMPRelayVersion, minClientSMPRelayVersion, minServerSMPRelayVersion, alpnSupportedSMPHandshakes, supportedServerSMPRelayVRange) import Simplex.Messaging.Transport.Server (TransportServerConfig (..)) import Simplex.Messaging.Util (bshow, diffToMicroseconds) import Simplex.Messaging.Version (VersionRange (..)) @@ -230,8 +229,8 @@ pattern Rcvd' aMsgId rcvdMsgId <- RCVD MsgMeta {integrity = MsgOk, recipient = ( smpCfgVPrev :: ProtocolClientConfig SMPVersion smpCfgVPrev = (smpCfg agentCfg) {serverVRange = prevRange $ serverVRange $ smpCfg agentCfg} -ntfCfgVPrev :: ProtocolClientConfig NTFVersion -ntfCfgVPrev = (ntfCfg agentCfg) {clientALPN = Nothing, serverVRange = V.mkVersionRange (VersionNTF 1) (VersionNTF 1)} +-- ntfCfgVPrev :: ProtocolClientConfig NTFVersion +-- ntfCfgVPrev = (ntfCfg agentCfg) {clientALPN = Nothing, serverVRange = V.mkVersionRange (VersionNTF 1) (VersionNTF 1)} agentCfgVPrev :: AgentConfig agentCfgVPrev = agentCfgVPrevPQ {e2eEncryptVRange = prevRange $ e2eEncryptVRange agentCfg} @@ -242,8 +241,8 @@ agentCfgVPrevPQ = { sndAuthAlg = C.AuthAlg C.SEd25519, smpAgentVRange = prevRange $ smpAgentVRange agentCfg, smpClientVRange = prevRange $ smpClientVRange agentCfg, - smpCfg = smpCfgVPrev, - ntfCfg = ntfCfgVPrev + smpCfg = smpCfgVPrev + -- ntfCfg = ntfCfgVPrev -- previous version is not supported } agentCfgRatchetVPrev :: AgentConfig @@ -336,16 +335,16 @@ functionalAPITests ps = do describe "two way concurrently (50)" $ testMatrix2Stress ps $ runAgentClientStressTestConc 50 xdescribe "two way concurrently (1000)" $ testMatrix2Stress ps $ runAgentClientStressTestConc 1000 describe "Establishing duplex connection, different PQ settings" $ do - testPQMatrix2 ps $ runAgentClientTestPQ False True + testPQMatrix2 ps $ runAgentClientTestPQ True True describe "Establishing duplex connection v2, different Ratchet versions" $ testRatchetMatrix2 ps runAgentClientTest describe "Establish duplex connection via contact address" $ testMatrix2 ps runAgentClientContactTest describe "Establish duplex connection via contact address, different PQ settings" $ do - testPQMatrix2NoInv ps $ runAgentClientContactTestPQ False True PQSupportOn + testPQMatrix2NoInv ps $ runAgentClientContactTestPQ True True PQSupportOn describe "Establish duplex connection via contact address v2, different Ratchet versions" $ testRatchetMatrix2 ps runAgentClientContactTest - describe "Establish duplex connection via contact address, different PQ settings" $ do + describe "Establish duplex connection via contact address, different PQ settings (3 clients)" $ do testPQMatrix3 ps $ runAgentClientContactTestPQ3 True it "should support rejecting contact request" $ withSmpServer ps testRejectContactRequest @@ -422,8 +421,6 @@ functionalAPITests ps = do testRatchetSyncSimultaneous ps #endif describe "Subscription mode OnlyCreate" $ do - it "messages delivered only when polled (v8 - slow handshake)" $ - withSmpServer ps testOnlyCreatePullSlowHandshake it "messages delivered only when polled" $ withSmpServer ps testOnlyCreatePull describe "Inactive client disconnection" $ do @@ -466,8 +463,6 @@ functionalAPITests ps = do testBasicMatrix2 ps testAcceptContactAsync it "should delete connections using async command when server connection fails" $ testDeleteConnectionAsync ps - it "join connection when reply queue creation fails (v8 - slow handshake)" $ - testJoinConnectionAsyncReplyErrorV8 ps it "join connection when reply queue creation fails" $ testJoinConnectionAsyncReplyError ps describe "delete connection waiting for delivery" $ do @@ -515,9 +510,9 @@ functionalAPITests ps = do describe "should switch two connections simultaneously, abort one" $ testServerMatrix2 ps testSwitch2ConnectionsAbort1 describe "SMP basic auth" $ do - forM_ (nub [prevVersion authCmdsSMPVersion, authCmdsSMPVersion, currentServerSMPRelayVersion]) $ \v -> do - let baseId = if v >= sndAuthKeySMPVersion then 1 else 3 - sqSecured = if v >= sndAuthKeySMPVersion then True else False + forM_ ([prevVersion currentServerSMPRelayVersion, currentServerSMPRelayVersion] :: [VersionSMP]) $ \v -> do + let baseId = 1 + sqSecured = True describe ("v" <> show v <> ": with server auth") $ do -- allow NEW | server auth, v | clnt1 auth, v | clnt2 auth, v | 2 - success, 1 - JOIN fail, 0 - NEW fail it "success " $ testBasicAuth ps True (Just "abcd", v) (Just "abcd", v) (Just "abcd", v) sqSecured baseId `shouldReturn` 2 @@ -573,7 +568,7 @@ testBasicAuth (t, msType) allowNewQueues srv@(srvAuth, srvVersion) clnt1 clnt2 s | canCreate1 && canCreate2 = 2 | canCreate1 = 1 | otherwise = 0 - created <- withSmpServerConfigOn t testCfg testPort $ \_ -> testCreateQueueAuth srvVersion clnt1 clnt2 sqSecured baseId + created <- withSmpServerConfigOn t testCfg testPort $ \_ -> testCreateQueueAuth clnt1 clnt2 sqSecured baseId created `shouldBe` expected pure created @@ -584,7 +579,6 @@ canCreateQueue allowNew (srvAuth, _) (clntAuth, _) = testMatrix2 :: HasCallStack => (ASrvTransport, AStoreType) -> (PQSupport -> SndQueueSecured -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec testMatrix2 ps runTest = do it "current, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 agentCfg agentCfg initAgentServersProxy 1 $ runTest PQSupportOn True True - it "v8, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 agentProxyCfgV8 agentProxyCfgV8 initAgentServersProxy 3 $ runTest PQSupportOn False True it "current" $ withSmpServer ps $ runTestCfg2 agentCfg agentCfg 1 $ runTest PQSupportOn True False it "prev" $ withSmpServer ps $ runTestCfg2 agentCfgVPrev agentCfgVPrev 1 $ runTest PQSupportOff False False it "prev to current" $ withSmpServer ps $ runTestCfg2 agentCfgVPrev agentCfg 1 $ runTest PQSupportOff False False @@ -593,14 +587,12 @@ testMatrix2 ps runTest = do testMatrix2Stress :: HasCallStack => (ASrvTransport, AStoreType) -> (PQSupport -> SndQueueSecured -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec testMatrix2Stress ps runTest = do it "current, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 aCfg aCfg initAgentServersProxy 1 $ runTest PQSupportOn True True - it "v8, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 aProxyCfgV8 aProxyCfgV8 initAgentServersProxy 1 $ runTest PQSupportOn False True it "current" $ withSmpServer ps $ runTestCfg2 aCfg aCfg 1 $ runTest PQSupportOn True False it "prev" $ withSmpServer ps $ runTestCfg2 aCfgVPrev aCfgVPrev 1 $ runTest PQSupportOff False False it "prev to current" $ withSmpServer ps $ runTestCfg2 aCfgVPrev aCfg 1 $ runTest PQSupportOff False False it "current to prev" $ withSmpServer ps $ runTestCfg2 aCfg aCfgVPrev 1 $ runTest PQSupportOff False False where aCfg = agentCfg {messageRetryInterval = fastMessageRetryInterval} - aProxyCfgV8 = agentProxyCfgV8 {messageRetryInterval = fastMessageRetryInterval} aCfgVPrev = agentCfgVPrev {messageRetryInterval = fastMessageRetryInterval} testBasicMatrix2 :: HasCallStack => (ASrvTransport, AStoreType) -> (SndQueueSecured -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec @@ -613,7 +605,6 @@ testBasicMatrix2 ps runTest = do testRatchetMatrix2 :: HasCallStack => (ASrvTransport, AStoreType) -> (PQSupport -> SndQueueSecured -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec testRatchetMatrix2 ps runTest = do it "current, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 agentCfg agentCfg initAgentServersProxy 1 $ runTest PQSupportOn True True - it "v8, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 agentProxyCfgV8 agentProxyCfgV8 initAgentServersProxy 3 $ runTest PQSupportOn False True it "ratchet current" $ withSmpServer ps $ runTestCfg2 agentCfg agentCfg 1 $ runTest PQSupportOn True False it "ratchet prev" $ withSmpServer ps $ runTestCfg2 agentCfgRatchetVPrev agentCfgRatchetVPrev 1 $ runTest PQSupportOff True False it "ratchets prev to current" $ withSmpServer ps $ runTestCfg2 agentCfgRatchetVPrev agentCfg 1 $ runTest PQSupportOff True False @@ -658,7 +649,7 @@ pqMatrix2_ pqInv ps test = do it "pq-inv/dh handshake" $ runTest $ \a b -> test (a, IKUsePQ) (b, PQSupportOff) it "pq-inv/pq handshake" $ runTest $ \a b -> test (a, IKUsePQ) (b, PQSupportOn) where - runTest = withSmpServerProxy ps . runTestCfgServers2 agentProxyCfgV8 agentProxyCfgV8 initAgentServersProxy 3 + runTest = withSmpServerProxy ps . runTestCfgServers2 agentCfg agentCfg initAgentServersProxy 1 testPQMatrix3 :: HasCallStack => @@ -677,8 +668,8 @@ testPQMatrix3 ps test = do where runTest test' = withSmpServerProxy ps $ - runTestCfgServers2 agentProxyCfgV8 agentProxyCfgV8 servers 3 $ \a b baseMsgId -> - withAgent 3 agentProxyCfgV8 servers testDB3 $ \c -> test' a b c baseMsgId + runTestCfgServers2 agentCfg agentCfg servers 1 $ \a b baseMsgId -> + withAgent 3 agentCfg servers testDB3 $ \c -> test' a b c baseMsgId servers = initAgentServersProxy runTestCfg2 :: HasCallStack => AgentConfig -> AgentConfig -> AgentMsgId -> (HasCallStack => AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> IO () @@ -1011,7 +1002,7 @@ runAgentClientContactTestPQ3 viaProxy (alice, aPQ) (bob, bPQ) (tom, tPQ) baseId liftIO $ pqSup' `shouldBe` PQSupportOn bId <- A.prepareConnectionToAccept alice 1 True invId (CR.connPQEncryption aPQ) sqSecuredAccept <- acceptContact alice 1 bId True invId "alice's connInfo" (CR.connPQEncryption aPQ) SMSubscribe - liftIO $ sqSecuredAccept `shouldBe` False -- agent cfg is v8 + liftIO $ sqSecuredAccept `shouldBe` True ("", _, A.CONF confId pqSup'' _ "alice's connInfo") <- get b liftIO $ pqSup'' `shouldBe` pq allowConnection b aId confId "bob's connInfo" @@ -2321,42 +2312,6 @@ testRatchetSyncSimultaneous ps = do disposeAgentClient bob disposeAgentClient bob2 -testOnlyCreatePullSlowHandshake :: IO () -testOnlyCreatePullSlowHandshake = withAgentClientsCfg2 agentProxyCfgV8 agentProxyCfgV8 $ \alice bob -> runRight_ $ do - (bobId, qInfo) <- createConnection alice 1 True SCMInvitation Nothing SMOnlyCreate - (aliceId, sqSecured) <- joinConnection bob 1 True qInfo "bob's connInfo" SMOnlyCreate - liftIO $ sqSecured `shouldBe` False - Just ("", _, CONF confId _ "bob's connInfo") <- getMsg alice bobId $ timeout 5_000000 $ get alice - getMSGNTF alice bobId - allowConnection alice bobId confId "alice's connInfo" - liftIO $ threadDelay 1_000000 - getMsg bob aliceId $ - get bob ##> ("", aliceId, INFO "alice's connInfo") - getMSGNTF bob aliceId - liftIO $ threadDelay 1_000000 - getMsg alice bobId $ pure () - inAnyOrder - (get alice) - [ \case ("", c, CON) -> c == bobId; _ -> False, - \case ("", c, MSGNTF {}) -> c == bobId; _ -> False - ] - getMsg bob aliceId $ - get bob ##> ("", aliceId, CON) - getMSGNTF bob aliceId - -- exchange messages - 4 <- sendMessage alice bobId SMP.noMsgFlags "hello" - get alice ##> ("", bobId, SENT 4) - getMsg bob aliceId $ - get bob =##> \case ("", c, Msg "hello") -> c == aliceId; _ -> False - ackMessage bob aliceId 4 Nothing - getMSGNTF bob aliceId - 5 <- sendMessage bob aliceId SMP.noMsgFlags "hello too" - get bob ##> ("", aliceId, SENT 5) - getMsg alice bobId $ - get alice =##> \case ("", c, Msg "hello too") -> c == bobId; _ -> False - ackMessage alice bobId 5 Nothing - getMSGNTF alice bobId - getMsg :: AgentClient -> ConnId -> ExceptT AgentErrorType IO a -> ExceptT AgentErrorType IO a getMsg c cId action = do liftIO $ noMessages c "nothing should be delivered before GET" @@ -3084,53 +3039,6 @@ networkOrTimeoutError = \case NETWORK _ -> True _ -> False -testJoinConnectionAsyncReplyErrorV8 :: HasCallStack => (ASrvTransport, AStoreType) -> IO () -testJoinConnectionAsyncReplyErrorV8 ps@(t, ASType qsType _) = do - let initAgentServersSrv2 = initAgentServers {smp = userServers [testSMPServer2]} - withAgent 1 cfg' initAgentServers testDB $ \a -> - withAgent 2 cfg' initAgentServersSrv2 testDB2 $ \b -> do - (aId, bId) <- withSmpServerStoreLogOn ps testPort $ \_ -> runRight $ do - bId <- prepareConnectionToCreate a 1 True SCMInvitation PQSupportOn - createConnectionAsync a "1" bId True SCMInvitation IKPQOn SMSubscribe - ("1", bId', INV (ACR _ qInfo)) <- get a - liftIO $ bId' `shouldBe` bId - aId <- prepareConnectionToJoin b 1 True qInfo PQSupportOn - joinConnectionAsync b "2" False aId True qInfo "bob's connInfo" PQSupportOn SMSubscribe - liftIO $ threadDelay 500000 - ConnectionStats {rcvQueuesInfo = [], sndQueuesInfo = [SndQueueInfo {}]} <- getConnectionServers b aId - pure (aId, bId) - nGet a =##> \case ("", "", DOWN _ [c]) -> c == bId; _ -> False - withSmpServerConfigOn t (cfgJ2QS qsType) testPort2 $ \_ -> do - get b =##> \case ("2", c, JOINED sqSecured) -> c == aId && not sqSecured; _ -> False - confId <- withSmpServerStoreLogOn ps testPort $ \_ -> do - pGet a >>= \case - ("", "", AEvt _ (UP _ [_])) -> do - ("", _, CONF confId _ "bob's connInfo") <- get a - pure confId - ("", _, AEvt _ (CONF confId _ "bob's connInfo")) -> do - ("", "", UP _ [_]) <- nGet a - pure confId - r -> error $ "unexpected response " <> show r - nGet a =##> \case ("", "", DOWN _ [c]) -> c == bId; _ -> False - runRight_ $ do - allowConnectionAsync a "3" bId confId "alice's connInfo" - get a ##> ("3", bId, OK) - liftIO $ threadDelay 500000 - ConnectionStats {rcvQueuesInfo = [RcvQueueInfo {}], sndQueuesInfo = [SndQueueInfo {}]} <- getConnectionServers b aId - pure () - withSmpServerStoreLogOn ps testPort $ \_ -> runRight_ $ do - nGet a =##> \case ("", "", UP _ [c]) -> c == bId; _ -> False - get a ##> ("", bId, CON) - get b ##> ("", aId, INFO "alice's connInfo") - get b ##> ("", aId, CON) - exchangeGreetingsMsgId 4 a bId b aId - where - cfg' = - agentCfgVPrevPQ - { smpClientVRange = V.mkVersionRange initialSMPClientVersion srvHostnamesSMPClientVersion, -- before SKEY - smpCfg = smpCfgVPrev {serverVRange = V.mkVersionRange minServerSMPRelayVersion sendingProxySMPVersion} -- before SKEY - } - testJoinConnectionAsyncReplyError :: HasCallStack => (ASrvTransport, AStoreType) -> IO () testJoinConnectionAsyncReplyError ps@(t, ASType qsType _) = do let initAgentServersSrv2 = initAgentServers {smp = userServers [testSMPServer2]} @@ -3603,8 +3511,8 @@ testSwitch2ConnectionsAbort1 servers = do withB :: (AgentClient -> IO a) -> IO a withB = withAgent 2 agentCfg servers testDB2 -testCreateQueueAuth :: HasCallStack => VersionSMP -> (Maybe BasicAuth, VersionSMP) -> (Maybe BasicAuth, VersionSMP) -> SndQueueSecured -> AgentMsgId -> IO Int -testCreateQueueAuth srvVersion clnt1 clnt2 sqSecured baseId = do +testCreateQueueAuth :: HasCallStack => (Maybe BasicAuth, VersionSMP) -> (Maybe BasicAuth, VersionSMP) -> SndQueueSecured -> AgentMsgId -> IO Int +testCreateQueueAuth clnt1 clnt2 sqSecured baseId = do a <- getClient 1 clnt1 testDB b <- getClient 2 clnt2 testDB2 r <- runRight $ do @@ -3630,9 +3538,9 @@ testCreateQueueAuth srvVersion clnt1 clnt2 sqSecured baseId = do where getClient clientId (clntAuth, clntVersion) db = let servers = initAgentServers {smp = userServers' [ProtoServerWithAuth testSMPServer clntAuth]} - alpn_ = if clntVersion >= authCmdsSMPVersion then Just alpnSupportedSMPHandshakes else Nothing + alpn_ = Just alpnSupportedSMPHandshakes smpCfg = defaultClientConfig alpn_ False $ V.mkVersionRange minClientSMPRelayVersion clntVersion - sndAuthAlg = if srvVersion >= authCmdsSMPVersion && clntVersion >= authCmdsSMPVersion then C.AuthAlg C.SX25519 else C.AuthAlg C.SEd25519 + sndAuthAlg = C.AuthAlg C.SX25519 in getSMPAgentClient' clientId agentCfg {smpCfg, sndAuthAlg} servers db testSMPServerConnectionTest :: (ASrvTransport, AStoreType) -> Maybe BasicAuth -> SMPServerWithAuth -> IO (Maybe ProtocolTestFailure) diff --git a/tests/AgentTests/NotificationTests.hs b/tests/AgentTests/NotificationTests.hs index 6a1c5cef9..fdd23f6d6 100644 --- a/tests/AgentTests/NotificationTests.hs +++ b/tests/AgentTests/NotificationTests.hs @@ -184,10 +184,10 @@ testNtfMatrix ps@(_, msType) runTest = do describe "next and current" $ do it "curr servers; curr clients" $ runNtfTestCfg ps 1 cfg' ntfServerCfg agentCfg agentCfg runTest it "curr servers; prev clients" $ runNtfTestCfg ps 1 cfg' ntfServerCfg agentCfgVPrevPQ agentCfgVPrevPQ runTest - it "prev servers; prev clients" $ runNtfTestCfg ps 1 cfgVPrev' ntfServerCfgVPrev agentCfgVPrevPQ agentCfgVPrevPQ runTest - it "prev servers; curr clients" $ runNtfTestCfg ps 1 cfgVPrev' ntfServerCfgVPrev agentCfg agentCfg runTest + it "prev servers; prev clients" $ runNtfTestCfg ps 1 cfgVPrev' ntfServerCfg agentCfgVPrevPQ agentCfgVPrevPQ runTest + it "prev servers; curr clients" $ runNtfTestCfg ps 1 cfgVPrev' ntfServerCfg agentCfg agentCfg runTest -- servers can be upgraded in any order - it "servers: curr SMP, prev NTF; prev clients" $ runNtfTestCfg ps 1 cfg' ntfServerCfgVPrev agentCfgVPrevPQ agentCfgVPrevPQ runTest + -- it "servers: curr SMP, prev NTF; prev clients" $ runNtfTestCfg ps 1 cfg' ntfServerCfg agentCfgVPrevPQ agentCfgVPrevPQ runTest it "servers: prev SMP, curr NTF; prev clients" $ runNtfTestCfg ps 1 cfgVPrev' ntfServerCfg agentCfgVPrevPQ agentCfgVPrevPQ runTest -- one of two clients can be upgraded it "servers: curr SMP, curr NTF; clients: curr/prev" $ runNtfTestCfg ps 1 cfg' ntfServerCfg agentCfg agentCfgVPrevPQ runTest diff --git a/tests/CoreTests/BatchingTests.hs b/tests/CoreTests/BatchingTests.hs index 8a285721b..d929085de 100644 --- a/tests/CoreTests/BatchingTests.hs +++ b/tests/CoreTests/BatchingTests.hs @@ -30,148 +30,60 @@ import Util batchingTests :: Spec batchingTests = do describe "batchTransmissions" $ do - describe "SMP v6 (previous)" $ do - it "should batch with 106 subscriptions per batch" testBatchSubscriptionsV6 - it "should break on message that does not fit" testBatchWithMessageV6 - it "should break on large message" testBatchWithLargeMessageV6 - describe "SMP current" $ do - it "should batch with 135 subscriptions per batch" testBatchSubscriptions - it "should break on message that does not fit" testBatchWithMessage - it "should break on large message" testBatchWithLargeMessage + it "should batch with 135 subscriptions per batch" testBatchSubscriptions + it "should break on message that does not fit" testBatchWithMessage + it "should break on large message" testBatchWithLargeMessage describe "batchTransmissions'" $ do - describe "SMP v6 (previous)" $ do - it "should batch with 106 subscriptions per batch" testClientBatchSubscriptionsV6 - it "should break on message that does not fit" testClientBatchWithMessageV6 - it "should break on large message" testClientBatchWithLargeMessageV6 - describe "SMP current" $ do - it "should batch with 135 subscriptions per batch" testClientBatchSubscriptions - it "should batch with 255 ENDs per batch" testClientBatchENDs - it "should batch with 80 NMSGs per batch" testClientBatchNMSGs - it "should batch subscription responses with message" testBatchSubResponses - it "should break on message that does not fit" testClientBatchWithMessage - it "should break on large message" testClientBatchWithLargeMessage - -testBatchSubscriptionsV6 :: IO () -testBatchSubscriptionsV6 = do - sessId <- atomically . C.randomBytes 32 =<< C.newRandom - subs <- replicateM 250 $ randomSUBv6 sessId - let thParams = testTHandleParams minServerSMPRelayVersion sessId - batches1 = batchTransmissions thParams {batch = False} $ L.fromList subs - all lenOk1 batches1 `shouldBe` True - length batches1 `shouldBe` 250 - let batches = batchTransmissions thParams $ L.fromList subs - length batches `shouldBe` 3 - [TBTransmissions s1 n1 _, TBTransmissions s2 n2 _, TBTransmissions s3 n3 _] <- pure batches - (n1, n2, n3) `shouldBe` (38, 106, 106) - all lenOk [s1, s2, s3] `shouldBe` True + it "should batch with 135 subscriptions per batch" testClientBatchSubscriptions + it "should batch with 255 ENDs per batch" testClientBatchENDs + it "should batch with 80 NMSGs per batch" testClientBatchNMSGs + it "should batch subscription responses with message" testBatchSubResponses + it "should break on message that does not fit" testClientBatchWithMessage + it "should break on large message" testClientBatchWithLargeMessage testBatchSubscriptions :: IO () testBatchSubscriptions = do sessId <- atomically . C.randomBytes 32 =<< C.newRandom subs <- replicateM 300 $ randomSUB sessId - let thParams = testTHandleParams currentClientSMPRelayVersion sessId - batches1 = batchTransmissions thParams {batch = False} $ L.fromList subs - all lenOk1 batches1 `shouldBe` True - length batches1 `shouldBe` 300 + let thParams = testTHandleParams sessId let batches = batchTransmissions thParams $ L.fromList subs length batches `shouldBe` 3 [TBTransmissions s1 n1 _, TBTransmissions s2 n2 _, TBTransmissions s3 n3 _] <- pure batches (n1, n2, n3) `shouldBe` (30, 135, 135) all lenOk [s1, s2, s3] `shouldBe` True -testBatchWithMessageV6 :: IO () -testBatchWithMessageV6 = do - sessId <- atomically . C.randomBytes 32 =<< C.newRandom - subs1 <- replicateM 60 $ randomSUBv6 sessId - send <- randomSENDv6 sessId 8000 - subs2 <- replicateM 40 $ randomSUBv6 sessId - let thParams = testTHandleParams minServerSMPRelayVersion sessId - cmds = subs1 <> [send] <> subs2 - batches1 = batchTransmissions thParams {batch = False} $ L.fromList cmds - all lenOk1 batches1 `shouldBe` True - length batches1 `shouldBe` 101 - let batches = batchTransmissions thParams $ L.fromList cmds - length batches `shouldBe` 2 - [TBTransmissions s1 n1 _, TBTransmissions s2 n2 _] <- pure batches - (n1, n2) `shouldBe` (47, 54) - all lenOk [s1, s2] `shouldBe` True - testBatchWithMessage :: IO () testBatchWithMessage = do sessId <- atomically . C.randomBytes 32 =<< C.newRandom subs1 <- replicateM 60 $ randomSUB sessId send <- randomSEND sessId 8000 subs2 <- replicateM 40 $ randomSUB sessId - let thParams = testTHandleParams currentClientSMPRelayVersion sessId + let thParams = testTHandleParams sessId cmds = subs1 <> [send] <> subs2 - batches1 = batchTransmissions thParams {batch = False} $ L.fromList cmds - all lenOk1 batches1 `shouldBe` True - length batches1 `shouldBe` 101 let batches = batchTransmissions thParams $ L.fromList cmds length batches `shouldBe` 2 [TBTransmissions s1 n1 _, TBTransmissions s2 n2 _] <- pure batches (n1, n2) `shouldBe` (33, 68) all lenOk [s1, s2] `shouldBe` True -testBatchWithLargeMessageV6 :: IO () -testBatchWithLargeMessageV6 = do - sessId <- atomically . C.randomBytes 32 =<< C.newRandom - subs1 <- replicateM 50 $ randomSUBv6 sessId - send <- randomSENDv6 sessId 17000 - subs2 <- replicateM 150 $ randomSUBv6 sessId - let thParams = testTHandleParams minServerSMPRelayVersion sessId - cmds = subs1 <> [send] <> subs2 - batches1 = batchTransmissions thParams {batch = False} $ L.fromList cmds - all lenOk1 batches1 `shouldBe` False - length batches1 `shouldBe` 201 - let batches1' = take 50 batches1 <> drop 51 batches1 - all lenOk1 batches1' `shouldBe` True - length batches1' `shouldBe` 200 - let batches = batchTransmissions thParams $ L.fromList cmds - length batches `shouldBe` 4 - [TBTransmissions s1 n1 _, TBError TELargeMsg _, TBTransmissions s2 n2 _, TBTransmissions s3 n3 _] <- pure batches - (n1, n2, n3) `shouldBe` (50, 44, 106) - all lenOk [s1, s2, s3] `shouldBe` True - testBatchWithLargeMessage :: IO () testBatchWithLargeMessage = do sessId <- atomically . C.randomBytes 32 =<< C.newRandom subs1 <- replicateM 60 $ randomSUB sessId send <- randomSEND sessId 17000 subs2 <- replicateM 150 $ randomSUB sessId - let thParams = testTHandleParams currentClientSMPRelayVersion sessId + let thParams = testTHandleParams sessId cmds = subs1 <> [send] <> subs2 - batches1 = batchTransmissions thParams {batch = False} $ L.fromList cmds - all lenOk1 batches1 `shouldBe` False - length batches1 `shouldBe` 211 - let batches1' = take 60 batches1 <> drop 61 batches1 - all lenOk1 batches1' `shouldBe` True - length batches1' `shouldBe` 210 let batches = batchTransmissions thParams $ L.fromList cmds length batches `shouldBe` 4 [TBTransmissions s1 n1 _, TBError TELargeMsg _, TBTransmissions s2 n2 _, TBTransmissions s3 n3 _] <- pure batches (n1, n2, n3) `shouldBe` (60, 15, 135) all lenOk [s1, s2, s3] `shouldBe` True -testClientBatchSubscriptionsV6 :: IO () -testClientBatchSubscriptionsV6 = do - client <- testClientStubV6 - subs <- replicateM 250 $ randomSUBCmdV6 client - let batches1 = batchTransmissions' (thParams client) {batch = False} $ L.fromList subs - all lenOk1 batches1 `shouldBe` True - let batches = batchTransmissions' (thParams client) $ L.fromList subs - length batches `shouldBe` 3 - [TBTransmissions s1 n1 rs1, TBTransmissions s2 n2 rs2, TBTransmissions s3 n3 rs3] <- pure batches - (n1, n2, n3) `shouldBe` (38, 106, 106) - (length rs1, length rs2, length rs3) `shouldBe` (38, 106, 106) - all lenOk [s1, s2, s3] `shouldBe` True - testClientBatchSubscriptions :: IO () testClientBatchSubscriptions = do client <- testClientStub subs <- replicateM 300 $ randomSUBCmd client - let batches1 = batchTransmissions' (thParams client) {batch = False} $ L.fromList subs - all lenOk1 batches1 `shouldBe` True let batches = batchTransmissions' (thParams client) $ L.fromList subs length batches `shouldBe` 3 [TBTransmissions s1 n1 rs1, TBTransmissions s2 n2 rs2, TBTransmissions s3 n3 rs3] <- pure batches @@ -184,8 +96,6 @@ testClientBatchENDs = do client <- testClientStub ends <- replicateM 300 randomENDCmd let ends' = map (\t -> Right (Nothing, encodeTransmission (thParams client) t)) ends - batches1 = batchTransmissions (thParams client) {batch = False} $ L.fromList ends' - all lenOk1 batches1 `shouldBe` True let batches = batchTransmissions (thParams client) $ L.fromList ends' length batches `shouldBe` 2 [TBTransmissions s1 n1 rs1, TBTransmissions s2 n2 rs2] <- pure batches @@ -199,8 +109,6 @@ testClientBatchNMSGs = do ts <- getSystemTime ntfs <- replicateM 200 $ randomNMSGCmd ts let ntfs' = map (\t -> Right (Nothing, encodeTransmission (thParams client) t)) ntfs - batches1 = batchTransmissions (thParams client) {batch = False} $ L.fromList ntfs' - all lenOk1 batches1 `shouldBe` True let batches = batchTransmissions (thParams client) $ L.fromList ntfs' length batches `shouldBe` 3 [TBTransmissions s1 n1 rs1, TBTransmissions s2 n2 rs2, TBTransmissions s3 n3 rs3] <- pure batches @@ -222,23 +130,6 @@ testBatchSubResponses = do batches' = batchTransmissions (thParams client) $ L.fromList msgs' length batches' `shouldBe` 2 -testClientBatchWithMessageV6 :: IO () -testClientBatchWithMessageV6 = do - client <- testClientStubV6 - subs1 <- replicateM 60 $ randomSUBCmdV6 client - send <- randomSENDCmdV6 client 8000 - subs2 <- replicateM 40 $ randomSUBCmdV6 client - let cmds = subs1 <> [send] <> subs2 - batches1 = batchTransmissions' (thParams client) {batch = False} $ L.fromList cmds - all lenOk1 batches1 `shouldBe` True - length batches1 `shouldBe` 101 - let batches = batchTransmissions' (thParams client) $ L.fromList cmds - length batches `shouldBe` 2 - [TBTransmissions s1 n1 rs1, TBTransmissions s2 n2 rs2] <- pure batches - (n1, n2) `shouldBe` (47, 54) - (length rs1, length rs2) `shouldBe` (47, 54) - all lenOk [s1, s2] `shouldBe` True - testClientBatchWithMessage :: IO () testClientBatchWithMessage = do client <- testClientStub @@ -246,45 +137,13 @@ testClientBatchWithMessage = do send <- randomSENDCmd client 8000 subs2 <- replicateM 40 $ randomSUBCmd client let cmds = subs1 <> [send] <> subs2 - batches1 = batchTransmissions' (thParams client) {batch = False} $ L.fromList cmds - all lenOk1 batches1 `shouldBe` True - length batches1 `shouldBe` 101 - let batches = batchTransmissions' (thParams client) $ L.fromList cmds + batches = batchTransmissions' (thParams client) $ L.fromList cmds length batches `shouldBe` 2 [TBTransmissions s1 n1 rs1, TBTransmissions s2 n2 rs2] <- pure batches (n1, n2) `shouldBe` (33, 68) (length rs1, length rs2) `shouldBe` (33, 68) all lenOk [s1, s2] `shouldBe` True -testClientBatchWithLargeMessageV6 :: IO () -testClientBatchWithLargeMessageV6 = do - client <- testClientStubV6 - subs1 <- replicateM 50 $ randomSUBCmdV6 client - send <- randomSENDCmdV6 client 17000 - subs2 <- replicateM 150 $ randomSUBCmdV6 client - let cmds = subs1 <> [send] <> subs2 - batches1 = batchTransmissions' (thParams client) {batch = False} $ L.fromList cmds - all lenOk1 batches1 `shouldBe` False - length batches1 `shouldBe` 201 - let batches1' = take 50 batches1 <> drop 51 batches1 - all lenOk1 batches1' `shouldBe` True - length batches1' `shouldBe` 200 - -- - let batches = batchTransmissions' (thParams client) $ L.fromList cmds - length batches `shouldBe` 4 - [TBTransmissions s1 n1 rs1, TBError TELargeMsg _, TBTransmissions s2 n2 rs2, TBTransmissions s3 n3 rs3] <- pure batches - (n1, n2, n3) `shouldBe` (50, 44, 106) - (length rs1, length rs2, length rs3) `shouldBe` (50, 44, 106) - all lenOk [s1, s2, s3] `shouldBe` True - -- - let cmds' = [send] <> subs1 <> subs2 - let batches' = batchTransmissions' (thParams client) $ L.fromList cmds' - length batches' `shouldBe` 3 - [TBError TELargeMsg _, TBTransmissions s1' n1' rs1', TBTransmissions s2' n2' rs2'] <- pure batches' - (n1', n2') `shouldBe` (94, 106) - (length rs1', length rs2') `shouldBe` (94, 106) - all lenOk [s1', s2'] `shouldBe` True - testClientBatchWithLargeMessage :: IO () testClientBatchWithLargeMessage = do client <- testClientStub @@ -292,14 +151,7 @@ testClientBatchWithLargeMessage = do send <- randomSENDCmd client 17000 subs2 <- replicateM 150 $ randomSUBCmd client let cmds = subs1 <> [send] <> subs2 - batches1 = batchTransmissions' (thParams client) {batch = False} $ L.fromList cmds - all lenOk1 batches1 `shouldBe` False - length batches1 `shouldBe` 211 - let batches1' = take 60 batches1 <> drop 61 batches1 - all lenOk1 batches1' `shouldBe` True - length batches1' `shouldBe` 210 - -- - let batches = batchTransmissions' (thParams client) $ L.fromList cmds + batches = batchTransmissions' (thParams client) $ L.fromList cmds length batches `shouldBe` 4 [TBTransmissions s1 n1 rs1, TBError TELargeMsg _, TBTransmissions s2 n2 rs2, TBTransmissions s3 n3 rs3] <- pure batches (n1, n2, n3) `shouldBe` (60, 15, 135) @@ -314,49 +166,30 @@ testClientBatchWithLargeMessage = do (length rs1', length rs2') `shouldBe` (75, 135) all lenOk [s1', s2'] `shouldBe` True -testClientStubV6 :: IO (ProtocolClient SMPVersion ErrorType BrokerMsg) -testClientStubV6 = do - g <- C.newRandom - sessId <- atomically $ C.randomBytes 32 g - smpClientStub g sessId minServerSMPRelayVersion Nothing - testClientStub :: IO (ProtocolClient SMPVersion ErrorType BrokerMsg) testClientStub = do g <- C.newRandom sessId <- atomically $ C.randomBytes 32 g (rKey, _) <- atomically $ C.generateAuthKeyPair C.SX25519 g - thAuth_ <- testTHandleAuth currentClientSMPRelayVersion g rKey + thAuth_ <- testTHandleAuth g rKey smpClientStub g sessId currentClientSMPRelayVersion thAuth_ -randomSUBv6 :: ByteString -> IO (Either TransportError (Maybe TAuthorizations, ByteString)) -randomSUBv6 = randomSUB_ C.SEd25519 minServerSMPRelayVersion - randomSUB :: ByteString -> IO (Either TransportError (Maybe TAuthorizations, ByteString)) -randomSUB = randomSUB_ C.SEd25519 currentClientSMPRelayVersion - --- TODO [certs rcv] test with the additional certificate signature -randomSUB_ :: (C.AlgorithmI a, C.AuthAlgorithm a) => C.SAlgorithm a -> VersionSMP -> ByteString -> IO (Either TransportError (Maybe TAuthorizations, ByteString)) -randomSUB_ a v sessId = do +randomSUB sessId = do g <- C.newRandom rId <- atomically $ C.randomBytes 24 g nonce@(C.CbNonce corrId) <- atomically $ C.randomCbNonce g - (rKey, rpKey) <- atomically $ C.generateAuthKeyPair a g - thAuth_ <- testTHandleAuth v g rKey - let thParams = testTHandleParams v sessId + (rKey, rpKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g + thAuth_ <- testTHandleAuth g rKey + let thParams = testTHandleParams sessId TransmissionForAuth {tForAuth, tToSend} = encodeTransmissionForAuth thParams (CorrId corrId, EntityId rId, Cmd SRecipient SUB) pure $ (,tToSend) <$> authTransmission thAuth_ True (Just rpKey) nonce tForAuth -randomSUBCmdV6 :: ProtocolClient SMPVersion ErrorType BrokerMsg -> IO (PCTransmission ErrorType BrokerMsg) -randomSUBCmdV6 = randomSUBCmd_ C.SEd25519 - randomSUBCmd :: ProtocolClient SMPVersion ErrorType BrokerMsg -> IO (PCTransmission ErrorType BrokerMsg) -randomSUBCmd = randomSUBCmd_ C.SEd25519 -- same as v6 - -randomSUBCmd_ :: (C.AlgorithmI a, C.AuthAlgorithm a) => C.SAlgorithm a -> ProtocolClient SMPVersion ErrorType BrokerMsg -> IO (PCTransmission ErrorType BrokerMsg) -randomSUBCmd_ a c = do +randomSUBCmd c = do g <- C.newRandom rId <- atomically $ C.randomBytes 24 g - (_, rpKey) <- atomically $ C.generateAuthKeyPair a g + (_, rpKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g mkTransmission c (EntityId rId, Just rpKey, Cmd SRecipient SUB) randomENDCmd :: IO (Transmission BrokerMsg) @@ -389,44 +222,37 @@ randomMSG = do corrId <- atomically $ C.randomBytes 24 g rId <- atomically $ C.randomBytes 24 g msgId <- atomically $ C.randomBytes 24 g - msg <- atomically $ C.randomBytes (maxMessageLength currentClientSMPRelayVersion) g + msg <- atomically $ C.randomBytes maxMessageLength g pure (CorrId corrId, EntityId rId, MSG RcvMessage {msgId, msgBody = EncRcvMsgBody msg}) -randomSENDv6 :: ByteString -> Int -> IO (Either TransportError (Maybe TAuthorizations, ByteString)) -randomSENDv6 = randomSEND_ C.SEd25519 minServerSMPRelayVersion - randomSEND :: ByteString -> Int -> IO (Either TransportError (Maybe TAuthorizations, ByteString)) -randomSEND = randomSEND_ C.SX25519 currentClientSMPRelayVersion - -randomSEND_ :: (C.AlgorithmI a, C.AuthAlgorithm a) => C.SAlgorithm a -> VersionSMP -> ByteString -> Int -> IO (Either TransportError (Maybe TAuthorizations, ByteString)) -randomSEND_ a v sessId len = do +randomSEND sessId len = do g <- C.newRandom sId <- atomically $ C.randomBytes 24 g nonce@(C.CbNonce corrId) <- atomically $ C.randomCbNonce g - (sKey, spKey) <- atomically $ C.generateAuthKeyPair a g - thAuth_ <- testTHandleAuth v g sKey + (sKey, spKey) <- atomically $ C.generateAuthKeyPair C.SX25519 g + thAuth_ <- testTHandleAuth g sKey msg <- atomically $ C.randomBytes len g - let thParams = testTHandleParams v sessId + let thParams = testTHandleParams sessId TransmissionForAuth {tForAuth, tToSend} = encodeTransmissionForAuth thParams (CorrId corrId, EntityId sId, Cmd SSender $ SEND noMsgFlags msg) pure $ (,tToSend) <$> authTransmission thAuth_ False (Just spKey) nonce tForAuth -testTHandleParams :: VersionSMP -> ByteString -> THandleParams SMPVersion 'TClient -testTHandleParams v sessionId = +testTHandleParams :: ByteString -> THandleParams SMPVersion 'TClient +testTHandleParams sessionId = THandleParams { sessionId, blockSize = smpBlockSize, - thVersion = v, + thVersion = currentClientSMPRelayVersion, thServerVRange = supportedServerSMPRelayVRange, thAuth = Nothing, - implySessId = v >= authCmdsSMPVersion, + implySessId = True, encryptBlock = Nothing, - batch = True, - serviceAuth = v >= serviceCertsSMPVersion + serviceAuth = True } -testTHandleAuth :: VersionSMP -> TVar ChaChaDRG -> C.APublicAuthKey -> IO (Maybe (THandleAuth 'TClient)) -testTHandleAuth v g (C.APublicAuthKey a peerServerPubKey) = case a of - C.SX25519 | v >= authCmdsSMPVersion -> do +testTHandleAuth :: TVar ChaChaDRG -> C.APublicAuthKey -> IO (Maybe (THandleAuth 'TClient)) +testTHandleAuth g (C.APublicAuthKey a peerServerPubKey) = case a of + C.SX25519 -> do ca <- head <$> XS.readCertificates "tests/fixtures/ca.crt" serverCert <- head <$> XS.readCertificates "tests/fixtures/server.crt" serverKey <- head <$> XF.readKeyFile "tests/fixtures/server.key" @@ -436,24 +262,13 @@ testTHandleAuth v g (C.APublicAuthKey a peerServerPubKey) = case a of pure $ Just THAuthClient {peerServerPubKey, peerServerCertKey, clientService = Nothing, sessSecret = Nothing} _ -> pure Nothing -randomSENDCmdV6 :: ProtocolClient SMPVersion ErrorType BrokerMsg -> Int -> IO (PCTransmission ErrorType BrokerMsg) -randomSENDCmdV6 = randomSENDCmd_ C.SEd25519 - randomSENDCmd :: ProtocolClient SMPVersion ErrorType BrokerMsg -> Int -> IO (PCTransmission ErrorType BrokerMsg) -randomSENDCmd = randomSENDCmd_ C.SX25519 - -randomSENDCmd_ :: (C.AlgorithmI a, C.AuthAlgorithm a) => C.SAlgorithm a -> ProtocolClient SMPVersion ErrorType BrokerMsg -> Int -> IO (PCTransmission ErrorType BrokerMsg) -randomSENDCmd_ a c len = do +randomSENDCmd c len = do g <- C.newRandom sId <- atomically $ C.randomBytes 24 g - (_, rpKey) <- atomically $ C.generateAuthKeyPair a g + (_, rpKey) <- atomically $ C.generateAuthKeyPair C.SX25519 g msg <- atomically $ C.randomBytes len g mkTransmission c (EntityId sId, Just rpKey, Cmd SSender $ SEND noMsgFlags msg) lenOk :: ByteString -> Bool lenOk s = 0 < B.length s && B.length s <= smpBlockSize - 2 - -lenOk1 :: TransportBatch r -> Bool -lenOk1 = \case - TBTransmission s _ -> lenOk s - _ -> False diff --git a/tests/NtfClient.hs b/tests/NtfClient.hs index 9835e1cdd..b0ee811d3 100644 --- a/tests/NtfClient.hs +++ b/tests/NtfClient.hs @@ -160,16 +160,16 @@ ntfServerCfg = startOptions = defaultStartOptions } -ntfServerCfgVPrev :: NtfServerConfig -ntfServerCfgVPrev = - ntfServerCfg - { ntfServerVRange = prevRange $ ntfServerVRange ntfServerCfg, - smpAgentCfg = smpAgentCfg' {smpCfg = smpCfg' {serverVRange = prevRange serverVRange'}} - } - where - smpAgentCfg' = smpAgentCfg ntfServerCfg - smpCfg' = smpCfg smpAgentCfg' - serverVRange' = serverVRange smpCfg' +-- ntfServerCfgVPrev :: NtfServerConfig +-- ntfServerCfgVPrev = +-- ntfServerCfg +-- { ntfServerVRange = prevRange $ ntfServerVRange ntfServerCfg, +-- smpAgentCfg = smpAgentCfg' {smpCfg = smpCfg' {serverVRange = prevRange serverVRange'}} +-- } +-- where +-- smpAgentCfg' = smpAgentCfg ntfServerCfg +-- smpCfg' = smpCfg smpAgentCfg' +-- serverVRange' = serverVRange smpCfg' withNtfServerThreadOn :: HasCallStack => ASrvTransport -> ServiceName -> PostgresStoreCfg -> (HasCallStack => ThreadId -> IO a) -> IO a withNtfServerThreadOn t port' dbStoreConfig = diff --git a/tests/SMPAgentClient.hs b/tests/SMPAgentClient.hs index 7f3ebb14d..d375b6c21 100644 --- a/tests/SMPAgentClient.hs +++ b/tests/SMPAgentClient.hs @@ -15,7 +15,7 @@ import Data.List.NonEmpty (NonEmpty) import qualified Data.List.NonEmpty as L import Data.Map.Strict (Map) import qualified Data.Map.Strict as M -import SMPClient (proxyVRangeV8, ntfTestPort, testPort) +import SMPClient (ntfTestPort, testPort) import Simplex.Messaging.Agent.Env.SQLite import Simplex.Messaging.Agent.Protocol import Simplex.Messaging.Agent.RetryInterval @@ -103,9 +103,6 @@ agentCfg = where networkConfig = defaultNetworkConfig {tcpConnectTimeout = NetworkTimeout 1_000000 1_000000, tcpTimeout = NetworkTimeout 2_000000 2_000000} -agentProxyCfgV8 :: AgentConfig -agentProxyCfgV8 = agentCfg {smpCfg = (smpCfg agentCfg) {serverVRange = proxyVRangeV8}} - fastRetryInterval :: RetryInterval fastRetryInterval = defaultReconnectInterval {initialInterval = 50_000} diff --git a/tests/SMPClient.hs b/tests/SMPClient.hs index 1dcd350bc..e5adaa749 100644 --- a/tests/SMPClient.hs +++ b/tests/SMPClient.hs @@ -179,9 +179,7 @@ testSMPClient_ host port vr serviceCreds_ client = do Right th -> client th Left e -> error $ show e where - clientALPN - | authCmdsSMPVersion `isCompatible` vr = Just alpnSupportedSMPHandshakes - | otherwise = Nothing + clientALPN = Just alpnSupportedSMPHandshakes runSMPClient :: Transport c => TProxy c 'TServer -> (THandleSMP c 'TClient -> IO a) -> IO a runSMPClient _ test' = testSMPClient test' @@ -309,9 +307,6 @@ serverStoreConfig_ useDbStoreLog = \case dbStoreLogPath = if useDbStoreLog then Just testStoreLogFile else Nothing storeCfg = PostgresStoreCfg {dbOpts = testStoreDBOpts, dbStoreLogPath, confirmMigrations = MCYesUp, deletedTTL = 86400} -cfgV7 :: AServerConfig -cfgV7 = updateCfg cfg $ \cfg' -> cfg' {smpServerVRange = mkVersionRange minServerSMPRelayVersion authCmdsSMPVersion} - cfgVPrev :: AStoreType -> AServerConfig cfgVPrev msType = updateCfg (cfgMS msType) $ \cfg' -> cfg' {smpServerVRange = prevRange $ smpServerVRange cfg'} @@ -351,9 +346,6 @@ proxyCfgShortTimeout = nt = NetworkTimeout {backgroundTimeout = 4_000000, interactiveTimeout = 4_000000} in cfg' {smpAgentCfg = aCfg {smpCfg = cCfg {networkConfig = (networkConfig cCfg) {tcpConnectTimeout = nt}}}} -proxyVRangeV8 :: VersionRangeSMP -proxyVRangeV8 = mkVersionRange minServerSMPRelayVersion sendingProxySMPVersion - withSmpServerStoreMsgLogOn :: HasCallStack => (ASrvTransport, AStoreType) -> ServiceName -> (HasCallStack => ThreadId -> IO a) -> IO a withSmpServerStoreMsgLogOn (t, msType) = withSmpServerConfigOn t $ updateCfg (cfgMS msType) $ \cfg' -> cfg' {storeNtfsFile = Just testStoreNtfsFile, serverStatsBackupFile = Just testServerStatsBackupFile} diff --git a/tests/SMPProxyTests.hs b/tests/SMPProxyTests.hs index 88525eac0..7190b69c9 100644 --- a/tests/SMPProxyTests.hs +++ b/tests/SMPProxyTests.hs @@ -77,7 +77,7 @@ smpProxyTests = do let srv1 = SMPServer testHost testPort testKeyHash srv2 = SMPServer testHost2 testPort2 testKeyHash describe "client API" $ do - let maxLen = maxMessageLength encryptedBlockSMPVersion + let maxLen = maxMessageLength describe "one server" $ do it "deliver via proxy" . oneServer $ do deliverMessageViaProxy srv1 srv1 C.SEd448 "hello 1" "hello 2" @@ -137,10 +137,6 @@ smpProxyTests = do agentDeliverMessageViaProxy ([srv1], SPMNever, False) ([srv2], SPMNever, False) C.SEd448 "hello 1" "hello 2" 1 it "first via proxy for unknown" . twoServers $ agentDeliverMessageViaProxy ([srv1], SPMUnknown, True) ([srv1, srv2], SPMUnknown, False) C.SEd448 "hello 1" "hello 2" 1 - it "without proxy with fallback" . twoServers_ proxyCfg cfgV7 $ - agentDeliverMessageViaProxy ([srv1], SPMUnknown, False) ([srv2], SPMUnknown, False) C.SEd448 "hello 1" "hello 2" 3 - it "fails when fallback is prohibited" . twoServers_ proxyCfg cfgV7 $ - agentViaProxyVersionError it "retries sending when destination or proxy relay is offline" $ \_ -> agentViaProxyRetryOffline it "retries sending when destination relay session disconnects in proxy" $ \_ -> @@ -218,7 +214,7 @@ proxyConnectDeadRelay n d proxyServ = do g <- C.newRandom -- set up proxy ts <- getCurrentTime - pc' <- getProtocolClient g NRMInteractive (1, proxyServ, Nothing) defaultSMPClientConfig {serverVRange = mkVersionRange minServerSMPRelayVersion sendingProxySMPVersion} [] Nothing ts (\_ -> pure ()) + pc' <- getProtocolClient g NRMInteractive (1, proxyServ, Nothing) defaultSMPClientConfig [] Nothing ts (\_ -> pure ()) pc <- either (fail . show) pure pc' THAuthClient {} <- maybe (fail "getProtocolClient returned no thAuth") pure $ thAuth $ thParams pc -- get proxy session @@ -334,18 +330,6 @@ agentDeliverMessagesViaProxyConc agentServers msgs = aCfg = agentCfg {sndAuthAlg = C.AuthAlg C.SEd448, rcvAuthAlg = C.AuthAlg C.SEd448} servers srvs = (initAgentServersProxy_ SPMAlways SPFAllow) {smp = userServers srvs} -agentViaProxyVersionError :: IO () -agentViaProxyVersionError = - withAgent 1 agentCfg (servers [SMPServer testHost testPort testKeyHash]) testDB $ \alice -> do - Left (A.BROKER _ (TRANSPORT TEVersion)) <- - withAgent 2 agentCfg (servers [SMPServer testHost2 testPort2 testKeyHash]) testDB2 $ \bob -> runExceptT $ do - (_bobId, CCLink qInfo Nothing) <- A.createConnection alice NRMInteractive 1 True True SCMInvitation Nothing Nothing CR.IKPQOn SMSubscribe - aliceId <- A.prepareConnectionToJoin bob 1 True qInfo PQSupportOn - A.joinConnection bob NRMInteractive 1 aliceId True qInfo "bob's connInfo" PQSupportOn SMSubscribe - pure () - where - servers srvs = (initAgentServersProxy_ SPMUnknown SPFProhibit) {smp = userServers srvs} - agentViaProxyRetryOffline :: IO () agentViaProxyRetryOffline = do let srv1 = SMPServer testHost testPort testKeyHash @@ -442,14 +426,14 @@ agentViaProxyRetryNoSession = do testNoProxy :: AStoreType -> IO () testNoProxy msType = do withSmpServerConfigOn (transport @TLS) (cfgMS msType) testPort2 $ \_ -> do - testSMPClient_ "127.0.0.1" testPort2 proxyVRangeV8 Nothing $ \(th :: THandleSMP TLS 'TClient) -> do + testSMPClient_ "127.0.0.1" testPort2 supportedServerSMPRelayVRange Nothing $ \(th :: THandleSMP TLS 'TClient) -> do (_, _, reply) <- sendRecv th (Nothing, "0", NoEntity, SMP.PRXY testSMPServer Nothing) reply `shouldBe` Right (SMP.ERR $ SMP.PROXY SMP.BASIC_AUTH) testProxyAuth :: AStoreType -> IO () testProxyAuth msType = do withSmpServerConfigOn (transport @TLS) proxyCfgAuth testPort $ \_ -> do - testSMPClient_ "127.0.0.1" testPort proxyVRangeV8 Nothing $ \(th :: THandleSMP TLS 'TClient) -> do + testSMPClient_ "127.0.0.1" testPort supportedServerSMPRelayVRange Nothing $ \(th :: THandleSMP TLS 'TClient) -> do (_, _, reply) <- sendRecv th (Nothing, "0", NoEntity, SMP.PRXY testSMPServer2 $ Just "wrong") reply `shouldBe` Right (SMP.ERR $ SMP.PROXY SMP.BASIC_AUTH) where @@ -459,7 +443,7 @@ testProxyAuth msType = do -- On success the reply is PKEY; otherwise it is the proxy error for the relay connection. requestRelaySession :: IO (Either SMP.ErrorType SMP.BrokerMsg) requestRelaySession = - testSMPClient_ "localhost" testPort proxyVRangeV8 Nothing $ \(th :: THandleSMP TLS 'TClient) -> + testSMPClient_ "localhost" testPort supportedServerSMPRelayVRange Nothing $ \(th :: THandleSMP TLS 'TClient) -> (\(_, _, reply) -> reply) <$> sendRecv th (Nothing, "1", NoEntity, SMP.PRXY testSMPServer2 Nothing) -- Shared "phase 2" of the reconnection tests: start a healthy relay, confirm it is reachable @@ -468,7 +452,7 @@ requestRelaySession = requireProxyReconnect :: IO () requireProxyReconnect = withSmpServerConfigOn (transport @TLS) proxyCfgJ2 testPort2 $ \_ -> do - testSMPClient_ "127.0.0.1" testPort2 proxyVRangeV8 Nothing $ \(th :: THandleSMP TLS 'TClient) -> do + testSMPClient_ "127.0.0.1" testPort2 supportedServerSMPRelayVRange Nothing $ \(th :: THandleSMP TLS 'TClient) -> do (_, _, reply) <- sendRecv th (Nothing, "0", NoEntity, SMP.PING) reply `shouldBe` Right SMP.PONG threadDelay 1500000 -- > persistErrorInterval (1s), so the stored connection error has expired @@ -524,7 +508,7 @@ testAgentClientReconnectAfterCancel = threadDelay 1000000 -- let the connect to the stalling relay start, then kill it mid-flight cancel t withSmpServerConfigOn (transport @TLS) cfgJ2 testPort2 $ \_ -> do - testSMPClient_ "127.0.0.1" testPort2 proxyVRangeV8 Nothing $ \(th :: THandleSMP TLS 'TClient) -> do + testSMPClient_ "127.0.0.1" testPort2 supportedServerSMPRelayVRange Nothing $ \(th :: THandleSMP TLS 'TClient) -> do (_, _, reply) <- sendRecv th (Nothing, "0", NoEntity, SMP.PING) reply `shouldBe` Right SMP.PONG -- the relay is up and reachable, so a timeout can only be the poisoned var r <- timeout 8000000 $ runExceptT $ A.createConnection a NRMInteractive 1 True True SCMInvitation Nothing Nothing CR.IKPQOn SMSubscribe diff --git a/tests/ServerTests.hs b/tests/ServerTests.hs index 9dabf1963..116b4f0ec 100644 --- a/tests/ServerTests.hs +++ b/tests/ServerTests.hs @@ -56,7 +56,6 @@ import Simplex.Messaging.Server.StoreLog (StoreLogRecord (..), closeStoreLog) import Simplex.Messaging.Transport import Simplex.Messaging.Transport.Credentials import Simplex.Messaging.Util (whenM) -import Simplex.Messaging.Version (mkVersionRange) import System.Directory (doesDirectoryExist, doesFileExist, removeDirectoryRecursive, removeFile) import System.IO (IOMode (..), withFile) import System.TimeIt (timeItT) @@ -261,12 +260,12 @@ testCreateSecure = Resp "dabc" _ err5 <- sendRecv s ("", "dabc", sId, _SEND "hello") (err5, ERR AUTH) #== "rejects unsigned SEND" - let maxAllowedMessage = B.replicate (maxMessageLength currentClientSMPRelayVersion) '-' + let maxAllowedMessage = B.replicate maxMessageLength '-' Resp "bcda" _ OK <- signSendRecv s sKey ("bcda", sId, _SEND maxAllowedMessage) Resp "" _ (Msg mId3 msg3) <- tGet1 r (dec mId3 msg3, Right maxAllowedMessage) #== "delivers message of max size" - let biggerMessage = B.replicate (maxMessageLength currentClientSMPRelayVersion + 1) '-' + let biggerMessage = B.replicate (maxMessageLength + 1) '-' Resp "bcda" _ (ERR LARGE_MSG) <- signSendRecv s sKey ("bcda", sId, _SEND biggerMessage) pure () @@ -308,12 +307,12 @@ testCreateSndSecure = Resp "dabc" _ err5 <- sendRecv s ("", "dabc", sId, _SEND "hello") (err5, ERR AUTH) #== "rejects unsigned SEND" - let maxAllowedMessage = B.replicate (maxMessageLength currentClientSMPRelayVersion) '-' + let maxAllowedMessage = B.replicate maxMessageLength '-' Resp "bcda" _ OK <- signSendRecv s sKey ("bcda", sId, _SEND maxAllowedMessage) Resp "" _ (Msg mId3 msg3) <- tGet1 r (dec mId3 msg3, Right maxAllowedMessage) #== "delivers message of max size" - let biggerMessage = B.replicate (maxMessageLength currentClientSMPRelayVersion + 1) '-' + let biggerMessage = B.replicate (maxMessageLength + 1) '-' Resp "bcda" _ (ERR LARGE_MSG) <- signSendRecv s sKey ("bcda", sId, _SEND biggerMessage) pure () @@ -1272,7 +1271,7 @@ testTiming = describe "should have similar time for auth error, whether queue exists or not, for all key types" $ forM_ timingTests $ \tst -> it (testName tst) $ \(ATransport t, msType) -> - smpTest2Cfg (cfgMS msType) (mkVersionRange minServerSMPRelayVersion authCmdsSMPVersion) t $ \rh sh -> + smpTest2Cfg (cfgMS msType) supportedServerSMPRelayVRange t $ \rh sh -> testSameTiming rh sh tst msType where testName :: (C.AuthAlg, C.AuthAlg, Int) -> String @@ -1305,7 +1304,11 @@ testTiming = (dhPub, dhPriv :: C.PrivateKeyX25519) <- atomically $ C.generateKeyPair g Resp "abcd" NoEntity (Ids rId sId srvDh) <- signSendRecv rh rKey ("abcd", NoEntity, New rPub dhPub) let dec = decryptMsgV3 $ C.dh' srvDh dhPriv - Resp "cdab" _ OK <- signSendRecv rh rKey ("cdab", rId, SUB) + Resp "cdab" _ resp <- signSendRecv rh rKey ("cdab", rId, SUB) + case resp of + OK -> pure () + SOK Nothing -> pure () + r -> expectationFailure $ "unexpected response: " <> show r (_, badKey) <- atomically $ C.generateAuthKeyPair badKeyAlg g runTimingTest rh badKey rId SUB