core (pq): cross-version tests (#3885)

* core (pq): cross-version tests

* next (fails)

* enable all tests

* fix versions

* update simplexmq

* tests

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
spaced4ndy
2024-03-10 18:57:57 +04:00
committed by GitHub
parent 60a73a539e
commit 8660bf420a
5 changed files with 141 additions and 10 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
location: https://github.com/simplex-chat/simplexmq.git
tag: dab55e0a9b03577f643af7922afa061801d82ed5
tag: 851ed2d02e2a78c15893ad8bc9c5a4d917eb6a35
source-repository-package
type: git
+1 -1
View File
@@ -1,5 +1,5 @@
{
"https://github.com/simplex-chat/simplexmq.git"."dab55e0a9b03577f643af7922afa061801d82ed5" = "0dzqsvzxby83nla0rpx3xzj2y18lvmgs5ldjv5i1yp52npc88s1m";
"https://github.com/simplex-chat/simplexmq.git"."851ed2d02e2a78c15893ad8bc9c5a4d917eb6a35" = "0rm13iknnqhdb42nmyjc2wj85z23p337bp026ihnychax5s1216j";
"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";
+29 -5
View File
@@ -28,6 +28,7 @@ import Simplex.Chat
import Simplex.Chat.Controller (ChatCommand (..), ChatConfig (..), ChatController (..), ChatDatabase (..), ChatLogLevel (..))
import Simplex.Chat.Core
import Simplex.Chat.Options
import Simplex.Chat.Protocol (currentChatVersion, pqEncryptionCompressionVersion)
import Simplex.Chat.Store
import Simplex.Chat.Store.Profiles
import Simplex.Chat.Terminal
@@ -37,12 +38,13 @@ import Simplex.FileTransfer.Description (kb, mb)
import Simplex.FileTransfer.Server (runXFTPServerBlocking)
import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..), defaultFileExpiration)
import Simplex.Messaging.Agent.Env.SQLite
import Simplex.Messaging.Agent.Protocol (supportedSMPAgentVRange, pattern VersionSMPA)
import Simplex.Messaging.Agent.Protocol (currentSMPAgentVersion, duplexHandshakeSMPAgentVersion, pqdrSMPAgentVersion, supportedSMPAgentVRange)
import Simplex.Messaging.Agent.RetryInterval
import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..))
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
import Simplex.Messaging.Client (ProtocolClientConfig (..), defaultNetworkConfig)
import Simplex.Messaging.Crypto.Ratchet (supportedE2EEncryptVRange, pattern PQSupportOff, pattern VersionE2E)
import Simplex.Messaging.Crypto.Ratchet (supportedE2EEncryptVRange, pattern PQSupportOff)
import qualified Simplex.Messaging.Crypto.Ratchet as CR
import Simplex.Messaging.Server (runSMPServerBlocking)
import Simplex.Messaging.Server.Env.STM
import Simplex.Messaging.Transport
@@ -147,22 +149,38 @@ testAgentCfgVPrev =
smpCfg = (smpCfg testAgentCfg) {serverVRange = prevRange $ serverVRange $ smpCfg testAgentCfg}
}
testAgentCfgVNext :: AgentConfig
testAgentCfgVNext =
testAgentCfg
{ smpClientVRange = nextRange $ smpClientVRange testAgentCfg,
smpAgentVRange = \_ -> mkVersionRange duplexHandshakeSMPAgentVersion $ max pqdrSMPAgentVersion currentSMPAgentVersion,
e2eEncryptVRange = \_ -> mkVersionRange CR.kdfX3DHE2EEncryptVersion $ max CR.pqRatchetE2EEncryptVersion CR.currentE2EEncryptVersion,
smpCfg = (smpCfg testAgentCfg) {serverVRange = nextRange $ serverVRange $ smpCfg testAgentCfg}
}
testAgentCfgV1 :: AgentConfig
testAgentCfgV1 =
testAgentCfg
{ smpClientVRange = v1Range,
smpAgentVRange = \_ -> versionToRange (VersionSMPA 2), -- duplexHandshakeSMPAgentVersion,
e2eEncryptVRange = \_ -> versionToRange (VersionE2E 2), -- kdfX3DHE2EEncryptVersion,
smpAgentVRange = \_ -> versionToRange duplexHandshakeSMPAgentVersion,
e2eEncryptVRange = \_ -> versionToRange CR.kdfX3DHE2EEncryptVersion,
smpCfg = (smpCfg testAgentCfg) {serverVRange = versionToRange batchCmdsSMPVersion}
}
testCfgVPrev :: ChatConfig
testCfgVPrev =
testCfg
{ chatVRange = prevRange . chatVRange testCfg,
{ chatVRange = \_ -> prevRange $ chatVRange testCfg PQSupportOff,
agentConfig = testAgentCfgVPrev
}
testCfgVNext :: ChatConfig
testCfgVNext =
testCfg
{ chatVRange = \_ -> mkVersionRange initialChatVersion $ max pqEncryptionCompressionVersion currentChatVersion,
agentConfig = testAgentCfgVNext
}
testCfgV1 :: ChatConfig
testCfgV1 =
testCfg
@@ -173,12 +191,18 @@ testCfgV1 =
prevRange :: VersionRange v -> VersionRange v
prevRange vr = vr {maxVersion = max (minVersion vr) (prevVersion $ maxVersion vr)}
nextRange :: VersionRange v -> VersionRange v
nextRange vr = vr {maxVersion = max (minVersion vr) (nextVersion $ maxVersion vr)}
v1Range :: VersionRange v
v1Range = mkVersionRange (Version 1) (Version 1)
prevVersion :: Version v -> Version v
prevVersion (Version v) = Version (v - 1)
nextVersion :: Version v -> Version v
nextVersion (Version v) = Version (v + 1)
testCfgCreateGroupDirect :: ChatConfig
testCfgCreateGroupDirect =
mkCfgCreateGroupDirect testCfg
+77 -2
View File
@@ -23,9 +23,9 @@ import Simplex.Chat.Controller (ChatConfig (..))
import Simplex.Chat.Options (ChatOpts (..))
import Simplex.Chat.Protocol (currentChatVersion, pqEncryptionCompressionVersion, supportedChatVRange)
import Simplex.Chat.Store (agentStoreFile, chatStoreFile)
import Simplex.Chat.Types (VersionRangeChat, authErrDisableCount, sameVerificationCode, verificationCode, pattern VersionChat)
import Simplex.Chat.Types (VersionRangeChat, authErrDisableCount, sameVerificationCode, verificationCode, VersionChat, pattern VersionChat)
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), pattern PQSupportOn, pattern PQSupportOff, pattern PQEncOn, pattern PQEncOff)
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)
@@ -131,6 +131,8 @@ chatDirectTests = do
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
@@ -2821,6 +2823,79 @@ runTestPQConnectViaAddress (alice, aPQ) (bob, bPQ) = do
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 <- genProfileImgForLink
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 <- genProfileImgForAddress
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
+33 -1
View File
@@ -13,6 +13,7 @@ import Control.Concurrent.Async (concurrently_)
import Control.Concurrent.STM
import Control.Monad (unless, when)
import Control.Monad.Except (runExceptT)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Base64 as B64
import qualified Data.ByteString.Char8 as B
import Data.Char (isDigit)
@@ -128,6 +129,23 @@ pqMatrix2 runTest = do
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
@@ -236,7 +254,21 @@ sndRcvImg pqEnc enabled (cc1, msg, v1) (cc2, v2) = do
cc2 `pqRcvForContact` 2 `shouldReturn` pqEnc
cc2 `pqVerForContact` 2 `shouldReturn` v2
where
lrgLen = maxEncodedMsgLength PQSupportOff * 3 `div` 4 - 98 -- this is max size for binary image preview given the rest of the message
lrgLen = maxEncodedMsgLength PQSupportOff * 3 `div` 4 - 110 -- 98 is ~ max size for binary image preview given the rest of the message
genProfileImgForLink :: IO ByteString
genProfileImgForLink = do
g <- C.newRandom
atomically $ B64.encode <$> C.randomBytes lrgLen g
where
lrgLen = maxConnInfoLength PQSupportOff * 3 `div` 4 - 240 -- 214 is the magic number to make tests pass (10737)
genProfileImgForAddress :: IO ByteString
genProfileImgForAddress = do
g <- C.newRandom
atomically $ B64.encode <$> C.randomBytes lrgLen g
where
lrgLen = maxConnInfoLength PQSupportOff * 3 `div` 4 - 260 -- 238 is the magic number to make tests pass (10713)
-- PQ combinators /