//******************************************************************************* // // 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. // //******************************************************************************* //******************************************************************************* // // NOTICE // // This file has been modified from the original source: // https://www.microsoft.com/en-us/download/details.aspx?id=52439 // // To see changes, check the commit history here: // https://github.com/kevlened/msrCrypto/commits/master // //******************************************************************************* // Don't enforce entropy in environments without document var glb = typeof global !== "undefined" ? global : typeof window !== "undefined" ? window : typeof self !== "undefined" ? self : undefined; if (typeof document === 'undefined' && glb) { glb.document = { attachEvent: function() {} }; } 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; var isPermanentForceSync = glb && glb.msrCryptoPermanentForceSync; // Support for webWorkers IE10+. var webWorkerSupport = (typeof Worker !== "undefined") && !isPermanentForceSync; // Is this script running in an instance of a webWorker? var runningInWorkerInstance = (typeof importScripts !== "undefined") && !isPermanentForceSync; // 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) { /// /// /// /// /// 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) { /// /// Converts byte data to Base64 string /// An array of bytes values (numbers from 0-255) /// Converts to a Base64Url string if True (default = false) /// /// /// /// Converts byte data to Base64 string /// A UInt8Array /// Converts to a Base64Url string if True (default = false) /// /// /// /// Converts text to Base64 string /// Text string /// Converts to a Base64Url string if True (default = false) /// /// 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) { /// /// Converts a Base64/Base64Url string to a text /// A Base64/Base64Url encoded string /// /// 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) { /// /// Converts a Base64/Base64Url string to an Array /// A Base64/Base64Url encoded string /// /// // 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) { /// /// Returns the name of an object type /// /// /// return Object.prototype.toString.call(object).slice(8, -1); } function bytesToHexString(bytes, separate) { /// /// Converts an Array of bytes values (0-255) to a Hex string /// /// Inserts a separator for display purposes (default = false) /// /// 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) { /// /// Converts four bytes to a 32-bit int /// /// The bytes to convert /// Optional starting point /// 32-bit number index = (index || 0); return (bytes[index] << 24) | (bytes[index + 1] << 16) | (bytes[index + 2] << 8) | bytes[index + 3]; } function stringToBytes(messageString) { /// /// Converts a String to an Array of byte values (0-255) /// /// /// var bytes = new Array(messageString.length); for (var i = 0; i < bytes.length; i++) { bytes[i] = messageString.charCodeAt(i); } return bytes; } function hexToBytesArray(hexString) { /// /// Converts a Hex-String to an Array of byte values (0-255) /// /// /// 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) { /// /// Creates a shallow clone of an Object /// /// /// var newObject = {}; for (var propertyName in object) { if (object.hasOwnProperty(propertyName)) { newObject[propertyName] = object[propertyName]; } } return newObject; } function unpackData(base64String, arraySize, toUint32s) { /// /// Unpacks Base64 encoded data into arrays of data. /// Base64 encoded data /// Break data into sub-arrays of a given length /// Treat data as 32-bit data instead of byte data /// /// 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) { /// /// Converts a 32-bit number to an Array of 4 bytes /// 32-bit number /// /// return [(int32 >>> 24) & 255, (int32 >>> 16) & 255, (int32 >>> 8) & 255, int32 & 255]; } function int32ArrayToBytes(int32Array) { /// /// Converts an Array 32-bit numbers to an Array bytes /// Array of 32-bit numbers /// /// var result = []; for (var i = 0; i < int32Array.length; i++) { result = result.concat(int32ToBytes(int32Array[i])); } return result; } function xorVectors(a, b) { /// /// Exclusive OR (XOR) two arrays. /// Input array. /// Input array. /// XOR of the two arrays. The length is minimum of the two input array lengths. /// 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) { /// /// Get an array filled with zeroes (or optional fillValue.) /// Requested array length. /// /// /// // 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) { /// /// Converts a UInt8Array to a regular JavaScript Array /// /// /// // 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) { /// /// Pads the end of an array with a specified value /// /// The value to pad to the array /// The final resulting length with padding /// /// while (array.length < finalLength) { array.push(value); } return array; } function padFront(array, value, finalLength) { /// /// Pads the front of an array with a specified value /// /// The value to pad to the array /// The final resulting length with padding /// /// while (array.length < finalLength) { array.unshift(value); } return array; } function arraysEqual(array1, array2) { /// /// Checks if two Arrays are equal by comparing their values. /// /// /// /// 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) { /// /// Verify that an Array contains only byte values (0-255) /// /// Returns true if all values are 0-255 /// 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) { /// Swap big endian bytes to little endian bytes. /// UInt8Array - representing a big-integer. /// UInt8Array - the number with endianness swapped. 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) { /// Parse a String in a given base into a little endian digit array. /// Input unsigned integer in a string. /// /// =2 and <=36. Default = 10. ]]> /// /// Array of digits in little endian; [0] is LSW. // 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) { /// Convert a big-endian byte array to a number in string in radix. /// A big integer as a little-endian digit array. /// Radix from 2 to 26. Default = 10. /// The number in base radix as a string. 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) { /// 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. /// A positive integer. /// UInt8Array - 2^i as a big-endian byte array. var requiredBytes = Math.ceil((i + 1) / 8); var out = createArray(requiredBytes); out[0] = Math.pow(2, i % 8); return out; } function computeBitArray(bytes) { /// Given an array of bytes in big-endian format, compute UInt8Array with /// one element for each bit (0 or 1), in little-endian order. /// An array of bytes in big-endian format. /// An array of 0's and 1's representing the bits in little-endian. 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) { /// Return the 0-based index of the first non-zero bit starting at the most significant bit position. /// Value to scan. /// Zero-based index of the first non-zero bit. 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) { /// Returns the (1 indexed) index of the highest set bit. /// A big-endian big integer byte array. /// The index of the highest bit. 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) { /// /// Digits to recode /// Window size /// Recoded digits} // 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) { /// From an array of digits, return a sequential set of bits. /// /// /// 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) { /// From an array of digits, return a sub-set of bits from an arbitray index. /// Array of digits /// Zero-index position of start bit /// Number of bits to return /// Value of n-bits 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) { /// Copies a range of elements from one array to another array. /// Source array to copy from. /// The index in the source array at which copying begins. /// The array that receives the data. /// The index in the destination array at which storing begins. /// The number of elements to copy. while (length-- > 0) { destination[destIndex + length] = source[sourceIndex + length]; } } function isZero(array) { /// Check if an array is zero. All elements are zero. /// UInt16Array - An array to be checked. /// var i; for (i = 0; i < array.length; i += 1) { if (array[i] !== 0) { return false; } } return true; } function isEven(array) { /// Returns true if this number is even. /// /// return (array[0] & 0x1) === 0x0; } function sequenceEqual(left, right) { /// Compare two indexable collections for sequence equality. /// The left array. /// The right array. /// True if both arrays are the same. 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) { /// Convert an unsigned number from big-endian bytes to little endian digits. /// The number in unsigned big-endian byte format. /// The digits in little-endian. // 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) { /// Construct a big endian array of bytes from a litte-endian array of digits. /// Always returns at least one byte and trims leading zeros. /// The digits in little-endian. /// Remove the leading zeros from the result (default true) /// Minimum length to trim down to. Valid only if trim is true. Default=1. /// Encoded bytes in big-endian format. 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) { /// Construct an array of digits from a positive integer. /// A positive integer to be converted to digit form. /// The number of digits to use for the digit form. /// The given integer in digit form. 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) { /// Return the index of the most significant word of x, 0-indexed. /// If x is zero (no significant index), then -1 is returned. /// Digit array. /// Index of the most significant word, or -1 if digits is zero. 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) { /// Compare two digit arrays by value. Returns an integer indicating the comparison result. /// Digit arrays are in little endian. /// The object on the left side of the comparison. /// The object on the right side of the comparison. /// 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. 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) { /// 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. /// 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. /// Digit array. /// Output length to pad with zeroes. /// Pad with zeroes to length if true [false]. /// Resized digits array; same input object. // 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) { /// 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. /// Source digit array. /// Destination digit array. May be the same as source. /// Number of bits to shift, must be less than DIGIT_BITS and greater or equal to zero. Default is 1. /// Number of items to shift from he source array. Default is source.length. /// 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. /// 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) { /// Shift a number array to the left by given bits, i.e., multiply by 2^bits. /// Source digit array. /// Destination digit array. May be the same as source. /// Number of bits to shift, must be less than DIGIT_BITS and greater or equal to zero. Default is 1. /// Number of items to shift from he source array. Default is source.length. /// An additional MSW digit may be added if the leftshift out from the current MSW produces a non-zero result. [0] is LSW. 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) { /// Add two arrays of digits into a third array: sum = addend1 + addend2. Carry is recorded in the output if there is one. /// The first addend. /// The second added. /// The output sum buffer addend1 + addend2. /// If carry out then 1, otherwise 0. // 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) { /// Subtraction: difference = minuend - subtrahend. Condition: minuend.length <= subtrahend.length. /// Minuend. /// Subtrahend. /// The difference. /// Returns -1 if there is a borrow (minuend < subtrahend), or 0 if there isn't (minuend >= subtrahend). 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) { /// Multiply two arrays of digits into a third array using schoolbook. /// Multiplicand. /// Multiplier. /// Product = multiplicant * multiplier. /// The result buffer; same as the product argument. // 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) { /// Computes the quotient q and remainder r when dividend is divided by /// divisor. /// The dividend. /// The divisor. /// Receives the quotient (n digits). /// Receives the remainder (n digits). /// Temporary storage (n digits). /// Temporary storage (n digits). /// This is an implementation of Figure 9-1 is Knuth's Algorithm D [Knu2 sec. 4.3.1]. /// Throws error on division by zero. /// 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) { /// Integer reduction by a modulus to compute number mod modulus. This function uses division, /// and should not be used for repetitive operations. /// Input number to reduce. /// Modulus to reduce the input by. /// Output remainder = number mod modulus. /// Temporary space, optional. /// Temporary space, optional. /// The resulting remainder is in 0..modulus-1; same as "remainder". // TODO: More efficient reduction implementation var quotient = []; divRem(number, modulus, quotient, remainder, temp1, temp2); return remainder; } function modMul(multiplicant, multiplier, modulus, product, temp1, temp2) { /// 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. /// Multiplicand. /// Multiplier. /// Modulus to reduce the product. /// Output product = multiplicant * multiplier mod modulus. /// Scratch space (optional). /// Scratch space (optional). /// The resulting product in in 0..modulus-1; same as product. var quotient = []; multiply(multiplicant, multiplier, quotient); divRem(quotient, modulus, quotient, product, temp1, temp2); return product; } function eea(a, b, upp, vpp, rpp) { /// Extended Euclidean Algorithm, Berlekamp's version. On return /// b*upp - a*vpp = (-1)(k-1)*rpp. /// The first number a. /// The second number b. /// a^-1 mod b if gcd=1. Optional. /// b^-1 mod a if gcd=1. Optional./ /// gcd(a,b). /// k value. /// Algebraic Coding Theory, Pages 24-30. /// 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 /// // 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 aCompute greatest common divisor or a and b. /// First integer input. /// Second integer input. /// GCD output (optional). /// GCD(a,b), the same object as the output parameter if given or a new object otherwise. 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) { /// Modular multiplicative inverse a^-1 mod n. /// The number to invert. Condition: a < n, or the result would be n^-1 mod a. /// The modulus. /// a^-1 mod n (optional). /// True to pad the returned value to the length of the modulus (optional). /// a^-1 mod n. Same as the aInv parameter if the parameter is specified. //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) { /// Modular exponentiation in an integer group. /// The base of the exponentiation. /// The exponent. /// Modulus to reduce the result. /// Output element that takes the modular exponentiation result (optional). /// Modular exponentiation result, same as if not null, or a new object. 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) { /// Construct a new montgomeryMultiplier object with the given modulus. /// A prime modulus in little-endian digit form /// Montgomery Multiplier class /// This class implements high performance montgomery multiplication using /// CIOS, as well as modular exponentiation. function computeM0Prime(m0) { /// Compute m' = -(m^-1) mod b, 16 bit digits. Based on Tolga Acar's code. /// Digit m. /// Digit m'. 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) { /// Montgomery multiplication with the CIOS method. /// Multiplicant. /// Multiplier. /// Computed result multiplicant * multiplier * r^-1 mod n. /// Context (optional = this). 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) { /// Convert the digits in standard form to Montgomery residue representation. /// Input digits to convert, and also the output converted digits. // 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) { /// Convert from Montgomery residue representation to the standard form. /// Input digits to convert, and also the output converted digits. 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) { /// Compute base to exponent mod m into result. /// Base of length s in the context. /// Exponent. /// Output as base raised to exponent, and reduced to the modulus in the context. /// result base^exponent mod m; the same result object. // 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) { /// Construct a new IntegerGroup object with the given modulus. /// A big-endian number to represent the modulus in a byte array. /// 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. // 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) { /// Create a new element object from a byte value. /// Desired element in big-endian format in an array of bytes. /// An element object representing the given element. 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) { /// Create a new element object from an integer value. /// Desired element as an integer. /// An element object representing the given element. var digits = intToDigits(integer, this.m_digitWidth); return integerGroupElement(digits, this); } function createElementFromDigits(digits) { /// Create a new element object from a digit array. /// Desired element as a digit array. /// Object initialized with the given value. cryptoMath.normalizeDigitArray(digits, this.m_digitWidth, true); return integerGroupElement(digits, this); } function equals(otherGroup) { /// Return true if the given object is equivalent to this one. /// ) /// True if the given objects are equivalent. return compareDigits(this.m_modulus, otherGroup.m_modulus) === 0; } function add(addend1, addend2, sum) { /// Add this element to another element. /// /// /// 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) { /// Subtract an element from another element. /// /// /// 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) { /// Compute the modular inverse of the given element. /// The element to be inverted. /// Receives the inverse element. cryptoMath.modInv(element.m_digits, this.m_modulus, outputElement.m_digits); } function multiply(multiplicant, multiplier, product) { /// Multiply an element by another element in the integer group. /// Multiplicand. /// Multiplier. /// Product reduced by the group modulus. /// Same as . return cryptoMath.modMul(multiplicant.m_digits, multiplier.m_digits, this.m_modulus, product.m_digits, temp0, temp1); } function modexp(valueElement, exponent, outputElement) { /// Modular exponentiation in an integer group. /// The base input to the exponentiation. /// The exponentas an unsigned integer. /// Output element that takes the modular exponentiation result. /// Computed result. Same as if not null, a new object otherwise. 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) { /// integerGroupElement inner class. /// Create a new integer element mod n. /// /// /// An array of digits representing the element. /// /// /// The parent group to which this element belongs. /// // The value given in digits // must be >= 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) { /// Compare an elements to this for equality. /// Element to compare. /// True if elements are equal, false otherwise. 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() { /// Elliptic Curve Cryptography (ECC) functions. // 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) { /// /// /// /// /// /// /// 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) { /// /// /// /// /// /// /// var returnObj; // 'optional' parameters if (typeof z === "undefined") { z = null; } if (typeof isInMontgomeryForm === "undefined") { isInMontgomeryForm = false; } function equals( 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) { /// /// 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) { /// // 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) { /// 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) { /// /// // 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) { /// /// // 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) { /// 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. /// Window size /// /// Precomputation table 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) { /// /// 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) { /// /// /// 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) { /// /// /// 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) { /// /// /// // 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) { /// /// /// // 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) { /// Input point to negate. /// (x, p - y). if (point !== outputPoint) { point.copy(outputPoint); } modSub(point.curve.p, point.y, outputPoint.y); } function convertToMontgomeryForm(point) { /// 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) { /// 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) { /// 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) { /// 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) { /// /// Point validation // Check if point P=(x,y) lies on the curve and if x,y are in [0, p-1] /// 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) { /// /// /// 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) { /// /// 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) { /// /// 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) { /// 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. /// Input EC point to encode. /// A bytes array containing 0x04 | X | Y, where X and Y are big endian encoded x and y coordinates. 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) { /// /// 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) { /// // 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) { /// Given a number a, returns a solution x to x^2 = a (mod p). /// An integer a. /// The square root of the number a modulo p, if it exists, /// otherwise null. // 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) { /// An integer a. 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) { /// /// /// Type definition for block function /// /// Block data /// Block number to operate on /// /// K constants /// Array to hold w values /// Hash values /// return []; } var msrcryptoSha = function (name, der, h, k, blockBytes, blockFunction, truncateTo) { /// /// Returns a hash function using the passed in parameters. /// /// Name of the hash function. /// /// /// /// The number of bytes in a block. /// Function for processing blocks. /// Truncate the resulting hash to a fixed length. /// 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) { /// /// 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. /// /// Byte data to hash /// Unprocessed data at the end of the message that did /// not fill an entire block. 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() { /// /// Converts stored hash values (32-bit ints) to bytes. /// /// // 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) { /// /// Builds and appends padding to a message /// /// Message to pad /// The message array + padding 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) { /// /// Computes the hash of an entire message. /// /// Byte array to hash /// Hash of message bytes buffer = hashBlocks(messageBytes); return finish(); } function process(messageBytes) { /// /// Call process repeatedly to hash a stream of bytes. Then call 'finish' to /// complete the hash and get the final result. /// /// // 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() { /// /// Called after one or more calls to process. This will finalize the hashing /// of the 'streamed' data and return the hash. /// /// Hash of message bytes // 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 /// var msrcryptoSha1 = (function () { function hashBlock(message, blockIndex, hv, k, w) { /// /// Block function for hashing algorithm to use. /// /// Block data to hash /// The block of the data to hash /// Initial hash values /// K constants /// Buffer for w values /// Updated initial hash values 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) { /// /// Block function for hashing algorithm to use. /// /// Block data to hash /// The block of the data to hash /// Initial hash values /// K constants /// Buffer for w values /// Updated initial hash values 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) { /// /// Block function for hashing algorithm to use. /// /// Block data to hash /// The block of the data to hash /// Initial hash values /// K constants /// Buffer for w values /// Updated initial hash values 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) { /// Computes the HMAC /// Data to MAC /// Array of bytes for key /// sha-224, sha-256, sha-384, sha-512 (default sha-256) /// Returns an array of bytes as the HMAC keyBytes = key; selectHashAlgorithm(hashAlgorithm); processHmac(dataBytes); var result = finishHmac(); clearState(); return result; }, process: function (dataBytes, key, hashAlgorithm) { /// Computes a partial HMAC to be followed by subsequent process calls or finish() /// Data to MAC /// Array of bytes for key /// sha-224, sha-256, sha-384, sha-512 (default sha-256) if (!hashFunction) { keyBytes = key; selectHashAlgorithm(hashAlgorithm); } processHmac(dataBytes); }, finish: function (key, hashAlgorithm) { /// Computes the final HMAC upon partial computations from previous process() calls. /// Array of bytes for key /// sha-224, sha-256, sha-384, sha-512 (default sha-256) /// Returns an array of bytes as the HMAC // 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 { /// Advanced Encryption Standard implementation per FIPS 197. 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) { /// Operates on the state column by column, performing a multiplication by x^4 + 1 in GF(2^8) /// the current state (length 16) /// The mixed state 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) { /// Operates on the state column by column, performing a multiplication by x^4 + 1 in GF(2^8) /// the current state (length 16) /// The mixed state 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) { /// /// Apply PKCS7 padding to message /// An array of blocks to pad < /// 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) { /// /// Remove PKCS7 padding from the message /// /// True for legal padding. False if not. /// 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) { /// perform the encryption of the plain text message /// the plain text to encrypt /// the encrypted message 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) { /// perform the decryption of the encrypted message /// the plain text to encrypt /// the encrypted message 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() { /// Pseudo Random Number Generator function/class. /// 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. /// 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) { /// Adds one to a big integer represented in an array (the first argument). /// The counter byte array to add one to encoded in big endian; index 0 is the MSW. 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() { /// Instantiate the PRNG with given entropy and personalization string. /// Array of bytes obtained from the source of entropy input. /// Optional application-provided personalization string. 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) { /// Reseed the PRNG with additional entropy. /// Input entropy. /// Optional additional entropy input. 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) { /// Add the providedData to the internal entropy pool, and update internal state. /// Input to add to the internal entropy pool. 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) { /// Generate pseudo-random bits, and update the internal PRNG state. /// Number of pseudorandom bytes to be returned. /// Application-provided additional input array (optional). /// Generated pseudorandom bytes. 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, /// Reseed the PRNG with additional entropy. /// Input entropy. /// Optional additional entropy input. init: function (entropy, personalization) { /// Initialize the PRNG by seeing with entropy and optional input data. /// Input entropy. /// Optional input. 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() { /// Opportunistic entropy collector. /// 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. /// 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() { /// Initialize the internal pool with as much randomness as one can get in JS. /// In the worst case, there is zero bits of entropy. 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) { /// 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. /// Entropy input. 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) { /// Add the mouse coordinates to the entropy pool and the Date. /// Event data with mouse information. 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 () { /// Add date to the entropy pool. /// Date valueOf() returns milliseconds since midnight 1/1/1970 UTC in a 32 bit integer 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) { /// Mix in entropy into the pool. /// Entropy to mix in. entropyPoolPrng.reseed(entropy); }, read: function (length) { /// Read entropy from the entropy pool. This function fails if there isn't enough entropy. /// Number of bytes of requested entropy. /// Entropy if there is enough in the pool, or undefined if there isn't enough entropy. 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) { /// Result in a digit array. 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>> 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) { /// /// /// /// /// 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) { /// /// /// For testing purposes we allow the key bytes to be passed in /// instead of randomly generated. /// /// 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) { /// /// Creates a new promise. /// /// A function that takes two parameters: function(resolved, rejected){...} /// A new Promise object 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) { /// /// Called by the executor function when the function has succeeded. /// /// A result value that will be passed to the then() function. // 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) { /// /// Joins two or more promises and returns only when all the specified promises have completed or been rejected. /// /// Array of promises. /// Returns a Promise. 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) { /// /// 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. /// /// Required. One or more promises. /// Result of first promise to resolve or fail. 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) { /// /// Creates a new rejected promise with a result equal to the passed in argument. /// /// Required. The reason why the promise was rejected. /// return new Promise( function (resolve, reject) { reject(rejectReason); }); }; window.Promise.resolve = function (resolveResult) { /// /// Creates a new resolved promise with a result equal to its argument. /// /// Required. The value returned with the completed promise. /// 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. } }; } /// Obj,oncomplete,onerror 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 = 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) { jobCompleted(worker); if(e.data && e.data.type !== "process"){ 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) { /// /// Encrypt a UInt8Array of data. Encrypt will return an ArrayBuffer if supported, otherwise it will return a regular Array. /// /// /// UInt8Array /// /// /// /// Encrypt an array of bytes. Encrypt will return an ArrayBuffer if supported, otherwise it will return a regular Array. /// /// /// An array of bytes (number from 0-255) /// /// /// /// Encrypt an array of bytes. Encrypt will return an ArrayBuffer if supported, otherwise it will return a regular Array. /// /// /// A continuous array of bytes (number values from 0-255) /// /// return executeOperation("encrypt", arguments, 0); }, decrypt: function (algorithm, keyHandle, buffer) { /// /// 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) /// /// /// UInt8Array /// /// /// /// Decrypt an array of byte values. Decrypt will return an ArrayBuffer if supported, otherwise it will return a regular Array. /// /// /// An array of bytes values (numbers from 0-255) /// /// return executeOperation("decrypt", arguments, 0); }, sign: function (algorithm, keyHandle, buffer) { /// /// 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) /// /// /// UInt8Array /// /// /// /// Sign an array of byte values. Sign will return an ArrayBuffer if supported, otherwise it will return a regular Array. /// /// /// An array of bytes values (numbers from 0-255) /// /// return executeOperation("sign", arguments, 0); }, verify: function (algorithm, keyHandle, signature, buffer) { /// /// Verify a signature. /// /// /// UInt8Array /// UInt8Array /// /// /// /// Verify a signature. /// /// /// UInt8Array /// An array of bytes values (numbers from 0-255) /// /// /// /// Verify a signature. /// /// /// An array of bytes values (numbers from 0-255) /// An array of bytes values (numbers from 0-255) /// /// /// /// Verify a signature. /// /// /// An array of bytes values (numbers from 0-255) /// UInt8Array /// /// return executeOperation("verify", arguments, 0); }, digest: function (algorithm, buffer) { /// /// Digest data using a specified cryptographic hash algorithm /// /// UInt8Array /// /// /// /// Digest data using a specified cryptographic hash algorithm /// /// An array of bytes values (numbers from 0-255) /// /// return executeOperation("digest", arguments, 0); }, generateKey: function (algorithm, extractable, keyUsage) { /// /// Generate a new key for use with the algorithm specified by the algorithm parameter /// /// /// /// /// return executeOperation("generateKey", arguments, 1); }, deriveKey: function (algorithm, baseKey, derivedKeyType, extractable, keyUsage) { /// /// Generate a key for the specified derivedKeyType, using the specified cryptographic key derivation algorithm with the given baseKey as input. /// /// /// /// /// /// /// return executeOperation("deriveKey", arguments, 1); }, deriveBits: function (algorithm, baseKey, length) { /// /// Generate an array of bytes from a given baseKey as input. /// /// /// Number of bytes to return. /// /// return executeOperation("deriveBits", arguments, 0); }, importKey: function (format, keyData, algorithm, extractable, keyUsage) { /// /// Constructs a new Key object using the key data specified by the keyData parameter. /// /// An object representing a key in jwk format. /// /// /// /// /// return executeOperation("importKey", arguments, 1); }, exportKey: function (format, keyHandle) { /// /// Exports the given key material of the Key object as specified by the key parameter. /// /// /// /// // 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) { /// /// Returns a KeyOperation object which will asynchronously return an array containing the key material of key, encrypted with keyEncryptionKey using the specified keyWrappingAlgorithm. /// /// /// /// /// /// return executeOperation("wrapKey", arguments, 0); }, unwrapKey: function (format, unwrappingKey, unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsage) { /// /// Construct a Key object from encrypted key material. /// /// An array of bytes values (numbers from 0-255) /// /// /// /// /// /// /// /// Construct a Key object from encrypted key material. /// /// /// /// /// /// /// /// 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 = { /// Microsoft Research Javascript Crypto Library Subtle interface. subtle: msrcryptoSubtle, getRandomValues: function(array) { /// /// Places cryptographically random values into the given array. /// /// /// /// /// Places cryptographically random values into the given array. /// /// Returns ArrayBufferView if supported. /// 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) { /// /// Add entropy to the PRNG. /// Entropy input to seed or reseed the PRNG. /// 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) { /// /// Convert string or array data to Base64. /// Byte values (numbers 0-255) /// Return Base64Url encoding (this is different from Base64 encoding.) /// /// /// /// Convert string or array data to Base64. /// /// Return Base64Url encoding (this is different from Base64 encoding.) /// /// return msrcryptoUtilities.toBase64(data, false); }, base64ToString: function (base64String) { /// /// Decode a Base64 encoded string to a plain string. /// Base64 encoded string. /// /// return msrcryptoUtilities.base64ToString(base64String); }, /// URL of the this msrCrypto script. 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); } function standardizeAlgoName(name) { var upper = name.toUpperCase(); if (upper === 'RSASSA-PKCS1-V1_5') return 'RSASSA-PKCS1-v1_5'; return upper; } function standardizeAlgo(algo) { algo.name = algo.name && standardizeAlgoName(algo.name); if (algo.hash) algo.hash.name = algo.hash.name && algo.hash.name.toUpperCase(); } function b64u2bin(b64u) { return atob(b64u .replace(/-/g, '+') .replace(/_/g, '/')); } function bin2Uint8(bin) { var uint8Array = typeof Uint8Array === 'undefined' ? [] : new Uint8Array(bin.length); for (var s = 0, sl = bin.length; s < sl; s++) { uint8Array[s] = bin.charCodeAt(s); } return uint8Array; } var algMap = { HMAC: { 'SHA-256': 'HS256', 'SHA-384': 'HS384', 'SHA-512': 'HS512' }, 'AES-GCM': { 128: 'A128GCM', 192: 'A192GCM', 256: 'A256GCM' }, 'AES-CBC': { 128: 'A128CBC', 192: 'A192CBC', 256: 'A256CBC' } }; function getAlg(algorithm, key) { var key1 = algorithm.name; if (!key1) key1 = algorithm; var key2 = algorithm.length || (key.length * 8); if (key1 === 'HMAC') { key2 = algorithm.hash.name; } var alg = algMap[key1] && algMap[key1][key2]; if (!alg) { console.log(algorithm, key); throw new Error('Could not find an appropriate alg'); } return alg; } 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; } if (publicMethods && publicMethods.subtle) { var originalImportKey = publicMethods.subtle.importKey; publicMethods.subtle.importKey = function importKey() { var importType = arguments[0]; var key = arguments[1]; var algorithm = arguments[2]; if (importType === 'raw') { key = msrcryptoUtilities.toArray(key); key = arguments[1] = { kty: 'oct', k: msrcryptoUtilities.toBase64(key), alg: getAlg(algorithm, key), ext: true } } return originalImportKey.apply(this, arguments) .then(function(res) { standardizeAlgo(res.algorithm); switch(res.type) { case 'secret': res.usages = ['sign', 'verify']; break; case 'private': res.usages = ['sign']; break; case 'public': res.usages = ['verify']; break; } if (key.kty === 'RSA') { res.algorithm.modulusLength = b64u2bin(key.n).length * 8; res.algorithm.publicExponent = bin2Uint8(b64u2bin(key.e)); } return res; }); } var originalGenerateKey = publicMethods.subtle.generateKey; publicMethods.subtle.generateKey = function generateKey() { return originalGenerateKey.apply(this, arguments) .then(function(res) { if (res.publicKey) { res.publicKey.usages = ['verify']; standardizeAlgo(res.publicKey.algorithm); res.privateKey.usages = ['sign']; standardizeAlgo(res.privateKey.algorithm); } else { res.usages = ['sign', 'verify']; standardizeAlgo(res.algorithm); } return res; }); } var originalExportKey = publicMethods.subtle.exportKey; publicMethods.subtle.exportKey = function exportKey() { var exportType = arguments[0]; return originalExportKey.apply(this, arguments) .then(function(res) { if (res.kty === 'RSA' || res.kty === 'EC') { if (res.d) { res.key_ops = ['sign']; } else { res.key_ops = ['verify']; } } if (exportType === 'raw') { res = toArrayBufferIfSupported(msrcryptoUtilities.base64ToBytes(res.k)); } return res; }); } } return publicMethods; })(); export const subtle = msrCrypto.subtle; export default msrCrypto;