Skip to content
This repository has been archived by the owner on Sep 26, 2024. It is now read-only.

Commit

Permalink
Change Cryptr to custom encrypter for stronger encryption
Browse files Browse the repository at this point in the history
  • Loading branch information
Petteri authored and Petteri committed Jan 6, 2018
1 parent b7076d2 commit f2f2769
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 62 deletions.
84 changes: 36 additions & 48 deletions StudioHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
const request = require('request'),
mime = require('mime-types'),
Promise = require('bluebird'),
secretHelper = require('./lib/secret-helper'),
fs = require('fs'),
path = require('path'),
os = require('os'),
Cryptr = require('cryptr'),
ignore = require('ignore'),
throat = require('throat')(Promise),
ProgressBar = require('progress');
Expand Down Expand Up @@ -57,8 +57,7 @@ class StudioHelper {
throw Error('StudioHelper#constructor: settings.studio must be set');
}

this.credentialsSecret = this._createCredentialsSecret(CREDENTIALS_SECRET_BASE);
this.cryptr = new Cryptr(this.credentialsSecret);
this._createCredentialsSecret(CREDENTIALS_SECRET_BASE);
this.apiUrl = 'https://' + settings.studio + API_URL;
this.authToken = '';

Expand Down Expand Up @@ -165,34 +164,37 @@ class StudioHelper {
* @private
*/
_createCredentialsSecret(secretBase) {
// Include mac address in secret
let mac = this._getFirstMac();
// Add parts of current path for little bit of extra secrecy :P
let currentDirParts = __dirname.split('').map((l,i) => (i + 1) % 3 ? l : '').join('');
// And cpu model
let cpuModel = '';

let cpus = os.cpus();
if (cpus && cpus.length) {
cpuModel = cpus[0].model;
}
const secretStr = secretBase +
secretHelper.getFirstMAC() +
secretHelper.getCPUModel() +
secretHelper.getCurrentPath();

this._credentialsSecret = secretStr;

return secretBase + mac + currentDirParts + cpuModel;
return this._credentialsSecret;
}

/**
* @private
*/
_getCredentialsSecret() {
return this._credentialsSecret;
}

/**
* @private
*/
_getCredentials() {
let data = null;
let dataString = null;

try {
data = JSON.parse(fs.readFileSync(this.credentialsFile, 'utf8'));
dataString = fs.readFileSync(this.credentialsFile, 'utf8');
} catch (e) {
}

if (data) {
data = this._getDecryptedData(data);
if (dataString) {
data = this._getDecryptedData(dataString);
}

return data;
Expand Down Expand Up @@ -553,45 +555,31 @@ class StudioHelper {
/**
* @private
*/
_getEncryptedData(data) {
let encryptedData = null;
for (let key in data) {
if (data.hasOwnProperty(key)) {
if (!encryptedData) {
encryptedData = {};
}
_getEncryptedString(data) {
const encryptedString = secretHelper.encryptSync(
this._getCredentialsSecret(),
JSON.stringify(data)
);

encryptedData[key] = this.cryptr.encrypt(data[key]);
}
}
return encryptedData;
return encryptedString;
}

/**
* @private
*/
_getDecryptedData(data) {
let decryptedData = null;
let decrypted = false;
_getDecryptedData(dataString) {
let data = null;

try {
for (let key in data) {
if (data.hasOwnProperty(key)) {
if (!decryptedData) {
decryptedData = {};
}

decryptedData[key] = this.cryptr.decrypt(data[key]);
const decryptedString = secretHelper.decryptSync(
this._getCredentialsSecret(),
dataString
);

// Test that authToken is formatted correctly
if (key === 'authToken' && /^[\w:\-]+$/.test(decryptedData[key])) {
decrypted = true;
}
}
}
data = JSON.parse(decryptedString);
} catch (e) {}

return decrypted ? decryptedData : null;
return data;
}

/**
Expand All @@ -600,9 +588,9 @@ class StudioHelper {
_updateCredentials(data) {
let self = this;

data = this._getEncryptedData(data);
const dataString = this._getEncryptedString(data);

fs.writeFile(this.credentialsFile, JSON.stringify(data), function(err) {
fs.writeFile(this.credentialsFile, dataString, function(err) {
if (err) {
self._log(err);
}
Expand Down
160 changes: 160 additions & 0 deletions lib/secret-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
const os = require('os');
const Crypto = require('crypto');

class SecretHelper {
constructor() {
this.iterations = 196935;
}
/**
* Get first non-internal MAC address if any available
* @return {string} MAC
*/
getFirstMAC() {
const allowed = ['eth0', 'eth1', 'en0', 'en1'];
const interfaces = os.networkInterfaces();

for (let iface in interfaces) {
if (interfaces.hasOwnProperty(iface)) {
const interfaceArr = interfaces[iface];

if (allowed.indexOf(iface) !== -1) {
for (let i = 0, l = interfaceArr.length; i < l; i++) {
const addressData = interfaceArr[i];

if (!addressData.internal && addressData.mac) {
return addressData.mac;
}
}
}
}
}

return '';
}

/**
* Get first CPU Model
* @return {string} CPU
*/
getCPUModel() {
// And cpu model
const cpus = os.cpus();
if (cpus && cpus.length) {
return cpus[0].model;
}
}

/**
* Get parts of current path
* @param {number} [divider=3] Return every nth letter, default is 3
* @return {string} path
*/
getCurrentPath(divider) {
divider = divider >= 0 ? divider : 3;
// Add parts of current path for little bit of extra secrecy :P
return __dirname.split('').map((l,i) => (i + 1) % divider ? l : '').join('');
}

/**
* @private
* @param {string} secret - Secret string
* @param {string} salt - Salt string
* @return {Buffer} key
*/
getKeyBuffer(secret, salt) {
const iterations = this.iterations || 196934;

return new Promise((resolve, reject) => {
Crypto.pbkdf2(secret, salt, iterations, 16, 'sha512', (err, derivedKey) => {
if (err) {
return reject(err);
}
//console.log('key', derivedKey.toString('hex'));
return resolve(derivedKey);
});
});
}

/**
* @private
* @param {string} secret - Secret string
* @param {string} salt - Salt string
* @return {Buffer} key
*/
getKeyBufferSync(secret, salt) {
const iterations = this.iterations || 196934;

return Crypto.pbkdf2Sync(secret, salt, iterations, 16, 'sha512');
}

/**
* Encrypts string
* @param {string} secret - Encrypt using this as secret
* @param {string} dataString - String to encrypt
* @param {Object} options - options
* @return {Promise} hash string
*/
encrypt(secret, dataString, options) {
const iv = Crypto.randomBytes(16);
const salt = Crypto.randomBytes(16);

const getCipher = (keyBuffer) => {
const cipher = Crypto.createCipheriv('aes-128-cbc', keyBuffer, iv);
const encrypted = cipher.update(dataString);
const finalBuffer = Buffer.concat([encrypted, cipher.final()]);

//Need to retain salt and IV for decryption, so this can be appended to the output with a separator (non-hex for this example)
return salt.toString('hex') + ':' + iv.toString('hex') + ':' + finalBuffer.toString('hex');
}

if (options && options.sync) {
const keyBuffer = this.getKeyBufferSync(secret, salt);
return getCipher(keyBuffer);
}

return this.getKeyBuffer(secret, salt).then(keyBuffer => {
return getCipher(keyBuffer);
});
}

/**
* Decrypts string
* @param {string} secret - Decrypt using this as secret
* @param {string} dataString - String to decrypt as provided by secretHelper.encrypt() (salt:iv:cipher)
* @param {Object} options - options
* @return {undefined}
*/
decrypt(secret, dataString, options) {
const parts = dataString.split(':');
const salt = new Buffer(parts[0], 'hex');
const iv = new Buffer(parts[1], 'hex');
const cipher = new Buffer(parts[2], 'hex');

const decipher = (keyBuffer) => {
const decipher = Crypto.createDecipheriv('aes-128-cbc', keyBuffer, iv);
const decrypted = decipher.update(cipher);
const clearText = Buffer.concat([decrypted, decipher.final()]).toString();

return clearText;
}

if(options && options.sync) {
const keyBuffer = this.getKeyBufferSync(secret, salt);
return decipher(keyBuffer);
}

return this.getKeyBuffer(secret, salt).then(keyBuffer => {
return decipher(keyBuffer);
});
}

decryptSync(secret, dataString) {
return this.decrypt(secret, dataString, {sync: true});
}

encryptSync(secret, dataString) {
return this.encrypt(secret, dataString, {sync: true});
}
}

module.exports = new SecretHelper();
22 changes: 9 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"author": "Petteri",
"dependencies": {
"bluebird": "^3.4.5",
"cryptr": "^2.0.0",
"ignore": "^3.1.5",
"inquirer": "^1.1.3",
"mime-types": "^2.1.11",
Expand Down

0 comments on commit f2f2769

Please sign in to comment.