2017-05-19 05:56:09 +00:00
|
|
|
(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(
|
2017-07-11 21:07:16 +00:00
|
|
|
"raw"
|
|
|
|
, Unibabel.utf8ToBuffer(nodeObj.secret)
|
|
|
|
, { "name": kdf.kdf }
|
|
|
|
, false
|
|
|
|
, ["deriveKey"]).
|
2017-05-19 05:56:09 +00:00
|
|
|
// 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);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}());
|