node-luks

0.1.0 • Public • Published

LUKS for nodejs


Implementation of the Linux Unified Key Setup for nodejs. The goal is to provide a way to encrypt/decrypt data in a secure and established way by following the LUKS Spec. An unlockable master key using multiple pass keys serves to encrypt and decrypt the data.

While the default values adhere to the spec defaults, many variables are customizable in order to gain complete control over the data encryption.

The resulting header may not be compatible with cryptsetup(8).

Use cases

  • Encrypt sensitive data using multiple passkeys (or only one of course)
  • Store encrypted user files on a webserver which can only be read using the user password or an additional passphrase (e.g.: ProtonMail)
  • Simply as powerful password hasher
  • ...

Install

$ npm install

Test

$ npm test

Function Reference

constructor([options : Object])

LUKS Header Creation

createLUKSHeaderWithMasterKey(masterKey : Buffer [,options : Object]) : Buffer
createLUKSHeader(passKey : Buffer|string [,options : Object]) : Buffer

LUKS Header Information

getLUKSHeaderInfo(luksHeader : Buffer) : LUKSInfo
getLUKSKeyslotIndex(luksHeader : Buffer, passKey : Buffer|string [,options : Object]) : number
getLUKSMasterKey(luksHeader : Buffer, passKey : Buffer|string [,options : Object]) : Buffer

LUKS Passkey Modification

addLUKSKeyWithMasterKey(luksHeader : Buffer, masterKey : Buffer, passKey : Buffer|string [,options : Object]) : number
addLUKSKey(luksHeader : Buffer, existingPassKey : Buffer|string, newPassKey : Buffer|string [,options : Object]) : number
removeLUKSKey(luksHeader : Buffer, index : number [,options : Object]) : void

LUKS Data Encryption/Decryption

encryptLUKSWithMasterKey(luksHeader : Buffer, masterKey : Buffer [,options : Object]) : stream.Duplex
encryptLUKS(luksHeader : Buffer, passKey : Buffer|string [,options : Object]) : stream.Duplex
decryptLUKSWithMasterKey(luksHeader : Buffer, masterKey : Buffer [,options : Object]) : stream.Duplex
decryptLUKS(luksHeader : Buffer, passKey : Buffer|string [,options : Object]) : stream.Duplex

Options

Default options

{
    luks_numkeys: 8,                /* amount of key slots ; default: 8 */
    luks_mkd_iter: 1000,            /* amount of iterations for the master key digest ; default: 1000 */
                                    /* cryptsetup infers the value using a benchmark */
    luks_stripes: 4000,             /* amount of stripes for the anti-forensic split ; default: 4000 */
    luks_align_keyslots: 4096,      /* alignment for keyslot in bytes ; default: 4096 */
    luks_sector_size: 512,          /* LUKS version 1 always use sector of fixed size 512 bytes ; default: 512 */
    luks_ciphername: 'aes',         /* Name of the cipher : aes, twofish, serpent, cast5, cast6 ; default: 'aes' */
                                    /* (IMPORTANT! Only 'aes' is supported in this version) */
    luks_ciphermode: 'xts-plain64', /* Name of the cipher mode : ecb, cbc-plain, cbc-essiv:hash, xts-plain64 ; default: 'xts-plain64' ; default: 'xts-plain64' */
                                    /* (IMPORTANT! Only 'xts-plain64' is supported in this version) */
    luks_hashspec: 'sha256',        /* Name of the hash spec : sha1, sha256, sha512, ripemd160 ; default: 'sha256' */
    luks_key_bytes: 32,             /* length of the master key in bytes (32 => 256bits, 64 => 512bits) ; default: 32 */
                                    /* depends on the cipher used (e.g.: 32 for aes-128-xts and 64 for aes-256-xts, the key is cut in half for xts) */
    luks_pwd_iter: 2000             /* number of iterations for the password in an individual keyslot ; default: 2000 */
                                    /* cryptsetup infers the value using a benchmark */
}

Example for web oriented applications

{
    luks_numkeys: 2,                /* 2 keyslots should be sufficient, at least one is used to store the PBKDF2'ed user password */
    luks_mkd_iter: 5000,            /* PBKDF2 iterations for the master key digest, this really depends on the performance of the server */
                                    /* too low and brute force attacks might be possible, too high and the system will spend a considerable amount of time to recover the master key */
    luks_stripes: 1,                /* striping does not seem as useful in this context, the key material area is effectively the size of the master key */
    luks_align_keyslots: 64,        /* set alignment to the same size as the master key */
    luks_sector_size: 64,           /* sector size also the same size as the master key */
    luks_ciphername: 'aes',         /* aes is hardware accelerated on most systems */
    luks_ciphermode: 'xts-plain64', /* the default of cryptsetup */
    luks_hashspec: 'sha512',        /* nice hashing :) */
    luks_key_bytes: 64,             /* 512bits for aes-xts corresponds to aes-256-xts, the key is split in 2 */
    luks_pwd_iter: 10000            /* PBKDF2 iterations for the passkey, same as for luks_mkd_iter it really depends on the performance of the machine */
                                    /* the user supplied passkey is not stored as is in a keyblock, it is run through PBKDF2 to get an entropy rich version of the passkey in order to encrypt the master key */
}

