diff --git a/src/Simplex/Messaging/Agent.hs b/src/Simplex/Messaging/Agent.hs index 5d9a7ceef..1084881ad 100644 --- a/src/Simplex/Messaging/Agent.hs +++ b/src/Simplex/Messaging/Agent.hs @@ -192,7 +192,27 @@ import Simplex.Messaging.Encoding.String import Simplex.Messaging.Notifications.Protocol (DeviceToken, NtfRegCode (NtfRegCode), NtfTknStatus (..), NtfTokenId, PNMessageData (..), pnMessagesP) import Simplex.Messaging.Notifications.Types import Simplex.Messaging.Parsers (parse) -import Simplex.Messaging.Protocol (BrokerMsg, Cmd (..), ErrorType (AUTH), MsgBody, MsgFlags (..), NtfServer, ProtoServerWithAuth (..), ProtocolType (..), ProtocolTypeI (..), QueueLinkData, SMPMsgMeta, SParty (..), SProtocolType (..), SndPublicAuthKey, SubscriptionMode (..), UserProtocol, VersionSMPC) +import Simplex.Messaging.Protocol + ( BrokerMsg, + Cmd (..), + ErrorType (AUTH), + MsgBody, + MsgFlags (..), + NtfServer, + ProtoServerWithAuth (..), + ProtocolType (..), + ProtocolTypeI (..), + QueueLinkData, + QueueMode (..), + SMPMsgMeta, + SParty (..), + SProtocolType (..), + SndPublicAuthKey, + SubscriptionMode (..), + UserProtocol, + VersionSMPC, + senderCanSecure, + ) import qualified Simplex.Messaging.Protocol as SMP import Simplex.Messaging.ServiceScheme (ServiceScheme (..)) import qualified Simplex.Messaging.TMap as TM @@ -831,7 +851,7 @@ setContactShortLink' c connId userData = pure (linkId, shortLinkKey, (linkEncFixedData, d)) Nothing -> do sigKeys@(_, privSigKey) <- atomically $ C.generateKeyPair @'C.Ed25519 g - let qUri = SMPQueueUri vr $ SMPQueueAddress server sndId (C.publicKey e2ePrivKey) False + let qUri = SMPQueueUri vr $ SMPQueueAddress server sndId (C.publicKey e2ePrivKey) (Just QMContact) connReq = CRContactUri $ ConnReqUriData SSSimplex smpAgentVRange [qUri] Nothing (linkKey, linkData) = SL.encodeSignLinkData sigKeys smpAgentVRange connReq userData (linkId, k) = SL.contactShortLinkKdf linkKey @@ -937,8 +957,8 @@ newRcvConnSrv c userId connId enableNtfs cMode userData_ clientData pqInitKeys s AgentConfig {smpClientVRange = vr, smpAgentVRange} <- asks config -- TODO [notifications] the remaining 24 bytes are reserved for notifier ID let sndId = SMP.EntityId $ B.take 24 $ C.sha3_384 corrId - sndSecure = case cMode of SCMContact -> False; SCMInvitation -> True - qUri = SMPQueueUri vr $ SMPQueueAddress srv sndId e2eDhKey sndSecure + qm = case cMode of SCMContact -> QMContact; SCMInvitation -> QMMessaging + qUri = SMPQueueUri vr $ SMPQueueAddress srv sndId e2eDhKey (Just qm) connReq <- createConnReq qUri let (linkKey, linkData) = SL.encodeSignLinkData sigKeys smpAgentVRange connReq userData qd <- case cMode of @@ -1647,7 +1667,7 @@ submitPendingMsg c cData sq = do void $ getDeliveryWorker True c cData sq runSmpQueueMsgDelivery :: AgentClient -> ConnData -> SndQueue -> (Worker, TMVar ()) -> AM () -runSmpQueueMsgDelivery c@AgentClient {subQ} ConnData {connId} sq@SndQueue {userId, server, sndSecure} (Worker {doWork}, qLock) = do +runSmpQueueMsgDelivery c@AgentClient {subQ} ConnData {connId} sq@SndQueue {userId, server, queueMode} (Worker {doWork}, qLock) = do AgentConfig {messageRetryInterval = ri, messageTimeout, helloTimeout, quotaExceededTimeout} <- asks config forever $ do atomically $ endAgentOperation c AOSndNetwork @@ -1733,7 +1753,7 @@ runSmpQueueMsgDelivery c@AgentClient {subQ} ConnData {connId} sq@SndQueue {userI Right proxySrv_ -> do case msgType of AM_CONN_INFO - | sndSecure -> notify (CON pqEncryption) >> setStatus Active + | senderCanSecure queueMode -> notify (CON pqEncryption) >> setStatus Active | otherwise -> setStatus Confirmed AM_CONN_INFO_REPLY -> setStatus Confirmed AM_RATCHET_INFO -> pure () @@ -2558,7 +2578,7 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), _v, sessId mapM_ (atomically . writeTBQueue subQ) . reverse =<< readTVarIO pending processSMP :: forall c. RcvQueue -> Connection c -> ConnData -> BrokerMsg -> TVar [ATransmission] -> AM () processSMP - rq@RcvQueue {rcvId = rId, sndSecure, e2ePrivKey, e2eDhSecret, status} + rq@RcvQueue {rcvId = rId, queueMode, e2ePrivKey, e2eDhSecret, status} conn cData@ConnData {connId, connAgentVersion, ratchetSyncState = rss} smpMsg @@ -2587,7 +2607,7 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), _v, sessId (SMP.PHConfirmation senderKey, AgentConfirmation {e2eEncryption_, encConnInfo, agentVersion}) -> smpConfirmation srvMsgId conn (Just senderKey) e2ePubKey e2eEncryption_ encConnInfo phVer agentVersion >> ack (SMP.PHEmpty, AgentConfirmation {e2eEncryption_, encConnInfo, agentVersion}) - | sndSecure -> smpConfirmation srvMsgId conn Nothing e2ePubKey e2eEncryption_ encConnInfo phVer agentVersion >> ack + | senderCanSecure queueMode -> smpConfirmation srvMsgId conn Nothing e2ePubKey e2eEncryption_ encConnInfo phVer agentVersion >> ack | otherwise -> prohibited "handshake: missing sender key" >> ack (SMP.PHEmpty, AgentInvitation {connReq, connInfo}) -> smpInvitation srvMsgId conn connReq connInfo >> ack @@ -3133,7 +3153,7 @@ secureConfirmQueue c cData@ConnData {connId, connAgentVersion, pqSupport} sq srv pure . smpEncode $ AgentConfirmation {agentVersion = connAgentVersion, e2eEncryption_, encConnInfo} agentSecureSndQueue :: AgentClient -> ConnData -> SndQueue -> AM SndQueueSecured -agentSecureSndQueue c ConnData {connAgentVersion} sq@SndQueue {sndSecure, status} +agentSecureSndQueue c ConnData {connAgentVersion} sq@SndQueue {queueMode, status} | sndSecure && status == New = do secureSndQueue c sq withStore' c $ \db -> setSndQueueStatus db sq Secured @@ -3142,6 +3162,7 @@ agentSecureSndQueue c ConnData {connAgentVersion} sq@SndQueue {sndSecure, status | sndSecure && status == Secured = pure initiatorRatchetOnConf | otherwise = pure False where + sndSecure = senderCanSecure queueMode initiatorRatchetOnConf = connAgentVersion >= ratchetOnConfSMPAgentVersion mkAgentConfirmation :: AgentClient -> ConnData -> SndQueue -> SMPServerWithAuth -> ConnInfo -> SubscriptionMode -> AM AgentMessage @@ -3226,7 +3247,7 @@ agentRatchetDecrypt' g db connId rc encAgentMsg = do liftEither $ bimap (SEAgentError . cryptoError) (,CR.rcRcvKEM rc') agentMsgBody_ newSndQueue :: UserId -> ConnId -> Compatible SMPQueueInfo -> Maybe (C.AAuthKeyPair) -> AM' (NewSndQueue, C.PublicKeyX25519) -newSndQueue userId connId (Compatible (SMPQueueInfo smpClientVersion SMPQueueAddress {smpServer, senderId, sndSecure, dhPublicKey = rcvE2ePubDhKey})) sndKeys_ = do +newSndQueue userId connId (Compatible (SMPQueueInfo smpClientVersion SMPQueueAddress {smpServer, senderId, queueMode, dhPublicKey = rcvE2ePubDhKey})) sndKeys_ = do C.AuthAlg a <- asks $ sndAuthAlg . config g <- asks random (sndPublicKey, sndPrivateKey) <- maybe (atomically $ C.generateAuthKeyPair a g) pure sndKeys_ @@ -3237,7 +3258,7 @@ newSndQueue userId connId (Compatible (SMPQueueInfo smpClientVersion SMPQueueAdd connId, server = smpServer, sndId = senderId, - sndSecure, + queueMode, sndPublicKey, sndPrivateKey, e2eDhSecret = C.dh' rcvE2ePubDhKey e2ePrivKey, diff --git a/src/Simplex/Messaging/Agent/Client.hs b/src/Simplex/Messaging/Agent/Client.hs index 2355cdefa..240b25f7e 100644 --- a/src/Simplex/Messaging/Agent/Client.hs +++ b/src/Simplex/Messaging/Agent/Client.hs @@ -1390,7 +1390,7 @@ newRcvQueue_ c userId connId (ProtoServerWithAuth srv auth) vRange cqrd subMode e2ePrivKey, e2eDhSecret = Nothing, sndId, - sndSecure, + queueMode, shortLink, status = New, dbQueueId = DBNewQueue, @@ -1401,9 +1401,7 @@ newRcvQueue_ c userId connId (ProtoServerWithAuth srv auth) vRange cqrd subMode clientNtfCreds = Nothing, deleteErrors = 0 } - qUri = SMPQueueUri vRange $ SMPQueueAddress srv sndId e2eDhKey sndSecure - -- TODO [short links] maybe switch to queue mode? - sndSecure = senderCanSecure queueMode + qUri = SMPQueueUri vRange $ SMPQueueAddress srv sndId e2eDhKey queueMode pure (rq, qUri, tSess, sessionId thParams') where mkShortLinkCreds :: (THandleParams SMPVersion 'TClient, QueueIdsKeys) -> AM (Maybe ShortLinkCreds) @@ -1621,8 +1619,8 @@ logSecret' = B64.encode . B.take 3 {-# INLINE logSecret' #-} sendConfirmation :: AgentClient -> SndQueue -> ByteString -> AM (Maybe SMPServer) -sendConfirmation c sq@SndQueue {userId, server, connId, sndId, sndSecure, sndPublicKey, sndPrivateKey, e2ePubKey = e2ePubKey@Just {}} agentConfirmation = do - let (privHdr, spKey) = if sndSecure then (SMP.PHEmpty, Just sndPrivateKey) else (SMP.PHConfirmation sndPublicKey, Nothing) +sendConfirmation c sq@SndQueue {userId, server, connId, sndId, queueMode, sndPublicKey, sndPrivateKey, e2ePubKey = e2ePubKey@Just {}} agentConfirmation = do + let (privHdr, spKey) = if senderCanSecure queueMode then (SMP.PHEmpty, Just sndPrivateKey) else (SMP.PHConfirmation sndPublicKey, Nothing) clientMsg = SMP.ClientMessage privHdr agentConfirmation msg <- agentCbEncrypt sq e2ePubKey $ smpEncode clientMsg sendOrProxySMPMessage c userId server connId "" spKey sndId (MsgFlags {notification = True}) msg diff --git a/src/Simplex/Messaging/Agent/Protocol.hs b/src/Simplex/Messaging/Agent/Protocol.hs index e596b3e5d..dcf9e0329 100644 --- a/src/Simplex/Messaging/Agent/Protocol.hs +++ b/src/Simplex/Messaging/Agent/Protocol.hs @@ -207,6 +207,7 @@ import Simplex.Messaging.Protocol MsgId, NMsgMeta, ProtocolServer (..), + QueueMode (..), SMPClientVersion, SMPServer, SMPServerWithAuth, @@ -222,6 +223,8 @@ import Simplex.Messaging.Protocol sameSrvAddr, sndAuthKeySMPClientVersion, srvHostnamesSMPClientVersion, + shortLinksSMPClientVersion, + senderCanSecure, pattern ProtoServerWithAuth, pattern SMPServer, ) @@ -1166,16 +1169,20 @@ data SMPQueueInfo = SMPQueueInfo {clientVersion :: VersionSMPC, queueAddress :: deriving (Eq, Show) instance Encoding SMPQueueInfo where - smpEncode (SMPQueueInfo clientVersion SMPQueueAddress {smpServer, senderId, dhPublicKey, sndSecure}) - | clientVersion >= sndAuthKeySMPClientVersion && sndSecure = smpEncode (clientVersion, smpServer, senderId, dhPublicKey, sndSecure) - | clientVersion > initialSMPClientVersion = smpEncode (clientVersion, smpServer, senderId, dhPublicKey) + smpEncode (SMPQueueInfo clientVersion SMPQueueAddress {smpServer, senderId, dhPublicKey, queueMode}) + | clientVersion >= shortLinksSMPClientVersion = addrEnc <> smpEncode queueMode + | clientVersion >= sndAuthKeySMPClientVersion && sndSecure = addrEnc <> smpEncode sndSecure + | clientVersion > initialSMPClientVersion = addrEnc | otherwise = smpEncode clientVersion <> legacyEncodeServer smpServer <> smpEncode (senderId, dhPublicKey) + where + addrEnc = smpEncode (clientVersion, smpServer, senderId, dhPublicKey) + sndSecure = senderCanSecure queueMode smpP = do clientVersion <- smpP smpServer <- if clientVersion > initialSMPClientVersion then smpP else updateSMPServerHosts <$> legacyServerP (senderId, dhPublicKey) <- smpP - sndSecure <- fromMaybe False <$> optional smpP - pure $ SMPQueueInfo clientVersion SMPQueueAddress {smpServer, senderId, dhPublicKey, sndSecure} + queueMode <- smpP <|> optional ((\ss -> if ss then QMMessaging else QMContact) <$> smpP) + pure $ SMPQueueInfo clientVersion SMPQueueAddress {smpServer, senderId, dhPublicKey, queueMode} -- This instance seems contrived and there was a temptation to split a common part of both types. -- But this is created to allow backward and forward compatibility where SMPQueueUri @@ -1202,7 +1209,7 @@ data SMPQueueAddress = SMPQueueAddress { smpServer :: SMPServer, senderId :: SMP.SenderId, dhPublicKey :: C.PublicKeyX25519, - sndSecure :: Bool -- TODO [short links] replace with queueMode? + queueMode :: Maybe QueueMode } deriving (Eq, Show) @@ -1229,42 +1236,57 @@ sameQAddress (srv, qId) (srv', qId') = sameSrvAddr srv srv' && qId == qId' {-# INLINE sameQAddress #-} instance StrEncoding SMPQueueUri where - strEncode (SMPQueueUri vr SMPQueueAddress {smpServer = srv, senderId = qId, dhPublicKey, sndSecure}) + strEncode (SMPQueueUri vr SMPQueueAddress {smpServer = srv, senderId = qId, dhPublicKey, queueMode}) | minVersion vr >= srvHostnamesSMPClientVersion = strEncode srv <> "/" <> strEncode qId <> "#/?" <> query queryParams | otherwise = legacyStrEncodeServer srv <> "/" <> strEncode qId <> "#/?" <> query (queryParams <> srvParam) where query = strEncode . QSP QEscape - queryParams = [("v", strEncode vr), ("dh", strEncode dhPublicKey)] <> [("k", "s") | sndSecure] + queryParams = [("v", strEncode vr), ("dh", strEncode dhPublicKey)] <> queueModeParam <> sndSecureParam + where + queueModeParam = case queueMode of + Just QMMessaging -> [("q", "m")] + Just QMContact -> [("q", "c")] + Nothing -> [] + sndSecureParam = [("k", "s") | senderCanSecure queueMode && minVersion vr < shortLinksSMPClientVersion] srvParam = [("srv", strEncode $ TransportHosts_ hs) | not (null hs)] hs = L.tail $ host srv strP = do srv@ProtocolServer {host = h :| host} <- strP <* A.char '/' senderId <- strP <* optional (A.char '/') <* A.char '#' - (vr, hs, dhPublicKey, sndSecure) <- versioned <|> unversioned + (vr, hs, dhPublicKey, queueMode) <- versioned <|> unversioned let srv' = srv {host = h :| host <> hs} smpServer = if maxVersion vr < srvHostnamesSMPClientVersion then updateSMPServerHosts srv' else srv' - pure $ SMPQueueUri vr SMPQueueAddress {smpServer, senderId, dhPublicKey, sndSecure} + pure $ SMPQueueUri vr SMPQueueAddress {smpServer, senderId, dhPublicKey, queueMode} where - unversioned = (versionToRange initialSMPClientVersion,[],,False) <$> strP <* A.endOfInput + unversioned = (versionToRange initialSMPClientVersion,[],,Nothing) <$> strP <* A.endOfInput versioned = do dhKey_ <- optional strP query <- optional (A.char '/') *> A.char '?' *> strP vr <- queryParam "v" query dhKey <- maybe (queryParam "dh" query) pure dhKey_ hs_ <- queryParam_ "srv" query - let sndSecure = queryParamStr "k" query == Just "s" - pure (vr, maybe [] thList_ hs_, dhKey, sndSecure) + let queueMode = case queryParamStr "q" query of + Just "m" -> Just QMMessaging + Just "c" -> Just QMContact + _ | queryParamStr "k" query == Just "s" -> Just QMMessaging + _ -> Nothing + pure (vr, maybe [] thList_ hs_, dhKey, queueMode) instance Encoding SMPQueueUri where - smpEncode (SMPQueueUri clientVRange SMPQueueAddress {smpServer, senderId, dhPublicKey, sndSecure}) - | maxVersion clientVRange >= sndAuthKeySMPClientVersion && sndSecure = - smpEncode (clientVRange, smpServer, senderId, dhPublicKey, sndSecure) - | otherwise = - smpEncode (clientVRange, smpServer, senderId, dhPublicKey) + smpEncode (SMPQueueUri clientVRange@(VersionRange minV maxV) SMPQueueAddress {smpServer, senderId, dhPublicKey, queueMode}) + -- The condition is for minVersion as earlier clients won't be able to support it. + -- The alternative would be to encode both queueMode and sndSecure + | minV >= shortLinksSMPClientVersion = addrEnc <> smpEncode queueMode + -- Earlier versions won't be able to ignore sndSecure, so we don't include it when it is False + | minV >= sndAuthKeySMPClientVersion || (maxV >= sndAuthKeySMPClientVersion && sndSecure) = addrEnc <> smpEncode sndSecure + | otherwise = addrEnc + where + addrEnc = smpEncode (clientVRange, smpServer, senderId, dhPublicKey) + sndSecure = senderCanSecure queueMode smpP = do (clientVRange, smpServer, senderId, dhPublicKey) <- smpP - sndSecure <- fromMaybe False <$> optional smpP - pure $ SMPQueueUri clientVRange SMPQueueAddress {smpServer, senderId, dhPublicKey, sndSecure} + queueMode <- smpP <|> optional ((\ss -> if ss then QMMessaging else QMContact) <$> smpP) + pure $ SMPQueueUri clientVRange SMPQueueAddress {smpServer, senderId, dhPublicKey, queueMode} data ConnectionRequestUri (m :: ConnectionMode) where CRInvitationUri :: ConnReqUriData -> RcvE2ERatchetParamsUri 'C.X448 -> ConnectionRequestUri CMInvitation diff --git a/src/Simplex/Messaging/Agent/Store.hs b/src/Simplex/Messaging/Agent/Store.hs index a04422780..79e194cf1 100644 --- a/src/Simplex/Messaging/Agent/Store.hs +++ b/src/Simplex/Messaging/Agent/Store.hs @@ -43,10 +43,10 @@ import Simplex.Messaging.Protocol NotifierId, NtfPrivateAuthKey, NtfPublicAuthKey, + QueueMode, RcvDhSecret, RcvNtfDhSecret, RcvPrivateAuthKey, - SenderCanSecure, SndPrivateAuthKey, SndPublicAuthKey, VersionSMPC, @@ -92,7 +92,7 @@ data StoredRcvQueue (q :: QueueStored) = RcvQueue -- | sender queue ID sndId :: SMP.SenderId, -- | sender can secure the queue - sndSecure :: SenderCanSecure, + queueMode :: Maybe QueueMode, -- | short link ID and credentials shortLink :: Maybe ShortLinkCreds, -- | queue status @@ -172,7 +172,7 @@ data StoredSndQueue (q :: QueueStored) = SndQueue -- | sender queue ID sndId :: SMP.SenderId, -- | sender can secure the queue - sndSecure :: SenderCanSecure, + queueMode :: Maybe QueueMode, -- | key pair used by the sender to authorize transmissions -- TODO combine keys to key pair so that types match sndPublicKey :: SndPublicAuthKey, diff --git a/src/Simplex/Messaging/Agent/Store/AgentStore.hs b/src/Simplex/Messaging/Agent/Store/AgentStore.hs index 3b7f69d14..237787590 100644 --- a/src/Simplex/Messaging/Agent/Store/AgentStore.hs +++ b/src/Simplex/Messaging/Agent/Store/AgentStore.hs @@ -1971,12 +1971,12 @@ insertRcvQueue_ db connId' rq@RcvQueue {..} serverKeyHash_ = do [sql| INSERT INTO rcv_queues ( host, port, rcv_id, conn_id, rcv_private_key, rcv_dh_secret, e2e_priv_key, e2e_dh_secret, - snd_id, snd_secure, status, rcv_queue_id, rcv_primary, replace_rcv_queue_id, smp_client_version, server_key_hash, + snd_id, queue_mode, status, rcv_queue_id, rcv_primary, replace_rcv_queue_id, smp_client_version, server_key_hash, link_id, link_key, link_priv_sig_key, link_enc_fixed_data ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?); |] ( (host server, port server, rcvId, connId', rcvPrivateKey, rcvDhSecret, e2ePrivKey, e2eDhSecret) - :. (sndId, BI sndSecure, status, qId, BI primary, dbReplaceQueueId, smpClientVersion, serverKeyHash_) + :. (sndId, queueMode, status, qId, BI primary, dbReplaceQueueId, smpClientVersion, serverKeyHash_) :. (shortLinkId <$> shortLink, shortLinkKey <$> shortLink, linkPrivSigKey <$> shortLink, linkEncFixedData <$> shortLink) ) pure (rq :: NewRcvQueue) {connId = connId', dbQueueId = qId} @@ -1993,14 +1993,14 @@ insertSndQueue_ db connId' sq@SndQueue {..} serverKeyHash_ = do db [sql| INSERT INTO snd_queues - (host, port, snd_id, snd_secure, conn_id, snd_public_key, snd_private_key, e2e_pub_key, e2e_dh_secret, + (host, port, snd_id, queue_mode, conn_id, snd_public_key, snd_private_key, e2e_pub_key, e2e_dh_secret, status, snd_queue_id, snd_primary, replace_snd_queue_id, smp_client_version, server_key_hash) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON CONFLICT (host, port, snd_id) DO UPDATE SET host=EXCLUDED.host, port=EXCLUDED.port, snd_id=EXCLUDED.snd_id, - snd_secure=EXCLUDED.snd_secure, + queue_mode=EXCLUDED.queue_mode, conn_id=EXCLUDED.conn_id, snd_public_key=EXCLUDED.snd_public_key, snd_private_key=EXCLUDED.snd_private_key, @@ -2013,7 +2013,7 @@ insertSndQueue_ db connId' sq@SndQueue {..} serverKeyHash_ = do smp_client_version=EXCLUDED.smp_client_version, server_key_hash=EXCLUDED.server_key_hash |] - ((host server, port server, sndId, BI sndSecure, connId', sndPublicKey, sndPrivateKey, e2ePubKey, e2eDhSecret) + ((host server, port server, sndId, queueMode, connId', sndPublicKey, sndPrivateKey, e2ePubKey, e2eDhSecret) :. (status, qId, BI primary, dbReplaceQueueId, smpClientVersion, serverKeyHash_)) pure (sq :: NewSndQueue) {connId = connId', dbQueueId = qId} @@ -2144,7 +2144,7 @@ rcvQueueQuery :: Query rcvQueueQuery = [sql| SELECT c.user_id, COALESCE(q.server_key_hash, s.key_hash), q.conn_id, q.host, q.port, q.rcv_id, q.rcv_private_key, q.rcv_dh_secret, - q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.snd_secure, q.status, + q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.queue_mode, q.status, q.rcv_queue_id, q.rcv_primary, q.replace_rcv_queue_id, q.switch_status, q.smp_client_version, q.delete_errors, q.ntf_public_key, q.ntf_private_key, q.ntf_id, q.rcv_ntf_dh_secret, q.link_id, q.link_key, q.link_priv_sig_key, q.link_enc_fixed_data @@ -2154,13 +2154,13 @@ rcvQueueQuery = |] toRcvQueue :: - (UserId, C.KeyHash, ConnId, NonEmpty TransportHost, ServiceName, SMP.RecipientId, SMP.RcvPrivateAuthKey, SMP.RcvDhSecret, C.PrivateKeyX25519, Maybe C.DhSecretX25519, SMP.SenderId, BoolInt) + (UserId, C.KeyHash, ConnId, NonEmpty TransportHost, ServiceName, SMP.RecipientId, SMP.RcvPrivateAuthKey, SMP.RcvDhSecret, C.PrivateKeyX25519, Maybe C.DhSecretX25519, SMP.SenderId, Maybe QueueMode) :. (QueueStatus, DBQueueId 'QSStored, BoolInt, Maybe Int64, Maybe RcvSwitchStatus, Maybe VersionSMPC, Int) :. (Maybe SMP.NtfPublicAuthKey, Maybe SMP.NtfPrivateAuthKey, Maybe SMP.NotifierId, Maybe RcvNtfDhSecret) :. (Maybe SMP.LinkId, Maybe LinkKey, Maybe C.PrivateKeyEd25519, Maybe EncDataBytes) -> RcvQueue toRcvQueue - ( (userId, keyHash, connId, host, port, rcvId, rcvPrivateKey, rcvDhSecret, e2ePrivKey, e2eDhSecret, sndId, BI sndSecure) + ( (userId, keyHash, connId, host, port, rcvId, rcvPrivateKey, rcvDhSecret, e2ePrivKey, e2eDhSecret, sndId, queueMode) :. (status, dbQueueId, BI primary, dbReplaceQueueId, rcvSwchStatus, smpClientVersion_, deleteErrors) :. (ntfPublicKey_, ntfPrivateKey_, notifierId_, rcvNtfDhSecret_) :. (shortLinkId_, shortLinkKey_, linkPrivSigKey_, linkEncFixedData_) @@ -2173,7 +2173,7 @@ toRcvQueue shortLink = case (shortLinkId_, shortLinkKey_, linkPrivSigKey_, linkEncFixedData_) of (Just shortLinkId, Just shortLinkKey, Just linkPrivSigKey, Just linkEncFixedData) -> Just ShortLinkCreds {shortLinkId, shortLinkKey, linkPrivSigKey, linkEncFixedData} _ -> Nothing - in RcvQueue {userId, connId, server, rcvId, rcvPrivateKey, rcvDhSecret, e2ePrivKey, e2eDhSecret, sndId, sndSecure, shortLink, status, dbQueueId, primary, dbReplaceQueueId, rcvSwchStatus, smpClientVersion, clientNtfCreds, deleteErrors} + in RcvQueue {userId, connId, server, rcvId, rcvPrivateKey, rcvDhSecret, e2ePrivKey, e2eDhSecret, sndId, queueMode, shortLink, status, dbQueueId, primary, dbReplaceQueueId, rcvSwchStatus, smpClientVersion, clientNtfCreds, deleteErrors} getRcvQueueById :: DB.Connection -> ConnId -> Int64 -> IO (Either StoreError RcvQueue) getRcvQueueById db connId dbRcvId = @@ -2194,7 +2194,7 @@ sndQueueQuery :: Query sndQueueQuery = [sql| SELECT - c.user_id, COALESCE(q.server_key_hash, s.key_hash), q.conn_id, q.host, q.port, q.snd_id, q.snd_secure, + c.user_id, COALESCE(q.server_key_hash, s.key_hash), q.conn_id, q.host, q.port, q.snd_id, q.queue_mode, q.snd_public_key, q.snd_private_key, q.e2e_pub_key, q.e2e_dh_secret, q.status, q.snd_queue_id, q.snd_primary, q.replace_snd_queue_id, q.switch_status, q.smp_client_version FROM snd_queues q @@ -2203,18 +2203,18 @@ sndQueueQuery = |] toSndQueue :: - (UserId, C.KeyHash, ConnId, NonEmpty TransportHost, ServiceName, SenderId, BoolInt) + (UserId, C.KeyHash, ConnId, NonEmpty TransportHost, ServiceName, SenderId, Maybe QueueMode) :. (Maybe SndPublicAuthKey, SndPrivateAuthKey, Maybe C.PublicKeyX25519, C.DhSecretX25519, QueueStatus) :. (DBQueueId 'QSStored, BoolInt, Maybe Int64, Maybe SndSwitchStatus, VersionSMPC) -> SndQueue toSndQueue - ( (userId, keyHash, connId, host, port, sndId, BI sndSecure) + ( (userId, keyHash, connId, host, port, sndId, queueMode) :. (sndPubKey, sndPrivateKey@(C.APrivateAuthKey a pk), e2ePubKey, e2eDhSecret, status) :. (dbQueueId, BI primary, dbReplaceQueueId, sndSwchStatus, smpClientVersion) ) = let server = SMPServer host port keyHash sndPublicKey = fromMaybe (C.APublicAuthKey a (C.publicKey pk)) sndPubKey - in SndQueue {userId, connId, server, sndId, sndSecure, sndPublicKey, sndPrivateKey, e2ePubKey, e2eDhSecret, status, dbQueueId, primary, dbReplaceQueueId, sndSwchStatus, smpClientVersion} + in SndQueue {userId, connId, server, sndId, queueMode, sndPublicKey, sndPrivateKey, e2ePubKey, e2eDhSecret, status, dbQueueId, primary, dbReplaceQueueId, sndSwchStatus, smpClientVersion} getSndQueueById :: DB.Connection -> ConnId -> Int64 -> IO (Either StoreError SndQueue) getSndQueueById db connId dbSndId = diff --git a/src/Simplex/Messaging/Agent/Store/Postgres/Migrations/M20250322_short_links.hs b/src/Simplex/Messaging/Agent/Store/Postgres/Migrations/M20250322_short_links.hs index 774ad50d6..be627ea0a 100644 --- a/src/Simplex/Messaging/Agent/Store/Postgres/Migrations/M20250322_short_links.hs +++ b/src/Simplex/Messaging/Agent/Store/Postgres/Migrations/M20250322_short_links.hs @@ -17,6 +17,14 @@ ALTER TABLE rcv_queues ADD COLUMN link_enc_fixed_data BYTEA; CREATE UNIQUE INDEX idx_rcv_queues_link_id ON rcv_queues(host, port, link_id); +ALTER TABLE rcv_queues ADD COLUMN queue_mode TEXT; +UPDATE rcv_queues SET queue_mode = 'M' WHERE snd_secure = 1; +ALTER TABLE rcv_queues DROP COLUMN snd_secure; + +ALTER TABLE snd_queues ADD COLUMN queue_mode TEXT; +UPDATE snd_queues SET queue_mode = 'M' WHERE snd_secure = 1; +ALTER TABLE snd_queues DROP COLUMN snd_secure; + CREATE TABLE inv_short_links( inv_short_link_id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, host TEXT NOT NULL, @@ -44,4 +52,12 @@ ALTER TABLE rcv_queues DROP COLUMN link_enc_fixed_data; DROP INDEX idx_inv_short_links_link_id; DROP TABLE inv_short_links; + +ALTER TABLE rcv_queues ADD COLUMN snd_secure INTEGER NOT NULL DEFAULT 0; +UPDATE rcv_queues SET snd_secure = 1 WHERE queue_mode = 'M'; +ALTER TABLE rcv_queues DROP COLUMN queue_mode; + +ALTER TABLE snd_queues ADD COLUMN snd_secure INTEGER NOT NULL DEFAULT 0; +UPDATE snd_queues SET snd_secure = 1 WHERE queue_mode = 'M'; +ALTER TABLE snd_queues DROP COLUMN queue_mode; |] diff --git a/src/Simplex/Messaging/Agent/Store/SQLite/Migrations/M20250322_short_links.hs b/src/Simplex/Messaging/Agent/Store/SQLite/Migrations/M20250322_short_links.hs index 70a15b8cc..c6cbd423b 100644 --- a/src/Simplex/Messaging/Agent/Store/SQLite/Migrations/M20250322_short_links.hs +++ b/src/Simplex/Messaging/Agent/Store/SQLite/Migrations/M20250322_short_links.hs @@ -15,6 +15,14 @@ ALTER TABLE rcv_queues ADD COLUMN link_enc_fixed_data BLOB; CREATE UNIQUE INDEX idx_rcv_queues_link_id ON rcv_queues(host, port, link_id); +ALTER TABLE rcv_queues ADD COLUMN queue_mode TEXT; +UPDATE rcv_queues SET queue_mode = 'M' WHERE snd_secure = 1; +ALTER TABLE rcv_queues DROP COLUMN snd_secure; + +ALTER TABLE snd_queues ADD COLUMN queue_mode TEXT; +UPDATE snd_queues SET queue_mode = 'M' WHERE snd_secure = 1; +ALTER TABLE snd_queues DROP COLUMN snd_secure; + CREATE TABLE inv_short_links( inv_short_link_id INTEGER PRIMARY KEY AUTOINCREMENT, host TEXT NOT NULL, @@ -41,4 +49,12 @@ ALTER TABLE rcv_queues DROP COLUMN link_enc_fixed_data; DROP INDEX idx_inv_short_links_link_id; DROP TABLE inv_short_links; + +ALTER TABLE rcv_queues ADD COLUMN snd_secure INTEGER NOT NULL DEFAULT 0; +UPDATE rcv_queues SET snd_secure = 1 WHERE queue_mode = 'M'; +ALTER TABLE rcv_queues DROP COLUMN queue_mode; + +ALTER TABLE snd_queues ADD COLUMN snd_secure INTEGER NOT NULL DEFAULT 0; +UPDATE snd_queues SET snd_secure = 1 WHERE queue_mode = 'M'; +ALTER TABLE snd_queues DROP COLUMN queue_mode; |] diff --git a/src/Simplex/Messaging/Agent/Store/SQLite/Migrations/agent_schema.sql b/src/Simplex/Messaging/Agent/Store/SQLite/Migrations/agent_schema.sql index 5db250ad0..fde671d7a 100644 --- a/src/Simplex/Messaging/Agent/Store/SQLite/Migrations/agent_schema.sql +++ b/src/Simplex/Messaging/Agent/Store/SQLite/Migrations/agent_schema.sql @@ -55,12 +55,12 @@ CREATE TABLE rcv_queues( server_key_hash BLOB, switch_status TEXT, deleted INTEGER NOT NULL DEFAULT 0, - snd_secure INTEGER NOT NULL DEFAULT 0, last_broker_ts TEXT, link_id BLOB, link_key BLOB, link_priv_sig_key BLOB, link_enc_fixed_data BLOB, + queue_mode TEXT, PRIMARY KEY(host, port, rcv_id), FOREIGN KEY(host, port) REFERENCES servers ON DELETE RESTRICT ON UPDATE CASCADE, @@ -83,7 +83,7 @@ CREATE TABLE snd_queues( replace_snd_queue_id INTEGER NULL, server_key_hash BLOB, switch_status TEXT, - snd_secure INTEGER NOT NULL DEFAULT 0, + queue_mode TEXT, PRIMARY KEY(host, port, snd_id), FOREIGN KEY(host, port) REFERENCES servers ON DELETE RESTRICT ON UPDATE CASCADE diff --git a/src/Simplex/Messaging/Protocol.hs b/src/Simplex/Messaging/Protocol.hs index fde0f8e68..b486a7a38 100644 --- a/src/Simplex/Messaging/Protocol.hs +++ b/src/Simplex/Messaging/Protocol.hs @@ -57,7 +57,6 @@ module Simplex.Messaging.Protocol ProtocolEncoding (..), Command (..), SubscriptionMode (..), - SenderCanSecure, NewQueueReq (..), QueueReqData (..), QueueMode (..), @@ -171,6 +170,7 @@ module Simplex.Messaging.Protocol legacyStrEncodeServer, srvHostnamesSMPClientVersion, sndAuthKeySMPClientVersion, + shortLinksSMPClientVersion, sameSrvAddr, sameSrvAddr', noAuthSrv, @@ -262,8 +262,11 @@ srvHostnamesSMPClientVersion = VersionSMPC 2 sndAuthKeySMPClientVersion :: VersionSMPC sndAuthKeySMPClientVersion = VersionSMPC 3 +shortLinksSMPClientVersion :: VersionSMPC +shortLinksSMPClientVersion = VersionSMPC 4 + currentSMPClientVersion :: VersionSMPC -currentSMPClientVersion = VersionSMPC 3 +currentSMPClientVersion = VersionSMPC 4 supportedSMPClientVRange :: VersionRangeSMPC supportedSMPClientVRange = mkVersionRange initialSMPClientVersion currentSMPClientVersion @@ -538,8 +541,6 @@ instance Encoding QueueReqData where -- smpEncode (NewNtfCreds authKey dhKey) = smpEncode (authKey, dhKey) -- smpP = NewNtfCreds <$> smpP <*> smpP -type SenderCanSecure = Bool - newtype EncTransmission = EncTransmission ByteString deriving (Show) diff --git a/src/Simplex/Messaging/Server/QueueStore/Postgres.hs b/src/Simplex/Messaging/Server/QueueStore/Postgres.hs index 6aca44640..186a1574d 100644 --- a/src/Simplex/Messaging/Server/QueueStore/Postgres.hs +++ b/src/Simplex/Messaging/Server/QueueStore/Postgres.hs @@ -44,6 +44,7 @@ import Data.List (intersperse) import qualified Data.Map.Strict as M import Data.Maybe (catMaybes, fromMaybe) import qualified Data.Text as T +import Data.Text.Encoding (decodeLatin1, encodeUtf8) import Data.Time.Clock.System (SystemTime (..), getSystemTime) import Database.PostgreSQL.Simple (Binary (..), Only (..), Query, SqlError, (:.) (..)) import qualified Database.PostgreSQL.Simple as DB @@ -57,6 +58,7 @@ import Simplex.Messaging.Agent.Client (withLockMap) import Simplex.Messaging.Agent.Lock (Lock) import Simplex.Messaging.Agent.Store.Postgres (createDBStore, closeDBStore) import Simplex.Messaging.Agent.Store.Postgres.Common +import Simplex.Messaging.Encoding import Simplex.Messaging.Protocol import Simplex.Messaging.Server.QueueStore import Simplex.Messaging.Server.QueueStore.Postgres.Config @@ -66,13 +68,13 @@ import Simplex.Messaging.Server.QueueStore.Types import Simplex.Messaging.Server.StoreLog import Simplex.Messaging.TMap (TMap) import qualified Simplex.Messaging.TMap as TM -import Simplex.Messaging.Util (firstRow, ifM, tshow, (<$$>)) +import Simplex.Messaging.Util (eitherToMaybe, firstRow, ifM, tshow, (<$$>)) import System.Exit (exitFailure) import System.IO (IOMode (..), hFlush, stdout) import UnliftIO.STM #if !defined(dbPostgres) -import Simplex.Messaging.Agent.Store.Postgres.DB (blobFieldDecoder) +import Simplex.Messaging.Agent.Store.Postgres.DB (blobFieldDecoder, fromTextField_) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Encoding.String #endif @@ -527,6 +529,10 @@ instance ToField EntityId where toField (EntityId s) = toField $ Binary s deriving newtype instance FromField EntityId #if !defined(dbPostgres) +instance FromField QueueMode where fromField = fromTextField_ $ eitherToMaybe . smpDecode . encodeUtf8 + +instance ToField QueueMode where toField = toField . decodeLatin1 . smpEncode + instance ToField (C.DhSecret 'C.X25519) where toField = toField . Binary . C.dhBytes' instance FromField (C.DhSecret 'C.X25519) where fromField = blobFieldDecoder strDecode diff --git a/src/Simplex/Messaging/Server/QueueStore/QueueInfo.hs b/src/Simplex/Messaging/Server/QueueStore/QueueInfo.hs index dbb716c53..9cebd8af7 100644 --- a/src/Simplex/Messaging/Server/QueueStore/QueueInfo.hs +++ b/src/Simplex/Messaging/Server/QueueStore/QueueInfo.hs @@ -10,18 +10,12 @@ import qualified Data.Aeson.TH as JQ import qualified Data.Attoparsec.ByteString.Char8 as A import qualified Data.ByteString.Lazy.Char8 as LB import Data.Text (Text) +import Data.Text.Encoding (decodeLatin1, encodeUtf8) import Data.Time.Clock (UTCTime) +import Simplex.Messaging.Agent.Store.DB (FromField (..), ToField (..), fromTextField_) import Simplex.Messaging.Encoding import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON) -import Simplex.Messaging.Util ((<$?>)) - -#if defined(dbServerPostgres) -import Data.Text.Encoding (decodeLatin1, encodeUtf8) -import Database.PostgreSQL.Simple.FromField (FromField (..)) -import Database.PostgreSQL.Simple.ToField (ToField (..)) -import Simplex.Messaging.Agent.Store.Postgres.DB (fromTextField_) -import Simplex.Messaging.Util (eitherToMaybe) -#endif +import Simplex.Messaging.Util (eitherToMaybe, (<$?>)) data QueueInfo = QueueInfo { qiSnd :: Bool, @@ -63,11 +57,9 @@ instance Encoding QueueMode where 'C' -> pure QMContact _ -> fail "bad QueueMode" -#if defined(dbServerPostgres) instance FromField QueueMode where fromField = fromTextField_ $ eitherToMaybe . smpDecode . encodeUtf8 instance ToField QueueMode where toField = toField . decodeLatin1 . smpEncode -#endif $(JQ.deriveJSON (enumJSON $ dropPrefix "Q") ''QSubThread) diff --git a/tests/AgentTests/ConnectionRequestTests.hs b/tests/AgentTests/ConnectionRequestTests.hs index 5ac090b8b..4a8b52cd5 100644 --- a/tests/AgentTests/ConnectionRequestTests.hs +++ b/tests/AgentTests/ConnectionRequestTests.hs @@ -22,7 +22,7 @@ import Simplex.Messaging.Agent.Protocol import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Crypto.Ratchet import Simplex.Messaging.Encoding.String -import Simplex.Messaging.Protocol (EntityId (..), ProtocolServer (..), currentSMPClientVersion, supportedSMPClientVRange, pattern VersionSMPC) +import Simplex.Messaging.Protocol (EntityId (..), ProtocolServer (..), QueueMode (..), currentSMPClientVersion, supportedSMPClientVRange, pattern VersionSMPC) import Simplex.Messaging.ServiceScheme (ServiceScheme (..)) import Simplex.Messaging.Version import Test.Hspec @@ -39,11 +39,11 @@ queueAddr = { smpServer = srv, senderId = EntityId "\223\142z\251", dhPublicKey = testDhKey, - sndSecure = False + queueMode = Nothing } queueAddrSK :: SMPQueueAddress -queueAddrSK = queueAddr {sndSecure = True} +queueAddrSK = queueAddr {queueMode = Just QMMessaging} queueAddr1 :: SMPQueueAddress queueAddr1 = queueAddr {smpServer = srv1} @@ -62,16 +62,16 @@ queueSK :: SMPQueueUri queueSK = SMPQueueUri supportedSMPClientVRange queueAddrSK queueStr :: ByteString -queueStr = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=1-3&dh=" <> url testDhKeyStr <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion" +queueStr = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=1-4&dh=" <> url testDhKeyStr <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion" queueStrSK :: ByteString -queueStrSK = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=1-3&dh=" <> url testDhKeyStr <> "&k=s" <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion" +queueStrSK = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=1-4&dh=" <> url testDhKeyStr <> "&q=m&k=s" <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion" queue1 :: SMPQueueUri queue1 = SMPQueueUri supportedSMPClientVRange queueAddr1 queue1Str :: ByteString -queue1Str = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=1-3&dh=" <> url testDhKeyStr +queue1Str = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=1-4&dh=" <> url testDhKeyStr queueV1 :: SMPQueueUri queueV1 = SMPQueueUri (mkVersionRange (VersionSMPC 1) (VersionSMPC 1)) queueAddr @@ -85,10 +85,10 @@ queueNew :: SMPQueueUri queueNew = SMPQueueUri (mkVersionRange (VersionSMPC 2) currentSMPClientVersion) queueAddr queueNewStr :: ByteString -queueNewStr = "smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion:5223/3456-w==#/?v=2-3&dh=" <> url testDhKeyStr +queueNewStr = "smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion:5223/3456-w==#/?v=2-4&dh=" <> url testDhKeyStr queueNewStr' :: ByteString -queueNewStr' = "smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion:5223/3456-w==#/?v=2-3&dh=" <> testDhKeyStr +queueNewStr' = "smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion:5223/3456-w==#/?v=2-4&dh=" <> testDhKeyStr queueNewNoPort :: SMPQueueUri queueNewNoPort = (queueNew :: SMPQueueUri) {queueAddress = queueAddrNoPort} @@ -97,7 +97,7 @@ queueNew1 :: SMPQueueUri queueNew1 = SMPQueueUri (mkVersionRange (VersionSMPC 2) currentSMPClientVersion) queueAddr1 queueNew1Str :: ByteString -queueNew1Str = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=2-3&dh=" <> url testDhKeyStr +queueNew1Str = "smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=2-4&dh=" <> url testDhKeyStr queueNew1NoPort :: SMPQueueUri queueNew1NoPort = (queueNew1 :: SMPQueueUri) {queueAddress = queueAddrNoPort1} @@ -217,14 +217,14 @@ connectionRequestTests = describe "connection request parsing / serializing" $ do it "should serialize and parse SMP queue URIs" $ do queue #==# queueStr - queue #== ("smp://1234-w==@smp.simplex.im:5223/3456-w==#" <> testDhKeyStr <> "/?v=1-3&extra_param=abc&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion") + queue #== ("smp://1234-w==@smp.simplex.im:5223/3456-w==#" <> testDhKeyStr <> "/?v=1-4&extra_param=abc&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion") queueSK #==# queueStrSK queue1 #==# queue1Str queueNew #==# queueNewStr queueNew #== queueNewStr' queueNew1 #==# queueNew1Str - queueNewNoPort #==# ("smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion/3456-w==#/?v=2-3&dh=" <> url testDhKeyStr) - queueNew1NoPort #==# ("smp://1234-w==@smp.simplex.im/3456-w==#/?v=2-3&dh=" <> url testDhKeyStr) + queueNewNoPort #==# ("smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion/3456-w==#/?v=2-4&dh=" <> url testDhKeyStr) + queueNew1NoPort #==# ("smp://1234-w==@smp.simplex.im/3456-w==#/?v=2-4&dh=" <> url testDhKeyStr) queueV1 #==# ("smp://1234-w==@smp.simplex.im:5223/3456-w==#/?v=1&dh=" <> url testDhKeyStr <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion") queueV1 #== ("smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion:5223/3456-w==#" <> testDhKeyStr) queueV1 #== ("smp://1234-w==@smp.simplex.im:5223/3456-w==#/?extra_param=abc&v=1&dh=" <> testDhKeyStr <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion") diff --git a/tests/AgentTests/FunctionalAPITests.hs b/tests/AgentTests/FunctionalAPITests.hs index 35301c687..43cfd0d1e 100644 --- a/tests/AgentTests/FunctionalAPITests.hs +++ b/tests/AgentTests/FunctionalAPITests.hs @@ -95,13 +95,13 @@ import Simplex.Messaging.Crypto.Ratchet (InitialKeys (..), PQEncryption (..), PQ import qualified Simplex.Messaging.Crypto.Ratchet as CR import Simplex.Messaging.Encoding.String import Simplex.Messaging.Notifications.Transport (NTFVersion, pattern VersionNTF) -import Simplex.Messaging.Protocol (BasicAuth, ErrorType (..), MsgBody, ProtocolServer (..), SubscriptionMode (..), supportedSMPClientVRange) +import Simplex.Messaging.Protocol (BasicAuth, ErrorType (..), MsgBody, ProtocolServer (..), SubscriptionMode (..), initialSMPClientVersion, srvHostnamesSMPClientVersion, supportedSMPClientVRange) import qualified Simplex.Messaging.Protocol as SMP import Simplex.Messaging.Server.Env.STM (AServerStoreCfg (..), AStoreType (..), ServerConfig (..), ServerStoreCfg (..), StorePaths (..)) import Simplex.Messaging.Server.Expiration import Simplex.Messaging.Server.MsgStore.Types (SMSType (..), SQSType (..)) import Simplex.Messaging.Server.QueueStore.QueueInfo -import Simplex.Messaging.Transport (ATransport (..), SMPVersion, VersionSMP, authCmdsSMPVersion, currentServerSMPRelayVersion, minClientSMPRelayVersion, minServerSMPRelayVersion, sndAuthKeySMPVersion, supportedSMPHandshakes) +import Simplex.Messaging.Transport (ATransport (..), SMPVersion, VersionSMP, authCmdsSMPVersion, currentServerSMPRelayVersion, minClientSMPRelayVersion, minServerSMPRelayVersion, sendingProxySMPVersion, sndAuthKeySMPVersion, supportedSMPHandshakes) import Simplex.Messaging.Util (bshow, diffToMicroseconds) import Simplex.Messaging.Version (VersionRange (..)) import qualified Simplex.Messaging.Version as V @@ -202,7 +202,7 @@ pattern Rcvd :: AgentMsgId -> AEvent 'AEConn pattern Rcvd agentMsgId <- RCVD MsgMeta {integrity = MsgOk} [MsgReceipt {agentMsgId, msgRcptStatus = MROk}] smpCfgVPrev :: ProtocolClientConfig SMPVersion -smpCfgVPrev = (smpCfg agentCfg) {clientALPN = Nothing, serverVRange = prevRange $ serverVRange $ smpCfg agentCfg} +smpCfgVPrev = (smpCfg agentCfg) {serverVRange = prevRange $ serverVRange $ smpCfg agentCfg} ntfCfgVPrev :: ProtocolClientConfig NTFVersion ntfCfgVPrev = (ntfCfg agentCfg) {clientALPN = Nothing, serverVRange = V.mkVersionRange (VersionNTF 1) (VersionNTF 1)} @@ -501,18 +501,18 @@ testMatrix2 ps runTest = do it "current, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 agentCfg agentCfg (initAgentServersProxy SPMAlways SPFProhibit) 1 $ runTest PQSupportOn True True it "v8, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 agentProxyCfgV8 agentProxyCfgV8 (initAgentServersProxy SPMAlways SPFProhibit) 3 $ runTest PQSupportOn False True it "current" $ withSmpServer ps $ runTestCfg2 agentCfg agentCfg 1 $ runTest PQSupportOn True False - it "prev" $ withSmpServer ps $ runTestCfg2 agentCfgVPrev agentCfgVPrev 3 $ runTest PQSupportOff False False - it "prev to current" $ withSmpServer ps $ runTestCfg2 agentCfgVPrev agentCfg 3 $ runTest PQSupportOff False False - it "current to prev" $ withSmpServer ps $ runTestCfg2 agentCfg agentCfgVPrev 3 $ runTest PQSupportOff False 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 + it "current to prev" $ withSmpServer ps $ runTestCfg2 agentCfg agentCfgVPrev 1 $ runTest PQSupportOff False False testMatrix2Stress :: HasCallStack => (ATransport, AStoreType) -> (PQSupport -> SndQueueSecured -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec testMatrix2Stress ps runTest = do it "current, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 aCfg aCfg (initAgentServersProxy SPMAlways SPFProhibit) 1 $ runTest PQSupportOn True True - it "v8, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 aProxyCfgV8 aProxyCfgV8 (initAgentServersProxy SPMAlways SPFProhibit) 3 $ runTest PQSupportOn False True + it "v8, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 aProxyCfgV8 aProxyCfgV8 (initAgentServersProxy SPMAlways SPFProhibit) 1 $ runTest PQSupportOn False True it "current" $ withSmpServer ps $ runTestCfg2 aCfg aCfg 1 $ runTest PQSupportOn True False - it "prev" $ withSmpServer ps $ runTestCfg2 aCfgVPrev aCfgVPrev 3 $ runTest PQSupportOff False False - it "prev to current" $ withSmpServer ps $ runTestCfg2 aCfgVPrev aCfg 3 $ runTest PQSupportOff False False - it "current to prev" $ withSmpServer ps $ runTestCfg2 aCfg aCfgVPrev 3 $ runTest PQSupportOff False 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} @@ -521,9 +521,9 @@ testMatrix2Stress ps runTest = do testBasicMatrix2 :: HasCallStack => (ATransport, AStoreType) -> (SndQueueSecured -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec testBasicMatrix2 ps runTest = do it "current" $ withSmpServer ps $ runTestCfg2 agentCfg agentCfg 1 $ runTest True - it "prev" $ withSmpServer ps $ runTestCfg2 agentCfgVPrevPQ agentCfgVPrevPQ 3 $ runTest False - it "prev to current" $ withSmpServer ps $ runTestCfg2 agentCfgVPrevPQ agentCfg 3 $ runTest False - it "current to prev" $ withSmpServer ps $ runTestCfg2 agentCfg agentCfgVPrevPQ 3 $ runTest False + it "prev" $ withSmpServer ps $ runTestCfg2 agentCfgVPrevPQ agentCfgVPrevPQ 1 $ runTest False + it "prev to current" $ withSmpServer ps $ runTestCfg2 agentCfgVPrevPQ agentCfg 1 $ runTest False + it "current to prev" $ withSmpServer ps $ runTestCfg2 agentCfg agentCfgVPrevPQ 1 $ runTest False testRatchetMatrix2 :: HasCallStack => (ATransport, AStoreType) -> (PQSupport -> SndQueueSecured -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec testRatchetMatrix2 ps runTest = do @@ -1131,7 +1131,8 @@ testContactShortLink :: HasCallStack => (ATransport, AStoreType) -> IO () testContactShortLink ps = withAgentClients3 $ \a b c -> withSmpServer ps $ do let userData = "some user data" - (contactId, (connReq, Just shortLink)) <- runRight $ A.createConnection a 1 True SCMContact (Just userData) Nothing CR.IKPQOn SMSubscribe + (contactId, (connReq0, Just shortLink)) <- runRight $ A.createConnection a 1 True SCMContact (Just userData) Nothing CR.IKPQOn SMSubscribe + Right connReq <- pure $ strDecode (strEncode connReq0) (connReq', userData') <- runRight $ getConnShortLink b 1 shortLink strDecode (strEncode shortLink) `shouldBe` Right shortLink connReq' `shouldBe` connReq @@ -1175,7 +1176,8 @@ testContactShortLink ps = testAddContactShortLink :: HasCallStack => (ATransport, AStoreType) -> IO () testAddContactShortLink ps = withAgentClients3 $ \a b c -> withSmpServer ps $ do - (contactId, (connReq, Nothing)) <- runRight $ A.createConnection a 1 True SCMContact Nothing Nothing CR.IKPQOn SMSubscribe + (contactId, (connReq0, Nothing)) <- runRight $ A.createConnection a 1 True SCMContact Nothing Nothing CR.IKPQOn SMSubscribe + Right connReq <- pure $ strDecode (strEncode connReq0) let userData = "some user data" shortLink <- runRight $ setContactShortLink a contactId userData (connReq', userData') <- runRight $ getConnShortLink b 1 shortLink @@ -2498,8 +2500,8 @@ testWaitDeliveryTimeout2 ps = testJoinConnectionAsyncReplyErrorV8 :: HasCallStack => (ATransport, AStoreType) -> IO () testJoinConnectionAsyncReplyErrorV8 ps@(t, ASType qsType _) = do let initAgentServersSrv2 = initAgentServers {smp = userServers [testSMPServer2]} - withAgent 1 agentCfgVPrevPQ initAgentServers testDB $ \a -> - withAgent 2 agentCfgVPrevPQ initAgentServersSrv2 testDB2 $ \b -> do + withAgent 1 cfg' initAgentServers testDB $ \a -> + withAgent 2 cfg' initAgentServersSrv2 testDB2 $ \b -> do (aId, bId) <- withSmpServerStoreLogOn ps testPort $ \_ -> runRight $ do bId <- createConnectionAsync a 1 "1" True SCMInvitation (IKNoPQ PQSupportOn) SMSubscribe ("1", bId', INV (ACR _ qInfo)) <- get a @@ -2533,6 +2535,12 @@ testJoinConnectionAsyncReplyErrorV8 ps@(t, ASType qsType _) = do 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 => (ATransport, AStoreType) -> IO () testJoinConnectionAsyncReplyError ps@(t, ASType qsType _) = do diff --git a/tests/AgentTests/NotificationTests.hs b/tests/AgentTests/NotificationTests.hs index da85d25aa..ea0ebd29b 100644 --- a/tests/AgentTests/NotificationTests.hs +++ b/tests/AgentTests/NotificationTests.hs @@ -175,15 +175,15 @@ testNtfMatrix :: HasCallStack => (ATransport, AStoreType) -> (APNSMockServer -> 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 3 cfg' ntfServerCfg agentCfgVPrevPQ agentCfgVPrevPQ runTest - it "prev servers; prev clients" $ runNtfTestCfg ps 3 cfgVPrev' ntfServerCfgVPrev agentCfgVPrevPQ agentCfgVPrevPQ 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 -- servers can be upgraded in any order - it "servers: curr SMP, prev NTF; prev clients" $ runNtfTestCfg ps 3 cfg' ntfServerCfgVPrev agentCfgVPrevPQ agentCfgVPrevPQ runTest - it "servers: prev SMP, curr NTF; prev clients" $ runNtfTestCfg ps 3 cfgVPrev' ntfServerCfg agentCfgVPrevPQ agentCfgVPrevPQ runTest + it "servers: curr SMP, prev NTF; prev clients" $ runNtfTestCfg ps 1 cfg' ntfServerCfgVPrev 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 3 cfg' ntfServerCfg agentCfg agentCfgVPrevPQ runTest - it "servers: curr SMP, curr NTF; clients: prev/curr" $ runNtfTestCfg ps 3 cfg' ntfServerCfg agentCfgVPrevPQ agentCfg runTest + it "servers: curr SMP, curr NTF; clients: curr/prev" $ runNtfTestCfg ps 1 cfg' ntfServerCfg agentCfg agentCfgVPrevPQ runTest + it "servers: curr SMP, curr NTF; clients: prev/curr" $ runNtfTestCfg ps 1 cfg' ntfServerCfg agentCfgVPrevPQ agentCfg runTest where cfg' = cfgMS msType cfgVPrev' = cfgVPrev msType diff --git a/tests/AgentTests/SQLiteTests.hs b/tests/AgentTests/SQLiteTests.hs index 7be9ac465..fb6c72996 100644 --- a/tests/AgentTests/SQLiteTests.hs +++ b/tests/AgentTests/SQLiteTests.hs @@ -52,7 +52,7 @@ import Simplex.Messaging.Crypto.File (CryptoFile (..)) import Simplex.Messaging.Crypto.Ratchet (InitialKeys (..), pattern PQSupportOn) import qualified Simplex.Messaging.Crypto.Ratchet as CR import Simplex.Messaging.Encoding.String (StrEncoding (..)) -import Simplex.Messaging.Protocol (EntityId (..), SubscriptionMode (..), pattern VersionSMPC) +import Simplex.Messaging.Protocol (EntityId (..), SubscriptionMode (..), QueueMode (..), pattern VersionSMPC) import qualified Simplex.Messaging.Protocol as SMP import System.Random import Test.Hspec @@ -226,7 +226,7 @@ rcvQueue1 = e2ePrivKey = testPrivDhKey, e2eDhSecret = Nothing, sndId = EntityId "2345", - sndSecure = True, + queueMode = Just QMMessaging, shortLink = Nothing, status = New, dbQueueId = DBNewQueue, @@ -245,7 +245,7 @@ sndQueue1 = connId = "conn1", server = smpServer1, sndId = EntityId "3456", - sndSecure = True, + queueMode = Just QMMessaging, sndPublicKey = testPublicAuthKey, sndPrivateKey = testPrivateAuthKey, e2ePubKey = Nothing, @@ -405,7 +405,7 @@ testUpgradeRcvConnToDuplex = connId = "conn1", server = SMPServer "smp.simplex.im" "5223" testKeyHash, sndId = EntityId "2345", - sndSecure = True, + queueMode = Just QMMessaging, sndPublicKey = testPublicAuthKey, sndPrivateKey = testPrivateAuthKey, e2ePubKey = Nothing, @@ -439,7 +439,7 @@ testUpgradeSndConnToDuplex = e2ePrivKey = testPrivDhKey, e2eDhSecret = Nothing, sndId = EntityId "4567", - sndSecure = True, + queueMode = Just QMMessaging, shortLink = Nothing, status = New, dbQueueId = DBNewQueue, diff --git a/tests/AgentTests/SchemaDump.hs b/tests/AgentTests/SchemaDump.hs index 0b6f4ebc1..376a13cb4 100644 --- a/tests/AgentTests/SchemaDump.hs +++ b/tests/AgentTests/SchemaDump.hs @@ -115,7 +115,9 @@ testUsersMigrationOld = do skipComparisonForDownMigrations :: [String] skipComparisonForDownMigrations = [ -- on down migration idx_messages_internal_snd_id_ts index moves down to the end of the file - "m20230814_indexes" + "m20230814_indexes", + -- snd_secure and last_broker_ts columns swap order on down migration + "m20250322_short_links" ] getSchema :: FilePath -> FilePath -> IO String diff --git a/tests/CoreTests/TRcvQueuesTests.hs b/tests/CoreTests/TRcvQueuesTests.hs index 4433d2a4d..4098fd0f4 100644 --- a/tests/CoreTests/TRcvQueuesTests.hs +++ b/tests/CoreTests/TRcvQueuesTests.hs @@ -17,7 +17,7 @@ import Simplex.Messaging.Agent.Protocol (ConnId, QueueStatus (..), UserId) import Simplex.Messaging.Agent.Store (DBQueueId (..), RcvQueue, StoredRcvQueue (..)) import qualified Simplex.Messaging.Agent.TRcvQueues as RQ import qualified Simplex.Messaging.Crypto as C -import Simplex.Messaging.Protocol (EntityId (..), RecipientId, SMPServer, pattern NoEntity, pattern VersionSMPC) +import Simplex.Messaging.Protocol (EntityId (..), RecipientId, SMPServer, QueueMode (..), pattern NoEntity, pattern VersionSMPC) import Test.Hspec import UnliftIO @@ -197,7 +197,7 @@ dummyRQ userId server connId rcvId = e2ePrivKey = "MC4CAQAwBQYDK2VuBCIEINCzbVFaCiYHoYncxNY8tSIfn0pXcIAhLBfFc0m+gOpk", e2eDhSecret = Nothing, sndId = NoEntity, - sndSecure = True, + queueMode = Just QMMessaging, shortLink = Nothing, status = New, dbQueueId = DBQueueId 0,