extensions

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Apr 22, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package extensions - Application ID Extension (RFC 9420 §5.3.3)

Package extensions - Required Capabilities Extension (RFC 9420 §11.1)

Package extensions implements MLS extensions per RFC 9420 §13.

Extensions add optional information to MLS objects and are used in:

  • KeyPackages: client capabilities and features
  • GroupInfo: group parameters for new members joining via Welcome
  • GroupContext: shared state ensuring all members have same view
  • LeafNodes: per-member configuration (e.g., application-specific IDs)

Implemented Extension Types

The package provides concrete implementations for all required MLS extensions:

| Extension Type | ID | Location | RFC Section | Description | |-------------------------|--------|--------------|-------------|----------------------------------| | ApplicationID | 0x0001 | LeafNode | §5.3.3 | Application-specific identifier | | RatchetTree | 0x0002 | GroupInfo | §12.4.3.3 | Full tree for new members | | RequiredCapabilities | 0x0003 | GroupContext | §11.1 | Required features for group | | ExternalPub | 0x0004 | GroupInfo | §12.4.3.2 | HPKE key for External Commit | | ExternalSenders | 0x0005 | GroupContext | §12.1.8.1 | Allowed external senders | | LastResort | 0x000A | KeyPackage | §16.8 | Backup KeyPackage |

Usage

## Creating Extensions

Use the typed extension constructors:

appID := NewApplicationIDExtension([]byte("my-app-id"))
ext, err := appID.ToExtension()

## Adding to KeyPackage

kp, priv, err := keypackages.Generate(cred, cs,
    keypackages.WithExtensions([]extensions.Extension{*ext}),
)

## Adding to GroupInfo

groupInfo, err := g.GetGroupInfo(sigKey,
    group.WithRatchetTree(true),     // Include ratchet tree (default)
    group.WithExternalPub(false), // Omit external pub key
)

Custom Extension Handlers

For custom extensions not defined in this package, use the generic Extension type directly:

ext := Extension{
    Type: ExtensionType(0xFF01), // Custom type ID
    Data: []byte("custom data"),
}

Applications may inspect and validate custom extensions but must not modify them unless they are the sender.

Serialization

Extensions are serialized in ascending order by type ID per RFC 9420 §13.4. This ensures deterministic tree hashes across all group members.

Errors

Operations return typed errors for programmatic handling:

  • ErrInvalidExtension: when extension data is malformed
  • Extension type not supported: returns error on unmarshal

Package extensions implements MLS extensions per RFC 9420 §13.

Extensions add optional information to MLS objects. They appear in:

  • KeyPackages: client capabilities
  • GroupInfo: group parameters for new members
  • GroupContext: ensures all members have same view

Extension Structure (RFC 9420 §13) RFC 9420 §13)" aria-label="Go to Extension Structure (RFC 9420 §13)">¶

```text ┌─────────────────────────────────────────┐ │ Extension (RFC 9420 §13) │ ├─────────────────────────────────────────┤ │ extension_type : uint16 │ │ extension_data : opaque<V> │ └─────────────────────────────────────────┘ ```

Implemented Extensions

| Type | ID | Location | RFC | Description | |------|-----|-----------|-----|-------------| | ApplicationID | 0x0001 | LeafNode | §5.3.3 | App identifier | | RatchetTree | 0x0002 | GroupInfo | §12.4.3.3 | Full tree | | RequiredCapabilities | 0x0003 | GroupContext | §11.1 | Required features | | ExternalPub | 0x0004 | GroupInfo | §12.4.3.2 | HPKE key for External Commit | | ExternalSenders | 0x0005 | GroupContext | §12.1.8.1 | External senders | | LastResort | 0x000A | KeyPackage | §16.8 | Backup KeyPackage |

Usage

// Create extension collection exts := extensions.NewExtensions()

// Add ApplicationID appId := extensions.NewApplicationIDExtension([]byte("my-app")) genericExt, err := appId.ToExtension()

if err != nil {
    return err
}

if err := exts.Add(*genericExt); err != nil {
    return err
}

// Serialize (deterministic order per RFC 9420 §13.4) data := exts.Marshal()

RFC Compliance

  • RFC 9420 §13: Extensions
  • RFC 9420 §13.4: Serialization (ascending type order)
  • RFC 9420 §13.4: Serialization order (ascending by type)
  • RFC 9420 §13.5: GREASE handling

Implementation Notes

**Serialization Order:** Extensions MUST be serialized in ascending order by ExtensionType (RFC 9420 §13.4). This ensures deterministic GroupContext hashes across all members.