Info Object

/**
 * @typedef LUKSInfo
 * @type {Object} 
 * @property {string} magic - LUKS magic value. (binary)
 * @property {number} version - LUKS header version.
 * @property {string} cipherName - Name of the cipher.
 * @property {string} cipherMode - Name of the ciphermode.
 * @property {string} hashSpec - Name of the hash spec.
 * @property {number} payloadOffset - Offset in sectors where the bulk data starts.
 * @property {number} keyBytes - Length of the master key in bytes.
 * @property {string} mkDigest - The digest of the masterKey. (binary)
 * @property {string} mkDigestSalt - The salt of the digest. (binary)
 * @property {number} mkDigestIterations - Amount of iterations for the master key digest.
 * @property {string} uuid - UUID for the header.
 * @property {number} numkeys - Amount of keyslots.
 * @property {number} emptyKeySlotIndex - Next available keyslot index.
 * @property {Object[]} keyblock - Array of keyblocks.
 * @property {number} keyblock[].active - Active flag of the keyslot.
 * @property {number} keyblock[].passwordIterations - Number of iterations for the password in an individual keyslot.
 * @property {string} keyblock[].passwordSalt - Salt used for during the password hashing.
 * @property {number} keyblock[].keyMaterialOffset - Offset of the key material in sectors.
 * @property {number} keyblock[].stripes - Amount of stripes for the anti-forensic split.
 */

LUKS Header Layout

+-----------------------------+----------+------+
|Field Name                   |Type      |Bytes |
+-----------------------------+----------+------+
|LUKS_MAGIC                   | chars    |     6|
|LUKS_VERSION                 | UInt16BE |     2|
|LUKS_CIPHERNAME              | charsnt  |    32|
|LUKS_CIPHERMODE              | charsnt  |    32|
|LUKS_HASHSPEC                | charsnt  |    32|
|LUKS_PAYLOAD_OFFSET          | UInt32BE |     4|
|LUKS_KEY_BYTES               | UInt32BE |     4|
|LUKS_MK_DIGEST               | chars    |    20|
|LUKS_MK_DIGEST_SALT          | chars    |    32|
|LUKS_MK_DIGEST_ITER          | UInt32BE |     4|
|LUKS_UUID                    | charsnt  |    40|
+-----------------------------+----------+------+
|KEYBLOCK_ACTIVE              | UInt32BE |     4|
|KEYBLOCK_PWD_ITER            | UInt32BE |     4|
|KEYBLOCK_PWD_SALT            | chars    |    32|
|KEYBLOCK_KEY_MATERIAL_OFFSET | UInt32BE |     4|
|KEYBLOCK_STRIPES             | UInt32BE |     4|
+-----------------------------+----------+------+
| KEYBLOCKS ...               |          |      |
+-----------------------------+----------+------+
| KEY MATERIALS ...           |          |      |
+-----------------------------+----------+------+

chars         : byte array
charsnt       : null terminated byte array
UInt16BE      : 16 bits big endian unsigned int
UInt32BE      : 32 bits big endian unsigned int
KEYBLOCKS     : the remaining keyblocks if more than one
KEY MATERIALS : KEYBLOCK_KEY_MATERIAL_OFFSET marks the beginning of a key material in sector counts aligned to the sectorsize and keyslot alignment

Function Reference (cont.)

constructor([options : Object])

/**
 * LUKS Constructor
 * @constructor
 * @public
 * @param {LUKSOptions} [options] Customizable variables, fallback to default options for missing fields
 */
Example:
const LUKS = require('node-luks');
var luks = new LUKS();

createLUKSHeaderWithMasterKey(masterKey : Buffer [,options : Object]) : Buffer

/**
 * Generates a new LUKS header using a masterKey
 * @public
 * @param {Buffer} masterKey - the length of the key should make sense for the chosen cipher mode
 * @param {LUKSOptions} [options] - options to use during header creation
 * @returns {Buffer} the generated luks header
 */
Example:
const Crypto = require('crypto');
const LUKS = require('node-luks');
var luks = new LUKS(); // use default options
var masterKey = Crypto.randomBytes(32); // using the default aes-128-xts cipher
var luksHeader = luks.createLUKSHeaderWithMasterKey(masterKey);

