Backchannel
THIS IS A PROOF OF CONCEPT PROTOTYPE. IT HAS NOT HAD ANY KIND OF SECURITY OR CRYPTOGRAPHY REVIEW. THIS SOFTWARE MIGHT BE UNSAFE.
Identities in Backchannel are created by redeeming a one-time invitation code. The code, much like the role of a phone number, is a lookup method to connect two people. Unlike a phone number, the code is temporary, and immediately discarded after use. The expiration of invitation codes gives certain advantages. There is no equivalent of “giving someone your permanent address or number” with this design. Although perhaps inconvenient for some use cases, this property can also be a strength. This design is inherently resistant to anonymous spam.
Once a user passes this code to another, both of their applications generate a strong shared cryptographic secret using SPAKE2, a PAKE protocol. This shared secret represents the relationships between those two users, and is used to generate a secure end-to-end encrypted connection.
Users then assign a name for each other, rather than naming themselves. Names in Backchannel are private – i.e., they are only seen by the person who created them, just like in a phone’s contact list. Naming contacts privately is important. Because there is no self-described user profile system, a user cannot be impersonated by someone else within the application.
Read more about Backchannel in our paper, at https://www.inkandswitch.com/backchannel/
Application prototype examples
Usage
This library is intended for use in a browser-like environment (progressive web app and electron would work too) as a websocket client.
npm install @inkandswitch/backchannel
Basics
Get a list of contacts
import Backchannel, { EVENTS } from '@inkandswitch/backchannel';
const DBNAME = 'myapp'
const SETTINGS = {
relay: 'ws://localhost:3001'
}
let backchannel = new Backchannel(DBNAME, SETTINGS)
backchannel.once(EVENTS.OPEN, () => {
let contacts = backchannel.listContacts()
})
Two users decide upon a single number that is at least 6 characters long OR generate a one-time invitation code for them:
let random = crypto.randomBytes(3)
let code = parseInt(Buffer.from(random).toString('hex'), 16)
Once both users have entered in a code, split the code into 'mailbox' and 'password' pieces. The password should be kept secret! The mailbox is public to the relay and so should not be derivative or in any way related to the password.
let mailbox = code.slice(0, 3)
let password = code.slice(3)
let key = await backchannel.accept(mailbox, password)
Add a contact and assign them a name and avatar
let id = await backchannel.addContact(key)
await backchannel.editName(id, 'jennie')
await backchannel.editAvatar(id, [avatarbytes])
Send an end to end encrypted message
let message = await backchannel.sendMessage(id, 'hi jennie')
Link a device, this works the same as adding a contact, but you call addDevice instead
let key = await backchannel.accept(mailbox, password)
let id = await backchannel.addDevice(key)
Unlink all devices
backchannel.on(EVENTS.ACK, ({ contactId }) => {
console.log('Unlinked device with id=', contactId)
})
await backchannel.unlinkDevice()
Running a Relay
Backchannel depends upon @local-first/relay, which is a websocket relay. Clients need to connect to the same relay in order to find each other. You can run the relay either on the local machine or in the cloud. One way to do this in the cloud quickly is to remix it on Glitch.
You can use the relay provided in this repository at bin/server.js:
const { Server } = require('@localfirst/relay/dist')
const DEFAULT_PORT = 3001
const port = Number(process.env.PORT) || DEFAULT_PORT
const server = new Server({ port })
server.listen()
And run it with
$ node bin/server.js
Contributing
Viewing the Documentation
The docs can be auto-generated locally. The following command outputs the generated documentation into
build/docs/api
.
npm run docs
Then you can view the docs locally with an http-server, you can use something
like the node http-server
.
npm i -g http-server
http-server build/docs/api
Or with npx:
npx http-server build/docs/api
You can then view the main documentation locally at http://localhost:port/classes/backend_backchannel.backchannel.html
Testing
Open two browser windows that are not in private browsing mode. They can be tabs in the same browser program. Opening a private window doesn't work with IndexedDB.
Because we're using IndexedDB, to do local testing with the same browser on the
same machine, you should open one of the tabs or windows at
localhost:3000
and the other at 127.0.0.1:3000
. This will ensure
that they both have their own isolated database.
To run automated tests,
npm run relay
and then
npm test
Deployment
To deploy the minified production build, run
npm run build
To build the api documentation, run
npm run docs
Contributors
- Karissa McKelvey, @okdistribute, Lead
- Ben Royer, Design
- Chris Sun, @daiyi, Frontend/UI
Advisors
- Cade Diehm
- Peter van Hardenberg, @pvh
- سلمان الجماز, @saljam
- Herb Caudill, @herbcaudill
- Martin Kleppman, @ept
License
MIT