77 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			77 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
(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(
 | 
						|
      "raw",
 | 
						|
      Unibabel.utf8ToBuffer(passphrase),
 | 
						|
      { "name": kdfname },
 | 
						|
      false,
 | 
						|
      ["deriveKey"]).
 | 
						|
    // 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();
 | 
						|
}());
 |