//*******************************************************************************
//
// 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 */
/* global msrcryptoUtilities */
/* jshint -W016 */
/* jshint -W052 */
///
///
/// msrcrypto, der, sha, uint, int, maj
/// #endregion JSCop/JsHint
var msrcryptoSha512 = (function () {
function add(x0, x1, y0, y1, resultArray) {
// The sum here may result in a number larger than 32-bits.
// Or-ing with zero forces to a 32-bit signed internal state
// and a truncation to a 32-bit number;
var lowSum = (x1 + y1) | 0;
// If lowSum is less than either parameter (x1 or y1), we know we overflowed
// and a carry will need to be added to the high order bits.
// The 32-bit integer is signed. So large numbers can flip to negative values.
// The zero-shift pulls the number back out of the 32-bit state so we know
// we're comparing positive values.
var carry = (lowSum >>> 0 < y1 >>> 0);
resultArray[0] = (x0 + y0 + carry) | 0;
resultArray[1] = lowSum;
return;
}
var hashFunction = function (hashName, der, h, k, truncateTo) {
var blockBytes = 128,
hv = [],
w = [],
buffer = [],
blocksProcessed = 0;
initializeHashValues();
// Hashing using 64 bit registers. 2-32bit values as high and low
function hashBlocks( /*@type(Array)*/ message) {
var blockCount = Math.floor(message.length / blockBytes),
t, i,
tah, tal, tbh, tbl, xh, xl,
tc = [],
td = [],
te = [],
block, index;
for (block = 0; block < blockCount; block++) {
for (t = 0; t < 32; t++) {
index = block * blockBytes + t * 4;
w[t] = message.slice(index, index + 4);
w[t] = (w[t][0] << 24) | (w[t][1] << 16) | (w[t][2] << 8) | w[t][3];
}
// 16 ≤ t ≤ 80
for (t = 32; t < 160; t += 2) {
xh = w[t - 30];
xl = w[t - 29];
tah = (xh >>> 1 | xl << 31) ^ (xh >>> 8 | xl << 24) ^ (xh >>> 7);
tal = (xl >>> 1 | xh << 31) ^ (xl >>> 8 | xh << 24) ^ (xl >>> 7 | xh << 25);
xh = w[t - 4];
xl = w[t - 3];
tbh = (xh >>> 19 | xl << 13) ^ (xl >>> 29 | xh << 3) ^ (xh >>> 6);
tbl = (xl >>> 19 | xh << 13) ^ (xh >>> 29 | xl << 3) ^ (xl >>> 6 | xh << 26);
add(tbh, tbl, w[t - 14], w[t - 13], tc);
add(tah, tal, tc[0], tc[1], tc);
add(w[t - 32], w[t - 31], tc[0], tc[1], tc);
w[t] = tc[0]; w[t + 1] = tc[1];
}
var ah = hv[0], al = hv[1],
bh = hv[2], bl = hv[3],
ch = hv[4], cl = hv[5],
dh = hv[6], dl = hv[7],
eh = hv[8], el = hv[9],
fh = hv[10], fl = hv[11],
gh = hv[12], gl = hv[13],
hh = hv[14], hl = hv[15];
for (i = 0; i < 160; i += 2) {
// S1 =======================================================================
tah = (eh >>> 14 | el << 18) ^ (eh >>> 18 | el << 14) ^ (el >>> 9 | eh << 23);
tal = (el >>> 14 | eh << 18) ^ (el >>> 18 | eh << 14) ^ (eh >>> 9 | el << 23);
// Ch
tbh = (eh & fh) ^ (gh & ~eh);
tbl = (el & fl) ^ (gl & ~el);
// C = h + S1
add(hh, hl, tah, tal, tc);
// D = ch + kConstants-i
add(tbh, tbl, k[i], k[i + 1], td);
// E = w[i] + C
add(tc[0], tc[1], w[i], w[i + 1], te);
// E = E + D TEMP
add(td[0], td[1], te[0], te[1], te);
// D = C + E
add(te[0], te[1], dh, dl, tc);
dh = tc[0]; dl = tc[1];
// S0
tal = (al >>> 28 | ah << 4) ^ (ah >>> 2 | al << 30) ^ (ah >>> 7 | al << 25);
tah = (ah >>> 28 | al << 4) ^ (al >>> 2 | ah << 30) ^ (al >>> 7 | ah << 25);
tbl = (al & (bl ^ cl)) ^ (bl & cl);
tbh = (ah & (bh ^ ch)) ^ (bh & ch);
// S0 + maj
add(te[0], te[1], tah, tal, tc);
tah = tc[0]; tal = tc[1];
// 'temp' = temp + (S0 + maj)
add(tbh, tbl, tah, tal, tc);
tah = tc[0]; tal = tc[1];
hh = gh;
hl = gl; // 'h' = g
gh = fh;
gl = fl; // 'g' = f
fh = eh;
fl = el; // 'f' = e
eh = dh;
el = dl; // 'e' = d
dh = ch;
dl = cl; // 'd' = c
ch = bh;
cl = bl; // 'c' = b
bh = ah;
bl = al; // 'b' = a
ah = tah;
al = tal; // 'a' = temp
}
// This is how you would add without calling add()
// hv[1] = ((hv[1] + al) | 0) >>> 0;
// hv[0] = hv[0] + ah + (hv[1] < al >>> 0) | 0;
add(hv[0], hv[1], ah, al, tc);
hv[0] = tc[0]; hv[1] = tc[1];
add(hv[2], hv[3], bh, bl, tc);
hv[2] = tc[0]; hv[3] = tc[1];
add(hv[4], hv[5], ch, cl, tc);
hv[4] = tc[0]; hv[5] = tc[1];
add(hv[6], hv[7], dh, dl, tc);
hv[6] = tc[0]; hv[7] = tc[1];
add(hv[8], hv[9], eh, el, tc);
hv[8] = tc[0]; hv[9] = tc[1];
add(hv[10], hv[11], fh, fl, tc);
hv[10] = tc[0]; hv[11] = tc[1];
add(hv[12], hv[13], gh, gl, tc);
hv[12] = tc[0]; hv[13] = tc[1];
add(hv[14], hv[15], hh, hl, tc);
hv[14] = tc[0]; hv[15] = tc[1];
}
// 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() {
// Move the results to an uint8 array
var hash = [];
for (var i = 0; i < 16; i++) {
hash = hash.concat([hv[i] >>> 24, (hv[i] >>> 16) & 255, (hv[i] >>> 8) & 255, hv[i] & 255]);
}
return hash.slice(0, truncateTo / 8);
}
function padBlock(/*@type(Array)*/message) {
var padLen = blockBytes - message.length;
// If there is 16 or less bytes of padding, pad an additional block.
if (padLen <= 16) {
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 (32-bits worth)
// JavaScript arrays have a max size of 32-bits.
paddedMessage.push(messageLenBits >>> 24 & 255);
paddedMessage.push(messageLenBits >>> 16 & 255);
paddedMessage.push(messageLenBits >>> 8 & 255);
paddedMessage.push(messageLenBits & 255);
return paddedMessage;
}
function bufferToArray(/*@type(Array)*/dataBuffer) {
if (dataBuffer.slice) {
return dataBuffer;
}
return (dataBuffer.length === 1) ? [dataBuffer[0]] : Array.apply(null, dataBuffer);
}
function initializeHashValues() {
// Set the initial hash values
for (var l = 0; l < h.length; l++) {
hv[l] = h[l][0] << 24 | h[l][1] << 16 | h[l][2] << 8 | h[l][3];
}
}
function computeHash(messageBytes) {
buffer = hashBlocks(bufferToArray(messageBytes));
return finish();
}
function process(messageBytes) {
// Append the new data to the buffer (previous unprocessed data)
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 = hashBlocks(padBlock(buffer));
// Buffer should be empty now
if (buffer.length !== 0) {
throw new Error("buffer.length !== 0");
}
var result = hashToBytes();
// Clear the state so this instance can be reused
buffer = [];
initializeHashValues();
blocksProcessed = 0;
return result;
}
return {
name: hashName,
computeHash: computeHash,
process: process,
finish: finish,
der: der,
hashLen: truncateTo,
maxMessageSize: 0xFFFFFFFF // (2^32 - 1 is max array size in JavaScript)
};
};
var h384, h512, k512,
der384, der512, der512_224, der512_256,
upd = msrcryptoUtilities.unpackData;
h384 = upd("y7udXcEFnthimikqNnzVB5FZAVowcN0XFS/s2PcOWTlnMyZn/8ALMY60SodoWBUR2wwuDWT5j6dHtUgdvvpPpA==", 4);
h512 = upd("agnmZ/O8yQi7Z66FhMqnOzxu83L+lPgrpU/1Ol8dNvFRDlJ/reaC0ZsFaIwrPmwfH4PZq/tBvWtb4M0ZE34heQ", 4);
k512 = upd(
"QoovmNcoriJxN0SRI+9lzbXA+8/sTTsv6bXbpYGJ27w5VsJb80i1OFnxEfG2BdAZkj+CpK8ZT5urHF7" +
"V2m2BGNgHqpijAwJCEoNbAUVwb74kMYW+TuSyjFUMfcPV/7Ticr5ddPJ7iW+A3rH+OxaWsZvcBqclxx" +
"I1wZvxdM9pJpTkm2nBnvFK0u++R4Y4TyXjD8GdxouM1bUkDKHMd6ycZS3pLG9ZKwJ1SnSEqm6m5INcs" +
"KncvUH71Hb5iNqDEVO1mD5RUu5m36uoMcZtLbQyELADJ8iY+yE/v1l/x77vDuTG4AvzPaiPwtWnkUeT" +
"CqclBspjUeADgm8UKSlnCg5ucCe3CoVG0i/8LhshOFwmySZNLG38WsQq7VM4DROdlbPfZQpzVIuvY95" +
"2agq7PHeyqIHCyS5H7a7mknIshRSCNTuiv+ihTPEDZKgaZku8QjABwkuLcND4l5HHbFGjBlS+MNGS6B" +
"nW71IY1pkGJFVlqRD0DjWFV3EgKhBqoHAyu9G4GaTBFrjS0MgeN2wIUUGrUydId0zfjuuZNLC8teGbS" +
"Kg5HAyzxclaY07YqkrjQYrLW5zKT3dj43NoLm/z1rK4o3SPgu5d77L8eKVjb0MXL2CEyHgUofCrcozH" +
"AggaZDnskL7/+iNjHiikUGzr3oK96b75o/eyxnkVxnF48uNyUyvKJz7O6iZhnNGGuMchwMIH6tp91s3" +
"g6x71fU9/7m7ReAbwZ6pyF2+6CmN9xaLImKYRP5gEvvkNrhtxCzUTHEcbKNt39SMEfYQyyqt7QMckkz" +
"yevgoVyb68Qx1nxJwQDUxMxdS+yz5Ctll/KZz8ZX4qX8tvqzrW+uxsRBmMSkdYFw==", 4, 1);
// DER encoding
der384 = upd("MEEwDQYJYIZIAWUDBAICBQAEMA");
der512 = upd("MFEwDQYJYIZIAWUDBAIDBQAEQA");
der512_224 = upd("MC0wDQYJYIZIAWUDBAIFBQAEHA");
der512_256 = upd("MDEwDQYJYIZIAWUDBAIGBQAEIA");
return {
sha384: hashFunction("SHA-384", der384, h384, k512, 384),
sha512: hashFunction("SHA-512", der512, h512, k512, 512),
sha512_224: hashFunction("SHA-512.224", der512_224, h512, k512, 224),
sha512_256: hashFunction("SHA-512.256", der512_256, h512, k512, 256)
};
})();
if (typeof operations !== "undefined") {
msrcryptoSha512.hash384 = function (/*@dynamic*/p) {
if (p.operationSubType === "process") {
msrcryptoSha512.sha384.process(p.buffer);
return;
}
if (p.operationSubType === "finish") {
return msrcryptoSha512.sha384.finish();
}
return msrcryptoSha512.sha384.computeHash(p.buffer);
};
msrcryptoSha512.hash512 = function (/*@dynamic*/p) {
if (p.operationSubType === "process") {
msrcryptoSha512.sha512.process(p.buffer);
return;
}
if (p.operationSubType === "finish") {
return msrcryptoSha512.sha512.finish();
}
return msrcryptoSha512.sha512.computeHash(p.buffer);
};
operations.register("digest", "sha-384", msrcryptoSha512.hash384);
operations.register("digest", "sha-512", msrcryptoSha512.hash512);
}
msrcryptoHashFunctions["sha-384"] = msrcryptoSha512.sha384;
msrcryptoHashFunctions["sha-512"] = msrcryptoSha512.sha512;