From 529ba7a0e792659dd16f527c0fa2f18e594a692f Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Mon, 22 Apr 2024 20:46:14 +0400 Subject: [PATCH 1/4] ios: 5.7 what's new (#4062) * ios: 5.7 what's new * add about pq * update * update --------- Co-authored-by: Evgeny Poberezkin --- .../Views/Onboarding/WhatsNewView.swift | 25 ++++++++++++ apps/ios/SimpleX.xcodeproj/project.pbxproj | 40 +++++++++---------- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift b/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift index 73ea5720c6..edb3dc17d3 100644 --- a/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift +++ b/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift @@ -375,6 +375,31 @@ private let versionDescriptions: [VersionDescription] = [ description: "Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" ), ] + ), + VersionDescription( + version: "v5.7", + features: [ + FeatureDescription( + icon: "key", + title: "Quantum resistant encryption", + description: "Will be enabled in direct chats!" + ), + FeatureDescription( + icon: "arrowshape.turn.up.forward", + title: "Forward and save messages", + description: "Message source remains private." + ), + FeatureDescription( + icon: "music.note", + title: "In-call sounds", + description: "When connecting audio and video calls." + ), + FeatureDescription( + icon: "antenna.radiowaves.left.and.right", + title: "Network management", + description: "More reliable network connection." + ) + ] ) ] diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 16bd7311a3..cccc267695 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -29,11 +29,11 @@ 5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; }; 5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; }; 5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; }; - 5C22178E2BD5CBAC00A8B0E7 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217892BD5CBAC00A8B0E7 /* libffi.a */; }; - 5C22178F2BD5CBAC00A8B0E7 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22178A2BD5CBAC00A8B0E7 /* libgmp.a */; }; - 5C2217902BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22178B2BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a */; }; - 5C2217912BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22178C2BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a */; }; - 5C2217922BD5CBAC00A8B0E7 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22178D2BD5CBAC00A8B0E7 /* libgmpxx.a */; }; + 5C2217982BD6B0F200A8B0E7 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217932BD6B0F200A8B0E7 /* libgmp.a */; }; + 5C2217992BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217942BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a */; }; + 5C22179A2BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217952BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a */; }; + 5C22179B2BD6B0F200A8B0E7 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217962BD6B0F200A8B0E7 /* libgmpxx.a */; }; + 5C22179C2BD6B0F200A8B0E7 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217972BD6B0F200A8B0E7 /* libffi.a */; }; 5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; }; 5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; }; 5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; }; @@ -279,11 +279,11 @@ 5C13730A28156D2700F43030 /* ContactConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionView.swift; sourceTree = ""; }; 5C13730C2815740A00F43030 /* DebugJSON.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DebugJSON.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = ""; }; - 5C2217892BD5CBAC00A8B0E7 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; - 5C22178A2BD5CBAC00A8B0E7 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; - 5C22178B2BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a"; sourceTree = ""; }; - 5C22178C2BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a"; sourceTree = ""; }; - 5C22178D2BD5CBAC00A8B0E7 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; + 5C2217932BD6B0F200A8B0E7 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; + 5C2217942BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a"; sourceTree = ""; }; + 5C2217952BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a"; sourceTree = ""; }; + 5C2217962BD6B0F200A8B0E7 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; + 5C2217972BD6B0F200A8B0E7 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; 5C245F3C2B501E98001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; 5C245F3D2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = "tr.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; }; 5C245F3E2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -525,13 +525,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5C22179B2BD6B0F200A8B0E7 /* libgmpxx.a in Frameworks */, 5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */, - 5C2217902BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a in Frameworks */, - 5C22178E2BD5CBAC00A8B0E7 /* libffi.a in Frameworks */, - 5C2217912BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a in Frameworks */, - 5C22178F2BD5CBAC00A8B0E7 /* libgmp.a in Frameworks */, + 5C22179A2BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a in Frameworks */, + 5C2217992BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a in Frameworks */, + 5C22179C2BD6B0F200A8B0E7 /* libffi.a in Frameworks */, + 5C2217982BD6B0F200A8B0E7 /* libgmp.a in Frameworks */, 5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */, - 5C2217922BD5CBAC00A8B0E7 /* libgmpxx.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -595,11 +595,11 @@ 5C764E5C279C70B7000C6508 /* Libraries */ = { isa = PBXGroup; children = ( - 5C2217892BD5CBAC00A8B0E7 /* libffi.a */, - 5C22178A2BD5CBAC00A8B0E7 /* libgmp.a */, - 5C22178D2BD5CBAC00A8B0E7 /* libgmpxx.a */, - 5C22178B2BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a */, - 5C22178C2BD5CBAC00A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a */, + 5C2217972BD6B0F200A8B0E7 /* libffi.a */, + 5C2217932BD6B0F200A8B0E7 /* libgmp.a */, + 5C2217962BD6B0F200A8B0E7 /* libgmpxx.a */, + 5C2217942BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG-ghc9.6.3.a */, + 5C2217952BD6B0F200A8B0E7 /* libHSsimplex-chat-5.7.0.1-CNnVmAzCniZCwENW90FRcG.a */, ); path = Libraries; sourceTree = ""; From 7567791866b11ffc797de3747b2dae7022564e49 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Mon, 22 Apr 2024 20:46:48 +0400 Subject: [PATCH 2/4] core: enable PQ encryption for contacts (#4049) * wip * migration * remove migration * remove flag wip * Revert "remove migration" This reverts commit 9e64663f9d13852202e5d732e846fbcc240c4449. * wip * remove vrange parameterization * tests mostly pass * more tests pass * enable for upgraded contacts * remove migration * core: test large messages (#4061) * update simplexmq, fix tests * test test * another test * enable tests * empty * empty --- cabal.project | 2 +- scripts/nix/sha256map.nix | 2 +- src/Simplex/Chat.hs | 144 ++++----- src/Simplex/Chat/Controller.hs | 11 +- src/Simplex/Chat/Protocol.hs | 10 +- src/Simplex/Chat/Store/Connections.hs | 9 +- src/Simplex/Chat/Store/Direct.hs | 24 +- src/Simplex/Chat/Store/Files.hs | 18 +- src/Simplex/Chat/Store/Groups.hs | 96 +++--- src/Simplex/Chat/Store/Messages.hs | 22 +- src/Simplex/Chat/Store/Profiles.hs | 6 +- src/Simplex/Chat/Store/Shared.hs | 8 +- src/Simplex/Chat/View.hs | 1 - tests/Bots/DirectoryTests.hs | 8 +- tests/ChatClient.hs | 24 +- tests/ChatTests/Direct.hs | 403 ++++++-------------------- tests/ChatTests/Groups.hs | 44 ++- tests/ChatTests/Profiles.hs | 4 +- tests/ChatTests/Utils.hs | 49 +--- tests/ProtocolTests.hs | 18 +- 20 files changed, 321 insertions(+), 582 deletions(-) diff --git a/cabal.project b/cabal.project index bddea2468e..572c22c5fd 100644 --- a/cabal.project +++ b/cabal.project @@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: b08314722d2c8f4d640dd5baba73c71e09ff0dda + tag: fe28e02be779fb0357a7f650bd327a75f4aaab95 source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index dfc8a85b71..c4e857d97e 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."b08314722d2c8f4d640dd5baba73c71e09ff0dda" = "18jzn66sx7iaaz0l12xbkwa2qvvz1mrj8b7i8rkxrcr2rhilabga"; + "https://github.com/simplex-chat/simplexmq.git"."fe28e02be779fb0357a7f650bd327a75f4aaab95" = "1hwh0kx6ljskjx7svxpwmga79dfa2vz9qq0mwbqr6kkb9kf6qdd3"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 2e19dd73d9..6d6437fe37 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -103,7 +103,7 @@ import Simplex.Messaging.Client (defaultNetworkConfig) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..)) import qualified Simplex.Messaging.Crypto.File as CF -import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport (..), pattern IKNoPQ, pattern IKPQOff, pattern PQEncOff, pattern PQEncOn, pattern PQSupportOff, pattern PQSupportOn) +import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport (..), pattern IKPQOff, pattern IKPQOn, pattern PQEncOff, pattern PQEncOn, pattern PQSupportOff, pattern PQSupportOn) import qualified Simplex.Messaging.Crypto.Ratchet as CR import Simplex.Messaging.Encoding import Simplex.Messaging.Encoding.String @@ -250,7 +250,6 @@ newChatController encryptLocalFiles <- newTVarIO False tempDirectory <- newTVarIO optTempDirectory contactMergeEnabled <- newTVarIO True - pqExperimentalEnabled <- newTVarIO PQSupportOff pure ChatController { firstTime, @@ -287,8 +286,7 @@ newChatController encryptLocalFiles, tempDirectory, logFilePath = logFile, - contactMergeEnabled, - pqExperimentalEnabled + contactMergeEnabled } where configServers :: DefaultAgentServers @@ -408,7 +406,7 @@ subscribeUsers onlyNeeded users = do subscribe vr us subscribe vr us' where - subscribe :: (PQSupport -> VersionRangeChat) -> [User] -> CM' () + subscribe :: VersionRangeChat -> [User] -> CM' () subscribe vr = mapM_ $ runExceptT . subscribeUserConnections vr onlyNeeded Agent.subscribeConnections startFilesToReceive :: [User] -> CM' () @@ -492,7 +490,7 @@ processChatCommand cmd = chatVersionRange >>= (`processChatCommand'` cmd) {-# INLINE processChatCommand #-} -processChatCommand' :: (PQSupport -> VersionRangeChat) -> ChatCommand -> CM ChatResponse +processChatCommand' :: VersionRangeChat -> ChatCommand -> CM ChatResponse processChatCommand' vr = \case ShowActiveUser -> withUser' $ pure . CRActiveUser CreateActiveUser NewUser {profile, sameServers, pastTimestamp} -> do @@ -634,20 +632,6 @@ processChatCommand' vr = \case ok_ APISetEncryptLocalFiles on -> chatWriteVar encryptLocalFiles on >> ok_ SetContactMergeEnabled onOff -> chatWriteVar contactMergeEnabled onOff >> ok_ - APISetPQEncryption onOff -> chatWriteVar pqExperimentalEnabled onOff >> ok_ - APISetContactPQ ctId pqEnc -> withUser $ \user -> do - ct@Contact {activeConn} <- withStore $ \db -> getContact db vr user ctId - case activeConn of - Just conn@Connection {connId, pqSupport, pqEncryption} - | pqEncryption == pqEnc -> pure $ CRContactPQAllowed user ct pqEnc - | otherwise -> do - let pqSup = PQSupport $ pqEnc == PQEncOn || pqSupport == PQSupportOn - conn' = conn {pqSupport = pqSup, pqEncryption = pqEnc} :: Connection - ct' = ct {activeConn = Just conn'} :: Contact - withStore' $ \db -> updateConnSupportPQ db connId pqSup pqEnc - pure $ CRContactPQAllowed user ct' pqEnc - Nothing -> throwChatError $ CEContactNotActive ct - SetContactPQ cName pqEnc -> withContactName cName (`APISetContactPQ` pqEnc) APIExportArchive cfg -> checkChatStopped $ lift (exportArchive cfg) >> ok_ ExportArchive -> do ts <- liftIO getCurrentTime @@ -1470,10 +1454,9 @@ processChatCommand' vr = \case -- [incognito] generate profile for connection incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing subMode <- chatReadVar subscriptionMode - pqSup <- chatReadVar pqExperimentalEnabled - (connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing (IKNoPQ pqSup) subMode + (connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing IKPQOn subMode -- TODO PQ pass minVersion from the current range - conn <- withStore' $ \db -> createDirectConnection db user connId cReq ConnNew incognitoProfile subMode initialChatVersion pqSup + conn <- withStore' $ \db -> createDirectConnection db user connId cReq ConnNew incognitoProfile subMode initialChatVersion PQSupportOn pure $ CRInvitation user cReq conn AddContact incognito -> withUser $ \User {userId} -> processChatCommand $ APIAddContact userId incognito @@ -1499,8 +1482,7 @@ processChatCommand' vr = \case -- [incognito] generate profile to send incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing let profileToSend = userProfileToSend user incognitoProfile Nothing False - pqSup <- chatReadVar pqExperimentalEnabled - lift (withAgent' $ \a -> connRequestPQSupport a pqSup cReq) >>= \case + lift (withAgent' $ \a -> connRequestPQSupport a PQSupportOn cReq) >>= \case Nothing -> throwChatError CEInvalidConnReq -- TODO PQ the error above should be CEIncompatibleConnReqVersion, also the same API should be called in Plan Just (agentV, pqSup') -> do @@ -1541,8 +1523,7 @@ processChatCommand' vr = \case processChatCommand $ APIListContacts userId APICreateMyAddress userId -> withUserId userId $ \user -> procCmd $ do subMode <- chatReadVar subscriptionMode - -- TODO v5.7 pass IPPQOn - (connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact Nothing IKPQOff subMode + (connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact Nothing IKPQOn subMode withStore $ \db -> createUserContactLink db user connId cReq subMode pure $ CRUserContactLinkCreated user cReq CreateMyAddress -> withUser $ \User {userId} -> @@ -1671,8 +1652,8 @@ processChatCommand' vr = \case addContactConn ct ctConns = case contactSendConn_ ct of Right conn | directOrUsed ct -> (ct, conn) : ctConns _ -> ctConns - ctSndEvent :: (Contact, Connection) -> (ConnOrGroupId, PQSupport, ChatMsgEvent 'Json) - ctSndEvent (_, Connection {connId, pqSupport}) = (ConnectionId connId, pqSupport, XMsgNew $ MCSimple (extMsgContent mc Nothing)) + ctSndEvent :: (Contact, Connection) -> (ConnOrGroupId, ChatMsgEvent 'Json) + ctSndEvent (_, Connection {connId}) = (ConnectionId connId, XMsgNew $ MCSimple (extMsgContent mc Nothing)) ctMsgReq :: (Contact, Connection) -> SndMessage -> MsgReq ctMsgReq (_, conn) SndMessage {msgId, msgBody} = (conn, MsgFlags {notification = hasNotification XMsgNew_}, msgBody, msgId) zipWith3' :: (a -> b -> c -> d) -> NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d @@ -1762,7 +1743,7 @@ processChatCommand' vr = \case subMode <- chatReadVar subscriptionMode dm <- encodeConnInfo $ XGrpAcpt membershipMemId agentConnId <- withAgent $ \a -> joinConnection a (aUserId user) True connRequest dm PQSupportOff subMode - let chatV = vr PQSupportOff `peerConnChatVersion` peerChatVRange + let chatV = vr `peerConnChatVersion` peerChatVRange withStore' $ \db -> do createMemberConnection db userId fromMember agentConnId chatV peerChatVRange subMode updateGroupMemberStatus db userId fromMember GSMemAccepted @@ -1925,6 +1906,7 @@ processChatCommand' vr = \case unless (maxVersion peerChatVRange >= groupDirectInvVersion) $ throwChatError CEPeerChatVRangeIncompatible when (isJust $ memberContactId m) $ throwChatError $ CECommandError "member contact already exists" subMode <- chatReadVar subscriptionMode + -- TODO PQ should negotitate contact connection with PQSupportOn? (connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing IKPQOff subMode -- [incognito] reuse membership incognito profile ct <- withStore' $ \db -> createMemberContact db user connId cReq g m mConn subMode @@ -2289,7 +2271,7 @@ processChatCommand' vr = \case connect' (Just gLinkId) cReqHash xContactId True where connect' groupLinkId cReqHash xContactId inGroup = do - pqSup <- if inGroup then pure PQSupportOff else chatReadVar pqExperimentalEnabled + let pqSup = if inGroup then PQSupportOff else PQSupportOn (connId, incognitoProfile, subMode, chatV) <- requestContact user incognito cReq xContactId inGroup pqSup conn <- withStore' $ \db -> createConnReqConnection db userId connId cReqHash xContactId incognitoProfile groupLinkId subMode chatV pqSup pure $ CRSentInvitation user conn incognitoProfile @@ -2297,7 +2279,7 @@ processChatCommand' vr = \case connectContactViaAddress user incognito ct cReq = withInvitationLock "connectContactViaAddress" (strEncode cReq) $ do newXContactId <- XContactId <$> drgRandomBytes 16 - pqSup <- chatReadVar pqExperimentalEnabled + let pqSup = PQSupportOn (connId, incognitoProfile, subMode, chatV) <- requestContact user incognito cReq newXContactId False pqSup let cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq ct' <- withStore $ \db -> createAddressContactConnection db vr user ct connId cReqHash newXContactId incognitoProfile subMode chatV pqSup @@ -2370,8 +2352,8 @@ processChatCommand' vr = \case mergedProfile = userProfileToSend user Nothing (Just ct) False ct' = updateMergedPreferences user' ct mergedProfile' = userProfileToSend user' Nothing (Just ct') False - ctSndEvent :: ChangedProfileContact -> (ConnOrGroupId, PQSupport, ChatMsgEvent 'Json) - ctSndEvent ChangedProfileContact {mergedProfile', conn = Connection {connId, pqSupport}} = (ConnectionId connId, pqSupport, XInfo mergedProfile') + ctSndEvent :: ChangedProfileContact -> (ConnOrGroupId, ChatMsgEvent 'Json) + ctSndEvent ChangedProfileContact {mergedProfile', conn = Connection {connId}} = (ConnectionId connId, XInfo mergedProfile') ctMsgReq :: ChangedProfileContact -> Either ChatError SndMessage -> Either ChatError MsgReq ctMsgReq ChangedProfileContact {conn} = fmap $ \SndMessage {msgId, msgBody} -> @@ -3116,10 +3098,10 @@ getRcvFilePath fileId fPath_ fn keepHandle = case fPath_ of acceptContactRequest :: User -> UserContactRequest -> Maybe IncognitoProfile -> Bool -> CM Contact acceptContactRequest user UserContactRequest {agentInvitationId = AgentInvId invId, cReqChatVRange, localDisplayName = cName, profileId, profile = cp, userContactLinkId, xContactId, pqSupport} incognitoProfile contactUsed = do subMode <- chatReadVar subscriptionMode - pqSup <- chatReadVar pqExperimentalEnabled + let pqSup = PQSupportOn vr <- chatVersionRange let profileToSend = profileToSendOnAccept user incognitoProfile False - chatV = vr pqSup `peerConnChatVersion` cReqChatVRange + chatV = vr `peerConnChatVersion` cReqChatVRange pqSup' = pqSup `CR.pqSupportAnd` pqSupport dm <- encodeConnInfoPQ pqSup' chatV $ XInfo profileToSend acId <- withAgent $ \a -> acceptContact a True invId dm pqSup' subMode @@ -3130,7 +3112,7 @@ acceptContactRequestAsync user UserContactRequest {agentInvitationId = AgentInvI subMode <- chatReadVar subscriptionMode let profileToSend = profileToSendOnAccept user incognitoProfile False vr <- chatVersionRange - chatV <- (\pq -> vr pq `peerConnChatVersion` cReqChatVRange) <$> chatReadVar pqExperimentalEnabled + let chatV = vr `peerConnChatVersion` cReqChatVRange (cmdId, acId) <- agentAcceptContactAsync user True invId (XInfo profileToSend) subMode pqSup chatV withStore' $ \db -> do ct@Contact {activeConn} <- createAcceptedContact db user acId chatV cReqChatVRange cName profileId p userContactLinkId xContactId incognitoProfile subMode pqSup contactUsed @@ -3160,7 +3142,7 @@ acceptGroupJoinRequestAsync } subMode <- chatReadVar subscriptionMode vr <- chatVersionRange - chatV <- (\pq -> vr pq `peerConnChatVersion` cReqChatVRange) <$> chatReadVar pqExperimentalEnabled + let chatV = vr `peerConnChatVersion` cReqChatVRange connIds <- agentAcceptContactAsync user True invId msg subMode PQSupportOff chatV withStore $ \db -> do liftIO $ createAcceptedMemberConnection db user connIds chatV ucr groupMemberId subMode @@ -3206,7 +3188,7 @@ agentSubscriber = do type AgentBatchSubscribe = AgentClient -> [ConnId] -> ExceptT AgentErrorType IO (Map ConnId (Either AgentErrorType ())) -subscribeUserConnections :: (PQSupport -> VersionRangeChat) -> Bool -> AgentBatchSubscribe -> User -> CM () +subscribeUserConnections :: VersionRangeChat -> Bool -> AgentBatchSubscribe -> User -> CM () subscribeUserConnections vr onlyNeeded agentBatchSubscribe user = do -- get user connections ce <- asks $ subscriptionEvents . config @@ -3500,7 +3482,7 @@ expireChatItems user@User {userId} ttl sync = do cancelFilesInProgress user filesInfo deleteFilesLocally filesInfo withStore' $ \db -> deleteContactExpiredCIs db user ct expirationDate - processGroup :: (PQSupport -> VersionRangeChat) -> UTCTime -> UTCTime -> GroupInfo -> CM () + processGroup :: VersionRangeChat -> UTCTime -> UTCTime -> GroupInfo -> CM () processGroup vr expirationDate createdAtCutoff gInfo = do lift waitChatStartedAndActivated filesInfo <- withStore' $ \db -> getGroupExpiredFileInfo db user gInfo expirationDate createdAtCutoff @@ -3658,7 +3640,7 @@ processAgentMsgSndFile _corrId aFileId msg = do case L.nonEmpty fds of Just fds' -> loopSend fds' Nothing -> pure msgDeliveryId - sendFileError :: Text -> (PQSupport -> VersionRangeChat) -> FileTransferMeta -> CM () + sendFileError :: Text -> VersionRangeChat -> FileTransferMeta -> CM () sendFileError err vr ft = do logError $ "Sent file error: " <> err ci <- withStore $ \db -> do @@ -3724,7 +3706,7 @@ processAgentMsgRcvFile _corrId aFileId msg = do agentXFTPDeleteRcvFile aFileId fileId toView $ CRRcvFileError user ci e ft -processAgentMessageConn :: (PQSupport -> VersionRangeChat) -> User -> ACorrId -> ConnId -> ACommand 'Agent 'AEConn -> CM () +processAgentMessageConn :: VersionRangeChat -> User -> ACorrId -> ConnId -> ACommand 'Agent 'AEConn -> CM () processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = do -- Missing connection/entity errors here will be sent to the view but not shown as CRITICAL alert, -- as in this case no need to ACK message - we can't process messages for this connection anyway. @@ -4105,7 +4087,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = profileToSend = profileToSendOnAccept user profileMode True void $ sendDirectMemberMessage conn (XGrpLinkMem profileToSend) groupId sendIntroductions members = do - intros <- withStore' $ \db -> createIntroductions db (maxVersion $ vr PQSupportOff) members m + intros <- withStore' $ \db -> createIntroductions db (maxVersion vr) members m shuffledIntros <- liftIO $ shuffleIntros intros if m `supportsVersion` batchSendVersion then do @@ -4557,9 +4539,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = Nothing -> do -- [incognito] generate profile to send, create connection with incognito profile incognitoProfile <- if acceptIncognito then Just . NewIncognito <$> liftIO generateRandomProfile else pure Nothing - pqSup <- chatReadVar pqExperimentalEnabled - let pqSup' = pqSup `CR.pqSupportAnd` reqPQSup - ct <- acceptContactRequestAsync user cReq incognitoProfile True pqSup' + ct <- acceptContactRequestAsync user cReq incognitoProfile True reqPQSup toView $ CRAcceptingContactRequest user ct Just groupId -> do gInfo <- withStore $ \db -> getGroupInfo db vr user groupId @@ -5644,8 +5624,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = | maxVersion mcvr >= groupDirectInvVersion -> pure Nothing | otherwise -> Just <$> createConn subMode let customUserProfileId = localProfileId <$> incognitoMembershipProfile gInfo - vr' = vr PQSupportOff - chatV = maybe (minVersion vr') (\peerVR -> vr' `peerConnChatVersion` fromChatVRange peerVR) memChatVRange + chatV = maybe (minVersion vr) (\peerVR -> vr `peerConnChatVersion` fromChatVRange peerVR) memChatVRange void $ withStore $ \db -> createIntroReMember db user gInfo m chatV memInfo memRestrictions groupConnIds directConnIds customUserProfileId subMode _ -> messageError "x.grp.mem.intro can be only sent by host member" where @@ -5693,7 +5672,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = directConnIds <- forM directConnReq $ \dcr -> joinAgentConnectionAsync user True dcr dm subMode let customUserProfileId = localProfileId <$> incognitoMembershipProfile gInfo mcvr = maybe chatInitialVRange fromChatVRange memChatVRange - chatV = vr PQSupportOff `peerConnChatVersion` mcvr + chatV = vr `peerConnChatVersion` mcvr withStore' $ \db -> createIntroToMemberContact db user m toMember chatV mcvr groupConnIds directConnIds customUserProfileId subMode xGrpMemRole :: GroupInfo -> GroupMember -> MemberId -> GroupMemberRole -> RcvMessage -> UTCTime -> CM () @@ -5892,6 +5871,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = joinConn subMode = do -- [incognito] send membership incognito profile let p = userProfileToSend user (fromLocalProfile <$> incognitoMembershipProfile g) Nothing False + -- TODO PQ should negotitate contact connection with PQSupportOn? (use encodeConnInfoPQ) dm <- encodeConnInfo $ XInfo p joinAgentConnectionAsync user True connReq dm subMode createItems mCt' m' = do @@ -6048,19 +6028,24 @@ metaBrokerTs MsgMeta {broker = (_, brokerTs)} = brokerTs sameMemberId :: MemberId -> GroupMember -> Bool sameMemberId memId GroupMember {memberId} = memId == memberId --- TODO v5.7 for contacts only version upgrade should trigger enabling PQ support/encryption updatePeerChatVRange :: Connection -> VersionRangeChat -> CM Connection -updatePeerChatVRange conn@Connection {connId, connChatVersion = v, peerChatVRange, pqSupport} msgVRange = do - v' <- lift $ upgradedConnVersion pqSupport v msgVRange - if msgVRange /= peerChatVRange || v' /= v +updatePeerChatVRange conn@Connection {connId, connChatVersion = v, peerChatVRange, connType, pqSupport, pqEncryption} msgVRange = do + v' <- lift $ upgradedConnVersion v msgVRange + conn' <- if msgVRange /= peerChatVRange || v' /= v then do withStore' $ \db -> setPeerChatVRange db connId v' msgVRange pure conn {connChatVersion = v', peerChatVRange = msgVRange} else pure conn + -- TODO v6.0 remove/review: for contacts only version upgrade should trigger enabling PQ support/encryption + if connType == ConnContact && v' >= pqEncryptionCompressionVersion && (pqSupport /= PQSupportOn || pqEncryption /= PQEncOn) + then do + withStore' $ \db -> updateConnSupportPQ db connId PQSupportOn PQEncOn + pure conn' {pqSupport = PQSupportOn, pqEncryption = PQEncOn} + else pure conn' updateMemberChatVRange :: GroupMember -> Connection -> VersionRangeChat -> CM (GroupMember, Connection) updateMemberChatVRange mem@GroupMember {groupMemberId} conn@Connection {connId, connChatVersion = v, peerChatVRange} msgVRange = do - v' <- lift $ upgradedConnVersion PQSupportOff v msgVRange + v' <- lift $ upgradedConnVersion v msgVRange if msgVRange /= peerChatVRange || v' /= v then do withStore' $ \db -> do @@ -6070,11 +6055,11 @@ updateMemberChatVRange mem@GroupMember {groupMemberId} conn@Connection {connId, pure (mem {memberChatVRange = msgVRange, activeConn = Just conn'}, conn') else pure (mem, conn) -upgradedConnVersion :: PQSupport -> VersionChat -> VersionRangeChat -> CM' VersionChat -upgradedConnVersion pqSup v peerVR = do +upgradedConnVersion :: VersionChat -> VersionRangeChat -> CM' VersionChat +upgradedConnVersion v peerVR = do vr <- chatVersionRange' -- don't allow reducing agreed connection version - pure $ maybe v (\(Compatible v') -> max v v') $ vr pqSup `compatibleVersion` peerVR + pure $ maybe v (\(Compatible v') -> max v v') $ vr `compatibleVersion` peerVR parseFileDescription :: FilePartyI p => Text -> CM (ValidFileDescription p) parseFileDescription = @@ -6241,7 +6226,7 @@ cancelSndFileTransfer user@User {userId} ft@SndFileTransfer {fileId, connId, age Just _ -> do vr <- chatVersionRange (sharedMsgId, conn) <- withStore $ \db -> (,) <$> getSharedMsgIdByFileId db userId fileId <*> getConnectionById db vr user connId - void $ sendDirectMessage_ conn PQSupportOff (BFileChunk sharedMsgId FileChunkCancel) (ConnectionId connId) + void $ sendDirectMessage_ conn (BFileChunk sharedMsgId FileChunkCancel) (ConnectionId connId) _ -> withAgent $ \a -> void . sendMessage a acId PQEncOff SMP.noMsgFlags $ smpEncode FileChunkCancel pure fileConnId fileConnId = if isNothing fileInline then Just acId else Nothing @@ -6281,8 +6266,8 @@ deleteOrUpdateMemberRecord user@User {userId} member = sendDirectContactMessage :: MsgEncodingI e => User -> Contact -> ChatMsgEvent e -> CM (SndMessage, Int64) sendDirectContactMessage user ct chatMsgEvent = do - conn@Connection {connId, pqSupport} <- liftEither $ contactSendConn_ ct - r <- sendDirectMessage_ conn pqSupport chatMsgEvent (ConnectionId connId) + conn@Connection {connId} <- liftEither $ contactSendConn_ ct + r <- sendDirectMessage_ conn chatMsgEvent (ConnectionId connId) let (sndMessage, msgDeliveryId, pqEnc') = r void $ createContactPQSndItem user ct conn pqEnc' pure (sndMessage, msgDeliveryId) @@ -6301,37 +6286,37 @@ contactSendConn_ ct@Contact {activeConn} = case activeConn of -- unlike sendGroupMemberMessage, this function will not store message as pending -- TODO v5.8 we could remove pending messages once all clients support forwarding sendDirectMemberMessage :: MsgEncodingI e => Connection -> ChatMsgEvent e -> GroupId -> CM (SndMessage, Int64, PQEncryption) -sendDirectMemberMessage conn chatMsgEvent groupId = sendDirectMessage_ conn PQSupportOff chatMsgEvent (GroupId groupId) +sendDirectMemberMessage conn chatMsgEvent groupId = sendDirectMessage_ conn chatMsgEvent (GroupId groupId) -sendDirectMessage_ :: MsgEncodingI e => Connection -> PQSupport -> ChatMsgEvent e -> ConnOrGroupId -> CM (SndMessage, Int64, PQEncryption) -sendDirectMessage_ conn pqSup chatMsgEvent connOrGroupId = do +sendDirectMessage_ :: MsgEncodingI e => Connection -> ChatMsgEvent e -> ConnOrGroupId -> CM (SndMessage, Int64, PQEncryption) +sendDirectMessage_ conn chatMsgEvent connOrGroupId = do when (connDisabled conn) $ throwChatError (CEConnectionDisabled conn) - msg@SndMessage {msgId, msgBody} <- createSndMessage chatMsgEvent connOrGroupId pqSup + msg@SndMessage {msgId, msgBody} <- createSndMessage chatMsgEvent connOrGroupId -- TODO move compressed body to SndMessage and compress in createSndMessage (msgDeliveryId, pqEnc') <- deliverMessage conn (toCMEventTag chatMsgEvent) msgBody msgId pure (msg, msgDeliveryId, pqEnc') -createSndMessage :: MsgEncodingI e => ChatMsgEvent e -> ConnOrGroupId -> PQSupport -> CM SndMessage -createSndMessage chatMsgEvent connOrGroupId pqSup = - liftEither . runIdentity =<< lift (createSndMessages $ Identity (connOrGroupId, pqSup, chatMsgEvent)) +createSndMessage :: MsgEncodingI e => ChatMsgEvent e -> ConnOrGroupId -> CM SndMessage +createSndMessage chatMsgEvent connOrGroupId = + liftEither . runIdentity =<< lift (createSndMessages $ Identity (connOrGroupId, chatMsgEvent)) -createSndMessages :: forall e t. (MsgEncodingI e, Traversable t) => t (ConnOrGroupId, PQSupport, ChatMsgEvent e) -> CM' (t (Either ChatError SndMessage)) +createSndMessages :: forall e t. (MsgEncodingI e, Traversable t) => t (ConnOrGroupId, ChatMsgEvent e) -> CM' (t (Either ChatError SndMessage)) createSndMessages idsEvents = do g <- asks random vr <- chatVersionRange' withStoreBatch $ \db -> fmap (createMsg db g vr) idsEvents where - createMsg :: DB.Connection -> TVar ChaChaDRG -> (PQSupport -> VersionRangeChat) -> (ConnOrGroupId, PQSupport, ChatMsgEvent e) -> IO (Either ChatError SndMessage) - createMsg db g vr (connOrGroupId, pqSup, evnt) = runExceptT $ do + createMsg :: DB.Connection -> TVar ChaChaDRG -> VersionRangeChat -> (ConnOrGroupId, ChatMsgEvent e) -> IO (Either ChatError SndMessage) + createMsg db g vr (connOrGroupId, evnt) = runExceptT $ do withExceptT ChatErrorStore $ createNewSndMessage db g connOrGroupId evnt encodeMessage where encodeMessage sharedMsgId = - encodeChatMessage maxEncodedMsgLength ChatMessage {chatVRange = vr pqSup, msgId = Just sharedMsgId, chatMsgEvent = evnt} + encodeChatMessage maxEncodedMsgLength ChatMessage {chatVRange = vr, msgId = Just sharedMsgId, chatMsgEvent = evnt} sendGroupMemberMessages :: forall e. MsgEncodingI e => User -> Connection -> NonEmpty (ChatMsgEvent e) -> GroupId -> CM () sendGroupMemberMessages user conn events groupId = do when (connDisabled conn) $ throwChatError (CEConnectionDisabled conn) - let idsEvts = L.map (GroupId groupId,PQSupportOff,) events + let idsEvts = L.map (GroupId groupId,) events (errs, msgs) <- lift $ partitionEithers . L.toList <$> createSndMessages idsEvts unless (null errs) $ toView $ CRChatErrors (Just user) errs forM_ (L.nonEmpty msgs) $ \msgs' -> do @@ -6367,12 +6352,12 @@ batchSndMessagesJSON = batchMessages maxEncodedMsgLength . L.toList encodeConnInfo :: MsgEncodingI e => ChatMsgEvent e -> CM ByteString encodeConnInfo chatMsgEvent = do vr <- chatVersionRange - encodeConnInfoPQ PQSupportOff (maxVersion $ vr PQSupportOff) chatMsgEvent + encodeConnInfoPQ PQSupportOff (maxVersion vr) chatMsgEvent encodeConnInfoPQ :: MsgEncodingI e => PQSupport -> VersionChat -> ChatMsgEvent e -> CM ByteString encodeConnInfoPQ pqSup v chatMsgEvent = do vr <- chatVersionRange - let info = ChatMessage {chatVRange = vr pqSup, msgId = Nothing, chatMsgEvent} + let info = ChatMessage {chatVRange = vr, msgId = Nothing, chatMsgEvent} case encodeChatMessage maxEncodedInfoLength info of ECMEncoded connInfo -> case pqSup of PQSupportOn | v >= pqEncryptionCompressionVersion && B.length connInfo > maxCompressedInfoLength -> do @@ -6459,7 +6444,7 @@ sendGroupMessage user gInfo members chatMsgEvent = do sendGroupMessage' :: MsgEncodingI e => User -> GroupInfo -> [GroupMember] -> ChatMsgEvent e -> CM (SndMessage, [GroupMember]) sendGroupMessage' user GroupInfo {groupId} members chatMsgEvent = do - msg@SndMessage {msgId, msgBody} <- createSndMessage chatMsgEvent (GroupId groupId) PQSupportOff + msg@SndMessage {msgId, msgBody} <- createSndMessage chatMsgEvent (GroupId groupId) recipientMembers <- liftIO $ shuffleMembers (filter memberCurrent members) let msgFlags = MsgFlags {notification = hasNotification $ toCMEventTag chatMsgEvent} (toSend, pending) = foldr addMember ([], []) recipientMembers @@ -6514,7 +6499,7 @@ memberSendAction chatMsgEvent members m@GroupMember {invitedByGroupMemberId} = c sendGroupMemberMessage :: MsgEncodingI e => User -> GroupMember -> ChatMsgEvent e -> Int64 -> Maybe Int64 -> CM () -> CM () sendGroupMemberMessage user m@GroupMember {groupMemberId} chatMsgEvent groupId introId_ postDeliver = do - msg <- createSndMessage chatMsgEvent (GroupId groupId) PQSupportOff + msg <- createSndMessage chatMsgEvent (GroupId groupId) messageMember msg `catchChatError` (\e -> toView (CRChatError (Just user) e)) where messageMember :: SndMessage -> CM () @@ -6931,11 +6916,11 @@ waitChatStartedAndActivated = do activated <- readTVar chatActivated unless (isJust started && activated) retry -chatVersionRange :: CM (PQSupport -> VersionRangeChat) +chatVersionRange :: CM VersionRangeChat chatVersionRange = lift chatVersionRange' {-# INLINE chatVersionRange #-} -chatVersionRange' :: CM' (PQSupport -> VersionRangeChat) +chatVersionRange' :: CM' VersionRangeChat chatVersionRange' = do ChatConfig {chatVRange} <- asks config pure chatVRange @@ -6983,9 +6968,6 @@ chatCommandP = "/remote_hosts_folder " *> (SetRemoteHostsFolder <$> filePath), "/_files_encrypt " *> (APISetEncryptLocalFiles <$> onOffP), "/contact_merge " *> (SetContactMergeEnabled <$> onOffP), - "/_pq @" *> (APISetContactPQ <$> A.decimal <* A.space <*> (PQEncryption <$> onOffP)), - "/pq @" *> (SetContactPQ <$> displayName <* A.space <*> (PQEncryption <$> onOffP)), - "/pq " *> (APISetPQEncryption . PQSupport <$> onOffP), "/_db export " *> (APIExportArchive <$> jsonP), "/db export" $> ExportArchive, "/_db import " *> (APIImportArchive <$> jsonP), diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 00c2e153f1..29566634f4 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -76,7 +76,7 @@ import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Crypto.File (CryptoFile (..)) import qualified Simplex.Messaging.Crypto.File as CF -import Simplex.Messaging.Crypto.Ratchet (PQEncryption, PQSupport (..)) +import Simplex.Messaging.Crypto.Ratchet (PQEncryption) import Simplex.Messaging.Encoding.String import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus) import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, parseAll, parseString, sumTypeJSON) @@ -126,7 +126,7 @@ coreVersionInfo simplexmqCommit = data ChatConfig = ChatConfig { agentConfig :: AgentConfig, - chatVRange :: PQSupport -> VersionRangeChat, + chatVRange :: VersionRangeChat, confirmMigrations :: MigrationConfirmation, defaultServers :: DefaultAgentServers, tbqSize :: Natural, @@ -230,8 +230,7 @@ data ChatController = ChatController encryptLocalFiles :: TVar Bool, tempDirectory :: TVar (Maybe FilePath), logFilePath :: Maybe FilePath, - contactMergeEnabled :: TVar Bool, - pqExperimentalEnabled :: TVar PQSupport -- TODO v5.7 remove + contactMergeEnabled :: TVar Bool } data HelpSection = HSMain | HSFiles | HSGroups | HSContacts | HSMyAddress | HSIncognito | HSMarkdown | HSMessages | HSRemote | HSSettings | HSDatabase @@ -268,9 +267,6 @@ data ChatCommand | SetRemoteHostsFolder FilePath | APISetEncryptLocalFiles Bool | SetContactMergeEnabled Bool - | APISetPQEncryption PQSupport - | APISetContactPQ ContactId PQEncryption - | SetContactPQ ContactName PQEncryption | APIExportArchive ArchiveConfig | ExportArchive | APIImportArchive ArchiveConfig @@ -736,7 +732,6 @@ data ChatResponse | CRRemoteCtrlSessionCode {remoteCtrl_ :: Maybe RemoteCtrlInfo, sessionCode :: Text} | CRRemoteCtrlConnected {remoteCtrl :: RemoteCtrlInfo} | CRRemoteCtrlStopped {rcsState :: RemoteCtrlSessionState, rcStopReason :: RemoteCtrlStopReason} - | CRContactPQAllowed {user :: User, contact :: Contact, pqEncryption :: PQEncryption} | CRContactPQEnabled {user :: User, contact :: Contact, pqEnabled :: PQEncryption} | CRSQLResult {rows :: [Text]} | CRSlowSQLQueries {chatQueries :: [SlowSQLQuery], agentQueries :: [SlowSQLQuery]} diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 6cdc52a499..bfa8383974 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -49,7 +49,6 @@ import Simplex.Chat.Types.Shared import Simplex.Chat.Types.Util import Simplex.Messaging.Agent.Protocol (VersionSMPA, pqdrSMPAgentVersion) import Simplex.Messaging.Compression (compress1, decompressBatch) -import Simplex.Messaging.Crypto.Ratchet (PQSupport (..), pattern PQSupportOff, pattern PQSupportOn) import Simplex.Messaging.Encoding import Simplex.Messaging.Encoding.String import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, fromTextField_, fstToLower, parseAll, sumTypeJSON, taggedObjectJSON) @@ -70,14 +69,11 @@ import Simplex.Messaging.Version hiding (version) -- This indirection is needed for backward/forward compatibility testing. -- Testing with real app versions is still needed, as tests use the current code with different version ranges, not the old code. currentChatVersion :: VersionChat -currentChatVersion = VersionChat 7 +currentChatVersion = VersionChat 8 -- This should not be used directly in code, instead use `chatVRange` from ChatConfig (see comment above) --- TODO remove parameterization in 5.7 -supportedChatVRange :: PQSupport -> VersionRangeChat -supportedChatVRange pq = mkVersionRange initialChatVersion $ case pq of - PQSupportOn -> pqEncryptionCompressionVersion - PQSupportOff -> currentChatVersion +supportedChatVRange :: VersionRangeChat +supportedChatVRange = mkVersionRange initialChatVersion currentChatVersion {-# INLINE supportedChatVRange #-} -- version range that supports skipping establishing direct connections in a group and establishing direct connection via x.grp.direct.inv diff --git a/src/Simplex/Chat/Store/Connections.hs b/src/Simplex/Chat/Store/Connections.hs index 0e543eacf2..d42fd1d6f6 100644 --- a/src/Simplex/Chat/Store/Connections.hs +++ b/src/Simplex/Chat/Store/Connections.hs @@ -36,7 +36,6 @@ import Simplex.Chat.Types.Preferences import Simplex.Messaging.Agent.Protocol (ConnId) import Simplex.Messaging.Agent.Store.SQLite (firstRow, firstRow', maybeFirstRow) import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB -import Simplex.Messaging.Crypto.Ratchet (PQSupport) import Simplex.Messaging.Util (eitherToMaybe) getChatLockEntity :: DB.Connection -> AgentConnId -> ExceptT StoreError IO ChatLockEntity @@ -64,7 +63,7 @@ getChatLockEntity db agentConnId = do ExceptT . firstRow fromOnly (SEInternalError "group member connection group_id not found") $ DB.query db "SELECT group_id FROM group_members WHERE group_member_id = ?" (Only groupMemberId) -getConnectionEntity :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> AgentConnId -> ExceptT StoreError IO ConnectionEntity +getConnectionEntity :: DB.Connection -> VersionRangeChat -> User -> AgentConnId -> ExceptT StoreError IO ConnectionEntity getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do c@Connection {connType, entityId} <- getConnection_ case entityId of @@ -185,7 +184,7 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do userContact_ [(cReq, groupId)] = Right UserContact {userContactLinkId, connReqContact = cReq, groupId} userContact_ _ = Left SEUserContactLinkNotFound -getConnectionEntityByConnReq :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> (ConnReqInvitation, ConnReqInvitation) -> IO (Maybe ConnectionEntity) +getConnectionEntityByConnReq :: DB.Connection -> VersionRangeChat -> User -> (ConnReqInvitation, ConnReqInvitation) -> IO (Maybe ConnectionEntity) getConnectionEntityByConnReq db vr user@User {userId} (cReqSchema1, cReqSchema2) = do connId_ <- maybeFirstRow fromOnly $ @@ -196,7 +195,7 @@ getConnectionEntityByConnReq db vr user@User {userId} (cReqSchema1, cReqSchema2) -- multiple connections can have same via_contact_uri_hash if request was repeated; -- this function searches for latest connection with contact so that "known contact" plan would be chosen; -- deleted connections are filtered out to allow re-connecting via same contact address -getContactConnEntityByConnReqHash :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe ConnectionEntity) +getContactConnEntityByConnReqHash :: DB.Connection -> VersionRangeChat -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe ConnectionEntity) getContactConnEntityByConnReqHash db vr user@User {userId} (cReqHash1, cReqHash2) = do connId_ <- maybeFirstRow fromOnly $ @@ -216,7 +215,7 @@ getContactConnEntityByConnReqHash db vr user@User {userId} (cReqHash1, cReqHash2 (userId, cReqHash1, cReqHash2, ConnDeleted) maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db vr user) connId_ -getConnectionsToSubscribe :: DB.Connection -> (PQSupport -> VersionRangeChat) -> IO ([ConnId], [ConnectionEntity]) +getConnectionsToSubscribe :: DB.Connection -> VersionRangeChat -> IO ([ConnId], [ConnectionEntity]) getConnectionsToSubscribe db vr = do aConnIds <- map fromOnly <$> DB.query_ db "SELECT agent_conn_id FROM connections where to_subscribe = 1" entities <- forM aConnIds $ \acId -> do diff --git a/src/Simplex/Chat/Store/Direct.hs b/src/Simplex/Chat/Store/Direct.hs index ba2586b0ca..650940b520 100644 --- a/src/Simplex/Chat/Store/Direct.hs +++ b/src/Simplex/Chat/Store/Direct.hs @@ -126,7 +126,7 @@ deletePendingContactConnection db userId connId = |] (userId, connId, ConnContact) -createAddressContactConnection :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Contact -> ConnId -> ConnReqUriHash -> XContactId -> Maybe Profile -> SubscriptionMode -> VersionChat -> PQSupport -> ExceptT StoreError IO Contact +createAddressContactConnection :: DB.Connection -> VersionRangeChat -> User -> Contact -> ConnId -> ConnReqUriHash -> XContactId -> Maybe Profile -> SubscriptionMode -> VersionChat -> PQSupport -> ExceptT StoreError IO Contact createAddressContactConnection db vr user@User {userId} Contact {contactId} acId cReqHash xContactId incognitoProfile subMode chatV pqSup = do PendingContactConnection {pccConnId} <- liftIO $ createConnReqConnection db userId acId cReqHash xContactId incognitoProfile Nothing subMode chatV pqSup liftIO $ DB.execute db "UPDATE connections SET contact_id = ? WHERE connection_id = ?" (contactId, pccConnId) @@ -153,7 +153,7 @@ createConnReqConnection db userId acId cReqHash xContactId incognitoProfile grou pccConnId <- insertedRowId db pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = True, viaUserContactLink = Nothing, groupLinkId, customUserProfileId, connReqInv = Nothing, localAlias = "", createdAt, updatedAt = createdAt} -getConnReqContactXContactId :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ConnReqUriHash -> IO (Maybe Contact, Maybe XContactId) +getConnReqContactXContactId :: DB.Connection -> VersionRangeChat -> User -> ConnReqUriHash -> IO (Maybe Contact, Maybe XContactId) getConnReqContactXContactId db vr user@User {userId} cReqHash = do getContactByConnReqHash db vr user cReqHash >>= \case c@(Just _) -> pure (c, Nothing) @@ -167,7 +167,7 @@ getConnReqContactXContactId db vr user@User {userId} cReqHash = do "SELECT xcontact_id FROM connections WHERE user_id = ? AND via_contact_uri_hash = ? LIMIT 1" (userId, cReqHash) -getContactByConnReqHash :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ConnReqUriHash -> IO (Maybe Contact) +getContactByConnReqHash :: DB.Connection -> VersionRangeChat -> User -> ConnReqUriHash -> IO (Maybe Contact) getContactByConnReqHash db vr user@User {userId} cReqHash = maybeFirstRow (toContact vr user) $ DB.query @@ -279,12 +279,12 @@ setContactDeleted db user@User {userId} ct@Contact {contactId} = do currentTs <- getCurrentTime DB.execute db "UPDATE contacts SET deleted = 1, updated_at = ? WHERE user_id = ? AND contact_id = ?" (currentTs, userId, contactId) -getDeletedContacts :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> IO [Contact] +getDeletedContacts :: DB.Connection -> VersionRangeChat -> User -> IO [Contact] getDeletedContacts db vr user@User {userId} = do contactIds <- map fromOnly <$> DB.query db "SELECT contact_id FROM contacts WHERE user_id = ? AND deleted = 1" (Only userId) rights <$> mapM (runExceptT . getDeletedContact db vr user) contactIds -getDeletedContact :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ExceptT StoreError IO Contact +getDeletedContact :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO Contact getDeletedContact db vr user contactId = getContact_ db vr user contactId True deleteContactProfile_ :: DB.Connection -> UserId -> ContactId -> IO () @@ -521,18 +521,18 @@ updateContactLDN_ db user@User {userId} contactId displayName newName updatedAt (newName, updatedAt, userId, contactId) safeDeleteLDN db user displayName -getContactByName :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ContactName -> ExceptT StoreError IO Contact +getContactByName :: DB.Connection -> VersionRangeChat -> User -> ContactName -> ExceptT StoreError IO Contact getContactByName db vr user localDisplayName = do cId <- getContactIdByName db user localDisplayName getContact db vr user cId -getUserContacts :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> IO [Contact] +getUserContacts :: DB.Connection -> VersionRangeChat -> User -> IO [Contact] getUserContacts db vr user@User {userId} = do contactIds <- map fromOnly <$> DB.query db "SELECT contact_id FROM contacts WHERE user_id = ? AND deleted = 0" (Only userId) contacts <- rights <$> mapM (runExceptT . getContact db vr user) contactIds pure $ filter (\Contact {activeConn} -> isJust activeConn) contacts -createOrUpdateContactRequest :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> InvitationId -> VersionRangeChat -> Profile -> Maybe XContactId -> PQSupport -> ExceptT StoreError IO ContactOrRequest +createOrUpdateContactRequest :: DB.Connection -> VersionRangeChat -> User -> Int64 -> InvitationId -> VersionRangeChat -> Profile -> Maybe XContactId -> PQSupport -> ExceptT StoreError IO ContactOrRequest createOrUpdateContactRequest db vr user@User {userId} userContactLinkId invId (VersionRange minV maxV) Profile {displayName, fullName, image, contactLink, preferences} xContactId_ pqSup = liftIO (maybeM getContact' xContactId_) >>= \case Just contact -> pure $ CORContact contact @@ -732,10 +732,10 @@ getContactIdByName db User {userId} cName = ExceptT . firstRow fromOnly (SEContactNotFoundByName cName) $ DB.query db "SELECT contact_id FROM contacts WHERE user_id = ? AND local_display_name = ? AND deleted = 0" (userId, cName) -getContact :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ExceptT StoreError IO Contact +getContact :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO Contact getContact db vr user contactId = getContact_ db vr user contactId False -getContact_ :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> Bool -> ExceptT StoreError IO Contact +getContact_ :: DB.Connection -> VersionRangeChat -> User -> Int64 -> Bool -> ExceptT StoreError IO Contact getContact_ db vr user@User {userId} contactId deleted = ExceptT . firstRow (toContact vr user) (SEContactNotFound contactId) $ DB.query @@ -791,7 +791,7 @@ getPendingContactConnections db User {userId} = do |] [":user_id" := userId, ":conn_type" := ConnContact] -getContactConnections :: DB.Connection -> (PQSupport -> VersionRangeChat) -> UserId -> Contact -> IO [Connection] +getContactConnections :: DB.Connection -> VersionRangeChat -> UserId -> Contact -> IO [Connection] getContactConnections db vr userId Contact {contactId} = connections =<< liftIO getConnections_ where @@ -811,7 +811,7 @@ getContactConnections db vr userId Contact {contactId} = connections [] = pure [] connections rows = pure $ map (toConnection vr) rows -getConnectionById :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ExceptT StoreError IO Connection +getConnectionById :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO Connection getConnectionById db vr User {userId} connId = ExceptT $ do firstRow (toConnection vr) (SEConnectionNotFoundById connId) $ DB.query diff --git a/src/Simplex/Chat/Store/Files.hs b/src/Simplex/Chat/Store/Files.hs index 8ac54c7e9b..81a5897c89 100644 --- a/src/Simplex/Chat/Store/Files.hs +++ b/src/Simplex/Chat/Store/Files.hs @@ -174,7 +174,7 @@ getPendingSndChunks db fileId connId = |] (fileId, connId) -createSndDirectFTConnection :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> (CommandId, ConnId) -> SubscriptionMode -> IO () +createSndDirectFTConnection :: DB.Connection -> VersionRangeChat -> User -> Int64 -> (CommandId, ConnId) -> SubscriptionMode -> IO () createSndDirectFTConnection db vr user@User {userId} fileId (cmdId, acId) subMode = do currentTs <- getCurrentTime Connection {connId} <- createSndFileConnection_ db vr userId fileId acId subMode @@ -194,7 +194,7 @@ createSndGroupFileTransfer db userId GroupInfo {groupId} filePath FileInvitation fileId <- insertedRowId db pure FileTransferMeta {fileId, xftpSndFile = Nothing, xftpRedirectFor = Nothing, fileName, filePath, fileSize, fileInline, chunkSize, cancelled = False} -createSndGroupFileTransferConnection :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> (CommandId, ConnId) -> GroupMember -> SubscriptionMode -> IO () +createSndGroupFileTransferConnection :: DB.Connection -> VersionRangeChat -> User -> Int64 -> (CommandId, ConnId) -> GroupMember -> SubscriptionMode -> IO () createSndGroupFileTransferConnection db vr user@User {userId} fileId (cmdId, acId) GroupMember {groupMemberId} subMode = do currentTs <- getCurrentTime Connection {connId} <- createSndFileConnection_ db vr userId fileId acId subMode @@ -430,10 +430,10 @@ lookupChatRefByFileId db User {userId} fileId = (userId, fileId) -- TODO v6.0 remove -createSndFileConnection_ :: DB.Connection -> (PQSupport -> VersionRangeChat) -> UserId -> Int64 -> ConnId -> SubscriptionMode -> IO Connection +createSndFileConnection_ :: DB.Connection -> VersionRangeChat -> UserId -> Int64 -> ConnId -> SubscriptionMode -> IO Connection createSndFileConnection_ db vr userId fileId agentConnId subMode = do currentTs <- getCurrentTime - createConnection_ db userId ConnSndFile (Just fileId) agentConnId (minVersion $ vr PQSupportOff) chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode CR.PQSupportOff + createConnection_ db userId ConnSndFile (Just fileId) agentConnId (minVersion vr) chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode CR.PQSupportOff updateSndFileStatus :: DB.Connection -> SndFileTransfer -> FileStatus -> IO () updateSndFileStatus db SndFileTransfer {fileId, connId} status = do @@ -695,7 +695,7 @@ getRcvFileTransfer_ db userId fileId = do _ -> pure Nothing cancelled = fromMaybe False cancelled_ -acceptRcvFileTransfer :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> (CommandId, ConnId) -> ConnStatus -> FilePath -> SubscriptionMode -> ExceptT StoreError IO AChatItem +acceptRcvFileTransfer :: DB.Connection -> VersionRangeChat -> User -> Int64 -> (CommandId, ConnId) -> ConnStatus -> FilePath -> SubscriptionMode -> ExceptT StoreError IO AChatItem acceptRcvFileTransfer db vr user@User {userId} fileId (cmdId, acId) connStatus filePath subMode = ExceptT $ do currentTs <- getCurrentTime acceptRcvFT_ db user fileId filePath Nothing currentTs @@ -707,7 +707,7 @@ acceptRcvFileTransfer db vr user@User {userId} fileId (cmdId, acId) connStatus f setCommandConnId db user cmdId connId runExceptT $ getChatItemByFileId db vr user fileId -getContactByFileId :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> FileTransferId -> ExceptT StoreError IO Contact +getContactByFileId :: DB.Connection -> VersionRangeChat -> User -> FileTransferId -> ExceptT StoreError IO Contact getContactByFileId db vr user@User {userId} fileId = do cId <- getContactIdByFileId getContact db vr user cId @@ -716,7 +716,7 @@ getContactByFileId db vr user@User {userId} fileId = do ExceptT . firstRow fromOnly (SEContactNotFoundByFileId fileId) $ DB.query db "SELECT contact_id FROM files WHERE user_id = ? AND file_id = ?" (userId, fileId) -acceptRcvInlineFT :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> FileTransferId -> FilePath -> ExceptT StoreError IO AChatItem +acceptRcvInlineFT :: DB.Connection -> VersionRangeChat -> User -> FileTransferId -> FilePath -> ExceptT StoreError IO AChatItem acceptRcvInlineFT db vr user fileId filePath = do liftIO $ acceptRcvFT_ db user fileId filePath (Just IFMOffer) =<< getCurrentTime getChatItemByFileId db vr user fileId @@ -725,7 +725,7 @@ startRcvInlineFT :: DB.Connection -> User -> RcvFileTransfer -> FilePath -> Mayb startRcvInlineFT db user RcvFileTransfer {fileId} filePath rcvFileInline = acceptRcvFT_ db user fileId filePath rcvFileInline =<< getCurrentTime -xftpAcceptRcvFT :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> FileTransferId -> FilePath -> ExceptT StoreError IO AChatItem +xftpAcceptRcvFT :: DB.Connection -> VersionRangeChat -> User -> FileTransferId -> FilePath -> ExceptT StoreError IO AChatItem xftpAcceptRcvFT db vr user fileId filePath = do liftIO $ acceptRcvFT_ db user fileId filePath Nothing =<< getCurrentTime getChatItemByFileId db vr user fileId @@ -1000,7 +1000,7 @@ getLocalCryptoFile db userId fileId sent = pure $ CryptoFile filePath fileCryptoArgs _ -> throwError $ SEFileNotFound fileId -updateDirectCIFileStatus :: forall d. MsgDirectionI d => DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> CIFileStatus d -> ExceptT StoreError IO AChatItem +updateDirectCIFileStatus :: forall d. MsgDirectionI d => DB.Connection -> VersionRangeChat -> User -> Int64 -> CIFileStatus d -> ExceptT StoreError IO AChatItem updateDirectCIFileStatus db vr user fileId fileStatus = do aci@(AChatItem cType d cInfo ci) <- getChatItemByFileId db vr user fileId case (cType, testEquality d $ msgDirection @d) of diff --git a/src/Simplex/Chat/Store/Groups.hs b/src/Simplex/Chat/Store/Groups.hs index cd62f17f4c..23796f95e7 100644 --- a/src/Simplex/Chat/Store/Groups.hs +++ b/src/Simplex/Chat/Store/Groups.hs @@ -145,7 +145,7 @@ import Simplex.Messaging.Agent.Protocol (ConnId, UserId) import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow) import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import qualified Simplex.Messaging.Crypto as C -import Simplex.Messaging.Crypto.Ratchet (PQSupport, pattern PQEncOff, pattern PQSupportOff) +import Simplex.Messaging.Crypto.Ratchet (pattern PQEncOff, pattern PQSupportOff) import Simplex.Messaging.Protocol (SubscriptionMode (..)) import Simplex.Messaging.Util (eitherToMaybe, ($>>=), (<$$>)) import Simplex.Messaging.Version @@ -157,9 +157,9 @@ type GroupMemberRow = ((Int64, Int64, MemberId, VersionChat, VersionChat, GroupM type MaybeGroupMemberRow = ((Maybe Int64, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe Bool, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId, Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe ImageData, Maybe ConnReqContact, Maybe LocalAlias, Maybe Preferences)) -toGroupInfo :: (PQSupport -> VersionRangeChat) -> Int64 -> GroupInfoRow -> GroupInfo +toGroupInfo :: VersionRangeChat -> Int64 -> GroupInfoRow -> GroupInfo toGroupInfo vr userContactId ((groupId, localDisplayName, displayName, fullName, description, image, hostConnCustomUserProfileId, enableNtfs_, sendRcpts, favorite, groupPreferences) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt, customData) :. userMemberRow) = - let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr PQSupportOff} + let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr} chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts, favorite} fullGroupPreferences = mergeGroupPreferences groupPreferences groupProfile = GroupProfile {displayName, fullName, description, image, groupPreferences} @@ -191,7 +191,7 @@ createGroupLink db User {userId} groupInfo@GroupInfo {groupId, localDisplayName} userContactLinkId <- insertedRowId db void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId initialChatVersion chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode PQSupportOff -getGroupLinkConnection :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupInfo -> ExceptT StoreError IO Connection +getGroupLinkConnection :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> ExceptT StoreError IO Connection getGroupLinkConnection db vr User {userId} groupInfo@GroupInfo {groupId} = ExceptT . firstRow (toConnection vr) (SEGroupLinkNotFound groupInfo) $ DB.query @@ -264,7 +264,7 @@ setGroupLinkMemberRole :: DB.Connection -> User -> Int64 -> GroupMemberRole -> I setGroupLinkMemberRole db User {userId} userContactLinkId memberRole = DB.execute db "UPDATE user_contact_links SET group_link_member_role = ? WHERE user_id = ? AND user_contact_link_id = ?" (memberRole, userId, userContactLinkId) -getGroupAndMember :: DB.Connection -> User -> Int64 -> (PQSupport -> VersionRangeChat) -> ExceptT StoreError IO (GroupInfo, GroupMember) +getGroupAndMember :: DB.Connection -> User -> Int64 -> VersionRangeChat -> ExceptT StoreError IO (GroupInfo, GroupMember) getGroupAndMember db User {userId, userContactId} groupMemberId vr = ExceptT . firstRow toGroupAndMember (SEInternalError "referenced group member not found") $ DB.query @@ -309,7 +309,7 @@ getGroupAndMember db User {userId, userContactId} groupMemberId vr = in (groupInfo, (member :: GroupMember) {activeConn = toMaybeConnection vr connRow}) -- | creates completely new group with a single member - the current user -createNewGroup :: DB.Connection -> (PQSupport -> VersionRangeChat) -> TVar ChaChaDRG -> User -> GroupProfile -> Maybe Profile -> ExceptT StoreError IO GroupInfo +createNewGroup :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupProfile -> Maybe Profile -> ExceptT StoreError IO GroupInfo createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = ExceptT $ do let GroupProfile {displayName, fullName, description, image, groupPreferences} = groupProfile fullGroupPreferences = mergeGroupPreferences groupPreferences @@ -352,7 +352,7 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = Exc } -- | creates a new group record for the group the current user was invited to, or returns an existing one -createGroupInvitation :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Contact -> GroupInvitation -> Maybe ProfileId -> ExceptT StoreError IO (GroupInfo, GroupMemberId) +createGroupInvitation :: DB.Connection -> VersionRangeChat -> User -> Contact -> GroupInvitation -> Maybe ProfileId -> ExceptT StoreError IO (GroupInfo, GroupMemberId) createGroupInvitation _ _ _ Contact {localDisplayName, activeConn = Nothing} _ _ = throwError $ SEContactNotReady localDisplayName createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activeConn = Just Connection {customUserProfileId, peerChatVRange}} GroupInvitation {fromMember, invitedMember, connRequest, groupProfile} incognitoProfileId = do liftIO getInvitationGroupId_ >>= \case @@ -397,7 +397,7 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ |] (profileId, localDisplayName, connRequest, customUserProfileId, userId, True, currentTs, currentTs, currentTs, currentTs) insertedRowId db - let hostVRange = const $ adjustedMemberVRange vr peerChatVRange + let hostVRange = adjustedMemberVRange vr peerChatVRange GroupMember {groupMemberId} <- createContactMemberInv_ db user groupId Nothing contact fromMember GCHostMember GSMemInvited IBUnknown Nothing currentTs hostVRange membership <- createContactMemberInv_ db user groupId (Just groupMemberId) user invitedMember GCUserMember GSMemInvited (IBContact contactId) incognitoProfileId currentTs vr let chatSettings = ChatSettings {enableNtfs = MFAll, sendRcpts = Nothing, favorite = False} @@ -419,9 +419,9 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ groupMemberId ) -adjustedMemberVRange :: (PQSupport -> VersionRangeChat) -> VersionRangeChat -> VersionRangeChat -adjustedMemberVRange getVR vr@(VersionRange minV maxV) = - let maxV' = min maxV (maxVersion $ getVR PQSupportOff) +adjustedMemberVRange :: VersionRangeChat -> VersionRangeChat -> VersionRangeChat +adjustedMemberVRange chatVR vr@(VersionRange minV maxV) = + let maxV' = min maxV (maxVersion chatVR) in fromMaybe vr $ safeVersionRange minV (max minV maxV') getHostMemberId_ :: DB.Connection -> User -> GroupId -> ExceptT StoreError IO GroupMemberId @@ -429,7 +429,7 @@ getHostMemberId_ db User {userId} groupId = ExceptT . firstRow fromOnly (SEHostMemberIdNotFound groupId) $ DB.query db "SELECT group_member_id FROM group_members WHERE user_id = ? AND group_id = ? AND member_category = ?" (userId, groupId, GCHostMember) -createContactMemberInv_ :: IsContact a => DB.Connection -> User -> GroupId -> Maybe GroupMemberId -> a -> MemberIdRole -> GroupMemberCategory -> GroupMemberStatus -> InvitedBy -> Maybe ProfileId -> UTCTime -> (PQSupport -> VersionRangeChat) -> ExceptT StoreError IO GroupMember +createContactMemberInv_ :: IsContact a => DB.Connection -> User -> GroupId -> Maybe GroupMemberId -> a -> MemberIdRole -> GroupMemberCategory -> GroupMemberStatus -> InvitedBy -> Maybe ProfileId -> UTCTime -> VersionRangeChat -> ExceptT StoreError IO GroupMember createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMemberId userOrContact MemberIdRole {memberId, memberRole} memberCategory memberStatus invitedBy incognitoProfileId createdAt vr = do incognitoProfile <- forM incognitoProfileId $ \profileId -> getProfileById db userId profileId (localDisplayName, memberProfile) <- case (incognitoProfile, incognitoProfileId) of @@ -457,7 +457,7 @@ createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMe memberChatVRange } where - memberChatVRange@(VersionRange minV maxV) = vr PQSupportOff + memberChatVRange@(VersionRange minV maxV) = vr insertMember_ :: IO ContactName insertMember_ = do let localDisplayName = localDisplayName' userOrContact @@ -493,7 +493,7 @@ createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMe ) pure $ Right incognitoLdn -createGroupInvitedViaLink :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Connection -> GroupLinkInvitation -> ExceptT StoreError IO (GroupInfo, GroupMember) +createGroupInvitedViaLink :: DB.Connection -> VersionRangeChat -> User -> Connection -> GroupLinkInvitation -> ExceptT StoreError IO (GroupInfo, GroupMember) createGroupInvitedViaLink db vr @@ -564,7 +564,7 @@ setGroupInvitationChatItemId db User {userId} groupId chatItemId = do -- TODO return the last connection that is ready, not any last connection -- requires updating connection status -getGroup :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupId -> ExceptT StoreError IO Group +getGroup :: DB.Connection -> VersionRangeChat -> User -> GroupId -> ExceptT StoreError IO Group getGroup db vr user groupId = do gInfo <- getGroupInfo db vr user groupId members <- liftIO $ getGroupMembers db vr user gInfo @@ -619,12 +619,12 @@ deleteGroupProfile_ db userId groupId = |] (userId, groupId) -getUserGroups :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> IO [Group] +getUserGroups :: DB.Connection -> VersionRangeChat -> User -> IO [Group] getUserGroups db vr user@User {userId} = do groupIds <- map fromOnly <$> DB.query db "SELECT group_id FROM groups WHERE user_id = ?" (Only userId) rights <$> mapM (runExceptT . getGroup db vr user) groupIds -getUserGroupDetails :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Maybe ContactId -> Maybe String -> IO [GroupInfo] +getUserGroupDetails :: DB.Connection -> VersionRangeChat -> User -> Maybe ContactId -> Maybe String -> IO [GroupInfo] getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ = map (toGroupInfo vr userContactId) <$> DB.query @@ -647,7 +647,7 @@ getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ = where search = fromMaybe "" search_ -getUserGroupsWithSummary :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Maybe ContactId -> Maybe String -> IO [(GroupInfo, GroupSummary)] +getUserGroupsWithSummary :: DB.Connection -> VersionRangeChat -> User -> Maybe ContactId -> Maybe String -> IO [(GroupInfo, GroupSummary)] getUserGroupsWithSummary db vr user _contactId_ search_ = getUserGroupDetails db vr user _contactId_ search_ >>= mapM (\g@GroupInfo {groupId} -> (g,) <$> getGroupSummary db user groupId) @@ -688,7 +688,7 @@ checkContactHasGroups :: DB.Connection -> User -> Contact -> IO (Maybe GroupId) checkContactHasGroups db User {userId} Contact {contactId} = maybeFirstRow fromOnly $ DB.query db "SELECT group_id FROM group_members WHERE user_id = ? AND contact_id = ? LIMIT 1" (userId, contactId) -getGroupInfoByName :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupName -> ExceptT StoreError IO GroupInfo +getGroupInfoByName :: DB.Connection -> VersionRangeChat -> User -> GroupName -> ExceptT StoreError IO GroupInfo getGroupInfoByName db vr user gName = do gId <- getGroupIdByName db user gName getGroupInfo db vr user gId @@ -712,7 +712,7 @@ groupMemberQuery = ) |] -getGroupMember :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupId -> GroupMemberId -> ExceptT StoreError IO GroupMember +getGroupMember :: DB.Connection -> VersionRangeChat -> User -> GroupId -> GroupMemberId -> ExceptT StoreError IO GroupMember getGroupMember db vr user@User {userId} groupId groupMemberId = ExceptT . firstRow (toContactMember vr user) (SEGroupMemberNotFound groupMemberId) $ DB.query @@ -720,7 +720,7 @@ getGroupMember db vr user@User {userId} groupId groupMemberId = (groupMemberQuery <> " WHERE m.group_id = ? AND m.group_member_id = ? AND m.user_id = ?") (userId, groupId, groupMemberId, userId) -getGroupMemberById :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMemberId -> ExceptT StoreError IO GroupMember +getGroupMemberById :: DB.Connection -> VersionRangeChat -> User -> GroupMemberId -> ExceptT StoreError IO GroupMember getGroupMemberById db vr user@User {userId} groupMemberId = ExceptT . firstRow (toContactMember vr user) (SEGroupMemberNotFound groupMemberId) $ DB.query @@ -728,7 +728,7 @@ getGroupMemberById db vr user@User {userId} groupMemberId = (groupMemberQuery <> " WHERE m.group_member_id = ? AND m.user_id = ?") (userId, groupMemberId, userId) -getGroupMemberByMemberId :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupInfo -> MemberId -> ExceptT StoreError IO GroupMember +getGroupMemberByMemberId :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> MemberId -> ExceptT StoreError IO GroupMember getGroupMemberByMemberId db vr user@User {userId} GroupInfo {groupId} memberId = ExceptT . firstRow (toContactMember vr user) (SEGroupMemberNotFoundByMemberId memberId) $ DB.query @@ -736,7 +736,7 @@ getGroupMemberByMemberId db vr user@User {userId} GroupInfo {groupId} memberId = (groupMemberQuery <> " WHERE m.group_id = ? AND m.member_id = ?") (userId, groupId, memberId) -getGroupMembers :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupInfo -> IO [GroupMember] +getGroupMembers :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> IO [GroupMember] getGroupMembers db vr user@User {userId, userContactId} GroupInfo {groupId} = do map (toContactMember vr user) <$> DB.query @@ -744,7 +744,7 @@ getGroupMembers db vr user@User {userId, userContactId} GroupInfo {groupId} = do (groupMemberQuery <> " WHERE m.group_id = ? AND m.user_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?)") (userId, groupId, userId, userContactId) -getGroupMembersForExpiration :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupInfo -> IO [GroupMember] +getGroupMembersForExpiration :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> IO [GroupMember] getGroupMembersForExpiration db vr user@User {userId, userContactId} GroupInfo {groupId} = do map (toContactMember vr user) <$> DB.query @@ -760,7 +760,7 @@ getGroupMembersForExpiration db vr user@User {userId, userContactId} GroupInfo { ) (userId, groupId, userId, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted, GSMemUnknown) -toContactMember :: (PQSupport -> VersionRangeChat) -> User -> (GroupMemberRow :. MaybeConnectionRow) -> GroupMember +toContactMember :: VersionRangeChat -> User -> (GroupMemberRow :. MaybeConnectionRow) -> GroupMember toContactMember vr User {userContactId} (memberRow :. connRow) = (toGroupMember userContactId memberRow) {activeConn = toMaybeConnection vr connRow} @@ -778,7 +778,7 @@ getGroupCurrentMembersCount db User {userId} GroupInfo {groupId} = do (groupId, userId) pure $ length $ filter memberCurrent' statuses -getGroupInvitation :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupId -> ExceptT StoreError IO ReceivedGroupInvitation +getGroupInvitation :: DB.Connection -> VersionRangeChat -> User -> GroupId -> ExceptT StoreError IO ReceivedGroupInvitation getGroupInvitation db vr user groupId = getConnRec_ user >>= \case Just connRequest -> do @@ -913,7 +913,7 @@ createAcceptedMemberConnection Connection {connId} <- createConnection_ db userId ConnMember (Just groupMemberId) agentConnId chatV cReqChatVRange Nothing (Just userContactLinkId) Nothing 0 createdAt subMode PQSupportOff setCommandConnId db user cmdId connId -getContactViaMember :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> ExceptT StoreError IO Contact +getContactViaMember :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> ExceptT StoreError IO Contact getContactViaMember db vr user@User {userId} GroupMember {groupMemberId} = do contactId <- ExceptT $ @@ -1174,7 +1174,7 @@ getIntroduction db reMember toMember = ExceptT $ do in Right GroupMemberIntro {introId, reMember, toMember, introStatus, introInvitation} toIntro _ = Left SEIntroNotFound -getForwardIntroducedMembers :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> Bool -> IO [GroupMember] +getForwardIntroducedMembers :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> Bool -> IO [GroupMember] getForwardIntroducedMembers db vr user invitee highlyAvailable = do memberIds <- map fromOnly <$> query filter memberCurrent . rights <$> mapM (runExceptT . getGroupMemberById db vr user) memberIds @@ -1194,7 +1194,7 @@ getForwardIntroducedMembers db vr user invitee highlyAvailable = do WHERE to_group_member_id = ? AND intro_status NOT IN (?,?,?) |] -getForwardInvitedMembers :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> Bool -> IO [GroupMember] +getForwardInvitedMembers :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> Bool -> IO [GroupMember] getForwardInvitedMembers db vr user forwardMember highlyAvailable = do memberIds <- map fromOnly <$> query filter memberCurrent . rights <$> mapM (runExceptT . getGroupMemberById db vr user) memberIds @@ -1288,7 +1288,7 @@ createMemberConnection_ :: DB.Connection -> UserId -> Int64 -> ConnId -> Version createMemberConnection_ db userId groupMemberId agentConnId chatV peerChatVRange viaContact connLevel currentTs subMode = createConnection_ db userId ConnMember (Just groupMemberId) agentConnId chatV peerChatVRange viaContact Nothing Nothing connLevel currentTs subMode PQSupportOff -getViaGroupMember :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Contact -> IO (Maybe (GroupInfo, GroupMember)) +getViaGroupMember :: DB.Connection -> VersionRangeChat -> User -> Contact -> IO (Maybe (GroupInfo, GroupMember)) getViaGroupMember db vr User {userId, userContactId} Contact {contactId} = maybeFirstRow toGroupAndMember $ DB.query @@ -1333,7 +1333,7 @@ getViaGroupMember db vr User {userId, userContactId} Contact {contactId} = member = toGroupMember userContactId memberRow in (groupInfo, (member :: GroupMember) {activeConn = toMaybeConnection vr connRow}) -getViaGroupContact :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> IO (Maybe Contact) +getViaGroupContact :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> IO (Maybe Contact) getViaGroupContact db vr user@User {userId} GroupMember {groupMemberId} = do contactId_ <- maybeFirstRow fromOnly $ @@ -1384,7 +1384,7 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName, (ldn, currentTs, userId, groupId) safeDeleteLDN db user localDisplayName -getGroupInfo :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ExceptT StoreError IO GroupInfo +getGroupInfo :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO GroupInfo getGroupInfo db vr User {userId, userContactId} groupId = ExceptT . firstRow (toGroupInfo vr userContactId) (SEGroupNotFound groupId) $ DB.query @@ -1407,7 +1407,7 @@ getGroupInfo db vr User {userId, userContactId} groupId = |] (groupId, userId, userContactId) -getGroupInfoByUserContactLinkConnReq :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo) +getGroupInfoByUserContactLinkConnReq :: DB.Connection -> VersionRangeChat -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo) getGroupInfoByUserContactLinkConnReq db vr user@User {userId} (cReqSchema1, cReqSchema2) = do groupId_ <- maybeFirstRow fromOnly $ @@ -1421,7 +1421,7 @@ getGroupInfoByUserContactLinkConnReq db vr user@User {userId} (cReqSchema1, cReq (userId, cReqSchema1, cReqSchema2) maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db vr user) groupId_ -getGroupInfoByGroupLinkHash :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe GroupInfo) +getGroupInfoByGroupLinkHash :: DB.Connection -> VersionRangeChat -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe GroupInfo) getGroupInfoByGroupLinkHash db vr user@User {userId, userContactId} (groupLinkHash1, groupLinkHash2) = do groupId_ <- maybeFirstRow fromOnly $ @@ -1448,7 +1448,7 @@ getGroupMemberIdByName db User {userId} groupId groupMemberName = ExceptT . firstRow fromOnly (SEGroupMemberNameNotFound groupId groupMemberName) $ DB.query db "SELECT group_member_id FROM group_members WHERE user_id = ? AND group_id = ? AND local_display_name = ?" (userId, groupId, groupMemberName) -getActiveMembersByName :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ContactName -> ExceptT StoreError IO [(GroupInfo, GroupMember)] +getActiveMembersByName :: DB.Connection -> VersionRangeChat -> User -> ContactName -> ExceptT StoreError IO [(GroupInfo, GroupMember)] getActiveMembersByName db vr user@User {userId} groupMemberName = do groupMemberIds :: [(GroupId, GroupMemberId)] <- liftIO $ @@ -1469,7 +1469,7 @@ getActiveMembersByName db vr user@User {userId} groupMemberName = do where ts GroupInfo {chatTs, updatedAt} = fromMaybe updatedAt chatTs -getMatchingContacts :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Contact -> IO [Contact] +getMatchingContacts :: DB.Connection -> VersionRangeChat -> User -> Contact -> IO [Contact] getMatchingContacts db vr user@User {userId} Contact {contactId, profile = LocalProfile {displayName, fullName, image}} = do contactIds <- map fromOnly <$> case image of @@ -1489,7 +1489,7 @@ getMatchingContacts db vr user@User {userId} Contact {contactId, profile = Local AND p.display_name = ? AND p.full_name = ? |] -getMatchingMembers :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Contact -> IO [GroupMember] +getMatchingMembers :: DB.Connection -> VersionRangeChat -> User -> Contact -> IO [GroupMember] getMatchingMembers db vr user@User {userId} Contact {profile = LocalProfile {displayName, fullName, image}} = do memberIds <- map fromOnly <$> case image of @@ -1508,7 +1508,7 @@ getMatchingMembers db vr user@User {userId} Contact {profile = LocalProfile {dis AND p.display_name = ? AND p.full_name = ? |] -getMatchingMemberContacts :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> IO [Contact] +getMatchingMemberContacts :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> IO [Contact] getMatchingMemberContacts _ _ _ GroupMember {memberContactId = Just _} = pure [] getMatchingMemberContacts db vr user@User {userId} GroupMember {memberProfile = LocalProfile {displayName, fullName, image}} = do contactIds <- @@ -1547,7 +1547,7 @@ createSentProbeHash db userId probeId to = do "INSERT INTO sent_probe_hashes (sent_probe_id, contact_id, group_member_id, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?)" (probeId, ctId, gmId, userId, currentTs, currentTs) -matchReceivedProbe :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ContactOrMember -> Probe -> IO [ContactOrMember] +matchReceivedProbe :: DB.Connection -> VersionRangeChat -> User -> ContactOrMember -> Probe -> IO [ContactOrMember] matchReceivedProbe db vr user@User {userId} from (Probe probe) = do let probeHash = C.sha256Hash probe cgmIds <- @@ -1579,7 +1579,7 @@ matchReceivedProbe db vr user@User {userId} from (Probe probe) = do (x : _) -> [x] ctIds' <> memIds -matchReceivedProbeHash :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ContactOrMember -> ProbeHash -> IO (Maybe (ContactOrMember, Probe)) +matchReceivedProbeHash :: DB.Connection -> VersionRangeChat -> User -> ContactOrMember -> ProbeHash -> IO (Maybe (ContactOrMember, Probe)) matchReceivedProbeHash db vr user@User {userId} from (ProbeHash probeHash) = do probeIds <- maybeFirstRow id $ @@ -1602,7 +1602,7 @@ matchReceivedProbeHash db vr user@User {userId} from (ProbeHash probeHash) = do (ctId, gmId, probeHash, userId, currentTs, currentTs) pure probeIds $>>= \(Only probe :. cgmIds) -> (,Probe probe) <$$> getContactOrMember_ db vr user cgmIds -matchSentProbe :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ContactOrMember -> Probe -> IO (Maybe ContactOrMember) +matchSentProbe :: DB.Connection -> VersionRangeChat -> User -> ContactOrMember -> Probe -> IO (Maybe ContactOrMember) matchSentProbe db vr user@User {userId} _from (Probe probe) = do cgmIds $>>= getContactOrMember_ db vr user where @@ -1623,7 +1623,7 @@ matchSentProbe db vr user@User {userId} _from (Probe probe) = do |] (userId, probe, ctId, gmId) -getContactOrMember_ :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> (Maybe ContactId, Maybe GroupId, Maybe GroupMemberId) -> IO (Maybe ContactOrMember) +getContactOrMember_ :: DB.Connection -> VersionRangeChat -> User -> (Maybe ContactId, Maybe GroupId, Maybe GroupMemberId) -> IO (Maybe ContactOrMember) getContactOrMember_ db vr user ids = fmap eitherToMaybe . runExceptT $ case ids of (Just ctId, _, _) -> COMContact <$> getContact db vr user ctId @@ -1631,7 +1631,7 @@ getContactOrMember_ db vr user ids = _ -> throwError $ SEInternalError "" -- if requested merge direction is overruled (toFromContacts), keepLDN is kept -mergeContactRecords :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Contact -> Contact -> ExceptT StoreError IO Contact +mergeContactRecords :: DB.Connection -> VersionRangeChat -> User -> Contact -> Contact -> ExceptT StoreError IO Contact mergeContactRecords db vr user@User {userId} to@Contact {localDisplayName = keepLDN} from = do let (toCt, fromCt) = toFromContacts to from Contact {contactId = toContactId, localDisplayName = toLDN} = toCt @@ -1721,7 +1721,7 @@ associateMemberWithContactRecord when (memProfileId /= profileId) $ deleteUnusedProfile_ db userId memProfileId when (memLDN /= localDisplayName) $ deleteUnusedDisplayName_ db userId memLDN -associateContactWithMemberRecord :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> Contact -> ExceptT StoreError IO Contact +associateContactWithMemberRecord :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> Contact -> ExceptT StoreError IO Contact associateContactWithMemberRecord db vr @@ -1958,7 +1958,7 @@ createMemberContact mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito ctConn pure Contact {contactId, localDisplayName, profile = memberProfile, activeConn = Just ctConn, viaGroup = Nothing, contactUsed = True, contactStatus = CSActive, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, contactGroupMemberId = Just groupMemberId, contactGrpInvSent = False, customData = Nothing} -getMemberContact :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ContactId -> ExceptT StoreError IO (GroupInfo, GroupMember, Contact, ConnReqInvitation) +getMemberContact :: DB.Connection -> VersionRangeChat -> User -> ContactId -> ExceptT StoreError IO (GroupInfo, GroupMember, Contact, ConnReqInvitation) getMemberContact db vr user contactId = do ct <- getContact db vr user contactId let Contact {contactGroupMemberId, activeConn} = ct @@ -2138,7 +2138,7 @@ setXGrpLinkMemReceived db mId xGrpLinkMemReceived = do "UPDATE group_members SET xgrplinkmem_received = ?, updated_at = ? WHERE group_member_id = ?" (xGrpLinkMemReceived, currentTs, mId) -createNewUnknownGroupMember :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupInfo -> MemberId -> Text -> ExceptT StoreError IO GroupMember +createNewUnknownGroupMember :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> MemberId -> Text -> ExceptT StoreError IO GroupMember createNewUnknownGroupMember db vr user@User {userId, userContactId} GroupInfo {groupId} memberId memberName = do currentTs <- liftIO getCurrentTime let memberProfile = profileFromName memberName @@ -2160,9 +2160,9 @@ createNewUnknownGroupMember db vr user@User {userId, userContactId} GroupInfo {g insertedRowId db getGroupMemberById db vr user groupMemberId where - VersionRange minV maxV = vr PQSupportOff + VersionRange minV maxV = vr -updateUnknownMemberAnnounced :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupMember -> GroupMember -> MemberInfo -> ExceptT StoreError IO GroupMember +updateUnknownMemberAnnounced :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> GroupMember -> MemberInfo -> ExceptT StoreError IO GroupMember updateUnknownMemberAnnounced db vr user@User {userId} invitingMember unknownMember@GroupMember {groupMemberId, memberChatVRange} MemberInfo {memberRole, v, profile} = do _ <- updateMemberProfile db user unknownMember profile currentTs <- liftIO getCurrentTime diff --git a/src/Simplex/Chat/Store/Messages.hs b/src/Simplex/Chat/Store/Messages.hs index 2b31a215da..69157eba0a 100644 --- a/src/Simplex/Chat/Store/Messages.hs +++ b/src/Simplex/Chat/Store/Messages.hs @@ -484,7 +484,7 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe ciQuoteGroup [] = ciQuote Nothing $ CIQGroupRcv Nothing ciQuoteGroup ((Only itemId :. memberRow) : _) = ciQuote itemId . CIQGroupRcv . Just $ toGroupMember userContactId memberRow -getChatPreviews :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Bool -> PaginationByTime -> ChatListQuery -> IO [Either StoreError AChat] +getChatPreviews :: DB.Connection -> VersionRangeChat -> User -> Bool -> PaginationByTime -> ChatListQuery -> IO [Either StoreError AChat] getChatPreviews db vr user withPCC pagination query = do directChats <- findDirectChatPreviews_ db user pagination query groupChats <- findGroupChatPreviews_ db user pagination query @@ -621,7 +621,7 @@ findDirectChatPreviews_ db User {userId} pagination clq = ) ([":user_id" := userId, ":rcv_new" := CISRcvNew, ":search" := search] <> pagParams) -getDirectChatPreview_ :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ChatPreviewData 'CTDirect -> ExceptT StoreError IO AChat +getDirectChatPreview_ :: DB.Connection -> VersionRangeChat -> User -> ChatPreviewData 'CTDirect -> ExceptT StoreError IO AChat getDirectChatPreview_ db vr user (DirectChatPD _ contactId lastItemId_ stats) = do contact <- getContact db vr user contactId lastItem <- case lastItemId_ of @@ -717,7 +717,7 @@ findGroupChatPreviews_ db User {userId} pagination clq = ) ([":user_id" := userId, ":rcv_new" := CISRcvNew, ":search" := search] <> pagParams) -getGroupChatPreview_ :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ChatPreviewData 'CTGroup -> ExceptT StoreError IO AChat +getGroupChatPreview_ :: DB.Connection -> VersionRangeChat -> User -> ChatPreviewData 'CTGroup -> ExceptT StoreError IO AChat getGroupChatPreview_ db vr user (GroupChatPD _ groupId lastItemId_ stats) = do groupInfo <- getGroupInfo db vr user groupId lastItem <- case lastItemId_ of @@ -923,7 +923,7 @@ getContactConnectionChatPreviews_ db User {userId} pagination clq = case clq of aChat = AChat SCTContactConnection $ Chat (ContactConnection conn) [] stats in ACPD SCTContactConnection $ ContactConnectionPD updatedAt aChat -getDirectChat :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTDirect) +getDirectChat :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTDirect) getDirectChat db vr user contactId pagination search_ = do let search = fromMaybe "" search_ ct <- getContact db vr user contactId @@ -1043,7 +1043,7 @@ getDirectChatBefore_ db user@User {userId} ct@Contact {contactId} beforeChatItem |] (userId, contactId, search, beforeChatItemId, count) -getGroupChat :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTGroup) +getGroupChat :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTGroup) getGroupChat db vr user groupId pagination search_ = do let search = fromMaybe "" search_ g <- getGroupInfo db vr user groupId @@ -1526,7 +1526,7 @@ toGroupChatItem currentTs userContactId (((itemId, itemTs, AMsgDirection msgDir, ciTimed :: Maybe CITimed ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt} -getAllChatItems :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ChatPagination -> Maybe String -> ExceptT StoreError IO [AChatItem] +getAllChatItems :: DB.Connection -> VersionRangeChat -> User -> ChatPagination -> Maybe String -> ExceptT StoreError IO [AChatItem] getAllChatItems db vr user@User {userId} pagination search_ = do itemRefs <- rights . map toChatItemRef <$> case pagination of @@ -2179,7 +2179,7 @@ deleteLocalChatItem db User {userId} NoteFolder {noteFolderId} ci = do |] (userId, noteFolderId, itemId) -getChatItemByFileId :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ExceptT StoreError IO AChatItem +getChatItemByFileId :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO AChatItem getChatItemByFileId db vr user@User {userId} fileId = do (chatRef, itemId) <- ExceptT . firstRow' toChatItemRef (SEChatItemNotFoundByFileId fileId) $ @@ -2195,13 +2195,13 @@ getChatItemByFileId db vr user@User {userId} fileId = do (userId, fileId) getAChatItem db vr user chatRef itemId -lookupChatItemByFileId :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> Int64 -> ExceptT StoreError IO (Maybe AChatItem) +lookupChatItemByFileId :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO (Maybe AChatItem) lookupChatItemByFileId db vr user fileId = do fmap Just (getChatItemByFileId db vr user fileId) `catchError` \case SEChatItemNotFoundByFileId {} -> pure Nothing e -> throwError e -getChatItemByGroupId :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupId -> ExceptT StoreError IO AChatItem +getChatItemByGroupId :: DB.Connection -> VersionRangeChat -> User -> GroupId -> ExceptT StoreError IO AChatItem getChatItemByGroupId db vr user@User {userId} groupId = do (chatRef, itemId) <- ExceptT . firstRow' toChatItemRef (SEChatItemNotFoundByGroupId groupId) $ @@ -2227,7 +2227,7 @@ getChatRefViaItemId db User {userId} itemId = do (Nothing, Just groupId) -> Right $ ChatRef CTGroup groupId (_, _) -> Left $ SEBadChatItem itemId Nothing -getAChatItem :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ChatRef -> ChatItemId -> ExceptT StoreError IO AChatItem +getAChatItem :: DB.Connection -> VersionRangeChat -> User -> ChatRef -> ChatItemId -> ExceptT StoreError IO AChatItem getAChatItem db vr user chatRef itemId = case chatRef of ChatRef CTDirect contactId -> do ct <- getContact db vr user contactId @@ -2476,7 +2476,7 @@ createCIModeration db GroupInfo {groupId} moderatorMember itemMemberId itemShare |] (groupId, groupMemberId' moderatorMember, itemMemberId, itemSharedMId, msgId, moderatedAtTs) -getCIModeration :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> GroupInfo -> MemberId -> Maybe SharedMsgId -> IO (Maybe CIModeration) +getCIModeration :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> MemberId -> Maybe SharedMsgId -> IO (Maybe CIModeration) getCIModeration _ _ _ _ _ Nothing = pure Nothing getCIModeration db vr user GroupInfo {groupId} itemMemberId (Just sharedMsgId) = do r_ <- diff --git a/src/Simplex/Chat/Store/Profiles.hs b/src/Simplex/Chat/Store/Profiles.hs index 0e2445572c..312907fb16 100644 --- a/src/Simplex/Chat/Store/Profiles.hs +++ b/src/Simplex/Chat/Store/Profiles.hs @@ -328,7 +328,7 @@ createUserContactLink db User {userId} agentConnId cReq subMode = userContactLinkId <- insertedRowId db void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId initialChatVersion chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode CR.PQSupportOff -getUserAddressConnections :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> ExceptT StoreError IO [Connection] +getUserAddressConnections :: DB.Connection -> VersionRangeChat -> User -> ExceptT StoreError IO [Connection] getUserAddressConnections db vr User {userId} = do cs <- liftIO getUserAddressConnections_ if null cs then throwError SEUserContactLinkNotFound else pure cs @@ -349,7 +349,7 @@ getUserAddressConnections db vr User {userId} = do |] (userId, userId) -getUserContactLinks :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> IO [(Connection, UserContact)] +getUserContactLinks :: DB.Connection -> VersionRangeChat -> User -> IO [(Connection, UserContact)] getUserContactLinks db vr User {userId} = map toUserContactConnection <$> DB.query @@ -475,7 +475,7 @@ getUserContactLinkByConnReq db User {userId} (cReqSchema1, cReqSchema2) = |] (userId, cReqSchema1, cReqSchema2) -getContactWithoutConnViaAddress :: DB.Connection -> (PQSupport -> VersionRangeChat) -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe Contact) +getContactWithoutConnViaAddress :: DB.Connection -> VersionRangeChat -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe Contact) getContactWithoutConnViaAddress db vr user@User {userId} (cReqSchema1, cReqSchema2) = do ctId_ <- maybeFirstRow fromOnly $ diff --git a/src/Simplex/Chat/Store/Shared.hs b/src/Simplex/Chat/Store/Shared.hs index ef7cda4802..fe86b0c2df 100644 --- a/src/Simplex/Chat/Store/Shared.hs +++ b/src/Simplex/Chat/Store/Shared.hs @@ -166,12 +166,12 @@ type ConnectionRow = (Int64, ConnId, Int, Maybe Int64, Maybe Int64, Bool, Maybe type MaybeConnectionRow = (Maybe Int64, Maybe ConnId, Maybe Int, Maybe Int64, Maybe Int64, Maybe Bool, Maybe GroupLinkId, Maybe Int64, Maybe ConnStatus, Maybe ConnType, Maybe Bool, Maybe LocalAlias) :. EntityIdsRow :. (Maybe UTCTime, Maybe Text, Maybe UTCTime, Maybe PQSupport, Maybe PQEncryption, Maybe PQEncryption, Maybe PQEncryption, Maybe Int, Maybe VersionChat, Maybe VersionChat, Maybe VersionChat) -toConnection :: (PQSupport -> VersionRangeChat) -> ConnectionRow -> Connection +toConnection :: VersionRangeChat -> ConnectionRow -> Connection toConnection vr ((connId, acId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, contactConnInitiated, localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (createdAt, code_, verifiedAt_, pqSupport, pqEncryption, pqSndEnabled, pqRcvEnabled, authErrCounter, chatV, minVer, maxVer)) = Connection { connId, agentConnId = AgentConnId acId, - connChatVersion = fromMaybe (vr pqSupport `peerConnChatVersion` peerChatVRange) chatV, + connChatVersion = fromMaybe (vr `peerConnChatVersion` peerChatVRange) chatV, peerChatVRange = peerChatVRange, connLevel, viaContact, @@ -201,7 +201,7 @@ toConnection vr ((connId, acId, connLevel, viaContact, viaUserContactLink, viaGr entityId_ ConnSndFile = sndFileId entityId_ ConnUserContact = userContactLinkId -toMaybeConnection :: (PQSupport -> VersionRangeChat) -> MaybeConnectionRow -> Maybe Connection +toMaybeConnection :: VersionRangeChat -> MaybeConnectionRow -> Maybe Connection toMaybeConnection vr ((Just connId, Just agentConnId, Just connLevel, viaContact, viaUserContactLink, Just viaGroupLink, groupLinkId, customUserProfileId, Just connStatus, Just connType, Just contactConnInitiated, Just localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (Just createdAt, code_, verifiedAt_, Just pqSupport, Just pqEncryption, pqSndEnabled_, pqRcvEnabled_, Just authErrCounter, connChatVersion, Just minVer, Just maxVer)) = Just $ toConnection vr ((connId, agentConnId, connLevel, viaContact, viaUserContactLink, viaGroupLink, groupLinkId, customUserProfileId, connStatus, connType, contactConnInitiated, localAlias) :. (contactId, groupMemberId, sndFileId, rcvFileId, userContactLinkId) :. (createdAt, code_, verifiedAt_, pqSupport, pqEncryption, pqSndEnabled_, pqRcvEnabled_, authErrCounter, connChatVersion, minVer, maxVer)) toMaybeConnection _ _ = Nothing @@ -382,7 +382,7 @@ deleteUnusedIncognitoProfileById_ db User {userId} profileId = type ContactRow = (ContactId, ProfileId, ContactName, Maybe Int64, ContactName, Text, Maybe ImageData, Maybe ConnReqContact, LocalAlias, Bool, ContactStatus) :. (Maybe MsgFilter, Maybe Bool, Bool, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime, Maybe GroupMemberId, Bool, Maybe CustomData) -toContact :: (PQSupport -> VersionRangeChat) -> User -> ContactRow :. MaybeConnectionRow -> Contact +toContact :: VersionRangeChat -> User -> ContactRow :. MaybeConnectionRow -> Contact toContact vr user (((contactId, profileId, localDisplayName, viaGroup, displayName, fullName, image, contactLink, localAlias, contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, favorite, preferences, userPreferences, createdAt, updatedAt, chatTs, contactGroupMemberId, contactGrpInvSent, customData)) :. connRow) = let profile = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias} activeConn = toMaybeConnection vr connRow diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 89d0136478..a10acc884a 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -342,7 +342,6 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe CRRemoteCtrlConnected RemoteCtrlInfo {remoteCtrlId = rcId, ctrlDeviceName} -> ["remote controller " <> sShow rcId <> " session started with " <> plain ctrlDeviceName] CRRemoteCtrlStopped {} -> ["remote controller stopped"] - CRContactPQAllowed u c (CR.PQEncryption pqOn) -> ttyUser u [ttyContact' c <> ": enable " <> (if pqOn then "quantum resistant" else "standard") <> " end-to-end encryption"] CRContactPQEnabled u c (CR.PQEncryption pqOn) -> ttyUser u [ttyContact' c <> ": " <> (if pqOn then "quantum resistant" else "standard") <> " end-to-end encryption enabled"] CRSQLResult rows -> map plain rows CRSlowSQLQueries {chatQueries, agentQueries} -> diff --git a/tests/Bots/DirectoryTests.hs b/tests/Bots/DirectoryTests.hs index fbabccfb54..7cd775c5ee 100644 --- a/tests/Bots/DirectoryTests.hs +++ b/tests/Bots/DirectoryTests.hs @@ -809,7 +809,9 @@ testRestoreDirectory tmp = do groupFoundN 3 bob "privacy" groupFound bob "security" groupFoundN 3 cath "privacy" - groupFound cath "security" + cath #> "@SimpleX-Directory security" + cath <## "SimpleX-Directory: quantum resistant end-to-end encryption enabled" + groupFoundN' 2 cath "security" listGroups :: HasCallStack => TestCC -> TestCC -> TestCC -> IO () listGroups superUser bob cath = do @@ -1055,6 +1057,10 @@ groupFound = groupFoundN 2 groupFoundN :: Int -> TestCC -> String -> IO () groupFoundN count u name = do u #> ("@SimpleX-Directory " <> name) + groupFoundN' count u name + +groupFoundN' :: Int -> TestCC -> String -> IO () +groupFoundN' count u name = do u <# ("SimpleX-Directory> > " <> name) u <## " Found 1 group(s)." u <#. ("SimpleX-Directory> " <> name) diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index a827236a15..4897a3b3d3 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -44,7 +44,7 @@ import Simplex.Messaging.Agent.RetryInterval import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), closeSQLiteStore) import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import Simplex.Messaging.Client (ProtocolClientConfig (..), defaultNetworkConfig) -import Simplex.Messaging.Crypto.Ratchet (supportedE2EEncryptVRange, pattern PQSupportOff) +import Simplex.Messaging.Crypto.Ratchet (supportedE2EEncryptVRange) import qualified Simplex.Messaging.Crypto.Ratchet as CR import Simplex.Messaging.Server (runSMPServerBlocking) import Simplex.Messaging.Server.Env.STM @@ -146,8 +146,8 @@ testAgentCfgVPrev :: AgentConfig testAgentCfgVPrev = testAgentCfg { smpClientVRange = prevRange $ smpClientVRange testAgentCfg, - smpAgentVRange = \_ -> prevRange $ supportedSMPAgentVRange PQSupportOff, - e2eEncryptVRange = \_ -> prevRange $ supportedE2EEncryptVRange PQSupportOff, + smpAgentVRange = prevRange supportedSMPAgentVRange, + e2eEncryptVRange = prevRange supportedE2EEncryptVRange, smpCfg = (smpCfg testAgentCfg) {serverVRange = prevRange $ serverVRange $ smpCfg testAgentCfg} } @@ -155,8 +155,8 @@ testAgentCfgVNext :: AgentConfig testAgentCfgVNext = testAgentCfg { smpClientVRange = nextRange $ smpClientVRange testAgentCfg, - smpAgentVRange = \_ -> mkVersionRange duplexHandshakeSMPAgentVersion $ max pqdrSMPAgentVersion currentSMPAgentVersion, - e2eEncryptVRange = \_ -> mkVersionRange CR.kdfX3DHE2EEncryptVersion $ max CR.pqRatchetE2EEncryptVersion CR.currentE2EEncryptVersion, + smpAgentVRange = mkVersionRange duplexHandshakeSMPAgentVersion $ max pqdrSMPAgentVersion currentSMPAgentVersion, + e2eEncryptVRange = mkVersionRange CR.kdfX3DHE2EEncryptVersion $ max CR.pqRatchetE2EEncryptVersion CR.currentE2EEncryptVersion, smpCfg = (smpCfg testAgentCfg) {serverVRange = nextRange $ serverVRange $ smpCfg testAgentCfg} } @@ -164,29 +164,29 @@ testAgentCfgV1 :: AgentConfig testAgentCfgV1 = testAgentCfg { smpClientVRange = v1Range, - smpAgentVRange = \_ -> versionToRange duplexHandshakeSMPAgentVersion, - e2eEncryptVRange = \_ -> versionToRange CR.kdfX3DHE2EEncryptVersion, + smpAgentVRange = versionToRange duplexHandshakeSMPAgentVersion, + e2eEncryptVRange = versionToRange CR.kdfX3DHE2EEncryptVersion, smpCfg = (smpCfg testAgentCfg) {serverVRange = versionToRange batchCmdsSMPVersion} } testCfgVPrev :: ChatConfig testCfgVPrev = testCfg - { chatVRange = \_ -> prevRange $ chatVRange testCfg PQSupportOff, + { chatVRange = prevRange $ chatVRange testCfg, agentConfig = testAgentCfgVPrev } testCfgVNext :: ChatConfig testCfgVNext = testCfg - { chatVRange = \_ -> mkVersionRange initialChatVersion $ max pqEncryptionCompressionVersion currentChatVersion, + { chatVRange = mkVersionRange initialChatVersion $ max pqEncryptionCompressionVersion currentChatVersion, agentConfig = testAgentCfgVNext } testCfgV1 :: ChatConfig testCfgV1 = testCfg - { chatVRange = const v1Range, + { chatVRange = v1Range, agentConfig = testAgentCfgV1 } @@ -210,7 +210,7 @@ testCfgCreateGroupDirect = mkCfgCreateGroupDirect testCfg mkCfgCreateGroupDirect :: ChatConfig -> ChatConfig -mkCfgCreateGroupDirect cfg = cfg {chatVRange = const groupCreateDirectVRange} +mkCfgCreateGroupDirect cfg = cfg {chatVRange = groupCreateDirectVRange} groupCreateDirectVRange :: VersionRangeChat groupCreateDirectVRange = mkVersionRange (VersionChat 1) (VersionChat 1) @@ -220,7 +220,7 @@ testCfgGroupLinkViaContact = mkCfgGroupLinkViaContact testCfg mkCfgGroupLinkViaContact :: ChatConfig -> ChatConfig -mkCfgGroupLinkViaContact cfg = cfg {chatVRange = const groupLinkViaContactVRange} +mkCfgGroupLinkViaContact cfg = cfg {chatVRange = groupLinkViaContactVRange} groupLinkViaContactVRange :: VersionRangeChat groupLinkViaContactVRange = mkVersionRange (VersionChat 1) (VersionChat 2) diff --git a/tests/ChatTests/Direct.hs b/tests/ChatTests/Direct.hs index 4b0389ab90..82eb4def39 100644 --- a/tests/ChatTests/Direct.hs +++ b/tests/ChatTests/Direct.hs @@ -10,7 +10,7 @@ import ChatClient import ChatTests.Utils import Control.Concurrent (threadDelay) import Control.Concurrent.Async (concurrently_) -import Control.Monad (forM_, when) +import Control.Monad (forM_) import Data.Aeson (ToJSON) import qualified Data.Aeson as J import qualified Data.ByteString.Char8 as B @@ -21,11 +21,10 @@ import qualified Simplex.Chat.AppSettings as AS import Simplex.Chat.Call import Simplex.Chat.Controller (ChatConfig (..)) import Simplex.Chat.Options (ChatOpts (..)) -import Simplex.Chat.Protocol (currentChatVersion, pqEncryptionCompressionVersion, supportedChatVRange) +import Simplex.Chat.Protocol (supportedChatVRange) import Simplex.Chat.Store (agentStoreFile, chatStoreFile) -import Simplex.Chat.Types (VersionRangeChat, authErrDisableCount, sameVerificationCode, verificationCode, VersionChat, pattern VersionChat) +import Simplex.Chat.Types (VersionRangeChat, authErrDisableCount, sameVerificationCode, verificationCode, pattern VersionChat) import qualified Simplex.Messaging.Crypto as C -import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), pattern PQEncOff, pattern PQEncOn, pattern PQSupportOff, pattern PQSupportOn) import Simplex.Messaging.Util (safeDecodeUtf8) import Simplex.Messaging.Version import System.Directory (copyFile, doesDirectoryExist, doesFileExist) @@ -47,6 +46,7 @@ chatDirectTests = do it "direct timed message" testDirectTimedMessage it "repeat AUTH errors disable contact" testRepeatAuthErrorsDisableContact it "should send multiline message" testMultilineMessage + it "send large message" testLargeMessage describe "duplicate contacts" $ do it "duplicate contacts are separate (contacts don't merge)" testDuplicateContactsSeparate it "new contact is separate with multiple duplicate contacts (contacts don't merge)" testDuplicateContactsMultipleSeparate @@ -116,25 +116,18 @@ chatDirectTests = do it "should send delivery receipts depending on configuration" testConfigureDeliveryReceipts describe "negotiate connection peer chat protocol version range" $ do describe "peer version range correctly set for new connection via invitation" $ do - testInvVRange (supportedChatVRange PQSupportOff) (supportedChatVRange PQSupportOff) - testInvVRange (supportedChatVRange PQSupportOff) vr11 - testInvVRange vr11 (supportedChatVRange PQSupportOff) + testInvVRange supportedChatVRange supportedChatVRange + testInvVRange supportedChatVRange vr11 + testInvVRange vr11 supportedChatVRange testInvVRange vr11 vr11 describe "peer version range correctly set for new connection via contact request" $ do - testReqVRange (supportedChatVRange PQSupportOff) (supportedChatVRange PQSupportOff) - testReqVRange (supportedChatVRange PQSupportOff) vr11 - testReqVRange vr11 (supportedChatVRange PQSupportOff) + testReqVRange supportedChatVRange supportedChatVRange + testReqVRange supportedChatVRange vr11 + testReqVRange vr11 supportedChatVRange testReqVRange vr11 vr11 it "update peer version range on received messages" testUpdatePeerChatVRange describe "network statuses" $ do it "should get network statuses" testGetNetworkStatuses - describe "PQ tests" $ do - describe "enable PQ before connection, connect via invitation link" $ pqMatrix2 runTestPQConnectViaLink - describe "enable PQ before connection, connect via contact address" $ pqMatrix2 runTestPQConnectViaAddress - describe "connect via invitation link with PQ encryption enabled" $ pqVersionTestMatrix2 runTestPQVersionsViaLink - describe "connect via contact address with PQ encryption enabled" $ pqVersionTestMatrix2 runTestPQVersionsViaAddress - it "should enable PQ after several messages in connection without PQ" testPQEnableContact - it "should enable PQ, reduce envelope size and enable compression" testPQEnableContactCompression where testInvVRange vr1 vr2 = it (vRangeStr vr1 <> " - " <> vRangeStr vr2) $ testConnInvChatVRange vr1 vr2 testReqVRange vr1 vr2 = it (vRangeStr vr1 <> " - " <> vRangeStr vr2) $ testConnReqChatVRange vr1 vr2 @@ -142,7 +135,7 @@ chatDirectTests = do testAddContact :: HasCallStack => SpecWith FilePath testAddContact = versionTestMatrix2 runTestAddContact where - runTestAddContact alice bob = do + runTestAddContact pqExpected alice bob = do alice ##> "/_connect 1" inv <- getInvitation alice bob ##> ("/_connect 1 " <> inv) @@ -151,46 +144,50 @@ testAddContact = versionTestMatrix2 runTestAddContact (bob <## "alice (Alice): contact is connected") (alice <## "bob (Bob): contact is connected") threadDelay 100000 - chatsEmpty alice bob + chatsEmpty alice #> "@bob hello there 🙂" bob <# "alice> hello there 🙂" alice ##> "/_unread chat @2 on" alice <## "ok" alice ##> "/_unread chat @2 off" alice <## "ok" - chatsOneMessage alice bob + chatsOneMessage bob #> "@alice hello there" alice <# "bob> hello there" bob #> "@alice how are you?" alice <# "bob> how are you?" - chatsManyMessages alice bob - chatsEmpty alice bob = do - alice @@@ [("@bob", lastChatFeature)] - alice #$> ("/_get chat @2 count=100", chat, chatFeatures) - bob @@@ [("@alice", lastChatFeature)] - bob #$> ("/_get chat @2 count=100", chat, chatFeatures) - chatsOneMessage alice bob = do - alice @@@ [("@bob", "hello there 🙂")] - alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "hello there 🙂")]) - bob @@@ [("@alice", "hello there 🙂")] - bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "hello there 🙂")]) - chatsManyMessages alice bob = do - alice @@@ [("@bob", "how are you?")] - alice #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(1, "hello there 🙂"), (0, "hello there"), (0, "how are you?")]) - bob @@@ [("@alice", "how are you?")] - bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "hello there 🙂"), (1, "hello there"), (1, "how are you?")]) - -- pagination - alice #$> ("/_get chat @2 after=" <> itemId 1 <> " count=100", chat, [(0, "hello there"), (0, "how are you?")]) - alice #$> ("/_get chat @2 before=" <> itemId 2 <> " count=100", chat, chatFeatures <> [(1, "hello there 🙂")]) - -- search - alice #$> ("/_get chat @2 count=100 search=ello ther", chat, [(1, "hello there 🙂"), (0, "hello there")]) - -- read messages - alice #$> ("/_read chat @2 from=1 to=100", id, "ok") - bob #$> ("/_read chat @2 from=1 to=100", id, "ok") - alice #$> ("/_read chat @2", id, "ok") - bob #$> ("/_read chat @2", id, "ok") - alice #$> ("/read user", id, "ok") - alice #$> ("/_read user 1", id, "ok") + chatsManyMessages + where + chatsEmpty = do + alice @@@ [("@bob", lastChatFeature)] + alice #$> ("/_get chat @2 count=100", chat, features) + bob @@@ [("@alice", lastChatFeature)] + bob #$> ("/_get chat @2 count=100", chat, features) + chatsOneMessage = do + alice @@@ [("@bob", "hello there 🙂")] + alice #$> ("/_get chat @2 count=100", chat, features <> [(1, "hello there 🙂")]) + bob @@@ [("@alice", "hello there 🙂")] + bob #$> ("/_get chat @2 count=100", chat, features <> [(0, "hello there 🙂")]) + chatsManyMessages = do + alice @@@ [("@bob", "how are you?")] + alice #$> ("/_get chat @2 count=100", chat, features <> [(1, "hello there 🙂"), (0, "hello there"), (0, "how are you?")]) + bob @@@ [("@alice", "how are you?")] + bob #$> ("/_get chat @2 count=100", chat, features <> [(0, "hello there 🙂"), (1, "hello there"), (1, "how are you?")]) + -- pagination + alice #$> ("/_get chat @2 after=" <> itemId 1 <> " count=100", chat, [(0, "hello there"), (0, "how are you?")]) + alice #$> ("/_get chat @2 before=" <> itemId 2 <> " count=100", chat, features <> [(1, "hello there 🙂")]) + -- search + alice #$> ("/_get chat @2 count=100 search=ello ther", chat, [(1, "hello there 🙂"), (0, "hello there")]) + -- read messages + alice #$> ("/_read chat @2 from=1 to=100", id, "ok") + bob #$> ("/_read chat @2 from=1 to=100", id, "ok") + alice #$> ("/_read chat @2", id, "ok") + bob #$> ("/_read chat @2", id, "ok") + alice #$> ("/read user", id, "ok") + alice #$> ("/_read user 1", id, "ok") + features = if pqExpected + then chatFeatures + else (0, e2eeInfoNoPQStr) : tail chatFeatures testDuplicateContactsSeparate :: HasCallStack => FilePath -> IO () testDuplicateContactsSeparate = @@ -728,6 +725,20 @@ testMultilineMessage = testChat3 aliceProfile bobProfile cathProfile $ \alice bo cath <# "alice> hello" cath <## "there" +testLargeMessage :: HasCallStack => FilePath -> IO () +testLargeMessage = + testChat2 aliceProfile bobProfile $ + \alice bob -> do + connectUsers alice bob + + img <- genProfileImg + let profileImage = "data:image/png;base64," <> B.unpack img + alice `send` ("/_profile 1 {\"displayName\": \"alice2\", \"fullName\": \"\", \"image\": \"" <> profileImage <> "\"}") + _trimmedCmd1 <- getTermLine alice + alice <## "user profile is changed to alice2 (your 1 contacts are notified)" + bob <## "contact alice changed to alice2" + bob <## "use @alice2 to send messages" + testGetSetSMPServers :: HasCallStack => FilePath -> IO () testGetSetSMPServers = testChat2 aliceProfile bobProfile $ @@ -1649,17 +1660,17 @@ testUsersDifferentCIExpirationTTL tmp = do -- set ttl for first user alice ##> "/user alice" showActiveUser alice "alice (Alice)" - alice #$> ("/_ttl 1 1", id, "ok") + alice #$> ("/_ttl 1 2", id, "ok") -- set ttl for second user alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/_ttl 2 3", id, "ok") + alice #$> ("/_ttl 2 4", id, "ok") -- first user messages alice ##> "/user alice" showActiveUser alice "alice (Alice)" - alice #$> ("/ttl", id, "old messages are set to be deleted after: 1 second(s)") + alice #$> ("/ttl", id, "old messages are set to be deleted after: 2 second(s)") alice #> "@bob alice 3" bob <# "alice> alice 3" @@ -1671,7 +1682,7 @@ testUsersDifferentCIExpirationTTL tmp = do -- second user messages alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/ttl", id, "old messages are set to be deleted after: 3 second(s)") + alice #$> ("/ttl", id, "old messages are set to be deleted after: 4 second(s)") alice #> "@bob alisa 3" bob <# "alisa> alisa 3" @@ -1680,7 +1691,7 @@ testUsersDifferentCIExpirationTTL tmp = do alice #$> ("/_get chat @4 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) - threadDelay 2000000 + threadDelay 3000000 -- messages both before and after setting chat item ttl are deleted -- first user messages @@ -1916,13 +1927,13 @@ testUsersTimedMessages tmp = do withNewTestChat tmp "bob" bobProfile $ \bob -> do withNewTestChat tmp "alice" aliceProfile $ \alice -> do connectUsers alice bob - configureTimedMessages alice bob "2" "1" + configureTimedMessages alice bob "2" "2" -- create second user and configure timed messages for contact alice ##> "/create user alisa" showActiveUser alice "alisa" connectUsers alice bob - configureTimedMessages alice bob "4" "2" + configureTimedMessages alice bob "4" "3" -- first user messages alice ##> "/user alice" @@ -1943,7 +1954,7 @@ testUsersTimedMessages tmp = do alice <# "bob> alisa 2" -- messages are deleted after ttl - threadDelay 500000 + threadDelay 1500000 alice ##> "/user alice" showActiveUser alice "alice (Alice)" @@ -2094,7 +2105,7 @@ testUserPrivacy = alice <##? chatHistory alice ##> "/_get items before=13 count=10" alice - <##? [ ConsoleString ("bob> " <> e2eeInfoNoPQStr), + <##? [ ConsoleString ("bob> " <> e2eeInfoPQStr), "bob> Disappearing messages: allowed", "bob> Full deletion: off", "bob> Message reactions: enabled", @@ -2165,7 +2176,7 @@ testUserPrivacy = alice <## "messages are shown" alice <## "profile is visible" chatHistory = - [ ConsoleString ("bob> " <> e2eeInfoNoPQStr), + [ ConsoleString ("bob> " <> e2eeInfoPQStr), "bob> Disappearing messages: allowed", "bob> Full deletion: off", "bob> Message reactions: enabled", @@ -2347,6 +2358,7 @@ testMarkContactVerified = alice <## "sending messages via: localhost" alice <## "you've shared main profile with this contact" alice <## connVerified + alice <## "quantum resistant end-to-end encryption" alice <## currentChatVRangeInfo where connVerified @@ -2517,6 +2529,7 @@ testSyncRatchetCodeReset tmp = bob <## "sending messages via: localhost" bob <## "you've shared main profile with this contact" bob <## connVerified + bob <## "quantum resistant end-to-end encryption" bob <## currentChatVRangeInfo where connVerified @@ -2667,8 +2680,8 @@ testConfigureDeliveryReceipts tmp = testConnInvChatVRange :: HasCallStack => VersionRangeChat -> VersionRangeChat -> FilePath -> IO () testConnInvChatVRange ct1VRange ct2VRange tmp = - withNewTestChatCfg tmp testCfg {chatVRange = const ct1VRange} "alice" aliceProfile $ \alice -> do - withNewTestChatCfg tmp testCfg {chatVRange = const ct2VRange} "bob" bobProfile $ \bob -> do + withNewTestChatCfg tmp testCfg {chatVRange = ct1VRange} "alice" aliceProfile $ \alice -> do + withNewTestChatCfg tmp testCfg {chatVRange = ct2VRange} "bob" bobProfile $ \bob -> do connectUsers alice bob alice ##> "/i bob" @@ -2679,8 +2692,8 @@ testConnInvChatVRange ct1VRange ct2VRange tmp = testConnReqChatVRange :: HasCallStack => VersionRangeChat -> VersionRangeChat -> FilePath -> IO () testConnReqChatVRange ct1VRange ct2VRange tmp = - withNewTestChatCfg tmp testCfg {chatVRange = const ct1VRange} "alice" aliceProfile $ \alice -> do - withNewTestChatCfg tmp testCfg {chatVRange = const ct2VRange} "bob" bobProfile $ \bob -> do + withNewTestChatCfg tmp testCfg {chatVRange = ct1VRange} "alice" aliceProfile $ \alice -> do + withNewTestChatCfg tmp testCfg {chatVRange = ct2VRange} "bob" bobProfile $ \bob -> do alice ##> "/ad" cLink <- getContactLink alice True bob ##> ("/c " <> cLink) @@ -2707,7 +2720,7 @@ testUpdatePeerChatVRange tmp = contactInfoChatVRange alice vr11 bob ##> "/i alice" - contactInfoChatVRange bob (supportedChatVRange PQSupportOff) + contactInfoChatVRange bob supportedChatVRange withTestChat tmp "bob" $ \bob -> do bob <## "1 contacts connected (use /cs for the list)" @@ -2716,10 +2729,10 @@ testUpdatePeerChatVRange tmp = alice <# "bob> hello 1" alice ##> "/i bob" - contactInfoChatVRange alice (supportedChatVRange PQSupportOff) + contactInfoChatVRange alice supportedChatVRange bob ##> "/i alice" - contactInfoChatVRange bob (supportedChatVRange PQSupportOff) + contactInfoChatVRange bob supportedChatVRange withTestChatCfg tmp cfg11 "bob" $ \bob -> do bob <## "1 contacts connected (use /cs for the list)" @@ -2731,9 +2744,9 @@ testUpdatePeerChatVRange tmp = contactInfoChatVRange alice vr11 bob ##> "/i alice" - contactInfoChatVRange bob (supportedChatVRange PQSupportOff) + contactInfoChatVRange bob supportedChatVRange where - cfg11 = testCfg {chatVRange = const vr11} :: ChatConfig + cfg11 = testCfg {chatVRange = vr11} :: ChatConfig testGetNetworkStatuses :: HasCallStack => FilePath -> IO () testGetNetworkStatuses tmp = do @@ -2759,259 +2772,5 @@ contactInfoChatVRange cc (VersionRange minVer maxVer) = do cc <## "sending messages via: localhost" cc <## "you've shared main profile with this contact" cc <## "connection not verified, use /code command to see security code" + cc <## "quantum resistant end-to-end encryption" cc <## ("peer chat protocol version range: (" <> show minVer <> ", " <> show maxVer <> ")") - -runTestPQConnectViaLink :: HasCallStack => (TestCC, PQEnabled) -> (TestCC, PQEnabled) -> IO () -runTestPQConnectViaLink (alice, aPQ) (bob, bPQ) = do - when aPQ $ pqOn alice - when bPQ $ pqOn bob - - connectUsers alice bob - - (alice, "hi") `pqSend` bob - (bob, "hey") `pqSend` alice - - alice ##> "/_get chat @2 count=100" - ra <- chat <$> getTermLine alice - ra `shouldContain` [(0, e2eeInfo)] - alice `pqForContact` 2 `shouldReturn` PQEncryption pqEnabled - - bob ##> "/_get chat @2 count=100" - rb <- chat <$> getTermLine bob - rb `shouldContain` [(0, e2eeInfo)] - bob `pqForContact` 2 `shouldReturn` PQEncryption pqEnabled - where - pqEnabled = aPQ && bPQ - pqSend = if pqEnabled then (+#>) else (\#>) - e2eeInfo = if pqEnabled then e2eeInfoPQStr else e2eeInfoNoPQStr - -pqOn :: TestCC -> IO () -pqOn cc = do - cc ##> "/pq on" - cc <## "ok" - -runTestPQConnectViaAddress :: HasCallStack => (TestCC, PQEnabled) -> (TestCC, PQEnabled) -> IO () -runTestPQConnectViaAddress (alice, aPQ) (bob, bPQ) = do - when aPQ $ pqOn alice - when bPQ $ pqOn bob - - alice ##> "/ad" - cLink <- getContactLink alice True - bob ##> ("/c " <> cLink) - alice <#? bob - alice @@@ [("<@bob", "")] - alice ##> "/ac bob" - alice <## "bob (Bob): accepting contact request..." - concurrently_ - (bob <## "alice (Alice): contact is connected") - (alice <## "bob (Bob): contact is connected") - - (alice, "hi") `pqSend` bob - (bob, "hey") `pqSend` alice - - alice ##> "/_get chat @2 count=100" - ra <- chat <$> getTermLine alice - ra `shouldContain` [(0, e2eeInfo)] - alice `pqForContact` 2 `shouldReturn` PQEncryption pqEnabled - - bob ##> "/_get chat @2 count=100" - rb <- chat <$> getTermLine bob - rb `shouldContain` [(0, e2eeInfo)] - bob `pqForContact` 2 `shouldReturn` PQEncryption pqEnabled - where - pqEnabled = aPQ && bPQ - pqSend = if pqEnabled then (+#>) else (\#>) - e2eeInfo = if pqEnabled then e2eeInfoPQStr else e2eeInfoNoPQStr - -runTestPQVersionsViaLink :: HasCallStack => TestCC -> TestCC -> Bool -> VersionChat -> IO () -runTestPQVersionsViaLink alice bob pqExpected vExpected = do - img <- genProfileImg - let profileImage = "data:image/png;base64," <> B.unpack img - alice `send` ("/set profile image " <> profileImage) - _trimmedCmd1 <- getTermLine alice - alice <## "profile image updated" - bob `send` ("/set profile image " <> profileImage) - _trimmedCmd2 <- getTermLine bob - bob <## "profile image updated" - - pqOn alice - pqOn bob - - connectUsers alice bob - - (alice, "hi", vExpected) `pqSend` (bob, vExpected) - (bob, "hey", vExpected) `pqSend` (alice, vExpected) - - alice ##> "/_get chat @2 count=100" - ra <- chat <$> getTermLine alice - ra `shouldContain` [(0, e2eeInfo)] - alice `pqForContact` 2 `shouldReturn` PQEncryption pqExpected - - bob ##> "/_get chat @2 count=100" - rb <- chat <$> getTermLine bob - rb `shouldContain` [(0, e2eeInfo)] - bob `pqForContact` 2 `shouldReturn` PQEncryption pqExpected - where - pqSend = if pqExpected then (+:#>) else (\:#>) - e2eeInfo = if pqExpected then e2eeInfoPQStr else e2eeInfoNoPQStr - -runTestPQVersionsViaAddress :: HasCallStack => TestCC -> TestCC -> Bool -> VersionChat -> IO () -runTestPQVersionsViaAddress alice bob pqExpected vExpected = do - img <- genProfileImg - let profileImage = "data:image/png;base64," <> B.unpack img - alice `send` ("/set profile image " <> profileImage) - _trimmedCmd1 <- getTermLine alice - alice <## "profile image updated" - bob `send` ("/set profile image " <> profileImage) - _trimmedCmd2 <- getTermLine bob - bob <## "profile image updated" - - pqOn alice - pqOn bob - - alice ##> "/ad" - cLink <- getContactLink alice True - bob ##> ("/c " <> cLink) - alice <#? bob - alice @@@ [("<@bob", "")] - alice ##> "/ac bob" - alice <## "bob (Bob): accepting contact request..." - concurrently_ - (bob <## "alice (Alice): contact is connected") - (alice <## "bob (Bob): contact is connected") - - (alice, "hi", vExpected) `pqSend` (bob, vExpected) - (bob, "hey", vExpected) `pqSend` (alice, vExpected) - - alice ##> "/_get chat @2 count=100" - ra <- chat <$> getTermLine alice - ra `shouldContain` [(0, e2eeInfo)] - alice `pqForContact` 2 `shouldReturn` PQEncryption pqExpected - - bob ##> "/_get chat @2 count=100" - rb <- chat <$> getTermLine bob - rb `shouldContain` [(0, e2eeInfo)] - bob `pqForContact` 2 `shouldReturn` PQEncryption pqExpected - where - pqSend = if pqExpected then (+:#>) else (\:#>) - e2eeInfo = if pqExpected then e2eeInfoPQStr else e2eeInfoNoPQStr - -testPQEnableContact :: HasCallStack => FilePath -> IO () -testPQEnableContact = - testChat2 aliceProfile bobProfile $ \alice bob -> do - connectUsers alice bob - (alice, "hi") \#> bob - (bob, "hey") \#> alice - - alice ##> "/_get chat @2 count=100" - ra <- chat <$> getTermLine alice - ra `shouldContain` [(0, e2eeInfoNoPQStr)] - PQEncOff <- alice `pqForContact` 2 - - bob ##> "/_get chat @2 count=100" - rb <- chat <$> getTermLine bob - rb `shouldContain` [(0, e2eeInfoNoPQStr)] - PQEncOff <- bob `pqForContact` 2 - - sendMany PQEncOff alice bob - PQEncOff <- alice `pqForContact` 2 - PQEncOff <- bob `pqForContact` 2 - - -- enabling experimental flags doesn't enable PQ in previously created connection - pqOn alice - sendMany PQEncOff alice bob - PQEncOff <- alice `pqForContact` 2 - PQEncOff <- bob `pqForContact` 2 - - pqOn bob - sendMany PQEncOff alice bob - PQEncOff <- alice `pqForContact` 2 - PQEncOff <- bob `pqForContact` 2 - - -- if only one contact allows PQ, it's not enabled - alice ##> "/pq @bob on" - alice <## "bob: enable quantum resistant end-to-end encryption" - sendMany PQEncOff alice bob - PQEncOff <- alice `pqForContact` 2 - PQEncOff <- bob `pqForContact` 2 - - -- both contacts have to allow PQ to enable it - bob ##> "/pq @alice on" - bob <## "alice: enable quantum resistant end-to-end encryption" - - (alice, "1") \#> bob - (bob, "2") \#> alice - (alice, "3") \#> bob - (bob, "4") \#> alice - (alice, "5") +#> bob - - PQEncOff <- alice `pqForContact` 2 - PQEncOff <- bob `pqForContact` 2 - - (bob, "6") ++#> alice - -- equivalent to: - -- bob `send` "@alice 6" - -- bob <## "alice: quantum resistant end-to-end encryption enabled" - -- bob <# "@alice 6" - -- alice <## "bob: quantum resistant end-to-end encryption enabled" - -- alice <# "bob> 6" - - PQEncOn <- alice `pqForContact` 2 - alice #$> ("/_get chat @2 count=2", chat, [(0, e2eeInfoPQStr), (0, "6")]) - - PQEncOn <- bob `pqForContact` 2 - bob #$> ("/_get chat @2 count=2", chat, [(1, e2eeInfoPQStr), (1, "6")]) - - (alice, "6") +#> bob - (bob, "7") +#> alice - - sendMany PQEncOn alice bob - - PQEncOn <- alice `pqForContact` 2 - PQEncOn <- bob `pqForContact` 2 - pure () - -sendMany :: PQEncryption -> TestCC -> TestCC -> IO () -sendMany pqEnc alice bob = - forM_ [(1 :: Int) .. 10] $ \i -> do - sndRcv pqEnc False (alice, show i) bob - sndRcv pqEnc False (bob, show i) alice - -testPQEnableContactCompression :: HasCallStack => FilePath -> IO () -testPQEnableContactCompression = - testChat2 aliceProfile bobProfile $ \alice bob -> do - connectUsers alice bob - (alice, "hi") \#> bob - (bob, "hey") \#> alice - PQEncOff <- alice `pqForContact` 2 - PQEncOff <- bob `pqForContact` 2 - (alice, "lrg 1", v) \:#> (bob, v) - (bob, "lrg 2", v) \:#> (alice, v) - PQSupportOff <- alice `pqSupportForCt` 2 - alice ##> "/pq @bob on" - alice <## "bob: enable quantum resistant end-to-end encryption" - PQSupportOn <- alice `pqSupportForCt` 2 - (alice, "lrg 3", v) \:#> (bob, v) - (bob, "lrg 4", v) \:#> (alice, v) - PQSupportOff <- bob `pqSupportForCt` 2 - bob ##> "/pq @alice on" - bob <## "alice: enable quantum resistant end-to-end encryption" - PQSupportOn <- bob `pqSupportForCt` 2 - threadDelay 300000 - (alice, "lrg 1", v) \:#> (bob, v') - threadDelay 300000 - (bob, "lrg 2", v') \:#> (alice, v') - threadDelay 300000 - (alice, "lrg 3", v') \:#> (bob, v') - threadDelay 300000 - (bob, "lrg 4", v') \:#> (alice, v') - threadDelay 300000 - (alice, "lrg 5", v') +:#> (bob, v') - PQEncOff <- alice `pqForContact` 2 - PQEncOff <- bob `pqForContact` 2 - (bob, "lrg 6", v') ++:#> (alice, v') - (alice, "lrg 7", v') +:#> (bob, v') - (bob, "lrg 8", v') +:#> (alice, v') - where - v = currentChatVersion - v' = pqEncryptionCompressionVersion diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index 4108f799df..ff577dc737 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -9,7 +9,7 @@ import ChatTests.Utils import Control.Concurrent (threadDelay) import Control.Concurrent.Async (concurrently_) import Control.Monad (void, when) -import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as B import Data.List (isInfixOf) import qualified Data.Text as T import Simplex.Chat.Controller (ChatConfig (..)) @@ -18,7 +18,6 @@ import Simplex.Chat.Store (agentStoreFile, chatStoreFile) import Simplex.Chat.Types (VersionRangeChat) import Simplex.Chat.Types.Shared (GroupMemberRole (..)) import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB -import Simplex.Messaging.Crypto.Ratchet (pattern PQSupportOff) import System.Directory (copyFile) import System.FilePath (()) import Test.Hspec hiding (it) @@ -29,6 +28,7 @@ chatGroupTests = do describe "add contacts, create group and send/receive messages" testGroupMatrix it "v1: add contacts, create group and send/receive messages" testGroup it "v1: add contacts, create group and send/receive messages, check messages" testGroupCheckMessages + it "send large message" testGroupLargeMessage it "create group with incognito membership" testNewGroupIncognito it "create and join group with 4 members" testGroup2 it "create and delete group" testGroupDelete @@ -150,19 +150,19 @@ chatGroupTests = do it "member was blocked before joining group" testBlockForAllBeforeJoining it "can't repeat block, unblock" testBlockForAllCantRepeat where - _0 = supportedChatVRange PQSupportOff -- don't create direct connections + _0 = supportedChatVRange -- don't create direct connections _1 = groupCreateDirectVRange -- having host configured with older version doesn't have effect in tests -- because host uses current code and sends version in MemberInfo testNoDirect vrMem2 vrMem3 noConns = it ( "host " - <> vRangeStr (supportedChatVRange PQSupportOff) + <> vRangeStr supportedChatVRange <> (", 2nd mem " <> vRangeStr vrMem2) <> (", 3rd mem " <> vRangeStr vrMem3) <> (if noConns then " : 2 3" else " : 2 <##> 3") ) - $ testNoGroupDirectConns (supportedChatVRange PQSupportOff) vrMem2 vrMem3 noConns + $ testNoGroupDirectConns supportedChatVRange vrMem2 vrMem3 noConns testGroup :: HasCallStack => FilePath -> IO () testGroup = @@ -357,6 +357,20 @@ testGroupShared alice bob cath checkMessages directConnections = do alice #$> ("/_unread chat #1 on", id, "ok") alice #$> ("/_unread chat #1 off", id, "ok") +testGroupLargeMessage :: HasCallStack => FilePath -> IO () +testGroupLargeMessage = + testChat2 aliceProfile bobProfile $ + \alice bob -> do + createGroup2 "team" alice bob + + img <- genProfileImg + let profileImage = "data:image/png;base64," <> B.unpack img + alice `send` ("/_group_profile #1 {\"displayName\": \"team\", \"fullName\": \"\", \"image\": \"" <> profileImage <> "\", \"groupPreferences\": {\"directMessages\": {\"enable\": \"on\"}, \"history\": {\"enable\": \"on\"}}}") + _trimmedCmd1 <- getTermLine alice + alice <## "profile image updated" + bob <## "alice updated group #team:" + bob <## "profile image updated" + testNewGroupIncognito :: HasCallStack => FilePath -> IO () testNewGroupIncognito = testChatCfg2 testCfgGroupLinkViaContact aliceProfile bobProfile $ @@ -3594,9 +3608,9 @@ testConfigureGroupDeliveryReceipts tmp = testNoGroupDirectConns :: HasCallStack => VersionRangeChat -> VersionRangeChat -> VersionRangeChat -> Bool -> FilePath -> IO () testNoGroupDirectConns hostVRange mem2VRange mem3VRange noDirectConns tmp = - withNewTestChatCfg tmp testCfg {chatVRange = const hostVRange} "alice" aliceProfile $ \alice -> do - withNewTestChatCfg tmp testCfg {chatVRange = const mem2VRange} "bob" bobProfile $ \bob -> do - withNewTestChatCfg tmp testCfg {chatVRange = const mem3VRange} "cath" cathProfile $ \cath -> do + withNewTestChatCfg tmp testCfg {chatVRange = hostVRange} "alice" aliceProfile $ \alice -> do + withNewTestChatCfg tmp testCfg {chatVRange = mem2VRange} "bob" bobProfile $ \bob -> do + withNewTestChatCfg tmp testCfg {chatVRange = mem3VRange} "cath" cathProfile $ \cath -> do createGroup3 "team" alice bob cath if noDirectConns then contactsDontExist bob cath @@ -3885,6 +3899,20 @@ testMemberContactMessage = (bob <## "alice (Alice): contact is connected") bob #$> ("/_get chat #1 count=1", chat, [(0, "started direct connection with you")]) + + -- exchanging messages will enable PQ (see Chat "TODO PQ" - perhaps connection should be negotiated with PQ on) + alice <##> bob + alice <##> bob + + alice `send` "@bob hi" + alice <## "bob: quantum resistant end-to-end encryption enabled" + alice <# "@bob hi" + bob <## "alice: quantum resistant end-to-end encryption enabled" + bob <# "alice> hi" + + bob #> "@alice hey" + alice <# "bob> hey" + alice <##> bob -- bob and cath connect diff --git a/tests/ChatTests/Profiles.hs b/tests/ChatTests/Profiles.hs index a6cc491456..294540a62c 100644 --- a/tests/ChatTests/Profiles.hs +++ b/tests/ChatTests/Profiles.hs @@ -307,6 +307,7 @@ testProfileLink = cc <## ("contact address: " <> cLink) cc <## "you've shared main profile with this contact" cc <## "connection not verified, use /code command to see security code" + cc <## "quantum resistant end-to-end encryption" cc <## currentChatVRangeInfo checkAliceNoProfileLink cc = do cc ##> "/info alice" @@ -315,6 +316,7 @@ testProfileLink = cc <##. "sending messages via" cc <## "you've shared main profile with this contact" cc <## "connection not verified, use /code command to see security code" + cc <## "quantum resistant end-to-end encryption" cc <## currentChatVRangeInfo testUserContactLinkAutoAccept :: HasCallStack => FilePath -> IO () @@ -1516,7 +1518,7 @@ testSetContactPrefs = testChat2 aliceProfile bobProfile $ alice ##> "/_set prefs @2 {}" alice <## "your preferences for bob did not change" (bob ("/_get chat @2 count=100", chat, startFeatures) bob #$> ("/_get chat @2 count=100", chat, startFeatures) let sendVoice = "/_send @2 json {\"filePath\": \"test.txt\", \"msgContent\": {\"type\": \"voice\", \"text\": \"\", \"duration\": 10}}" diff --git a/tests/ChatTests/Utils.hs b/tests/ChatTests/Utils.hs index aa6579efee..a3ee0d5ca8 100644 --- a/tests/ChatTests/Utils.hs +++ b/tests/ChatTests/Utils.hs @@ -87,15 +87,16 @@ ifCI xrun run d t = do skip :: String -> SpecWith a -> SpecWith a skip = before_ . pendingWith -versionTestMatrix2 :: (HasCallStack => TestCC -> TestCC -> IO ()) -> SpecWith FilePath +-- Bool is pqExpected - see testAddContact +versionTestMatrix2 :: (HasCallStack => Bool -> TestCC -> TestCC -> IO ()) -> SpecWith FilePath versionTestMatrix2 runTest = do - it "current" $ testChat2 aliceProfile bobProfile runTest - it "prev" $ testChatCfg2 testCfgVPrev aliceProfile bobProfile runTest - it "prev to curr" $ runTestCfg2 testCfg testCfgVPrev runTest - it "curr to prev" $ runTestCfg2 testCfgVPrev testCfg runTest - it "old (1st supported)" $ testChatCfg2 testCfgV1 aliceProfile bobProfile runTest - it "old to curr" $ runTestCfg2 testCfg testCfgV1 runTest - it "curr to old" $ runTestCfg2 testCfgV1 testCfg runTest + it "current" $ testChat2 aliceProfile bobProfile (runTest True) + it "prev" $ testChatCfg2 testCfgVPrev aliceProfile bobProfile (runTest False) + it "prev to curr" $ runTestCfg2 testCfg testCfgVPrev (runTest False) + it "curr to prev" $ runTestCfg2 testCfgVPrev testCfg (runTest False) + it "old (1st supported)" $ testChatCfg2 testCfgV1 aliceProfile bobProfile (runTest False) + it "old to curr" $ runTestCfg2 testCfg testCfgV1 (runTest False) + it "curr to old" $ runTestCfg2 testCfgV1 testCfg (runTest False) versionTestMatrix3 :: (HasCallStack => TestCC -> TestCC -> TestCC -> IO ()) -> SpecWith FilePath versionTestMatrix3 runTest = do @@ -119,34 +120,6 @@ runTestCfg3 aliceCfg bobCfg cathCfg runTest tmp = withNewTestChatCfg tmp cathCfg "cath" cathProfile $ \cath -> runTest alice bob cath -type PQEnabled = Bool - -pqMatrix2 :: (HasCallStack => (TestCC, PQEnabled) -> (TestCC, PQEnabled) -> IO ()) -> SpecWith FilePath -pqMatrix2 runTest = do - it "PQ: off, off" $ test False False - it "PQ: on, off" $ test False True - it "PQ: off, on" $ test True False - it "PQ: on, on" $ test True True - where - test aPQ bPQ = testChat2 aliceProfile bobProfile $ \a b -> runTest (a, aPQ) (b, bPQ) - -pqVersionTestMatrix2 :: (HasCallStack => TestCC -> TestCC -> Bool -> VersionChat -> IO ()) -> SpecWith FilePath -pqVersionTestMatrix2 runTest = do - it "current" $ testChat2 aliceProfile bobProfile (runTest' True pqEncryptionCompressionVersion) - it "prev" $ testChatCfg2 testCfgVPrev aliceProfile bobProfile (runTest' False (VersionChat 6)) - it "prev to curr" $ runTestCfg2 testCfg testCfgVPrev (runTest' False (VersionChat 6)) - it "curr to prev" $ runTestCfg2 testCfgVPrev testCfg (runTest' False (VersionChat 6)) - it "old (1st supported)" $ testChatCfg2 testCfgV1 aliceProfile bobProfile (runTest' False (VersionChat 1)) - it "old to curr" $ runTestCfg2 testCfg testCfgV1 (runTest' False (VersionChat 1)) - it "curr to old" $ runTestCfg2 testCfgV1 testCfg (runTest' False (VersionChat 1)) - it "next" $ testChatCfg2 testCfgVNext aliceProfile bobProfile (runTest' True pqEncryptionCompressionVersion) - it "next to curr" $ runTestCfg2 testCfg testCfgVNext (runTest' True pqEncryptionCompressionVersion) - it "curr to next" $ runTestCfg2 testCfgVNext testCfg (runTest' True pqEncryptionCompressionVersion) - it "next to prev" $ runTestCfg2 testCfgVPrev testCfgVNext (runTest' False (VersionChat 6)) - it "prev to next" $ runTestCfg2 testCfgVNext testCfgVPrev (runTest' False (VersionChat 6)) - where - runTest' pqExpected v a b = runTest a b pqExpected v - withTestChatGroup3Connected :: HasCallStack => FilePath -> String -> (HasCallStack => TestCC -> IO a) -> IO a withTestChatGroup3Connected tmp dbPrefix action = do withTestChat tmp dbPrefix $ \cc -> do @@ -287,7 +260,7 @@ chatFeaturesF = map (\(a, _, c) -> (a, c)) chatFeatures'' chatFeatures'' :: [((Int, String), Maybe (Int, String), Maybe String)] chatFeatures'' = - [ ((0, e2eeInfoNoPQStr), Nothing, Nothing), + [ ((0, e2eeInfoPQStr), Nothing, Nothing), ((0, "Disappearing messages: allowed"), Nothing, Nothing), ((0, "Full deletion: off"), Nothing, Nothing), ((0, "Message reactions: enabled"), Nothing, Nothing), @@ -708,7 +681,7 @@ checkActionDeletesFile file action = do currentChatVRangeInfo :: String currentChatVRangeInfo = - "peer chat protocol version range: " <> vRangeStr (supportedChatVRange PQSupportOff) + "peer chat protocol version range: " <> vRangeStr supportedChatVRange vRangeStr :: VersionRange v -> String vRangeStr (VersionRange minVer maxVer) = "(" <> show minVer <> ", " <> show maxVer <> ")" diff --git a/tests/ProtocolTests.hs b/tests/ProtocolTests.hs index 18fb677be2..c4dfa7da25 100644 --- a/tests/ProtocolTests.hs +++ b/tests/ProtocolTests.hs @@ -50,7 +50,7 @@ testDhPubKey :: C.PublicKeyX448 testDhPubKey = "MEIwBQYDK2VvAzkAmKuSYeQ/m0SixPDS8Wq8VBaTS1cW+Lp0n0h4Diu+kUpR+qXx4SDJ32YGEFoGFGSbGPry5Ychr6U=" testE2ERatchetParams :: RcvE2ERatchetParamsUri 'C.X448 -testE2ERatchetParams = E2ERatchetParamsUri (supportedE2EEncryptVRange PQSupportOn) testDhPubKey testDhPubKey Nothing +testE2ERatchetParams = E2ERatchetParamsUri supportedE2EEncryptVRange testDhPubKey testDhPubKey Nothing testConnReq :: ConnectionRequestUri 'CMInvitation testConnReq = CRInvitationUri connReqData testE2ERatchetParams @@ -132,8 +132,8 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do "{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"}}}" ##==## ChatMessage chatInitialVRange (Just $ SharedMsgId "\1\2\3\4") (XMsgNew (MCSimple (extMsgContent (MCText "hello") Nothing))) it "x.msg.new chat message with chat version range" $ - "{\"v\":\"1-7\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"}}}" - ##==## ChatMessage (supportedChatVRange PQSupportOff) (Just $ SharedMsgId "\1\2\3\4") (XMsgNew (MCSimple (extMsgContent (MCText "hello") Nothing))) + "{\"v\":\"1-8\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"}}}" + ##==## ChatMessage supportedChatVRange (Just $ SharedMsgId "\1\2\3\4") (XMsgNew (MCSimple (extMsgContent (MCText "hello") Nothing))) it "x.msg.new quote" $ "{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello to you too\",\"type\":\"text\"},\"quote\":{\"content\":{\"text\":\"hello there!\",\"type\":\"text\"},\"msgRef\":{\"msgId\":\"BQYHCA==\",\"sent\":true,\"sentAt\":\"1970-01-01T00:00:01.000000001Z\"}}}}" ##==## ChatMessage @@ -242,14 +242,14 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do "{\"v\":\"1\",\"event\":\"x.grp.mem.new\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" #==# XGrpMemNew MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile} it "x.grp.mem.new with member chat version range" $ - "{\"v\":\"1\",\"event\":\"x.grp.mem.new\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-7\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" - #==# XGrpMemNew MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange $ supportedChatVRange PQSupportOff, profile = testProfile} + "{\"v\":\"1\",\"event\":\"x.grp.mem.new\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-8\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" + #==# XGrpMemNew MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange supportedChatVRange, profile = testProfile} it "x.grp.mem.intro" $ "{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" #==# XGrpMemIntro MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile} Nothing it "x.grp.mem.intro with member chat version range" $ - "{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-7\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" - #==# XGrpMemIntro MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange $ supportedChatVRange PQSupportOff, profile = testProfile} Nothing + "{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-8\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" + #==# XGrpMemIntro MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange supportedChatVRange, profile = testProfile} Nothing it "x.grp.mem.intro with member restrictions" $ "{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberRestrictions\":{\"restriction\":\"blocked\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" #==# XGrpMemIntro MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile} (Just MemberRestrictions {restriction = MRSBlocked}) @@ -263,8 +263,8 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do "{\"v\":\"1\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"directConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2-3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2-3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" #==# XGrpMemFwd MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile} IntroInvitation {groupConnReq = testConnReq, directConnReq = Just testConnReq} it "x.grp.mem.fwd with member chat version range and w/t directConnReq" $ - "{\"v\":\"1\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2-3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-7\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" - #==# XGrpMemFwd MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange $ supportedChatVRange PQSupportOff, profile = testProfile} IntroInvitation {groupConnReq = testConnReq, directConnReq = Nothing} + "{\"v\":\"1\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2-3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-8\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" + #==# XGrpMemFwd MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange supportedChatVRange, profile = testProfile} IntroInvitation {groupConnReq = testConnReq, directConnReq = Nothing} it "x.grp.mem.info" $ "{\"v\":\"1\",\"event\":\"x.grp.mem.info\",\"params\":{\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}" #==# XGrpMemInfo (MemberId "\1\2\3\4") testProfile From 22fd2d7189c6f79c843d5e07fa523819cc55120f Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Mon, 22 Apr 2024 20:50:28 +0400 Subject: [PATCH 3/4] ios: remove experimental PQ toggle (#4051) * ios: remove experimental PQ toggle * remove header --- apps/ios/Shared/Model/SimpleXAPI.swift | 13 ----- apps/ios/Shared/Views/Chat/ChatInfoView.swift | 53 +------------------ .../Views/UserSettings/DeveloperView.swift | 25 --------- apps/ios/SimpleXChat/APITypes.swift | 11 +--- apps/ios/SimpleXChat/AppGroup.swift | 4 +- 5 files changed, 3 insertions(+), 103 deletions(-) diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 65e24eeb25..b4c9a48d5d 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -272,18 +272,6 @@ func apiGetAppSettings(settings: AppSettings) throws -> AppSettings { throw r } -func apiSetPQEncryption(_ enable: Bool) throws { - let r = chatSendCmdSync(.apiSetPQEncryption(enable: enable)) - if case .cmdOk = r { return } - throw r -} - -func apiSetContactPQ(_ contactId: Int64, _ enable: Bool) async throws -> Contact { - let r = await chatSendCmd(.apiSetContactPQ(contactId: contactId, enable: enable)) - if case let .contactPQAllowed(_, contact, _) = r { return contact } - throw r -} - func apiExportArchive(config: ArchiveConfig) async throws { try await sendCommandOkResp(.apiExportArchive(config: config)) } @@ -1320,7 +1308,6 @@ func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = ni try apiSetTempFolder(tempFolder: getTempFilesDirectory().path) try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path) try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get()) - try apiSetPQEncryption(pqExperimentalEnabledDefault.get()) m.chatInitialized = true m.currentUser = try apiGetActiveUser() if m.currentUser == nil { diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift index 8b60fc7649..56e7cec697 100644 --- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift @@ -103,7 +103,6 @@ struct ChatInfoView: View { @State private var sendReceipts = SendReceipts.userDefault(true) @State private var sendReceiptsUserDefault = true @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false - @AppStorage(GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED, store: groupDefaults) private var pqExperimentalEnabled = false enum ChatInfoViewAlert: Identifiable { case clearChatAlert @@ -111,7 +110,6 @@ struct ChatInfoView: View { case switchAddressAlert case abortSwitchAddressAlert case syncConnectionForceAlert - case allowContactPQEncryptionAlert case error(title: LocalizedStringKey, error: LocalizedStringKey = "") var id: String { @@ -121,7 +119,6 @@ struct ChatInfoView: View { case .switchAddressAlert: return "switchAddressAlert" case .abortSwitchAddressAlert: return "abortSwitchAddressAlert" case .syncConnectionForceAlert: return "syncConnectionForceAlert" - case .allowContactPQEncryptionAlert: return "allowContactPQEncryptionAlert" case let .error(title, _): return "error \(title)" } } @@ -168,19 +165,9 @@ struct ChatInfoView: View { } .disabled(!contact.ready || !contact.active) - if pqExperimentalEnabled, - let conn = contact.activeConn { + if let conn = contact.activeConn { Section { infoRow(Text(String("E2E encryption")), conn.connPQEnabled ? "Quantum resistant" : "Standard") - if !conn.pqEncryption { - allowPQButton() - } - } header: { - Text(String("Quantum resistant E2E encryption")) - } footer: { - if !conn.pqEncryption { - Text(String("After allowing quantum resistant encryption, it will be enabled after several messages if your contact also allows it.")) - } } } @@ -256,7 +243,6 @@ struct ChatInfoView: View { case .switchAddressAlert: return switchAddressAlert(switchContactAddress) case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchContactAddress) case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncContactConnection(force: true) }) - case .allowContactPQEncryptionAlert: return allowContactPQEncryptionAlert() case let .error(title, error): return mkAlert(title: title, message: error) } } @@ -430,15 +416,6 @@ struct ChatInfoView: View { } } - private func allowPQButton() -> some View { - Button { - alert = .allowContactPQEncryptionAlert - } label: { - Label(String("Allow PQ encryption"), systemImage: "exclamationmark.triangle") - .foregroundColor(.orange) - } - } - private func networkStatusRow() -> some View { HStack { Text("Network status") @@ -572,34 +549,6 @@ struct ChatInfoView: View { } } } - - private func allowContactPQEncryption() { - Task { - do { - let ct = try await apiSetContactPQ(contact.apiId, true) - contact = ct - await MainActor.run { - chatModel.updateContact(ct) - dismiss() - } - } catch let error { - logger.error("allowContactPQEncryption apiSetContactPQ error: \(responseError(error))") - let a = getErrorAlert(error, "Error allowing contact PQ encryption") - await MainActor.run { - alert = .error(title: a.title, error: a.message) - } - } - } - } - - func allowContactPQEncryptionAlert() -> Alert { - Alert( - title: Text(String("Allow quantum resistant encryption?")), - message: Text(String("This is an experimental feature, it is not recommended to enable it for important chats.")), - primaryButton: .destructive(Text(String("Allow")), action: allowContactPQEncryption), - secondaryButton: .cancel() - ) - } } func switchAddressAlert(_ switchAddress: @escaping () -> Void) -> Alert { diff --git a/apps/ios/Shared/Views/UserSettings/DeveloperView.swift b/apps/ios/Shared/Views/UserSettings/DeveloperView.swift index 9b11c6d0f7..3bbfbfe33e 100644 --- a/apps/ios/Shared/Views/UserSettings/DeveloperView.swift +++ b/apps/ios/Shared/Views/UserSettings/DeveloperView.swift @@ -12,7 +12,6 @@ import SimpleXChat struct DeveloperView: View { @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false @AppStorage(GROUP_DEFAULT_CONFIRM_DB_UPGRADES, store: groupDefaults) private var confirmDatabaseUpgrades = false - @AppStorage(GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED, store: groupDefaults) private var pqExperimentalEnabled = false @Environment(\.colorScheme) var colorScheme var body: some View { @@ -43,33 +42,9 @@ struct DeveloperView: View { } footer: { (developerTools ? Text("Show:") : Text("Hide:")) + Text(" ") + Text("Database IDs and Transport isolation option.") } - - if developerTools { - Section { - settingsRow("key") { - Toggle("Post-quantum E2EE", isOn: $pqExperimentalEnabled) - .onChange(of: pqExperimentalEnabled) { - setPQExperimentalEnabled($0) - } - } - } header: { - Text(String("Experimental")) - } footer: { - Text(String("In this version applies only to new contacts.")) - } - } } } } - - private func setPQExperimentalEnabled(_ enable: Bool) { - do { - try apiSetPQEncryption(enable) - } catch let error { - let err = responseError(error) - logger.error("apiSetPQEncryption \(err)") - } - } } struct DeveloperView_Previews: PreviewProvider { diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 1d02f227c8..3aa610e4af 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -32,8 +32,6 @@ public enum ChatCommand { case setTempFolder(tempFolder: String) case setFilesFolder(filesFolder: String) case apiSetEncryptLocalFiles(enable: Bool) - case apiSetPQEncryption(enable: Bool) - case apiSetContactPQ(contactId: Int64, enable: Bool) case apiExportArchive(config: ArchiveConfig) case apiImportArchive(config: ArchiveConfig) case apiDeleteStorage @@ -172,8 +170,6 @@ public enum ChatCommand { case let .setTempFolder(tempFolder): return "/_temp_folder \(tempFolder)" case let .setFilesFolder(filesFolder): return "/_files_folder \(filesFolder)" case let .apiSetEncryptLocalFiles(enable): return "/_files_encrypt \(onOff(enable))" - case let .apiSetPQEncryption(enable): return "/pq \(onOff(enable))" - case let .apiSetContactPQ(contactId, enable): return "/_pq @\(contactId) \(onOff(enable))" case let .apiExportArchive(cfg): return "/_db export \(encodeJSON(cfg))" case let .apiImportArchive(cfg): return "/_db import \(encodeJSON(cfg))" case .apiDeleteStorage: return "/_db delete" @@ -326,8 +322,6 @@ public enum ChatCommand { case .setTempFolder: return "setTempFolder" case .setFilesFolder: return "setFilesFolder" case .apiSetEncryptLocalFiles: return "apiSetEncryptLocalFiles" - case .apiSetPQEncryption: return "apiSetPQEncryption" - case .apiSetContactPQ: return "apiSetContactPQ" case .apiExportArchive: return "apiExportArchive" case .apiImportArchive: return "apiImportArchive" case .apiDeleteStorage: return "apiDeleteStorage" @@ -653,7 +647,6 @@ public enum ChatResponse: Decodable, Error { case remoteCtrlConnected(remoteCtrl: RemoteCtrlInfo) case remoteCtrlStopped(rcsState: RemoteCtrlSessionState, rcStopReason: RemoteCtrlStopReason) // pq - case contactPQAllowed(user: UserRef, contact: Contact, pqEncryption: Bool) case contactPQEnabled(user: UserRef, contact: Contact, pqEnabled: Bool) // misc case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration]) @@ -809,8 +802,7 @@ public enum ChatResponse: Decodable, Error { case .remoteCtrlSessionCode: return "remoteCtrlSessionCode" case .remoteCtrlConnected: return "remoteCtrlConnected" case .remoteCtrlStopped: return "remoteCtrlStopped" - case .contactPQAllowed: return "contactPQAllowed" - case .contactPQEnabled: return "contactPQAllowed" + case .contactPQEnabled: return "contactPQEnabled" case .versionInfo: return "versionInfo" case .cmdOk: return "cmdOk" case .chatCmdError: return "chatCmdError" @@ -967,7 +959,6 @@ public enum ChatResponse: Decodable, Error { case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nsessionCode: \(sessionCode)" case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl) case .remoteCtrlStopped: return noDetails - case let .contactPQAllowed(u, contact, pqEncryption): return withUser(u, "contact: \(String(describing: contact))\npqEncryption: \(pqEncryption)") case let .contactPQEnabled(u, contact, pqEnabled): return withUser(u, "contact: \(String(describing: contact))\npqEnabled: \(pqEnabled)") case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))" case .cmdOk: return noDetails diff --git a/apps/ios/SimpleXChat/AppGroup.swift b/apps/ios/SimpleXChat/AppGroup.swift index cb07ba43ce..0511a8486c 100644 --- a/apps/ios/SimpleXChat/AppGroup.swift +++ b/apps/ios/SimpleXChat/AppGroup.swift @@ -41,7 +41,7 @@ let GROUP_DEFAULT_STORE_DB_PASSPHRASE = "storeDBPassphrase" public let GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE = "initialRandomDBPassphrase" public let GROUP_DEFAULT_CONFIRM_DB_UPGRADES = "confirmDBUpgrades" public let GROUP_DEFAULT_CALL_KIT_ENABLED = "callKitEnabled" -public let GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED = "pqExperimentalEnabled" +public let GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED = "pqExperimentalEnabled" // no longer used public let APP_GROUP_NAME = "group.chat.simplex.app" @@ -199,8 +199,6 @@ public let confirmDBUpgradesGroupDefault = BoolDefault(defaults: groupDefaults, public let callKitEnabledGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_CALL_KIT_ENABLED) -public let pqExperimentalEnabledDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED) - public class DateDefault { var defaults: UserDefaults var key: String From 9e3f528d447c42246c26f1f0dcb4a17884c244ef Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Mon, 22 Apr 2024 20:50:52 +0400 Subject: [PATCH 4/4] android: remove experimental PQ toggle (#4060) --- .../chat/simplex/common/model/SimpleXAPI.kt | 18 +------- .../chat/simplex/common/platform/Core.kt | 1 - .../simplex/common/views/chat/ChatInfoView.kt | 46 +------------------ .../views/usersettings/DeveloperView.kt | 8 ---- 4 files changed, 3 insertions(+), 70 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 71ab78634e..0bf3f23d2c 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -163,7 +163,6 @@ class AppPreferences { val confirmDBUpgrades = mkBoolPreference(SHARED_PREFS_CONFIRM_DB_UPGRADES, false) val selfDestruct = mkBoolPreference(SHARED_PREFS_SELF_DESTRUCT, false) val selfDestructDisplayName = mkStrPreference(SHARED_PREFS_SELF_DESTRUCT_DISPLAY_NAME, null) - val pqExperimentalEnabled = mkBoolPreference(SHARED_PREFS_PQ_EXPERIMENTAL_ENABLED, false) val currentTheme = mkStrPreference(SHARED_PREFS_CURRENT_THEME, DefaultTheme.SYSTEM.name) val systemDarkTheme = mkStrPreference(SHARED_PREFS_SYSTEM_DARK_THEME, DefaultTheme.SIMPLEX.name) @@ -328,7 +327,7 @@ class AppPreferences { private const val SHARED_PREFS_CONFIRM_DB_UPGRADES = "ConfirmDBUpgrades" private const val SHARED_PREFS_SELF_DESTRUCT = "LocalAuthenticationSelfDestruct" private const val SHARED_PREFS_SELF_DESTRUCT_DISPLAY_NAME = "LocalAuthenticationSelfDestructDisplayName" - private const val SHARED_PREFS_PQ_EXPERIMENTAL_ENABLED = "PQExperimentalEnabled" + private const val SHARED_PREFS_PQ_EXPERIMENTAL_ENABLED = "PQExperimentalEnabled" // no longer used private const val SHARED_PREFS_CURRENT_THEME = "CurrentTheme" private const val SHARED_PREFS_SYSTEM_DARK_THEME = "SystemDarkTheme" private const val SHARED_PREFS_THEMES = "Themes" @@ -680,15 +679,6 @@ object ChatController { throw Exception("failed to get app settings: ${r.responseType} ${r.details}") } - suspend fun apiSetPQEncryption(enable: Boolean) = sendCommandOkResp(null, CC.ApiSetPQEncryption(enable)) - - suspend fun apiSetContactPQ(rh: Long?, contactId: Long, enable: Boolean): Contact? { - val r = sendCmd(rh, CC.ApiSetContactPQ(contactId, enable)) - if (r is CR.ContactPQAllowed) return r.contact - apiErrorAlert("apiSetContactPQ", "Error allowing contact PQ", r) - return null - } - suspend fun apiExportArchive(config: ArchiveConfig) { val r = sendCmd(null, CC.ApiExportArchive(config)) if (r is CR.CmdOk) return @@ -2393,8 +2383,6 @@ sealed class CC { class SetFilesFolder(val filesFolder: String): CC() class SetRemoteHostsFolder(val remoteHostsFolder: String): CC() class ApiSetEncryptLocalFiles(val enable: Boolean): CC() - class ApiSetPQEncryption(val enable: Boolean): CC() - class ApiSetContactPQ(val contactId: Long, val enable: Boolean): CC() class ApiExportArchive(val config: ArchiveConfig): CC() class ApiImportArchive(val config: ArchiveConfig): CC() class ApiDeleteStorage: CC() @@ -2532,8 +2520,6 @@ sealed class CC { is SetFilesFolder -> "/_files_folder $filesFolder" is SetRemoteHostsFolder -> "/remote_hosts_folder $remoteHostsFolder" is ApiSetEncryptLocalFiles -> "/_files_encrypt ${onOff(enable)}" - is ApiSetPQEncryption -> "/pq ${onOff(enable)}" - is ApiSetContactPQ -> "/_pq @$contactId ${onOff(enable)}" is ApiExportArchive -> "/_db export ${json.encodeToString(config)}" is ApiImportArchive -> "/_db import ${json.encodeToString(config)}" is ApiDeleteStorage -> "/_db delete" @@ -2676,8 +2662,6 @@ sealed class CC { is SetFilesFolder -> "setFilesFolder" is SetRemoteHostsFolder -> "setRemoteHostsFolder" is ApiSetEncryptLocalFiles -> "apiSetEncryptLocalFiles" - is ApiSetPQEncryption -> "apiSetPQEncryption" - is ApiSetContactPQ -> "apiSetContactPQ" is ApiExportArchive -> "apiExportArchive" is ApiImportArchive -> "apiImportArchive" is ApiDeleteStorage -> "apiDeleteStorage" diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt index 00370f5231..0d447a4a5a 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt @@ -94,7 +94,6 @@ suspend fun initChatController(useKey: String? = null, confirmMigrations: Migrat controller.apiSetRemoteHostsFolder(remoteHostsDir.absolutePath) } controller.apiSetEncryptLocalFiles(controller.appPrefs.privacyEncryptLocalFiles.get()) - controller.apiSetPQEncryption(controller.appPrefs.pqExperimentalEnabled.get()) // If we migrated successfully means previous re-encryption process on database level finished successfully too if (appPreferences.encryptionStartedAt.get() != null) appPreferences.encryptionStartedAt.set(null) val user = chatController.apiGetActiveUser(null) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt index 42257cf723..dcd36e026b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt @@ -58,7 +58,6 @@ fun ChatInfoView( val currentUser = remember { chatModel.currentUser }.value val connStats = remember(contact.id, connectionStats) { mutableStateOf(connectionStats) } val developerTools = chatModel.controller.appPrefs.developerTools.get() - val pqExperimentalEnabled = chatModel.controller.appPrefs.pqExperimentalEnabled.get() if (chat != null && currentUser != null) { val contactNetworkStatus = remember(chatModel.networkStatuses.toMap(), contact) { mutableStateOf(chatModel.contactNetworkStatus(contact)) @@ -81,7 +80,6 @@ fun ChatInfoView( localAlias, connectionCode, developerTools, - pqExperimentalEnabled, onLocalAliasChanged = { setContactAlias(chat, it, chatModel) }, @@ -140,17 +138,6 @@ fun ChatInfoView( } }) }, - allowContactPQ = { - showAllowContactPQAlert(allowContactPQ = { - withBGApi { - val ct = chatModel.controller.apiSetContactPQ(chatRh, contact.contactId, true) - if (ct != null) { - chatModel.updateContact(chatRh, ct) - } - close.invoke() - } - }) - }, verifyClicked = { ModalManager.end.showModalCloseable { close -> remember { derivedStateOf { (chatModel.getContactChat(contact.contactId)?.chatInfo as? ChatInfo.Direct)?.contact } }.value?.let { ct -> @@ -301,7 +288,6 @@ fun ChatInfoLayout( localAlias: String, connectionCode: String?, developerTools: Boolean, - pqExperimentalEnabled: Boolean, onLocalAliasChanged: (String) -> Unit, openPreferences: () -> Unit, deleteContact: () -> Unit, @@ -310,7 +296,6 @@ fun ChatInfoLayout( abortSwitchContactAddress: () -> Unit, syncContactConnection: () -> Unit, syncContactConnectionForce: () -> Unit, - allowContactPQ: () -> Unit, verifyClicked: () -> Unit, ) { val cStats = connStats.value @@ -360,13 +345,9 @@ fun ChatInfoLayout( } val conn = contact.activeConn - if (pqExperimentalEnabled && conn != null) { - SectionView("Quantum resistant E2E encryption") { + if (conn != null) { + SectionView { InfoRow("E2E encryption", if (conn.connPQEnabled) "Quantum resistant" else "Standard") - if (!conn.pqEncryption) { - AllowContactPQButton(allowContactPQ) - SectionTextFooter("After allowing quantum resistant e2e encryption, it will be enabled after several messages if your contact also allows it.") - } SectionDividerSpaced() } } @@ -627,17 +608,6 @@ fun SynchronizeConnectionButtonForce(syncConnectionForce: () -> Unit) { ) } -@Composable -fun AllowContactPQButton(allowContactPQ: () -> Unit) { - SettingsActionItem( - painterResource(MR.images.ic_warning), - "Allow PQ encryption", - click = allowContactPQ, - textColor = WarningOrange, - iconColor = WarningOrange - ) -} - @Composable fun VerifyCodeButton(contactVerified: Boolean, onClick: () -> Unit) { SettingsActionItem( @@ -741,16 +711,6 @@ fun showSyncConnectionForceAlert(syncConnectionForce: () -> Unit) { ) } -fun showAllowContactPQAlert(allowContactPQ: () -> Unit) { - AlertManager.shared.showAlertDialog( - title = "Allow quantum resistant encryption?", - text = "This is an experimental feature, it is not recommended to enable it for important chats.", - confirmText = "Allow", - onConfirm = allowContactPQ, - destructive = true, - ) -} - @Preview @Composable fun PreviewChatInfoLayout() { @@ -768,7 +728,6 @@ fun PreviewChatInfoLayout() { localAlias = "", connectionCode = "123", developerTools = false, - pqExperimentalEnabled = false, connStats = remember { mutableStateOf(null) }, contactNetworkStatus = NetworkStatus.Connected(), onLocalAliasChanged = {}, @@ -780,7 +739,6 @@ fun PreviewChatInfoLayout() { abortSwitchContactAddress = {}, syncContactConnection = {}, syncContactConnectionForce = {}, - allowContactPQ = {}, verifyClicked = {}, ) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/DeveloperView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/DeveloperView.kt index a03a015207..ef8c30b43a 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/DeveloperView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/DeveloperView.kt @@ -57,14 +57,6 @@ fun DeveloperView( SettingsPreferenceItem(painterResource(MR.images.ic_report), stringResource(MR.strings.show_internal_errors), appPreferences.showInternalErrors) SettingsPreferenceItem(painterResource(MR.images.ic_avg_pace), stringResource(MR.strings.show_slow_api_calls), appPreferences.showSlowApiCalls) } - - SectionSpacer() - SectionView("Experimental".uppercase()) { - SettingsPreferenceItem(painterResource(MR.images.ic_vpn_key_filled), "Post-quantum E2EE", m.controller.appPrefs.pqExperimentalEnabled, onChange = { enable -> - withBGApi { m.controller.apiSetPQEncryption(enable) } - }) - SectionTextFooter("In this version applies only to new contacts.") - } } SectionBottomSpacer() }