0.2.0 • Published 1 year ago

burgerid v0.2.0

Weekly downloads
-
License
AGPL-3.0-only
Repository
-
Last release
1 year ago

Burger Identities

Portable Identities for the Internet

Introduction

Portable Identities? What's that?

The goal of portable identities is to have a way to be yourself on the internet and convince the people you interact with there that it's the same you each time, regardless of which website or service you're using. You won't have to rely on the website or service itself to provide your identity (like a username@service.com address), but instead you can bring your own identity with you wherever you go.

That's a lot to take in. Here's a quick example.

Say you have account on Twitter called me@twitter.com, but you decide to move to a different service in late 2022 because ... reasons. Say you decide to join a Mastodon instance as myself@masto.inst.

Without a portable identity, you need to come up with some cleverness to convince your followers that me@twitter.com and myself@masto.host are really the same person. Maybe you use a verifiction link that proves you own a domain name. Maybe you post a photo of yourself that only you could post.

With a portable identity, you don't need any of that cleverness. You just show your identity information, and your followers will be able to tell it's the same you from before.

If you ever decide to move to a different Mastodon instance after that, keep using the same portable identity and your followers will know it's you there too.

In short, portable identities make it easier to move between services. This helps avoid being locked-in to any single service.

Cool. How does it work?

Asymmetric cryptography, mostly.

Your Burger Idenitity contains both public and private parts. Through the power of asymmetric cryptography, the public part contains (among other things) a unique identifier and the private part contains (among other things) a way to prove that you are the owner of that unique identifier.

As long as you have a copy of the private part, and only you, no one else could claim ownership of your unique identifier, and therefore no one else could claim ownership of your identity.

One major goal for Burger is to make managing your identity and keeping it secure as easy as humanly possible. Your identity is saved as a single file, both the public and private parts. Although the private part is protected with a passphrase. For someone to use your identity, they need both the file and your passphrase at the same time. So to keep your identity secure, you just need to:

  • keep your identity file somewhere where you won't lose it (like in a backed-up folder), and
  • keep your passphrase in a safe place (like your head or a password database).

If you use a high-quality passphrase (ie, you're reasonably certain someone couldn't guess it even if they could guess an astronomical number of times), and you don't re-use that passphrase anywhere else, then you could even keep your identity file somewhere super convenient, but not-quite private, like a Dropbox service, or similar.

Ok, but why are they called Buger?

The name "Burger" fits in with the theme of restaurant life that's used by a constellation of related projects we've developed. Food-runner has been released already, but some other members of the constellation haven't been released yet. The name may make more sense later on. =)

Portable Identies are a building block for End-to-End Encrypted (E2EE) apps

Portable identities won't solve every communication problem you may be interested in, but they're Part of a Balanced Breakfast of several related topics:

Private messaging

Your Burger identity also contains an asymmetric key pair that can be used for encryption. Meaning, two people using Burger identities can create encrypted messages for each other. Although, the details of how those messages should be transported and delivered are left to the downstream application.

Authentication

Authentication code is not directly included in the Burger Identities library, becauase each downstream application will have different needs and capabilities there. But the general recipe for authentication using Burger identities is something like the following challenge-response protoocol.

A person presents the public part of an identity and claims to be its owner. To authenticate them:

  • Generate an unguessable message, like a secure random nonce.
  • Encrypt the message for the identity.
  • Send the encrypted message to the person claiming to own the identity.
  • Authenticate the person if (and only if) they can respond with the unencrypted message. But:
    • Don't authenticate the person if they take a long time to respond.
    • Don't let someone try a large amount of authenticationa attempts in a short amount of time.

Public Key Infrastructure (PKI)

Two people securely exchanging identity information over an untrusted channel like the internet is an extremely challening problem and is also outside the scope of this library. It's very hard to ensure that the identity information can be exchanged without an adversary intercepting and changing the identity information, a machine-in-the-middle attack. Each downstream application should choose a PKI method that makes sense for the users of their app.

