browserSha1.js
1 var Buffer = require('buffer/').Buffer; 2 var hashUtils = require('./browserHashUtils'); 3 4 var BLOCK_SIZE = 64; 5 6 var DIGEST_LENGTH = 20; 7 8 var KEY = new Uint32Array([ 9 0x5a827999, 10 0x6ed9eba1, 11 0x8f1bbcdc | 0, 12 0xca62c1d6 | 0 13 ]); 14 15 var INIT = [ 16 0x6a09e667, 17 0xbb67ae85, 18 0x3c6ef372, 19 0xa54ff53a, 20 0x510e527f, 21 0x9b05688c, 22 0x1f83d9ab, 23 0x5be0cd19, 24 ]; 25 26 var MAX_HASHABLE_LENGTH = Math.pow(2, 53) - 1; 27 28 /** 29 * @api private 30 */ 31 function Sha1() { 32 this.h0 = 0x67452301; 33 this.h1 = 0xEFCDAB89; 34 this.h2 = 0x98BADCFE; 35 this.h3 = 0x10325476; 36 this.h4 = 0xC3D2E1F0; 37 // The first 64 bytes (16 words) is the data chunk 38 this.block = new Uint32Array(80); 39 this.offset = 0; 40 this.shift = 24; 41 this.totalLength = 0; 42 } 43 44 /** 45 * @api private 46 */ 47 module.exports = exports = Sha1; 48 49 Sha1.BLOCK_SIZE = BLOCK_SIZE; 50 51 Sha1.prototype.update = function (data) { 52 if (this.finished) { 53 throw new Error('Attempted to update an already finished hash.'); 54 } 55 56 if (hashUtils.isEmptyData(data)) { 57 return this; 58 } 59 60 data = hashUtils.convertToBuffer(data); 61 62 var length = data.length; 63 this.totalLength += length * 8; 64 for (var i = 0; i < length; i++) { 65 this.write(data[i]); 66 } 67 68 return this; 69 }; 70 71 Sha1.prototype.write = function write(byte) { 72 this.block[this.offset] |= (byte & 0xff) << this.shift; 73 if (this.shift) { 74 this.shift -= 8; 75 } else { 76 this.offset++; 77 this.shift = 24; 78 } 79 80 if (this.offset === 16) this.processBlock(); 81 }; 82 83 Sha1.prototype.digest = function (encoding) { 84 // Pad 85 this.write(0x80); 86 if (this.offset > 14 || (this.offset === 14 && this.shift < 24)) { 87 this.processBlock(); 88 } 89 this.offset = 14; 90 this.shift = 24; 91 92 // 64-bit length big-endian 93 this.write(0x00); // numbers this big aren't accurate in javascript anyway 94 this.write(0x00); // ..So just hard-code to zero. 95 this.write(this.totalLength > 0xffffffffff ? this.totalLength / 0x10000000000 : 0x00); 96 this.write(this.totalLength > 0xffffffff ? this.totalLength / 0x100000000 : 0x00); 97 for (var s = 24; s >= 0; s -= 8) { 98 this.write(this.totalLength >> s); 99 } 100 // The value in state is little-endian rather than big-endian, so flip 101 // each word into a new Uint8Array 102 var out = new Buffer(DIGEST_LENGTH); 103 var outView = new DataView(out.buffer); 104 outView.setUint32(0, this.h0, false); 105 outView.setUint32(4, this.h1, false); 106 outView.setUint32(8, this.h2, false); 107 outView.setUint32(12, this.h3, false); 108 outView.setUint32(16, this.h4, false); 109 110 return encoding ? out.toString(encoding) : out; 111 }; 112 113 Sha1.prototype.processBlock = function processBlock() { 114 // Extend the sixteen 32-bit words into eighty 32-bit words: 115 for (var i = 16; i < 80; i++) { 116 var w = this.block[i - 3] ^ this.block[i - 8] ^ this.block[i - 14] ^ this.block[i - 16]; 117 this.block[i] = (w << 1) | (w >>> 31); 118 } 119 120 // Initialize hash value for this chunk: 121 var a = this.h0; 122 var b = this.h1; 123 var c = this.h2; 124 var d = this.h3; 125 var e = this.h4; 126 var f, k; 127 128 // Main loop: 129 for (i = 0; i < 80; i++) { 130 if (i < 20) { 131 f = d ^ (b & (c ^ d)); 132 k = 0x5A827999; 133 } 134 else if (i < 40) { 135 f = b ^ c ^ d; 136 k = 0x6ED9EBA1; 137 } 138 else if (i < 60) { 139 f = (b & c) | (d & (b | c)); 140 k = 0x8F1BBCDC; 141 } 142 else { 143 f = b ^ c ^ d; 144 k = 0xCA62C1D6; 145 } 146 var temp = (a << 5 | a >>> 27) + f + e + k + (this.block[i]|0); 147 e = d; 148 d = c; 149 c = (b << 30 | b >>> 2); 150 b = a; 151 a = temp; 152 } 153 154 // Add this chunk's hash to result so far: 155 this.h0 = (this.h0 + a) | 0; 156 this.h1 = (this.h1 + b) | 0; 157 this.h2 = (this.h2 + c) | 0; 158 this.h3 = (this.h3 + d) | 0; 159 this.h4 = (this.h4 + e) | 0; 160 161 // The block is now reusable. 162 this.offset = 0; 163 for (i = 0; i < 16; i++) { 164 this.block[i] = 0; 165 } 166 };