mls

package module
v0.0.0-...-1c42a37 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 30, 2026 License: MIT Imports: 31 Imported by: 0

README

mls

Package mls implements the Messaging Layer Security protocol, RFC 9420, with no dependencies beyond the Go standard library and golang.org/x/crypto.

go get github.com/tmc/mls

It requires Go 1.27 or later.

tlssyntax implements the TLS presentation language as MLS extends it: optional values, and vectors with variable-size length headers. The mls package defines the protocol's structures on top of it, along with the cryptography that protects them.

var msg mls.Message
if err := mls.Unmarshal(data, &msg); err != nil {
	return err
}
if msg.WireFormat == mls.WireFormatKeyPackage {
	fmt.Println(msg.KeyPackage.LeafNode.Credential.Identity)
}

License

MIT. See LICENSE. The test vectors in testdata come from the MLS working group and carry their own license. The code ported from CIRCL in internal/fp448 and internal/ed448 is under CIRCL's BSD license, in each package's LICENSE file.

State

Complete for all seven cipher suites. The wire format covers every structure in RFC 9420, and above it the package implements the cipher suites (over the Go 1.27 crypto/hpke package, and for X448 an internal HPKE), the key schedule, the secret tree and its ratchets, transcript hashes, signatures and hash references, message framing and protection, the ratchet tree with its hashes and parent hashes, TreeKEM update paths, the group state machine, external joins, external proposals, reinitialization and branching.

alice, err := mls.NewClient(cs, cred, 24*time.Hour)
g, err := alice.NewGroup(groupID, nil)
next, commit, welcome, err := g.Commit([]*mls.Proposal{
	{Type: mls.ProposalTypeAdd, Add: &mls.Add{KeyPackage: *bobKeyPackage}},
})

All seven RFC 9420 suites are supported. Suites 4 and 6 need X448 and Ed448, which neither the standard library nor x/crypto provides; the field and scalar arithmetic in internal/fp448 and internal/ed448 is ported from CIRCL, and internal/x448, internal/ed448 and internal/hpkex448 build X448, Ed448 and DHKEM(X448, HKDF-SHA512) on it. Suites 3 and 6 use ChaCha20-Poly1305 for message protection, which the standard library exposes only through HPKE, so that one algorithm comes from golang.org/x/crypto/chacha20poly1305; everything else is stdlib.

In FIPS 140-3 mode (GODEBUG=fips140=on, or a GOFIPS140 build such as the certified v1.0.0 module), only suites 2, 5 and 7 (P-256, P-521, P-384) are supported; the X25519 suites 1 and 3 and the X448 suites 4 and 6 report ErrUnsupportedCipherSuite. GODEBUG=fips140=only is not supported: MLS derives its own AES-GCM nonces, which the Go module does not approve for encryption. See the package documentation.

Everything RFC 9420 defines is implemented.

The multicred subpackage implements the multi-credential and weak multi-credential types of draft-ietf-mls-extensions, registered through mls.RegisterCredential.

Tests

The tests run the working group's test vectors. All sixteen suites pass: messages, deserialization, tree-math, crypto-basics, key-schedule, psk_secret, transcript-hashes, secret-tree, message-protection, tree-operations, tree-validation, welcome, treekem, passive-client-welcome, passive-client-random, and passive-client-handling-commit, on all seven cipher suites. The vector files are vendored in testdata, from mls-implementations commit cfd450286d1b, the one the interop harness uses; go generate ./... fetches them again from that commit.

The wire format is fuzzed. FuzzMessage, FuzzKeyPackage, FuzzRatchetTree, FuzzWelcome, FuzzGroupInfo and FuzzProposal seed their corpora from the working group's own encodings and check two properties: decoding must not panic, and anything that decodes must re-encode to the bytes it came from. The second is the one that matters for security, because a structure with two encodings lets an attacker change the bytes a signature covers without changing the value it is checked against.

go test -fuzz FuzzMessage

So is the group's evolution. FuzzGroup, in the separate module groupfuzz, runs several members through sequences of proposals, commits, joins and application messages, delivered late and in the order a delivery service would, and checks after each step that the members in the latest epoch agree on its group context, tree and secrets.

cd groupfuzz && go test -fuzz FuzzGroup

Security

This package has not been audited. It implements RFC 9420 and passes the working group's vectors, which is evidence about correctness, not about resistance to attack.

One property it does not claim: secrets live in ordinary Go byte slices and are not zeroed when consumed, so RFC 9420 Section 9.2's deletion requirement is met logically but not in memory.

Against traffic analysis the package offers only what RFC 9420 Section 15.1 offers, which is padding: set Client.Padding and Group.Protect pads each message to a multiple of it. The default of zero pads nothing and puts the exact length of every message on the wire.

Every operation that touches a private key runs under crypto/subtle.WithDataIndependentTiming, which on an arm64 CPU with FEAT_DIT removes the operand dependence of instructions whose latency would otherwise vary. That is a floor, not a guarantee: it does not make variable-time code constant time, and it does not hide the cache-timing signal of the table-driven AES the standard library falls back to on a CPU without AES instructions. On such a CPU prefer a ChaCha20-Poly1305 cipher suite.

Report a vulnerability privately, not in an issue; see SECURITY.md.

Documentation

Overview

Package mls implements the Messaging Layer Security protocol, RFC 9420.

This package defines the protocol's structures and their encodings. Each structure implements github.com/tmc/mls/tlssyntax.Marshaler and github.com/tmc/mls/tlssyntax.Unmarshaler, so Marshal and Unmarshal convert between a structure and its encoding:

var kp mls.KeyPackage
if err := mls.Unmarshal(data, &kp); err != nil {
	return err
}

The structures follow the specification field for field, including its variant records: a structure that the specification writes as a select carries the selector as an ordinary field, and only the fields that selector admits are encoded. LeafNode.Source, for example, decides whether a LeafNode carries a lifetime, a parent hash, or neither.

Above the wire format, the package provides the cryptographic layer the protocol is built from: CipherSuite holds the labeled derivation, signature and HPKE operations of Section 5 and Section 8, and RatchetTree implements the tree hashes, parent hashes, membership changes and TreeKEM update paths. The key schedule of Section 8 and the secret tree of Section 9 are internal to the package; a Group reaches them for you, and Group.Export is the supported way to derive an application secret from an epoch.

Client and Group tie these together into the group state machine. RFC 9420, Section 14 requires that generating a commit not modify the client's state, since the delivery service may accept a different commit for the same epoch; this package enforces that by construction, because a Group is one epoch's state and a commit never modifies it. A client generates its keys with NewClient and either starts a group with Client.NewGroup or joins one from a welcome message with Client.Join, or from a GroupInfo alone with Client.JoinExternal, which is the external commit of Section 8.3. A party outside a group may propose to it as well, either as a sender the group provisioned (ExternalClient.Propose) or by asking to be added (Client.ProposeAdd); a member keeps such a proposal, and so commits it, only if its Client.ExternalProposal policy accepts it; by default it accepts provisioned senders and refuses requests to be added. A member replaces its own keys with Group.ProposeUpdate, which keeps the new encryption key so that the member can follow the commit that applies it. A group ends either by losing its members or by being reinitialized: see Group.Reinit, Group.Reinitialize, Group.Branch and Client.Resume.

Processing a commit does not change the group it applies to: Group.Commit and Group.Handle return the group's state in the epoch that the commit begins, so a caller that has not yet confirmed a commit can keep using the epoch it has. Within an epoch, though, a Group is not immutable: protecting and unprotecting messages advance its ratchets and proposals are recorded in it, so a Group is not safe for concurrent use.

alice, err := mls.NewClient(cs, cred, 24*time.Hour)
g, err := alice.NewGroup(groupID, nil)
next, commit, welcome, err := g.Commit([]*mls.Proposal{add})

All seven cipher suites of RFC 9420 are implemented. The Go standard library provides neither X448 nor Ed448, so suites 4 and 6 use internal implementations of them and of their HPKE.

Decoding never authenticates: a decoded structure is well formed, and the caller must still verify its signatures.

Credential types outside RFC 9420 are registered by subpackages: see RegisterCredential and github.com/tmc/mls/multicred.

Post-quantum cipher suites

The package also implements the eleven cipher suites of draft-ietf-mls-pq-ciphersuites-06, from MLKEM768X25519AES128GCMSHA256Ed25519 to MLKEM1024AES256GCMSHA384MLDSA87. Their KEMs are the ML-KEM and hybrid KEMs of crypto/hpke (draft-ietf-hpke-pq), and three of them sign with ML-DSA from crypto/mldsa: pure ML-DSA with an empty context string, as draft-ietf-tls-mldsa specifies for TLS, over the same SignContent as every other suite (RFC 9420, Section 5.1.2). ECDSA signs with the hash its TLS signature scheme names, so a P-256 suite signs with SHA-256 even where the suite's hash is SHA-384.

The draft leaves the code points to IANA, which has not assigned them. This package uses the provisional code points of OpenMLS, which carries these suites behind its draft-ietf-mls-pq-ciphersuites feature, so that the two can interoperate; OpenMLS has none for the MLKEM768-P256 and MLKEM1024-P384 suites, which use 0xF003 to 0xF005 from the private-use range. Every one of these code points will change when IANA assigns the real ones, and the suites themselves may change with the draft.

Key packages and commits in these suites are large: an ML-KEM-1024 public key is 1568 bytes and an ML-DSA-87 signature 4627 bytes.

Security

This package has not been audited. Passing the working group's vectors is evidence about correctness, not about resistance to attack.

Every operation that touches a private key runs under crypto/subtle.WithDataIndependentTiming, which on an arm64 CPU with FEAT_DIT removes the operand dependence of instructions whose latency would otherwise vary. That is a floor, not a guarantee. It does not make variable-time code constant time, and in particular it does not hide a cache-timing signal, because the mode says nothing about which memory a program touches. The case that matters here is AES: on a CPU without AES instructions the standard library falls back to a table-driven implementation whose lookups are indexed by key material, which is the input a flush-and-reload attacker measures. On such a CPU prefer a ChaCha20-Poly1305 cipher suite, whose implementation is table-free.

One property the package does not claim: secrets live in ordinary Go byte slices and are not zeroed when consumed, so the deletion requirement of RFC 9420, Section 9.2 is met logically but not in memory; Go offers no way to guarantee a wipe survives the optimizer.

Against traffic analysis the package offers what RFC 9420, Section 15.1 offers, which is padding: set Client.Padding and Group.Protect pads each message to a multiple of it. The default of zero pads nothing and puts the exact length of every message on the wire.

FIPS 140-3

In FIPS 140-3 mode (see crypto/fips140), set by GODEBUG=fips140=on or by building with GOFIPS140, including the certified module version GOFIPS140=v1.0.0, only P256AES128GCMSHA256P256, P384AES256GCMSHA384P384, P521AES256GCMSHA512P521 and the post-quantum suites that use neither X25519 nor ChaCha20-Poly1305 are supported. The suites that use X25519 or X448 report ErrUnsupportedCipherSuite, whether a client uses one or a received message names one: X25519, X448 and Ed448 are not approved algorithms, and ChaCha20-Poly1305 comes from golang.org/x/crypto, outside the module. That includes the MLKEM768-X25519 suites, although the module runs that hybrid as an approved ML-KEM service, since a deployment that needs approved algorithms has the MLKEM768-P256 and pure ML-KEM suites. The module v1.0.0 has no ML-DSA, so built with GOFIPS140=v1.0.0 the ML-DSA suites report ErrUnsupportedCipherSuite as well.

GODEBUG=fips140=only is not supported. MLS derives each AES-GCM nonce itself, from the secret tree and the reuse guard (RFC 9420, Sections 6.3.1 and 9.1), and the module approves GCM encryption only with nonces it generates itself or builds for specific protocols such as TLS, SSH and HPKE. In fips140=on mode such encryption runs but is recorded as a non-approved service; decryption with a supplied nonce is approved. With GOFIPS140=v1.0.0 the same holds for the AES-GCM inside crypto/hpke, which encrypts group secrets and path secrets. In fips140=only mode crypto/cipher.NewGCM refuses the construction, and CipherSuite.AEAD reports ErrUnsupportedCipherSuite, so a group can neither protect nor unprotect messages.

Example
package main

import (
	"fmt"

	"github.com/tmc/mls"
)

func main() {
	kp := mls.KeyPackage{
		Version:     mls.Version10,
		CipherSuite: mls.X25519AES128GCMSHA256Ed25519,
		InitKey:     []byte("init key"),
		LeafNode: mls.LeafNode{
			EncryptionKey: []byte("encryption key"),
			SignatureKey:  []byte("signature key"),
			Credential:    mls.Credential{Type: mls.CredentialTypeBasic, Identity: []byte("alice")},
			Capabilities: mls.Capabilities{
				Versions:     []mls.ProtocolVersion{mls.Version10},
				CipherSuites: []mls.CipherSuite{mls.X25519AES128GCMSHA256Ed25519},
			},
			Source:   mls.LeafNodeSourceKeyPackage,
			Lifetime: mls.Lifetime{NotBefore: 0, NotAfter: 1 << 40},
		},
	}

	data, err := mls.Marshal(&kp)
	if err != nil {
		panic(err)
	}

	var back mls.KeyPackage
	if err := mls.Unmarshal(data, &back); err != nil {
		panic(err)
	}
	fmt.Printf("%d bytes, %s, %s, identity %q\n",
		len(data), back.Version, back.CipherSuite, back.LeafNode.Credential.Identity)
}
Output:
80 bytes, mls10, MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, identity "alice"
Example (Group)