For example, an application that has some degree of centralized authority may want to use a certificate-based PKI method. But an application based on private social networking over real-life relationships may be better served by relying on already-existing secure channels to transmit public identity information and avoid appealing to a specific external authority. In that case, PKI could be served by, for example:

  • another encrypted chat app
  • a QR code transfer between two devices in physical proximity

Burger identities can evolve to use future improvements in cryptography

Burger identities have a few useful properties that help make them a little more future-proof:

  • Buger identities are versioned.
  • Each version uses a specific set of cryptographic tools.
  • Identities of different versions can talk to each other.
  • A person can migrate to a new identity of a different version.

These properties means a person can use one version of a Burger identity today and then switch to a newer (and better?) version some time in the future.

For identity version 1, we went with tried-and-true large-key RSA as the underlying toolbox for the asymmetric cryptography. It's the simplest thing to start with, and probably the implementation we had the highest chance of getting right. Or at least close to right.

But other versions can use different cryptographic tools to implement Burger identities. Perhaps a version 2 could be based on elliptic curve cryptograpy (ECC). And perhaps a version 3 could end up being whatever the infosec community decides is a good toolset for a future world that has effective quantum computers.

Status

Burger is in the very early stages of development. The public API may not have settled yet and users should still expect breaking changes from time to time.

Cryptography is hard, and this project hasn't had much contact with reality yet. After that happens, hopefully soon, API and format stability will happen after we decide where the goal posts should be.

Implementations

Burger Identities can be implemented in any language that can access cryptography libraries. This implementation is written in Javascript and relies on the browser (or Node.js in tests) to provide access to cryptography functions. Other known implementations include:

Getting Started

Install the package using your favorite package manager using the package name:

burgerid

Create an identity and perform basic functions with it

This example also uses functions from the food-runner library.

import * as burger from 'burgerid';
import * as bson from 'food-runner/src/bson.js';

// create a new identity for Alice
const idAlice = await burger.make('Alice', 'passphrase');

// save the identity to a binary file, and re-open it
const /** @type {ArrayBuffer} */ saved = bson.serialize(idAlice.save());
const id2 = burger.open(bson.deserialize(saved));

// save the identity to a JSON string, and re-open it
const /** @type {string} */ savedJson = JSON.stringify(idAlice.saveJson());
const id3 = burger.open(JSON.parse(saved));

// get the unique identifier (uid) for the identity
const /** @type {ArrayBuffer} */ uid = idAlice.uid();

// unlock the identity with the passphrase
await idAlice.unlock('passphrase'); // returns true
idAlice.isLocked(); // return false

// encrypt and sign a message to Bob
const cleartext = bson.serialize({ msg: 'Hello, Bob'});
const idBobPublic = burger.open(/* ... */); // assume Alice has Bob's public identity already
const ciphertext = await idBobPublic.encryptLargeFor(cleartext);
const signature = await idAlice.sign(ciphertext);

// Alice is done sending, so we can lock the identity.
// This prevents anyone from encrypting or signing messages
// with this identity until the passphrase is entered again.
await idAlice.lock();

// export the public part of Alice's identity for Bob to use
const idAlicePublic = idAlice.savePublic();

// Bob can verify and read the message
if (!await idAlicePublic.verify(ciphertext, signature)) {
    throw new Error("Message failed verification");
}
const idBob = burger.open(/* ... */); // assume Bob has Bob's identity already
await idBob.unlock("bob's passphrase");
const cleartext2 = await idBob.decryptLargeAs(ciphertext);
const msg = bson.deserialize(cleartext2).msg; // Hello, Bob

// Bob is done decrypting, so we can lock this identity too
await bobId.lock();

More examples

For more examples, see the extensive test suite.

Documentation

This is a very new project. Documentation is practically non-existent at the moment.

The source code itself has many JSDoc comments, and the Getting Started section above has some simple examples. The interface for the Identity class has some useful JSDoc comments. That's about it so far.

If/when this project gets more attention, the documention will be improved.

License

Copyright (C) 2022 Cuchaz Interactive, LLC and contributors (see AUTHORS.md)

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.