Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/2-features/mls-stale-app-messages
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MLS application messages for older epochs are now rejected
27 changes: 27 additions & 0 deletions integration/test/Test/MLS.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,33 @@ testSendMessageNoReturnToSender = do
)
wsSender

testStaleApplicationMessage :: HasCallStack => Domain -> App ()
testStaleApplicationMessage otherDomain = do
[alice, bob, charlie, dave, eve] <-
createAndConnectUsers [OwnDomain, otherDomain, OwnDomain, OwnDomain, OwnDomain]
[alice1, bob1, charlie1] <- traverse createMLSClient [alice, bob, charlie]
traverse_ uploadNewKeyPackage [bob1, charlie1]
void $ createNewGroup alice1

-- alice adds bob first
void $ createAddCommit alice1 [bob] >>= sendAndConsumeCommitBundle

-- bob prepares some application messages
[msg1, msg2] <- replicateM 2 $ createApplicationMessage bob1 "hi alice"

-- alice adds charlie and dave with different commits
void $ createAddCommit alice1 [charlie] >>= sendAndConsumeCommitBundle
void $ createAddCommit alice1 [dave] >>= sendAndConsumeCommitBundle

-- bob's application messages still go through
void $ postMLSMessage bob1 msg1.message >>= getJSON 201

-- alice adds eve
void $ createAddCommit alice1 [eve] >>= sendAndConsumeCommitBundle

-- bob's application messages are now rejected
void $ postMLSMessage bob1 msg2.message >>= getJSON 409

testMixedProtocolUpgrade :: HasCallStack => Domain -> App ()
testMixedProtocolUpgrade secondDomain = do
(alice, tid) <- createTeam OwnDomain
Expand Down
14 changes: 13 additions & 1 deletion services/galley/src/Galley/API/MLS/Message.hs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ postMLSMessageToLocalConv ::
Sem r ([LocalConversationUpdate], Maybe UnreachableUsers)
postMLSMessageToLocalConv qusr c con msg ctype convOrSubId = do
lConvOrSub <- fetchConvOrSub qusr msg.groupId ctype convOrSubId
let convOrSub = tUnqualified lConvOrSub

for_ msg.sender $ \sender ->
void $ getSenderIdentity qusr c sender lConvOrSub
Expand All @@ -380,12 +381,23 @@ postMLSMessageToLocalConv qusr c con msg ctype convOrSubId = do
IncomingMessageContentPublic pub -> case pub.content of
FramedContentCommit _commit -> throwS @'MLSUnsupportedMessage
FramedContentApplicationData _ -> throwS @'MLSUnsupportedMessage
-- proposal message
FramedContentProposal prop ->
processProposal qusr lConvOrSub msg.groupId msg.epoch pub prop
IncomingMessageContentPrivate -> do
when ((tUnqualified lConvOrSub).migrationState == MLSMigrationMixed) $
-- application message:

-- reject all application messages if the conv is in mixed state
when (convOrSub.migrationState == MLSMigrationMixed) $
throwS @'MLSUnsupportedMessage

-- reject application messages older than 2 epochs
let epochInt :: Epoch -> Integer
epochInt = fromIntegral . epochNumber
when
(epochInt msg.epoch < epochInt convOrSub.mlsMeta.cnvmlsEpoch - 2)
$ throwS @'MLSStaleMessage

unreachables <- propagateMessage qusr (Just c) lConvOrSub con msg.rawMessage (tUnqualified lConvOrSub).members
pure ([], unreachables)

Expand Down