├── LICENSE.md
├── .github
├── CODEOWNERS
└── workflows
│ ├── archive.yml
│ ├── publish.yml
│ └── ghpages.yml
├── .note.xml
├── .gitignore
├── Makefile
├── README.md
├── CONTRIBUTING.md
├── .circleci
└── config.yml
└── draft-ietf-mls-extensions.md
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # License
2 |
3 | See the
4 | [guidelines for contributions](https://github.com/mlswg/mls-extensions/CONTRIBUTING.md).
5 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | draft-ietf-mls-extensions.md git@raphaelrobert.com alwenjo@amazon.com mulmarta@amazon.com rohan.mahy@gmail.com rlb@ipv.sx konrad.kohbrok@datashrine.de
2 |
--------------------------------------------------------------------------------
/.note.xml:
--------------------------------------------------------------------------------
1 |
2 | Source for this draft and an issue tracker can be found at
3 | .
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 | *.pdf
3 | *.redxml
4 | *.swp
5 | *.txt
6 | *.upload
7 | *~
8 | .DS_Store
9 | .refcache
10 | .tags
11 | .targets.mk
12 | .vscode
13 | /*-[0-9][0-9].xml
14 | archive.json
15 | report.xml
16 | venv/
17 | lib
18 | draft-ietf-mls-extensions.xml
19 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | LIBDIR := lib
2 | include $(LIBDIR)/main.mk
3 |
4 | $(LIBDIR)/main.mk:
5 | ifneq (,$(shell grep "path *= *$(LIBDIR)" .gitmodules 2>/dev/null))
6 | git submodule sync
7 | git submodule update $(CLONE_ARGS) --init
8 | else
9 | git clone -q --depth 10 $(CLONE_ARGS) \
10 | -b main https://github.com/martinthomson/i-d-template $(LIBDIR)
11 | endif
12 |
--------------------------------------------------------------------------------
/.github/workflows/archive.yml:
--------------------------------------------------------------------------------
1 | name: "Archive Issues and Pull Requests"
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * 0,2,4'
6 | repository_dispatch:
7 | types: [archive]
8 |
9 | jobs:
10 | build:
11 | name: "Archive Issues and Pull Requests"
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: "Checkout"
15 | uses: actions/checkout@v2
16 |
17 | - name: "Update Archive"
18 | uses: martinthomson/i-d-template@v1
19 | with:
20 | make: archive
21 | env:
22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23 |
24 | - name: "Update GitHub Pages"
25 | uses: martinthomson/i-d-template@v1
26 | with:
27 | make: gh-archive
28 | env:
29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30 |
31 | - name: "Save Archive"
32 | uses: actions/upload-artifact@v4
33 | with:
34 | path: archive.json
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The Messaging Layer Security (MLS) Extensions
2 |
3 | This is the working area for the IETF [MLS Working Group](https://datatracker.ietf.org/wg/mls/documents/) Internet-Draft, "The Messaging Layer Security (MLS) Extensions".
4 |
5 | * [Editor's Copy](https://mlswg.github.io/mls-extensions/#go.draft-ietf-mls-extensions.html)
6 | * [Working Group Draft](https://tools.ietf.org/html/draft-ietf-mls-extensions)
7 | * [Compare Editor's Copy to Working Group Draft](https://mlswg.github.io/mls-extensions/#go.draft-ietf-mls-extensions.diff)
8 |
9 | ## Building the Draft
10 |
11 | Formatted text and HTML versions of the draft can be built using `make`.
12 |
13 | ```sh
14 | $ make
15 | ```
16 |
17 | This requires that you have the necessary software installed. See
18 | [the instructions](https://github.com/martinthomson/i-d-template/blob/master/doc/SETUP.md).
19 |
20 |
21 | ## Contributing
22 |
23 | See the
24 | [guidelines for contributions](https://github.com/mlswg/mls-extensions/blob/extensions-import/CONTRIBUTING.md).
25 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: "Publish New Draft Version"
2 |
3 | on:
4 | push:
5 | tags:
6 | - "draft-*"
7 |
8 | jobs:
9 | build:
10 | name: "Publish New Draft Version"
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: "Checkout"
14 | uses: actions/checkout@v2
15 |
16 | # See https://github.com/actions/checkout/issues/290
17 | - name: "Get Tag Annotations"
18 | run: git fetch -f origin ${{ github.ref }}:${{ github.ref }}
19 |
20 | - name: "Cache Setup"
21 | id: cache-setup
22 | run: |
23 | mkdir -p "$HOME"/.cache/xml2rfc
24 | echo "::set-output name=path::$HOME/.cache/xml2rfc"
25 | date -u "+::set-output name=date::%FT%T"
26 |
27 | - name: "Cache References"
28 | uses: actions/cache@v2
29 | with:
30 | path: ${{ steps.cache-setup.outputs.path }}
31 | key: refcache-${{ steps.date.outputs.date }}
32 | restore-keys: |
33 | refcache-${{ steps.date.outputs.date }}
34 | refcache-
35 |
36 | - name: "Upload to Datatracker"
37 | uses: martinthomson/i-d-template@v1
38 | with:
39 | make: upload
40 |
--------------------------------------------------------------------------------
/.github/workflows/ghpages.yml:
--------------------------------------------------------------------------------
1 | name: "Update Editor's Copy"
2 |
3 | on:
4 | push:
5 | paths-ignore:
6 | - README.md
7 | - CONTRIBUTING.md
8 | - LICENSE.md
9 | - .gitignore
10 | pull_request:
11 | paths-ignore:
12 | - README.md
13 | - CONTRIBUTING.md
14 | - LICENSE.md
15 | - .gitignore
16 |
17 | jobs:
18 | build:
19 | name: "Update Editor's Copy"
20 | runs-on: ubuntu-latest
21 | steps:
22 | - name: "Checkout"
23 | uses: actions/checkout@v2
24 |
25 | - name: "Cache Setup"
26 | id: cache-setup
27 | run: |
28 | mkdir -p "$HOME"/.cache/xml2rfc
29 | echo "::set-output name=path::$HOME/.cache/xml2rfc"
30 | date -u "+::set-output name=date::%FT%T"
31 |
32 | - name: "Cache References"
33 | uses: actions/cache@v2
34 | with:
35 | path: ${{ steps.cache-setup.outputs.path }}
36 | key: refcache-${{ steps.cache-setup.outputs.date }}
37 | restore-keys: |
38 | refcache-${{ steps.cache-setup.outputs.date }}
39 | refcache-
40 |
41 | - name: "Build Drafts"
42 | uses: martinthomson/i-d-template@v1
43 |
44 | - name: "Update GitHub Pages"
45 | uses: martinthomson/i-d-template@v1
46 | if: ${{ github.event_name == 'push' }}
47 | with:
48 | make: gh-pages
49 | env:
50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51 |
52 | - name: "Archive Built Drafts"
53 | uses: actions/upload-artifact@v4
54 | with:
55 | path: |
56 | draft-*.html
57 | draft-*.txt
58 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | This repository relates to activities in the Internet Engineering Task Force
4 | ([IETF](https://www.ietf.org/)). All material in this repository is considered
5 | Contributions to the IETF Standards Process, as defined in the intellectual
6 | property policies of IETF currently designated as
7 | [BCP 78](https://www.rfc-editor.org/info/bcp78),
8 | [BCP 79](https://www.rfc-editor.org/info/bcp79) and the
9 | [IETF Trust Legal Provisions (TLP) Relating to IETF Documents](http://trustee.ietf.org/trust-legal-provisions.html).
10 |
11 | Any edit, commit, pull request, issue, comment or other change made to this
12 | repository constitutes Contributions to the IETF Standards Process
13 | (https://www.ietf.org/).
14 |
15 | You agree to comply with all applicable IETF policies and procedures, including,
16 | BCP 78, 79, the TLP, and the TLP rules regarding code components (e.g. being
17 | subject to a Simplified BSD License) in Contributions.
18 |
19 |
20 | ## Other Resources
21 |
22 | Discussion of this work occurs on the
23 | [mls working group mailing list](https://mailarchive.ietf.org/arch/browse/mls/)
24 | ([subscribe](https://www.ietf.org/mailman/listinfo/mls)). In addition to
25 | contributions in GitHub, you are encouraged to participate in discussions there.
26 |
27 | **Note**: Some working groups adopt a policy whereby substantive discussion of
28 | technical issues needs to occur on the mailing list.
29 |
30 | You might also like to familiarize yourself with other
31 | [working group documents](https://datatracker.ietf.org/wg/mls/documents/).
32 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | docker:
5 | - image: martinthomson/i-d-template:latest
6 | working_directory: ~/draft
7 |
8 | steps:
9 | - run:
10 | name: "Print Configuration"
11 | command: |
12 | xml2rfc --version
13 | gem list -q kramdown-rfc2629
14 | echo -n 'mmark '; mmark --version
15 |
16 | - restore_cache:
17 | name: "Restoring cache - Git"
18 | keys:
19 | - v2-cache-git-{{ .Branch }}-{{ .Revision }}
20 | - v2-cache-git-{{ .Branch }}
21 | - v2-cache-git-
22 |
23 | - restore_cache:
24 | name: "Restoring cache - References"
25 | keys:
26 | - v1-cache-references-{{ epoch }}
27 | - v1-cache-references-
28 |
29 | # Workaround for https://discuss.circleci.com/t/22437
30 | - run:
31 | name: Tag Checkout
32 | command: |
33 | if [ -n "$CIRCLE_TAG" ] && [ -d .git ]; then
34 | remote=$(echo "$CIRCLE_REPOSITORY_URL" | \
35 | sed -e 's,/^git.github.com:,https://github.com/,')
36 | git fetch -f "$remote" "refs/tags/$CIRCLE_TAG:refs/tags/$CIRCLE_TAG" || \
37 | (echo 'Removing .git cache for tag build'; rm -rf .git)
38 | fi
39 |
40 | - checkout
41 |
42 | # Build txt and html versions of drafts
43 | - run:
44 | name: "Build Drafts"
45 | command: "make 'CLONE_ARGS=--reference ~/git-reference'"
46 |
47 | # Update editor's copy on gh-pages
48 | - run:
49 | name: "Update GitHub Pages"
50 | command: |
51 | if [ "${CIRCLE_TAG#draft-}" == "$CIRCLE_TAG" ]; then
52 | make gh-pages
53 | fi
54 |
55 | # For tagged builds, upload to the datatracker.
56 | - deploy:
57 | name: "Upload to Datatracker"
58 | command: |
59 | if [ "${CIRCLE_TAG#draft-}" != "$CIRCLE_TAG" ]; then
60 | make upload
61 | fi
62 |
63 | # Archive GitHub Issues
64 | - run:
65 | name: "Archive GitHub Issues"
66 | command: "make archive || make archive DISABLE_ARCHIVE_FETCH=true && make gh-archive"
67 |
68 | # Create and store artifacts
69 | - run:
70 | name: "Create Artifacts"
71 | command: "make artifacts CI_ARTIFACTS=/tmp/artifacts"
72 |
73 | - store_artifacts:
74 | path: /tmp/artifacts
75 |
76 | - run:
77 | name: "Prepare for Caching"
78 | command: "git reflog expire --expire=now --all && git gc --prune=now"
79 |
80 | - save_cache:
81 | name: "Saving Cache - Git"
82 | key: v2-cache-git-{{ .Branch }}-{{ .Revision }}
83 | paths:
84 | - ~/draft/.git
85 |
86 | - save_cache:
87 | name: "Saving Cache - Drafts"
88 | key: v1-cache-references-{{ epoch }}
89 | paths:
90 | - ~/.cache/xml2rfc
91 |
92 |
93 | workflows:
94 | version: 2
95 | build:
96 | jobs:
97 | - build:
98 | filters:
99 | tags:
100 | only: /.*?/
101 |
--------------------------------------------------------------------------------
/draft-ietf-mls-extensions.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: The Messaging Layer Security (MLS) Extensions
3 | abbrev: MLS
4 | docname: draft-ietf-mls-extensions-latest
5 | submissiontype: IETF
6 | category: std
7 |
8 | number:
9 | date:
10 | consensus: true
11 | v: 3
12 | area: "Security"
13 | workgroup: "Messaging Layer Security"
14 | keyword:
15 | - messaging layer security
16 | - end-to-end encryption
17 | - application api
18 | - extension
19 | - extensibility
20 | venue:
21 | group: "Messaging Layer Security"
22 | type: "Working Group"
23 | mail: "mls@ietf.org"
24 | arch: "https://mailarchive.ietf.org/arch/browse/mls/"
25 | github: "mlswg/mls-extensions"
26 | latest: "https://mlswg.github.io/mls-extensions/draft-ietf-mls-extensions.html"
27 |
28 | author:
29 | - ins: R. Robert
30 | name: Raphael Robert
31 | organization: Phoenix R&D
32 | email: ietf@raphaelrobert.com
33 |
34 | contributor:
35 | - name: Joel Alwen
36 | org: Amazon
37 | email: alwenjo@amazon.com
38 | - name: Konrad Kohbrok
39 | org: Phoenix R&D
40 | email: konrad.kohbrok@datashrine.de
41 | - name: Rohan Mahy
42 | org: Rohan Mahy Consulting Services
43 | email: rohan.ietf@gmail.com
44 | - name: Marta Mularczyk
45 | org: Amazon
46 | email: mulmarta@amazon.com
47 | - name: Richard Barnes
48 | org: Cisco Systems
49 | email: rlb@ipv.sx
50 |
51 | normative:
52 |
53 | informative:
54 |
55 | --- abstract
56 |
57 | The Messaging Layer Security (MLS) protocol is an asynchronous group
58 | authenticated key exchange protocol. MLS provides a number of capabilities
59 | to applications, as well as several extension points internal to the protocol. This
60 | document provides a consolidated application API, guidance for how the
61 | protocol's extension points should be used, and a few concrete examples of both
62 | core protocol extensions and uses of the application API.
63 |
64 | --- middle
65 |
66 | # Introduction
67 |
68 | This document defines extensions to MLS {{!RFC9420}} that are not part of the
69 | main protocol specification, and uses them to explain how to extend the
70 | core operation of the MLS protocol. It also describes how applications can
71 | safely interact with the MLS to take advantage of security features of MLS.
72 |
73 | The MLS protocol is designed to be integrated into applications, in order to
74 | provide security services that the application requires. There are two
75 | questions to answer when designing such an integration:
76 |
77 | 1. How does the application provide the services that MLS requires?
78 | 2. How does the application use MLS to get security benefits?
79 |
80 | The MLS Architecture {{?I-D.ietf-mls-architecture}} describes the requirements
81 | for the first of these questions, namely the structure of the Delivery Service
82 | and Authentication Service that MLS requires. The next section of this document
83 | focuses on the second question.
84 |
85 | MLS itself offers some basic functions that applications can use, such as the
86 | secure message encapsulation (PrivateMessage), the MLS exporter, and the epoch
87 | authenticator. Current MLS applications make use of these mechanisms to acheive
88 | a variety of confidentiality and authentication properties.
89 |
90 | As application designers become familiar with MLS, there is
91 | interest in leveraging other cryptographic tools that an MLS group provides:
92 |
93 | - HPKE (Hybrid Public Key Encryption {{!RFC9180}}) and signature key pairs for
94 | each member, where the private key is known only to that member, and the
95 | public key is authenticated to the other members.
96 |
97 | - A pre-shared key mechanism that can allow an application to inject data into
98 | the MLS key schedule.
99 |
100 | - An exporter mechanism that allows applications to derive secrets from the MLS
101 | key schedule.
102 |
103 | - Association of data with Commits as a synchronization mechanism.
104 |
105 | - Binding of information to the GroupContext to confirm group agreement.
106 |
107 | There is also interest in exposing an MLS group to multiple loosely-coordinated
108 | components of an application. To accommodate such cases, the above mechanisms
109 | need to be exposed in such a way that the usage of different components do not
110 | conflict with each other, or with MLS itself.
111 |
112 | This document defines a set of mechanisms that application components can use to
113 | ensure that their use of these facilities is properly domain-separated from MLS
114 | itself, and from other application components that might be using the same MLS
115 | group.
116 |
117 |
118 | # Conventions and Definitions
119 |
120 | {::boilerplate bcp14-tagged}
121 |
122 | This document makes heavy use of the terminology and the names of structs in the
123 | MLS specification {{!RFC9420}}. In addition, we introduce the following new terms:
124 |
125 | Application:
126 | : The system that instantiates, manages, and uses an MLS group. Each MLS group
127 | is used by exactly one application, but an application may maintain multiple
128 | groups.
129 |
130 | Application component:
131 | : A subsystem of an application that has access to an MLS group.
132 |
133 | Component ID:
134 | : An identifier for an application component. These identifiers are assigned by
135 | the application.
136 |
137 | # Developing Extensions for the MLS Protocol
138 |
139 | MLS is highly extensible, and was designed to be used in a variety of different
140 | applications. As such, it is important to separate extensions that change the
141 | behavior of an MLS stack, and application usages of MLS that just take advantage
142 | of features of MLS or specific extensions (hereafter referred to as *components*). Furthermore it is essential that application components do not change the security properties of MLS or require new security analysis of the MLS protocol itself.
143 |
144 | # The Safe Application Interface
145 |
146 | The mechansms in this section take MLS mechanisms that are either not
147 | inherently designed to be used by applications, or not inherently designed to be
148 | used by multiple application components, and adds a domain separator that
149 | separates application usage from MLS usage, and application components' usage
150 | from each other:
151 |
152 | - Public-key encryption operations are tagged so that encrypted data
153 | will only decrypt in the context of a given component.
154 |
155 | - Signing operations are similarly tagged so that signatures will only verify
156 | in the context of a given component.
157 |
158 | - Exported values include an identifier for the component to which they are
159 | being exported, so that different components will get different exported
160 | values.
161 |
162 | - Pre-shared keys are identified as originating from a specific component, so
163 | that differnet components' contributions to the MLS key schedule will not
164 | collide.
165 |
166 | - Additional Authenticated Data (AAD) can be domain separated by component.
167 |
168 | Similarly, the content of application messages (`application_data`) can be
169 | distinguished and routed to different parts of an application according to
170 | the media type of that content using the content negotiation mechanism defined
171 | in {{content-advertisement}}.
172 |
173 | We also define new general mechanisms that allow applications to take advantage
174 | of the extensibility mechanisms of MLS without having to define extensions
175 | themselves:
176 |
177 | - An `app_data_dictionary` extension type that associates application data with
178 | MLS messages, or with the state of the group.
179 |
180 | - An AppEphemeral proposal type that enables arbitrary application data to
181 | be associated to a Commit.
182 |
183 | - An AppDataUpdate proposal type that enables efficient updates to
184 | an `app_data_dictionary` GroupContext extension.
185 |
186 | As with the above, information carried in these proposals and extension marked
187 | as belonging to a specific application component, so that components can manage
188 | their information independently.
189 |
190 | The separation between components is acheived by the application assigning each
191 | component a unique component ID number. These numbers are then incorporated
192 | into the appopriate calculations in the protocol to achieve the required
193 | separation.
194 |
195 | ## Component IDs
196 |
197 | A component ID is a four-byte value that uniquely identifies a component within
198 | the scope of an application.
199 |
200 | ~~~
201 | uint32 ComponentID;
202 | ~~~
203 |
204 | > TODO: What are the uniqueness requirements on these? It seems like the more
205 | > diversity, the better. For example, if a ComponentID is reused across
206 | > applications (e.g., via an IANA registry), then there will be a risk of replay
207 | > across applications. Maybe we should include a binder to the group/epoch as
208 | > well, something derived from the key schedule.
209 |
210 | > TODO: It might be better to frame these in terms of "data types" instead of
211 | > components, to avoid presuming software architecture. Though that makes less
212 | > sense for the more "active" portions of the API, e.g., signing and encryption.
213 |
214 | When a label is required for an operation, the following data structure is used.
215 | The `label` field identifies the operation being performed. The `component_id`
216 | field identifies the component performing the operation. The `context` field is
217 | specified by the operation in question.
218 |
219 | ~~~ tls
220 | struct {
221 | opaque label;
222 | ComponentID component_id;
223 | opaque context;
224 | } ComponentOperationLabel;
225 | ~~~
226 |
227 |
228 | ## Hybrid Public Key Encryption (HPKE) Keys {#safe-hpke}
229 |
230 | This component of the API allows components to make use of the HPKE key pairs
231 | generated by MLS. A component identified by a ComponentID can use any HPKE
232 | key pair for any operation defined in {{!RFC9180}}, such as encryption,
233 | exporting keys and the PSK mode, as long as the `info` input to `SetupS`
234 | and `SetupR` is set to ComponentOperationLabel with `component_id` set
235 | to the appopriate ComponentID. The `context` can be set to an arbitrary Context
236 | specified by the application designer and can be empty if not needed. For
237 | example, a component can use a key pair PublicKey, PrivateKey to encrypt data
238 | as follows:
239 |
240 | ~~~ tls
241 | SafeEncryptWithContext(ComponentID, PublicKey, Context, Plaintext) =
242 | SealBase(PublicKey, ComponentOperationLabel, "", Plaintext)
243 |
244 | SafeDecryptWithContext(ComponentID, PrivateKey, Context,
245 | KEMOutput, Ciphertext) = OpenBase(KEMOutput, PrivateKey,
246 | ComponentOperationLabel, "", Ciphertext)
247 | ~~~
248 |
249 | Where the fields of ComponentOperationLabel are set to
250 |
251 | ~~~ tls
252 | label = "MLS 1.0 Application"
253 | component_id = ComponentID
254 | context = Context
255 | ~~~
256 |
257 | > TODO: Should this use EncryptWithLabel / DecryptWithLabel? That wouldn't
258 | > cover other modes / exports, but you could say "mutatis mutandis".
259 |
260 | For operations involving the secret key, ComponentID MUST be set to the
261 | ComponentID of the component performing the operation, and not to the ID of
262 | any other component. In particular, this means that a component cannot decrypt
263 | data meant for another component, while components can encrypt data that other
264 | components can decrypt.
265 |
266 | In general, a ciphertext encrypted with a PublicKey can be decrypted by any
267 | entity who has the corresponding PrivateKey at a given point in time according
268 | to the MLS protocol (or application component). For convenience, the following
269 | list summarizes lifetimes of MLS key pairs.
270 |
271 | - The key pair of a non-blank ratchet tree node. The PrivateKey of such a key pair
272 | is known to all members in the node’s subtree. In particular, a PrivateKey of a
273 | leaf node is known only to the member in that leaf. A member in the subtree
274 | stores the PrivateKey for a number of epochs, as long as the PublicKey does not
275 | change. The key pair of the root node SHOULD NOT be used, since the external key
276 | pair recalled below gives better security.
277 | - The external_priv, external_pub key pair used for external initialization. The
278 | external_priv key is known to all group members in the current epoch. A member
279 | stores external_priv only for the current epoch. Using this key pair gives
280 | better security guarantees than using the key pair of the root of the ratchet
281 | tree and should always be preferred.
282 | - The init_key in a KeyPackage and the corresponding secret key. The secret key
283 | is known only to the owner of the KeyPackage and is deleted immediately after it
284 | is used to join a group.
285 |
286 |
287 | ## Signature Keys
288 |
289 | MLS session states contain a number of signature keys including the ones in the
290 | LeafNode structs. Application components can safely sign content and verify
291 | signatures using these keys via the SafeSignWithLabel and SafeVerifyWithLabel
292 | functions, respectively, much like how the basic MLS protocol uses SignWithLabel
293 | and VerifyWithLabel.
294 |
295 | In more detail, a component identified by ComponentID should sign and verify
296 | using:
297 |
298 | ~~~ tls
299 | SafeSignWithLabel(ComponentID, SignatureKey, Label, Content) =
300 | SignWithLabel(SignatureKey, "ComponentOperationLabel",
301 | ComponentOperationLabel)
302 |
303 | SafeVerifyWithLabel(ComponentID, VerificationKey, Label, Content,
304 | SignatureValue) = VerifyWithLabel(VerificationKey,
305 | "ComponentOperationLabel",
306 | ComponentOperationLabel,
307 | SignatureValue)
308 | ~~~
309 |
310 | Where the fields of ComponentOperationLabel are set to
311 |
312 | ~~~ tls
313 | label = Label
314 | component_id = ComponentID
315 | context = Content
316 | ~~~
317 |
318 | For signing operations, the ComponentID MUST be set to the ComponentID of the
319 | component performing the signature, and not to the ID of any other component.
320 | This means that a component cannot produce signatures in place of other
321 | component. However, components can verify signatures computed by other
322 | components. Domain separation is ensured by explicitly including the ComponentID
323 | with every operation.
324 |
325 | ## Exported Secrets
326 |
327 | An application component can use MLS as a group key agreement protocol by
328 | exporting symmetric keys. Such keys can be exported (i.e. derived from MLS key
329 | material) in two phases per epoch: Either at the start of the epoch, or during
330 | the epoch. Derivation at the start of the epoch has the added advantage that the
331 | source key material is deleted after use, allowing the derived key material to
332 | be deleted later even during the same MLS epoch to achieve forward secrecy. The
333 | following protocol secrets can be used to derive key from for use by application
334 | components:
335 |
336 | - `exporter_secret` at the beginning of an epoch
337 | - `application_export_secret` during an epoch
338 |
339 | The `application_export_secret` is an additional secret derived from the
340 | `epoch_secret` at the beginning of the epoch in the same way as the other
341 | secrets listed in Table 4 of {{!RFC9420}} using the label "application_export".
342 |
343 | Any derivation performed by an application component either from the
344 | `exporter_secret` or the `application_export_secret` has to use the following
345 | function:
346 |
347 | ~~~ tls
348 | DeriveApplicationSecret(Secret, Label) =
349 | ExpandWithLabel(Secret, "ApplicationExport " +
350 | ComponentID + " " + Label)
351 | ~~~
352 |
353 | Where ExpandWithLabel is defined in {{Section 8 of RFC9420}} and where
354 | ComponentID MUST be set to the ComponentID of the component performing the
355 | export.
356 |
357 | > TODO: This section seems over-complicated to me. Why is it not sufficient to
358 | > just use the `exporter_secret`? Or the `MLS-Exporter` mechanism with a
359 | > label structured to include the ComponentID?
360 |
361 |
362 | ## Pre-Shared Keys (PSKs)
363 |
364 | PSKs represent key material that is injected into the MLS key schedule when
365 | creating or processing a commit as defined in {{Section 8.4 of !RFC9420}}. Its
366 | injection into the key schedule means that all group members have to agree on
367 | the value of the PSK.
368 |
369 | While PSKs are typically cryptographic keys which due to their properties add to
370 | the overall security of the group, the PSK mechanism can also be used to ensure
371 | that all members of a group agree on arbitrary pieces of data represented as
372 | octet strings (without the necessity of sending the data itself over the wire).
373 | For example, a component can use the PSK mechanism to enforce that all group
374 | members have access to and agree on a password or a shared file.
375 |
376 | This is achieved by creating a new epoch via a PSK proposal. Transitioning to
377 | the new epoch requires using the information agreed upon.
378 |
379 | To facilitate using PSKs in a safe way, this document defines a new PSKType for
380 | application components. This provides domain separation between pre-shared keys
381 | used by the core MLS protocol and applications, and between those used by
382 | different components.
383 |
384 | ~~~ tls-presentation
385 | enum {
386 | // ...
387 | application(3),
388 | (255)
389 | } PSKType;
390 |
391 | struct {
392 | PSKType psktype;
393 | select (PreSharedKeyID.psktype) {
394 | // ...
395 | case application:
396 | ComponentID component_id;
397 | opaque psk_id;
398 | };
399 | opaque psk_nonce;
400 | } PreSharedKeyID;
401 | ~~~
402 |
403 | > TODO: It seems like you could also do this by structuring the `external`
404 | > PSKType as (component_id, psk_id). I guess this approach separates this API
405 | > from other external PSKs.
406 |
407 |
408 | ## Attaching Application Data to MLS Messages
409 |
410 | The MLS GroupContext, LeafNode, KeyPackage, and GroupInfo objects each have an
411 | `extensions` field that can carry additional data not defined by the MLS
412 | specification. The `app_data_dictionary` extension provides a generic container
413 | that applications can use to attach application data to these messages. Each
414 | usage of the extension serves a slightly different purpose:
415 |
416 | * GroupContext: Confirms that all members of the group agree on the application
417 | data, and automatically distributes it to new joiners.
418 |
419 | * KeyPackage and LeafNode: Associates the application data to a particular
420 | client, and advertises it to the other members of the group.
421 |
422 | * GroupInfo: Distributes the application data confidentially to the new joiners
423 | for whom the GroupInfo is encrypted (as a Welcome message).
424 |
425 | The content of the `app_data_dictionary` extension is a serialized
426 | AppDataDictionary object:
427 |
428 | ~~~ tls-presentation
429 | struct {
430 | ComponentID component_id;
431 | opaque data;
432 | } ComponentData;
433 |
434 | struct {
435 | ComponentData component_data;
436 | } AppDataDictionary;
437 | ~~~
438 |
439 | The entries in the `component_data` MUST be sorted by `component_id`, and there
440 | MUST be at most one entry for each `component_id`.
441 |
442 | An `app_data_dictionary` extension in a LeafNode, KeyPackage, or GroupInfo can
443 | be set when the object is created. An `app_data_dictionary` extension in the
444 | GroupContext needs to be managed using the tools available to update GroupContext extensions. The creator of the group can set extensions unilaterally. Thereafter, the AppDataUpdate proposal described in the next section is used to update the `app_data_dictionary` extension.
445 |
446 | ## Updating Application Data in the GroupContext {#appdataupdate}
447 |
448 | Updating the `app_data_dictionary` with a GroupContextExtensions proposal is
449 | cumbersome. The application data needs to be transmitted in its entirety,
450 | along with any other extensions, whether or not they are being changed. And a
451 | GroupContextExtensions proposal always requires an UpdatePath, which updating
452 | application state never should.
453 |
454 | The AppDataUpdate proposal allows the `app_data_dictionary` extension to
455 | be updated without these costs. Instead of sending the whole value of the
456 | extension, it sends only an update, which is interpreted by the application to
457 | provide the new content for the `app_data_dictionary` extension. No other
458 | extensions are sent or updated, and no UpdatePath is required.
459 |
460 | ~~~
461 | enum {
462 | invalid(0),
463 | update(1),
464 | remove(2),
465 | (255)
466 | } AppDataUpdateOperation;
467 |
468 | struct {
469 | ComponentID component_id;
470 | AppDataUpdateOperation op;
471 |
472 | select (AppDataUpdate.op) {
473 | case update: opaque update;
474 | case remove: struct{};
475 | };
476 | } AppDataUpdate;
477 | ~~~
478 |
479 | An AppDataUpdate proposal is invalid if its `component_id` references a
480 | component that is not known to the application, or if it specifies the removal
481 | of state for a `component_id` that has no state present. A proposal list is
482 | invalid if it includes multiple AppDataUpdate proposals that `remove`
483 | state for the same `component_id`, or proposals that both `update` and `remove`
484 | state for the same `component_id`. In other words, for a given `component_id`,
485 | a proposal list is valid only if it contains (a) a single `remove` operation or
486 | (b) one or more `update` operation.
487 |
488 | AppDataUpdate proposals are processed after any default proposals (i.e., those
489 | defined in {{RFC9420}}), and any AppEphemeral proposals (defined in
490 | {{app-ephemeral}}).
491 |
492 | When an MLS group contains the AppDataUpdate proposal type in the
493 | `proposal_types` list in the group's `required_capabilities` extension, a
494 | GroupContextExtensions proposal MUST NOT add, remove, or modify the
495 | `app_data_dictionary` GroupContext extension. In other words, when every member of
496 | the group supports the AppDataUpdate proposal, a GroupContextExtensions proposal
497 | could be sent to update some other extension(s), but the `app_data_dictionary`
498 | GroupContext extension, if it exists, is left as it was.
499 |
500 | A commit can contain a GroupContextExtensions proposal which modifies
501 | GroupContext extensions other than `app_data_dictionary`, and can be followed by
502 | zero or more AppDataUpdate proposals. This allows modifications to both the
503 | `app_data_dictionary` extension (via AppDataUpdate) and other extensions (via
504 | GroupContextExtensions) in the same Commit.
505 |
506 | A client applies AppDataUpdate proposals by component ID. For each
507 | `component_id` field that appears in an AppDataUpdate proposal in the
508 | Commit, the client assembles a list of AppDataUpdate proposals with that
509 | `component_id`, in the order in which they appear in the Commit, and processes
510 | them in the following way:
511 |
512 | * If the list comprises a single proposal with the `op` field set to `remove`:
513 |
514 | * If there is an entry in the `component_states` vector in the
515 | `application_state` extension with the specified `component_id`, remove
516 | it.
517 |
518 | * Otherwise, the proposal is invalid.
519 |
520 | * If the list comprises one or more proposals, all with `op` field set to
521 | `update`:
522 |
523 | * Provide the application logic registered to the `component_id` value with
524 | the content of the `update` field from each proposal, in the order
525 | specified.
526 |
527 | * The application logic returns either an opaque value `new_data` that will be
528 | stored as the new application data for this component, or else an
529 | indication that it considers this update invalid.
530 |
531 | * If the application logic considers the update invalid, the MLS client MUST
532 | consider the proposal list invalid.
533 |
534 | * If no `app_data_dictionary` extension is present in the GroupContext, add one
535 | to the end of the `extensions` list in the GroupContext.
536 |
537 | * If there is an entry in the `component_data` vector in the
538 | `app_data_dictionary` extension with the specified `component_id`, then set
539 | its `data` field to the specified `new_data`.
540 |
541 | * Otherwise, insert a new entry in the `component_states` vector with the
542 | specified `component_id` and the `data` field set to the `new_data`
543 | value. The new entry is inserted at the proper point to keep the
544 | `component_states` vector sorted by `component_id`.
545 |
546 | * Otherwise, the proposal list is invalid.
547 |
548 | > NOTE: An alternative design here would be to have the `update` operation
549 | > simply set the new value for the `app_data_dictionary` GCE, instead of sending a
550 | > diff. This would be simpler in that the MLS stack wouldn't have to ask the
551 | > application for the new state value, and would discourage applications from
552 | > storing large state in the GroupContext directly (which bloats Welcome
553 | > messages). It would effectively require the state in the GroupContext to be a
554 | > hash of the real state, to avoid large AppDataUpdate proposals. This
555 | > pushes some complexity onto the application, since the application has to
556 | > define a hashing algorithm, and define its own scheme for initializing new
557 | > joiners.
558 |
559 | AppDataUpdate proposals do not require an UpdatePath.
560 | An AppDataUpdate proposal can be sent by an external sender. Likewise,
561 | AppDataUpdate proposals can be included in an external commit. Applications
562 | can make more restrictive validity rules for the update of their components,
563 | such that some components would not be valid at the application when sent in
564 | an external commit or via an external proposer.
565 |
566 |
567 | ## Attaching Application Data to a Commit {#app-ephemeral}
568 |
569 | The AppEphemeral proposal type allows an application component to associate
570 | application data to a Commit, so that the member processing the Commit knows
571 | that all other group members will be processing the same data. AppEphemeral
572 | proposals are ephemeral in the sense that they do not change any persistent
573 | state related to MLS, aside from their appearance in the transcript hash.
574 |
575 | The content of an AppEphemeral proposal is the same as an `app_data_dictionary`
576 | extension. The proposal type is set in {{iana-considerations}}.
577 |
578 | ~~~ tls-presentation
579 | struct {
580 | ComponentID component_id;
581 | opaque data;
582 | } AppEphemeral;
583 | ~~~
584 |
585 | An AppEphemeral proposal is invalid if it contains a `component_id` that is
586 | unknown to the application, or if the `app_data_dictionary` field contains any
587 | `ComponentData` entry whose `data` field is considered invalid by the
588 | application logic registered to the indicated `component_id`.
589 |
590 | AppEphemeral proposals MUST be processed after any default proposals (i.e.,
591 | those defined in {{RFC9420}}), but before any AppDataUpdate proposals.
592 |
593 | A client applies an AppEphemeral proposal by providing the contents of the
594 | `app_data_dictionary` field to the component identified by the `component_id`. If
595 | a Commit references more than one AppEphemeral proposal for the same
596 | `component_id` value, then they MUST be processed in the order in which they are
597 | specified in the Commit.
598 |
599 | AppEphemeral proposals do not require an UpdatePath.
600 | An AppEphemeral proposal can be sent by an external sender. Likewise,
601 | AppEphemeral proposals can be included in an external commit. Applications
602 | can make more restrictive validity rules for ephemeral updates of their
603 | components, such that some components would not be valid at the application when
604 | sent in an external commit or via an external proposer.
605 |
606 |
607 | ## Safe Additional Authenticated Data (AAD) {#safe-aad}
608 |
609 | An MLS PrivateMessage can contain arbitrary additional application-specific
610 | AAD. The corresponding `authenticated_data` field appears in several MLS
611 | structs: `FramedContent` (used indirectly to generate the message signature
612 | and tags), `PrivateContentAAD` (used as the AAD input to the
613 | `PrivateMessage.ciphertext`), and `PrivateMessage` (to convey the AAD).
614 |
615 | The Safe AAD API defines a framing used to allow multiple application
616 | components to add AAD safely to the `authenticated_data` without conflicts or
617 | ambiguity.
618 |
619 | When any AAD safe extension is included in the `authenticated_data` field,
620 | the "safe" AAD items MUST come before any non-safe data in the
621 | `authenticated_data` field. Safe AAD items are framed using the `SafeAAD`
622 | struct and are sorted in increasing numerical order of the `component_id`.
623 | The struct is described below:
624 |
625 | ~~~ tls-presentation
626 | struct {
627 | ComponentID component_id;
628 | opaque aad_item_data;
629 | } SafeAADItem;
630 |
631 | struct {
632 | SafeAADItem aad_items;
633 | } SafeAAD;
634 | ~~~
635 |
636 | If the `SafeAAD` is present or not in the `authenticated_data` is determined by
637 | the presence of the `safe_aad` component in the `app_data_dictionary` extension
638 | in the GroupContext (see {{negotiation}}). If `safe_aad` is present, but none
639 | of the "safe" AAD components have data to send in a particular message, the
640 | `aad_items` is a zero-length vector.
641 |
642 |
643 | # Negotiating Extensions and Components {#negotiation}
644 |
645 | MLS defines a `Capabilities` struct for LeafNodes (in turn used in
646 | KeyPackages), which describes which extensions are supported by the
647 | associated node.
648 | However, that struct (defined in {{Section 7.2 of !RFC9420}}) only has
649 | fields for a subset of the extensions possible in MLS, as reproduced below.
650 |
651 | ~~~ tls-presentation
652 | struct {
653 | ProtocolVersion versions;
654 | CipherSuite cipher_suites;
655 | ExtensionType extensions;
656 | ProposalType proposals;
657 | CredentialType credentials;
658 | } Capabilities;
659 | ~~~
660 |
661 | > The "MLS Extensions Types" registry represents extensibility of four
662 | core structs (`GroupContext`, `GroupInfo`, `KeyPackage`, and `LeafNode`)
663 | that have far reaching effects on the use of the protocol. The majority of
664 | MLS extensions in {{!RFC9420}} extend one or more of these core structs.
665 |
666 | Likewise, the `required_capabilities` GroupContext extension (defined
667 | in {{Section 11.1 of !RFC9420}} and reproduced below) contains all
668 | mandatory to support non-default extensions in its `extension_types` vector.
669 | Its `proposal_types` vector contains any mandatory to support Proposals.
670 | Its `credential_types` vector contains any mandatory credential types.
671 |
672 | ~~~
673 | struct {
674 | ExtensionType extension_types;
675 | ProposalType proposal_types;
676 | CredentialType credential_types;
677 | } RequiredCapabilities;
678 | ~~~
679 |
680 | Due to an oversight in {{!RFC9420}}, the Capabilities struct does not include
681 | MLS Wire Formats. Instead, this document defines two extensions: `supported_wire_formats` (which can appear in LeafNodes), and
682 | `required_wire_formats` (which can appear in the GroupContext).
683 |
684 | ~~~ tls-presentation
685 | struct {
686 | WireFormat wire_formats;
687 | } WireFormats
688 |
689 | WireFormats supported_wire_formats;
690 | WireFormats requires_wire_formats;
691 | ~~~
692 |
693 | This document also defines new components of the `app_data_dictionary`
694 | extension for supported and required Safe AAD, media types, and components.
695 |
696 | The `safe_aad` component contains a list of components IDs. When present (in an
697 | `app_data_dictionary` extension) in a LeafNode, the semantic is the list of
698 | supported components that use Safe AAD. When present (in an
699 | `app_data_dictionary` extension) in the GroupContext, the semantic is the list
700 | of required Safe AAD components (those that must be understood by the entire
701 | group). If the `safe_aad` component is present, even with an empty list, (in the
702 | `app_data_dictionary` extension) in the GroupContext, then the
703 | `authenticated_data` field always starts with the SafeAAD struct defined in
704 | {{safe-aad}}.
705 |
706 | ~~~ tls-presentation
707 | struct {
708 | ComponentID component_ids;
709 | } ComponentsList;
710 |
711 | ComponentsList safe_aad;
712 | ~~~
713 |
714 | The list of required and supported components follows the same model with the
715 | new component `app_components`. When present in a LeafNode, the semantic is the
716 | list of supported components. When present in the GroupContext, the semantic is
717 | the list of required components.
718 |
719 | ~~~ tls-presentation
720 | ComponentsList app_components;
721 | ~~~
722 |
723 | Finally, the supported and required media types (formerly called MIME types)
724 | are communicated in the `content_media_types` component (see
725 | {{content-advertisement}}).
726 |
727 |
728 | # Extensions
729 |
730 | ## AppAck
731 |
732 | An AppAck object is used to acknowledge receipt of application messages.
733 | Though this information implies no change to the group, it is conveyed inside
734 | an AppEphermeral Proposal with a component ID `app_ack`, so that it is included
735 | in the group's transcript by being included in Commit messages.
736 |
737 | ~~~ tls
738 | struct {
739 | uint32 sender;
740 | uint32 first_generation;
741 | uint32 last_generation;
742 | } MessageRange;
743 |
744 | struct {
745 | MessageRange received_ranges;
746 | } AppAck;
747 | ~~~
748 |
749 | An AppAck represents a set of messages received by the sender in the
750 | current epoch. Messages are represented by the `sender` and `generation` values
751 | in the MLSCiphertext for the message. Each MessageRange represents receipt of a
752 | span of messages whose `generation` values form a continuous range from
753 | `first_generation` to `last_generation`, inclusive.
754 |
755 | AppAck objects are sent as a guard against the Delivery Service dropping
756 | application messages. The sequential nature of the `generation` field provides
757 | a degree of loss detection, since gaps in the `generation` sequence indicate
758 | dropped messages. AppAck completes this story by addressing the scenario where
759 | the Delivery Service drops all messages after a certain point, so that a later
760 | generation is never observed. Obviously, there is a risk that AppAck messages
761 | could be suppressed as well, but their inclusion in the transcript means that if
762 | they are suppressed then the group cannot advance at all.
763 |
764 |
765 | ## Targeted messages
766 |
767 | ### Description
768 |
769 | MLS application messages make sending encrypted messages to all group members
770 | easy and efficient. Sometimes application protocols mandate that messages are
771 | only sent to specific group members, either for privacy or for efficiency
772 | reasons.
773 |
774 | Targeted messages are a way to achieve this without having to create a new group
775 | with the sender and the specific recipients – which might not be possible or
776 | desired. Instead, targeted messages define the format and encryption of a
777 | message that is sent from a member of an existing group to another member of
778 | that group.
779 |
780 | The goal is to provide a one-shot messaging mechanism that provides
781 | confidentiality and authentication, reusing mechanisms from {{!RFC9420}}, in
782 | particular {{!RFC9180}}.
783 |
784 | ### Format
785 |
786 | This extension uses the `mls_extension_message` WireFormat, where the content is a `TargetedMessage`.
787 |
788 | ~~~ tls
789 | struct {
790 | opaque group_id;
791 | uint64 epoch;
792 | uint32 recipient_leaf_index;
793 | opaque authenticated_data;
794 | opaque encrypted_sender_auth_data;
795 | opaque hpke_ciphertext;
796 | } TargetedMessage;
797 |
798 | enum {
799 | hpke_auth_psk(0),
800 | signature_hpke_psk(1),
801 | } TargetedMessageAuthScheme;
802 |
803 | struct {
804 | uint32 sender_leaf_index;
805 | TargetedMessageAuthScheme authentication_scheme;
806 | select (authentication_scheme) {
807 | case HPKEAuthPsk:
808 | case SignatureHPKEPsk:
809 | opaque signature;
810 | }
811 | opaque kem_output;
812 | } TargetedMessageSenderAuthData;
813 |
814 | struct {
815 | opaque group_id;
816 | uint64 epoch;
817 | uint32 recipient_leaf_index;
818 | opaque authenticated_data;
819 | TargetedMessageSenderAuthData sender_auth_data;
820 | } TargetedMessageTBM;
821 |
822 | struct {
823 | opaque group_id;
824 | uint64 epoch;
825 | uint32 recipient_leaf_index;
826 | opaque authenticated_data;
827 | uint32 sender_leaf_index;
828 | TargetedMessageAuthScheme authentication_scheme;
829 | opaque kem_output;
830 | opaque hpke_ciphertext;
831 | } TargetedMessageTBS;
832 |
833 | struct {
834 | opaque group_id;
835 | uint64 epoch;
836 | opaque label = "MLS 1.0 targeted message psk";
837 | } PSKId;
838 | ~~~
839 |
840 | Note that `TargetedMessageTBS` is only used with the
841 | `TargetedMessageAuthScheme.SignatureHPKEPsk` authentication mode.
842 |
843 | ### Encryption
844 |
845 | Targeted messages uses HPKE to encrypt the message content between two leaves.
846 |
847 | #### Sender data encryption
848 |
849 | In addition, `TargetedMessageSenderAuthData` is encrypted in a similar way to
850 | `MLSSenderData` as described in {{Section 6.3.2 of !RFC9420}}. The
851 | `TargetedMessageSenderAuthData.sender_leaf_index` field is the leaf index of the
852 | sender. The `TargetedMessageSenderAuthData.authentication_scheme` field is the
853 | authentication scheme used to authenticate the sender. The
854 | `TargetedMessageSenderAuthData.signature` field is the signature of the
855 | `TargetedMessageTBS` structure. The `TargetedMessageSenderAuthData.kem_output`
856 | field is the KEM output of the HPKE encryption.
857 |
858 | The key and nonce provided to the AEAD are computed as the KDF of the first
859 | KDF.Nh bytes of the `hpke_ciphertext` generated in the following section. If the
860 | length of the hpke_ciphertext is less than KDF.Nh, the whole hpke_ciphertext is
861 | used. In pseudocode, the key and nonce are derived as:
862 |
863 | ~~~ tls
864 | sender_auth_data_secret
865 | = DeriveExtensionSecret(extension_secret, "targeted message sender auth data")
866 |
867 | ciphertext_sample = hpke_ciphertext[0..KDF.Nh-1]
868 |
869 | sender_data_key = ExpandWithLabel(sender_auth_data_secret, "key",
870 | ciphertext_sample, AEAD.Nk)
871 | sender_data_nonce = ExpandWithLabel(sender_auth_data_secret, "nonce",
872 | ciphertext_sample, AEAD.Nn)
873 | ~~~
874 |
875 | The Additional Authenticated Data (AAD) for the `SenderAuthData` ciphertext is
876 | the first three fields of `TargetedMessage`:
877 |
878 | ~~~ tls
879 | struct {
880 | opaque group_id;
881 | uint64 epoch;
882 | uint32 recipient_leaf_index;
883 | } SenderAuthDataAAD;
884 | ~~~
885 |
886 | #### Padding
887 |
888 | The `TargetedMessage` structure does not include a padding field. It is the
889 | responsibility of the sender to add padding to the `message` as used in the next
890 | section.
891 |
892 | ### Authentication
893 |
894 | For ciphersuites that support it, HPKE `mode_auth_psk` is used for
895 | authentication. For other ciphersuites, HPKE `mode_psk` is used along with a
896 | signature. The authentication scheme is indicated by the `authentication_scheme`
897 | field in `TargetedMessageContent`. See {{guidance-on-authentication-schemes}}
898 | for more information.
899 |
900 | For the PSK part of the authentication, clients export a dedicated secret:
901 |
902 | ~~~ tls
903 | targeted_message_psk
904 | = DeriveExtensionSecret(extension_secret, "targeted message psk")
905 | ~~~
906 |
907 | The functions `SealAuth` and `OpenAuth` defined in {{!RFC9180}} are used as
908 | described in {{safe-hpke}} with an empty context. Other functions are defined in
909 | {{!RFC9420}}.
910 |
911 | #### Authentication with HPKE
912 |
913 | The sender MUST set the authentication scheme to
914 | `TargetedMessageAuthScheme.HPKEAuthPsk`.
915 |
916 | As described in {{safe-hpke}} the `hpke_context` is a LabeledExtensionContent struct
917 | with the following content, where `group_context` is the serialized context of
918 | the group.
919 |
920 | ~~~ tls
921 | label = "MLS 1.0 ExtensionData"
922 | extension_type = ExtensionType
923 | extension_data = group_context
924 | ~~~
925 |
926 |
927 | The sender then computes the following:
928 |
929 | ~~~ tls
930 | (kem_output, hpke_ciphertext) = SealAuthPSK(receiver_node_public_key,
931 | hpke_context,
932 | targeted_message_tbm,
933 | message,
934 | targeted_message_psk,
935 | psk_id,
936 | sender_node_private_key)
937 | ~~~
938 |
939 | The recipient computes the following:
940 |
941 | ~~~ tls
942 | message = OpenAuthPSK(kem_output,
943 | receiver_node_private_key,
944 | hpke_context,
945 | targeted_message_tbm,
946 | hpke_ciphertext,
947 | targeted_message_psk,
948 | psk_id,
949 | sender_node_public_key)
950 | ~~~
951 |
952 | #### Authentication with signatures
953 |
954 | The sender MUST set the authentication scheme to
955 | `TargetedMessageAuthScheme.SignatureHPKEPsk`. The signature is done using the
956 | `signature_key` of the sender's `LeafNode` and the corresponding signature
957 | scheme used in the group.
958 |
959 | The sender then computes the following with `hpke_context` defined as in
960 | {{authentication-with-hpke}}:
961 |
962 | ~~~ tls
963 | (kem_output, hpke_ciphertext) = SealPSK(receiver_node_public_key,
964 | hpke_context,
965 | targeted_message_tbm,
966 | message,
967 | targeted_message_psk,
968 | epoch)
969 | ~~~
970 |
971 | The signature is computed as follows, where the `extension_type` is the type of
972 | this extension (see {{iana-considerations}}).
973 |
974 | ~~~ tls
975 | signature = SafeSignWithLabel(extension_type, ., "TargetedMessageTBS", targeted_message_tbs)
976 | ~~~
977 |
978 | The recipient computes the following:
979 |
980 | ~~~ tls
981 | message = OpenPSK(kem_output,
982 | receiver_node_private_key,
983 | hpke_context,
984 | targeted_message_tbm,
985 | hpke_ciphertext,
986 | targeted_message_psk,
987 | epoch)
988 | ~~~
989 |
990 | The recipient MUST verify the message authentication:
991 |
992 | ~~~ tls
993 | SafeVerifyWithLabel.verify(extension_type,
994 | sender_leaf_node.signature_key,
995 | "TargetedMessageTBS",
996 | targeted_message_tbs,
997 | signature)
998 | ~~~
999 |
1000 | ### Guidance on authentication schemes
1001 |
1002 | If the group’s ciphersuite does not support HPKE `mode_auth_psk`,
1003 | implementations MUST choose `TargetedMessageAuthScheme.SignatureHPKEPsk`.
1004 |
1005 | If the group’s ciphersuite does support HPKE `mode_auth_psk`, implementations
1006 | CAN choose `TargetedMessageAuthScheme.HPKEAuthPsk` if better efficiency and/or
1007 | repudiability is desired. Implementations SHOULD consult
1008 | {{Section 9.1.1 of !RFC9180}} beforehand.
1009 |
1010 | ## Content Advertisement
1011 |
1012 | ### Description
1013 |
1014 | This section defines a minimal framing format so MLS clients can signal
1015 | which media type is being sent inside the MLS `application_data` object when
1016 | multiple formats are permitted in the same group.
1017 |
1018 | It also defines a new `content_media_types` application component which is used to indicate support for specific formats, using the extensive IANA Media Types
1019 | registry (formerly called MIME Types). When the `content_media_types` component
1020 | is present (in the `app_data_dictionary` extension) in a LeafNode, it indicates
1021 | that node's support for a particular (non-empty) list of media types. When the
1022 | `content_media_types` component is present (in the `app_data_dictionary`
1023 | extension) in the GroupContext, it indicates a (non-empty) list of media types
1024 | that need to be supported by all members of that MLS group, *and* that the
1025 | `application_data` will be framed using the application framing format
1026 | described later in {{app-framing}}. This allows clients to confirm that all
1027 | members of a group can communicate.
1028 |
1029 | >Note that when the membership of a group changes, or when the policy of the
1030 | group changes, it is responsibility of the committer to insure that the
1031 | membership and policies are compatible.
1032 |
1033 | As clients are upgraded to support new formats they can use these extensions
1034 | to detect when all members support a new or more efficient encoding, or select
1035 | the relevant format or formats to send.
1036 |
1037 | Vendor-specific media subtypes starting with `vnd.` can be registered with IANA
1038 | without standards action as described in {{?RFC6838}}. Implementations which
1039 | wish to send multiple formats in a single application message, may be interested
1040 | in the `multipart/alternative` media type defined in {{?RFC2046}} or may use or
1041 | define another type with similar semantics (for example using TLS Presentation
1042 | Language syntax {{!RFC8446}}).
1043 |
1044 | >Note that the usage of IANA media types in general does not imply the usage of
1045 | MIME Headers {{?RFC2045}} for framing.
1046 |
1047 |
1048 | ### Syntax
1049 |
1050 | MediaType is a TLS encoding of a single IANA media type (including top-level
1051 | type and subtype) and any of its parameters. Even if the `parameter_value`
1052 | would have required formatting as a `quoted-string` in a text encoding, only
1053 | the contents inside the `quoted-string` are included in `parameter_value`.
1054 | Likewise, only the second character of a `quoted-pair` is included in
1055 | `parameter_value`; the first escaping backslash ("\") is omitted.
1056 | MediaTypeList is an ordered list of MediaType objects.
1057 |
1058 | ~~~ tls
1059 | struct {
1060 | opaque parameter_name;
1061 | /* Note: parameter_value never includes the quotation marks of */
1062 | /* an RFC 2045 quoted-string or the first "\" of a quoted-pair */
1063 | opaque parameter_value;
1064 | } Parameter;
1065 |
1066 | struct {
1067 | /* media_type is an IANA top-level media type, a "/" character,
1068 | * and the IANA media subtype */
1069 | opaque media_type;
1070 |
1071 | /* a list of zero or more parameters defined for the subtype */
1072 | Parameter parameters;
1073 | } MediaType;
1074 |
1075 | struct {
1076 | /* must contain at least one item */
1077 | MediaType media_types;
1078 | } MediaTypeList;
1079 |
1080 | MediaTypeList content_media_types;
1081 | ~~~
1082 |
1083 | Example IANA media types with optional parameters:
1084 |
1085 | ~~~ artwork
1086 | image/png
1087 | text/plain ;charset="UTF-8"
1088 | application/json
1089 | application/vnd.example.msgbus+cbor
1090 | ~~~
1091 |
1092 | For the example media type for `text/plain`, the `media_type` field
1093 | would be `text/plain`, `parameters` would contain a single Parameter
1094 | with a `parameter_name` of `charset` and a `parameter_value` of `UTF-8`.
1095 |
1096 | ### Expected Behavior
1097 |
1098 | An MLS client which implements this section SHOULD include the
1099 | `content_media_types` component (in the `app_data_dictionary` extension)
1100 | in its LeafNodes, listing all the media types it can receive. As usual, the
1101 | client also includes `content_media_types` in the `app_components` list (in the
1102 | `app_data_dictionary` extension) and support for the `app_data_dictionary`
1103 | extension in its `capabilities.extensions` field in its LeafNodes (including
1104 | in LeafNodes inside its KeyPackages).
1105 |
1106 | When creating a new MLS group for an application using this specification,
1107 | the group MAY include a `content_media_types` component (in the
1108 | `app_data_dictionary` extension) in the GroupContext. (The creating
1109 | client also includes its `content_media_types` component in its own
1110 | LeafNode as described in the previous paragraph.)
1111 |
1112 | MLS clients SHOULD NOT add an MLS client to an MLS group with
1113 | `content_media_types` in its GroupContext unless the MLS client advertises it
1114 | can support all of the required MediaTypes.
1115 | As an exception, a client could be preconfigured to know that certain clients
1116 | support the required types. Likewise, an MLS client is already forbidden from
1117 | issuing or committing a GroupContextExtensions Proposal which introduces
1118 | required extensions which are not supported by all members in the resulting
1119 | epoch.
1120 |
1121 | ### Framing of application_data {#app-framing}
1122 |
1123 | When an MLS group contains the `content_media_types` component (in the
1124 | `app_data_dictionary` extension) in its GroupContext, the `application_data`
1125 | sent in that group is interpreted as `ApplicationFraming` as defined below:
1126 |
1127 | ~~~ tls
1128 | struct {
1129 | MediaType media_type;
1130 | opaque application_content;
1131 | } ApplicationFraming;
1132 | ~~~
1133 |
1134 | The `media_type` MAY be zero length, in which case, the media type of the
1135 | `application_content` is interpreted as the first MediaType specified in
1136 | the `content_media_types` component in the GroupContext.
1137 |
1138 | ## SelfRemove Proposal
1139 |
1140 | The design of the MLS protocol prevents a member of
1141 | an MLS group from removing itself immediately from the group. (To cause
1142 | an immediate change in the group, a member must send a Commit message.
1143 | However the sender of a Commit message knows the keying material of the
1144 | new epoch and therefore needs to be part of the group.) Instead a member
1145 | wishing to remove itself can send a Remove Proposal and wait for another
1146 | member to Commit its Proposal.
1147 |
1148 | Unfortunately, MLS clients that join via an External Commit ignore
1149 | pending, but otherwise valid, Remove Proposals. The member trying to remove
1150 | itself has to monitor the group and send a new Remove Proposal in every new
1151 | epoch until the member is removed. In a
1152 | group with a burst of external joiners, a member connected over a
1153 | high-latency link (or one that is merely unlucky) might have to wait
1154 | several epochs to remove itself. A real-world situation in which this happens
1155 | is a member trying to remove itself from a conference call as several dozen
1156 | new participants are trying to join (often on the hour).
1157 |
1158 | This section describes a new `SelfRemove` Proposal extension type. It is
1159 | designed to be included in External Commits.
1160 |
1161 | ### Extension Description
1162 |
1163 | This document specifies a new MLS Proposal type called `SelfRemove`. Its syntax
1164 | is described using the TLS Presentation Language {{!RFC8446}} below (its content
1165 | is an empty struct). It is allowed in External Commits and requires an UpdatePath.
1166 | SelfRemove proposals are only allowed in a Commit by reference. SelfRemove
1167 | cannot be sent as an external proposal.
1168 |
1169 | ~~~ tls-presentation
1170 | struct {} SelfRemove;
1171 |
1172 | struct {
1173 | ProposalType msg_type;
1174 | select (Proposal.msg_type) {
1175 | case add: Add;
1176 | case update: Update;
1177 | case remove: Remove;
1178 | case psk: PreSharedKey;
1179 | case reinit: ReInit;
1180 | case external_init: ExternalInit;
1181 | case group_context_extensions: GroupContextExtensions;
1182 | case self_remove: SelfRemove;
1183 | };
1184 | } Proposal;
1185 | ~~~
1186 |
1187 | The description of behavior below only applies if all the
1188 | members of a group support this extension in their
1189 | capabilities; such a group is a "self-remove-capable group".
1190 |
1191 | An MLS client which supports this extension can send a
1192 | SelfRemove Proposal whenever it would like to remove itself
1193 | from a self-remove-capable group. Because the point of a
1194 | SelfRemove Proposal is to be available to external joiners
1195 | (which are not yet members), these proposals MUST be sent
1196 | in an MLS PublicMessage.
1197 |
1198 | Whenever a member receives a SelfRemove Proposal, it includes
1199 | it along with any other pending Propsals when sending a Commit.
1200 | It already MUST send a Commit of pending Proposals before sending
1201 | new application messages.
1202 |
1203 | When a member receives a Commit referencing one or more SelfRemove Proposals,
1204 | it treats the proposal like a Remove Proposal, except the leaf node to remove
1205 | is determined by looking in the Sender `leaf_index` of the original Proposal.
1206 | The member is able to verify that the Sender was a member.
1207 |
1208 | Whenever a new joiner is about to join a self-remove-capable group with an
1209 | External Commit, the new joiner MUST fetch any pending SelfRemove Proposals
1210 | along with the GroupInfo object, and include the SelfRemove Proposals
1211 | in its External Commit by reference. (An ExternalCommit can contain zero or
1212 | more SelfRemove proposals). The new joiner MUST validate the SelfRemove
1213 | Proposal before including it by reference, except that it skips the validation
1214 | of the `membership_tag` because a non-member cannot verify membership.
1215 |
1216 | During validation, SelfRemove proposals are processed after Update proposals
1217 | and before Remove proposals. If there is a pending SelfRemove proposal for a specific
1218 | leaf node and a pending Remove proposal for the same leaf node, the Remove proposal is
1219 | invalid. A client MUST NOT issue more than one SelfRemove proposal per epoch.
1220 |
1221 | The MLS Delivery Service (DS) needs to validate SelfRemove Proposals it
1222 | receives (except that it cannot validate the `membership_tag`). If the DS
1223 | provides a GroupInfo object to an external joiner, the DS SHOULD attach any
1224 | SelfRemove proposals known to the DS to the GroupInfo object.
1225 |
1226 | As with Remove proposals, clients need to be able to receive a Commit
1227 | message which removes them from the group via a SelfRemove. If the DS does
1228 | not forward a Commit to a removed client, it needs to inform the removed
1229 | client out-of-band.
1230 |
1231 | ## Last resort KeyPackages
1232 |
1233 | Type: KeyPackage extension
1234 |
1235 | ### Description
1236 |
1237 | {{Section 10 of !RFC9420}} details that clients are required to pre-publish
1238 | KeyPackages so that other clients can add them to groups asynchronously. It
1239 | also states that they should not be re-used:
1240 |
1241 | > KeyPackages are intended to be used only once and SHOULD NOT be reused except
1242 | > in the case of a "last resort" KeyPackage (see Section 16.8). Clients MAY
1243 | > generate and publish multiple KeyPackages to support multiple cipher suites.
1244 |
1245 | {{Section 16.8 of !RFC9420}} then introduces the notion of last-resort
1246 | KeyPackages as follows:
1247 |
1248 | > An application MAY allow for reuse of a "last resort" KeyPackage in order to
1249 | > prevent denial-of-service attacks.
1250 |
1251 | However, {{!RFC9420}} does not specify how to distinguish regular KeyPackages
1252 | from last-resort ones. The last_resort_key_package KeyPackage application
1253 | component defined in this section fills this gap and allows clients to specifically mark KeyPackages as KeyPackages of last resort that MAY be used
1254 | more than once in scenarios where all other KeyPackages have already been used.
1255 |
1256 | The component allows clients that pre-publish KeyPackages to signal to the
1257 | Delivery Service which KeyPackage(s) are meant to be used as last resort
1258 | KeyPackages.
1259 |
1260 | An additional benefit of using a component rather than communicating the
1261 | information out-of-band is that the component is still present in Add proposals.
1262 | Clients processing such Add proposals can authenticate that a KeyPackage is a
1263 | last-resort KeyPackage and MAY make policy decisions based on that information.
1264 |
1265 | ### Format
1266 |
1267 | The purpose of the application component is simply to mark a given KeyPackage,
1268 | which means it carries no additional data.
1269 |
1270 | As a result, a LastResort Extension contains the `component_id` with an empty
1271 | `data` field.
1272 |
1273 | ## Multi-Credentials
1274 |
1275 | Multi-credentials address use cases where there might not be a single
1276 | credential that captures all of a client's authenticated attributes. For
1277 | example, an enterprise messaging client may wish to provide attributes both
1278 | from its messaging service, to prove that its user has a given handle in
1279 | that service, and from its corporate owner, to prove that its user is an
1280 | employee of the corporation. Multi-credentials can also be used in migration
1281 | scenarios, where some clients in a group might wish to rely on a newer type
1282 | of credential, but other clients haven't yet been upgraded.
1283 |
1284 | New credential types `MultiCredential` and `WeakMultiCredential` are
1285 | defined as shown below. These credential types are indicated with
1286 | the values `multi` and `weak-multi` (see {{iana-creds}}).
1287 |
1288 | ~~~ tls-presentation
1289 | struct {
1290 | CipherSuite cipher_suite;
1291 | Credential credential;
1292 | SignaturePublicKey credential_key;
1293 |
1294 | /* SignWithLabel(., "CredentialBindingTBS", CredentialBindingTBS) */
1295 | opaque signature;
1296 | } CredentialBinding
1297 |
1298 | struct {
1299 | CredentialBinding bindings;
1300 | } MultiCredential;
1301 |
1302 | struct {
1303 | CredentialBinding bindings;
1304 | } WeakMultiCredential;
1305 | ~~~
1306 |
1307 | The two types of credentials are processed in exactly the same way. The only
1308 | difference is in how they are treated when evaluating support by other clients,
1309 | as discussed below.
1310 |
1311 | ### Credential Bindings
1312 |
1313 | A multi-credential consists of a collection of "credential bindings". Each
1314 | credential binding is a signed statement by the holder of the credential that
1315 | the signature key in the LeafNode belongs to the holder of that credential.
1316 | Specifically, the signature is computed using the MLS `SignWithLabel` function,
1317 | with label `"CredentialBindingTBS"` and with a content that covers the contents
1318 | of the CredentialBinding, plus the `signature_key` field from the LeafNode in
1319 | which this credential will be embedded.
1320 |
1321 | ~~~ tls-presentation
1322 | struct {
1323 | CipherSuite cipher_suite;
1324 | Credential credential;
1325 | SignaturePublicKey credential_key;
1326 | SignaturePublicKey signature_key;
1327 | } CredentialBindingTBS;
1328 | ~~~
1329 |
1330 | The `cipher_suite` for a credential is NOT REQUIRED to match the cipher suite
1331 | for the MLS group in which it is used, but MUST meet the support requirements
1332 | with regard to support by group members discussed below.
1333 |
1334 | ### Verifying a Multi-Credential
1335 |
1336 | A credential binding is supported by a client if the client supports the
1337 | credential type and cipher suite of the binding. A credential binding is valid
1338 | in the context of a given LeafNode if both of the following are true:
1339 |
1340 | * The `credential` is valid according to the MLS Authentication Service.
1341 |
1342 | * The `credential_key` corresponds to the specified `credential`, in the same
1343 | way that the `signature_key` would have to correspond to the credential if
1344 | the credential were presented in a LeafNode.
1345 |
1346 | * The `signature` field is valid with respect to the `signature_key` value in
1347 | the leaf node.
1348 |
1349 | A client that receives a credential of type `multi` in a LeafNode MUST verify
1350 | that all of the following are true:
1351 |
1352 | * All members of the group support credential type `multi`.
1353 |
1354 | * For each credential binding in the multi-credential:
1355 |
1356 | * Every member of the group supports the cipher suite and credential type
1357 | values for the binding.
1358 |
1359 | * The binding is valid in the context of the LeafNode.
1360 |
1361 | A client that receives a credential of type `weak-multi` in a LeafNode MUST verify
1362 | that all of the following are true:
1363 |
1364 | * All members of the group support credential type `weak-multi`.
1365 |
1366 | * Each member of the group supports at least one binding in the
1367 | multi-credential. (Different members may support different subsets.)
1368 |
1369 | * Every binding that this client supports is valid in the context of the
1370 | LeafNode.
1371 |
1372 |
1373 | # IANA Considerations
1374 |
1375 | This document requests the addition of various new values under the heading
1376 | of "Messaging Layer Security". Each registration is organized under the
1377 | relevant registry Type.
1378 |
1379 | This document also requests the creation of a new MLS applications components
1380 | registry as described in {{iana-components}}.
1381 |
1382 | RFC EDITOR: Please replace XXXX throughout with the RFC number assigned to
1383 | this document
1384 |
1385 | ## MLS Wire Formats
1386 |
1387 | ### MLS Targeted Message
1388 |
1389 | The `mls_targeted_message` MLS Wire Format is used to send a message
1390 | to a subset of members of an MLS group.
1391 |
1392 | * Value: 0x0006 (suggested)
1393 | * Name: mls_targeted_message
1394 | * Recommended: Y
1395 | * Reference: RFC XXXX
1396 |
1397 |
1398 | ## MLS Extension Types
1399 |
1400 | ### app_data_dictionary MLS Extension
1401 |
1402 | The `app_data_dictionary` MLS Extension Type is used inside KeyPackage,
1403 | LeafNode, GroupContext, or GroupInfo objects. It contains a sorted list of
1404 | application component data objects (at most one per component).
1405 |
1406 | * Value: 0x0006 (suggested)
1407 | * Name: app_data_dictionary
1408 | * Message(s): KP: This extension may appear in KeyPackage objects
1409 | LN: This extension may appear in LeafNode objects
1410 | GC: This extension may appear in GroupContext objects
1411 | GI: This extension may appear in GroupInfo objects
1412 | * Recommended: Y
1413 | * Reference: RFC XXXX
1414 |
1415 | ### supported_wire_formats MLS Extension
1416 |
1417 | The `supported_wire_formats` MLS Extension Type is used inside LeafNode
1418 | objects. It contains a list of non-default Wire Formats supported by the
1419 | client node.
1420 |
1421 | * Value: 0x0007 (suggested)
1422 | * Name: supported_wire_formats
1423 | * Message(s): LN: This extension may appear in LeafNode objects
1424 | * Recommended: Y
1425 | * Reference: RFC XXXX
1426 |
1427 | ### required_wire_formats MLS Extension
1428 |
1429 | The `required_wire_formats` MLS Extension Type is used inside GroupContext
1430 | objects. It contains a list of non-default Wire Formats that are mandatory for
1431 | all MLS members of the group to support.
1432 |
1433 | * Value: 0x0008 (suggested)
1434 | * Name: required_wire_formats
1435 | * Message(s): GC: This extension may appear in GroupContext objects
1436 | * Recommended: Y
1437 | * Reference: RFC XXXX
1438 |
1439 | ### targeted_messages_capability MLS Extension
1440 |
1441 | The `targeted_messages_capability` MLS Extension Type is used in the
1442 | `capabilities.extensions` field of LeafNodes to indicate the support for the
1443 | Targeted Messages Extension, and in the `required_capabilities.extension_types`
1444 | field of the GroupContext to indicate all members of the group must support it.
1445 | The extension does not carry any payload.
1446 |
1447 | * Value: 0x0009 (suggested)
1448 | * Name: targeted_messages_capability
1449 | * Message(s): LN: This extension may appear in LeafNode objects
1450 | GC: This extension may appear in GroupContext objects
1451 | * Recommended: Y
1452 | * Reference: RFC XXXX
1453 |
1454 |
1455 | ## MLS Proposal Types
1456 |
1457 | ### AppDataUpdate Proposal
1458 |
1459 | The `app_data_update` MLS Proposal Type is used to efficiently update
1460 | application component data stored in the `app_data_dictionary` GroupContext
1461 | extension.
1462 |
1463 | * Value: 0x0008 (suggested)
1464 | * Name: app_data_update
1465 | * Recommended: Y
1466 | * External: Y
1467 | * Path Required: N
1468 |
1469 | ### AppEphemeral Proposal
1470 | The `app_ephemeral` MLS Proposal Type is used to send opaque ephemeral
1471 | application data that needs to be synchronized with a specific MLS epoch.
1472 |
1473 | * Value: 0x0009 (suggested)
1474 | * Name: app_ephemeral
1475 | * Recommended: Y
1476 | * External: Y
1477 | * Path Required: N
1478 |
1479 | ### SelfRemove Proposal
1480 |
1481 | The `self_remove` MLS Proposal Type is used for a member to remove itself
1482 | from a group more efficiently than using a `remove` proposal type, as the
1483 | `self_remove` type is permitted in External Commits.
1484 |
1485 | * Value: 0x0008 (suggested)
1486 | * Name: self_remove
1487 | * Recommended: Y
1488 | * External: N
1489 | * Path Required: Y
1490 |
1491 | ## MLS Credential Types {#iana-creds}
1492 |
1493 | ### Multi Credential
1494 |
1495 | * Value: 0x0003 (suggested)
1496 | * Name: multi
1497 | * Recommended: Y
1498 | * Reference: RFC XXXX
1499 |
1500 | ### Weak Multi Credential
1501 |
1502 | * Value: 0x0004
1503 | * Name: weak-multi
1504 | * Recommended: Y
1505 | * Reference: RFC XXXX
1506 |
1507 |
1514 |
1515 | ## MLS Component Types {#iana-components}
1516 |
1517 | This document requests the creation of a new IANA "MLS Component Types" registry under the "Messaging Layer Security" group registry heading. Assignments to this registry in the range 0x0000 0000 to 0x7FFF FFFF are via Specification Required
1518 | policy {{!RFC8126}} using the MLS Designated Experts. Assignments in the range
1519 | 0x8000 0000 to 0xFFFF FFFF are for private use.
1520 |
1521 | Template:
1522 |
1523 | - Value: The numeric value of the component ID
1524 | - Name: The name of the component
1525 | - Where: The objects(s) in which the component may appear,
1526 | drawn from the following list:
1527 | - AD: SafeAAD objects
1528 | - AE: AppEpheral proposals
1529 | - ES: Exporter Secret labels
1530 | - GC: GroupContext objects
1531 | - GI: GroupInfo objects
1532 | - HP: HPKE key labels
1533 | - KP: KeyPackage objects
1534 | - LN: LeafNode objects
1535 | - PS: PSK labels
1536 | - SK: Signature Key labels
1537 | - Recommended: Same as in {{Section 17.1 of !RFC9420}}
1538 | - Reference: The document where this component is defined
1539 |
1540 | The restrictions noted in the "Where" column are to be enforced by the
1541 | application. MLS implementations MUST NOT impose restrictions on where
1542 | component IDs are used in which parts of MLS, unless specifically directed to by
1543 | the application.
1544 |
1545 | Initial Contents:
1546 |
1547 | | Value | Name | Where | R | Ref |
1548 | |---------------+--------------------------+-------+---+---------|
1549 | | 0x0000 0000 | RESERVED | N/A | - | RFCXXXX |
1550 | | 0x0000 0001 | app_components | LN,GC | Y | RFCXXXX |
1551 | | 0x0000 0002 | safe_aad | LN,GC | Y | RFCXXXX |
1552 | | 0x0000 0003 | content_media_types | LN,GC | Y | RFCXXXX |
1553 | | 0x0000 0004 | last_resort_key_package | KP | Y | RFCXXXX |
1554 | | 0x0000 0005 | app_ack | AE | Y | RFCXXXX |
1555 | | 0x8000 0000 -
1556 | 0xFFFF FFFF | Reserved for Private Use | N/A | N | RFCXXXX |
1557 |
1558 |
1559 | # Security considerations
1560 |
1561 | ## Safe Application API
1562 |
1563 | The Safe Application API provides the following security guarantee: If an
1564 | application uses MLS with application components, the security guarantees of
1565 | the base MLS protocol and the security guarantees of each application component
1566 | analyzed in isolation, still hold for the composed application of the MLS
1567 | protocol. In other words, the Safe Application API protects applications from
1568 | careless component developers. It is not possible that a combination of
1569 | components (the developers of which did not know about each other) impedes the
1570 | security of the base MLS protocol or any other component. No further analysis of
1571 | the combination is necessary. This also means that any security vulnerabilities
1572 | introduced by one component do not spread to other components or the base MLS
1573 | implementation.
1574 |
1575 | ## AppAck
1576 |
1577 | When AppAck objects are received, they allow clients to detect if the Delivery
1578 | Service (or an intermediary) dropped application messages, since gaps in the
1579 | `generation` sequence indicate dropped messages. When AppAck messages
1580 | are accepted by the Delivery Service, but not received by some members, the
1581 | members who have missed the corresponding AppEphemeral proposals will not be
1582 | able to send or receive a commit message, because the proposal is included
1583 | in the transcript hash. Likewise if AppAck objects and/or commits are sent
1584 | periodically by every member, other members will be able to detect a member
1585 | that is no longer sending on that schedule or whose handshake messages are being
1586 | suppressed by the DS.
1587 |
1588 | > Note: External Commits do not typically contain pending proposals (including
1589 | > AppEphemeral proposals). Client that send an AppAck component in an
1590 | > AppEphemeral proposal will need to send a new AppAck component in an
1591 | > AppEphemeral proposal (in the new epoch) after receiving an External Commit
1592 | > until it has been incorporated into an accepted Commit.
1593 |
1594 | The schedule on which AppAck objects are sent in AppEphemeral proposals is up to
1595 | the application,and determines which cases of loss/suppression are detected.
1596 | For example:
1597 |
1598 | - The application might have the committer include an AppAck whenever a
1599 | Commit is sent, so that other members could know when one of their messages
1600 | did not reach the committer.
1601 |
1602 | - The application could have a client send an AppAck whenever an application
1603 | message is sent, covering all messages received since its last AppAck. This
1604 | would provide a complete view of any losses experienced by active members.
1605 |
1606 | - The application could simply have clients send AppAck proposals on a timer, so
1607 | that all participants' state would be known.
1608 |
1609 | An application using AppAck to guard against loss/suppression of
1610 | application messages also needs to ensure that AppAck messages and the Commits
1611 | that reference them are not dropped. One way to do this is to always encrypt
1612 | Proposal and Commit messages, to make it more difficult for the Delivery Service
1613 | to recognize which messages contain AppAcks. The application can also have
1614 | clients enforce an AppAck schedule, reporting loss if an AppAck is not received
1615 | at the expected time.
1616 |
1617 | ## Targeted Messages
1618 |
1619 | In addition to the sender authentication, Targeted Messages are authenticated by
1620 | using a preshared key (PSK) between the sender and the recipient. The PSK is
1621 | exported from the group key schedule using the label "targeted message psk".
1622 | This ensures that the PSK is only valid for a specific group and epoch, and the
1623 | Forward Secrecy and Post-Compromise Security guarantees of the group key
1624 | schedule apply to the targeted messages as well. The PSK also ensures that an
1625 | attacker needs access to the private group state in addition to the
1626 | HPKE/signature's private keys. This improves confidentiality guarantees against
1627 | passive attackers and authentication guarantees against active attackers.
1628 |
1629 | ## Content Advertisement
1630 |
1631 | Use of the `content_media_types` component could leak some private information
1632 | visible in KeyPackages and inside an MLS group. This could be used to infer a
1633 | specific implementation, platform, or even version. Clients should carefully
1634 | consider the privacy implications in their environment of making a list of
1635 | acceptable media types available.
1636 |
1637 | Implementations need to be prepared to parse media types containing long
1638 | parameter lists, potentially containing characters which would be escaped or
1639 | quoted in {{!RFC5322}}.
1640 |
1641 | ## SelfRemove
1642 |
1643 | An external recipient of a SelfRemove Proposal cannot verify the
1644 | `membership_tag`. However, an external joiner also has no way to
1645 | completely validate a GroupInfo object that it receives. An insider
1646 | can prevent an External Join by providing either an invalid GroupInfo object
1647 | or an invalid SelfRemove Proposal. The security properties of external joins
1648 | does not change with the addition of this proposal type.
1649 |
1650 |
1651 | ## Multi Credentials
1652 |
1653 | Using a Weak Multi Credential reduces the overall credential security to the
1654 | security of the least secure of its credential bindings.
1655 |
1656 | --- back
1657 |
1658 | # Change Log
1659 |
1660 | RFC EDITOR PLEASE DELETE THIS SECTION.
1661 |
1662 | draft-06
1663 |
1664 | - Integrate notion of Application API from draft-barnes-mls-appsync
1665 |
1666 | draft-05
1667 |
1668 | - Include definition of ExtensionState extension
1669 | - Add safe use of AAD to Safe Extensions framework
1670 | - Clarify how capabilities negotiation works in Safe Extensions framework
1671 |
1672 | draft-04
1673 |
1674 | - No changes (prevent expiration)
1675 |
1676 | draft-03
1677 |
1678 | - Add Last Resort KeyPackage extension
1679 | - Add Safe Extensions framework
1680 | - Add SelfRemove Proposal
1681 |
1682 | draft-02
1683 |
1684 | - No changes (prevent expiration)
1685 |
1686 | draft-01
1687 |
1688 | - Add Content Advertisement extensions
1689 |
1690 | draft-00
1691 |
1692 | - Initial adoption of draft-robert-mls-protocol-00 as a WG item.
1693 | - Add Targeted Messages extension (\*)
1694 |
1695 | # Old Safe Extensions Text
1696 |
1697 | The MLS specification is extensible in a variety of ways (see {{Section 13 of
1698 | !RFC9420}}) and describes the negotiation and other handling of extensions and
1699 | their data within the protocol. However, it does not provide guidance on how
1700 | extensions can or should safely interact with the base MLS protocol. The goal of
1701 | this section is to simplify the task of developing MLS extensions.
1702 |
1703 |
1704 | ## Extension state: anchoring, storage and agreement
1705 |
1706 | The safe extension framework can help an MLS extension ensure that all group
1707 | members agree on a piece of extension-specific state by using the
1708 | `ExtensionState` GroupContext extension. The ownership of an `ExtensionState`
1709 | extension in the context of the safe extension framework is determined by the
1710 | `extension_type` field. The extension with a matching `extension_type` is called
1711 | the owning extension.
1712 |
1713 | ~~~tls
1714 | enum {
1715 | reserved(0),
1716 | read(1),
1717 | none(2),
1718 | (255)
1719 | } Permissions;
1720 |
1721 | enum {
1722 | reserved(0),
1723 | hash(1),
1724 | data(2),
1725 | } HashOrData;
1726 |
1727 | struct {
1728 | HashOrData hash_or_data;
1729 | select(hash_or_data) {
1730 | case hash:
1731 | HashReference state_hash;
1732 | case data:
1733 | opaque state;
1734 | }
1735 | } ExtensionPayload;
1736 |
1737 | struct {
1738 | extensionType extension_type;
1739 | Permissions read;
1740 | ExtensionPayload payload;
1741 | } ExtensionState;
1742 | ~~~
1743 |
1744 | The `ExtensionState` GroupContext extension contains data either directly (if
1745 | `hash_or_data = data`) or inditectly via a hash (if `hash_or_data = hash`).
1746 |
1747 | The owning extension can read and write the state stored in an `ExtensionState`
1748 | extension using an extension-defined proposal (see ). The semantics
1749 | of the proposal determines how the state is changed.
1750 |
1751 | The `read` variable determines the permissions that other MLS extensions have
1752 | w.r.t. the data stored within. `read` allows other MLS extensions to read that
1753 | data via their own proposals, while `none` marks the data as private to the
1754 | owning MLS extension.
1755 |
1756 | Other extensions may never write to the `ExtensionState` of the owning MLS
1757 | extension.
1758 |
1759 | ### Direct vs. hash-based storage
1760 |
1761 | Storing the data directly in the `ExtensionState` means the data becomes part of
1762 | the group state. Depending on the application design, this can be advantageous,
1763 | because it is distributed via Welcome messages. However, it could also mean that
1764 | the data is visible to the delivery service. Additionally, if the application
1765 | makes use of GroupContextExtension proposals, it may be necessary to send all of
1766 | the data with each such extension.
1767 |
1768 | Including the data by hash only allows group members to agree on the data
1769 | indirectly, relying on the collision resistance of the associated hash function.
1770 | The data itself, however, may have to be transmitted out-of-band to new joiners.
1771 |
1772 | ## Extension Design Guidance
1773 |
1774 | While extensions can modify the protocol flow of MLS and the associated
1775 | properties in arbitrary ways, the base MLS protocol already enables a number of
1776 | functionalities that extensions can use without modifying MLS itself. Extension
1777 | authors should consider using these built-in mechanisms before employing more
1778 | intrusive changes to the protocol.
1779 |
--------------------------------------------------------------------------------