UNPKG

object-encrypter

Version:

Encrypt/decrypt javascript objects as strings with TTL

123 lines (97 loc) 3.23 kB
var crypto = require('crypto'); function isObject (val) { return Object.prototype.toString.call(val) === '[object Object]'; } function hmac (secret, data) { return crypto.createHmac('md5', secret).update(data).digest('hex'); } var hmac_length = hmac('secret', 'data').length; function split (signatures) { var set = {}; for (var i = 0, l = signatures.length; i < l; i += hmac_length) { set[signatures.slice(i, i+hmac_length)] = true; } return set; } function signer (secrets) { var fn = secrets .map(function (secret) { return function (data) { return hmac(secret, data); }; }) .reduce(function (prev, sign) { return function (data) { return prev(data) + sign(data); }; }); return fn; } function verifier (secrets) { var verify = secrets .map(function (secret) { return function (data, signatures) { return !!signatures[hmac(secret, data)]; }; }) .reduce(function (prev, verify) { return function (data, signatures) { return prev(data, signatures) || verify(data, signatures); }; }); var fn = function (data, signature) { return verify(data, split(signature)); }; return fn; } module.exports = function (secrets, options) { secrets = [].concat(secrets); options = options || {}; options.ttl = options.ttl || false; options.algorithm = options.algorithm || 'aes-256-cbc'; options.outputEncoding = options.outputEncoding || 'base64'; var sign = signer(secrets); var verify = verifier(secrets); return { encrypt: function (obj, ttl) { if (!isObject(obj)) { throw new Error('Only hash-map like objects can be encrypted'); } if (options.ttl) { obj.expires = ttl ? Date.now() + ttl : 0; } obj = JSON.stringify(obj); obj += '\n'+sign(obj); var cipher = crypto.createCipher(options.algorithm, secrets.join('')); var crypted = cipher.update(obj, 'utf8', options.outputEncoding); crypted += cipher.final(options.outputEncoding); return crypted; }, decrypt: function (str) { if (!str) { return null; } var decipher = crypto.createDecipher(options.algorithm, secrets.join('')); str = decipher.update(str, options.outputEncoding, 'utf8'); str += decipher.final('utf8'); var index = str.indexOf('\n'); if (index === -1) { return null; } var sig = str.slice(index+1); var obj = str.slice(0, index); if (!verify(obj, sig)) { return null; } try { obj = JSON.parse(obj); if (options.ttl) { return obj.expires >= Date.now() ? obj : null; } return obj; } catch(e) { return null; } } }; };