**Duplicates:** Adding an extension of the same type replaces the existing one.

**GREASE:** GREASE constants (0x0A0A, 0x1A1A, etc.) test extensibility. Implementations must handle unknown types gracefully.

Package extensions - External Pub Extension (RFC 9420 §12.4.3.2)

Package extensions - External Senders Extension (RFC 9420 §12.1.8.1)

Package extensions - Last Resort Extension (RFC 9420 §16.8)

Package extensions - Ratchet Tree Extension (RFC 9420 §12.4.3.3)

Index

Constants

This section is empty.

Variables

View Source
var ErrInvalidExtension = errors.New("extensions: invalid extension")

ErrInvalidExtension is returned when an extension has invalid or nil data.

Functions

This section is empty.

Types

type ApplicationIDExtension

type ApplicationIDExtension struct {
	ApplicationID []byte // Application identifier (opaque<V>)
}

ApplicationIDExtension adds an application-specific identifier to a KeyPackage.

Per RFC 9420 §5.3.3, this extension identifies the application or service using the MLS client. Useful when multiple apps share MLS infrastructure.

Structure (RFC 9420 §5.3.3) RFC 9420 §5.3.3)" aria-label="Go to Structure (RFC 9420 §5.3.3)">¶

```text ┌─────────────────────────────────────────┐ │ ApplicationIDExtension │ ├─────────────────────────────────────────┤ │ application_id: opaque<V> │ └─────────────────────────────────────────┘ ```

Location

- KeyPackage: Yes - LeafNode: Yes - GroupInfo: No - GroupContext: No

Common Formats

- UTF-8 string: "com.example.chat", "discord-voice" - Reverse DNS: "com.company.app" (recommended) - Arbitrary bytes: app-specific binary identifiers

Example

// Create from bytes ext := NewApplicationIDExtension([]byte("my-app-identifier"))

// Create from string ext := NewApplicationIDExtensionFromString("com.example.chat")

// Validate

if err := ext.Validate(); err != nil {
    return err
}

// Serialize data := ext.Marshal()

RFC Compliance

RFC 9420 §5.3.3: "The ApplicationId extension allows applications to add an explicit, application-defined identifier to a KeyPackage."

func FromApplicationIDExtension

func FromApplicationIDExtension(ext *Extension) (*ApplicationIDExtension, error)

FromApplicationIDExtension creates an ApplicationIDExtension from a generic Extension.

Returns error if Type is not ExtensionTypeApplicationID.

func NewApplicationIDExtension

func NewApplicationIDExtension(appID []byte) *ApplicationIDExtension

NewApplicationIDExtension creates an ApplicationIDExtension.

The application_id can be any byte sequence up to 65535 bytes. Recommended format: reverse DNS ("com.example.app").

func UnmarshalApplicationIDExtension

func UnmarshalApplicationIDExtension(data []byte) (*ApplicationIDExtension, error)

UnmarshalApplicationIDExtension parses an ApplicationIDExtension from TLS.

Reads application_id as variable-length bytes per RFC 9420 §5.3.3.

func (*ApplicationIDExtension) Equal

Equal compares two ApplicationIDExtension instances.

func (*ApplicationIDExtension) Len

func (a *ApplicationIDExtension) Len() int

Len returns the length of the ApplicationID in bytes.

func (*ApplicationIDExtension) Marshal

func (a *ApplicationIDExtension) Marshal() []byte

Marshal serializes the extension to TLS format.

```text ┌─────────────────────────────────────────┐ │ application_id_length: varint │ ├─────────────────────────────────────────┤ │ application_id: opaque[] │ └─────────────────────────────────────────┘ ```

func (*ApplicationIDExtension) String

func (a *ApplicationIDExtension) String() string

String returns the ApplicationID as a human-readable string.

Attempts UTF-8 decoding. Falls back to hex if invalid UTF-8.

func (*ApplicationIDExtension) ToExtension

func (a *ApplicationIDExtension) ToExtension() (*Extension, error)

ToExtension converts to a generic Extension.

Useful for adding to an Extensions collection.

func (*ApplicationIDExtension) Validate

func (a *ApplicationIDExtension) Validate() error

Validate validates the extension per RFC 9420 §5.3.3.

Validation Rules

- ApplicationID must not be nil - ApplicationID must not be empty - ApplicationID <= 65535 bytes (varint limit)

type Extension

type Extension struct {
	Type ExtensionType
	Data []byte
}

Extension represents a generic MLS extension per RFC 9420 §13.

