Internet-Draft | MLS Member-only Secrets | October 2025 |
Mahy | Expires 19 April 2026 | [Page] |
This document describes a way to keep secrets in MLS groups that can be read by members, but not by in intermediary.¶
This note is to be removed before publishing as an RFC.¶
The latest revision of this draft can be found at https://rohanmahy.github.io/mls-member-secrets/draft-mahy-mls-member-secrets.html. Status information for this document may be found at https://datatracker.ietf.org/doc/draft-mahy-mls-member-secrets/.¶
Discussion of this document takes place on the Messaging Layer Security mailing list (mailto:mls@ietf.org), which is archived at https://mailarchive.ietf.org/arch/browse/mls/. Subscribe at https://www.ietf.org/mailman/listinfo/mls/.¶
Source for this draft and an issue tracker can be found at https://github.com/rohanmahy/mls-member-secrets.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 19 April 2026.¶
Copyright (c) 2025 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
This is a Work in Progress mechanism to keep some secrets with respect to non-members, including from the MLS Distribution Service (DS).¶
Realted work on a similar topic includes Minimal Metadata Rooms (MMR) [MMR].¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
In MLS, the term Directory Server (DS) refers to...¶
One key per group (MMR) in PR #102¶
Use a separate MLS group using PrivateMessage to generate secrets¶
struct { } EphemeralSecret EphemeralSecret EphemeralSecretsData<V>; struct { } EphemeralSecretsUpdate;¶
This document defines two new MLS application components to facilitate sharing secrets with members, that are hidden from the DS.
It defines a new MLS key schedule Exporter Label, member_identity_disclosure_secret
.¶
These two application components provide a way to efficiently update encryption secrets inside LeafNode and GroupContext objects, when needed to achieve privacy from the DS, including former members colluding with the DS.¶
During any epoch when a disclosure is added or updated, the new (added or updated) ephemeral keys are encrypted using the member_identity_disclosure_secret
for the epoch about to be committed.
Also the member_identity_disclosure_secret
s for any previous epochs that are still being used to encrypt individual credentials are encrypted using the target epoch member_identity_disclosure_secret
in the OldEpochMemberEncryptionKeys struct.¶
struct { opaque bytes<V>; } BlindedClaimHash; struct { uint32 leaf_index; BlindedClaimHash blinded_claim_hash; uint64 encrypted_during_epoch; } PerDisclosureEpochEncryptor; struct { PerDisclosureEpochEncryptor per_disclosure_epoch_map<V>; } PerDisclosureEncryptionMap; PerDisclosureEncryptionMap per_disclosure_encryption_map; struct { BlindedClaimHash removed_disclosures<V>; PerDisclosureEpochEncryptor updated_disclosures<V>; } PerDisclosureEncryptionMapUpdate; PerDisclosureEncryptionMapUpdate per_disclosure_encryption_map_update; struct { uint64 epoch; opaque encrypted_member_secret<V>; } OldEpochEncryptionKey; struct { OldEpochEncryptionKey old_epoch_encyption_keys<V>; } OldEpochMemberEncryptionKeys; OldEpochMemberEncryptionKeys old_epoch_member_encryption; OldEpochMemberEncryptionKeys old_epoch_member_encryption_update;¶
Using the scheme above, any member-only disclosures are encrypted with a member-only key from the epoch in which these disclosures originally appear. New joiners are provided a way to view the old per-epoch keys, so the new joiners can decrypt the member-only disclosures of pre-existing members, but that not every disclosure needs to be updated during every commit.¶
In many implementations, the AS and DS are tightly-coupled, so hiding information from the DS which is known to the SD-CWT Issuer is not a priority. However, if PLP is desired, PLP updates are needed only when there is a Remove proposal (or SelfRemove proposal) and an Add proposal in the same or later commit. In other words if Alice removes Bob in epoch 4, but the next add isn't until Alice adds Cathy in epoch 6, then a PLP update needs to be included in epoch 6. This prevents Cathy from colluding with the DS to decrypt a disclosure about Bob.¶
To perform a PLP update, the (PerDisclosureEpochEncryptor) ephemeral keys encrypted from any epoch or earlier than the epoch containing a Remove or SelfRemove, are re-encrypted using the about-to-be committed member_identity_disclosure_secret
. As with any other commit, unused previous epochs are pruned from OldEpochMemberEncryptionKeys, and all the remaining member_identity_disclosure_secret
s for old epochs are encrypted using the target epoch member_identity_disclosure_secret
.¶
To possibly modify later with the SafeEncryptWithLabel. ???¶
encrypted_disclosure = EncryptWithLabel(public_key, label, context, plaintext) disclosure = DecryptWithLabel(private_key, label, context, kem_output, ciphertext)¶
This mechanism could¶
If the same signature keys are used across groups, a former member colluding¶
This document has no IANA actions.¶
TODO acknowledge.¶