A NodeJS password vault.
Please be aware that breaking changes will occur during 0.* (alpha) development. Until 1.0 is released, assume that every minor version contains breaking changes to encryption, structure and API.
This repository holds the core system functionality:
- Archive reading/writing
- Encryption/decryption
- Low-level processing and manipulation
- Archive format handling
Buttercup manages credentials in an encrypted archive. The archive utilises a delta-based (history) archive description method to load and save data. This allows for more robust saving and loading whilst protecting against external file changes by allowing some degree of merging.
- AES 256 bit CBC encryption (w/ HMAC-sha256)
- Archive change delta tracking
- WebDAV/ownCloud remote file support
- Text compression with GZIP
Buttercup can be easily used as a component, allowing for powerful and secure data storage within projects.
You can read the API documentation here, and the change-log here. Please read our contribution style guide before making any issues or pull-requests.
Create an archive and a data source: The datasource is where the archive will be written to.
var Buttercup = require("buttercup");
var archive = new Buttercup.Archive(),
datasource = new Buttercup.FileDatasource("~/myArchive.bcup");
datasource.save(archive, "my secure password");
Using the same system, you can also load an archive:
var Buttercup = require("buttercup");
var datasource = new Buttercup.OwnCloudDatasource(
"http://www.mycloud.com",
"/personal/security/passwords.bcup",
"inigo",
"montoya1987"
);
// `load()` returns a promise
datasource.load("my archive password").then(function(archive) {
// `archive` is the archive, loaded from remote. Saving on this datasource will
// write back to the remote.
});
Workspaces are designed to help manage the necessary archive management tasks that come from creating a user-friendly password management application. Features like remote update merging need to be handled internally, and so the workspace helps manage such complex tasks.
In reality, the workspace is just a container:
var Buttercup = require("buttercup");
var workspace = new Buttercup.Workspace(),
archive = new Buttercup.Archive(),
datasource = new Buttercup.FileDatasource("~/myArchive.bcup");
datasource
.load("password")
.then(function(archive) {
workspace
.setArchive(archive)
.setDatasource(datasource)
.setPassword("Fezzik, tear his arms off");
});
The workspace can handle some complex operations like merge conflicts - for instance, if a user has an archive open, and remotely the archive is modified, the workspace can help perform a merge between the two.
A merge is quite basic, in that the remote commands (that differ) are run first, and then the local run next. All deletion commands that could cause conflicts are stripped, so the resulting merge will still contain items that were deleted in the differing portions before the merge.
// Check for differences
workspace.archiveDiffersFromDatasource()
.then(function(differs) {
if (differs) {
// Merge differences first
return workspace.mergeFromDatasource();
}
})
.then(function() {
// Save the archive
workspace.save();
});
Merging is especially helpful for situations where archives can be modified at any time, such as with cloud storage.
You can search within archives for certain entries or groups:
archive
.findEntriesByProperty("title", /^Home-[a-z]+$/i)
.forEach(function(entry) {
// Do something with entry
});
archive
.findGroupsByTitle("banking")
.forEach(function(group) {});
You can import from other password archive formats, such as KeePass. Checkout the Buttercup Importer project.
Some things in Buttercup are best run purely on Node, such has password-based key derivation. When preparing this for the web (such as with Webpack or Browserify), things can move very slowly. There are implementations for functions, such as PBKDF2, that exist for web use that are many times faster than the output of such build utilities.
You can override PBKDF2 by doing the following (documented on iocane):
var Buttercup = require("buttercup");
Buttercup.vendor.iocane.components.setPBKDF2(newPBKDF2Function);
// Where 'newPBKDF2Function' is a function that returns a Promise with the hash in a Buffer
Entries and groups have attributes, describing how they should be treated by the various interfaces that interact with the archive. Attributes are not visible to the users and can contain a variety of different properties.
One such use for attributes is the description of how to present the groups and entries to the user - like with item icons.
You can view all of the icons stored in Buttercup by running the following:
var Buttercup = require("buttercup"),
images = Buttercup.tools.design
.getImageNames()
.map(Buttercup.tools.design.getImageData);
images[0] // { "name": "about", "image": "data:image/svg;base64..." }
You can get the various display options for an entry by using the following methods:
var imageDetails = Buttercup.tools.design.getEntryImageDetails(entry);
// imageDetails === { "name": "key", "image": "data:image..." }
var displayInfo = entry.getDisplayInfo();
// displayInfo === { "title": "Title", "username": "Username", "password": "Password" }