```text

struct {
    ExtensionType extension_type;    // uint16
    opaque extension_data<V>;        // variable-length
} Extension;

```

Example

ext := &Extension{
    Type: ExtensionTypeApplicationID,
    Data: []byte("my-app-id"),
}

if err := ext.Validate(); err != nil {
    return err
}

data := ext.Marshal()

func UnmarshalExtension

func UnmarshalExtension(data []byte) (*Extension, error)

UnmarshalExtension parses an Extension from TLS format.

Example

data := []byte{0x00, 0x01, 0x04, 't', 'e', 's', 't'} ext, err := UnmarshalExtension(data)

if err != nil {
    return err
}

// ext.Type == ExtensionTypeApplicationID // ext.Data == []byte("test")

func (*Extension) Equal

func (e *Extension) Equal(other *Extension) bool

Equal compares two extensions for equality.

func (*Extension) Marshal

func (e *Extension) Marshal() []byte

Marshal serializes an Extension to TLS format per RFC 9420 §13.

```text ┌─────────────────────────────────────────┐ │ TLS Encoding │ ├─────────────────────────────────────────┤ │ extension_type : uint16 │ │ extension_data_length : varint │ │ extension_data : opaque │ └─────────────────────────────────────────┘ ```

func (*Extension) Validate

func (e *Extension) Validate() error

Validate validates an Extension.

Validation Rules

- Data must not be nil - Type must be known (or GREASE for extensibility)

type ExtensionType

type ExtensionType uint16

ExtensionType identifies an MLS extension per RFC 9420 §17.3.

Extension types are registered in the IANA registry. GREASE values (0x0A0A, 0x1A1A, etc.) test extensibility (RFC 9420 §13.5).

const (
	// ExtensionTypeApplicationID - RFC 9420 §5.3.3
	// Location: LeafNode
	// Data: opaque application_id<V>
	ExtensionTypeApplicationID ExtensionType = 0x0001

	// ExtensionTypeRatchetTree - RFC 9420 §12.4.3.3
	// Location: GroupInfo
	// Usage: Help new members join via External Commit
	ExtensionTypeRatchetTree ExtensionType = 0x0002

	// ExtensionTypeRequiredCapabilities - RFC 9420 §11.1
	// Location: GroupContext
	// Usage: Ensure all members support same features
	ExtensionTypeRequiredCapabilities ExtensionType = 0x0003

	// ExtensionTypeExternalPub - RFC 9420 §12.4.3.2
	// Location: GroupInfo
	// Usage: HPKE public key for External Commit
	ExtensionTypeExternalPub ExtensionType = 0x0004

	// ExtensionTypeExternalSenders - RFC 9420 §12.1.8.1
	// Location: GroupContext
	// Usage: List of allowed external senders
	ExtensionTypeExternalSenders ExtensionType = 0x0005

	// ExtensionTypeLastResort - RFC 9420 §16.8
	// Location: KeyPackage
	// Usage: Backup KeyPackage when normal ones exhausted
	ExtensionTypeLastResort ExtensionType = 0x000A
)

type Extensions

type Extensions struct {
	// contains filtered or unexported fields
}

Extensions is a collection of MLS extensions.

Internal structure: ```text ┌─────────────────────────────────────────┐ │ Extensions │ ├─────────────────────────────────────────┤ │ extensions : map[Type]Extension │ // O(1) lookup │ ordered : []Type │ // ascending order └─────────────────────────────────────────┘ ```

RFC 9420 §13.4 requires ascending order by ExtensionType for deterministic GroupContext hashes.

Example

exts := NewExtensions()

if err := exts.Add(Extension{Type: ExtensionTypeExternalSenders, Data: []byte{0x03}}); err != nil {
    return err
}

if err := exts.Add(Extension{Type: ExtensionTypeApplicationID, Data: []byte{0x01}}); err != nil {
    return err
}

// Marshal produces deterministic order: ApplicationID, ExternalSenders data := exts.Marshal()

func NewExtensions

func NewExtensions() *Extensions

NewExtensions creates an empty extension collection.

func UnmarshalExtensions

func UnmarshalExtensions(data []byte) (*Extensions, error)

UnmarshalExtensions parses extensions from TLS format.

func (*Extensions) Add

func (e *Extensions) Add(ext Extension) error

Add adds an extension to the collection.

If the extension type already exists, it is replaced. Extensions are maintained in ascending order per RFC 9420 §13.4.

```text Before: ordered = [0x0001, 0x0003, 0x0005] Add: Type = 0x0002 After: ordered = [0x0001, 0x0002, 0x0003, 0x0005] ```

