Create end-to-end encrypted WebSocket channels!
This package implements secret negotation via Extended Triple Diffie-Hellman (X3DH), allowing two peers to establish a WebSocket channel encrypted end-to-end with the Double Ratchet Algorithm and header encryption.
WARNING: this library has NOT received a formal security audit, use at your own risk.
npm i triple-double
npm run cert
This generates a private key and self-signed certificate and writes them to private/
.
Note: the client will need the certificate to connect to the server.
[host=] [port=] npm start
The following code snippets assume top-level async/await
for readability purposes.
A secure, out-of-band channel is needed to communicate public keys and session IDs between peers.
Find the complete code in ./example.js
and run it with npm run example
.
Alice only has to perform this step if:
- She hasn't published her bundle yet
- She runs out of one-time prekeys
- She wants to publish a new signed prekey
We'll assume she hasn't published her bundle yet.
See here for more details.
// Alice's code
const fs = require('fs')
const { Client } = require('triple-double')
const ca = fs.readFileSync('/path/to/cert')
const host = '1.2.3.4'
const port = 8888
const alice = new Client({ ca, host, port })
const pubKey = await alice.publishBundle()
// Send public key to Bob out-of-band
See here for more details.
// Bob's code
const fs = require('fs')
const { Client } = require('triple-double')
const ca = fs.readFileSync('/path/to/cert')
const host = '1.2.3.4'
const port = 8888
const bob = new Client({ ca, host, port })
const peerKey = Buffer.from(/* alice's public key */)
const plaintext = 'intial plaintext'
const sid = await bob.sendInitMessage(peerKey, plaintext)
// Send session ID to alice out-of-band
See here for more details.
// Alice's code continued
const plaintext = await alice.recvInitMessage('<session ID from Bob>')
At this point, the peers can establish a secure WebSocket channel.
This operation won't complete until both peers are connected.
// Alice's code continued
await alice.connect('<session ID>')
// Bob's code continued
await bob.connect('<session ID>')
After connecting, the peers can send messages to each other!
These messages are encrypted with Double Ratchet (including header encryption).
// Alice's code continued
alice.on('message', ({ sid, plaintext }) => {
if (sid === '<session ID>') {
// handle Bob's plaintext
}
})
alice.send('<session ID>', 'hello "bob"')
// Bob's code continued
bob.on('message', ({ sid, plaintext }) => {
if (sid === '<session ID>') {
// handle Alice's plaintext
}
})
bob.send('<session ID>', 'hello "alice"')
Alice and Bob can establish secure channels to other peers, if they so choose.
Once a peer calls disconnect()
with the session ID, the channel closes and both peers receive "disconnect" events.
// Alice's code continued
alice.on('disconnect', sid => {
if (sid === '<session ID>') {
// Disconnected from Bob
}
})
alice.disconnect('<session ID>')
// Bob's code continued
bob.on('disconnect', sid => {
if (sid === '<session ID>') {
// Disconnected from Alice
}
})
npm run doc
This generates the documentation and writes it to out/
.
Then you can open out/index.html
in your browser.
npm test
npm run lint
Go for it! Whether it's code cleanup, a bugfix, or feature request, your input is seriously appreciated.
Unsure where to start? Take a look at the code or open an issue.