Skip to content

Commit 2d3faff

Browse files
smattingpcapriotti
andauthored
MLS: Backend sends remove proposal upon user deletion (#2650)
Co-authored-by: Paolo Capriotti <[email protected]>
1 parent 33be1d9 commit 2d3faff

File tree

28 files changed

+486
-118
lines changed

28 files changed

+486
-118
lines changed

cassandra-schema.cql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ CREATE TABLE galley_test.member (
169169
conversation_role text,
170170
hidden boolean,
171171
hidden_ref text,
172-
mls_clients set<text>,
172+
mls_clients_keypackages set<frozen<tuple<text, blob>>>,
173173
otr_archived boolean,
174174
otr_archived_ref text,
175175
otr_muted boolean,
@@ -263,7 +263,7 @@ CREATE TABLE galley_test.member_remote_user (
263263
user_remote_domain text,
264264
user_remote_id uuid,
265265
conversation_role text,
266-
mls_clients set<text>,
266+
mls_clients_keypackages set<frozen<tuple<text, blob>>>,
267267
PRIMARY KEY (conv, user_remote_domain, user_remote_id)
268268
) WITH CLUSTERING ORDER BY (user_remote_domain ASC, user_remote_id ASC)
269269
AND bloom_filter_fp_chance = 0.1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
External remove proposals are now sent to a group when a user is deleted
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
../../../cassandra-schema.cql
1+
../../../../cassandra-schema.cql

libs/galley-types/src/Galley/Types/Conversations/Members.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,14 @@ import qualified Data.Set as Set
3737
import Imports
3838
import Wire.API.Conversation
3939
import Wire.API.Conversation.Role (RoleName, roleNameWireAdmin)
40+
import Wire.API.MLS.KeyPackage
4041
import Wire.API.Provider.Service (ServiceRef)
4142

4243
-- | Internal (cassandra) representation of a remote conversation member.
4344
data RemoteMember = RemoteMember
4445
{ rmId :: Remote UserId,
4546
rmConvRoleName :: RoleName,
46-
rmMLSClients :: Set ClientId
47+
rmMLSClients :: Set (ClientId, KeyPackageRef)
4748
}
4849
deriving stock (Show)
4950

@@ -64,7 +65,7 @@ data LocalMember = LocalMember
6465
lmStatus :: MemberStatus,
6566
lmService :: Maybe ServiceRef,
6667
lmConvRoleName :: RoleName,
67-
lmMLSClients :: Set ClientId
68+
lmMLSClients :: Set (ClientId, KeyPackageRef)
6869
}
6970
deriving stock (Show)
7071

libs/wire-api/src/Wire/API/Error/Galley.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ type instance MapError 'MLSClientMismatch = 'StaticError 409 "mls-client-mismatc
192192

193193
type instance MapError 'MLSStaleMessage = 'StaticError 409 "mls-stale-message" "The conversation epoch in a message is too old"
194194

195-
type instance MapError 'MLSCommitMissingReferences = 'StaticError 409 "mls-commit-missing-references" "The commit is not refrencing all pending proposals"
195+
type instance MapError 'MLSCommitMissingReferences = 'StaticError 409 "mls-commit-missing-references" "The commit is not referencing all pending proposals"
196196

197197
type instance MapError 'MLSSelfRemovalNotAllowed = 'StaticError 409 "mls-self-removal-not-allowed" "Self removal from group is not allowed"
198198

libs/wire-api/src/Wire/API/MLS/Credential.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
module Wire.API.MLS.Credential where
2121

22+
import Cassandra.CQL
2223
import Control.Error.Util
2324
import Control.Lens ((?~))
2425
import Data.Aeson (FromJSON (..), FromJSONKey (..), ToJSON (..), ToJSONKey (..))
@@ -84,6 +85,14 @@ data SignatureSchemeTag = Ed25519
8485
deriving stock (Bounded, Enum, Eq, Ord, Show, Generic)
8586
deriving (Arbitrary) via GenericUniform SignatureSchemeTag
8687

88+
instance Cql SignatureSchemeTag where
89+
ctype = Tagged TextColumn
90+
toCql = CqlText . signatureSchemeName
91+
fromCql (CqlText name) =
92+
note ("Unexpected signature scheme: " <> T.unpack name) $
93+
signatureSchemeFromName name
94+
fromCql _ = Left "SignatureScheme: Text expected"
95+
8796
signatureSchemeNumber :: SignatureSchemeTag -> Word16
8897
signatureSchemeNumber Ed25519 = 0x807
8998

libs/wire-api/src/Wire/API/MLS/KeyPackage.hs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,15 @@ module Wire.API.MLS.KeyPackage
3737
)
3838
where
3939

