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