Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@bicycle-codes/x3dh

bicycle-codes26ISC0.2.2TypeScript support: included
X3DH, Key agreement, cryptography, Diffie-Hellman

readme

x3dh

tests types module semantic versioning install size License: ISC

TypeScript implementation of X3DH, as described in Going Bark: A Furry's Guide to End-to-End Encryption.

Support soatok on Patreon

The install size is kind of large because this package includes a pre-bundled sodium.

fork

This is a fork of soatok/rawr-x3dh.

Contents

What's This?

This library implements the Extended Triple Diffie-Hellman key exchange, with a few minor tweaks:

  1. Identity keys are Ed25519 public keys, not X25519 public keys. See this for an explanation.
  2. Encryption/decryption and KDF implementations are pluggable (assuming you implement the interface I provide), so you aren't married to HKDF or a particular cipher. (Although I recommend hard-coding it to your application!)

Installation

npm i -S @bicycle-codes/x3dh

If you're working server-side, you'll also want to install sodium-native, so that sodium-plus will run faster.

If you're working in a browser or browser extension, don't install sodium-native.

Usage

First, you'll want to import the X3DH class from our module.

import { X3DH } from '@bicycle-codes/x3dh'

const x3dh = new X3DH()

Note: You can pass some classes to the constructor to replace my algorithm implementations for your own.

import { X3DH } from '@bicycle-codes/x3dh'

const x3dh = new X3DH(
    sessionKeyManager, /* SessionKeyManagerInterface */
    identityKeyManager, /* IdentityKeyManagerInterface */
    symmetricEncryptionHandler, /* SymmetricEncryptionInterface */
    keyDerivationFunction /* KeyDerivationFunction */
)

Once your X3DH object's instantiated, you will be able to initialize handshakes either as a sender or as a recipient. Then you will be able to encrypt additional messages on either side, and the encryption key shall ratchet forward.

const firstEncrypted = await x3dh.initSend(
    'recipient@server2',
    serverApiCallFunc,
    firstMessage
); 

The serverApiCallFunc parameter should be a function that sends a request to the server to obtain the identity key, signed pre-key, and optional one-time key for the handshake.

See the definition of the InitClientFunction type in lib/index.ts.

Once this has completed, you can call encryptNext() multiple times to append messages to send.

const nextEncrypted = await x3dh.encryptNext(
    'recipient@server2',
    'This is a follow-up message UwU'
);

On the other side, your communication partner will use the following feature.

const [sender, firstMessage] = await x3dh.initRecv(senderInfo);
const nextMessage = await x3dh.decryptNext(sender, nextEncrypted);

Note: initRecv() will always return the sender identity (a string) and the message (a Buffer that can be converted to a string). The sender identity should be usable for decryptNext() calls.

However, that doesn't mean it's trustworthy! This library only implements the X3DH pattern. It doesn't implement the Gossamer integration.

Should I Use This?

Don't use it in production until version 1.0.0 has been tagged. The API can break at any moment until that happens (especially if I decide I hate the default key management classes I wrote).

However, feel free to test and play with it.

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

Generated by auto-changelog.

v0.2.2

Commits

v0.2.1 - 2025-01-04

Merged

  • explicity defining string array type for the encodedbundle #5

Commits

v0.2.0 - 2020-11-30

Merged

  • Icon #3
  • Significantly Improve the README #2

Commits

  • Improve README 7ab5144
  • Export missing types. Version 0.2.0 19ae5c5
  • Address concern trolls about professionalism 6b9899d

v0.1.0 - 2020-11-23

Commits