From 850515cc42744562d369fb7f6e608c30d2a3cb35 Mon Sep 17 00:00:00 2001 From: Timo Hocker Date: Wed, 4 Mar 2020 14:51:22 +0100 Subject: [PATCH] add encryption --- Jenkinsfile | 2 +- index.js | 78 ++++++++++++++++++++++++++++++++++++++++++++++ test/encryption.js | 21 +++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 test/encryption.js diff --git a/Jenkinsfile b/Jenkinsfile index 81d532c..12d359d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,7 @@ pipeline { VERSION = VersionNumber([ versionNumberString: '${BUILDS_ALL_TIME}', - versionPrefix: '1.0.', + versionPrefix: '1.1.', worstResultForIncrement: 'SUCCESS' ]) } diff --git a/index.js b/index.js index f3186e6..5d49b9e 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,16 @@ const crypto = require ('crypto'); const encoding = require ('@scode/encoding-helper'); +const encryption = { + algorithm: 'aes-256-gcm', + nonce_size: 12, + tag_size: 16, + key_size: 16, + hash: 'sha256', + salt_size: 16, + iterations: 32767 +}; + /** * creates a random string * @@ -134,10 +144,78 @@ function checksum (data) { return md.digest ('hex'); } +/** + * encrypt plain text with aes + * + * @param {string} text plaintext + * @param {string} pass password + * @returns {string} encrypted + */ +function encrypt_aes (text, pass) { + const salt = crypto.randomBytes (16); + // eslint-disable-next-line no-sync + const key = crypto.pbkdf2Sync ( + Buffer.from (pass, 'utf-8'), + salt, + 32767, + 32, + 'sha256' + ); + const nonce = crypto.randomBytes (12); + const cipher = crypto.createCipheriv ('aes-256-gcm', key, nonce); + return Buffer.concat ([ + salt, + nonce, + cipher.update (Buffer.from (text)), + cipher.final (), + cipher.getAuthTag () + ]) + .toString ('base64'); +} + +/** + * decrypt an aes string + * + * @param {string} ciphertext encrypted text + * @param {string} pass password + * @returns {string} plaintext + */ +function decrypt_aes (ciphertext, pass) { + const buf = Buffer.from (ciphertext, 'base64'); + const salt = buf.slice (0, encryption.salt_size); + // eslint-disable-next-line no-sync + const key = crypto.pbkdf2Sync ( + Buffer.from (pass, 'utf-8'), + salt, + encryption.iterations, + encryption.key_size, + encryption.hash + ); + const nonce = buf.slice (encryption.salt_size, encryption.nonce_size); + const enc = buf.slice ( + encryption.salt_size + encryption.nonce_size, + buf.length - encryption.salt_size - encryption.tag_size + ); + const tag = buf.slice ( + encryption.salt_size + + encryption.nonce_size + + enc.length + ); + const cipher = crypto.createDecipheriv (encryption.algorithm, key, nonce); + cipher.setAuthTag (tag); + return Buffer.concat ([ + cipher.update (enc), + cipher.final () + ]) + .toString ('utf-8'); +} + module.exports = { checksum, create_salt, decode_signed, + decrypt_aes, + encrypt_aes, get_signature_info, hash_sha512, random_hex, diff --git a/test/encryption.js b/test/encryption.js new file mode 100644 index 0000000..3637a03 --- /dev/null +++ b/test/encryption.js @@ -0,0 +1,21 @@ +'use strict'; + +const test = require ('ava'); +const crypto = require ('../index'); + +test ('encryption', (t) => { + const enc = crypto.encrypt_aes ('foo', 'bar'); + t.is (typeof enc, 'string'); +}); + +test ('decryption', (t) => { + const enc = crypto.encrypt_aes ('foo', 'bar'); + const dec = crypto.decrypt_aes (enc, 'bar'); + t.is (dec, 'foo'); +}); + +test ('fail decryption', (t) => { + const enc = crypto.encrypt_aes ('foo', 'bar'); + const dec = crypto.decrypt_aes (enc, 'baz'); + t.is (dec, null); +});