/* * Copyright (C) Sapphirecode - All Rights Reserved * Created by Timo Hocker , March 2020 */ // @ts-nocheck /* eslint-disable no-magic-numbers */ 'use strict'; const crypto = require ('crypto'); const encoding = require ('@scode/encoding-helper'); const encryption_mode_cbc_256 = { algorithm: 'aes-256-cbc', nonce_size: 16, key_size: 32, hash: 'sha256', salt_size: 16, iterations: 32767 }; const encryption_mode_cbc_128 = { algorithm: 'aes-128-cbc', nonce_size: 16, key_size: 16, hash: 'sha256', salt_size: 16, iterations: 40 }; /** * creates a random string * * @param {number} len string length default: 6 * @returns {string} random string */ function random_string (len = 8) { if (len < 1) throw new Error ('invalid length'); return crypto.randomBytes (Math.ceil (len * 3 / 4)) .toString ('base64') .substr (0, len); } /** * creates a random hexadecimal string * * @param {number} len length * @returns {string} hex string */ function random_hex (len = 8) { if (len < 1) throw new Error ('invalid length'); return crypto.randomBytes (Math.ceil (len / 2)) .toString ('hex') .substr (0, len); } /** * creates a 64 character long random hex string * * @returns {string} salt */ function create_salt () { return random_hex (64); } /** * creates a sha512 hash * * @param {string} str string input * @param {string} salt salt * @returns {string} salt */ function hash_sha512 (str, salt) { const md = crypto.createHash ('sha512'); md.update (str); md.update (salt); return md.digest ('hex'); } /** * sign an object * * @param {any} obj object to sign * @param {string} key key to use * @param {string} key_info key identifier * @returns {string} signed object */ function sign_object (obj, key, key_info = null) { const payload = { iat: Date.now (), key_info, obj }; const str = encoding.to_b64 (JSON.stringify (payload)); const token = encoding.to_b64 (hash_sha512 (str, key), 'hex'); const res = `${str}.${token}`; return encodeURIComponent (res); } /** * verify a signed object and return its contents * * @param {string} str string to verify * @param {string} key used key * @param {number} timeout timeout (optional) * @returns {any} returns object if successful, else null */ function verify_signature (str, key, timeout = 0) { const dec = decodeURIComponent (str) .split ('.'); const json = JSON.parse (encoding.to_utf8 (dec[0], 'base64')); const token = encoding.to_hex (dec[1], 'base64'); const verify_token = hash_sha512 (dec[0], key); if (token !== verify_token) return null; const time = Date.now () - json.iat; if (timeout !== 0 && time > timeout) return null; return json.obj; } /** * get a signed object info and data * * @param {string} str string to decode * @returns {any} data */ function get_signature_info (str) { const dec = decodeURIComponent (str) .split ('.'); const json = JSON.parse (encoding.to_utf8 (dec[0], 'base64')); return json; } /** * decode a signed object without verifying the signature * * @param {string} str string to decode * @returns {any} object */ function decode_signed (str) { return get_signature_info (str).obj; } /** * creates a sha256 hash * * @param {any} data input * @returns {string} hash */ function checksum (data) { const md = crypto.createHash ('sha256'); md.update (String (data)); return md.digest ('hex'); } /** * encrypt plain text with aes * * @param {string} text plaintext * @param {string} pass password * @param {object} mode encryption mode * @returns {string} encrypted */ function encrypt_aes (text, pass, mode = encryption_mode_cbc_256) { const salt = crypto.randomBytes (mode.salt_size); // eslint-disable-next-line no-sync const key = crypto.pbkdf2Sync ( Buffer.from (pass), salt, mode.iterations, mode.key_size, mode.hash ); const nonce = crypto.randomBytes (mode.nonce_size); const cipher = crypto.createCipheriv (mode.algorithm, key, nonce); return Buffer.concat ([ salt, nonce, cipher.update (Buffer.from (text)), cipher.final () ]) .toString ('base64'); } /** * decrypt an aes string * * @param {string} ciphertext encrypted text * @param {string} pass password * @param {object} mode encryption mode * @param {boolean} rethrow rethrow exceptions instead of returning null * @returns {string} plaintext */ function decrypt_aes ( ciphertext, pass, mode = encryption_mode_cbc_256, rethrow = false ) { try { let buf = Buffer.from (ciphertext, 'base64'); const salt = buf.slice (0, mode.salt_size); buf = buf.slice (mode.salt_size); // eslint-disable-next-line no-sync const key = crypto.pbkdf2Sync ( Buffer.from (pass), salt, mode.iterations, mode.key_size, mode.hash ); const nonce = buf.slice (0, mode.nonce_size); buf = buf.slice (mode.nonce_size); const cipher = crypto.createDecipheriv (mode.algorithm, key, nonce); return Buffer.concat ([ cipher.update (buf), cipher.final () ]) .toString (); } catch (e) { if (rethrow) throw e; } return null; } module.exports = { checksum, create_salt, decode_signed, decrypt_aes, encrypt_aes, encryption_mode_cbc_128, encryption_mode_cbc_256, get_signature_info, hash_sha512, random_hex, random_string, sign_object, verify_signature };