libomemo.js is a TypeScript implementation of the OMEMO Multi-End Message and Object Encryption protocol for XMPP. It provides ratcheting forward secrecy for synchronous and asynchronous messaging environments, enabling secure multi-device encrypted communication.
A fork of libsignal-protocol-javascript by Open Whisper Systems, adapted for the XMPP OMEMO specification.
- Features
- Installation
- Requirements
- Quick Start
- API Reference
- Building from Source
- Testing
- Contributing
- License
- Double Ratchet Protocol — Forward secrecy and post-compromise security
- Multi-device support — Encrypt messages for multiple devices simultaneously
- PreKey management — Asynchronous session establishment via PreKey bundles
- TypeScript native — Full type definitions included
- Browser & Node.js compatible — ESM, UMD, and CommonJS support
- Curve25519 — High-performance elliptic curve cryptography (compiled via Emscripten)
npm install libomemo.jsOr include the UMD build directly in your webpage:
<script src="dist/libomemo.umd.js"></script>This library requires a modern JavaScript environment with support for:
ArrayBufferTypedArrayPromiseWebCryptowith:- AES-CBC
- HMAC SHA-256
These are available in all modern browsers and Node.js 15+.
// ES Modules
import { KeyHelper, SessionBuilder, SessionCipher, OMEMOAddress } from "libomemo.js";
// CommonJS
const { KeyHelper, SessionBuilder, SessionCipher, OMEMOAddress } = require("libomemo.js");
// Browser (UMD)
const { KeyHelper, SessionBuilder, SessionCipher, OMEMOAddress } = libomemo;Generate identity keys, registration ID, and PreKeys at install time:
const registrationId = KeyHelper.generateRegistrationId();
// Store registrationId somewhere durable and safe.
const identityKeyPair = await KeyHelper.generateIdentityKeyPair();
// Store identityKeyPair somewhere durable and safe.
const preKey = await KeyHelper.generatePreKey(keyId);
store.storePreKey(preKey.keyId, preKey.keyPair);
const signedPreKey = await KeyHelper.generateSignedPreKey(identityKeyPair, keyId);
store.storeSignedPreKey(signedPreKey.keyId, signedPreKey.keyPair);
// Register preKeys and signedPreKey with the XMPP serverImplement a storage interface for managing keys and session state (see src/session/store.ts for an example), then establish sessions:
const store = new MyOMEMOProtocolStore();
const address = new OMEMOAddress(recipientId, deviceId);
const sessionBuilder = new SessionBuilder(store, address);
// Process a PreKey bundle from the server
try {
await sessionBuilder.processPreKey({
registrationId: <Number>,
identityKey: <ArrayBuffer>,
signedPreKey: {
keyId: <Number>,
publicKey: <ArrayBuffer>,
signature: <ArrayBuffer>
},
preKey: {
keyId: <Number>,
publicKey: <ArrayBuffer>
}
});
// Session established — ready to encrypt
} catch (error) {
// Handle identity key conflict
}const sessionCipher = new SessionCipher(store, address);
const ciphertext = await sessionCipher.encrypt("Hello world");
// ciphertext -> { type: <Number>, body: <string> }const sessionCipher = new SessionCipher(store, address);
// Decrypt a PreKey message (establishes session if needed)
try {
const plaintext = await sessionCipher.decryptPreKeyWhisperMessage(ciphertext);
} catch (error) {
// Handle identity key conflict
}
// Decrypt a regular message using existing session
const plaintext = await sessionCipher.decryptWhisperMessage(ciphertext);Key generation utilities for OMEMO protocol setup.
| Method | Description |
|---|---|
generateRegistrationId() |
Generate a unique registration ID |
generateIdentityKeyPair() |
Generate an identity key pair |
generatePreKey(keyId) |
Generate an unsigned PreKey |
generateSignedPreKey(identityKeyPair, keyId) |
Generate a signed PreKey |
Handles session establishment with remote recipients.
| Method | Description |
|---|---|
processPreKey(preKeyBundle) |
Build a session from a PreKey bundle |
Encrypts and decrypts messages for established sessions.
| Method | Description |
|---|---|
encrypt(plaintext) |
Encrypt a message |
decryptPreKeyWhisperMessage(ciphertext) |
Decrypt and establish session |
decryptWhisperMessage(ciphertext) |
Decrypt using existing session |
Represents a recipient address (JID + device ID tuple).
const address = new OMEMOAddress(recipientId, deviceId);Low-level cryptographic functions for advanced use cases:
getRandomBytes, encrypt, decrypt, sign, hash, HKDF, verifyMAC, createKeyPair, ECDHE, Ed25519Sign, Ed25519Verify
- Node.js 18+
- Emscripten (for compiling native Curve25519 code)
# Install dependencies
npm install
# Compile native Curve25519 code (requires Emscripten)
npm run compile
# Build TypeScript distribution
npm run dist
# Full build (compile + dist)
npm run build
# Watch mode for development
npm run dev# Run all tests (Node.js + Headless Chrome)
npm test
# Run tests in Chrome browser
npm run test:browser
# Run tests in headless Chrome only
npm run test:headless
# Run Node.js tests only
npm run test:nodeContributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests (
npm test) and linting (npm run lint) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please ensure all new functionality includes tests and follows existing code conventions.
Copyright 2015-2018 Open Whisper Systems Copyright 2022-2026 JC Brand
Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html