├── 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 | --------------------------------------------------------------------------------