Path: blob/trunk/third_party/closure/goog/crypt/sha1.js
2868 views
// Copyright 2005 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview SHA-1 cryptographic hash.16* Variable names follow the notation in FIPS PUB 180-3:17* http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf.18*19* Usage:20* var sha1 = new goog.crypt.sha1();21* sha1.update(bytes);22* var hash = sha1.digest();23*24* Performance:25* Chrome 23: ~400 Mbit/s26* Firefox 16: ~250 Mbit/s27*28*/2930goog.provide('goog.crypt.Sha1');3132goog.require('goog.crypt.Hash');33343536/**37* SHA-1 cryptographic hash constructor.38*39* The properties declared here are discussed in the above algorithm document.40* @constructor41* @extends {goog.crypt.Hash}42* @final43* @struct44*/45goog.crypt.Sha1 = function() {46goog.crypt.Sha1.base(this, 'constructor');4748this.blockSize = 512 / 8;4950/**51* Holds the previous values of accumulated variables a-e in the compress_52* function.53* @type {!Array<number>}54* @private55*/56this.chain_ = [];5758/**59* A buffer holding the partially computed hash result.60* @type {!Array<number>}61* @private62*/63this.buf_ = [];6465/**66* An array of 80 bytes, each a part of the message to be hashed. Referred to67* as the message schedule in the docs.68* @type {!Array<number>}69* @private70*/71this.W_ = [];7273/**74* Contains data needed to pad messages less than 64 bytes.75* @type {!Array<number>}76* @private77*/78this.pad_ = [];7980this.pad_[0] = 128;81for (var i = 1; i < this.blockSize; ++i) {82this.pad_[i] = 0;83}8485/**86* @private {number}87*/88this.inbuf_ = 0;8990/**91* @private {number}92*/93this.total_ = 0;9495this.reset();96};97goog.inherits(goog.crypt.Sha1, goog.crypt.Hash);9899100/** @override */101goog.crypt.Sha1.prototype.reset = function() {102this.chain_[0] = 0x67452301;103this.chain_[1] = 0xefcdab89;104this.chain_[2] = 0x98badcfe;105this.chain_[3] = 0x10325476;106this.chain_[4] = 0xc3d2e1f0;107108this.inbuf_ = 0;109this.total_ = 0;110};111112113/**114* Internal compress helper function.115* @param {!Array<number>|!Uint8Array|string} buf Block to compress.116* @param {number=} opt_offset Offset of the block in the buffer.117* @private118*/119goog.crypt.Sha1.prototype.compress_ = function(buf, opt_offset) {120if (!opt_offset) {121opt_offset = 0;122}123124var W = this.W_;125126// get 16 big endian words127if (goog.isString(buf)) {128for (var i = 0; i < 16; i++) {129// TODO(user): [bug 8140122] Recent versions of Safari for Mac OS and iOS130// have a bug that turns the post-increment ++ operator into pre-increment131// during JIT compilation. We have code that depends heavily on SHA-1 for132// correctness and which is affected by this bug, so I've removed all uses133// of post-increment ++ in which the result value is used. We can revert134// this change once the Safari bug135// (https://bugs.webkit.org/show_bug.cgi?id=109036) has been fixed and136// most clients have been updated.137W[i] = (buf.charCodeAt(opt_offset) << 24) |138(buf.charCodeAt(opt_offset + 1) << 16) |139(buf.charCodeAt(opt_offset + 2) << 8) |140(buf.charCodeAt(opt_offset + 3));141opt_offset += 4;142}143} else {144for (var i = 0; i < 16; i++) {145W[i] = (buf[opt_offset] << 24) | (buf[opt_offset + 1] << 16) |146(buf[opt_offset + 2] << 8) | (buf[opt_offset + 3]);147opt_offset += 4;148}149}150151// expand to 80 words152for (var i = 16; i < 80; i++) {153var t = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];154W[i] = ((t << 1) | (t >>> 31)) & 0xffffffff;155}156157var a = this.chain_[0];158var b = this.chain_[1];159var c = this.chain_[2];160var d = this.chain_[3];161var e = this.chain_[4];162var f, k;163164// TODO(user): Try to unroll this loop to speed up the computation.165for (var i = 0; i < 80; i++) {166if (i < 40) {167if (i < 20) {168f = d ^ (b & (c ^ d));169k = 0x5a827999;170} else {171f = b ^ c ^ d;172k = 0x6ed9eba1;173}174} else {175if (i < 60) {176f = (b & c) | (d & (b | c));177k = 0x8f1bbcdc;178} else {179f = b ^ c ^ d;180k = 0xca62c1d6;181}182}183184var t = (((a << 5) | (a >>> 27)) + f + e + k + W[i]) & 0xffffffff;185e = d;186d = c;187c = ((b << 30) | (b >>> 2)) & 0xffffffff;188b = a;189a = t;190}191192this.chain_[0] = (this.chain_[0] + a) & 0xffffffff;193this.chain_[1] = (this.chain_[1] + b) & 0xffffffff;194this.chain_[2] = (this.chain_[2] + c) & 0xffffffff;195this.chain_[3] = (this.chain_[3] + d) & 0xffffffff;196this.chain_[4] = (this.chain_[4] + e) & 0xffffffff;197};198199200/** @override */201goog.crypt.Sha1.prototype.update = function(bytes, opt_length) {202// TODO(johnlenz): tighten the function signature and remove this check203if (bytes == null) {204return;205}206207if (!goog.isDef(opt_length)) {208opt_length = bytes.length;209}210211var lengthMinusBlock = opt_length - this.blockSize;212var n = 0;213// Using local instead of member variables gives ~5% speedup on Firefox 16.214var buf = this.buf_;215var inbuf = this.inbuf_;216217// The outer while loop should execute at most twice.218while (n < opt_length) {219// When we have no data in the block to top up, we can directly process the220// input buffer (assuming it contains sufficient data). This gives ~25%221// speedup on Chrome 23 and ~15% speedup on Firefox 16, but requires that222// the data is provided in large chunks (or in multiples of 64 bytes).223if (inbuf == 0) {224while (n <= lengthMinusBlock) {225this.compress_(bytes, n);226n += this.blockSize;227}228}229230if (goog.isString(bytes)) {231while (n < opt_length) {232buf[inbuf] = bytes.charCodeAt(n);233++inbuf;234++n;235if (inbuf == this.blockSize) {236this.compress_(buf);237inbuf = 0;238// Jump to the outer loop so we use the full-block optimization.239break;240}241}242} else {243while (n < opt_length) {244buf[inbuf] = bytes[n];245++inbuf;246++n;247if (inbuf == this.blockSize) {248this.compress_(buf);249inbuf = 0;250// Jump to the outer loop so we use the full-block optimization.251break;252}253}254}255}256257this.inbuf_ = inbuf;258this.total_ += opt_length;259};260261262/** @override */263goog.crypt.Sha1.prototype.digest = function() {264var digest = [];265var totalBits = this.total_ * 8;266267// Add pad 0x80 0x00*.268if (this.inbuf_ < 56) {269this.update(this.pad_, 56 - this.inbuf_);270} else {271this.update(this.pad_, this.blockSize - (this.inbuf_ - 56));272}273274// Add # bits.275for (var i = this.blockSize - 1; i >= 56; i--) {276this.buf_[i] = totalBits & 255;277totalBits /= 256; // Don't use bit-shifting here!278}279280this.compress_(this.buf_);281282var n = 0;283for (var i = 0; i < 5; i++) {284for (var j = 24; j >= 0; j -= 8) {285digest[n] = (this.chain_[i] >> j) & 255;286++n;287}288}289290return digest;291};292293294