40+
import Cassandra.CQL hiding (Set)
4041
import Control.Applicative
4142
import Control.Lens hiding (set, (.=))
4243
import Data.Aeson (FromJSON, ToJSON)
4344
import Data.Binary
4445
import Data.Binary.Get
4546
import Data.Binary.Put
4647
import qualified Data.ByteString as B
48+
import qualified Data.ByteString.Lazy as LBS
4749
import Data.Id
4850
import Data.Json.Util
4951
import Data.Qualified
@@ -79,6 +81,12 @@ instance ToSchema KeyPackageData where
7981
.= named "KeyPackage" base64Schema
8082
)
8183

84+
instance Cql KeyPackageData where
85+
ctype = Tagged BlobColumn
86+
toCql = CqlBlob . LBS.fromStrict . kpData
87+
fromCql (CqlBlob b) = pure . KeyPackageData . LBS.toStrict $ b
88+
fromCql _ = Left "Expected CqlBlob"
89+
8290
data KeyPackageBundleEntry = KeyPackageBundleEntry
8391
{ kpbeUser :: Qualified UserId,
8492
kpbeClient :: ClientId,
@@ -132,6 +140,12 @@ instance ParseMLS KeyPackageRef where
132140
instance SerialiseMLS KeyPackageRef where
133141
serialiseMLS = putByteString . unKeyPackageRef
134142

143+
instance Cql KeyPackageRef where
144+
ctype = Tagged BlobColumn
145+
toCql = CqlBlob . LBS.fromStrict . unKeyPackageRef
146+
fromCql (CqlBlob b) = pure . KeyPackageRef . LBS.toStrict $ b
147+
fromCql _ = Left "Expected CqlBlob"
148+
135149
-- | Compute key package ref given a ciphersuite and the raw key package data.
136150
kpRef :: CipherSuiteTag -> KeyPackageData -> KeyPackageRef
137151
kpRef cs =

libs/wire-api/src/Wire/API/MLS/Message.hs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,11 @@ module Wire.API.MLS.Message
3838
MLSMessageSendingStatus (..),
3939
KnownFormatTag (..),
4040
verifyMessageSignature,
41-
mkRemoveProposalMessage,
41+
mkSignedMessage,
4242
)
4343
where
4444

4545
import Control.Lens ((?~))
46-
import Crypto.Error
4746
import Crypto.PubKey.Ed25519
4847
import qualified Data.Aeson as A
4948
import Data.Binary
@@ -341,14 +340,14 @@ verifyMessageSignature :: CipherSuiteTag -> Message 'MLSPlainText -> ByteString
341340
verifyMessageSignature cs msg pubkey =
342341
csVerifySignature cs pubkey (rmRaw (msgTBS msg)) (msgSignature (msgExtraFields msg))
343342

