(function () { 'use strict'; var hashMap = { 'md5': 'MD5' , 'sha1': 'SHA-1' , 'sha256': 'SHA-256' , 'sha384': 'SHA-384' , 'sha512': 'SHA-512' //, 'sha3': 'SHA-3' }; function getForgeProof(nodeObj) { return new Promise(function (resolve, reject) { var kdf = { node: nodeObj.node , type: nodeObj.type , kdf: 'PBKDF2' , algo: nodeObj.algo || 'SHA-256' , bits: nodeObj.bits || 128 , iter: nodeObj.iter || Math.floor(Math.random() * 100) + 1001 , salt: null }; // generate a password-based 16-byte key // note an optional message digest can be passed as the final parameter if (nodeObj.salt) { kdf.salt = Unibabel.bufferToBinaryString(Unibabel.hexToBuffer(nodeObj.salt)); } else { // uses binary string kdf.salt = forge.random.getBytesSync(16); } // kdf.proof = forge.pkcs5.pbkdf2(nodeObj.secret, kdf.salt, kdf.iter, kdf.byteLen); // generate key asynchronously forge.pkcs5.pbkdf2( nodeObj.secret , kdf.salt , kdf.iter // 100 , (kdf.bits / 8) // 16 , kdf.algo.replace(/\-/g, '').toLowerCase() // sha256 , function(err, derivedKey) { // do something w/derivedKey if (err) { reject(err); return; } kdf.salt = Unibabel.bufferToHex(Unibabel.binaryStringToBuffer(kdf.salt)); kdf.proof = Unibabel.bufferToHex(Unibabel.binaryStringToBuffer(derivedKey)); resolve(kdf); }); }); } function getWebCryptoProof(nodeObj) { if (!window.crypto) { return new Promise(function (resolve, reject) { reject(new Error("Web Crypto Not Implemented")); }); } var crypto = window.crypto; var Unibabel = window.Unibabel; var kdf = { node: nodeObj.node , type: nodeObj.type , kdf: 'PBKDF2' , algo: hashMap[nodeObj.algo] || (nodeObj.algo || 'SHA-256').toUpperCase().replace(/SHA-?/, 'SHA-') , bits: nodeObj.bits || 128 , iter: nodeObj.iter || Math.floor(Math.random() * 100) + 1001 , salt: null }; // generate a password-based 16-byte key // note an optional message digest can be passed as the final parameter if (nodeObj.salt) { kdf.salt = Unibabel.hexToBuffer(nodeObj.salt); } else { // uses binary string kdf.salt = crypto.getRandomValues(new Uint8Array(16)); } // 100 - probably safe even on a browser running from a raspberry pi using pure js ployfill // 10000 - no noticeable speed decrease on my MBP // 100000 - you can notice // 1000000 - annoyingly long // something a browser on a raspberry pi or old phone could do var aesname = "AES-CBC"; // AES-CTR is also popular var extractable = true; // First, create a PBKDF2 "key" containing the passphrase return crypto.subtle.importKey( "raw" , Unibabel.utf8ToBuffer(nodeObj.secret) , { "name": kdf.kdf } , false , ["deriveKey"]). // Derive a key from the password then(function (passphraseKey) { var keyconf = { "name": kdf.kdf , "salt": kdf.salt , "iterations": kdf.iter , "hash": kdf.algo }; return crypto.subtle.deriveKey( keyconf , passphraseKey // required to be 128 or 256 bits , { "name": aesname, "length": kdf.bits } // Key we want , extractable // Extractble , [ "encrypt", "decrypt" ] // For new key ); }). // Export it so we can display it then(function (aesKey) { return crypto.subtle.exportKey("raw", aesKey).then(function (arrbuf) { kdf.salt = Unibabel.bufferToHex(kdf.salt); kdf.proof = Unibabel.bufferToHex(new Uint8Array(arrbuf)); return kdf; }); }); /*. catch(function (err) { window.alert("Key derivation failed: " + err.message); }); */ } // kdf, algo, iter, bits, secret window.getProofOfSecret = function (opts) { return getWebCryptoProof(opts).then(function (data) { return data; }, function (err) { return getForgeProof(opts); }); }; }());