createLUKSHeader(passKey : Buffer|string [,options : Object]) : Buffer

/**
 * Creates a luks header initialized with a passKey
 * @public
 * @param {Buffer|string} passKey - used to decrypt the master key
 * @param {LUKSOptions} [options] - options to use during header creation
 * @returns {Buffer} the luks header
 */
Example:
const LUKS = require('node-luks');
var luks = new LUKS(); // use default options
var passKey = 'verysecretpassword';
var luksHeader = luks.createLUKSHeader(passKey);

getLUKSHeaderInfo(luksHeader : Buffer) : LUKSInfo

/**
 * Parses a LUKS header for its information, also acts as header validation
 * @public
 * @throws Will throw if the luksHeader is not a buffer : 'Not a buffer'
 * @throws Will throw if the header is invalid : 'Not a luks header'
 * @param {Buffer} luksHeader - to fetch information for
 * @returns {LUKSInfo} header information
 */
Example:
const LUKS = require('node-luks');
var luks = new LUKS(); // use default options
var passKey = 'verysecretpassword';
var luksHeader = luks.createLUKSHeader(passKey);
var info = luks.getLUKSHeaderInfo(luksHeader);

getLUKSKeyslotIndex(luksHeader : Buffer, passKey : Buffer|string [,options : Object]) : number

/**
 * Fetches the keyslot index that matches the passKey
 * If multiple keyslots use the same passKey, then the first one to match will be returned
 * @public
 * @param {Buffer} luksHeader - the complete luks header
 * @param {Buffer|string} passKey - the passKey to search a matching keyslot for
 * @param {LUKSOptions} [options] - options to use during keyslot search
 * @returns {number} an index >0 if found, -1 if not
 */
Example:
const LUKS = require('node-luks');
var luks = new LUKS(); // use default options
var passKey = 'verysecretpassword';
var luksHeader = luks.createLUKSHeader(passKey);
var index = luks.getLUKSKeyslotIndex(luksHeader,passKey);

getLUKSMasterKey(luksHeader : Buffer, passKey : Buffer|string [,options : Object]) : Buffer

/**
 * Recover the masterkey using a passkey
 * @public
 * @param {Buffer} luksHeader - the header
 * @param {Buffer|string} passKey - to unlock one of the keyslots
 * @param {LUKSOptions} [options] - options to supply to the function
 * @returns {Buffer} the masterkey or null if passkey did not match any stored key
 */
Example:
const LUKS = require('node-luks');
var luks = new LUKS(); // use default options
var passKey = 'verysecretpassword';
var luksHeader = luks.createLUKSHeader(passKey);
var masterKey = luks.getLUKSMasterKey(luksHeader,passKey);

addLUKSKeyWithMasterKey(luksHeader : Buffer, masterKey : Buffer, passKey : Buffer|string [,options : Object]) : number

/**
 * Adds a new LUKS key to the header
 * @param {Buffer} luksHeader the complete buffer containing the header
 * @param {Buffer} masterKey the master key
 * @param {Buffer|String} passKey the passkey to unlock the master key
 * @param {Object} [options] options to supply to the function
 * @returns {Number} The keyslot the passKey has been added to
 */
Example:
const LUKS = require('node-luks');
var luks = new LUKS(); // use default options
var passKey = 'verysecretpassword';
var luksHeader = luks.createLUKSHeader(passKey);
var masterKey = luks.getLUKSMasterKey(luksHeader,passKey);
var anotherPassKey = 'anothersecretpassword';
var index = luks.addLUKSKeyWithMasterKey(luksHeader,masterKey,anotherPassKey);

addLUKSKey(luksHeader : Buffer, existingPassKey : Buffer|string, newPassKey : Buffer|string [,options : Object]) : number

/**
 * Adds a new passKey to a luks header using an existing passKey
 * @public
 * @throws Will throw if the masterKey could not be unlocked : 'Could not unlock masterkey'
 * @param {Buffer} luksHeader - the luks header
 * @param {Buffer|string} existingPassKey - existing passKey to unlock masterkey
 * @param {Buffer|string} newPassKey - the new key to add to an available keyblock
 * @param {LUKSOptions} [options] - options to use during key insertion
 * @returns {number} The keyslot the passKey has been added to
 */
Example:
const LUKS = require('node-luks');
var luks = new LUKS(); // use default options
var passKey = 'verysecretpassword';
var luksHeader = luks.createLUKSHeader(passKey);
var anotherPassKey = 'anothersecretpassword';
var index = luks.addLUKSKey(luksHeader,passKey,anotherPassKey);

removeLUKSKey(luksHeader : Buffer, index : number [,options : Object]) : void

