diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..30cb1a0 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,38 @@ +{ + "env": { + "es6": true, + "node": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "module", + "impliedStrict": true, + "ecmaFeatures": { + "experimentalObjectRestSpread": true + } + }, + "rules": { + "indent": [ + "error", + 4, + { + "SwitchCase": 1 + } + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "double" + ], + "semi": [ + "error", + "always" + ], + "no-console": 0, + "no-unused-vars": "warn" + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8fb1670 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +.basic256rc.js diff --git a/.travis.yml b/.travis.yml index 34a663e..ffdc18a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,7 @@ sudo: enabled group: edge node_js: - "node" - - "7" - - "6" - - "5" - - "4" + - "8" install: - npm install script: diff --git a/DontRunMe.js b/DontRunMe.js index 8eaf6f2..40850dd 100644 --- a/DontRunMe.js +++ b/DontRunMe.js @@ -1,28 +1,60 @@ -'use strict'; +"use strict"; -let crypto = require('crypto'); // define crypto -let fs = require('fs'); // define filesys +const detectNewline = require("detect-newline"); +const crypto = require("crypto"); // define crypto +const fs = require("fs"); // define filesys +let projectRoot = require("path").dirname(require.main.filename).replace(/[\/\\]node_modules[\/\\].*/g, ""); // eslint-disable-line no-useless-escape +let fetchedKey, fetchedHMAC, convertedConfig = false; -function randomValueHex (len) { +const exit = (msg) => { + console.log(msg); + return setTimeout(() => { + process.exit(0); + }, 2000); +}; + +const randomValueHex = (len) => { return crypto.randomBytes(Math.ceil(len/2)) - .toString('hex') // convert to hexadecimal format + .toString("hex") // convert to hexadecimal format .slice(0,len); // return required number of characters }; -if (fs.existsSync("./config.js")) { - return setTimeout(function(){ - process.exit(0); // exit script if config already exists - }, 833); -} +const main = () => { + if (fs.existsSync(`${projectRoot}/.gitignore`)) { + var file = fs.readFileSync(`${projectRoot}/.gitignore`).toString(); + var newlineChar = detectNewline(file); + if (!file.includes(".basic256rc.js")) fs.appendFileSync(`${projectRoot}/.gitignore`, `${newlineChar}.basic256rc.js${newlineChar}`); + } + + if (fs.existsSync(`${projectRoot}/.basic256rc.js`)) { + return exit("\n.basic256rc.js already exists, stopping setup.\n"); + } + + if (fs.existsSync("./config.js")) { + try { + var c = require("./config.js").k; + if (c.key) fetchedKey = c.key; + if (c.hmac_key) fetchedHMAC = c.hmac_key; + convertedConfig = true; + } catch (e) { + fetchedKey = null, + fetchedHMAC = null; + console.warn(`\nThere is an old config.js file in package.\nHowever, reading of the keys have failed:\n\n${e.stack}\n`); + } + } -let key = randomValueHex(32); // create random hex val for enc key -let hmac = randomValueHex(32); // create random hex val for hmac key -let randFold = "./" + randomValueHex(32) + "/"; // create random hex val with filesys encoding for folder -let randFile = randomValueHex(32) + ".json"; // create random hex val with .json ending for file -let resSysop = randFold + randFile; // merge foldername and filename + const enduserconfig = { + key: fetchedKey || randomValueHex(32), // create random hex val for enc key + hmac_key: fetchedHMAC || randomValueHex(32) // create random hex val for hmac key + }; -fs.mkdirSync(randFold); // create folder -fs.appendFileSync(resSysop, "{\n \"key\": \"" + key + "\",\n \"hmac_key\": \"" + hmac + "\"\n}\n"); // create file with keys necessary -fs.appendFileSync("./config.js", "\'use strict\';\n\nvar k = require(\"" + resSysop + "\");\n\nmodule.exports = {\n k\n};\n\n"); // generate config file with necessary info + fs.appendFileSync(`${projectRoot}/.basic256rc.js`, `"use strict"; + +module.exports = ${JSON.stringify(enduserconfig, null, 4)} +`); // generate config file with necessary info + + if (convertedConfig) return exit("\nYour old configuration is saved to a file named .basic256rc.js has been created on the project root.\nDON'T FORGET TO BACK THIS UP.\n"); + return exit("\nA file named .basic256rc.js has been created on the project root. DON'T FORGET TO BACK THIS UP.\n"); +}; -setTimeout(function(){process.exit(0);}, 2000); // exit script +main(); diff --git a/LICENSE b/LICENSE index 2ccfd4d..3d847b3 100644 --- a/LICENSE +++ b/LICENSE @@ -176,7 +176,7 @@ END OF TERMS AND CONDITIONS Copyright 2014 Levi Gross - Copyright 2016 linuxgemini. All Rights Reserved. + Copyright 2018 linuxgemini. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index c39df8c..c8bd960 100644 --- a/README.md +++ b/README.md @@ -3,29 +3,37 @@ basic256.js [![Build Status](https://travis-ci.org/linuxgemini/basic256.js.svg?branch=master)](https://travis-ci.org/linuxgemini/basic256.js) +WARNING +------- + +**THIS PACKAGE SAVES IMPORTANT KEYS ON YOUR PROJECT, DON'T LOSE IT.** + A basic encryption/decryption script/API for resting data for Node.js users. *Slightly* modified the work of [Levi Gross](http://www.levigross.com/2014/03/30/how-to-write-an-encrypt-and-decrypt-api-for-data-at-rest-in-nodejs/). -Usage without downloading from NPM +Usage ----- -Gather basic256.js first, copy all files inside to your project folder/direcory. +Open a terminal in your project folder and make sure that you have a package.json file. -And do this on your terminalif you are not root: +And do this on your terminal if you are not root: - npm install +` +$ npm install --save basic256.js +` -If you are running as root, do this: +Then make your script connected. Example: - npm install --unsafe-perm +```js +const b256 = require("basic256.js"); +const basic256 = new b256(); -Then make your script connected. Example: +var blob = basic256.encrypt("FOO"); // This encrypts the string "FOO". +console.log(blob); // This will show the encrypted string. + +var unblob = basic256.decrypt(blob); // This decrypts the encrypted string. +console.log(unblob); // This will show the decrypted string. (Which in this case, it is "FOO") +``` - var crypter = require("./basic256.js"); - - var blob = crypter.enc.run("FOO"); // This encrypts the string "FOO". - console.log(blob); // This will show the encrypted string. - - var unblob = crypter.dec.run(blob); // This decrypts the encrypted string. - console.log(unblob); // This will show the decrypted string. (Which in this case, it is "FOO") +**Don't forget to back your .basic256rc.js file as it contains your keys to encrypt and decrypt strings.** diff --git a/basic256.js b/basic256.js index 304b2f4..b27b4f0 100644 --- a/basic256.js +++ b/basic256.js @@ -1,90 +1,93 @@ -'use strict'; +"use strict"; + +let crypto = require("crypto"); +let projectRoot = require("path").dirname(require.main.filename).replace(/[\/\\]node_modules[\/\\].*/g, ""); // eslint-disable-line no-useless-escape + +/** + * A basic encryption/decryption script/API for resting data for Node.js users. + * @class + */ +class basic256 { + constructor() { + try { + var savedKeys = require(`${projectRoot}/.basic256rc.js`); + } catch (e) { + throw new Error("An error happened while loading the key"); + } -let crypto = require('crypto'); + this.ALGORITHM = "AES-256-CBC"; + this.HMAC_ALGORITHM = "SHA256"; + this.KEY = savedKeys.key; // Use the automated script. + this.HMAC_KEY = savedKeys.hmac_key; // Use the automated script. + } -function UserException(message) { - this.message = message; - this.name = 'UserException'; -} + encrypt(plain_text) { + if (!plain_text || typeof (plain_text) !== "string") throw new Error("Plain text was not found."); -try { - var savedKeys = require("./config.js").k; -} catch (e) { - throw new UserException('No Configuration Exists'); -} + var IV = Buffer.from(tools.randomValueHex(16)); // ensure that the IV (initialization vector) is random + var encryptor, cipher_text, hmac; -var ALGORITHM, KEY, HMAC_ALGORITHM, HMAC_KEY; + encryptor = crypto.createCipheriv(this.ALGORITHM, this.KEY, IV); + encryptor.setEncoding("hex"); + encryptor.write(plain_text); + encryptor.end(); -ALGORITHM = 'AES-256-CBC'; // CBC because CTR isn't possible with the current version of the Node.JS crypto library -HMAC_ALGORITHM = 'SHA256'; -KEY = savedKeys.key; // Use the automated script. -HMAC_KEY = savedKeys.hmac_key; // Use the automated script. + cipher_text = encryptor.read(); -function randomValueHex (len) { - return crypto.randomBytes(Math.ceil(len/2)) - .toString('hex') // convert to hexadecimal format - .slice(0,len); // return required number of characters -}; + hmac = crypto.createHmac(this.HMAC_ALGORITHM, this.HMAC_KEY); + hmac.update(cipher_text); + hmac.update(IV.toString("hex")); // ensure that both the IV and the cipher-text is protected by the HMAC -var constant_time_compare = function (val1, val2) { - var sentinel; - - if (val1.length !== val2.length) { - return false; + // The IV isn't a secret so it can be stored along side everything else + return cipher_text + "$" + IV.toString("hex") + "$" + hmac.digest("hex"); } + decrypt(cipher_text) { + if (!cipher_text || typeof (cipher_text) !== "string" || !cipher_text.match(/\$/g)) throw new Error("A valid cipher text was not found."); - for (var i = 0; i <= (val1.length - 1); i++) { - sentinel |= val1.charCodeAt(i) ^ val2.charCodeAt(i); - } + var cipher_blob = cipher_text.split("$"); - return sentinel === 0 -}; + if (cipher_blob.length !== 3) throw new Error("Cipher text is broken."); -module.exports = { + var ct = cipher_blob[0]; + var IV = Buffer.from(cipher_blob[1], "hex"); + var hmac = cipher_blob[2]; + var chmac, decryptor; - "enc": { - run : function (plain_text) { + chmac = crypto.createHmac(this.HMAC_ALGORITHM, this.HMAC_KEY); + chmac.update(ct); + chmac.update(IV.toString("hex")); - var IV = Buffer.from(randomValueHex(16)); // ensure that the IV (initialization vector) is random - var encryptor, cipher_text, hmac; + if (!tools.constant_time_compare(chmac.digest("hex"), hmac)) { + throw new Error("Encrypted Blob has been tampered with."); + } - encryptor = crypto.createCipheriv(ALGORITHM, KEY, IV); - encryptor.setEncoding('hex'); - encryptor.write(plain_text); - encryptor.end(); + decryptor = crypto.createDecipheriv(this.ALGORITHM, this.KEY, IV); + var decryptedText = decryptor.update(ct, "hex", "utf-8"); + return decryptedText + decryptor.final("utf-8"); + } - cipher_text = encryptor.read(); +} - hmac = crypto.createHmac(HMAC_ALGORITHM, HMAC_KEY); - hmac.update(cipher_text); - hmac.update(IV.toString('hex')); // ensure that both the IV and the cipher-text is protected by the HMAC +class tools { + static constant_time_compare(val1, val2) { + var sentinel; - // The IV isn't a secret so it can be stored along side everything else - return cipher_text + "$" + IV.toString('hex') + "$" + hmac.digest('hex') + if (val1.length !== val2.length) { + return false; } - }, - - "dec": { - run : function (cipher_text) { - var cipher_blob = cipher_text.split("$"); - var ct = cipher_blob[0]; - var IV = Buffer.from(cipher_blob[1], 'hex'); - var hmac = cipher_blob[2]; - var chmac, decryptor; - - chmac = crypto.createHmac(HMAC_ALGORITHM, HMAC_KEY); - chmac.update(ct); - chmac.update(IV.toString('hex')); - - if (!constant_time_compare(chmac.digest('hex'), hmac)) { - console.log("Encrypted Blob has been tampered with..."); - return null; - } - - decryptor = crypto.createDecipheriv(ALGORITHM, KEY, IV); - var decryptedText = decryptor.update(ct, 'hex', 'utf-8'); - return decryptedText + decryptor.final('utf-8'); + + for (var i = 0; i <= (val1.length - 1); i++) { + sentinel |= val1.charCodeAt(i) ^ val2.charCodeAt(i); } + + return sentinel === 0; + } + static randomValueHex(len) { + return crypto.randomBytes(Math.ceil(len / 2)) + .toString("hex") // convert to hexadecimal format + .slice(0, len); // return required number of characters } } + +module.exports = basic256; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..26253dc --- /dev/null +++ b/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "basic256.js", + "version": "1.2.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=" + } + } +} diff --git a/package.json b/package.json index 12c0918..865a697 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,10 @@ { "name": "basic256.js", - "version": "0.0.1", + "version": "1.2.3", "description": "A basic encryption/decryption script/API for resting data for Node.js users.", + "engines": { + "node": ">=8.4.0" + }, "readme": "README.md", "maintainers": [ "linuxgemini (ilteris@asenkron.com.tr)" @@ -13,7 +16,11 @@ }, "license": "Apache-2.0", "scripts": { - "install": "node ./DontRunMe.js", - "test": "node ./test.js" - } + "install": "node DontRunMe.js", + "test": "node test.js" + }, + "dependencies": { + "detect-newline": "^2.1.0" + }, + "main": "./basic256" } diff --git a/test.js b/test.js index 0e51652..d7622d6 100644 --- a/test.js +++ b/test.js @@ -1,17 +1,59 @@ -'use strict'; +"use strict"; -var m = require("./basic256"); +try { + var base = require("./basic256"); +} catch (error) { + setTimeout(() => { + console.error(`Huge error in library\n${error.stack}`); + process.exit(1); + }, 1000); +} -console.log("Encrypting string \"foo\"..."); -var encStr = m.enc.run("foo"); -console.log("\nDecrypting the string below...\n" + encStr); -var decStr = m.dec.run(encStr); -console.log("\n\nResult: " + decStr); +const basic256 = new base(); -if (decStr === "foo") { - console.log("\nSUCCESS!"); - setTimeout(function(){process.exit(0);},853); -} else { - console.error("\nFAILURE!"); - setTimeout(function(){process.exit(1);},853); +const testText = "Lorem ipsum dolor sit amet."; +let errCount = 0; +let successCount = 0; + +var ciphertext, returningtext; + +try { + ciphertext = basic256.encrypt(testText); + returningtext = basic256.decrypt(ciphertext); + if (returningtext === testText) { + successCount++; + console.log("Initial example works."); + } +} catch (e) { + console.log("Initial example doesn't work."); + errCount++; +} + +try { + ciphertext = basic256.encrypt(testText.split(" ")); // planned error. + errCount++; +} catch (er) { + console.log("String detection before encryption works."); + successCount++; +} + +try { + ciphertext = basic256.encrypt(testText); + returningtext = basic256.decrypt(ciphertext.slice(3)); // planned error. + errCount++; +} catch (err) { + console.log("Cipher text tampering detection works."); + successCount++; } + +if (errCount === 0 && successCount === 3) { + setTimeout(() => { + console.log("Test passed."); + process.exit(0); + }, 2222); +} else { + setTimeout(() => { + console.log("Test failed."); + process.exit(1); + }, 2222); +} \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy