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 ¶
- Variables
- func Marshal(v tlssyntax.Marshaler) ([]byte, error)
- func RegisterCredential(t CredentialType, body func() CredentialCodec)
- func Unmarshal(data []byte, v tlssyntax.Unmarshaler) error
- type Add
- type ApplicationID
- type AuthenticatedContent
- func (c *AuthenticatedContent) MarshalTLS(w *tlssyntax.Writer)
- func (c *AuthenticatedContent) PublicMessage(cs CipherSuite, membershipKey []byte, ctx *GroupContext) (*PublicMessage, error)
- func (c *AuthenticatedContent) Ref(cs CipherSuite) (HashReference, error)
- func (c *AuthenticatedContent) Sign(cs CipherSuite, priv []byte, version ProtocolVersion, ctx *GroupContext) error
- func (c *AuthenticatedContent) UnmarshalTLS(r *tlssyntax.Reader)
- func (c *AuthenticatedContent) Verify(cs CipherSuite, pub SignaturePublicKey, version ProtocolVersion, ...) error
- type Capabilities
- type CipherSuite
- func (cs CipherSuite) AEAD(key []byte) (cipher.AEAD, error)
- func (cs CipherSuite) AEADKeySize() int
- func (cs CipherSuite) AEADNonceSize() int
- func (cs CipherSuite) ConfirmationTag(confirmationKey, confirmedTranscriptHash []byte) ([]byte, error)
- func (cs CipherSuite) ConfirmedTranscriptHash(interim []byte, commit *AuthenticatedContent) ([]byte, error)
- func (cs CipherSuite) DecryptWithLabel(priv []byte, label string, context []byte, ct *HPKECiphertext) ([]byte, error)
- func (cs CipherSuite) DeriveKeyPair(ikm []byte) (priv []byte, pub HPKEPublicKey, err error)
- func (cs CipherSuite) DeriveSecret(secret []byte, label string) ([]byte, error)
- func (cs CipherSuite) DeriveTreeSecret(secret []byte, label string, generation uint32, length uint16) ([]byte, error)
- func (cs CipherSuite) EncryptWithLabel(pub HPKEPublicKey, label string, context, plaintext []byte) (*HPKECiphertext, error)
- func (cs CipherSuite) ExpandWithLabel(secret []byte, label string, context []byte, length uint16) ([]byte, error)
- func (cs CipherSuite) ExternalInit(externalPub HPKEPublicKey) (kemOutput, initSecret []byte, err error)
- func (cs CipherSuite) Extract(salt, ikm []byte) ([]byte, error)
- func (cs CipherSuite) GenerateKeyPair() (priv []byte, pub HPKEPublicKey, err error)
- func (cs CipherSuite) GenerateSignatureKeyPair() (priv []byte, pub SignaturePublicKey, err error)
- func (cs CipherSuite) Hash(data []byte) ([]byte, error)
- func (cs CipherSuite) HashSize() int
- func (cs CipherSuite) InterimTranscriptHash(confirmed, confirmationTag []byte) ([]byte, error)
- func (cs CipherSuite) JoinerSecret(initSecret, commitSecret []byte, ctx *GroupContext) ([]byte, error)
- func (cs CipherSuite) MembershipTag(membershipKey []byte, content *AuthenticatedContent, version ProtocolVersion, ...) ([]byte, error)
- func (cs CipherSuite) PSKSecret(ids []PreSharedKeyID, psks [][]byte) ([]byte, error)
- func (cs CipherSuite) PublicKey(priv []byte) (HPKEPublicKey, error)
- func (cs CipherSuite) RefHash(label string, value []byte) ([]byte, error)
- func (cs CipherSuite) SenderDataKey(senderDataSecret, ciphertext []byte) (key, nonce []byte, err error)
- func (cs CipherSuite) SignWithLabel(priv []byte, label string, content []byte) ([]byte, error)
- func (cs CipherSuite) String() string
- func (cs CipherSuite) Supported() bool
- func (cs CipherSuite) VerifyWithLabel(pub SignaturePublicKey, label string, content, sig []byte) error
- func (cs CipherSuite) WelcomeSecret(joinerSecret, pskSecret []byte) ([]byte, error)
- type Client
- func (c *Client) Join(w *Welcome, tree RatchetTree) (*Group, error)
- func (c *Client) JoinExternal(info *GroupInfo, tree RatchetTree, psks []PreSharedKeyID) (*Group, *Message, error)
- func (c *Client) NewGroup(groupID []byte, extensions Extensions) (*Group, error)
- func (c *Client) ProposeAdd(groupID []byte, epoch uint64) (*Message, error)
- func (c *Client) Resume(w *Welcome, tree RatchetTree, old *Group) (*Group, error)
- type Commit
- type ContentType
- type Credential
- type CredentialCodec
- type CredentialType
- type EncryptedGroupSecrets
- type Extension
- type ExtensionType
- type Extensions
- func (es Extensions) Decode(t ExtensionType, body tlssyntax.Unmarshaler) (bool, error)
- func (es Extensions) Find(t ExtensionType) *Extension
- func (es *Extensions) MarshalTLS(w *tlssyntax.Writer)
- func (es *Extensions) Set(t ExtensionType, body tlssyntax.Marshaler) error
- func (es *Extensions) UnmarshalTLS(r *tlssyntax.Reader)
- type ExternalClient
- type ExternalInit
- type ExternalPub
- type ExternalSender
- type ExternalSenders
- type FramedContent
- type FramedContentAuthData
- type Group
- func (g *Group) AddProposal(c *AuthenticatedContent) error
- func (g *Group) ApplyCommit(c *AuthenticatedContent) (*Group, error)
- func (g *Group) Branch(groupID []byte, extensions Extensions, members []*KeyPackage) (*Group, *Message, error)
- func (g *Group) Commit(extra []*Proposal) (*Group, *Message, *Message, error)
- func (g *Group) Epoch() uint64
- func (g *Group) EpochAuthenticator() []byte
- func (g *Group) Export(label string, context []byte, length uint16) ([]byte, error)
- func (g *Group) GroupInfo() (*Message, error)
- func (g *Group) Handle(m *Message) (*Group, *AuthenticatedContent, error)
- func (g *Group) Propose(p *Proposal) (*Message, error)
- func (g *Group) ProposeUpdate(leaf *LeafNode, encPriv []byte) (*Message, error)
- func (g *Group) Protect(authenticatedData, plaintext []byte) (*Message, error)
- func (g *Group) Reinit() *Reinit
- func (g *Group) Reinitialize(c *Client, members []*KeyPackage) (*Group, *Message, error)
- func (g *Group) Unprotect(m *Message) (*AuthenticatedContent, error)
- type GroupContext
- type GroupContextExtensions
- type GroupInfo
- type GroupSecrets
- type HPKECiphertext
- type HPKEPublicKey
- type HashReference
- type Header
- type KeyPackage
- func (p *KeyPackage) MarshalTLS(w *tlssyntax.Writer)
- func (p *KeyPackage) Ref() (HashReference, error)
- func (p *KeyPackage) Sign(priv []byte) error
- func (p *KeyPackage) SignedContent() ([]byte, error)
- func (p *KeyPackage) UnmarshalTLS(r *tlssyntax.Reader)
- func (p *KeyPackage) Validate(now time.Time) error
- func (p *KeyPackage) Verify() error
- type LeafIndex
- type LeafNode
- func (n *LeafNode) MarshalTLS(w *tlssyntax.Writer)
- func (n *LeafNode) Sign(cs CipherSuite, priv []byte, groupID []byte, leafIndex LeafIndex) error
- func (n *LeafNode) SignedContent(groupID []byte, leafIndex LeafIndex) ([]byte, error)
- func (n *LeafNode) UnmarshalTLS(r *tlssyntax.Reader)
- func (n *LeafNode) Verify(cs CipherSuite, groupID []byte, leafIndex LeafIndex) error
- type LeafNodeSource
- type Lifetime
- type Message
- type Node
- type NodeIndex
- type NodeType
- type PSKType
- type ParentNode
- type PreSharedKey
- type PreSharedKeyID
- type PrivateMessage
- type Proposal
- type ProposalOrRef
- type ProposalOrRefType
- type ProposalType
- type ProtocolVersion
- type PublicMessage
- type RatchetTree
- func (t *RatchetTree) Add(leaf *LeafNode) LeafIndex
- func (t RatchetTree) Clone() RatchetTree
- func (t *RatchetTree) CreateUpdatePath(cs CipherSuite, i LeafIndex, leafSecret, sigPriv []byte, ctx *GroupContext, ...) (*UpdatePath, *TreeSecrets, []byte, error)
- func (t RatchetTree) DecryptPathSecrets(cs CipherSuite, i LeafIndex, up *UpdatePath, ctx *GroupContext, s *TreeSecrets, ...) ([]byte, error)
- func (t RatchetTree) FilteredDirectPath(i LeafIndex) []NodeIndex
- func (t RatchetTree) Leaf(i LeafIndex) *LeafNode
- func (t *RatchetTree) MarshalTLS(w *tlssyntax.Writer)
- func (t RatchetTree) Members() iter.Seq2[LeafIndex, *LeafNode]
- func (t *RatchetTree) MergeUpdatePath(cs CipherSuite, i LeafIndex, up *UpdatePath) error
- func (t RatchetTree) Node(x NodeIndex) *Node
- func (t RatchetTree) ParentHash(cs CipherSuite, p, s NodeIndex) ([]byte, error)
- func (t *RatchetTree) Remove(i LeafIndex)
- func (t RatchetTree) Resolution(x NodeIndex) []NodeIndex
- func (t RatchetTree) RootHash(cs CipherSuite) ([]byte, error)
- func (t RatchetTree) Size() LeafIndex
- func (t RatchetTree) TreeHash(cs CipherSuite, x NodeIndex) ([]byte, error)
- func (t *RatchetTree) UnmarshalTLS(r *tlssyntax.Reader)
- func (t *RatchetTree) Update(i LeafIndex, leaf *LeafNode)
- func (t RatchetTree) VerifyLeafSignatures(cs CipherSuite, groupID []byte) error
- func (t RatchetTree) VerifyParentHashes(cs CipherSuite) error
- type Reinit
- type Remove
- type RequiredCapabilities
- type ResumptionPSKUsage
- type Sender
- type SenderData
- type SenderType
- type SignaturePublicKey
- type TreeSecrets
- type Update
- type UpdatePath
- type UpdatePathNode
- type Welcome
- func (m *Welcome) AddMember(ref HashReference, initKey HPKEPublicKey, secrets *GroupSecrets) error
- func (m *Welcome) GroupInfo(welcomeSecret []byte) (*GroupInfo, error)
- func (m *Welcome) GroupSecrets(ref HashReference, initPriv []byte) (*GroupSecrets, error)
- func (m *Welcome) MarshalTLS(w *tlssyntax.Writer)
- func (m *Welcome) SetGroupInfo(welcomeSecret []byte, info *GroupInfo) error
- func (m *Welcome) UnmarshalTLS(r *tlssyntax.Reader)
- type WireFormat
Examples ¶
Constants ¶
This section is empty.
Variables ¶
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.
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.
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.
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.
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.
var ErrBadMembershipTag = errors.New("mls: membership tag does not verify")
ErrBadMembershipTag is reported when the membership tag of a PublicMessage does not verify.
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.
var ErrBadPadding = errors.New("mls: padding is not zero")
ErrBadPadding is reported when the padding of a PrivateMessage is not all zero.
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.
var ErrBadSignature = errors.New("mls: signature does not verify")
ErrBadSignature is reported when a signature does not verify.
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.
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.
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.
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.
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.
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.
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.
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.
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.
var ErrExpired = errors.New("mls: key package has expired")
ErrExpired is reported for a key package used outside the lifetime of its leaf node.
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.
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.
var ErrLeafRange = errors.New("mls: leaf index out of range")
ErrLeafRange is reported for a leaf index outside the group.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
var ErrNotProposal = errors.New("mls: content is not a proposal")
ErrNotProposal is reported for content that must be a proposal and is not.
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.
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.
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.
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.
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.
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.
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.
var ErrRemoved = errors.New("mls: member was removed from the group")
ErrRemoved is reported when a commit removes the member applying it.
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.
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.
var ErrUnknownPSK = errors.New("mls: unknown pre-shared key")
ErrUnknownPSK is reported for a pre-shared key the client cannot supply.
var ErrUnknownProposal = errors.New("mls: unknown proposal")
ErrUnknownProposal is reported for a proposal that a commit refers to but that the member never received.
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.
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.
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.
var ErrUnsupportedVersion = errors.New("mls: unsupported protocol version")
ErrUnsupportedVersion is reported for a protocol version other than mls10.
Functions ¶
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.
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 (*Add) UnmarshalTLS ¶
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 ¶
func (c *AuthenticatedContent) Ref(cs CipherSuite) (HashReference, error)
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 ¶
func (c *AuthenticatedContent) Verify(cs CipherSuite, pub SignaturePublicKey, version ProtocolVersion, ctx *GroupContext) error
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 ¶
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 ¶
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 ¶
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 (*Commit) UnmarshalTLS ¶
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 (*Extension) UnmarshalTLS ¶
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 ¶
func (es *Extensions) Set(t ExtensionType, body tlssyntax.Marshaler) error
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 ¶
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 ¶
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) EpochAuthenticator ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 (*GroupInfo) SignedContent ¶
SignedContent returns the GroupInfoTBS bytes that g.Signature covers. See RFC 9420, Section 12.4.3.
func (*GroupInfo) UnmarshalTLS ¶
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 ¶
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 ¶
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 ¶
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.
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 (*LeafNode) Sign ¶
Sign signs n with priv, which must be the private half of n.SignatureKey, and records the signature in n.Signature.
func (*LeafNode) SignedContent ¶
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 ¶
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 ¶
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 ¶
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 (*Lifetime) UnmarshalTLS ¶
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 (*Message) UnmarshalTLS ¶
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 (*Node) UnmarshalTLS ¶
type NodeIndex ¶
type NodeIndex uint32
A NodeIndex identifies a node in the array representation of a ratchet tree, counting both leaf and parent nodes.
type NodeType ¶
type NodeType uint8
A NodeType identifies which node a ratchet tree entry holds.
Node types. See RFC 9420, Section 12.4.3.1.
type PSKType ¶
type PSKType uint8
A PSKType identifies where a PreSharedKeyID comes from.
PSK types. See RFC 9420, Section 8.4.
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 {
}
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 {
}
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
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 (*Proposal) UnmarshalTLS ¶
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 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 (*Reinit) UnmarshalTLS ¶
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 (*Remove) UnmarshalTLS ¶
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 (*Sender) UnmarshalTLS ¶
type SenderData ¶
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 (*Update) UnmarshalTLS ¶
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 ¶
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 (*Welcome) SetGroupInfo ¶
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 ¶
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.
Source Files
¶
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. |