func (*Extensions) All

func (e *Extensions) All() []Extension

All returns all extensions in ascending order by ExtensionType.

Per RFC 9420 §13.4, extensions MUST be ordered ascending by type.

func (*Extensions) Clone

func (e *Extensions) Clone() *Extensions

Clone creates a deep copy of the Extensions.

func (*Extensions) Get

func (e *Extensions) Get(typ ExtensionType) (Extension, bool)

Get retrieves an extension by type.

Returns the extension and true if it exists.

func (*Extensions) Has

func (e *Extensions) Has(typ ExtensionType) bool

Has checks if an extension type exists.

func (*Extensions) Len

func (e *Extensions) Len() int

Len returns the number of extensions.

func (*Extensions) Marshal

func (e *Extensions) Marshal() []byte

Marshal serializes all extensions to TLS format.

Extensions are serialized in ascending order per RFC 9420 §13.4. This is critical for deterministic GroupContext hashes.

```text ┌─────────────────────────────────────────┐ │ Extensions Encoding │ ├─────────────────────────────────────────┤ │ extensions_length : varint │ │ Extension[] │ │ (ascending by type) │ └─────────────────────────────────────────┘ ```

func (*Extensions) Remove

func (e *Extensions) Remove(typ ExtensionType)

Remove removes an extension by type (no-op if not found).

type ExternalPubExtension

type ExternalPubExtension struct {
	ExternalPub []byte // Encoded HPKE public key (opaque<V>)
}

ExternalPubExtension contains an HPKE public key for External Commit.

Per RFC 9420 §12.4.3.2, this extension appears in GroupInfo and provides the HPKE public key that new members use to encrypt their External Commit.

Structure (RFC 9420 §12.4.3.2) RFC 9420 §12.4.3.2)" aria-label="Go to Structure (RFC 9420 §12.4.3.2)">¶

```text ┌─────────────────────────────────────────┐ │ ExternalPubExtension │ ├─────────────────────────────────────────┤ │ external_pub: HPKEPublicKey │ └─────────────────────────────────────────┘ ```

Location

- GroupInfo: Yes - KeyPackage: No - GroupContext: No

External Commit Flow

```text ┌─────────────────────────────────────────────────────────────┐ │ 1. New member obtains GroupInfo with ExternalPub │ │ 2. Extracts external_pub from extension │ │ 3. Uses HPKE to encrypt Commit with external_pub │ │ 4. Sends External Commit to group │ │ 5. Group processes Commit and welcomes new member │ └─────────────────────────────────────────────────────────────┘ ```

HPKE Public Key Format

Encoded as opaque<V> per RFC 9180. Format depends on KEM:

- DHKEM P-256: 65 bytes (0x04 || X || Y) - DHKEM X25519: 32 bytes

Example

// Create with HPKE public key publicKey := []byte{0x04, ...} // 65 bytes for P-256 ext := NewExternalPubExtension(publicKey)

// Validate

if err := ext.Validate(); err != nil {
    return err
}

// Serialize data := ext.Marshal()

RFC Compliance

RFC 9420 §12.4.3.2: "The ExternalPub extension is used in GroupInfo to provide the information necessary for a new member to join the group via an External Commit."

func FromExternalPubExtension

func FromExternalPubExtension(ext *Extension) (*ExternalPubExtension, error)

FromExternalPubExtension creates an ExternalPubExtension from a generic Extension.

Returns error if Type is not ExtensionTypeExternalPub.

func NewExternalPubExtension

func NewExternalPubExtension(publicKey []byte) *ExternalPubExtension

NewExternalPubExtension creates an ExternalPubExtension.

The HPKE public key must be encoded per RFC 9180.

func UnmarshalExternalPubExtension

func UnmarshalExternalPubExtension(data []byte) (*ExternalPubExtension, error)

UnmarshalExternalPubExtension parses an ExternalPubExtension from TLS.

Reads external_pub as variable-length bytes.

func (*ExternalPubExtension) Equal

Equal compares two ExternalPubExtension instances.

func (*ExternalPubExtension) IsP256

func (e *ExternalPubExtension) IsP256() bool

IsP256 checks if the public key is a P-256 key.

P-256 keys are 65 bytes and start with 0x04 (uncompressed point).

func (*ExternalPubExtension) IsX25519

func (e *ExternalPubExtension) IsX25519() bool

IsX25519 checks if the public key is an X25519 key.

X25519 keys are 32 bytes.

func (*ExternalPubExtension) Len

func (e *ExternalPubExtension) Len() int

Len returns the length of the ExternalPub in bytes.

func (*ExternalPubExtension) Marshal

func (e *ExternalPubExtension) Marshal() []byte

Marshal serializes the extension to TLS format.

```text ┌─────────────────────────────────────────┐ │ external_pub_length: varint │ ├─────────────────────────────────────────┤ │ external_pub: opaque[] │ └─────────────────────────────────────────┘ ```

func (*ExternalPubExtension) String

func (e *ExternalPubExtension) String() string

String returns a human-readable representation.

Shows first few bytes in hex format.

func (*ExternalPubExtension) ToExtension

func (e *ExternalPubExtension) ToExtension() (*Extension, error)

ToExtension converts to a generic Extension.

func (*ExternalPubExtension) Validate

func (e *ExternalPubExtension) Validate() error

Validate validates the extension.

Validation Rules

- ExternalPub must not be nil - ExternalPub must not be empty - P-256 keys: 65 bytes, must start with 0x04

type ExternalSender

type ExternalSender struct {
	Credential *credentials.Credential
	PublicKey  *ecdsa.PublicKey // Signature public key
	// contains filtered or unexported fields
}

ExternalSender represents an external sender allowed to send proposals.

External senders are entities that can send proposals to a group without being full members. A common use case is a delivery service that manages group membership on behalf of its users.

Structure (RFC 9420 §12.1.8.1) RFC 9420 §12.1.8.1)" aria-label="Go to Structure (RFC 9420 §12.1.8.1)">¶

```text

struct {
    SignaturePublicKey credential_public_key;
    opaque credential<V>;
} ExternalSender;

```

Location

- **KeyPackage**: No ❌ - **GroupInfo**: No ❌ - **GroupContext**: Yes ✅

Purpose

External senders allow non-members to send proposals to a group. This is useful for:

  • Delivery services managing group membership
  • Bots or automated systems
  • Gateway services

func ParseSingleExternalSender added in v1.1.0

func ParseSingleExternalSender(data []byte) (*ExternalSender, error)

ParseSingleExternalSender parses an ExternalSendersExtension payload and returns the first sender entry.

Discord voice gateway opcode 25 sends a single ExternalSender entry directly as VL(signature_key) || Credential_inline, without the outer senders<V> wrapper that UnmarshalExternalSendersExtension expects.

func (*ExternalSender) Equal

func (s *ExternalSender) Equal(other *ExternalSender) bool

Equal compares two ExternalSenders for equality.

func (*ExternalSender) Validate

func (s *ExternalSender) Validate() error

Validate validates an ExternalSender.

Validation Rules

- Credential must not be nil - PublicKey must not be nil - Credential must be valid

type ExternalSendersExtension

type ExternalSendersExtension struct {
	Senders []ExternalSender
}

ExternalSendersExtension contains a list of external senders.

Structure (RFC 9420 §12.1.8.1) RFC 9420 §12.1.8.1)" aria-label="Go to Structure (RFC 9420 §12.1.8.1)">¶

```text

struct {
    ExternalSender senders<V>;
} ExternalSendersExtension;

```

Example

// Create extension ext := NewExternalSendersExtension()

// Add sender

sender := ExternalSender{
    Credential: cred,
    PublicKey:  pubKey,
}

if err := ext.AddSender(sender); err != nil {
    return err
}

// Validate

if err := ext.Validate(); err != nil {
    return err
}

// Serialize data := ext.Marshal()

func NewExternalSendersExtension

func NewExternalSendersExtension() *ExternalSendersExtension

NewExternalSendersExtension creates a new ExternalSendersExtension.

func UnmarshalExternalSendersExtension

func UnmarshalExternalSendersExtension(data []byte) (*ExternalSendersExtension, error)

UnmarshalExternalSendersExtension parses an ExternalSendersExtension from TLS format.

Decoding

Reads a vector of ExternalSender structures.

Example

data := []byte{...} // serialized data ext, err := UnmarshalExternalSendersExtension(data)

if err != nil {
    return err
}

// ext.Senders contains parsed senders

func (*ExternalSendersExtension) AddSender

func (e *ExternalSendersExtension) AddSender(sender ExternalSender) error

AddSender adds an external sender to the extension.

func (*ExternalSendersExtension) Equal

Equal compares two ExternalSendersExtensions for equality.

func (*ExternalSendersExtension) ExtensionType

func (e *ExternalSendersExtension) ExtensionType() ExtensionType

ExtensionType returns the type code for this extension.

func (*ExternalSendersExtension) FindSender

FindSender finds an external sender by credential.

func (*ExternalSendersExtension) FindSenderByPublicKey