344-
mkRemoveProposalMessage ::
343+
mkSignedMessage ::
345344
SecretKey ->
346345
PublicKey ->
347346
GroupId ->
348347
Epoch ->
349-
KeyPackageRef ->
350-
Maybe (Message 'MLSPlainText)
351-
mkRemoveProposalMessage priv pub gid epoch ref = maybeCryptoError $ do
348+
MessagePayload 'MLSPlainText ->
349+
Message 'MLSPlainText
350+
mkSignedMessage priv pub gid epoch payload =
352351
let tbs =
353352
mkRawMLS $
354353
MessageTBS
@@ -357,7 +356,7 @@ mkRemoveProposalMessage priv pub gid epoch ref = maybeCryptoError $ do
357356
tbsMsgEpoch = epoch,
358357
tbsMsgAuthData = mempty,
359358
tbsMsgSender = PreconfiguredSender 0,
360-
tbsMsgPayload = ProposalMessage (mkRemoveProposal ref)
359+
tbsMsgPayload = payload
361360
}
362-
let sig = BA.convert $ sign priv pub (rmRaw tbs)
363-
pure (Message tbs (MessageExtraFields sig Nothing Nothing))
361+
sig = BA.convert $ sign priv pub (rmRaw tbs)
362+
in Message tbs (MessageExtraFields sig Nothing Nothing)

libs/wire-api/src/Wire/API/MLS/Serialisation.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ module Wire.API.MLS.Serialisation
3232
fromMLSEnum,
3333
toMLSEnum',
3434
toMLSEnum,
35+
encodeMLS,
36+
encodeMLS',
3537
decodeMLS,
3638
decodeMLS',
3739
decodeMLSWith,
@@ -173,6 +175,13 @@ newtype BinaryMLS a = BinaryMLS a
173175
instance Binary a => ParseMLS (BinaryMLS a) where
174176
parseMLS = BinaryMLS <$> get
175177

178+
-- | Encode an MLS value to a lazy bytestring.
179+
encodeMLS :: SerialiseMLS a => a -> LByteString
180+
encodeMLS = runPut . serialiseMLS
181+
182+
encodeMLS' :: SerialiseMLS a => a -> ByteString
183+
encodeMLS' = LBS.toStrict . encodeMLS
184+
176185
-- | Decode an MLS value from a lazy bytestring. Return an error message in case of failure.
177186
decodeMLS :: ParseMLS a => LByteString -> Either Text a
178187
decodeMLS = decodeMLSWith parseMLS

libs/wire-api/test/unit/Test/Wire/API/MLS.hs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,27 @@ testRemoveProposalMessageSignature = withSystemTempDirectory "mls" $ \tmp -> do
192192

193193
secretKey <- Ed25519.generateSecretKey
194194
let publicKey = Ed25519.toPublic secretKey
195-
let message = fromJust (mkRemoveProposalMessage secretKey publicKey gid (Epoch 1) (fromJust (kpRef' kp)))
195+
let message = mkSignedMessage secretKey publicKey gid (Epoch 1) (ProposalMessage (mkRemoveProposal (fromJust (kpRef' kp))))
196+
196197
let messageFilename = "signed-message.mls"
197198
BS.writeFile (tmp </> messageFilename) (rmRaw (mkRawMLS message))
198199
let signerKeyFilename = "signer-key.bin"
199200
BS.writeFile (tmp </> signerKeyFilename) (convert publicKey)
200201

201-
void . liftIO $ spawn (cli qcid tmp ["check-signature", "--group", tmp </> groupFilename, "--message", tmp </> messageFilename, "--signer-key", tmp </> signerKeyFilename]) Nothing
202+
void . liftIO $
203+
spawn
204+
( cli
205+
qcid
206+
tmp
207+
[ "consume",
208+
"--group",
209+
tmp </> groupFilename,
210+
"--signer-key",
211+
tmp </> signerKeyFilename,
212+
tmp </> messageFilename
213+
]
214+
)
215+
Nothing
202216

203217
createGroup :: FilePath -> String -> String -> GroupId -> IO ()
204218
createGroup tmp store groupName gid = do

0 commit comments

Comments
 (0)