Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm ... this will make it hard to merge it back to upstream if we actually want to do this one day

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would. It is very abandoned upstream, though. No commits since 2016, open issues/PRs with no feedback since 2017.

"arrowParens": "always",
"trailingComma": "all",
"singleQuote": true,
"htmlWhitespaceSensitivity": "ignore"
}
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
dist: bionic
sudo: false
language: node_js
cache: npm

node_js:
- "12"

addons:
postgresql: "9.6"
hosts:
- db

Expand Down
192 changes: 99 additions & 93 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,105 +1,111 @@
var crypto = require('crypto');

function EncryptedField(Sequelize, key, opt) {
if (!(this instanceof EncryptedField)) {
return new EncryptedField(Sequelize, key, opt);
}

var self = this;

opt = opt || {};
self._algorithm = opt.algorithm || 'aes-256-cbc';
self._iv_length = opt.iv_length || 16;
self.encrypted_field_name = undefined;

var extraDecryptionKeys = [];
if (opt.extraDecryptionKeys) {
extraDecryptionKeys = Array.isArray(opt.extraDecryptionKeys) ?
opt.extraDecryptionKeys :
Array(opt.extraDecryptionKeys);
}
self.decryptionKeys = ([key].concat(extraDecryptionKeys))
.map(function (key) {
return new Buffer(key, 'hex');
});
self.encryptionKey = self.decryptionKeys[0];
self.Sequelize = Sequelize;
};
if (!(this instanceof EncryptedField)) {
return new EncryptedField(Sequelize, key, opt);
}

var self = this;

opt = opt || {};
self._algorithm = opt.algorithm || 'aes-256-cbc';
self._iv_length = opt.iv_length || 16;
self.encrypted_field_name = undefined;

var extraDecryptionKeys = [];
if (opt.extraDecryptionKeys) {
extraDecryptionKeys = Array.isArray(opt.extraDecryptionKeys)
? opt.extraDecryptionKeys
: Array(opt.extraDecryptionKeys);
}
self.decryptionKeys = [key].concat(extraDecryptionKeys).map(function(key) {
return new Buffer(key, 'hex');
});
self.encryptionKey = self.decryptionKeys[0];
self.Sequelize = Sequelize;
}

EncryptedField.prototype.vault = function(name) {
var self = this;

if (self.encrypted_field_name) {
throw new Error('vault already initialized');
}

self.encrypted_field_name = name;

return {
type: self.Sequelize.BLOB,
get: function() {
var previous = this.getDataValue(name);
if (!previous) {
return {};
}

previous = new Buffer(previous);

function decrypt(key) {
var iv = previous.slice(0, self._iv_length);
var content = previous.slice(self._iv_length, previous.length);
var decipher = crypto.createDecipheriv(self._algorithm, key, iv);

var json = decipher.update(content, undefined, 'utf8') + decipher.final('utf8');
return JSON.parse(json);
}

var keyCount = self.decryptionKeys.length;
for (var i = 0; i < keyCount; i++) {
try {
return decrypt(self.decryptionKeys[i]);
} catch (error) {
if (i >= keyCount - 1) {
throw error;
}
}
}
},
set: function(value) {
// if new data is set, we will use a new IV
var new_iv = crypto.randomBytes(self._iv_length);

var cipher = crypto.createCipheriv(self._algorithm, self.encryptionKey, new_iv);

cipher.end(JSON.stringify(value), 'utf-8');
var enc_final = Buffer.concat([new_iv, cipher.read()]);
var previous = this.setDataValue(name, enc_final);
var self = this;

if (self.encrypted_field_name) {
throw new Error('vault already initialized');
}

self.encrypted_field_name = name;

return {
type: self.Sequelize.BLOB,
get: function() {
var previous = this.getDataValue(name);
if (!previous) {
return {};
}

previous = new Buffer(previous);

function decrypt(key) {
var iv = previous.slice(0, self._iv_length);
var content = previous.slice(self._iv_length, previous.length);
var decipher = crypto.createDecipheriv(self._algorithm, key, iv);

var json =
decipher.update(content, undefined, 'utf8') + decipher.final('utf8');
return JSON.parse(json);
}

var keyCount = self.decryptionKeys.length;
for (var i = 0; i < keyCount; i++) {
try {
return decrypt(self.decryptionKeys[i]);
} catch (error) {
if (i >= keyCount - 1) {
throw error;
}
}
}
}
},
set: function(value) {
// if new data is set, we will use a new IV
var new_iv = crypto.randomBytes(self._iv_length);

var cipher = crypto.createCipheriv(
self._algorithm,
self.encryptionKey,
new_iv,
);

cipher.end(JSON.stringify(value), 'utf-8');
var enc_final = Buffer.concat([new_iv, cipher.read()]);
var previous = this.setDataValue(name, enc_final);
},
};
};

EncryptedField.prototype.field = function(name) {
var self = this;

if (!self.encrypted_field_name) {
throw new Error('you must initialize the vault field before using encrypted fields');
}
var encrypted_field_name = self.encrypted_field_name;

return {
type: self.Sequelize.VIRTUAL,
set: function set_encrypted(val) {
// use `this` not self because we need to reference the sequelize instance
// not our EncryptedField instance
var encrypted = this[encrypted_field_name];
encrypted[name] = val;
this[encrypted_field_name] = encrypted;
},
get: function get_encrypted() {
var encrypted = this[encrypted_field_name];
return encrypted[name];
}
}
var self = this;

if (!self.encrypted_field_name) {
throw new Error(
'you must initialize the vault field before using encrypted fields',
);
}
var encrypted_field_name = self.encrypted_field_name;

return {
type: self.Sequelize.VIRTUAL,
set: function set_encrypted(val) {
// use `this` not self because we need to reference the sequelize instance
// not our EncryptedField instance
var encrypted = this[encrypted_field_name];
encrypted[name] = val;
this[encrypted_field_name] = encrypted;
},
get: function get_encrypted() {
var encrypted = this[encrypted_field_name];
return encrypted[name];
},
};
};

module.exports = EncryptedField;
18 changes: 10 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"description": "encrypted sequelize fields",
"main": "index.js",
"scripts": {
"format": "prettier --write '*.?s' 'test/*.?s'",
"test": "mocha"
},
"repository": {
Expand All @@ -21,13 +22,14 @@
},
"homepage": "https://github.com/defunctzombie/sequelize-encrypted",
"devDependencies": {
"babel-plugin-transform-async-to-generator": "6.8.0",
"babel-plugin-transform-es2015-modules-commonjs": "6.10.3",
"babel-polyfill": "6.9.1",
"babel-preset-es2015": "6.9.0",
"babel-register": "6.9.0",
"mocha": "2.0.1",
"pg": "6.0.1",
"sequelize": "3.23.4"
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"babel-polyfill": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-register": "^6.26.0",
"mocha": "^6.2.1",
"pg": "^6.4.1",
"prettier": "^1.19.1",
"sequelize": "^3.34.0"
}
}
Loading