func (e *ExternalSendersExtension) FindSenderByPublicKey(pubKey *ecdsa.PublicKey) (*ExternalSender, bool)

FindSenderByPublicKey finds an external sender by public key.

func (*ExternalSendersExtension) Len

func (e *ExternalSendersExtension) Len() int

Len returns the number of external senders.

func (*ExternalSendersExtension) Marshal

func (e *ExternalSendersExtension) Marshal() []byte

Marshal serializes the ExternalSendersExtension to TLS format.

Encoding

```text ┌─────────────────────────────────────────────────────────────┐ │ ExternalSendersExtension Encoding │ ├─────────────────────────────────────────────────────────────┤ │ senders<V> │ │ ├─ public_key<V> : opaque (ECDSA uncompressed point) │ │ └─ credential<V> : opaque (Credential encoding) │ └─────────────────────────────────────────────────────────────┘ ```

func (*ExternalSendersExtension) ToExtension

func (e *ExternalSendersExtension) ToExtension() (*Extension, error)

ToExtension converts this to a generic Extension.

func (*ExternalSendersExtension) Validate

func (e *ExternalSendersExtension) Validate() error

Validate validates the ExternalSendersExtension.

type LastResortExtension

type LastResortExtension struct {
}

LastResortExtension marks a KeyPackage for "last resort" use.

Per RFC 9420 §16.8, this extension marks KeyPackages that should only be used when no other KeyPackages are available.

Structure (RFC 9420 §16.8) RFC 9420 §16.8)" aria-label="Go to Structure (RFC 9420 §16.8)">¶

```text ┌─────────────────────────────────────────┐ │ LastResortExtension │ ├─────────────────────────────────────────┤ │ (no data - marker only) │ └─────────────────────────────────────────┘ ```

Location

- KeyPackage: Yes - GroupInfo: No - GroupContext: No

Usage Flow

```text ┌─────────────────────────────────────────────────────────────┐ │ 1. Client generates normal KeyPackages │ │ 2. Client generates "last resort" KeyPackages │ │ 3. Upload both to delivery service │ │ 4. DS uses normal KeyPackages first │ │ 5. If exhausted, uses "last resort" KeyPackages │ └─────────────────────────────────────────────────────────────┘ ```

Example

// Create last resort extension ext := NewLastResortExtension()

// Validate (always succeeds - no data to validate)

if err := ext.Validate(); err != nil {
    return err
}

// Convert to generic extension genericExt, err := ext.ToExtension()

if err != nil {
    return err
}

RFC Compliance

RFC 9420 §16.8: "The LastResort extension is used to mark KeyPackages that should only be used as a last resort, when no other KeyPackages are available."

func FromLastResortExtension

func FromLastResortExtension(ext *Extension) (*LastResortExtension, error)

FromLastResortExtension creates a LastResortExtension from a generic Extension.

Returns error if Type is not ExtensionTypeLastResort.

func NewLastResortExtension

func NewLastResortExtension() *LastResortExtension

NewLastResortExtension creates a LastResortExtension.

func UnmarshalLastResortExtension

func UnmarshalLastResortExtension(_ []byte) (*LastResortExtension, error)

UnmarshalLastResortExtension parses a LastResortExtension from TLS.

Reads empty extension data (no data to parse).

func (*LastResortExtension) Equal

Equal compares two LastResortExtension instances.

All LastResortExtension instances are equal (no data).

func (*LastResortExtension) Len

func (l *LastResortExtension) Len() int

Len returns the length of the extension data (always 0).

func (*LastResortExtension) Marshal

func (l *LastResortExtension) Marshal() []byte

Marshal serializes the extension to TLS format.

Since LastResortExtension has no data, it marshals to empty bytes.

```text ┌─────────────────────────────────────────┐ │ extension_data_length: varint = 0 │ └─────────────────────────────────────────┘ ```

func (*LastResortExtension) String

func (l *LastResortExtension) String() string

String returns a human-readable representation.

func (*LastResortExtension) ToExtension

func (l *LastResortExtension) ToExtension() (*Extension, error)

ToExtension converts to a generic Extension.

func (*LastResortExtension) Validate

func (l *LastResortExtension) Validate() error

Validate validates the extension.

LastResortExtension is always valid (has no data).

type RatchetTreeExtension

type RatchetTreeExtension struct {
	Tree *treesync.RatchetTree
}

RatchetTreeExtension contains the full ratchet tree for a group.

Per RFC 9420 §12.4.3.3, this extension appears in GroupInfo and provides the complete ratchet tree to new members joining via External Commit.

Structure (RFC 9420 §12.4.3.3) RFC 9420 §12.4.3.3)" aria-label="Go to Structure (RFC 9420 §12.4.3.3)">¶

```text

struct {
    opaque ratchet_tree<V>;
        // Vector of nodes:
        // - present: uint8 (0 = empty, 1 = present)
        // - if present:
        //   - node_type: uint8 (1 = leaf, 2 = parent)
        //   - type-specific data
} RatchetTreeExtension;

```

Location

- GroupInfo: Yes - KeyPackage: No - GroupContext: No

Purpose

New members joining via External Commit need the tree structure to:

  1. Verify existing leaves
  2. Calculate path secrets
  3. Encrypt their Commit correctly

Example

// Create extension with tree tree := getRatchetTree() ext := NewRatchetTreeExtension(tree)

// Validate

if err := ext.Validate(); err != nil {
    return err
}

// Serialize data := ext.Marshal()

Parent Hash Validation

Parent hashes ensure tree integrity. Each parent node contains a hash of its children, creating a chain of tgo from root to leaves.

RFC Compliance

RFC 9420 §12.4.3.3: "The RatchetTree extension provides the full public state of the ratchet tree to allow new members to initialize their state."

func FromExtension

func FromExtension(ext *Extension) (*RatchetTreeExtension, error)

FromExtension creates a RatchetTreeExtension from a generic Extension.

func NewRatchetTreeExtension

func NewRatchetTreeExtension(tree *treesync.RatchetTree) *RatchetTreeExtension

NewRatchetTreeExtension creates a RatchetTreeExtension.

func UnmarshalRatchetTreeExtension

func UnmarshalRatchetTreeExtension(data []byte) (*RatchetTreeExtension, error)

UnmarshalRatchetTreeExtension parses a RatchetTreeExtension from TLS.

Decodes the ratchet_tree vector per RFC 9420 §7.

func (*RatchetTreeExtension) Equal

Equal compares two RatchetTreeExtension instances.

Compares tree hashes rather than full structure for efficiency.

func (*RatchetTreeExtension) ExtensionType

func (r *RatchetTreeExtension) ExtensionType() ExtensionType

ExtensionType returns the type code for this extension.

func (*RatchetTreeExtension) GetTree

GetTree returns the ratchet tree.

func (*RatchetTreeExtension) Marshal

func (r *RatchetTreeExtension) Marshal() []byte

Marshal serializes the RatchetTreeExtension to TLS format.

Tree is encoded as a vector of nodes per RFC 9420 §7.

```text ┌─────────────────────────────────────────────────────────────┐ │ RatchetTreeExtension Encoding │ ├─────────────────────────────────────────────────────────────┤ │ ratchet_tree<V> │ │ └─ Node[] │ │ ├─ present: uint8 (0 = empty, 1 = present) │ │ └─ if present: │ │ ├─ node_type: uint8 (1 = leaf, 2 = parent) │ │ ├─ LeafNode or ParentNode data │ └─────────────────────────────────────────────────────────────┘ ```

func (*RatchetTreeExtension) SetTree

func (r *RatchetTreeExtension) SetTree(tree *treesync.RatchetTree)

SetTree sets the ratchet tree.

func (*RatchetTreeExtension) ToExtension

func (r *RatchetTreeExtension) ToExtension() (*Extension, error)

ToExtension converts to a generic Extension.

func (*RatchetTreeExtension) Validate

func (r *RatchetTreeExtension) Validate() error

Validate validates the RatchetTreeExtension.

type RequiredCapabilitiesExtension

type RequiredCapabilitiesExtension struct {
	ProtocolVersions []uint16                     // Protocol versions required
	CipherSuites     []uint16                     // Cipher suites required
	Extensions       []ExtensionType              // Extensions that must be supported
	Proposals        []uint16                     // Proposal types that must be supported
	Credentials      []credentials.CredentialType // Credential types required
}

RequiredCapabilitiesExtension specifies capabilities required for group members.

This extension is used in GroupContext to ensure all members support the required features before joining the group.

Structure (RFC 9420 §11.1) RFC 9420 §11.1)" aria-label="Go to Structure (RFC 9420 §11.1)">¶

```text

struct {
    ProtocolVersion protocol_versions<V>;
    CipherSuite cipher_suites<V>;
    ExtensionType extensions<V>;
    ProposalType proposals<V>;
    CredentialType credentials<V>;
} RequiredCapabilities;

```

Location

- **KeyPackage**: No ❌ - **GroupInfo**: No ❌ - **GroupContext**: Yes ✅

