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 new file mode 100644 index 0000000..ffdc18a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: node_js +sudo: enabled +group: edge +node_js: + - "node" + - "8" +install: + - npm install +script: + - npm test diff --git a/Basic256.js b/Basic256.js deleted file mode 100644 index f0db765..0000000 --- a/Basic256.js +++ /dev/null @@ -1,81 +0,0 @@ -// HELP ME MAKE THIS SHITTY CIPHER API GREAT AGAIN // - -crypto = require('crypto'); - -function randomValueHex (len) { - return crypto.randomBytes(Math.ceil(len/2)) - .toString('hex') // convert to hexadecimal format - .slice(0,len); // return required number of characters -}; - -var ALGORITHM, KEY, HMAC_ALGORITHM, HMAC_KEY; - -ALGORITHM = 'AES-256-CBC'; // CBC because CTR isn't possible with the current version of the Node.JS crypto library -HMAC_ALGORITHM = 'SHA256'; -KEY = randomValueHex(32); // This key should be stored in an environment variable -HMAC_KEY = randomValueHex(32); // This key should be stored in an environment variable - -var constant_time_compare = function (val1, val2) { - var sentinel; - - if (val1.length !== val2.length) { - return false; - } - - - for (var i = 0; i <= (val1.length - 1); i++) { - sentinel |= val1.charCodeAt(i) ^ val2.charCodeAt(i); - } - - return sentinel === 0 -}; - -module.exports = { - - "enc": { - run : function (plain_text) { - - var IV = new Buffer(randomValueHex(16)); // ensure that the IV (initialization vector) is random - var cipher_text; - var hmac; - var encryptor; - - encryptor = crypto.createCipheriv(ALGORITHM, KEY, IV); - encryptor.setEncoding('hex'); - encryptor.write(plain_text); - encryptor.end(); - - 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 - - // The IV isn't a secret so it can be stored along side everything else - return cipher_text + "$" + IV.toString('hex') + "$" + hmac.digest('hex') - } - }, - - "dec": { - run : function (cipher_text) { - var cipher_blob = cipher_text.split("$"); - var ct = cipher_blob[0]; - var IV = new Buffer(cipher_blob[1], 'hex'); - var hmac = cipher_blob[2]; - var 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', 'utf8'); - return decryptedText + decryptor.final('utf-8'); - } - } -} diff --git a/DontRunMe.js b/DontRunMe.js new file mode 100644 index 0000000..40850dd --- /dev/null +++ b/DontRunMe.js @@ -0,0 +1,60 @@ +"use strict"; + +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; + +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 + .slice(0,len); // return required number of characters +}; + +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`); + } + } + + 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.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"); +}; + +main(); diff --git a/LICENSE b/LICENSE index 2cf16c0..3d847b3 100644 --- a/LICENSE +++ b/LICENSE @@ -175,8 +175,8 @@ END OF TERMS AND CONDITIONS - Copyright 2014 Levi Gross, tandrewnichols - Copyright 2016 linuxgemini. All Rights Reserved. + Copyright 2014 Levi Gross + 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 f0545af..c8bd960 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,39 @@ -Basic256.js +basic256.js ========================= -A basic encryption/decription script/API for Node.js users. -Based on the work by [Levi Gross](http://www.levigross.com/2014/03/30/how-to-write-an-encrypt-and-decrypt-api-for-data-at-rest-in-nodejs/). +[![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 ----- -Gather Basic256.js, -Make your script connected. Example: +Open a terminal in your project folder and make sure that you have a package.json file. + +And do this on your terminal if you are not root: + +` +$ npm install --save basic256.js +` + +Then make your script connected. Example: + +```js +const b256 = require("basic256.js"); +const basic256 = new b256(); + +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 new file mode 100644 index 0000000..b27b4f0 --- /dev/null +++ b/basic256.js @@ -0,0 +1,93 @@ +"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"); + } + + 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. + } + + encrypt(plain_text) { + if (!plain_text || typeof (plain_text) !== "string") throw new Error("Plain text was not found."); + + var IV = Buffer.from(tools.randomValueHex(16)); // ensure that the IV (initialization vector) is random + var encryptor, cipher_text, hmac; + + encryptor = crypto.createCipheriv(this.ALGORITHM, this.KEY, IV); + encryptor.setEncoding("hex"); + encryptor.write(plain_text); + encryptor.end(); + + cipher_text = encryptor.read(); + + 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 + + // 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."); + + var cipher_blob = cipher_text.split("$"); + + if (cipher_blob.length !== 3) throw new Error("Cipher text is broken."); + + var ct = cipher_blob[0]; + var IV = Buffer.from(cipher_blob[1], "hex"); + var hmac = cipher_blob[2]; + var chmac, decryptor; + + chmac = crypto.createHmac(this.HMAC_ALGORITHM, this.HMAC_KEY); + chmac.update(ct); + chmac.update(IV.toString("hex")); + + if (!tools.constant_time_compare(chmac.digest("hex"), hmac)) { + throw new Error("Encrypted Blob has been tampered with."); + } + + decryptor = crypto.createDecipheriv(this.ALGORITHM, this.KEY, IV); + var decryptedText = decryptor.update(ct, "hex", "utf-8"); + return decryptedText + decryptor.final("utf-8"); + } + +} + +class tools { + static constant_time_compare(val1, val2) { + var sentinel; + + if (val1.length !== val2.length) { + return false; + } + + 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 new file mode 100644 index 0000000..865a697 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "basic256.js", + "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)" + ], + "author": "linuxgemini", + "repository": { + "type": "git", + "url": "git+https://github.com/linuxgemini/basic256.js.git" + }, + "license": "Apache-2.0", + "scripts": { + "install": "node DontRunMe.js", + "test": "node test.js" + }, + "dependencies": { + "detect-newline": "^2.1.0" + }, + "main": "./basic256" +} diff --git a/test.js b/test.js new file mode 100644 index 0000000..d7622d6 --- /dev/null +++ b/test.js @@ -0,0 +1,59 @@ +"use strict"; + +try { + var base = require("./basic256"); +} catch (error) { + setTimeout(() => { + console.error(`Huge error in library\n${error.stack}`); + process.exit(1); + }, 1000); +} + +const basic256 = new base(); + +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