🚀. Socket Launch Week Day 3:Socket Firewall Now Blocks Malicious VS Code and Open VSX Extensions.Learn more
Sign In

ts-mls

Package Overview
Dependencies
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ts-mls

[![CI](https://github.com/LukaJCB/ts-mls/actions/workflows/ci.yml/badge.svg)](https://github.com/LukaJCB/ts-mls/actions/workflows/ci.yml) [![npm version](https://badge.fury.io/js/ts-mls.svg)](https://badge.fury.io/js/ts-mls) [![Coverage Status](https://co

latest
Source
npmnpm
Version
1.6.2
Version published
Weekly downloads
4.5K
79.84%
Maintainers
1
Weekly downloads
 
Created
Source

ts-mls: A TypeScript MLS (Messaging Layer Security - RFC 9420) implementation

CI npm version Coverage Status

Typescript implementation of Messaging Layer Security (RFC 9420, MLS).

This project aims to be a full implementation of RFC 9420 and focuses on immutability and type safety. It is suitable for browsers, Node.js, or serverless environments and supports the recently standardized Post Quantum public-key algorithms (FIPS-203, FIPS-204) as well as the X-Wing hybrid KEM combining X25519 and ML-KEM.

Installation

Node.js Requirement: Node.js 20+ is required when using this library in Node.js environments.

# npm
npm install ts-mls

# yarn
yarn add ts-mls

# pnpm
pnpm add ts-mls

This project currently only has a single dependency, @hpke/core. However, to support different Ciphersuites, you may need to install other libraries. As an example, to use the MLS_128_DHKEMP256_AES128GCM_SHA256_P256 Ciphersuite, you would also have to install @noble/curves:

# npm
npm install @noble/curves

# yarn
yarn add @noble/curves

# pnpm
pnpm add @noble/curves

Please refer to the subsequent table to understand which additional dependencies are required to install for each Ciphersuite.

Supported Ciphersuites

The following cipher suites are supported:

KEMAEADKDFHashSignatureNameIDDependencies
DHKEM-X25519-HKDF-SHA256AES128GCMHKDF-SHA256SHA-256Ed25519MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed255191
DHKEM-P256-HKDF-SHA256AES128GCMHKDF-SHA256SHA-256P256MLS_128_DHKEMP256_AES128GCM_SHA256_P2562@noble/curves
DHKEM-X25519-HKDF-SHA256CHACHA20POLY1305HKDF-SHA256SHA-256Ed25519MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed255193@hpke/chacha20poly1305
DHKEM-X448-HKDF-SHA512AES256GCMHKDF-SHA512SHA-512Ed448MLS_256_DHKEMX448_AES256GCM_SHA512_Ed4484@noble/curves, @hpke/dhkem-x448
DHKEM-P521-HKDF-SHA512AES256GCMHKDF-SHA512SHA-512P521MLS_256_DHKEMP521_AES256GCM_SHA512_P5215@noble/curves
DHKEM-X448-HKDF-SHA512CHACHA20POLY1305HKDF-SHA512SHA-512Ed448MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed4486@hpke/chacha20poly1305, @noble/curves, @hpke/dhkem-x448
DHKEM-P384-HKDF-SHA384AES256GCMHKDF-SHA384SHA-384P384MLS_256_DHKEMP384_AES256GCM_SHA384_P3847@noble/curves
ML-KEM-512AES256GCMHKDF-SHA256SHA-256Ed25519MLS_128_MLKEM512_AES128GCM_SHA256_Ed2551977@hpke/ml-kem
ML-KEM-512CHACHA20POLY1305HKDF-SHA256SHA-256Ed25519MLS_128_MLKEM512_CHACHA20POLY1305_SHA256_Ed2551978@hpke/ml-kem, @hpke/chacha20poly1305
ML-KEM-768AES256GCMHKDF-SHA384SHA-384Ed25519MLS_256_MLKEM768_AES256GCM_SHA384_Ed2551979@hpke/ml-kem
ML-KEM-768CHACHA20POLY1305HKDF-SHA384SHA-384Ed25519MLS_256_MLKEM768_CHACHA20POLY1305_SHA384_Ed2551980@hpke/ml-kem, @hpke/chacha20poly1305
ML-KEM-1024AES256GCMHKDF-SHA512SHA-512Ed25519MLS_256_MLKEM1024_AES256GCM_SHA512_Ed2551981@hpke/ml-kem
ML-KEM-1024CHACHA20POLY1305HKDF-SHA512SHA-512Ed25519MLS_256_MLKEM1024_CHACHA20POLY1305_SHA512_Ed2551982@hpke/ml-kem, @hpke/chacha20poly1305
X-WingAES256GCMHKDF-SHA512SHA-512Ed25519MLS_256_XWING_AES256GCM_SHA512_Ed2551983@hpke/hybridkem-x-wing
X-WingCHACHA20POLY1305HKDF-SHA512SHA-512Ed25519MLS_256_XWING_CHACHA20POLY1305_SHA512_Ed2551984@hpke/hybridkem-x-wing, @hpke/chacha20poly1305
ML-KEM-1024AES256GCMHKDF-SHA512SHA-512ML-DSA-87MLS_256_MLKEM1024_AES256GCM_SHA512_MLDSA8785@hpke/ml-kem, @noble/post-quantum
ML-KEM-1024CHACHA20POLY1305HKDF-SHA512SHA-512ML-DSA-87MLS_256_MLKEM1024_CHACHA20POLY1305_SHA512_MLDSA8786@hpke/ml-kem, @hpke/chacha20poly1305, @noble/post-quantum
X-WingAES256GCMHKDF-SHA512SHA-512ML-DSA-87MLS_256_XWING_AES256GCM_SHA512_MLDSA8787@hpke/hybridkem-x-wing, @noble/post-quantum
X-WingCHACHA20POLY1305HKDF-SHA512SHA-512ML-DSA-87MLS_256_XWING_CHACHA20POLY1305_SHA512_MLDSA8788@hpke/hybridkem-x-wing, @hpke/chacha20poly1305, @noble/post-quantum

⚠️ Security Disclaimer

This library has not undergone a formal security audit. While care has been taken to implement the MLS protocol correctly and securely, it may contain undiscovered vulnerabilities. If you plan to use this library in a production or security-critical context, proceed with caution and consider conducting an independent security review.

Basic Usage

import {
  createApplicationMessage,
  createCommit,
  createGroup,
  joinGroup,
  processPrivateMessage,
  getCiphersuiteImpl,
  getCiphersuiteFromName,
  Credential,
  defaultCapabilities,
  defaultLifetime,
  emptyPskIndex,
  generateKeyPackage,
  encodeMlsMessage,
  decodeMlsMessage,
  Proposal,
  zeroOutUint8Array,
} from "ts-mls"

const impl = await getCiphersuiteImpl(getCiphersuiteFromName("MLS_256_XWING_AES256GCM_SHA512_Ed25519"))

// alice generates her key package
const aliceCredential: Credential = { credentialType: "basic", identity: new TextEncoder().encode("alice") }
const alice = await generateKeyPackage(aliceCredential, defaultCapabilities(), defaultLifetime, [], impl)

const groupId = new TextEncoder().encode("group1")

// alice creates a new group
let aliceGroup = await createGroup(groupId, alice.publicPackage, alice.privatePackage, [], impl)

// bob generates his key package
const bobCredential: Credential = { credentialType: "basic", identity: new TextEncoder().encode("bob") }
const bob = await generateKeyPackage(bobCredential, defaultCapabilities(), defaultLifetime, [], impl)

// bob sends keyPackage to alice
const keyPackageMessage = encodeMlsMessage({
  keyPackage: bob.publicPackage,
  wireformat: "mls_key_package",
  version: "mls10",
})

// alice decodes bob's keyPackage
const decodedKeyPackage = decodeMlsMessage(keyPackageMessage, 0)![0]

if (decodedKeyPackage.wireformat !== "mls_key_package") throw new Error("Expected key package")

// alice creates proposal to add bob
const addBobProposal: Proposal = {
  proposalType: "add",
  add: {
    keyPackage: decodedKeyPackage.keyPackage,
  },
}

// alice commits
const commitResult = await createCommit({ state: aliceGroup, cipherSuite: impl }, { extraProposals: [addBobProposal] })

aliceGroup = commitResult.newState

// alice deletes the keys used to encrypt the commit message
commitResult.consumed.forEach(zeroOutUint8Array)

// alice sends welcome message to bob
const encodedWelcome = encodeMlsMessage({
  welcome: commitResult.welcome!,
  wireformat: "mls_welcome",
  version: "mls10",
})

// bob decodes the welcome message
const decodedWelcome = decodeMlsMessage(encodedWelcome, 0)![0]

if (decodedWelcome.wireformat !== "mls_welcome") throw new Error("Expected welcome")

// bob creates his own group state
let bobGroup = await joinGroup(
  decodedWelcome.welcome,
  bob.publicPackage,
  bob.privatePackage,
  emptyPskIndex,
  impl,
  aliceGroup.ratchetTree,
)

const messageToBob = new TextEncoder().encode("Hello bob!")

// alice creates a message to the group
const aliceCreateMessageResult = await createApplicationMessage(aliceGroup, messageToBob, impl)

aliceGroup = aliceCreateMessageResult.newState

// alice deletes the keys used to encrypt the application message
aliceCreateMessageResult.consumed.forEach(zeroOutUint8Array)

// alice sends the message to bob
const encodedPrivateMessageAlice = encodeMlsMessage({
  privateMessage: aliceCreateMessageResult.privateMessage,
  wireformat: "mls_private_message",
  version: "mls10",
})

// bob decodes the message
const decodedPrivateMessageAlice = decodeMlsMessage(encodedPrivateMessageAlice, 0)![0]

if (decodedPrivateMessageAlice.wireformat !== "mls_private_message") throw new Error("Expected private message")

// bob receives the message
const bobProcessMessageResult = await processPrivateMessage(
  bobGroup,
  decodedPrivateMessageAlice.privateMessage,
  emptyPskIndex,
  impl,
)

bobGroup = bobProcessMessageResult.newState

if (bobProcessMessageResult.kind === "newState") throw new Error("Expected application message")

// bob deletes the keys used to decrypt the application message
bobProcessMessageResult.consumed.forEach(zeroOutUint8Array)

console.log(bobProcessMessageResult.message)

Documentation

Please visit the /docs directory for further documentation on different scenarios.

Contributing

We welcome contributions! Please read our CONTRIBUTING.md for guidelines on how to set up your environment, run checks, and submit changes.

License

MIT

Keywords

mls

FAQs

Package last updated on 07 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts