Skip to content

Instantly share code, notes, and snippets.

@ajmas
Last active February 20, 2022 11:07
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save ajmas/b7c2df45d69a131b62b18ae91ee0602b to your computer and use it in GitHub Desktop.
Save ajmas/b7c2df45d69a131b62b18ae91ee0602b to your computer and use it in GitHub Desktop.
Code to encrypt a Sequelize fields
// Code to encrypt data in sequelize fields
// We are using ascii85 as a way save on storage. Also, note that
// the space delimiter we are using is a bit of an abuse since in
// normal cases ascii85 will skip over it, but since we are using
// after encoding and before encoding, it shouldn't be an issue.
//
// Fields are broken down to facilitate unit testing.
//
// based on code here: http://vancelucas.com/blog/stronger-encryption-and-decryption-in-node-js/
//
// Use this when definiing your model. For example:
//
// model = {
// myField: fieldEncryption('myField', {
// type: Sequelize.STRING,
// field: 'my_field'
// });
// }
//
const crypto = require('crypto');
const ascii85 = require('ascii85');
const algorithm = 'aes-128-ctr';
// use something like `openssl rand -hex 11` to generate
// ensure this is not hard coded!!!
const key = 'C75s251tNDMZcmc=';
function encode85(text) {
return ascii85.encode(text, { delimiter: false }).toString()
}
function decode85(text) {
return ascii85.decode(text);
}
function encrypt85(value) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(value, 'utf8');
encrypted = Buffer.concat([encrypted, cipher.final()]);
return encode85(iv) + ' ' + encode85(encrypted);
}
function decrypt85(value) {
const textParts = value.split(' ');
const iv = decode85(textParts.shift());
const textIv = textParts.join(' ');
const encryptedText = decode85(textIv);
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
}
function fieldEncryption (fieldName, options = {}) {
const ops = {
set: function (val) {
if (val && val !== null) {
this.setDataValue(fieldName, encrypt85(val));
} else {
this.setDataValue(fieldName, null);
}
},
get: function () {
const value = this.getDataValue(fieldName);
if (value && value !== null) {
return decrypt85(value);
} else {
return null;
}
}
}
return Object.assign(ops, options);
}
module.exports.fieldEncryption = fieldEncryption;
@sunwukonga
Copy link

sunwukonga commented Dec 11, 2019

For a smoother key generation. I'd suggest using Buffer.from(key, encoding) instead.

With base64 encoding of 128 bits (128/8 = 16 bytes)

  openssl rand -base64 16
  # Vx2SM+jM6PMRSdv9tZXO0g==

Note: 24 characters if all characters utf8 1byte characters, i.e. 24 bytes = 192 bits if we naively put into:

const base64key = "Vx2SM+jM6PMRSdv9tZXO0g=="
const decipher = crypto.createDecipheriv(algorithm, base64key, iv)

Instead, to get the proper 128 bits with:

const base64key = "Vx2SM+jM6PMRSdv9tZXO0g=="
const decipher = crypto.createDecipheriv(algorithm, Buffer.from(base64key, 'base64'), iv)

Similarly, with a hex encoding:

  openssl rand -hex 16
  # e1bed392243ee706038777f2e548595a

and used with:

const hexkey = "e1bed392243ee706038777f2e548595a"
const decipher = crypto.createDecipheriv(algorithm, Buffer.from(base64key, 'hex'), iv)

Finally, with utf8 (the default, so technically don't need the encoding 'utf8' or even to convert the utf8key to a buffer first at all):

const utf8key = "/8"8'U;Pow*B5>2r"  /* these are 1 byte utf8 characters */
const decipher = crypto.createDecipheriv(algorithm, Buffer.from(utf8key, 'utf8'), iv)

Note: because utf8 encoding of characters can be 1,2,3, or 4 bytes, it is not always simply a case of counting 16 characters.

E.g. The following utf8 strings are all 128 bits (16 bytes) long:
"󠊘𡎈􎀉󎡯񮬨" (4 byte characters)
"䞛䵂荩貽㸅!" (3 byte characters terminated with a 1 byte character)
"Ղߊ˳ĠʲÅͱƸ" (2 byte characters)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment