| 
									
										
										
										
											2017-05-19 00:56:09 -05:00
										 |  |  | (function () { | 
					
						
							|  |  |  |   'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var crypto = window.crypto; | 
					
						
							|  |  |  |   var Unibabel = window.Unibabel; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function deriveKey(saltHex, passphrase, iter) { | 
					
						
							|  |  |  |     var keyLenBits = 128; | 
					
						
							|  |  |  |     var kdfname = "PBKDF2"; | 
					
						
							|  |  |  |     var aesname = "AES-CBC"; // AES-CTR is also popular
 | 
					
						
							|  |  |  |     // 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
 | 
					
						
							|  |  |  |     var iterations = iter || 100; // something a browser on a raspberry pi or old phone could do
 | 
					
						
							|  |  |  |     var hashname = "SHA-256"; | 
					
						
							|  |  |  |     var extractable = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     console.log(''); | 
					
						
							|  |  |  |     console.log('passphrase', passphrase); | 
					
						
							|  |  |  |     console.log('salt (hex)', saltHex); | 
					
						
							|  |  |  |     //console.log('salt (hex)', Unibabel.bufferToHex(saltBuf));
 | 
					
						
							|  |  |  |     console.log('iterations', iterations); | 
					
						
							|  |  |  |     console.log('keyLen (bytes)', keyLenBits / 8); | 
					
						
							|  |  |  |     console.log('digest', hashname); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // First, create a PBKDF2 "key" containing the password
 | 
					
						
							|  |  |  |     return crypto.subtle.importKey( | 
					
						
							| 
									
										
										
										
											2017-07-11 15:07:16 -06:00
										 |  |  |       "raw" | 
					
						
							|  |  |  |     , Unibabel.utf8ToBuffer(passphrase) | 
					
						
							|  |  |  |     , { "name": kdfname } | 
					
						
							|  |  |  |     , false | 
					
						
							|  |  |  |     , ["deriveKey"]). | 
					
						
							| 
									
										
										
										
											2017-05-19 00:56:09 -05:00
										 |  |  |     // Derive a key from the password
 | 
					
						
							|  |  |  |     then(function (passphraseKey) { | 
					
						
							|  |  |  |       return crypto.subtle.deriveKey( | 
					
						
							|  |  |  |         { "name": kdfname | 
					
						
							|  |  |  |         , "salt": Unibabel.hexToBuffer(saltHex) | 
					
						
							|  |  |  |         , "iterations": iterations | 
					
						
							|  |  |  |         , "hash": hashname | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       , passphraseKey | 
					
						
							|  |  |  |         // required to be 128 or 256 bits
 | 
					
						
							|  |  |  |       , { "name": aesname, "length": keyLenBits } // 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) { | 
					
						
							|  |  |  |         return new Uint8Array(arrbuf); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }). | 
					
						
							|  |  |  |     catch(function (err) { | 
					
						
							|  |  |  |       window.alert("Key derivation failed: " + err.message); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function test() { | 
					
						
							|  |  |  |     // Part of the salt is application-specific (same on iOS, Android, and Web)
 | 
					
						
							|  |  |  |     var saltHex = '942c2db750b5f57f330226b2b498c6d3'; | 
					
						
							|  |  |  |     var passphrase = 'Pizzas are like children'; | 
					
						
							|  |  |  |     //var passphrase = "I'm a ☢ ☃ who speaks 中国语文!";
 | 
					
						
							|  |  |  |     var iter = 1672; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // NOTE: the salt will be truncated to the length of the hash algo being used
 | 
					
						
							|  |  |  |     return deriveKey(saltHex, passphrase, iter).then(function (keyBuf) { | 
					
						
							|  |  |  |       var hexKey = Unibabel.bufferToHex(keyBuf); | 
					
						
							|  |  |  |       console.log('[TEST] hexKey'); | 
					
						
							|  |  |  |       console.log(hexKey); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   test(); | 
					
						
							|  |  |  | }()); |