//******************************************************************************* // // Copyright 2014 Microsoft // // 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. // //******************************************************************************* /// #region JSCop/JsHint /* global operations */ /* jshint -W016 */ /* jshint -W052 */ /// /// msrcrypto, der, sha /// JS3057.AvoidImplicitTypeCoercion /// #endregion JSCop/JsHint var msrcryptoSha256 = (function () { var hashFunction = function (name, der, h, k, truncateTo) { var blockBytes = 64; var hv = h.slice(); var w = new Array(blockBytes); var buffer = []; var blocksProcessed = 0; function hashBlocks(/*@type(Array)*/message) { var blockCount = Math.floor(message.length / blockBytes); var ra, rb, rc, rd, re, rf, rg, rh; var t, block, i, temp, x1, x0, index; // Process each 64-byte block of the message for (block = 0; block < blockCount; block++) { // 0 ≤ t ≤ 15 for (i = 0; i < 16; i++) { index = block * blockBytes + i * 4; // Convert 4 bytes to 32-bit integer w[i] = (message[index] << 24) | (message[index + 1] << 16) | (message[index + 2] << 8) | message[index + 3]; } // 16 ≤ t ≤ 63 for (t = 16; t < 64; t++) { x0 = w[t - 15]; x1 = w[t - 2]; w[t] = (((x1 >>> 17) | (x1 << 15)) ^ ((x1 >>> 19) | (x1 << 13)) ^ (x1 >>> 10)); w[t] += w[t - 7]; w[t] += (((x0 >>> 7) | (x0 << 25)) ^ ((x0 >>> 18) | (x0 << 14)) ^ (x0 >>> 3)); w[t] += w[t - 16]; } ra = hv[0]; rb = hv[1]; rc = hv[2]; rd = hv[3]; re = hv[4]; rf = hv[5]; rg = hv[6]; rh = hv[7]; for (i = 0; i < 64; i++) { temp = rh + ((re >>> 6 | re << 26) ^ (re >>> 11 | re << 21) ^ (re >>> 25 | re << 7)) + ((re & rf) ^ ((~re) & rg)) + k[i] + w[i]; rd += temp; temp += ((ra >>> 2 | ra << 30) ^ (ra >>> 13 | ra << 19) ^ (ra >>> 22 | ra << 10)) + ((ra & (rb ^ rc)) ^ (rb & rc)); rh = rg; // 'h' = g rg = rf; // 'g' = f rf = re; // 'f' = e re = rd; // 'e' = d rd = rc; // 'd' = c rc = rb; // 'c' = b rb = ra; // 'b' = a ra = temp; // 'a' = temp } // Need to mask 32-bits when using regular arrays hv[0] += ra & 0xFFFFFFFF; hv[1] += rb & 0xFFFFFFFF; hv[2] += rc & 0xFFFFFFFF; hv[3] += rd & 0xFFFFFFFF; hv[4] += re & 0xFFFFFFFF; hv[5] += rf & 0xFFFFFFFF; hv[6] += rg & 0xFFFFFFFF; hv[7] += rh & 0xFFFFFFFF; } // Keep track of the number of blocks processed. // We have to put the total message size into the padding. blocksProcessed += blockCount; // Return the unprocessed data. return message.slice(blockCount * blockBytes); } function hashToBytes() { var hash = new Array(256); // Copy the 32-bit values to a byte array for (var i = 0, byteIndex = 0; i < 8; i += 1, byteIndex += 4) { hash[byteIndex] = hv[i] >>> 24; hash[byteIndex + 1] = hv[i] >>> 16 & 0xFF; hash[byteIndex + 2] = hv[i] >>> 8 & 0xFF; hash[byteIndex + 3] = hv[i] & 0xFF; } return hash.slice(0, truncateTo / 8); } // This can be optimized. // Currently the amount of padding is computed. Then a new array, big enough // to hold the message + padding is created. The message is copied to the // new array and the padding is placed at the end. // We don't really need to create an entire new array and copy to it. // We can just build the last padded block and store it. // Then when computing the hash, substitute it for the last message block. function padBlock( /*@type(Array)*/ message) { var padLen = blockBytes - message.length; // If there is 8 or less bytes of padding, pad an additional block. if (padLen <= 8) { padLen += blockBytes; } // Create a new Array that will contain the message + padding var paddedMessage = message.slice(); // Set the 1 bit at the end of the message data paddedMessage.push(128); // Pad the array with zero. Leave 4 bytes for the message size. for (var i = 1; i < padLen - 4; i++) { paddedMessage.push(0); } // Set the length equal to the previous data len + the new data len var messageLenBits = (message.length + blocksProcessed * blockBytes) * 8; // Set the message length in the last 4 bytes paddedMessage.push(messageLenBits >>> 24 & 255); paddedMessage.push(messageLenBits >>> 16 & 255); paddedMessage.push(messageLenBits >>> 8 & 255); paddedMessage.push(messageLenBits & 255); return paddedMessage; } function bufferToArray(buffer) { // Checking for slice method to determine if this a regular array. if (buffer.pop) { return buffer; } return (buffer.length === 1) ? [buffer[0]] : Array.apply(null, buffer); } function /*@type(Array)*/ computeHash(messageBytes) { // Convert the input to an Array - it could be a typed array buffer = hashBlocks(bufferToArray(messageBytes)); return finish(); } function process(messageBytes) { // Append the new data to the buffer (previous unprocessed data) // Convert the input to an Array - it could be a typed array buffer = buffer.concat(bufferToArray(messageBytes)); // If there is at least one block of data, hash it if (buffer.length >= 64) { // The remaining unprocessed data goes back into the buffer buffer = hashBlocks(buffer); } return; } function finish() { // All the full blocks of data have been processed. Now we pad the rest and hash. // Buffer should be empty now. if (hashBlocks(padBlock(buffer)).length !== 0) { throw new Error("buffer.length !== 0"); } var result = hashToBytes(); // Clear the hash values so this instance can be reused buffer = []; hv = h.slice(); blocksProcessed = 0; return result; } return { name: name, computeHash: computeHash, process: process, finish: finish, der: der, hashLen: truncateTo, maxMessageSize: 0xFFFFFFFF // (2^32 - 1 is max array size in JavaScript) }; }; var k256, h224, h256, der224, der256, upd = msrcryptoUtilities.unpackData; h224 = upd("wQWe2DZ81QcwcN0X9w5ZOf/ACzFoWBURZPmPp776T6Q", 4, 1); h256 = upd("agnmZ7tnroU8bvNypU/1OlEOUn+bBWiMH4PZq1vgzRk", 4, 1); k256 = upd("QoovmHE3RJG1wPvP6bXbpTlWwltZ8RHxkj+CpKscXtXYB6qYEoNbASQxhb5VDH3Dcr5ddIDesf6b3AanwZvxdOSbacHvvkeGD8GdxiQMocwt6SxvSnSEqlywqdx2+YjamD5RUqgxxm2wAyfIv1l/x8bgC/PVp5FHBspjURQpKWcntwqFLhshOE0sbfxTOA0TZQpzVHZqCruBwskuknIshaK/6KGoGmZLwkuLcMdsUaPRkugZ1pkGJPQONYUQaqBwGaTBFh43bAgnSHdMNLC8tTkcDLNO2KpKW5zKT2gub/N0j4LueKVjb4TIeBSMxwIIkL7/+qRQbOu++aP3xnF48g", 4, 1); // SHA-224 DER encoding // 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C der224 = upd("MC0wDQYJYIZIAWUDBAIEBQAEHA"); // SHA-256 DER encoding // 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 der256 = upd("MDEwDQYJYIZIAWUDBAIBBQAEIA"); return { sha224: hashFunction("SHA-224", der224, h224, k256, 224), sha256: hashFunction("SHA-256", der256, h256, k256, 256) }; })(); if (typeof operations !== "undefined") { msrcryptoSha256.hash256 = function (/*@dynamic*/p) { if (p.operationSubType === "process") { msrcryptoSha256.sha256.process(p.buffer); return; } if (p.operationSubType === "finish") { return msrcryptoSha256.sha256.finish(); } return msrcryptoSha256.sha256.computeHash(p.buffer); }; msrcryptoSha256.hash224 = function (/*@dynamic*/p) { if (p.operationSubType === "process") { msrcryptoSha256.sha224.process(p.buffer); return; } if (p.operationSubType === "finish") { return msrcryptoSha256.sha224.finish(); } return msrcryptoSha256.sha224.computeHash(p.buffer); }; operations.register("digest", "sha-224", msrcryptoSha256.hash224); operations.register("digest", "sha-256", msrcryptoSha256.hash256); } msrcryptoHashFunctions["sha-224"] = msrcryptoSha256.sha224; msrcryptoHashFunctions["sha-256"] = msrcryptoSha256.sha256;