/**
 * Removes the passKey at index setting the keyblock to disabled
 * @public
 * @throws Will throw if the index is out of bounds : 'Index is out of bounds'
 * @param {Buffer} luksHeader - the luks header
 * @param {number} index - index starting at 0 should not go over the maximum amount of keyslots
 * @param {LUKSOptions} [options] - options to use during key removal
 */
Example:
const LUKS = require('node-luks');
var luks = new LUKS(); // use default options
var passKey = 'verysecretpassword';
var luksHeader = luks.createLUKSHeader(passKey);
var anotherPassKey = 'anothersecretpassword';
var index = luks.addLUKSKey(luksHeader,passKey,anotherPassKey);
luks.removeLUKSKey(luksHeader,0);

encryptLUKSWithMasterKey(luksHeader : Buffer, masterKey : Buffer [,options : Object]) : stream.Duplex

/**
 * Creates a duplex stream in which you write unencrypted data and read encrypted data
 * @public
 * @throws Will throw if the masterKey is not a buffer : 'MasterKey is not a buffer'
 * @param {Buffer} luksHeader - the luks header
 * @param {Buffer} masterKey - used to encrypt the data
 * @param {LUKSOptions} [options] - options to use during encryption
 * @returns {stream.Duplex} 
 */
Example:
const FS = require('fs');
const LUKS = require('node-luks');
var luks = new LUKS(); // use default options
var passKey = 'verysecretpassword';
var luksHeader = luks.createLUKSHeader(passKey);
var masterKey = luks.getLUKSMasterKey(luksHeader,passKey);
FS.createReadStream('unencryptedfile')
.pipe(luks.encryptLUKSWithMasterKey(luksHeader,masterKey))
.pipe(FS.createWriteStream('encryptedfile'));

encryptLUKS(luksHeader : Buffer, passKey : Buffer|string [,options : Object]) : stream.Duplex

/**
 * Creates a duplex stream in which you write unencrypted data and read encrypted data
 * @public
 * @throws Will throw if the masterKey could not be unlocked using the passKey : 'Could not unlock masterkey'
 * @param {Buffer} luksHeader - the luks header
 * @param {Buffer|string} passKey - passkey to decrypt masterkey
 * @param {LUKSOptions} [options] - options to use during encryption
 * @returns {stream.Duplex} 
 */
Example:
const FS = require('fs');
const LUKS = require('node-luks');
var luks = new LUKS(); // use default options
var passKey = 'verysecretpassword';
var luksHeader = luks.createLUKSHeader(passKey);
FS.createReadStream('unencryptedfile')
.pipe(luks.encryptLUKS(luksHeader,passKey))
.pipe(FS.createWriteStream('encryptedfile'));

decryptLUKSWithMasterKey(luksHeader : Buffer, masterKey : Buffer [,options : Object]) : stream.Duplex

/**
 * Creates a duplex stream in which you write encrypted data and read unencrypted data
 * @public
 * @throws Will throw if the masterKey is not a buffer : 'MasterKey is not a buffer'
 * @param {Buffer} luksHeader - the luks header
 * @param {Buffer} masterKey - masterKey to decrypt data
 * @param {LUKSOptions} [options] - options to use during encryption
 * @returns {stream.Duplex} 
 */
Example:
const FS = require('fs');
const LUKS = require('node-luks');
var luks = new LUKS(); // use default options
var passKey = 'verysecretpassword';
var luksHeader = luks.createLUKSHeader(passKey);
var masterKey = luks.getLUKSMasterKey(luksHeader,passKey);
FS.createReadStream('encryptedfile')
.pipe(luks.decryptLUKSWithMasterKey(luksHeader,masterKey))
.pipe(FS.createWriteStream('unencryptedfile'));

decryptLUKS(luksHeader : Buffer, passKey : Buffer|string [,options : Object]) : stream.Duplex

/**
 * Creates a duplex stream in which you write encrypted data and read unencrypted data
 * @public
 * @throws Will throw if the masterKey could not be unlocked using the passKey : 'Could not unlock masterkey'
 * @param {Buffer} luksHeader - the luks header
 * @param {Buffer|string} passKey - passkey to decrypt masterkey
 * @param {LUKSOptions} [options] - options to use during encryption
 * @returns {stream.Duplex} 
 */
Example:
const FS = require('fs');
const LUKS = require('node-luks');
var luks = new LUKS(); // use default options
var passKey = 'verysecretpassword';
var luksHeader = luks.createLUKSHeader(passKey);
FS.createReadStream('encryptedfile')
.pipe(luks.decryptLUKS(luksHeader,passKey))
.pipe(FS.createWriteStream('unencryptedfile'));

Package Sidebar

Install

npm i node-luks

Weekly Downloads

2

Version

0.1.0

License

MIT

Last publish

Collaborators

  • popsulfr