Lawnchair and MongoDB-inspired libsodium-backed encrypted persistent document storage. Designed (and tested) for Cordova/Phonegap and Node.js
Lawncipher v2.0 is still in beta stage. There are some problems with document validation (against an IndexModel) and some race conditions.
For stability, please use Lawncipher v1 instead, and install it simply with npm install lawncipher
If you want to use Lawncipher v2, install it with npm install lawncipher@beta
Building a rather versatile and persistent encrypted document storage.
- Lawncipher is a document store
- The entirety of Lawncipher data is encrypted using either a password or a 256-bit key. (In case a password is used, it is transformed into a 256-bit root key using scrypt)
- The security limits and precautions of Lawncipher are listed in the threat model.
- Instead of tables containing rows, Lawncipher has collections containing documents
- A document in Lawncipher has a unique ID and at least one of these two things:
- A blob : could a JSON object, a string or arbitrary binary data (in a
Uint8Array
). It is stored encrypted and stored in a dedicated file, and decrypted when retrieved from the collection. - An indexData : An object, containing the query-able attributes of the document, stored in the collection's index.
- A blob : could a JSON object, a string or arbitrary binary data (in a
- Lawncipher is blob-first: when running a query, and the result list is being built, for a given result document, the result list will contain its blob. If the document doesn't have a blob, the indexData will take its place in the result list.
- A schema, called "Index model", can be set for the indexData in a given collection. This schema gives the list and type of attributes that will be stored in the index. It can also determine whether a given attribute gives the IDs to the documents of the collection; as well as whether the value of a given attribute must be unique across the collection (without giving document IDs).
- When inserting a document, if a JSON object is given, the indexData can be implicitly extracted from the document.
- A document can be forced to expire, using TTLs (Time-to-live)
npm install lawncipher
Then, we are good to go:
var Lawncipher = require('lawncipher');
Lawncipher.init();
var db = new Lawncipher.db('path/to/my/database');
db.openWithPassword('strongPasswordWow', function(err){
if (err){
if (err == 'INVALID_ROOTKEY'){
//Invalid password
}
return;
}
//Do things with the database
});
Install the Cordova plugins:
- cordova-plugin-file-node-like
- cordova-plugin-scrypt (Optional, but highly recommended, especially on iOS)
Then install Lawncipher:
bower install lawncipher
Once we have installed Lawncipher (and the plugins mentioned above) and that we have imported Lawncipher into our application:
//Initialize the file system
window.plugins.nodefs.init(function(err){
if (err){
console.error('Error while initializing the file system: ' + err);
return;
}
var fs = window.plugins.nodefs(window._fs);
//Try to use MiniSodium, fallback to libsodium.js
Lawncipher.init('?minisodium?');
//Optional call. If you have installed cordova-plugin-scrypt. Redundant if MiniSodium is available
Lawncipher.useCordovaPluginScrypt();
var db = new Lawncipher.db('path/to/my/db', fs);
db.openWithPassword('strongPasswordWow', function(err){
if (err){
if (err == 'INVALID_ROOTKEY'){
//Invalid password
}
return;
}
//Do things with the database
});
});
Lawncipher Retrieving a document by its ID (here, 'abc')
Collection.find('abc', callbackFunction)
Lawncipher
Collection.find({firstName: 'Steve', lastName: 'Jobs'}, callback)
SQL
SELECT * FROM tableName WHERE firstName = 'Steve' AND lastName = 'Jobs'
Lawncipher
Collection.find({firstName: 'Steve', $not: {lastName: 'Jobs'}}, callback)
SQL
SELECT * FROM tableName WHERE firstName = 'Steve' AND lastName <> 'Jobs'
Lawncipher
Collection.find({$or: [{firstName: 'Steve'}, {lastName: 'Jobs'}]}, callback)
SQL
SELECT * FROM tableName WHERE firstName = 'Steve' OR lastName = 'Jobs'
Lawncipher
Collection.find({firstName: 'Steve', $or: [{lastName: 'Wozniak'}, {lastName: 'Jobs'}])
SQL
SELECT * FROM tableName WHERE firstName = 'Steve' AND (lastName = 'Wozniak' OR lastName = 'Jobs')
Lawncipher
Collection.find({firstName: 'Steve', $sort: {lastName: 'asc'}, $skip: 100}, callback, 100)
SQL
SELECT * FROM tableName WHERE firstName = 'Steve' ORDER BY lastName ASC LIMIT 100 OFFSET 100
(get the 101-200 guys who are called Steve, ordered alphabetically by lastName)
Here is how you can run unit tests in the compatible runtimes
Go to the directory where the Lawncipher library is located, and run
npm test
A small test app has been built for that purpose.
The Lawncipher API is documented here.
The Lawncipher interals and file formats are documented here.
Lawncipher is licensed under the terms of the MIT license.