etkframework
Version:
First test release of Etk over colored coins SDK
174 lines (156 loc) • 6.92 kB
JavaScript
/**
* Etk Helper library.
* == Zero-Knowledge Secure Key Server Storage ==
* == Host Proof Hosting ==
* The module uses pbkdf2-sha256 library for SHA256 hash function generation.
*
* Copyright (C) 2015 Akul Mathur
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Parts of the software are provided under separate licenses, as follows:
"colu-nodejs" SDK is under the MIT License
"pbkdf2-sha256" is under the BSD License
"bip38" is under the MIT License
"scryptsy" is under the MIT License
"coinstring" is under the MIT License
* Core Developer(s): @codecakes Akul Mathur
* Maintainer(s):
* @codecakes Akul Mathur
* Description:
*
* The expected outcome can be understood by understanding the
* Stanford JS Crypto Library for Key Derivation Function. A demo is here:
* http://bitwiseshiftleft.github.io/sjcl/demo/
*
* Encryption:
* The applied process/suggested usage, is like this:
* 1. User accesses client app/web UI that can access encrypted string;
* 2. User enters password
* 3. The local client app/web UI uses a KDF to derive an encryption key from user's password
* 4. Client app/web UI decrypts the string using the key.
*
* Decryption:
* Since this Token CC module is "browserify"-ied,
* the applied implementation layer that calls this function does the following:
* 1. After Client verification via SRP Authentication protocol, the encrypted cipher
* is retrieved using the user Password, key and initialization vector stored for the client.
* 2. PBKDF uses verification id, IV and a hashed key to decrypt the string to the original key;
* 3. Step #2 is repeated to get Seed Key and Address Pvt Key on client side
* thus no one but the owner/client has the secure keys.
*
*/
;
const
crypto = require("crypto"),
pbkdf2 = require("pbkdf2-sha256"),
algorithm = 'aes-256-ctr',
randomBytesSize = 16,
iterations = 4096,
keyLenBytes = 32,
HMAC_ALGORITHM = 'SHA256',
HMAC_KEY = crypto.randomBytes(32), // This key should be stored in an environment variable
hashedLen = function hashedLen (hashed, byteLen) {
byteLen = byteLen || 16;
for (var x=0, ln = hashed.length; x<32-ln; x++ ) {
hashed += (hashed[x]);
}
return (new Buffer(hashed)).slice(0,byteLen);
},
encrypt = function encrypt (password, secret, iv, buf, encryptCB) {
/**
* @params:
* - password: A User supplied password
* - secret: The User Credential that needs to be encrypted and stored on server
* - encryptCB: A Callback function that works on the result
* Should be encryptCB(cipherText, key, iv)
*
* Usually, this is either the decrypt function OR storage function;
*
* @Returns:
* - cipherText, key, iv, err
* err is undefined is operation successful.
*/
var err;
try {
const
// buf = new Buffer(crypto.randomBytes(randomBytesSize)),//this is the salt
// iv = new Buffer(crypto.randomBytes(randomBytesSize)),
//use pbkdf2 to derive key
key = pbkdf2( password, buf.toString('ascii'), iterations, keyLenBytes ),
/** Following can also be used
* encryptor.setEncoding('hex');
encryptor.write(plain_text);
encryptor.end();
cipher_text = encryptor.read();
*/
cipher = crypto.createCipheriv(algorithm, key, iv),
cipherText = cipher.update(secret, 'utf8', 'hex') + cipher.final('hex');
/** store buf:iv:cipherText
* For eg
* encryptCB could use
* hashCipherText() function in this module
* to store the crypted version of cipherText.
* This way the server doesn't know what the cipherText is
* to recover the pvt key.
*/
encryptCB(err, cipherText, iv);
}
catch (e) {
err = e;
encryptCB(err, null, null);
}
},
decrypt = function decrypt (password, buf, cipherText, iv, cbDecrypt) {
let err;
const
key = pbkdf2( password, buf.toString('ascii'), iterations, keyLenBytes );
try {
if (cipherText === key && key === iv && iv === null) {
throw Error("Null Values");
}
const
decipher = crypto.createDecipheriv(algorithm, key, iv),
//decipherText or usually the pvtkey;
decipherText = decipher.update(cipherText, 'hex', 'utf8') + decipher.final('utf8');
cbDecrypt( err, decipherText);
} catch (e) {
cbDecrypt(err, null);
}
},
hashCipherText = function hashCipherText (cipherText, password) {
const
cipher = crypto.createCipher(algorithm, password),
crypted = cipher.update(cipherText,'utf8','hex') + cipher.final('hex');
return crypted;
},
dehashCipherText = function dehashCipherText (cryptedText, password) {
const
decipher = crypto.createDecipher(algorithm, password),
cipherText = decipher.update(cryptedText,'hex','utf8') + decipher.final('utf8');
return cipherText;
};
exports.hashedLen = hashedLen;
exports.encrypt = encrypt;
exports.decrypt = decrypt;
exports.hashCipherText = hashCipherText;
exports.dehashCipherText = dehashCipherText;
// For testing and debugging purpose only
// var password = "breakItDown",
// secret = "Scotty too Notty",
// buf = new Buffer(crypto.randomBytes(randomBytesSize)),//this is the salt
// iv = new Buffer(crypto.randomBytes(randomBytesSize));
// console.log(buf.toString('hex'), iv.toString('hex'));
// encrypt(password, secret, iv, buf, function(err, cipherText, iv) {
// console.log(cipherText) ;
// decrypt(password, buf, cipherText, iv, function(err, decipherText) {
// console.log(decipherText);
// });
// });