This example runs a group through its life: Alice creates it and adds Bob, they exchange a message, and Alice removes Bob. The messages pass through their encodings, as they would through a delivery service. The cipher suite is one that FIPS 140-3 mode also accepts.

package main

import (
	"errors"
	"fmt"
	"log"
	"time"

	"github.com/tmc/mls"
)

func main() {
	cs := mls.P256AES128GCMSHA256P256
	alice, err := mls.NewClient(cs, mls.Credential{Type: mls.CredentialTypeBasic, Identity: []byte("alice")}, time.Hour)
	if err != nil {
		log.Fatal(err)
	}
	bob, err := mls.NewClient(cs, mls.Credential{Type: mls.CredentialTypeBasic, Identity: []byte("bob")}, time.Hour)
	if err != nil {
		log.Fatal(err)
	}

	// Alice creates the group and commits an Add of Bob's key
	// package. The commit returns Alice's state in the next epoch and
	// a Welcome for Bob.
	a0, err := alice.NewGroup([]byte("group"), nil)
	if err != nil {
		log.Fatal(err)
	}
	add := &mls.Proposal{Type: mls.ProposalTypeAdd, Add: &mls.Add{KeyPackage: *bob.KeyPackage}}
	a1, _, welcome, err := a0.Commit([]*mls.Proposal{add})
	if err != nil {
		log.Fatal(err)
	}
	b1, err := bob.Join(deliver(welcome).Welcome, nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("bob joined at epoch", b1.Epoch())

	// Bob protects a message; Alice unprotects it.
	msg, err := b1.Protect(nil, []byte("hello, alice"))
	if err != nil {
		log.Fatal(err)
	}
	got, err := a1.Unprotect(deliver(msg))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("alice read %q from leaf %d\n", got.Content.ApplicationData, got.Content.Sender.LeafIndex)

	// Alice removes Bob, and handling the commit reports the removal
	// to Bob.
	remove := &mls.Proposal{Type: mls.ProposalTypeRemove, Remove: &mls.Remove{Removed: uint32(b1.Index)}}
	a2, commit, _, err := a1.Commit([]*mls.Proposal{remove})
	if err != nil {
		log.Fatal(err)
	}
	_, _, err = b1.Handle(deliver(commit))
	fmt.Println("alice is at epoch", a2.Epoch())
	fmt.Println("bob removed:", errors.Is(err, mls.ErrRemoved))
}

// deliver passes m through its encoding, as a delivery service would.
func deliver(m *mls.Message) *mls.Message {
	b, err := mls.Marshal(m)
	if err != nil {
		log.Fatal(err)
	}
	var out mls.Message
	if err := mls.Unmarshal(b, &out); err != nil {
		log.Fatal(err)
	}
	return &out
}
Output:
bob joined at epoch 1
alice read "hello, alice" from leaf 1
alice is at epoch 2
bob removed: true

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrApplicationNotEncrypted = errors.New("mls: application data must be sent as a PrivateMessage")

ErrApplicationNotEncrypted is reported when application data is framed as a PublicMessage. RFC 9420, Section 6.2 requires that application messages always be encrypted.

View Source
var ErrBadConfirmationTag = errors.New("mls: confirmation tag does not verify")

ErrBadConfirmationTag is reported for a commit or group info whose confirmation tag does not match the key schedule.

View Source
var ErrBadExternalCommit = errors.New("mls: malformed external commit")

ErrBadExternalCommit is reported for a commit from a new member that does not meet the rules of RFC 9420, Section 12.4.3.2: it must carry an update path and exactly one ExternalInit proposal, and it must not refer to proposals by reference or carry any proposal but ExternalInit, Remove and PreSharedKey.

View Source
var ErrBadExternalSender = errors.New("mls: malformed external proposal")

ErrBadExternalSender is reported for a proposal from outside the group that RFC 9420, Section 12.1.8 does not allow: one that names no entry in the external_senders extension, that carries a proposal type an external sender may not send, or that is encrypted, which a non-member cannot do.

View Source
var ErrBadLeafNodeSource = errors.New("mls: wrong leaf node source")

ErrBadLeafNodeSource is reported for a leaf node whose source is not the one its position requires: key_package in a key package, update in an Update proposal, commit in an update path. See RFC 9420, Section 7.3.

View Source
var ErrBadMembershipTag = errors.New("mls: membership tag does not verify")

ErrBadMembershipTag is reported when the membership tag of a PublicMessage does not verify.

View Source
var ErrBadPSKNonce = errors.New("mls: psk_nonce has the wrong length")

ErrBadPSKNonce is reported for a welcome message naming a pre-shared key whose nonce is not as long as the suite's hash output. See RFC 9420, Section 8.4.

View Source
var ErrBadPadding = errors.New("mls: padding is not zero")

ErrBadPadding is reported when the padding of a PrivateMessage is not all zero.

View Source
var ErrBadParentHash = errors.New("mls: parent node is not parent-hash valid")

ErrBadParentHash is reported when a parent node in a ratchet tree cannot be chained back to a leaf by parent hashes, which means it was not introduced by a member of the group.

View Source
var ErrBadSignature = errors.New("mls: signature does not verify")

ErrBadSignature is reported when a signature does not verify.

View Source
var ErrBadTreeHash = errors.New("mls: tree hash does not match")

ErrBadTreeHash is reported for a ratchet tree that does not match the tree hash in the group context that summarizes it.

View Source
var ErrBadTreeKEM = errors.New("mls: malformed update path")

ErrBadTreeKEM is reported for an update path that does not match the ratchet tree it is applied to: a wrong number of nodes, or a public key that does not match the path secret encrypted under it.

View Source
var ErrBadUnmergedLeaves = errors.New("mls: malformed unmerged leaves")

ErrBadUnmergedLeaves is reported for a parent node whose unmerged_leaves does not list a set of leaves below it that the nodes in between agree on. The list decides a node's resolution, and so who a commit encrypts path secrets to.

View Source
var ErrBlankParent = errors.New("mls: node is blank or is not a parent")

ErrBlankParent is reported when a parent hash is computed for a node that is blank or is a leaf, which means the tree does not have the shape the operation requires.

View Source
var ErrConsumed = errors.New("mls: secret has already been consumed")

ErrConsumed is reported for a secret that the deletion schedule of RFC 9420, Section 9.2 has already discarded.

View Source
var ErrDuplicateExtension = errors.New("mls: duplicate extension")

ErrDuplicateExtension is reported for a list of extensions holding more than one extension of the same type, which RFC 9420, Section 13.4 forbids.

View Source
var ErrDuplicateLeafKey = errors.New("mls: leaf node key is already in use")

ErrDuplicateLeafKey is reported for a leaf node whose encryption or signature key is already in use in the group, which RFC 9420, Section 7.3 forbids.

View Source
var ErrDuplicateNodeKey = errors.New("mls: encryption key appears at more than one node")

ErrDuplicateNodeKey is reported for a ratchet tree in which one encryption key appears at more than one node. Whoever holds that key's private half can read both positions.

View Source
var ErrEmptyTree = errors.New("mls: ratchet tree is empty")

ErrEmptyTree is reported for a ratchet tree with no leaves. A group always has at least one member, so an empty tree is malformed.

View Source
var ErrExpired = errors.New("mls: key package has expired")

ErrExpired is reported for a key package used outside the lifetime of its leaf node.

View Source
var ErrExternalProposal = errors.New("mls: proposal from outside the group not accepted")

ErrExternalProposal is reported for a proposal from outside the group that the client's Client.ExternalProposal did not accept, or, with no policy, for a client's request to be added.

View Source
var ErrGenerationJump = errors.New("mls: message generation is too far ahead")

ErrGenerationJump is reported for a message whose generation is so far ahead of the ratchet that reaching it would cost more than losing the message does.

View Source
var ErrLeafRange = errors.New("mls: leaf index out of range")

ErrLeafRange is reported for a leaf index outside the group.

View Source
var ErrMalformedTree = errors.New("mls: malformed ratchet tree")

ErrMalformedTree is reported for a ratchet tree whose array does not have the shape of RFC 9420, Section 12.4.3.3: a leaf at an odd index or a parent at an even one, a blank last node, or a node past the width of the tree.

View Source
var ErrMembership = errors.New("mls: new group's members do not match the old group's")

ErrMembership is reported by Group.Reinitialize and Group.Branch for a new group whose members are not the ones RFC 9420, Sections 11.2 and 11.3 allow: the old group's members, all of them for a reinitialization and some of them for a branch.

View Source
var ErrMissingGroupContext = errors.New("mls: group context required for this sender type")

ErrMissingGroupContext is reported when signing or verifying content whose sender type requires the group context, without one.

View Source
var ErrNoExternalPub = errors.New("mls: group info has no external_pub extension")

ErrNoExternalPub is reported for a GroupInfo that an external join cannot use because it has no external_pub extension.

View Source
var ErrNoRatchetTree = errors.New("mls: no ratchet tree")

ErrNoRatchetTree is reported when a welcome message carries no ratchet_tree extension and the caller supplied no tree.

View Source
var ErrNotCommit = errors.New("mls: content is not a commit")

ErrNotCommit is reported when a transcript hash is computed over content that is not a commit.

View Source
var ErrNotForGroup = errors.New("mls: message is not for this group and epoch")

ErrNotForGroup is reported for a message addressed to another group or to another epoch of this one.

View Source
var ErrNotInPath = errors.New("mls: no path secret for this member")

ErrNotInPath is reported when an update path carries no path secret that the member can decrypt.

View Source
var ErrNotInWelcome = errors.New("mls: welcome message has no secrets for this key package")

ErrNotInWelcome is reported when a welcome message carries no secrets for the key package it was offered to.

View Source
var ErrNotMember = errors.New("mls: sender is not a group member")

ErrNotMember is reported when content that only a member may send has some other sender type.

View Source
var ErrNotProposal = errors.New("mls: content is not a proposal")

ErrNotProposal is reported for content that must be a proposal and is not.

View Source
var ErrNotReinitialized = errors.New("mls: group has not been reinitialized")

ErrNotReinitialized is reported by Group.Reinitialize for a group whose members have not committed a Reinit proposal.

View Source
var ErrNotResumed = errors.New("mls: group does not resume the one given")

ErrNotResumed is reported by Client.Resume for a welcome message that does not carry the group it names into a new one, as RFC 9420, Sections 11.2 and 11.3 require.

View Source
var ErrOwnCommit = errors.New("mls: cannot apply one's own commit")

ErrOwnCommit is reported when a member applies a commit it sent itself, which it cannot: Group.Commit already returned the state that commit begins.

View Source
var ErrPSKCount = errors.New("mls: pre-shared key count does not match identifier count")

ErrPSKCount is reported when the pre-shared key identifiers and the key material given for them do not correspond.

View Source
var ErrPathRequired = errors.New("mls: commit requires an update path")

ErrPathRequired is reported for a commit that carries no update path where RFC 9420, Section 12.4 requires one. Without a path the commit secret is all zero, so the epoch it begins is derived entirely from secrets the previous epoch's members already hold, and a member the commit removes can follow the group.

View Source
var ErrProposalList = errors.New("mls: invalid proposal list")

ErrProposalList is reported for a commit whose list of proposals breaks one of the rules of RFC 9420, Section 12.2. The message says which rule.

View Source
var ErrReinitialized = errors.New("mls: group has been reinitialized")

ErrReinitialized is reported for a group that has committed a Reinit proposal, which ends it: its members move to the group Group.Reinitialize creates. See RFC 9420, Section 11.2.

View Source
var ErrRemoved = errors.New("mls: member was removed from the group")

ErrRemoved is reported when a commit removes the member applying it.

View Source
var ErrSameGroupID = errors.New("mls: new group has the group id of the old one")

ErrSameGroupID is reported by Group.Branch for a new group given the group ID of the group it branches from. A branch is a new group, and RFC 9420, Sections 11 and 12.4.3.1 ask that group IDs be unique.

View Source
var ErrTooManyProposals = errors.New("mls: too many proposals in one epoch")

ErrTooManyProposals is reported for a proposal that would take the number a group remembers in one epoch past the limit, in all or from its sender.

View Source
var ErrUnknownPSK = errors.New("mls: unknown pre-shared key")

ErrUnknownPSK is reported for a pre-shared key the client cannot supply.

View Source
var ErrUnknownProposal = errors.New("mls: unknown proposal")

ErrUnknownProposal is reported for a proposal that a commit refers to but that the member never received.

View Source
var ErrUnknownVariant = errors.New("mls: unknown variant")

ErrUnknownVariant reports a variant tag outside the ranges this package implements: a credential type, proposal type, wire format, or similar value from an extension or a future version. Callers distinguish it from a malformed encoding, which decoders report as tlssyntax.ErrMalformed, with errors.Is.

View Source
var ErrUnsupportedCapability = errors.New("mls: unsupported capability")

ErrUnsupportedCapability is reported for a leaf node that does not support what it must: an extension it carries, a credential type another member uses, or the group's required capabilities. See RFC 9420, Section 7.3.

View Source
var ErrUnsupportedCipherSuite = errors.New("mls: unsupported cipher suite")

ErrUnsupportedCipherSuite is reported for cipher suites this package cannot implement. In FIPS 140-3 mode, the suites that use X25519 or X448 are unsupported; see the package documentation.

View Source
var ErrUnsupportedVersion = errors.New("mls: unsupported protocol version")

ErrUnsupportedVersion is reported for a protocol version other than mls10.

Functions

func Marshal

func Marshal(v tlssyntax.Marshaler) ([]byte, error)

Marshal returns the encoding of v.

func RegisterCredential

func RegisterCredential(t CredentialType, body func() CredentialCodec)

RegisterCredential records how to encode and decode the body of a credential of type t; body returns a fresh value to decode into. It panics if t is already registered or is a type this package defines itself.

Registration is meant to happen from an init function, as github.com/tmc/mls/multicred does, but it is safe to call at any time: the registry is guarded, so a registration cannot race a credential being encoded or decoded.

func Unmarshal

func Unmarshal(data []byte, v tlssyntax.Unmarshaler) error

Unmarshal decodes data into v. It reports an error if decoding fails or if any bytes remain after v is decoded.

Types

type Add

type Add struct {
	KeyPackage KeyPackage
}

An Add proposal requests that the client offering key_package be added to the group. See RFC 9420, Section 12.1.1.

func (*Add) MarshalTLS

func (a *Add) MarshalTLS(w *tlssyntax.Writer)

func (*Add) UnmarshalTLS

func (a *Add) UnmarshalTLS(r *tlssyntax.Reader)

type ApplicationID

type ApplicationID struct {
	ID []byte
}

An ApplicationID carries an application-chosen identifier for the member at a leaf, for applications that need one that outlives the member's keys. It says nothing about the member's identity, which is the credential's business. See RFC 9420, Section 7.2.

func (*ApplicationID) MarshalTLS

func (a *ApplicationID) MarshalTLS(w *tlssyntax.Writer)

func (*ApplicationID) UnmarshalTLS

func (a *ApplicationID) UnmarshalTLS(r *tlssyntax.Reader)

type AuthenticatedContent

type AuthenticatedContent struct {
	WireFormat WireFormat
	Content    FramedContent
	Auth       FramedContentAuthData
}

AuthenticatedContent is a FramedContent with the signature that covers it, independent of how it was carried on the wire. It is what the transcript hashes and proposal references are computed over. See RFC 9420, Section 6.1.

func (*AuthenticatedContent) MarshalTLS

func (c *AuthenticatedContent) MarshalTLS(w *tlssyntax.Writer)

func (*AuthenticatedContent) PublicMessage

func (c *AuthenticatedContent) PublicMessage(cs CipherSuite, membershipKey []byte, ctx *GroupContext) (*PublicMessage, error)

PublicMessage frames c for sending in the clear, adding the membership tag that proves the sender belongs to the group. Only proposals and commits may be sent this way: application data must be encrypted. See RFC 9420, Section 6.2.

func (*AuthenticatedContent) Ref

Ref returns the proposal reference for a proposal sent as c. See RFC 9420, Section 5.2.

func (*AuthenticatedContent) Sign

func (c *AuthenticatedContent) Sign(cs CipherSuite, priv []byte, version ProtocolVersion, ctx *GroupContext) error

Sign signs the content with priv and records the signature in Auth.

func (*AuthenticatedContent) UnmarshalTLS

func (c *AuthenticatedContent) UnmarshalTLS(r *tlssyntax.Reader)

func (*AuthenticatedContent) Verify

Verify checks the signature in Auth against pub.

type Capabilities

type Capabilities struct {
	Versions     []ProtocolVersion
	CipherSuites []CipherSuite
	Extensions   []ExtensionType
	Proposals    []ProposalType
	Credentials  []CredentialType
}

Capabilities lists what a client supports. A client must be able to handle every value it lists here, and other members rely on that when choosing extensions and proposals. See RFC 9420, Section 7.2.

func (*Capabilities) MarshalTLS

func (c *Capabilities) MarshalTLS(w *tlssyntax.Writer)

func (*Capabilities) UnmarshalTLS

func (c *Capabilities) UnmarshalTLS(r *tlssyntax.Reader)

type CipherSuite

type CipherSuite uint16

A CipherSuite identifies the set of cryptographic algorithms a group uses. See RFC 9420, Section 17.1.

const (
	X25519AES128GCMSHA256Ed25519        CipherSuite = 1
	P256AES128GCMSHA256P256             CipherSuite = 2
	X25519ChaCha20Poly1305SHA256Ed25519 CipherSuite = 3
	X448AES256GCMSHA512Ed448            CipherSuite = 4
	P521AES256GCMSHA512P521             CipherSuite = 5
	X448ChaCha20Poly1305SHA512Ed448     CipherSuite = 6
	P384AES256GCMSHA384P384             CipherSuite = 7
)

Cipher suites.

const (
	MLKEM768X25519AES128GCMSHA256Ed25519        CipherSuite = 0x004F // TBD1
	MLKEM768X25519AES256GCMSHA384Ed25519        CipherSuite = 0x004E // TBD2
	MLKEM768P256AES128GCMSHA256P256             CipherSuite = 0xF003 // TBD3
	MLKEM768P256AES256GCMSHA384P256             CipherSuite = 0xF004 // TBD4
	MLKEM1024P384AES256GCMSHA384P384            CipherSuite = 0xF005 // TBD5
	MLKEM768AES256GCMSHA384Ed25519              CipherSuite = 0xF042 // TBD6
	MLKEM768AES256GCMSHA384P256                 CipherSuite = 0x0050 // TBD7
	MLKEM1024AES256GCMSHA384P384                CipherSuite = 0x0042 // TBD8
	MLKEM768X25519ChaCha20Poly1305SHA384MLDSA44 CipherSuite = 0x0052 // TBD9
	MLKEM768AES256GCMSHA384MLDSA65              CipherSuite = 0x0051 // TBD10
	MLKEM1024AES256GCMSHA384MLDSA87             CipherSuite = 0x0907 // TBD11
)

Post-quantum cipher suites, from draft-ietf-mls-pq-ciphersuites-06. The draft leaves the code points to IANA, which has not yet assigned them; these are provisional and will change. A group, key package or stored state in one of these suites may not be understood by a later version of this package. See the package documentation.

func (CipherSuite) AEAD

func (cs CipherSuite) AEAD(key []byte) (cipher.AEAD, error)

AEAD returns the cipher suite's AEAD keyed with key: AES-GCM, or ChaCha20-Poly1305 from golang.org/x/crypto, since the standard library exposes that construction only through crypto/hpke.

MLS derives the AEAD nonces itself (RFC 9420, Sections 6.3.1 and 9.1), which is not an approved GCM IV construction, so under GODEBUG=fips140=only AEAD reports ErrUnsupportedCipherSuite.

func (CipherSuite) AEADKeySize

func (cs CipherSuite) AEADKeySize() int

AEADKeySize and AEADNonceSize return the key and nonce sizes of the cipher suite's AEAD, called Nk and Nn in RFC 9420.

func (CipherSuite) AEADNonceSize

func (cs CipherSuite) AEADNonceSize() int

AEADNonceSize is the nonce length of the cipher suite's AEAD, in bytes.

func (CipherSuite) ConfirmationTag

func (cs CipherSuite) ConfirmationTag(confirmationKey, confirmedTranscriptHash []byte) ([]byte, error)

ConfirmationTag is the MAC that proves the sender of a commit knows the confirmation key of the epoch the commit creates. See RFC 9420, Section 6.1.

func (CipherSuite) ConfirmedTranscriptHash

func (cs CipherSuite) ConfirmedTranscriptHash(interim []byte, commit *AuthenticatedContent) ([]byte, error)

ConfirmedTranscriptHash returns the confirmed transcript hash of the epoch that commit ends, given the interim transcript hash of the epoch before it. commit must be a commit.

func (CipherSuite) DecryptWithLabel

func (cs CipherSuite) DecryptWithLabel(priv []byte, label string, context []byte, ct *HPKECiphertext) ([]byte, error)

DecryptWithLabel reverses EncryptWithLabel.

func (CipherSuite) DeriveKeyPair

func (cs CipherSuite) DeriveKeyPair(ikm []byte) (priv []byte, pub HPKEPublicKey, err error)

DeriveKeyPair derives an HPKE key pair from the input keying material ikm, as RFC 9180's DeriveKeyPair does. The ratchet tree uses it to turn a node secret into the node's key pair.

func (CipherSuite) DeriveSecret

func (cs CipherSuite) DeriveSecret(secret []byte, label string) ([]byte, error)

DeriveSecret is ExpandWithLabel with an empty context and an output the size of the cipher suite's hash. See RFC 9420, Section 8.

func (CipherSuite) DeriveTreeSecret

func (cs CipherSuite) DeriveTreeSecret(secret []byte, label string, generation uint32, length uint16) ([]byte, error)

DeriveTreeSecret is ExpandWithLabel with the generation as context. See RFC 9420, Section 9.

func (CipherSuite) EncryptWithLabel

func (cs CipherSuite) EncryptWithLabel(pub HPKEPublicKey, label string, context, plaintext []byte) (*HPKECiphertext, error)

EncryptWithLabel encrypts plaintext to pub under label and context, returning the HPKE encapsulated key and the ciphertext. See RFC 9420, Section 5.1.3.

func (CipherSuite) ExpandWithLabel

func (cs CipherSuite) ExpandWithLabel(secret []byte, label string, context []byte, length uint16) ([]byte, error)

ExpandWithLabel implements the function of the same name in RFC 9420, Section 8: it expands secret into length bytes, bound to label and context.

func (CipherSuite) ExternalInit

func (cs CipherSuite) ExternalInit(externalPub HPKEPublicKey) (kemOutput, initSecret []byte, err error)

ExternalInit generates the init secret for an epoch begun by an external commit, along with the KEM output that lets the members of the group derive the same secret from their external key pair. See RFC 9420, Section 8.3.

func (CipherSuite) Extract

func (cs CipherSuite) Extract(salt, ikm []byte) ([]byte, error)

Extract is the HKDF extract step of the key schedule.

func (CipherSuite) GenerateKeyPair

func (cs CipherSuite) GenerateKeyPair() (priv []byte, pub HPKEPublicKey, err error)

GenerateKeyPair returns a fresh HPKE key pair, as used for a leaf node's encryption key and for a key package's init key.

func (CipherSuite) GenerateSignatureKeyPair

func (cs CipherSuite) GenerateSignatureKeyPair() (priv []byte, pub SignaturePublicKey, err error)

GenerateSignatureKeyPair returns a fresh signature key pair in the form SignWithLabel and VerifyWithLabel expect.

func (CipherSuite) Hash

func (cs CipherSuite) Hash(data []byte) ([]byte, error)

Hash returns the cipher suite's hash of data.

func (CipherSuite) HashSize

func (cs CipherSuite) HashSize() int

HashSize returns the output size of the cipher suite's hash function, called Nh in RFC 9420.

func (CipherSuite) InterimTranscriptHash

func (cs CipherSuite) InterimTranscriptHash(confirmed, confirmationTag []byte) ([]byte, error)

InterimTranscriptHash extends a confirmed transcript hash with the confirmation tag of the same commit.

func (CipherSuite) JoinerSecret

func (cs CipherSuite) JoinerSecret(initSecret, commitSecret []byte, ctx *GroupContext) ([]byte, error)

JoinerSecret derives the joiner secret that begins an epoch. It is the one part of the schedule a new member receives directly, in the GroupSecrets of a welcome message.

func (CipherSuite) MembershipTag

func (cs CipherSuite) MembershipTag(membershipKey []byte, content *AuthenticatedContent, version ProtocolVersion, ctx *GroupContext) ([]byte, error)

MembershipTag is the MAC that proves the sender of a PublicMessage is a member of the group. See RFC 9420, Section 6.1.

func (CipherSuite) PSKSecret

func (cs CipherSuite) PSKSecret(ids []PreSharedKeyID, psks [][]byte) ([]byte, error)

PSKSecret combines the pre-shared keys a commit injects into the single secret that the key schedule consumes. ids and psks must be parallel: psks[i] is the key material named by ids[i]. See RFC 9420, Section 8.4.

func (CipherSuite) PublicKey

func (cs CipherSuite) PublicKey(priv []byte) (HPKEPublicKey, error)

PublicKey returns the HPKE public key matching the private key priv.

func (CipherSuite) RefHash

func (cs CipherSuite) RefHash(label string, value []byte) ([]byte, error)

RefHash computes the labeled hash used to reference key packages and proposals. See RFC 9420, Section 5.2.

func (CipherSuite) SenderDataKey

func (cs CipherSuite) SenderDataKey(senderDataSecret, ciphertext []byte) (key, nonce []byte, err error)

SenderDataKey derives the key and nonce that protect the sender data of a PrivateMessage. Both are bound to a sample of the message's ciphertext, so that an attacker who has not seen the message cannot derive them. See RFC 9420, Section 6.3.2.

func (CipherSuite) SignWithLabel

func (cs CipherSuite) SignWithLabel(priv []byte, label string, content []byte) ([]byte, error)

SignWithLabel signs content with priv, binding the signature to label. See RFC 9420, Section 5.1.2.

func (CipherSuite) String

func (cs CipherSuite) String() string

String returns the name RFC 9420 or draft-ietf-mls-pq-ciphersuites gives the cipher suite, or its number if it is not one this package knows.

func (CipherSuite) Supported

func (cs CipherSuite) Supported() bool

Supported reports whether this package implements cs. In FIPS 140-3 mode it reports false for the suites that use X25519 or X448, and built with GOFIPS140=v1.0.0 for the suites that use ML-DSA.

func (CipherSuite) VerifyWithLabel

func (cs CipherSuite) VerifyWithLabel(pub SignaturePublicKey, label string, content, sig []byte) error

VerifyWithLabel checks a signature produced by SignWithLabel.

func (CipherSuite) WelcomeSecret

func (cs CipherSuite) WelcomeSecret(joinerSecret, pskSecret []byte) ([]byte, error)

WelcomeSecret derives the secret that protects the group info in a welcome message. Unlike the rest of the schedule it does not depend on the group context, since a new member cannot read the context until it has decrypted the group info. See RFC 9420, Section 8.

type Client

type Client struct {
	CipherSuite CipherSuite
	KeyPackage  *KeyPackage

	InitPriv       []byte // private half of KeyPackage.InitKey
	EncryptionPriv []byte // private half of KeyPackage.LeafNode.EncryptionKey
	SignaturePriv  []byte // private half of KeyPackage.LeafNode.SignatureKey

	// PSK returns the pre-shared key named by id. It is consulted
	// for external pre-shared keys; resumption keys come from the
	// group's own history. A nil PSK rejects any commit that needs
	// an external key.
	PSK func(id PreSharedKeyID) ([]byte, error)

	// ExternalProposal decides whether to accept a proposal from
	// outside the group: from an external sender the group
	// provisioned, or from a client asking to be added (see
	// [ExternalClient.Propose] and [Client.ProposeAdd]). An accepted
	// proposal is committed by the member's next commit like any
	// other. A nil ExternalProposal accepts proposals from the
	// senders the group's external_senders extension lists, which
	// RFC 9420, Section 12.1.8.1 makes the group's authorization of
	// them, and refuses every client asking to be added, which
	// authenticates only the key package it carries.
	//
	// Members should share a policy: a member does not keep a
	// proposal it refused, so it cannot process a commit that
	// refers to that proposal.
	//
	// Such a proposal names its group only by group ID and epoch:
	// its signature does not cover the group context (RFC 9420,
	// Section 6.1). A group whose ID is not unique, as Section 11
	// asks, accepts proposals meant for another group of that ID.
	ExternalProposal func(c *AuthenticatedContent) error

	// SameIdentity reports whether two credentials identify the
	// same member. RFC 9420 leaves this to the application
	// (Sections 11.3 and 12.4.3.2), and this package asks it when
	// a branch or reinitialization must hold only the old group's
	// members and when an external commit removes the joiner's
	// earlier leaf. A nil SameIdentity compares the credentials
	// byte for byte, which fails for an X.509 credential whose
	// certificate changed with the signature key.
	SameIdentity func(a, b *Credential) bool

	// Padding blurs the length of the messages [Group.Protect]
	// produces: the plaintext is padded with zeros until its length
	// is a multiple of Padding. Zero or one means no padding, which
	// puts the exact length of every message on the wire. RFC 9420,
	// Section 15.1 leaves the policy to the application, since what
	// it costs and what it hides both depend on the traffic.
	Padding int

	// PublicHandshake makes [Group.Propose], [Group.ProposeUpdate]
	// and [Group.Commit] send their messages as PublicMessages
	// rather than PrivateMessages. RFC 9420, Section 6 says handshake
	// messages SHOULD be encrypted and MAY be public where the
	// delivery service must inspect them, as it must to check the
	// group's membership changes. Messages from outside the group,
	// including an external commit, are always public, since their
	// senders hold no keys to encrypt them with.
	PublicHandshake bool
}

A Client holds the private keys of one member. It is the starting point for joining a group: the key package is published to whoever will add the client, and the three private keys are kept.

func NewClient

func NewClient(cs CipherSuite, cred Credential, lifetime time.Duration) (*Client, error)

NewClient generates the key material of one member: a signature key pair that outlives the group, and an init and encryption key pair for a single key package, which offers the client for addition to a group and is meant to be used once. The key package is valid from now until now plus lifetime.

func (*Client) Join

func (c *Client) Join(w *Welcome, tree RatchetTree) (*Group, error)

Join builds a member's view of a group from a welcome message. The ratchet tree comes from the welcome's group info if it carries a ratchet_tree extension, and otherwise must be supplied in tree. See RFC 9420, Section 12.4.3.1.

func (*Client) JoinExternal

func (c *Client) JoinExternal(info *GroupInfo, tree RatchetTree, psks []PreSharedKeyID) (*Group, *Message, error)

JoinExternal joins a group from a GroupInfo, without being added by a member. It returns the member's view of the group in the epoch the join begins, and the external commit that carries the join to the group: the caller must publish that commit, and must discard the group if the group does not accept it.

The ratchet tree comes from the group info if it carries a ratchet_tree extension, and otherwise must be supplied in tree. The group info must carry an external_pub extension; its signature is verified here against the leaf of the member that signed it.

The commit also injects the pre-shared keys psks, each by a PreSharedKey proposal, which is the only kind of proposal besides ExternalInit and a resync's Remove that an external commit may carry. Their secrets come from c.PSK, and each nonce must be fresh and as long as the suite's hash output (RFC 9420, Section 8.4).

A member rejoining after losing its state resyncs: the commit also removes the member's prior leaf, which is the leaf holding c's signature key. A client that has since changed its signature key finds no such leaf, and its prior leaf stays in the group until a member removes it. The prior leaf is not looked for by credential, since a member may hold one leaf per device under one identity.

A GroupInfo is specific to an epoch, and the join ends that epoch, so each GroupInfo is good for one external join. See RFC 9420, Section 12.4.3.2.

func (*Client) NewGroup

func (c *Client) NewGroup(groupID []byte, extensions Extensions) (*Group, error)

NewGroup creates a group whose only member is c. Other members join by being added in a later commit. The epoch secret of the first epoch is random rather than derived, since there is no previous epoch to derive it from. See RFC 9420, Section 11.

func (*Client) ProposeAdd

func (c *Client) ProposeAdd(groupID []byte, epoch uint64) (*Message, error)

ProposeAdd frames a proposal by which c asks to be added to the group named by groupID in the given epoch, without being invited by a member. The group's members decide whether to commit it, through Client.ExternalProposal; RFC 9420, Section 12.1.8 leaves that decision to the application, since the proposal authenticates only the key package it carries.

func (*Client) Resume

func (c *Client) Resume(w *Welcome, tree RatchetTree, old *Group) (*Group, error)

Resume joins the group that old was reinitialized or branched into. It differs from Client.Join in that the welcome message carries a resumption pre-shared key drawn from old, and in the checks RFC 9420, Sections 11.2 and 11.3 require of the new group: it must be at epoch 1, it must match the parameters of the group it resumes, a reinitialized group must be resumed from the epoch that committed the Reinit, a reinitialized group must hold exactly the members of old, and a branch must hold only members of old, all as c.SameIdentity judges. A branch must also have a new group ID, since Section 11 asks that group IDs be unique.

type Commit

type Commit struct {
	Proposals []ProposalOrRef
	Path      *UpdatePath
}

A Commit ends an epoch: it applies a list of proposals and, unless every one of them is an Add, updates the committer's direct path. See RFC 9420, Section 12.4.

func (*Commit) MarshalTLS

func (c *Commit) MarshalTLS(w *tlssyntax.Writer)

func (*Commit) UnmarshalTLS

func (c *Commit) UnmarshalTLS(r *tlssyntax.Reader)

type ContentType

type ContentType uint8

A ContentType identifies what a FramedContent carries.

const (
	ContentTypeApplication ContentType = 1
	ContentTypeProposal    ContentType = 2
	ContentTypeCommit      ContentType = 3
)

Content types. See RFC 9420, Section 6.

func (ContentType) String

func (t ContentType) String() string

String returns the name of the content type.

type Credential

type Credential struct {
	Type CredentialType

	Identity     []byte   // Type == CredentialTypeBasic
	Certificates [][]byte // Type == CredentialTypeX509

	// Body holds the credential of any other Type that has been
	// given to [RegisterCredential].
	Body CredentialCodec
}

A Credential presents the identity of a group member. Which field holds that identity depends on Type: a basic credential carries an uninterpreted Identity, an x509 credential carries a chain of DER-encoded certificates, leaf first, and a credential of a registered type carries its own Body. See RFC 9420, Section 5.3.

func (*Credential) MarshalTLS

func (c *Credential) MarshalTLS(w *tlssyntax.Writer)

func (*Credential) UnmarshalTLS

func (c *Credential) UnmarshalTLS(r *tlssyntax.Reader)

type CredentialCodec

type CredentialCodec interface {
	tlssyntax.Marshaler
	tlssyntax.Unmarshaler
}

A CredentialCodec is the body of a credential whose type this package does not define itself, such as one from a draft extension. See RegisterCredential.

type CredentialType

type CredentialType uint16

A CredentialType identifies the form of a Credential.

const (
	CredentialTypeBasic CredentialType = 1
	CredentialTypeX509  CredentialType = 2
)

Credential types. See RFC 9420, Section 17.5.

func (CredentialType) String

func (t CredentialType) String() string

String returns the name of the credential type, or its number if it is not one this package knows.

type EncryptedGroupSecrets

type EncryptedGroupSecrets struct {
	NewMember             HashReference
	EncryptedGroupSecrets HPKECiphertext
}

EncryptedGroupSecrets carries one new member's copy of the group secrets in a Welcome, addressed by the hash of its key package. See RFC 9420, Section 12.4.3.1.

func (*EncryptedGroupSecrets) MarshalTLS

func (s *EncryptedGroupSecrets) MarshalTLS(w *tlssyntax.Writer)

func (*EncryptedGroupSecrets) UnmarshalTLS

func (s *EncryptedGroupSecrets) UnmarshalTLS(r *tlssyntax.Reader)

type Extension

type Extension struct {
	Type ExtensionType
	Data []byte
}

An Extension carries data outside the base protocol. Its contents are opaque here; interpreting them is the business of whatever defines the extension type. See RFC 9420, Section 7.2.

func (*Extension) MarshalTLS

func (e *Extension) MarshalTLS(w *tlssyntax.Writer)

func (*Extension) UnmarshalTLS

func (e *Extension) UnmarshalTLS(r *tlssyntax.Reader)

type ExtensionType

type ExtensionType uint16

An ExtensionType identifies the contents of an Extension.

const (
	ExtensionTypeApplicationID        ExtensionType = 1
	ExtensionTypeRatchetTree          ExtensionType = 2
	ExtensionTypeRequiredCapabilities ExtensionType = 3
	ExtensionTypeExternalPub          ExtensionType = 4
	ExtensionTypeExternalSenders      ExtensionType = 5
)

Extension types. See RFC 9420, Section 17.3.

func (ExtensionType) GREASE

func (t ExtensionType) GREASE() bool

GREASE reports whether t is one of the reserved values that a client may insert at random to check that its peers ignore extensions they do not recognize. A GREASE extension carries no meaning, and its contents are arbitrary. See RFC 9420, Section 13.5.

func (ExtensionType) String

func (t ExtensionType) String() string

String returns the name of the extension type, or its number if it is not one this package knows.

type Extensions

type Extensions []Extension

Extensions is a vector of extensions, written "Extension extensions<V>" in the specification.

func (Extensions) Decode

func (es Extensions) Decode(t ExtensionType, body tlssyntax.Unmarshaler) (bool, error)

Decode decodes the extension of type t into body. It reports whether the extension was present; a missing extension is not an error, so that a caller can distinguish absence from a malformed value.

Extensions whose type this package does not define keep their contents in Extension.Data, since RFC 9420, Section 13.4 requires that unrecognized extensions be ignored rather than rejected.

func (Extensions) Find

func (es Extensions) Find(t ExtensionType) *Extension

Find returns the first extension of the given type, or nil.

Example
package main

import (
	"fmt"

	"github.com/tmc/mls"
)

func main() {
	exts := mls.Extensions{
		{Type: mls.ExtensionTypeApplicationID, Data: []byte("chat")},
		{Type: mls.ExtensionTypeRatchetTree, Data: []byte{0x00}},
	}
	e := exts.Find(mls.ExtensionTypeApplicationID)
	fmt.Printf("%s: %q\n", e.Type, e.Data)
	fmt.Println(exts.Find(mls.ExtensionTypeExternalPub))
}
Output:
application_id: "chat"
<nil>

func (*Extensions) MarshalTLS

func (es *Extensions) MarshalTLS(w *tlssyntax.Writer)

func (*Extensions) Set

Set encodes body as the extension of type t, replacing any extension of that type already present.

func (*Extensions) UnmarshalTLS

func (es *Extensions) UnmarshalTLS(r *tlssyntax.Reader)

type ExternalClient

type ExternalClient struct {
	CipherSuite   CipherSuite
	Index         uint32
	SignaturePriv []byte
}

An ExternalClient sends proposals to a group without being a member of it. The group must carry an external_senders extension listing the client's signature key, and Index must be its position in that list. See RFC 9420, Section 12.1.8.

A client learns the group ID and epoch a proposal must name from the messages the group sends; see ParseHeader.

func (*ExternalClient) Propose

func (c *ExternalClient) Propose(groupID []byte, epoch uint64, p *Proposal) (*Message, error)

Propose frames p as a proposal to the group named by groupID in the given epoch. Only the proposal types RFC 9420, Section 12.1.8 admits may be sent this way: Add, Remove, PreSharedKey, Reinit and GroupContextExtensions.

type ExternalInit

type ExternalInit struct {
	KEMOutput []byte
}

An ExternalInit proposal carries the KEM output a new member used to join by external commit. See RFC 9420, Section 12.1.6.

func (*ExternalInit) MarshalTLS

func (e *ExternalInit) MarshalTLS(w *tlssyntax.Writer)

func (*ExternalInit) UnmarshalTLS

func (e *ExternalInit) UnmarshalTLS(r *tlssyntax.Reader)

type ExternalPub

type ExternalPub struct {
	ExternalPub HPKEPublicKey
}

An ExternalPub publishes the public half of a group's external init key, which lets a non-member join by external commit. It appears in a GroupInfo. See RFC 9420, Section 12.4.3.2.

func (*ExternalPub) MarshalTLS

func (e *ExternalPub) MarshalTLS(w *tlssyntax.Writer)

func (*ExternalPub) UnmarshalTLS

func (e *ExternalPub) UnmarshalTLS(r *tlssyntax.Reader)

type ExternalSender

type ExternalSender struct {
	SignatureKey SignaturePublicKey
	Credential   Credential
}

An ExternalSender is a party that may send proposals to a group without being a member of it. See RFC 9420, Section 12.1.8.1.

func (*ExternalSender) MarshalTLS

func (s *ExternalSender) MarshalTLS(w *tlssyntax.Writer)

func (*ExternalSender) UnmarshalTLS

func (s *ExternalSender) UnmarshalTLS(r *tlssyntax.Reader)

type ExternalSenders

type ExternalSenders []ExternalSender

ExternalSenders is the group context extension that lists the parties allowed to send proposals from outside the group. The index in a Sender of type external is an index into this list. See RFC 9420, Section 12.1.8.1.

func (*ExternalSenders) MarshalTLS

func (ss *ExternalSenders) MarshalTLS(w *tlssyntax.Writer)

func (*ExternalSenders) UnmarshalTLS

func (ss *ExternalSenders) UnmarshalTLS(r *tlssyntax.Reader)

type FramedContent

type FramedContent struct {
	GroupID           []byte
	Epoch             uint64
	Sender            Sender
	AuthenticatedData []byte

	ContentType     ContentType
	ApplicationData []byte    // ContentType == ContentTypeApplication
	Proposal        *Proposal // ContentType == ContentTypeProposal
	Commit          *Commit   // ContentType == ContentTypeCommit
}

FramedContent is the content of a message together with the group and sender it belongs to. ContentType decides which of ApplicationData, Proposal, and Commit is present. See RFC 9420, Section 6.

func (*FramedContent) MarshalTLS

func (c *FramedContent) MarshalTLS(w *tlssyntax.Writer)

func (*FramedContent) UnmarshalTLS

func (c *FramedContent) UnmarshalTLS(r *tlssyntax.Reader)

type FramedContentAuthData

type FramedContentAuthData struct {
	ContentType ContentType // not encoded; selects the variant

	Signature       []byte
	ConfirmationTag []byte // ContentType == ContentTypeCommit
}

FramedContentAuthData carries the signature over a FramedContent, and for a commit the confirmation tag as well. The ContentType it was decoded under decides whether ConfirmationTag is present; keep it in sync with the content this authenticates. See RFC 9420, Section 6.1.

func (*FramedContentAuthData) MarshalTLS

func (a *FramedContentAuthData) MarshalTLS(w *tlssyntax.Writer)

func (*FramedContentAuthData) UnmarshalTLS

func (a *FramedContentAuthData) UnmarshalTLS(r *tlssyntax.Reader)

type Group

type Group struct {
	CipherSuite CipherSuite
	Context     GroupContext
	Tree        RatchetTree

	Index   LeafIndex    // the member's own leaf
	Secrets *TreeSecrets // the member's private view of the tree
	// contains filtered or unexported fields
}

A Group is a member's view of an MLS group at one epoch.

A commit does not change a Group: Group.Commit, Group.ApplyCommit and a Group.Handle of a commit return the group's state in the epoch the commit begins, and leave g as it was. The old Group remains usable for its own epoch, to decrypt messages that arrive late or to try again if the delivery service rejects the commit; a caller drops it once it no longer needs it, since it holds that epoch's secrets.

Within an epoch a Group does change. Group.Protect and Group.Unprotect advance the secret tree's ratchets, and Group.Propose, Group.ProposeUpdate, Group.AddProposal and a Group.Handle of a proposal record the proposal. A Group is therefore not safe for concurrent use; a caller that shares one between goroutines must serialize its calls.

func (*Group) AddProposal

func (g *Group) AddProposal(c *AuthenticatedContent) error

AddProposal remembers a proposal so that a later commit can refer to it by reference. Group.Handle calls it for a proposal message; call it directly only for a proposal whose content was already recovered and authenticated some other way, such as by Group.Unprotect.

A proposal from outside the group, whether from an external sender or from a client asking to be added, is remembered only if the client's Client.ExternalProposal accepts it, or, with no policy, only if it comes from a sender the group provisioned. At most maxProposals proposals are remembered in one epoch, and at most maxSenderProposals from any one sender.

A member that rotates its keys twice in one epoch sends two updates, and both are remembered. RFC 9420, Section 12.2 allows a commit to cover at most one update for any one leaf but leaves the choice to the committer, so a commit from another member may carry either. This group's own commits carry the later one.

func (*Group) ApplyCommit

func (g *Group) ApplyCommit(c *AuthenticatedContent) (*Group, error)

ApplyCommit applies a commit sent in this epoch and returns the group's state in the epoch the commit begins. g is left unchanged. Group.Handle calls it for a commit message; call it directly only for a commit whose content was already recovered and authenticated some other way, such as by Group.Unprotect, for example to inspect the commit before applying it. See RFC 9420, Section 12.4.2.

func (*Group) Branch

func (g *Group) Branch(groupID []byte, extensions Extensions, members []*KeyPackage) (*Group, *Message, error)

Branch creates a new group holding a subset of g's members, with g's version and cipher suite but a new group ID, as RFC 9420, Section 11.3 describes. The new group's context carries extensions, which need not be g's; pass g.Context.Extensions to keep them. members are the key packages of the subgroup's members, which the caller fetches afresh, and each must identify a member of g, as g's client's SameIdentity judges; g's own member is the creator and needs none. It returns the new group at epoch 1 and the welcome message for the others.

Unlike Group.Reinitialize, Branch needs no new client: the suite is g's, and the commit that adds the members replaces the creator's leaf keys along its update path.

func (*Group) Commit

func (g *Group) Commit(extra []*Proposal) (*Group, *Message, *Message, error)

Commit ends the epoch. It applies the extra proposals, which are carried in the commit itself, along with the proposals g has seen in this epoch that are valid and that can be committed with them; the others are left out. An invalid extra proposal is an error. It returns the group's state in the new epoch, the commit to send to the other members, and a welcome for the members the commit adds, which is nil if it adds none. g is left unchanged, so that if the delivery service accepts a different commit for this epoch the member can apply that one instead. See RFC 9420, Section 14.

The commit always updates the committer's direct path, which RFC 9420, Section 12.4 requires unless every proposal is an Add.

func (*Group) Epoch

func (g *Group) Epoch() uint64

Epoch is the number of the epoch g describes.

func (*Group) EpochAuthenticator

func (g *Group) EpochAuthenticator() []byte

EpochAuthenticator is the value that members compare out of band to confirm they agree on the history of the group. See RFC 9420, Section 8.

func (*Group) Export

func (g *Group) Export(label string, context []byte, length uint16) ([]byte, error)

Export implements MLS-Exporter: it derives an application secret of the given length, bound to label and context, that only the members of this epoch can compute. Two members of the same epoch that pass the same arguments get the same secret, and no other epoch does. See RFC 9420, Section 8.5.

Example

Members of the same epoch export the same secret for the same label and context, and no one outside the group can compute it.

package main

import (
	"bytes"
	"fmt"
	"log"
	"time"

	"github.com/tmc/mls"
)

// deliver passes m through its encoding, as a delivery service would.
func deliver(m *mls.Message) *mls.Message {
	b, err := mls.Marshal(m)
	if err != nil {
		log.Fatal(err)
	}
	var out mls.Message
	if err := mls.Unmarshal(b, &out); err != nil {
		log.Fatal(err)
	}
	return &out
}

func main() {
	cs := mls.P256AES128GCMSHA256P256
	alice, err := mls.NewClient(cs, mls.Credential{Type: mls.CredentialTypeBasic, Identity: []byte("alice")}, time.Hour)
	if err != nil {
		log.Fatal(err)
	}
	bob, err := mls.NewClient(cs, mls.Credential{Type: mls.CredentialTypeBasic, Identity: []byte("bob")}, time.Hour)
	if err != nil {
		log.Fatal(err)
	}
	g, err := alice.NewGroup([]byte("group"), nil)
	if err != nil {
		log.Fatal(err)
	}
	a1, _, welcome, err := g.Commit([]*mls.Proposal{{Type: mls.ProposalTypeAdd, Add: &mls.Add{KeyPackage: *bob.KeyPackage}}})
	if err != nil {
		log.Fatal(err)
	}
	b1, err := bob.Join(deliver(welcome).Welcome, nil)
	if err != nil {
		log.Fatal(err)
	}

	ka, err := a1.Export("example call key", []byte("call 1"), 32)
	if err != nil {
		log.Fatal(err)
	}
	kb, err := b1.Export("example call key", []byte("call 1"), 32)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(len(ka), bytes.Equal(ka, kb))
}
Output:
32 true

func (*Group) GroupInfo

func (g *Group) GroupInfo() (*Message, error)

GroupInfo returns a signed description of the group's current epoch, carrying the ratchet tree and the external_pub extension so that a client can join by external commit with Client.JoinExternal.

A GroupInfo is not public information: it names every member of the group. An application publishes it only to clients it is willing to let join. Each one is good for a single external join, since the join ends the epoch it describes. See RFC 9420, Section 12.4.3.2.

func (*Group) Handle

func (g *Group) Handle(m *Message) (*Group, *AuthenticatedContent, error)

Handle processes a message sent to the group, and is how a member follows it. It returns the message's authenticated content together with the group's state after the message:

  • an application message leaves the group as it was, and Handle returns g, with the plaintext in the content's ApplicationData;
  • a proposal is remembered in g until a commit refers to it, and Handle returns g;
  • a commit ends the epoch, and Handle returns the group's state in the next one, leaving g as it was.

Since a private message's content type is encrypted, a caller cannot tell these apart before Handle decrypts the message, and decrypting consumes the sender's key for that generation: so Handle, not the caller, dispatches on the type.

On error Handle returns a nil group, and g remains usable. The keys of a private message that decrypted are consumed even if its content was then rejected.

func (*Group) Propose

func (g *Group) Propose(p *Proposal) (*Message, error)

Propose frames a proposal as a handshake message of this epoch and remembers it, so that a commit made in this epoch - by g or by another member that receives the message - can refer to it by reference.

func (*Group) ProposeUpdate

func (g *Group) ProposeUpdate(leaf *LeafNode, encPriv []byte) (*Message, error)

ProposeUpdate proposes leaf as the sender's own new leaf node and keeps encPriv, the private half of leaf.EncryptionKey, so that the group can follow the commit that applies the proposal. A member that proposes an update any other way cannot process that commit: the committer encrypts the path secrets to the new key, and only the proposer holds the matching private key.

The leaf must have source update and must be signed over the group ID and the member's own leaf index. Rotating the signature key and the credential along with the encryption key is what recovers from a compromised signature key.

A commit must not carry its sender's own update, so a member with an update outstanding drops it when it commits.

func (*Group) Protect

func (g *Group) Protect(authenticatedData, plaintext []byte) (*Message, error)

Protect frames application data as a private message of this epoch, padded as the client's Client.Padding asks. It advances the member's own application ratchet in g.

func (*Group) Reinit

func (g *Group) Reinit() *Reinit

Reinit returns the reinitialization a commit of this epoch carried, or nil if it carried none. A group that has committed a Reinit proposal is finished: its members send nothing further in it and move to the group Group.Reinitialize creates. See RFC 9420, Section 11.2.

func (*Group) Reinitialize

func (g *Group) Reinitialize(c *Client, members []*KeyPackage) (*Group, *Message, error)

Reinitialize creates the group that the members of g move to, which is the third step of RFC 9420, Section 11.2: g must have committed a Reinit proposal, and the new group takes its group ID, version, cipher suite and extensions from it. c is the member's client in the new group, and members are the key packages of everyone else who is to join; since the new group may use a different cipher suite, all of them are made afresh, for the Reinit's suite.

It returns the new group at epoch 1 and the welcome message that carries the rest of the membership into it. The members verify the link to g through a resumption pre-shared key; see Client.Resume.

Any member of g may do this, not only the one that committed the Reinit, so that a group is not stranded by whoever went offline. Section 11.2 makes the new group one "with the same membership", so c must identify the same member as g's own leaf, and c and members together must identify exactly g's members, as c.SameIdentity judges.

func (*Group) Unprotect

func (g *Group) Unprotect(m *Message) (*AuthenticatedContent, error)

Unprotect recovers the authenticated content of a message sent to the group, checking that it comes from a member of this epoch and uses the group's protocol version. Decrypting a private message advances the sender's ratchet in g past the message's generation, so the same private message cannot be unprotected twice.

type GroupContext

type GroupContext struct {
	Version                 ProtocolVersion
	CipherSuite             CipherSuite
	GroupID                 []byte
	Epoch                   uint64
	TreeHash                []byte
	ConfirmedTranscriptHash []byte
	Extensions              Extensions
}

A GroupContext summarizes the state of a group at one epoch. It is mixed into the key schedule and signed over, so members that disagree about it cannot talk to each other. See RFC 9420, Section 8.1.

func (*GroupContext) MarshalTLS

func (c *GroupContext) MarshalTLS(w *tlssyntax.Writer)

func (*GroupContext) UnmarshalTLS

func (c *GroupContext) UnmarshalTLS(r *tlssyntax.Reader)

type GroupContextExtensions

type GroupContextExtensions struct {
	Extensions Extensions
}

A GroupContextExtensions proposal replaces the extensions in the group's GroupContext. See RFC 9420, Section 12.1.7.

func (*GroupContextExtensions) MarshalTLS

func (g *GroupContextExtensions) MarshalTLS(w *tlssyntax.Writer)

func (*GroupContextExtensions) UnmarshalTLS

func (g *GroupContextExtensions) UnmarshalTLS(r *tlssyntax.Reader)

type GroupInfo

type GroupInfo struct {
	GroupContext    GroupContext
	Extensions      Extensions
	ConfirmationTag []byte
	Signer          uint32
	Signature       []byte
}

A GroupInfo tells a joining member the state of the group. It is signed by an existing member, named by Signer, over a GroupInfoTBS; see GroupInfo.Sign and GroupInfo.Verify. See RFC 9420, Section 12.4.3.

func (*GroupInfo) MarshalTLS

func (g *GroupInfo) MarshalTLS(w *tlssyntax.Writer)

func (*GroupInfo) Sign

func (g *GroupInfo) Sign(priv []byte) error

Sign signs g with priv, the signature key of the member named by g.Signer.

func (*GroupInfo) SignedContent

func (g *GroupInfo) SignedContent() ([]byte, error)

SignedContent returns the GroupInfoTBS bytes that g.Signature covers. See RFC 9420, Section 12.4.3.

func (*GroupInfo) UnmarshalTLS

func (g *GroupInfo) UnmarshalTLS(r *tlssyntax.Reader)

func (*GroupInfo) Verify

func (g *GroupInfo) Verify(pub SignaturePublicKey) error

Verify checks g.Signature against pub, which must be the signature key of the member at g.Signer.

type GroupSecrets

type GroupSecrets struct {
	JoinerSecret []byte
	PathSecret   []byte // nil if the commit carried no path
	PSKs         []PreSharedKeyID
}

GroupSecrets is the plaintext a joiner recovers from a Welcome: enough key material to derive the epoch secrets, plus the path secret for the lowest common ancestor with the committer, if the commit had a path. See RFC 9420, Section 12.4.3.1.

func (*GroupSecrets) MarshalTLS

func (g *GroupSecrets) MarshalTLS(w *tlssyntax.Writer)

func (*GroupSecrets) UnmarshalTLS

func (g *GroupSecrets) UnmarshalTLS(r *tlssyntax.Reader)

type HPKECiphertext

type HPKECiphertext struct {
	KEMOutput  []byte
	Ciphertext []byte
}

An HPKECiphertext is a single-shot HPKE encryption: an encapsulated key and the ciphertext it protects. See RFC 9420, Section 7.6.

func (*HPKECiphertext) MarshalTLS

func (c *HPKECiphertext) MarshalTLS(w *tlssyntax.Writer)

func (*HPKECiphertext) UnmarshalTLS

func (c *HPKECiphertext) UnmarshalTLS(r *tlssyntax.Reader)

type HPKEPublicKey

type HPKEPublicKey []byte

An HPKEPublicKey is a public key for HPKE encryption, in the format the group's cipher suite defines.

type HashReference

type HashReference []byte

A HashReference names another object by hash. A KeyPackageRef names a KeyPackage; a ProposalRef names a proposal. See RFC 9420, Section 5.2.

type Header struct {
	Version    ProtocolVersion
	WireFormat WireFormat
	GroupID    []byte // framed wire formats only; nil otherwise
	Epoch      uint64 // valid when GroupID is non-nil
}

A Header is what a relay can learn from a message without being able to decode it: which version and wire format it is, and, for the two framed formats, which group and epoch it belongs to. See ParseHeader.

func ParseHeader

func ParseHeader(b []byte) (Header, error)

ParseHeader reads the routing header of an encoded Message without decoding the message. It succeeds for wire formats this package cannot decode, so that a relay can route and forward bytes it must not mangle; it reports an error only if the header itself is truncated or malformed. It does not authenticate anything: a header is what the sender claims, and only decoding the message under a group's keys proves any of it.

type KeyPackage

type KeyPackage struct {
	Version     ProtocolVersion
	CipherSuite CipherSuite
	InitKey     HPKEPublicKey
	LeafNode    LeafNode
	Extensions  Extensions
	Signature   []byte
}

A KeyPackage offers a client for addition to a group: it publishes the client's leaf node along with an init key that an adder uses to encrypt the group's secrets to it. A key package is meant to be used once. It is signed by the client over a KeyPackageTBS; see KeyPackage.Sign and KeyPackage.Verify. See RFC 9420, Section 10.

func (*KeyPackage) MarshalTLS

func (p *KeyPackage) MarshalTLS(w *tlssyntax.Writer)

func (*KeyPackage) Ref

func (p *KeyPackage) Ref() (HashReference, error)

Ref returns the key package reference by which proposals and welcome messages name p. See RFC 9420, Section 5.2.

func (*KeyPackage) Sign

func (p *KeyPackage) Sign(priv []byte) error

Sign signs p with priv, which must be the private half of p.LeafNode.SignatureKey, and records the signature in p.Signature.

func (*KeyPackage) SignedContent

func (p *KeyPackage) SignedContent() ([]byte, error)

SignedContent returns the KeyPackageTBS bytes that p.Signature covers: every field of the key package but the signature itself. See RFC 9420, Section 10.

func (*KeyPackage) UnmarshalTLS

func (p *KeyPackage) UnmarshalTLS(r *tlssyntax.Reader)

func (*KeyPackage) Validate

func (p *KeyPackage) Validate(now time.Time) error

Validate checks a key package before it is used to add a client to a group: its own signature, the leaf node rules of RFC 9420, Section 7.3 that do not depend on the group, and that now falls within the leaf node's lifetime. A zero now skips the lifetime check.

func (*KeyPackage) Verify

func (p *KeyPackage) Verify() error

Verify checks p.Signature against the signature key in p's leaf node. It does not validate the leaf node itself; see LeafNode.Verify.

type LeafIndex

type LeafIndex uint32

A LeafIndex identifies a member's leaf, counting only leaves. LeafIndex i is NodeIndex 2*i.

func (LeafIndex) NodeIndex

func (i LeafIndex) NodeIndex() NodeIndex

NodeIndex returns the array index of leaf i.

type LeafNode

type LeafNode struct {
	EncryptionKey HPKEPublicKey
	SignatureKey  SignaturePublicKey
	Credential    Credential
	Capabilities  Capabilities

	Source     LeafNodeSource
	Lifetime   Lifetime // Source == LeafNodeSourceKeyPackage
	ParentHash []byte   // Source == LeafNodeSourceCommit

	Extensions Extensions
	Signature  []byte
}

A LeafNode describes one member's appearance in the ratchet tree. It is signed by that member over a LeafNodeTBS; see LeafNode.Sign and LeafNode.Verify.

Source decides which of Lifetime and ParentHash is present: a leaf from a KeyPackage carries a Lifetime, a leaf installed by a commit carries a ParentHash, and a leaf from an Update proposal carries neither. See RFC 9420, Section 7.2.

func (*LeafNode) MarshalTLS

func (n *LeafNode) MarshalTLS(w *tlssyntax.Writer)

func (*LeafNode) Sign

func (n *LeafNode) Sign(cs CipherSuite, priv []byte, groupID []byte, leafIndex LeafIndex) error

Sign signs n with priv, which must be the private half of n.SignatureKey, and records the signature in n.Signature.

func (*LeafNode) SignedContent

func (n *LeafNode) SignedContent(groupID []byte, leafIndex LeafIndex) ([]byte, error)

SignedContent returns the LeafNodeTBS bytes that n.Signature covers. A leaf node from an update or a commit is bound to its position, so groupID and leafIndex must identify it; a leaf node from a key package is not, and they are ignored. See RFC 9420, Section 7.2.

func (*LeafNode) UnmarshalTLS

func (n *LeafNode) UnmarshalTLS(r *tlssyntax.Reader)

func (*LeafNode) Verify

func (n *LeafNode) Verify(cs CipherSuite, groupID []byte, leafIndex LeafIndex) error

Verify checks n.Signature against n.SignatureKey.

type LeafNodeSource

type LeafNodeSource uint8

A LeafNodeSource records why a LeafNode was created, and decides which of its variant fields is present.

const (
	LeafNodeSourceKeyPackage LeafNodeSource = 1
	LeafNodeSourceUpdate     LeafNodeSource = 2
	LeafNodeSourceCommit     LeafNodeSource = 3
)

Leaf node sources. See RFC 9420, Section 7.2.

func (LeafNodeSource) String

func (s LeafNodeSource) String() string

String returns the name of the leaf node source.

type Lifetime

type Lifetime struct {
	NotBefore uint64
	NotAfter  uint64
}

A Lifetime bounds the time over which a KeyPackage may be used, as absolute seconds since the UNIX epoch. Clients are expected to reject key packages outside this window, subject to their own clock skew allowance. See RFC 9420, Section 7.2.

func (Lifetime) Contains

func (l Lifetime) Contains(t time.Time) bool

Contains reports whether the lifetime covers t.

A sender MUST check the lifetime of a leaf node it puts in a message; a receiver is only advised to, since a leaf node can expire between being sent and being received. See RFC 9420, Section 7.3.

func (*Lifetime) MarshalTLS

func (l *Lifetime) MarshalTLS(w *tlssyntax.Writer)

func (*Lifetime) UnmarshalTLS

func (l *Lifetime) UnmarshalTLS(r *tlssyntax.Reader)

type Message

type Message struct {
	Version    ProtocolVersion
	WireFormat WireFormat

	PublicMessage  *PublicMessage  // WireFormat == WireFormatPublicMessage
	PrivateMessage *PrivateMessage // WireFormat == WireFormatPrivateMessage
	Welcome        *Welcome        // WireFormat == WireFormatWelcome
	GroupInfo      *GroupInfo      // WireFormat == WireFormatGroupInfo
	KeyPackage     *KeyPackage     // WireFormat == WireFormatKeyPackage
}

A Message is the outermost structure of the protocol: everything sent between clients and the delivery service is one of these. WireFormat decides which of the remaining fields is present. See RFC 9420, Section 6.

func (*Message) MarshalTLS

func (m *Message) MarshalTLS(w *tlssyntax.Writer)

func (*Message) UnmarshalTLS

func (m *Message) UnmarshalTLS(r *tlssyntax.Reader)

type Node

type Node struct {
	Type   NodeType
	Leaf   *LeafNode   // Type == NodeTypeLeaf
	Parent *ParentNode // Type == NodeTypeParent
}

A Node is one occupied node of the ratchet tree: either a leaf or a parent, according to Type. See RFC 9420, Section 12.4.3.1.

func (*Node) MarshalTLS

func (n *Node) MarshalTLS(w *tlssyntax.Writer)

func (*Node) UnmarshalTLS

func (n *Node) UnmarshalTLS(r *tlssyntax.Reader)

type NodeIndex

type NodeIndex uint32

A NodeIndex identifies a node in the array representation of a ratchet tree, counting both leaf and parent nodes.

func (NodeIndex) IsLeaf

func (x NodeIndex) IsLeaf() bool

IsLeaf reports whether x is a leaf node.

func (NodeIndex) LeafIndex

func (x NodeIndex) LeafIndex() LeafIndex

LeafIndex returns the leaf index of node x, which must be a leaf.

type NodeType

type NodeType uint8

A NodeType identifies which node a ratchet tree entry holds.

const (
	NodeTypeLeaf   NodeType = 1
	NodeTypeParent NodeType = 2
)

Node types. See RFC 9420, Section 12.4.3.1.

func (NodeType) String

func (t NodeType) String() string

String returns the name of the node type.

type PSKType

type PSKType uint8

A PSKType identifies where a PreSharedKeyID comes from.

const (
	PSKTypeExternal   PSKType = 1
	PSKTypeResumption PSKType = 2
)

PSK types. See RFC 9420, Section 8.4.

func (PSKType) String

func (t PSKType) String() string

String returns the name of the pre-shared key type.

type ParentNode

type ParentNode struct {
	EncryptionKey  HPKEPublicKey
	ParentHash     []byte
	UnmergedLeaves []uint32
}

A ParentNode describes an interior node of the ratchet tree. Its encryption key is known only to the members at the leaves below it. UnmergedLeaves lists, in increasing order, the leaves below the node whose members do not hold that key. See RFC 9420, Section 7.1.

func (*ParentNode) MarshalTLS

func (n *ParentNode) MarshalTLS(w *tlssyntax.Writer)

func (*ParentNode) UnmarshalTLS

func (n *ParentNode) UnmarshalTLS(r *tlssyntax.Reader)

type PreSharedKey

type PreSharedKey struct {
	PSK PreSharedKeyID
}

A PreSharedKey proposal injects a pre-shared key into the key schedule at the next epoch. See RFC 9420, Section 12.1.4.

func (*PreSharedKey) MarshalTLS

func (p *PreSharedKey) MarshalTLS(w *tlssyntax.Writer)

func (*PreSharedKey) UnmarshalTLS

func (p *PreSharedKey) UnmarshalTLS(r *tlssyntax.Reader)

type PreSharedKeyID

type PreSharedKeyID struct {
	Type PSKType

	PSKID []byte // Type == PSKTypeExternal

	Usage      ResumptionPSKUsage // Type == PSKTypeResumption
	PSKGroupID []byte             // Type == PSKTypeResumption
	PSKEpoch   uint64             // Type == PSKTypeResumption

	PSKNonce []byte
}

A PreSharedKeyID names a pre-shared key to mix into the key schedule. Type decides whether it names an external key or an epoch of some group to resume from. The nonce makes each injection of the same key distinct. See RFC 9420, Section 8.4.

A caller may leave PSKNonce empty: Group.Propose, Group.Commit, Client.JoinExternal and ExternalClient.Propose fill it with a fresh nonce of the right length. A nonce of any other length is rejected.

func (*PreSharedKeyID) MarshalTLS

func (p *PreSharedKeyID) MarshalTLS(w *tlssyntax.Writer)

func (*PreSharedKeyID) UnmarshalTLS

func (p *PreSharedKeyID) UnmarshalTLS(r *tlssyntax.Reader)

type PrivateMessage

type PrivateMessage struct {
	GroupID             []byte
	Epoch               uint64
	ContentType         ContentType
	AuthenticatedData   []byte
	EncryptedSenderData []byte
	Ciphertext          []byte
}

A PrivateMessage carries encrypted content. Its group, epoch, and content type stay in the clear so that a delivery service can route it without being able to read it. See RFC 9420, Section 6.3.

func (*PrivateMessage) MarshalTLS

func (m *PrivateMessage) MarshalTLS(w *tlssyntax.Writer)

func (*PrivateMessage) UnmarshalTLS

func (m *PrivateMessage) UnmarshalTLS(r *tlssyntax.Reader)

type Proposal

type Proposal struct {
	Type ProposalType

	Add                    *Add
	Update                 *Update
	Remove                 *Remove
	PreSharedKey           *PreSharedKey
	Reinit                 *Reinit
	ExternalInit           *ExternalInit
	GroupContextExtensions *GroupContextExtensions
}

A Proposal is one requested change to a group. Type decides which of the remaining fields is present. See RFC 9420, Section 12.1.

func (*Proposal) MarshalTLS

func (p *Proposal) MarshalTLS(w *tlssyntax.Writer)

func (*Proposal) UnmarshalTLS

func (p *Proposal) UnmarshalTLS(r *tlssyntax.Reader)

type ProposalOrRef

type ProposalOrRef struct {
	Type ProposalOrRefType

	Proposal  *Proposal     // Type == ProposalOrRefTypeProposal
	Reference HashReference // Type == ProposalOrRefTypeReference
}

A ProposalOrRef is one entry in a Commit: either a proposal carried inline, or a reference to one sent earlier in the epoch. See RFC 9420, Section 12.4.

func (*ProposalOrRef) MarshalTLS

func (p *ProposalOrRef) MarshalTLS(w *tlssyntax.Writer)

func (*ProposalOrRef) UnmarshalTLS

func (p *ProposalOrRef) UnmarshalTLS(r *tlssyntax.Reader)

type ProposalOrRefType

type ProposalOrRefType uint8

A ProposalOrRefType says whether a commit carries a proposal inline or by reference. See RFC 9420, Section 12.4.

const (
	ProposalOrRefTypeProposal  ProposalOrRefType = 1
	ProposalOrRefTypeReference ProposalOrRefType = 2
)

Proposal-or-reference types.

func (ProposalOrRefType) String

func (t ProposalOrRefType) String() string

String reports whether the proposal is carried inline or by reference.

type ProposalType

type ProposalType uint16

A ProposalType identifies the kind of change a Proposal requests.

const (
	ProposalTypeAdd                    ProposalType = 1
	ProposalTypeUpdate                 ProposalType = 2
	ProposalTypeRemove                 ProposalType = 3
	ProposalTypePreSharedKey           ProposalType = 4
	ProposalTypeReinit                 ProposalType = 5
	ProposalTypeExternalInit           ProposalType = 6
	ProposalTypeGroupContextExtensions ProposalType = 7
)

Proposal types. See RFC 9420, Section 17.4.

func (ProposalType) String

func (t ProposalType) String() string

String returns the name of the proposal type, or its number if it is not one this package knows.

type ProtocolVersion

type ProtocolVersion uint16

A ProtocolVersion identifies a version of the protocol.

const (
	Version10 ProtocolVersion = 1
)

Protocol versions. See RFC 9420, Section 6.

func (ProtocolVersion) String

func (v ProtocolVersion) String() string

String returns the name of the protocol version.

type PublicMessage

type PublicMessage struct {
	Content       FramedContent
	Auth          FramedContentAuthData
	MembershipTag []byte // Content.Sender.Type == SenderTypeMember
}

A PublicMessage carries content that is authenticated but not encrypted. A message from a member also carries a membership tag. See RFC 9420, Section 6.2.

func (*PublicMessage) AuthenticatedContent

func (m *PublicMessage) AuthenticatedContent(cs CipherSuite, membershipKey []byte, ctx *GroupContext) (*AuthenticatedContent, error)

AuthenticatedContent recovers the content of a public message, checking the membership tag if there is one. It refuses application data, which may be sent only as a PrivateMessage. The caller must still verify the signature; see AuthenticatedContent.Verify. See RFC 9420, Section 6.2.

func (*PublicMessage) MarshalTLS

func (m *PublicMessage) MarshalTLS(w *tlssyntax.Writer)

func (*PublicMessage) UnmarshalTLS

func (m *PublicMessage) UnmarshalTLS(r *tlssyntax.Reader)

type RatchetTree

type RatchetTree []*Node

A RatchetTree is the array representation of a group's ratchet tree, as carried in a ratchet_tree extension. A nil entry is a blank node. See RFC 9420, Section 12.4.3.1.

func (*RatchetTree) Add

func (t *RatchetTree) Add(leaf *LeafNode) LeafIndex

Add places leaf at the leftmost blank leaf, extending the tree if every leaf is occupied, and records it as unmerged in each node above it. See RFC 9420, Sections 7.7 and 12.1.1.

func (RatchetTree) Clone

func (t RatchetTree) Clone() RatchetTree

Clone returns a copy of t that shares no state with it. The array copy alone is not enough: RatchetTree.Add records the new leaf in the unmerged_leaves of the nodes above it, which would otherwise be a write through to the tree it was copied from. A group holds the tree of one epoch and must not be changed by the next.

func (*RatchetTree) CreateUpdatePath

func (t *RatchetTree) CreateUpdatePath(cs CipherSuite, i LeafIndex, leafSecret, sigPriv []byte, ctx *GroupContext, exclude []LeafIndex) (*UpdatePath, *TreeSecrets, []byte, error)

CreateUpdatePath refreshes the key pairs along leaf i's direct path and returns the UpdatePath that tells the rest of the group about it, the sender's new private state, and the commit secret that the new epoch's key schedule takes as input.

The tree is updated in place. All of the new key material derives from leafSecret, which must be freshly sampled and as long as the cipher suite's hash. The new leaf node is signed with sigPriv, and the path secrets are encrypted under ctx, whose TreeHash this function recomputes from the updated tree. Leaves added by the same commit are named in exclude, and receive no path secret. See RFC 9420, Section 7.5.

func (RatchetTree) DecryptPathSecrets

func (t RatchetTree) DecryptPathSecrets(cs CipherSuite, i LeafIndex, up *UpdatePath, ctx *GroupContext, s *TreeSecrets, exclude []LeafIndex) ([]byte, error)

DecryptPathSecrets recovers from an update path sent by the member at leaf i every path secret that s is entitled to, records them in s, and returns the epoch's commit secret. The tree must already have been merged, and ctx must be the group context of the new epoch; its TreeHash is recomputed from the merged tree. Leaves that the same commit added are named in exclude. See RFC 9420, Section 7.5.

func (RatchetTree) FilteredDirectPath

func (t RatchetTree) FilteredDirectPath(i LeafIndex) []NodeIndex

FilteredDirectPath returns the nodes of leaf i's direct path whose copath child has a non-empty resolution. Only those nodes need key pairs of their own: encrypting to any other node on the path would be the same as encrypting to its child. See RFC 9420, Section 4.2.

func (RatchetTree) Leaf

func (t RatchetTree) Leaf(i LeafIndex) *LeafNode

Leaf returns the leaf node of the member at i, or nil if that leaf is blank or i is not a leaf of the tree.

func (*RatchetTree) MarshalTLS

func (t *RatchetTree) MarshalTLS(w *tlssyntax.Writer)

func (RatchetTree) Members

func (t RatchetTree) Members() iter.Seq2[LeafIndex, *LeafNode]

Members returns an iterator over the members of the group, in leaf order. Blank leaves are skipped: every leaf the iterator yields is occupied, so callers need no nil check. Ranging over Size and calling RatchetTree.Leaf is the form to use when the blanks themselves matter.

func (*RatchetTree) MergeUpdatePath

func (t *RatchetTree) MergeUpdatePath(cs CipherSuite, i LeafIndex, up *UpdatePath) error

MergeUpdatePath applies the public keys of an update path sent by the member at leaf i, updating the tree in place, and checks that the new leaf node's parent hash chains to the root. See RFC 9420, Section 7.5.

func (RatchetTree) Node

func (t RatchetTree) Node(x NodeIndex) *Node

Node returns the node at x, or nil if it is blank or beyond the trimmed end of the array. A node that is not the kind its position requires, a leaf at an even index and a parent at an odd one, is treated as blank, as the tree hash treats it.

func (RatchetTree) ParentHash

func (t RatchetTree) ParentHash(cs CipherSuite, p, s NodeIndex) ([]byte, error)

ParentHash returns the parent hash of the non-blank parent node p with copath child s, the summary that a node below p records to attest that p was set by a member of the group. See RFC 9420, Section 7.9.

func (*RatchetTree) Remove

func (t *RatchetTree) Remove(i LeafIndex)

Remove blanks the leaf at i along with the nodes above it, then trims the tree if its right half has emptied. It does nothing if i is not a leaf of the tree. See RFC 9420, Sections 7.7 and 12.1.3.

func (RatchetTree) Resolution

func (t RatchetTree) Resolution(x NodeIndex) []NodeIndex

Resolution is the ordered list of non-blank nodes that covers the subtree under x: x itself and its unmerged leaves if x is not blank, and otherwise the resolutions of its children in order. See RFC 9420, Section 4.2.

func (RatchetTree) RootHash

func (t RatchetTree) RootHash(cs CipherSuite) ([]byte, error)

RootHash is the tree hash of the whole tree, which the group context carries so that members can confirm they agree on it.

func (RatchetTree) Size

func (t RatchetTree) Size() LeafIndex

Size is the number of leaves in the tree. Every tree in MLS is perfect, so the number of leaves is a power of two, and a tree whose trailing blank nodes have been trimmed still has the size of the whole. See RFC 9420, Section 4.1.

func (RatchetTree) TreeHash

func (t RatchetTree) TreeHash(cs CipherSuite, x NodeIndex) ([]byte, error)

TreeHash returns the tree hash of the subtree headed by x, which summarizes everything below it. See RFC 9420, Section 7.8.

func (*RatchetTree) UnmarshalTLS

func (t *RatchetTree) UnmarshalTLS(r *tlssyntax.Reader)

func (*RatchetTree) Update

func (t *RatchetTree) Update(i LeafIndex, leaf *LeafNode)

Update replaces the leaf node at i and blanks the nodes above it, whose keys the old member knew. It does nothing if i is not a leaf of the tree. See RFC 9420, Section 12.1.2.

func (RatchetTree) VerifyLeafSignatures

func (t RatchetTree) VerifyLeafSignatures(cs CipherSuite, groupID []byte) error

VerifyLeafSignatures checks the signature on every non-blank leaf, binding each to its position in the group.

func (RatchetTree) VerifyParentHashes

func (t RatchetTree) VerifyParentHashes(cs CipherSuite) error

VerifyParentHashes checks that every non-blank parent node in the tree was introduced by a member: each must be chained to a leaf by a parent hash. A new member does this when it joins. See RFC 9420, Section 7.9.2.

type Reinit

type Reinit struct {
	GroupID     []byte
	Version     ProtocolVersion
	CipherSuite CipherSuite
	Extensions  Extensions
}

A Reinit proposal asks that the group be torn down and recreated with different parameters. See RFC 9420, Section 12.1.5.

func (*Reinit) MarshalTLS

func (ri *Reinit) MarshalTLS(w *tlssyntax.Writer)

func (*Reinit) UnmarshalTLS

func (ri *Reinit) UnmarshalTLS(r *tlssyntax.Reader)

type Remove

type Remove struct {
	Removed uint32
}

A Remove proposal requests that the member at the given leaf index be removed from the group. See RFC 9420, Section 12.1.3.

func (*Remove) MarshalTLS

func (rm *Remove) MarshalTLS(w *tlssyntax.Writer)

func (*Remove) UnmarshalTLS

func (rm *Remove) UnmarshalTLS(r *tlssyntax.Reader)

type RequiredCapabilities

type RequiredCapabilities struct {
	ExtensionTypes  []ExtensionType
	ProposalTypes   []ProposalType
	CredentialTypes []CredentialType
}

RequiredCapabilities lists what every member of a group must support. It appears in the group context, so that the requirement is one the whole group agrees on. See RFC 9420, Section 11.1.

func (*RequiredCapabilities) MarshalTLS

func (c *RequiredCapabilities) MarshalTLS(w *tlssyntax.Writer)

func (*RequiredCapabilities) Supported

func (c *RequiredCapabilities) Supported(caps *Capabilities) bool

Supported reports whether c lists nothing that caps does not support. A member may only be added to a group if its capabilities support the group's required capabilities. The default extension and proposal types are supported without being listed; credential types have no defaults. See RFC 9420, Sections 7.2 and 11.1.

func (*RequiredCapabilities) UnmarshalTLS

func (c *RequiredCapabilities) UnmarshalTLS(r *tlssyntax.Reader)

type ResumptionPSKUsage

type ResumptionPSKUsage uint8

A ResumptionPSKUsage says why a resumption PSK is being used.

const (
	ResumptionPSKUsageApplication ResumptionPSKUsage = 1
	ResumptionPSKUsageReinit      ResumptionPSKUsage = 2
	ResumptionPSKUsageBranch      ResumptionPSKUsage = 3
)

Resumption PSK usages. See RFC 9420, Section 8.4.

func (ResumptionPSKUsage) String

func (u ResumptionPSKUsage) String() string

String returns the name of the resumption key usage.

type Sender

type Sender struct {
	Type SenderType

	LeafIndex   uint32 // Type == SenderTypeMember
	SenderIndex uint32 // Type == SenderTypeExternal
}

A Sender identifies who sent a FramedContent. SenderType decides whether a leaf index is present. See RFC 9420, Section 6.

func (*Sender) MarshalTLS

func (s *Sender) MarshalTLS(w *tlssyntax.Writer)

func (*Sender) UnmarshalTLS

func (s *Sender) UnmarshalTLS(r *tlssyntax.Reader)

type SenderData

type SenderData struct {
	LeafIndex  uint32
	Generation uint32
	ReuseGuard [4]byte
}

SenderData names the key a PrivateMessage was encrypted under. It travels encrypted in PrivateMessage.EncryptedSenderData. See RFC 9420, Section 6.3.2.

func (*SenderData) MarshalTLS

func (d *SenderData) MarshalTLS(w *tlssyntax.Writer)

func (*SenderData) UnmarshalTLS

func (d *SenderData) UnmarshalTLS(r *tlssyntax.Reader)

type SenderType

type SenderType uint8

A SenderType identifies the kind of sender of a FramedContent.

const (
	SenderTypeMember            SenderType = 1
	SenderTypeExternal          SenderType = 2
	SenderTypeNewMemberProposal SenderType = 3
	SenderTypeNewMemberCommit   SenderType = 4
)

Sender types. See RFC 9420, Section 6.

func (SenderType) String

func (t SenderType) String() string

String returns the name of the sender type.

type SignaturePublicKey

type SignaturePublicKey []byte

A SignaturePublicKey is a public key for signature verification, in the format the group's cipher suite defines.

type TreeSecrets

type TreeSecrets struct {
	Index   LeafIndex            // the member's own leaf
	Leaf    []byte               // HPKE private key of that leaf
	Secrets map[NodeIndex][]byte // path secrets, by node
}

TreeSecrets is a member's private view of a ratchet tree: the HPKE private key of its own leaf, and the path secret of each node on its direct path whose key it knows. Every other private key the member holds is derived from one of those path secrets. See RFC 9420, Section 7.5.

func NewTreeSecrets

func NewTreeSecrets(i LeafIndex, leafPriv []byte) *TreeSecrets

NewTreeSecrets returns the private state of the member at leaf i whose leaf node holds the HPKE private key leafPriv.

func (*TreeSecrets) Consistent

func (s *TreeSecrets) Consistent(cs CipherSuite, t RatchetTree) error

Consistent reports whether every private key in s matches the corresponding public key in t. A member checks this after joining.

func (*TreeSecrets) PrivateKey

func (s *TreeSecrets) PrivateKey(cs CipherSuite, x NodeIndex) ([]byte, error)

PrivateKey returns the HPKE private key s holds for node x, or nil if it holds none.

func (*TreeSecrets) SetPath

func (s *TreeSecrets) SetPath(cs CipherSuite, t RatchetTree, x NodeIndex, secret []byte) error

SetPath records secret as the path secret of node x and derives the path secrets of every node above x on the member's filtered direct path, checking each against the public key in t. A new member does this with the path secret a welcome message carries. See RFC 9420, Section 12.4.3.1.

type Update

type Update struct {
	LeafNode LeafNode
}

An Update proposal replaces the sender's leaf node with a fresh one. See RFC 9420, Section 12.1.2.

func (*Update) MarshalTLS

func (u *Update) MarshalTLS(w *tlssyntax.Writer)

func (*Update) UnmarshalTLS

func (u *Update) UnmarshalTLS(r *tlssyntax.Reader)

type UpdatePath

type UpdatePath struct {
	LeafNode LeafNode
	Nodes    []UpdatePathNode
}

An UpdatePath carries the committer's new leaf node and the new keys along its direct path to the root. See RFC 9420, Section 6.1.

func (*UpdatePath) MarshalTLS

func (p *UpdatePath) MarshalTLS(w *tlssyntax.Writer)

func (*UpdatePath) UnmarshalTLS

func (p *UpdatePath) UnmarshalTLS(r *tlssyntax.Reader)

type UpdatePathNode

type UpdatePathNode struct {
	EncryptionKey       HPKEPublicKey
	EncryptedPathSecret []HPKECiphertext
}

An UpdatePathNode is one node on the direct path of a committer's leaf: a fresh public key, and the path secret above it encrypted to each node in that node's resolution. See RFC 9420, Section 6.1.

func (*UpdatePathNode) MarshalTLS

func (n *UpdatePathNode) MarshalTLS(w *tlssyntax.Writer)

func (*UpdatePathNode) UnmarshalTLS

func (n *UpdatePathNode) UnmarshalTLS(r *tlssyntax.Reader)

type Welcome

type Welcome struct {
	CipherSuite        CipherSuite
	Secrets            []EncryptedGroupSecrets
	EncryptedGroupInfo []byte
}

A Welcome brings new members into a group. It carries the group's secrets encrypted separately to each new member, and the group's GroupInfo encrypted under a key those secrets derive. See RFC 9420, Section 12.4.3.1.

func (*Welcome) AddMember

func (m *Welcome) AddMember(ref HashReference, initKey HPKEPublicKey, secrets *GroupSecrets) error

AddMember encrypts secrets to the holder of the key package named by ref, whose init key is initKey.

func (*Welcome) GroupInfo

func (m *Welcome) GroupInfo(welcomeSecret []byte) (*GroupInfo, error)

GroupInfo decrypts the group info a welcome message carries, under the welcome secret derived from the joiner secret and any pre-shared keys the commit injected.

func (*Welcome) GroupSecrets

func (m *Welcome) GroupSecrets(ref HashReference, initPriv []byte) (*GroupSecrets, error)

GroupSecrets decrypts the group secrets a welcome message carries for the holder of initPriv, the private half of the init key in the key package named by ref.

func (*Welcome) MarshalTLS

func (m *Welcome) MarshalTLS(w *tlssyntax.Writer)

func (*Welcome) SetGroupInfo

func (m *Welcome) SetGroupInfo(welcomeSecret []byte, info *GroupInfo) error

SetGroupInfo encrypts info into the welcome message. Callers add the per-member secrets with Welcome.AddMember afterwards, since those are bound to the encrypted group info.

func (*Welcome) UnmarshalTLS

func (m *Welcome) UnmarshalTLS(r *tlssyntax.Reader)

type WireFormat

type WireFormat uint16

A WireFormat identifies which message a Message carries.

const (
	WireFormatPublicMessage  WireFormat = 1
	WireFormatPrivateMessage WireFormat = 2
	WireFormatWelcome        WireFormat = 3
	WireFormatGroupInfo      WireFormat = 4
	WireFormatKeyPackage     WireFormat = 5
)

Wire formats. See RFC 9420, Section 17.5.

func (WireFormat) String

func (f WireFormat) String() string

String returns the name of the wire format.

Directories

Path Synopsis
internal
ed448
Package ed448 implements the Ed448 signature algorithm of RFC 8032, Section 5.2, in its pure form (not Ed448ph).
Package ed448 implements the Ed448 signature algorithm of RFC 8032, Section 5.2, in its pure form (not Ed448ph).
fp448
Package fp448 implements constant-time arithmetic modulo p = 2^448 - 2^224 - 1, the field of Curve448 and Edwards448.
Package fp448 implements constant-time arithmetic modulo p = 2^448 - 2^224 - 1, the field of Curve448 and Edwards448.
hpkex448
Package hpkex448 implements HPKE (RFC 9180) in base mode with the KEM DHKEM(X448, HKDF-SHA512) and the KDF HKDF-SHA512, the HPKE of MLS cipher suites 4 and 6, which crypto/hpke does not provide.
Package hpkex448 implements HPKE (RFC 9180) in base mode with the KEM DHKEM(X448, HKDF-SHA512) and the KDF HKDF-SHA512, the HPKE of MLS cipher suites 4 and 6, which crypto/hpke does not provide.
x448
Package x448 implements the X448 Diffie-Hellman function of RFC 7748, Section 5, in constant time.
Package x448 implements the X448 Diffie-Hellman function of RFC 7748, Section 5, in constant time.
Package multicred implements the multi-credentials of draft-ietf-mls-extensions, which let one member present several credentials at once.
Package multicred implements the multi-credentials of draft-ietf-mls-extensions, which let one member present several credentials at once.
Package tlssyntax encodes and decodes the TLS presentation language as extended by the Messaging Layer Security protocol, RFC 9420.
Package tlssyntax encodes and decodes the TLS presentation language as extended by the Messaging Layer Security protocol, RFC 9420.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL