| // Copyright 2012 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| /** |
| * @fileoverview Implementation of PBKDF2 in JavaScript. |
| * @see http://en.wikipedia.org/wiki/PBKDF2 |
| * |
| * Currently we only support HMAC-SHA1 as the underlying hash function. To add a |
| * new hash function, add a static method similar to deriveKeyFromPasswordSha1() |
| * and implement the specific computeBlockCallback() using the hash function. |
| * |
| * Usage: |
| * var key = pbkdf2.deriveKeySha1( |
| * stringToByteArray('password'), stringToByteArray('salt'), 1000, 128); |
| * |
| */ |
| |
| goog.provide('goog.crypt.pbkdf2'); |
| |
| goog.require('goog.array'); |
| goog.require('goog.asserts'); |
| goog.require('goog.crypt'); |
| goog.require('goog.crypt.Hmac'); |
| goog.require('goog.crypt.Sha1'); |
| |
| |
| /** |
| * Derives key from password using PBKDF2-SHA1 |
| * @param {!Array<number>} password Byte array representation of the password |
| * from which the key is derived. |
| * @param {!Array<number>} initialSalt Byte array representation of the salt. |
| * @param {number} iterations Number of interations when computing the key. |
| * @param {number} keyLength Length of the output key in bits. |
| * Must be multiple of 8. |
| * @return {!Array<number>} Byte array representation of the output key. |
| */ |
| goog.crypt.pbkdf2.deriveKeySha1 = function( |
| password, initialSalt, iterations, keyLength) { |
| // Length of the HMAC-SHA1 output in bits. |
| var HASH_LENGTH = 160; |
| |
| /** |
| * Compute each block of the key using HMAC-SHA1. |
| * @param {!Array<number>} index Byte array representation of the index of |
| * the block to be computed. |
| * @return {!Array<number>} Byte array representation of the output block. |
| */ |
| var computeBlock = function(index) { |
| // Initialize the result to be array of 0 such that its xor with the first |
| // block would be the first block. |
| var result = goog.array.repeat(0, HASH_LENGTH / 8); |
| // Initialize the salt of the first iteration to initialSalt || i. |
| var salt = initialSalt.concat(index); |
| var hmac = new goog.crypt.Hmac(new goog.crypt.Sha1(), password, 64); |
| // Compute and XOR each iteration. |
| for (var i = 0; i < iterations; i++) { |
| // The salt of the next iteration is the result of the current iteration. |
| salt = hmac.getHmac(salt); |
| result = goog.crypt.xorByteArray(result, salt); |
| } |
| return result; |
| }; |
| |
| return goog.crypt.pbkdf2.deriveKeyFromPassword_( |
| computeBlock, HASH_LENGTH, keyLength); |
| }; |
| |
| |
| /** |
| * Compute each block of the key using PBKDF2. |
| * @param {Function} computeBlock Function to compute each block of the output |
| * key. |
| * @param {number} hashLength Length of each block in bits. This is determined |
| * by the specific hash function used. Must be multiple of 8. |
| * @param {number} keyLength Length of the output key in bits. |
| * Must be multiple of 8. |
| * @return {!Array<number>} Byte array representation of the output key. |
| * @private |
| */ |
| goog.crypt.pbkdf2.deriveKeyFromPassword_ = |
| function(computeBlock, hashLength, keyLength) { |
| goog.asserts.assert(keyLength % 8 == 0, 'invalid output key length'); |
| |
| // Compute and concactate each block of the output key. |
| var numBlocks = Math.ceil(keyLength / hashLength); |
| goog.asserts.assert(numBlocks >= 1, 'invalid number of blocks'); |
| var result = []; |
| for (var i = 1; i <= numBlocks; i++) { |
| var indexBytes = goog.crypt.pbkdf2.integerToByteArray_(i); |
| result = result.concat(computeBlock(indexBytes)); |
| } |
| |
| // Trim the last block if needed. |
| var lastBlockSize = keyLength % hashLength; |
| if (lastBlockSize != 0) { |
| var desiredBytes = ((numBlocks - 1) * hashLength + lastBlockSize) / 8; |
| result.splice(desiredBytes, (hashLength - lastBlockSize) / 8); |
| } |
| return result; |
| }; |
| |
| |
| /** |
| * Converts an integer number to a 32-bit big endian byte array. |
| * @param {number} n Integer number to be converted. |
| * @return {!Array<number>} Byte Array representation of the 32-bit big endian |
| * encoding of n. |
| * @private |
| */ |
| goog.crypt.pbkdf2.integerToByteArray_ = function(n) { |
| var result = new Array(4); |
| result[0] = n >> 24 & 0xFF; |
| result[1] = n >> 16 & 0xFF; |
| result[2] = n >> 8 & 0xFF; |
| result[3] = n & 0xFF; |
| return result; |
| }; |