Minibone is a compact, versatile, and misuse-resistant library designed to make incorporating end-to-end encryption in your applications remarkably simple. It allows you to store and manage your users' sensitive data while ensuring that only the users themselves can access and decrypt the information — helping you minimize the blast radius of breaches, meet compliance requirements, enhance privacy, and build trust.
Building secure-by-design applications is hard. Minibone makes it practical.
Minibone is built atop the Web Crypto API. It's restricted to a conservative suite of symmetric algorithms for quantum resistance and robustness.
Specifically, Minibone uses AES-GCM-256
for encryption, HKDF-SHA-256
for key derivation, and PBKDF2-SHA-256
with 500,000 iterations for password-based key derivation. Minibone also opts to keep its dependencies to the bare minimum to reduce the risk of supply-chain attacks.
Minibone is designed to run on a client
device (e.g., your desktop, mobile, or web-based app), storing data with a provider
(e.g., a SaaS platform) through a communication channel
(e.g., HTTPS). In this scenario, Minibone is designed to assure confidentiality and integrity, but not availability [1] or freshness [2], when the provider
and/or the communication channel
are compromised.
We assume that the client application and device are not compromised and not otherwise vulnerable to side-channel attacks.
- A malicious
provider
could selectively delete data they store. A compromisedcommunication channel
could selectively drop messages based on metadata. - A malicious
provider
could selectively revert data to earlier versions. A compromisedcommunication channel
could replay messages associated with earlier versions.
Minibone is hosted on NPM.
You can add it to your project by running the npm
command below or an equivalent command in your package manager.
npm i minibone
import Minibone from 'minibone'
// Define a unique service identifier
const serviceIdentifier: any = 'my-unique-service-identifier'
// Virtual API, communication channel and storage provider
class Backend {
private userBundles: Map<string, Uint8Array> = new Map()
private dataBundles: Map<string, Uint8Array> = new Map()
registerUser = async (uid: string, bundle: Uint8Array): Promise<void> => {this.userBundles.set(uid, bundle)}
fetchUser = async (uid: string): Promise<Uint8Array> => this.userBundles.get(uid) ?? new Uint8Array()
putData = async (uid: string, data: Uint8Array): Promise<void> => {this.dataBundles.set(uid, data)}
fetchData = async (uid: string): Promise<Uint8Array> => this.dataBundles.get(uid) ?? new Uint8Array()
}
const virtualBackend = new Backend();
// Register a user; initialize their minibone instance
const minibone: Minibone = await Minibone.create()
// Encrypt and send the user's minibone to the provider
const userName: any = 'some-unique-user-name'
const payload: Uint8Array = await minibone.save('secure-user-secret', [serviceIdentifier, userName])
await virtualBackend.registerUser(userName, payload)
// Encrypt user data
const data: any = {
sq6wmgv2zcsrix6t: 'BETWEEN SUBTLE SHADING AND THE ABSENCE OF LIGHT LIES THE NUANCE OF IQLUSION.',
}
const encrypted: Uint8Array = await minibone.encrypt(data)
await virtualBackend.putData(minibone.uid, encrypted)
// Fetch and load the user's minibone. You probably want to guard payload retrieval behind multi-factor authentication in production.
const payload: Uint8Array = await virtualBackend.fetchUser(userName)
const loadedMinibone: Minibone = await Minibone.load(payload, 'secure-user-secret', [serviceIdentifier, userName])
// Decrypt data using the reconstructed minibone
const fetched: Uint8Array = await virtualBackend.fetchData(minibone.uid)
const decryptedData: any = await loadedMinibone.decrypt(fetched)
Minibone is designed to be simple to use and difficult to abuse. That said, there are a few important aspects to keep in mind when interfacing with Minibone.
- It's important for the context vector (the second parameter of
minibone.save
and third parameter ofMinibone.load
) to be globally unique to reduce the risk of key reuse and maximize the marginal cost of rainbow table attacks. - When prompting end users for a passphrase or master secret, this secret must remain client-side. We recommend using a battle-tested password strength estimator (e.g., zxcvbn). User secrets should be deleted immediately after use to make it just that bit harder for attackers.
Minibone relies solely on symmetric cryptography. While this makes it robust against a number of contemporary and future attacks, it also makes data sharing, assured identity, access control, and real-time collaborative workflows infeasible to implement.
Minibone's enterprise counterpart, Backbone, was designed from first principles to support complex multi-user, multi-enterprise workflows under total end-to-end encryption with a stricter threat model.
If these are a priority, reach out to us by emailing us at root@backbone.dev.
Built with 🦴 by Backbone