Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
3e1f203
Add variable-sized integer serialisation
pcapriotti Feb 28, 2023
d264162
Implement new MLS structures
smatting Mar 1, 2023
8af5135
Fix KeyPackage parser
pcapriotti Mar 22, 2023
87e588b
Fix MLS signature verification
pcapriotti Mar 23, 2023
26ce33d
Update paths now contain leaf nodes
pcapriotti Mar 24, 2023
7dcc23f
Remove proposals now have indices instead of refs
pcapriotti Mar 27, 2023
0689a26
Adapt integration tests to remove proposal changes
pcapriotti Apr 3, 2023
21c4f3a
Compute new node index for add proposals
pcapriotti Apr 4, 2023
5418005
New commit bundle API
stefanwire Apr 4, 2023
9cef1fa
Add instances for roundtrip tests of MLS types
pcapriotti Apr 5, 2023
67a6118
fix adding users to MLS conversations
stefanwire Apr 6, 2023
404b35a
fix integration test: send other user's commit
stefanwire Apr 11, 2023
0def49c
readGroupState for the new group.json format
pcapriotti Apr 12, 2023
c3d9d0c
Generate welcome recipients when processing bundle
pcapriotti Apr 12, 2023
d1cfaaf
Send recipients as part of a welcome RPC
pcapriotti Apr 12, 2023
c190ce3
Use commit bundles in failure tests
pcapriotti Apr 12, 2023
482f865
Implement new proposal ref computation
pcapriotti Apr 12, 2023
c178e0d
fix integration test admin removes user from a conversation
stefanwire Apr 13, 2023
bf90649
switch mls-test-cli call to external-proposal
stefanwire Apr 13, 2023
6ad8bd2
Implement validation of leaf nodes in galley
pcapriotti Apr 13, 2023
9a00f3d
Apply proposals in the correct order
pcapriotti Apr 14, 2023
ecdab59
Remove redundant GroupContext structure
pcapriotti Apr 14, 2023
3e4f5f1
Re-implement processing of external commits
pcapriotti Apr 14, 2023
2896665
add references from data types to MLS spec
stefanwire Apr 14, 2023
4b756f4
Remove key package mapping code
pcapriotti Apr 18, 2023
d93febf
fix more integration tests
stefanwire Apr 18, 2023
d03d750
track client scheduled for removal in Cassandra
stefanwire Apr 18, 2023
59258c3
minor typos
stefanwire Apr 20, 2023
5161420
split executing proposals for int and ext commits
stefanwire Apr 20, 2023
003e625
execute remove proposals before add proposals
stefanwire Apr 20, 2023
d9a03b4
rename Word32 and ref to LeafIndex and idx
stefanwire Apr 21, 2023
31c66ee
Remove MissingSenderClient error
pcapriotti Apr 18, 2023
08895ea
Remove some prefixes from MLS structures
pcapriotti Apr 18, 2023
82969e3
Remove prefixes from RawMLS fields
pcapriotti Apr 18, 2023
edf4647
Reorganise TODOs
pcapriotti Apr 21, 2023
54c074a
Check epoch again after taking commit lock
pcapriotti Apr 21, 2023
26209c6
Remove MLSPackageRefNotFound error
pcapriotti Apr 21, 2023
6c3242a
Simplify testRemoveUserParent
pcapriotti Apr 21, 2023
25a3c48
Simplify testRemoveCreatorParent
pcapriotti Apr 21, 2023
dcf12bb
Pass correct list of clients to planClientRemoval
pcapriotti Apr 24, 2023
aebb275
Fix assertion in external add proposal test
pcapriotti Apr 24, 2023
85c0d6a
Propagate actual message, not just commit
pcapriotti Apr 24, 2023
041e93c
Fix signature calculation when generating messages
pcapriotti Apr 25, 2023
805ed96
Pass removal key to mls-test-cli on group creation
pcapriotti Apr 25, 2023
8c567a4
Take pending clients into account in removal logic
pcapriotti Apr 25, 2023
52d7f14
Fix assertion in remove proposal test
pcapriotti Apr 25, 2023
3b764fa
apply linter suggestions
stefanwire Apr 26, 2023
bf63e2d
fix unit test: MLS remove proposal
stefanwire Apr 26, 2023
58021a7
Upgrade mls-test-cli in the nix environment
pcapriotti Apr 26, 2023
1ced1de
Update cassandra-schema.cql
pcapriotti Apr 27, 2023
31dbc38
disable testing the keypackage lifetime
stefanwire Apr 27, 2023
99eba2b
remove checks for keypackage assignments
stefanwire Apr 27, 2023
415695e
validate bare proposals and inline proposal
stefanwire Apr 27, 2023
4bb764b
rephrase and filter the left TODOs
stefanwire Apr 27, 2023
6143306
Verify that capabilities include basic credentials
pcapriotti Apr 28, 2023
709f2c3
Add nonce to PreSharedKeyID structure
pcapriotti Apr 28, 2023
da40f1b
Split Galley.API.MLS.Message
pcapriotti Apr 28, 2023
8959662
Inline executeIntCommitProposalAction
pcapriotti Apr 28, 2023
8df69f4
Use more specific type for external commit actions
pcapriotti Apr 28, 2023
d115873
Re-organise TODOs
pcapriotti Apr 28, 2023
887a83a
Simplify processProposal arguments
pcapriotti Apr 28, 2023
a12fd75
Remove LWT in planMLSClientRemoval
pcapriotti Apr 28, 2023
0eea5fb
Restore unsupported proposal test
pcapriotti May 2, 2023
a2ab595
Restore disabled MLS unit tests
pcapriotti May 2, 2023
9f14837
Add CHANGELOG entries
pcapriotti May 2, 2023
3287ad8
Document IndexMap and ClientMap
pcapriotti May 2, 2023
99d1a51
fixup! Restore unsupported proposal test
pcapriotti May 3, 2023
bb7817a
Linter fix
pcapriotti May 3, 2023
642b1ba
fixup! Upgrade mls-test-cli in the nix environment
pcapriotti May 3, 2023
8d99e70
Fix: make git-add-cassandra-schema-impl lists to many keyspaces
smatting May 3, 2023
482d7c0
postMLSMessageToLocalConv: return no events
smatting May 3, 2023
7f4ac30
Remove unused paExternalInit
smatting May 3, 2023
33927e1
Renew certificates for e2e integration tests (#3243)
akshaymankar Apr 24, 2023
981ef85
fix broken tests
smatting May 3, 2023
c093266
ExternalCommitAction: remove superfluous ClientIdentity
smatting May 3, 2023
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
6 changes: 1 addition & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,7 @@ git-add-cassandra-schema: db-migrate git-add-cassandra-schema-impl

.PHONY: git-add-cassandra-schema-impl
git-add-cassandra-schema-impl:
$(eval CASSANDRA_CONTAINER := $(shell docker ps | grep '/cassandra:' | perl -ne '/^(\S+)\s/ && print $$1'))
( echo '-- automatically generated with `make git-add-cassandra-schema`'; \
docker exec -i $(CASSANDRA_CONTAINER) /usr/bin/cqlsh -e "DESCRIBE schema;" ) \
| sed "s/CREATE TABLE galley_test.member_client/-- NOTE: this table is unused. It was replaced by mls_group_member_client\nCREATE TABLE galley_test.member_client/g" \
> ./cassandra-schema.cql
./hack/bin/cassandra_dump_schema > ./cassandra-schema.cql
git add ./cassandra-schema.cql

.PHONY: cqlsh
Expand Down
2 changes: 2 additions & 0 deletions cassandra-schema.cql
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,8 @@ CREATE TABLE galley_test.mls_group_member_client (
user uuid,
client text,
key_package_ref blob,
leaf_node_index int,
removal_pending boolean,
PRIMARY KEY (group_id, user_domain, user, client)
) WITH CLUSTERING ORDER BY (user_domain ASC, user ASC, client ASC)
AND bloom_filter_fp_chance = 0.01
Expand Down
7 changes: 7 additions & 0 deletions changelog.d/1-api-changes/mls-upgrade
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Switch to MLS draft 20. The following endpoints are affected by the change:

- All endpoints with `message/mls` content type now expect and return draft-20 MLS structures.
- `POST /conversations` does not require `creator_client` anymore.
- `POST /mls/commit-bundles` now expects a "stream" of MLS messages, i.e. a sequence of TLS-serialised messages, one after the other, in any order. Its protobuf interface has been removed.
- `POST /mls/welcome` has been removed. Welcome messages can now only be sent as part of a commit bundle.
- `POST /mls/message` does not accept commit messages anymore. All commit messages must be sent as part of a commit bundle.
1 change: 1 addition & 0 deletions changelog.d/5-internal/key-package-mapping
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Brig does not perform key package ref mapping anymore. Claimed key packages are simply removed from the `mls_key_packages` table. The `mls_key_package_refs` table is now unused, and will be removed in the future.
32 changes: 32 additions & 0 deletions hack/bin/cassandra_dump_schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env python3

import subprocess
from subprocess import PIPE
from itertools import zip_longest
import re

def run_cqlsh(container, expr):
p = subprocess.run(["docker", "exec", "-i", container, '/usr/bin/cqlsh', '-e', expr], stdout=PIPE, check=True).stdout.decode('utf8').strip()
return p

def transpose(a):
return [x for col in zip_longest(*a, fillvalue='') for x in col]

def main():
container = subprocess.run(["docker", "ps", "--filter=name=cassandra", "--format={{.ID}}"], stdout=PIPE, check=True).stdout.decode('utf8').rstrip()
s = run_cqlsh(container, 'DESCRIBE keyspaces;')

ks = []
for line in s.splitlines():
ks.append(re.split('\s+', line))

keyspaces = transpose(ks)
print("-- automatically generated with `make git-add-cassandra-schema`\n")
for keyspace in keyspaces:
if keyspace.endswith('_test'):
s = run_cqlsh(container, f'DESCRIBE keyspace {keyspace}')
print(s.replace('CREATE TABLE galley_test.member_client','-- NOTE: this table is unused. It was replaced by mls_group_member_client\nCREATE TABLE galley_test.member_client'))
print()

if __name__ == '__main__':
main()
2 changes: 1 addition & 1 deletion hack/python/wire/mlscli.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def add_member(state, kpfiles):
"<group-in>",
"--welcome-out",
welcome_file,
"--group-state-out",
"--group-info-out",
pgs_file,
"--group-out",
"<group-out>",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import Test.QuickCheck (Arbitrary)
import Wire.API.Federation.API.Common
import Wire.API.Federation.Endpoint
import Wire.API.Federation.Version
import Wire.API.MLS.Credential
import Wire.API.MLS.CipherSuite
import Wire.API.MLS.KeyPackage
import Wire.API.User (UserProfile)
import Wire.API.User.Client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,11 @@ data ConversationUpdateResponse
via (CustomEncoded ConversationUpdateResponse)

-- | A wrapper around a raw welcome message
newtype MLSWelcomeRequest = MLSWelcomeRequest
{ unMLSWelcomeRequest :: Base64ByteString
data MLSWelcomeRequest = MLSWelcomeRequest
{ -- | A serialised welcome message.
welcomeMessage :: Base64ByteString,
-- | Recipients local to the target backend.
recipients :: [(UserId, ClientId)]
}
deriving stock (Eq, Generic, Show)
deriving (Arbitrary) via (GenericUniform MLSWelcomeRequest)
Expand Down
1 change: 1 addition & 0 deletions libs/wire-api/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ mkDerivation {
process
proto-lens
QuickCheck
random
saml2-web-sso
schema-profunctor
servant
Expand Down
7 changes: 2 additions & 5 deletions libs/wire-api/src/Wire/API/Error/Galley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ data GalleyError
MLSNotEnabled
| MLSNonEmptyMemberList
| MLSDuplicatePublicKey
| MLSKeyPackageRefNotFound
| MLSInvalidLeafNodeIndex
| MLSUnsupportedMessage
| MLSProposalNotFound
| MLSUnsupportedProposal
Expand All @@ -85,7 +85,6 @@ data GalleyError
| MLSClientSenderUserMismatch
| MLSWelcomeMismatch
| MLSMissingGroupInfo
| MLSMissingSenderClient
| MLSUnexpectedSenderClient
| MLSSubConvUnsupportedConvType
| MLSSubConvClientNotInParent
Expand Down Expand Up @@ -201,7 +200,7 @@ type instance MapError 'MLSNonEmptyMemberList = 'StaticError 400 "non-empty-memb

type instance MapError 'MLSDuplicatePublicKey = 'StaticError 400 "mls-duplicate-public-key" "MLS public key for the given signature scheme already exists"

type instance MapError 'MLSKeyPackageRefNotFound = 'StaticError 404 "mls-key-package-ref-not-found" "A referenced key package could not be mapped to a known client"
type instance MapError 'MLSInvalidLeafNodeIndex = 'StaticError 400 "mls-invalid-leaf-node-index" "A referenced leaf node index points to a blank or non-existing node"

type instance MapError 'MLSUnsupportedMessage = 'StaticError 422 "mls-unsupported-message" "Attempted to send a message with an unsupported combination of content type and wire format"

Expand All @@ -227,8 +226,6 @@ type instance MapError 'MLSWelcomeMismatch = 'StaticError 400 "mls-welcome-misma

type instance MapError 'MLSMissingGroupInfo = 'StaticError 404 "mls-missing-group-info" "The conversation has no group information"

type instance MapError 'MLSMissingSenderClient = 'StaticError 403 "mls-missing-sender-client" "The client has to refresh their access token and provide their client ID"

type instance MapError 'MLSSubConvUnsupportedConvType = 'StaticError 403 "mls-subconv-unsupported-convtype" "MLS subconversations are only supported for regular conversations"

type instance MapError 'MLSSubConvClientNotInParent = 'StaticError 403 "mls-subconv-join-parent-missing" "MLS client cannot join the subconversation because it is not member of the parent conversation"
Expand Down
111 changes: 111 additions & 0 deletions libs/wire-api/src/Wire/API/MLS/AuthenticatedContent.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com>
--
-- This program is free software: you can redistribute it and/or modify it under
-- the terms of the GNU Affero General Public License as published by the Free
-- Software Foundation, either version 3 of the License, or (at your option) any
-- later version.
--
-- This program is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
-- details.
--
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see <https://www.gnu.org/licenses/>.

module Wire.API.MLS.AuthenticatedContent
( AuthenticatedContent (..),
TaggedSender (..),
authContentRef,
publicMessageRef,
mkSignedPublicMessage,
)
where

import Crypto.PubKey.Ed25519
import Imports
import Wire.API.MLS.CipherSuite
import Wire.API.MLS.Context
import Wire.API.MLS.Epoch
import Wire.API.MLS.Group
import Wire.API.MLS.LeafNode
import Wire.API.MLS.Message
import Wire.API.MLS.Proposal
import Wire.API.MLS.ProtocolVersion
import Wire.API.MLS.Serialisation

-- | Needed to compute proposal refs.
-- https://messaginglayersecurity.rocks/mls-protocol/draft-ietf-mls-protocol-20/draft-ietf-mls-protocol.html#section-6-7
data AuthenticatedContent = AuthenticatedContent
{ wireFormat :: WireFormatTag,
content :: RawMLS FramedContent,
authData :: RawMLS FramedContentAuthData
}
deriving (Eq, Show)

instance SerialiseMLS AuthenticatedContent where
serialiseMLS ac = do
serialiseMLS ac.wireFormat
serialiseMLS ac.content
serialiseMLS ac.authData

msgAuthContent :: PublicMessage -> AuthenticatedContent
msgAuthContent msg =
AuthenticatedContent
{ wireFormat = WireFormatPublicTag,
content = msg.content,
authData = msg.authData
}

-- | Compute the proposal ref given a ciphersuite and the raw proposal data.
authContentRef :: CipherSuiteTag -> AuthenticatedContent -> ProposalRef
authContentRef cs = ProposalRef . csHash cs proposalContext . mkRawMLS

publicMessageRef :: CipherSuiteTag -> PublicMessage -> ProposalRef
publicMessageRef cs = authContentRef cs . msgAuthContent

-- | Sender, plus with a membership tag in the case of a member sender.
data TaggedSender
= TaggedSenderMember LeafIndex ByteString
| TaggedSenderExternal Word32
| TaggedSenderNewMemberProposal
| TaggedSenderNewMemberCommit

taggedSenderToSender :: TaggedSender -> Sender
taggedSenderToSender (TaggedSenderMember i _) = SenderMember i
taggedSenderToSender (TaggedSenderExternal n) = SenderExternal n
taggedSenderToSender TaggedSenderNewMemberProposal = SenderNewMemberProposal
taggedSenderToSender TaggedSenderNewMemberCommit = SenderNewMemberCommit

taggedSenderMembershipTag :: TaggedSender -> Maybe ByteString
taggedSenderMembershipTag (TaggedSenderMember _ t) = Just t
taggedSenderMembershipTag _ = Nothing

-- | Craft a message with the backend itself as a sender. Return the message and its ref.
mkSignedPublicMessage ::
SecretKey -> PublicKey -> GroupId -> Epoch -> TaggedSender -> FramedContentData -> PublicMessage
mkSignedPublicMessage priv pub gid epoch sender payload =
let framedContent =
mkRawMLS
FramedContent
{ groupId = gid,
epoch = epoch,
sender = taggedSenderToSender sender,
content = payload,
authenticatedData = mempty
}
tbs =
FramedContentTBS
{ protocolVersion = defaultProtocolVersion,
wireFormat = WireFormatPublicTag,
content = framedContent,
groupContext = Nothing
}
sig = signWithLabel "FramedContentTBS" priv pub (mkRawMLS tbs)
in PublicMessage
{ content = framedContent,
authData = mkRawMLS (FramedContentAuthData sig Nothing),
membershipTag = taggedSenderMembershipTag sender
}
55 changes: 55 additions & 0 deletions libs/wire-api/src/Wire/API/MLS/Capabilities.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com>
--
-- This program is free software: you can redistribute it and/or modify it under
-- the terms of the GNU Affero General Public License as published by the Free
-- Software Foundation, either version 3 of the License, or (at your option) any
-- later version.
--
-- This program is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
-- details.
--
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see <https://www.gnu.org/licenses/>.

module Wire.API.MLS.Capabilities where

import Imports
import Test.QuickCheck
import Wire.API.MLS.CipherSuite
import Wire.API.MLS.Credential
import Wire.API.MLS.ProposalTag
import Wire.API.MLS.ProtocolVersion
import Wire.API.MLS.Serialisation
import Wire.Arbitrary

-- | https://messaginglayersecurity.rocks/mls-protocol/draft-ietf-mls-protocol-20/draft-ietf-mls-protocol.html#section-7.2-2
data Capabilities = Capabilities
{ versions :: [ProtocolVersion],
ciphersuites :: [CipherSuite],
extensions :: [Word16],
proposals :: [ProposalTag],
credentials :: [CredentialTag]
}
deriving (Show, Eq, Generic)
deriving (Arbitrary) via (GenericUniform Capabilities)

instance ParseMLS Capabilities where
parseMLS =
Capabilities
<$> parseMLSVector @VarInt parseMLS
<*> parseMLSVector @VarInt parseMLS
<*> parseMLSVector @VarInt parseMLS
<*> parseMLSVector @VarInt parseMLS
<*> parseMLSVector @VarInt parseMLS

instance SerialiseMLS Capabilities where
serialiseMLS caps = do
serialiseMLSVector @VarInt serialiseMLS caps.versions
serialiseMLSVector @VarInt serialiseMLS caps.ciphersuites
serialiseMLSVector @VarInt serialiseMLS caps.extensions
serialiseMLSVector @VarInt serialiseMLS caps.proposals
serialiseMLSVector @VarInt serialiseMLS caps.credentials
Loading