walnut.js/lib/com.daplie.walnut.current/scripts/daplie-pbkdf2.js

142 lines
4.2 KiB
JavaScript

(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);
});
};
}());