Gazebo_simulation-Frontend/node_modules/msrcrypto/msrcrypto.js
2020-12-21 10:29:31 -05:00

9653 lines
349 KiB
JavaScript
Executable File

//*******************************************************************************
//
// Copyright 2018 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.
//
//*******************************************************************************
var msrCryptoVersion = "1.5.6";
var msrCrypto = msrCrypto || (function () {
"use strict";
var operations = {};
operations.register = function (operationType, algorithmName, functionToCall) {
if (!operations[operationType]) {
operations[operationType] = {};
}
var op = operations[operationType];
if (!op[algorithmName]) {
op[algorithmName] = functionToCall;
}
};
operations.exists = function (operationType, algorithmName) {
if (!operations[operationType]) {
return false;
}
return (operations[operationType][algorithmName]) ? true : false;
};
/// Store the URL for this script. We will need this later to instantiate
/// new web workers (if supported).
var scriptUrl = (function () {
if (typeof document !== "undefined") {
// Use error.stack to find out the name of this script
try {
throw new Error();
} catch (e) {
if (e.stack) {
var match = /\w+:\/\/(.+?\/)*.+\.js/.exec(e.stack);
return (match && match.length > 0) ? match[0] : null;
}
}
} else if (typeof self !== "undefined") {
// If this script is being run in a WebWorker, 'document' will not exist
// but we can use self.
return self.location.href;
}
// We must be running in an environment without document or self.
return null;
})();
// Indication if the user provided entropy into the entropy pool.
var fprngEntropyProvided = false;
// Support for webWorkers IE10+.
var webWorkerSupport = (typeof Worker !== "undefined");
// Is this script running in an instance of a webWorker?
var runningInWorkerInstance = (typeof importScripts !== "undefined");
// Typed Arrays support?
var typedArraySupport = (typeof Uint8Array !== "undefined");
// Property setter/getter support IE9+.
var setterSupport = (function () {
try {
Object.defineProperty({}, "oncomplete", {});
return true;
} catch (ex) {
return false;
}
}());
// Run in async mode (requires web workers) and user can override to sync mode
// by setting the .forceSync property to true on the subtle interface
// this can be changes 'on the fly'.
var asyncMode = webWorkerSupport;
var createProperty = function (parentObject, propertyName, initialValue, getterFunction, setterFunction) {
/// <param name="parentObject" type="Object"/>
/// <param name="propertyName" type="String"/>
/// <param name="initialValue" type="Object"/>
/// <param name="getterFunction" type="Function"/>
/// <param name="setterFunction" type="Function" optional="true"/>
if (!setterSupport) {
parentObject[propertyName] = initialValue;
return;
}
var setGet = {};
getterFunction && (setGet.get = getterFunction);
setterFunction && (setGet.set = setterFunction);
Object.defineProperty(
parentObject,
propertyName, setGet);
};
// Collection of hash functions for global availability.
// Each hashfunction will add itself to the collection as it is evaluated.
var msrcryptoHashFunctions = {};
var msrcryptoUtilities = (function () {
var encodingChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var btoaSupport = (typeof btoa !== "undefined");
function toBase64(data, base64Url) {
/// <signature>
/// <summary>Converts byte data to Base64 string</summary>
/// <param name="data" type="Array">An array of bytes values (numbers from 0-255)</param>
/// <param name="base64Url" type="Boolean" optional="true">Converts to a Base64Url string if True (default = false)</param>
/// <returns type="String" />
/// </signature>
/// <signature>
/// <summary>Converts byte data to Base64 string</summary>
/// <param name="data" type="UInt8Array">A UInt8Array</param>
/// <param name="base64Url" type="Boolean" optional="true">Converts to a Base64Url string if True (default = false)</param>
/// <returns type="String" />
/// </signature>
/// <signature>
/// <summary>Converts text to Base64 string</summary>
/// <param name="data" type="String">Text string</param>
/// <param name="base64Url" type="Boolean" optional="true">Converts to a Base64Url string if True (default = false)</param>
/// <returns type="String" />
/// </signature>
var output = "";
if (!base64Url) {
base64Url = false;
}
// If the input is an array type, convert it to a string.
// The built-in btoa takes strings.
if (data.pop || data.subarray) {
data = String.fromCharCode.apply(null, data);
}
if (btoaSupport) {
output = btoa(data);
} else {
var char1, char2, char3, enc1, enc2, enc3, enc4;
var i;
for (i = 0; i < data.length; i += 3) {
// Get the next three chars.
char1 = data.charCodeAt(i);
char2 = data.charCodeAt(i + 1);
char3 = data.charCodeAt(i + 2);
// Encode three bytes over four 6-bit values.
// [A7,A6,A5,A4,A3,A2,A1,A0][B7,B6,B5,B4,B3,B2,B1,B0][C7,C6,C5,C4,C3,C2,C1,C0].
// [A7,A6,A5,A4,A3,A2][A1,A0,B7,B6,B5,B4][B3,B2,B1,B0,C7,C6][C5,C4,C3,C2,C1,C0].
// 'enc1' = high 6-bits from char1
enc1 = char1 >> 2;
// 'enc2' = 2 low-bits of char1 + 4 high-bits of char2
enc2 = ((char1 & 0x3) << 4) | (char2 >> 4);
// 'enc3' = 4 low-bits of char2 + 2 high-bits of char3
enc3 = ((char2 & 0xF) << 2) | (char3 >> 6);
// 'enc4' = 6 low-bits of char3
enc4 = char3 & 0x3F;
// 'char2' could be 'nothing' if there is only one char left to encode
// if so, set enc3 & enc4 to 64 as padding.
if (isNaN(char2)) {
enc3 = enc4 = 64;
// If there was only two chars to encode char3 will be 'nothing'
// set enc4 to 64 as padding.
} else if (isNaN(char3)) {
enc4 = 64;
}
// Lookup the base-64 value for each encoding.
output = output +
encodingChars.charAt(enc1) +
encodingChars.charAt(enc2) +
encodingChars.charAt(enc3) +
encodingChars.charAt(enc4);
}
}
if (base64Url) {
return output.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=/g, "");
}
return output;
}
function base64ToString(encodedString) {
/// <signature>
/// <summary>Converts a Base64/Base64Url string to a text</summary>
/// <param name="encodedString" type="String">A Base64/Base64Url encoded string</param>
/// <returns type="String" />
/// </signature>
if (btoaSupport) {
// This could be encoded as base64url (different from base64)
encodedString = encodedString.replace(/-/g, "+").replace(/_/g, "/");
// In case the padding is missing, add some.
while (encodedString.length % 4 !== 0) {
encodedString += "=";
}
return atob(encodedString);
}
return String.fromCharCode.apply(null, base64ToBytes(encodedString));
}
function base64ToBytes(encodedString) {
/// <signature>
/// <summary>Converts a Base64/Base64Url string to an Array</summary>
/// <param name="encodedString" type="String">A Base64/Base64Url encoded string</param>
/// <returns type="Array" />
/// </signature>
// This could be encoded as base64url (different from base64)
encodedString = encodedString.replace(/-/g, "+").replace(/_/g, "/");
// In case the padding is missing, add some.
while (encodedString.length % 4 !== 0) {
encodedString += "=";
}
var output = [];
var char1, char2, char3;
var enc1, enc2, enc3, enc4;
var i;
// Remove any chars not in the base-64 space.
encodedString = encodedString.replace(/[^A-Za-z0-9\+\/\=]/g, "");
for (i = 0; i < encodedString.length; i += 4) {
// Get 4 characters from the encoded string.
enc1 = encodingChars.indexOf(encodedString.charAt(i));
enc2 = encodingChars.indexOf(encodedString.charAt(i + 1));
enc3 = encodingChars.indexOf(encodedString.charAt(i + 2));
enc4 = encodingChars.indexOf(encodedString.charAt(i + 3));
// Convert four 6-bit values to three characters.
// [A7,A6,A5,A4,A3,A2][A1,A0,B7,B6,B5,B4][B3,B2,B1,B0,C7,C6][C5,C4,C3,C2,C1,C0].
// [A7,A6,A5,A4,A3,A2,A1,A0][B7,B6,B5,B4,B3,B2,B1,B0][C7,C6,C5,C4,C3,C2,C1,C0].
// 'char1' = all 6 bits of enc1 + 2 high-bits of enc2.
char1 = (enc1 << 2) | (enc2 >> 4);
// 'char2' = 4 low-bits of enc2 + 4 high-bits of enc3.
char2 = ((enc2 & 15) << 4) | (enc3 >> 2);
// 'char3' = 2 low-bits of enc3 + all 6 bits of enc4.
char3 = ((enc3 & 3) << 6) | enc4;
// Convert char1 to string character and append to output
output.push(char1);
// 'enc3' could be padding
// if so, 'char2' is ignored.
if (enc3 !== 64) {
output.push(char2);
}
// 'enc4' could be padding
// if so, 'char3' is ignored.
if (enc4 !== 64) {
output.push(char3);
}
}
return output;
}
function getObjectType(object) {
/// <signature>
/// <summary>Returns the name of an object type</summary>
/// <param name="object" type="Object"></param>
/// <returns type="String" />
/// </signature>
return Object.prototype.toString.call(object).slice(8, -1);
}
function bytesToHexString(bytes, separate) {
/// <signature>
/// <summary>Converts an Array of bytes values (0-255) to a Hex string</summary>
/// <param name="bytes" type="Array"/>
/// <param name="separate" type="Boolean" optional="true">Inserts a separator for display purposes (default = false)</param>
/// <returns type="String" />
/// </signature>
var result = "";
if (typeof separate === "undefined") {
separate = false;
}
for (var i = 0; i < bytes.length; i++) {
if (separate && (i % 4 === 0) && i !== 0) {
result += "-";
}
var hexval = bytes[i].toString(16).toUpperCase();
// Add a leading zero if needed.
if (hexval.length === 1) {
result += "0";
}
result += hexval;
}
return result;
}
function bytesToInt32(bytes, index) {
/// <summary>
/// Converts four bytes to a 32-bit int
/// </summary>
/// <param name="bytes">The bytes to convert</param>
/// <param name="index" optional="true">Optional starting point</param>
/// <returns type="Number">32-bit number</returns>
index = (index || 0);
return (bytes[index] << 24) |
(bytes[index + 1] << 16) |
(bytes[index + 2] << 8) |
bytes[index + 3];
}
function stringToBytes(messageString) {
/// <signature>
/// <summary>Converts a String to an Array of byte values (0-255)</summary>
/// <param name="messageString" type="String"/>
/// <returns type="Array" />
/// </signature>
var bytes = new Array(messageString.length);
for (var i = 0; i < bytes.length; i++) {
bytes[i] = messageString.charCodeAt(i);
}
return bytes;
}
function hexToBytesArray(hexString) {
/// <signature>
/// <summary>Converts a Hex-String to an Array of byte values (0-255)</summary>
/// <param name="hexString" type="String"/>
/// <returns type="Array" />
/// </signature>
hexString = hexString.replace(/\-/g, "");
var result = [];
while (hexString.length >= 2) {
result.push(parseInt(hexString.substring(0, 2), 16));
hexString = hexString.substring(2, hexString.length);
}
return result;
}
function clone(object) {
/// <signature>
/// <summary>Creates a shallow clone of an Object</summary>
/// <param name="object" type="Object"/>
/// <returns type="Object" />
/// </signature>
var newObject = {};
for (var propertyName in object) {
if (object.hasOwnProperty(propertyName)) {
newObject[propertyName] = object[propertyName];
}
}
return newObject;
}
function unpackData(base64String, arraySize, toUint32s) {
/// <signature>
/// <summary>Unpacks Base64 encoded data into arrays of data.</summary>
/// <param name="base64String" type="String">Base64 encoded data</param>
/// <param name="arraySize" type="Number" optional="true">Break data into sub-arrays of a given length</param>
/// <param name="toUint32s" type="Boolean" optional="true">Treat data as 32-bit data instead of byte data</param>
/// <returns type="Array" />
/// </signature>
var bytes = base64ToBytes(base64String),
data = [],
i;
if (isNaN(arraySize)) {
return bytes;
} else {
for (i = 0; i < bytes.length; i += arraySize) {
data.push(bytes.slice(i, i + arraySize));
}
}
if (toUint32s) {
for (i = 0; i < data.length; i++) {
data[i] = (data[i][0] << 24) + (data[i][1] << 16) + (data[i][2] << 8) + data[i][3];
}
}
return data;
}
function int32ToBytes(int32) {
/// <signature>
/// <summary>Converts a 32-bit number to an Array of 4 bytes</summary>
/// <param name="int32" type="Number">32-bit number</param>
/// <returns type="Array" />
/// </signature>
return [(int32 >>> 24) & 255, (int32 >>> 16) & 255, (int32 >>> 8) & 255, int32 & 255];
}
function int32ArrayToBytes(int32Array) {
/// <signature>
/// <summary>Converts an Array 32-bit numbers to an Array bytes</summary>
/// <param name="int32Array" type="Array">Array of 32-bit numbers</param>
/// <returns type="Array" />
/// </signature>
var result = [];
for (var i = 0; i < int32Array.length; i++) {
result = result.concat(int32ToBytes(int32Array[i]));
}
return result;
}
function xorVectors(a, b) {
/// <signature>
/// <summary>Exclusive OR (XOR) two arrays.</summary>
/// <param name="a" type="Array">Input array.</param>
/// <param name="b" type="Array">Input array.</param>
/// <returns type="Array">XOR of the two arrays. The length is minimum of the two input array lengths.</returns>
/// </signature>
var length = Math.min(a.length, b.length),
res = new Array(length);
for (var i = 0 ; i < length ; i += 1) {
res[i] = a[i] ^ b[i];
}
return res;
}
function getVector(length, fillValue) {
/// <signature>
/// <summary>Get an array filled with zeroes (or optional fillValue.)</summary>
/// <param name="length" type="Number">Requested array length.</param>
/// <param name="fillValue" type="Number" optional="true"></param>
/// <returns type="Array"></returns>
/// </signature>
// Use a default value of zero
fillValue || (fillValue = 0);
var res = new Array(length);
for (var i = 0; i < length; i += 1) {
res[i] = fillValue;
}
return res;
}
function toArray(typedArray) {
/// <signature>
/// <summary>Converts a UInt8Array to a regular JavaScript Array</summary>
/// <param name="typedArray" type="UInt8Array"></param>
/// <returns type="Array"></returns>
/// </signature>
// If undefined or null return an empty array
if (!typedArray) {
return [];
}
// If already an Array return it
if (typedArray.pop) {
return typedArray;
}
// If it's an ArrayBuffer, convert it to a Uint8Array first
if (typedArray.isView || getObjectType(typedArray) === "ArrayBuffer") {
typedArray = new Uint8Array(typedArray);
}
// A single element array will cause a new Array to be created with the length
// equal to the value of the single element. Not what we want.
// We'll return a new single element array with the single value.
return (typedArray.length === 1) ? [typedArray[0]] : Array.apply(null, typedArray);
}
function padEnd(array, value, finalLength) {
/// <signature>
/// <summary>Pads the end of an array with a specified value</summary>
/// <param name="array" type="Array"></param>
/// <param name="value" type="Number">The value to pad to the array</param>
/// <param name="finalLength" type="Number">The final resulting length with padding</param>
/// <returns type="Array"></returns>
/// </signature>
while (array.length < finalLength) {
array.push(value);
}
return array;
}
function padFront(array, value, finalLength) {
/// <signature>
/// <summary>Pads the front of an array with a specified value</summary>
/// <param name="array" type="Array"></param>
/// <param name="value" type="Number">The value to pad to the array</param>
/// <param name="finalLength" type="Number">The final resulting length with padding</param>
/// <returns type="Array"></returns>
/// </signature>
while (array.length < finalLength) {
array.unshift(value);
}
return array;
}
function arraysEqual(array1, array2) {
/// <signature>
/// <summary>Checks if two Arrays are equal by comparing their values.</summary>
/// <param name="array1" type="Array"></param>
/// <param name="array2" type="Array"></param>
/// <returns type="Array"></returns>
/// </signature>
var result = true;
if (array1.length !== array2.length) {
result = false;
}
for (var i = 0; i < array1.length; i++) {
if (array1[i] !== array2[i]) {
result = false;
}
}
return result;
}
function verifyByteArray(array) {
/// <signature>
/// <summary>Verify that an Array contains only byte values (0-255)</summary>
/// <param name="array" type="Array"></param>
/// <returns type="Boolean">Returns true if all values are 0-255</returns>
/// </signature>
if (getObjectType(array) !== "Array") {
return false;
}
var element;
for (var i = 0; i < array.length; i++) {
element = array[i];
if (isNaN(element) || element < 0 || element > 255) {
return false;
}
}
return true;
}
return {
toBase64: toBase64,
base64ToString: base64ToString,
base64ToBytes: base64ToBytes,
getObjectType: getObjectType,
bytesToHexString: bytesToHexString,
bytesToInt32: bytesToInt32,
stringToBytes: stringToBytes,
unpackData: unpackData,
hexToBytesArray: hexToBytesArray,
int32ToBytes: int32ToBytes,
int32ArrayToBytes: int32ArrayToBytes,
toArray: toArray,
arraysEqual: arraysEqual,
clone: clone,
xorVectors: xorVectors,
padEnd: padEnd,
padFront: padFront,
getVector: getVector,
verifyByteArray: verifyByteArray
};
})();
var msrcryptoWorker = (function () {
// If we're running in a webworker we need to postMessage to return our result
// otherwise just return the value as normal.
function returnResult(result) {
if (runningInWorkerInstance) {
self.postMessage(result);
}
return result;
}
return {
jsCryptoRunner: function ( e) {
var operation = e.data.operationType;
var result;
if (!operations.exists(operation, e.data.algorithm.name)) {
throw new Error("unregistered algorithm.");
}
var func = operations[operation][e.data.algorithm.name];
var p = e.data;
if (p.operationSubType === "process") {
func(p);
result = returnResult({ type: "process" });
} else {
result = returnResult(func(p));
}
return result;
}
};
})();
// If this is running in a webworker we need self.onmessage to receive messages from
// the calling script.
// If we are in 'synchronous mode' (everything running in one script)
// we don't want to override self.onmessage.
if (runningInWorkerInstance) {
self.onmessage = function ( e) {
// When this worker first gets instantiated we will receive seed data
// for this workers prng.
if (e.data.prngSeed) {
var entropy = e.data.prngSeed;
msrcryptoPseudoRandom.init(entropy);
return;
}
// Process the crypto operation
msrcryptoWorker.jsCryptoRunner(e);
};
}
var msrcryptoJwk = (function () {
var utils = msrcryptoUtilities;
function stringToArray(stringData) {
var result = [];
for (var i = 0; i < stringData.length; i++) {
result[i] = stringData.charCodeAt(i);
}
if (result[result.length - 1] === 0) {
result.pop();
}
return result;
}
function getKeyType(keyHandle) {
var algType = keyHandle.algorithm.name.slice(0, 3).toLowerCase();
if (algType === "rsa") {
return "RSA";
}
if (algType === "ecd") {
return "EC";
}
return "oct";
}
var algorithmMap = {
hmac : function(algorithm) {
return "HS" + algorithm.hash.name.substring(algorithm.hash.name.indexOf('-') + 1);
},
"aes-cbc" : function(algorithm) {
return "A" + algorithm.length.toString() + "CBC";
},
"aes-gcm" : function(algorithm) {
return "A" + algorithm.length.toString() + "GCM";
},
"rsaes-pkcs1-v1_5": function (algorithm) {
return "RSA1_5";
},
"rsassa-pkcs1-v1_5": function (algorithm) {
return "RS" + algorithm.hash.name.substring(algorithm.hash.name.indexOf('-') + 1);
},
"rsa-oaep": function (algorithm) {
return "RS-OAEP-" + algorithm.hash.name.substring(algorithm.hash.name.indexOf('-') + 1);
},
"rsa-pss": function (algorithm) {
return "PS" + algorithm.hash.name.substring(algorithm.hash.name.indexOf('-') + 1);
},
"ecdsa": function (algorithm) {
return "EC-" + algorithm.namedCurve.substring(algorithm.namedCurve.indexOf('-') + 1);
},
"ecdh": function (algorithm) {
return "EC-" + algorithm.namedCurve.substring(algorithm.namedCurve.indexOf('-') + 1);
}
};
function keyToJwk(keyHandle, keyData) {
var key = {};
key.kty = getKeyType(keyHandle);
key.ext = keyHandle.extractable;
key.alg = algorithmMap[keyHandle.algorithm.name.toLowerCase()](keyHandle.algorithm);
key.key_ops = keyHandle.keyUsage;
// Using .pop to determine if a property value is an array.
if (keyData.pop) {
key.k = utils.toBase64(keyData, true);
} else {
// Convert the base64Url properties to byte arrays
for (var property in keyData) {
if (keyData[property].pop) {
key[property] = utils.toBase64(keyData[property], true);
}
}
}
if (keyHandle.algorithm.namedCurve) {
key["crv"] = keyHandle.algorithm.namedCurve;
}
return key;
}
function keyToJwkOld(keyHandle, keyData) {
var key = {};
key.kty = getKeyType(keyHandle);
key.extractable = keyHandle.extractable;
// Using .pop to determine if a property value is an array.
if (keyData.pop) {
key.k = utils.toBase64(keyData, true);
} else {
// Convert the base64Url properties to byte arrays
for (var property in keyData) {
if (keyData[property].pop) {
key[property] = utils.toBase64(keyData[property], true);
}
}
}
if (keyHandle.algorithm.namedCurve) {
key["crv"] = keyHandle.algorithm.namedCurve;
}
var stringData = JSON.stringify(key, null, '\t');
return stringToArray(stringData);
}
// 'jwkKeyData' is an array of bytes. Each byte is a charCode for a json key string
function jwkToKey(keyData, algorithm, propsToArray) {
// Convert the json string to an object
var jsonKeyObject = JSON.parse(JSON.stringify(keyData)); //JSON.parse(jsonString);
// Convert the base64url encoded properties to byte arrays
for (var i = 0; i < propsToArray.length; i += 1) {
var propValue = jsonKeyObject[propsToArray[i]];
if (propValue) {
jsonKeyObject[propsToArray[i]] =
utils.base64ToBytes(propValue);
}
}
return jsonKeyObject;
}
return {
keyToJwkOld: keyToJwkOld,
keyToJwk: keyToJwk,
jwkToKey: jwkToKey
};
})();
function msrcryptoMath() {
// 'number' of bits per digit. Must be even.
var DIGIT_BITS = 24;
// 'number' of bytes per digit.
var DIGIT_NUM_BYTES = Math.floor(DIGIT_BITS / 8);
// digit mask.
var DIGIT_MASK = (1 << DIGIT_BITS) - 1;
// digit base.
var DIGIT_BASE = (1 << DIGIT_BITS);
// max digit value, unsigned
var DIGIT_MAX = DIGIT_MASK;
// Construct scaler for DIGIT_NUM_BYTES, so I don't have to multiply in the loop
var DIGIT_SCALER = [1, 256];
for (var i = 2; i <= DIGIT_NUM_BYTES; i++) {
DIGIT_SCALER[i] = DIGIT_SCALER[i - 1] * 256;
}
// Number of trailing zero bits in numbers 0..15 (4 bits). [0] is for 0, [15] is for 15.
var Zero = [0];
var One = [1];
// Create an array, mimics the constructors for typed arrays.
function createArray( parameter) {
var i, array = null;
if (!arguments.length || typeof arguments[0] === "number") {
// A number.
array = new Array(parameter);
for (i = 0; i < parameter; i += 1) {
array[i] = 0;
}
} else if (typeof arguments[0] === "object") {
// An array or other index-able object
array = new Array(parameter.length);
for (i = 0; i < parameter.length; i += 1) {
array[i] = parameter[i];
}
}
return array;
}
function swapEndianness(bytes) {
/// <summary>Swap big endian bytes to little endian bytes.</summary>
/// <param name="bytes" type="Bytes">UInt8Array - representing a big-integer.</param>
/// <returns type="Bytes">UInt8Array - the number with endianness swapped.</returns>
var out = new Array(bytes.length);
var i = 0;
while (i < bytes.length) {
out[i] = bytes[bytes.length - i - 1];
i += 1;
}
return out;
}
function stringToDigits(number, radix) {
/// <summary>Parse a String in a given base into a little endian digit array.</summary>
/// <param name="number" type="String">Input unsigned integer in a string.</param>
/// <param name="radix" optional="true" integer="true">
/// <![CDATA[ Radix of the input. Must be >=2 and <=36. Default = 10. ]]>
/// </param>
/// <returns type="Array">Array of digits in little endian; [0] is LSW.</returns>
// skip leading and trailing whitespace.
number = number.replace(/^\s+|\s+$/g, '');
var num = [0];
var buffer = [0];
radix = radix || 10; // default radix is 10
for (var i = 0; i < number.length; i += 1) {
// Extract character
var char = parseInt(number[i], radix);
if (isNaN(char)) {
throw new Error("Failed to convert string to integer in radix " + radix.toString());
}
// 'buffer' = 'num' * 'radix'
multiply(num, radix, buffer);
// 'num' = 'buffer' + 'char'
add(buffer, [ char], num);
normalizeDigitArray(num);
}
return num;
}
function digitsToString(digits, radix) {
/// <summary>Convert a big-endian byte array to a number in string in radix.</summary>
/// <param name="digits" type="Digits">A big integer as a little-endian digit array.</param>
/// <param name="radix" optional="true" integer="true">Radix from 2 to 26. Default = 10.</param>
/// <returns type="String">The number in base radix as a string.</returns>
radix = radix || 10;
if (DIGIT_BASE <= radix) {
throw new Error("DIGIT_BASE is smaller than RADIX; cannot convert.");
}
var wordLength = digits.length;
var quotient = [];
var remainder = [];
var temp1 = [];
var temp2 = [];
var divisor = [];
var a = [];
var i;
// Find the largest divisor that fits in a digit in radix
//divisor[0] = 10000; // Largest power of ten fitting in a digit
var sb = "";
var pad = "0";
divisor[0] = radix;
while (Math.floor(DIGIT_BASE / divisor[0]) >= radix) {
divisor[0] = divisor[0] * radix;
pad = pad.concat("0");
}
for (i = 0; i < wordLength; i += 1) {
a[i] = digits[i];
}
do {
var allZeros = true;
for (i = 0; i < a.length; i += 1) {
if (a[i] !== 0) {
allZeros = false;
break;
}
}
if (allZeros) {
break;
}
divRem(a, divisor, quotient, remainder, temp1, temp2);
normalizeDigitArray(quotient, a.length, true);
var newDigits = remainder[0].toString(radix);
sb = pad.substring(0, pad.length - newDigits.length) + newDigits + sb;
var swap = a;
a = quotient;
quotient = swap;
} while (true);
// Trim leading zeros
while (sb.length !== 0 && sb[0] === "0") {
sb = sb.substring(1, sb.length);
}
if (sb.length === 0) {
sb = "0";
}
return sb;
}
function powerOfTwo(i) {
/// <summary>Given a positive integer i, return a big integer in big-endian format
/// equal to 2^i. This is useful for creating fields of certain size.</summary>
/// <param name="i" type="Number">A positive integer.</param>
/// <returns>UInt8Array - 2^i as a big-endian byte array.</returns>
var requiredBytes = Math.ceil((i + 1) / 8);
var out = createArray(requiredBytes);
out[0] = Math.pow(2, i % 8);
return out;
}
function computeBitArray(bytes) {
/// <summary>Given an array of bytes in big-endian format, compute UInt8Array with
/// one element for each bit (0 or 1), in little-endian order.</summary>
/// <param name="bytes" type="Digits">An array of bytes in big-endian format.</param>
/// <returns type="Digits">An array of 0's and 1's representing the bits in little-endian.</returns>
var out = createArray(bytes.length * 8);
var bitLength = 0;
var i = bytes.length - 1;
while (i >= 0) {
var j = 0;
while (j < 8) {
var mask = (1 << j);
var bit = ((bytes[i] & mask) === mask) ? 1 : 0;
var thisBitIndex = (8 * ((bytes.length - i) - 1)) + j;
if (bit === 1) {
bitLength = thisBitIndex + 1;
}
out[thisBitIndex] = bit;
j += 1;
}
i--;
}
return out.slice(0, bitLength);
}
function bitScanForward(value) {
/// <summary>Return the 0-based index of the first non-zero bit starting at the most significant bit position.</summary>
/// <param name="value" type="Number" integer="true">Value to scan.</param>
/// <returns>Zero-based index of the first non-zero bit.</returns>
var mask = DIGIT_BASE >>> 1;
var index = DIGIT_BITS;
while (index-- > 0) {
if ((value & mask) === mask) {
break;
}
mask = mask >>> 1;
}
return index;
}
function highestSetBit(bytes) {
/// <summary>Returns the (1 indexed) index of the highest set bit.</summary>
/// <param name="bytes" type="Array">A big-endian big integer byte array.</param>
/// <returns type="Number">The index of the highest bit.</returns>
var i = 0;
var bitLength = 0;
while (i < bytes.length) {
if (bitLength === 0) {
// Look for highest set bit in this byte
var j = 7;
while (j >= 0 && bitLength === 0) {
var mask = (1 << j);
if ((bytes[i] & mask) === mask) {
bitLength = j + 1;
}
j--;
}
} else {
bitLength += 8;
}
i += 1;
}
return bitLength;
}
function fixedWindowRecode(digits, windowSize, t) {
/// <summary></summary>
/// <param name="digits" type="Array">Digits to recode</param>
/// <param name="windowSize" type="Number">Window size</param>
/// <returns type="Array">Recoded digits</returns>}
// Make a copy of digits because we are going to modify it with shifts below.
digits = digits.slice();
var recodedDigits = [],
windowSizeBits = Math.pow(2, windowSize),
windowSizeMinus1Bits = Math.pow(2, windowSize - 1);
for (var i = 0; i < t; i++) {
// k_digits[i] := (Z!k mod 2^w) - 2^(w-1);
recodedDigits[i] = (digits[0] % windowSizeBits) - windowSizeMinus1Bits;
// k := (k - k_digits[i])/2^(w-1);
digits[0] = digits[0] - recodedDigits[i];
// PERF : can probably do this faster
cryptoMath.shiftRight(digits, digits, windowSize - 1);
}
recodedDigits[i] = digits[0];
return recodedDigits;
}
function fetchBits(digits, startBit, count) {
/// <summary>From an array of digits, return a sequential set of bits.</summary>
/// <param name="digits" type=""></param>
/// <param name="start" type=""></param>
/// <param name="end" type=""></param>
var startDigit = Math.floor(startBit / cryptoMath.DIGIT_BITS);
var endDigit = startDigit + 1;
var shiftRight = (startBit % cryptoMath.DIGIT_BITS);
var shiftLeft = cryptoMath.DIGIT_BITS - shiftRight;
var bits = (digits[startDigit] >>> shiftRight) | (digits[endDigit] << shiftLeft);
return bits & (cryptoMath.DIGIT_MASK >>> (cryptoMath.DIGIT_BITS - count));
}
function fetchBits2(digits, startBit, count) {
/// <summary>From an array of digits, return a sub-set of bits from an arbitray index.</summary>
/// <param name="digits" type="Array">Array of digits</param>
/// <param name="startBit" type="Number">Zero-index position of start bit</param>
/// <param name="count" type="Number">Number of bits to return</param>
/// <returns type="Number">Value of n-bits</returns>
var startDigit = Math.floor(startBit / DIGIT_BITS),
shiftRight = (startBit % DIGIT_BITS);
return (digits[startDigit] >>> shiftRight) |
(digits[startDigit + 1] << (DIGIT_BITS - shiftRight))
& (DIGIT_MASK >>> (DIGIT_BITS - count));
}
function copyArray( source, sourceIndex, destination, destIndex, length) {
/// <summary>Copies a range of elements from one array to another array.</summary>
/// <param name="source" type="Array">Source array to copy from.</param>
/// <param name="sourceIndex" type="Number">The index in the source array at which copying begins.</param>
/// <param name="destination" type="Array">The array that receives the data.</param>
/// <param name="destIndex" type="Number">The index in the destination array at which storing begins.</param>
/// <param name="length" type="Number">The number of elements to copy.</param>
while (length-- > 0) {
destination[destIndex + length] = source[sourceIndex + length];
}
}
function isZero(array) {
/// <summary>Check if an array is zero. All elements are zero.</summary>
/// <param name="array" type="Digits">UInt16Array - An array to be checked.</param>
/// <returns type="Boolean"/>
var i;
for (i = 0; i < array.length; i += 1) {
if (array[i] !== 0) {
return false;
}
}
return true;
}
function isEven(array) {
/// <summary>Returns true if this number is even.</summary>
/// <param name="array" type="Digits"/>
/// <returns type="Boolean"/>
return (array[0] & 0x1) === 0x0;
}
function sequenceEqual(left, right) {
/// <summary>Compare two indexable collections for sequence equality.</summary>
/// <param name="left" type="Digits">The left array.</param>
/// <param name="right" type="Digits">The right array.</param>
/// <returns type="Boolean">True if both arrays are the same.</returns>
if (left.length !== right.length) {
return false;
}
for (var i = 0; i < left.length; i += 1) {
if (left[i] !== right[i]) {
return false;
}
}
return true;
}
function bytesToDigits(bytes) {
/// <summary>Convert an unsigned number from big-endian bytes to little endian digits.</summary>
/// <param name="bytes" type="Bytes">The number in unsigned big-endian byte format.</param>
/// <returns type="Array">The digits in little-endian.</returns>
// Construct scaler for DIGIT_NUM_BYTES, so I don't have to multiply in the loop
var arrayLength = Math.floor((bytes.length + DIGIT_NUM_BYTES - 1) / DIGIT_NUM_BYTES);
var array = new Array(arrayLength);
array[0] = 0;
var digit = 0, index = 0, scIndex = 0;
for (var i = bytes.length - 1; i >= 0; i--) {
digit = digit + (DIGIT_SCALER[scIndex++] * (bytes[i] & 0x0ff));
if (DIGIT_SCALER[scIndex] === DIGIT_BASE) {
scIndex = 0;
array[index++] = digit;
digit = 0;
}
}
// Last digit (MSW), if there is a need
if (digit !== 0) {
array[index] = digit;
}
// Replace potential undefined elements with zeros
while (array[--arrayLength] == null) array[arrayLength] = 0;
return array;
}
function digitsToBytes(digits, trim, minTrimLength) {
/// <summary>Construct a big endian array of bytes from a litte-endian array of digits.
/// Always returns at least one byte and trims leading zeros.</summary>
/// <param name="digits" type="Array">The digits in little-endian.</param>
/// <param name="trim" type="Boolean" optional="true">Remove the leading zeros from the result (default true)</param>
/// <param name="minTrimLength" type="Number" optional="true">Minimum length to trim down to. Valid only if trim is true. Default=1.</param>
/// <returns type="Array">Encoded bytes in big-endian format.</returns>
var i, j, byte1;
var bytes = [0];
if (typeof trim === "undefined") {
trim = true;
}
for (i = 0; i < digits.length; i += 1) {
byte1 = digits[i];
for (j = 0; j < DIGIT_NUM_BYTES; j += 1) {
bytes[i * DIGIT_NUM_BYTES + j] = byte1 & 0x0FF;
byte1 = Math.floor(byte1 / 256);
}
}
bytes = swapEndianness(bytes);
if (minTrimLength === undefined) {
minTrimLength = 1;
}
if (trim) {
while (bytes.length > minTrimLength && bytes[0] === 0) {
bytes.shift();
}
}
return bytes;
}
function intToDigits(value, numDigits) {
/// <summary>Construct an array of digits from a positive integer.</summary>
/// <param name="value" type="Number" integer="true">A positive integer to be converted to digit form.</param>
/// <param name="numDigits" type="Number" optional="true" integer="true">The number of digits to use for the digit form.</param>
/// <returns type="Array">The given integer in digit form.</returns>
if (typeof numDigits === "undefined") {
if (value <= 1) {
numDigits = 1; // Special case <= 1
} else {
var numBits = Math.log(value) / Math.LN2;
numDigits = Math.ceil(numBits / DIGIT_BITS);
}
}
var digitRepresentation = [];
while (value > 0) {
digitRepresentation.push(value % DIGIT_BASE);
value = Math.floor(value / DIGIT_BASE);
}
while (digitRepresentation.length < numDigits) {
digitRepresentation.push(0);
}
return digitRepresentation;
}
function mswIndex(digits) {
/// <summary>Return the index of the most significant word of x, 0-indexed.
/// If x is zero (no significant index), then -1 is returned.</summary>
/// <param name="digits" type="Array">Digit array.</param>
/// <returns type="Number">Index of the most significant word, or -1 if digits is zero.</returns>
for (var i = digits.length - 1; i >= 0; i--) {
if (digits[i] !== undefined && digits[i] !== 0) {
return i;
}
}
return (digits[0] === 0) ? -1 : 0;
}
function compareDigits(left, right) {
/// <summary>Compare two digit arrays by value. Returns an integer indicating the comparison result.
/// Digit arrays are in little endian.</summary>
/// <param name="left" type="Array">The object on the left side of the comparison.</param>
/// <param name="right" type="Array">The object on the right side of the comparison.</param>
/// <returns type="Number">A value that indicates the relative order of the objects
/// being compared. The value is 0 if the items are equal,
/// negative if the left object precedes the right object,
/// and positive otherwise.</returns>
var comparisonResult = 0;
var nLeft = mswIndex(left) + 1;
var nRight = mswIndex(right) + 1;
if (nLeft > nRight) {
comparisonResult = 1;
} else if (nRight > nLeft) {
comparisonResult = -1;
} else {
while ((nLeft-- > 0) && (comparisonResult === 0)) {
comparisonResult = left[nLeft] - right[nLeft];
}
}
return comparisonResult;
}
function normalizeDigitArray(digits, length, pad) {
/// <summary>Normalize a digit array by truncating any leading zeroes and adjusting its length.
/// Set the length if given, and pad it with zeroes to that length of padding is requested.</summary>
/// <remarks>Normalization results with a zero-indexed length of the array such that the MSW is not zero.
/// If the final array length is zero and no non-zero digits are found, assign digits[0]=0 and set length to 1.
/// Optionally, pad with zeroes to the given length, and set the array length.</remarks>
/// <param name="digits" type="Array">Digit array.</param>
/// <param name="length" type="Number" integer="true" optional="true">Output length to pad with zeroes.</param>
/// <param name="pad" type="Boolean" optional="true">Pad with zeroes to length if true [false].</param>
/// <returns type="Array">Resized digits array; same input object.</returns>
// Trim. Find the trimmed length and the position to start padding from (if padding is requested).
var i = mswIndex(digits);
// set the length to the given length (if given) or the trimmed length
digits.length = length || i + 1;
// Pad to the length
if (pad) {
while (++i < digits.length) {
digits[i] = 0;
}
}
if (digits.length <= 0) {
// no non-zero digits found.
digits[0] = 0;
digits.length = 1;
}
return digits;
}
function shiftRight(source, destination, bits, length) {
/// <summary>Shift a big integer to the right by the given number of bits or 1 if bits is not specified,
/// effectively dividing by two (or 2^bits) and ignoring the remainder.</summary>
/// <param name="source" type="Array">Source digit array.</param>
/// <param name="destination" type="Array">Destination digit array. May be the same as source.</param>
/// <param name="bits" integer="true" optional="true">Number of bits to shift, must be less than DIGIT_BITS and greater or equal to zero. Default is 1.</param>
/// <param name="length" optional="true" integer="true">Number of items to shift from he source array. Default is source.length.</param>
/// <remarks>This is a numerical shift. Integers are stored in arrays in little-endian format.
/// Thus, this function shifts an array from higher order indices into lower indices. [0] is LSW.
/// </remarks>
if (bits === undefined) {
bits = 1;
} else if (bits >= DIGIT_BITS || bits < 0) {
throw new Error("Invalid bit count for shiftRight");
}
if (length === undefined) {
length = source.length;
}
var n = length - 1;
var leftShiftBitCount = DIGIT_BITS - bits;
for (var i = 0; i < n; i++) {
destination[i] = ((source[i + 1] << leftShiftBitCount) | (source[i] >>> bits)) & DIGIT_MASK;
//a[i] = high|low = low bits of a[i+1] | high bits of a[i]
}
destination[n] = source[n] >>> bits;
}
function shiftLeft(source, destination, bits, length) {
/// <summary>Shift a number array to the left by given bits, i.e., multiply by 2^bits.</summary>
/// <param name="source" type="Array">Source digit array.</param>
/// <param name="destination" type="Array">Destination digit array. May be the same as source.</param>
/// <param name="bits" integer="true" optional="true">Number of bits to shift, must be less than DIGIT_BITS and greater or equal to zero. Default is 1.</param>
/// <param name="length" optional="true" integer="true">Number of items to shift from he source array. Default is source.length.</param>
/// <remarks>An additional MSW digit may be added if the leftshift out from the current MSW produces a non-zero result. [0] is LSW.</remarks>
if (bits === undefined) {
bits = 1;
} else if (bits >= DIGIT_BITS || bits < 0) {
throw new Error("bit count must be smaller than DIGIT_BITS and positive in shiftLeft");
}
if (length === undefined) {
length = source.length;
}
var rightShiftBitCount = DIGIT_BITS - bits;
// The following line is correct. destination should remain undefined if there are no bits going into it.
destination[length] = (source[length - 1] >>> (DIGIT_BITS - bits)) || destination[length];
for (var i = length - 1; i > 0; i--) {
destination[i] = ((source[i] << bits) | ((source[i - 1] >>> rightShiftBitCount))) & DIGIT_MASK;
// a[i] = high|low = low bits of a[i] | high bits of a[i-1]
}
destination[0] = (source[0] << bits) & DIGIT_MASK;
}
//// //// //// //// //// //// //// //// //// //// //// //// //// /
// Low level math routines
//// //// //// //// //// //// //// //// //// //// //// //// //// /
function add(addend1, addend2, sum) {
/// <summary>Add two arrays of digits into a third array: sum = addend1 + addend2. Carry is recorded in the output if there is one.</summary>
/// <param name="addend1" type="Array">The first addend.</param>
/// <param name="addend2" type="Array">The second added.</param>
/// <param name="sum" type="Array">The output sum buffer addend1 + addend2.</param>
/// <returns type="Number" integer="true">If carry out then 1, otherwise 0.</returns>
// Determine which is shorter
var shortArray = addend1;
var longArray = addend2;
if (addend2.length < addend1.length) {
shortArray = addend2;
longArray = addend1;
}
// Perform the addition
var s = shortArray.length;
var carry = 0;
var i;
for (i = 0; i < s; i += 1) {
carry += shortArray[i] + longArray[i];
sum[i] = carry & DIGIT_MASK;
carry = (carry >> DIGIT_BITS);
}
for (i = s; i < longArray.length; i += 1) {
carry += longArray[i];
sum[i] = carry & DIGIT_MASK;
carry = (carry >> DIGIT_BITS);
}
// Set output length
sum.length = longArray.length;
// Is there a carry into the next digit?
if (carry !== 0) {
sum[i] = carry & DIGIT_MASK;
}
return carry;
}
function subtract(minuend, subtrahend, difference) {
/// <summary>Subtraction: difference = minuend - subtrahend. Condition: minuend.length &lt;= subtrahend.length.</summary>
/// <param name="minuend" type="Array">Minuend.</param>
/// <param name="subtrahend" type="Array">Subtrahend.</param>
/// <param name="difference" type="Array">The difference.</param>
/// <returns type="Number" integer="true">Returns -1 if there is a borrow (minuend &lt; subtrahend), or 0 if there isn't (minuend &gt;= subtrahend).</returns>
var s = subtrahend.length;
if (minuend.length < subtrahend.length) {
s = mswIndex(subtrahend) + 1;
if (minuend.length < s) {
throw new Error("Subtrahend is longer than minuend, not supported.");
}
}
var i, carry = 0;
for (i = 0; i < s; i += 1) {
carry += minuend[i] - subtrahend[i];
difference[i] = carry & DIGIT_MASK;
carry = carry >> DIGIT_BITS;
}
// Propagate the carry by subtracting from minuend into difference
while (i < minuend.length) {
carry += minuend[i];
difference[i++] = carry & DIGIT_MASK;
carry = carry >> DIGIT_BITS;
}
return carry;
}
function multiply(multiplicant, multiplier, product) {
/// <summary>Multiply two arrays of digits into a third array using schoolbook.</summary>
/// <param name="multiplicant" type="Array">Multiplicand.</param>
/// <param name="multiplier">Multiplier.</param>
/// <param name="product" type="Array">Product = multiplicant * multiplier.</param>
/// <returns type="Array">The result buffer; same as the product argument.</returns>
// Single number or an array?
var mplr = (typeof multiplier === "number") ? [multiplier] : multiplier;
var s = Math.max(multiplicant.length, mplr.length);
var i, j, u;
// P <- 0
// We only have to do this for half of result
// since the upper half is over-written on the first i iteration.
for (i = 0; i < s; i += 1) {
product[i] = 0;
}
// For i from 0 by 1 to s - 1 do
for (i = 0; i < mplr.length; i += 1) {
// 'u <- 0'
u = 0;
// For j from 0 by 1 to s - 1 do
for (j = 0; j < multiplicant.length; j += 1) {
// '(u,v) <- a_j * b_i + p_(i+j) + u'
u += multiplicant[j] * mplr[i] + product[i + j];
// 'p_(i+j) <- v'
product[i + j] = (u & DIGIT_MASK) /* v */;
u = Math.floor(u / DIGIT_BASE); // 'v <- u, u <- 0'
}
product[multiplicant.length + i] = (u & DIGIT_MASK);
}
// set product length; there may still be leading zero digits after this
product.length = multiplicant.length + mplr.length;
return product;
}
function divRem(dividend, divisor, quotient, remainder, temp1, temp2) {
/// <summary>Computes the quotient q and remainder r when dividend is divided by
/// divisor.</summary>
/// <param name="dividend" type="Array">The dividend.</param>
/// <param name="divisor" type="Array">The divisor.</param>
/// <param name="quotient" type="Array">Receives the quotient (n digits).</param>
/// <param name="remainder" type="Array">Receives the remainder (n digits).</param>
/// <param name="temp1" type="Array" optional="true">Temporary storage (n digits).</param>
/// <param name="temp2" type="Array" optional="true">Temporary storage (n digits).</param>
/// <remarks>This is an implementation of Figure 9-1 is Knuth's Algorithm D [Knu2 sec. 4.3.1].
/// Throws error on division by zero.
/// </remarks>
var m = mswIndex(dividend) + 1; // zero-based length
var n = mswIndex(divisor) + 1; // zero-based length
var qhat, rhat, carry, p, t, i, j;
// Check for quick results and clear out conditionals
if (m < n) {
// dividend < divisor. q=0, remainder=dividend
copyArray(dividend, 0, remainder, 0, dividend.length);
remainder.length = dividend.length;
normalizeDigitArray(remainder);
quotient[0] = 0;
quotient.length = 1;
return;
} else if (n === 0 || (n === 1 && divisor[n - 1] === 0)) { // self-explanatory
throw new Error("Division by zero.");
} else if (n === 1) {
// divisor is single digit; do a simpler division
t = divisor[0];
rhat = 0;
for (j = m - 1; j >= 0; j--) {
p = (rhat * DIGIT_BASE) + dividend[j];
quotient[j] = (p / t) & DIGIT_MASK;
rhat = (p - quotient[j] * t) & DIGIT_MASK;
}
quotient.length = m;
normalizeDigitArray(quotient);
remainder[0] = rhat;
remainder.length = 1;
return;
}
// Normalization step. Align dividend and divisor so that their
// most significant digits are at the same index.
// Shift divisor by so many bits (0..DIGIT_BITS-1) to make MSB non-zero.
var s = DIGIT_BITS - 1 - bitScanForward(divisor[n - 1]);
var vn = temp1 || [];
vn.length = n;
shiftLeft(divisor, vn, s, n);
var un = temp2 || [];
un.length = m;
shiftLeft(dividend, un, s, m);
un[m] = un[m] || 0; // must not be undefined
// Main division loop with quotient estimate qhat
quotient.length = m - n + 1;
remainder.length = n;
for (j = m - n; j >= 0; j--) {
// Estimate quotient qhat using two-digit by one-digit division
// because 3-digit by 2-digit division is more complex. Then, correct qhat after this.
qhat = Math.floor((un[j + n] * DIGIT_BASE + un[j + n - 1]) / vn[n - 1]);
rhat = (un[j + n] * DIGIT_BASE + un[j + n - 1]) - qhat * vn[n - 1];
// If the quotient estimate is large, reduce the quotient estimate till the following is satisfied:
// qhat = {un[j+n, j+n-1, j+n-2]} div {uv[n-1,n-2]}
while (true) {
if (qhat >= DIGIT_BASE || (qhat * vn[n - 2]) > ((rhat * DIGIT_BASE) + un[j + n - 2])) {
qhat = qhat - 1;
rhat = rhat + vn[n - 1];
if (rhat < DIGIT_BASE) {
continue;
}
}
break;
}
// Multiply the [shifted] divisor by the quotient estimate and subtract the product from the dividend
// un = un - qhat*vn
carry = 0;
for (i = 0; i < n; i++) {
p = qhat * vn[i];
t = un[i + j] - carry - (p & DIGIT_MASK);
un[i + j] = t & DIGIT_MASK;
//carry = (p >>> DIGIT_BITS) - (t >> DIGIT_BITS);
// Don't shift: integer shifts are defined over 32-bit numbers in JS.
carry = Math.floor(p / DIGIT_BASE) - Math.floor(t / DIGIT_BASE);
}
t = un[j + n] - carry;
un[j + n] = t & DIGIT_MASK;
// Store the estimated quotient digit (may need correction)
quotient[j] = qhat & DIGIT_MASK;
// Correction needed?
if (t < 0) {
// quotient too big (at most by 1 divisor)
// decrement the quotient, and add [shifted] divisor back to the running dividend (remainder)
quotient[j] = quotient[j] - 1;
// un = un + vn
carry = 0;
for (i = 0; i < n; i++) {
t = un[i + j] + vn[i] + carry;
un[i + j] = t & DIGIT_MASK;
carry = t >> DIGIT_BITS;
}
un[j + n] = (un[j + n] + carry) & DIGIT_MASK;
}
}
// De-normalize the remainder (shift right by s bits).
for (i = 0; i < n; i++) {
remainder[i] = ((un[i] >>> s) | (un[i + 1] << (DIGIT_BITS - s))) & DIGIT_MASK;
}
// Compute correct lengths for the quotient and remainder
normalizeDigitArray(quotient);
normalizeDigitArray(remainder);
}
function reduce(number, modulus, remainder, temp1, temp2) {
/// <summary>Integer reduction by a modulus to compute number mod modulus. This function uses division,
/// and should not be used for repetitive operations.</summary>
/// <param name="number" type="Array">Input number to reduce.</param>
/// <param name="modulus" type="Array">Modulus to reduce the input by.</param>
/// <param name="remainder" type="Array">Output remainder = number mod modulus.</param>
/// <param name="temp1" type="Array" optional="true">Temporary space, optional.</param>
/// <param name="temp2" type="Array" optional="true">Temporary space, optional.</param>
/// <returns type="Array">The resulting remainder is in 0..modulus-1; same as "remainder".</returns>
// TODO: More efficient reduction implementation
var quotient = [];
divRem(number, modulus, quotient, remainder, temp1, temp2);
return remainder;
}
function modMul(multiplicant, multiplier, modulus, product, temp1, temp2) {
/// <summary>Moduler multiplication of two numbers for a modulus. This function uses multiply and divide method,
/// and should not be used for repetitive operations.
/// product can be same as multiplicant and multiplier.</summary>
/// <param name="multiplicant" type="Array">Multiplicand.</param>
/// <param name="multiplier">Multiplier.</param>
/// <param name="modulus" type="Array">Modulus to reduce the product.</param>
/// <param name="product" type="Array">Output product = multiplicant * multiplier mod modulus.</param>
/// <param name="temp1" type="Array" optional="true">Scratch space (optional).</param>
/// <param name="temp2" type="Array" optional="true">Scratch space (optional).</param>
/// <returns type="Array">The resulting product in in 0..modulus-1; same as product.</returns>
var quotient = [];
multiply(multiplicant, multiplier, quotient);
divRem(quotient, modulus, quotient, product, temp1, temp2);
return product;
}
function eea(a, b, upp, vpp, rpp) {
/// <summary>Extended Euclidean Algorithm, Berlekamp's version. On return
/// b*upp - a*vpp = (-1)(k-1)*rpp.</summary>
/// <param name="a" type="Array">The first number a.</param>
/// <param name="b" type="Array">The second number b.</param>
/// <param name="upp" type="Array">a^-1 mod b if gcd=1. Optional.</param>
/// <param name="vpp" type="Array">b^-1 mod a if gcd=1. Optional./</param>
/// <param name="rpp" type="Array">gcd(a,b).</param>
/// <returns type="Number">k value.</returns>
/// <remarks>Algebraic Coding Theory, Pages 24-30.<code>
/// if k is odd
/// a*a^-1 = 1 mod b ---> a^-1 = b - vpp
/// b*b^-1 = 1 mod a ---> b^-1 = vpp
/// if k is even
/// a*a^-1 = 1 mod b ---> a^-1 = upp
/// b*b^-1 = 1 mod a ---> b^-1 = a - upp
/// </code></remarks>
// Initialize rpp and rp from two inputs a and b s.t. rpp >= rp
var rp; // initialized from a or b
if (isZero(a)) { // gcd = (0,b) = b
copyArray(b, 0, rpp, 0, b.length);
rpp.length = b.length;
return 0;
} else if (isZero(b)) { // gcd = (a,0) = a
copyArray(a, 0, rpp, 0, a.length);
rpp.length = a.length;
return 0;
} else if (compareDigits(a, b) < 0) {
rp = a.slice(0);
copyArray(b, 0, rpp, 0, b.length); rpp.length = b.length;
} else {
rp = b.slice(0);
copyArray(a, 0, rpp, 0, a.length); rpp.length = a.length;
}
normalizeDigitArray(rpp);
normalizeDigitArray(rp);
var q = new Array(rpp.length);
var r = new Array(rpp.length);
var v = new Array(rpp.length);
var vppPresent = vpp !== undefined;
var vp;
if (vppPresent) {
vp = new Array(rpp.length);
vp[0] = 1; vp.length = 1;
vpp[0] = 0; vpp.length = 1;
}
var up;
var u = new Array(rpp.length);
var uppPresent = upp !== undefined;
if (uppPresent) {
up = new Array(rpp.length);
up[0] = 0; up.length = 1;
upp[0] = 1; upp.length = 1;
}
// k starts at -1 so that on return, it is >=0.
// In the following discussion, assume a<b and this is computing a^-1 mod b where (a,b)=1, a<b.
// Initialize rp=a, rpp=b.
// The integer k keeps track of the sign of a^-1 (0 = positive) in b = q*a + r with 0 = q*a + r mod b
// such that for q=a^-1 and r=1 (which is gcd=1 for inverse to exist), we have q*a = (-1)^k mod b.
// Thus, for odd k, q*a = -1 mod b, and a^-1 = b-q as in the description.
var k = -1;
// At the end, gcd = rp = (a,b)
var upp_out = upp;
var vpp_out = vpp;
var rpp_out = rpp;
var save;
// Recycle u and v as temp variables in division (divRem).
while (!isZero(rp)) {
// rpp = q*rp + r: compute q, r
divRem(rpp, rp, q, r, u, v);
if (uppPresent) {
// u = q*up + upp
// upp=up, up=u, u=upp
multiply(q, up, u);
add(u, upp, u);
normalizeDigitArray(u);
save = upp;
upp = up;
up = u;
u = save;
}
if (vppPresent) {
// v = q*vp + vpp
// vpp=vp, vp=v, v=vpp
multiply(q, vp, v);
add(v, vpp, v);
normalizeDigitArray(v);
save = vpp;
vpp = vp;
vp = v;
v = save;
}
// rpp=rp, rp=r, r=rpp
save = rpp;
rpp = rp;
rp = r;
r = save;
k++;
}
// copy to output upp, vpp, rpp
if (uppPresent) {
copyArray(upp, 0, upp_out, 0, upp.length); upp_out.length = upp.length;
}
if (vppPresent) {
copyArray(vpp, 0, vpp_out, 0, vpp.length); vpp_out.length = vpp.length;
}
copyArray(rpp, 0, rpp_out, 0, rpp.length); rpp_out.length = rpp.length;
return k;
}
function gcd(a, b, output) {
/// <summary>Compute greatest common divisor or a and b.</summary>
/// <param name="a" type="Array">First integer input.</param>
/// <param name="b" type="Array">Second integer input.</param>
/// <param name="output" type="Array" optional="true">GCD output (optional).</param>
/// <returns type="Array">GCD(a,b), the same object as the output parameter if given or a new object otherwise.</returns>
var aa = a;
var bb = b;
if (compareDigits(a, b) > 0) {
aa = b;
bb = a;
}
eea(aa, bb, undefined, undefined, output);
return normalizeDigitArray(output);
}
function modInv(a, n, aInv, pad) {
/// <summary>Modular multiplicative inverse a^-1 mod n.</summary>
/// <param name="a" type="Array">The number to invert. Condition: a &lt; n, or the result would be n^-1 mod a.</param>
/// <param name="n" type="Array">The modulus.</param>
/// <param name="aInv" type="Array" optional="true">a^-1 mod n (optional).</param>
/// <param name="pad" type="Boolean" optional="true">True to pad the returned value to the length of the modulus (optional).</param>
/// <returns type="Array">a^-1 mod n. Same as the aInv parameter if the parameter is specified.</returns>
//var gcd = eea(a, n, inv);
var upp = new Array(n.length);
var vpp = new Array(n.length);
var rpp = new Array(n.length);
var k = eea(a, n, vpp, upp, rpp);
aInv = aInv || [];
if (compareDigits(rpp, One) !== 0) {
aInv[0] = NaN;
aInv.length = 1;
} else {
// gcd = 1, there is an inverse.
// Compute inverse from Berlekamp's EEA outputs.
if ((k & 1) === 1) {
subtract(n, upp, aInv);
} else {
copyArray(upp, 0, aInv, 0, upp.length); aInv.length = upp.length;
}
if (pad) {
normalizeDigitArray(aInv, n.length, true);
} else {
normalizeDigitArray(aInv);
}
}
return aInv;
}
function modExp(base, exponent, modulus, result) {
/// <summary>Modular exponentiation in an integer group.</summary>
/// <param name="base" type="Array">The base of the exponentiation.</param>
/// <param name="exponent" type="Array">The exponent.</param>
/// <param name="modulus" type="Array">Modulus to reduce the result.</param>
/// <param name="result" type="Array" optional="true">Output element that takes the modular exponentiation result (optional).</param>
/// <returns type="Array">Modular exponentiation result, same as <paramref name="result"/> if not null, or a new object.</returns>
result = result || [];
// If exponent is 0 return 1
if (compareDigits(exponent, Zero) === 0) {
result[0] = 1;
} else if (compareDigits(exponent, One) === 0) {
// If exponent is 1 return valueElement
copyArray(base, 0, result, 0, base.length);
result.length = base.length;
} else {
var montmul = new MontgomeryMultiplier(modulus);
normalizeDigitArray(base, montmul.s, true);
montmul.modExp(
base,
exponent,
result);
result.length = modulus.length;
}
return result;
}
function MontgomeryMultiplier(modulus) {
/// <summary>Construct a new montgomeryMultiplier object with the given modulus.</summary>
/// <param name="modulus" type="Array">A prime modulus in little-endian digit form</param>
/// <remarks>Montgomery Multiplier class
/// This class implements high performance montgomery multiplication using
/// CIOS, as well as modular exponentiation.</remarks>
function computeM0Prime(m0) {
/// <summary>Compute m' = -(m^-1) mod b, 16 bit digits. Based on Tolga Acar's code.</summary>
/// <param name="m0" type="Number" integer="true">Digit m.</param>
/// <returns type="Number">Digit m'.</returns>
var m0Pr = 1;
var a = 2;
var b = 3;
var c = b & m0;
for (var i = 2; i <= DIGIT_BITS; i += 1) {
if (a < c) {
m0Pr += a;
}
a = a << 1;
b = (b << 1) | 1;
c = m0 * m0Pr & b;
}
var result = (~m0Pr & DIGIT_MASK) + 1;
return result;
}
function montgomeryMultiply(multiplicant, multiplier, result, ctx) {
/// <summary>Montgomery multiplication with the CIOS method.</summary>
/// <param name="multiplicant" type="Array">Multiplicant.</param>
/// <param name="multiplier" type="Array">Multiplier.</param>
/// <param name="result" type="Array">Computed result multiplicant * multiplier * r^-1 mod n.</param>
/// <param name="ctx" type="MontgomeryMultiplier" optional="true">Context (optional = this).</param>
ctx = ctx || this;
// Upper digits of result
var resultHigh = 0;
// Precompute offsets
var s = ctx.m.length;
var sMinus1 = s - 1;
// Local cache of m0, m', digitmask, digitbits
var mPrime = ctx.mPrime;
var m0 = ctx.m0;
var left0 = multiplicant[0];
var uv = 0, rightI, q, i, j, k;
// Clear the result array
for (i = 0; i < s; i += 1) {
result[i] = 0;
}
for (i = 0; i < s; i += 1) {
rightI = multiplier[i]; // Cache array value
// 'u <- 0'
// ---- UNROLL FIRST ITERATION (j == 0) ----
uv = left0 * rightI + result[0];
result[0] = uv & DIGIT_MASK;
uv = Math.floor(uv / DIGIT_BASE);
// ---- REMAINING ITERATIONS ----
for (j = 1; j < s; j += 1) {
// '(u,v) <- a_j * b_i + z_j + u'
// uv = uv >>> DIGIT_BITS;
// Don't shift: JS supports shifts over 32-bit integers, only.
uv = multiplicant[j] * rightI + result[j] + uv;
result[j] = uv & DIGIT_MASK;
uv = Math.floor(uv / DIGIT_BASE);
}
// -------------------------------
// '(u,v) <- z_s + u'.
// 'z_s <- v'.
// 'z_s+1 <- u'.
resultHigh = resultHigh + uv;
// 'q <- z_0 * m' mod digitBase
q = (result[0] * mPrime) & DIGIT_MASK;
// '(u,v) <- z_0 + m_0 * q'
uv = Math.floor((result[0] + (m0 * q)) / DIGIT_BASE);
// For j from 1 by 1 to s-1
for (j = 1, k = 0; j < s; j += 1, k++) {
// '(u,v) <- m_j * q + z_j + u'
uv = ctx.m[j] * q + result[j] + uv;
// 'z_j-1 <- v'
result[k] = uv & DIGIT_MASK;
uv = Math.floor(uv / DIGIT_BASE);
}
// '(u,v) <- z_s + u'.
// 'z_s-1 <- v'.
// 'z_s <- z_s+1 + u'.
resultHigh += uv;
result[sMinus1] = resultHigh & DIGIT_MASK;
resultHigh = Math.floor(resultHigh / DIGIT_BASE);
}
// Subtract modulus
// Make sure temp1 isn't also our passed-in result array.
// This can happen if temp1 was returned as a result to a previous call.
var resultMinusM = ctx.temp1 === result ? ctx.temp2 : ctx.temp1,
carry = 0;
for (i = 0; i < s; i += 1) {
carry = result[i] - ctx.m[i] + (carry >> DIGIT_BITS);
resultMinusM[i] = carry & DIGIT_MASK;
}
carry = (resultHigh & DIGIT_MASK) + (carry >> DIGIT_BITS);
carry = (resultHigh >>> DIGIT_BITS) + (carry >> DIGIT_BITS);
// Use carry as a mask to copy result into the return array
for (i = 0; i < s; i += 1) {
result[i] = (carry & (resultMinusM[i] ^ result[i])) ^ resultMinusM[i];
}
return;
}
function convertToMontgomeryForm( digits) {
/// <summary>Convert the digits in standard form to Montgomery residue representation.</summary>
/// <param name="digits" type="Array">Input digits to convert, and also the output converted digits.</param>
// Pad missing digits with zeroes
if (digits.length < this.s) {
digits.length = this.s;
for (var i = 0; i < this.s; i++) {
digits[i] = isNaN(digits[i]) ? 0 : digits[i];
}
}
var result = createArray(digits.length);
this.montgomeryMultiply(digits, this.rSquaredModm, result);
for (i = 0; i < this.s; i += 1) {
digits[i] = result[i];
}
}
function convertToStandardForm(digits) {
/// <summary>Convert from Montgomery residue representation to the standard form.</summary>
/// <param name="digits" type="Array">Input digits to convert, and also the output converted digits.</param>
this.montgomeryMultiply(digits, this.one, this.temp1);
for (var i = 0; i < this.s; i += 1) {
digits[i] = this.temp1[i];
}
}
function modExp(base, exponent, result) {
/// <summary>Compute base to exponent mod m into result.</summary>
/// <param name="base" type="Array">Base of length s in the context.</param>
/// <param name="exponent" type="Array">Exponent.</param>
/// <param name="result" type="Array">Output as base raised to exponent, and reduced to the modulus in the context.</param>
/// <returns type="Array">result base^exponent mod m; the same result object.</returns>
// Skip leading zero bits in the exponent
// The total number of bits to scan in the exponent must be an integral multiple of
// the number of bits to use in the exponent.
var i;
var expBitsToScan = 2; // scan 2 bits at a time
var expMask = DIGIT_MASK >>> (DIGIT_BITS - expBitsToScan);
for (i = exponent.length - 1; i > 0 && exponent[i] === 0; i--) {
}
var bitsToScan = i * DIGIT_BITS + bitScanForward(exponent[i]) + 1;
bitsToScan = bitsToScan + (expBitsToScan - (bitsToScan % expBitsToScan));
var shiftAmt = (bitsToScan % DIGIT_BITS) - expBitsToScan;
if (shiftAmt < 0) {
shiftAmt += DIGIT_BITS;
}
var mask = expMask << shiftAmt;
// Prepare the precomputation table of base for k bits
// base[0..3] = [r, r*base, r*base^2, r*base^3] mod m
for (i = 1; i < baseTable.length; i++) {
modMul(baseTable[i - 1], base, this.m, baseTable[i], temp1, temp2);
normalizeDigitArray(baseTable[i], this.s, true);
}
// a is the running result: a = 1*r mod m
// TODO: Skip the first loop iteration below to avoid 1*1 mod m (minor optimization)
var fourthPower = new Array(this.s);
var squared = result;
var partialResult = temp2;
copyArray(this.rModM, 0, partialResult, 0, this.s);
// Scan the exponent expBitsToScan bits at a time
var tableIndex;
while (bitsToScan > 0) {
// result <- Mont(a, a);
// result <- Mont(result, a);
this.montgomeryMultiply(partialResult, partialResult, squared);
this.montgomeryMultiply(squared, squared, fourthPower);
// tableIndex <- the current bits of the scanned exponent
tableIndex = (exponent[Math.floor((bitsToScan - 1) / DIGIT_BITS)] & mask) >>> shiftAmt;
// aDigits = result * table[tableIndex]
this.montgomeryMultiply(fourthPower, baseTable[tableIndex], partialResult);
bitsToScan = bitsToScan - expBitsToScan;
shiftAmt = shiftAmt - expBitsToScan;
mask = mask >>> expBitsToScan;
if (mask === 0) {
mask = expMask << (DIGIT_BITS - expBitsToScan);
shiftAmt = DIGIT_BITS - expBitsToScan;
}
}
// result = Mont(a, 1)
this.montgomeryMultiply(partialResult, this.one, result);
return result;
}
// Modulus
var m = modulus;
// First digit of modulus
var m0 = m[0];
// Operand size (number of digits)
var s = m.length;
// The number one - used by modpow
var one = createArray(s);
one[0] = 1;
// Compute m' = -(m^-1) mod b used by CIOS
var mPrime = computeM0Prime(m0);
// Create r and compute r mod m
// Since we are base b integers of length s, we want
// 'r = b^n = b^s'.
var quotient = createArray(2 * s + 1);
var rRemainder = createArray(s + 1); // becomes rModM
var temp1 = createArray(2 * s + 1);
var temp2 = createArray(2 * s + 1);
var rDigits = rRemainder;
rDigits[s] = 1;
divRem(
rDigits,
m,
quotient,
rRemainder,
temp1,
temp2);
var rModM = normalizeDigitArray(rRemainder, s, true);
// Compute R^2 mod m
var rSquaredModm = createArray(2 * s + 1);
var rSquaredDigits = rSquaredModm;
rSquaredDigits[s * 2] = 1;
divRem(
rSquaredDigits,
m,
quotient,
rSquaredModm,
temp1,
temp2);
normalizeDigitArray(rSquaredModm, s, true);
// Ready to do MontMul now - compute R^3
var rCubedModm = createArray(s);
var ctx = {
m: m, mPrime: mPrime, m0: m0, temp1: temp1, temp2: temp2
};
montgomeryMultiply(
rSquaredModm,
rSquaredModm,
rCubedModm,
ctx);
// Allocate space for multi-bit modular exponentiation
var baseTable = new Array(4);
baseTable[0] = rModM;
baseTable[1] = new Array(s);
baseTable[2] = new Array(s);
baseTable[3] = new Array(s);
// Return a per-instance context for Montgomery multiplier.
// There is no need to use the "new" keyword when using this function.
return {
// Modulus
m: modulus,
// First digit of modulus
m0: m0,
// Compute m' = -(m^-1) mod b used by CIOS
mPrime: mPrime,
rSquaredModm: rSquaredModm,
s: s,
rModM: rModM,
rCubedModm: rCubedModm,
one: one,
temp1: temp1,
temp2: temp2,
// Functions
convertToMontgomeryForm: convertToMontgomeryForm,
convertToStandardForm: convertToStandardForm,
montgomeryMultiply: montgomeryMultiply,
modExp: modExp
};
}
function IntegerGroup(modulusBytes) {
/// <summary>Construct a new IntegerGroup object with the given modulus.</summary>
/// <param name="modulusBytes" type="Array">A big-endian number to represent the modulus in a byte array.</param>
/// <remarks>This class represents the set of integers mod n. It is meant to be used in
/// a variety of situations, for example to perform operations in the additive
/// or multiplicative groups mod n. The modulus can be an arbitrary integer and
/// in the case that it is a prime p then the integer group is the field Fp. The
/// user should be aware of what type of object the given modulus produces, and
/// thus which operations are valid.</remarks>
// Modulus
var m_modulus = bytesToDigits(modulusBytes);
// Length of an element in digits
var m_digitWidth = m_modulus.length;
// Setup numeric constants
var m_zero = intToDigits(0, m_digitWidth);
var m_one = intToDigits(1, m_digitWidth);
// Temp storage.
// Allocation in js is very slow, we use these temp arrays to avoid it.
var temp0 = createArray(m_digitWidth);
var temp1 = createArray(m_digitWidth);
// Create montgomery multiplier object
var montmul = new MontgomeryMultiplier(m_modulus);
function createElementFromBytes(bytes) {
/// <summary>Create a new element object from a byte value.</summary>
/// <param name="bytes" type="Array">Desired element in big-endian format in an array of bytes.</param>
/// <returns type="integerGroupElement">An element object representing the given element.</returns>
var digits = bytesToDigits(bytes);
// Check size of the new element
if (cryptoMath.compareDigits(digits, this.m_modulus) >= 0) {
// Too many digits
throw new Error("The number provided is not an element of this group");
}
// expand to the group modulus length
normalizeDigitArray(digits, this.m_digitWidth, true);
return integerGroupElement(digits, this);
}
function createElementFromInteger(integer) {
/// <summary>Create a new element object from an integer value.</summary>
/// <param name="integer" type="Number" integer="true">Desired element as an integer.</param>
/// <returns type="integerGroupElement">An element object representing the given element.</returns>
var digits = intToDigits(integer, this.m_digitWidth);
return integerGroupElement(digits, this);
}
function createElementFromDigits(digits) {
/// <summary>Create a new element object from a digit array.</summary>
/// <param name="digits" type="Array">Desired element as a digit array.</param>
/// <returns type="integerGroupElement">Object initialized with the given value.</returns>
cryptoMath.normalizeDigitArray(digits, this.m_digitWidth, true);
return integerGroupElement(digits, this);
}
function equals(otherGroup) {
/// <summary>Return true if the given object is equivalent to this one.</summary>
/// <param name="otherGroup" type="IntegerGroup"/>)
/// <returns type="Boolean">True if the given objects are equivalent.</returns>
return compareDigits(this.m_modulus, otherGroup.m_modulus) === 0;
}
function add(addend1, addend2, sum) {
/// <summary>Add this element to another element.</summary>
/// <param name="addend1" type="integerGroupElement"/>
/// <param name="addend2" type="integerGroupElement"/>
/// <param name="sum" type="integerGroupElement"/>
var i;
var s = this.m_digitWidth;
var result = sum.m_digits;
cryptoMath.add(addend1.m_digits, addend2.m_digits, result);
var mask = compareDigits(result, this.m_modulus) >= 0 ? DIGIT_MASK : 0;
// Conditional reduction by the modulus (one subtraction, only) only if the sum>modulus in almost constant time.
// The result is unmodified if the computed sum < modulus already.
var carry = 0;
for (i = 0; i < s; i += 1) {
carry = result[i] - (this.m_modulus[i] & mask) + carry;
result[i] = carry & DIGIT_MASK;
carry = (carry >> DIGIT_BITS);
}
result.length = s;
}
function subtract(leftElement, rightElement, outputElement) {
/// <summary>Subtract an element from another element.</summary>
/// <param name="leftElement" type="integerGroupElement"/>
/// <param name="rightElement" type="integerGroupElement"/>
/// <param name="outputElement" type="integerGroupElement"/>
var i, s = this.m_digitWidth;
var result = outputElement.m_digits;
var carry = cryptoMath.subtract(leftElement.m_digits, rightElement.m_digits, outputElement.m_digits);
// Final borrow?
if (carry === -1) {
carry = 0;
for (i = 0; i < s; i += 1) {
carry += result[i] + this.m_modulus[i];
result[i] = carry & DIGIT_MASK;
carry = carry >> DIGIT_BITS;
}
}
}
function inverse(element, outputElement) {
/// <summary>Compute the modular inverse of the given element.</summary>
/// <param name="element" type="integerGroupElement">The element to be inverted.</param>
/// <param name="outputElement" type="integerGroupElement">Receives the inverse element.</param>
cryptoMath.modInv(element.m_digits, this.m_modulus, outputElement.m_digits);
}
function multiply(multiplicant, multiplier, product) {
/// <summary>Multiply an element by another element in the integer group.</summary>
/// <param name="multiplicant" type="integerGroupElement">Multiplicand.</param>
/// <param name="multiplier" type="integerGroupElement">Multiplier.</param>
/// <param name="product" type="integerGroupElement">Product reduced by the group modulus.</param>
/// <returns type="Array">Same as <paramref name="product"/>.</returns>
return cryptoMath.modMul(multiplicant.m_digits, multiplier.m_digits, this.m_modulus, product.m_digits, temp0, temp1);
}
function modexp(valueElement, exponent, outputElement) {
/// <summary>Modular exponentiation in an integer group.</summary>
/// <param name="valueElement" type="integerGroupElement">The base input to the exponentiation.</param>
/// <param name="exponent" type="Array">The exponentas an unsigned integer.</param>
/// <param name="outputElement" type="integerGroupElement" optional="true">Output element that takes the modular exponentiation result.</param>
/// <returns type="integerGroupElement">Computed result. Same as <paramref name="outputElement"/> if not null, a new object otherwise.</returns>
outputElement = outputElement || integerGroupElement([], this);
// If exponent is 0 return 1
if (compareDigits(exponent, m_zero) === 0) {
outputElement.m_digits = intToDigits(1, this.m_digitWidth);
} else if (compareDigits(exponent, m_one) === 0) {
// If exponent is 1 return valueElement
for (var i = 0; i < valueElement.m_digits.length; i++) {
outputElement.m_digits[i] = valueElement.m_digits[i];
}
outputElement.m_digits.length = valueElement.m_digits.length;
} else {
this.montmul.modExp(
valueElement.m_digits,
exponent,
outputElement.m_digits);
outputElement.m_digits.length = this.montmul.s;
}
return outputElement;
}
function integerGroupElement(digits, group) {
/// <summary>integerGroupElement inner class.
/// Create a new integer element mod n.
/// </summary>
/// <param name="digits" type="Array">
/// An array of digits representing the element.
/// </param>
/// <param name="group" type="IntegerGroup">
/// The parent group to which this element belongs.
/// </param>
// The value given in digits
// must be &gt;= 0 and &;lt; modulus. Note that the constructor should not be
// visible to the user, user should use group.createElementFromDigits(). This way we
// can use any digit size and endian-ness we wish internally, operating in
// our chosen representation until such time as the user wishes to produce
// a byte array as output, which will be done by calling
// toByteArrayUnsigned(). Note that other properties and methods are meant
// to be "public" of course and thus callable by the user.
return {
// Variables
m_digits: digits,
m_group: group,
// Functions
equals: function (element) {
/// <summary>Compare an elements to this for equality.</summary>
/// <param name="element" type="integerGroupElement">Element to compare.</param>
/// <returns>True if elements are equal, false otherwise.</returns>
return (compareDigits(this.m_digits, element.m_digits) === 0) &&
this.m_group.equals(this.m_group, element.m_group);
}
};
}
return {
// Variables
m_modulus: m_modulus,
m_digitWidth: m_digitWidth,
montmul: montmul,
// Functions
createElementFromInteger: createElementFromInteger,
createElementFromBytes: createElementFromBytes,
createElementFromDigits: createElementFromDigits,
equals: equals,
add: add,
subtract: subtract,
multiply: multiply,
inverse: inverse,
modexp: modexp
};
}
return {
DIGIT_BITS: DIGIT_BITS,
DIGIT_NUM_BYTES: DIGIT_NUM_BYTES,
DIGIT_MASK: DIGIT_MASK,
DIGIT_BASE: DIGIT_BASE,
DIGIT_MAX: DIGIT_MAX,
Zero: Zero,
One: One,
normalizeDigitArray: normalizeDigitArray,
swapEndianness: swapEndianness,
bytesToDigits: bytesToDigits,
stringToDigits: stringToDigits,
digitsToString: digitsToString,
intToDigits: intToDigits,
digitsToBytes: digitsToBytes,
sequenceEqual: sequenceEqual,
isZero: isZero,
isEven: isEven,
powerOfTwo: powerOfTwo,
shiftRight: shiftRight,
shiftLeft: shiftLeft,
compareDigits: compareDigits,
computeBitArray: computeBitArray,
bitLength: highestSetBit,
fixedWindowRecode: fixedWindowRecode,
IntegerGroup: IntegerGroup,
add: add,
subtract: subtract,
multiply: multiply,
divRem: divRem,
reduce: reduce,
modInv: modInv,
modExp: modExp,
modMul: modMul,
MontgomeryMultiplier: MontgomeryMultiplier,
gcd: gcd
};
}
var cryptoMath = cryptoMath || msrcryptoMath();
/// cryptoECC.js ==================================================================================
/// Implementation of Elliptic Curve math routines for cryptographic applications.
function MsrcryptoECC() {
/// <summary>Elliptic Curve Cryptography (ECC) functions.</summary>
// Create an array, mimics the constructors for typed arrays.
function createArray( parameter) {
var i, array = null;
if (!arguments.length || typeof arguments[0] === "number") {
// A number.
array = [];
for (i = 0; i < parameter; i += 1) {
array[i] = 0;
}
} else if (typeof arguments[0] === "object") {
// An array or other index-able object
array = [];
for (i = 0; i < parameter.length; i += 1) {
array[i] = parameter[i];
}
}
return array;
}
var btd = cryptoMath.bytesToDigits;
var EllipticCurveFp = function (p1, a1, b1, order, gx, gy) {
/// <param name="p1" type="Digits"/>
/// <param name="a1" type="Digits"/>
/// <param name="b1" type="Digits"/>
/// <param name="order" type="Digits"/>
/// <param name="gx" type="Digits"/>
/// <param name="gy" type="Digits"/>
/// <returns type="EllipticCurveFp"/>
var fieldStorageBitLength = p1.length;
var generator = EllipticCurvePointFp(this, false, gx, gy, null, false);
return {
p: p1, // field prime
a: a1, // Weierstrass coefficient a
b: b1, // Weierstrass coefficient b
order: order, // EC group order
generator: generator, // EC group generator
allocatePointStorage: function () {
return EllipticCurvePointFp(
this,
false,
cryptoMath.intToDigits(0, fieldStorageBitLength),
cryptoMath.intToDigits(0, fieldStorageBitLength)
);
},
createPointAtInfinity: function () {
return EllipticCurvePointFp(
this,
true,
cryptoMath.intToDigits(0, fieldStorageBitLength),
cryptoMath.intToDigits(0, fieldStorageBitLength)
);
}
};
};
var createWeierstrassCurve = function (curveData) {
var newCurve = new EllipticCurveFp(
btd(curveData.p), // P
btd(curveData.a), // A
btd(curveData.b), // B
btd(curveData.order), // Order
btd(curveData.gx), // gX
btd(curveData.gy) // gy
);
newCurve.type = curveData.type;
newCurve.name = curveData.name;
newCurve.generator.curve = newCurve;
return newCurve;
};
var createTedCurve = function (curveData) {
var btd = cryptoMath.bytesToDigits;
var newCurve = new EllipticCurveFp(
btd(curveData.p), // P
btd(curveData.a), // A
btd(curveData.d), // D
btd(curveData.order), // Order
btd(curveData.gx), // gX
btd(curveData.gy) // gy
);
newCurve.type = curveData.type;
if (newCurve.type == 1) {
newCurve.d = newCurve.b.slice();
delete newCurve.b;
}
newCurve.rbits = curveData.info[2];
newCurve.name = curveData.name;
newCurve.generator.curve = newCurve;
return newCurve;
};
var EllipticCurvePointFp = function (curve, isInfinity, x, y, z, isInMontgomeryForm) {
/// <param name="curve" type="EllipticCurveFp"/>
/// <param name="isInfinity" type="Boolean"/>
/// <param name="x" type="Digits"/>
/// <param name="y" type="Digits"/>
/// <param name="z" type="Digits" optional="true"/>
/// <param name="isInMontgomeryForm" type="Boolean" optional="true"/>
/// <returns type="EllipticCurvePointFp"/>
var returnObj;
// 'optional' parameters
if (typeof z === "undefined") {
z = null;
}
if (typeof isInMontgomeryForm === "undefined") {
isInMontgomeryForm = false;
}
function equals( ellipticCurvePointFp) {
/// <param name="ellipticCurvePointFp" type="EllipticCurvePointFp"/>
// If null
if (!ellipticCurvePointFp) {
return false;
}
// Infinity == infinity
if (returnObj.isInfinity && ellipticCurvePointFp.isInfinity) {
return true;
}
// Otherwise its member-wise comparison
if (returnObj.z === null && ellipticCurvePointFp.z !== null) {
return false;
}
if (returnObj.z !== null && ellipticCurvePointFp.z === null) {
return false;
}
if (returnObj.z === null) {
return (cryptoMath.compareDigits(returnObj.x, ellipticCurvePointFp.x) === 0 &&
cryptoMath.compareDigits(returnObj.y, ellipticCurvePointFp.y) === 0 &&
returnObj.isInMontgomeryForm === ellipticCurvePointFp.isInMontgomeryForm);
}
return (cryptoMath.compareDigits(returnObj.x, ellipticCurvePointFp.x) === 0 &&
cryptoMath.compareDigits(returnObj.y, ellipticCurvePointFp.y) === 0 &&
cryptoMath.compareDigits(returnObj.z, ellipticCurvePointFp.z) === 0 &&
returnObj.isInMontgomeryForm === ellipticCurvePointFp.isInMontgomeryForm);
}
function copyTo( source, destination) {
/// <param name="source" type="EllipticCurvePointFp"/>
/// <param name="destination" type="EllipticCurvePointFp"/>
destination.curve = source.curve;
destination.x = source.x.slice();
destination.y = source.y.slice();
if (source.z !== null) {
destination.z = source.z.slice();
} else {
destination.z = null;
}
setterSupport || (destination.isAffine = source.isAffine);
destination.isInMontgomeryForm = source.isInMontgomeryForm;
destination.isInfinity = source.isInfinity;
if (!destination.equals(source)) {
throw new Error("Instances should be equal.");
}
}
function clone() {
var clonePoint = EllipticCurvePointFp(
returnObj.curve,
returnObj.isInfinity,
createArray(returnObj.x),
createArray(returnObj.y),
returnObj.z ? createArray(returnObj.z) : null,
returnObj.isInMontgomeryForm);
returnObj.ta && (clonePoint.ta = createArray(returnObj.ta));
returnObj.tb && (clonePoint.tb = createArray(returnObj.tb));
return clonePoint;
}
returnObj = {
equals: function (ellipticCurvePointFp) {
return equals(ellipticCurvePointFp);
},
copy: function (destination) {
copyTo(this, destination);
return;
},
clone: function () {
return clone();
}
};
createProperty(returnObj, "curve", curve, function () { return curve; }, function (val) { curve = val; });
createProperty(returnObj, "x", x, function () { return x; }, function (val) { x = val; });
createProperty(returnObj, "y", y, function () { return y; }, function (val) { y = val; });
createProperty(returnObj, "z", z, function () { return z; }, function (val) { z = val; });
createProperty(returnObj, "isInMontgomeryForm", isInMontgomeryForm, function () { return isInMontgomeryForm; }, function (val) { isInMontgomeryForm = val; });
createProperty(returnObj, "isInfinity", isInfinity, function () { return isInfinity; }, function (val) { isInfinity = val; });
createProperty(returnObj, "isAffine", (z === null), function () { return (z === null); });
return returnObj;
};
var EllipticCurveOperatorFp = function (curve) {
/// <param name="curve" type="EllipticCurveFp"/>
// Store a reference to the curve.
var m_curve = curve;
var tedCurve = (curve.type === 1);
var fieldElementWidth = curve.p.length;
var montgomeryMultiplier = cryptoMath.MontgomeryMultiplier(curve.p);
// Pre-compute and store the montgomeryized form of A, and set our
// zero flag to determine whether or not we should use implementations
// optimized for A = 0.
var montgomerizedA = curve.a.slice();
montgomeryMultiplier.convertToMontgomeryForm(montgomerizedA);
var aequalsZero = cryptoMath.isZero(curve.a);
var one = cryptoMath.One;
var onemontgomery = createArray(fieldElementWidth);
onemontgomery[0] = 1;
montgomeryMultiplier.convertToMontgomeryForm(onemontgomery);
var group = cryptoMath.IntegerGroup(cryptoMath.digitsToBytes(montgomeryMultiplier.m), true);
// Setup temp storage.
var temp0 = createArray(fieldElementWidth);
var temp1 = createArray(fieldElementWidth);
var temp2 = createArray(fieldElementWidth);
var temp3 = createArray(fieldElementWidth);
var temp4 = createArray(fieldElementWidth);
var temp5 = createArray(fieldElementWidth);
var temp6 = createArray(fieldElementWidth);
var temp7 = createArray(fieldElementWidth);
var swap0 = createArray(fieldElementWidth);
// Some additional temp storage used in point conversion routines.
var conversionTemp0 = createArray(fieldElementWidth);
var conversionTemp1 = createArray(fieldElementWidth);
var conversionTemp2 = createArray(fieldElementWidth);
function modSub(left, right, result) {
var resultElement = group.createElementFromInteger(0);
resultElement.m_digits = result;
group.subtract(
group.createElementFromDigits(left),
group.createElementFromDigits(right),
resultElement);
}
function modAdd(left, right, result) {
var resultElement = group.createElementFromInteger(0);
resultElement.m_digits = result;
group.add(
group.createElementFromDigits(left),
group.createElementFromDigits(right),
resultElement);
}
function modInv(number, result) {
cryptoMath.modInv(number, m_curve.p, result);
}
function modDivByTwo( dividend, result) {
var s = dividend.length;
var modulus = curve.p;
// If dividend is odd, add modulus
if ((dividend[0] & 0x1) === 0x1) {
var carry = 0;
for (var i = 0; i < s; i += 1) {
carry += dividend[i] + modulus[i];
result[i] = carry & cryptoMath.DIGIT_MASK;
carry = (carry >>> cryptoMath.DIGIT_BITS);
}
// Put carry bit into position for masking in
carry = carry << (cryptoMath.DIGIT_BITS - 1);
// Bit shift
cryptoMath.shiftRight(result, result);
// Mask in the carry bit
result[s - 1] |= carry;
} else {
// Shift directly into result
cryptoMath.shiftRight(dividend, result);
}
}
function montgomeryMultiply(left, right, result) {
montgomeryMultiplier.montgomeryMultiply(
left,
right,
result);
}
function montgomerySquare(left, result) {
montgomeryMultiplier.montgomeryMultiply(
left,
left,
result);
}
function correctInversion(digits) {
/// <param name="digits" type="Digits"/>
var results = createArray(digits.length);
montgomeryMultiply(digits, montgomeryMultiplier.rCubedModm, results);
for (var i = 0; i < results.length; i += 1) {
digits[i] = results[i];
}
}
function doubleAequalsNeg3(point, outputPoint) {
/// <param name="point" type="EllipticCurvePointFp"/>
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
// If point = infinity then outputPoint := infinity.
if (point.isInfinity) {
outputPoint.isInfinity = true;
return;
}
// t1 = z^2
montgomerySquare(point.z, temp1);
// t4 = zy
montgomeryMultiply(point.z, point.y, temp4);
// t2 = x + z^2
// t2 = x + t1
modAdd(point.x, temp1, temp2);
// t1 = x - z^2
// t1 = x - t1
modSub(point.x, temp1, temp1);
// Zfinal = zy
outputPoint.z = temp4.slice();
// t3 = (x + z^2)(x - z^2)
montgomeryMultiply(temp1, temp2, temp3);
// t2 = (x + z^2)(x - z^2)/2
modDivByTwo(temp3, temp2);
// t1 = alpha = 3(x + z^2)(x - z^2)/2
modAdd(temp3, temp2, temp1);
// t2 = y^2
montgomerySquare(point.y, temp2);
// t4 = alpha^2
montgomerySquare(temp1, temp4);
// t3 = beta = xy^2
montgomeryMultiply(point.x, temp2, temp3);
// t4 = alpha^2-beta
modSub(temp4, temp3, temp4);
// Xfinal = alpha^2-2beta
modSub(temp4, temp3, outputPoint.x);
// t4 = beta-Xfinal
modSub(temp3, outputPoint.x, temp4);
// t3 = y^4
montgomerySquare(temp2, temp3);
// t3 = y^4
montgomeryMultiply(temp1, temp4, temp2);
// Yfinal = alpha.(beta-Xfinal)-y^4
modSub(temp2, temp3, outputPoint.y);
// Finalize the flags on the output point.
outputPoint.isInfinity = false;
outputPoint.isInMontgomeryForm = true;
}
function doubleAequals0(point, outputPoint) {
/// <param name="point" type="EllipticCurvePointFp"/>
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
// If point = infinity then outputPoint := infinity.
if (point.isInfinity) {
outputPoint.isInfinity = true;
return;
}
// 't3:=Y1^2;'
montgomerySquare(point.y, temp3);
// 't4:=X1^2;'
montgomerySquare(point.x, temp4);
// 't4:=3*t4;'
modAdd(temp4, temp4, temp0);
modAdd(temp0, temp4, temp4);
// 't5:=X1*t3;'
montgomeryMultiply(point.x, temp3, temp5);
// 't0:=t3^2;'
montgomerySquare(temp3, temp0);
// 't1:=t4/2;'
modDivByTwo(temp4, temp1);
// 't3:=t1^2;'
montgomerySquare(temp1, temp3);
// 'Z_out:=Y1*Z1;'
montgomeryMultiply(point.y, point.z, swap0);
for (var i = 0; i < swap0.length; i += 1) {
outputPoint.z[i] = swap0[i];
}
// 'X_out:=t3-2*t5;'
modSub(temp3, temp5, outputPoint.x);
modSub(outputPoint.x, temp5, outputPoint.x);
// 't4:=t5-X_out;'
modSub(temp5, outputPoint.x, temp4);
// 't2:=t1*t4;'
montgomeryMultiply(temp1, temp4, temp2);
// 'Y_out:=t2-t0;'
modSub(temp2, temp0, outputPoint.y);
// Finalize the flags on the output point.
outputPoint.isInfinity = false;
outputPoint.isInMontgomeryForm = true;
}
// Given a povar P on an elliptic curve, return a table of
// size 2^(w-2) filled with pre-computed values for
// P, 3P, 5P, ... Etc.
function generatePrecomputationTable(w, generatorPoint) {
/// <summary>Given a point P on an elliptic curve, return a table of
/// size 2^(w-2) filled with pre-computed values for
/// P, 3P, 5P, ... Etc.</summary>
/// <param name="w" type="Array">Window size</param>
/// <param name="generatorPoint" type="EllipticCurvePointFp"></param>
/// <returns type="Array">Precomputation table</returns>
var validationPoint = generatorPoint.clone();
convertToStandardForm(validationPoint);
if (!validatePoint(validationPoint)) {
throw new Error("Invalid Parameter");
}
// Create a Jacobian clone
var pointJac = generatorPoint.clone();
convertToJacobianForm(pointJac);
var tablePos = [generatorPoint.clone()];
// Q := P;
var qJac = pointJac.clone();
// Px2 = 2 * P
var px2 = pointJac.clone();
double(pointJac, px2);
convertToAffineForm(px2);
var qAff;
for (var i = 1; i < Math.pow(2, w - 2) ; i++) {
//Q := Q+P2;
mixedAdd(qJac, px2, qJac);
qAff = qJac.clone();
convertToAffineForm(qAff);
tablePos[i] = qAff;
}
return tablePos;
}
function double(point, outputPoint) {
/// <param name="point" type="EllipticCurvePointFp"/>
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
if (typeof point === "undefined") {
throw new Error("point undefined");
}
if (typeof outputPoint === "undefined") {
throw new Error("outputPoint undefined");
}
//// if (!point.curve.equals(outputPoint.curve)) {
//// throw new Error("point and outputPoint must be from the same curve object.");
//// }
if (point.isAffine) {
throw new Error("Given point was in Affine form. Use convertToJacobian() first.");
}
if (!point.isInMontgomeryForm) {
throw new Error("Given point must be in Montgomery form. Use montgomeryize() first.");
}
// Currently we support only two curve types, those with A=-3, and
// those with A=0. In the future we will implement general support.
// For now we switch here, assuming that the curve was validated in
// the constructor.
if (aequalsZero) {
doubleAequals0(point, outputPoint);
} else {
doubleAequalsNeg3(point, outputPoint);
}
}
function mixedDoubleAdd(jacobianPoint, affinePoint, outputPoint) {
/// <param name="jacobianPoint" type="EllipticCurvePointFp"/>
/// <param name="affinePoint" type="EllipticCurvePointFp"/>
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
if (jacobianPoint.isInfinity) {
affinePoint.copy(outputPoint);
this.convertToJacobianForm(outputPoint);
return;
}
if (affinePoint.isInfinity) {
jacobianPoint.copy(outputPoint);
return;
}
// Ok then we do the full double and add.
// Note: in pseudo-code the capital X,Y,Z is Jacobian point, lower
// case x, y, z is Affine point.
// 't5:=Z1^ 2;'
montgomerySquare(jacobianPoint.z, temp5);
// 't6:=Z1*t5;'
montgomeryMultiply(jacobianPoint.z, temp5, temp6);
// 't4:=x2*t5;'
montgomeryMultiply(affinePoint.x, temp5, temp4);
// 't5:=y2*t6;'
montgomeryMultiply(affinePoint.y, temp6, temp5);
// 't1:=t4-X1;'
modSub(temp4, jacobianPoint.x, temp1);
// 't2:=t5-Y1;'
modSub(temp5, jacobianPoint.y, temp2);
//if t1 eq 0 then
// if t2 eq 0 then
// X2,Y2,Z2 := DBL(X1,Y1,Z1,prime,rr,m,RR);
// return mADD(X2,Y2,Z2,x2,y2,prime,rr,m,RR);
// else
// return X1,Y1,Z1;
// end if;
//end if;
// 't4:=t2^2;'
montgomerySquare(temp2, temp4);
// 't6:=t1^2;'
montgomerySquare(temp1, temp6);
// 't5:=t6*X1;'
montgomeryMultiply(temp6, jacobianPoint.x, temp5);
// 't0:=t1*t6;'
montgomeryMultiply(temp1, temp6, temp0);
// 't3:=t4-2*t5;'
modSub(temp4, temp5, temp3);
modSub(temp3, temp5, temp3);
// 't4:=Z1*t1;'
montgomeryMultiply(jacobianPoint.z, temp1, temp4);
// 't3:=t3-t5;'
modSub(temp3, temp5, temp3);
// 't6:=t0*Y1;'
montgomeryMultiply(temp0, jacobianPoint.y, temp6);
// 't3:=t3-t0;'
modSub(temp3, temp0, temp3);
var temp3isZero = true;
for (var i = 0; i < temp3.length; i++) {
if (temp3[i] !== 0) {
temp3isZero = false;
break;
}
}
if (temp3isZero) {
for (i = 0; i < outputPoint.x.length; i++) {
outputPoint.x[i] = 0;
outputPoint.y[i] = 0;
outputPoint.z[i] = 0;
}
outputPoint.y[0] = 1;
return;
}
//if t3 eq 0 then
// return 0,1,0;
//end if;
// 't1:=2*t6;'
modAdd(temp6, temp6, temp1);
// 'Zout:=t4*t3;'
montgomeryMultiply(temp4, temp3, outputPoint.z);
// 't4:=t2*t3;'
montgomeryMultiply(temp2, temp3, temp4);
// 't0:=t3^2;'
montgomerySquare(temp3, temp0);
// 't1:=t1+t4;'
modAdd(temp1, temp4, temp1);
// 't4:=t0*t5;'
montgomeryMultiply(temp0, temp5, temp4);
// 't7:=t1^2;'
montgomerySquare(temp1, temp7);
// 't4:=t0*t5;'
montgomeryMultiply(temp0, temp3, temp5);
// 'Xout:=t7-2*t4;'
modSub(temp7, temp4, outputPoint.x);
modSub(outputPoint.x, temp4, outputPoint.x);
// 'Xout:=Xout-t5;'
modSub(outputPoint.x, temp5, outputPoint.x);
// 't3:=Xout-t4;'
modSub(outputPoint.x, temp4, temp3);
// 't0:=t5*t6;'
montgomeryMultiply(temp5, temp6, temp0);
// 't4:=t1*t3;'
montgomeryMultiply(temp1, temp3, temp4);
// 'Yout:=t4-t0;'
modSub(temp4, temp0, outputPoint.y);
outputPoint.isInfinity = false;
outputPoint.isInMontgomeryForm = true;
}
function mixedAdd(jacobianPoint, affinePoint, outputPoint) {
/// <param name="jacobianPoint" type="EllipticCurvePointFp"/>
/// <param name="affinePoint" type="EllipticCurvePointFp"/>
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
if (jacobianPoint === null) {
throw new Error("jacobianPoint");
}
if (affinePoint === null) {
throw new Error("affinePoint");
}
if (outputPoint === null) {
throw new Error("outputPoint");
}
if (jacobianPoint.curve !== affinePoint.curve ||
jacobianPoint.curve !== outputPoint.curve) {
throw new Error("All points must be from the same curve object.");
}
if (jacobianPoint.isAffine) {
throw new Error(
"Given jacobianPoint was in Affine form. Use ConvertToJacobian() before calling DoubleJacobianAddAffinePoints().");
}
if (!affinePoint.isAffine) {
throw new Error(
"Given affinePoint was in Jacobian form. Use ConvertToAffine() before calling DoubleJacobianAddAffinePoints().");
}
if (outputPoint.isAffine) {
throw new Error(
"Given jacobianPoint was in Jacobian form. Use ConvertToJacobian() before calling DoubleJacobianAddAffinePoints().");
}
if (!jacobianPoint.isInMontgomeryForm) {
throw new Error("Jacobian point must be in Montgomery form");
}
if (!affinePoint.isInMontgomeryForm) {
throw new Error("Affine point must be in Montgomery form");
}
if (jacobianPoint.isInfinity) {
affinePoint.copy(outputPoint);
this.convertToJacobianForm(outputPoint);
return;
}
if (affinePoint.isInfinity) {
jacobianPoint.copy(outputPoint);
return;
}
// Ok then we do the full double and add.
// Note: in pseudo-code the capital X1,Y1,Z1 is Jacobian point,
// lower case x2, y2, z2 is Affine point.
//if (X1 eq 0) and (Y1 eq 1) and (Z1 eq 0) then
// z2 := ToMontgomery(1,prime,rr,m,RR);
// return x2,y2;
//end if;
//if (x2 eq 0) and (y2 eq 1) then
// return X1,Y1,Z1;
//end if;
// 't1 := Z1^2;'.
montgomerySquare(jacobianPoint.z, temp1);
// 't2 := t1 * Z1;'
montgomeryMultiply(temp1, jacobianPoint.z, temp2);
// 't3 := t1 * x2;'
montgomeryMultiply(temp1, affinePoint.x, temp3);
// 't4 := t2 * y2;'
montgomeryMultiply(temp2, affinePoint.y, temp4);
// 't1 := t3 - X1;'
modSub(temp3, jacobianPoint.x, temp1);
// 't2 := t4 - Y1;'
modSub(temp4, jacobianPoint.y, temp2);
// If t1 != 0 then
var i;
for (i = 0; i < temp1.length; i += 1) {
if (temp1[i] !== 0) {
// 'Zout := Z1 * t1;'
montgomeryMultiply(jacobianPoint.z, temp1, temp0);
for (var j = 0; j < fieldElementWidth; j += 1) {
outputPoint.z[j] = temp0[j];
}
// 't3 := t1^2;'
montgomerySquare(temp1, temp3);
// 't4 := t3 * t1;'
montgomeryMultiply(temp3, temp1, temp4);
// 't5 := t3 * X1;'
montgomeryMultiply(temp3, jacobianPoint.x, temp5);
// 't1 := 2 * t5;'
modAdd(temp5, temp5, temp1);
// 'Xout := t2^2;'
montgomerySquare(temp2, outputPoint.x);
// 'Xout := Xout - t1;'
modSub(outputPoint.x, temp1, outputPoint.x);
// 'Xout := Xout - t4;'
modSub(outputPoint.x, temp4, outputPoint.x);
// 't3 := t5 - Xout;'
modSub(temp5, outputPoint.x, temp3);
// 't5 := t3*t2;'
montgomeryMultiply(temp2, temp3, temp5);
// 't6 := t4*Y1;'
montgomeryMultiply(jacobianPoint.y, temp4, temp6);
// 'Yout := t5-t6;'
modSub(temp5, temp6, outputPoint.y);
outputPoint.isInfinity = false;
outputPoint.isInMontgomeryForm = true;
return;
}
}
// Else if T2 != 0 then
for (i = 0; i < temp2.length; i += 1) {
if (temp2[i] !== 0) {
// Return infinity
outputPoint.isInfinity = true;
outputPoint.isInMontgomeryForm = true;
return;
}
}
// Else use DBL routine to return 2(x2, y2, 1)
affinePoint.copy(outputPoint);
this.convertToJacobianForm(outputPoint);
this.double(outputPoint, outputPoint);
outputPoint.isInMontgomeryForm = true;
}
function scalarMultiply(k, point, outputPoint, muliplyBy4) {
/// <param name="k" type="Digits"/>
/// <param name="point" type="EllipticCurvePointFp"/>
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
// Special case for the point at infinity or k == 0
if (point.isInfinity || cryptoMath.isZero(k)) {
outputPoint.isInfinity = true;
return;
}
// Runtime check for 1 <= k < order to ensure we don't get hit by
// subgroup attacks. Since k is a FixedWidth it is a positive integer
// and we already checked for zero above. So it must be >= 1 already.
if (cryptoMath.compareDigits(k, curve.order) >= 0) {
throw new Error("The scalar k must be in the range 1 <= k < order.");
}
// copy k so we can modify it without modifying the passed in array.
k = k.slice();
if (point.curve.type === 1) {
var pointIsEP = (typeof point.ta !== 'undefined');
if (!pointIsEP) {
convertToExtendedProjective(point);
}
scalarMultiplyTed(k, point, outputPoint, muliplyBy4);
// Convert the points back to standard if they arrived that way.
if (!pointIsEP) {
normalizeTed(point);
}
} else {
var pointIsMF = point.isInMontgomeryForm,
outputIsMF = outputPoint.isInMontgomeryForm,
outputIsAffine = outputPoint.isAffine;
// Convert parameters to Montgomory form if not already.
if (!pointIsMF) {
convertToMontgomeryForm(point);
}
if (!outputIsMF) {
convertToMontgomeryForm(outputPoint);
}
scalarMultiplyW(k, point, outputPoint);
// outputPoint returns as Jacobian - convert back to original state.
if (outputIsAffine) {
convertToAffineForm(outputPoint);
}
// Convert the points back to standard if they arrived that way.
if (!pointIsMF) {
convertToStandardForm(point);
}
if (!outputIsMF) {
convertToStandardForm(outputPoint);
}
}
return;
}
function scalarMultiplyW(k, point, outputPoint) {
/// <param name="k" type="Digits"/>
/// <param name="point" type="EllipticCurvePointFp"/>
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
// The point should be in Montgomery form.
var validationPoint = point.clone();
convertToStandardForm(validationPoint);
if (!validatePoint(validationPoint)) {
throw new Error("Invalid Parameters.");
}
var odd = (k[0] & 1),
tempk = [];
// If (odd) then k = temp else k = k
modSub(point.curve.order, k, tempk);
for (i = 0; i < k.length; i++) {
k[i] = ((odd - 1) & (k[i] ^ tempk[i])) ^ k[i];
}
// Change w based on the size of the digits,
// 5 is good for 256 bits, use 6 for bigger sizes.
var w = (fieldElementWidth <= 8) ? 5 : 6;
var m = point.curve.p.length * cryptoMath.DIGIT_BITS;
var t = Math.ceil(m / (w - 1));
var kDigits = cryptoMath.fixedWindowRecode(k, w, t);
var Tm = generatePrecomputationTable(w, point);
var position =
Math.floor(Math.abs(kDigits[t]) - 1) / 2;
var Q = Tm[position].clone();
convertToJacobianForm(Q);
for (var i = t - 1; i >= 0; i--) {
for (var j = 0; j < (w - 2) ; j++) {
double(Q, Q);
}
position = Math.floor((Math.abs(kDigits[i]) - 1) / 2);
var L = tableLookupW(Tm, position);
if (kDigits[i] < 0) {
negate(L, L);
}
mixedDoubleAdd(Q, L, Q);
}
// if k is even, negate Q
modSub(point.curve.p, Q.y, tempk);
for (i = 0; i < Q.y.length; i++) {
Q.y[i] = ((odd - 1) & (Q.y[i] ^ tempk[i])) ^ Q.y[i];
}
Q.copy(outputPoint);
return;
}
function tableLookupW(table, index) {
var pos = (index + 1) % table.length;
for (var i = 0; i < table.length; i++) {
var L = table[pos].clone();
pos = (pos + 1) % table.length;
}
return L;
}
function negate(point, outputPoint) {
/// <param name="point" type="EllipticCurvePointFp">Input point to negate.</param>
/// <param name="outputPoint" type="EllipticCurvePointFp">(x, p - y).</param>
if (point !== outputPoint) {
point.copy(outputPoint);
}
modSub(point.curve.p, point.y, outputPoint.y);
}
function convertToMontgomeryForm(point) {
/// <param name="point" type="EllipticCurvePointFp"/>
if (point.isInMontgomeryForm) {
throw new Error("The given point is already in Montgomery form.");
}
if (!point.isInfinity) {
montgomeryMultiplier.convertToMontgomeryForm(point.x);
montgomeryMultiplier.convertToMontgomeryForm(point.y);
if (point.z !== null) {
montgomeryMultiplier.convertToMontgomeryForm(point.z);
}
if (typeof point.ta !== 'undefined') {
montgomeryMultiplier.convertToMontgomeryForm(point.ta);
montgomeryMultiplier.convertToMontgomeryForm(point.tb);
}
}
point.isInMontgomeryForm = true;
}
function convertToStandardForm(point) {
/// <param name="point" type="EllipticCurvePointFp"/>
if (!point.isInMontgomeryForm) {
throw new Error("The given point is not in montgomery form.");
}
if (!point.isInfinity) {
montgomeryMultiplier.convertToStandardForm(point.x);
montgomeryMultiplier.convertToStandardForm(point.y);
if (point.z !== null) {
montgomeryMultiplier.convertToStandardForm(point.z);
}
if (typeof point.ta !== 'undefined') {
montgomeryMultiplier.convertToStandardForm(point.ta);
montgomeryMultiplier.convertToStandardForm(point.tb);
}
}
point.isInMontgomeryForm = false;
}
function convertToAffineForm(point) {
/// <param name="point" type="EllipticCurvePointFp"/>
if (point.isInfinity) {
point.z = null;
setterSupport || (point.isAffine = true);
return;
}
// DETERMINE 1/Z IN MONTGOMERY FORM --------------------------------
// Call out to the basic inversion function, not the one in this class.
cryptoMath.modInv(point.z, curve.p, conversionTemp2, true);
if (point.isInMontgomeryForm) {
montgomeryMultiply(conversionTemp2, montgomeryMultiplier.rCubedModm, conversionTemp1);
var swap = conversionTemp2;
conversionTemp2 = conversionTemp1;
conversionTemp1 = swap;
}
// CONVERT TO AFFINE COORDS ----------------------------------------
// 'temp0 <- 1/z^2'
montgomerySquare(conversionTemp2, conversionTemp0);
// Compute point.x = x / z^2 mod p
// NOTE: We cannot output directly to the X digit array since it is
// used for input to the multiplication routine, so we output to temp1
// and copy.
montgomeryMultiply(point.x, conversionTemp0, conversionTemp1);
for (var i = 0; i < fieldElementWidth; i += 1) {
point.x[i] = conversionTemp1[i];
}
// Compute point.y = y / z^3 mod p
// temp1 <- y * 1/z^2.
montgomeryMultiply(point.y, conversionTemp0, conversionTemp1);
// 'y <- temp1 * temp2 (which == 1/z)'
montgomeryMultiply(conversionTemp1, conversionTemp2, point.y);
// Finally, point.z = z / z mod p = 1
// We use z = NULL for this case to make detecting Jacobian form
// faster (otherwise we would have to scan the entire Z digit array).
point.z = null;
delete point.ta;
delete point.tb;
setterSupport || (point.isAffine = true);
}
function convertToJacobianForm(point) {
/// <param name="point" type="EllipticCurvePointFp"/>
if (!point.isAffine) {
throw new Error("The given point is not in Affine form.");
}
setterSupport || (point.isAffine = false);
var clonedDigits,
i,
zOne = point.isInMontgomeryForm ? onemontgomery : one;
clonedDigits = createArray(zOne.length);
for (i = 0; i < zOne.length; i += 1) {
clonedDigits[i] = zOne[i];
}
point.z = clonedDigits;
return;
}
function validatePoint(point) {
/// <summary>
/// Point validation
// Check if point P=(x,y) lies on the curve and if x,y are in [0, p-1]
/// </summary>
if (point.isInfinity) {
return false;
}
// Does P lie on the curve?
cryptoMath.modMul(point.y, point.y, point.curve.p, temp1)
cryptoMath.modMul(point.x, point.x, point.curve.p, temp2);
cryptoMath.modMul(point.x, temp2, point.curve.p, temp3);
modAdd(temp3, point.curve.b, temp2);
cryptoMath.modMul(point.x, point.curve.a, point.curve.p, temp3);
modAdd(temp2, temp3, temp2);
modSub(temp1, temp2, temp1);
if (cryptoMath.isZero(temp1) == false) {
return false;
}
return true;
}
/// Ted functions
function validatePointTed(point) {
if (point.ta) {
point = point.clone();
normalizeTed(point);
}
// Does P lie on the curve?
cryptoMath.modMul(point.y, point.y, point.curve.p, temp3);
cryptoMath.modMul(point.x, point.x, point.curve.p, temp2);
cryptoMath.add(temp2, temp3, temp1);
cryptoMath.reduce(temp4, point.curve.p, temp4);
cryptoMath.modMul(temp2, temp3, point.curve.p, temp4);
cryptoMath.modMul(point.curve.d, temp4, point.curve.p, temp3);
cryptoMath.add(temp3, [1], temp2);
cryptoMath.reduce(temp2, point.curve.p, temp2);
cryptoMath.subtract(temp1, temp2, temp1);
cryptoMath.reduce(temp1, point.curve.p, temp1);
if (cryptoMath.isZero(temp1) == false) {
return false;
}
return true;
}
function generatePrecomputationTableTed(npoints, point) {
// Precomputation function, points are stored using representation (X,Y,Z,dT)
// Twisted Edwards a=1 curve
var Q = point.clone(),
P2 = Q.clone(),
T = [];
// Generating P2 = 2(X1,Y1,Z1,T1a,T1b) -> (XP2,YP2,ZP2,d*TP2) and T[0] = P = (X1,Y1,Z1,T1a,T1b)
T[0] = convert_R1_to_R2(point);
doubleTed(Q, Q);
P2 = convert_R1_to_R2(Q);
Q = point.clone();
for (var i = 1; i < npoints; i++) {
// T[i] = 2P+T[i-1] = (2*i+1)P = (XP2,Y2P,ZP2,d*TP2) + (X_(2*i-1), Y_(2*i-1), Z_(2*i-1), Ta_(2*i-1), Tb_(2*i-1)) = (X_(2*i+1), Y_(2*i+1), Z_(2*i+1), d*T_(2*i+1))
addTedExtended(P2, Q, Q);
T[i] = convert_R1_to_R2(Q);
}
return T;
}
function convertToExtendedProjective(affinePoint) {
affinePoint.ta = affinePoint.x.slice();
affinePoint.tb = affinePoint.y.slice();
affinePoint.z = [1];
}
function scalarMultiplyTed(k, point, outputPoint, multiplyBy4) {
if (!validatePointTed(point)) {
throw new Error("Invalid Parameter");
}
var rbits = point.curve.rbits;
multiplyBy4 = typeof multiplyBy4 === 'undefined' ? true : multiplyBy4;
var w = (fieldElementWidth <= 8) ? 5 : 6;
var t = Math.floor((rbits + (w - 2)) / (w - 1));
var i, j;
// copy k so we can modify it without modifying the passed in array.
k = k.slice();
var T = point.clone();
convertToExtendedProjective(T);
if (multiplyBy4) {
doubleTed(T, T);
doubleTed(T, T);
}
var precomputationTable = generatePrecomputationTableTed(1 << (w - 2), T);
var odd = (k[0] & 1),
tempk = [];
// If (odd) then k = temp else k = k
modSub(point.curve.order, k, tempk);
for (i = 0; i < k.length; i++) {
k[i] = ((odd - 1) & (k[i] ^ tempk[i])) ^ k[i];
}
var kDigits = cryptoMath.fixedWindowRecode(k, w, t);
var position =
Math.floor(Math.abs(kDigits[t]) - 1) / 2;
var R = precomputationTable[position];
T.x = R.x.slice();
T.y = R.y.slice();
T.z = R.z.slice();
for (i = t - 1; i >= 0; i--) {
for (j = 0; j < (w - 1) ; j++) {
doubleTed(T, T);
}
position = Math.floor((Math.abs(kDigits[i]) - 1) / 2);
var L = tableLookupTed(precomputationTable, position);
if (kDigits[i] < 0) {
modSub(point.curve.p, L.x, L.x);
modSub(point.curve.p, L.td, L.td);
}
addTedExtended(L, T, T);
}
// If (odd) then T.x = temp else T.x = T.x
modSub(point.curve.p, T.x, tempk);
for (i = 0; i < T.x.length; i++) {
T.x[i] = ((odd - 1) & (T.x[i] ^ tempk[i])) ^ T.x[i];
}
normalizeTed(T);
outputPoint.x = T.x.slice();
outputPoint.y = T.y.slice();
return;
}
function tableLookupTed(table, index) {
var pos = (index + 1) % table.length;
for (var i = 0; i < table.length; i++) {
var L = {
x: table[pos].x.slice(),
y: table[pos].y.slice(),
z: table[pos].z.slice(),
td: table[pos].td.slice()
}
pos = (pos + 1) % table.length;
}
return L;
}
function normalizeTed(point) {
cryptoMath.modInv(point.z, curve.p, conversionTemp2, true);
cryptoMath.modMul(point.x, conversionTemp2, curve.p, point.x);
cryptoMath.modMul(point.y, conversionTemp2, curve.p, point.y);
delete point.ta;
delete point.tb;
point.z = null;
return;
}
function doubleTed(point, outputPoint) {
if (typeof point.ta === 'undefined') {
throw new Error("Point should be in Extended Projective form.");
}
// t0 = x1^2
cryptoMath.modMul(point.x, point.x, point.curve.p, temp0);
// t1 = y1^2
cryptoMath.modMul(point.y, point.y, point.curve.p, temp1);
// Ta = z1^2
cryptoMath.modMul(point.z, point.z, point.curve.p, point.ta);
// (new) Tbfinal = Y1^2-X1^2
modSub(temp1, temp0, outputPoint.tb);
//(new) t0 = X1^2+Y1^2
modAdd(temp0, temp1, temp0);
//(ok) Ta = 2z1^2
modAdd(point.ta, point.ta, point.ta);
// (ok) y = 2y1
modAdd(point.y, point.y, point.y);
// (new) t1 = 2z1^2-(X1^2+Y1^2)
modSub(point.ta, temp0, temp1);
// Tafinal = 2x1y1
cryptoMath.modMul(point.x, point.y, point.curve.p, outputPoint.ta);
// Yfinal = (x1^2+y1^2)(y1^2-x1^2)
cryptoMath.modMul(temp0, outputPoint.tb, point.curve.p, outputPoint.y);
// Xfinal = 2x1y1[2z1^2-(y1^2-x1^2)]
cryptoMath.modMul(temp1, outputPoint.ta, point.curve.p, outputPoint.x);
// Zfinal = (y1^2-x1^2)[2z1^2-(y1^2-x1^2)]
cryptoMath.modMul(temp0, temp1, point.curve.p, outputPoint.z);
return;
}
function addTed(point1 /*Q*/, point2 /*P*/, outputPoint) {
var cm = cryptoMath;
var modulus = point1.curve.p;
var temp1 = [];
if (typeof point1.ta === 'undefined') {
throw new Error("Point1 should be in Extended Projective form.");
}
if (typeof point2.ta === 'undefined') {
throw new Error("Point2 should be in Extended Projective form.");
}
var qq = convert_R1_to_R2(point1);
addTedExtended(qq, point2, outputPoint);
return;
}
function convert_R1_to_R2(point) {
var curve = point.curve,
modulus = curve.p,
qq = {
x: point.x.slice(),
y: point.y.slice(),
z: point.z.slice(),
td: [],
curve: point.curve
};
cryptoMath.modMul(point.ta, point.tb, modulus, conversionTemp0);
cryptoMath.modMul(conversionTemp0, curve.d, modulus, qq.td);
return qq;
}
function addTedExtended(qq /*Q*/, point2 /*P*/, outputPoint) {
// Complete point addition P = P+Q, including the cases P!=Q, P=Q, P=-Q, P=neutral and Q=neutral
// Twisted Edwards a=1 curve
// Inputs: P = (X1,Y1,Z1,Ta,Tb), where T1 = Ta*Tb, corresponding to extended twisted Edwards coordinates (X1:Y1:Z1:T1)
// Q = (X2,Y2,Z2,dT2), corresponding to extended twisted Edwards coordinates (X2:Y2:Z2:T2)
// Output: P = (X1,Y1,Z1,Ta,Tb), where T1 = Ta*Tb, corresponding to extended twisted Edwards coordinates (X1:Y1:Z1:T1)
var cm = cryptoMath;
var modulus = point2.curve.p;
temp1 = []; temp2 = []; temp3 = [];
//FP_MUL(P->Z, Q->Z, t3); // t3 = Z1*Z2
cm.modMul(point2.z, qq.z, modulus, temp3);
//FP_MUL(P->Ta, P->Tb, t1); // t1 = T1
cm.modMul(point2.ta, point2.tb, modulus, temp1);
//FP_ADD(P->X, P->Y, P->Ta); // Ta = (X1+Y1)
modAdd(point2.x, point2.y, point2.ta);
//FP_MUL(t1, Q->Td, t2); // t2 = dT1*T2
cm.modMul(temp1, qq.td, modulus, temp2);
//FP_ADD(Q->X, Q->Y, P->Tb); // Tb = (X2+Y2)
modAdd(qq.x, qq.y, point2.tb);
//FP_SUB(t3, t2, t1); // t1 = theta
modSub(temp3, temp2, temp1);
//FP_ADD(t3, t2, t3); // t3 = alpha
modAdd(temp3, temp2, temp3);
//FP_MUL(P->Ta, P->Tb, t2); // t2 = (X1+Y1)(X2+Y2)
cm.modMul(point2.ta, point2.tb, modulus, temp2);
//FP_MUL(P->X, Q->X, P->Z); // Z = X1*X2
cm.modMul(point2.x, qq.x, modulus, point2.z);
//FP_MUL(P->Y, Q->Y, P->X); // X = Y1*Y2
cm.modMul(point2.y, qq.y, modulus, point2.x);
//FP_SUB(t2, P->Z, t2);
modSub(temp2, point2.z, temp2);
//FP_SUB(P->X, P->Z, P->Ta); // Tafinal = omega = Y1*Y2-X1*X2
modSub(point2.x, point2.z, outputPoint.ta);
//FP_SUB(t2, P->X, P->Tb); // Tbfinal = beta = (X1+Y1)(X2+Y2)-X1*X2-Y1*Y2
modSub(temp2, point2.x, outputPoint.tb);
//FP_MUL(P->Ta, t3, P->Y); // Yfinal = alpha*omega
cm.modMul(outputPoint.ta, temp3, modulus, outputPoint.y);
//FP_MUL(P->Tb, t1, P->X); // Xfinal = beta*theta
cm.modMul(outputPoint.tb, temp1, modulus, outputPoint.x);
//FP_MUL(t3, t1, P->Z); // Zfinal = theta*alpha
cm.modMul(temp3, temp1, modulus, outputPoint.z);
return;
}
function convertTedToWeierstrass(tedPoint, wPoint) {
/// <summary></summary>
/// <param name="tedPoint" type=""></param>
/// <param name="outputPoint" type=""></param>
var a = tedPoint.curve.a.slice(),
d = tedPoint.curve.d.slice(),
p = tedPoint.curve.p,
modMul = cryptoMath.modMul,
modInv = cryptoMath.modInv;
// t1 = 5
temp1 = [5];
// t2 = 5a
modMul(a, temp1, p, temp2);
// t2 = 5a-d
modSub(temp2, d, temp2);
// t3 = 5d
modMul(d, temp1, p, temp3);
// t1 = a-5d
modSub(a, temp3, temp1);
// t3 = yTE*(a-5d)
modMul(tedPoint.y, temp1, p, temp3);
// t2 = (5a-d) + yTE*(a-5d)
modAdd(temp3, temp2, temp2);
// t1 = 1
temp1 = [1];
// t3 = 1-yTE
modSub(temp1, tedPoint.y, temp3);
// t1 = 12
temp1 = [12];
// t4 = 12(1-yTE)
modMul(temp1, temp3, p, temp4);
// t4 = 1/12(1-yTE)
modInv(temp4, p, temp4, true);
// t1 = xTE*(1-yTE)
modMul(tedPoint.x, temp3, p, temp1);
// t3 = 2xTE*(1-yTE)
modAdd(temp1, temp1, temp3);
// t3 = 4xTE*(1-yTE)
modAdd(temp3, temp3, temp3);
// t3 = 1/4xTE*(1-yTE)
modInv(temp3, p, temp3, true);
// Xfinal = ((5a-d) + yTE*(a-5d))/12(1-yTE)
modMul(temp4, temp2, p, wPoint.x);
// t1 = 1
temp1 = [1];
// t1 = yTE+1
modAdd(tedPoint.y, temp1, temp1);
// t2 = a-d
modSub(a, d, temp2);
// t4 = (a-d)*(yTE+1)
modMul(temp1, temp2, p, temp4);
// Yfinal = ((a-d)*(yTE+1))/4xTE*(1-yTE)
modMul(temp4, temp3, p, wPoint.y);
return;
}
function convertWeierstrassToTed(wPoint, tedPoint) {
var a = tedPoint.curve.a.slice(),
d = tedPoint.curve.d.slice(),
p = tedPoint.curve.p,
modMul = cryptoMath.modMul,
modInv = cryptoMath.modInv;
modAdd(wPoint.x, wPoint.x, temp1);
modAdd(wPoint.x, temp1, temp1);
// t1 = 6xW
modAdd(temp1, temp1, temp1);
// t2 = 6xW - a
modSub(temp1, a, temp2);
// t2 = 6xW - a - d
modSub(temp2, d, temp2);
modAdd(wPoint.y, wPoint.y, temp3);
modAdd(wPoint.y, temp3, temp3);
// t3 = 6yW
modAdd(temp3, temp3, temp3);
// t3 = 1/6yW
modInv(temp3, p, temp3, true);
// Xfinal = (6xW - a - d)/6yW
modMul(temp2, temp3, p, tedPoint.x);
// t1 = 12xW
modAdd(temp1, temp1, temp1);
// t2 = 12xW + d
modAdd(temp1, d, temp2);
// t1 = 12xW + a
modAdd(temp1, a, temp1);
modAdd(a, a, temp3);
// t2 = 12xW + d - 2a
modSub(temp2, temp3, temp2);
// t2 = 12xW + d - 4a
modSub(temp2, temp3, temp2);
// t2 = 12xW + d - 5a
modSub(temp2, a, temp2);
modAdd(d, d, temp3);
// t1 = 12xW + a - 2d
modSub(temp1, temp3, temp1);
// t1 = 12xW + a - 4d
modSub(temp1, temp3, temp1);
// t1 = 12xW + a - 5d
modSub(temp1, d, temp1);
// t1 = 1/(12xW + a - 5d)
modInv(temp1, p, temp1, true);
// Yfinal = (12xW + d - 5a)/(12xW + a - 5d)
modMul(temp1, temp2, p, tedPoint.y);
return;
}
var methods = {
convertToMontgomeryForm: convertToMontgomeryForm,
convertToStandardForm: convertToStandardForm,
convertToAffineForm: convertToAffineForm,
convertToJacobianForm: convertToJacobianForm,
// For tests
generatePrecomputationTable: function (w, generatorPoint) {
/// <param name="w" type="Number"/>
/// <param name="generatorPoint" type="EllipticCurvePointFp"/>
return generatePrecomputationTable(w, generatorPoint);
}
};
if (tedCurve) {
methods.double = doubleTed;
methods.add = addTed;
methods.scalarMultiply = scalarMultiply;
methods.normalize = normalizeTed;
methods.convertToExtendedProjective = convertToExtendedProjective;
methods.convertTedToWeierstrass = convertTedToWeierstrass;
methods.convertWeierstrassToTed = convertWeierstrassToTed;
methods.generatePrecomputationTable = function (w, generatorPoint) {
/// <param name="w" type="Number"/>
/// <param name="generatorPoint" type="EllipticCurvePointFp"/>
return generatePrecomputationTableTed(w, generatorPoint);
};
} else {
methods.double = double;
methods.mixedDoubleAdd = mixedDoubleAdd;
methods.mixedAdd = mixedAdd;
methods.scalarMultiply = scalarMultiply;
methods.negate = negate;
}
return methods;
};
var sec1EncodingFp = function () {
return {
encodePoint: function ( point) {
/// <summary>Encode an EC point without compression.
/// This function encodes a given points into a bytes array containing 0x04 | X | Y, where X and Y are big endian bytes of x and y coordinates.</summary>
/// <param name="point" type="EllipticCurvePointFp">Input EC point to encode.</param>
/// <returns type="Array">A bytes array containing 0x04 | X | Y, where X and Y are big endian encoded x and y coordinates.</returns>
if (!point) {
throw new Error("point");
}
if (!point.isAffine) {
throw new Error("Point must be in affine form.");
}
if (point.isInMontgomeryForm) {
throw new Error("Point must not be in Montgomery form.");
}
if (point.isInfinity) {
return createArray(1); /* [0] */
} else {
var xOctetString = cryptoMath.digitsToBytes(point.x);
var yOctetString = cryptoMath.digitsToBytes(point.y);
var pOctetString = cryptoMath.digitsToBytes(point.curve.p); // just to get byte length of p
var mlen = pOctetString.length;
if (mlen < xOctetString.length || mlen < yOctetString.length) {
throw new Error("Point coordinate(s) are bigger than the field order.");
}
var output = createArray(2 * mlen + 1); // for encoded x and y
output[0] = 0x04;
var offset = mlen - xOctetString.length;
for (var i = 0; i < xOctetString.length; i++) {
output[i + 1 + offset] = xOctetString[i];
}
offset = mlen - yOctetString.length;
for (i = 0; i < yOctetString.length; i++) {
output[mlen + i + 1 + offset] = yOctetString[i];
}
return output;
}
},
decodePoint: function (encoded, curve) {
/// <param name="encoded" type="Digits"/>
/// <param name="curve" type="EllipticCurveFp"/>
if (encoded.length < 1) {
throw new Error("Byte array must have non-zero length");
}
var pOctetString = cryptoMath.digitsToBytes(curve.p);
var mlen = pOctetString.length;
if (encoded[0] === 0x0 && encoded.length === 1) {
return curve.createPointAtInfinity();
} else if (encoded[0] === 0x04 && encoded.length === 1 + 2 * mlen) {
// Standard encoding.
// Each point is a big endian string of bytes of length.
// 'ceiling(log_2(Q)/8)'
// Zero-padded and representing the magnitude of the coordinate.
var xbytes = createArray(mlen);
var ybytes = createArray(mlen);
for (var i = 0; i < mlen; i++) {
xbytes[i] = encoded[i + 1];
ybytes[i] = encoded[mlen + i + 1];
}
var x = cryptoMath.bytesToDigits(xbytes);
var y = cryptoMath.bytesToDigits(ybytes);
return EllipticCurvePointFp(curve, false, x, y);
} else {
// We don't support other encoding features such as compression
throw new Error("Unsupported encoding format");
}
}
};
};
var ModularSquareRootSolver = function (modulus) {
/// <param name="modulus" type="Digits"/>
// The modulus we are going to use.
var p = modulus;
// Special-K not just for breakfast anymore! This is k = (p-3)/4 + 1
// which is used for NIST curves (or any curve of with P= 3 mod 4).
// This field is null if p is not of the special form, or k if it is.
var specialK = [];
if (typeof modulus === "undefined") {
throw new Error("modulus");
}
// Support for odd moduli, only.
if (cryptoMath.isEven(modulus)) {
throw new Error("Only odd moduli are supported");
}
// A montgomery multiplier object for doing fast squaring.
var mul = cryptoMath.MontgomeryMultiplier(p);
// 'p === 3 mod 4' then we can use the special super fast version.
// Otherwise we must use the slower general case algorithm.
if (p[0] % 4 === 3) {
// 'special k = (p + 1) / 4'
cryptoMath.add(p, cryptoMath.One, specialK);
cryptoMath.shiftRight(specialK, specialK, 2);
} else {
specialK = null;
}
// Temp storage
var temp0 = new Array(p.length);
var temp1 = new Array(p.length);
function squareRootNistCurves(a) {
/// <summary>Given a number a, returns a solution x to x^2 = a (mod p).</summary>
/// <param name="a" type="Array">An integer a.</param>
/// <returns type="Array">The square root of the number a modulo p, if it exists,
/// otherwise null.</returns>
// beta = a^k mod n where k=(n+1)/4 for n == 3 mod 4, thus a^(1/2) mod n
var beta = cryptoMath.intToDigits(0, 16);
mul.modExp(a, specialK, beta);
// Okay now we gotta double check by squaring.
var aPrime = [0];
cryptoMath.modMul(beta, beta, mul.m, aPrime);
// If a != x^2 then a has no square root
if (cryptoMath.compareDigits(a, aPrime) !== 0) {
return null;
}
return beta;
}
var publicMethods = {
squareRoot: function (a) {
if (specialK !== null) {
// Use the special case fast code
return squareRootNistCurves(a);
} else {
// Use the general case code
throw new Error("GeneralCase not supported.");
}
},
// Given an integer a, this routine returns the Jacobi symbol (a/p),
// where p is the modulus given in the constructor, which for p an
// odd prime is also the Legendre symbol. From "Prime Numbers, A
// Computational Perspective" by Crandall and Pomerance, alg. 2.3.5.
// The Legendre symbol is defined as:
// 0 if a === 0 mod p.
// 1 if a is a quadratic residue (mod p).
// -1 if a is a quadratic non-reside (mod p).
jacobiSymbol: function (a) {
/// <param name="a">An integer a.</param>
var modEightMask = 0x7,
modFourMask = 0x3,
aPrime,
pPrime;
// Clone our inputs, we are going to destroy them
aPrime = a.slice();
pPrime = p.slice();
// 'a = a mod p'.
cryptoMath.reduce(aPrime, pPrime, aPrime, temp0, temp1);
// 't = 1'
var t = 1;
// While (a != 0)
while (!cryptoMath.isZero(aPrime)) {
// While a is even
while (cryptoMath.isEven(aPrime)) {
// 'a <- a / 2'
cryptoMath.shiftRight(aPrime, aPrime);
// If (p mod 8 in {3,5}) t = -t;
var pMod8 = (pPrime[0] & modEightMask);
if (pMod8 === 3 || pMod8 === 5) {
t = -t;
}
}
// Swap variables
// (a, p) = (p, a).
var tmp = aPrime;
aPrime = pPrime;
pPrime = tmp;
// If (a === p === 3 (mod 4)) t = -t;
var aMod4 = (aPrime[0] & modFourMask);
var pMod4 = (pPrime[0] & modFourMask);
if (aMod4 === 3 && pMod4 === 3) {
t = -t;
}
// 'a = a mod p'
cryptoMath.reduce(aPrime, pPrime, aPrime, temp0, temp1);
}
// If (p == 1) return t else return 0
if (cryptoMath.compareDigits(pPrime, cryptoMath.One) === 0) {
return t;
} else {
return 0;
}
}
};
return publicMethods;
};
var curvesInternal = {};
var createCurve = function (curveName) {
var curveData = curvesInternal[curveName.toUpperCase()];
if (!curveData) {
throw new Error(curveName + " Unsupported curve.");
}
if (curveData.type === 0) {
return createWeierstrassCurve(curveData);
}
if (curveData.type === 1) {
return createTedCurve(curveData);
}
throw new Error(curveName + " Unsupported curve type.");
};
return {
createCurve: createCurve,
curves: curvesInternal,
sec1EncodingFp: sec1EncodingFp,
EllipticCurvePointFp: EllipticCurvePointFp,
EllipticCurveOperatorFp: EllipticCurveOperatorFp,
ModularSquareRootSolver: ModularSquareRootSolver
};
}
var cryptoECC = cryptoECC || MsrcryptoECC();
var curve_P256 = {
name: "P-256",
type: 0, // Curve Type 0 = Weierstrass, 1 Twisted Edwards
p: [0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
a: [0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC],
b: [0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3, 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B],
order: [0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51],
gx: [0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96],
gy: [0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5],
cf: 1 // co-factor
};
var curve_P384 = {
name: "P-384",
type: 0, // Curve Type 0 = Weierstrass, 1 Twisted Edwards
p: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF],
a: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFC],
b: [0xB3, 0x31, 0x2F, 0xA7, 0xE2, 0x3E, 0xE7, 0xE4, 0x98, 0x8E, 0x05, 0x6B, 0xE3, 0xF8, 0x2D, 0x19, 0x18, 0x1D, 0x9C, 0x6E, 0xFE, 0x81, 0x41, 0x12, 0x03, 0x14, 0x08, 0x8F, 0x50, 0x13, 0x87, 0x5A, 0xC6, 0x56, 0x39, 0x8D, 0x8A, 0x2E, 0xD1, 0x9D, 0x2A, 0x85, 0xC8, 0xED, 0xD3, 0xEC, 0x2A, 0xEF],
order: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF, 0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A, 0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73],
gx: [0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, 0x37, 0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD, 0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, 0x98, 0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, 0x38, 0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29, 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, 0xB7],
gy: [0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, 0x6F, 0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC, 0x29, 0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, 0x7C, 0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, 0xC0, 0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81, 0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, 0x5F],
cf: 1 // co-factor
};
var curve_P521 = {
name: "P-521",
type: 0, // Curve Type 0 = Weierstrass, 1 Twisted Edwards
p: [0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
a: [0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC],
b: [0x00, 0x51, 0x95, 0x3E, 0xB9, 0x61, 0x8E, 0x1C, 0x9A, 0x1F, 0x92, 0x9A, 0x21, 0xA0, 0xB6, 0x85, 0x40, 0xEE, 0xA2, 0xDA, 0x72, 0x5B, 0x99, 0xB3, 0x15, 0xF3, 0xB8, 0xB4, 0x89, 0x91, 0x8E, 0xF1, 0x09, 0xE1, 0x56, 0x19, 0x39, 0x51, 0xEC, 0x7E, 0x93, 0x7B, 0x16, 0x52, 0xC0, 0xBD, 0x3B, 0xB1, 0xBF, 0x07, 0x35, 0x73, 0xDF, 0x88, 0x3D, 0x2C, 0x34, 0xF1, 0xEF, 0x45, 0x1F, 0xD4, 0x6B, 0x50, 0x3F, 0x00],
order: [0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x51, 0x86, 0x87, 0x83, 0xBF, 0x2F, 0x96, 0x6B, 0x7F, 0xCC, 0x01, 0x48, 0xF7, 0x09, 0xA5, 0xD0, 0x3B, 0xB5, 0xC9, 0xB8, 0x89, 0x9C, 0x47, 0xAE, 0xBB, 0x6F, 0xB7, 0x1E, 0x91, 0x38, 0x64, 0x09],
gx: [0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04, 0x04, 0xE9, 0xCD, 0x9E, 0x3E, 0xCB, 0x66, 0x23, 0x95, 0xB4, 0x42, 0x9C, 0x64, 0x81, 0x39, 0x05, 0x3F, 0xB5, 0x21, 0xF8, 0x28, 0xAF, 0x60, 0x6B, 0x4D, 0x3D, 0xBA, 0xA1, 0x4B, 0x5E, 0x77, 0xEF, 0xE7, 0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2, 0xFF, 0xA8, 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85, 0x6A, 0x42, 0x9B, 0xF9, 0x7E, 0x7E, 0x31, 0xC2, 0xE5, 0xBD, 0x66],
gy: [0x01, 0x18, 0x39, 0x29, 0x6A, 0x78, 0x9A, 0x3B, 0xC0, 0x04, 0x5C, 0x8A, 0x5F, 0xB4, 0x2C, 0x7D, 0x1B, 0xD9, 0x98, 0xF5, 0x44, 0x49, 0x57, 0x9B, 0x44, 0x68, 0x17, 0xAF, 0xBD, 0x17, 0x27, 0x3E, 0x66, 0x2C, 0x97, 0xEE, 0x72, 0x99, 0x5E, 0xF4, 0x26, 0x40, 0xC5, 0x50, 0xB9, 0x01, 0x3F, 0xAD, 0x07, 0x61, 0x35, 0x3C, 0x70, 0x86, 0xA2, 0x72, 0xC2, 0x40, 0x88, 0xBE, 0x94, 0x76, 0x9F, 0xD1, 0x66, 0x50],
cf: 1 // co-factor
};
if (typeof cryptoECC !== 'undefined') {
// Add curves to ECC object
cryptoECC.curves["P-256"] = curve_P256;
cryptoECC.curves["P-384"] = curve_P384;
cryptoECC.curves["P-521"] = curve_P521;
}
var curve_BN254 = {
name: "BN-254",
type: 0, // Curve Type 0 = Weierstrass, 1 Twisted Edwards
p: [0x25, 0x23, 0x64, 0x82, 0x40, 0x00, 0x00, 0x01, 0xBA, 0x34, 0x4D, 0x80, 0x00, 0x00, 0x00, 0x08, 0x61, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13],
a: [0x00],
b: [0x02],
order: [0x25, 0x23, 0x64, 0x82, 0x40, 0x00, 0x00, 0x01, 0xBA, 0x34, 0x4D, 0x80, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x9F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x10, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D],
gx: [0x25, 0x23, 0x64, 0x82, 0x40, 0x00, 0x00, 0x01, 0xBA, 0x34, 0x4D, 0x80, 0x00, 0x00, 0x00, 0x08, 0x61, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12],
gy: [0x01],
cf: 1 // co-factor
};
if (typeof cryptoECC !== 'undefined') {
// Add curves to ECC object
cryptoECC.curves["BN-254"] = curve_BN254;
}
//
// "numsp256d1": Weierstrass curve a=-3, E: y^2 = x^3 - 3x + 152961, p = 2^256-189
//
var curve_numsp256d1 = {
// Curve ID, 2 x targeted security level, order bitlength, prime bitlength
info: ['numsp256d1', 256, 256, 256],
// Curve Type 0 = Weierstrass, 1 Twisted Edwards
type: 0,
// Prime p = 2^256-189
p: [0x43, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].reverse(),
// Parameter "a"
a: [0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].reverse(),
// Parameter "b"
b: [0x81, 0x55, 0x02].reverse(),
// Order of the group
order: [0x25, 0xa8, 0x51, 0x47, 0x29, 0x20, 0xab, 0x20, 0x60, 0x5c, 0x26, 0xea, 0x75, 0x82, 0x3c, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff].reverse(),
// x(generator)
gx: [0xB1, 0xAC, 0x1A, 0xB2, 0x1E, 0xEE, 0x52, 0xBC, 0x3A, 0xC7, 0xD4, 0x03, 0x09, 0x9B, 0x57, 0x83, 0x09, 0xCB, 0x42, 0x4F, 0xA0, 0x95, 0x7A, 0x29, 0x61, 0xDB, 0xAA, 0x5A, 0xB6, 0xD6, 0x9E, 0xBC].reverse(),
// y(generator)
gy: [0x9F, 0xDE, 0x84, 0x21, 0xCB, 0xB9, 0xB5, 0x80, 0xBB, 0x0F, 0x31, 0x15, 0xD1, 0xC3, 0x55, 0xC9, 0x35, 0xE0, 0x04, 0x7E, 0xF7, 0x8B, 0x44, 0x73, 0xA6, 0xB6, 0x99, 0x33, 0xF1, 0xC0, 0x8F, 0xD0].reverse(),
// co-factor
cf: 1
};
//
// "numsp256t1": twisted Edwards curve a=1, E: x^2 + y^2 = 1 - 15342x^2y^2, p = 2^256-189
//
var curve_numsp256t1 = {
// Curve ID, 2 x targeted security level, order bitlength, prime bitlength
info: ['numsp256t1', 256, 255, 256],
// Name
name: "numsp256t1",
// Curve Type 0 = Weierstrass, 1 Twisted Edwards
type: 1,
// Prime p = 2^256-189
p: [0x43, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].reverse(),
// Parameter "a"
a: [0x01],
// Parameter "d"
d: [0x55, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].reverse(),
// Order of the subgroup
order: [0xF5, 0x4A, 0xDD, 0xEE, 0x90, 0xB1, 0x47, 0x1A, 0x9B, 0x43, 0x59, 0x2F, 0xA5, 0x5A, 0x95, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40].reverse(),
// x(generator)
gx: [0xDA, 0x13, 0xED, 0x2E, 0x90, 0xC0, 0xDE, 0xA0, 0x86, 0x35, 0x08, 0xE3, 0x0E, 0x8A, 0x39, 0x0C, 0xD6, 0x9B, 0x20, 0x69, 0x5F, 0x3D, 0x1E, 0xCD, 0x7D, 0x23, 0xEA, 0x6A, 0xFB, 0x14, 0x75, 0x8A].reverse(),
// y(generator)
gy: [0xE6, 0x89, 0x8A, 0x79, 0xE7, 0x16, 0xA6, 0x2F, 0xD3, 0x6E, 0x85, 0x10, 0xD8, 0x61, 0x5F, 0x71, 0x10, 0x80, 0x4B, 0xA6, 0xD9, 0x65, 0x96, 0xCE, 0xC7, 0x25, 0xD9, 0xD9, 0x9F, 0x3E, 0xD5, 0x44].reverse(),
// co-factor
cf: 4
};
//
// "numsp384d1": Weierstrass curve a=-3, E: y^2 = x^3 - 3x - 34568, p = 2^384-317
//
var curve_numsp384d1 = {
// Curve ID, 2 x targeted security level, order bitlength, prime bitlength
info: ['numsp384d1', 384, 384, 384],
// Name
name: "numsp384d1",
// Curve Type 0 = Weierstrass, 1 Twisted Edwards
type: 0,
// Prime p = 2^384-317
p: [0xC3, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].reverse(),
// Parameter "a"
a: [0xC0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].reverse(),
// Parameter "b"
b: [0xBB, 0x77, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].reverse(),
// Order of the group
order: [0xb9, 0x61, 0x0e, 0x7b, 0xf6, 0x81, 0x4d, 0x60, 0x7a, 0xe2, 0x37, 0x4c, 0x3d, 0x9d, 0xda, 0xbe, 0x81, 0x68, 0x5d, 0xeb, 0x1e, 0xaf, 0x1e, 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff].reverse(),
// x(generator)
gx: [0x2A, 0x15, 0x98, 0x20, 0x04, 0xBA, 0x9C, 0xEB, 0x7B, 0xC4, 0x61, 0x0F, 0x10, 0xED, 0x2E, 0x52, 0x42, 0xC7, 0x6C, 0x2A, 0x1B, 0x29, 0xBD, 0xF3, 0xF4, 0xF9, 0x81, 0xFB, 0xCD, 0xC1, 0x25, 0x02,
0xA6, 0xF1, 0x05, 0x41, 0x22, 0xCA, 0x80, 0x48, 0x1C, 0x18, 0x6F, 0xB1, 0xF0, 0x56, 0x79, 0x75].reverse(),
// y(generator)
gy: [0x16, 0x07, 0x18, 0x66, 0xEC, 0xB8, 0x74, 0x5C, 0x26, 0xAD, 0xF4, 0xBF, 0xDB, 0xB4, 0xD6, 0xBC, 0x7E, 0x83, 0x1A, 0x12, 0x7D, 0x83, 0x20, 0xB9, 0x9C, 0x73, 0x7F, 0xF8, 0x77, 0x69, 0x04, 0xB0,
0x7E, 0xCF, 0x84, 0x05, 0x30, 0x3D, 0xE3, 0xD7, 0x38, 0x8E, 0x9B, 0xE1, 0x68, 0xE3, 0xDE, 0xAC].reverse(),
// co-factor
cf: 1
};
//
// "numsp384t1": twisted Edwards curve a=1, E: x^2 + y^2 = 1 - 11556x^2y^2, p = 2^384-317
//
var curve_numsp384t1 = {
// Curve ID, 2 x targeted security level, order bitlength, prime bitlength
info: ['numsp384t1', 384, 382, 384],
// Name
name: "numsp384t1",
// Curve Type 0 = Weierstrass, 1 Twisted Edwards
type: 1,
// Prime p = 2^2^384-317
p: [ 0xC3, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ].reverse(),
// Parameter "a"
a: [ 0x01 ],
// Parameter "d"
d: [ 0x9F, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ].reverse(),
// Order of the subgroup
order: [ 0x7D, 0x89, 0xA3, 0xE6, 0xC4, 0xDC, 0xB9, 0x20, 0x79, 0xC8, 0x35, 0xAB, 0x5A, 0x55, 0xE4, 0x61, 0xCF, 0xE1, 0x6B, 0xB4, 0x1C, 0x1A, 0x47, 0xE2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F ].reverse(),
// x(generator)
gx: [ 0xDE, 0x6B, 0x20, 0x6C, 0xE4, 0x40, 0xD5, 0x50, 0x13, 0x94, 0x45, 0x65, 0xB1, 0x92, 0xF2, 0x6F, 0x40, 0x63, 0x31, 0xF3, 0xA8, 0xFF, 0x63, 0x57, 0x00, 0x4C, 0xBE, 0xE5, 0x46, 0xF4, 0x0B, 0xB3,
0xB5, 0x5D, 0xE5, 0x9A, 0x12, 0xA2, 0xB6, 0xC0, 0x6C, 0x26, 0xA9, 0x45, 0xFB, 0x11, 0xB1, 0x61 ].reverse(),
// y(generator)
gy: [ 0x92, 0x93, 0x72, 0xF0, 0xE1, 0x03, 0x8D, 0x9D, 0xDC, 0x48, 0xEC, 0x46, 0xF9, 0xB0, 0x72, 0x00, 0x4B, 0x96, 0x45, 0xF6, 0xF7, 0x98, 0x0F, 0x83, 0x56, 0x5F, 0x42, 0xF1, 0x74, 0x82, 0xAD, 0x16,
0xD7, 0x0D, 0xB1, 0x23, 0xA4, 0xB1, 0x38, 0x87, 0xB0, 0xEE, 0xA6, 0xB9, 0x67, 0x3E, 0x98, 0x82 ].reverse(),
// co-factor
cf: 4
};
//
// "numsp512d1": Weierstrass curve a=-3, E: y^2 = x^3 - 3x + 121243, p = 2^512-569
//
var curve_numsp512d1 = {
// Curve ID, 2 x targeted security level, order bitlength, prime bitlength
info: ['numsp512d1', 512, 512, 512],
// Name
name: "numsp512d1",
// Curve Type 0 = Weierstrass, 1 Twisted Edwards
type: 0,
// Prime p = 2^512-569
p: [0xC7, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].reverse(),
// Parameter "a"
a: [0xC4, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].reverse(),
// Parameter "b"
b: [0x9B, 0xD9, 0x01].reverse(),
// Order of the group
order: [0x5d, 0x55, 0x33, 0x04, 0x39, 0x3f, 0x15, 0xce, 0x43, 0xd2, 0x7c, 0x60, 0x36, 0x8b, 0x56, 0x3b, 0xc6, 0xbd, 0xd0, 0x97, 0xed, 0x58, 0xc2, 0x4f, 0x1b, 0x83, 0xe7, 0x94, 0xfb, 0xa4, 0x3c, 0x5b,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff].reverse(),
// x(generator)
gx: [0x57, 0xAE, 0xAB, 0x8C, 0x95, 0x87, 0x82, 0xDC, 0xE2, 0x5D, 0x6F, 0x7D, 0x13, 0x60, 0x5D, 0x1D, 0x83, 0x15, 0x56, 0x25, 0x86, 0x42, 0x79, 0x93, 0x9E, 0x35, 0x6B, 0x07, 0x51, 0xA1, 0x21, 0x50,
0xF9, 0xD9, 0x06, 0x53, 0xC2, 0xE0, 0x06, 0x45, 0x85, 0xF6, 0x01, 0xB5, 0x3B, 0xD8, 0xCA, 0x98, 0x52, 0x3B, 0x3D, 0xA0, 0x02, 0x70, 0x2B, 0xDA, 0x93, 0x0A, 0x1D, 0x14, 0x47, 0x34, 0xC0, 0x3A].reverse(),
// y(generator)
gy: [0xA6, 0x27, 0x35, 0x38, 0x60, 0x87, 0xA0, 0x23, 0xE9, 0x0F, 0xFD, 0x4C, 0x1E, 0x5C, 0x2B, 0xCF, 0x02, 0x56, 0x5A, 0xB2, 0x40, 0xA8, 0x21, 0xC1, 0xE9, 0xED, 0x0E, 0x8B, 0xDA, 0x15, 0x84, 0xA2,
0x14, 0x4F, 0xD1, 0x7B, 0x0C, 0x26, 0x4B, 0x8F, 0x8C, 0xBB, 0xBC, 0xAB, 0xDE, 0xDB, 0x97, 0x4B, 0x00, 0xB1, 0xEB, 0x63, 0xDC, 0xEE, 0x0E, 0xCE, 0xB3, 0x56, 0xAD, 0x29, 0xCA, 0x54, 0x3A, 0x94].reverse(),
// co-factor
cf: 4
}
//
// "numsp512t1": twisted Edwards curve a=1, E: x^2 + y^2 = 1 - 78296x^2y^2, p = 2^512-569
//
var curve_numsp512t1 = {
// Curve ID, , 2 x targeted security level, order bitlength, prime bitlength
info: ['numsp512t1', 512, 510, 512],
// Name
name: "numsp512t1",
// Curve Type 0 = Weierstrass, 1 Twisted Edwards
type: 1,
// Prime p = 2^512-569
p: [0xC7, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].reverse(),
// Parameter "a"
a: [0x01].reverse(),
// Parameter "d"
d: [0xEF, 0xCB, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].reverse(),
// Order of the subgroup
order: [0x6D, 0xD4, 0xEE, 0x1B, 0xF5, 0x8C, 0x46, 0x67, 0xFF, 0xEC, 0xEF, 0x6D, 0x78, 0x05, 0x46, 0x2A, 0xF5, 0x86, 0xB6, 0x70, 0xC9, 0xD8, 0x3F, 0x9E, 0xBA, 0x91, 0xCF, 0x2F, 0x6D, 0x63, 0xF0, 0xB4,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F].reverse(),
// x(generator)
gx: [0xFE, 0x57, 0xEC, 0x99, 0x29, 0xAB, 0xB9, 0xC5, 0x15, 0xF0, 0xC4, 0x7C, 0x42, 0x25, 0xE5, 0x0F, 0xAD, 0x04, 0x89, 0x56, 0x92, 0xC9, 0xBD, 0x78, 0x0F, 0x73, 0x46, 0xEE, 0x4E, 0xC1, 0x21, 0x46,
0x47, 0x81, 0x3B, 0x27, 0xBE, 0x7E, 0xA1, 0x27, 0x82, 0xA3, 0xC4, 0x4D, 0x9F, 0xE7, 0xD1, 0x2F, 0x33, 0xC5, 0xD3, 0x88, 0x78, 0xCB, 0x18, 0x7A, 0x9C, 0xB6, 0x8D, 0x12, 0x6D, 0x31, 0x8E, 0xDF].reverse(),
// y(generator)
gy: [0xE1, 0xF5, 0xE2, 0xC1, 0xC0, 0xDE, 0x6D, 0x32, 0x1F, 0xD0, 0xF1, 0x9B, 0x8A, 0xD3, 0x66, 0x02, 0xFD, 0xC1, 0xEC, 0x2A, 0x86, 0x06, 0x1A, 0x60, 0x62, 0x35, 0x96, 0xE9, 0xF2, 0x53, 0xCA, 0x20,
0x41, 0x83, 0x9E, 0x90, 0x95, 0x6B, 0x2B, 0xA9, 0x22, 0x9D, 0x25, 0xD8, 0x26, 0xF7, 0x76, 0xE4, 0x6E, 0x25, 0x2A, 0xA8, 0x77, 0xF5, 0xB0, 0x98, 0x71, 0xCA, 0x49, 0x9D, 0xF3, 0xBF, 0x09, 0x6D].reverse(),
// co-factor
cf: 4
}
if (typeof cryptoECC !== 'undefined') {
// Add curves to ECC object - use uppercase names
cryptoECC.curves["NUMSP256D1"] = curve_numsp256d1;
cryptoECC.curves["NUMSP384D1"] = curve_numsp384d1;
cryptoECC.curves["NUMSP512D1"] = curve_numsp512t1;
cryptoECC.curves["NUMSP256T1"] = curve_numsp256t1;
cryptoECC.curves["NUMSP384T1"] = curve_numsp384t1;
cryptoECC.curves["NUMSP512T1"] = curve_numsp512t1;
}
function BlockFunctionTypeDef(message, blockIndex, initialHashValues, k, w) {
/// <signature>
/// <summary>
/// Type definition for block function
/// </summary>
/// <param name="message" type="Array">Block data</param>
/// <param name="blockIndex" type="Number">Block number to operate on</param>
/// <param name="initialHashValues" type="Array"></param>
/// <param name="k" type="Array">K constants</param>
/// <param name="w" type="Array">Array to hold w values</param>
/// <returns type="Array">Hash values</returns>
/// </signature>
return [];
}
var msrcryptoSha = function (name, der, h, k, blockBytes, blockFunction, truncateTo) {
/// <summary>
/// Returns a hash function using the passed in parameters.
/// </summary>
/// <param name="name" type="String">Name of the hash function.</param>
/// <param name="der" type="Array"></param>
/// <param name="h" type="Array"></param>
/// <param name="k" type="Array"></param>
/// <param name="blockBytes" type="Number">The number of bytes in a block.</param>
/// <param name="blockFunction" type="BlockFunctionTypeDef">Function for processing blocks.</param>
/// <param name="truncateTo" type="Number">Truncate the resulting hash to a fixed length.</param>
/// <returns type="Object"></returns>
var utils = msrcryptoUtilities;
// Make a copy of h so we don't alter the initialization array.
var hv = h.slice(),
w = new Array(blockBytes),
buffer = [],
blocksProcessed = 0;
function hashBlocks(message) {
/// <summary>
/// Breaks a array of data into full blocks and hashes each block in sequence.
/// Data at the end of the message that does not fill an entire block is
/// returned.
/// </summary>
/// <param name="message" type="Array">Byte data to hash</param>
/// <returns type="Array">Unprocessed data at the end of the message that did
/// not fill an entire block.</returns>
var blockCount = Math.floor(message.length / blockBytes);
// Process each block of the message
for (var block = 0; block < blockCount; block++) {
blockFunction(message, block, hv, k, w);
}
// 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() {
/// <summary>
/// Converts stored hash values (32-bit ints) to bytes.
/// </summary>
/// <returns type="Array"></returns>
// Move the results to an uint8 array.
var hash = [];
for (var i = 0; i < hv.length; i++) {
hash = hash.concat(utils.int32ToBytes(hv[i]));
}
// Truncate the results depending on the hash algorithm used.
hash.length = (truncateTo / 8);
return hash;
}
function addPadding(messageBytes) {
/// <summary>
/// Builds and appends padding to a message
/// </summary>
/// <param name="messageBytes" type="Array">Message to pad</param>
/// <returns type="Array">The message array + padding</returns>
var padLen = blockBytes - messageBytes.length % blockBytes;
// If there is 8 (16 for sha-512) or less bytes of padding, pad an additional block.
(padLen <= (blockBytes / 8)) && (padLen += blockBytes);
// Create a new Array that will contain the message + padding
var padding = utils.getVector(padLen);
// Set the 1 bit at the end of the message data
padding[0] = 128;
// Set the length equal to the previous data len + the new data len
var messageLenBits = (messageBytes.length + blocksProcessed * blockBytes) * 8;
// Set the message length in the last 4 bytes
padding[padLen - 4] = messageLenBits >>> 24 & 255;
padding[padLen - 3] = messageLenBits >>> 16 & 255;
padding[padLen - 2] = messageLenBits >>> 8 & 255;
padding[padLen - 1] = messageLenBits & 255;
return messageBytes.concat(padding);
}
function computeHash(messageBytes) {
/// <summary>
/// Computes the hash of an entire message.
/// </summary>
/// <param name="messageBytes" type="Array">Byte array to hash</param>
/// <returns type="Array">Hash of message bytes</returns>
buffer = hashBlocks(messageBytes);
return finish();
}
function process(messageBytes) {
/// <summary>
/// Call process repeatedly to hash a stream of bytes. Then call 'finish' to
/// complete the hash and get the final result.
/// </summary>
/// <param name="messageBytes" type="Array"></param>
// Append the new data to the buffer (previous unprocessed data)
buffer = buffer.concat(messageBytes);
// If there is at least one block of data, hash it
if (buffer.length >= blockBytes) {
// The remaining unprocessed data goes back into the buffer
buffer = hashBlocks(buffer);
}
return;
}
function finish() {
/// <summary>
/// Called after one or more calls to process. This will finalize the hashing
/// of the 'streamed' data and return the hash.
/// </summary>
/// <returns type="Array">Hash of message bytes</returns>
// All the full blocks of data have been processed. Now we pad the rest and hash.
// Buffer should be empty now.
if (hashBlocks(addPadding(buffer)).length !== 0) {
throw new Error("buffer.length !== 0");
}
// Convert the intermediate hash values to bytes
var result = hashToBytes();
// Reset the buffer
buffer = [];
// Restore the initial hash values
hv = h.slice();
// Reset the block counter
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)
};
};
/// JS2025.InsertSpaceBeforeCommentText,
/// JS2027.PunctuateCommentsCorrectly,
/// JS2005.UseShortFormInitializations,
/// JS3056.DeclareVariablesOnceOnly,
/// JS2073.CommentIsMisspelled,
/// JS3053.IncorrectNumberOfArguments,
/// JS3058.DeclareVariablesBeforeUse,
/// JS2074.IdentifierNameIsMisspelled,
/// JS3055.InconsistentReturnType
/// </disable>
var msrcryptoSha1 = (function () {
function hashBlock(message, blockIndex, hv, k, w) {
/// <summary>
/// Block function for hashing algorithm to use.
/// </summary>
/// <param name="message" type="Array">Block data to hash</param>
/// <param name="blockIndex" type="Number">The block of the data to hash</param>
/// <param name="hv" type="Array">Initial hash values</param>
/// <param name="k" type="Array">K constants</param>
/// <param name="w" type="Array">Buffer for w values</param>
/// <returns type="Array">Updated initial hash values</returns>
var t, i, temp, x0, blockSize = 64, mask = 0xFFFFFFFF;
var ra = hv[0],
rb = hv[1],
rc = hv[2],
rd = hv[3],
re = hv[4];
// 0 ≤ t ≤ 15
for (i = 0; i < 16; i++) {
w[i] = utils.bytesToInt32(message, blockIndex * blockSize + i * 4);
}
// 16 ≤ t ≤ 79
for (t = 16; t < 80; t++) {
x0 = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
w[t] = (x0 << 1) | (x0 >>> 31);
}
for (i = 0; i < 80; i++) {
// Ch(x, y, z)=(x & y) ^ (~x & z)
temp = ((ra << 5) | (ra >>> 27));
temp +=
i >= 60 ? (rb ^ rc ^ rd) :
i >= 40 ? ((rb & rc) ^ (rb & rd) ^ (rc & rd)) :
i >= 20 ? (rb ^ rc ^ rd) :
/*i<=20*/ ((rb & rc) ^ ((~rb) & rd));
temp += (re + k[i] + w[i]);
re = rd;
rd = rc;
rc = ((rb << 30) | (rb >>> 2));
rb = ra;
ra = temp;
}
// Update the hash values
hv[0] += ra & mask;
hv[1] += rb & mask;
hv[2] += rc & mask;
hv[3] += rd & mask;
hv[4] += re & mask;
return hv;
}
var utils = msrcryptoUtilities,
upd = utils.unpackData,
h = upd("Z0UjAe/Nq4mYutz+EDJUdsPS4fA=", 4, 1),
k = upd("WoJ5mVqCeZlagnmZWoJ5mVqCeZlagnmZWoJ5mVqCeZlagnmZWoJ5mVqCeZlagnmZWoJ5mVqCeZlagnmZWoJ5mVqCeZlagnmZWoJ5mVqCeZlu2euhbtnroW7Z66Fu2euhbtnroW7Z66Fu2euhbtnroW7Z66Fu2euhbtnroW7Z66Fu2euhbtnroW7Z66Fu2euhbtnroW7Z66Fu2euhbtnroY8bvNyPG7zcjxu83I8bvNyPG7zcjxu83I8bvNyPG7zcjxu83I8bvNyPG7zcjxu83I8bvNyPG7zcjxu83I8bvNyPG7zcjxu83I8bvNyPG7zcymLB1spiwdbKYsHWymLB1spiwdbKYsHWymLB1spiwdbKYsHWymLB1spiwdbKYsHWymLB1spiwdbKYsHWymLB1spiwdbKYsHWymLB1spiwdY", 4, 1),
der = upd("MCEwCQYFKw4DAhoFAAQU");
return {
sha1: msrcryptoSha("SHA-1", der, h, k, 64, hashBlock, 160)
};
})();
if (typeof operations !== "undefined") {
msrcryptoSha1.hash = function ( p) {
if (p.operationSubType === "process") {
msrcryptoSha1.sha1.process(p.buffer);
return;
}
if (p.operationSubType === "finish") {
return msrcryptoSha1.sha1.finish();
}
return msrcryptoSha1.sha1.computeHash(p.buffer);
};
operations.register("digest", "sha-1", msrcryptoSha1.hash);
}
msrcryptoHashFunctions["sha-1"] = msrcryptoSha1.sha1;
var msrcryptoSha256 = (function () {
var utils = msrcryptoUtilities;
function hashBlock(message, blockIndex, hv, k, w) {
/// <summary>
/// Block function for hashing algorithm to use.
/// </summary>
/// <param name="message" type="Array">Block data to hash</param>
/// <param name="blockIndex" type="Number">The block of the data to hash</param>
/// <param name="hv" type="Array">Initial hash values</param>
/// <param name="k" type="Array">K constants</param>
/// <param name="w" type="Array">Buffer for w values</param>
/// <returns type="Array">Updated initial hash values</returns>
var t, i, temp, x0, x1, blockSize = 64, mask = 0xFFFFFFFF;
var ra = hv[0],
rb = hv[1],
rc = hv[2],
rd = hv[3],
re = hv[4],
rf = hv[5],
rg = hv[6],
rh = hv[7];
// 0 ≤ t ≤ 15
for (i = 0; i < 16; i++) {
w[i] = utils.bytesToInt32(message, blockIndex * blockSize + i * 4);
}
// 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 - 7]
+ (((x0 >>> 7) | (x0 << 25)) ^ ((x0 >>> 18) | (x0 << 14)) ^ (x0 >>> 3))
+ w[t - 16];
w[t] = w[t] & mask;
}
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
}
// Update the hash values
hv[0] += ra & mask;
hv[1] += rb & mask;
hv[2] += rc & mask;
hv[3] += rd & mask;
hv[4] += re & mask;
hv[5] += rf & mask;
hv[6] += rg & mask;
hv[7] += rh & mask;
return hv;
}
var k256, h224, h256, der224, der256, upd = utils.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: msrcryptoSha("SHA-224", der224, h224, k256, 64, hashBlock, 224),
sha256: msrcryptoSha("SHA-256", der256, h256, k256, 64, hashBlock, 256)
};
})();
if (typeof operations !== "undefined") {
msrcryptoSha256.hash256 = function ( 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 ( 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;
var msrcryptoSha512 = (function () {
var utils = msrcryptoUtilities;
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;
}
function hashBlock(message, blockIndex, hv, k, w) {
/// <summary>
/// Block function for hashing algorithm to use.
/// </summary>
/// <param name="message" type="Array">Block data to hash</param>
/// <param name="blockIndex" type="Number">The block of the data to hash</param>
/// <param name="hv" type="Array">Initial hash values</param>
/// <param name="k" type="Array">K constants</param>
/// <param name="w" type="Array">Buffer for w values</param>
/// <returns type="Array">Updated initial hash values</returns>
var t, i, blockBytes = 128,
tah, tal, tbh, tbl, xh, xl,
tc = [], td = [], te = [], index;
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 (t = 0; t < 32; t++) {
index = blockIndex * 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];
}
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];
return hv;
}
var h384, h512, k512,
der384, der512, der512_224, der512_256,
upd = utils.unpackData;
h384 = upd("y7udXcEFnthimikqNnzVB5FZAVowcN0XFS/s2PcOWTlnMyZn/8ALMY60SodoWBUR2wwuDWT5j6dHtUgdvvpPpA==", 4, 1);
h512 = upd("agnmZ/O8yQi7Z66FhMqnOzxu83L+lPgrpU/1Ol8dNvFRDlJ/reaC0ZsFaIwrPmwfH4PZq/tBvWtb4M0ZE34heQ", 4, 1);
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: msrcryptoSha("SHA-384", der384, h384, k512, 128, hashBlock, 384),
sha512: msrcryptoSha("SHA-512", der512, h512, k512, 128, hashBlock, 512),
sha512_224: msrcryptoSha("SHA-512.224", der512_224, h512, k512, 128, hashBlock, 224),
sha512_256: msrcryptoSha("SHA-512.256", der512_256, h512, k512, 128, hashBlock, 256)
};
})();
if (typeof operations !== "undefined") {
msrcryptoSha512.hash384 = function ( 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 ( 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;
var msrcryptoHmac = (function () {
var sha256,
sha512,
sha1;
if (typeof msrcryptoSha256 !== "undefined") {
sha256 = msrcryptoSha256;
}
if (typeof msrcryptoSha512 !== "undefined") {
sha512 = msrcryptoSha512;
}
if (typeof msrcryptoSha1 !== "undefined") {
sha1 = msrcryptoSha1;
}
var hashFunction;
var blockSize;
var keyBytes;
var ipad;
var opad;
function xorArrays(array1, array2) {
var newArray = new Array(array1);
for (var i = 0 ; i < array1.length; i++) {
newArray[i] = array1[i] ^ array2[i];
}
return newArray;
}
// Returns a new Array with zeroes padded on the end
function padZeros(bytes, paddedLength) {
var paddedArray = bytes.slice();
for (var i = bytes.length ; i < paddedLength; i++) {
paddedArray.push(0);
}
return paddedArray;
}
function padKey() {
if (keyBytes.length === blockSize) {
return keyBytes;
}
if (keyBytes.length > blockSize) {
return padZeros(hashFunction.computeHash(keyBytes), blockSize);
}
// If keyBytes.length < blockSize
return padZeros(keyBytes, blockSize);
}
var paddedKey = null;
var keyXorOpad;
function processHmac(messageBytes) {
var keyXorIpad;
var k0IpadText;
// If this is the first process call, do some initial computations
if (!paddedKey) {
ipad = new Array(blockSize);
opad = new Array(blockSize);
for (var i = 0; i < blockSize; i++) { ipad[i] = 0x36; opad[i] = 0x5c; }
paddedKey = padKey();
keyXorIpad = xorArrays(paddedKey, ipad);
keyXorOpad = xorArrays(paddedKey, opad);
k0IpadText = keyXorIpad.concat(messageBytes);
hashFunction.process(k0IpadText);
// Subsequent process calls just add to the hash
} else {
hashFunction.process(messageBytes);
}
return;
}
function finishHmac() {
var hashK0IpadText = hashFunction.finish();
var k0IpadK0OpadText = keyXorOpad.concat(hashK0IpadText);
return hashFunction.computeHash(k0IpadK0OpadText);
}
function clearState() {
keyBytes = null;
hashFunction = null;
paddedKey = null;
}
function selectHashAlgorithm(hashAlgorithmName) {
switch (hashAlgorithmName.toLowerCase()) {
case "sha-1":
if (sha1 === undefined) {
throw new Error("Sha1 object not found");
}
hashFunction = sha1.sha1;
blockSize = 64;
break;
case "sha-224":
hashFunction = sha256.sha224;
blockSize = 64;
break;
case "sha-256":
hashFunction = sha256.sha256;
blockSize = 64;
break;
case "sha-384":
if (sha512 === undefined) {
throw new Error("Sha512 object not found");
}
hashFunction = sha512.sha384;
blockSize = 128;
break;
case "sha-512":
if (sha512 === undefined) {
throw new Error("Sha512 object not found");
}
hashFunction = sha512.sha512;
blockSize = 128;
break;
default:
throw new Error("unsupported hash alorithm (sha-224, sha-256, sha-384, sha-512)");
}
}
return {
computeHmac: function (dataBytes, key, hashAlgorithm) {
/// <summary>Computes the HMAC</summary>
/// <param name="dataBytes" type="Array">Data to MAC</param>
/// <param name="key" type="Array">Array of bytes for key</param>
/// <param name="hashAlgorithm" type="String">sha-224, sha-256, sha-384, sha-512 (default sha-256)</param>
/// <returns type="Array">Returns an array of bytes as the HMAC</returns>
keyBytes = key;
selectHashAlgorithm(hashAlgorithm);
processHmac(dataBytes);
var result = finishHmac();
clearState();
return result;
},
process: function (dataBytes, key, hashAlgorithm) {
/// <summary>Computes a partial HMAC to be followed by subsequent process calls or finish()</summary>
/// <param name="dataBytes" type="Array">Data to MAC</param>
/// <param name="key" type="Array">Array of bytes for key</param>
/// <param name="hashAlgorithm" type="String">sha-224, sha-256, sha-384, sha-512 (default sha-256)</param>
if (!hashFunction) {
keyBytes = key;
selectHashAlgorithm(hashAlgorithm);
}
processHmac(dataBytes);
},
finish: function (key, hashAlgorithm) {
/// <summary>Computes the final HMAC upon partial computations from previous process() calls.</summary>
/// <param name="key" type="Array">Array of bytes for key</param>
/// <param name="hashAlgorithm" type="String">sha-224, sha-256, sha-384, sha-512 (default sha-256)</param>
/// <returns type="Array">Returns an array of bytes as the HMAC</returns>
// Finish could be called before any processing. We'll return the hmac
// of an empty buffer.
if (!hashFunction) {
keyBytes = key;
selectHashAlgorithm(hashAlgorithm);
processHmac([]);
}
var result = finishHmac();
clearState();
return result;
}
};
})();
if (typeof operations !== "undefined") {
msrcryptoHmac.signHmac = function (p) {
var hashName = p.algorithm.hash.name;
if (p.operationSubType === "process") {
msrcryptoHmac.process(p.buffer, p.keyData, hashName);
return;
}
if (p.operationSubType === "finish") {
return msrcryptoHmac.finish(p.keyData, hashName);
}
return msrcryptoHmac.computeHmac(p.buffer, p.keyData, hashName);
};
msrcryptoHmac.verifyHmac = function (p) {
var hashName = p.algorithm.hash.name;
if (p.operationSubType === "process") {
msrcryptoHmac.process(p.buffer, p.keyData, hashName);
return;
}
if (p.operationSubType === "finish") {
return msrcryptoUtilities.arraysEqual(msrcryptoHmac.finish(p.keyData, hashName), p.signature);
}
return msrcryptoUtilities.arraysEqual(msrcryptoHmac.computeHmac(p.buffer, p.keyData, hashName), p.signature);
};
msrcryptoHmac.generateKey = function (p) {
var keyLength = p.algorithm.length;
var defaultKeyLengths = { "sha-256": 32, "sha-384": 48, "sha-512": 64 };
if (!keyLength) {
keyLength = defaultKeyLengths[p.algorithm.hash.name.toLowerCase()];
}
return {
type: "keyGeneration",
keyData: msrcryptoPseudoRandom.getBytes(keyLength),
keyHandle: {
algorithm: p.algorithm,
extractable: p.extractable,
keyUsage: null || p.keyUsage,
type: "secret"
}
};
};
msrcryptoHmac.importKey = function (p) {
var keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, ["k"]);
keyObject.alg = keyObject.alg.replace("HS", "sha-");
return {
type: "keyImport",
keyData: keyObject.k,
keyHandle: {
algorithm: { name: "hmac", hash: { name: keyObject.alg } },
extractable: p.extractable || keyObject.extractable,
keyUsage: null || p.keyUsage, // IE11 returns null here
type: "secret"
}
};
};
msrcryptoHmac.exportKey = function (p) {
var jsonKeyStringArray = msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData);
return { type: "keyExport", keyHandle: jsonKeyStringArray };
};
operations.register("importKey", "hmac", msrcryptoHmac.importKey);
operations.register("exportKey", "hmac", msrcryptoHmac.exportKey);
operations.register("generateKey", "hmac", msrcryptoHmac.generateKey);
operations.register("sign", "hmac", msrcryptoHmac.signHmac);
operations.register("verify", "hmac", msrcryptoHmac.verifyHmac);
}
var msrcryptoBlockCipher = (function() {
var aesConstants,
multByTwo,
multByThree,
multBy14,
multBy13,
multBy11,
multBy9,
sBoxTable,
invSBoxTable,
rConTable;
return {
/// <summary>Advanced Encryption Standard implementation per FIPS 197.</summary>
aes: function ( keyBytes) {
// Set up the constants the first time we create an AES object only.
if (!aesConstants) {
aesConstants = msrcryptoUtilities.unpackData("AAIEBggKDA4QEhQWGBocHiAiJCYoKiwuMDI0Njg6PD5AQkRGSEpMTlBSVFZYWlxeYGJkZmhqbG5wcnR2eHp8foCChIaIioyOkJKUlpianJ6goqSmqKqsrrCytLa4ury+wMLExsjKzM7Q0tTW2Nrc3uDi5Obo6uzu8PL09vj6/P4bGR8dExEXFQsJDw0DAQcFOzk/PTMxNzUrKS8tIyEnJVtZX11TUVdVS0lPTUNBR0V7eX99c3F3dWtpb21jYWdlm5mfnZORl5WLiY+Ng4GHhbu5v72zsbe1q6mvraOhp6Xb2d/d09HX1cvJz83DwcfF+/n//fPx9/Xr6e/t4+Hn5QADBgUMDwoJGBseHRQXEhEwMzY1PD86OSgrLi0kJyIhYGNmZWxvaml4e359dHdycVBTVlVcX1pZSEtOTURHQkHAw8bFzM/Kydjb3t3U19LR8PP29fz/+vno6+7t5Ofi4aCjpqWsr6qpuLu+vbS3srGQk5aVnJ+amYiLjo2Eh4KBm5idnpeUkZKDgIWGj4yJiquora6npKGis7C1tr+8ubr7+P3+9/Tx8uPg5ebv7Onqy8jNzsfEwcLT0NXW39zZ2ltYXV5XVFFSQ0BFRk9MSUpraG1uZ2RhYnNwdXZ/fHl6Ozg9Pjc0MTIjICUmLywpKgsIDQ4HBAECExAVFh8cGRoADhwSODYkKnB+bGJIRlRa4O788tjWxMqQnoyCqKa0utvVx8nj7f/xq6W3uZOdj4E7NScpAw0fEUtFV1lzfW9hraOxv5WbiYfd08HP5ev5901DUV91e2lnPTMhLwULGRd2eGpkTkBSXAYIGhQ+MCIslpiKhK6gsrzm6Pr03tDCzEFPXVN5d2VrMT8tIwkHFRuhr72zmZeFi9HfzcPp5/X7mpSGiKKsvrDq5Pb40tzOwHp0ZmhCTF5QCgQWGDI8LiDs4vD+1NrIxpySgI6kqri2DAIQHjQ6KCZ8cmBuREpYVjc5KyUPARMdR0lbVX9xY23X2cvF7+Hz/aepu7WfkYONAA0aFzQ5LiNoZXJ/XFFGS9Ddysfk6f7zuLWir4yBlpu7tqGsj4KVmNPeycTn6v3wa2ZxfF9SRUgDDhkUNzotIG1gd3pZVENOBQgfEjE8Kya9sKeqiYSTntXYz8Lh7Pv21tvMweLv+PW+s6SpioeQnQYLHBEyPyglbmN0eVpXQE3a18DN7uP0+bK/qKWGi5yRCgcQHT4zJClib3h1VltMQWFse3ZVWE9CCQQTHj0wJyqxvKumhYifktnUw87t4Pf6t7qtoIOOmZTf0sXI6+bx/GdqfXBTXklEDwIVGDs2ISwMARYbODUiL2RpfnNQXUpH3NHGy+jl8v+0ua6jgI2alwALFh0sJzoxWFNORXR/Ymmwu6atnJeKgejj/vXEz9LZe3BtZldcQUojKDU+DwQZEsvA3dbn7PH6k5iFjr+0qaL2/eDr2tHMx66luLOCiZSfRk1QW2phfHceFQgDMjkkL42Gm5Chqre81d7DyPny7+Q9NisgERoHDGVuc3hJQl9U9/zh6tvQzcavpLmyg4iVnkdMUVprYH12HxQJAjM4JS6Mh5qRoKu2vdTfwsn48+7lPDcqIRAbBg1kb3J5SENeVQEKFxwtJjswWVJPRHV+Y2ixuqesnZaLgOni//TFztPYenFsZ1ZdQEsiKTQ/DgUYE8rB3Nfm7fD7kpmEj761qKMACRIbJC02P0hBWlNsZX53kJmCi7S9pq/Y0crD/PXu5zsyKSAfFg0Ec3phaFdeRUyrormwj4adlOPq8fjHztXcdn9kbVJbQEk+NywlGhMIAebv9P3Cy9DZrqe8tYqDmJFNRF9WaWB7cgUMFx4hKDM63dTPxvnw6+KVnIeOsbijquzl/vfIwdrTpK22v4CJkpt8dW5nWFFKQzQ9Ji8QGQIL197FzPP64eiflo2Eu7KpoEdOVVxjanF4DwYdFCsiOTCak4iBvrespdLbwMn2/+TtCgMYES4nPDVCS1BZZm90faGos7qFjJee6eD78s3E39YxOCMqFRwHDnlwa2JdVE9GY3x3e/Jrb8UwAWcr/terdsqCyX36WUfwrdSir5ykcsC3/ZMmNj/3zDSl5fFx2DEVBMcjwxiWBZoHEoDi6yeydQmDLBobblqgUjvWsynjL4RT0QDtIPyxW2rLvjlKTFjP0O+q+0NNM4VF+QJ/UDyfqFGjQI+SnTj1vLbaIRD/89LNDBPsX5dEF8Snfj1kXRlzYIFP3CIqkIhG7rgU3l4L2+AyOgpJBiRcwtOsYpGV5HnnyDdtjdVOqWxW9Opleq4IunglLhymtMbo3XQfS72LinA+tWZIA/YOYTVXuYbBHZ7h+JgRadmOlJseh+nOVSjfjKGJDb/mQmhBmS0PsFS7FlIJatUwNqU4v0CjnoHz1/t84zmCmy//hzSOQ0TE3unLVHuUMqbCIz3uTJULQvrDTgguoWYo2SSydluiSW2L0SVy+PZkhmiYFtSkXMxdZbaSbHBIUP3tudpeFUZXp42dhJDYqwCMvNMK9+RYBbizRQbQLB6Pyj8PAsGvvQMBE4prOpERQU9n3OqX8s/O8LTmc5asdCLnrTWF4vk36Bx1325H8RpxHSnFiW+3Yg6qGL4b/FY+S8bSeSCa28D+eM1a9B/dqDOIB8cxsRIQWSeA7F9gUX+pGbVKDS3lep+TyZzvoOA7Ta4q9bDI67s8g1OZYRcrBH66d9Ym4WkUY1UhDH2NAQIECBAgQIAbNmzYq02aL168Y8aXNWrUs33678WROXLk071hwp8lSpQzZsyDHTp06MuNAQIECBAgQIAbNmzYq02aL168Y8aXNWrUs33678WROXLk071hwp8lSpQzZsyDHTp06MuNAQIECBAgQIAbNmzYq02aL168Y8aXNWrUs33678WROXLk071hwp8lSpQzZsyDHTp06MuNAQIECBAgQIAbNmzYq02aL168Y8aXNWrUs33678WROXLk071hwp8lSpQzZsyDHTp06MuNAQIECBAgQIAbNmzYq02aL168Y8aXNWrUs33678WROXLk071hwp8lSpQzZsyDHTp06MuN", 256, false);
multByTwo = aesConstants[0];
multByThree = aesConstants[1];
multBy14 = aesConstants[2];
multBy13 = aesConstants[3];
multBy11 = aesConstants[4];
multBy9 = aesConstants[5];
sBoxTable = aesConstants[6];
invSBoxTable = aesConstants[7];
rConTable = aesConstants[8];
}
var blockSize = 128,
keyLength,
nK,
nB = 4,
nR,
key;
switch (keyBytes.length * 8) {
case 128:
keyLength = 128;
nK = 4;
nR = 10;
break;
case 192:
keyLength = 192;
nK = 6;
nR = 12;
break;
case 256:
keyLength = 256;
nK = 8;
nR = 14;
break;
default:
throw new Error("Unsupported keyLength");
}
var shiftRows = function (a) {
var tmp = a[1]; a[1] = a[5]; a[5] = a[9]; a[9] = a[13]; a[13] = tmp;
tmp = a[2]; a[2] = a[10]; a[10] = tmp;
tmp = a[6]; a[6] = a[14]; a[14] = tmp;
tmp = a[15]; a[15] = a[11]; a[11] = a[7]; a[7] = a[3]; a[3] = tmp;
};
var invShiftRows = function (a) {
var tmp = a[13]; a[13] = a[9]; a[9] = a[5]; a[5] = a[1]; a[1] = tmp;
tmp = a[10]; a[10] = a[2]; a[2] = tmp;
tmp = a[14]; a[14] = a[6]; a[6] = tmp;
tmp = a[3]; a[3] = a[7]; a[7] = a[11]; a[11] = a[15]; a[15] = tmp;
};
var mixColumns = function (state) {
/// <summary>Operates on the state column by column, performing a multiplication by x^4 + 1 in GF(2^8)</summary>
/// <param name="state" type="Array"> the current state (length 16)</param>
/// <returns type="Array">The mixed state</returns>
var a = state[0], b = state[1], c = state[2], d = state[3],
e = state[4], f = state[5], g = state[6], h = state[7],
i = state[8], j = state[9], k = state[10], l = state[11],
m = state[12], n = state[13], o = state[14], p = state[15];
state[0] = multByTwo[a] ^ multByThree[b] ^ c ^ d;
state[1] = a ^ multByTwo[b] ^ multByThree[c] ^ d;
state[2] = a ^ b ^ multByTwo[c] ^ multByThree[d];
state[3] = multByThree[a] ^ b ^ c ^ multByTwo[d];
state[4] = multByTwo[e] ^ multByThree[f] ^ g ^ h;
state[5] = e ^ multByTwo[f] ^ multByThree[g] ^ h;
state[6] = e ^ f ^ multByTwo[g] ^ multByThree[h];
state[7] = multByThree[e] ^ f ^ g ^ multByTwo[h];
state[8] = multByTwo[i] ^ multByThree[j] ^ k ^ l;
state[9] = i ^ multByTwo[j] ^ multByThree[k] ^ l;
state[10] = i ^ j ^ multByTwo[k] ^ multByThree[l];
state[11] = multByThree[i] ^ j ^ k ^ multByTwo[l];
state[12] = multByTwo[m] ^ multByThree[n] ^ o ^ p;
state[13] = m ^ multByTwo[n] ^ multByThree[o] ^ p;
state[14] = m ^ n ^ multByTwo[o] ^ multByThree[p];
state[15] = multByThree[m] ^ n ^ o ^ multByTwo[p];
};
var invMixColumns = function (state) {
/// <summary>Operates on the state column by column, performing a multiplication by x^4 + 1 in GF(2^8)</summary>
/// <param name="state" type="Array"> the current state (length 16)</param>
/// <returns type="Array">The mixed state</returns>
var a = state[0], b = state[1], c = state[2], d = state[3],
e = state[4], f = state[5], g = state[6], h = state[7],
i = state[8], j = state[9], k = state[10], l = state[11],
m = state[12], n = state[13], o = state[14], p = state[15];
state[0] = multBy14[a] ^ multBy11[b] ^ multBy13[c] ^ multBy9[d];
state[1] = multBy9[a] ^ multBy14[b] ^ multBy11[c] ^ multBy13[d];
state[2] = multBy13[a] ^ multBy9[b] ^ multBy14[c] ^ multBy11[d];
state[3] = multBy11[a] ^ multBy13[b] ^ multBy9[c] ^ multBy14[d];
state[4] = multBy14[e] ^ multBy11[f] ^ multBy13[g] ^ multBy9[h];
state[5] = multBy9[e] ^ multBy14[f] ^ multBy11[g] ^ multBy13[h];
state[6] = multBy13[e] ^ multBy9[f] ^ multBy14[g] ^ multBy11[h];
state[7] = multBy11[e] ^ multBy13[f] ^ multBy9[g] ^ multBy14[h];
state[8] = multBy14[i] ^ multBy11[j] ^ multBy13[k] ^ multBy9[l];
state[9] = multBy9[i] ^ multBy14[j] ^ multBy11[k] ^ multBy13[l];
state[10] = multBy13[i] ^ multBy9[j] ^ multBy14[k] ^ multBy11[l];
state[11] = multBy11[i] ^ multBy13[j] ^ multBy9[k] ^ multBy14[l];
state[12] = multBy14[m] ^ multBy11[n] ^ multBy13[o] ^ multBy9[p];
state[13] = multBy9[m] ^ multBy14[n] ^ multBy11[o] ^ multBy13[p];
state[14] = multBy13[m] ^ multBy9[n] ^ multBy14[o] ^ multBy11[p];
state[15] = multBy11[m] ^ multBy13[n] ^ multBy9[o] ^ multBy14[p];
};
var xorWord = function (a, b) {
return [a[0] ^ b[0], a[1] ^ b[1], a[2] ^ b[2], a[3] ^ b[3]];
};
var addRoundKey = function ( state, keySchedule, offset) {
for (var i = 0 ; i < state.length ; i += 1) {
state[i] ^= keySchedule[i + offset];
}
};
var rotWord = function ( word) {
var a = word[0];
word[0] = word[1]; word[1] = word[2]; word[2] = word[3]; word[3] = a;
};
var subWord = function ( word) {
for (var i = 0 ; i < word.length ; i += 1) {
word[i] = sBoxTable[word[i]];
}
};
var invSubWord = function ( word) {
for (var i = 0 ; i < word.length ; i += 1) {
word[i] = invSBoxTable[word[i]];
}
};
var getWord = function (tab, i) {
return [tab[4 * i], tab[4 * i + 1], tab[4 * i + 2], tab[4 * i + 3]];
};
var setWord = function ( left, right, indexL, indexR) {
left[4 * indexL] = right[4 * indexR];
left[4 * indexL + 1] = right[4 * indexR + 1];
left[4 * indexL + 2] = right[4 * indexR + 2];
left[4 * indexL + 3] = right[4 * indexR + 3];
};
var expandKey = function (key) {
var temp, res = [], i = 0;
while (i < 4 * nK) {
res.push(key[i++]);
}
i = nK;
while (i < nB * (nR + 1)) {
temp = getWord(res, i - 1);
if (i % nK === 0) {
var index = i / nK;
var rcon = [rConTable[index], 0, 0, 0];
rotWord(temp);
subWord(temp);
temp = xorWord(temp, rcon);
} else if (nK > 6 && i % nK === 4) {
subWord(temp);
}
var newWord = xorWord(getWord(res, i - nK), temp);
setWord(res, newWord, i, 0);
i += 1;
}
return res;
};
key = expandKey(keyBytes);
return {
encrypt: function (dataBytes) {
var state = dataBytes,
round;
addRoundKey(state, key, 0);
for (round = 1 ; round <= nR - 1 ; round += 1) {
subWord(state);
shiftRows(state);
mixColumns(state);
addRoundKey(state, key, 4 * round * nB);
}
subWord(state);
shiftRows(state);
addRoundKey(state, key, 4 * nR * nB);
return state;
},
decrypt: function (dataBytes) {
var state = dataBytes,
round;
addRoundKey(state, key, 4 * nR * nB);
for (round = nR - 1 ; round >= 1 ; round -= 1) {
invShiftRows(state);
invSubWord(state);
addRoundKey(state, key, 4 * round * nB);
invMixColumns(state);
}
invShiftRows(state);
invSubWord(state);
addRoundKey(state, key, 0);
return state;
},
clear: function () {
// Reset the state
},
keyLength: keyLength,
blockSize: blockSize
};
}
};
})();
var msrcryptoPadding = msrcryptoPadding || {};
msrcryptoPadding.pkcsv7 = function (blockSize) {
function pad(messageBlocks) {
/// <signature>
/// <summary>Apply PKCS7 padding to message</summary>
/// <param name="messageBlocks" type="Array">An array of blocks to pad</param> <
/// </signature>
var lastIndex = messageBlocks.length - 1 >= 0 ? messageBlocks.length - 1 : 0,
lastBlock = messageBlocks[lastIndex],
lastBlockLength = lastBlock.length,
createNewBlock = (lastBlockLength === blockSize);
if (createNewBlock) {
var newBlock = [], i;
for (i = 0 ; i < blockSize; i += 1) {
newBlock.push(blockSize);
}
messageBlocks.push(newBlock);
} else {
var byteToAdd = (blockSize - lastBlockLength) & 0xff;
while (lastBlock.length !== blockSize) {
lastBlock.push(byteToAdd);
}
}
}
function unpad(messageBytes) {
/// <signature>
/// <summary>Remove PKCS7 padding from the message</summary>
/// <param name="messageBytes" type="Array"></param>
/// <returns type="Boolean">True for legal padding. False if not.</returns>
/// </signature>
var verified = true;
// Verify the cipher text is an increment of block length
if (messageBytes.length % blockSize !== 0) {
verified = false;
}
// Get the last block
var lastBlock = messageBytes.slice(-blockSize);
// Get value of the last element in the block
// This will be the number of padding bytes on the end if the
// message was decrypted correctly.
var padLen = lastBlock[lastBlock.length - 1];
for (var i = 0; i < blockSize; i++) {
var isPaddingElement = (blockSize - i <= padLen);
var isCorrectValue = (lastBlock[i] === padLen);
verified = (isPaddingElement ? isCorrectValue : true) && verified;
}
var trimLen = verified ? padLen : 0;
messageBytes.length -= trimLen;
return verified;
}
return {
pad: pad,
unpad: unpad
};
};
var msrcryptoCbc = function (blockCipher) {
var blockSize = blockCipher.blockSize / 8;
var paddingScheme = msrcryptoPadding.pkcsv7(blockSize);
// Merges an array of block arrays into a single byte array
var mergeBlocks = function ( tab) {
var res = [], i, j;
for (i = 0 ; i < tab.length; i += 1) {
var block = tab[i];
for (j = 0 ; j < block.length; j += 1) {
res.push(block[j]);
}
}
return res;
};
// Breaks an array of bytes into an array of block size arrays of bytes
function getBlocks(dataBytes) {
var blocks = [];
// Append incoming bytes to the end of the existing buffered bytes
mBuffer = mBuffer.concat(dataBytes);
var blockCount = Math.floor(mBuffer.length / blockSize);
for (var i = 0; i < blockCount; i++) {
blocks.push(mBuffer.slice(i * blockSize, (i + 1) * blockSize));
}
// Set the buffer to the remaining bytes
mBuffer = mBuffer.slice(blockCount * blockSize);
return blocks;
}
function encryptBlocks(blocks) {
var result = [],
toEncrypt;
for (var i = 0; i < blocks.length; i++) {
toEncrypt = msrcryptoUtilities.xorVectors(mIvBytes, blocks[i]);
result.push(blockCipher.encrypt(toEncrypt));
mIvBytes = result[i];
}
return result;
}
function decryptBlocks(blocks) {
var result = [],
toDecrypt,
decrypted;
for (var i = 0 ; i < blocks.length; i += 1) {
toDecrypt = blocks[i].slice(0, blocks[i].length);
decrypted = blockCipher.decrypt(toDecrypt);
result.push(msrcryptoUtilities.xorVectors(mIvBytes, decrypted));
mIvBytes = blocks[i];
}
return result;
}
function clearState() {
mBuffer = [];
mResultBuffer = [];
mIvBytes = null;
}
var mBuffer = [],
mResultBuffer = [],
mIvBytes;
return {
init: function (ivBytes) {
if (ivBytes.length !== blockSize) {
throw new Error("Invalid iv size");
}
mIvBytes = ivBytes.slice();
},
// Does a full encryption of the input
encrypt: function (plainBytes) {
/// <summary>perform the encryption of the plain text message</summary>
/// <param name="plainBytes" type="Array">the plain text to encrypt</param>
/// <returns type="Array">the encrypted message</returns>
this.processEncrypt(plainBytes);
return this.finishEncrypt();
},
// Encrypts full blocks of streamed input
processEncrypt: function (plainBytes) {
var result = encryptBlocks(getBlocks(plainBytes));
mResultBuffer = mResultBuffer.concat(mergeBlocks(result));
return;
},
// Call when done streaming input
finishEncrypt: function () {
var blocks = mBuffer.length === 1 ? [[mBuffer[0]]] : [mBuffer];
paddingScheme.pad(blocks);
var result = mResultBuffer.concat(mergeBlocks(encryptBlocks(blocks)));
clearState();
return result;
},
// Does a full decryption and returns the result
decrypt: function ( cipherBytes) {
/// <summary>perform the decryption of the encrypted message</summary>
/// <param name="encryptedBytes" type="Array">the plain text to encrypt</param>
/// <returns type="Array">the encrypted message</returns>
this.processDecrypt(cipherBytes);
return this.finishDecrypt();
},
// Decrypts full blocks of streamed data
processDecrypt: function (cipherBytes) {
var result = decryptBlocks(getBlocks(cipherBytes));
mResultBuffer = mResultBuffer.concat(mergeBlocks(result));
return;
},
// Called to finalize streamed decryption
finishDecrypt: function () {
var result = mResultBuffer;
// Strip the padding.
var verified = paddingScheme.unpad(result);
clearState();
return result;
}
};
};
var cbcInstance = null;
if (typeof operations !== "undefined") {
msrcryptoCbc.workerEncrypt = function (p) {
var result;
if (!cbcInstance) {
cbcInstance = msrcryptoCbc(msrcryptoBlockCipher.aes(p.keyData));
cbcInstance.init(p.algorithm.iv);
}
if (p.operationSubType === "process") {
cbcInstance.processEncrypt(p.buffer);
return;
}
if (p.operationSubType === "finish") {
result = cbcInstance.finishEncrypt();
cbcInstance = null;
return result;
}
result = cbcInstance.encrypt(p.buffer);
cbcInstance = null;
return result;
};
msrcryptoCbc.workerDecrypt = function (p) {
var result;
if (!cbcInstance) {
cbcInstance = msrcryptoCbc(msrcryptoBlockCipher.aes(p.keyData));
cbcInstance.init(p.algorithm.iv);
}
if (p.operationSubType === "process") {
cbcInstance.processDecrypt(p.buffer);
return;
}
if (p.operationSubType === "finish") {
result = cbcInstance.finishDecrypt();
cbcInstance = null;
return result;
}
result = cbcInstance.decrypt(p.buffer);
cbcInstance = null;
return result;
};
msrcryptoCbc.generateKey = function (p) {
if (p.algorithm.length % 8 !== 0) {
throw new Error();
}
return {
type: "keyGeneration",
keyData: msrcryptoPseudoRandom.getBytes(Math.floor(p.algorithm.length / 8)),
keyHandle: {
algorithm: p.algorithm,
extractable: p.extractable,
keyUsage: null || p.keyUsage,
type: "secret"
}
};
};
msrcryptoCbc.importKey = function (p) {
var keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, ["k"]);
p.algorithm.length = keyObject.k.length * 8;
return {
type: "keyImport",
keyData: keyObject.k,
keyHandle: {
algorithm: p.algorithm,
extractable: p.extractable || keyObject.extractable,
keyUsage: null || p.keyUsage,
type: "secret"
}
};
};
msrcryptoCbc.exportKey = function (p) {
var jsonKeyStringArray = msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData);
return { type: "keyExport", keyHandle: jsonKeyStringArray };
};
operations.register("importKey", "aes-cbc", msrcryptoCbc.importKey);
operations.register("exportKey", "aes-cbc", msrcryptoCbc.exportKey);
operations.register("generateKey", "aes-cbc", msrcryptoCbc.generateKey);
operations.register("encrypt", "aes-cbc", msrcryptoCbc.workerEncrypt);
operations.register("decrypt", "aes-cbc", msrcryptoCbc.workerDecrypt);
}
var msrcryptoGcm = function ( blockCipher) {
var utils = msrcryptoUtilities;
var mBuffer = [], mIvBytes, mAdditionalBytes, mTagLength, mJ0, mJ0inc, mH = blockCipher.encrypt(utils.getVector(16)), mGHashState = utils.getVector(16), mGHashBuffer = [], mCipherText = [], mGctrCb, mBytesProcessed = 0;
function ghash( hashSubkey, dataBytes) {
var blockCount = Math.floor(dataBytes.length / 16),
dataBlock;
for (var i = 0; i < blockCount; i++) {
dataBlock = dataBytes.slice(i * 16, i * 16 + 16);
mGHashState = blockMultiplication(utils.xorVectors(mGHashState, dataBlock), hashSubkey);
}
mGHashBuffer = dataBytes.slice(blockCount * 16);
return mGHashState;
}
function finishGHash() {
var u = 16 * Math.ceil(mBytesProcessed / 16) - mBytesProcessed;
var lenA = numberTo8Bytes(mAdditionalBytes.length * 8),
lenC = numberTo8Bytes(mBytesProcessed * 8);
var p = mGHashBuffer.concat(utils.getVector(u)).concat(lenA).concat(lenC);
return ghash(mH, p);
}
function blockMultiplication( blockX, blockY) {
var z = utils.getVector(16),
v = blockY.slice(),
r = [0xe1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
bit;
for (var i = 0; i < 128; i++) {
bit = getBit(blockX, i);
if (bit === 1) {
z = utils.xorVectors(z, v);
}
if (v[15] & 1) {
shiftRight(v);
v = utils.xorVectors(v, r);
} else {
shiftRight(v);
}
}
return z;
}
function shiftRight( dataBytes) {
for (var i = dataBytes.length - 1; i > 0; i--) {
dataBytes[i] = ((dataBytes[i - 1] & 1) << 7) | (dataBytes[i] >>> 1);
}
dataBytes[0] = dataBytes[0] >>> 1;
return dataBytes;
}
function getBit( byteArray, bitNumber) {
var byteIndex = Math.floor(bitNumber / 8);
return (byteArray[byteIndex] >> (7 - (bitNumber % 8))) & 1;
}
function inc( dataBytes) {
var carry = 256;
for (var i = 1; i <= 4; i++) {
carry = (carry >>> 8) + dataBytes[dataBytes.length - i];
dataBytes[dataBytes.length - i] = carry & 255;
}
return dataBytes;
}
function gctr( icb, dataBytes) {
var blockCount = Math.ceil(dataBytes.length / 16),
dataBlock,
result = [];
// We copy icb the first time gctr is called
if (mGctrCb !== icb) {
mGctrCb = icb.slice();
}
for (var block = 0; block < blockCount; block++) {
dataBlock = dataBytes.slice(block * 16, block * 16 + 16);
// The block cipher alters the input array, so we pass a copy.
var e = blockCipher.encrypt(mGctrCb.slice());
result = result.concat(utils.xorVectors(dataBlock, e));
mGctrCb = inc(mGctrCb);
}
return result;
}
function numberTo8Bytes(number) {
return [
0, 0, 0, 0,
(number >>> 24) & 255,
(number >>> 16) & 255,
(number >>> 8) & 255,
number & 255
];
}
function padBlocks( dataBytes) {
var padLen = 16 * Math.ceil(mAdditionalBytes.length / 16) - mAdditionalBytes.length;
return dataBytes.concat(utils.getVector(padLen));
}
function clearState() {
mBytesProcessed = 0;
mBuffer = [];
mCipherText = [];
mGHashState = utils.getVector(16);
mGHashBuffer = [];
mGctrCb = mIvBytes = mAdditionalBytes = null;
}
function init( ivBytes, additionalBytes, tagLength) {
mAdditionalBytes = additionalBytes || [];
mTagLength = isNaN(tagLength) ? 128 : tagLength;
if (mTagLength % 8 !== 0) {
throw new Error("DataError");
}
mIvBytes = ivBytes;
if (mIvBytes.length === 12) {
mJ0 = mIvBytes.concat([0, 0, 0, 1]);
} else {
var l = 16 * Math.ceil(mIvBytes.length / 16) - mIvBytes.length;
mJ0 = ghash(mH,
mIvBytes
.concat(utils.getVector(l + 8))
.concat(numberTo8Bytes(mIvBytes.length * 8)));
// Reset the ghash state so we don't affect the encrypt/decrypt ghash
mGHashState = utils.getVector(16);
}
mJ0inc = inc(mJ0.slice());
ghash(mH, padBlocks(mAdditionalBytes));
}
function encrypt( plainBytes) {
mBytesProcessed = plainBytes.length;
var c = gctr(mJ0inc, plainBytes);
ghash(mH, c);
var s = finishGHash();
var t = gctr(mJ0, s).slice(0, mTagLength / 8);
clearState();
return c.slice().concat(t);
}
function decrypt( cipherBytes, tagBytes) {
mBytesProcessed = cipherBytes.length;
var p = gctr(mJ0inc, cipherBytes);
ghash(mH, cipherBytes);
var s = finishGHash();
var t = gctr(mJ0, s).slice(0, mTagLength / 8);
clearState();
if (utils.arraysEqual(t, tagBytes)) {
return p;
} else {
return null;
}
}
function processEncrypt( plainBytes) {
// Append incoming bytes to the end of the existing buffered bytes
mBuffer = mBuffer.concat(plainBytes);
// Get a run of full blocks
var fullBlocks = mBuffer.slice(0, Math.floor(mBuffer.length / 16) * 16);
// Keep track of the total plain bytes processed
mBytesProcessed += fullBlocks.length;
// Set the buffer to the remaining bytes
mBuffer = mBuffer.slice(fullBlocks.length);
// Process the full block with gctr - gctr maintains it's own state
var c = gctr(mGctrCb || mJ0inc, fullBlocks);
mCipherText = mCipherText.concat(c);
// Process the returned blocks from gcrt
ghash(mH, c);
}
function processDecrypt( cipherBytes) {
// Append incoming bytes to the end of the existing buffered bytes
mBuffer = mBuffer.concat(cipherBytes);
// Get a run of full blocks.
// We leave enough data on the end so we don't process the tag.
var fullBlocks = mBuffer.slice(0, Math.floor((mBuffer.length - mTagLength / 8) / 16) * 16);
// Keep track of the total plain bytes processed
mBytesProcessed += fullBlocks.length;
// Set the buffer to the remaining bytes
mBuffer = mBuffer.slice(fullBlocks.length);
// Process the full block with gctr - gctr maintains it's own state
var c = gctr(mGctrCb || mJ0inc, fullBlocks);
mCipherText = mCipherText.concat(c);
// Process the returned blocks from gcrt
ghash(mH, fullBlocks);
}
function finishEncrypt() {
var c = gctr(mGctrCb, mBuffer);
mCipherText = mCipherText.concat(c);
mBytesProcessed += mBuffer.length;
var s = finishGHash();
var t = gctr(mJ0, s).slice(0, mTagLength / 8);
var result = mCipherText.slice().concat(t);
clearState();
return result;
}
function finishDecrypt() {
var tagLength = Math.floor(mTagLength / 8);
var tagBytes = mBuffer.slice( -tagLength);
mBuffer = mBuffer.slice(0, mBuffer.length - tagLength);
var c = gctr(mGctrCb, mBuffer);
mCipherText = mCipherText.concat(c);
mBytesProcessed += mBuffer.length;
var s = finishGHash();
var t = gctr(mJ0, s).slice(0, mTagLength / 8);
var result = mCipherText.slice();
clearState();
if (utils.arraysEqual(t, tagBytes)) {
return result;
} else {
throw new Error("OperationError");
}
}
return {
init: init,
encrypt: encrypt,
decrypt: decrypt,
processEncrypt: processEncrypt,
processDecrypt: processDecrypt,
finishEncrypt: finishEncrypt,
finishDecrypt: finishDecrypt
};
};
var gcm;
if (typeof operations !== "undefined") {
msrcryptoGcm.encrypt = function ( p) {
var result;
if (!gcm) {
gcm = msrcryptoGcm(msrcryptoBlockCipher.aes(p.keyData));
gcm.init(p.algorithm.iv, p.algorithm.additionalData, p.algorithm.tagLength);
}
if (p.operationSubType === "process") {
gcm.processEncrypt(p.buffer);
return;
}
if (p.operationSubType === "finish") {
result = gcm.finishEncrypt();
gcm = null;
return result;
}
result = gcm.encrypt(p.buffer);
gcm = null;
return result;
};
msrcryptoGcm.decrypt = function ( p) {
var result;
if (!gcm) {
gcm = msrcryptoGcm(msrcryptoBlockCipher.aes(p.keyData));
gcm.init(p.algorithm.iv, p.algorithm.additionalData, p.algorithm.tagLength);
}
if (p.operationSubType === "process") {
gcm.processDecrypt(p.buffer);
return;
}
if (p.operationSubType === "finish") {
result = gcm.finishDecrypt();
gcm = null;
return result;
}
var tagLength = Math.floor(p.algorithm.tagLength / 8);
var cipherBytes = p.buffer.slice(0, p.buffer.length - tagLength);
var tagBytes = p.buffer.slice( -tagLength);
result = gcm.decrypt(cipherBytes, tagBytes);
gcm = null;
return result;
};
msrcryptoGcm.generateKey = function ( p) {
if (p.algorithm.length % 8 !== 0) {
throw new Error();
}
return {
type: "keyGeneration",
keyData: msrcryptoPseudoRandom.getBytes(Math.floor(p.algorithm.length / 8)),
keyHandle: {
algorithm: p.algorithm,
extractable: p.extractable,
keyUsage: null || p.keyUsage,
type: "secret"
}
};
};
msrcryptoGcm.importKey = function ( p) {
var keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, ["k"]);
return {
type: "keyImport",
keyData: keyObject.k,
keyHandle: {
algorithm: p.algorithm,
extractable: p.extractable || keyObject.extractable,
keyUsage: null || p.keyUsage,
type: "secret"
}
};
};
msrcryptoGcm.exportKey = function ( p) {
var jsonKeyStringArray = msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData);
return { type: "keyExport", keyHandle: jsonKeyStringArray };
};
operations.register("importKey", "aes-gcm", msrcryptoGcm.importKey);
operations.register("exportKey", "aes-gcm", msrcryptoGcm.exportKey);
operations.register("generateKey", "aes-gcm", msrcryptoGcm.generateKey);
operations.register("encrypt", "aes-gcm", msrcryptoGcm.encrypt);
operations.register("decrypt", "aes-gcm", msrcryptoGcm.decrypt);
} function MsrcryptoPrng() {
/// <summary>Pseudo Random Number Generator function/class.</summary>
/// <remarks>This is the PRNG engine, not the entropy collector.
/// The engine must be initialized with adequate entropy in order to generate cryptographically secure
/// random numbers. It is hard to get entropy, but see the entropy functoin/class for the entropy gatherer.
/// This is not an object instantiation, but the definition of the object. The actual
/// object must be instantiated somewhere else as needed.
/// </remarks>
if (!(this instanceof MsrcryptoPrng)) {
throw new Error("create MsrcryptoPrng object with new keyword");
}
// Fallback for browsers which do not implements crypto API yet
// implementation of http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf.
// Use AES-256 in CTR mode of operation as defined in Section 10.2.1.
var initialized = false;
// Internal state definitions are as follows.
// v : internal variable that will ultimately be the random output
// key: the AES key (256 bits)
// keyLen: the AES key length in bytes
// reseedCounter: the number of requests for pseudorandom bits since instantiation/reseeding
// reseedInterval: Maximum number of generate calls per seed or reseed. SP800-90A says 2^48 for AES, we use 2^24.
var key;
var v;
var keyLen;
var seedLen;
var reseedCounter = 1;
var reseedInterval = 1 << 24;
// Initialize this instance (constructor like function)
initialize();
function addOne(counter) {
/// <summary>Adds one to a big integer represented in an array (the first argument).</summary>
/// <param name="counter" counter="Array">The counter byte array to add one to encoded in big endian; index 0 is the MSW.</param>
var i;
for (i = counter.length - 1; i >= 0; i -= 1) {
counter[i] += 1;
if (counter[i] >= 256) {
counter[i] = 0;
}
if (counter[i]) {
break;
}
}
}
function initialize() {
/// <summary>Instantiate the PRNG with given entropy and personalization string.</summary>
/// <param name="entropy" type="Array">Array of bytes obtained from the source of entropy input.</param>
/// <param name="personalizationString" type="Array">Optional application-provided personalization string.</param>
key = msrcryptoUtilities.getVector(32);
v = msrcryptoUtilities.getVector(16); // AES block length
keyLen = 32;
seedLen = 48; // From SP800-90A, section 10.2.1 as of 2014.
reseedCounter = 1;
}
function reseed(entropy, additionalEntropy) {
/// <summary>Reseed the PRNG with additional entropy.</summary>
/// <param name="entropy" type="Array">Input entropy.</param>
/// <param name="additionalEntropy" type="Array">Optional additional entropy input.</param>
additionalEntropy = additionalEntropy || [0];
if (additionalEntropy.length > seedLen) {
throw new Error("Incorrect entropy or additionalEntropy length");
}
additionalEntropy = additionalEntropy.concat(msrcryptoUtilities.getVector(seedLen - additionalEntropy.length));
// Process the entropy input in blocks with the same additional entropy.
// This is equivalent to the caller chunking entropy in blocks and calling this function for each chunk.
entropy = entropy.concat(msrcryptoUtilities.getVector((seedLen - (entropy.length % seedLen)) % seedLen));
for (var i = 0; i < entropy.length; i += seedLen) {
var seedMaterial = msrcryptoUtilities.xorVectors(entropy.slice(i, i + seedLen), additionalEntropy);
update(seedMaterial);
}
reseedCounter = 1;
}
function update(providedData) {
/// <summary>Add the providedData to the internal entropy pool, and update internal state.</summary>
/// <param name="providedData" type="Array">Input to add to the internal entropy pool.</param>
var temp = [];
var blockCipher = new msrcryptoBlockCipher.aes(key);
while (temp.length < seedLen) {
addOne(v);
var toEncrypt = v.slice(0, 16);
var outputBlock = blockCipher.encrypt(toEncrypt); // AES-256
temp = temp.concat(outputBlock);
}
temp = msrcryptoUtilities.xorVectors(temp, providedData);
key = temp.slice(0, keyLen);
v = temp.slice(keyLen);
}
function generate(requestedBytes, additionalInput) {
/// <summary>Generate pseudo-random bits, and update the internal PRNG state.</summary>
/// <param name="requestedBytes" type="Number">Number of pseudorandom bytes to be returned.</param>
/// <param name="additionalInput" type="Array">Application-provided additional input array (optional).</param>
/// <returns>Generated pseudorandom bytes.</returns>
if (requestedBytes >= 65536) {
throw new Error("too much random requested");
}
if (reseedCounter > reseedInterval) {
throw new Error("Reseeding is required");
}
if (additionalInput && additionalInput.length > 0) {
while (additionalInput.length < seedLen) {
additionalInput = additionalInput.concat(msrcryptoUtilities.getVector(seedLen - additionalInput.length));
}
update(additionalInput);
} else {
additionalInput = msrcryptoUtilities.getVector(seedLen);
}
var temp = [];
var blockCipher = new msrcryptoBlockCipher.aes(key);
while (temp.length < requestedBytes) {
addOne(v);
var toEncrypt = v.slice(0, v.length);
var outputBlock = blockCipher.encrypt(toEncrypt);
temp = temp.concat(outputBlock);
}
temp = temp.slice(0, requestedBytes);
update(additionalInput);
reseedCounter += 1;
return temp;
}
return {
reseed: reseed,
/// <summary>Reseed the PRNG with additional entropy.</summary>
/// <param name="entropy" type="Array">Input entropy.</param>
/// <param name="additionalEntropy" type="Array">Optional additional entropy input.</param>
init: function (entropy, personalization) {
/// <summary>Initialize the PRNG by seeing with entropy and optional input data.</summary>
/// <param name="entropy" type="Array">Input entropy.</param>
/// <param name="personalization" type="Array">Optional input.</param>
if (entropy.length < seedLen) {
throw new Error("Initial entropy length too short");
}
initialize();
reseed(entropy, personalization);
initialized = true;
},
getBytes: function (length, additionalInput) {
if (!initialized) {
throw new Error("can't get randomness before initialization");
}
return generate(length, additionalInput);
},
getNonZeroBytes: function (length, additionalInput) {
if (!initialized) {
throw new Error("can't get randomness before initialization");
}
var result = [], buff;
while (result.length < length) {
buff = generate(length, additionalInput);
for (var i = 0 ; i < buff.length; i += 1) {
if (buff[i] !== 0) {
result.push(buff[i]);
}
}
}
return result.slice(0, length);
}
};
}
// This is the PRNG object per instantiation, including one per worker.
// The instance in the main thread is used to seed the instances in workers.
// TODO: Consider combining the entropy pool in the main thread with the PRNG instance in the main thread.
var msrcryptoPseudoRandom = new MsrcryptoPrng();
function MsrcryptoEntropy() {
/// <summary>Opportunistic entropy collector.</summary>
/// <remarks>See E.Stark, M.Hamburg, D.Boneh, "Symmetric Cryptography in Javascript", ACSAC, 2009.
/// This is not an object instantiation, but the definition of the object. The actual
/// object must be instantiated somewhere else as needed.
/// If window.{crypto,msCrypto}.getRandomValues() function is present, do not register mouse and JS load events,
/// because they slow down the execution, and it is not clear how much they contributed over and above
/// a cryptographic random value.
/// </remarks>
var poolLength = 48; // In bytes, from SP800-90A, Section 10.2.1. See random.js for constraints.
var collectorPool = [];
var collectorPoolLength = 128; // Bytes to collect before stopping; collectors are restartable.
var collectorsRegistered = 0;
var entropyPoolPrng = new MsrcryptoPrng();
var initialized = false;
var cryptographicPRNGPresent = false;
var headerList = ["Cookie", "RedirectUri", "ETag", "x-ms-client-antiforgery-id", "x-ms-client-request-id", "x-ms-client-session-id", "SubscriptionPool"];
function collectEntropy() {
/// <summary>Initialize the internal pool with as much randomness as one can get in JS.
/// In the worst case, there is zero bits of entropy.</summary>
var i, pool = [];
// In Safari, as of r39510, reportedly, Math.random() is cryptographically secure on Mac and Windows.
// Even if it isn't, mix that in via XORing into the existing array.
// According to ECMA, Math.random() returns [0,1). Thus, multiply it by 256 to get [0,256).
for (i = 0; i < poolLength; i += 1) {
pool[i] = Math.floor(Math.random() * 256);
}
// For browsers that implement window.crypto.getRandomValues, use it.
var prngCrypto = window.crypto || window.msCrypto; // WARNING: !!! Do not put this in a function (remember polyfill) !!!
if (prngCrypto && typeof prngCrypto.getRandomValues === "function") {
if (window.Uint8Array) {
var res = new window.Uint8Array(poolLength);
prngCrypto.getRandomValues(res);
pool = pool.concat(Array.apply(null, res));
cryptographicPRNGPresent = true;
}
}
// Read HTTP headers that contain entropy and reseed the entropy pool
var req = new XMLHttpRequest();
for (i = 0; i < headerList.length; i += 1) {
try {
var header = req.getResponseHeader(headerList[i]);
if (header) {
var arr = msrcryptoUtilities.stringToBytes(header);
pool = pool.concat(arr);
}
}
catch (err) {
// Ignore any header I can't get
}
}
if (!cryptographicPRNGPresent) {
// Add any data in the collector pool, empty the collector pool, and restart collectors.
pool = pool.concat(collectorPool.splice(0, collectorPool.length));
collectors.startCollectors();
}
// Worst case: initialized with Math.random()
initialized ? entropyPoolPrng.reseed(pool) : entropyPoolPrng.init(pool);
initialized = true;
}
function updatePool(entropyData) {
/// <summary>Collect the incoming data into the pool, and
/// empty the pool into the entropy PRNG state when the pool is full.
/// This function is additive entropy, only; this is not the main source of entropy.</summary>
/// <param name="entropyData" type="Array">Entropy input.</param>
for (var i = 0; i < entropyData.length; ++i) {
collectorPool.push(entropyData[i]);
}
if (collectorPool.length >= collectorPoolLength) {
// Stop the collectors (performance reasons).
// The real entropy does not come from the event callbacks: these are at best uniquifiers.
collectors.stopCollectors();
}
}
// Event listeners are not supported in IE 8.
// See https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener?redirectlocale=en-US&redirectslug=DOM%2FEventTarget.addEventListener
// to add IE8 support.
// BUGBUG: For the time being, I am not bothering with IE8 support - fix this.
var collectors = (function () {
return {
startCollectors: function () {
if (!this.collectorsRegistered) {
if (window.addEventListener) {
window.addEventListener("mousemove", this.MouseEventCallBack, true);
window.addEventListener("load", this.LoadTimeCallBack, true);
} else if (document.attachEvent) {
document.attachEvent("onmousemove", this.MouseEventCallBack);
document.attachEvent("onload", this.LoadTimeCallBack);
} else {
throw new Error("Can't attach events for entropy collection");
}
this.collectorsRegistered = 1;
}
},
stopCollectors: function () {
if (this.collectorsRegistered) {
if (window.removeEventListener) {
window.removeEventListener("mousemove", this.MouseEventCallBack, 1);
window.removeEventListener("load", this.LoadTimeCallBack, 1);
} else if (window.detachEvent) {
window.detachEvent("onmousemove", this.MouseEventCallBack);
window.detachEvent("onload", this.LoadTimeCallBack);
}
this.collectorsRegistered = 0;
}
},
MouseEventCallBack: function (eventData) {
/// <summary>Add the mouse coordinates to the entropy pool and the Date.</summary>
/// <param name="eventData">Event data with mouse information.</param>
var d = (new Date()).valueOf();
var x = eventData.x || eventData.clientX || eventData.offsetX || 0;
var y = eventData.y || eventData.clientY || eventData.offsetY || 0;
var arr = [d & 0x0ff, (d >> 8) & 0x0ff, (d >> 16) & 0x0ff, (d >> 24) & 0x0ff,
x & 0x0ff, (x >> 8) & 0x0ff, y & 0x0ff, (y >> 8) & 0x0ff];
updatePool(arr);
},
LoadTimeCallBack: function () {
/// <summary>Add date to the entropy pool.</summary>
/// <remarks>Date valueOf() returns milliseconds since midnight 1/1/1970 UTC in a 32 bit integer</remarks>
var d = (new Date()).valueOf();
var dateArray = [d & 0x0ff, (d >> 8) & 0x0ff, (d >> 16) & 0x0ff, (d >> 24) & 0x0ff];
updatePool(dateArray);
}
};
})();
return {
init: function () {
collectEntropy();
// Register collectors
if (!cryptographicPRNGPresent && !collectorsRegistered) {
try {
collectors.startCollectors();
}
catch (e) {
// Ignore errors instead of trying to do something browser specific. That is not tractable.
// It is expected that the calling program injects most of the entropy or the build-in collectors
// contributes rather than registered events.
}
}
},
reseed: function (entropy) {
/// <summary>Mix in entropy into the pool.</summary>
/// <param name="entropy" type="Array">Entropy to mix in.</param>
entropyPoolPrng.reseed(entropy);
},
read: function (length) {
/// <summary>Read entropy from the entropy pool. This function fails if there isn't enough entropy.</summary>
/// <param name="length" type="Number">Number of bytes of requested entropy.</param>
/// <returns type="Array">Entropy if there is enough in the pool, or undefined if there isn't enough entropy.</returns>
if (!initialized) {
throw new Error("Entropy pool is not initialized.");
}
var ret = entropyPoolPrng.getBytes(length);
// TODO: Do this async?
// No, another call may come through before the pool is reseeded.
// All PRNGs have their own running state anyhow. They can reseed themselves in async mode, if need be.
collectEntropy();
return ret;
}
};
}
var msrcryptoRsaBase = function (keyStruct) {
var utils = msrcryptoUtilities,
keyIsPrivate = keyStruct.hasOwnProperty("n") && keyStruct.hasOwnProperty("d"),
keyIsCrt = keyStruct.hasOwnProperty("p") && keyStruct.hasOwnProperty("q"),
modulusLength = keyStruct.n.length;
function toBytes(digits) {
var bytes = cryptoMath.digitsToBytes(digits);
// Add leading zeros until the message is the proper length.
utils.padFront(bytes, 0, modulusLength);
return bytes;
}
function modExp( dataBytes, expBytes, modulusBytes) {
/// <returns type="Array">Result in a digit array.</returns>
var exponent = cryptoMath.bytesToDigits(expBytes);
var group = cryptoMath.IntegerGroup(modulusBytes);
var base = group.createElementFromBytes(dataBytes);
var result = group.modexp(base, exponent);
return result.m_digits;
}
function decryptModExp(cipherBytes) {
var resultElement = modExp(cipherBytes, keyStruct.d, keyStruct.n);
return toBytes(resultElement);
}
function decryptCrt(cipherBytes) {
var p = keyStruct.p,
q = keyStruct.q,
dp = keyStruct.dp,
dq = keyStruct.dq,
invQ = keyStruct.qi,
pDigits = cryptoMath.bytesToDigits(p),
qDigits = cryptoMath.bytesToDigits(q),
temp = new Array(pDigits.length + qDigits.length),
m1Digits = new Array(pDigits.length + 1),
m2Digits = new Array(qDigits.length + 1),
cDigits = cryptoMath.bytesToDigits(cipherBytes);
// 'm1' = (c mod p)^dP mod p
cryptoMath.reduce(cDigits, pDigits, temp);
cryptoMath.modExp(temp, cryptoMath.bytesToDigits(dp), pDigits, m1Digits);
// 'm2' = (c mod q)^dQ mod q
cryptoMath.reduce(cDigits, qDigits, temp);
cryptoMath.modExp(temp, cryptoMath.bytesToDigits(dq), qDigits, m2Digits);
// 'diff' = (m1 - m2). Compute as follows to have |m1 - m2|.
// m1 - m2 if m1>=m2.
// m2 - m1 if m1<m2.
// Correct the sign after modular multiplication by qInv by subtracting the product from p.
var carry = cryptoMath.subtract(m1Digits, m2Digits, temp);
if (carry !== 0) {
cryptoMath.subtract(m2Digits, m1Digits, temp);
}
// 'h' = (m1 - m2)^qInv mod p
cryptoMath.modMul(temp, cryptoMath.bytesToDigits(invQ), pDigits, cDigits);
if (carry !== 0) {
cryptoMath.subtract(pDigits, cDigits, cDigits);
}
// 'm2' + q*h
cryptoMath.multiply(cDigits, qDigits, temp);
cryptoMath.add(m2Digits, temp, m1Digits);
return toBytes(m1Digits);
}
return {
encrypt: function (messageBytes) {
return toBytes(modExp(messageBytes, keyStruct.e, keyStruct.n));
},
decrypt: function (cipherBytes) {
if (keyIsCrt) {
return decryptCrt(cipherBytes);
}
if (keyIsPrivate) {
return decryptModExp(cipherBytes);
}
throw new Error("missing private key");
}
};
};
var rsaShared = {
mgf1: function (seedBytes, maskLen, hashFunction) {
var t = [], bytes, hash, counter,
hashByteLen = hashFunction.hashLen / 8;
for (counter = 0; counter <= Math.floor(maskLen / hashByteLen) ; counter += 1) {
bytes = [counter >>> 24 & 0xff,
counter >>> 16 & 0xff,
counter >>> 8 & 0xff,
counter & 0xff];
hash = hashFunction.computeHash(seedBytes.concat(bytes));
t = t.concat(hash);
}
return t.slice(0, maskLen);
},
checkMessageVsMaxHash: function (messageBytes, hashFunction) {
// The max array size in JS is 2^32-1
if (messageBytes.length > (hashFunction.maxMessageSize || 0xFFFFFFFF)) {
throw new Error("message too long");
}
return;
}
};
var rsaMode = rsaMode || {};
rsaMode.oaep = function (keyStruct, hashFunction) {
var utils = msrcryptoUtilities,
random = msrcryptoPseudoRandom,
size = keyStruct.n.length;
if (hashFunction === null) {
throw new Error("must supply hashFunction");
}
function pad( message, label) {
var lHash, psLen, psArray, i, db, seed;
var dbMask, maskeddb, seedMask, maskedSeed;
var encodedMessage;
if (message.length > (size - 2 * (hashFunction.hashLen / 8) - 2)) {
throw new Error("Message too long.");
}
label || (label = []);
lHash = hashFunction.computeHash( label);
psLen = size - message.length - (2 * lHash.length) - 2;
psArray = utils.getVector(psLen);
// 'db' = 'lHash' || 'psArray' || 0x01 || message
db = lHash.concat(psArray, [1], message);
seed = random.getBytes(lHash.length);
dbMask = rsaShared.mgf1(seed, size - lHash.length - 1, hashFunction);
maskeddb = utils.xorVectors(db, dbMask);
seedMask = rsaShared.mgf1(maskeddb, lHash.length, hashFunction);
maskedSeed = utils.xorVectors(seed, seedMask);
encodedMessage = [0].concat(maskedSeed).concat(maskeddb);
message = encodedMessage.slice();
return encodedMessage;
}
function unpad( encodedBytes, labelBytes) {
var lHash, maskedSeed, maskeddb, seedMask;
var seed, dbMask, db;
var lHashp;
if (!labelBytes) {
labelBytes = [];
}
lHash = hashFunction.computeHash(labelBytes);
if (encodedBytes[0] !== 0) {
throw new Error("Encryption Error");
}
maskedSeed = encodedBytes.slice(1, lHash.length + 1);
maskeddb = encodedBytes.slice(lHash.length + 1);
seedMask = rsaShared.mgf1(maskeddb, lHash.length, hashFunction);
seed = utils.xorVectors(maskedSeed, seedMask);
dbMask = rsaShared.mgf1(seed, size - lHash.length - 1, hashFunction);
db = utils.xorVectors(maskeddb, dbMask);
lHashp = db.slice(0, lHash.length);
// lHashp should equal lHash or 'Encryption Error'
if (!utils.arraysEqual(lHash, lHashp)) {
throw new Error("Encryption Error");
}
db = db.slice(lHash.length);
// There will be a bunch of zeros followed by a
var i = db.indexOf(1);
return db.slice(i + 1);
}
return {
pad: function ( messageBytes, labelBytes) {
return pad(messageBytes, labelBytes);
},
unpad: function ( encodedBytes, labelBytes) {
return unpad(encodedBytes, labelBytes);
}
};
};
var rsaMode = rsaMode || {};
rsaMode.pkcs1Encrypt = function (keyStruct) {
var random = msrcryptoPseudoRandom,
size = keyStruct.n.length;
function pad(data) {
var randomness;
if (data.length > size - 11) {
throw new Error("message too long");
}
// A minimum of 8 random bytes
randomness = random.getNonZeroBytes(size - data.length - 3);
return [0, 2].concat(randomness, [0], data);
}
function unpad(paddedData) {
var i;
for (i = 1; i < paddedData.length; i += 1) {
if (paddedData[i] === 0) {
break;
}
}
return paddedData.slice(i + 1);
}
return {
pad: function (messageBytes) {
return pad(messageBytes);
},
unpad: function (encodedBytes) {
return unpad(encodedBytes);
}
};
};
rsaMode.pkcs1Sign = function (keyStruct, hashFunction) {
var utils = msrcryptoUtilities,
size = keyStruct.n.length;
function emsa_pkcs1_v15_encode(messageBytes) {
var paddedData,
hash,
tlen;
hash = hashFunction.computeHash(messageBytes.slice());
paddedData = hashFunction.der.concat(hash);
tlen = paddedData.length;
if (size < tlen + 11) {
throw new Error("intended encoded message length too short");
}
return [0x00, 0x01].concat(
utils.getVector(size - tlen - 3, 0xFF),
[0],
paddedData);
}
return {
sign: function (messageBytes) {
return emsa_pkcs1_v15_encode(messageBytes);
},
verify: function (signatureBytes, messageBytes) {
var emp = emsa_pkcs1_v15_encode(messageBytes);
return utils.arraysEqual(signatureBytes, emp);
}
};
};
var rsaMode = rsaMode || {};
rsaMode.pss = function (keyStruct, hashFunction) {
var utils = msrcryptoUtilities,
random = msrcryptoPseudoRandom;
function emsa_pss_encode(messageBytes, saltLength, salt) {
var emBits = (keyStruct.n.length * 8) - 1,
emLen = Math.ceil(emBits / 8), mHash = hashFunction.computeHash(messageBytes);
saltLength = salt ? salt.length : saltLength || mHash.length;
if (emLen < (mHash.length + saltLength + 2)) {
throw new Error("encoding error");
} salt = salt || random.getBytes(saltLength);
// M' = (0x) 00 00 00 00 00 00 00 00 || mHash || salt
var mp = [0, 0, 0, 0, 0, 0, 0, 0].concat(mHash, salt);
var h = hashFunction.computeHash(mp);
var ps = utils.getVector(emLen - salt.length - h.length - 2);
var db = ps.concat([1], salt);
var dbMask = rsaShared.mgf1(h, emLen - h.length - 1, hashFunction);
var maskedDb = utils.xorVectors(db, dbMask);
// Set the ((8 * emLen) - emBits) of the leftmost octect in maskedDB to zero
var mask = 0;
for (var i = 0; i < 8 - ((8 * emLen) - emBits) ; i++) {
mask += 1 << i;
}
maskedDb[0] &= mask;
var em = maskedDb.concat(h, [0xbc]);
return em;
}
function emsa_pss_verify( signatureBytes, messageBytes, saltLength) {
var emBits = (keyStruct.n.length * 8) - 1;
var emLen = Math.ceil(emBits / 8);
var mHash = hashFunction.computeHash(messageBytes);
var hLen = mHash.length;
saltLength = saltLength || hLen;
if (emLen < (hLen + saltLength + 2)) {
return false;
}
var maskedDb = signatureBytes.slice(0, emLen - hLen - 1);
var h = signatureBytes.slice(maskedDb.length, maskedDb.length + hLen);
var dbMask = rsaShared.mgf1(h, emLen - hLen - 1, hashFunction);
var db = utils.xorVectors(maskedDb, dbMask);
// Set the leftmost 8 * emLen - emBits of db[0] to zero
db[0] &= 0xFF >>> (8 - ((8 * emLen) - emBits));
// Verify the leftmost bytes are zero
for (var i = 0; i < (emLen - hLen - saltLength - 2) ; i++) {
if (db[i] !== 0) {
return false;
}
}
if (db[emLen - hLen - saltLength - 2] !== 0x01) {
return false;
}
var salt = db.slice(-saltLength);
// M' = (0x) 00 00 00 00 00 00 00 00 || mHash || salt
var mp = [0, 0, 0, 0, 0, 0, 0, 0].concat(mHash, salt);
var hp = hashFunction.computeHash(mp);
return utils.arraysEqual(hp, h);
}
return {
sign: function (messageBytes, saltLength, salt) {
return emsa_pss_encode(messageBytes, saltLength, salt);
},
verify: function (signatureBytes, messageBytes, saltLength) {
return emsa_pss_verify(signatureBytes, messageBytes, saltLength);
}
};
};
var msrcryptoRsa = function (keyStruct, mode, hashFunction) {
var rsaBase = msrcryptoRsaBase(keyStruct);
if (!mode) {
throw new Error("padding mode");
}
function checkHash() {
if (!hashFunction || !hashFunction.computeHash) {
throw new Error("missing hash function");
}
}
var paddingFunction = null,
unPaddingFunction = null;
var padding;
switch (mode) {
case "rsaes-pkcs1-v1_5":
padding = rsaMode.pkcs1Encrypt(keyStruct);
break;
case "rsassa-pkcs1-v1_5":
checkHash();
padding = rsaMode.pkcs1Sign(keyStruct, hashFunction);
break;
case "rsa-oaep":
checkHash();
padding = rsaMode.oaep(keyStruct, hashFunction);
break;
case "rsa-pss":
checkHash();
padding = rsaMode.pss(keyStruct, hashFunction);
break;
case "raw":
padding = {
pad: function (mb) { return mb; },
unpad: function (eb) { return eb; }
};
break;
default:
throw new Error("invalid mode");
}
if (padding) {
paddingFunction = padding.pad || padding.sign;
unPaddingFunction = padding.unpad || padding.verify;
}
var returnObj = {
encrypt: function ( dataBytes, labelBytes) {
var paddedData;
if (paddingFunction !== null) {
// OAEP padding can take two arguments
paddedData = paddingFunction(dataBytes, labelBytes);
} else {
// Slice() has optional arguments
paddedData = dataBytes.slice();
}
return rsaBase.encrypt(paddedData);
},
decrypt: function ( cipherBytes, labelBytes) {
var decryptedData = rsaBase.decrypt(cipherBytes);
if (unPaddingFunction !== null) {
// OAEP padding can take two arguments
decryptedData = unPaddingFunction(decryptedData, labelBytes);
} else {
decryptedData = decryptedData.slice(0);
}
return decryptedData;
},
signData: function ( messageBytes, saltLength, salt) {
return rsaBase.decrypt(paddingFunction(messageBytes, saltLength, salt));
},
verifySignature: function ( signature, messageBytes, saltLength) {
var decryptedSig = rsaBase.encrypt(signature);
return unPaddingFunction(decryptedSig, messageBytes, saltLength);
},
mode: mode
};
return returnObj;
};
if (typeof operations !== "undefined") {
msrcryptoRsa.sign = function ( p) {
var rsaObj,
hashName = p.algorithm.hash.name,
hashFunc = msrcryptoHashFunctions[hashName.toLowerCase()],
saltLength = p.algorithm.saltLength,
salt = p.algorithm.salt;
rsaObj = msrcryptoRsa(p.keyData, p.algorithm.name, hashFunc);
return rsaObj.signData(p.buffer, saltLength, salt);
};
msrcryptoRsa.verify = function ( p) {
var hashName = p.algorithm.hash.name,
hashFunc = msrcryptoHashFunctions[hashName.toLowerCase()],
rsaObj,
saltLength = p.algorithm.saltLength;
rsaObj = msrcryptoRsa(p.keyData, p.algorithm.name, hashFunc);
return rsaObj.verifySignature(p.signature, p.buffer, saltLength);
};
msrcryptoRsa.workerEncrypt = function ( p) {
var result,
rsaObj,
hashFunc,
hashName;
switch (p.algorithm.name) {
case "rsaes-pkcs1-v1_5":
rsaObj = msrcryptoRsa(p.keyData, p.algorithm.name);
result = rsaObj.encrypt(p.buffer);
break;
case "rsa-oaep":
hashName = p.algorithm.hash.name;
if (!hashName) {
throw new Error("unsupported hash algorithm");
}
hashFunc = msrcryptoHashFunctions[hashName.toLowerCase()];
rsaObj = msrcryptoRsa(p.keyData, p.algorithm.name, hashFunc);
result = rsaObj.encrypt(p.buffer);
break;
default:
throw new Error("unsupported algorithm");
}
return result;
};
msrcryptoRsa.workerDecrypt = function ( p) {
var result,
rsaObj,
hashFunc;
switch (p.algorithm.name) {
case "rsaes-pkcs1-v1_5":
rsaObj = msrcryptoRsa(p.keyData, p.algorithm.name);
result = rsaObj.decrypt(p.buffer);
break;
case "rsa-oaep":
var hashName = p.algorithm.hash.name;
if (!hashName) {
throw new Error("unsupported hash algorithm");
}
hashFunc = msrcryptoHashFunctions[hashName.toLowerCase()];
rsaObj = msrcryptoRsa(p.keyData, p.algorithm.name, hashFunc);
result = rsaObj.decrypt(p.buffer);
break;
default:
throw new Error("unsupported algorithm");
}
return result;
};
msrcryptoRsa.importKey = function ( p) {
var keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, ["n", "e", "d", "q", "p", "dq", "dp", "qi"]);
return {
type: "keyImport",
keyData: keyObject,
keyHandle: {
algorithm: p.algorithm,
extractable: p.extractable || keyObject.extractable,
keyUsage: null || p.keyUsage, // IE11 returns null here
type: (keyObject.d || keyObject.dq) ? "private" : "public"
}
};
};
msrcryptoRsa.exportKey = function ( p) {
var jsonKeyStringArray = msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData);
return { type: "keyExport", keyHandle: jsonKeyStringArray };
};
operations.register("sign", "rsassa-pkcs1-v1_5", msrcryptoRsa.sign);
operations.register("sign", "rsa-pss", msrcryptoRsa.sign);
operations.register("verify", "rsassa-pkcs1-v1_5", msrcryptoRsa.verify);
operations.register("verify", "rsa-pss", msrcryptoRsa.verify);
operations.register("encrypt", "rsa-oaep", msrcryptoRsa.workerEncrypt);
operations.register("encrypt", "rsaes-pkcs1-v1_5", msrcryptoRsa.workerEncrypt);
operations.register("decrypt", "rsa-oaep", msrcryptoRsa.workerDecrypt);
operations.register("decrypt", "rsaes-pkcs1-v1_5", msrcryptoRsa.workerDecrypt);
operations.register("importKey", "rsa-oaep", msrcryptoRsa.importKey);
operations.register("importKey", "rsaes-pkcs1-v1_5", msrcryptoRsa.importKey);
operations.register("importKey", "rsassa-pkcs1-v1_5", msrcryptoRsa.importKey);
operations.register("importKey", "rsa-pss", msrcryptoRsa.importKey);
operations.register("exportKey", "rsa-oaep", msrcryptoRsa.exportKey);
operations.register("exportKey", "rsaes-pkcs1-v1_5", msrcryptoRsa.exportKey);
operations.register("exportKey", "rsassa-pkcs1-v1_5", msrcryptoRsa.exportKey);
operations.register("exportKey", "rsa-pss", msrcryptoRsa.exportKey);
}
/// The "concat" key derivation function from NIST SP-800-56A.
var msrcryptoKdf = function (hashFunction) {
var utils = msrcryptoUtilities;
function deriveKey(secretBytes, otherInfo, keyOutputLength) {
/// <summary></summary>
/// <param name="secretBytes" type="Array"></param>
/// <param name="otherInfo" type="Array"></param>
/// <param name="keyOutputLength" type="Number"></param>
/// <returns type="Array"></returns>
var reps = Math.ceil(keyOutputLength / (hashFunction.hashLen / 8)),
counter = 1,
digest = secretBytes.concat(otherInfo),
output = [];
for (var i = 0; i < reps; i++) {
var data = utils.int32ToBytes(counter++).concat(digest);
var /*type(Array)*/ h = hashFunction.computeHash(data);
output = output.concat(h);
}
return output.slice(0, keyOutputLength);
}
return {
deriveKey: deriveKey
};
};
var msrcryptoKdfInstance = null;
if (typeof operations !== "undefined") {
msrcryptoKdf.deriveKey = function ( p) {
var utils = msrcryptoUtilities;
var hashName = p.algorithm.hash.name;
var hashFunction = msrcryptoHashFunctions[hashName.toLowerCase()];
msrcryptoKdfInstance = msrcryptoKdf(hashFunction);
var alg = p.algorithm;
var otherInfo =
utils.toArray(alg.algorithmId).concat(
utils.toArray(alg.partyUInfo),
utils.toArray(alg.partyVInfo),
utils.toArray(alg.publicInfo),
utils.toArray(alg.privateInfo));
var result =
msrcryptoKdfInstance.deriveKey(p.keyData, otherInfo, p.derivedKeyType.length);
msrcryptoKdfInstance = null;
return {
type: "keyDerive",
keyData: result,
keyHandle: {
algorithm: p.derivedKeyType,
extractable: p.extractable,
keyUsage: null || p.keyUsage,
type: "secret"
}
};
};
msrcryptoKdf.deriveBits = function ( p) {
var hashName = p.algorithm.hash.name;
var hashFunction = msrcryptoHashFunctions[hashName.toLowerCase()];
msrcryptoKdfInstance = msrcryptoKdf(hashFunction);
var alg = p.algorithm;
var otherInfo =
alg.algorithmId.concat(
alg.partyUInfo,
alg.partyVInfo,
alg.publicInfo || [],
alg.privateInfo || []);
var result =
msrcryptoKdfInstance.deriveKey(p.keyData, otherInfo, p.length);
msrcryptoKdfInstance = null;
return result;
};
operations.register("deriveKey", "concat", msrcryptoKdf.deriveKey);
operations.register("deriveBits", "concat", msrcryptoKdf.deriveBits);
}
var msrcryptoEcdh = function (curve) {
var btd = cryptoMath.bytesToDigits,
dtb = cryptoMath.digitsToBytes,
e = curve,
ecop = new cryptoECC.EllipticCurveOperatorFp(curve);
function generateKey(privateKeyBytes) {
/// <summary></summary>
/// <param name="privateKeyBytes" type="Array" optional="true">
/// For testing purposes we allow the key bytes to be passed in
/// instead of randomly generated.
/// </param>
/// <returns type=""></returns>
var privateKey = [],
randomBytes = msrcryptoPseudoRandom.getBytes(
curve.order.length * cryptoMath.DIGIT_NUM_BYTES);
cryptoMath.reduce(
cryptoMath.bytesToDigits(randomBytes),
e.order,
privateKey);
var publicKey = e.allocatePointStorage();
ecop.scalarMultiply(privateKey, e.generator, publicKey);
return {
privateKey: {
x: dtb(publicKey.x),
y: dtb(publicKey.y),
d: dtb(privateKey)
},
publicKey: {
x: dtb(publicKey.x),
y: dtb(publicKey.y)
}
};
}
function deriveBits(privateKey, publicKey, length) {
var publicPoint = new cryptoECC.EllipticCurvePointFp(
e, false, btd(publicKey.x), btd(publicKey.y), null, false);
var sharedSecretPoint = e.allocatePointStorage();
ecop.convertToJacobianForm(sharedSecretPoint);
ecop.convertToMontgomeryForm(sharedSecretPoint);
ecop.scalarMultiply(btd(privateKey.d), publicPoint, sharedSecretPoint);
ecop.convertToAffineForm(sharedSecretPoint);
ecop.convertToStandardForm(sharedSecretPoint);
var secretBytes = cryptoMath.digitsToBytes(sharedSecretPoint.x);
if (length && (secretBytes.length * 8) < length) {
throw new Error("DataError");
}
return length ? secretBytes.slice(0, length / 8) : secretBytes;
}
function computePublicKey(privateKeyBytes) {
if (!e.generator.isInMontgomeryForm) {
ecop.convertToMontgomeryForm(e.generator);
}
var publicKey = e.allocatePointStorage();
ecop.convertToJacobianForm(publicKey);
ecop.convertToMontgomeryForm(publicKey);
ecop.scalarMultiply(btd(privateKeyBytes), e.generator, publicKey);
return {
x: dtb(publicKey.x),
y: dtb(publicKey.y)
};
}
return {
generateKey: generateKey,
deriveBits: deriveBits,
computePublicKey: computePublicKey
};
};
var ecdhInstance = null;
if (typeof operations !== "undefined") {
msrcryptoEcdh.deriveBits = function (p) {
var curve = cryptoECC.createCurve(p.algorithm.namedCurve.toUpperCase());
var privateKey = p.keyData;
var publicKey = p.additionalKeyData;
ecdhInstance = msrcryptoEcdh(curve);
var secretBytes = ecdhInstance.deriveBits(privateKey, publicKey, p.length);
return secretBytes;
};
msrcryptoEcdh.generateKey = function (p) {
var curve = cryptoECC.createCurve(p.algorithm.namedCurve.toUpperCase());
ecdhInstance = msrcryptoEcdh(curve);
var keyPairData = ecdhInstance.generateKey();
return {
type: "keyPairGeneration",
keyPair: {
publicKey: {
keyData: keyPairData.publicKey,
keyHandle: {
algorithm: p.algorithm,
extractable: p.extractable,
keyUsage: p.keyUsage,
type: "public"
}
},
privateKey: {
keyData: keyPairData.privateKey,
keyHandle: {
algorithm: p.algorithm,
extractable: p.extractable,
keyUsage: p.keyUsage,
type: "private"
}
}
}
};
};
msrcryptoEcdh.importKey = function (p) {
var keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, ["x", "y", "d", "crv"]);
// If only private key data 'd' is imported, create x and y
if (keyObject.d && (!keyObject.x || !keyObject.y)) {
var curve = cryptoECC.createCurve(p.algorithm.namedCurve.toUpperCase());
ecdhInstance = msrcryptoEcdh(curve);
var publicKey = ecdhInstance.computePublicKey(keyObject.d);
keyObject.x = publicKey.x;
keyObject.y = publicKey.y;
}
return {
type: "keyImport",
keyData: keyObject,
keyHandle: {
algorithm: p.algorithm,
extractable: p.extractable || keyObject.extractable,
keyUsage: p.keyUsage,
type: (keyObject.d) ? "private" : "public"
}
};
};
msrcryptoEcdh.exportKey = function (p) {
var jsonKeyStringArray = msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData);
return { type: "keyExport", keyHandle: jsonKeyStringArray };
};
operations.register("importKey", "ecdh", msrcryptoEcdh.importKey);
operations.register("exportKey", "ecdh", msrcryptoEcdh.exportKey);
operations.register("generateKey", "ecdh", msrcryptoEcdh.generateKey);
operations.register("deriveBits", "ecdh", msrcryptoEcdh.deriveBits);
}
var msrcryptoEcdsa = function (curve) {
var btd = cryptoMath.bytesToDigits,
dtb = cryptoMath.digitsToBytes,
ecop = new cryptoECC.EllipticCurveOperatorFp(curve),
orderByteLength = dtb(curve.order).length,
tedCurve = (curve.type === 1);
function createKey(privateKeyBytes) {
return createKeyInternal(btd(privateKeyBytes));
}
function createKeyInternal(privateKeyDigits) {
var publicKey = curve.allocatePointStorage();
ecop.scalarMultiply(privateKeyDigits, curve.generator, publicKey);
return {
publicKey: publicKey,
privateKey: privateKeyDigits
};
}
function generateKey(randomBytes) {
var privateKey = [];
if (!randomBytes) {
randomBytes = msrcryptoPseudoRandom.getBytes(
curve.order.length * cryptoMath.DIGIT_NUM_BYTES);
}
cryptoMath.reduce(
cryptoMath.bytesToDigits(randomBytes),
curve.order,
privateKey);
return createKeyInternal(privateKey);
}
function getDigest(messageBytes) {
// if the message length is longer than the curve-order, truncate.
if (messageBytes.length > orderByteLength) {
messageBytes.length = orderByteLength;
}
var digest = btd(messageBytes);
if (tedCurve) {
var shift = (8 - curve.rbits % 8);
cryptoMath.shiftRight(digest, digest, shift);
}
cryptoMath.reduce(digest, curve.order, digest);
return digest;
}
function sign(privateKey, messageBytes, ephemeralKey) {
if (!ephemeralKey) {
ephemeralKey = generateKey();
}
var r = ephemeralKey.publicKey.x,
k = ephemeralKey.privateKey,
d = btd(privateKey.d),
digest = getDigest(messageBytes.slice()),
s = [],
tmp = [],
signature = null;
cryptoMath.reduce(r, curve.order, r);
cryptoMath.modMul(r, d, curve.order, s);
cryptoMath.add(s, digest, s);
cryptoMath.reduce(s, curve.order, s);
cryptoMath.modInv(k, curve.order, tmp);
cryptoMath.modMul(s, tmp, curve.order, s);
// ensure the bytes arrays are of the expected size
var rBytes = msrcryptoUtilities.padFront(dtb(r, true, orderByteLength), 0, orderByteLength);
var sBytes = msrcryptoUtilities.padFront(dtb(s, true, orderByteLength), 0, orderByteLength);
signature = rBytes.concat(sBytes);
return signature;
}
function verify(publicKey, signatureBytes, messageBytes) {
var split = Math.floor(signatureBytes.length / 2),
r = btd(signatureBytes.slice(0, split)),
s = btd(signatureBytes.slice(split)),
digest = getDigest(messageBytes.slice()),
u1 = [],
u2 = [];
var publicPoint = new cryptoECC.EllipticCurvePointFp(
curve, false, btd(publicKey.x), btd(publicKey.y), null, false);
cryptoMath.modInv(s, curve.order, s);
cryptoMath.modMul(digest, s, curve.order, u1);
cryptoMath.modMul(r, s, curve.order, u2);
var r0 = curve.allocatePointStorage();
var r1 = curve.allocatePointStorage();
if (tedCurve) {
cryptoMath.add(u1, u1, u1);
cryptoMath.add(u1, u1, u1);
cryptoMath.reduce(u1, curve.order, u1);
ecop.scalarMultiply(u1, curve.generator, r0, false);
ecop.scalarMultiply(u2, publicPoint, r1, false);
ecop.convertToExtendedProjective(r0);
ecop.convertToExtendedProjective(r1);
ecop.add(r1, r0, r0);
ecop.normalize(r0);
} else {
ecop.scalarMultiply(u1, curve.generator, r0);
ecop.scalarMultiply(u2, publicPoint, r1);
ecop.convertToJacobianForm(r0);
ecop.convertToMontgomeryForm(r0);
ecop.convertToMontgomeryForm(r1);
ecop.mixedAdd(r0, r1, r0);
ecop.convertToAffineForm(r0);
ecop.convertToStandardForm(r0);
}
if (r0.isInfinity) {
return false;
}
cryptoMath.reduce(r0.x, curve.order, r0.x);
return (cryptoMath.compareDigits(r0.x, r) === 0);
}
return {
createKey: createKey,
generateKey: generateKey,
sign: sign,
verify: verify
};
};
if (typeof operations !== "undefined") {
msrcryptoEcdsa.sign = function ( p) {
var hashName = p.algorithm.hash.name,
curve = cryptoECC.createCurve(p.keyHandle.algorithm.namedCurve.toUpperCase()),
hashFunc = msrcryptoHashFunctions[hashName.toLowerCase()],
digest = hashFunc.computeHash(p.buffer);
var ecdsa = msrcryptoEcdsa(curve);
return ecdsa.sign(p.keyData, digest);
};
msrcryptoEcdsa.verify = function ( p) {
var hashName = p.algorithm.hash.name,
curve = cryptoECC.createCurve(p.keyHandle.algorithm.namedCurve.toUpperCase()),
hashFunc = msrcryptoHashFunctions[hashName.toLowerCase()],
digest = hashFunc.computeHash(p.buffer);
var ecdsa = msrcryptoEcdsa(curve);
return ecdsa.verify(p.keyData, p.signature, digest);
};
msrcryptoEcdsa.generateKey = function (p) {
var curve = cryptoECC.createCurve(p.algorithm.namedCurve.toUpperCase());
var ecdsa = msrcryptoEcdsa(curve);
var keyPairData = ecdsa.generateKey();
var dtb = cryptoMath.digitsToBytes;
// Sometimes the result is a byte short because the byte-conversion
// trims leading zeros. We pad the zeros back on if needed.
function padTo8BytesIncrement(array) {
return msrcryptoUtilities.padFront(array, 0, Math.ceil(array.length / 8) * 8);
}
var x = padTo8BytesIncrement(dtb(keyPairData.publicKey.x));
var y = padTo8BytesIncrement(dtb(keyPairData.publicKey.y));
var d = padTo8BytesIncrement(dtb(keyPairData.privateKey));
return {
type: "keyPairGeneration",
keyPair: {
publicKey: {
keyData: {
x: x,
y: y
},
keyHandle: {
algorithm: p.algorithm,
extractable: p.extractable,
keyUsage: null || p.keyUsage,
type: "public"
}
},
privateKey: {
keyData: {
x: x,
y: y,
d: d
},
keyHandle: {
algorithm: p.algorithm,
extractable: p.extractable,
keyUsage: null || p.keyUsage,
type: "private"
}
}
}
};
};
msrcryptoEcdsa.importKey = function (p) {
var keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, ["x", "y", "d", "crv"]);
// If only private key data 'd' is imported, create x and y
if (keyObject.d && (!keyObject.x || !keyObject.y)) {
var curve = msrcryptoEcdsa.curves[p.algorithm.namedCurve]();
var ecdsa = msrcryptoEcdsa(curve);
var publicKey = ecdsa.computePublicKey(keyObject.d);
keyObject.x = publicKey.x;
keyObject.y = publicKey.y;
}
return {
type: "keyImport",
keyData: keyObject,
keyHandle: {
algorithm: p.algorithm,
extractable: p.extractable || keyObject.extractable,
keyUsage: null || p.keyUsage, // IE11 returns null here
type: (keyObject.d) ? "private" : "public"
}
};
};
msrcryptoEcdsa.exportKey = function (p) {
var jsonKeyStringArray = msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData);
return { type: "keyExport", keyHandle: jsonKeyStringArray };
};
operations.register("sign", "ecdsa", msrcryptoEcdsa.sign);
operations.register("verify", "ecdsa", msrcryptoEcdsa.verify);
operations.register("generateKey", "ecdsa", msrcryptoEcdsa.generateKey);
operations.register("importKey", "ecdsa", msrcryptoEcdsa.importKey);
operations.register("exportKey", "ecdsa", msrcryptoEcdsa.exportKey);
}
var msrcryptoSubtle;
// This code is not used in web worker instance.
if (!runningInWorkerInstance) {
msrcryptoSubtle = (function() {
// If no native Promise support add ours
if (!window.Promise) {
window.Promise = function (executor, id) {
/// <summary>
/// Creates a new promise.
/// </summary>
/// <param name="executor" type="function">A function that takes two parameters: function(resolved, rejected){...}</param>
/// <returns type="Promise">A new Promise object</returns>
if (!(this instanceof Promise)) {
throw new Error("use 'new' keyword with Promise constructor");
}
var successResult = null,
failReason = null,
thenResolved = [],
thenRejected = [],
rejectThenPromise = [],
resolveThenPromise = [];
this.then = function (onCompleted, onRejected) {
var thenFunctionResult;
// If we already have a result because resolveFunction was synchronous,
// then just call onCompleted with the result.
if (successResult) {
thenFunctionResult = onCompleted(successResult.result);
if (thenFunctionResult && thenFunctionResult.then) {
return thenFunctionResult;
}
// Create a new promise; resolve with the result;
// return the resolved promise.
return Promise.resolve(thenFunctionResult);
}
// If we already have a fail reason from a rejected promise
if (failReason) {
thenFunctionResult = onRejected ? onRejected(failReason.result) : failReason.result;
if (thenFunctionResult && thenFunctionResult.then) {
return thenFunctionResult;
}
// Create a new promise; reject with the result;
// return the resolved promise.
return Promise.resolve(thenFunctionResult);
}
// If we do not have a result, store the onCompleted/onRejected functions
// to call when we do get a result.
thenResolved.push(onCompleted);
if (onRejected) {
thenRejected.push(onRejected);
}
// Return a new promise object. This will allow chaining with then/catch().
return new Promise(function (resolve, reject) {
resolveThenPromise.push(resolve);
rejectThenPromise.push(reject);
});
}
this["catch"] = function (onRejected) {
var catchFunctionResult;
// If we already have a result because resolveFunction was synchronous,
// then just call onRejected with the result.
if (failReason) {
catchFunctionResult = onRejected(failReason.result);
if (catchFunctionResult && catchFunctionResult.then) {
return catchFunctionResult;
}
return Promise.resolve(catchFunctionResult);
}
// If we do not have a result, store the onRejected function
// to call when we do get a result.
thenRejected.push(onRejected);
// Return a new promise object. This will allow chaining with then/catch().
return new Promise(function (resolve, reject) {
resolveThenPromise.push(resolve);
rejectThenPromise.push(reject);
});
}
var resolve = function (param) {
/// <summary>
/// Called by the executor function when the function has succeeded.
/// </summary>
/// <param name="param">A result value that will be passed to the then() function.</param>
// Call each attached Then function with the result
for (var i = 0; i < thenResolved.length; i++) {
var result = thenResolved[i](param);
// If the result of the then() function is a Promise,
// set then() to call the chained resolve function.
if (result && result.then) {
result.then(resolveThenPromise[i]);
} else {
// If a then() promise was chained to this promise, call its resolve
// function.
if (resolveThenPromise[i]) {
resolveThenPromise[i](result);
}
}
}
// If the onCompleted function has not yet been assigned, store the result.
successResult = { result: param };
return;
}
function reject(param) {
// Call each catch function on this promise
for (var i = 0; i < thenRejected.length; i++) {
var reason = thenRejected[i](param);
// If the result of the catch() function is a Promise,
// set then() to call the chained resolve function.
if (reason && reason.then) {
reason.then(resolveThenPromise[i], rejectThenPromise[i]);
} else {
if (resolveThenPromise[i]) {
resolveThenPromise[i](reason);
}
}
}
// If the onCompleted function has not yet been assigned, store the result.
failReason = { result: param };
return;
};
// Call the executor function passing the resolve & reject functions of
// this instance.
executor(resolve, reject);
return;
}
//#region static methods
window.Promise.all = function (promiseArray) {
/// <summary>
/// Joins two or more promises and returns only when all the specified promises have completed or been rejected.
/// </summary>
/// <param name="promiseArray" type="Array">Array of promises.</param>
/// <returns type="Promise">Returns a Promise.</returns>
var results = [],
resultCount = 0;
// Generates a then function for each promise
function then(index, resolve) {
return function (result) {
// We want the results to have the same results index as it was passed in.
results[index] = result;
// If all of the promises have returned results, call the resolve function
// with the results array.
if (++resultCount == promiseArray.length) {
resolve(results);
}
}
}
// Create a new Promise to return. It's resolve function will call then()
// on each promise in the arguments list.
var promiseAll = new Promise(
function (resolve, reject) {
for (var i = 0; i < promiseArray.length; i++) {
promiseArray[i].then(then(i, resolve));
// If a promise fails, return the reason
promiseArray[i]["catch"](function (reason) { reject(reason); });
}
});
return promiseAll;
};
window.Promise.race = function (promiseArray) {
/// <summary>
/// Creates a new promise that will resolve or reject with the same result value as the first promise to resolve or reject among the passed in arguments.
/// </summary>
/// <param name="promises" type="Array">Required. One or more promises.</param>
/// <returns type="Promise">Result of first promise to resolve or fail.</returns>
var resolved = false;
// Generates a then function for each promise
function then(resolveFunction) {
return function (result) {
// When the first promise succeeds/fails, return the answer and ignore the rest.
if (!resolved) {
resolved = true;
resolveFunction(result);
}
}
}
// Create a new Promise to return. It's resolve function will call then()
// on each promise in the arguments list.
var promiseRace = new Promise(
function (resolve, reject) {
for (var i = 0; i < promiseArray.length; i++) {
promiseArray[i].then(then(resolve), then(reject));
}
});
return promiseRace;
};
window.Promise.reject = function (rejectReason) {
/// <summary>
/// Creates a new rejected promise with a result equal to the passed in argument.
/// </summary>
/// <param name="rejectReason" type="">Required. The reason why the promise was rejected.</param>
/// <returns type=""></returns>
return new Promise(
function (resolve, reject) {
reject(rejectReason);
});
};
window.Promise.resolve = function (resolveResult) {
/// <summary>
/// Creates a new resolved promise with a result equal to its argument.
/// </summary>
/// <param name="resolveResult" type="">Required. The value returned with the completed promise.</param>
/// <returns type=""></returns>
return new Promise(
function (resolve, reject) {
resolve(resolveResult);
});
};
//#endregion static methods
}
// This worker is used when webworkers aren't available.
// It will function synchronously but use the same
// mechanisms that the asynchronous webworkers use.
function syncWorker() {
var result;
// PostMessage is how you interact with a worker. You post some data to the worker
// and it will process it and return it's data to the onmessage function.
// Since we're really running synchronously, we call the crypto function in
// PostMessage and wait for the result. Then we call the OnMessage fuction with
// that result. This will give the same behavior as a web-worker.
function postMessage(data) {
// Web-workers will automatically return an error message when an
// error is thrown within the web worker.
// When using a sync worker, we'll have to catch thrown errors, so we
// need a try/catch block here.
try {
result = msrcryptoWorker.jsCryptoRunner( { data: data });
} catch (ex) {
this.onerror({ data: ex.message, type: "error" });
return;
}
// 'process' operations don't return values, so we don't
// forward the worker return message.
if (!data.operationSubType || data.operationSubType !== "process") {
this.onmessage({ data: result });
}
}
return {
postMessage: postMessage,
onmessage: null,
onerror: null,
terminate: function () {
// This is a no-op to be compatible with webworker.
}
};
}
/// <dictionary>Obj,oncomplete,onerror</dictionary>
function baseOperation(processResults) {
var result = null,
oncompleteCallback = null,
onerrorCallback = null,
retObj,
promise,
resolveFunc,
rejectFunc;
// Create a new promise
promise = new Promise(
function (resolve, reject) {
resolveFunc = resolve;
rejectFunc = reject;
});
// Called when the worker returns a result
function opDispatchEvent( e) {
// If the event is an Error call the onError callback
if (e.type === "error") {
// If the onerror callback has been set, call it.
// If the onerror callback has been set, call it.
if (rejectFunc) {
rejectFunc.apply(promise, [e.message ? e.message : e]);
}
return;
}
// Otherwise call the oncomplete callback
this.result = processResults(e.data);
// Resolve the promise with the result
resolveFunc.apply(promise, [this.result]);
return;
}
retObj = {
dispatchEvent: opDispatchEvent,
promise : promise,
result: null
};
return retObj;
}
function keyOperation() {
function processResult(result) {
// Could be the result of an import, export, generate.
// Get the keyData and keyHandle out.
switch (result.type) {
// KeyImport: save the new key
case "keyGeneration":
case "keyImport":
case "keyDerive":
keys.add(result.keyHandle, result.keyData);
return result.keyHandle;
// KeyExport: return the export data
case "keyExport":
return result.keyHandle;
case "keyPairGeneration":
keys.add(result.keyPair.publicKey.keyHandle, result.keyPair.publicKey.keyData);
keys.add(result.keyPair.privateKey.keyHandle, result.keyPair.privateKey.keyData);
return {
publicKey: result.keyPair.publicKey.keyHandle,
privateKey: result.keyPair.privateKey.keyHandle
};
default:
throw new Error("Unknown key operation");
}
return;
}
return baseOperation(processResult);
}
function cryptoOperation(cryptoContext) {
function processResult(result) {
// If the browser supports typed-arrays, return an ArrayBuffer like IE11.
result = toArrayBufferIfSupported(result);
// A normal array will be returned.
return result;
}
var op = baseOperation(processResult);
op.process = function (buffer) {
cryptoContext.operationSubType = "process";
cryptoContext.buffer = utils.toArray(buffer);
workerManager.continueJob(this,
utils.clone(cryptoContext));
};
op.finish = function () {
cryptoContext.operationSubType = "finish";
cryptoContext.buffer = [];
workerManager.continueJob(this,
utils.clone(cryptoContext));
};
op.abort = function () {
workerManager.abortJob(this);
};
op.onabort = null;
op.onprogress = null;
op.algorithm = cryptoContext.algorithm || null;
op.key = cryptoContext.keyHandle || null;
return op;
}
function toArrayBufferIfSupported(dataArray) {
// If the browser supports typed-arrays, return an ArrayBuffer like IE11.
if (typedArraySupport && dataArray.pop) {
// We can't write to an ArrayBuffer directly so we create a Uint8Array
// and return it's buffer property.
return (new Uint8Array(dataArray)).buffer;
}
// Do nothing and just return the passed-in array.
return dataArray;
}
// Storage for the keyData.
// Stored as {keyHandle: keyHandle, keyData: keyData} objects.
var keys = [];
keys.add = function (keyHandle, keyData) {
keys.push({ keyHandle: keyHandle, keyData: keyData });
};
keys.remove = function (keyHandle) {
for (var i = 0; i < keys.length; i++) {
if (keys[i].keyHandle === keyHandle) {
keys = keys.splice(i, 1);
return;
}
}
};
keys.lookup = function (keyHandle) {
for (var i = 0; i < keys.length; i++) {
if (keys[i].keyHandle === keyHandle) {
return keys[i].keyData;
}
}
return null;
};
// Manages the pool of webworkers and job queue.
// We first try to find an idle webworker and pass it a crypto job.
// If there are no workers or they are all busy, we'll create a new one.
// If we're at our (somewhat arbitrary) limit for workers we'll queue the
// job until a worker is free.
// When a worker finishes and the queue is empty it will kill itself to
// free resources.
// However, we will keep a couple idle workers alive for future use.
// In the case webworkers are not supported <IE10 we will run in synchronous
// mode. Jobs will be executed synchronously as they arrive using a single
// syncWorker (pretend webworker that just runs synchronously in this same script).
var workerManager = (function () {
// The max number of webworkers we'll spawn.
var maxWorkers = 12;
// The number of idle webworkers we'll allow to live for future use.
var maxFreeWorkers = 2;
// Storage for webworker.
var workerPool = [];
// Queue for jobs when all workers are busy.
var jobQueue = [];
// Each job gets and id.
var jobId = 0;
function getFreeWorker() {
purgeWorkerType(!asyncMode);
// Get the first non-busy worker
for (var i = 0; i < workerPool.length; i++) {
if (!workerPool[i].busy) {
return workerPool[i];
}
}
return null;
}
function purgeWorkerType(webWorker) {
for (var i = workerPool.length - 1; i >= 0; i -= 1) {
if (workerPool[i].isWebWorker === webWorker) {
workerPool[i].terminate();
workerPool.splice(i, 1);
}
}
}
function freeWorkerCount() {
var freeWorkers = 0;
for (var i = 0; i < workerPool.length; i++) {
if (!workerPool[i].busy) {
freeWorkers += 1;
}
}
return freeWorkers;
}
function addWorkerToPool(worker) {
workerPool.push(worker);
}
function removeWorkerFromPool(worker) {
// Find this worker in the array.
for (var i = 0; i < workerPool.length; i++) {
if (workerPool[i] === worker) {
// Kill the webworker.
worker.terminate();
// Remove the worker object from the pool.
workerPool.splice(i, 1);
return;
}
}
}
function lookupWorkerByOperation(operation) {
// Find this worker in the array.
for (var i = 0; i < workerPool.length; i++) {
if (workerPool[i].operation === operation) {
return workerPool[i];
}
}
// Didn't find the worker!?
return null;
}
function queueJob(operation, data) {
jobQueue.push({ operation: operation, data: data, id: jobId++ });
}
function jobCompleted(worker) {
worker.busy = false;
worker.operation = null;
// Check the queue for waiting jobs if in async mode
if (asyncMode) {
if (jobQueue.length > 0) {
var job = jobQueue.shift();
continueJob(job.operation, job.data);
} else if (freeWorkerCount() > maxFreeWorkers) {
removeWorkerFromPool(worker);
}
}
}
function createNewWorker(operation) {
// Use a web worker if supported
// else use a synchronous worker.
var worker;
if (asyncMode) {
try {
worker = new Worker(scriptUrl);
worker.postMessage({ prngSeed: msrcryptoPseudoRandom.getBytes(48) });
worker.isWebWorker = true;
} catch (ex) {
asyncMode = false;
publicMethods.forceSync = true;
worker = syncWorker();
worker.isWebWorker = false;
}
} else {
worker = syncWorker();
worker.isWebWorker = false;
}
// Store the operation object as a property on the worker
// so we can know which operation this worker is working for.
worker.operation = operation;
worker.busy = false;
// The worker will call this function when it completes its job.
worker.onmessage = function ( e) {
var op = worker.operation;
// Check if there are queued jobs for this operation
for (var i = 0; i < jobQueue.length; i++) {
if (jobQueue[i].operation === worker.operation) {
var job = jobQueue[i];
jobQueue.splice(i, 1);
postMessageToWorker(worker, job.data);
return;
}
}
// Send the results to the operation object and it will fire
// it's onCompleted event.
if (op && e.data.type !== "process") {
jobCompleted(worker);
op.dispatchEvent(e);
}
};
// If an error occurs within the worker.
worker.onerror = function ( e) {
var op = worker.operation;
jobCompleted(worker);
// Send the error to the operation object and it will fire
// it's onError event.
op.dispatchEvent(e);
};
// Add this new worker to the worker pool.
addWorkerToPool(worker);
return worker;
}
function abortJob(cryptoOperationObject) {
var worker = lookupWorkerByOperation(cryptoOperationObject);
if (worker) {
removeWorkerFromPool(worker);
}
}
// Creates or reuses a worker and starts it up on work.
function runJob( operation, data) {
var worker = null;
// If the caller adds the "forceSync" property and sets it to true.
// Then run in synchronous mode even if webworkers are available.
// This can be turned on or off on the fly.
asyncMode = webWorkerSupport && !(publicMethods.forceSync);
// Get the first idle worker.
worker = getFreeWorker();
// Queue this job if all workers are busy and we're at our max instances
if (asyncMode && worker === null && workerPool.length >= maxWorkers) {
queueJob(operation, data);
return;
}
// No idle workers, we'll have to create a new one.
if (worker === null) {
worker = createNewWorker(operation);
}
if (worker === null) {
queueJob(operation, data);
throw new Error("could not create new worker");
}
// Store the operation object as a property on the worker
// so we can know which operation this worker is working for.
worker.operation = operation;
// Mark this worker as 'busy'. It's about to run a job.
worker.busy = true;
// Start the worker
postMessageToWorker(worker, data);
}
function continueJob(/*type(cryptoOperation)*/operation, data) {
// Lookup the worker that is handling this operation
var worker = lookupWorkerByOperation(operation);
if (worker) {
postMessageToWorker(worker, data);
return;
}
// If we didn't find a worker, this is probably the first
// 'process' message so we need to start a new worker.
runJob(operation, data);
}
function postMessageToWorker(worker, data) {
// Start the worker now if using webWorkers
// else, defer running until later.
if (asyncMode) {
worker.data = data;
worker.postMessage(data);
} else {
var func = (function (postData) {
return function () {
return worker.postMessage(postData);
}
})(data);
var id = setTimeout(func, 0);
}
return;
}
return {
runJob: runJob,
continueJob: continueJob,
abortJob: abortJob
};
})();
var utils = msrcryptoUtilities;
function checkOperation(operationType, algorithmName) {
if (!operations.exists(operationType, algorithmName)) {
throw new Error("unsupported algorithm");
}
}
// The list of possible parameters passed to the subtle interface.
var subtleParameters = [
/* 0 */ { name: "algorithm", type: "Object", required: true },
/* 1 */ { name: "keyHandle", type: "Object", required: true },
/* 2 */ { name: "buffer", type: "Array", required: false },
/* 3 */ { name: "signature", type: "Array", required: true },
/* 4 */ { name: "format", type: "String", required: true },
/* 5 */ { name: "keyData", type: "Object", required: true },
/* 6 */ { name: "extractable", type: "Boolean", required: false },
/* 7 */ { name: "keyUsage", type: "Array", required: false },
/* 8 */ { name: "derivedKeyType", type: "Object", required: true },
/* 9 */ { name: "length", type: "Number", required: false },
/* 10 */ { name: "extractable", type: "Boolean", required: true },
/* 11 */ { name: "keyUsage", type: "Array", required: true }
];
// The set of expected parameters passed to each subtle function.
var subtleParametersSets = {
encrypt: [0, 1, 2],
decrypt: [0, 1, 2],
sign: [0, 1, 2],
verify: [0, 1, 3, 2],
digest: [0, 2],
generateKey: [0, 6, 7],
importKey: [4, 5, 0, 10, 11],
exportKey: [0, 4, 1, 6, 7],
deriveKey: [0, 1, 8, 6, 7],
deriveBits: [0, 1, 9],
wrapKey: [1, 1, 0],
unwrapKey: [2, 0, 1, 6, 7]
};
// Looks up the stored key data for a given keyHandle
function lookupKeyData(handle) {
var data = keys.lookup(handle);
if (!data) {
throw new Error("key not found");
}
return data;
}
// This function processes each parameter passed by the user. Each parameter
// is compared against an expected parameter. It should be of the expected type.
// Typed-Array parameters are converted to regular Arrays.
function buildParameterCollection(operationName, parameterSet) {
var parameterCollection = { operationType: operationName },
operationParameterSet = subtleParametersSets[operationName];
for (var i = 0; i < operationParameterSet.length; i += 1) {
var expectedParam = subtleParameters[operationParameterSet[i]];
var actualParam = parameterSet[i];
// Verify the required parameters are present.
if (actualParam==null) {
if (expectedParam.required) {
throw new Error(expectedParam.name);
} else {
continue;
}
}
// If this parameter is a typed-array convert it to a regular array.
if (actualParam.subarray) {
actualParam = utils.toArray(actualParam);
}
// If this parameter is an ArrayBuffer convert it to a regular array.
if (utils.getObjectType(actualParam) == "ArrayBuffer") {
actualParam = utils.toArray(actualParam);
}
// Verify the actual parameter is of the expected type.
if (msrcryptoUtilities.getObjectType(actualParam) !== expectedParam.type) {
throw new Error(expectedParam.name);
}
// If this parameter is an algorithm object convert it's name to lowercase.
if (expectedParam.name === "algorithm") {
actualParam.name = actualParam.name.toLowerCase();
// If the algorithm has a typed-array IV, convert it to a regular array.
if (actualParam.iv) {
actualParam.iv = utils.toArray(actualParam.iv);
}
// If the algorithm has a typed-array Salt, convert it to a regular array.
if (actualParam.salt) {
actualParam.salt = utils.toArray(actualParam.salt);
}
// If the algorithm has a typed-array AdditionalData, convert it to a regular array.
if (actualParam.additionalData) {
actualParam.additionalData = utils.toArray(actualParam.additionalData);
}
// If this algorithm has a hash property in the form 'hash: hashName'
// Convert it to hash: {name: hashName} as per the W3C spec.
if (actualParam.hash && !actualParam.hash.name && msrcryptoUtilities.getObjectType(actualParam.hash) === "String") {
actualParam.hash = { name: actualParam.hash };
}
}
// KeyWrap has two keyHandle paramters. We add '1' to the second param name
// to avoid a duplicate name.
if (parameterCollection.hasOwnProperty(expectedParam.name)) {
parameterCollection[expectedParam.name + "1"] = actualParam;
} else {
parameterCollection[expectedParam.name] = actualParam;
}
}
return parameterCollection;
}
function executeOperation(operationName, parameterSet, keyFunc) {
var pc = buildParameterCollection(operationName, parameterSet);
// Verify this type of operation is supported by this library (encrypt, digest, etc...)
checkOperation(operationName, pc.algorithm.name);
// Add the key data to the parameter object
if (pc.keyHandle) {
pc.keyData = lookupKeyData(pc.keyHandle);
}
// Add the key data to the parameter object
// KeyWrap has two keyHandle parameters - this handles the second key.
if (pc.keyHandle1) {
pc.keyData1 = lookupKeyData(pc.keyHandle1);
}
// ECDH.DeriveBits passes a public key in the algorithm
if (pc.algorithm && pc.algorithm.public) {
pc.additionalKeyData = lookupKeyData(pc.algorithm.public);
}
var op = keyFunc ? keyOperation(pc) : cryptoOperation(pc);
// Run the crypto now if a buffer is supplied
// else wait until process() and finish() are called.
if (keyFunc || pc.buffer || operationName === "deriveBits" || operationName === "wrapKey") {
workerManager.runJob(op, pc);
}
return op.promise;
}
var publicMethods = {
encrypt: function (algorithm, keyHandle, buffer) {
/// <signature>
/// <summary>Encrypt a UInt8Array of data. Encrypt will return an ArrayBuffer if supported, otherwise it will return a regular Array.</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="key" type="Key"></param>
/// <param name="buffer" type="UInt8Array" optional="true">UInt8Array</param>
/// <returns type="ArrayBuffer" />
/// </signature>
/// <signature>
/// <summary>Encrypt an array of bytes. Encrypt will return an ArrayBuffer if supported, otherwise it will return a regular Array.</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="key" type="Key"></param>
/// <param name="buffer" type="Array" optional="true">An array of bytes (number from 0-255)</param>
/// <returns type="Array" />
/// </signature>
/// <signature>
/// <summary>Encrypt an array of bytes. Encrypt will return an ArrayBuffer if supported, otherwise it will return a regular Array.</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="key" type="Key"></param>
/// <param name="buffer" type="Array" optional="true">A continuous array of bytes (number values from 0-255)</param>
/// <returns type="ArrayBuffer" />
/// </signature>
return executeOperation("encrypt", arguments, 0);
},
decrypt: function (algorithm, keyHandle, buffer) {
/// <signature>
/// <summary>Decrypt a UInt8Array of data.
/// Decrypt will return an ArrayBuffer if supported, otherwise it will return an Array of byte values (numbers from 0-255)</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="key" type="Key"></param>
/// <param name="buffer" type="UInt8Array" optional="true">UInt8Array</param>
/// <returns type="CryptoOperation" />
/// </signature>
/// <signature>
/// <summary>Decrypt an array of byte values. Decrypt will return an ArrayBuffer if supported, otherwise it will return a regular Array.</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="key" type="Key"></param>
/// <param name="buffer" type="Array" optional="true">An array of bytes values (numbers from 0-255)</param>
/// <returns type="CryptoOperation" />
/// </signature>
return executeOperation("decrypt", arguments, 0);
},
sign: function (algorithm, keyHandle, buffer) {
/// <signature>
/// <summary>Sign a UInt8Array of data.
/// Sign will return a signature as an ArrayBuffer if supported, otherwise it will return an Array of byte values (numbers from 0-255)</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="key" type="Key"></param>
/// <param name="buffer" type="UInt8Array" optional="true">UInt8Array</param>
/// <returns type="CryptoOperation" />
/// </signature>
/// <signature>
/// <summary>Sign an array of byte values. Sign will return an ArrayBuffer if supported, otherwise it will return a regular Array.</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="key" type="Key"></param>
/// <param name="buffer" type="Array" optional="true">An array of bytes values (numbers from 0-255)</param>
/// <returns type="CryptoOperation" />
/// </signature>
return executeOperation("sign", arguments, 0);
},
verify: function (algorithm, keyHandle, signature, buffer) {
/// <signature>
/// <summary>Verify a signature.</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="key" type="Key"></param>
/// <param name="signature" type="UInt8Array">UInt8Array</param>
/// <param name="buffer" type="UInt8Array" optional="true">UInt8Array</param>
/// <returns type="CryptoOperation" />
/// </signature>
/// <signature>
/// <summary>Verify a signature.</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="key" type="Key"></param>
/// <param name="signature" type="UInt8Array">UInt8Array</param>
/// <param name="buffer" type="Array" optional="true">An array of bytes values (numbers from 0-255)</param>
/// <returns type="CryptoOperation" />
/// </signature>
/// <signature>
/// <summary>Verify a signature.</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="key" type="Key"></param>
/// <param name="signature" type="Array">An array of bytes values (numbers from 0-255)</param>
/// <param name="buffer" type="Array" optional="true">An array of bytes values (numbers from 0-255)</param>
/// <returns type="CryptoOperation" />
/// </signature>
/// <signature>
/// <summary>Verify a signature.</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="key" type="Key"></param>
/// <param name="signature" type="Array">An array of bytes values (numbers from 0-255)</param>
/// <param name="buffer" type="UInt8Array" optional="true">UInt8Array</param>
/// <returns type="CryptoOperation" />
/// </signature>
return executeOperation("verify", arguments, 0);
},
digest: function (algorithm, buffer) {
/// <signature>
/// <summary>Digest data using a specified cryptographic hash algorithm</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="buffer" type="UInt8Array" optional="true">UInt8Array</param>
/// <returns type="CryptoOperation" />
/// </signature>
/// <signature>
/// <summary>Digest data using a specified cryptographic hash algorithm</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="buffer" type="Array" optional="true">An array of bytes values (numbers from 0-255)</param>
/// <returns type="CryptoOperation" />
/// </signature>
return executeOperation("digest", arguments, 0);
},
generateKey: function (algorithm, extractable, keyUsage) {
/// <signature>
/// <summary>Generate a new key for use with the algorithm specified by the algorithm parameter</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="extractable" type="Boolean" optional="true"></param>
/// <param name="keyUsage" type="Array" optional="true"></param>
/// <returns type="KeyOperation" />
/// </signature>
return executeOperation("generateKey", arguments, 1);
},
deriveKey: function (algorithm, baseKey, derivedKeyType, extractable, keyUsage) {
/// <signature>
/// <summary>Generate a key for the specified derivedKeyType, using the specified cryptographic key derivation algorithm with the given baseKey as input.</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="baseKey" type="Key"></param>
/// <param name="deriveKeyType" type="Algorithm"></param>
/// <param name="extractable" type="Boolean" optional="true"></param>
/// <param name="keyUsage" type="Array" optional="true"></param>
/// <returns type="KeyOperation" />
/// </signature>
return executeOperation("deriveKey", arguments, 1);
},
deriveBits: function (algorithm, baseKey, length) {
/// <signature>
/// <summary>Generate an array of bytes from a given baseKey as input.</summary>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="baseKey" type="Key"></param>
/// <param name="length" type="Number">Number of bytes to return.</param>
/// <returns type="CryptoOperation" />
/// </signature>
return executeOperation("deriveBits", arguments, 0);
},
importKey: function (format, keyData, algorithm, extractable, keyUsage) {
/// <signature>
/// <summary>Constructs a new Key object using the key data specified by the keyData parameter.</summary>
/// <param name="format" type="String"></param>
/// <param name="keyData" type="Object">An object representing a key in jwk format.</param>
/// <param name="algorithm" type="Algorithm"></param>
/// <param name="extractable" type="Boolean" optional="true"></param>
/// <param name="keyUsage" type="Array" optional="true"></param>
/// <returns type="KeyOperation" />
/// </signature>
return executeOperation("importKey", arguments, 1);
},
exportKey: function (format, keyHandle) {
/// <signature>
/// <summary>Exports the given key material of the Key object as specified by the key parameter.</summary>
/// <param name="format" type="String"></param>
/// <param name="key" type="Key"></param>
/// <returns type="KeyOperation" />
/// </signature>
// Export is one of the few calls where the caller does not supply an algorithm
// since it's already a property of the key to be exported.
// So, we're pulling it out of the key and adding it to the parameter set since
// it is used as a switch to route the parameters to the right function.
// Now we don't have to treat this as a special case in the underlying code.
return executeOperation("exportKey", [keyHandle.algorithm, format, keyHandle], 1);
},
wrapKey: function (format, key, wrappingKey, wrappingKeyAlgorithm) {
/// <signature>
/// <summary>Returns a KeyOperation object which will asynchronously return an array containing the key material of key, encrypted with keyEncryptionKey using the specified keyWrappingAlgorithm.</summary>
/// <param name="format" type="String"></param>
/// <param name="key" type="Key"></param>
/// <param name="wrappingKey" type="Key"></param>
/// <param name="wrappingKeyAlgorithm" type="Algorithm"></param>
/// <returns type="KeyOperation" />
/// </signature>
return executeOperation("wrapKey", arguments, 0);
},
unwrapKey: function (format, unwrappingKey, unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsage) {
/// <signature>
/// <summary>Construct a Key object from encrypted key material.</summary>
/// <param name="format" type="String"></param>
/// <param name="unwrappingKey" type="Array">An array of bytes values (numbers from 0-255)</param>
/// <param name="unwrapAlgorithm" type="Algorithm"></param>
/// <param name="keyEncryptionKey" type="Key"></param>
/// <param name="extractableunwrappedKeyAlgorithm type="Boolean" optional="true"></param>
/// <param name="keyUsage" type="Array" optional="true"></param>
/// <returns type="KeyOperation" />
/// </signature>
/// <signature>
/// <summary>Construct a Key object from encrypted key material.</summary>
/// <param name="format" type="String"></param>
/// <param name="unwrappingKey" type="UInt8Array"></param>
/// <param name="unwrapAlgorithm" type="Algorithm"></param>
/// <param name="unwrappedKeyAlgorithm" type="Key"></param>
/// <param name="extractable" type="Boolean" optional="true"></param>
/// <param name="keyUsage" type="Array" optional="true"></param>
/// <returns type="KeyOperation" />
/// </signature>
return executeOperation("unwrapKey", arguments, 1);
}
};
return publicMethods;
})();
}
var msrcryptoWrapKey = (function () {
var utils = msrcryptoUtilities;
function wrapKey(params) {
var rsaObj = msrcryptoRsa(
params.keyData1,
params.keyHandle1.algorithm.name,
msrcryptoHashFunctions["sha-1"]);
var tagLength = 128;
var keyToWrapJwk = msrcryptoJwk.keyToJwkOld(params.keyHandle, params.keyData);
var jweHeader = {
"alg": params.keyHandle1.algorithm.name.toUpperCase(),
"enc": "A128GCM"
};
var encodedJweHeader =
utils.toBase64(JSON.stringify(jweHeader), true);
var cmk = msrcryptoPseudoRandom.getBytes(32);
var jweEncryptedKey = rsaObj.encrypt(cmk);
var encodedJweEncryptedKey = utils.toBase64(jweEncryptedKey, true);
var jweIv = msrcryptoPseudoRandom.getBytes(12);
var encodedJweIv = utils.toBase64(jweIv, true);
var additionalData = encodedJweHeader.concat(".", encodedJweEncryptedKey, ".", encodedJweIv);
var gcm = msrcryptoGcm(msrcryptoBlockCipher.aes(cmk));
gcm.init(jweIv, utils.stringToBytes(additionalData), tagLength);
var ciphertextPlusTag = gcm.encrypt(keyToWrapJwk);
var tag = ciphertextPlusTag.slice(-(tagLength / 8));
var encodedIntegrityValue = utils.toBase64(tag, true);
var encodedCiphertext =
utils.toBase64(ciphertextPlusTag.slice(0, ciphertextPlusTag.length - tag.length), true);
var jwe = {
recipients: [{
header: encodedJweHeader,
encrypted_key: encodedJweEncryptedKey,
integrity_value: encodedIntegrityValue
}
],
initialization_vector: encodedJweIv,
ciphertext: encodedCiphertext
}
return utils.stringToBytes(JSON.stringify(jwe));
}
function unwrapKey(params) {
var b64Tobytes = utils.base64ToBytes;
var keyDataJwk =
JSON.parse(String.fromCharCode.apply(null, params.buffer));
var header = utils.base64ToString(keyDataJwk.recipients[0].header);
var encrypted_key =
b64Tobytes(keyDataJwk.recipients[0].encrypted_key);
var integrity_value =
b64Tobytes(keyDataJwk.recipients[0].integrity_value);
var initialization_vector =
b64Tobytes(keyDataJwk.initialization_vector);
var ciphertext =
b64Tobytes(keyDataJwk.ciphertext);
var hashFunc = msrcryptoHashFunctions["sha-1"];
var rsaObj = msrcryptoRsa(params.keyData, params.keyHandle.algorithm.name, hashFunc);
var inKey = rsaObj.decrypt(encrypted_key);
var additionalData =
keyDataJwk.recipients[0].header.concat(".", keyDataJwk.recipients[0].encrypted_key, ".", keyDataJwk.initialization_vector);
var gcm = msrcryptoGcm(msrcryptoBlockCipher.aes(inKey));
gcm.init(initialization_vector, utils.stringToBytes(additionalData), 128);
var result = gcm.decrypt(ciphertext, integrity_value);
var keyObject = msrcryptoJwk.jwkToKey(result, params.algorithm, ["k"]);
return {
type: "keyImport",
keyData: keyObject.k,
keyHandle: {
algorithm: { name: params.algorithm.name },
extractable: params.extractable || keyObject.extractable,
keyUsage: params.keyUsage,
type: "secret"
}
}
}
return {
wrapKey: wrapKey,
unwrapKey: unwrapKey
};
})();
if (typeof operations !== "undefined") {
operations.register("wrapKey", "aes-gcm", msrcryptoWrapKey.wrapKey);
operations.register("unwrapKey", "aes-cbc", msrcryptoWrapKey.unwrapKey);
}
var publicMethods = {
/// <field type = 'Object' static="false">Microsoft Research Javascript Crypto Library Subtle interface.</field>
subtle: msrcryptoSubtle,
getRandomValues: function(array) {
/// <signature>
/// <summary>Places cryptographically random values into the given array.</summary>
/// <param name="array" type="Array"></param>
/// <returns type="Array" />
/// </signature>
/// <signature>
/// <summary>Places cryptographically random values into the given array.</summary>
/// <param name="array" type="ArrayBufferView"></param>
/// <returns type="ArrayBufferView">Returns ArrayBufferView if supported.</returns>
/// </signature>
var i;
var randomValues = msrcryptoPseudoRandom.getBytes(array.length);
for (i = 0; i < array.length; i+=1) {
array[i] = randomValues[i];
}
return array;
},
initPrng: function (entropyData) {
/// <signature>
/// <summary>Add entropy to the PRNG.</summary>
/// <param name="entropyData" type="Array">Entropy input to seed or reseed the PRNG.</param>
/// </signature>
var entropyDataType = Object.prototype.toString.call(entropyData);
if (entropyDataType !== "[object Array]" && entropyDataType !== "[object Uint8Array]") {
throw new Error("entropyData must be a Array or Uint8Array");
}
// Mix the user-provided entropy into the entropy pool - only in the main thread.
entropyPool && entropyPool.reseed(entropyData);
// Reseed the PRNG that was initialized below
msrcryptoPseudoRandom.reseed(entropyPool.read(48));
fprngEntropyProvided = true;
},
toBase64: function (data, toBase64Url) {
/// <signature>
/// <summary>Convert string or array data to Base64.</summary>
/// <param name="data" type="Array">Byte values (numbers 0-255)</param>
/// <param name="toBase64Url" type="Boolean" optional="true">Return Base64Url encoding (this is different from Base64 encoding.)</param>
/// <returns type="Array" />
/// </signature>
/// <signature>
/// <summary>Convert string or array data to Base64.</summary>
/// <param name="data" type="String"></param>
/// <param name="toBase64Url" type="Boolean" optional="true">Return Base64Url encoding (this is different from Base64 encoding.)</param>
/// <returns type="Array" />
/// </signature>
return msrcryptoUtilities.toBase64(data, false);
},
base64ToString: function (base64String) {
/// <signature>
/// <summary>Decode a Base64 encoded string to a plain string.</summary>
/// <param name="base64String" type="String">Base64 encoded string.</param>
/// <returns type="String" />
/// </signature>
return msrcryptoUtilities.base64ToString(base64String);
},
/// <field type = 'String'>URL of the this msrCrypto script.</field>
url : scriptUrl
};
// Expose the math library if present
if (typeof cryptoMath !== "undefined") {
publicMethods.cryptoMath = cryptoMath;
}
if (typeof testInterface !== "undefined") {
publicMethods.testInterface = testInterface;
}
// Initialize the main entropy pool instance on the main thread, only.
// I want only the main thread to create and manage the central entropy pool.
// All workers would have their own PRNG instance initialized by injected entropy from the main thread.
var entropyPool;
if (!runningInWorkerInstance) {
entropyPool = entropyPool || new MsrcryptoEntropy();
// Initialize the entropy pool in the main thread.
// There is only one entropy pool.
entropyPool.init();
var localEntropy = entropyPool.read(48); // 48 is from SP800-90A; could be longer
msrcryptoPseudoRandom.init(localEntropy);
}
return publicMethods;
})();