Purpose

RequiredCapabilities ensures all group members support:

  • Specific protocol versions
  • Required cipher suites
  • Mandatory extensions
  • Supported proposal types
  • Accepted credential types

Example

// Create extension req := NewRequiredCapabilities() req.AddProtocolVersion(0x01) // MLS 1.0 req.AddCipherSuite(0x0002) // MLS_128_DHKEMP256... req.AddExtension(ExtensionTypeExternalSenders)

// Validate

if err := req.Validate(); err != nil {
    return err
}

// Serialize data := req.Marshal()

// Deserialize req2, err := UnmarshalRequiredCapabilities(data)

func NewRequiredCapabilities

func NewRequiredCapabilities() *RequiredCapabilitiesExtension

NewRequiredCapabilities creates a new RequiredCapabilities extension.

func UnmarshalRequiredCapabilities

func UnmarshalRequiredCapabilities(data []byte) (*RequiredCapabilitiesExtension, error)

UnmarshalRequiredCapabilities parses a RequiredCapabilities extension from TLS format.

Decoding

Reads five vectors: protocol_versions, cipher_suites, extensions, proposals, credentials.

Example

data := []byte{...} // serialized data req, err := UnmarshalRequiredCapabilities(data)

if err != nil {
    return err
}

// req.ProtocolVersions contains parsed versions

func (*RequiredCapabilitiesExtension) AddCipherSuite

func (r *RequiredCapabilitiesExtension) AddCipherSuite(cs uint16)

AddCipherSuite adds a required cipher suite.

func (*RequiredCapabilitiesExtension) AddCredential

AddCredential adds a required credential type.

func (*RequiredCapabilitiesExtension) AddExtension

func (r *RequiredCapabilitiesExtension) AddExtension(ext ExtensionType)

AddExtension adds a required extension type.

func (*RequiredCapabilitiesExtension) AddProposal

func (r *RequiredCapabilitiesExtension) AddProposal(proposal uint16)

AddProposal adds a required proposal type.

func (*RequiredCapabilitiesExtension) AddProtocolVersion

func (r *RequiredCapabilitiesExtension) AddProtocolVersion(version uint16)

AddProtocolVersion adds a required protocol version.

func (*RequiredCapabilitiesExtension) Equal

Equal compares two RequiredCapabilities extensions for equality.

func (*RequiredCapabilitiesExtension) HasCipherSuite

func (r *RequiredCapabilitiesExtension) HasCipherSuite(cs uint16) bool

HasCipherSuite checks if a cipher suite is required.

func (*RequiredCapabilitiesExtension) HasCredential

HasCredential checks if a credential type is required.

func (*RequiredCapabilitiesExtension) HasExtension

func (r *RequiredCapabilitiesExtension) HasExtension(ext ExtensionType) bool

HasExtension checks if an extension type is required.

func (*RequiredCapabilitiesExtension) HasProtocolVersion

func (r *RequiredCapabilitiesExtension) HasProtocolVersion(version uint16) bool

HasProtocolVersion checks if a protocol version is required.

func (*RequiredCapabilitiesExtension) IsEmpty

func (r *RequiredCapabilitiesExtension) IsEmpty() bool

IsEmpty returns true if the extension has no required capabilities.

func (*RequiredCapabilitiesExtension) Marshal

func (r *RequiredCapabilitiesExtension) Marshal() []byte

Marshal serializes the RequiredCapabilities extension to TLS format.

Encoding (RFC 9420 §11.1) RFC 9420 §11.1)" aria-label="Go to Encoding (RFC 9420 §11.1)">¶

```text ┌─────────────────────────────────────────────────────────────┐ │ RequiredCapabilities Encoding │ ├─────────────────────────────────────────────────────────────┤ │ protocol_versions<V> : opaque<V> │ │ cipher_suites<V> : opaque<V> │ │ extensions<V> : opaque<V> │ │ proposals<V> : opaque<V> │ │ credentials<V> : opaque<V> │ └─────────────────────────────────────────────────────────────┘ ```

func (*RequiredCapabilitiesExtension) SupportsAll

SupportsAll checks if this extension supports all capabilities from another. Returns true if all capabilities in other are present in this.

func (*RequiredCapabilitiesExtension) Validate

func (r *RequiredCapabilitiesExtension) Validate() error

Validate validates the RequiredCapabilities extension.

Validation Rules

- protocol_versions must not be empty - cipher_suites must not be empty - Protocol version 0 is invalid - Cipher suite 0 is invalid

Jump to

Keyboard shortcuts

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