mirror of
https://github.com/simplex-chat/simplexmq.git
synced 2026-08-27 22:34:59 +00:00
agent: drop versions before 07/2024 (support of PQ encryption and securing reply queue) (#1841)
* agent: drop versions before 07/2024 (support of PQ encryption and securing reply queue) * fix encoding, add error context * fix agent tests * fix test --------- Co-authored-by: sh <github.shum@liber.li>
This commit is contained in:
@@ -48,7 +48,7 @@ module Simplex.Messaging.Agent
|
||||
createUser,
|
||||
deleteUser,
|
||||
setUserService,
|
||||
connRequestPQSupport,
|
||||
connRequestAgentVersion,
|
||||
prepareConnectionToCreate,
|
||||
createConnectionAsync,
|
||||
setConnShortLinkAsync,
|
||||
@@ -903,18 +903,16 @@ newConnNoQueues c userId enableNtfs cMode pqSupport = do
|
||||
-- TODO [short links] TBC, but probably we will need async join for contact addresses as the contact will be created after user confirming the connection,
|
||||
-- and join should retry, the same as 1-time invitation joins.
|
||||
joinConnAsync :: AgentClient -> ACorrId -> Bool -> ConnId -> Bool -> ConnectionRequestUri c -> ConnInfo -> PQSupport -> SubscriptionMode -> AM ()
|
||||
joinConnAsync c corrId updateConn connId enableNtfs cReqUri@CRInvitationUri {} cInfo pqSup subMode = do
|
||||
joinConnAsync c corrId updateConn connId enableNtfs cReqUri@CRInvitationUri {} cInfo pqSupport subMode = do
|
||||
when updateConn $ throwE $ CMD PROHIBITED "joinConnAsync: updateConn not allowed for invitation URI"
|
||||
withInvLock c (strEncode cReqUri) "joinConnAsync" $
|
||||
lift (compatibleInvitationUri cReqUri) >>= \case
|
||||
Just (_, Compatible (CR.E2ERatchetParams v _ _ _), Compatible connAgentVersion) -> do
|
||||
let pqSupport = pqSup `CR.pqSupportAnd` versionPQSupport_ connAgentVersion (Just v)
|
||||
Just _ ->
|
||||
enqueueCommand c corrId connId Nothing $ AClientCommand $ JOIN (JRConnReq enableNtfs (ACR sConnectionMode cReqUri) pqSupport) subMode cInfo
|
||||
Nothing -> throwE $ AGENT A_VERSION
|
||||
joinConnAsync c corrId updateConn connId enableNtfs cReqUri@CRContactUri {} cInfo pqSup subMode =
|
||||
joinConnAsync c corrId updateConn connId enableNtfs cReqUri@CRContactUri {} cInfo pqSupport subMode =
|
||||
lift (compatibleContactUri cReqUri) >>= \case
|
||||
Just (_, rks_, Compatible connAgentVersion) -> do
|
||||
let pqSupport = pqSup `CR.pqSupportAnd` versionPQSupport_ connAgentVersion (addrKeysE2EVersion <$> rks_)
|
||||
Just (_, _, Compatible connAgentVersion) -> do
|
||||
when updateConn $ withStore' c $ \db -> updateNewConnJoin db connId connAgentVersion pqSupport enableNtfs
|
||||
enqueueCommand c corrId connId Nothing $ AClientCommand $ JOIN (JRConnReq enableNtfs (ACR sConnectionMode cReqUri) pqSupport) subMode cInfo
|
||||
Nothing -> throwE $ AGENT A_VERSION
|
||||
@@ -1371,21 +1369,20 @@ newQueueNtfSubscription c RcvQueue {userId, connId, server, clientNtfCreds} ntfS
|
||||
liftIO $ sendNtfSubCommand ns (NSCCreate, [connId])
|
||||
|
||||
newConnToJoin :: forall c. AgentClient -> UserId -> ConnId -> Bool -> Maybe UTCTime -> ConnectionRequestUri c -> PQSupport -> AM ConnId
|
||||
newConnToJoin c userId connId enableNtfs serviceRequestExpiresAt cReq pqSup = case cReq of
|
||||
newConnToJoin c userId connId enableNtfs serviceRequestExpiresAt cReq pqSupport = case cReq of
|
||||
CRInvitationUri {} ->
|
||||
lift (compatibleInvitationUri cReq) >>= \case
|
||||
Just (_, Compatible (CR.E2ERatchetParams v _ _ _), aVersion) -> create aVersion (Just v)
|
||||
Just (_, _, aVersion) -> create aVersion
|
||||
Nothing -> throwE $ AGENT A_VERSION
|
||||
CRContactUri {} ->
|
||||
lift (compatibleContactUri cReq) >>= \case
|
||||
Just (_, rks_, aVersion) -> create aVersion (addrKeysE2EVersion <$> rks_)
|
||||
Just (_, _, aVersion) -> create aVersion
|
||||
Nothing -> throwE $ AGENT A_VERSION
|
||||
where
|
||||
create :: Compatible VersionSMPA -> Maybe CR.VersionE2E -> AM ConnId
|
||||
create (Compatible connAgentVersion) e2eV_ = do
|
||||
create :: Compatible VersionSMPA -> AM ConnId
|
||||
create (Compatible connAgentVersion) = do
|
||||
g <- asks random
|
||||
let pqSupport = pqSup `CR.pqSupportAnd` versionPQSupport_ connAgentVersion e2eV_
|
||||
cData = ConnData {userId, connId, connAgentVersion, enableNtfs, lastExternalSndId = 0, deleted = False, ratchetSyncState = RSOk, pqSupport, serviceRequestExpiresAt}
|
||||
let cData = ConnData {userId, connId, connAgentVersion, enableNtfs, lastExternalSndId = 0, deleted = False, ratchetSyncState = RSOk, pqSupport, serviceRequestExpiresAt}
|
||||
withStore c $ \db -> createNewConn db g cData SCMInvitation
|
||||
|
||||
newConnToAccept :: AgentClient -> UserId -> ConnId -> Bool -> InvitationId -> PQSupport -> AM ConnId
|
||||
@@ -1411,12 +1408,11 @@ joinConn c nm userId connId enableNtfs cReq cInfo pqSupport subMode = do
|
||||
joinConnSrv c nm userId connId enableNtfs cReq cInfo pqSupport subMode srv
|
||||
|
||||
startJoinInvitation :: AgentClient -> UserId -> ConnId -> Maybe SndQueue -> Bool -> ConnectionRequestUri 'CMInvitation -> PQSupport -> AM ((ConnData, SndQueue), (Maybe (CR.SndE2ERatchetParams 'C.X448), Maybe SMP.LinkId))
|
||||
startJoinInvitation c userId connId sq_ enableNtfs cReqUri pqSup =
|
||||
startJoinInvitation c userId connId sq_ enableNtfs cReqUri pqSupport =
|
||||
lift (compatibleInvitationUri cReqUri) >>= \case
|
||||
Just (qInfo, Compatible e2eRcvParams@(CR.E2ERatchetParams v _ _ _), Compatible connAgentVersion) -> do
|
||||
-- this case avoids re-generating queue keys and subsequent failure of SKEY that timed out
|
||||
-- e2ePubKey is always present, it's Maybe historically
|
||||
let pqSupport = pqSup `CR.pqSupportAnd` versionPQSupport_ connAgentVersion (Just v)
|
||||
g <- asks random
|
||||
maxSupported <- asks $ maxVersion . e2eEncryptVRange . config
|
||||
let cData = ConnData {userId, connId, connAgentVersion, enableNtfs, lastExternalSndId = 0, deleted = False, ratchetSyncState = RSOk, pqSupport, serviceRequestExpiresAt = Nothing}
|
||||
@@ -1445,7 +1441,7 @@ startJoinInvitation c userId connId sq_ enableNtfs cReqUri pqSup =
|
||||
|
||||
createRatchet_ :: DB.Connection -> TVar ChaChaDRG -> ConnId -> CR.VersionE2E -> PQSupport -> CR.RcvE2ERatchetParams 'C.X448 -> ExceptT StoreError IO (CR.RatchetX448, CR.SndE2ERatchetParams 'C.X448)
|
||||
createRatchet_ db g connId maxSupported pqSupport e2eRcvParams@(CR.E2ERatchetParams v _ rcDHRr kem_) = do
|
||||
(pks, e2eSndParams) <- liftIO $ CR.generateSndE2EParams g v (CR.replyKEM_ v kem_ pqSupport)
|
||||
(pks, e2eSndParams) <- liftIO $ CR.generateSndE2EParams g v (CR.replyKEM_ kem_ pqSupport)
|
||||
(_, rcDHRs) <- atomically $ C.generateKeyPair g
|
||||
rcParams <- liftEitherWith (SEAgentError . cryptoError) $ CR.pqX3dhSnd pks e2eRcvParams
|
||||
let rcVs = CR.RatchetVersions {current = v, maxSupported}
|
||||
@@ -1463,14 +1459,13 @@ startJoinInvitationDR c userId ConnData {connId} DRInvitation {ratchetState, rep
|
||||
liftIO $ createRatchet db connId ratchetState
|
||||
ExceptT $ updateNewConnSnd db connId q
|
||||
|
||||
connRequestPQSupport :: AgentClient -> PQSupport -> ConnectionRequestUri c -> IO (Maybe (VersionSMPA, PQSupport))
|
||||
connRequestPQSupport c pqSup cReq = withAgentEnv' c $ case cReq of
|
||||
CRInvitationUri {} -> invPQSupported <$$> compatibleInvitationUri cReq
|
||||
where
|
||||
invPQSupported (_, Compatible (CR.E2ERatchetParams e2eV _ _ _), Compatible agentV) = (agentV, pqSup `CR.pqSupportAnd` versionPQSupport_ agentV (Just e2eV))
|
||||
CRContactUri {} -> ctPQSupported <$$> compatibleContactUri cReq
|
||||
where
|
||||
ctPQSupported (_, rks_, Compatible agentV) = (agentV, pqSup `CR.pqSupportAnd` versionPQSupport_ agentV (addrKeysE2EVersion <$> rks_))
|
||||
connRequestAgentVersion :: AgentClient -> ConnectionRequestUri c -> IO (Maybe VersionSMPA)
|
||||
connRequestAgentVersion c cReq = withAgentEnv' c $ case cReq of
|
||||
CRInvitationUri {} -> aVersion <$$> compatibleInvitationUri cReq
|
||||
CRContactUri {} -> aVersion <$$> compatibleContactUri cReq
|
||||
where
|
||||
aVersion :: (Compatible SMPQueueInfo, r, Compatible VersionSMPA) -> VersionSMPA
|
||||
aVersion (_, _, Compatible agentV) = agentV
|
||||
|
||||
compatibleInvitationUri :: ConnectionRequestUri 'CMInvitation -> AM' (Maybe (Compatible SMPQueueInfo, Compatible (CR.RcvE2ERatchetParams 'C.X448), Compatible VersionSMPA))
|
||||
compatibleInvitationUri (CRInvitationUri ConnReqUriData {crAgentVRange, crSmpQueues = (qUri :| _)} e2eRcvParamsUri) = do
|
||||
@@ -1495,13 +1490,6 @@ compatibleContactUri (CRContactUri ConnReqUriData {crAgentVRange, crSmpQueues =
|
||||
Just (ratchetKeyId, e2eRcvParams) ->
|
||||
Just . (ratchetKeyId,) <$> (e2eRcvParams `compatibleVersion` e2eVR)
|
||||
|
||||
versionPQSupport_ :: VersionSMPA -> Maybe CR.VersionE2E -> PQSupport
|
||||
versionPQSupport_ agentV e2eV_ = PQSupport $ agentV >= pqdrSMPAgentVersion && maybe True (>= CR.pqRatchetE2EEncryptVersion) e2eV_
|
||||
{-# INLINE versionPQSupport_ #-}
|
||||
|
||||
addrKeysE2EVersion :: (RatchetKeyId, Compatible (CR.RcvE2ERatchetParams 'C.X448)) -> CR.VersionE2E
|
||||
addrKeysE2EVersion (_, Compatible (CR.E2ERatchetParams e2eV _ _ _)) = e2eV
|
||||
|
||||
joinConnSrv :: AgentClient -> NetworkRequestMode -> UserId -> ConnId -> Bool -> ConnectionRequestUri c -> ConnInfo -> PQSupport -> SubscriptionMode -> SMPServerWithAuth -> AM SndQueueSecured
|
||||
joinConnSrv c nm userId connId enableNtfs cReq cInfo pqSup subMode srv =
|
||||
joinConnSrv' c nm userId connId enableNtfs cReq cInfo pqSup subMode srv $ \replyQInfo _ -> AgentConnInfoReply (replyQInfo :| []) cInfo
|
||||
@@ -1522,14 +1510,14 @@ joinConnSrv' c nm userId connId enableNtfs inv@CRInvitationUri {} cInfo pqSup su
|
||||
((cData, sq), (e2eSndParams, lnkId_)) <- startJoinInvitation c userId connId sq_ enableNtfs inv pqSup
|
||||
secureConfirmQueue c nm cData rq_ sq srv cInfo e2eSndParams subMode
|
||||
>>= (mapM_ (delInvSL c connId srv) lnkId_ $>)
|
||||
joinConnSrv' c nm userId connId enableNtfs cReqUri@CRContactUri {} cInfo pqSup subMode srv mkInner =
|
||||
joinConnSrv' c nm userId connId enableNtfs cReqUri@CRContactUri {} cInfo pqSupport subMode srv mkInner =
|
||||
lift (compatibleContactUri cReqUri) >>= \case
|
||||
Just (qInfo, ratchet_, Compatible v) ->
|
||||
withInvLock c (strEncode cReqUri) "joinConnSrv" $ do
|
||||
SomeConn cType conn <- withStore c (`getConn` connId)
|
||||
envelope <- case ratchet_ of
|
||||
Nothing -> do
|
||||
let pqInitKeys = CR.joinContactInitialKeys (v >= pqdrSMPAgentVersion) pqSup
|
||||
let pqInitKeys = CR.joinContactInitialKeys pqSupport
|
||||
CCLink cReq _ <- case conn of
|
||||
NewConnection _ -> newRcvConnSrv c NRMBackground userId connId enableNtfs SCMInvitation Nothing Nothing pqInitKeys False subMode srv
|
||||
RcvConnection _ rq -> mkJoinInvitation rq pqInitKeys
|
||||
@@ -1538,8 +1526,7 @@ joinConnSrv' c nm userId connId enableNtfs cReqUri@CRContactUri {} cInfo pqSup s
|
||||
Just (ratchetKeyId, Compatible e2eParams@(CR.E2ERatchetParams e2eV _ _ _)) -> do
|
||||
g <- asks random
|
||||
e2eVR <- asks $ e2eEncryptVRange . config
|
||||
let pqSupport = pqSup `CR.pqSupportAnd` versionPQSupport_ v (Just e2eV)
|
||||
maxV = maxVersion e2eVR
|
||||
let maxV = maxVersion e2eVR
|
||||
rq <- case conn of
|
||||
NewConnection _ -> do
|
||||
e2eKeys <- atomically $ C.generateKeyPair g
|
||||
@@ -2681,15 +2668,14 @@ ackMessage' c connId msgId rcptInfo_ = withConnLockNotify c connId "ackMessage"
|
||||
del :: AM ()
|
||||
del = withStore' c $ \db -> deleteMsg db connId $ InternalId msgId
|
||||
sendRcpt :: Connection 'CDuplex -> AM ()
|
||||
sendRcpt (DuplexConnection cData@ConnData {connAgentVersion} _ sqs) = do
|
||||
sendRcpt (DuplexConnection cData _ sqs) = do
|
||||
msg@RcvMsg {msgType, msgReceipt} <- withStore c $ \db -> getRcvMsg db connId $ InternalId msgId
|
||||
case rcptInfo_ of
|
||||
Just rcptInfo -> do
|
||||
unless (msgType == AM_A_MSG_) . throwE $ CMD PROHIBITED "ackMessage: receipt not allowed"
|
||||
when (connAgentVersion >= deliveryRcptsSMPAgentVersion) $ do
|
||||
let RcvMsg {msgMeta = MsgMeta {sndMsgId}, internalHash} = msg
|
||||
rcpt = A_RCVD [AMessageReceipt {agentMsgId = sndMsgId, msgHash = internalHash, rcptInfo}]
|
||||
void $ enqueueMessages c cData sqs SMP.MsgFlags {notification = False} rcpt
|
||||
let RcvMsg {msgMeta = MsgMeta {sndMsgId}, internalHash} = msg
|
||||
rcpt = A_RCVD [AMessageReceipt {agentMsgId = sndMsgId, msgHash = internalHash, rcptInfo}]
|
||||
void $ enqueueMessages c cData sqs SMP.MsgFlags {notification = False} rcpt
|
||||
Nothing -> case (msgType, msgReceipt) of
|
||||
-- only remove sent message if receipt hash was Ok, both to debug and for future redundancy
|
||||
(AM_A_RCVD_, Just MsgReceipt {agentMsgId = sndMsgId, msgRcptStatus = MROk}) ->
|
||||
@@ -2982,7 +2968,7 @@ connectionStats c = \case
|
||||
rcvQueuesInfo = [],
|
||||
sndQueuesInfo = [],
|
||||
ratchetSyncState,
|
||||
ratchetSyncSupported = connAgentVersion >= ratchetSyncSMPAgentVersion,
|
||||
ratchetSyncSupported = True,
|
||||
subStatus = Nothing
|
||||
}
|
||||
rcvQueueInfo :: RcvQueue -> AM RcvQueueInfo
|
||||
@@ -3479,7 +3465,7 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), THandlePar
|
||||
_ -> pure ()
|
||||
processClientMsg srvTs msgFlags msgBody = do
|
||||
clientMsg@SMP.ClientMsgEnvelope {cmHeader = SMP.PubHeader phVer e2ePubKey_} <-
|
||||
parseMessage msgBody
|
||||
parseMessage "4" msgBody
|
||||
clientVRange <- asks $ smpClientVRange . config
|
||||
unless (phVer `isCompatible` clientVRange || phVer <= agreedClientVerion) . throwE $ AGENT A_VERSION
|
||||
case (e2eDhSecret, e2ePubKey_) of
|
||||
@@ -3567,7 +3553,7 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), THandlePar
|
||||
notify $ ERR (AGENT $ A_DUPLICATE $ Just DroppedMsg {brokerTs, attempts})
|
||||
ackDel internalId
|
||||
else
|
||||
liftEither (parse smpP (AGENT A_MESSAGE) agentMsgBody) >>= \case
|
||||
liftEither (parse smpP (AGENT $ A_MESSAGE "parse msg body 1") agentMsgBody) >>= \case
|
||||
AgentMessage _ (A_MSG body) -> do
|
||||
logServer "<--" c srv rId $ "MSG <MSG>:" <> logSecret' srvMsgId
|
||||
notify $ MSG msgMeta msgFlags body
|
||||
@@ -3602,7 +3588,7 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), THandlePar
|
||||
liftIO $ lockConnForUpdate db connId
|
||||
rc <- ExceptT $ getRatchetForUpdate db connId -- ratchet state pre-decryption - required for processing EREADY
|
||||
(agentMsgBody, pqEncryption) <- agentRatchetDecrypt' g db connId rc encAgentMessage
|
||||
liftEither (parse smpP (SEAgentError $ AGENT A_MESSAGE) agentMsgBody) >>= \case
|
||||
liftEither (parse smpP (SEAgentError $ AGENT $ A_MESSAGE "parse msg body 2") agentMsgBody) >>= \case
|
||||
agentMsg@(AgentMessage APrivHeader {sndMsgId, prevMsgHash} aMessage) -> do
|
||||
let msgType = agentMessageType agentMsg
|
||||
internalHash = C.sha256Hash agentMsgBody
|
||||
@@ -3688,8 +3674,8 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), THandlePar
|
||||
decryptClientMessage :: C.DhSecretX25519 -> SMP.ClientMsgEnvelope -> AM (SMP.PrivHeader, AgentMsgEnvelope)
|
||||
decryptClientMessage e2eDh SMP.ClientMsgEnvelope {cmNonce, cmEncBody} = do
|
||||
clientMsg <- liftEither $ agentCbDecrypt e2eDh cmNonce cmEncBody
|
||||
SMP.ClientMessage privHeader clientBody <- parseMessage clientMsg
|
||||
agentEnvelope <- parseMessage clientBody
|
||||
SMP.ClientMessage privHeader clientBody <- parseMessage "5" clientMsg
|
||||
agentEnvelope <- parseMessage "6" clientBody
|
||||
-- Version check is removed here, because when connecting via v1 contact address the agent still sends v2 message,
|
||||
-- to allow duplexHandshake mode, in case the receiving agent was updated to v2 after the address was created.
|
||||
-- aVRange <- asks $ smpAgentVRange . config
|
||||
@@ -3698,8 +3684,8 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), THandlePar
|
||||
-- else throwE $ AGENT A_VERSION
|
||||
pure (privHeader, agentEnvelope)
|
||||
|
||||
parseMessage :: Encoding a => ByteString -> AM a
|
||||
parseMessage = liftEither . parse smpP (AGENT A_MESSAGE)
|
||||
parseMessage :: Encoding a => String -> ByteString -> AM a
|
||||
parseMessage cxt = liftEither . parse smpP (AGENT $ A_MESSAGE $ "parse message " <> cxt)
|
||||
|
||||
-- checking agreed versions to continue connection in case of client/agent version downgrades
|
||||
checkConfVersions :: VersionSMPA -> VersionSMPC -> AM ()
|
||||
@@ -3723,7 +3709,7 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), THandlePar
|
||||
-- create ratchet from sent invitation and received confirmation keys
|
||||
Just e2eSndParams -> do
|
||||
keys <- withStore c (`getRatchetX3dhKeys` connId)
|
||||
processConnInfo =<< initRcvRatchet_ agentVersion pqSupport keys e2eSndParams
|
||||
processConnInfo =<< initRcvRatchet_ pqSupport keys e2eSndParams
|
||||
-- use ratchet initialized from contact address ratchet keys during invitation
|
||||
Nothing -> withStore' c (`getRatchet` connId) >>= \case
|
||||
Left _ -> prohibited "conf: incorrect state"
|
||||
@@ -3732,7 +3718,7 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), THandlePar
|
||||
processConnInfo (rc, pqSupport') = do
|
||||
(agentMsgBody_, rc') <- decryptConnInfo rc encConnInfo
|
||||
case agentMsgBody_ of
|
||||
Right agentMsgBody -> parseMessage agentMsgBody >>= \case
|
||||
Right agentMsgBody -> parseMessage "1" agentMsgBody >>= \case
|
||||
AgentConnInfoReply smpQueues connInfo | isNothing serviceRequestExpiresAt -> do
|
||||
processConf rc' connInfo SMPConfirmation {senderKey, e2ePubKey, connInfo, smpReplyQueues = L.toList smpQueues, smpClientVersion = phVer}
|
||||
withStore' c $ \db -> updateRcvMsgHash db connId 1 (InternalRcvId 0) (C.sha256Hash agentMsgBody)
|
||||
@@ -3773,7 +3759,7 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), THandlePar
|
||||
DuplexConnection _ (rq'@RcvQueue {smpClientVersion = v'} :| _) _ | isNothing e2eEncryption -> do
|
||||
g <- asks random
|
||||
(agentMsgBody, pqEncryption) <- withStore c $ \db -> runExceptT $ agentRatchetDecrypt g db connId encConnInfo
|
||||
parseMessage agentMsgBody >>= \case
|
||||
parseMessage "2" agentMsgBody >>= \case
|
||||
AgentConnInfo connInfo -> do
|
||||
notify $ INFO pqSupport connInfo
|
||||
let dhSecret = C.dh' e2ePubKey e2ePrivKey
|
||||
@@ -3789,15 +3775,14 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), THandlePar
|
||||
_ -> prohibited "conf: incorrect state"
|
||||
_ -> prohibited "conf: status /= new"
|
||||
|
||||
initRcvRatchet_ :: VersionSMPA -> PQSupport -> CR.RcvE2EPrivRatchetParams 'C.X448 -> CR.SndE2ERatchetParams 'C.X448 -> AM (CR.RatchetX448, PQSupport)
|
||||
initRcvRatchet_ agentVersion pqSupport pks@(_, pk2, _) (CR.AE2ERatchetParams _ e2eSndParams@(CR.E2ERatchetParams e2eVersion _ _ _)) = do
|
||||
initRcvRatchet_ :: PQSupport -> CR.RcvE2EPrivRatchetParams 'C.X448 -> CR.SndE2ERatchetParams 'C.X448 -> AM (CR.RatchetX448, PQSupport)
|
||||
initRcvRatchet_ pqSupport pks@(_, pk2, _) (CR.AE2ERatchetParams _ e2eSndParams@(CR.E2ERatchetParams e2eVersion _ _ _)) = do
|
||||
e2eEncryptVRange <- asks $ e2eEncryptVRange . config
|
||||
unless (e2eVersion `isCompatible` e2eEncryptVRange) $ throwE $ AGENT A_VERSION
|
||||
rcParams <- liftError cryptoError $ CR.pqX3dhRcv pks e2eSndParams
|
||||
let rcVs = CR.RatchetVersions {current = e2eVersion, maxSupported = maxVersion e2eEncryptVRange}
|
||||
connPQSupport = pqSupport `CR.pqSupportAnd` versionPQSupport_ agentVersion (Just e2eVersion)
|
||||
rc = CR.initRcvRatchet rcVs pk2 rcParams connPQSupport
|
||||
pure (rc, connPQSupport)
|
||||
rc = CR.initRcvRatchet rcVs pk2 rcParams pqSupport
|
||||
pure (rc, pqSupport)
|
||||
|
||||
decryptConnInfo :: CR.RatchetX448 -> ByteString -> AM (Either C.CryptoError ByteString, CR.RatchetX448)
|
||||
decryptConnInfo rc encConnInfo = do
|
||||
@@ -3957,15 +3942,10 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), THandlePar
|
||||
case conn' of
|
||||
ContactConnection {} -> do
|
||||
-- show connection request even if invitaion via contact address is not compatible.
|
||||
-- in case invitation not compatible, assume there is no PQ encryption support.
|
||||
pqSupport <- lift $ maybe PQSupportOff pqSupported <$> compatibleInvitationUri connReq
|
||||
invId <- storeInvitation (CRInvitation connReq) cInfo False
|
||||
let srvs = L.map qServer $ crSmpQueues crData
|
||||
notify $ REQ invId pqSupport srvs cInfo False
|
||||
notify $ REQ invId PQSupportOn srvs cInfo False
|
||||
_ -> prohibited "inv: sent to message conn"
|
||||
where
|
||||
pqSupported (_, Compatible (CR.E2ERatchetParams v _ _ _), Compatible agentVersion) =
|
||||
PQSupportOn `CR.pqSupportAnd` versionPQSupport_ agentVersion (Just v)
|
||||
|
||||
storeInvitation :: ContactRequest -> ConnInfo -> Bool -> AM InvitationId
|
||||
storeInvitation connReq recipientConnInfo serviceRequest = do
|
||||
@@ -3983,15 +3963,15 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), THandlePar
|
||||
unlessM duplicateRequest $
|
||||
withStore' c (\db -> getAddressRatchetKeys db connId ratchetKeyId) >>= \case
|
||||
Right (pk1, pk2, pKem) -> do
|
||||
(rc, connPQSupport) <- initRcvRatchet_ agentVersion pqSupport (pk1, pk2, pKem) e2eSndParams
|
||||
(rc, connPQSupport) <- initRcvRatchet_ pqSupport (pk1, pk2, pKem) e2eSndParams
|
||||
(agentMsgBody_, ratchetState) <- decryptConnInfo rc encConnInfo
|
||||
case agentMsgBody_ of
|
||||
Right agentMsgBody -> do
|
||||
let mkDR replyQueue = DRInvitation {ratchetState, replyQueue, agentVersion, pqSupport = connPQSupport}
|
||||
parseMessage agentMsgBody >>= \case
|
||||
parseMessage "3" agentMsgBody >>= \case
|
||||
AgentConnInfoReply (replyQueue :| _) cInfo -> do
|
||||
invId <- storeInvitation (CRInvitationDR $ mkDR replyQueue) cInfo False
|
||||
notify $ REQ invId pqSupported (qServer replyQueue :| []) cInfo True
|
||||
notify $ REQ invId PQSupportOn (qServer replyQueue :| []) cInfo True
|
||||
AgentServiceRequest (replyQueue :| _) sig_ payload ->
|
||||
case verifyServiceReq rc payload sig_ of
|
||||
Left err -> logError ("service request: " <> T.pack err) >> notify (ERR $ AGENT $ A_SERVICE ASEBadSignature)
|
||||
@@ -4003,9 +3983,6 @@ processSMPTransmissions c@AgentClient {subQ} (tSess@(userId, srv, _), THandlePar
|
||||
Left _ -> prohibited "addr inv: unknown ratchetKeyId"
|
||||
_ -> prohibited "inv: sent to message conn"
|
||||
where
|
||||
pqSupported = case e2eSndParams of
|
||||
CR.AE2ERatchetParams _ (CR.E2ERatchetParams e2eVersion _ _ _) ->
|
||||
PQSupportOn `CR.pqSupportAnd` versionPQSupport_ agentVersion (Just e2eVersion)
|
||||
duplicateRequest = case e2eSndParams of
|
||||
CR.AE2ERatchetParams _ (CR.E2ERatchetParams _ k1 k2 _) -> do
|
||||
let rkHash = C.sha256Hash $ C.pubKeyBytes k1 <> C.pubKeyBytes k2
|
||||
@@ -4223,16 +4200,16 @@ enqueueRatchetKey c sq@SndQueue {connId} e2eEncryption = do
|
||||
pure internalId
|
||||
|
||||
-- encoded AgentMessage -> encoded EncAgentMessage
|
||||
agentRatchetEncrypt :: DB.Connection -> ConnData -> ByteString -> (VersionSMPA -> PQSupport -> Int) -> Maybe PQEncryption -> CR.VersionE2E -> ExceptT StoreError IO (ByteString, PQEncryption)
|
||||
agentRatchetEncrypt :: DB.Connection -> ConnData -> ByteString -> (PQSupport -> Int) -> Maybe PQEncryption -> CR.VersionE2E -> ExceptT StoreError IO (ByteString, PQEncryption)
|
||||
agentRatchetEncrypt db cData msg getPaddedLen pqEnc_ currentE2EVersion = do
|
||||
(mek, paddedLen, pqEnc) <- agentRatchetEncryptHeader db cData getPaddedLen pqEnc_ currentE2EVersion
|
||||
encMsg <- withExceptT (SEAgentError . cryptoError) $ CR.rcEncryptMsg mek paddedLen msg
|
||||
pure (encMsg, pqEnc)
|
||||
|
||||
agentRatchetEncryptHeader :: DB.Connection -> ConnData -> (VersionSMPA -> PQSupport -> Int) -> Maybe PQEncryption -> CR.VersionE2E -> ExceptT StoreError IO (CR.MsgEncryptKeyX448, Int, PQEncryption)
|
||||
agentRatchetEncryptHeader db ConnData {connId, connAgentVersion = v, pqSupport} getPaddedLen pqEnc_ currentE2EVersion = do
|
||||
agentRatchetEncryptHeader :: DB.Connection -> ConnData -> (PQSupport -> Int) -> Maybe PQEncryption -> CR.VersionE2E -> ExceptT StoreError IO (CR.MsgEncryptKeyX448, Int, PQEncryption)
|
||||
agentRatchetEncryptHeader db ConnData {connId, pqSupport} getPaddedLen pqEnc_ currentE2EVersion = do
|
||||
rc <- ExceptT $ getRatchetForUpdate db connId
|
||||
let paddedLen = getPaddedLen v pqSupport
|
||||
let paddedLen = getPaddedLen pqSupport
|
||||
(mek, rc') <- withExceptT (SEAgentError . cryptoError) $ CR.rcEncryptHeader rc pqEnc_ currentE2EVersion
|
||||
liftIO $ updateRatchet db connId rc' CR.SMDNoChange
|
||||
pure (mek, paddedLen, CR.rcSndKEM rc')
|
||||
|
||||
@@ -1950,7 +1950,7 @@ getQueueMessage c rq@RcvQueue {server, rcvId, rcvPrivateKey} = do
|
||||
|
||||
decryptSMPMessage :: RcvQueue -> SMP.RcvMessage -> AM SMP.ClientRcvMsgBody
|
||||
decryptSMPMessage rq SMP.RcvMessage {msgId, msgBody = SMP.EncRcvMsgBody body} =
|
||||
liftEither $ parse SMP.clientRcvMsgBodyP (AGENT A_MESSAGE) =<< decrypt body
|
||||
liftEither $ parse SMP.clientRcvMsgBodyP (AGENT $ A_MESSAGE "decrypt message") =<< decrypt body
|
||||
where
|
||||
decrypt = agentCbDecrypt (rcvDhSecret rq) (C.cbNonce msgId)
|
||||
|
||||
@@ -2254,7 +2254,7 @@ agentCbDecrypt dhSecret nonce msg =
|
||||
cryptoError :: C.CryptoError -> AgentErrorType
|
||||
cryptoError = \case
|
||||
C.CryptoLargeMsgError -> CMD LARGE "CryptoLargeMsgError"
|
||||
C.CryptoHeaderError _ -> AGENT A_MESSAGE -- parsing error
|
||||
C.CryptoHeaderError e -> AGENT $ A_MESSAGE $ "parse msg header " <> e
|
||||
C.CERatchetDuplicateMessage -> AGENT $ A_DUPLICATE Nothing
|
||||
C.AESDecryptError -> c DECRYPT_AES
|
||||
C.CBDecryptError -> c DECRYPT_CB
|
||||
|
||||
@@ -41,12 +41,8 @@ module Simplex.Messaging.Agent.Protocol
|
||||
VersionSMPA,
|
||||
VersionRangeSMPA,
|
||||
pattern VersionSMPA,
|
||||
duplexHandshakeSMPAgentVersion,
|
||||
ratchetSyncSMPAgentVersion,
|
||||
deliveryRcptsSMPAgentVersion,
|
||||
pqdrSMPAgentVersion,
|
||||
sndAuthKeySMPAgentVersion,
|
||||
ratchetOnConfSMPAgentVersion,
|
||||
rpcAddressSMPAgentVersion,
|
||||
currentSMPAgentVersion,
|
||||
supportedSMPAgentVRange,
|
||||
e2eEncConnInfoLength,
|
||||
@@ -304,6 +300,7 @@ import UnliftIO.Exception (Exception)
|
||||
-- 5 - post-quantum double ratchet (3/14/2024)
|
||||
-- 6 - secure reply queues with provided keys (6/14/2024)
|
||||
-- 7 - initialize ratchet on processing confirmation (7/18/2024)
|
||||
-- 8 - agent RPC and double ratchet PQ encryption from first message to contact address (8/01/2026)
|
||||
|
||||
data SMPAgentVersion
|
||||
|
||||
@@ -316,29 +313,20 @@ type VersionRangeSMPA = VersionRange SMPAgentVersion
|
||||
pattern VersionSMPA :: Word16 -> VersionSMPA
|
||||
pattern VersionSMPA v = Version v
|
||||
|
||||
duplexHandshakeSMPAgentVersion :: VersionSMPA
|
||||
duplexHandshakeSMPAgentVersion = VersionSMPA 2
|
||||
|
||||
ratchetSyncSMPAgentVersion :: VersionSMPA
|
||||
ratchetSyncSMPAgentVersion = VersionSMPA 3
|
||||
|
||||
deliveryRcptsSMPAgentVersion :: VersionSMPA
|
||||
deliveryRcptsSMPAgentVersion = VersionSMPA 4
|
||||
|
||||
pqdrSMPAgentVersion :: VersionSMPA
|
||||
pqdrSMPAgentVersion = VersionSMPA 5
|
||||
|
||||
sndAuthKeySMPAgentVersion :: VersionSMPA
|
||||
sndAuthKeySMPAgentVersion = VersionSMPA 6
|
||||
_sndAuthKeySMPAgentVersion :: VersionSMPA
|
||||
_sndAuthKeySMPAgentVersion = VersionSMPA 6
|
||||
|
||||
ratchetOnConfSMPAgentVersion :: VersionSMPA
|
||||
ratchetOnConfSMPAgentVersion = VersionSMPA 7
|
||||
|
||||
rpcAddressSMPAgentVersion :: VersionSMPA
|
||||
rpcAddressSMPAgentVersion = VersionSMPA 8
|
||||
|
||||
minSupportedSMPAgentVersion :: VersionSMPA
|
||||
minSupportedSMPAgentVersion = duplexHandshakeSMPAgentVersion
|
||||
minSupportedSMPAgentVersion = _sndAuthKeySMPAgentVersion
|
||||
|
||||
currentSMPAgentVersion :: VersionSMPA
|
||||
currentSMPAgentVersion = VersionSMPA 7
|
||||
currentSMPAgentVersion = VersionSMPA 8
|
||||
|
||||
supportedSMPAgentVRange :: VersionRangeSMPA
|
||||
supportedSMPAgentVRange = mkVersionRange minSupportedSMPAgentVersion currentSMPAgentVersion
|
||||
@@ -346,17 +334,17 @@ supportedSMPAgentVRange = mkVersionRange minSupportedSMPAgentVersion currentSMPA
|
||||
-- it is shorter to allow all handshake headers,
|
||||
-- including E2E (double-ratchet) parameters and
|
||||
-- signing key of the sender for the server
|
||||
e2eEncConnInfoLength :: VersionSMPA -> PQSupport -> Int
|
||||
e2eEncConnInfoLength v = \case
|
||||
e2eEncConnInfoLength :: PQSupport -> Int
|
||||
e2eEncConnInfoLength = \case
|
||||
-- reduced by 3726 (roughly the increase of message ratchet header size + key and ciphertext in reply link)
|
||||
PQSupportOn | v >= pqdrSMPAgentVersion -> 11106
|
||||
_ -> 14832
|
||||
PQSupportOn -> 11106
|
||||
PQSupportOff -> 14832
|
||||
|
||||
e2eEncAgentMsgLength :: VersionSMPA -> PQSupport -> Int
|
||||
e2eEncAgentMsgLength v = \case
|
||||
e2eEncAgentMsgLength :: PQSupport -> Int
|
||||
e2eEncAgentMsgLength = \case
|
||||
-- reduced by 2222 (the increase of message ratchet header size)
|
||||
PQSupportOn | v >= pqdrSMPAgentVersion -> 13618
|
||||
_ -> 15840
|
||||
PQSupportOn -> 13618
|
||||
PQSupportOff -> 15840
|
||||
|
||||
-- | SMP agent event
|
||||
type ATransmission = (ACorrId, AEntityId, AEvt)
|
||||
@@ -2195,7 +2183,7 @@ data ConnectionErrorType
|
||||
-- | Errors of another SMP agent.
|
||||
data SMPAgentError
|
||||
= -- | client or agent message that failed to parse
|
||||
A_MESSAGE
|
||||
A_MESSAGE {messageErr :: String}
|
||||
| -- | prohibited SMP/agent message
|
||||
A_PROHIBITED {prohibitedErr :: String}
|
||||
| -- | incompatible version of SMP client, agent or encryption protocols
|
||||
|
||||
@@ -475,8 +475,8 @@ type NoticeId = Int64
|
||||
|
||||
-- this function should be mirrored in the clients
|
||||
ratchetSyncAllowed :: ConnData -> Bool
|
||||
ratchetSyncAllowed ConnData {ratchetSyncState, connAgentVersion} =
|
||||
connAgentVersion >= ratchetSyncSMPAgentVersion && (ratchetSyncState `elem` ([RSAllowed, RSRequired] :: [RatchetSyncState]))
|
||||
ratchetSyncAllowed ConnData {ratchetSyncState} =
|
||||
ratchetSyncState `elem` ([RSAllowed, RSRequired] :: [RatchetSyncState])
|
||||
|
||||
-- this function should be mirrored in the clients
|
||||
ratchetSyncSendProhibited :: ConnData -> Bool
|
||||
|
||||
@@ -51,8 +51,6 @@ module Simplex.Messaging.Crypto.Ratchet
|
||||
VersionRangeE2E,
|
||||
pattern VersionE2E,
|
||||
RatchetVersions (..),
|
||||
kdfX3DHE2EEncryptVersion,
|
||||
pqRatchetE2EEncryptVersion,
|
||||
currentE2EEncryptVersion,
|
||||
supportedE2EEncryptVRange,
|
||||
generateRcvE2EParams,
|
||||
@@ -87,8 +85,6 @@ module Simplex.Messaging.Crypto.Ratchet
|
||||
RatchetKey (..),
|
||||
fullHeaderLen,
|
||||
applySMDiff,
|
||||
encodeMsgHeader,
|
||||
msgHeaderP,
|
||||
)
|
||||
where
|
||||
|
||||
@@ -102,7 +98,6 @@ import Crypto.Random (ChaChaDRG)
|
||||
import Data.Aeson (FromJSON (..), ToJSON (..))
|
||||
import qualified Data.Aeson as J
|
||||
import qualified Data.Aeson.TH as JQ
|
||||
import Data.Attoparsec.ByteString (Parser, peekWord8')
|
||||
import qualified Data.Attoparsec.ByteString.Char8 as A
|
||||
import qualified Data.ByteArray as BA
|
||||
import Data.ByteString.Char8 (ByteString)
|
||||
@@ -132,6 +127,7 @@ import UnliftIO.STM
|
||||
-- e2e encryption headers version history:
|
||||
-- 1 - binary protocol encoding (1/1/2022)
|
||||
-- 2 - use KDF in x3dh (10/20/2022)
|
||||
-- 3 - PQDR (3/14/2024)
|
||||
|
||||
data E2EVersion
|
||||
|
||||
@@ -144,17 +140,17 @@ type VersionRangeE2E = VersionRange E2EVersion
|
||||
pattern VersionE2E :: Word16 -> VersionE2E
|
||||
pattern VersionE2E v = Version v
|
||||
|
||||
kdfX3DHE2EEncryptVersion :: VersionE2E
|
||||
kdfX3DHE2EEncryptVersion = VersionE2E 2
|
||||
_pqRatchetE2EEncryptVersion :: VersionE2E
|
||||
_pqRatchetE2EEncryptVersion = VersionE2E 3
|
||||
|
||||
pqRatchetE2EEncryptVersion :: VersionE2E
|
||||
pqRatchetE2EEncryptVersion = VersionE2E 3
|
||||
minSupportedE2EEncryptVersion :: VersionE2E
|
||||
minSupportedE2EEncryptVersion = _pqRatchetE2EEncryptVersion
|
||||
|
||||
currentE2EEncryptVersion :: VersionE2E
|
||||
currentE2EEncryptVersion = VersionE2E 3
|
||||
|
||||
supportedE2EEncryptVRange :: VersionRangeE2E
|
||||
supportedE2EEncryptVRange = mkVersionRange kdfX3DHE2EEncryptVersion currentE2EEncryptVersion
|
||||
supportedE2EEncryptVRange = mkVersionRange minSupportedE2EEncryptVersion currentE2EEncryptVersion
|
||||
|
||||
data RatchetKEMState
|
||||
= RKSProposed -- only KEM encapsulation key
|
||||
@@ -238,9 +234,7 @@ data AnyE2ERatchetParams
|
||||
deriving instance Show AnyE2ERatchetParams
|
||||
|
||||
instance (RatchetKEMStateI s, AlgorithmI a) => Encoding (E2ERatchetParams s a) where
|
||||
smpEncode (E2ERatchetParams v k1 k2 kem_)
|
||||
| v >= pqRatchetE2EEncryptVersion = smpEncode (v, k1, k2, kem_)
|
||||
| otherwise = smpEncode (v, k1, k2)
|
||||
smpEncode (E2ERatchetParams v k1 k2 kem_) = smpEncode (v, k1, k2, kem_)
|
||||
smpP = toParams <$?> smpP
|
||||
where
|
||||
toParams :: AE2ERatchetParams a -> Either String (E2ERatchetParams s a)
|
||||
@@ -261,14 +255,9 @@ instance Encoding AnyE2ERatchetParams where
|
||||
case testEquality a a' of
|
||||
Nothing -> fail "bad e2e params: different key algorithms"
|
||||
Just Refl ->
|
||||
kemP v >>= \case
|
||||
smpP >>= \case
|
||||
Just (ARKP s kem) -> pure $ AnyE2ERatchetParams s a $ E2ERatchetParams v k1 k2 (Just kem)
|
||||
Nothing -> pure $ AnyE2ERatchetParams SRKSProposed a $ E2ERatchetParams v k1 k2 Nothing
|
||||
where
|
||||
kemP :: VersionE2E -> Parser (Maybe ARKEMParams)
|
||||
kemP v
|
||||
| v >= pqRatchetE2EEncryptVersion = smpP
|
||||
| otherwise = pure Nothing
|
||||
|
||||
instance VersionI E2EVersion (E2ERatchetParams s a) where
|
||||
type VersionRangeT E2EVersion (E2ERatchetParams s a) = E2ERatchetParamsUri s a
|
||||
@@ -307,11 +296,10 @@ instance (RatchetKEMStateI s, AlgorithmI a) => StrEncoding (E2ERatchetParamsUri
|
||||
[("v", strEncode vs), ("x3dh", strEncodeList [key1, key2])]
|
||||
<> maybe [] encodeKem kem_
|
||||
where
|
||||
encodeKem kem
|
||||
| maxVersion vs < pqRatchetE2EEncryptVersion = []
|
||||
| otherwise = case kem of
|
||||
RKParamsProposed k -> [("kem_key", strEncode k)]
|
||||
RKParamsAccepted ct k -> [("kem_ct", strEncode ct), ("kem_key", strEncode k)]
|
||||
encodeKem :: RKEMParams s -> [(ByteString, ByteString)]
|
||||
encodeKem kem = case kem of
|
||||
RKParamsProposed k -> [("kem_key", strEncode k)]
|
||||
RKParamsAccepted ct k -> [("kem_ct", strEncode ct), ("kem_key", strEncode k)]
|
||||
strP = toE2ERatchetParamsUri <$?> strP
|
||||
{-# INLINE strP #-}
|
||||
|
||||
@@ -328,25 +316,26 @@ instance StrEncoding AnyE2ERatchetParamsUri where
|
||||
strEncode (AnyE2ERatchetParamsUri _ _ ps) = strEncode ps
|
||||
strP = do
|
||||
query <- strP
|
||||
vr :: VersionRangeE2E <- queryParam "v" query
|
||||
vr :: VersionRangeE2E <- adjustE2EVRange <$> queryParam "v" query
|
||||
keys <- L.toList <$> queryParam "x3dh" query
|
||||
case keys of
|
||||
[APublicDhKey a k1, APublicDhKey a' k2] -> case testEquality a a' of
|
||||
Nothing -> fail "bad e2e params: different key algorithms"
|
||||
Just Refl ->
|
||||
kemP vr query >>= \case
|
||||
kemP query >>= \case
|
||||
Just (ARKP s kem) -> pure $ AnyE2ERatchetParamsUri s a $ E2ERatchetParamsUri vr k1 k2 (Just kem)
|
||||
Nothing -> pure $ AnyE2ERatchetParamsUri SRKSProposed a $ E2ERatchetParamsUri vr k1 k2 Nothing
|
||||
_ -> fail "bad e2e params"
|
||||
where
|
||||
kemP vr query
|
||||
| maxVersion vr >= pqRatchetE2EEncryptVersion =
|
||||
queryParam_ "kem_key" query
|
||||
$>>= \k -> Just . kemParams k <$> queryParam_ "kem_ct" query
|
||||
| otherwise = pure Nothing
|
||||
kemP query =
|
||||
queryParam_ "kem_key" query
|
||||
$>>= \k -> Just . kemParams k <$> queryParam_ "kem_ct" query
|
||||
kemParams k = \case
|
||||
Nothing -> ARKP SRKSProposed $ RKParamsProposed k
|
||||
Just ct -> ARKP SRKSAccepted $ RKParamsAccepted ct k
|
||||
adjustE2EVRange vr =
|
||||
let v = max minSupportedE2EEncryptVersion $ minVersion vr
|
||||
in fromMaybe vr $ safeVersionRange v (max v $ maxVersion vr)
|
||||
|
||||
instance (RatchetKEMStateI s, AlgorithmI a) => Encoding (E2ERatchetParamsUri s a) where
|
||||
smpEncode (E2ERatchetParamsUri vr k1 k2 kem_) = smpEncode (vr, k1, k2, kem_)
|
||||
@@ -432,16 +421,14 @@ generateE2EParams g v useKEM_ = do
|
||||
where
|
||||
kemParams :: IO (Maybe (RKEMParams s, PrivRKEMParams s))
|
||||
kemParams = case useKEM_ of
|
||||
Just useKem
|
||||
| v >= pqRatchetE2EEncryptVersion ->
|
||||
Just <$> do
|
||||
ks@(k, _) <- sntrup761Keypair g
|
||||
case useKem of
|
||||
ProposeKEM -> pure (RKParamsProposed k, PrivateRKParamsProposed ks)
|
||||
AcceptKEM k' -> do
|
||||
(ct, shared) <- sntrup761Enc g k'
|
||||
pure (RKParamsAccepted ct k, PrivateRKParamsAccepted ct shared ks)
|
||||
_ -> pure Nothing
|
||||
Just useKem -> Just <$> do
|
||||
ks@(k, _) <- sntrup761Keypair g
|
||||
case useKem of
|
||||
ProposeKEM -> pure (RKParamsProposed k, PrivateRKParamsProposed ks)
|
||||
AcceptKEM k' -> do
|
||||
(ct, shared) <- sntrup761Enc g k'
|
||||
pure (RKParamsAccepted ct k, PrivateRKParamsAccepted ct shared ks)
|
||||
Nothing -> pure Nothing
|
||||
|
||||
-- used by party initiating connection, Bob in double-ratchet spec
|
||||
generateRcvE2EParams :: (AlgorithmI a, DhAlgorithm a) => TVar ChaChaDRG -> VersionE2E -> PQSupport -> IO (RcvE2EPrivRatchetParams a, RcvE2ERatchetParams a)
|
||||
@@ -474,30 +461,30 @@ data RatchetInitParams = RatchetInitParams
|
||||
-- this is used by the peer joining the connection
|
||||
pqX3dhSnd :: DhAlgorithm a => AE2EPrivRatchetParams a -> E2ERatchetParams 'RKSProposed a -> Either CryptoError (RatchetInitParams, Maybe KEMKeyPair)
|
||||
-- 3. replied 2. received
|
||||
pqX3dhSnd (spk1, spk2, spKem_) (E2ERatchetParams v rk1 rk2 rKem_) = do
|
||||
pqX3dhSnd (spk1, spk2, spKem_) (E2ERatchetParams _ rk1 rk2 rKem_) = do
|
||||
(ks_, kem_) <- sndPq
|
||||
let initParams = pqX3dh (publicKey spk1, rk1) (dh' rk1 spk2) (dh' rk2 spk1) (dh' rk2 spk2) kem_
|
||||
pure (initParams, ks_)
|
||||
where
|
||||
sndPq :: Either CryptoError (Maybe KEMKeyPair, Maybe RatchetKEMAccepted)
|
||||
sndPq = case spKem_ of
|
||||
Just (APRKP _ ps) | v >= pqRatchetE2EEncryptVersion -> case (ps, rKem_) of
|
||||
Just (APRKP _ ps) -> case (ps, rKem_) of
|
||||
(PrivateRKParamsAccepted ct shared ks, Just (RKParamsProposed k)) -> Right (Just ks, Just $ RatchetKEMAccepted k shared ct)
|
||||
(PrivateRKParamsProposed ks, _) -> Right (Just ks, Nothing) -- both parties can send "proposal" in case of ratchet renegotiation
|
||||
_ -> Left CERatchetKEMState
|
||||
_ -> Right (Nothing, Nothing)
|
||||
Nothing -> Right (Nothing, Nothing)
|
||||
|
||||
-- this is used by the peer that created new connection, after receiving the reply
|
||||
pqX3dhRcv :: forall s a. (RatchetKEMStateI s, DhAlgorithm a) => RcvE2EPrivRatchetParams a -> E2ERatchetParams s a -> ExceptT CryptoError IO (RatchetInitParams, Maybe KEMKeyPair)
|
||||
-- 1. sent 4. received in reply
|
||||
pqX3dhRcv (rpk1, rpk2, rpKem_) (E2ERatchetParams v sk1 sk2 sKem_) = do
|
||||
pqX3dhRcv (rpk1, rpk2, rpKem_) (E2ERatchetParams _ sk1 sk2 sKem_) = do
|
||||
kem_ <- rcvPq
|
||||
let initParams = pqX3dh (sk1, publicKey rpk1) (dh' sk2 rpk1) (dh' sk1 rpk2) (dh' sk2 rpk2) (snd <$> kem_)
|
||||
pure (initParams, fst <$> kem_)
|
||||
where
|
||||
rcvPq :: ExceptT CryptoError IO (Maybe (KEMKeyPair, RatchetKEMAccepted))
|
||||
rcvPq = case sKem_ of
|
||||
Just (RKParamsAccepted ct k') | v >= pqRatchetE2EEncryptVersion -> case rpKem_ of
|
||||
Just (RKParamsAccepted ct k') -> case rpKem_ of
|
||||
Just (PrivateRKParamsProposed ks@(_, pk)) -> do
|
||||
shared <- liftIO $ sntrup761Dec ct pk
|
||||
pure $ Just (ks, RatchetKEMAccepted k' shared ct)
|
||||
@@ -721,31 +708,22 @@ data MsgHeader a = MsgHeader
|
||||
-- to allow extension without increasing the size, the actual header length is:
|
||||
-- 69 = 2 (original size) + 2 + 1+56 (Curve448) + 4 + 4
|
||||
-- The exact size is 2288, added reserve
|
||||
paddedHeaderLen :: VersionE2E -> PQSupport -> Int
|
||||
paddedHeaderLen v = \case
|
||||
PQSupportOn | v >= pqRatchetE2EEncryptVersion -> 2310
|
||||
_ -> 88
|
||||
paddedHeaderLen :: PQSupport -> Int
|
||||
paddedHeaderLen = \case
|
||||
PQSupportOn -> 2310
|
||||
PQSupportOff -> 88
|
||||
|
||||
-- only used in tests to validate correct padding
|
||||
-- (2 bytes - version size, 1 byte - header size)
|
||||
fullHeaderLen :: VersionE2E -> PQSupport -> Int
|
||||
fullHeaderLen v pq = 2 + 1 + paddedHeaderLen v pq + authTagSize + ivSize @AES256
|
||||
fullHeaderLen :: PQSupport -> Int
|
||||
fullHeaderLen pq = 2 + 1 + paddedHeaderLen pq + authTagSize + ivSize @AES256
|
||||
|
||||
-- pass the current version, as MsgHeader only includes the max supported version that can be different from the current
|
||||
encodeMsgHeader :: AlgorithmI a => VersionE2E -> MsgHeader a -> ByteString
|
||||
encodeMsgHeader v MsgHeader {msgMaxVersion, msgDHRs, msgKEM, msgPN, msgNs}
|
||||
| v >= pqRatchetE2EEncryptVersion = smpEncode (msgMaxVersion, msgDHRs, msgKEM, msgPN, msgNs)
|
||||
| otherwise = smpEncode (msgMaxVersion, msgDHRs, msgPN, msgNs)
|
||||
|
||||
-- pass the current version, as MsgHeader only includes the max supported version that can be different from the current
|
||||
msgHeaderP :: AlgorithmI a => VersionE2E -> Parser (MsgHeader a)
|
||||
msgHeaderP v = do
|
||||
msgMaxVersion <- smpP
|
||||
msgDHRs <- smpP
|
||||
msgKEM <- if v >= pqRatchetE2EEncryptVersion then smpP else pure Nothing
|
||||
msgPN <- smpP
|
||||
msgNs <- smpP
|
||||
pure MsgHeader {msgMaxVersion, msgDHRs, msgKEM, msgPN, msgNs}
|
||||
instance AlgorithmI a => Encoding (MsgHeader a) where
|
||||
smpEncode MsgHeader {msgMaxVersion, msgDHRs, msgKEM, msgPN, msgNs} =
|
||||
smpEncode (msgMaxVersion, msgDHRs, msgKEM, msgPN, msgNs)
|
||||
smpP = do
|
||||
(msgMaxVersion, msgDHRs, msgKEM, msgPN, msgNs) <- smpP
|
||||
pure MsgHeader {msgMaxVersion, msgDHRs, msgKEM, msgPN, msgNs}
|
||||
|
||||
data EncMessageHeader = EncMessageHeader
|
||||
{ ehVersion :: VersionE2E, -- this is current ratchet version
|
||||
@@ -757,26 +735,11 @@ data EncMessageHeader = EncMessageHeader
|
||||
-- this encoding depends on version in EncMessageHeader because it is "current" ratchet version
|
||||
instance Encoding EncMessageHeader where
|
||||
smpEncode EncMessageHeader {ehVersion, ehIV, ehAuthTag, ehBody} =
|
||||
smpEncode (ehVersion, ehIV, ehAuthTag) <> encodeLarge ehVersion ehBody
|
||||
smpEncode (ehVersion, ehIV, ehAuthTag, Large ehBody)
|
||||
smpP = do
|
||||
(ehVersion, ehIV, ehAuthTag) <- smpP
|
||||
ehBody <- largeP
|
||||
(ehVersion, ehIV, ehAuthTag, Large ehBody) <- smpP
|
||||
pure EncMessageHeader {ehVersion, ehIV, ehAuthTag, ehBody}
|
||||
|
||||
-- the encoder always uses 2-byte lengths for the new version, even for short headers without PQ keys.
|
||||
encodeLarge :: VersionE2E -> ByteString -> ByteString
|
||||
encodeLarge v s
|
||||
| v >= pqRatchetE2EEncryptVersion = smpEncode $ Large s
|
||||
| otherwise = smpEncode s
|
||||
|
||||
-- This parser relies on the fact that header cannot be shorter than 32 bytes (it is ~69 bytes without PQ KEM),
|
||||
-- therefore if the first byte is less or equal to 31 (x1F), then we have 2 byte-length limited to 8191.
|
||||
-- This allows upgrading the current version in one message.
|
||||
largeP :: Parser ByteString
|
||||
largeP = do
|
||||
len1 <- peekWord8'
|
||||
if len1 < 32 then unLarge <$> smpP else smpP
|
||||
|
||||
-- the header is length-prefixed to parse it as string and use as part of associated data for authenticated encryption
|
||||
data EncRatchetMessage = EncRatchetMessage
|
||||
{ emHeader :: ByteString,
|
||||
@@ -784,15 +747,12 @@ data EncRatchetMessage = EncRatchetMessage
|
||||
emBody :: ByteString
|
||||
}
|
||||
|
||||
encodeEncRatchetMessage :: VersionE2E -> EncRatchetMessage -> ByteString
|
||||
encodeEncRatchetMessage v EncRatchetMessage {emHeader, emBody, emAuthTag} =
|
||||
encodeLarge v emHeader <> smpEncode (emAuthTag, Tail emBody)
|
||||
|
||||
encRatchetMessageP :: Parser EncRatchetMessage
|
||||
encRatchetMessageP = do
|
||||
emHeader <- largeP
|
||||
(emAuthTag, Tail emBody) <- smpP
|
||||
pure EncRatchetMessage {emHeader, emBody, emAuthTag}
|
||||
instance Encoding EncRatchetMessage where
|
||||
smpEncode EncRatchetMessage {emHeader, emBody, emAuthTag} =
|
||||
smpEncode (Large emHeader, emAuthTag, Tail emBody)
|
||||
smpP = do
|
||||
(Large emHeader, emAuthTag, Tail emBody) <- smpP
|
||||
pure EncRatchetMessage {emHeader, emBody, emAuthTag}
|
||||
|
||||
newtype PQEncryption = PQEncryption {enablePQ :: Bool}
|
||||
deriving (Eq, Show)
|
||||
@@ -841,15 +801,15 @@ pqEncToSupport (PQEncryption pq) = PQSupport pq
|
||||
pqSupportAnd :: PQSupport -> PQSupport -> PQSupport
|
||||
pqSupportAnd (PQSupport s1) (PQSupport s2) = PQSupport $ s1 && s2
|
||||
|
||||
pqEnableSupport :: VersionE2E -> PQSupport -> PQEncryption -> PQSupport
|
||||
pqEnableSupport v (PQSupport sup) (PQEncryption enc) = PQSupport $ sup || (v >= pqRatchetE2EEncryptVersion && enc)
|
||||
pqEnableSupport :: PQSupport -> PQEncryption -> PQSupport
|
||||
pqEnableSupport (PQSupport sup) (PQEncryption enc) = PQSupport $ sup || enc
|
||||
|
||||
replyKEM_ :: VersionE2E -> Maybe (RKEMParams 'RKSProposed) -> PQSupport -> Maybe AUseKEM
|
||||
replyKEM_ v kem_ = \case
|
||||
PQSupportOn | v >= pqRatchetE2EEncryptVersion -> Just $ case kem_ of
|
||||
replyKEM_ :: Maybe (RKEMParams 'RKSProposed) -> PQSupport -> Maybe AUseKEM
|
||||
replyKEM_ kem_ = \case
|
||||
PQSupportOn -> Just $ case kem_ of
|
||||
Just (RKParamsProposed k) -> AUseKEM SRKSAccepted $ AcceptKEM k
|
||||
Nothing -> AUseKEM SRKSProposed ProposeKEM
|
||||
_ -> Nothing
|
||||
PQSupportOff -> Nothing
|
||||
|
||||
instance StrEncoding PQEncryption where
|
||||
strEncode pqMode
|
||||
@@ -898,9 +858,9 @@ connPQEncryption = \case
|
||||
IKUsePQ -> PQSupportOn
|
||||
IKLinkPQ pq -> pq -- default for creating connection is IKLinkPQ PQEncOn
|
||||
|
||||
joinContactInitialKeys :: Bool -> PQSupport -> InitialKeys
|
||||
joinContactInitialKeys pqCompatible = \case
|
||||
PQSupportOn | pqCompatible -> IKUsePQ
|
||||
joinContactInitialKeys :: PQSupport -> InitialKeys
|
||||
joinContactInitialKeys = \case
|
||||
PQSupportOn -> IKUsePQ
|
||||
pqEnc -> IKLinkPQ pqEnc
|
||||
|
||||
rcCheckCanPad :: Int -> ByteString -> ExceptT CryptoError IO ()
|
||||
@@ -916,14 +876,14 @@ rcEncryptHeader rc@Ratchet {rcSnd = Just sr@SndRatchet {rcCKs, rcHKs}, rcDHRs, r
|
||||
-- PQ encryption can be enabled or disabled
|
||||
rcEnableKEM' = fromMaybe rcEnableKEM pqEnc_
|
||||
-- support for PQ encryption (and therefore large headers/small envelopes) can only be enabled, it cannot be disabled
|
||||
rcSupportKEM' = pqEnableSupport v rcSupportKEM rcEnableKEM'
|
||||
rcSupportKEM' = pqEnableSupport rcSupportKEM rcEnableKEM'
|
||||
-- This sets max version to support PQ encryption.
|
||||
-- Current version upgrade happens when peer decrypts the message.
|
||||
-- TODO note that maxSupported will not downgrade here below current (v).
|
||||
maxSupported' = max supportedE2EVersion $ if pqEnc_ == Just PQEncOn then pqRatchetE2EEncryptVersion else v
|
||||
maxSupported' = max supportedE2EVersion $ if pqEnc_ == Just PQEncOn then minSupportedE2EEncryptVersion else v
|
||||
rcVersion' = rcVersion {maxSupported = maxSupported'}
|
||||
-- enc_header = HENCRYPT(state.HKs, header)
|
||||
(ehAuthTag, ehBody) <- encryptAEAD rcHKs ehIV (paddedHeaderLen v rcSupportKEM') rcAD (msgHeader v maxSupported')
|
||||
(ehAuthTag, ehBody) <- encryptAEAD rcHKs ehIV (paddedHeaderLen rcSupportKEM') rcAD (msgHeader maxSupported')
|
||||
-- return enc_header
|
||||
let emHeader = smpEncode EncMessageHeader {ehVersion = v, ehBody, ehAuthTag, ehIV}
|
||||
msgEncryptKey =
|
||||
@@ -951,9 +911,8 @@ rcEncryptHeader rc@Ratchet {rcSnd = Just sr@SndRatchet {rcCKs, rcHKs}, rcDHRs, r
|
||||
-- pn = state.PN,
|
||||
-- n = state.Ns
|
||||
-- )
|
||||
msgHeader v maxSupported' =
|
||||
encodeMsgHeader
|
||||
v
|
||||
msgHeader maxSupported' =
|
||||
smpEncode
|
||||
MsgHeader
|
||||
{ msgMaxVersion = maxSupported',
|
||||
msgDHRs = publicKey rcDHRs,
|
||||
@@ -976,11 +935,10 @@ data MsgEncryptKey a = MsgEncryptKey
|
||||
deriving (Show)
|
||||
|
||||
rcEncryptMsg :: AlgorithmI a => MsgEncryptKey a -> Int -> ByteString -> ExceptT CryptoError IO ByteString
|
||||
rcEncryptMsg MsgEncryptKey {msgKey = MessageKey mk iv, msgRcAD, msgEncHeader, msgRcVersion = v} paddedMsgLen msg = do
|
||||
rcEncryptMsg MsgEncryptKey {msgKey = MessageKey mk iv, msgRcAD, msgEncHeader} paddedMsgLen msg = do
|
||||
-- return ENCRYPT(mk, plaintext, CONCAT(AD, enc_header))
|
||||
(emAuthTag, emBody) <- encryptAEAD mk iv paddedMsgLen (msgRcAD <> msgEncHeader) msg
|
||||
let msg' = encodeEncRatchetMessage v EncRatchetMessage {emHeader = msgEncHeader, emBody, emAuthTag}
|
||||
pure msg'
|
||||
pure $ smpEncode EncRatchetMessage {emHeader = msgEncHeader, emBody, emAuthTag}
|
||||
|
||||
data SkippedMessage a
|
||||
= SMMessage (DecryptResult a)
|
||||
@@ -1004,7 +962,7 @@ rcDecrypt ::
|
||||
ByteString ->
|
||||
ExceptT CryptoError IO (DecryptResult a)
|
||||
rcDecrypt g rc@Ratchet {rcRcv, rcAD = Str rcAD, rcVersion} rcMKSkipped msg' = do
|
||||
encMsg@EncRatchetMessage {emHeader} <- parseE CryptoHeaderError encRatchetMessageP msg'
|
||||
encMsg@EncRatchetMessage {emHeader} <- parseE CryptoHeaderError smpP msg'
|
||||
encHdr <- parseE CryptoHeaderError smpP emHeader
|
||||
-- plaintext = TrySkippedMessageKeysHE(state, enc_header, cipher-text, AD)
|
||||
decryptSkipped encHdr encMsg >>= \case
|
||||
@@ -1049,7 +1007,7 @@ rcDecrypt g rc@Ratchet {rcRcv, rcAD = Str rcAD, rcVersion} rcMKSkipped msg' = do
|
||||
smkDiff :: SkippedMsgKeys -> SkippedMsgDiff
|
||||
smkDiff smks = if M.null smks then SMDNoChange else SMDAdd smks
|
||||
ratchetStep :: Ratchet a -> MsgHeader a -> ExceptT CryptoError IO (Ratchet a)
|
||||
ratchetStep rc'@Ratchet {rcDHRs, rcRK, rcNHKs, rcNHKr, rcSupportKEM, rcVersion = rv} MsgHeader {msgDHRs, msgKEM} = do
|
||||
ratchetStep rc'@Ratchet {rcDHRs, rcRK, rcNHKs, rcNHKr, rcSupportKEM} MsgHeader {msgDHRs, msgKEM} = do
|
||||
(kemSS, kemSS', rcKEM') <- pqRatchetStep rc' msgKEM
|
||||
-- state.DHRs = GENERATE_DH()
|
||||
(_, rcDHRs') <- atomically $ generateKeyPair @a g
|
||||
@@ -1064,7 +1022,7 @@ rcDecrypt g rc@Ratchet {rcRcv, rcAD = Str rcAD, rcVersion} rcMKSkipped msg' = do
|
||||
rc'
|
||||
{ rcDHRs = rcDHRs',
|
||||
rcKEM = rcKEM',
|
||||
rcSupportKEM = pqEnableSupport (current rv) rcSupportKEM rcEnableKEM',
|
||||
rcSupportKEM = pqEnableSupport rcSupportKEM rcEnableKEM',
|
||||
rcEnableKEM = rcEnableKEM',
|
||||
rcSndKEM = PQEncryption sndKEM,
|
||||
rcRcvKEM = PQEncryption rcvKEM,
|
||||
@@ -1078,17 +1036,17 @@ rcDecrypt g rc@Ratchet {rcRcv, rcAD = Str rcAD, rcVersion} rcMKSkipped msg' = do
|
||||
rcNHKr = rcNHKr'
|
||||
}
|
||||
pqRatchetStep :: Ratchet a -> Maybe ARKEMParams -> ExceptT CryptoError IO (Maybe KEMSharedKey, Maybe KEMSharedKey, Maybe RatchetKEM)
|
||||
pqRatchetStep Ratchet {rcKEM, rcEnableKEM = PQEncryption pqEnc, rcVersion = rv} = \case
|
||||
pqRatchetStep Ratchet {rcKEM, rcEnableKEM = PQEncryption pqEnc} = \case
|
||||
-- received message does not have KEM in header,
|
||||
-- but the user enabled KEM when sending previous message
|
||||
Nothing -> case rcKEM of
|
||||
Nothing | pqEnc && current rv >= pqRatchetE2EEncryptVersion -> do
|
||||
Nothing | pqEnc -> do
|
||||
rcPQRs <- liftIO $ sntrup761Keypair g
|
||||
pure (Nothing, Nothing, Just RatchetKEM {rcPQRs, rcKEMs = Nothing})
|
||||
_ -> pure (Nothing, Nothing, Nothing)
|
||||
-- received message has KEM in header.
|
||||
Just (ARKP _ ps)
|
||||
| pqEnc && current rv >= pqRatchetE2EEncryptVersion -> do
|
||||
| pqEnc -> do
|
||||
-- state.PQRr = header.kem
|
||||
(ss, rcPQRr) <- sharedSecret
|
||||
-- state.PQRct = PQKEM-ENC(state.PQRr, state.PQRss) // encapsulated additional shared secret KEM #1
|
||||
@@ -1156,9 +1114,9 @@ rcDecrypt g rc@Ratchet {rcRcv, rcAD = Str rcAD, rcVersion} rcMKSkipped msg' = do
|
||||
e -> throwE e
|
||||
-- header = HDECRYPT(state.NHKr, enc_header)
|
||||
decryptNextHeader hdr = (AdvanceRatchet,) <$> decryptHeader (rcNHKr rc) hdr
|
||||
decryptHeader k EncMessageHeader {ehVersion, ehBody, ehAuthTag, ehIV} = do
|
||||
decryptHeader k EncMessageHeader {ehBody, ehAuthTag, ehIV} = do
|
||||
header <- decryptAEAD k ehIV rcAD ehBody ehAuthTag `catchE` \_ -> throwE CERatchetHeader
|
||||
parseE' CryptoHeaderError (msgHeaderP ehVersion) header
|
||||
parseE' CryptoHeaderError smpP header
|
||||
decryptMessage :: MessageKey -> EncRatchetMessage -> ExceptT CryptoError IO (Either CryptoError ByteString)
|
||||
decryptMessage (MessageKey mk iv) EncRatchetMessage {emHeader, emBody, emAuthTag} =
|
||||
-- DECRYPT(mk, cipher-text, CONCAT(AD, enc_header))
|
||||
|
||||
@@ -120,6 +120,6 @@ decryptLinkData linkKey k (encFD, encMD) = do
|
||||
pure (sig, s)
|
||||
decode :: Encoding a => ByteString -> Either AgentErrorType a
|
||||
decode = msgErr . smpDecode
|
||||
msgErr = first (const $ AGENT A_MESSAGE)
|
||||
msgErr = first (const $ AGENT $ A_MESSAGE "parse link data")
|
||||
linkErr :: String -> Either AgentErrorType ()
|
||||
linkErr = Left . AGENT . A_LINK
|
||||
|
||||
@@ -26,7 +26,7 @@ import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.Ratchet
|
||||
import Simplex.Messaging.Encoding
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Protocol (EntityId (..), ProtocolServer (..), QueueMode (..), SubscriptionMode (..), 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 hiding (fit, it)
|
||||
@@ -146,8 +146,8 @@ connReqData1 = connReqData {crSmpQueues = [queue1]}
|
||||
connReqDataV1 :: ConnReqUriData
|
||||
connReqDataV1 = connReqData {crAgentVRange = mkVersionRange (VersionSMPA 1) (VersionSMPA 1)}
|
||||
|
||||
connReqDataV2 :: ConnReqUriData
|
||||
connReqDataV2 = connReqData {crAgentVRange = mkVersionRange (VersionSMPA 2) (VersionSMPA 2)}
|
||||
connReqDataV6 :: ConnReqUriData
|
||||
connReqDataV6 = connReqData {crAgentVRange = mkVersionRange (VersionSMPA 6) (VersionSMPA 6)}
|
||||
|
||||
connReqDataNew :: ConnReqUriData
|
||||
connReqDataNew = connReqData {crSmpQueues = [queueNew]}
|
||||
@@ -159,10 +159,10 @@ testDhPubKey :: C.PublicKeyX448
|
||||
testDhPubKey = "MEIwBQYDK2VvAzkAmKuSYeQ/m0SixPDS8Wq8VBaTS1cW+Lp0n0h4Diu+kUpR+qXx4SDJ32YGEFoGFGSbGPry5Ychr6U="
|
||||
|
||||
testE2ERatchetParams :: RcvE2ERatchetParamsUri 'C.X448
|
||||
testE2ERatchetParams = E2ERatchetParamsUri (mkVersionRange (VersionE2E 1) (VersionE2E 1)) testDhPubKey testDhPubKey Nothing
|
||||
testE2ERatchetParams = E2ERatchetParamsUri (mkVersionRange (VersionE2E 3) (VersionE2E 3)) testDhPubKey testDhPubKey Nothing
|
||||
|
||||
testE2ERatchetParamsStrUri :: ByteString
|
||||
testE2ERatchetParamsStrUri = "v%3D1%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D"
|
||||
testE2ERatchetParamsStrUri = "v%3D3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D"
|
||||
|
||||
testE2ERatchetParams12 :: RcvE2ERatchetParamsUri 'C.X448
|
||||
testE2ERatchetParams12 = E2ERatchetParamsUri supportedE2EEncryptVRange testDhPubKey testDhPubKey Nothing
|
||||
@@ -200,8 +200,8 @@ contactConnRequest = CRContactUri connReqData Nothing
|
||||
contactAddressDR :: AConnectionRequestUri
|
||||
contactAddressDR = ACR SCMContact $ CRContactUri connReqData (Just (RatchetKeyId "0123456789abcdef", testE2ERatchetParams))
|
||||
|
||||
contactAddressV2 :: AConnectionRequestUri
|
||||
contactAddressV2 = ACR SCMContact $ CRContactUri connReqDataV2 Nothing
|
||||
contactAddressV6 :: AConnectionRequestUri
|
||||
contactAddressV6 = ACR SCMContact $ CRContactUri connReqDataV6 Nothing
|
||||
|
||||
contactAddressNew :: AConnectionRequestUri
|
||||
contactAddressNew = ACR SCMContact $ CRContactUri connReqDataNew Nothing
|
||||
@@ -264,27 +264,27 @@ connectionRequestTests =
|
||||
queueV1NoPort #== ("smp://1234-w==@smp.simplex.im/3456-w==#/?v=1-1&dh=" <> url testDhKeyStr <> "&srv=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion")
|
||||
queueV1NoPort #== ("smp://1234-w==@smp.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion/3456-w==#" <> testDhKeyStr)
|
||||
it "should serialize and parse connection invitations and contact addresses" $ do
|
||||
connectionRequest #==# ("simplex:/invitation#/?v=2-7&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequest #== ("https://simplex.chat/invitation#/?v=2-7&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequestNoQM #==# ("simplex:/invitation#/?v=2-7&smp=" <> url queueStrNoQM <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequest1 #==# ("simplex:/invitation#/?v=2-7&smp=" <> url queue1Str <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequest2queues #==# ("simplex:/invitation#/?v=2-7&smp=" <> url (queueStr <> ";" <> queueStr) <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequestNew #==# ("simplex:/invitation#/?v=2-7&smp=" <> url queueNewStr <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequestNew1 #==# ("simplex:/invitation#/?v=2-7&smp=" <> url queueNew1Str <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequest2queuesNew #==# ("simplex:/invitation#/?v=2-7&smp=" <> url (queueNewStr <> ";" <> queueNewStr) <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequest #==# ("simplex:/invitation#/?v=6-8&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequest #== ("https://simplex.chat/invitation#/?v=6-8&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequestNoQM #==# ("simplex:/invitation#/?v=6-8&smp=" <> url queueStrNoQM <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequest1 #==# ("simplex:/invitation#/?v=6-8&smp=" <> url queue1Str <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequest2queues #==# ("simplex:/invitation#/?v=6-8&smp=" <> url (queueStr <> ";" <> queueStr) <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequestNew #==# ("simplex:/invitation#/?v=6-8&smp=" <> url queueNewStr <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequestNew1 #==# ("simplex:/invitation#/?v=6-8&smp=" <> url queueNew1Str <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequest2queuesNew #==# ("simplex:/invitation#/?v=6-8&smp=" <> url (queueNewStr <> ";" <> queueNewStr) <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequestV1 #== ("https://simplex.chat/invitation#/?v=1&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri)
|
||||
connectionRequestClientDataEmpty #==# ("simplex:/invitation#/?v=2-7&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri <> "&data=" <> url "{}")
|
||||
contactAddress #==# ("simplex:/contact#/?v=2-7&smp=" <> url queueStr)
|
||||
contactAddressDR #==# ("simplex:/contact#/?v=2-7&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri <> "&rk=MDEyMzQ1Njc4OWFiY2RlZg%3D%3D")
|
||||
contactAddress #== ("https://simplex.chat/contact#/?v=2-7&smp=" <> url queueStr)
|
||||
contactAddress2queues #==# ("simplex:/contact#/?v=2-7&smp=" <> url (queueStr <> ";" <> queueStr))
|
||||
contactAddressNew #==# ("simplex:/contact#/?v=2-7&smp=" <> url queueNewStr)
|
||||
contactAddress2queuesNew #==# ("simplex:/contact#/?v=2-7&smp=" <> url (queueNewStr <> ";" <> queueNewStr))
|
||||
contactAddressV2 #==# ("simplex:/contact#/?v=2&smp=" <> url queueStr)
|
||||
contactAddressV2 #== ("https://simplex.chat/contact#/?v=1&smp=" <> url queueStr) -- adjusted to v2
|
||||
contactAddressV2 #== ("https://simplex.chat/contact#/?v=1-2&smp=" <> url queueStr) -- adjusted to v2
|
||||
contactAddressV2 #== ("https://simplex.chat/contact#/?v=2-2&smp=" <> url queueStr)
|
||||
contactAddressClientData #==# ("simplex:/contact#/?v=2-7&smp=" <> url queueStr <> "&data=" <> url "{\"type\":\"group_link\", \"group_link_id\":\"abc\"}")
|
||||
connectionRequestClientDataEmpty #==# ("simplex:/invitation#/?v=6-8&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri <> "&data=" <> url "{}")
|
||||
contactAddress #==# ("simplex:/contact#/?v=6-8&smp=" <> url queueStr)
|
||||
contactAddressDR #==# ("simplex:/contact#/?v=6-8&smp=" <> url queueStr <> "&e2e=" <> testE2ERatchetParamsStrUri <> "&rk=MDEyMzQ1Njc4OWFiY2RlZg%3D%3D")
|
||||
contactAddress #== ("https://simplex.chat/contact#/?v=6-8&smp=" <> url queueStr)
|
||||
contactAddress2queues #==# ("simplex:/contact#/?v=6-8&smp=" <> url (queueStr <> ";" <> queueStr))
|
||||
contactAddressNew #==# ("simplex:/contact#/?v=6-8&smp=" <> url queueNewStr)
|
||||
contactAddress2queuesNew #==# ("simplex:/contact#/?v=6-8&smp=" <> url (queueNewStr <> ";" <> queueNewStr))
|
||||
contactAddressV6 #==# ("simplex:/contact#/?v=6&smp=" <> url queueStr)
|
||||
contactAddressV6 #== ("https://simplex.chat/contact#/?v=1&smp=" <> url queueStr) -- adjusted to v6
|
||||
contactAddressV6 #== ("https://simplex.chat/contact#/?v=1-2&smp=" <> url queueStr) -- adjusted to v6
|
||||
contactAddressV6 #== ("https://simplex.chat/contact#/?v=2-2&smp=" <> url queueStr)
|
||||
contactAddressClientData #==# ("simplex:/contact#/?v=6-8&smp=" <> url queueStr <> "&data=" <> url "{\"type\":\"group_link\", \"group_link_id\":\"abc\"}")
|
||||
it "should serialize / parse queue address, connection invitations and contact addresses as binary" $ do
|
||||
smpEncodingTest queue
|
||||
smpEncodingTest queueNoQM -- this passes, no queue mode patch in SMPQueueUri encoding
|
||||
@@ -309,7 +309,7 @@ connectionRequestTests =
|
||||
smpEncodingTest (aBinaryConnReq contactAddress2queues)
|
||||
smpEncodingTest (aBinaryConnReq contactAddressNew)
|
||||
smpEncodingTest (aBinaryConnReq contactAddress2queuesNew)
|
||||
smpEncodingTest (aBinaryConnReq contactAddressV2)
|
||||
smpEncodingTest (aBinaryConnReq contactAddressV6)
|
||||
smpEncodingTest (aBinaryConnReq contactAddressClientData)
|
||||
it "should serialize / parse short links" $ do
|
||||
CSLContact SLSServer CCTContact srv (LinkKey "0123456789abcdef0123456789abcdef") #==# "https://smp.simplex.im/a#MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY?h=jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&p=5223&c=1234-w"
|
||||
|
||||
@@ -39,8 +39,7 @@ doubleRatchetTests :: Spec
|
||||
doubleRatchetTests = do
|
||||
describe "double-ratchet encryption/decryption" $ do
|
||||
it "should serialize and parse message header" $ do
|
||||
testAlgs $ testMessageHeader kdfX3DHE2EEncryptVersion
|
||||
testAlgs $ testMessageHeader $ max pqRatchetE2EEncryptVersion currentE2EEncryptVersion
|
||||
testAlgs $ testMessageHeader currentE2EEncryptVersion
|
||||
describe "message tests" $ runMessageTests initRatchets False
|
||||
it "should encode/decode ratchet as JSON" $ do
|
||||
testAlgs testKeyJSON
|
||||
@@ -90,18 +89,15 @@ paddedMsgLen :: Int
|
||||
paddedMsgLen = 100
|
||||
|
||||
fullMsgLen :: Ratchet a -> Int
|
||||
fullMsgLen Ratchet {rcSupportKEM, rcVersion} = headerLenLength + fullHeaderLen v rcSupportKEM + C.authTagSize + paddedMsgLen
|
||||
fullMsgLen Ratchet {rcSupportKEM} = headerLenLength + fullHeaderLen rcSupportKEM + C.authTagSize + paddedMsgLen
|
||||
where
|
||||
v = current rcVersion
|
||||
headerLenLength
|
||||
| v >= pqRatchetE2EEncryptVersion = 3 -- two bytes are added because of two Large used in new encoding
|
||||
| otherwise = 1
|
||||
headerLenLength = 3 -- two bytes are added because of two Large used in new encoding
|
||||
|
||||
testMessageHeader :: forall a. AlgorithmI a => VersionE2E -> C.SAlgorithm a -> Expectation
|
||||
testMessageHeader v _ = do
|
||||
(k, _) <- atomically . C.generateKeyPair @a =<< C.newRandom
|
||||
let hdr = MsgHeader {msgMaxVersion = v, msgDHRs = k, msgKEM = Nothing, msgPN = 0, msgNs = 0}
|
||||
parseAll (msgHeaderP v) (encodeMsgHeader v hdr) `shouldBe` Right hdr
|
||||
smpDecode (smpEncode hdr) `shouldBe` Right hdr
|
||||
|
||||
testKEMParams :: Expectation
|
||||
testKEMParams = do
|
||||
@@ -119,15 +115,15 @@ testMessageHeaderKEM _ = do
|
||||
g <- C.newRandom
|
||||
(k, _) <- atomically $ C.generateKeyPair @a g
|
||||
(kem, _) <- sntrup761Keypair g
|
||||
let msgMaxVersion = max pqRatchetE2EEncryptVersion currentE2EEncryptVersion
|
||||
let msgMaxVersion = currentE2EEncryptVersion
|
||||
msgKEM = Just . ARKP SRKSProposed $ RKParamsProposed kem
|
||||
hdr = MsgHeader {msgMaxVersion, msgDHRs = k, msgKEM, msgPN = 0, msgNs = 0}
|
||||
parseAll (msgHeaderP msgMaxVersion) (encodeMsgHeader msgMaxVersion hdr) `shouldBe` Right hdr
|
||||
smpDecode (smpEncode hdr) `shouldBe` Right hdr
|
||||
(kem', _) <- sntrup761Keypair g
|
||||
(ct, _) <- sntrup761Enc g kem
|
||||
let msgKEM' = Just . ARKP SRKSAccepted $ RKParamsAccepted ct kem'
|
||||
hdr' = MsgHeader {msgMaxVersion, msgDHRs = k, msgKEM = msgKEM', msgPN = 0, msgNs = 0}
|
||||
parseAll (msgHeaderP msgMaxVersion) (encodeMsgHeader msgMaxVersion hdr') `shouldBe` Right hdr'
|
||||
smpDecode (smpEncode hdr') `shouldBe` Right hdr'
|
||||
|
||||
pattern Decrypted :: ByteString -> Either CryptoError (Either CryptoError ByteString)
|
||||
pattern Decrypted msg <- Right (Right msg)
|
||||
@@ -380,7 +376,7 @@ testEncodeDecode x = do
|
||||
testX3dh :: forall a. (AlgorithmI a, DhAlgorithm a) => C.SAlgorithm a -> IO ()
|
||||
testX3dh _ = do
|
||||
g <- C.newRandom
|
||||
let v = max pqRatchetE2EEncryptVersion currentE2EEncryptVersion
|
||||
let v = currentE2EEncryptVersion
|
||||
(pksBob@(_, _, Nothing), AE2ERatchetParams _ e2eBob) <- liftIO $ generateSndE2EParams @a g v Nothing
|
||||
(pksAlice@(_, _, Nothing), e2eAlice) <- liftIO $ generateRcvE2EParams @a g v PQSupportOff
|
||||
let paramsBob = pqX3dhSnd pksBob e2eAlice
|
||||
@@ -399,7 +395,7 @@ testX3dhV1 _ = do
|
||||
testPqX3dhProposeInReply :: forall a. (AlgorithmI a, DhAlgorithm a) => C.SAlgorithm a -> IO ()
|
||||
testPqX3dhProposeInReply _ = do
|
||||
g <- C.newRandom
|
||||
let v = max pqRatchetE2EEncryptVersion currentE2EEncryptVersion
|
||||
let v = currentE2EEncryptVersion
|
||||
-- initiate (no KEM)
|
||||
(pksAlice@(_, _, Nothing), e2eAlice) <- liftIO $ generateRcvE2EParams @a g v PQSupportOff
|
||||
-- propose KEM in reply
|
||||
@@ -411,7 +407,7 @@ testPqX3dhProposeInReply _ = do
|
||||
testPqX3dhProposeAccept :: forall a. (AlgorithmI a, DhAlgorithm a) => C.SAlgorithm a -> IO ()
|
||||
testPqX3dhProposeAccept _ = do
|
||||
g <- C.newRandom
|
||||
let v = max pqRatchetE2EEncryptVersion currentE2EEncryptVersion
|
||||
let v = currentE2EEncryptVersion
|
||||
-- initiate (propose KEM)
|
||||
(pksAlice@(_, _, Just _), e2eAlice) <- liftIO $ generateRcvE2EParams @a g v PQSupportOn
|
||||
E2ERatchetParams _ _ _ (Just (RKParamsProposed aliceKem)) <- pure e2eAlice
|
||||
@@ -424,7 +420,7 @@ testPqX3dhProposeAccept _ = do
|
||||
testPqX3dhProposeReject :: forall a. (AlgorithmI a, DhAlgorithm a) => C.SAlgorithm a -> IO ()
|
||||
testPqX3dhProposeReject _ = do
|
||||
g <- C.newRandom
|
||||
let v = max pqRatchetE2EEncryptVersion currentE2EEncryptVersion
|
||||
let v = currentE2EEncryptVersion
|
||||
-- initiate (propose KEM)
|
||||
(pksAlice@(_, _, Just _), e2eAlice) <- liftIO $ generateRcvE2EParams @a g v PQSupportOn
|
||||
E2ERatchetParams _ _ _ (Just (RKParamsProposed _)) <- pure e2eAlice
|
||||
@@ -437,7 +433,7 @@ testPqX3dhProposeReject _ = do
|
||||
testPqX3dhAcceptWithoutProposalError :: forall a. (AlgorithmI a, DhAlgorithm a) => C.SAlgorithm a -> IO ()
|
||||
testPqX3dhAcceptWithoutProposalError _ = do
|
||||
g <- C.newRandom
|
||||
let v = max pqRatchetE2EEncryptVersion currentE2EEncryptVersion
|
||||
let v = currentE2EEncryptVersion
|
||||
-- initiate (no KEM)
|
||||
(pksAlice@(_, _, Nothing), e2eAlice) <- liftIO $ generateRcvE2EParams @a g v PQSupportOff
|
||||
E2ERatchetParams _ _ _ Nothing <- pure e2eAlice
|
||||
@@ -451,7 +447,7 @@ testPqX3dhAcceptWithoutProposalError _ = do
|
||||
testPqX3dhProposeAgain :: forall a. (AlgorithmI a, DhAlgorithm a) => C.SAlgorithm a -> IO ()
|
||||
testPqX3dhProposeAgain _ = do
|
||||
g <- C.newRandom
|
||||
let v = max pqRatchetE2EEncryptVersion currentE2EEncryptVersion
|
||||
let v = currentE2EEncryptVersion
|
||||
-- initiate (propose KEM)
|
||||
(pksAlice@(_, _, Just _), e2eAlice) <- liftIO $ generateRcvE2EParams @a g v PQSupportOn
|
||||
E2ERatchetParams _ _ _ (Just (RKParamsProposed _)) <- pure e2eAlice
|
||||
@@ -514,7 +510,7 @@ withRatchets_ initRatchets_ test = do
|
||||
initRatchets :: (AlgorithmI a, DhAlgorithm a) => IO (Ratchet a, Ratchet a, Encrypt a, Decrypt a, EncryptDecryptSpec a)
|
||||
initRatchets = do
|
||||
g <- C.newRandom
|
||||
let v = max pqRatchetE2EEncryptVersion currentE2EEncryptVersion
|
||||
let v = currentE2EEncryptVersion
|
||||
(pksBob@(_, _, Nothing), AE2ERatchetParams _ e2eBob) <- liftIO $ generateSndE2EParams g v Nothing
|
||||
(pksAlice@(_, pkAlice2, Nothing), e2eAlice) <- liftIO $ generateRcvE2EParams g v PQSupportOff
|
||||
Right paramsBob <- pure $ pqX3dhSnd pksBob e2eAlice
|
||||
@@ -528,7 +524,7 @@ initRatchets = do
|
||||
initRatchetsKEMProposed :: forall a. (AlgorithmI a, DhAlgorithm a) => IO (Ratchet a, Ratchet a, Encrypt a, Decrypt a, EncryptDecryptSpec a)
|
||||
initRatchetsKEMProposed = do
|
||||
g <- C.newRandom
|
||||
let v = max pqRatchetE2EEncryptVersion currentE2EEncryptVersion
|
||||
let v = currentE2EEncryptVersion
|
||||
-- initiate (no KEM)
|
||||
(pksAlice@(_, pkAlice2, Nothing), e2eAlice) <- liftIO $ generateRcvE2EParams g v PQSupportOff
|
||||
-- propose KEM in reply
|
||||
@@ -545,7 +541,7 @@ initRatchetsKEMProposed = do
|
||||
initRatchetsKEMAccepted :: forall a. (AlgorithmI a, DhAlgorithm a) => IO (Ratchet a, Ratchet a, Encrypt a, Decrypt a, EncryptDecryptSpec a)
|
||||
initRatchetsKEMAccepted = do
|
||||
g <- C.newRandom
|
||||
let v = max pqRatchetE2EEncryptVersion currentE2EEncryptVersion
|
||||
let v = currentE2EEncryptVersion
|
||||
-- initiate (propose)
|
||||
(pksAlice@(_, pkAlice2, Just _), e2eAlice) <- liftIO $ generateRcvE2EParams g v PQSupportOn
|
||||
E2ERatchetParams _ _ _ (Just (RKParamsProposed aliceKem)) <- pure e2eAlice
|
||||
@@ -563,7 +559,7 @@ initRatchetsKEMAccepted = do
|
||||
initRatchetsKEMProposedAgain :: forall a. (AlgorithmI a, DhAlgorithm a) => IO (Ratchet a, Ratchet a, Encrypt a, Decrypt a, EncryptDecryptSpec a)
|
||||
initRatchetsKEMProposedAgain = do
|
||||
g <- C.newRandom
|
||||
let v = max pqRatchetE2EEncryptVersion currentE2EEncryptVersion
|
||||
let v = currentE2EEncryptVersion
|
||||
-- initiate (propose KEM)
|
||||
(pksAlice@(_, pkAlice2, Just _), e2eAlice) <- liftIO $ generateRcvE2EParams g v PQSupportOn
|
||||
-- propose KEM again in reply
|
||||
|
||||
@@ -233,8 +233,9 @@ smpCfgVPrev = (smpCfg agentCfg) {serverVRange = prevRange $ serverVRange $ smpCf
|
||||
-- ntfCfgVPrev :: ProtocolClientConfig NTFVersion
|
||||
-- ntfCfgVPrev = (ntfCfg agentCfg) {clientALPN = Nothing, serverVRange = V.mkVersionRange (VersionNTF 1) (VersionNTF 1)}
|
||||
|
||||
-- currently, previous e2e version is not supported
|
||||
agentCfgVPrev :: AgentConfig
|
||||
agentCfgVPrev = agentCfgVPrevPQ {e2eEncryptVRange = prevRange $ e2eEncryptVRange agentCfg}
|
||||
agentCfgVPrev = agentCfgVPrevPQ -- {e2eEncryptVRange = prevRange $ e2eEncryptVRange agentCfg}
|
||||
|
||||
agentCfgVPrevPQ :: AgentConfig
|
||||
agentCfgVPrevPQ =
|
||||
@@ -336,13 +337,13 @@ 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 True True
|
||||
testPQMatrix2 ps $ runAgentClientTestPQ 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 True True PQSupportOn
|
||||
testPQMatrix2NoInv ps $ runAgentClientContactTestPQ True True
|
||||
describe "Establish duplex connection via contact address v2, different Ratchet versions" $
|
||||
testRatchetMatrix2 ps runAgentClientContactTest
|
||||
describe "Establish duplex connection via contact address, different PQ settings (3 clients)" $ do
|
||||
@@ -503,7 +504,7 @@ functionalAPITests ps = do
|
||||
testWaitDeliveryNoPending ps
|
||||
it "should delete connection after waiting for delivery to complete" $
|
||||
testWaitDelivery ps
|
||||
it "should delete connection if message can'ps be delivered due to AUTH error" $
|
||||
it "should delete connection if message can't be delivered due to AUTH error" $
|
||||
testWaitDeliveryAUTHErr ps
|
||||
it "should delete connection by timeout even if message wasn't delivered" $
|
||||
testWaitDeliveryTimeout ps
|
||||
@@ -578,7 +579,6 @@ functionalAPITests ps = do
|
||||
withSmpServer ps testRatchetAdHash
|
||||
describe "Delivery receipts" $ do
|
||||
it "should send and receive delivery receipt" $ withSmpServer ps testDeliveryReceipts
|
||||
it "should send delivery receipt only in connection v3+" $ testDeliveryReceiptsVersion ps
|
||||
it "send delivery receipts concurrently with messages" $ testDeliveryReceiptsConcurrent ps
|
||||
describe "user network info" $ do
|
||||
it "should wait for user network" testWaitForUserNetwork
|
||||
@@ -613,27 +613,27 @@ testMatrix2 :: HasCallStack => (ASrvTransport, AStoreType) -> (PQSupport -> SndQ
|
||||
testMatrix2 ps runTest = do
|
||||
it "current, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 agentCfg agentCfg initAgentServersProxy 1 $ runTest PQSupportOn True 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
|
||||
it "current to prev" $ withSmpServer ps $ runTestCfg2 agentCfg agentCfgVPrev 1 $ runTest PQSupportOff False False
|
||||
it "prev" $ withSmpServer ps $ runTestCfg2 agentCfgVPrev agentCfgVPrev 1 $ runTest PQSupportOff True False
|
||||
it "prev to current" $ withSmpServer ps $ runTestCfg2 agentCfgVPrev agentCfg 1 $ runTest PQSupportOff True False
|
||||
it "current to prev" $ withSmpServer ps $ runTestCfg2 agentCfg agentCfgVPrev 1 $ runTest PQSupportOff True False
|
||||
|
||||
testMatrix2Stress :: HasCallStack => (ASrvTransport, AStoreType) -> (PQSupport -> SndQueueSecured -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec
|
||||
testMatrix2Stress :: HasCallStack => (ASrvTransport, AStoreType) -> (PQSupport -> 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 "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
|
||||
it "current, via proxy" $ withSmpServerProxy ps $ runTestCfgServers2 aCfg aCfg initAgentServersProxy 1 $ runTest PQSupportOn True
|
||||
it "current" $ withSmpServer ps $ runTestCfg2 aCfg aCfg 1 $ runTest PQSupportOn False
|
||||
it "prev" $ withSmpServer ps $ runTestCfg2 aCfgVPrev aCfgVPrev 1 $ runTest PQSupportOff False
|
||||
it "prev to current" $ withSmpServer ps $ runTestCfg2 aCfgVPrev aCfg 1 $ runTest PQSupportOff False
|
||||
it "current to prev" $ withSmpServer ps $ runTestCfg2 aCfg aCfgVPrev 1 $ runTest PQSupportOff False
|
||||
where
|
||||
aCfg = agentCfg {messageRetryInterval = fastMessageRetryInterval}
|
||||
aCfgVPrev = agentCfgVPrev {messageRetryInterval = fastMessageRetryInterval}
|
||||
|
||||
testBasicMatrix2 :: HasCallStack => (ASrvTransport, AStoreType) -> (SndQueueSecured -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec
|
||||
testBasicMatrix2 :: HasCallStack => (ASrvTransport, AStoreType) -> (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 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
|
||||
it "current" $ withSmpServer ps $ runTestCfg2 agentCfg agentCfg 1 runTest
|
||||
it "prev" $ withSmpServer ps $ runTestCfg2 agentCfgVPrevPQ agentCfgVPrevPQ 1 runTest
|
||||
it "prev to current" $ withSmpServer ps $ runTestCfg2 agentCfgVPrevPQ agentCfg 1 runTest
|
||||
it "current to prev" $ withSmpServer ps $ runTestCfg2 agentCfg agentCfgVPrevPQ 1 runTest
|
||||
|
||||
testRatchetMatrix2 :: HasCallStack => (ASrvTransport, AStoreType) -> (PQSupport -> SndQueueSecured -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec
|
||||
testRatchetMatrix2 ps runTest = do
|
||||
@@ -741,16 +741,16 @@ withAgentClients3 runTest =
|
||||
runTest a b c
|
||||
|
||||
runAgentClientTest :: HasCallStack => PQSupport -> SndQueueSecured -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()
|
||||
runAgentClientTest pqSupport sqSecured viaProxy alice bob baseId =
|
||||
runAgentClientTestPQ sqSecured viaProxy (alice, IKLinkPQ pqSupport) (bob, pqSupport) baseId
|
||||
runAgentClientTest pqSupport _sqSecured viaProxy alice bob baseId =
|
||||
runAgentClientTestPQ viaProxy (alice, IKLinkPQ pqSupport) (bob, pqSupport) baseId
|
||||
|
||||
runAgentClientTestPQ :: HasCallStack => SndQueueSecured -> Bool -> (AgentClient, InitialKeys) -> (AgentClient, PQSupport) -> AgentMsgId -> IO ()
|
||||
runAgentClientTestPQ sqSecured viaProxy (alice, aPQ) (bob, bPQ) baseId =
|
||||
runAgentClientTestPQ :: HasCallStack => Bool -> (AgentClient, InitialKeys) -> (AgentClient, PQSupport) -> AgentMsgId -> IO ()
|
||||
runAgentClientTestPQ viaProxy (alice, aPQ) (bob, bPQ) baseId =
|
||||
runRight_ $ do
|
||||
(bobId, CCLink qInfo Nothing) <- A.createConnection alice NRMInteractive 1 True True SCMInvitation Nothing Nothing aPQ False SMSubscribe
|
||||
aliceId <- A.prepareConnectionToJoin bob 1 True qInfo bPQ
|
||||
sqSecured' <- A.joinConnection bob NRMInteractive 1 aliceId True qInfo "bob's connInfo" bPQ SMSubscribe
|
||||
liftIO $ sqSecured' `shouldBe` sqSecured
|
||||
liftIO $ sqSecured' `shouldBe` True
|
||||
("", _, A.CONF confId pqSup' _ "bob's connInfo") <- get alice
|
||||
liftIO $ pqSup' `shouldBe` CR.connPQEncryption aPQ
|
||||
allowConnection alice bobId confId "alice's connInfo"
|
||||
@@ -787,10 +787,10 @@ runAgentClientTestPQ sqSecured viaProxy (alice, aPQ) (bob, bPQ) baseId =
|
||||
pqConnectionMode :: InitialKeys -> PQSupport -> Bool
|
||||
pqConnectionMode pqMode1 pqMode2 = supportPQ (CR.connPQEncryption pqMode1) && supportPQ pqMode2
|
||||
|
||||
runAgentClientStressTestOneWay :: HasCallStack => Int64 -> PQSupport -> SndQueueSecured -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()
|
||||
runAgentClientStressTestOneWay n pqSupport sqSecured viaProxy alice bob baseId = runRight_ $ do
|
||||
runAgentClientStressTestOneWay :: HasCallStack => Int64 -> PQSupport -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()
|
||||
runAgentClientStressTestOneWay n pqSupport viaProxy alice bob baseId = runRight_ $ do
|
||||
let pqEnc = PQEncryption $ supportPQ pqSupport
|
||||
(aliceId, bobId) <- makeConnection_ pqSupport sqSecured alice bob
|
||||
(aliceId, bobId) <- makeConnection_ pqSupport alice bob
|
||||
let proxySrv = if viaProxy then Just testSMPServer else Nothing
|
||||
message i = "message " <> bshow i
|
||||
concurrently_
|
||||
@@ -819,9 +819,9 @@ runAgentClientStressTestOneWay n pqSupport sqSecured viaProxy alice bob baseId =
|
||||
where
|
||||
msgId = subtract baseId . fst
|
||||
|
||||
runAgentClientStressTestConc :: HasCallStack => Int64 -> PQSupport -> SndQueueSecured -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()
|
||||
runAgentClientStressTestConc n pqSupport sqSecured viaProxy alice bob _baseId = runRight_ $ do
|
||||
(aliceId, bobId) <- makeConnection_ pqSupport sqSecured alice bob
|
||||
runAgentClientStressTestConc :: HasCallStack => Int64 -> PQSupport -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()
|
||||
runAgentClientStressTestConc n pqSupport viaProxy alice bob _baseId = runRight_ $ do
|
||||
(aliceId, bobId) <- makeConnection_ pqSupport alice bob
|
||||
amId <- newTVarIO 0
|
||||
bmId <- newTVarIO 0
|
||||
let n2 = n `div` 2
|
||||
@@ -883,13 +883,13 @@ testEnablePQEncryption :: HasCallStack => IO ()
|
||||
testEnablePQEncryption =
|
||||
withAgentClients2 $ \ca cb -> runRight_ $ do
|
||||
g <- liftIO C.newRandom
|
||||
(aId, bId) <- makeConnection_ PQSupportOff True ca cb
|
||||
(aId, bId) <- makeConnection_ PQSupportOff ca cb
|
||||
let a = (ca, aId)
|
||||
b = (cb, bId)
|
||||
(a, 2, "msg 1") \#>\ b
|
||||
(b, 3, "msg 2") \#>\ a
|
||||
-- 45 bytes is used by agent message envelope inside double ratchet message envelope
|
||||
let largeMsg g' pqEnc = atomically $ C.randomBytes (e2eEncAgentMsgLength pqdrSMPAgentVersion pqEnc - 45) g'
|
||||
let largeMsg g' pqEnc = atomically $ C.randomBytes (e2eEncAgentMsgLength pqEnc - 45) g'
|
||||
lrg <- largeMsg g PQSupportOff
|
||||
(a, 4, lrg) \#>\ b
|
||||
(b, 5, lrg) \#>\ a
|
||||
@@ -971,17 +971,17 @@ testAgentClient3 =
|
||||
|
||||
runAgentClientContactTest :: HasCallStack => PQSupport -> SndQueueSecured -> Bool -> AgentClient -> AgentClient -> AgentMsgId -> IO ()
|
||||
runAgentClientContactTest pqSupport sqSecured viaProxy alice bob baseId =
|
||||
runAgentClientContactTestPQ sqSecured viaProxy pqSupport (alice, IKLinkPQ pqSupport) (bob, pqSupport) baseId
|
||||
runAgentClientContactTestPQ sqSecured viaProxy (alice, IKLinkPQ pqSupport) (bob, pqSupport) baseId
|
||||
|
||||
runAgentClientContactTestPQ :: HasCallStack => SndQueueSecured -> Bool -> PQSupport -> (AgentClient, InitialKeys) -> (AgentClient, PQSupport) -> AgentMsgId -> IO ()
|
||||
runAgentClientContactTestPQ sqSecured viaProxy reqPQSupport (alice, aPQ) (bob, bPQ) baseId =
|
||||
runAgentClientContactTestPQ :: HasCallStack => SndQueueSecured -> Bool -> (AgentClient, InitialKeys) -> (AgentClient, PQSupport) -> AgentMsgId -> IO ()
|
||||
runAgentClientContactTestPQ sqSecured viaProxy (alice, aPQ) (bob, bPQ) baseId =
|
||||
runRight_ $ do
|
||||
(_, CCLink qInfo Nothing) <- A.createConnection alice NRMInteractive 1 True True SCMContact Nothing Nothing aPQ False SMSubscribe
|
||||
aliceId <- A.prepareConnectionToJoin bob 1 True qInfo bPQ
|
||||
sqSecuredJoin <- A.joinConnection bob NRMInteractive 1 aliceId True qInfo "bob's connInfo" bPQ SMSubscribe
|
||||
liftIO $ sqSecuredJoin `shouldBe` False -- joining via contact address connection
|
||||
("", _, A.REQ invId pqSup' _ "bob's connInfo" _) <- get alice
|
||||
liftIO $ pqSup' `shouldBe` reqPQSupport
|
||||
liftIO $ pqSup' `shouldBe` PQSupportOn
|
||||
bobId <- A.prepareConnectionToAccept alice 1 True invId (CR.connPQEncryption aPQ)
|
||||
sqSecured' <- acceptContact alice 1 bobId True invId "alice's connInfo" (CR.connPQEncryption aPQ) SMSubscribe
|
||||
liftIO $ sqSecured' `shouldBe` sqSecured
|
||||
@@ -2056,86 +2056,86 @@ connReqWithKeys cr rk = case cr of
|
||||
|
||||
testIncreaseConnAgentVersion :: HasCallStack => (ASrvTransport, AStoreType) -> IO ()
|
||||
testIncreaseConnAgentVersion ps = do
|
||||
alice <- getSMPAgentClient' 1 agentCfg {smpAgentVRange = mkVersionRange 1 2} initAgentServers testDB
|
||||
bob <- getSMPAgentClient' 2 agentCfg {smpAgentVRange = mkVersionRange 1 2} initAgentServers testDB2
|
||||
alice <- getSMPAgentClient' 1 agentCfg {smpAgentVRange = mkVersionRange 6 7} initAgentServers testDB
|
||||
bob <- getSMPAgentClient' 2 agentCfg {smpAgentVRange = mkVersionRange 6 7} initAgentServers testDB2
|
||||
withSmpServerStoreMsgLogOn ps testPort $ \_ -> do
|
||||
(aliceId, bobId) <- runRight $ do
|
||||
(aliceId, bobId) <- makeConnection_ PQSupportOff False alice bob
|
||||
(aliceId, bobId) <- makeConnection_ PQSupportOff alice bob
|
||||
exchangeGreetingsMsgId_ PQEncOff 2 alice bobId bob aliceId
|
||||
checkVersion alice bobId 2
|
||||
checkVersion bob aliceId 2
|
||||
checkVersion alice bobId 7
|
||||
checkVersion bob aliceId 7
|
||||
pure (aliceId, bobId)
|
||||
|
||||
-- version doesn't increase if incompatible
|
||||
|
||||
disposeAgentClient alice
|
||||
threadDelay 250000
|
||||
alice2 <- getSMPAgentClient' 3 agentCfg {smpAgentVRange = mkVersionRange 1 3} initAgentServers testDB
|
||||
alice2 <- getSMPAgentClient' 3 agentCfg {smpAgentVRange = mkVersionRange 6 8} initAgentServers testDB
|
||||
|
||||
runRight_ $ do
|
||||
subscribeConnection alice2 bobId
|
||||
exchangeGreetingsMsgId_ PQEncOff 4 alice2 bobId bob aliceId
|
||||
checkVersion alice2 bobId 2
|
||||
checkVersion bob aliceId 2
|
||||
checkVersion alice2 bobId 7
|
||||
checkVersion bob aliceId 7
|
||||
|
||||
-- version increases if compatible
|
||||
|
||||
disposeAgentClient bob
|
||||
threadDelay 250000
|
||||
bob2 <- getSMPAgentClient' 4 agentCfg {smpAgentVRange = mkVersionRange 1 3} initAgentServers testDB2
|
||||
bob2 <- getSMPAgentClient' 4 agentCfg {smpAgentVRange = mkVersionRange 6 8} initAgentServers testDB2
|
||||
|
||||
runRight_ $ do
|
||||
subscribeConnection bob2 aliceId
|
||||
exchangeGreetingsMsgId_ PQEncOff 6 alice2 bobId bob2 aliceId
|
||||
checkVersion alice2 bobId 3
|
||||
checkVersion bob2 aliceId 3
|
||||
checkVersion alice2 bobId 8
|
||||
checkVersion bob2 aliceId 8
|
||||
|
||||
-- version doesn't decrease, even if incompatible
|
||||
|
||||
disposeAgentClient alice2
|
||||
threadDelay 250000
|
||||
alice3 <- getSMPAgentClient' 5 agentCfg {smpAgentVRange = mkVersionRange 2 2} initAgentServers testDB
|
||||
alice3 <- getSMPAgentClient' 5 agentCfg {smpAgentVRange = mkVersionRange 7 7} initAgentServers testDB
|
||||
|
||||
runRight_ $ do
|
||||
subscribeConnection alice3 bobId
|
||||
exchangeGreetingsMsgId_ PQEncOff 8 alice3 bobId bob2 aliceId
|
||||
checkVersion alice3 bobId 3
|
||||
checkVersion bob2 aliceId 3
|
||||
checkVersion alice3 bobId 8
|
||||
checkVersion bob2 aliceId 8
|
||||
|
||||
disposeAgentClient bob2
|
||||
threadDelay 250000
|
||||
bob3 <- getSMPAgentClient' 6 agentCfg {smpAgentVRange = mkVersionRange 1 1} initAgentServers testDB2
|
||||
bob3 <- getSMPAgentClient' 6 agentCfg {smpAgentVRange = mkVersionRange 6 6} initAgentServers testDB2
|
||||
|
||||
runRight_ $ do
|
||||
subscribeConnection bob3 aliceId
|
||||
exchangeGreetingsMsgId_ PQEncOff 10 alice3 bobId bob3 aliceId
|
||||
checkVersion alice3 bobId 3
|
||||
checkVersion bob3 aliceId 3
|
||||
checkVersion alice3 bobId 8
|
||||
checkVersion bob3 aliceId 8
|
||||
disposeAgentClient alice3
|
||||
disposeAgentClient bob3
|
||||
|
||||
checkVersion :: AgentClient -> ConnId -> Word16 -> ExceptT AgentErrorType IO ()
|
||||
checkVersion :: HasCallStack => AgentClient -> ConnId -> Word16 -> ExceptT AgentErrorType IO ()
|
||||
checkVersion c connId v = do
|
||||
ConnectionStats {connAgentVersion} <- getConnectionServers c connId
|
||||
liftIO $ connAgentVersion `shouldBe` VersionSMPA v
|
||||
|
||||
testIncreaseConnAgentVersionMaxCompatible :: HasCallStack => (ASrvTransport, AStoreType) -> IO ()
|
||||
testIncreaseConnAgentVersionMaxCompatible ps = do
|
||||
alice <- getSMPAgentClient' 1 agentCfg {smpAgentVRange = mkVersionRange 1 2} initAgentServers testDB
|
||||
bob <- getSMPAgentClient' 2 agentCfg {smpAgentVRange = mkVersionRange 1 2} initAgentServers testDB2
|
||||
alice <- getSMPAgentClient' 1 agentCfg {smpAgentVRange = mkVersionRange 6 7} initAgentServers testDB
|
||||
bob <- getSMPAgentClient' 2 agentCfg {smpAgentVRange = mkVersionRange 6 7} initAgentServers testDB2
|
||||
withSmpServerStoreMsgLogOn ps testPort $ \_ -> do
|
||||
(aliceId, bobId) <- runRight $ do
|
||||
(aliceId, bobId) <- makeConnection_ PQSupportOff False alice bob
|
||||
(aliceId, bobId) <- makeConnection_ PQSupportOff alice bob
|
||||
exchangeGreetingsMsgId_ PQEncOff 2 alice bobId bob aliceId
|
||||
checkVersion alice bobId 2
|
||||
checkVersion bob aliceId 2
|
||||
checkVersion alice bobId 7
|
||||
checkVersion bob aliceId 7
|
||||
pure (aliceId, bobId)
|
||||
|
||||
-- version increases to max compatible
|
||||
|
||||
disposeAgentClient alice
|
||||
threadDelay 250000
|
||||
alice2 <- getSMPAgentClient' 3 agentCfg {smpAgentVRange = mkVersionRange 1 3} initAgentServers testDB
|
||||
alice2 <- getSMPAgentClient' 3 agentCfg {smpAgentVRange = mkVersionRange 6 8} initAgentServers testDB
|
||||
disposeAgentClient bob
|
||||
threadDelay 250000
|
||||
bob2 <- getSMPAgentClient' 4 agentCfg {smpAgentVRange = supportedSMPAgentVRange} initAgentServers testDB2
|
||||
@@ -2144,34 +2144,34 @@ testIncreaseConnAgentVersionMaxCompatible ps = do
|
||||
subscribeConnection alice2 bobId
|
||||
subscribeConnection bob2 aliceId
|
||||
exchangeGreetingsMsgId_ PQEncOff 4 alice2 bobId bob2 aliceId
|
||||
checkVersion alice2 bobId 3
|
||||
checkVersion bob2 aliceId 3
|
||||
checkVersion alice2 bobId 8
|
||||
checkVersion bob2 aliceId 8
|
||||
disposeAgentClient alice2
|
||||
disposeAgentClient bob2
|
||||
|
||||
testIncreaseConnAgentVersionStartDifferentVersion :: HasCallStack => (ASrvTransport, AStoreType) -> IO ()
|
||||
testIncreaseConnAgentVersionStartDifferentVersion ps = do
|
||||
alice <- getSMPAgentClient' 1 agentCfg {smpAgentVRange = mkVersionRange 1 2} initAgentServers testDB
|
||||
bob <- getSMPAgentClient' 2 agentCfg {smpAgentVRange = mkVersionRange 1 3} initAgentServers testDB2
|
||||
alice <- getSMPAgentClient' 1 agentCfg {smpAgentVRange = mkVersionRange 6 7} initAgentServers testDB
|
||||
bob <- getSMPAgentClient' 2 agentCfg {smpAgentVRange = mkVersionRange 6 8} initAgentServers testDB2
|
||||
withSmpServerStoreMsgLogOn ps testPort $ \_ -> do
|
||||
(aliceId, bobId) <- runRight $ do
|
||||
(aliceId, bobId) <- makeConnection_ PQSupportOff False alice bob
|
||||
(aliceId, bobId) <- makeConnection_ PQSupportOff alice bob
|
||||
exchangeGreetingsMsgId_ PQEncOff 2 alice bobId bob aliceId
|
||||
checkVersion alice bobId 2
|
||||
checkVersion bob aliceId 2
|
||||
checkVersion alice bobId 7
|
||||
checkVersion bob aliceId 7
|
||||
pure (aliceId, bobId)
|
||||
|
||||
-- version increases to max compatible
|
||||
|
||||
disposeAgentClient alice
|
||||
threadDelay 250000
|
||||
alice2 <- getSMPAgentClient' 3 agentCfg {smpAgentVRange = mkVersionRange 1 3} initAgentServers testDB
|
||||
alice2 <- getSMPAgentClient' 3 agentCfg {smpAgentVRange = mkVersionRange 6 8} initAgentServers testDB
|
||||
|
||||
runRight_ $ do
|
||||
subscribeConnection alice2 bobId
|
||||
exchangeGreetingsMsgId_ PQEncOff 4 alice2 bobId bob aliceId
|
||||
checkVersion alice2 bobId 3
|
||||
checkVersion bob aliceId 3
|
||||
checkVersion alice2 bobId 8
|
||||
checkVersion bob aliceId 8
|
||||
disposeAgentClient alice2
|
||||
disposeAgentClient bob
|
||||
|
||||
@@ -2731,20 +2731,20 @@ testOnlyCreatePull = withAgentClients2 $ \alice bob -> runRight_ $ do
|
||||
getMSGNTF alice bobId
|
||||
|
||||
makeConnection :: AgentClient -> AgentClient -> ExceptT AgentErrorType IO (ConnId, ConnId)
|
||||
makeConnection = makeConnection_ PQSupportOn True
|
||||
makeConnection = makeConnection_ PQSupportOn
|
||||
|
||||
makeConnection_ :: PQSupport -> SndQueueSecured -> AgentClient -> AgentClient -> ExceptT AgentErrorType IO (ConnId, ConnId)
|
||||
makeConnection_ pqEnc sqSecured alice bob = makeConnectionForUsers_ pqEnc sqSecured alice 1 bob 1
|
||||
makeConnection_ :: PQSupport -> AgentClient -> AgentClient -> ExceptT AgentErrorType IO (ConnId, ConnId)
|
||||
makeConnection_ pqEnc alice bob = makeConnectionForUsers_ pqEnc alice 1 bob 1
|
||||
|
||||
makeConnectionForUsers :: HasCallStack => AgentClient -> UserId -> AgentClient -> UserId -> ExceptT AgentErrorType IO (ConnId, ConnId)
|
||||
makeConnectionForUsers = makeConnectionForUsers_ PQSupportOn True
|
||||
makeConnectionForUsers = makeConnectionForUsers_ PQSupportOn
|
||||
|
||||
makeConnectionForUsers_ :: HasCallStack => PQSupport -> SndQueueSecured -> AgentClient -> UserId -> AgentClient -> UserId -> ExceptT AgentErrorType IO (ConnId, ConnId)
|
||||
makeConnectionForUsers_ pqSupport sqSecured alice aliceUserId bob bobUserId = do
|
||||
makeConnectionForUsers_ :: HasCallStack => PQSupport -> AgentClient -> UserId -> AgentClient -> UserId -> ExceptT AgentErrorType IO (ConnId, ConnId)
|
||||
makeConnectionForUsers_ pqSupport alice aliceUserId bob bobUserId = do
|
||||
(bobId, CCLink qInfo Nothing) <- A.createConnection alice NRMInteractive aliceUserId True True SCMInvitation Nothing Nothing (IKLinkPQ pqSupport) False SMSubscribe
|
||||
aliceId <- A.prepareConnectionToJoin bob bobUserId True qInfo pqSupport
|
||||
sqSecured' <- A.joinConnection bob NRMInteractive bobUserId aliceId True qInfo "bob's connInfo" pqSupport SMSubscribe
|
||||
liftIO $ sqSecured' `shouldBe` sqSecured
|
||||
liftIO $ sqSecured' `shouldBe` True
|
||||
("", _, A.CONF confId pqSup' _ "bob's connInfo") <- get alice
|
||||
liftIO $ pqSup' `shouldBe` pqSupport
|
||||
allowConnection alice bobId confId "alice's connInfo"
|
||||
@@ -2872,7 +2872,7 @@ testBatchedSubscriptions :: Int -> Int -> (ASrvTransport, AStoreType) -> IO ()
|
||||
testBatchedSubscriptions nCreate nDel ps@(t, ASType qsType _) = do
|
||||
(conns, conns') <- withAgentClientsCfgServers2 agentCfg agentCfg initAgentServers2 $ \a b -> do
|
||||
conns <- runServers $ do
|
||||
conns <- replicateM nCreate $ makeConnection_ PQSupportOff True a b
|
||||
conns <- replicateM nCreate $ makeConnection_ PQSupportOff a b
|
||||
forM_ conns $ \(aId, bId) -> exchangeGreetings_ PQEncOff a bId b aId
|
||||
let (aIds', bIds') = unzip $ take nDel conns
|
||||
delete a bIds'
|
||||
@@ -3017,8 +3017,8 @@ receiveMsg c cId msgId msg = do
|
||||
get c =##> \case ("", cId', Msg' mId' PQEncOn msg') -> cId' == cId && mId' == msgId && msg' == msg; _ -> False
|
||||
ackMessage c cId msgId Nothing
|
||||
|
||||
testAsyncCommands :: SndQueueSecured -> AgentClient -> AgentClient -> AgentMsgId -> IO ()
|
||||
testAsyncCommands sqSecured alice bob baseId =
|
||||
testAsyncCommands :: AgentClient -> AgentClient -> AgentMsgId -> IO ()
|
||||
testAsyncCommands alice bob baseId =
|
||||
runRight_ $ do
|
||||
bobId <- prepareConnectionToCreate alice 1 True SCMInvitation PQSupportOn
|
||||
createConnectionAsync alice "1" bobId True SCMInvitation IKPQOn False SMSubscribe
|
||||
@@ -3029,7 +3029,7 @@ testAsyncCommands sqSecured alice bob baseId =
|
||||
("2", aliceId', JOINED sqSecured') <- get bob
|
||||
liftIO $ do
|
||||
aliceId' `shouldBe` aliceId
|
||||
sqSecured' `shouldBe` sqSecured
|
||||
sqSecured' `shouldBe` True
|
||||
("", _, CONF confId _ "bob's connInfo") <- get alice
|
||||
allowConnectionAsync alice "3" bobId confId "alice's connInfo"
|
||||
get alice =##> \case ("3", _, OK) -> True; _ -> False
|
||||
@@ -3145,8 +3145,8 @@ testAsyncCommandsRestore ps = do
|
||||
get alice' =##> \case ("1", _, INV _) -> True; _ -> False
|
||||
pure ()
|
||||
|
||||
testAcceptContactAsync :: SndQueueSecured -> AgentClient -> AgentClient -> AgentMsgId -> IO ()
|
||||
testAcceptContactAsync sqSecured alice bob baseId =
|
||||
testAcceptContactAsync :: AgentClient -> AgentClient -> AgentMsgId -> IO ()
|
||||
testAcceptContactAsync alice bob baseId =
|
||||
runRight_ $ do
|
||||
(_, qInfo) <- createConnection alice 1 True SCMContact Nothing SMSubscribe
|
||||
(aliceId, sqSecuredJoin) <- joinConnection bob 1 True qInfo "bob's connInfo" SMSubscribe
|
||||
@@ -3154,7 +3154,7 @@ testAcceptContactAsync sqSecured alice bob baseId =
|
||||
("", _, REQ invId _ "bob's connInfo") <- get alice
|
||||
bobId <- prepareConnectionToAccept alice 1 True invId PQSupportOn
|
||||
acceptContactAsync alice "1" bobId True invId "alice's connInfo" PQSupportOn SMSubscribe
|
||||
get alice =##> \case ("1", c, JOINED sqSecured') -> c == bobId && sqSecured' == sqSecured; _ -> False
|
||||
get alice =##> \case ("1", c, JOINED sqSecured') -> c == bobId && sqSecured' == True; _ -> False
|
||||
("", _, CONF confId _ "alice's connInfo") <- get bob
|
||||
allowConnection bob aliceId confId "bob's connInfo"
|
||||
get alice ##> ("", bobId, INFO "bob's connInfo")
|
||||
@@ -3978,59 +3978,6 @@ testDeliveryReceipts =
|
||||
ackMessage b aId 5 (Just "") `catchError` \case (A.CMD PROHIBITED _) -> pure (); e -> liftIO $ expectationFailure ("unexpected error " <> show e)
|
||||
ackMessage b aId 5 Nothing
|
||||
|
||||
testDeliveryReceiptsVersion :: HasCallStack => (ASrvTransport, AStoreType) -> IO ()
|
||||
testDeliveryReceiptsVersion ps = do
|
||||
a <- getSMPAgentClient' 1 agentCfg {smpAgentVRange = mkVersionRange 1 3} initAgentServers testDB
|
||||
b <- getSMPAgentClient' 2 agentCfg {smpAgentVRange = mkVersionRange 1 3} initAgentServers testDB2
|
||||
withSmpServerStoreMsgLogOn ps testPort $ \_ -> do
|
||||
(aId, bId) <- runRight $ do
|
||||
(aId, bId) <- makeConnection_ PQSupportOff False a b
|
||||
checkVersion a bId 3
|
||||
checkVersion b aId 3
|
||||
(2, _) <- A.sendMessage a bId PQEncOff SMP.noMsgFlags "hello"
|
||||
get a ##> ("", bId, SENT 2)
|
||||
get b =##> \case ("", c, Msg' 2 PQEncOff "hello") -> c == aId; _ -> False
|
||||
ackMessage b aId 2 $ Just ""
|
||||
liftIO $ noMessages a "no delivery receipt (unsupported version)"
|
||||
(3, _) <- A.sendMessage b aId PQEncOff SMP.noMsgFlags "hello too"
|
||||
get b ##> ("", aId, SENT 3)
|
||||
get a =##> \case ("", c, Msg' 3 PQEncOff "hello too") -> c == bId; _ -> False
|
||||
ackMessage a bId 3 $ Just ""
|
||||
liftIO $ noMessages b "no delivery receipt (unsupported version)"
|
||||
pure (aId, bId)
|
||||
|
||||
disposeAgentClient a
|
||||
disposeAgentClient b
|
||||
a' <- getSMPAgentClient' 3 agentCfg {smpAgentVRange = supportedSMPAgentVRange} initAgentServers testDB
|
||||
b' <- getSMPAgentClient' 4 agentCfg {smpAgentVRange = supportedSMPAgentVRange} initAgentServers testDB2
|
||||
|
||||
runRight_ $ do
|
||||
subscribeConnection a' bId
|
||||
subscribeConnection b' aId
|
||||
exchangeGreetingsMsgId_ PQEncOff 4 a' bId b' aId
|
||||
checkVersion a' bId 7
|
||||
checkVersion b' aId 7
|
||||
(6, PQEncOff) <- A.sendMessage a' bId PQEncOn SMP.noMsgFlags "hello"
|
||||
get a' ##> ("", bId, SENT 6)
|
||||
get b' =##> \case ("", c, Msg' 6 PQEncOff "hello") -> c == aId; _ -> False
|
||||
ackMessage b' aId 6 $ Just ""
|
||||
get a' =##> \case ("", c, Rcvd 6) -> c == bId; _ -> False
|
||||
ackMessage a' bId 7 Nothing
|
||||
(8, PQEncOff) <- A.sendMessage b' aId PQEncOn SMP.noMsgFlags "hello too"
|
||||
get b' ##> ("", aId, SENT 8)
|
||||
get a' =##> \case ("", c, Msg' 8 PQEncOff "hello too") -> c == bId; _ -> False
|
||||
ackMessage a' bId 8 $ Just ""
|
||||
get b' =##> \case ("", c, Rcvd 8) -> c == aId; _ -> False
|
||||
ackMessage b' aId 9 Nothing
|
||||
(10, _) <- A.sendMessage a' bId PQEncOn SMP.noMsgFlags "hello 2"
|
||||
get a' ##> ("", bId, SENT 10)
|
||||
get b' =##> \case ("", c, Msg' 10 PQEncOff "hello 2") -> c == aId; _ -> False
|
||||
ackMessage b' aId 10 $ Just ""
|
||||
get a' =##> \case ("", c, Rcvd 10) -> c == bId; _ -> False
|
||||
ackMessage a' bId 11 Nothing
|
||||
disposeAgentClient a'
|
||||
disposeAgentClient b'
|
||||
|
||||
testDeliveryReceiptsConcurrent :: HasCallStack => (ASrvTransport, AStoreType) -> IO ()
|
||||
testDeliveryReceiptsConcurrent (t, msType) =
|
||||
withSmpServerConfigOn t cfg' testPort $ \_ -> do
|
||||
|
||||
Reference in New Issue
Block a user