TypeScript implementation of X3DH, as described in Going Bark: A Furry's Guide to End-to-End Encryption.
The install size is kind of large because this package includes a pre-bundled sodium
.
This is a fork of soatok/rawr-x3dh.
This library implements the Extended Triple Diffie-Hellman key exchange, with a few minor tweaks:
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.
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.
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.