core: add chat items to indicate decryption failures due to ratchets being out of sync (#2175)

* core: add chat items to indicate decryption failures due to ratchets being out of sync

* show ratchet errors in chat items, show all integrity errors

* show decryption errors, tests

* ios: chat items, remove item for duplicate messages

* android: decryption errors chat items

* eol
This commit is contained in:
Evgeny Poberezkin
2023-04-16 12:35:45 +02:00
committed by GitHub
parent 13cf1cc004
commit aea526f69d
21 changed files with 480 additions and 75 deletions
+46 -3
View File
@@ -47,6 +47,7 @@ import Data.Time (NominalDiffTime, addUTCTime, defaultTimeLocale, formatTime)
import Data.Time.Clock (UTCTime, diffUTCTime, getCurrentTime, nominalDiffTimeToSeconds)
import Data.Time.Clock.System (SystemTime, systemToUTCTime)
import Data.Time.LocalTime (getCurrentTimeZone, getZonedTime)
import Data.Word (Word32)
import qualified Database.SQLite.Simple as DB
import Simplex.Chat.Archive
import Simplex.Chat.Call
@@ -2630,6 +2631,19 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do
ERR err -> do
toView $ CRChatError (Just user) (ChatErrorAgent err $ Just connEntity)
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure ()
forM_ (agentMsgDecryptError err) $ \e@(mde, n) -> do
ci_ <- withStore $ \db ->
getDirectChatItemsLast db user contactId 1 ""
>>= liftIO
. mapM (\(ci, content') -> updateDirectChatItem' db user contactId ci content' False Nothing)
. (mdeUpdatedCI e <=< headMaybe)
case ci_ of
Just ci -> toView $ CRChatItemUpdated user (AChatItem SCTDirect SMDRcv (DirectChat ct) ci)
_ -> createInternalChatItem user (CDDirectRcv ct) (CIRcvDecryptionError mde n) Nothing
where
headMaybe = \case
x : _ -> Just x
_ -> Nothing
-- TODO add debugging output
_ -> pure ()
@@ -2791,9 +2805,40 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do
ERR err -> do
toView $ CRChatError (Just user) (ChatErrorAgent err $ Just connEntity)
when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure ()
forM_ (agentMsgDecryptError err) $ \e@(mde, n) -> do
ci_ <- withStore $ \db ->
getGroupMemberChatItemLast db user groupId (groupMemberId' m)
>>= liftIO
. mapM (\(ci, content') -> updateGroupChatItem db user groupId ci content' False Nothing)
. mdeUpdatedCI e
case ci_ of
Just ci -> toView $ CRChatItemUpdated user (AChatItem SCTGroup SMDRcv (GroupChat gInfo) ci)
_ -> createInternalChatItem user (CDGroupRcv gInfo m) (CIRcvDecryptionError mde n) Nothing
-- TODO add debugging output
_ -> pure ()
agentMsgDecryptError :: AgentErrorType -> Maybe (MsgDecryptError, Word32)
agentMsgDecryptError = \case
AGENT (A_CRYPTO RATCHET_HEADER) -> Just (MDERatchetHeader, 1)
AGENT (A_CRYPTO (RATCHET_EARLIER n)) -> Just (MDEEarlier, n + 1) -- 1 is added to account for the message that has A_DUPLICATE error
AGENT (A_CRYPTO (RATCHET_SKIPPED n)) -> Just (MDETooManySkipped, n)
-- we are not treating this as decryption error, as in many cases it happens as the result of duplicate or redundant delivery,
-- and we don't have a way to differentiate.
-- we could store the hashes of past messages in the agent, or delaying message deletion after ACK
-- A_DUPLICATE -> Nothing
_ -> Nothing
mdeUpdatedCI :: (MsgDecryptError, Word32) -> CChatItem c -> Maybe (ChatItem c 'MDRcv, CIContent 'MDRcv)
mdeUpdatedCI (mde', n') (CChatItem _ ci@ChatItem {content = CIRcvDecryptionError mde n})
| mde == mde' = case mde of
MDERatchetHeader -> r (n + n')
MDEEarlier -> r n -- the first error in a sequence has the largest number – it's the number of messages to receive to catch up, keeping it
MDETooManySkipped -> r n' -- the numbers are not added as sequential MDETooManySkipped will have it incremented by 1
| otherwise = Nothing
where
r n'' = Just (ci, CIRcvDecryptionError mde n'')
mdeUpdatedCI _ _ = Nothing
processSndFileConn :: ACommand 'Agent e -> ConnectionEntity -> Connection -> SndFileTransfer -> m ()
processSndFileConn agentMsg connEntity conn ft@SndFileTransfer {fileId, fileName, fileStatus} =
case agentMsg of
@@ -3469,9 +3514,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do
checkIntegrityCreateItem :: forall c. ChatTypeI c => ChatDirection c 'MDRcv -> MsgMeta -> m ()
checkIntegrityCreateItem cd MsgMeta {integrity, broker = (_, brokerTs)} = case integrity of
MsgOk -> pure ()
MsgError e -> case e of
MsgSkipped {} -> createInternalChatItem user cd (CIRcvIntegrityError e) (Just brokerTs)
_ -> toView $ CRMsgIntegrityError user e
MsgError e -> createInternalChatItem user cd (CIRcvIntegrityError e) (Just brokerTs)
xInfo :: Contact -> Profile -> m ()
xInfo c@Contact {profile = p} p' = unless (fromLocalProfile p == p') $ do