Adding float-no-zero branch hosted build
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,162 @@
|
||||
// Copyright 2005 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 ARC4 streamcipher implementation. A description of the
|
||||
* algorithm can be found at:
|
||||
* http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt.
|
||||
*
|
||||
* Usage:
|
||||
* <code>
|
||||
* var arc4 = new goog.crypt.Arc4();
|
||||
* arc4.setKey(key);
|
||||
* arc4.discard(1536);
|
||||
* arc4.crypt(bytes);
|
||||
* </code>
|
||||
*
|
||||
* Note: For converting between strings and byte arrays, goog.crypt.base64 may
|
||||
* be useful.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.Arc4');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* ARC4 streamcipher implementation.
|
||||
* @constructor
|
||||
*/
|
||||
goog.crypt.Arc4 = function() {
|
||||
/**
|
||||
* A permutation of all 256 possible bytes.
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.state_ = [];
|
||||
|
||||
/**
|
||||
* 8 bit index pointer into this.state_.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.index1_ = 0;
|
||||
|
||||
/**
|
||||
* 8 bit index pointer into this.state_.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.index2_ = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the cipher for use with new key.
|
||||
* @param {Array.<number>} key A byte array containing the key.
|
||||
* @param {number=} opt_length Indicates # of bytes to take from the key.
|
||||
*/
|
||||
goog.crypt.Arc4.prototype.setKey = function(key, opt_length) {
|
||||
goog.asserts.assertArray(key, 'Key parameter must be a byte array');
|
||||
|
||||
if (!opt_length) {
|
||||
opt_length = key.length;
|
||||
}
|
||||
|
||||
var state = this.state_;
|
||||
|
||||
for (var i = 0; i < 256; ++i) {
|
||||
state[i] = i;
|
||||
}
|
||||
|
||||
var j = 0;
|
||||
for (var i = 0; i < 256; ++i) {
|
||||
j = (j + state[i] + key[i % opt_length]) & 255;
|
||||
|
||||
var tmp = state[i];
|
||||
state[i] = state[j];
|
||||
state[j] = tmp;
|
||||
}
|
||||
|
||||
this.index1_ = 0;
|
||||
this.index2_ = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Discards n bytes of the keystream.
|
||||
* These days 1536 is considered a decent amount to drop to get the key state
|
||||
* warmed-up enough for secure usage. This is not done in the constructor to
|
||||
* preserve efficiency for use cases that do not need this.
|
||||
* NOTE: Discard is identical to crypt without actually xoring any data. It's
|
||||
* unfortunate to have this code duplicated, but this was done for performance
|
||||
* reasons. Alternatives which were attempted:
|
||||
* 1. Create a temp array of the correct length and pass it to crypt. This
|
||||
* works but needlessly allocates an array. But more importantly this
|
||||
* requires choosing an array type (Array or Uint8Array) in discard, and
|
||||
* choosing a different type than will be passed to crypt by the client
|
||||
* code hurts the javascript engines ability to optimize crypt (7x hit in
|
||||
* v8).
|
||||
* 2. Make data option in crypt so discard can pass null, this has a huge
|
||||
* perf hit for crypt.
|
||||
* @param {number} length Number of bytes to disregard from the stream.
|
||||
*/
|
||||
goog.crypt.Arc4.prototype.discard = function(length) {
|
||||
var i = this.index1_;
|
||||
var j = this.index2_;
|
||||
var state = this.state_;
|
||||
|
||||
for (var n = 0; n < length; ++n) {
|
||||
i = (i + 1) & 255;
|
||||
j = (j + state[i]) & 255;
|
||||
|
||||
var tmp = state[i];
|
||||
state[i] = state[j];
|
||||
state[j] = tmp;
|
||||
}
|
||||
|
||||
this.index1_ = i;
|
||||
this.index2_ = j;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* En- or decrypt (same operation for streamciphers like ARC4)
|
||||
* @param {Array.<number>|Uint8Array} data The data to be xor-ed in place.
|
||||
* @param {number=} opt_length The number of bytes to crypt.
|
||||
*/
|
||||
goog.crypt.Arc4.prototype.crypt = function(data, opt_length) {
|
||||
if (!opt_length) {
|
||||
opt_length = data.length;
|
||||
}
|
||||
var i = this.index1_;
|
||||
var j = this.index2_;
|
||||
var state = this.state_;
|
||||
|
||||
for (var n = 0; n < opt_length; ++n) {
|
||||
i = (i + 1) & 255;
|
||||
j = (j + state[i]) & 255;
|
||||
|
||||
var tmp = state[i];
|
||||
state[i] = state[j];
|
||||
state[j] = tmp;
|
||||
|
||||
data[n] ^= state[(state[i] + state[j]) & 255];
|
||||
}
|
||||
|
||||
this.index1_ = i;
|
||||
this.index2_ = j;
|
||||
};
|
||||
@@ -0,0 +1,272 @@
|
||||
// Copyright 2007 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 Base64 en/decoding. Not much to say here except that we
|
||||
* work with decoded values in arrays of bytes. By "byte" I mean a number
|
||||
* in [0, 255].
|
||||
*
|
||||
* @author doughtie@google.com (Gavin Doughtie)
|
||||
* @author fschneider@google.com (Fritz Schneider)
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.base64');
|
||||
goog.require('goog.crypt');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
// Static lookup maps, lazily populated by init_()
|
||||
|
||||
|
||||
/**
|
||||
* Maps bytes to characters.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.base64.byteToCharMap_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Maps characters to bytes.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.base64.charToByteMap_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Maps bytes to websafe characters.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.base64.byteToCharMapWebSafe_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Maps websafe characters to bytes.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.base64.charToByteMapWebSafe_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Our default alphabet, shared between
|
||||
* ENCODED_VALS and ENCODED_VALS_WEBSAFE
|
||||
* @type {string}
|
||||
*/
|
||||
goog.crypt.base64.ENCODED_VALS_BASE =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
|
||||
'abcdefghijklmnopqrstuvwxyz' +
|
||||
'0123456789';
|
||||
|
||||
|
||||
/**
|
||||
* Our default alphabet. Value 64 (=) is special; it means "nothing."
|
||||
* @type {string}
|
||||
*/
|
||||
goog.crypt.base64.ENCODED_VALS =
|
||||
goog.crypt.base64.ENCODED_VALS_BASE + '+/=';
|
||||
|
||||
|
||||
/**
|
||||
* Our websafe alphabet.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.crypt.base64.ENCODED_VALS_WEBSAFE =
|
||||
goog.crypt.base64.ENCODED_VALS_BASE + '-_.';
|
||||
|
||||
|
||||
/**
|
||||
* Whether this browser supports the atob and btoa functions. This extension
|
||||
* started at Mozilla but is now implemented by many browsers. We use the
|
||||
* ASSUME_* variables to avoid pulling in the full useragent detection library
|
||||
* but still allowing the standard per-browser compilations.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.crypt.base64.HAS_NATIVE_SUPPORT = goog.userAgent.GECKO ||
|
||||
goog.userAgent.WEBKIT ||
|
||||
goog.userAgent.OPERA ||
|
||||
typeof(goog.global.atob) == 'function';
|
||||
|
||||
|
||||
/**
|
||||
* Base64-encode an array of bytes.
|
||||
*
|
||||
* @param {Array.<number>|Uint8Array} input An array of bytes (numbers with
|
||||
* value in [0, 255]) to encode.
|
||||
* @param {boolean=} opt_webSafe Boolean indicating we should use the
|
||||
* alternative alphabet.
|
||||
* @return {string} The base64 encoded string.
|
||||
*/
|
||||
goog.crypt.base64.encodeByteArray = function(input, opt_webSafe) {
|
||||
if (!goog.isArrayLike(input)) {
|
||||
throw Error('encodeByteArray takes an array as a parameter');
|
||||
}
|
||||
|
||||
goog.crypt.base64.init_();
|
||||
|
||||
var byteToCharMap = opt_webSafe ?
|
||||
goog.crypt.base64.byteToCharMapWebSafe_ :
|
||||
goog.crypt.base64.byteToCharMap_;
|
||||
|
||||
var output = [];
|
||||
|
||||
for (var i = 0; i < input.length; i += 3) {
|
||||
var byte1 = input[i];
|
||||
var haveByte2 = i + 1 < input.length;
|
||||
var byte2 = haveByte2 ? input[i + 1] : 0;
|
||||
var haveByte3 = i + 2 < input.length;
|
||||
var byte3 = haveByte3 ? input[i + 2] : 0;
|
||||
|
||||
var outByte1 = byte1 >> 2;
|
||||
var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);
|
||||
var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6);
|
||||
var outByte4 = byte3 & 0x3F;
|
||||
|
||||
if (!haveByte3) {
|
||||
outByte4 = 64;
|
||||
|
||||
if (!haveByte2) {
|
||||
outByte3 = 64;
|
||||
}
|
||||
}
|
||||
|
||||
output.push(byteToCharMap[outByte1],
|
||||
byteToCharMap[outByte2],
|
||||
byteToCharMap[outByte3],
|
||||
byteToCharMap[outByte4]);
|
||||
}
|
||||
|
||||
return output.join('');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Base64-encode a string.
|
||||
*
|
||||
* @param {string} input A string to encode.
|
||||
* @param {boolean=} opt_webSafe If true, we should use the
|
||||
* alternative alphabet.
|
||||
* @return {string} The base64 encoded string.
|
||||
*/
|
||||
goog.crypt.base64.encodeString = function(input, opt_webSafe) {
|
||||
// Shortcut for Mozilla browsers that implement
|
||||
// a native base64 encoder in the form of "btoa/atob"
|
||||
if (goog.crypt.base64.HAS_NATIVE_SUPPORT && !opt_webSafe) {
|
||||
return goog.global.btoa(input);
|
||||
}
|
||||
return goog.crypt.base64.encodeByteArray(
|
||||
goog.crypt.stringToByteArray(input), opt_webSafe);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Base64-decode a string.
|
||||
*
|
||||
* @param {string} input to decode.
|
||||
* @param {boolean=} opt_webSafe True if we should use the
|
||||
* alternative alphabet.
|
||||
* @return {string} string representing the decoded value.
|
||||
*/
|
||||
goog.crypt.base64.decodeString = function(input, opt_webSafe) {
|
||||
// Shortcut for Mozilla browsers that implement
|
||||
// a native base64 encoder in the form of "btoa/atob"
|
||||
if (goog.crypt.base64.HAS_NATIVE_SUPPORT && !opt_webSafe) {
|
||||
return goog.global.atob(input);
|
||||
}
|
||||
return goog.crypt.byteArrayToString(
|
||||
goog.crypt.base64.decodeStringToByteArray(input, opt_webSafe));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Base64-decode a string.
|
||||
*
|
||||
* @param {string} input to decode (length not required to be a multiple of 4).
|
||||
* @param {boolean=} opt_webSafe True if we should use the
|
||||
* alternative alphabet.
|
||||
* @return {Array} bytes representing the decoded value.
|
||||
*/
|
||||
goog.crypt.base64.decodeStringToByteArray = function(input, opt_webSafe) {
|
||||
goog.crypt.base64.init_();
|
||||
|
||||
var charToByteMap = opt_webSafe ?
|
||||
goog.crypt.base64.charToByteMapWebSafe_ :
|
||||
goog.crypt.base64.charToByteMap_;
|
||||
|
||||
var output = [];
|
||||
|
||||
for (var i = 0; i < input.length; ) {
|
||||
var byte1 = charToByteMap[input.charAt(i++)];
|
||||
|
||||
var haveByte2 = i < input.length;
|
||||
var byte2 = haveByte2 ? charToByteMap[input.charAt(i)] : 0;
|
||||
++i;
|
||||
|
||||
var haveByte3 = i < input.length;
|
||||
var byte3 = haveByte3 ? charToByteMap[input.charAt(i)] : 0;
|
||||
++i;
|
||||
|
||||
var haveByte4 = i < input.length;
|
||||
var byte4 = haveByte4 ? charToByteMap[input.charAt(i)] : 0;
|
||||
++i;
|
||||
|
||||
if (byte1 == null || byte2 == null ||
|
||||
byte3 == null || byte4 == null) {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
var outByte1 = (byte1 << 2) | (byte2 >> 4);
|
||||
output.push(outByte1);
|
||||
|
||||
if (byte3 != 64) {
|
||||
var outByte2 = ((byte2 << 4) & 0xF0) | (byte3 >> 2);
|
||||
output.push(outByte2);
|
||||
|
||||
if (byte4 != 64) {
|
||||
var outByte3 = ((byte3 << 6) & 0xC0) | byte4;
|
||||
output.push(outByte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Lazy static initialization function. Called before
|
||||
* accessing any of the static map variables.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.base64.init_ = function() {
|
||||
if (!goog.crypt.base64.byteToCharMap_) {
|
||||
goog.crypt.base64.byteToCharMap_ = {};
|
||||
goog.crypt.base64.charToByteMap_ = {};
|
||||
goog.crypt.base64.byteToCharMapWebSafe_ = {};
|
||||
goog.crypt.base64.charToByteMapWebSafe_ = {};
|
||||
|
||||
// We want quick mappings back and forth, so we precompute two maps.
|
||||
for (var i = 0; i < goog.crypt.base64.ENCODED_VALS.length; i++) {
|
||||
goog.crypt.base64.byteToCharMap_[i] =
|
||||
goog.crypt.base64.ENCODED_VALS.charAt(i);
|
||||
goog.crypt.base64.charToByteMap_[goog.crypt.base64.byteToCharMap_[i]] = i;
|
||||
goog.crypt.base64.byteToCharMapWebSafe_[i] =
|
||||
goog.crypt.base64.ENCODED_VALS_WEBSAFE.charAt(i);
|
||||
goog.crypt.base64.charToByteMapWebSafe_[
|
||||
goog.crypt.base64.byteToCharMapWebSafe_[i]] = i;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,242 @@
|
||||
// Copyright 2007 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 Numeric base conversion library. Works for arbitrary bases and
|
||||
* arbitrary length numbers.
|
||||
*
|
||||
* For base-64 conversion use base64.js because it is optimized for the specific
|
||||
* conversion to base-64 while this module is generic. Base-64 is defined here
|
||||
* mostly for demonstration purpose.
|
||||
*
|
||||
* TODO: Make base64 and baseN classes that have common interface. (Perhaps...)
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.baseN');
|
||||
|
||||
|
||||
/**
|
||||
* Base-2, i.e. '01'.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.crypt.baseN.BASE_BINARY = '01';
|
||||
|
||||
|
||||
/**
|
||||
* Base-8, i.e. '01234567'.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.crypt.baseN.BASE_OCTAL = '01234567';
|
||||
|
||||
|
||||
/**
|
||||
* Base-10, i.e. '0123456789'.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.crypt.baseN.BASE_DECIMAL = '0123456789';
|
||||
|
||||
|
||||
/**
|
||||
* Base-16 using lower case, i.e. '0123456789abcdef'.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.crypt.baseN.BASE_LOWERCASE_HEXADECIMAL = '0123456789abcdef';
|
||||
|
||||
|
||||
/**
|
||||
* Base-16 using upper case, i.e. '0123456789ABCDEF'.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.crypt.baseN.BASE_UPPERCASE_HEXADECIMAL = '0123456789ABCDEF';
|
||||
|
||||
|
||||
/**
|
||||
* The more-known version of the BASE-64 encoding. Uses + and / characters.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.crypt.baseN.BASE_64 =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
|
||||
|
||||
/**
|
||||
* URL-safe version of the BASE-64 encoding.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.crypt.baseN.BASE_64_URL_SAFE =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
||||
|
||||
|
||||
/**
|
||||
* Converts a number from one numeric base to another.
|
||||
*
|
||||
* The bases are represented as strings, which list allowed digits. Each digit
|
||||
* should be unique. The bases can either be user defined, or any of
|
||||
* goog.crypt.baseN.BASE_xxx.
|
||||
*
|
||||
* The number is in human-readable format, most significant digit first, and is
|
||||
* a non-negative integer. Base designators such as $, 0x, d, b or h (at end)
|
||||
* will be interpreted as digits, so avoid them. Leading zeros will be trimmed.
|
||||
*
|
||||
* Note: for huge bases the result may be inaccurate because of overflowing
|
||||
* 64-bit doubles used by JavaScript for integer calculus. This may happen
|
||||
* if the product of the number of digits in the input and output bases comes
|
||||
* close to 10^16, which is VERY unlikely (100M digits in each base), but
|
||||
* may be possible in the future unicode world. (Unicode 3.2 has less than 100K
|
||||
* characters. However, it reserves some more, close to 1M.)
|
||||
*
|
||||
* @param {string} number The number to convert.
|
||||
* @param {string} inputBase The numeric base the number is in (all digits).
|
||||
* @param {string} outputBase Requested numeric base.
|
||||
* @return {string} The converted number.
|
||||
*/
|
||||
goog.crypt.baseN.recodeString = function(number, inputBase, outputBase) {
|
||||
if (outputBase == '') {
|
||||
throw Error('Empty output base');
|
||||
}
|
||||
|
||||
// Check if number is 0 (special case when we don't want to return '').
|
||||
var isZero = true;
|
||||
for (var i = 0, n = number.length; i < n; i++) {
|
||||
if (number.charAt(i) != inputBase.charAt(0)) {
|
||||
isZero = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isZero) {
|
||||
return outputBase.charAt(0);
|
||||
}
|
||||
|
||||
var numberDigits = goog.crypt.baseN.stringToArray_(number, inputBase);
|
||||
|
||||
var inputBaseSize = inputBase.length;
|
||||
var outputBaseSize = outputBase.length;
|
||||
|
||||
// result = 0.
|
||||
var result = [];
|
||||
|
||||
// For all digits of number, starting with the most significant ...
|
||||
for (var i = numberDigits.length - 1; i >= 0; i--) {
|
||||
|
||||
// result *= number.base.
|
||||
var carry = 0;
|
||||
for (var j = 0, n = result.length; j < n; j++) {
|
||||
var digit = result[j];
|
||||
// This may overflow for huge bases. See function comment.
|
||||
digit = digit * inputBaseSize + carry;
|
||||
if (digit >= outputBaseSize) {
|
||||
var remainder = digit % outputBaseSize;
|
||||
carry = (digit - remainder) / outputBaseSize;
|
||||
digit = remainder;
|
||||
} else {
|
||||
carry = 0;
|
||||
}
|
||||
result[j] = digit;
|
||||
}
|
||||
while (carry) {
|
||||
var remainder = carry % outputBaseSize;
|
||||
result.push(remainder);
|
||||
carry = (carry - remainder) / outputBaseSize;
|
||||
}
|
||||
|
||||
// result += number[i].
|
||||
carry = numberDigits[i];
|
||||
var j = 0;
|
||||
while (carry) {
|
||||
if (j >= result.length) {
|
||||
// Extend result with a leading zero which will be overwritten below.
|
||||
result.push(0);
|
||||
}
|
||||
var digit = result[j];
|
||||
digit += carry;
|
||||
if (digit >= outputBaseSize) {
|
||||
var remainder = digit % outputBaseSize;
|
||||
carry = (digit - remainder) / outputBaseSize;
|
||||
digit = remainder;
|
||||
} else {
|
||||
carry = 0;
|
||||
}
|
||||
result[j] = digit;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
return goog.crypt.baseN.arrayToString_(result, outputBase);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts a string representation of a number to an array of digit values.
|
||||
*
|
||||
* More precisely, the digit values are indices into the number base, which
|
||||
* is represented as a string, which can either be user defined or one of the
|
||||
* BASE_xxx constants.
|
||||
*
|
||||
* Throws an Error if the number contains a digit not found in the base.
|
||||
*
|
||||
* @param {string} number The string to convert, most significant digit first.
|
||||
* @param {string} base Digits in the base.
|
||||
* @return {Array.<number>} Array of digit values, least significant digit
|
||||
* first.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.baseN.stringToArray_ = function(number, base) {
|
||||
var index = {};
|
||||
for (var i = 0, n = base.length; i < n; i++) {
|
||||
index[base.charAt(i)] = i;
|
||||
}
|
||||
var result = [];
|
||||
for (var i = number.length - 1; i >= 0; i--) {
|
||||
var character = number.charAt(i);
|
||||
var digit = index[character];
|
||||
if (typeof digit == 'undefined') {
|
||||
throw Error('Number ' + number +
|
||||
' contains a character not found in base ' +
|
||||
base + ', which is ' + character);
|
||||
}
|
||||
result.push(digit);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts an array representation of a number to a string.
|
||||
*
|
||||
* More precisely, the elements of the input array are indices into the base,
|
||||
* which is represented as a string, which can either be user defined or one of
|
||||
* the BASE_xxx constants.
|
||||
*
|
||||
* Throws an Error if the number contains a digit which is outside the range
|
||||
* 0 ... base.length - 1.
|
||||
*
|
||||
* @param {Array.<number>} number Array of digit values, least significant
|
||||
* first.
|
||||
* @param {string} base Digits in the base.
|
||||
* @return {string} Number as a string, most significant digit first.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.baseN.arrayToString_ = function(number, base) {
|
||||
var n = number.length;
|
||||
var chars = [];
|
||||
var baseSize = base.length;
|
||||
for (var i = n - 1; i >= 0; i--) {
|
||||
var digit = number[i];
|
||||
if (digit >= baseSize || digit < 0) {
|
||||
throw Error('Number ' + number + ' contains an invalid digit: ' + digit);
|
||||
}
|
||||
chars.push(base.charAt(digit));
|
||||
}
|
||||
return chars.join('');
|
||||
};
|
||||
@@ -0,0 +1,284 @@
|
||||
// Copyright 2011 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 Asynchronous hash computer for the Blob interface.
|
||||
*
|
||||
* The Blob interface, part of the HTML5 File API, is supported on Chrome 7+,
|
||||
* Firefox 4.0 and Opera 11. No Blob interface implementation is expected on
|
||||
* Internet Explorer 10. Chrome 11, Firefox 5.0 and the subsequent release of
|
||||
* Opera are supposed to use vendor prefixes due to evolving API, see
|
||||
* http://dev.w3.org/2006/webapi/FileAPI/ for details.
|
||||
*
|
||||
* This implementation currently uses upcoming Chrome and Firefox prefixes,
|
||||
* plus the original Blob.slice specification, as implemented on Chrome 10
|
||||
* and Firefox 4.0.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.BlobHasher');
|
||||
goog.provide('goog.crypt.BlobHasher.EventType');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.crypt');
|
||||
goog.require('goog.crypt.Hash');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.fs');
|
||||
goog.require('goog.log');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Construct the hash computer.
|
||||
*
|
||||
* @param {!goog.crypt.Hash} hashFn The hash function to use.
|
||||
* @param {number=} opt_blockSize Processing block size.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.crypt.BlobHasher = function(hashFn, opt_blockSize) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The actual hash function.
|
||||
* @type {!goog.crypt.Hash}
|
||||
* @private
|
||||
*/
|
||||
this.hashFn_ = hashFn;
|
||||
|
||||
/**
|
||||
* The blob being processed or null if no blob is being processed.
|
||||
* @type {Blob}
|
||||
* @private
|
||||
*/
|
||||
this.blob_ = null;
|
||||
|
||||
/**
|
||||
* Computed hash value.
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.hashVal_ = null;
|
||||
|
||||
/**
|
||||
* Number of bytes already processed.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.bytesProcessed_ = 0;
|
||||
|
||||
/**
|
||||
* The number of bytes to hash or Infinity for no limit.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.hashingLimit_ = Infinity;
|
||||
|
||||
/**
|
||||
* Processing block size.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.blockSize_ = opt_blockSize || 5000000;
|
||||
|
||||
/**
|
||||
* File reader object. Will be null if no chunk is currently being read.
|
||||
* @type {FileReader}
|
||||
* @private
|
||||
*/
|
||||
this.fileReader_ = null;
|
||||
|
||||
/**
|
||||
* The logger used by this object.
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
this.logger_ = goog.log.getLogger('goog.crypt.BlobHasher');
|
||||
};
|
||||
goog.inherits(goog.crypt.BlobHasher, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Event names for hash computation events
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.crypt.BlobHasher.EventType = {
|
||||
STARTED: 'started',
|
||||
PROGRESS: 'progress',
|
||||
THROTTLED: 'throttled',
|
||||
COMPLETE: 'complete',
|
||||
ABORT: 'abort',
|
||||
ERROR: 'error'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Start the hash computation.
|
||||
* @param {!Blob} blob The blob of data to compute the hash for.
|
||||
*/
|
||||
goog.crypt.BlobHasher.prototype.hash = function(blob) {
|
||||
this.abort();
|
||||
this.hashFn_.reset();
|
||||
this.blob_ = blob;
|
||||
this.hashVal_ = null;
|
||||
this.bytesProcessed_ = 0;
|
||||
this.dispatchEvent(goog.crypt.BlobHasher.EventType.STARTED);
|
||||
|
||||
this.processNextBlock_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the maximum number of bytes to hash or Infinity for no limit. Can be
|
||||
* called before hash() to throttle the hash computation. The hash computation
|
||||
* can then be continued by repeatedly calling setHashingLimit() with greater
|
||||
* byte offsets. This is useful if you don't need the hash until some time in
|
||||
* the future, for example when uploading a file and you don't need the hash
|
||||
* until the transfer is complete.
|
||||
* @param {number} byteOffset The byte offset to compute the hash up to.
|
||||
* Should be a non-negative integer or Infinity for no limit. Negative
|
||||
* values are not allowed.
|
||||
*/
|
||||
goog.crypt.BlobHasher.prototype.setHashingLimit = function(byteOffset) {
|
||||
goog.asserts.assert(byteOffset >= 0, 'Hashing limit must be non-negative.');
|
||||
this.hashingLimit_ = byteOffset;
|
||||
|
||||
// Resume processing if a blob is currently being hashed, but no block read
|
||||
// is currently in progress.
|
||||
if (this.blob_ && !this.fileReader_) {
|
||||
this.processNextBlock_();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Abort hash computation.
|
||||
*/
|
||||
goog.crypt.BlobHasher.prototype.abort = function() {
|
||||
if (this.fileReader_) {
|
||||
this.fileReader_.abort();
|
||||
this.fileReader_ = null;
|
||||
}
|
||||
|
||||
if (this.blob_) {
|
||||
this.blob_ = null;
|
||||
this.dispatchEvent(goog.crypt.BlobHasher.EventType.ABORT);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} Number of bytes processed so far.
|
||||
*/
|
||||
goog.crypt.BlobHasher.prototype.getBytesProcessed = function() {
|
||||
return this.bytesProcessed_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Array.<number>} The computed hash value or null if not ready.
|
||||
*/
|
||||
goog.crypt.BlobHasher.prototype.getHash = function() {
|
||||
return this.hashVal_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper function setting up the processing for the next block, or finalizing
|
||||
* the computation if all blocks were processed.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.BlobHasher.prototype.processNextBlock_ = function() {
|
||||
goog.asserts.assert(this.blob_, 'A hash computation must be in progress.');
|
||||
|
||||
if (this.bytesProcessed_ < this.blob_.size) {
|
||||
|
||||
if (this.hashingLimit_ <= this.bytesProcessed_) {
|
||||
// Throttle limit reached. Wait until we are allowed to hash more bytes.
|
||||
this.dispatchEvent(goog.crypt.BlobHasher.EventType.THROTTLED);
|
||||
return;
|
||||
}
|
||||
|
||||
// We have to reset the FileReader every time, otherwise it fails on
|
||||
// Chrome, including the latest Chrome 12 beta.
|
||||
// http://code.google.com/p/chromium/issues/detail?id=82346
|
||||
this.fileReader_ = new FileReader();
|
||||
this.fileReader_.onload = goog.bind(this.onLoad_, this);
|
||||
this.fileReader_.onerror = goog.bind(this.onError_, this);
|
||||
|
||||
var endOffset = Math.min(this.hashingLimit_, this.blob_.size);
|
||||
var size = Math.min(endOffset - this.bytesProcessed_, this.blockSize_);
|
||||
var chunk = goog.fs.sliceBlob(this.blob_, this.bytesProcessed_,
|
||||
this.bytesProcessed_ + size);
|
||||
if (!chunk || chunk.size != size) {
|
||||
goog.log.error(this.logger_, 'Failed slicing the blob');
|
||||
this.onError_();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.fileReader_.readAsArrayBuffer) {
|
||||
this.fileReader_.readAsArrayBuffer(chunk);
|
||||
} else if (this.fileReader_.readAsBinaryString) {
|
||||
this.fileReader_.readAsBinaryString(chunk);
|
||||
} else {
|
||||
goog.log.error(this.logger_, 'Failed calling the chunk reader');
|
||||
this.onError_();
|
||||
}
|
||||
} else {
|
||||
this.hashVal_ = this.hashFn_.digest();
|
||||
this.blob_ = null;
|
||||
this.dispatchEvent(goog.crypt.BlobHasher.EventType.COMPLETE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle processing block loaded.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.BlobHasher.prototype.onLoad_ = function() {
|
||||
goog.log.info(this.logger_, 'Successfully loaded a chunk');
|
||||
|
||||
var array = null;
|
||||
if (this.fileReader_.result instanceof Array ||
|
||||
goog.isString(this.fileReader_.result)) {
|
||||
array = this.fileReader_.result;
|
||||
} else if (goog.global['ArrayBuffer'] && goog.global['Uint8Array'] &&
|
||||
this.fileReader_.result instanceof ArrayBuffer) {
|
||||
array = new Uint8Array(this.fileReader_.result);
|
||||
}
|
||||
if (!array) {
|
||||
goog.log.error(this.logger_, 'Failed reading the chunk');
|
||||
this.onError_();
|
||||
return;
|
||||
}
|
||||
|
||||
this.hashFn_.update(array);
|
||||
this.bytesProcessed_ += array.length;
|
||||
this.fileReader_ = null;
|
||||
this.dispatchEvent(goog.crypt.BlobHasher.EventType.PROGRESS);
|
||||
|
||||
this.processNextBlock_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles error.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.BlobHasher.prototype.onError_ = function() {
|
||||
this.fileReader_ = null;
|
||||
this.blob_ = null;
|
||||
this.dispatchEvent(goog.crypt.BlobHasher.EventType.ERROR);
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 Interface definition of a block cipher. A block cipher is a
|
||||
* pair of algorithms that implement encryption and decryption of input bytes.
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/Block_cipher
|
||||
*
|
||||
* @author nnaze@google.com (Nathan Naze)
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.BlockCipher');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Interface definition for a block cipher.
|
||||
* @interface
|
||||
*/
|
||||
goog.crypt.BlockCipher = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Encrypt a plaintext block. The implementation may expect (and assert)
|
||||
* a particular block length.
|
||||
* @param {!Array.<number>} input Plaintext array of input bytes.
|
||||
* @return {!Array.<number>} Encrypted ciphertext array of bytes. Should be the
|
||||
* same length as input.
|
||||
*/
|
||||
goog.crypt.BlockCipher.prototype.encrypt;
|
||||
|
||||
|
||||
/**
|
||||
* Decrypt a plaintext block. The implementation may expect (and assert)
|
||||
* a particular block length.
|
||||
* @param {!Array.<number>} input Ciphertext. Array of input bytes.
|
||||
* @return {!Array.<number>} Decrypted plaintext array of bytes. Should be the
|
||||
* same length as input.
|
||||
*/
|
||||
goog.crypt.BlockCipher.prototype.decrypt;
|
||||
@@ -0,0 +1,150 @@
|
||||
// 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 CBC mode for block ciphers. See
|
||||
* http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation
|
||||
* #Cipher-block_chaining_.28CBC.29. for description.
|
||||
*
|
||||
* @author nnaze@google.com (Nathan Naze)
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.Cbc');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.crypt');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implements the CBC mode for block ciphers. See
|
||||
* http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation
|
||||
* #Cipher-block_chaining_.28CBC.29
|
||||
*
|
||||
* @param {!goog.crypt.BlockCipher} cipher The block cipher to use.
|
||||
* @param {number=} opt_blockSize The block size of the cipher in bytes.
|
||||
* Defaults to 16 bytes.
|
||||
* @constructor
|
||||
*/
|
||||
goog.crypt.Cbc = function(cipher, opt_blockSize) {
|
||||
|
||||
/**
|
||||
* Block cipher.
|
||||
* @type {!goog.crypt.BlockCipher}
|
||||
* @private
|
||||
*/
|
||||
this.cipher_ = cipher;
|
||||
|
||||
/**
|
||||
* Block size in bytes.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.blockSize_ = opt_blockSize || 16;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encrypt a message.
|
||||
*
|
||||
* @param {!Array.<number>} plainText Message to encrypt. An array of bytes.
|
||||
* The length should be a multiple of the block size.
|
||||
* @param {!Array.<number>} initialVector Initial vector for the CBC mode.
|
||||
* An array of bytes with the same length as the block size.
|
||||
* @return {!Array.<number>} Encrypted message.
|
||||
*/
|
||||
goog.crypt.Cbc.prototype.encrypt = function(plainText, initialVector) {
|
||||
|
||||
goog.asserts.assert(
|
||||
plainText.length % this.blockSize_ == 0,
|
||||
'Data\'s length must be multiple of block size.');
|
||||
|
||||
goog.asserts.assert(
|
||||
initialVector.length == this.blockSize_,
|
||||
'Initial vector must be size of one block.');
|
||||
|
||||
// Implementation of
|
||||
// http://en.wikipedia.org/wiki/File:Cbc_encryption.png
|
||||
|
||||
var cipherText = [];
|
||||
var vector = initialVector;
|
||||
|
||||
// Generate each block of the encrypted cypher text.
|
||||
for (var blockStartIndex = 0;
|
||||
blockStartIndex < plainText.length;
|
||||
blockStartIndex += this.blockSize_) {
|
||||
|
||||
// Takes one block from the input message.
|
||||
var plainTextBlock = goog.array.slice(
|
||||
plainText,
|
||||
blockStartIndex,
|
||||
blockStartIndex + this.blockSize_);
|
||||
|
||||
var input = goog.crypt.xorByteArray(plainTextBlock, vector);
|
||||
var resultBlock = this.cipher_.encrypt(input);
|
||||
|
||||
goog.array.extend(cipherText, resultBlock);
|
||||
vector = resultBlock;
|
||||
}
|
||||
|
||||
return cipherText;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Decrypt a message.
|
||||
*
|
||||
* @param {!Array.<number>} cipherText Message to decrypt. An array of bytes.
|
||||
* The length should be a multiple of the block size.
|
||||
* @param {!Array.<number>} initialVector Initial vector for the CBC mode.
|
||||
* An array of bytes with the same length as the block size.
|
||||
* @return {!Array.<number>} Decrypted message.
|
||||
*/
|
||||
goog.crypt.Cbc.prototype.decrypt = function(cipherText, initialVector) {
|
||||
|
||||
goog.asserts.assert(
|
||||
cipherText.length % this.blockSize_ == 0,
|
||||
'Data\'s length must be multiple of block size.');
|
||||
|
||||
goog.asserts.assert(
|
||||
initialVector.length == this.blockSize_,
|
||||
'Initial vector must be size of one block.');
|
||||
|
||||
// Implementation of
|
||||
// http://en.wikipedia.org/wiki/File:Cbc_decryption.png
|
||||
|
||||
var plainText = [];
|
||||
var blockStartIndex = 0;
|
||||
var vector = initialVector;
|
||||
|
||||
// Generate each block of the decrypted plain text.
|
||||
while (blockStartIndex < cipherText.length) {
|
||||
|
||||
// Takes one block.
|
||||
var cipherTextBlock = goog.array.slice(
|
||||
cipherText,
|
||||
blockStartIndex,
|
||||
blockStartIndex + this.blockSize_);
|
||||
|
||||
var resultBlock = this.cipher_.decrypt(cipherTextBlock);
|
||||
var plainTextBlock = goog.crypt.xorByteArray(vector, resultBlock);
|
||||
|
||||
goog.array.extend(plainText, plainTextBlock);
|
||||
vector = cipherTextBlock;
|
||||
|
||||
blockStartIndex += this.blockSize_;
|
||||
}
|
||||
|
||||
return plainText;
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
// 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 Unit tests for CBC mode for block ciphers.
|
||||
*
|
||||
* @author nnaze@google.com (Nathan Naze)
|
||||
*/
|
||||
|
||||
/** @suppress {extraProvide} */
|
||||
goog.provide('goog.crypt.CbcTest');
|
||||
|
||||
goog.require('goog.crypt');
|
||||
goog.require('goog.crypt.Aes');
|
||||
goog.require('goog.crypt.Cbc');
|
||||
goog.require('goog.testing.jsunit');
|
||||
|
||||
goog.setTestOnly('goog.crypt.CbcTest');
|
||||
|
||||
function stringToBytes(s) {
|
||||
var bytes = new Array(s.length);
|
||||
for (var i = 0; i < s.length; ++i)
|
||||
bytes[i] = s.charCodeAt(i) & 255;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function runCbcAesTest(keyBytes, initialVectorBytes, plainTextBytes,
|
||||
cipherTextBytes) {
|
||||
|
||||
var aes = new goog.crypt.Aes(keyBytes);
|
||||
var cbc = new goog.crypt.Cbc(aes);
|
||||
|
||||
var encryptedBytes = cbc.encrypt(plainTextBytes, initialVectorBytes);
|
||||
assertEquals('Encrypted bytes should match cipher text.',
|
||||
goog.crypt.byteArrayToHex(cipherTextBytes),
|
||||
goog.crypt.byteArrayToHex(encryptedBytes));
|
||||
|
||||
var decryptedBytes = cbc.decrypt(cipherTextBytes, initialVectorBytes);
|
||||
assertEquals('Decrypted bytes should match plain text.',
|
||||
goog.crypt.byteArrayToHex(plainTextBytes),
|
||||
goog.crypt.byteArrayToHex(decryptedBytes));
|
||||
}
|
||||
|
||||
function testAesCbcCipherAlgorithm() {
|
||||
// Test values from http://www.ietf.org/rfc/rfc3602.txt
|
||||
|
||||
// Case #1
|
||||
runCbcAesTest(
|
||||
goog.crypt.hexToByteArray('06a9214036b8a15b512e03d534120006'),
|
||||
goog.crypt.hexToByteArray('3dafba429d9eb430b422da802c9fac41'),
|
||||
stringToBytes('Single block msg'),
|
||||
goog.crypt.hexToByteArray('e353779c1079aeb82708942dbe77181a'));
|
||||
|
||||
// Case #2
|
||||
runCbcAesTest(
|
||||
goog.crypt.hexToByteArray('c286696d887c9aa0611bbb3e2025a45a'),
|
||||
goog.crypt.hexToByteArray('562e17996d093d28ddb3ba695a2e6f58'),
|
||||
goog.crypt.hexToByteArray(
|
||||
'000102030405060708090a0b0c0d0e0f' +
|
||||
'101112131415161718191a1b1c1d1e1f'),
|
||||
goog.crypt.hexToByteArray(
|
||||
'd296cd94c2cccf8a3a863028b5e1dc0a' +
|
||||
'7586602d253cfff91b8266bea6d61ab1'));
|
||||
|
||||
// Case #3
|
||||
runCbcAesTest(
|
||||
goog.crypt.hexToByteArray('6c3ea0477630ce21a2ce334aa746c2cd'),
|
||||
goog.crypt.hexToByteArray('c782dc4c098c66cbd9cd27d825682c81'),
|
||||
stringToBytes('This is a 48-byte message (exactly 3 AES blocks)'),
|
||||
goog.crypt.hexToByteArray(
|
||||
'd0a02b3836451753d493665d33f0e886' +
|
||||
'2dea54cdb293abc7506939276772f8d5' +
|
||||
'021c19216bad525c8579695d83ba2684'));
|
||||
|
||||
// Case #4
|
||||
runCbcAesTest(
|
||||
goog.crypt.hexToByteArray('56e47a38c5598974bc46903dba290349'),
|
||||
goog.crypt.hexToByteArray('8ce82eefbea0da3c44699ed7db51b7d9'),
|
||||
goog.crypt.hexToByteArray(
|
||||
'a0a1a2a3a4a5a6a7a8a9aaabacadaeaf' +
|
||||
'b0b1b2b3b4b5b6b7b8b9babbbcbdbebf' +
|
||||
'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf' +
|
||||
'd0d1d2d3d4d5d6d7d8d9dadbdcdddedf'),
|
||||
goog.crypt.hexToByteArray(
|
||||
'c30e32ffedc0774e6aff6af0869f71aa' +
|
||||
'0f3af07a9a31a9c684db207eb0ef8e4e' +
|
||||
'35907aa632c3ffdf868bb7b29d3d46ad' +
|
||||
'83ce9f9a102ee99d49a53e87f4c3da55'));
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright 2008 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 Namespace with crypto related helper functions.
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt');
|
||||
|
||||
goog.require('goog.array');
|
||||
|
||||
|
||||
/**
|
||||
* Turns a string into an array of bytes; a "byte" being a JS number in the
|
||||
* range 0-255.
|
||||
* @param {string} str String value to arrify.
|
||||
* @return {!Array.<number>} Array of numbers corresponding to the
|
||||
* UCS character codes of each character in str.
|
||||
*/
|
||||
goog.crypt.stringToByteArray = function(str) {
|
||||
var output = [], p = 0;
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
var c = str.charCodeAt(i);
|
||||
while (c > 0xff) {
|
||||
output[p++] = c & 0xff;
|
||||
c >>= 8;
|
||||
}
|
||||
output[p++] = c;
|
||||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Turns an array of numbers into the string given by the concatenation of the
|
||||
* characters to which the numbers correspond.
|
||||
* @param {Array} array Array of numbers representing characters.
|
||||
* @return {string} Stringification of the array.
|
||||
*/
|
||||
goog.crypt.byteArrayToString = function(array) {
|
||||
return String.fromCharCode.apply(null, array);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Turns an array of numbers into the hex string given by the concatenation of
|
||||
* the hex values to which the numbers correspond.
|
||||
* @param {Array} array Array of numbers representing characters.
|
||||
* @return {string} Hex string.
|
||||
*/
|
||||
goog.crypt.byteArrayToHex = function(array) {
|
||||
return goog.array.map(array, function(numByte) {
|
||||
var hexByte = numByte.toString(16);
|
||||
return hexByte.length > 1 ? hexByte : '0' + hexByte;
|
||||
}).join('');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts a hex string into an integer array.
|
||||
* @param {string} hexString Hex string of 16-bit integers (two characters
|
||||
* per integer).
|
||||
* @return {!Array.<number>} Array of {0,255} integers for the given string.
|
||||
*/
|
||||
goog.crypt.hexToByteArray = function(hexString) {
|
||||
goog.asserts.assert(hexString.length % 2 == 0,
|
||||
'Key string length must be multiple of 2');
|
||||
var arr = [];
|
||||
for (var i = 0; i < hexString.length; i += 2) {
|
||||
arr.push(parseInt(hexString.substring(i, i + 2), 16));
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts a JS string to a UTF-8 "byte" array.
|
||||
* @param {string} str 16-bit unicode string.
|
||||
* @return {Array.<number>} UTF-8 byte array.
|
||||
*/
|
||||
goog.crypt.stringToUtf8ByteArray = function(str) {
|
||||
// TODO(user): Use native implementations if/when available
|
||||
str = str.replace(/\r\n/g, '\n');
|
||||
var out = [], p = 0;
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
var c = str.charCodeAt(i);
|
||||
if (c < 128) {
|
||||
out[p++] = c;
|
||||
} else if (c < 2048) {
|
||||
out[p++] = (c >> 6) | 192;
|
||||
out[p++] = (c & 63) | 128;
|
||||
} else {
|
||||
out[p++] = (c >> 12) | 224;
|
||||
out[p++] = ((c >> 6) & 63) | 128;
|
||||
out[p++] = (c & 63) | 128;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts a UTF-8 byte array to JavaScript's 16-bit Unicode.
|
||||
* @param {Array.<number>} bytes UTF-8 byte array.
|
||||
* @return {string} 16-bit Unicode string.
|
||||
*/
|
||||
goog.crypt.utf8ByteArrayToString = function(bytes) {
|
||||
// TODO(user): Use native implementations if/when available
|
||||
var out = [], pos = 0, c = 0;
|
||||
while (pos < bytes.length) {
|
||||
var c1 = bytes[pos++];
|
||||
if (c1 < 128) {
|
||||
out[c++] = String.fromCharCode(c1);
|
||||
} else if (c1 > 191 && c1 < 224) {
|
||||
var c2 = bytes[pos++];
|
||||
out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);
|
||||
} else {
|
||||
var c2 = bytes[pos++];
|
||||
var c3 = bytes[pos++];
|
||||
out[c++] = String.fromCharCode(
|
||||
(c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
|
||||
}
|
||||
}
|
||||
return out.join('');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* XOR two byte arrays.
|
||||
* @param {!Array.<number>} bytes1 Byte array 1.
|
||||
* @param {!Array.<number>} bytes2 Byte array 2.
|
||||
* @return {!Array.<number>} Resulting XOR of the two byte arrays.
|
||||
*/
|
||||
goog.crypt.xorByteArray = function(bytes1, bytes2) {
|
||||
goog.asserts.assert(
|
||||
bytes1.length == bytes2.length,
|
||||
'XOR array lengths must match');
|
||||
|
||||
var result = [];
|
||||
for (var i = 0; i < bytes1.length; i++) {
|
||||
result.push(bytes1[i] ^ bytes2[i]);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2011 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 Abstract cryptographic hash interface.
|
||||
*
|
||||
* See goog.crypt.Sha1 and goog.crypt.Md5 for sample implementations.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.Hash');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a cryptographic hash instance.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
goog.crypt.Hash = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Resets the internal accumulator.
|
||||
*/
|
||||
goog.crypt.Hash.prototype.reset = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Adds a byte array (array with values in [0-255] range) or a string (might
|
||||
* only contain 8-bit, i.e., Latin1 characters) to the internal accumulator.
|
||||
*
|
||||
* Many hash functions operate on blocks of data and implement optimizations
|
||||
* when a full chunk of data is readily available. Hence it is often preferable
|
||||
* to provide large chunks of data (a kilobyte or more) than to repeatedly
|
||||
* call the update method with few tens of bytes. If this is not possible, or
|
||||
* not feasible, it might be good to provide data in multiplies of hash block
|
||||
* size (often 64 bytes). Please see the implementation and performance tests
|
||||
* of your favourite hash.
|
||||
*
|
||||
* @param {Array.<number>|Uint8Array|string} bytes Data used for the update.
|
||||
* @param {number=} opt_length Number of bytes to use.
|
||||
*/
|
||||
goog.crypt.Hash.prototype.update = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @return {!Array.<number>} The finalized hash computed
|
||||
* from the internal accumulator.
|
||||
*/
|
||||
goog.crypt.Hash.prototype.digest = goog.abstractMethod;
|
||||
@@ -0,0 +1,184 @@
|
||||
// Copyright 2008 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 32-bit hashing functions.
|
||||
*
|
||||
* This is a direct port from the Google Java Hash class
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.hash32');
|
||||
|
||||
goog.require('goog.crypt');
|
||||
|
||||
|
||||
/**
|
||||
* Default seed used during hashing, digits of pie.
|
||||
* See SEED32 in http://go/base.hash.java
|
||||
* @type {number}
|
||||
*/
|
||||
goog.crypt.hash32.SEED32 = 314159265;
|
||||
|
||||
|
||||
/**
|
||||
* Arbitrary constant used during hashing.
|
||||
* See CONSTANT32 in http://go/base.hash.java
|
||||
* @type {number}
|
||||
*/
|
||||
goog.crypt.hash32.CONSTANT32 = -1640531527;
|
||||
|
||||
|
||||
/**
|
||||
* Hashes a string to a 32-bit value.
|
||||
* @param {string} str String to hash.
|
||||
* @return {number} 32-bit hash.
|
||||
*/
|
||||
goog.crypt.hash32.encodeString = function(str) {
|
||||
return goog.crypt.hash32.encodeByteArray(goog.crypt.stringToByteArray(str));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hashes a string to a 32-bit value, converting the string to UTF-8 before
|
||||
* doing the encoding.
|
||||
* @param {string} str String to hash.
|
||||
* @return {number} 32-bit hash.
|
||||
*/
|
||||
goog.crypt.hash32.encodeStringUtf8 = function(str) {
|
||||
return goog.crypt.hash32.encodeByteArray(
|
||||
goog.crypt.stringToUtf8ByteArray(str));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hashes an integer to a 32-bit value.
|
||||
* @param {number} value Number to hash.
|
||||
* @return {number} 32-bit hash.
|
||||
*/
|
||||
goog.crypt.hash32.encodeInteger = function(value) {
|
||||
// TODO(user): Does this make sense in JavaScript with doubles? Should we
|
||||
// force the value to be in the correct range?
|
||||
return goog.crypt.hash32.mix32_({
|
||||
a: value,
|
||||
b: goog.crypt.hash32.CONSTANT32,
|
||||
c: goog.crypt.hash32.SEED32
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hashes a "byte" array to a 32-bit value using the supplied seed.
|
||||
* @param {Array.<number>} bytes Array of bytes.
|
||||
* @param {number=} opt_offset The starting position to use for hash
|
||||
* computation.
|
||||
* @param {number=} opt_length Number of bytes that are used for hashing.
|
||||
* @param {number=} opt_seed The seed.
|
||||
* @return {number} 32-bit hash.
|
||||
*/
|
||||
goog.crypt.hash32.encodeByteArray = function(
|
||||
bytes, opt_offset, opt_length, opt_seed) {
|
||||
var offset = opt_offset || 0;
|
||||
var length = opt_length || bytes.length;
|
||||
var seed = opt_seed || goog.crypt.hash32.SEED32;
|
||||
|
||||
var mix = {
|
||||
a: goog.crypt.hash32.CONSTANT32,
|
||||
b: goog.crypt.hash32.CONSTANT32,
|
||||
c: seed
|
||||
};
|
||||
|
||||
var keylen;
|
||||
for (keylen = length; keylen >= 12; keylen -= 12, offset += 12) {
|
||||
mix.a += goog.crypt.hash32.wordAt_(bytes, offset);
|
||||
mix.b += goog.crypt.hash32.wordAt_(bytes, offset + 4);
|
||||
mix.c += goog.crypt.hash32.wordAt_(bytes, offset + 8);
|
||||
goog.crypt.hash32.mix32_(mix);
|
||||
}
|
||||
// Hash any remaining bytes
|
||||
mix.c += length;
|
||||
switch (keylen) { // deal with rest. Some cases fall through
|
||||
case 11: mix.c += (bytes[offset + 10]) << 24;
|
||||
case 10: mix.c += (bytes[offset + 9] & 0xff) << 16;
|
||||
case 9 : mix.c += (bytes[offset + 8] & 0xff) << 8;
|
||||
// the first byte of c is reserved for the length
|
||||
case 8 :
|
||||
mix.b += goog.crypt.hash32.wordAt_(bytes, offset + 4);
|
||||
mix.a += goog.crypt.hash32.wordAt_(bytes, offset);
|
||||
break;
|
||||
case 7 : mix.b += (bytes[offset + 6] & 0xff) << 16;
|
||||
case 6 : mix.b += (bytes[offset + 5] & 0xff) << 8;
|
||||
case 5 : mix.b += (bytes[offset + 4] & 0xff);
|
||||
case 4 :
|
||||
mix.a += goog.crypt.hash32.wordAt_(bytes, offset);
|
||||
break;
|
||||
case 3 : mix.a += (bytes[offset + 2] & 0xff) << 16;
|
||||
case 2 : mix.a += (bytes[offset + 1] & 0xff) << 8;
|
||||
case 1 : mix.a += (bytes[offset + 0] & 0xff);
|
||||
// case 0 : nothing left to add
|
||||
}
|
||||
return goog.crypt.hash32.mix32_(mix);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Performs an inplace mix of an object with the integer properties (a, b, c)
|
||||
* and returns the final value of c.
|
||||
* @param {Object} mix Object with properties, a, b, and c.
|
||||
* @return {number} The end c-value for the mixing.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.hash32.mix32_ = function(mix) {
|
||||
var a = mix.a, b = mix.b, c = mix.c;
|
||||
a -= b; a -= c; a ^= c >>> 13;
|
||||
b -= c; b -= a; b ^= a << 8;
|
||||
c -= a; c -= b; c ^= b >>> 13;
|
||||
a -= b; a -= c; a ^= c >>> 12;
|
||||
b -= c; b -= a; b ^= a << 16;
|
||||
c -= a; c -= b; c ^= b >>> 5;
|
||||
a -= b; a -= c; a ^= c >>> 3;
|
||||
b -= c; b -= a; b ^= a << 10;
|
||||
c -= a; c -= b; c ^= b >>> 15;
|
||||
mix.a = a; mix.b = b; mix.c = c;
|
||||
return c;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the word at a given offset. Treating an array of bytes a word at a
|
||||
* time is far more efficient than byte-by-byte.
|
||||
* @param {Array.<number>} bytes Array of bytes.
|
||||
* @param {number} offset Offset in the byte array.
|
||||
* @return {number} Integer value for the word.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.hash32.wordAt_ = function(bytes, offset) {
|
||||
var a = goog.crypt.hash32.toSigned_(bytes[offset + 0]);
|
||||
var b = goog.crypt.hash32.toSigned_(bytes[offset + 1]);
|
||||
var c = goog.crypt.hash32.toSigned_(bytes[offset + 2]);
|
||||
var d = goog.crypt.hash32.toSigned_(bytes[offset + 3]);
|
||||
return a + (b << 8) + (c << 16) + (d << 24);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts an unsigned "byte" to signed, that is, convert a value in the range
|
||||
* (0, 2^8-1) to (-2^7, 2^7-1) in order to be compatible with Java's byte type.
|
||||
* @param {number} n Unsigned "byte" value.
|
||||
* @return {number} Signed "byte" value.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.hash32.toSigned_ = function(n) {
|
||||
return n > 127 ? n - 256 : n;
|
||||
};
|
||||
@@ -0,0 +1,243 @@
|
||||
// Copyright 2011 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 Unit tests for the abstract cryptographic hash interface.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.hashTester');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.crypt');
|
||||
goog.require('goog.testing.PerformanceTable');
|
||||
goog.require('goog.testing.PseudoRandom');
|
||||
goog.require('goog.testing.asserts');
|
||||
goog.setTestOnly('hashTester');
|
||||
|
||||
|
||||
/**
|
||||
* Runs basic tests.
|
||||
*
|
||||
* @param {!goog.crypt.Hash} hash A hash instance.
|
||||
*/
|
||||
goog.crypt.hashTester.runBasicTests = function(hash) {
|
||||
// Compute first hash.
|
||||
hash.update([97, 158]);
|
||||
var golden1 = hash.digest();
|
||||
|
||||
// Compute second hash.
|
||||
hash.reset();
|
||||
hash.update('aB');
|
||||
var golden2 = hash.digest();
|
||||
assertTrue('Two different inputs resulted in a hash collision',
|
||||
!!goog.testing.asserts.findDifferences(golden1, golden2));
|
||||
|
||||
// Empty hash.
|
||||
hash.reset();
|
||||
var empty = hash.digest();
|
||||
assertTrue('Empty hash collided with a non-trivial one',
|
||||
!!goog.testing.asserts.findDifferences(golden1, empty) &&
|
||||
!!goog.testing.asserts.findDifferences(golden2, empty));
|
||||
|
||||
// Zero-length array update.
|
||||
hash.reset();
|
||||
hash.update([]);
|
||||
assertArrayEquals('Updating with an empty array did not give an empty hash',
|
||||
empty, hash.digest());
|
||||
|
||||
// Zero-length string update.
|
||||
hash.reset();
|
||||
hash.update('');
|
||||
assertArrayEquals('Updating with an empty string did not give an empty hash',
|
||||
empty, hash.digest());
|
||||
|
||||
// Recompute the first hash.
|
||||
hash.reset();
|
||||
hash.update([97, 158]);
|
||||
assertArrayEquals('The reset did not produce the initial state',
|
||||
golden1, hash.digest());
|
||||
|
||||
// Check for a trivial collision.
|
||||
hash.reset();
|
||||
hash.update([158, 97]);
|
||||
assertTrue('Swapping bytes resulted in a hash collision',
|
||||
!!goog.testing.asserts.findDifferences(golden1, hash.digest()));
|
||||
|
||||
// Compare array and string input.
|
||||
hash.reset();
|
||||
hash.update([97, 66]);
|
||||
assertArrayEquals('String and array inputs should give the same result',
|
||||
golden2, hash.digest());
|
||||
|
||||
// Compute in parts.
|
||||
hash.reset();
|
||||
hash.update('a');
|
||||
hash.update([158]);
|
||||
assertArrayEquals('Partial updates resulted in a different hash',
|
||||
golden1, hash.digest());
|
||||
|
||||
// Test update with specified length.
|
||||
hash.reset();
|
||||
hash.update('aB', 0);
|
||||
hash.update([97, 158, 32], 2);
|
||||
assertArrayEquals('Updating with an explicit buffer length did not work',
|
||||
golden1, hash.digest());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Runs block tests.
|
||||
*
|
||||
* @param {!goog.crypt.Hash} hash A hash instance.
|
||||
* @param {number} blockBytes Size of the hash block.
|
||||
*/
|
||||
goog.crypt.hashTester.runBlockTests = function(hash, blockBytes) {
|
||||
// Compute a message which is 1 byte shorter than hash block size.
|
||||
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
var message = '';
|
||||
for (var i = 0; i < blockBytes - 1; i++) {
|
||||
message += chars.charAt(i % chars.length);
|
||||
}
|
||||
|
||||
// Compute golden hash for 1 block + 2 bytes.
|
||||
hash.update(message + '123');
|
||||
var golden1 = hash.digest();
|
||||
|
||||
// Compute golden hash for 2 blocks + 1 byte.
|
||||
hash.reset();
|
||||
hash.update(message + message + '123');
|
||||
var golden2 = hash.digest();
|
||||
|
||||
// Almost fill a block, then overflow.
|
||||
hash.reset();
|
||||
hash.update(message);
|
||||
hash.update('123');
|
||||
assertArrayEquals(golden1, hash.digest());
|
||||
|
||||
// Fill a block.
|
||||
hash.reset();
|
||||
hash.update(message + '1');
|
||||
hash.update('23');
|
||||
assertArrayEquals(golden1, hash.digest());
|
||||
|
||||
// Overflow a block.
|
||||
hash.reset();
|
||||
hash.update(message + '12');
|
||||
hash.update('3');
|
||||
assertArrayEquals(golden1, hash.digest());
|
||||
|
||||
// Test single overflow with an array.
|
||||
hash.reset();
|
||||
hash.update(goog.crypt.stringToByteArray(message + '123'));
|
||||
assertArrayEquals(golden1, hash.digest());
|
||||
|
||||
// Almost fill a block, then overflow this and the next block.
|
||||
hash.reset();
|
||||
hash.update(message);
|
||||
hash.update(message + '123');
|
||||
assertArrayEquals(golden2, hash.digest());
|
||||
|
||||
// Fill two blocks.
|
||||
hash.reset();
|
||||
hash.update(message + message + '12');
|
||||
hash.update('3');
|
||||
assertArrayEquals(golden2, hash.digest());
|
||||
|
||||
// Test double overflow with an array.
|
||||
hash.reset();
|
||||
hash.update(goog.crypt.stringToByteArray(message));
|
||||
hash.update(goog.crypt.stringToByteArray(message + '123'));
|
||||
assertArrayEquals(golden2, hash.digest());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Runs performance tests.
|
||||
*
|
||||
* @param {function():!goog.crypt.Hash} hashFactory A hash factory.
|
||||
* @param {string} hashName Name of the hashing function.
|
||||
*/
|
||||
goog.crypt.hashTester.runPerfTests = function(hashFactory, hashName) {
|
||||
var body = goog.dom.getDocument().body;
|
||||
var perfTable = goog.dom.createElement('div');
|
||||
goog.dom.appendChild(body, perfTable);
|
||||
|
||||
var table = new goog.testing.PerformanceTable(perfTable);
|
||||
|
||||
function runPerfTest(byteLength, updateCount) {
|
||||
var label = (hashName + ': ' + updateCount + ' update(s) of ' + byteLength +
|
||||
' bytes');
|
||||
|
||||
function run(data, dataType) {
|
||||
table.run(function() {
|
||||
var hash = hashFactory();
|
||||
for (var i = 0; i < updateCount; i++) {
|
||||
hash.update(data, byteLength);
|
||||
}
|
||||
var digest = hash.digest();
|
||||
}, label + ' (' + dataType + ')');
|
||||
}
|
||||
|
||||
var byteArray = goog.crypt.hashTester.createRandomByteArray_(byteLength);
|
||||
var byteString = goog.crypt.hashTester.createByteString_(byteArray);
|
||||
|
||||
run(byteArray, 'byte array');
|
||||
run(byteString, 'byte string');
|
||||
}
|
||||
|
||||
var MESSAGE_LENGTH_LONG = 10000000; // 10 Mbytes
|
||||
var MESSAGE_LENGTH_SHORT = 10; // 10 bytes
|
||||
var MESSAGE_COUNT_SHORT = MESSAGE_LENGTH_LONG / MESSAGE_LENGTH_SHORT;
|
||||
|
||||
runPerfTest(MESSAGE_LENGTH_LONG, 1);
|
||||
runPerfTest(MESSAGE_LENGTH_SHORT, MESSAGE_COUNT_SHORT);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates and returns a random byte array.
|
||||
*
|
||||
* @param {number} length Length of the byte array.
|
||||
* @return {!Array.<number>} An array of bytes.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.hashTester.createRandomByteArray_ = function(length) {
|
||||
var random = new goog.testing.PseudoRandom(0);
|
||||
var bytes = [];
|
||||
|
||||
for (var i = 0; i < length; ++i) {
|
||||
// Generates an integer from 0 to 255.
|
||||
var b = Math.floor(random.random() * 0x100);
|
||||
bytes.push(b);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a string from an array of bytes.
|
||||
*
|
||||
* @param {!Array.<number>} bytes An array of bytes.
|
||||
* @return {string} The string encoded by the bytes.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.hashTester.createByteString_ = function(bytes) {
|
||||
var str = '';
|
||||
goog.array.forEach(bytes, function(b) {
|
||||
str += String.fromCharCode(b);
|
||||
});
|
||||
return str;
|
||||
};
|
||||
@@ -0,0 +1,162 @@
|
||||
// Copyright 2011 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 HMAC in JavaScript.
|
||||
*
|
||||
* Usage:
|
||||
* var hmac = new goog.crypt.Hmac(new goog.crypt.sha1(), key, 64);
|
||||
* var digest = hmac.getHmac(bytes);
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.crypt.Hmac');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.crypt.Hash');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {!goog.crypt.Hash} hasher An object to serve as a hash function.
|
||||
* @param {Array.<number>} key The secret key to use to calculate the hmac.
|
||||
* Should be an array of not more than {@code blockSize} integers in
|
||||
{0, 255}.
|
||||
* @param {number=} opt_blockSize Optional. The block size {@code hasher} uses.
|
||||
* If not specified, 16.
|
||||
* @extends {goog.crypt.Hash}
|
||||
*/
|
||||
goog.crypt.Hmac = function(hasher, key, opt_blockSize) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The underlying hasher to calculate hash.
|
||||
*
|
||||
* @type {!goog.crypt.Hash}
|
||||
* @private
|
||||
*/
|
||||
this.hasher_ = hasher;
|
||||
|
||||
/**
|
||||
* The block size.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.blockSize_ = opt_blockSize || 16;
|
||||
|
||||
/**
|
||||
* The outer padding array of hmac
|
||||
*
|
||||
* @type {!Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.keyO_ = new Array(this.blockSize_);
|
||||
|
||||
/**
|
||||
* The inner padding array of hmac
|
||||
*
|
||||
* @type {!Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.keyI_ = new Array(this.blockSize_);
|
||||
|
||||
this.initialize_(key);
|
||||
};
|
||||
goog.inherits(goog.crypt.Hmac, goog.crypt.Hash);
|
||||
|
||||
|
||||
/**
|
||||
* Outer padding byte of HMAC algorith, per http://en.wikipedia.org/wiki/HMAC
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.Hmac.OPAD_ = 0x5c;
|
||||
|
||||
|
||||
/**
|
||||
* Inner padding byte of HMAC algorith, per http://en.wikipedia.org/wiki/HMAC
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.Hmac.IPAD_ = 0x36;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes Hmac by precalculating the inner and outer paddings.
|
||||
*
|
||||
* @param {Array.<number>} key The secret key to use to calculate the hmac.
|
||||
* Should be an array of not more than {@code blockSize} integers in
|
||||
{0, 255}.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.Hmac.prototype.initialize_ = function(key) {
|
||||
if (key.length > this.blockSize_) {
|
||||
this.hasher_.update(key);
|
||||
key = this.hasher_.digest();
|
||||
}
|
||||
// Precalculate padded and xor'd keys.
|
||||
var keyByte;
|
||||
for (var i = 0; i < this.blockSize_; i++) {
|
||||
if (i < key.length) {
|
||||
keyByte = key[i];
|
||||
} else {
|
||||
keyByte = 0;
|
||||
}
|
||||
this.keyO_[i] = keyByte ^ goog.crypt.Hmac.OPAD_;
|
||||
this.keyI_[i] = keyByte ^ goog.crypt.Hmac.IPAD_;
|
||||
}
|
||||
// Be ready for an immediate update.
|
||||
this.hasher_.update(this.keyI_);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Hmac.prototype.reset = function() {
|
||||
this.hasher_.reset();
|
||||
this.hasher_.update(this.keyI_);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Hmac.prototype.update = function(bytes, opt_length) {
|
||||
this.hasher_.update(bytes, opt_length);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Hmac.prototype.digest = function() {
|
||||
var temp = this.hasher_.digest();
|
||||
this.hasher_.reset();
|
||||
this.hasher_.update(this.keyO_);
|
||||
this.hasher_.update(temp);
|
||||
return this.hasher_.digest();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calculates an HMAC for a given message.
|
||||
*
|
||||
* @param {Array.<number>} message An array of integers in {0, 255}.
|
||||
* @return {!Array.<number>} the digest of the given message.
|
||||
*/
|
||||
goog.crypt.Hmac.prototype.getHmac = function(message) {
|
||||
this.reset();
|
||||
this.update(message);
|
||||
return this.digest();
|
||||
};
|
||||
@@ -0,0 +1,429 @@
|
||||
// Copyright 2011 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 MD5 cryptographic hash.
|
||||
* Implementation of http://tools.ietf.org/html/rfc1321 with common
|
||||
* optimizations and tweaks (see http://en.wikipedia.org/wiki/MD5).
|
||||
*
|
||||
* Usage:
|
||||
* var md5 = new goog.crypt.Md5();
|
||||
* md5.update(bytes);
|
||||
* var hash = md5.digest();
|
||||
*
|
||||
* Performance:
|
||||
* Chrome 23 ~680 Mbit/s
|
||||
* Chrome 13 (in a VM) ~250 Mbit/s
|
||||
* Firefox 6.0 (in a VM) ~100 Mbit/s
|
||||
* IE9 (in a VM) ~27 Mbit/s
|
||||
* Firefox 3.6 ~15 Mbit/s
|
||||
* IE8 (in a VM) ~13 Mbit/s
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.Md5');
|
||||
|
||||
goog.require('goog.crypt.Hash');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* MD5 cryptographic hash constructor.
|
||||
* @constructor
|
||||
* @extends {goog.crypt.Hash}
|
||||
*/
|
||||
goog.crypt.Md5 = function() {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* Holds the current values of accumulated A-D variables (MD buffer).
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.chain_ = new Array(4);
|
||||
|
||||
/**
|
||||
* A buffer holding the data until the whole block can be processed.
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.block_ = new Array(64);
|
||||
|
||||
/**
|
||||
* The length of yet-unprocessed data as collected in the block.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.blockLength_ = 0;
|
||||
|
||||
/**
|
||||
* The total length of the message so far.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.totalLength_ = 0;
|
||||
|
||||
this.reset();
|
||||
};
|
||||
goog.inherits(goog.crypt.Md5, goog.crypt.Hash);
|
||||
|
||||
|
||||
/**
|
||||
* Integer rotation constants used by the abbreviated implementation.
|
||||
* They are hardcoded in the unrolled implementation, so it is left
|
||||
* here commented out.
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*
|
||||
goog.crypt.Md5.S_ = [
|
||||
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
|
||||
];
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sine function constants used by the abbreviated implementation.
|
||||
* They are hardcoded in the unrolled implementation, so it is left
|
||||
* here commented out.
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*
|
||||
goog.crypt.Md5.T_ = [
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
||||
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
||||
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
||||
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
|
||||
];
|
||||
*/
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Md5.prototype.reset = function() {
|
||||
this.chain_[0] = 0x67452301;
|
||||
this.chain_[1] = 0xefcdab89;
|
||||
this.chain_[2] = 0x98badcfe;
|
||||
this.chain_[3] = 0x10325476;
|
||||
|
||||
this.blockLength_ = 0;
|
||||
this.totalLength_ = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Internal compress helper function. It takes a block of data (64 bytes)
|
||||
* and updates the accumulator.
|
||||
* @param {Array.<number>|Uint8Array|string} buf The block to compress.
|
||||
* @param {number=} opt_offset Offset of the block in the buffer.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.Md5.prototype.compress_ = function(buf, opt_offset) {
|
||||
if (!opt_offset) {
|
||||
opt_offset = 0;
|
||||
}
|
||||
|
||||
// We allocate the array every time, but it's cheap in practice.
|
||||
var X = new Array(16);
|
||||
|
||||
// Get 16 little endian words. It is not worth unrolling this for Chrome 11.
|
||||
if (goog.isString(buf)) {
|
||||
for (var i = 0; i < 16; ++i) {
|
||||
X[i] = (buf.charCodeAt(opt_offset++)) |
|
||||
(buf.charCodeAt(opt_offset++) << 8) |
|
||||
(buf.charCodeAt(opt_offset++) << 16) |
|
||||
(buf.charCodeAt(opt_offset++) << 24);
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < 16; ++i) {
|
||||
X[i] = (buf[opt_offset++]) |
|
||||
(buf[opt_offset++] << 8) |
|
||||
(buf[opt_offset++] << 16) |
|
||||
(buf[opt_offset++] << 24);
|
||||
}
|
||||
}
|
||||
|
||||
var A = this.chain_[0];
|
||||
var B = this.chain_[1];
|
||||
var C = this.chain_[2];
|
||||
var D = this.chain_[3];
|
||||
var sum = 0;
|
||||
|
||||
/*
|
||||
* This is an abbreviated implementation, it is left here commented out for
|
||||
* reference purposes. See below for an unrolled version in use.
|
||||
*
|
||||
var f, n, tmp;
|
||||
for (var i = 0; i < 64; ++i) {
|
||||
|
||||
if (i < 16) {
|
||||
f = (D ^ (B & (C ^ D)));
|
||||
n = i;
|
||||
} else if (i < 32) {
|
||||
f = (C ^ (D & (B ^ C)));
|
||||
n = (5 * i + 1) % 16;
|
||||
} else if (i < 48) {
|
||||
f = (B ^ C ^ D);
|
||||
n = (3 * i + 5) % 16;
|
||||
} else {
|
||||
f = (C ^ (B | (~D)));
|
||||
n = (7 * i) % 16;
|
||||
}
|
||||
|
||||
tmp = D;
|
||||
D = C;
|
||||
C = B;
|
||||
sum = (A + f + goog.crypt.Md5.T_[i] + X[n]) & 0xffffffff;
|
||||
B += ((sum << goog.crypt.Md5.S_[i]) & 0xffffffff) |
|
||||
(sum >>> (32 - goog.crypt.Md5.S_[i]));
|
||||
A = tmp;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is an unrolled MD5 implementation, which gives ~30% speedup compared
|
||||
* to the abbreviated implementation above, as measured on Chrome 11. It is
|
||||
* important to keep 32-bit croppings to minimum and inline the integer
|
||||
* rotation.
|
||||
*/
|
||||
sum = (A + (D ^ (B & (C ^ D))) + X[0] + 0xd76aa478) & 0xffffffff;
|
||||
A = B + (((sum << 7) & 0xffffffff) | (sum >>> 25));
|
||||
sum = (D + (C ^ (A & (B ^ C))) + X[1] + 0xe8c7b756) & 0xffffffff;
|
||||
D = A + (((sum << 12) & 0xffffffff) | (sum >>> 20));
|
||||
sum = (C + (B ^ (D & (A ^ B))) + X[2] + 0x242070db) & 0xffffffff;
|
||||
C = D + (((sum << 17) & 0xffffffff) | (sum >>> 15));
|
||||
sum = (B + (A ^ (C & (D ^ A))) + X[3] + 0xc1bdceee) & 0xffffffff;
|
||||
B = C + (((sum << 22) & 0xffffffff) | (sum >>> 10));
|
||||
sum = (A + (D ^ (B & (C ^ D))) + X[4] + 0xf57c0faf) & 0xffffffff;
|
||||
A = B + (((sum << 7) & 0xffffffff) | (sum >>> 25));
|
||||
sum = (D + (C ^ (A & (B ^ C))) + X[5] + 0x4787c62a) & 0xffffffff;
|
||||
D = A + (((sum << 12) & 0xffffffff) | (sum >>> 20));
|
||||
sum = (C + (B ^ (D & (A ^ B))) + X[6] + 0xa8304613) & 0xffffffff;
|
||||
C = D + (((sum << 17) & 0xffffffff) | (sum >>> 15));
|
||||
sum = (B + (A ^ (C & (D ^ A))) + X[7] + 0xfd469501) & 0xffffffff;
|
||||
B = C + (((sum << 22) & 0xffffffff) | (sum >>> 10));
|
||||
sum = (A + (D ^ (B & (C ^ D))) + X[8] + 0x698098d8) & 0xffffffff;
|
||||
A = B + (((sum << 7) & 0xffffffff) | (sum >>> 25));
|
||||
sum = (D + (C ^ (A & (B ^ C))) + X[9] + 0x8b44f7af) & 0xffffffff;
|
||||
D = A + (((sum << 12) & 0xffffffff) | (sum >>> 20));
|
||||
sum = (C + (B ^ (D & (A ^ B))) + X[10] + 0xffff5bb1) & 0xffffffff;
|
||||
C = D + (((sum << 17) & 0xffffffff) | (sum >>> 15));
|
||||
sum = (B + (A ^ (C & (D ^ A))) + X[11] + 0x895cd7be) & 0xffffffff;
|
||||
B = C + (((sum << 22) & 0xffffffff) | (sum >>> 10));
|
||||
sum = (A + (D ^ (B & (C ^ D))) + X[12] + 0x6b901122) & 0xffffffff;
|
||||
A = B + (((sum << 7) & 0xffffffff) | (sum >>> 25));
|
||||
sum = (D + (C ^ (A & (B ^ C))) + X[13] + 0xfd987193) & 0xffffffff;
|
||||
D = A + (((sum << 12) & 0xffffffff) | (sum >>> 20));
|
||||
sum = (C + (B ^ (D & (A ^ B))) + X[14] + 0xa679438e) & 0xffffffff;
|
||||
C = D + (((sum << 17) & 0xffffffff) | (sum >>> 15));
|
||||
sum = (B + (A ^ (C & (D ^ A))) + X[15] + 0x49b40821) & 0xffffffff;
|
||||
B = C + (((sum << 22) & 0xffffffff) | (sum >>> 10));
|
||||
sum = (A + (C ^ (D & (B ^ C))) + X[1] + 0xf61e2562) & 0xffffffff;
|
||||
A = B + (((sum << 5) & 0xffffffff) | (sum >>> 27));
|
||||
sum = (D + (B ^ (C & (A ^ B))) + X[6] + 0xc040b340) & 0xffffffff;
|
||||
D = A + (((sum << 9) & 0xffffffff) | (sum >>> 23));
|
||||
sum = (C + (A ^ (B & (D ^ A))) + X[11] + 0x265e5a51) & 0xffffffff;
|
||||
C = D + (((sum << 14) & 0xffffffff) | (sum >>> 18));
|
||||
sum = (B + (D ^ (A & (C ^ D))) + X[0] + 0xe9b6c7aa) & 0xffffffff;
|
||||
B = C + (((sum << 20) & 0xffffffff) | (sum >>> 12));
|
||||
sum = (A + (C ^ (D & (B ^ C))) + X[5] + 0xd62f105d) & 0xffffffff;
|
||||
A = B + (((sum << 5) & 0xffffffff) | (sum >>> 27));
|
||||
sum = (D + (B ^ (C & (A ^ B))) + X[10] + 0x02441453) & 0xffffffff;
|
||||
D = A + (((sum << 9) & 0xffffffff) | (sum >>> 23));
|
||||
sum = (C + (A ^ (B & (D ^ A))) + X[15] + 0xd8a1e681) & 0xffffffff;
|
||||
C = D + (((sum << 14) & 0xffffffff) | (sum >>> 18));
|
||||
sum = (B + (D ^ (A & (C ^ D))) + X[4] + 0xe7d3fbc8) & 0xffffffff;
|
||||
B = C + (((sum << 20) & 0xffffffff) | (sum >>> 12));
|
||||
sum = (A + (C ^ (D & (B ^ C))) + X[9] + 0x21e1cde6) & 0xffffffff;
|
||||
A = B + (((sum << 5) & 0xffffffff) | (sum >>> 27));
|
||||
sum = (D + (B ^ (C & (A ^ B))) + X[14] + 0xc33707d6) & 0xffffffff;
|
||||
D = A + (((sum << 9) & 0xffffffff) | (sum >>> 23));
|
||||
sum = (C + (A ^ (B & (D ^ A))) + X[3] + 0xf4d50d87) & 0xffffffff;
|
||||
C = D + (((sum << 14) & 0xffffffff) | (sum >>> 18));
|
||||
sum = (B + (D ^ (A & (C ^ D))) + X[8] + 0x455a14ed) & 0xffffffff;
|
||||
B = C + (((sum << 20) & 0xffffffff) | (sum >>> 12));
|
||||
sum = (A + (C ^ (D & (B ^ C))) + X[13] + 0xa9e3e905) & 0xffffffff;
|
||||
A = B + (((sum << 5) & 0xffffffff) | (sum >>> 27));
|
||||
sum = (D + (B ^ (C & (A ^ B))) + X[2] + 0xfcefa3f8) & 0xffffffff;
|
||||
D = A + (((sum << 9) & 0xffffffff) | (sum >>> 23));
|
||||
sum = (C + (A ^ (B & (D ^ A))) + X[7] + 0x676f02d9) & 0xffffffff;
|
||||
C = D + (((sum << 14) & 0xffffffff) | (sum >>> 18));
|
||||
sum = (B + (D ^ (A & (C ^ D))) + X[12] + 0x8d2a4c8a) & 0xffffffff;
|
||||
B = C + (((sum << 20) & 0xffffffff) | (sum >>> 12));
|
||||
sum = (A + (B ^ C ^ D) + X[5] + 0xfffa3942) & 0xffffffff;
|
||||
A = B + (((sum << 4) & 0xffffffff) | (sum >>> 28));
|
||||
sum = (D + (A ^ B ^ C) + X[8] + 0x8771f681) & 0xffffffff;
|
||||
D = A + (((sum << 11) & 0xffffffff) | (sum >>> 21));
|
||||
sum = (C + (D ^ A ^ B) + X[11] + 0x6d9d6122) & 0xffffffff;
|
||||
C = D + (((sum << 16) & 0xffffffff) | (sum >>> 16));
|
||||
sum = (B + (C ^ D ^ A) + X[14] + 0xfde5380c) & 0xffffffff;
|
||||
B = C + (((sum << 23) & 0xffffffff) | (sum >>> 9));
|
||||
sum = (A + (B ^ C ^ D) + X[1] + 0xa4beea44) & 0xffffffff;
|
||||
A = B + (((sum << 4) & 0xffffffff) | (sum >>> 28));
|
||||
sum = (D + (A ^ B ^ C) + X[4] + 0x4bdecfa9) & 0xffffffff;
|
||||
D = A + (((sum << 11) & 0xffffffff) | (sum >>> 21));
|
||||
sum = (C + (D ^ A ^ B) + X[7] + 0xf6bb4b60) & 0xffffffff;
|
||||
C = D + (((sum << 16) & 0xffffffff) | (sum >>> 16));
|
||||
sum = (B + (C ^ D ^ A) + X[10] + 0xbebfbc70) & 0xffffffff;
|
||||
B = C + (((sum << 23) & 0xffffffff) | (sum >>> 9));
|
||||
sum = (A + (B ^ C ^ D) + X[13] + 0x289b7ec6) & 0xffffffff;
|
||||
A = B + (((sum << 4) & 0xffffffff) | (sum >>> 28));
|
||||
sum = (D + (A ^ B ^ C) + X[0] + 0xeaa127fa) & 0xffffffff;
|
||||
D = A + (((sum << 11) & 0xffffffff) | (sum >>> 21));
|
||||
sum = (C + (D ^ A ^ B) + X[3] + 0xd4ef3085) & 0xffffffff;
|
||||
C = D + (((sum << 16) & 0xffffffff) | (sum >>> 16));
|
||||
sum = (B + (C ^ D ^ A) + X[6] + 0x04881d05) & 0xffffffff;
|
||||
B = C + (((sum << 23) & 0xffffffff) | (sum >>> 9));
|
||||
sum = (A + (B ^ C ^ D) + X[9] + 0xd9d4d039) & 0xffffffff;
|
||||
A = B + (((sum << 4) & 0xffffffff) | (sum >>> 28));
|
||||
sum = (D + (A ^ B ^ C) + X[12] + 0xe6db99e5) & 0xffffffff;
|
||||
D = A + (((sum << 11) & 0xffffffff) | (sum >>> 21));
|
||||
sum = (C + (D ^ A ^ B) + X[15] + 0x1fa27cf8) & 0xffffffff;
|
||||
C = D + (((sum << 16) & 0xffffffff) | (sum >>> 16));
|
||||
sum = (B + (C ^ D ^ A) + X[2] + 0xc4ac5665) & 0xffffffff;
|
||||
B = C + (((sum << 23) & 0xffffffff) | (sum >>> 9));
|
||||
sum = (A + (C ^ (B | (~D))) + X[0] + 0xf4292244) & 0xffffffff;
|
||||
A = B + (((sum << 6) & 0xffffffff) | (sum >>> 26));
|
||||
sum = (D + (B ^ (A | (~C))) + X[7] + 0x432aff97) & 0xffffffff;
|
||||
D = A + (((sum << 10) & 0xffffffff) | (sum >>> 22));
|
||||
sum = (C + (A ^ (D | (~B))) + X[14] + 0xab9423a7) & 0xffffffff;
|
||||
C = D + (((sum << 15) & 0xffffffff) | (sum >>> 17));
|
||||
sum = (B + (D ^ (C | (~A))) + X[5] + 0xfc93a039) & 0xffffffff;
|
||||
B = C + (((sum << 21) & 0xffffffff) | (sum >>> 11));
|
||||
sum = (A + (C ^ (B | (~D))) + X[12] + 0x655b59c3) & 0xffffffff;
|
||||
A = B + (((sum << 6) & 0xffffffff) | (sum >>> 26));
|
||||
sum = (D + (B ^ (A | (~C))) + X[3] + 0x8f0ccc92) & 0xffffffff;
|
||||
D = A + (((sum << 10) & 0xffffffff) | (sum >>> 22));
|
||||
sum = (C + (A ^ (D | (~B))) + X[10] + 0xffeff47d) & 0xffffffff;
|
||||
C = D + (((sum << 15) & 0xffffffff) | (sum >>> 17));
|
||||
sum = (B + (D ^ (C | (~A))) + X[1] + 0x85845dd1) & 0xffffffff;
|
||||
B = C + (((sum << 21) & 0xffffffff) | (sum >>> 11));
|
||||
sum = (A + (C ^ (B | (~D))) + X[8] + 0x6fa87e4f) & 0xffffffff;
|
||||
A = B + (((sum << 6) & 0xffffffff) | (sum >>> 26));
|
||||
sum = (D + (B ^ (A | (~C))) + X[15] + 0xfe2ce6e0) & 0xffffffff;
|
||||
D = A + (((sum << 10) & 0xffffffff) | (sum >>> 22));
|
||||
sum = (C + (A ^ (D | (~B))) + X[6] + 0xa3014314) & 0xffffffff;
|
||||
C = D + (((sum << 15) & 0xffffffff) | (sum >>> 17));
|
||||
sum = (B + (D ^ (C | (~A))) + X[13] + 0x4e0811a1) & 0xffffffff;
|
||||
B = C + (((sum << 21) & 0xffffffff) | (sum >>> 11));
|
||||
sum = (A + (C ^ (B | (~D))) + X[4] + 0xf7537e82) & 0xffffffff;
|
||||
A = B + (((sum << 6) & 0xffffffff) | (sum >>> 26));
|
||||
sum = (D + (B ^ (A | (~C))) + X[11] + 0xbd3af235) & 0xffffffff;
|
||||
D = A + (((sum << 10) & 0xffffffff) | (sum >>> 22));
|
||||
sum = (C + (A ^ (D | (~B))) + X[2] + 0x2ad7d2bb) & 0xffffffff;
|
||||
C = D + (((sum << 15) & 0xffffffff) | (sum >>> 17));
|
||||
sum = (B + (D ^ (C | (~A))) + X[9] + 0xeb86d391) & 0xffffffff;
|
||||
B = C + (((sum << 21) & 0xffffffff) | (sum >>> 11));
|
||||
|
||||
this.chain_[0] = (this.chain_[0] + A) & 0xffffffff;
|
||||
this.chain_[1] = (this.chain_[1] + B) & 0xffffffff;
|
||||
this.chain_[2] = (this.chain_[2] + C) & 0xffffffff;
|
||||
this.chain_[3] = (this.chain_[3] + D) & 0xffffffff;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Md5.prototype.update = function(bytes, opt_length) {
|
||||
if (!goog.isDef(opt_length)) {
|
||||
opt_length = bytes.length;
|
||||
}
|
||||
var lengthMinusBlock = opt_length - 64;
|
||||
|
||||
// Copy some object properties to local variables in order to save on access
|
||||
// time from inside the loop (~10% speedup was observed on Chrome 11).
|
||||
var block = this.block_;
|
||||
var blockLength = this.blockLength_;
|
||||
var i = 0;
|
||||
|
||||
// The outer while loop should execute at most twice.
|
||||
while (i < opt_length) {
|
||||
// When we have no data in the block to top up, we can directly process the
|
||||
// input buffer (assuming it contains sufficient data). This gives ~30%
|
||||
// speedup on Chrome 14 and ~70% speedup on Firefox 6.0, but requires that
|
||||
// the data is provided in large chunks (or in multiples of 64 bytes).
|
||||
if (blockLength == 0) {
|
||||
while (i <= lengthMinusBlock) {
|
||||
this.compress_(bytes, i);
|
||||
i += 64;
|
||||
}
|
||||
}
|
||||
|
||||
if (goog.isString(bytes)) {
|
||||
while (i < opt_length) {
|
||||
block[blockLength++] = bytes.charCodeAt(i++);
|
||||
if (blockLength == 64) {
|
||||
this.compress_(block);
|
||||
blockLength = 0;
|
||||
// Jump to the outer loop so we use the full-block optimization.
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (i < opt_length) {
|
||||
block[blockLength++] = bytes[i++];
|
||||
if (blockLength == 64) {
|
||||
this.compress_(block);
|
||||
blockLength = 0;
|
||||
// Jump to the outer loop so we use the full-block optimization.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.blockLength_ = blockLength;
|
||||
this.totalLength_ += opt_length;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Md5.prototype.digest = function() {
|
||||
// This must accommodate at least 1 padding byte (0x80), 8 bytes of
|
||||
// total bitlength, and must end at a 64-byte boundary.
|
||||
var pad = new Array((this.blockLength_ < 56 ? 64 : 128) - this.blockLength_);
|
||||
|
||||
// Add padding: 0x80 0x00*
|
||||
pad[0] = 0x80;
|
||||
for (var i = 1; i < pad.length - 8; ++i) {
|
||||
pad[i] = 0;
|
||||
}
|
||||
// Add the total number of bits, little endian 64-bit integer.
|
||||
var totalBits = this.totalLength_ * 8;
|
||||
for (var i = pad.length - 8; i < pad.length; ++i) {
|
||||
pad[i] = totalBits & 0xff;
|
||||
totalBits /= 0x100; // Don't use bit-shifting here!
|
||||
}
|
||||
this.update(pad);
|
||||
|
||||
var digest = new Array(16);
|
||||
var n = 0;
|
||||
for (var i = 0; i < 4; ++i) {
|
||||
for (var j = 0; j < 32; j += 8) {
|
||||
digest[n++] = (this.chain_[i] >>> j) & 0xff;
|
||||
}
|
||||
}
|
||||
return digest;
|
||||
};
|
||||
@@ -0,0 +1,127 @@
|
||||
// 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.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;
|
||||
};
|
||||
@@ -0,0 +1,275 @@
|
||||
// Copyright 2005 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 SHA-1 cryptographic hash.
|
||||
* Variable names follow the notation in FIPS PUB 180-3:
|
||||
* http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf.
|
||||
*
|
||||
* Usage:
|
||||
* var sha1 = new goog.crypt.sha1();
|
||||
* sha1.update(bytes);
|
||||
* var hash = sha1.digest();
|
||||
*
|
||||
* Performance:
|
||||
* Chrome 23: ~400 Mbit/s
|
||||
* Firefox 16: ~250 Mbit/s
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.Sha1');
|
||||
|
||||
goog.require('goog.crypt.Hash');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SHA-1 cryptographic hash constructor.
|
||||
*
|
||||
* The properties declared here are discussed in the above algorithm document.
|
||||
* @constructor
|
||||
* @extends {goog.crypt.Hash}
|
||||
*/
|
||||
goog.crypt.Sha1 = function() {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* Holds the previous values of accumulated variables a-e in the compress_
|
||||
* function.
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.chain_ = [];
|
||||
|
||||
/**
|
||||
* A buffer holding the partially computed hash result.
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.buf_ = [];
|
||||
|
||||
/**
|
||||
* An array of 80 bytes, each a part of the message to be hashed. Referred to
|
||||
* as the message schedule in the docs.
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.W_ = [];
|
||||
|
||||
/**
|
||||
* Contains data needed to pad messages less than 64 bytes.
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.pad_ = [];
|
||||
|
||||
this.pad_[0] = 128;
|
||||
for (var i = 1; i < 64; ++i) {
|
||||
this.pad_[i] = 0;
|
||||
}
|
||||
|
||||
this.reset();
|
||||
};
|
||||
goog.inherits(goog.crypt.Sha1, goog.crypt.Hash);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Sha1.prototype.reset = function() {
|
||||
this.chain_[0] = 0x67452301;
|
||||
this.chain_[1] = 0xefcdab89;
|
||||
this.chain_[2] = 0x98badcfe;
|
||||
this.chain_[3] = 0x10325476;
|
||||
this.chain_[4] = 0xc3d2e1f0;
|
||||
|
||||
this.inbuf_ = 0;
|
||||
this.total_ = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Internal compress helper function.
|
||||
* @param {Array.<number>|Uint8Array|string} buf Block to compress.
|
||||
* @param {number=} opt_offset Offset of the block in the buffer.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.Sha1.prototype.compress_ = function(buf, opt_offset) {
|
||||
if (!opt_offset) {
|
||||
opt_offset = 0;
|
||||
}
|
||||
|
||||
var W = this.W_;
|
||||
|
||||
// get 16 big endian words
|
||||
if (goog.isString(buf)) {
|
||||
for (var i = 0; i < 16; i++) {
|
||||
// TODO(user): [bug 8140122] Recent versions of Safari for Mac OS and iOS
|
||||
// have a bug that turns the post-increment ++ operator into pre-increment
|
||||
// during JIT compilation. We have code that depends heavily on SHA-1 for
|
||||
// correctness and which is affected by this bug, so I've removed all uses
|
||||
// of post-increment ++ in which the result value is used. We can revert
|
||||
// this change once the Safari bug
|
||||
// (https://bugs.webkit.org/show_bug.cgi?id=109036) has been fixed and
|
||||
// most clients have been updated.
|
||||
W[i] = (buf.charCodeAt(opt_offset) << 24) |
|
||||
(buf.charCodeAt(opt_offset + 1) << 16) |
|
||||
(buf.charCodeAt(opt_offset + 2) << 8) |
|
||||
(buf.charCodeAt(opt_offset + 3));
|
||||
opt_offset += 4;
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < 16; i++) {
|
||||
W[i] = (buf[opt_offset] << 24) |
|
||||
(buf[opt_offset + 1] << 16) |
|
||||
(buf[opt_offset + 2] << 8) |
|
||||
(buf[opt_offset + 3]);
|
||||
opt_offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// expand to 80 words
|
||||
for (var i = 16; i < 80; i++) {
|
||||
var t = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];
|
||||
W[i] = ((t << 1) | (t >>> 31)) & 0xffffffff;
|
||||
}
|
||||
|
||||
var a = this.chain_[0];
|
||||
var b = this.chain_[1];
|
||||
var c = this.chain_[2];
|
||||
var d = this.chain_[3];
|
||||
var e = this.chain_[4];
|
||||
var f, k;
|
||||
|
||||
// TODO(user): Try to unroll this loop to speed up the computation.
|
||||
for (var i = 0; i < 80; i++) {
|
||||
if (i < 40) {
|
||||
if (i < 20) {
|
||||
f = d ^ (b & (c ^ d));
|
||||
k = 0x5a827999;
|
||||
} else {
|
||||
f = b ^ c ^ d;
|
||||
k = 0x6ed9eba1;
|
||||
}
|
||||
} else {
|
||||
if (i < 60) {
|
||||
f = (b & c) | (d & (b | c));
|
||||
k = 0x8f1bbcdc;
|
||||
} else {
|
||||
f = b ^ c ^ d;
|
||||
k = 0xca62c1d6;
|
||||
}
|
||||
}
|
||||
|
||||
var t = (((a << 5) | (a >>> 27)) + f + e + k + W[i]) & 0xffffffff;
|
||||
e = d;
|
||||
d = c;
|
||||
c = ((b << 30) | (b >>> 2)) & 0xffffffff;
|
||||
b = a;
|
||||
a = t;
|
||||
}
|
||||
|
||||
this.chain_[0] = (this.chain_[0] + a) & 0xffffffff;
|
||||
this.chain_[1] = (this.chain_[1] + b) & 0xffffffff;
|
||||
this.chain_[2] = (this.chain_[2] + c) & 0xffffffff;
|
||||
this.chain_[3] = (this.chain_[3] + d) & 0xffffffff;
|
||||
this.chain_[4] = (this.chain_[4] + e) & 0xffffffff;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Sha1.prototype.update = function(bytes, opt_length) {
|
||||
if (!goog.isDef(opt_length)) {
|
||||
opt_length = bytes.length;
|
||||
}
|
||||
|
||||
var lengthMinusBlock = opt_length - 64;
|
||||
var n = 0;
|
||||
// Using local instead of member variables gives ~5% speedup on Firefox 16.
|
||||
var buf = this.buf_;
|
||||
var inbuf = this.inbuf_;
|
||||
|
||||
// The outer while loop should execute at most twice.
|
||||
while (n < opt_length) {
|
||||
// When we have no data in the block to top up, we can directly process the
|
||||
// input buffer (assuming it contains sufficient data). This gives ~25%
|
||||
// speedup on Chrome 23 and ~15% speedup on Firefox 16, but requires that
|
||||
// the data is provided in large chunks (or in multiples of 64 bytes).
|
||||
if (inbuf == 0) {
|
||||
while (n <= lengthMinusBlock) {
|
||||
this.compress_(bytes, n);
|
||||
n += 64;
|
||||
}
|
||||
}
|
||||
|
||||
if (goog.isString(bytes)) {
|
||||
while (n < opt_length) {
|
||||
buf[inbuf] = bytes.charCodeAt(n);
|
||||
++inbuf;
|
||||
++n;
|
||||
if (inbuf == 64) {
|
||||
this.compress_(buf);
|
||||
inbuf = 0;
|
||||
// Jump to the outer loop so we use the full-block optimization.
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (n < opt_length) {
|
||||
buf[inbuf] = bytes[n];
|
||||
++inbuf;
|
||||
++n;
|
||||
if (inbuf == 64) {
|
||||
this.compress_(buf);
|
||||
inbuf = 0;
|
||||
// Jump to the outer loop so we use the full-block optimization.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.inbuf_ = inbuf;
|
||||
this.total_ += opt_length;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Sha1.prototype.digest = function() {
|
||||
var digest = [];
|
||||
var totalBits = this.total_ * 8;
|
||||
|
||||
// Add pad 0x80 0x00*.
|
||||
if (this.inbuf_ < 56) {
|
||||
this.update(this.pad_, 56 - this.inbuf_);
|
||||
} else {
|
||||
this.update(this.pad_, 64 - (this.inbuf_ - 56));
|
||||
}
|
||||
|
||||
// Add # bits.
|
||||
for (var i = 63; i >= 56; i--) {
|
||||
this.buf_[i] = totalBits & 255;
|
||||
totalBits /= 256; // Don't use bit-shifting here!
|
||||
}
|
||||
|
||||
this.compress_(this.buf_);
|
||||
|
||||
var n = 0;
|
||||
for (var i = 0; i < 5; i++) {
|
||||
for (var j = 24; j >= 0; j -= 8) {
|
||||
digest[n] = (this.chain_[i] >> j) & 255;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
return digest;
|
||||
};
|
||||
@@ -0,0 +1,275 @@
|
||||
// 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 Base class for SHA-2 cryptographic hash.
|
||||
*
|
||||
* Variable names follow the notation in FIPS PUB 180-3:
|
||||
* http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf.
|
||||
*
|
||||
* To implement specific SHA-2 such as SHA-256, create a sub-class with
|
||||
* overridded reset(). See sha256.js for an example.
|
||||
*
|
||||
* TODO(user): SHA-512/384 are not currently implemented. Could be added
|
||||
* if needed.
|
||||
*
|
||||
* Some code similar to SHA1 are borrowed from sha1.js written by mschilder@.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.Sha2');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.crypt.Hash');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SHA-2 cryptographic hash constructor.
|
||||
* This constructor should not be used directly to create the object. Rather,
|
||||
* one should use the constructor of the sub-classes.
|
||||
* @constructor
|
||||
* @extends {goog.crypt.Hash}
|
||||
*/
|
||||
goog.crypt.Sha2 = function() {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* A chunk holding the currently processed message bytes. Once the chunk has
|
||||
* 64 bytes, we feed it into computeChunk_ function and reset this.chunk_.
|
||||
* Sub-class needs to reset it when overriding reset().
|
||||
* @type {!Array.<number>}
|
||||
* @protected
|
||||
*/
|
||||
this.chunk = [];
|
||||
|
||||
/**
|
||||
* Current number of bytes in this.chunk_.
|
||||
* Sub-class needs to reset it when overriding reset().
|
||||
* @type {number}
|
||||
* @protected
|
||||
*/
|
||||
this.inChunk = 0;
|
||||
|
||||
/**
|
||||
* Total number of bytes in currently processed message.
|
||||
* Sub-class needs to reset it when overriding reset().
|
||||
* @type {number}
|
||||
* @protected
|
||||
*/
|
||||
this.total = 0;
|
||||
|
||||
/**
|
||||
* Contains data needed to pad messages less than 64 bytes.
|
||||
* @type {!Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.pad_ = goog.array.repeat(0, 64);
|
||||
this.pad_[0] = 128;
|
||||
|
||||
/**
|
||||
* Holds the previous values of accumulated hash a-h in the computeChunk_
|
||||
* function.
|
||||
* It is a subclass-dependent value. Sub-class needs to explicitly set it
|
||||
* when overriding reset().
|
||||
* @type {!Array.<number>}
|
||||
* @protected
|
||||
*/
|
||||
this.hash = [];
|
||||
|
||||
/**
|
||||
* The number of output hash blocks (each block is 4 bytes long).
|
||||
* It is a subclass-dependent value. Sub-class needs to explicitly set it
|
||||
* when overriding reset().
|
||||
* @type {number}
|
||||
* @protected
|
||||
*/
|
||||
this.numHashBlocks = 0;
|
||||
|
||||
this.reset();
|
||||
};
|
||||
goog.inherits(goog.crypt.Sha2, goog.crypt.Hash);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Sha2.prototype.reset = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to compute the hashes for a given 512-bit message chunk.
|
||||
* @param {!Array.<number>} chunk A 512-bit message chunk to be processed.
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.Sha2.prototype.computeChunk_ = function(chunk) {
|
||||
goog.asserts.assert(chunk.length == 64);
|
||||
|
||||
// Divide the chunk into 16 32-bit-words.
|
||||
var w = [];
|
||||
var index = 0;
|
||||
var offset = 0;
|
||||
while (offset < chunk.length) {
|
||||
w[index++] = (chunk[offset] << 24) |
|
||||
(chunk[offset + 1] << 16) |
|
||||
(chunk[offset + 2] << 8) |
|
||||
(chunk[offset + 3]);
|
||||
offset = index * 4;
|
||||
}
|
||||
|
||||
// Expand to 64 32-bit-words
|
||||
for (var i = 16; i < 64; i++) {
|
||||
var s0 = ((w[i - 15] >>> 7) | (w[i - 15] << 25)) ^
|
||||
((w[i - 15] >>> 18) | (w[i - 15] << 14)) ^
|
||||
(w[i - 15] >>> 3);
|
||||
var s1 = ((w[i - 2] >>> 17) | (w[i - 2] << 15)) ^
|
||||
((w[i - 2] >>> 19) | (w[i - 2] << 13)) ^
|
||||
(w[i - 2] >>> 10);
|
||||
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) & 0xffffffff;
|
||||
}
|
||||
|
||||
var a = this.hash[0];
|
||||
var b = this.hash[1];
|
||||
var c = this.hash[2];
|
||||
var d = this.hash[3];
|
||||
var e = this.hash[4];
|
||||
var f = this.hash[5];
|
||||
var g = this.hash[6];
|
||||
var h = this.hash[7];
|
||||
|
||||
for (var i = 0; i < 64; i++) {
|
||||
var S0 = ((a >>> 2) | (a << 30)) ^
|
||||
((a >>> 13) | (a << 19)) ^
|
||||
((a >>> 22) | (a << 10));
|
||||
var maj = ((a & b) ^ (a & c) ^ (b & c));
|
||||
var t2 = (S0 + maj) & 0xffffffff;
|
||||
var S1 = ((e >>> 6) | (e << 26)) ^
|
||||
((e >>> 11) | (e << 21)) ^
|
||||
((e >>> 25) | (e << 7));
|
||||
var ch = ((e & f) ^ ((~ e) & g));
|
||||
var t1 = (h + S1 + ch + this.K_[i] + w[i]) & 0xffffffff;
|
||||
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = (d + t1) & 0xffffffff;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = (t1 + t2) & 0xffffffff;
|
||||
}
|
||||
|
||||
this.hash[0] = (this.hash[0] + a) & 0xffffffff;
|
||||
this.hash[1] = (this.hash[1] + b) & 0xffffffff;
|
||||
this.hash[2] = (this.hash[2] + c) & 0xffffffff;
|
||||
this.hash[3] = (this.hash[3] + d) & 0xffffffff;
|
||||
this.hash[4] = (this.hash[4] + e) & 0xffffffff;
|
||||
this.hash[5] = (this.hash[5] + f) & 0xffffffff;
|
||||
this.hash[6] = (this.hash[6] + g) & 0xffffffff;
|
||||
this.hash[7] = (this.hash[7] + h) & 0xffffffff;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Sha2.prototype.update = function(message, opt_length) {
|
||||
if (!goog.isDef(opt_length)) {
|
||||
opt_length = message.length;
|
||||
}
|
||||
// Process the message from left to right up to |opt_length| bytes.
|
||||
// When we get a 512-bit chunk, compute the hash of it and reset
|
||||
// this.chunk_. The message might not be multiple of 512 bits so we
|
||||
// might end up with a chunk that is less than 512 bits. We store
|
||||
// such partial chunk in this.chunk_ and it will be filled up later
|
||||
// in digest().
|
||||
var n = 0;
|
||||
var inChunk = this.inChunk;
|
||||
|
||||
// The input message could be either byte array of string.
|
||||
if (goog.isString(message)) {
|
||||
while (n < opt_length) {
|
||||
this.chunk[inChunk++] = message.charCodeAt(n++);
|
||||
if (inChunk == 64) {
|
||||
this.computeChunk_(this.chunk);
|
||||
inChunk = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (n < opt_length) {
|
||||
this.chunk[inChunk++] = message[n++];
|
||||
if (inChunk == 64) {
|
||||
this.computeChunk_(this.chunk);
|
||||
inChunk = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Record the current bytes in chunk to support partial update.
|
||||
this.inChunk = inChunk;
|
||||
|
||||
// Record total message bytes we have processed so far.
|
||||
this.total += opt_length;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Sha2.prototype.digest = function() {
|
||||
var digest = [];
|
||||
var totalBits = this.total * 8;
|
||||
|
||||
// Append pad 0x80 0x00*.
|
||||
if (this.inChunk < 56) {
|
||||
this.update(this.pad_, 56 - this.inChunk);
|
||||
} else {
|
||||
this.update(this.pad_, 64 - (this.inChunk - 56));
|
||||
}
|
||||
|
||||
// Append # bits in the 64-bit big-endian format.
|
||||
for (var i = 63; i >= 56; i--) {
|
||||
this.chunk[i] = totalBits & 255;
|
||||
totalBits /= 256; // Don't use bit-shifting here!
|
||||
}
|
||||
this.computeChunk_(this.chunk);
|
||||
|
||||
// Finally, output the result digest.
|
||||
var n = 0;
|
||||
for (var i = 0; i < this.numHashBlocks; i++) {
|
||||
for (var j = 24; j >= 0; j -= 8) {
|
||||
digest[n++] = ((this.hash[i] >> j) & 255);
|
||||
}
|
||||
}
|
||||
return digest;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Constants used in SHA-2.
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.crypt.Sha2.prototype.K_ = [
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
||||
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
||||
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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 SHA-224 cryptographic hash.
|
||||
*
|
||||
* Usage:
|
||||
* var sha224 = new goog.crypt.Sha224();
|
||||
* sha224.update(bytes);
|
||||
* var hash = sha224.digest();
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.Sha224');
|
||||
|
||||
goog.require('goog.crypt.Sha2');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SHA-224 cryptographic hash constructor.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.crypt.Sha2}
|
||||
*/
|
||||
goog.crypt.Sha224 = function() {
|
||||
goog.base(this);
|
||||
};
|
||||
goog.inherits(goog.crypt.Sha224, goog.crypt.Sha2);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Sha224.prototype.reset = function() {
|
||||
this.chunk = [];
|
||||
this.inChunk = 0;
|
||||
this.total = 0;
|
||||
|
||||
this.hash = [
|
||||
0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
|
||||
0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4];
|
||||
this.numHashBlocks = 7;
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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 SHA-256 cryptographic hash.
|
||||
*
|
||||
* Usage:
|
||||
* var sha256 = new goog.crypt.Sha256();
|
||||
* sha256.update(bytes);
|
||||
* var hash = sha256.digest();
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.Sha256');
|
||||
|
||||
goog.require('goog.crypt.Sha2');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SHA-256 cryptographic hash constructor.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.crypt.Sha2}
|
||||
*/
|
||||
goog.crypt.Sha256 = function() {
|
||||
goog.base(this);
|
||||
};
|
||||
goog.inherits(goog.crypt.Sha256, goog.crypt.Sha2);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.crypt.Sha256.prototype.reset = function() {
|
||||
this.chunk = [];
|
||||
this.inChunk = 0;
|
||||
this.total = 0;
|
||||
|
||||
this.hash = [
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19];
|
||||
this.numHashBlocks = 8;
|
||||
};
|
||||
Reference in New Issue
Block a user