Message Validation

OpenMLS implements a variety of syntactical and semantical checks, both when parsing and processing incoming commits and when creating own commits.

Validation steps

Validation is enforced using Rust's type system. The chain of functions used to process incoming messages is described in the chapter on Processing incoming messages, where each function takes a distinct type as input and produces a distinct type as output, thus ensuring that the individual steps can't be skipped. We now detail which step performs which validation checks.

Syntax validation

Incoming messages in the shape of a byte string can only be deserialized into a MlsMessageIn struct. Deserialization ensures that the message is a syntactically correct MLS message, i.e., either a PublicMessage or a PrivateMessage. Further syntax checks are applied for the latter case once the message is decrypted.

Semantic validation

Every function in the processing chain performs several semantic validation steps. For a list of these steps, see below. In the following, we will give a brief overview of which function performs which category of checks.

Wire format policy and basic message consistency validation

MlsMessageIn struct instances can be passed into the .parse_message() function of the MlsGroup API, which validates that the message conforms to the group's wire format policy. The function also performs several basic semantic validation steps, such as consistency of Group id, Epoch, and Sender data between message and group (ValSem002-ValSem007). It also checks if the sender type (e.g., Member, NewMember, etc.) matches the type of the message (ValSem112), as well as the presence of a path in case of an External Commit (ValSem246).

.parse_message() then returns an UnverifiedMessage struct instance, which can in turn be used as input for .process_unverified_message().

Message-specific semantic validation

.process_unverified_message() performs all other semantic validation steps. In particular, it ensures that ...

  • the message is correctly authenticated by a signature (ValSem010), membership tag (ValSem008), and confirmation tag (ValSem205),
  • proposals are valid relative to one another and the current group state, e.g., no redundant adds or removes targeting non-members (ValSem101-ValSem112),
  • commits are valid relative to the group state and the proposals it covers (ValSem200-ValSem205) and
  • external commits are valid according to the spec (ValSem240-ValSem245, ValSem247 is checked as part of ValSem010).

After performing these steps, messages are returned as ProcessedMessages that the application can either use immediately (application messages) or inspect and decide if they find them valid according to the application's policy (proposals and commits). Proposals can then be stored in the proposal queue via .store_pending_proposal(), while commits can be merged into the group state via .merge_staged_commit().

Detailed list of validation steps

The following is a list of the individual semantic validation steps performed by OpenMLS, including the location of the tests.

Semantic validation of message framing

ValidationStepDescriptionImplementedTestedTest File
ValSem002Group idopenmls/src/group/tests/
ValSem004Sender: Member: check the sender points to a non-blank leafopenmls/src/group/tests/
ValSem005Application messages must use ciphertextopenmls/src/group/tests/
ValSem006Ciphertext: decryption needs to workopenmls/src/group/tests/
ValSem007Membership tag presenceopenmls/src/group/tests/
ValSem008Membership tag verificationopenmls/src/group/tests/
ValSem009Confirmation tag presenceopenmls/src/group/tests/
ValSem010Signature verificationopenmls/src/group/tests/
ValSem011PrivateMessageContent padding must be all-zeroopenmls/src/group/tests/

Semantic validation of proposals covered by a Commit

ValidationStepDescriptionImplementedTestedTest File
ValSem101Add Proposal: Signature public key in proposals must be unique among proposals & membersopenmls/src/group/tests/
ValSem102Add Proposal: Init key in proposals must be unique among proposalsopenmls/src/group/tests/
ValSem103Add Proposal: Encryption key in proposals must be unique among proposals & membersopenmls/src/group/tests/
ValSem104Add Proposal: Init key and encryption key must be differentopenmls/src/group/tests/
ValSem105Add Proposal: Ciphersuite & protocol version must match the groupopenmls/src/group/tests/
ValSem106Add Proposal: required capabilitiesopenmls/src/group/tests/
ValSem107Remove Proposal: Removed member must be unique among proposalsopenmls/src/group/tests/
ValSem108Remove Proposal: Removed member must be an existing group memberopenmls/src/group/tests/
ValSem109Update Proposal: required capabilitiesopenmls/src/group/tests/
ValSem110Update Proposal: Encryption key must be unique among proposals & membersopenmls/src/group/tests/
ValSem111Update Proposal: The sender of a full Commit must not include own update proposalsopenmls/src/group/tests/
ValSem112Update Proposal: The sender of a standalone update proposal must be of type memberopenmls/src/group/tests/

Commit message validation

ValidationStepDescriptionImplementedTestedTest File
ValSem200Commit must not cover inline self Remove proposalopenmls/src/group/tests/
ValSem201Path must be present, if at least one proposal requires a pathopenmls/src/group/tests/
ValSem202Path must be the right lengthopenmls/src/group/tests/
ValSem203Path secrets must decrypt correctlyopenmls/src/group/tests/
ValSem204Public keys from Path must be verified and match the private keys from the direct pathopenmls/src/group/tests/
ValSem205Confirmation tag must be successfully verifiedopenmls/src/group/tests/
ValSem206Path leaf node encryption key must be unique among proposals & membersopenmls/src/group/tests/
ValSem207Path encryption keys must be unique among proposals & membersopenmls/src/group/tests/

External Commit message validation

ValidationStepDescriptionImplementedTestedTest File
ValSem240External Commit must cover at least one inline ExternalInit proposalopenmls/src/group/tests/
ValSem241External Commit must cover at most one inline ExternalInit proposalopenmls/src/group/tests/
ValSem242External Commit must only cover inline proposal in allowlist (ExternalInit, Remove, PreSharedKey)openmls/src/group/tests/
ValSem243Identity of inline Remove proposal target and external committer must be the sameopenmls/src/group/tests/
ValSem244External Commit must not include any proposals by referenceopenmls/src/group/tests/
ValSem245External Commit must contain a pathopenmls/src/group/tests/
ValSem246External Commit signature must be verified using the credential in the path KeyPackageopenmls/src/group/tests/

Ratchet tree validation

ValidationStepDescriptionImplementedTestedTest File
ValSem300Exported ratchet trees must not have trailing blank nodes.YesYesopenmls/src/treesync/

PSK Validation

ValidationStepDescriptionImplementedTestedTest File
ValSem400The application SHOULD specify an upper limit on the number of past epochs for which the resumption_psk may be stored.
ValSem401The nonce of a PreSharedKeyID must have length KDF.Nh.openmls/src/group/tests/
ValSem402PSK in proposal must be of type Resumption (with usage Application) or External.openmls/src/group/tests/
ValSem403Proposal list must not contain multiple PreSharedKey proposals that reference the same PreSharedKeyID.