|
|
|
@@ -9,6 +9,7 @@
|
|
|
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
|
|
|
|
{-# LANGUAGE OverloadedLists #-}
|
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
|
{-# LANGUAGE PatternSynonyms #-}
|
|
|
|
|
{-# LANGUAGE RankNTypes #-}
|
|
|
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
|
|
|
{-# LANGUAGE TupleSections #-}
|
|
|
|
@@ -140,6 +141,7 @@ import Data.Text (Text)
|
|
|
|
|
import qualified Data.Text as T
|
|
|
|
|
import Data.Time.Clock
|
|
|
|
|
import Data.Time.Clock.System (systemToUTCTime)
|
|
|
|
|
import Data.Traversable (mapAccumL)
|
|
|
|
|
import Data.Word (Word16)
|
|
|
|
|
import Simplex.FileTransfer.Agent (closeXFTPAgent, deleteSndFileInternal, deleteSndFileRemote, deleteSndFilesInternal, deleteSndFilesRemote, startXFTPWorkers, toFSFilePath, xftpDeleteRcvFile', xftpDeleteRcvFiles', xftpReceiveFile', xftpSendDescription', xftpSendFile')
|
|
|
|
|
import Simplex.FileTransfer.Description (ValidFileDescription)
|
|
|
|
@@ -158,6 +160,7 @@ import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations
|
|
|
|
|
import Simplex.Messaging.Client (ProtocolClient (..), ServerTransmission)
|
|
|
|
|
import qualified Simplex.Messaging.Crypto as C
|
|
|
|
|
import Simplex.Messaging.Crypto.File (CryptoFile, CryptoFileArgs)
|
|
|
|
|
import Simplex.Messaging.Crypto.Ratchet (PQEncryption, pattern PQEncOn, pattern PQEncOff)
|
|
|
|
|
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
|
|
|
|
import Simplex.Messaging.Encoding
|
|
|
|
|
import Simplex.Messaging.Encoding.String
|
|
|
|
@@ -222,7 +225,7 @@ createConnectionAsync :: forall m c. (AgentErrorMonad m, ConnectionModeI c) => A
|
|
|
|
|
createConnectionAsync c userId aCorrId enableNtfs = withAgentEnv c .:. newConnAsync c userId aCorrId enableNtfs
|
|
|
|
|
|
|
|
|
|
-- | Join SMP agent connection (JOIN command) asynchronously, synchronous response is new connection id
|
|
|
|
|
joinConnectionAsync :: AgentErrorMonad m => AgentClient -> UserId -> ACorrId -> Bool -> ConnectionRequestUri c -> ConnInfo -> CR.PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
joinConnectionAsync :: AgentErrorMonad m => AgentClient -> UserId -> ACorrId -> Bool -> ConnectionRequestUri c -> ConnInfo -> PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
joinConnectionAsync c userId aCorrId enableNtfs = withAgentEnv c .:: joinConnAsync c userId aCorrId enableNtfs
|
|
|
|
|
|
|
|
|
|
-- | Allow connection to continue after CONF notification (LET command), no synchronous response
|
|
|
|
@@ -230,7 +233,7 @@ allowConnectionAsync :: AgentErrorMonad m => AgentClient -> ACorrId -> ConnId ->
|
|
|
|
|
allowConnectionAsync c = withAgentEnv c .:: allowConnectionAsync' c
|
|
|
|
|
|
|
|
|
|
-- | Accept contact after REQ notification (ACPT command) asynchronously, synchronous response is new connection id
|
|
|
|
|
acceptContactAsync :: AgentErrorMonad m => AgentClient -> ACorrId -> Bool -> ConfirmationId -> ConnInfo -> CR.PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
acceptContactAsync :: AgentErrorMonad m => AgentClient -> ACorrId -> Bool -> ConfirmationId -> ConnInfo -> PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
acceptContactAsync c aCorrId enableNtfs = withAgentEnv c .:: acceptContactAsync' c aCorrId enableNtfs
|
|
|
|
|
|
|
|
|
|
-- | Acknowledge message (ACK command) asynchronously, no synchronous response
|
|
|
|
@@ -254,7 +257,7 @@ createConnection :: AgentErrorMonad m => AgentClient -> UserId -> Bool -> SConne
|
|
|
|
|
createConnection c userId enableNtfs = withAgentEnv c .:: newConn c userId "" enableNtfs
|
|
|
|
|
|
|
|
|
|
-- | Join SMP agent connection (JOIN command)
|
|
|
|
|
joinConnection :: AgentErrorMonad m => AgentClient -> UserId -> Bool -> ConnectionRequestUri c -> ConnInfo -> CR.PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
joinConnection :: AgentErrorMonad m => AgentClient -> UserId -> Bool -> ConnectionRequestUri c -> ConnInfo -> PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
joinConnection c userId enableNtfs = withAgentEnv c .:: joinConn c userId "" enableNtfs
|
|
|
|
|
|
|
|
|
|
-- | Allow connection to continue after CONF notification (LET command)
|
|
|
|
@@ -262,7 +265,7 @@ allowConnection :: AgentErrorMonad m => AgentClient -> ConnId -> ConfirmationId
|
|
|
|
|
allowConnection c = withAgentEnv c .:. allowConnection' c
|
|
|
|
|
|
|
|
|
|
-- | Accept contact after REQ notification (ACPT command)
|
|
|
|
|
acceptContact :: AgentErrorMonad m => AgentClient -> Bool -> ConfirmationId -> ConnInfo -> CR.PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
acceptContact :: AgentErrorMonad m => AgentClient -> Bool -> ConfirmationId -> ConnInfo -> PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
acceptContact c enableNtfs = withAgentEnv c .:: acceptContact' c "" enableNtfs
|
|
|
|
|
|
|
|
|
|
-- | Reject contact (RJCT command)
|
|
|
|
@@ -292,16 +295,16 @@ resubscribeConnections :: AgentErrorMonad m => AgentClient -> [ConnId] -> m (Map
|
|
|
|
|
resubscribeConnections c = withAgentEnv c . resubscribeConnections' c
|
|
|
|
|
|
|
|
|
|
-- | Send message to the connection (SEND command)
|
|
|
|
|
sendMessage :: AgentErrorMonad m => AgentClient -> ConnId -> CR.PQEncryption -> MsgFlags -> MsgBody -> m (AgentMsgId, CR.PQEncryption)
|
|
|
|
|
sendMessage :: AgentErrorMonad m => AgentClient -> ConnId -> PQEncryption -> MsgFlags -> MsgBody -> m (AgentMsgId, PQEncryption)
|
|
|
|
|
sendMessage c = withAgentEnv c .:: sendMessage' c
|
|
|
|
|
|
|
|
|
|
type MsgReq = (ConnId, CR.PQEncryption, MsgFlags, MsgBody)
|
|
|
|
|
type MsgReq = (ConnId, PQEncryption, MsgFlags, MsgBody)
|
|
|
|
|
|
|
|
|
|
-- | Send multiple messages to different connections (SEND command)
|
|
|
|
|
sendMessages :: MonadUnliftIO m => AgentClient -> [MsgReq] -> m [Either AgentErrorType (AgentMsgId, CR.PQEncryption)]
|
|
|
|
|
sendMessages :: MonadUnliftIO m => AgentClient -> [MsgReq] -> m [Either AgentErrorType (AgentMsgId, PQEncryption)]
|
|
|
|
|
sendMessages c = withAgentEnv c . sendMessages' c
|
|
|
|
|
|
|
|
|
|
sendMessagesB :: (MonadUnliftIO m, Traversable t) => AgentClient -> t (Either AgentErrorType MsgReq) -> m (t (Either AgentErrorType (AgentMsgId, CR.PQEncryption)))
|
|
|
|
|
sendMessagesB :: (MonadUnliftIO m, Traversable t) => AgentClient -> t (Either AgentErrorType MsgReq) -> m (t (Either AgentErrorType (AgentMsgId, PQEncryption)))
|
|
|
|
|
sendMessagesB c = withAgentEnv c . sendMessagesB' c
|
|
|
|
|
|
|
|
|
|
ackMessage :: AgentErrorMonad m => AgentClient -> ConnId -> AgentMsgId -> Maybe MsgReceiptInfo -> m ()
|
|
|
|
@@ -316,7 +319,7 @@ abortConnectionSwitch :: AgentErrorMonad m => AgentClient -> ConnId -> m Connect
|
|
|
|
|
abortConnectionSwitch c = withAgentEnv c . abortConnectionSwitch' c
|
|
|
|
|
|
|
|
|
|
-- | Re-synchronize connection ratchet keys
|
|
|
|
|
synchronizeRatchet :: AgentErrorMonad m => AgentClient -> ConnId -> CR.PQEncryption -> Bool -> m ConnectionStats
|
|
|
|
|
synchronizeRatchet :: AgentErrorMonad m => AgentClient -> ConnId -> PQEncryption -> Bool -> m ConnectionStats
|
|
|
|
|
synchronizeRatchet c = withAgentEnv c .:. synchronizeRatchet' c
|
|
|
|
|
|
|
|
|
|
-- | Suspend SMP agent connection (OFF command)
|
|
|
|
@@ -555,14 +558,14 @@ newConnAsync c userId corrId enableNtfs cMode pqInitKeys subMode = do
|
|
|
|
|
enqueueCommand c corrId connId Nothing $ AClientCommand $ APC SAEConn $ NEW enableNtfs (ACM cMode) pqInitKeys subMode
|
|
|
|
|
pure connId
|
|
|
|
|
|
|
|
|
|
newConnNoQueues :: AgentMonad m => AgentClient -> UserId -> ConnId -> Bool -> SConnectionMode c -> CR.PQEncryption -> m ConnId
|
|
|
|
|
newConnNoQueues :: AgentMonad m => AgentClient -> UserId -> ConnId -> Bool -> SConnectionMode c -> PQEncryption -> m ConnId
|
|
|
|
|
newConnNoQueues c userId connId enableNtfs cMode pqEncryption = do
|
|
|
|
|
g <- asks random
|
|
|
|
|
connAgentVersion <- asks $ maxVersion . ($ pqEncryption) . smpAgentVRange . config
|
|
|
|
|
let cData = ConnData {userId, connId, connAgentVersion, enableNtfs, lastExternalSndId = 0, deleted = False, ratchetSyncState = RSOk, pqEncryption}
|
|
|
|
|
withStore c $ \db -> createNewConn db g cData cMode
|
|
|
|
|
|
|
|
|
|
joinConnAsync :: AgentMonad m => AgentClient -> UserId -> ACorrId -> Bool -> ConnectionRequestUri c -> ConnInfo -> CR.PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
joinConnAsync :: AgentMonad m => AgentClient -> UserId -> ACorrId -> Bool -> ConnectionRequestUri c -> ConnInfo -> PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
joinConnAsync c userId corrId enableNtfs cReqUri@(CRInvitationUri ConnReqUriData {crAgentVRange} _) cInfo pqEncryption subMode = do
|
|
|
|
|
withInvLock c (strEncode cReqUri) "joinConnAsync" $ do
|
|
|
|
|
aVRange <- asks $ ($ pqEncryption) . smpAgentVRange . config
|
|
|
|
@@ -584,7 +587,7 @@ allowConnectionAsync' c corrId connId confId ownConnInfo =
|
|
|
|
|
enqueueCommand c corrId connId (Just server) $ AClientCommand $ APC SAEConn $ LET confId ownConnInfo
|
|
|
|
|
_ -> throwError $ CMD PROHIBITED
|
|
|
|
|
|
|
|
|
|
acceptContactAsync' :: AgentMonad m => AgentClient -> ACorrId -> Bool -> InvitationId -> ConnInfo -> CR.PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
acceptContactAsync' :: AgentMonad m => AgentClient -> ACorrId -> Bool -> InvitationId -> ConnInfo -> PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
acceptContactAsync' c corrId enableNtfs invId ownConnInfo pqEnc subMode = do
|
|
|
|
|
Invitation {contactConnId, connReq} <- withStore c (`getInvitation` invId)
|
|
|
|
|
withStore c (`getConn` contactConnId) >>= \case
|
|
|
|
@@ -678,7 +681,7 @@ newRcvConnSrv c userId connId enableNtfs cMode clientData pqInitKeys subMode srv
|
|
|
|
|
withStore' c $ \db -> createRatchetX3dhKeys db connId pk1 pk2 pKem
|
|
|
|
|
pure (connId, CRInvitationUri crData $ toVersionRangeT e2eRcvParams e2eVRange)
|
|
|
|
|
|
|
|
|
|
joinConn :: AgentMonad m => AgentClient -> UserId -> ConnId -> Bool -> ConnectionRequestUri c -> ConnInfo -> CR.PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
joinConn :: AgentMonad m => AgentClient -> UserId -> ConnId -> Bool -> ConnectionRequestUri c -> ConnInfo -> PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
joinConn c userId connId enableNtfs cReq cInfo pqEnc subMode = do
|
|
|
|
|
srv <- case cReq of
|
|
|
|
|
CRInvitationUri ConnReqUriData {crSmpQueues = q :| _} _ ->
|
|
|
|
@@ -686,7 +689,7 @@ joinConn c userId connId enableNtfs cReq cInfo pqEnc subMode = do
|
|
|
|
|
_ -> getSMPServer c userId
|
|
|
|
|
joinConnSrv c userId connId enableNtfs cReq cInfo pqEnc subMode srv
|
|
|
|
|
|
|
|
|
|
startJoinInvitation :: AgentMonad m => UserId -> ConnId -> Bool -> ConnectionRequestUri 'CMInvitation -> CR.PQEncryption -> m (Compatible VersionSMPA, ConnData, NewSndQueue, CR.Ratchet 'C.X448, CR.SndE2ERatchetParams 'C.X448)
|
|
|
|
|
startJoinInvitation :: AgentMonad m => UserId -> ConnId -> Bool -> ConnectionRequestUri 'CMInvitation -> PQEncryption -> m (Compatible VersionSMPA, ConnData, NewSndQueue, CR.Ratchet 'C.X448, CR.SndE2ERatchetParams 'C.X448)
|
|
|
|
|
startJoinInvitation userId connId enableNtfs (CRInvitationUri ConnReqUriData {crAgentVRange, crSmpQueues = (qUri :| _)} e2eRcvParamsUri) pqEncryption = do
|
|
|
|
|
AgentConfig {smpClientVRange, smpAgentVRange, e2eEncryptVRange} <- asks config
|
|
|
|
|
let e2eVRange = e2eEncryptVRange pqEncryption
|
|
|
|
@@ -706,7 +709,7 @@ startJoinInvitation userId connId enableNtfs (CRInvitationUri ConnReqUriData {cr
|
|
|
|
|
pure (aVersion, cData, q, rc, e2eSndParams)
|
|
|
|
|
_ -> throwError $ AGENT A_VERSION
|
|
|
|
|
|
|
|
|
|
joinConnSrv :: AgentMonad m => AgentClient -> UserId -> ConnId -> Bool -> ConnectionRequestUri c -> ConnInfo -> CR.PQEncryption -> SubscriptionMode -> SMPServerWithAuth -> m ConnId
|
|
|
|
|
joinConnSrv :: AgentMonad m => AgentClient -> UserId -> ConnId -> Bool -> ConnectionRequestUri c -> ConnInfo -> PQEncryption -> SubscriptionMode -> SMPServerWithAuth -> m ConnId
|
|
|
|
|
joinConnSrv c userId connId enableNtfs inv@CRInvitationUri {} cInfo pqEnc subMode srv =
|
|
|
|
|
withInvLock c (strEncode inv) "joinConnSrv" $ do
|
|
|
|
|
(aVersion, cData, q, rc, e2eSndParams) <- startJoinInvitation userId connId enableNtfs inv pqEnc
|
|
|
|
@@ -716,7 +719,7 @@ joinConnSrv c userId connId enableNtfs inv@CRInvitationUri {} cInfo pqEnc subMod
|
|
|
|
|
liftIO $ createRatchet db connId' rc
|
|
|
|
|
pure r
|
|
|
|
|
let cData' = (cData :: ConnData) {connId = connId'}
|
|
|
|
|
tryError (confirmQueue aVersion c cData' sq srv cInfo (Just e2eSndParams) (Just pqEnc) subMode) >>= \case
|
|
|
|
|
tryError (confirmQueue aVersion c cData' sq srv cInfo (Just e2eSndParams) pqEnc subMode) >>= \case
|
|
|
|
|
Right _ -> pure connId'
|
|
|
|
|
Left e -> do
|
|
|
|
|
-- possible improvement: recovery for failure on network timeout, see rfcs/2022-04-20-smp-conf-timeout-recovery.md
|
|
|
|
@@ -734,7 +737,7 @@ joinConnSrv c userId connId enableNtfs (CRContactUri ConnReqUriData {crAgentVRan
|
|
|
|
|
pure connId'
|
|
|
|
|
_ -> throwError $ AGENT A_VERSION
|
|
|
|
|
|
|
|
|
|
joinConnSrvAsync :: AgentMonad m => AgentClient -> UserId -> ConnId -> Bool -> ConnectionRequestUri c -> ConnInfo -> CR.PQEncryption -> SubscriptionMode -> SMPServerWithAuth -> m ()
|
|
|
|
|
joinConnSrvAsync :: AgentMonad m => AgentClient -> UserId -> ConnId -> Bool -> ConnectionRequestUri c -> ConnInfo -> PQEncryption -> SubscriptionMode -> SMPServerWithAuth -> m ()
|
|
|
|
|
joinConnSrvAsync c userId connId enableNtfs inv@CRInvitationUri {} cInfo pqEnc subMode srv = do
|
|
|
|
|
(_aVersion, cData, q, rc, e2eSndParams) <- startJoinInvitation userId connId enableNtfs inv pqEnc
|
|
|
|
|
q' <- withStore c $ \db -> runExceptT $ do
|
|
|
|
@@ -772,7 +775,7 @@ allowConnection' c connId confId ownConnInfo = withConnLock c connId "allowConne
|
|
|
|
|
_ -> throwError $ CMD PROHIBITED
|
|
|
|
|
|
|
|
|
|
-- | Accept contact (ACPT command) in Reader monad
|
|
|
|
|
acceptContact' :: AgentMonad m => AgentClient -> ConnId -> Bool -> InvitationId -> ConnInfo -> CR.PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
acceptContact' :: AgentMonad m => AgentClient -> ConnId -> Bool -> InvitationId -> ConnInfo -> PQEncryption -> SubscriptionMode -> m ConnId
|
|
|
|
|
acceptContact' c connId enableNtfs invId ownConnInfo pqEnc subMode = withConnLock c connId "acceptContact" $ do
|
|
|
|
|
Invitation {contactConnId, connReq} <- withStore c (`getInvitation` invId)
|
|
|
|
|
withStore c (`getConn` contactConnId) >>= \case
|
|
|
|
@@ -913,29 +916,36 @@ getNotificationMessage' c nonce encNtfInfo = do
|
|
|
|
|
Nothing -> SMP.notification msgFlags
|
|
|
|
|
|
|
|
|
|
-- | Send message to the connection (SEND command) in Reader monad
|
|
|
|
|
sendMessage' :: forall m. AgentMonad m => AgentClient -> ConnId -> CR.PQEncryption -> MsgFlags -> MsgBody -> m (AgentMsgId, CR.PQEncryption)
|
|
|
|
|
sendMessage' :: forall m. AgentMonad m => AgentClient -> ConnId -> PQEncryption -> MsgFlags -> MsgBody -> m (AgentMsgId, PQEncryption)
|
|
|
|
|
sendMessage' c connId pqEnc msgFlags msg = liftEither . runIdentity =<< sendMessagesB' c (Identity (Right (connId, pqEnc, msgFlags, msg)))
|
|
|
|
|
|
|
|
|
|
-- | Send multiple messages to different connections (SEND command) in Reader monad
|
|
|
|
|
sendMessages' :: forall m. AgentMonad' m => AgentClient -> [MsgReq] -> m [Either AgentErrorType (AgentMsgId, CR.PQEncryption)]
|
|
|
|
|
sendMessages' :: forall m. AgentMonad' m => AgentClient -> [MsgReq] -> m [Either AgentErrorType (AgentMsgId, PQEncryption)]
|
|
|
|
|
sendMessages' c = sendMessagesB' c . map Right
|
|
|
|
|
|
|
|
|
|
sendMessagesB' :: forall m t. (AgentMonad' m, Traversable t) => AgentClient -> t (Either AgentErrorType MsgReq) -> m (t (Either AgentErrorType (AgentMsgId, CR.PQEncryption)))
|
|
|
|
|
sendMessagesB' :: forall m t. (AgentMonad' m, Traversable t) => AgentClient -> t (Either AgentErrorType MsgReq) -> m (t (Either AgentErrorType (AgentMsgId, PQEncryption)))
|
|
|
|
|
sendMessagesB' c reqs = withConnLocks c connIds "sendMessages" $ do
|
|
|
|
|
reqs' <- withStoreBatch c (\db -> fmap (bindRight $ \req@(connId, _, _, _) -> bimap storeError (req,) <$> getConn db connId) reqs)
|
|
|
|
|
let reqs'' = fmap (>>= prepareConn) reqs'
|
|
|
|
|
let (toEnable, reqs'') = mapAccumL prepareConn [] reqs'
|
|
|
|
|
void $ withStoreBatch' c $ \db -> map (enableConnPQEncryption db) toEnable
|
|
|
|
|
enqueueMessagesB c reqs''
|
|
|
|
|
where
|
|
|
|
|
prepareConn :: (MsgReq, SomeConn) -> Either AgentErrorType (ConnData, NonEmpty SndQueue, Maybe CR.PQEncryption, MsgFlags, AMessage)
|
|
|
|
|
prepareConn ((_, pqEnc, msgFlags, msg), SomeConn _ conn) = case conn of
|
|
|
|
|
prepareConn :: [ConnId] -> Either AgentErrorType (MsgReq, SomeConn) -> ([ConnId], Either AgentErrorType (ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage))
|
|
|
|
|
prepareConn acc (Left e) = (acc, Left e)
|
|
|
|
|
prepareConn acc (Right ((_, pqEnc, msgFlags, msg), SomeConn _ conn)) = case conn of
|
|
|
|
|
DuplexConnection cData _ sqs -> prepareMsg cData sqs
|
|
|
|
|
SndConnection cData sq -> prepareMsg cData [sq]
|
|
|
|
|
_ -> Left $ CONN SIMPLEX
|
|
|
|
|
_ -> (acc, Left $ CONN SIMPLEX)
|
|
|
|
|
where
|
|
|
|
|
prepareMsg :: ConnData -> NonEmpty SndQueue -> Either AgentErrorType (ConnData, NonEmpty SndQueue, Maybe CR.PQEncryption, MsgFlags, AMessage)
|
|
|
|
|
prepareMsg cData sqs
|
|
|
|
|
| ratchetSyncSendProhibited cData = Left $ CMD PROHIBITED
|
|
|
|
|
| otherwise = Right (cData, sqs, Just pqEnc, msgFlags, A_MSG msg)
|
|
|
|
|
prepareMsg :: ConnData -> NonEmpty SndQueue -> ([ConnId], Either AgentErrorType (ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage))
|
|
|
|
|
prepareMsg cData@ConnData {connId, pqEncryption} sqs
|
|
|
|
|
| ratchetSyncSendProhibited cData = (acc, Left $ CMD PROHIBITED)
|
|
|
|
|
-- connection is only updated if PQ encryption was disabled, and now it has to be enabled.
|
|
|
|
|
-- support for PQ encryption (small message envelopes) will not be disabled when message is sent.
|
|
|
|
|
| pqEnc == PQEncOn && pqEncryption == PQEncOff =
|
|
|
|
|
let cData' = cData {pqEncryption = pqEnc} :: ConnData
|
|
|
|
|
in (connId : acc, Right (cData', sqs, Just pqEnc, msgFlags, A_MSG msg))
|
|
|
|
|
| otherwise = (acc, Right (cData, sqs, Just pqEnc, msgFlags, A_MSG msg))
|
|
|
|
|
connIds = map (\(connId, _, _, _) -> connId) $ rights $ toList reqs
|
|
|
|
|
|
|
|
|
|
-- / async command processing v v v
|
|
|
|
@@ -1007,7 +1017,7 @@ runCommandProcessing c@AgentClient {subQ} server_ Worker {doWork} = do
|
|
|
|
|
_ -> throwError $ INTERNAL $ "incorrect connection type " <> show (internalCmdTag cmd)
|
|
|
|
|
ICDuplexSecure _rId senderKey -> withServer' . tryWithLock "ICDuplexSecure" . withDuplexConn $ \(DuplexConnection cData (rq :| _) (sq :| _)) -> do
|
|
|
|
|
secure rq senderKey
|
|
|
|
|
void $ enqueueMessage c cData sq Nothing SMP.MsgFlags {notification = True} HELLO
|
|
|
|
|
void $ enqueueMessage c cData sq SMP.MsgFlags {notification = True} HELLO
|
|
|
|
|
-- ICDeleteConn is no longer used, but it can be present in old client databases
|
|
|
|
|
ICDeleteConn -> withStore' c (`deleteCommand` cmdId)
|
|
|
|
|
ICDeleteRcvQueue rId -> withServer $ \srv -> tryWithLock "ICDeleteRcvQueue" $ do
|
|
|
|
@@ -1022,7 +1032,7 @@ runCommandProcessing c@AgentClient {subQ} server_ Worker {doWork} = do
|
|
|
|
|
Just rq1 -> when (status == Confirmed) $ do
|
|
|
|
|
secureQueue c rq' senderKey
|
|
|
|
|
withStore' c $ \db -> setRcvQueueStatus db rq' Secured
|
|
|
|
|
void . enqueueMessages c cData sqs Nothing SMP.noMsgFlags $ QUSE [((server, sndId), True)]
|
|
|
|
|
void . enqueueMessages c cData sqs SMP.noMsgFlags $ QUSE [((server, sndId), True)]
|
|
|
|
|
rq1' <- withStore' c $ \db -> setRcvSwitchStatus db rq1 $ Just RSSendingQUSE
|
|
|
|
|
let rqs' = updatedQs rq1' rqs
|
|
|
|
|
conn' = DuplexConnection cData rqs' sqs
|
|
|
|
@@ -1085,16 +1095,16 @@ runCommandProcessing c@AgentClient {subQ} server_ Worker {doWork} = do
|
|
|
|
|
notify cmd = atomically $ writeTBQueue subQ (corrId, connId, APC (sAEntity @e) cmd)
|
|
|
|
|
-- ^ ^ ^ async command processing /
|
|
|
|
|
|
|
|
|
|
enqueueMessages :: AgentMonad m => AgentClient -> ConnData -> NonEmpty SndQueue -> Maybe CR.PQEncryption -> MsgFlags -> AMessage -> m (AgentMsgId, CR.PQEncryption)
|
|
|
|
|
enqueueMessages c cData sqs pqEnc_ msgFlags aMessage = do
|
|
|
|
|
enqueueMessages :: AgentMonad m => AgentClient -> ConnData -> NonEmpty SndQueue -> MsgFlags -> AMessage -> m (AgentMsgId, PQEncryption)
|
|
|
|
|
enqueueMessages c cData sqs msgFlags aMessage = do
|
|
|
|
|
when (ratchetSyncSendProhibited cData) $ throwError $ INTERNAL "enqueueMessages: ratchet is not synchronized"
|
|
|
|
|
enqueueMessages' c cData sqs pqEnc_ msgFlags aMessage
|
|
|
|
|
enqueueMessages' c cData sqs msgFlags aMessage
|
|
|
|
|
|
|
|
|
|
enqueueMessages' :: AgentMonad m => AgentClient -> ConnData -> NonEmpty SndQueue -> Maybe CR.PQEncryption -> MsgFlags -> AMessage -> m (AgentMsgId, CR.PQEncryption)
|
|
|
|
|
enqueueMessages' c cData sqs pqEnc_ msgFlags aMessage =
|
|
|
|
|
liftEither . runIdentity =<< enqueueMessagesB c (Identity (Right (cData, sqs, pqEnc_, msgFlags, aMessage)))
|
|
|
|
|
enqueueMessages' :: AgentMonad m => AgentClient -> ConnData -> NonEmpty SndQueue -> MsgFlags -> AMessage -> m (AgentMsgId, CR.PQEncryption)
|
|
|
|
|
enqueueMessages' c cData sqs msgFlags aMessage =
|
|
|
|
|
liftEither . runIdentity =<< enqueueMessagesB c (Identity (Right (cData, sqs, Nothing, msgFlags, aMessage)))
|
|
|
|
|
|
|
|
|
|
enqueueMessagesB :: (AgentMonad' m, Traversable t) => AgentClient -> t (Either AgentErrorType (ConnData, NonEmpty SndQueue, Maybe CR.PQEncryption, MsgFlags, AMessage)) -> m (t (Either AgentErrorType (AgentMsgId, CR.PQEncryption)))
|
|
|
|
|
enqueueMessagesB :: (AgentMonad' m, Traversable t) => AgentClient -> t (Either AgentErrorType (ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage)) -> m (t (Either AgentErrorType (AgentMsgId, PQEncryption)))
|
|
|
|
|
enqueueMessagesB c reqs = do
|
|
|
|
|
reqs' <- enqueueMessageB c reqs
|
|
|
|
|
enqueueSavedMessageB c $ mapMaybe snd $ rights $ toList reqs'
|
|
|
|
@@ -1103,12 +1113,12 @@ enqueueMessagesB c reqs = do
|
|
|
|
|
isActiveSndQ :: SndQueue -> Bool
|
|
|
|
|
isActiveSndQ SndQueue {status} = status == Secured || status == Active
|
|
|
|
|
|
|
|
|
|
enqueueMessage :: forall m. AgentMonad m => AgentClient -> ConnData -> SndQueue -> Maybe CR.PQEncryption -> MsgFlags -> AMessage -> m (AgentMsgId, CR.PQEncryption)
|
|
|
|
|
enqueueMessage c cData sq pqEnc_ msgFlags aMessage =
|
|
|
|
|
liftEither . fmap fst . runIdentity =<< enqueueMessageB c (Identity (Right (cData, [sq], pqEnc_, msgFlags, aMessage)))
|
|
|
|
|
enqueueMessage :: forall m. AgentMonad m => AgentClient -> ConnData -> SndQueue -> MsgFlags -> AMessage -> m (AgentMsgId, PQEncryption)
|
|
|
|
|
enqueueMessage c cData sq msgFlags aMessage =
|
|
|
|
|
liftEither . fmap fst . runIdentity =<< enqueueMessageB c (Identity (Right (cData, [sq], Nothing, msgFlags, aMessage)))
|
|
|
|
|
|
|
|
|
|
-- this function is used only for sending messages in batch, it returns the list of successes to enqueue additional deliveries
|
|
|
|
|
enqueueMessageB :: forall m t. (AgentMonad' m, Traversable t) => AgentClient -> t (Either AgentErrorType (ConnData, NonEmpty SndQueue, Maybe CR.PQEncryption, MsgFlags, AMessage)) -> m (t (Either AgentErrorType ((AgentMsgId, CR.PQEncryption), Maybe (ConnData, [SndQueue], AgentMsgId))))
|
|
|
|
|
enqueueMessageB :: forall m t. (AgentMonad' m, Traversable t) => AgentClient -> t (Either AgentErrorType (ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage)) -> m (t (Either AgentErrorType ((AgentMsgId, PQEncryption), Maybe (ConnData, [SndQueue], AgentMsgId))))
|
|
|
|
|
enqueueMessageB c reqs = do
|
|
|
|
|
getAVRange <- asks $ smpAgentVRange . config
|
|
|
|
|
reqMids <- withStoreBatch c $ \db -> fmap (bindRight $ storeSentMsg db getAVRange) reqs
|
|
|
|
@@ -1117,22 +1127,23 @@ enqueueMessageB c reqs = do
|
|
|
|
|
let sqs' = filter isActiveSndQ sqs
|
|
|
|
|
pure $ Right ((msgId, pqSecr), if null sqs' then Nothing else Just (cData, sqs', msgId))
|
|
|
|
|
where
|
|
|
|
|
storeSentMsg :: DB.Connection -> (CR.PQEncryption -> VersionRangeSMPA) -> (ConnData, NonEmpty SndQueue, Maybe CR.PQEncryption, MsgFlags, AMessage) -> IO (Either AgentErrorType ((ConnData, NonEmpty SndQueue, Maybe CR.PQEncryption, MsgFlags, AMessage), InternalId, CR.PQEncryption))
|
|
|
|
|
storeSentMsg db getAVRange req@(ConnData {connId, connAgentVersion = v}, sq :| _, pqEnc_, msgFlags, aMessage) = fmap (first storeError) $ runExceptT $ do
|
|
|
|
|
storeSentMsg :: DB.Connection -> (PQEncryption -> VersionRangeSMPA) -> (ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage) -> IO (Either AgentErrorType ((ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage), InternalId, PQEncryption))
|
|
|
|
|
storeSentMsg db getAVRange req@(cData@ConnData {connId, pqEncryption}, sq :| _, pqEnc_, msgFlags, aMessage) = fmap (first storeError) $ runExceptT $ do
|
|
|
|
|
internalTs <- liftIO getCurrentTime
|
|
|
|
|
(internalId, internalSndId, prevMsgHash) <- liftIO $ updateSndIds db connId
|
|
|
|
|
let privHeader = APrivHeader (unSndId internalSndId) prevMsgHash
|
|
|
|
|
agentMsg = AgentMessage privHeader aMessage
|
|
|
|
|
agentMsgStr = smpEncode agentMsg
|
|
|
|
|
internalHash = C.sha256Hash agentMsgStr
|
|
|
|
|
(encAgentMessage, pqEncryption) <- agentRatchetEncrypt db connId agentMsgStr (e2eEncUserMsgLength v) pqEnc_
|
|
|
|
|
let agentVersion = maxVersion . getAVRange $ fromMaybe CR.PQEncOff pqEnc_
|
|
|
|
|
(encAgentMessage, pqEnc) <- agentRatchetEncrypt db cData agentMsgStr e2eEncUserMsgLength pqEnc_
|
|
|
|
|
-- agent version range is determined by the connection suppport of PQ encryption, that is may be enabled when message is sent
|
|
|
|
|
let agentVersion = maxVersion $ getAVRange pqEncryption
|
|
|
|
|
msgBody = smpEncode $ AgentMsgEnvelope {agentVersion, encAgentMessage}
|
|
|
|
|
msgType = agentMessageType agentMsg
|
|
|
|
|
msgData = SndMsgData {internalId, internalSndId, internalTs, msgType, msgFlags, msgBody, pqEncryption, internalHash, prevMsgHash}
|
|
|
|
|
msgData = SndMsgData {internalId, internalSndId, internalTs, msgType, msgFlags, msgBody, pqEncryption = pqEnc, internalHash, prevMsgHash}
|
|
|
|
|
liftIO $ createSndMsg db connId msgData
|
|
|
|
|
liftIO $ createSndMsgDelivery db connId sq internalId
|
|
|
|
|
pure (req, internalId, pqEncryption)
|
|
|
|
|
pure (req, internalId, pqEnc)
|
|
|
|
|
|
|
|
|
|
enqueueSavedMessage :: AgentMonad' m => AgentClient -> ConnData -> AgentMsgId -> SndQueue -> m ()
|
|
|
|
|
enqueueSavedMessage c cData msgId sq = enqueueSavedMessageB c $ Identity (cData, [sq], msgId)
|
|
|
|
@@ -1344,7 +1355,7 @@ ackMessage' c connId msgId rcptInfo_ = withConnLock c connId "ackMessage" $ do
|
|
|
|
|
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 Nothing SMP.MsgFlags {notification = False} rcpt
|
|
|
|
|
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}) ->
|
|
|
|
@@ -1374,7 +1385,7 @@ switchDuplexConnection c (DuplexConnection cData@ConnData {connId, userId} rqs s
|
|
|
|
|
let rq' = (q :: NewRcvQueue) {primary = True, dbReplaceQueueId = Just dbQueueId}
|
|
|
|
|
rq'' <- withStore c $ \db -> addConnRcvQueue db connId rq'
|
|
|
|
|
addSubscription c rq''
|
|
|
|
|
void . enqueueMessages c cData sqs Nothing SMP.noMsgFlags $ QADD [(qUri, Just (server, sndId))]
|
|
|
|
|
void . enqueueMessages c cData sqs SMP.noMsgFlags $ QADD [(qUri, Just (server, sndId))]
|
|
|
|
|
rq1 <- withStore' c $ \db -> setRcvSwitchStatus db rq $ Just RSSendingQADD
|
|
|
|
|
let rqs' = updatedQs rq1 rqs <> [rq'']
|
|
|
|
|
pure . connectionStats $ DuplexConnection cData rqs' sqs
|
|
|
|
@@ -1403,16 +1414,20 @@ abortConnectionSwitch' c connId =
|
|
|
|
|
_ -> throwError $ CMD PROHIBITED
|
|
|
|
|
_ -> throwError $ CMD PROHIBITED
|
|
|
|
|
|
|
|
|
|
synchronizeRatchet' :: AgentMonad m => AgentClient -> ConnId -> CR.PQEncryption -> Bool -> m ConnectionStats
|
|
|
|
|
synchronizeRatchet' :: AgentMonad m => AgentClient -> ConnId -> PQEncryption -> Bool -> m ConnectionStats
|
|
|
|
|
synchronizeRatchet' c connId pqEnc force = withConnLock c connId "synchronizeRatchet" $ do
|
|
|
|
|
withStore c (`getConn` connId) >>= \case
|
|
|
|
|
SomeConn _ (DuplexConnection cData@ConnData {pqEncryption} rqs sqs)
|
|
|
|
|
| ratchetSyncAllowed cData || force -> do
|
|
|
|
|
-- check queues are not switching?
|
|
|
|
|
cData' <- if pqEncryption == pqEnc then pure cData else withStore' c $ \db -> setConnPQEncryption db cData pqEnc
|
|
|
|
|
pqEnc' <-
|
|
|
|
|
if pqEnc == PQEncOn && pqEncryption == PQEncOff
|
|
|
|
|
then PQEncOn <$ withStore' c (`enableConnPQEncryption` connId)
|
|
|
|
|
else pure pqEncryption
|
|
|
|
|
let cData' = cData {pqEncryption = pqEnc'} :: ConnData
|
|
|
|
|
AgentConfig {e2eEncryptVRange} <- asks config
|
|
|
|
|
g <- asks random
|
|
|
|
|
(pk1, pk2, pKem, e2eParams) <- liftIO $ CR.generateRcvE2EParams g (maxVersion $ e2eEncryptVRange pqEnc) pqEnc
|
|
|
|
|
(pk1, pk2, pKem, e2eParams) <- liftIO $ CR.generateRcvE2EParams g (maxVersion $ e2eEncryptVRange pqEnc') pqEnc'
|
|
|
|
|
enqueueRatchetKeyMsgs c cData' sqs e2eParams
|
|
|
|
|
withStore' c $ \db -> do
|
|
|
|
|
setConnRatchetSync db connId RSStarted
|
|
|
|
@@ -1948,7 +1963,7 @@ processSMPTransmission c@AgentClient {smpClients, subQ} (tSess@(_, srv, _), _v,
|
|
|
|
|
notify (MSGNTF $ SMP.rcvMessageMeta srvMsgId msg')
|
|
|
|
|
where
|
|
|
|
|
queueDrained = case conn of
|
|
|
|
|
DuplexConnection _ _ sqs -> void $ enqueueMessages c cData sqs Nothing SMP.noMsgFlags $ QCONT (sndAddress rq)
|
|
|
|
|
DuplexConnection _ _ sqs -> void $ enqueueMessages c cData sqs SMP.noMsgFlags $ QCONT (sndAddress rq)
|
|
|
|
|
_ -> pure ()
|
|
|
|
|
processClientMsg srvTs msgFlags msgBody = do
|
|
|
|
|
clientMsg@SMP.ClientMsgEnvelope {cmHeader = SMP.PubHeader phVer e2ePubKey_} <-
|
|
|
|
@@ -2200,7 +2215,7 @@ processSMPTransmission c@AgentClient {smpClients, subQ} (tSess@(_, srv, _), _v,
|
|
|
|
|
enqueueDuplexHello :: SndQueue -> m ()
|
|
|
|
|
enqueueDuplexHello sq = do
|
|
|
|
|
let cData' = toConnData conn'
|
|
|
|
|
void $ enqueueMessage c cData' sq Nothing SMP.MsgFlags {notification = True} HELLO
|
|
|
|
|
void $ enqueueMessage c cData' sq SMP.MsgFlags {notification = True} HELLO
|
|
|
|
|
|
|
|
|
|
continueSending :: SMP.MsgId -> (SMPServer, SMP.SenderId) -> Connection 'CDuplex -> m ()
|
|
|
|
|
continueSending srvMsgId addr (DuplexConnection _ _ sqs) =
|
|
|
|
@@ -2257,7 +2272,7 @@ processSMPTransmission c@AgentClient {smpClients, subQ} (tSess@(_, srv, _), _v,
|
|
|
|
|
(Just sndPubKey, Just dhPublicKey) -> do
|
|
|
|
|
logServer "<--" c srv rId $ "MSG <QADD>:" <> logSecret srvMsgId <> " " <> logSecret (senderId queueAddress)
|
|
|
|
|
let sqInfo' = (sqInfo :: SMPQueueInfo) {queueAddress = queueAddress {dhPublicKey}}
|
|
|
|
|
void . enqueueMessages c cData' sqs Nothing SMP.noMsgFlags $ QKEY [(sqInfo', sndPubKey)]
|
|
|
|
|
void . enqueueMessages c cData' sqs SMP.noMsgFlags $ QKEY [(sqInfo', sndPubKey)]
|
|
|
|
|
sq1 <- withStore' c $ \db -> setSndSwitchStatus db sq $ Just SSSendingQKEY
|
|
|
|
|
let sqs'' = updatedQs sq1 sqs' <> [sq2]
|
|
|
|
|
conn' = DuplexConnection cData' rqs sqs''
|
|
|
|
@@ -2302,7 +2317,7 @@ processSMPTransmission c@AgentClient {smpClients, subQ} (tSess@(_, srv, _), _v,
|
|
|
|
|
withStore' c $ \db -> setSndQueueStatus db sq' Secured
|
|
|
|
|
let sq'' = (sq' :: SndQueue) {status = Secured}
|
|
|
|
|
-- sending QTEST to the new queue only, the old one will be removed if sent successfully
|
|
|
|
|
void $ enqueueMessages c cData' [sq''] Nothing SMP.noMsgFlags $ QTEST [addr]
|
|
|
|
|
void $ enqueueMessages c cData' [sq''] SMP.noMsgFlags $ QTEST [addr]
|
|
|
|
|
sq1' <- withStore' c $ \db -> setSndSwitchStatus db sq1 $ Just SSSendingQTEST
|
|
|
|
|
let sqs' = updatedQs sq1' sqs
|
|
|
|
|
conn' = DuplexConnection cData' rqs sqs'
|
|
|
|
@@ -2318,7 +2333,7 @@ processSMPTransmission c@AgentClient {smpClients, subQ} (tSess@(_, srv, _), _v,
|
|
|
|
|
let CR.Ratchet {rcSnd} = rcPrev
|
|
|
|
|
-- if ratchet was initialized as receiving, it means EREADY wasn't sent on key negotiation
|
|
|
|
|
when (isNothing rcSnd) . void $
|
|
|
|
|
enqueueMessages' c cData' sqs Nothing SMP.MsgFlags {notification = True} (EREADY lastExternalSndId)
|
|
|
|
|
enqueueMessages' c cData' sqs SMP.MsgFlags {notification = True} (EREADY lastExternalSndId)
|
|
|
|
|
|
|
|
|
|
smpInvitation :: SMP.MsgId -> Connection c -> ConnectionRequestUri 'CMInvitation -> ConnInfo -> m ()
|
|
|
|
|
smpInvitation srvMsgId conn' connReq@(CRInvitationUri crData _) cInfo = do
|
|
|
|
@@ -2401,7 +2416,7 @@ processSMPTransmission c@AgentClient {smpClients, subQ} (tSess@(_, srv, _), _v,
|
|
|
|
|
(_, rcDHRs) <- atomically . C.generateKeyPair =<< asks random
|
|
|
|
|
rcParams <- liftEitherWith cryptoError $ CR.pqX3dhSnd pk1 pk2 (CR.APRKP CR.SRKSProposed <$> pKem) e2eOtherPartyParams
|
|
|
|
|
recreateRatchet $ CR.initSndRatchet rcVs k2Rcv rcDHRs rcParams
|
|
|
|
|
void . enqueueMessages' c cData' sqs Nothing SMP.MsgFlags {notification = True} $ EREADY lastExternalSndId
|
|
|
|
|
void . enqueueMessages' c cData' sqs SMP.MsgFlags {notification = True} $ EREADY lastExternalSndId
|
|
|
|
|
|
|
|
|
|
checkMsgIntegrity :: PrevExternalSndId -> ExternalSndId -> PrevRcvMsgHash -> ByteString -> MsgIntegrity
|
|
|
|
|
checkMsgIntegrity prevExtSndId extSndId internalPrevMsgHash receivedPrevMsgHash
|
|
|
|
@@ -2428,22 +2443,22 @@ switchStatusError q expected actual =
|
|
|
|
|
<> (", actual=" <> show actual)
|
|
|
|
|
|
|
|
|
|
connectReplyQueues :: AgentMonad m => AgentClient -> ConnData -> ConnInfo -> NonEmpty SMPQueueInfo -> m ()
|
|
|
|
|
connectReplyQueues c cData@ConnData {userId, connId} ownConnInfo (qInfo :| _) = do
|
|
|
|
|
connectReplyQueues c cData@ConnData {userId, connId, pqEncryption} ownConnInfo (qInfo :| _) = do
|
|
|
|
|
clientVRange <- asks $ smpClientVRange . config
|
|
|
|
|
case qInfo `proveCompatible` clientVRange of
|
|
|
|
|
Nothing -> throwError $ AGENT A_VERSION
|
|
|
|
|
Just qInfo' -> do
|
|
|
|
|
sq <- newSndQueue userId connId qInfo'
|
|
|
|
|
sq' <- withStore c $ \db -> upgradeRcvConnToDuplex db connId sq
|
|
|
|
|
enqueueConfirmation c cData sq' ownConnInfo Nothing Nothing
|
|
|
|
|
enqueueConfirmation c cData sq' ownConnInfo Nothing pqEncryption
|
|
|
|
|
|
|
|
|
|
confirmQueueAsync :: forall m. AgentMonad m => AgentClient -> ConnData -> SndQueue -> SMPServerWithAuth -> ConnInfo -> Maybe (CR.SndE2ERatchetParams 'C.X448) -> CR.PQEncryption -> SubscriptionMode -> m ()
|
|
|
|
|
confirmQueueAsync :: forall m. AgentMonad m => AgentClient -> ConnData -> SndQueue -> SMPServerWithAuth -> ConnInfo -> Maybe (CR.SndE2ERatchetParams 'C.X448) -> PQEncryption -> SubscriptionMode -> m ()
|
|
|
|
|
confirmQueueAsync c cData sq srv connInfo e2eEncryption_ pqEnc subMode = do
|
|
|
|
|
storeConfirmation c cData sq e2eEncryption_ (Just pqEnc) =<< mkAgentConfirmation c cData sq srv connInfo subMode
|
|
|
|
|
storeConfirmation c cData sq e2eEncryption_ pqEnc =<< mkAgentConfirmation c cData sq srv connInfo subMode
|
|
|
|
|
submitPendingMsg c cData sq
|
|
|
|
|
|
|
|
|
|
confirmQueue :: forall m. AgentMonad m => Compatible VersionSMPA -> AgentClient -> ConnData -> SndQueue -> SMPServerWithAuth -> ConnInfo -> Maybe (CR.SndE2ERatchetParams 'C.X448) -> Maybe CR.PQEncryption -> SubscriptionMode -> m ()
|
|
|
|
|
confirmQueue (Compatible agentVersion) c cData@ConnData {connId, connAgentVersion = v} sq srv connInfo e2eEncryption_ pqEnc_ subMode = do
|
|
|
|
|
confirmQueue :: forall m. AgentMonad m => Compatible VersionSMPA -> AgentClient -> ConnData -> SndQueue -> SMPServerWithAuth -> ConnInfo -> Maybe (CR.SndE2ERatchetParams 'C.X448) -> PQEncryption -> SubscriptionMode -> m ()
|
|
|
|
|
confirmQueue (Compatible agentVersion) c cData@ConnData {connId} sq srv connInfo e2eEncryption_ pqEnc subMode = do
|
|
|
|
|
msg <- mkConfirmation =<< mkAgentConfirmation c cData sq srv connInfo subMode
|
|
|
|
|
sendConfirmation c sq msg
|
|
|
|
|
withStore' c $ \db -> setSndQueueStatus db sq Confirmed
|
|
|
|
@@ -2451,7 +2466,7 @@ confirmQueue (Compatible agentVersion) c cData@ConnData {connId, connAgentVersio
|
|
|
|
|
mkConfirmation :: AgentMessage -> m MsgBody
|
|
|
|
|
mkConfirmation aMessage = withStore c $ \db -> runExceptT $ do
|
|
|
|
|
void . liftIO $ updateSndIds db connId
|
|
|
|
|
(encConnInfo, _) <- agentRatchetEncrypt db connId (smpEncode aMessage) (e2eEncConnInfoLength v) pqEnc_
|
|
|
|
|
(encConnInfo, _) <- agentRatchetEncrypt db cData (smpEncode aMessage) e2eEncConnInfoLength (Just pqEnc)
|
|
|
|
|
pure . smpEncode $ AgentConfirmation {agentVersion, e2eEncryption_, encConnInfo}
|
|
|
|
|
|
|
|
|
|
mkAgentConfirmation :: AgentMonad m => AgentClient -> ConnData -> SndQueue -> SMPServerWithAuth -> ConnInfo -> SubscriptionMode -> m AgentMessage
|
|
|
|
@@ -2459,18 +2474,18 @@ mkAgentConfirmation c cData sq srv connInfo subMode = do
|
|
|
|
|
qInfo <- createReplyQueue c cData sq subMode srv
|
|
|
|
|
pure $ AgentConnInfoReply (qInfo :| []) connInfo
|
|
|
|
|
|
|
|
|
|
enqueueConfirmation :: AgentMonad m => AgentClient -> ConnData -> SndQueue -> ConnInfo -> Maybe (CR.SndE2ERatchetParams 'C.X448) -> Maybe CR.PQEncryption -> m ()
|
|
|
|
|
enqueueConfirmation c cData sq connInfo e2eEncryption_ pqEnc_ = do
|
|
|
|
|
storeConfirmation c cData sq e2eEncryption_ pqEnc_ $ AgentConnInfo connInfo
|
|
|
|
|
enqueueConfirmation :: AgentMonad m => AgentClient -> ConnData -> SndQueue -> ConnInfo -> Maybe (CR.SndE2ERatchetParams 'C.X448) -> PQEncryption -> m ()
|
|
|
|
|
enqueueConfirmation c cData sq connInfo e2eEncryption_ pqEnc = do
|
|
|
|
|
storeConfirmation c cData sq e2eEncryption_ pqEnc $ AgentConnInfo connInfo
|
|
|
|
|
submitPendingMsg c cData sq
|
|
|
|
|
|
|
|
|
|
storeConfirmation :: AgentMonad m => AgentClient -> ConnData -> SndQueue -> Maybe (CR.SndE2ERatchetParams 'C.X448) -> Maybe CR.PQEncryption -> AgentMessage -> m ()
|
|
|
|
|
storeConfirmation c ConnData {connId, connAgentVersion = v} sq e2eEncryption_ pqEnc_ agentMsg = withStore c $ \db -> runExceptT $ do
|
|
|
|
|
storeConfirmation :: AgentMonad m => AgentClient -> ConnData -> SndQueue -> Maybe (CR.SndE2ERatchetParams 'C.X448) -> PQEncryption -> AgentMessage -> m ()
|
|
|
|
|
storeConfirmation c cData@ConnData {connId, connAgentVersion = v} sq e2eEncryption_ pqEnc agentMsg = withStore c $ \db -> runExceptT $ do
|
|
|
|
|
internalTs <- liftIO getCurrentTime
|
|
|
|
|
(internalId, internalSndId, prevMsgHash) <- liftIO $ updateSndIds db connId
|
|
|
|
|
let agentMsgStr = smpEncode agentMsg
|
|
|
|
|
internalHash = C.sha256Hash agentMsgStr
|
|
|
|
|
(encConnInfo, pqEncryption) <- agentRatchetEncrypt db connId agentMsgStr (e2eEncConnInfoLength v) pqEnc_
|
|
|
|
|
(encConnInfo, pqEncryption) <- agentRatchetEncrypt db cData agentMsgStr e2eEncConnInfoLength (Just pqEnc)
|
|
|
|
|
let msgBody = smpEncode $ AgentConfirmation {agentVersion = v, e2eEncryption_, encConnInfo}
|
|
|
|
|
msgType = agentMessageType agentMsg
|
|
|
|
|
msgData = SndMsgData {internalId, internalSndId, internalTs, msgType, msgBody, pqEncryption, msgFlags = SMP.MsgFlags {notification = True}, internalHash, prevMsgHash}
|
|
|
|
@@ -2504,20 +2519,21 @@ enqueueRatchetKey c cData@ConnData {connId, pqEncryption} sq e2eEncryption = do
|
|
|
|
|
pure internalId
|
|
|
|
|
|
|
|
|
|
-- encoded AgentMessage -> encoded EncAgentMessage
|
|
|
|
|
agentRatchetEncrypt :: DB.Connection -> ConnId -> ByteString -> Int -> Maybe CR.PQEncryption -> ExceptT StoreError IO (ByteString, CR.PQEncryption)
|
|
|
|
|
agentRatchetEncrypt db connId msg paddedLen pqEnc_ = do
|
|
|
|
|
agentRatchetEncrypt :: DB.Connection -> ConnData -> ByteString -> (PQEncryption -> Int) -> Maybe PQEncryption -> ExceptT StoreError IO (ByteString, PQEncryption)
|
|
|
|
|
agentRatchetEncrypt db ConnData {connId, pqEncryption} msg getPaddedLen pqEnc_ = do
|
|
|
|
|
rc <- ExceptT $ getRatchet db connId
|
|
|
|
|
let paddedLen = getPaddedLen pqEncryption
|
|
|
|
|
(encMsg, rc') <- liftE (SEAgentError . cryptoError) $ CR.rcEncrypt rc paddedLen msg pqEnc_
|
|
|
|
|
liftIO $ updateRatchet db connId rc' CR.SMDNoChange
|
|
|
|
|
pure (encMsg, CR.rcSndKEM rc')
|
|
|
|
|
|
|
|
|
|
-- encoded EncAgentMessage -> encoded AgentMessage
|
|
|
|
|
agentRatchetDecrypt :: TVar ChaChaDRG -> DB.Connection -> ConnId -> ByteString -> ExceptT StoreError IO (ByteString, CR.PQEncryption)
|
|
|
|
|
agentRatchetDecrypt :: TVar ChaChaDRG -> DB.Connection -> ConnId -> ByteString -> ExceptT StoreError IO (ByteString, PQEncryption)
|
|
|
|
|
agentRatchetDecrypt g db connId encAgentMsg = do
|
|
|
|
|
rc <- ExceptT $ getRatchet db connId
|
|
|
|
|
agentRatchetDecrypt' g db connId rc encAgentMsg
|
|
|
|
|
|
|
|
|
|
agentRatchetDecrypt' :: TVar ChaChaDRG -> DB.Connection -> ConnId -> CR.RatchetX448 -> ByteString -> ExceptT StoreError IO (ByteString, CR.PQEncryption)
|
|
|
|
|
agentRatchetDecrypt' :: TVar ChaChaDRG -> DB.Connection -> ConnId -> CR.RatchetX448 -> ByteString -> ExceptT StoreError IO (ByteString, PQEncryption)
|
|
|
|
|
agentRatchetDecrypt' g db connId rc encAgentMsg = do
|
|
|
|
|
skipped <- liftIO $ getSkippedMsgKeys db connId
|
|
|
|
|
(agentMsgBody_, rc', skippedDiff) <- liftE (SEAgentError . cryptoError) $ CR.rcDecrypt g rc skipped encAgentMsg
|
|
|
|
|