Last active
February 20, 2022 11:07
-
-
Save ajmas/b7c2df45d69a131b62b18ae91ee0602b to your computer and use it in GitHub Desktop.
Code to encrypt a Sequelize fields
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For a smoother
key
generation. I'd suggest usingBuffer.from(key, encoding)
instead.With base64 encoding of 128 bits (128/8 = 16 bytes)
Note: 24 characters if all characters utf8 1byte characters, i.e. 24 bytes = 192 bits if we naively put into:
Instead, to get the proper 128 bits with:
Similarly, with a hex encoding:
and used with:
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):
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)