egg-cookies
Version:
cookies module for egg
84 lines (70 loc) • 1.94 kB
JavaScript
'use strict';
const debug = require('debug')('egg-cookies:keygrip');
const crypto = require('crypto');
const assert = require('assert');
const constantTimeCompare = require('scmp');
const replacer = {
'/': '_',
'+': '-',
'=': '',
};
// patch from https://github.com/crypto-utils/keygrip
class Keygrip {
constructor(keys) {
assert(Array.isArray(keys) && keys.length, 'keys must be provided and should be an array');
this.keys = keys;
this.hash = 'sha256';
this.cipher = 'aes-256-cbc';
}
// encrypt a message
encrypt(data, key) {
key = key || this.keys[0];
const cipher = crypto.createCipher(this.cipher, key);
return crypt(cipher, data);
}
// decrypt a single message
// returns false on bad decrypts
decrypt(data, key) {
if (!key) {
// decrypt every key
const keys = this.keys;
for (let i = 0; i < keys.length; i++) {
const value = this.decrypt(data, keys[i]);
if (value !== false) return { value, index: i };
}
return false;
}
try {
const cipher = crypto.createDecipher(this.cipher, key);
return crypt(cipher, data);
} catch (err) {
debug('crypt error', err.stack);
return false;
}
}
sign(data, key) {
// default to the first key
key = key || this.keys[0];
return crypto
.createHmac(this.hash, key)
.update(data)
.digest('base64')
.replace(/\/|\+|=/g, x => replacer[x]);
}
verify(data, digest) {
const keys = this.keys;
for (let i = 0; i < keys.length; i++) {
if (constantTimeCompare(new Buffer(digest), new Buffer(this.sign(data, keys[i])))) {
debug('data %s match key %s', data, keys[i]);
return i;
}
}
return -1;
}
}
function crypt(cipher, data) {
const text = cipher.update(data, 'utf8');
const pad = cipher.final();
return Buffer.concat([ text, pad ]);
}
module.exports = Keygrip;