about summary refs log tree commit diff
path: root/src/third-party/msrcrypto.js
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2022-07-19 14:40:06 -0500
committerPaul Frazee <pfrazee@gmail.com>2022-07-19 14:40:06 -0500
commit6b32698b3e020e5910c92b72a1677e7cd56287d6 (patch)
tree745f9676c581272e01fddf4387dc322145e93811 /src/third-party/msrcrypto.js
parent84dac9fa39c58da05af9fb3a2eb0e29699355c97 (diff)
downloadvoidsky-6b32698b3e020e5910c92b72a1677e7cd56287d6.tar.zst
Bundle official msrcrypto distro in the source (solves some bugs, probably safer)
Diffstat (limited to 'src/third-party/msrcrypto.js')
-rw-r--r--src/third-party/msrcrypto.js10144
1 files changed, 10144 insertions, 0 deletions
diff --git a/src/third-party/msrcrypto.js b/src/third-party/msrcrypto.js
new file mode 100644
index 000000000..b24885a69
--- /dev/null
+++ b/src/third-party/msrcrypto.js
@@ -0,0 +1,10144 @@
+// https://github.com/microsoft/MSR-JavaScript-Crypto
+// version "1.6.5"
+//*******************************************************************************
+//
+//    Copyright 2020 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.
+//
+//*******************************************************************************
+'use strict'
+
+var msrCryptoVersion = '1.6.5'
+
+;(function (root, factory) {
+  if (typeof define === 'function' && define.amd) {
+    define([], function () {
+      return (root.msrCrypto = factory(root))
+    })
+  } else if (typeof exports === 'object') {
+    module.exports = factory(root)
+  } else {
+    root.msrCrypto = factory(root)
+  }
+})(this, function (global) {
+  global = global || {}
+
+  var msrCrypto = function () {
+    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
+    }
+
+    var scriptUrl = (function () {
+      if (typeof document !== 'undefined') {
+        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' &&
+        typeof self.location !== 'undefined'
+      ) {
+        return self.location.href
+      }
+
+      return null
+    })()
+
+    var fprngEntropyProvided = false
+
+    var webWorkerSupport = typeof Worker !== 'undefined'
+
+    var runningInWorkerInstance =
+      typeof importScripts === 'function' && self instanceof WorkerGlobalScope
+
+    var workerInitialized = false
+
+    var typedArraySupport = typeof ArrayBuffer !== 'undefined'
+
+    var setterSupport = (function () {
+      try {
+        Object.defineProperty({}, 'oncomplete', {})
+        return true
+      } catch (ex) {
+        return false
+      }
+    })()
+
+    var asyncMode = false
+
+    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)
+    }
+
+    var msrcryptoHashFunctions = {}
+
+    var msrcryptoUtilities = (function () {
+      var encodingChars =
+        'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
+
+      function consoleLog(text) {
+        if ('console' in self && 'log' in console) {
+          console.log(text)
+        }
+      }
+
+      function toBase64(data, base64Url) {
+        var dataType = getObjectType(data)
+
+        if (
+          dataType !== 'Array' &&
+          dataType !== 'Uint8Array' &&
+          dataType !== 'ArrayBuffer'
+        ) {
+          throw new Error('invalid input')
+        }
+
+        var output = ''
+        var input = toArray(data)
+
+        if (!base64Url) {
+          base64Url = false
+        }
+
+        var char1, char2, char3, enc1, enc2, enc3, enc4
+        var i
+
+        for (i = 0; i < input.length; i += 3) {
+          char1 = input[i]
+          char2 = input[i + 1]
+          char3 = input[i + 2]
+
+          enc1 = char1 >> 2
+          enc2 = ((char1 & 0x3) << 4) | (char2 >> 4)
+          enc3 = ((char2 & 0xf) << 2) | (char3 >> 6)
+          enc4 = char3 & 0x3f
+
+          if (isNaN(char2)) {
+            enc3 = enc4 = 64
+          } else if (isNaN(char3)) {
+            enc4 = 64
+          }
+
+          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 base64ToBytes(encodedString) {
+        encodedString = encodedString.replace(/-/g, '+').replace(/_/g, '/')
+
+        while (encodedString.length % 4 !== 0) {
+          encodedString += '='
+        }
+
+        var output = []
+        var char1, char2, char3
+        var enc1, enc2, enc3, enc4
+        var i
+
+        encodedString = encodedString.replace(/[^A-Za-z0-9\+\/\=]/g, '')
+
+        for (i = 0; i < encodedString.length; i += 4) {
+          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))
+
+          char1 = (enc1 << 2) | (enc2 >> 4)
+          char2 = ((enc2 & 15) << 4) | (enc3 >> 2)
+          char3 = ((enc3 & 3) << 6) | enc4
+
+          output.push(char1)
+
+          if (enc3 !== 64) {
+            output.push(char2)
+          }
+
+          if (enc4 !== 64) {
+            output.push(char3)
+          }
+        }
+
+        return output
+      }
+
+      function getObjectType(object) {
+        return Object.prototype.toString.call(object).slice(8, -1)
+      }
+
+      function bytesToHexString(bytes, separate) {
+        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()
+          if (hexval.length === 1) {
+            result += '0'
+          }
+
+          result += hexval
+        }
+
+        return result
+      }
+
+      function bytesToInt32(bytes, index) {
+        index = index || 0
+
+        return (
+          (bytes[index] << 24) |
+          (bytes[index + 1] << 16) |
+          (bytes[index + 2] << 8) |
+          bytes[index + 3]
+        )
+      }
+
+      function hexToBytesArray(hexString) {
+        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) {
+        var newObject = {}
+        for (var propertyName in object) {
+          if (object.hasOwnProperty(propertyName)) {
+            newObject[propertyName] = object[propertyName]
+          }
+        }
+        return newObject
+      }
+
+      function unpackData(base64String, arraySize, toUint32s) {
+        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) {
+        return [
+          (int32 >>> 24) & 255,
+          (int32 >>> 16) & 255,
+          (int32 >>> 8) & 255,
+          int32 & 255,
+        ]
+      }
+
+      function int32ArrayToBytes(int32Array) {
+        var result = []
+        for (var i = 0; i < int32Array.length; i++) {
+          result = result.concat(int32ToBytes(int32Array[i]))
+        }
+        return result
+      }
+
+      function xorVectors(a, b, res) {
+        var length = Math.min(a.length, b.length),
+          res = res || new Array(length)
+        for (var i = 0; i < length; i += 1) {
+          res[i] = a[i] ^ b[i]
+        }
+        return res
+      }
+
+      function getVector(length, fillValue) {
+        if (isNaN(fillValue)) {
+          fillValue = 0
+        }
+
+        var res = new Array(length)
+        for (var i = 0; i < length; i += 1) {
+          res[i] = fillValue
+        }
+        return res
+      }
+
+      function toArray(typedArray) {
+        if (!typedArray) {
+          return []
+        }
+
+        if (typedArray.pop) {
+          return typedArray
+        }
+
+        if (getObjectType(typedArray) === 'ArrayBuffer') {
+          typedArray = new Uint8Array(typedArray)
+        } else if (typedArray.BYTES_PER_ELEMENT > 1) {
+          typedArray = new Uint8Array(typedArray.buffer)
+        }
+
+        if (typedArray.length === 1) {
+          return [typedArray[0]]
+        }
+
+        if (typedArray.length < 65536) {
+          return Array.apply(null, typedArray)
+        }
+
+        var returnArray = new Array(typedArray.length)
+        for (var i = 0; i < typedArray.length; i++) {
+          returnArray[i] = typedArray[i]
+        }
+
+        return returnArray
+      }
+
+      function padEnd(array, value, finalLength) {
+        while (array.length < finalLength) {
+          array.push(value)
+        }
+
+        return array
+      }
+
+      function padFront(array, value, finalLength) {
+        while (array.length < finalLength) {
+          array.unshift(value)
+        }
+
+        return array
+      }
+
+      function arraysEqual(array1, array2) {
+        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) {
+        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
+      }
+
+      function checkParam(param, type, errorMessage) {
+        if (!param) {
+          throw new Error(errorMessage)
+        }
+
+        if (type && getObjectType(param) !== type) {
+          throw new Error(errorMessage)
+        }
+
+        return true
+      }
+
+      function stringToBytes(text) {
+        var encodedBytes = []
+
+        for (var i = 0, j = 0; i < text.length; i++) {
+          var charCode = text.charCodeAt(i)
+
+          if (charCode < 128) {
+            encodedBytes[j++] = charCode
+          } else if (charCode < 2048) {
+            encodedBytes[j++] = (charCode >>> 6) | 192
+            encodedBytes[j++] = (charCode & 63) | 128
+          } else if (charCode < 0xd800 || charCode > 0xdfff) {
+            encodedBytes[j++] = (charCode >>> 12) | 224
+            encodedBytes[j++] = ((charCode >>> 6) & 63) | 128
+            encodedBytes[j++] = (charCode & 63) | 128
+          } else {
+            charCode =
+              (charCode - 0xd800) * 0x400 +
+              (text.charCodeAt(++i) - 0xdc00) +
+              0x10000
+            encodedBytes[j++] = (charCode >>> 18) | 240
+            encodedBytes[j++] = ((charCode >>> 12) & 63) | 128
+            encodedBytes[j++] = ((charCode >>> 6) & 63) | 128
+            encodedBytes[j++] = (charCode & 63) | 128
+          }
+        }
+
+        return encodedBytes
+      }
+
+      function bytesToString(textBytes) {
+        var result = '',
+          charCode
+
+        textBytes = toArray(textBytes)
+
+        for (var i = 0; i < textBytes.length; ) {
+          var encodedChar = textBytes[i++]
+
+          if (encodedChar < 128) {
+            charCode = encodedChar
+          } else if (encodedChar < 224) {
+            charCode = (encodedChar << 6) + textBytes[i++] - 0x3080
+          } else if (encodedChar < 240) {
+            charCode =
+              (encodedChar << 12) +
+              (textBytes[i++] << 6) +
+              textBytes[i++] -
+              0xe2080
+          } else {
+            charCode =
+              (encodedChar << 18) +
+              (textBytes[i++] << 12) +
+              (textBytes[i++] << 6) +
+              textBytes[i++] -
+              0x3c82080
+          }
+
+          if (charCode > 0xffff) {
+            var surrogateHigh =
+              Math.floor((charCode - 0x10000) / 0x400) + 0xd800
+            var surrogateLow = ((charCode - 0x10000) % 0x400) + 0xdc00
+            result += String.fromCharCode(surrogateHigh, surrogateLow)
+            continue
+          }
+
+          result += String.fromCharCode(charCode)
+        }
+
+        return result
+      }
+
+      return {
+        consoleLog: consoleLog,
+        toBase64: toBase64,
+        fromBase64: base64ToBytes,
+        checkParam: checkParam,
+        getObjectType: getObjectType,
+        bytesToHexString: bytesToHexString,
+        bytesToInt32: bytesToInt32,
+        stringToBytes: stringToBytes,
+        bytesToString: bytesToString,
+        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 asn1 = (function () {
+      var asn1Types = {
+        0x00: 'CUSTOM',
+        0x01: 'BOOLEAN',
+        0x02: 'INTEGER',
+        0x03: 'BIT STRING',
+        0x04: 'OCTET STRING',
+        0x05: 'NULL',
+        0x06: 'OBJECT IDENTIFIER',
+        0x10: 'SEQUENCE',
+        0x11: 'SET',
+        0x13: 'PRINTABLE STRING',
+        0x17: 'UTCTime',
+      }
+
+      var asn1Classes = {
+        0x00: 'UNIVERSAL',
+        0x01: 'APPLICATION',
+        0x02: 'Context-Defined',
+        0x03: 'PRIVATE',
+      }
+
+      function parse(bytes, force) {
+        force = !!force
+
+        var type = asn1Types[bytes[0] & 0x1f],
+          dataLen = bytes[1],
+          i = 0,
+          constructed = !!(bytes[0] & 0x20),
+          remainder,
+          child,
+          header
+
+        if (dataLen & 0x80) {
+          for (i = 0, dataLen = 0; i < (bytes[1] & 127); i++) {
+            dataLen = (dataLen << 8) + bytes[2 + i]
+          }
+        }
+
+        header = 2 + i
+
+        if (type === undefined || dataLen > bytes.length) {
+          return null
+        }
+
+        var obj = constructed ? [] : {}
+
+        obj.type = type
+        obj.header = header
+        obj.data = bytes.slice(0, dataLen + header)
+        if (constructed || force) {
+          if (obj.type === 'BIT STRING' && bytes[header] === 0) {
+            i++
+          }
+          remainder = bytes.slice(header, obj.data.length)
+          while (remainder.length > 0) {
+            child = parse(remainder)
+            if (child === null) {
+              break
+            }
+            obj.push(child)
+            remainder = remainder.slice(child.data.length)
+          }
+        }
+        return obj
+      }
+
+      function encode(asn1tree) {
+        throw new Error('not implemented')
+      }
+
+      function toString(objTree, indent) {
+        var output =
+          new Array(indent + 1).join(' ') +
+          objTree.type +
+          ' (' +
+          objTree.length +
+          ') ' +
+          bytesToHexString(objTree.data).substring(0, 16) +
+          '\n'
+
+        if (!objTree.children) {
+          return output
+        }
+
+        for (var i = 0; i < objTree.children.length; i++) {
+          output += toString(objTree.children[i], indent + 4) + ''
+        }
+
+        return output
+      }
+
+      return {
+        parse: parse,
+        encode: encode,
+        toString: function (objTree) {
+          return toString(objTree, 0)
+        },
+      }
+    })()
+
+    var msrcryptoWorker = (function () {
+      function returnResult(result) {
+        if (workerInitialized && runningInWorkerInstance) {
+          self.postMessage(result)
+        }
+        return result
+      }
+
+      var workerId, operationType, operationSubType
+
+      return {
+        jsCryptoRunner: function (e) {
+          workerId = e.data.workerid
+          operationType = e.data.operationType
+          operationSubType = e.data.operationSubType
+
+          var operation = e.data.operationType,
+            result,
+            func = operations[operation][e.data.algorithm.name],
+            p = e.data
+
+          if (!operations.exists(operation, e.data.algorithm.name)) {
+            throw new Error('unregistered algorithm.')
+          }
+
+          if (p.operationSubType) {
+            result = returnResult({
+              type: p.operationSubType,
+              result: func(p),
+            })
+          } else {
+            result = returnResult(func(p))
+          }
+
+          return result
+        },
+
+        returnResult: returnResult,
+      }
+    })()
+
+    if (runningInWorkerInstance) {
+      self.onmessage = function (e) {
+        if (!workerInitialized && e.data.prngSeed) {
+          var entropy = e.data.prngSeed
+          msrcryptoPseudoRandom.init(entropy)
+          workerInitialized = true
+          return msrcryptoWorker.returnResult({
+            initialized: true,
+          })
+        }
+
+        if (workerInitialized === true) {
+          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).toUpperCase()
+
+        if (algType === 'RSA') {
+          return 'RSA'
+        }
+
+        if (algType === 'ECD') {
+          return 'EC'
+        }
+
+        return 'oct'
+      }
+
+      function hashSize(algorithm) {
+        return algorithm.hash.name.substring(
+          algorithm.hash.name.indexOf('-') + 1,
+        )
+      }
+
+      var algorithmMap = {
+        HMAC: function (algorithm) {
+          return 'HS' + hashSize(algorithm)
+        },
+
+        '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' + hashSize(algorithm)
+        },
+
+        'RSA-OAEP': function (algorithm) {
+          if (algorithm.hash.name.toUpperCase() === 'SHA-1') {
+            return 'RSA-OAEP'
+          }
+          return 'RSA-OAEP-' + hashSize(algorithm)
+        },
+
+        'RSA-PSS': function (algorithm) {
+          return 'PS' + hashSize(algorithm)
+        },
+
+        ECDSA: 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
+        if (algorithmMap[keyHandle.algorithm.name.toUpperCase()]) {
+          key.alg = algorithmMap[keyHandle.algorithm.name.toUpperCase()](
+            keyHandle.algorithm,
+          )
+        }
+        key.key_ops = keyHandle.usages
+        if (keyData.pop) {
+          key.k = utils.toBase64(keyData, true)
+        } else {
+          for (var property in keyData) {
+            if (keyData[property].pop && property !== 'key_ops') {
+              key[property] = utils.toBase64(keyData[property], true)
+            }
+          }
+        }
+
+        if (keyHandle.algorithm.namedCurve) {
+          key.crv = keyHandle.algorithm.namedCurve
+        }
+
+        return key
+      }
+
+      function findUsage(usage, usages) {
+        for (var i = 0; i < usages.length; i++) {
+          if (usage.toUpperCase() === usages[i].toUpperCase()) {
+            return true
+          }
+        }
+        return false
+      }
+
+      function keyToJwkOld(keyHandle, keyData) {
+        var key = {}
+
+        key.kty = getKeyType(keyHandle)
+        key.extractable = keyHandle.extractable
+
+        if (keyData.pop) {
+          key.k = utils.toBase64(keyData, true)
+        } else {
+          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)
+      }
+
+      function jwkToKey(keyData, algorithm, propsToArray) {
+        var jsonKeyObject = JSON.parse(JSON.stringify(keyData))
+
+        for (var i = 0; i < propsToArray.length; i += 1) {
+          var propValue = jsonKeyObject[propsToArray[i]]
+          if (propValue) {
+            jsonKeyObject[propsToArray[i]] = utils.fromBase64(propValue)
+          }
+        }
+
+        return jsonKeyObject
+      }
+
+      return {
+        keyToJwkOld: keyToJwkOld,
+        keyToJwk: keyToJwk,
+        jwkToKey: jwkToKey,
+      }
+    })()
+
+    function msrcryptoMath() {
+      var DIGIT_BITS = 24
+      var DIGIT_NUM_BYTES = Math.floor(DIGIT_BITS / 8)
+      var DIGIT_MASK = (1 << DIGIT_BITS) - 1
+      var DIGIT_BASE = 1 << DIGIT_BITS
+      var DIGIT_MAX = DIGIT_MASK
+      var DIG_INV = 1 / DIGIT_BASE
+      var DIGIT_MAX_ADDS = 31
+
+      var DIGIT_SCALER = [1, 256]
+      for (var ds = 2; ds <= DIGIT_NUM_BYTES; ds++) {
+        DIGIT_SCALER[ds] = DIGIT_SCALER[ds - 1] * 256
+      }
+
+      var Zero = [0]
+      var One = [1]
+
+      function createArray(parameter) {
+        var i,
+          array = null
+        if (!arguments.length || typeof arguments[0] === 'number') {
+          array = new Array(parameter)
+          for (i = 0; i < parameter; i += 1) {
+            array[i] = 0
+          }
+        } else if (typeof arguments[0] === 'object') {
+          array = new Array(parameter.length)
+          for (i = 0; i < parameter.length; i += 1) {
+            array[i] = parameter[i]
+          }
+        }
+        return array
+      }
+
+      function stringToDigits(numberStr, radix) {
+        numberStr = numberStr.replace(/^\s+|\s+$/g, '')
+        var num = [0]
+        var buffer = [0]
+        radix = radix || 10
+        for (var i = 0; i < numberStr.length; i += 1) {
+          var char = parseInt(numberStr[i], radix)
+          if (isNaN(char)) {
+            throw new Error(
+              'Failed to convert string to integer in radix ' +
+                radix.toString(),
+            )
+          }
+
+          multiply(num, radix, buffer)
+
+          add(buffer, [char], num)
+          normalizeDigitArray(num)
+        }
+
+        return num
+      }
+
+      function digitsToString(digits, radix) {
+        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
+
+        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)
+
+        while (sb.length !== 0 && sb[0] === '0') {
+          sb = sb.substring(1, sb.length)
+        }
+
+        if (sb.length === 0) {
+          sb = '0'
+        }
+
+        return sb
+      }
+
+      function computeBitArray(bytes) {
+        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(digit) {
+        var index = 0
+
+        for (var i = 0; i < DIGIT_BITS; i++) {
+          index = Math.max(index, -((digit >>> i) & 1) & i)
+        }
+
+        return index
+      }
+
+      function highestSetBit(bytes) {
+        var i = 0
+        var bitLength = 0
+
+        while (i < bytes.length) {
+          if (bitLength === 0) {
+            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 = digits.slice()
+
+        var recodedDigits = [],
+          windowSizeBits = Math.pow(2, windowSize),
+          windowSizeMinus1Bits = Math.pow(2, windowSize - 1)
+
+        for (var i = 0; i < t; i++) {
+          recodedDigits[i] = (digits[0] % windowSizeBits) - windowSizeMinus1Bits
+
+          digits[0] = digits[0] - recodedDigits[i]
+
+          cryptoMath.shiftRight(digits, digits, windowSize - 1)
+        }
+
+        recodedDigits[i] = digits[0]
+
+        return recodedDigits
+      }
+
+      function fixedWindowRecode2(digits, windowSize) {
+        var digLen = digits.length,
+          bits = new Array(digLen * DIGIT_BITS),
+          i = 0,
+          j = 0,
+          k = 0,
+          r = 0,
+          dig,
+          result = new Array(Math.ceil((digLen * DIGIT_BITS) / windowSize))
+
+        for (k = 0, result[0] = 0; i < digLen; i++) {
+          for (j = 0, dig = digits[i]; j < DIGIT_BITS; j++, dig >>>= 1) {
+            if (k === windowSize) {
+              result[++r] = 0
+              k = 0
+            }
+            result[r] += (dig & 1) << k++
+          }
+        }
+
+        return result
+      }
+
+      function fetchBits(digits, startBit, count) {
+        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) {
+        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) {
+        while (length-- > 0) {
+          destination[destIndex + length] = source[sourceIndex + length]
+        }
+      }
+
+      function isZero(array) {
+        var i,
+          result = 0
+
+        for (i = 0; i < array.length; i += 1) {
+          result = result | array[i]
+        }
+        return !result
+      }
+
+      function isEven(array) {
+        return (array[0] & 0x1) === 0x0
+      }
+
+      function sequenceEqual(left, right) {
+        var equal = left.length === right.length
+
+        for (var i = 0; i < Math.min(left.length, right.length); i += 1) {
+          if (left[i] !== right[i]) {
+            equal = false
+          }
+        }
+
+        return equal
+      }
+
+      function bytesToDigits(bytes) {
+        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
+          }
+        }
+
+        if (digit !== 0) {
+          array[index] = digit
+        }
+
+        while (array[--arrayLength] == null) {
+          array[arrayLength] = 0
+        }
+
+        return array
+      }
+
+      function digitsToBytes(digits, trim, minTrimLength) {
+        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.reverse()
+
+        if (minTrimLength === undefined) {
+          minTrimLength = 1
+        }
+        if (trim) {
+          while (bytes.length > minTrimLength && bytes[0] === 0) {
+            bytes.shift()
+          }
+        }
+
+        return bytes
+      }
+
+      function intToDigits(value, numDigits) {
+        if (typeof numDigits === 'undefined') {
+          if (value <= 1) {
+            numDigits = 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) {
+        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) {
+        var result = 0,
+          val,
+          i
+
+        for (i = 0; i < Math.max(left.length, right.length); i++) {
+          val = ~~left[i] - ~~right[i]
+          result = val + (result & -!val)
+        }
+
+        return result
+      }
+
+      function normalizeDigitArray(digits, length, pad) {
+        var i = mswIndex(digits)
+
+        digits.length = length || i + 1
+
+        if (pad) {
+          while (++i < digits.length) {
+            digits[i] = 0
+          }
+        }
+
+        if (digits.length <= 0) {
+          digits[0] = 0
+          digits.length = 1
+        }
+
+        return digits
+      }
+
+      function shiftRight(source, destination, bits, length) {
+        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
+        }
+
+        destination[n] = source[n] >>> bits
+      }
+
+      function shiftLeft(source, destination, bits, length) {
+        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
+        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
+        }
+
+        destination[0] = (source[0] << bits) & DIGIT_MASK
+      }
+
+      function add(addend1, addend2, sum) {
+        var shortArray = addend1
+        var longArray = addend2
+        if (addend2.length < addend1.length) {
+          shortArray = addend2
+          longArray = addend1
+        }
+
+        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
+        }
+
+        sum.length = longArray.length
+
+        if (carry !== 0) {
+          sum[i] = carry & DIGIT_MASK
+        }
+
+        return carry
+      }
+
+      function subtract(minuend, subtrahend, difference) {
+        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
+        }
+
+        while (i < minuend.length) {
+          carry += minuend[i]
+          difference[i++] = carry & DIGIT_MASK
+          carry = carry >> DIGIT_BITS
+        }
+
+        return carry
+      }
+
+      function multiply(a, b, p) {
+        b = typeof b === 'number' ? [b] : b
+
+        var i,
+          j,
+          k,
+          l,
+          c,
+          t1,
+          t2,
+          alen = a.length,
+          blen = b.length,
+          bi
+
+        for (i = 0; i < alen + blen; i += 1) {
+          p[i] = 0
+        }
+
+        i = 0
+        l = 0
+
+        var maxRounds = 31
+        var ks = 0
+
+        while (i < blen) {
+          l = Math.min(l + maxRounds, blen)
+
+          for (; i < l; i++) {
+            bi = b[i]
+            for (j = 0; j < alen; j++) {
+              p[i + j] += a[j] * bi
+            }
+          }
+
+          c = 0
+          for (k = ks; k < i + alen; k++) {
+            t1 = p[k] + c
+            t2 = t1 & DIGIT_MASK
+            p[k] = t2
+            c = (t1 - t2) * DIG_INV
+          }
+          p[k] = c
+
+          ks += maxRounds
+        }
+
+        p.length = alen + blen
+
+        return p
+      }
+
+      function divRem(dividend, divisor, quotient, remainder, temp1, temp2) {
+        var m = mswIndex(dividend) + 1
+        var n = mswIndex(divisor) + 1
+        var qhat, rhat, carry, p, t, i, j
+
+        if (m < n) {
+          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)) {
+          throw new Error('Division by zero.')
+        } else if (n === 1) {
+          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
+        }
+
+        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
+
+        quotient.length = m - n + 1
+        remainder.length = n
+        for (j = m - n; j >= 0; j--) {
+          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]
+
+          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
+          }
+
+          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 = Math.floor(p / DIGIT_BASE) - Math.floor(t / DIGIT_BASE)
+          }
+
+          t = un[j + n] - carry
+          un[j + n] = t & DIGIT_MASK
+
+          quotient[j] = qhat & DIGIT_MASK
+
+          if (t < 0) {
+            quotient[j] = quotient[j] - 1
+
+            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
+          }
+        }
+
+        for (i = 0; i < n; i++) {
+          remainder[i] =
+            ((un[i] >>> s) | (un[i + 1] << (DIGIT_BITS - s))) & DIGIT_MASK
+        }
+
+        normalizeDigitArray(quotient)
+        normalizeDigitArray(remainder)
+      }
+
+      function reduce(number, modulus, remainder, temp1, temp2) {
+        var quotient = []
+        divRem(number, modulus, quotient, remainder, temp1, temp2)
+
+        return remainder
+      }
+
+      function modMul(
+        multiplicand,
+        multiplier,
+        modulus,
+        product,
+        temp1,
+        temp2,
+      ) {
+        var quotient = []
+        multiply(multiplicand, multiplier, quotient)
+        divRem(quotient, modulus, quotient, product, temp1, temp2)
+
+        return product
+      }
+
+      function eea(a, b, upp, vpp, rpp) {
+        var rp
+        if (isZero(a)) {
+          copyArray(b, 0, rpp, 0, b.length)
+          rpp.length = b.length
+          return 0
+        } else if (isZero(b)) {
+          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
+        }
+
+        var k = -1
+
+        var upp_out = upp
+        var vpp_out = vpp
+        var rpp_out = rpp
+        var save
+
+        while (!isZero(rp)) {
+          divRem(rpp, rp, q, r, u, v)
+
+          if (uppPresent) {
+            multiply(q, up, u)
+            add(u, upp, u)
+            normalizeDigitArray(u)
+            save = upp
+            upp = up
+            up = u
+            u = save
+          }
+
+          if (vppPresent) {
+            multiply(q, vp, v)
+            add(v, vpp, v)
+            normalizeDigitArray(v)
+            save = vpp
+            vpp = vp
+            vp = v
+            v = save
+          }
+
+          save = rpp
+          rpp = rp
+          rp = r
+          r = save
+
+          k++
+        }
+
+        if (uppPresent) {
+          copyArray(upp, 0, upp_out, 0, upp.length)
+          upp_out.length = upp.length
+        }
+        if (vppPresent) {
+          copyArray(vpp, 0, vpp_out, 0, vpp.length)
+          vpp_out.length = vpp.length
+        }
+        copyArray(rpp, 0, rpp_out, 0, rpp.length)
+        rpp_out.length = rpp.length
+
+        return k
+      }
+
+      function gcd(a, b, output) {
+        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) {
+        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 {
+          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 modInvCT(a, n, aInv, pad) {
+        var nMinus2 = []
+        aInv = aInv || []
+        subtract(n, [2], nMinus2)
+        modExp(a, nMinus2, n, aInv)
+        normalizeDigitArray(aInv)
+        return aInv
+      }
+
+      function modExp(base, exponent, modulus, result) {
+        result = result || []
+
+        if (compareDigits(exponent, Zero) === 0) {
+          result[0] = 1
+        } else if (compareDigits(exponent, One) === 0) {
+          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, context) {
+        function computeM0Prime(m0) {
+          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 montgomeryReduction(t, m, result) {
+          var m0 = m[0]
+          var mPrime = computeM0Prime(m0)
+          var n = m.length
+          var A = t.slice(0)
+          var ui = []
+          var uimbi = []
+          var uim = []
+          var bi = [1]
+
+          for (var i = 0; i < n; i++) {
+            ui = (A[i] * mPrime) % DIGIT_BASE
+
+            multiply(m, [ui], uim)
+            multiply(uim, bi, uimbi)
+
+            add(A, uimbi, A)
+
+            bi.unshift(0)
+          }
+
+          A = A.slice(n)
+          for (i = 0; i < A.length; i++) {
+            result[i] = A[i]
+          }
+        }
+
+        function montgomeryMultiply(multiplicand, multiplier, result, ctx) {
+          ctx = ctx || this
+
+          var m = ctx.m,
+            s = m.length,
+            mPrime = ctx.mPrime,
+            m0 = ctx.m0,
+            rightI,
+            r0,
+            q,
+            i = 0,
+            j,
+            jm1,
+            t1,
+            t2,
+            carry,
+            rounds = 0
+
+          var temp = createArray(s + 2)
+
+          while (i < s) {
+            rounds = Math.min(s, rounds + 16)
+
+            for (; i < rounds; ) {
+              rightI = ~~multiplier[i]
+
+              r0 = temp[0] + multiplicand[0] * rightI
+
+              q = ((r0 & DIGIT_MASK) * mPrime) & DIGIT_MASK
+
+              temp[1] += ((m0 * q + r0) * DIG_INV) | 0
+
+              for (j = 1, jm1 = 0; j < s; jm1 = j, j += 1) {
+                temp[jm1] = temp[j] + m[j] * q + multiplicand[j] * rightI
+              }
+              temp[jm1] = temp[j]
+              temp[j] = 0
+
+              i++
+            }
+
+            carry = 0
+            for (j = 0; j < s; j++) {
+              t1 = temp[j] + carry
+              t2 = t1 & DIGIT_MASK
+              temp[j] = t2
+              carry = (t1 - t2) * DIG_INV
+            }
+            temp[j] = carry
+          }
+
+          for (i = 0; i < s; i += 1) {
+            result[i] = temp[i]
+          }
+          result.length = s
+
+          var needSubtract = +(cryptoMath.compareDigits(temp, m) > 0)
+          cryptoMath.subtract(result, m, ctx.temp2)
+
+          ctSetArray(needSubtract, result, ctx.temp2)
+
+          return
+        }
+
+        function convertToMontgomeryForm(digits) {
+          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) {
+          this.montgomeryMultiply(digits, this.one, this.temp1)
+          for (var i = 0; i < this.s; i += 1) {
+            digits[i] = this.temp1[i]
+          }
+        }
+
+        function optimalWindowSize(length) {
+          var i = 2,
+            t1,
+            t0,
+            bits = length * DIGIT_BITS
+
+          t0 = 4 + Math.ceil(bits / 2) * 3 + 1
+          do {
+            i++
+            t1 = t0
+            t0 = Math.pow(2, i) + Math.ceil(bits / i) * (i + 1) + 1
+          } while (t0 < t1)
+
+          return i - 1
+        }
+
+        function modExp(base, exponent, result, skipSideChannel) {
+          skipSideChannel = !!skipSideChannel
+
+          var windowBits = optimalWindowSize(exponent.length)
+
+          var i,
+            j,
+            expBits = fixedWindowRecode2(exponent, windowBits).reverse(),
+            partialResult = this.rModM.slice(0),
+            baseTableLen = Math.pow(2, windowBits),
+            bt = baseTable
+
+          bt.length = baseTableLen
+          bt[0] = this.rModM
+          for (i = 1; i < baseTableLen; i++) {
+            bt[i] = []
+            multiply(bt[i - 1], base, bt[i])
+            this.reduce(bt[i])
+          }
+
+          var tableVal = []
+          var exp
+
+          for (i = 0; i < expBits.length; i++) {
+            for (j = 0; j < windowBits; j++) {
+              this.montgomeryMultiply(
+                partialResult,
+                partialResult,
+                partialResult,
+              )
+            }
+
+            exp = expBits[i]
+
+            skipSideChannel
+              ? (tableVal = bt[exp])
+              : getTableEntry(bt, exp, tableVal)
+
+            this.montgomeryMultiply(partialResult, tableVal, partialResult)
+          }
+
+          this.montgomeryMultiply(partialResult, this.one, result)
+
+          return result
+        }
+
+        function getTableEntry(bt, exp, tableVal) {
+          var z, t, mask, tableEntry, k
+          for (z = 0; z < bt[0].length; z++) {
+            tableVal[z] = 0
+          }
+          for (t = 0; t < bt.length; t++) {
+            tableEntry = bt[t]
+            mask = -(exp === t)
+            for (k = 0; k < tableEntry.length; k++) {
+              tableVal[k] = tableVal[k] | (tableEntry[k] & mask)
+            }
+          }
+        }
+
+        function ctSetArray(condition, a, b) {
+          var bMask = -condition
+          var aMask = ~bMask
+
+          for (var i = 0; i < a.length; i++) {
+            a[i] = (a[i] & aMask) | (b[i] & bMask)
+          }
+        }
+
+        function reduce(x, result) {
+          var k = this.m.length,
+            q1,
+            q2,
+            q3,
+            r1,
+            r2,
+            i,
+            needSubtract,
+            temp = []
+
+          result = result || x
+
+          q1 = x.slice(k - 1)
+          q2 = []
+          multiply(q1, this.mu, q2)
+          q3 = q2.slice(k + 1)
+
+          r1 = x.slice(0, k + 1)
+          r2 = []
+          multiply(q3, m, r2)
+          r2 = r2.slice(0, k + 1)
+
+          r1[k + 1] = compareDigits(r1, r2) >>> 31
+
+          for (i = 0; i < result.length; i++) {
+            result[i] = 0
+          }
+          subtract(r1, r2, result)
+
+          needSubtract = +(compareDigits(result, m) > 0)
+          cryptoMath.subtract(result, m, temp)
+          ctSetArray(needSubtract, result, temp)
+
+          normalizeDigitArray(result)
+
+          return
+        }
+
+        function computeContext(modulus) {
+          var s = modulus.length
+
+          var m0 = modulus[0]
+
+          var ctx = {
+            m: modulus,
+            mPrime: computeM0Prime(m0),
+            m0: m0,
+            temp1: createArray(2 * s + 1),
+            temp2: createArray(2 * s + 1),
+          }
+
+          var R = createArray(modulus.length * 2)
+          R[R.length] = 1
+          ctx.mu = []
+          divRem(R, modulus, ctx.mu, [])
+
+          var quotient = createArray(2 * s + 1)
+          var rRemainder = createArray(s + 1)
+          var temp1 = createArray(2 * s + 1)
+          var temp2 = createArray(2 * s + 1)
+          var rDigits = rRemainder
+          rDigits[s] = 1
+          divRem(rDigits, modulus, quotient, rRemainder, temp1, temp2)
+          ctx.rModM = normalizeDigitArray(rRemainder, s, true)
+
+          var rSquaredModm = createArray(2 * s + 1)
+          var rSquaredDigits = rSquaredModm
+          rSquaredDigits[s * 2] = 1
+          divRem(rSquaredDigits, modulus, quotient, rSquaredModm, temp1, temp2)
+          ctx.rSquaredModm = normalizeDigitArray(rSquaredModm, s, true)
+
+          ctx.rCubedModm = createArray(s)
+          montgomeryMultiply(rSquaredModm, rSquaredModm, ctx.rCubedModm, ctx)
+
+          return ctx
+        }
+
+        context = context || computeContext(modulus)
+
+        var m = context.m
+
+        var mu = context.mu
+
+        var m0 = context.m0
+
+        var s = m.length
+
+        var zeros = createArray(s + 1)
+
+        var one = zeros.slice(0, s)
+        one[0] = 1
+
+        var mPrime = context.mPrime
+
+        var rModM = context.rModM
+
+        var rSquaredModm = context.rSquaredModm
+
+        var rCubedModm = context.rCubedModm
+
+        var temp1 = createArray(2 * s + 1)
+        var temp2 = createArray(2 * s + 1)
+
+        var baseTable = new Array(4)
+        baseTable[0] = rModM
+        baseTable[1] = new Array(s)
+        baseTable[2] = new Array(s)
+        baseTable[3] = new Array(s)
+
+        return {
+          m: m,
+
+          m0: m0,
+
+          mPrime: mPrime,
+          mu: mu,
+
+          rSquaredModm: rSquaredModm,
+          s: s,
+          rModM: rModM,
+          rCubedModm: rCubedModm,
+          one: one,
+          temp1: temp1,
+          temp2: temp2,
+
+          convertToMontgomeryForm: convertToMontgomeryForm,
+          convertToStandardForm: convertToStandardForm,
+          montgomeryMultiply: montgomeryMultiply,
+          modExp: modExp,
+          reduce: reduce,
+
+          ctx: context,
+        }
+      }
+
+      function IntegerGroup(modulusBytes) {
+        var m_modulus = bytesToDigits(modulusBytes)
+
+        var m_digitWidth = m_modulus.length
+
+        var m_zero = intToDigits(0, m_digitWidth)
+        var m_one = intToDigits(1, m_digitWidth)
+
+        var temp0 = createArray(m_digitWidth)
+        var temp1 = createArray(m_digitWidth)
+
+        var montmul = new MontgomeryMultiplier(m_modulus)
+
+        function createElementFromBytes(bytes) {
+          var digits = bytesToDigits(bytes)
+
+          if (cryptoMath.compareDigits(digits, this.m_modulus) >= 0) {
+            throw new Error(
+              'The number provided is not an element of this group',
+            )
+          }
+
+          normalizeDigitArray(digits, this.m_digitWidth, true)
+          return integerGroupElement(digits, this)
+        }
+
+        function createElementFromInteger(integer) {
+          var digits = intToDigits(integer, this.m_digitWidth)
+          return integerGroupElement(digits, this)
+        }
+
+        function createElementFromDigits(digits) {
+          cryptoMath.normalizeDigitArray(digits, this.m_digitWidth, true)
+          return integerGroupElement(digits, this)
+        }
+
+        function equals(otherGroup) {
+          return compareDigits(this.m_modulus, otherGroup.m_modulus) === 0
+        }
+
+        function add(addend1, addend2, sum) {
+          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) >>> 31) - 1) & DIGIT_MASK
+
+          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) {
+          var i,
+            s = this.m_digitWidth
+          var result = outputElement.m_digits
+          var carry = cryptoMath.subtract(
+            leftElement.m_digits,
+            rightElement.m_digits,
+            outputElement.m_digits,
+          )
+
+          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) {
+          cryptoMath.modInv(
+            element.m_digits,
+            this.m_modulus,
+            outputElement.m_digits,
+          )
+        }
+
+        function multiply(multiplicand, multiplier, product) {
+          return cryptoMath.modMul(
+            multiplicand.m_digits,
+            multiplier.m_digits,
+            this.m_modulus,
+            product.m_digits,
+            temp0,
+            temp1,
+          )
+        }
+
+        function modexp(valueElement, exponent, outputElement) {
+          outputElement = outputElement || integerGroupElement([], this)
+
+          if (compareDigits(exponent, m_zero) === 0) {
+            outputElement.m_digits = intToDigits(1, this.m_digitWidth)
+          } else if (compareDigits(exponent, m_one) === 0) {
+            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) {
+          return {
+            m_digits: digits,
+            m_group: group,
+
+            equals: function (element) {
+              return (
+                compareDigits(this.m_digits, element.m_digits) === 0 &&
+                this.m_group.equals(this.m_group, element.m_group)
+              )
+            },
+          }
+        }
+
+        return {
+          m_modulus: m_modulus,
+          m_digitWidth: m_digitWidth,
+          montmul: montmul,
+
+          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,
+        bytesToDigits: bytesToDigits,
+        stringToDigits: stringToDigits,
+        digitsToString: digitsToString,
+        intToDigits: intToDigits,
+        digitsToBytes: digitsToBytes,
+        isZero: isZero,
+        isEven: isEven,
+
+        shiftRight: shiftRight,
+        shiftLeft: shiftLeft,
+        compareDigits: compareDigits,
+        bitLength: highestSetBit,
+
+        fixedWindowRecode: fixedWindowRecode,
+        IntegerGroup: IntegerGroup,
+
+        add: add,
+        subtract: subtract,
+        multiply: multiply,
+        divRem: divRem,
+        reduce: reduce,
+        modInv: modInv,
+        modInvCT: modInvCT,
+        modExp: modExp,
+        modMul: modMul,
+        MontgomeryMultiplier: MontgomeryMultiplier,
+        gcd: gcd,
+        sequenceEqual: sequenceEqual,
+        swapEndianness: function (bytes) {
+          return bytes.reverse()
+        },
+        computeBitArray: computeBitArray,
+      }
+    }
+
+    var cryptoMath = cryptoMath || msrcryptoMath()
+
+    function MsrcryptoECC() {
+      var btd = cryptoMath.bytesToDigits
+
+      function createArray(parameter) {
+        var i,
+          array = null
+        if (!arguments.length || typeof arguments[0] === 'number') {
+          array = []
+          for (i = 0; i < parameter; i += 1) {
+            array[i] = 0
+          }
+        } else if (typeof arguments[0] === 'object') {
+          array = []
+          for (i = 0; i < parameter.length; i += 1) {
+            array[i] = parameter[i]
+          }
+        }
+        return array
+      }
+
+      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,
+          a: a1,
+          b: b1,
+          order: order,
+          generator: 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),
+          btd(curveData.a),
+          btd(curveData.b),
+          btd(curveData.order),
+          btd(curveData.gx),
+          btd(curveData.gy),
+        )
+
+        newCurve.type = curveData.type
+        newCurve.name = curveData.name
+        newCurve.generator.curve = newCurve
+
+        return newCurve
+      }
+
+      var createTedCurve = function (curveData) {
+        var newCurve = new EllipticCurveFp(
+          btd(curveData.p),
+          btd(curveData.a),
+          btd(curveData.d),
+          btd(curveData.order),
+          btd(curveData.gx),
+          btd(curveData.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
+
+        if (typeof z === 'undefined') {
+          z = null
+        }
+
+        if (typeof isInMontgomeryForm === 'undefined') {
+          isInMontgomeryForm = false
+        }
+
+        function equals(ellipticCurvePointFp) {
+          if (!ellipticCurvePointFp) {
+            return false
+          }
+
+          if (returnObj.isInfinity && ellipticCurvePointFp.isInfinity) {
+            return true
+          }
+
+          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) {
+        var m_curve = curve
+
+        var tedCurve = curve.type === 1
+
+        var fieldElementWidth = curve.p.length
+
+        var montgomeryMultiplier = cryptoMath.MontgomeryMultiplier(curve.p)
+
+        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,
+        )
+
+        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)
+
+        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[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
+            }
+
+            carry = carry << (cryptoMath.DIGIT_BITS - 1)
+
+            cryptoMath.shiftRight(result, result)
+
+            result[s - 1] |= carry
+          } else {
+            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.isInfinity) {
+            outputPoint.isInfinity = true
+            return
+          }
+
+          montgomerySquare(point.z, temp1)
+
+          montgomeryMultiply(point.z, point.y, temp4)
+
+          modAdd(point.x, temp1, temp2)
+
+          modSub(point.x, temp1, temp1)
+
+          outputPoint.z = temp4.slice()
+
+          montgomeryMultiply(temp1, temp2, temp3)
+
+          modDivByTwo(temp3, temp2)
+
+          modAdd(temp3, temp2, temp1)
+
+          montgomerySquare(point.y, temp2)
+
+          montgomerySquare(temp1, temp4)
+
+          montgomeryMultiply(point.x, temp2, temp3)
+
+          modSub(temp4, temp3, temp4)
+
+          modSub(temp4, temp3, outputPoint.x)
+
+          modSub(temp3, outputPoint.x, temp4)
+
+          montgomerySquare(temp2, temp3)
+
+          montgomeryMultiply(temp1, temp4, temp2)
+
+          modSub(temp2, temp3, outputPoint.y)
+
+          outputPoint.isInfinity = false
+          outputPoint.isInMontgomeryForm = true
+        }
+
+        function doubleAequals0(point, outputPoint) {
+          if (point.isInfinity) {
+            outputPoint.isInfinity = true
+            return
+          }
+
+          montgomerySquare(point.y, temp3)
+
+          montgomerySquare(point.x, temp4)
+
+          modAdd(temp4, temp4, temp0)
+          modAdd(temp0, temp4, temp4)
+
+          montgomeryMultiply(point.x, temp3, temp5)
+
+          montgomerySquare(temp3, temp0)
+
+          modDivByTwo(temp4, temp1)
+
+          montgomerySquare(temp1, temp3)
+
+          montgomeryMultiply(point.y, point.z, swap0)
+          for (var i = 0; i < swap0.length; i += 1) {
+            outputPoint.z[i] = swap0[i]
+          }
+
+          modSub(temp3, temp5, outputPoint.x)
+          modSub(outputPoint.x, temp5, outputPoint.x)
+
+          modSub(temp5, outputPoint.x, temp4)
+
+          montgomeryMultiply(temp1, temp4, temp2)
+
+          modSub(temp2, temp0, outputPoint.y)
+
+          outputPoint.isInfinity = false
+          outputPoint.isInMontgomeryForm = true
+        }
+
+        function generatePrecomputationTable(w, generatorPoint) {
+          var validationPoint = generatorPoint.clone()
+          convertToStandardForm(validationPoint)
+          if (!validatePoint(validationPoint)) {
+            throw new Error('Invalid Parameter')
+          }
+
+          var pointJac = generatorPoint.clone()
+          convertToJacobianForm(pointJac)
+
+          var tablePos = [generatorPoint.clone()]
+
+          var qJac = pointJac.clone()
+
+          var px2 = pointJac.clone()
+          double(pointJac, px2)
+          convertToAffineForm(px2)
+
+          var qAff
+
+          for (var i = 1; i < Math.pow(2, w - 2); i++) {
+            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.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.',
+            )
+          }
+          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
+          }
+
+          montgomerySquare(jacobianPoint.z, temp5)
+
+          montgomeryMultiply(jacobianPoint.z, temp5, temp6)
+
+          montgomeryMultiply(affinePoint.x, temp5, temp4)
+
+          montgomeryMultiply(affinePoint.y, temp6, temp5)
+
+          modSub(temp4, jacobianPoint.x, temp1)
+
+          modSub(temp5, jacobianPoint.y, temp2)
+
+          if (cryptoMath.isZero(temp1)) {
+            if (cryptoMath.isZero(temp2)) {
+              double(jacobianPoint, outputPoint)
+              mixedAdd(outputPoint, affinePoint, outputPoint)
+              return
+            } else {
+              outputPoint.x = jacobianPoint.x.slice(0)
+              outputPoint.y = jacobianPoint.y.slice(0)
+              outputPoint.z = jacobianPoint.z.slice(0)
+              return
+            }
+          }
+
+          montgomerySquare(temp2, temp4)
+
+          montgomerySquare(temp1, temp6)
+
+          montgomeryMultiply(temp6, jacobianPoint.x, temp5)
+
+          montgomeryMultiply(temp1, temp6, temp0)
+
+          modSub(temp4, temp5, temp3)
+          modSub(temp3, temp5, temp3)
+
+          montgomeryMultiply(jacobianPoint.z, temp1, temp4)
+
+          modSub(temp3, temp5, temp3)
+
+          montgomeryMultiply(temp0, jacobianPoint.y, temp6)
+
+          modSub(temp3, temp0, temp3)
+
+          if (cryptoMath.isZero(temp3)) {
+            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
+          }
+
+          modAdd(temp6, temp6, temp1)
+
+          montgomeryMultiply(temp4, temp3, outputPoint.z)
+
+          montgomeryMultiply(temp2, temp3, temp4)
+
+          montgomerySquare(temp3, temp0)
+
+          modAdd(temp1, temp4, temp1)
+
+          montgomeryMultiply(temp0, temp5, temp4)
+
+          montgomerySquare(temp1, temp7)
+
+          montgomeryMultiply(temp0, temp3, temp5)
+
+          modSub(temp7, temp4, outputPoint.x)
+          modSub(outputPoint.x, temp4, outputPoint.x)
+
+          modSub(outputPoint.x, temp5, outputPoint.x)
+
+          modSub(outputPoint.x, temp4, temp3)
+
+          montgomeryMultiply(temp5, temp6, temp0)
+
+          montgomeryMultiply(temp1, temp3, temp4)
+
+          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
+          }
+
+          montgomerySquare(jacobianPoint.z, temp1)
+
+          montgomeryMultiply(temp1, jacobianPoint.z, temp2)
+
+          montgomeryMultiply(temp1, affinePoint.x, temp3)
+
+          montgomeryMultiply(temp2, affinePoint.y, temp4)
+
+          modSub(temp3, jacobianPoint.x, temp1)
+
+          modSub(temp4, jacobianPoint.y, temp2)
+
+          var i
+          for (i = 0; i < temp1.length; i += 1) {
+            if (temp1[i] !== 0) {
+              montgomeryMultiply(jacobianPoint.z, temp1, temp0)
+              for (var j = 0; j < fieldElementWidth; j += 1) {
+                outputPoint.z[j] = temp0[j]
+              }
+
+              montgomerySquare(temp1, temp3)
+
+              montgomeryMultiply(temp3, temp1, temp4)
+
+              montgomeryMultiply(temp3, jacobianPoint.x, temp5)
+
+              modAdd(temp5, temp5, temp1)
+
+              montgomerySquare(temp2, outputPoint.x)
+
+              modSub(outputPoint.x, temp1, outputPoint.x)
+
+              modSub(outputPoint.x, temp4, outputPoint.x)
+
+              modSub(temp5, outputPoint.x, temp3)
+
+              montgomeryMultiply(temp2, temp3, temp5)
+
+              montgomeryMultiply(jacobianPoint.y, temp4, temp6)
+
+              modSub(temp5, temp6, outputPoint.y)
+
+              outputPoint.isInfinity = false
+              outputPoint.isInMontgomeryForm = true
+
+              return
+            }
+          }
+
+          for (i = 0; i < temp2.length; i += 1) {
+            if (temp2[i] !== 0) {
+              outputPoint.isInfinity = true
+              outputPoint.isInMontgomeryForm = true
+              return
+            }
+          }
+          affinePoint.copy(outputPoint)
+          this.convertToJacobianForm(outputPoint)
+          this.double(outputPoint, outputPoint)
+          outputPoint.isInMontgomeryForm = true
+        }
+
+        function scalarMultiply(k, point, outputPoint, multiplyBy4) {
+          if (point.isInfinity || cryptoMath.isZero(k)) {
+            outputPoint.isInfinity = true
+            return
+          }
+
+          if (cryptoMath.compareDigits(k, curve.order) >= 0) {
+            throw new Error('The scalar k must be in the range 1 <= k < order.')
+          }
+
+          k = k.slice()
+
+          if (point.curve.type === 1) {
+            var pointIsEP = typeof point.ta !== 'undefined'
+
+            if (!pointIsEP) {
+              convertToExtendedProjective(point)
+            }
+
+            scalarMultiplyTed(k, point, outputPoint, multiplyBy4)
+
+            if (!pointIsEP) {
+              normalizeTed(point)
+            }
+          } else {
+            var pointIsMF = point.isInMontgomeryForm,
+              outputIsMF = outputPoint.isInMontgomeryForm,
+              outputIsAffine = outputPoint.isAffine
+
+            if (!pointIsMF) {
+              convertToMontgomeryForm(point)
+            }
+
+            if (!outputIsMF) {
+              convertToMontgomeryForm(outputPoint)
+            }
+
+            scalarMultiplyW(k, point, outputPoint)
+
+            if (outputIsAffine) {
+              convertToAffineForm(outputPoint)
+            }
+
+            if (!pointIsMF) {
+              convertToStandardForm(point)
+            }
+
+            if (!outputIsMF) {
+              convertToStandardForm(outputPoint)
+            }
+          }
+
+          return
+        }
+
+        function scalarMultiplyW(k, point, outputPoint) {
+          var validationPoint = point.clone()
+          convertToStandardForm(validationPoint)
+
+          if (!validatePoint(validationPoint)) {
+            throw new Error('Invalid Parameters.')
+          }
+
+          var odd = k[0] & 1,
+            tempk = []
+
+          modSub(point.curve.order, k, tempk)
+          for (i = 0; i < k.length; i++) {
+            k[i] = ((odd - 1) & (k[i] ^ tempk[i])) ^ k[i]
+          }
+
+          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)
+
+            modSub(L.curve.p, L.y, tempk)
+            var mask = -(kDigits[i] >>> 31)
+            for (var n = 0; n < L.y.length; n++) {
+              L.y[n] = (L.y[n] & ~mask) | (tempk[n] & mask)
+            }
+
+            mixedDoubleAdd(Q, L, 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 mask, L
+
+          for (var i = 0; i < table.length; i++) {
+            mask = +(i === index)
+            L = [L, table[i].clone()][mask]
+          }
+
+          return L
+        }
+
+        function tableLookupW0(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) {
+          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
+          }
+
+          cryptoMath.modInv(point.z, curve.p, conversionTemp2, true)
+
+          if (point.isInMontgomeryForm) {
+            montgomeryMultiply(
+              conversionTemp2,
+              montgomeryMultiplier.rCubedModm,
+              conversionTemp1,
+            )
+            var swap = conversionTemp2
+            conversionTemp2 = conversionTemp1
+            conversionTemp1 = swap
+          }
+
+          montgomerySquare(conversionTemp2, conversionTemp0)
+
+          montgomeryMultiply(point.x, conversionTemp0, conversionTemp1)
+          for (var i = 0; i < fieldElementWidth; i += 1) {
+            point.x[i] = conversionTemp1[i]
+          }
+
+          montgomeryMultiply(point.y, conversionTemp0, conversionTemp1)
+          montgomeryMultiply(conversionTemp1, conversionTemp2, point.y)
+
+          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) {
+          if (point.isInfinity) {
+            return false
+          }
+
+          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
+        }
+
+        function validatePointTed(point) {
+          if (point.ta) {
+            point = point.clone()
+            normalizeTed(point)
+          }
+
+          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)
+
+          if (cryptoMath.isZero(temp1) === false) {
+            cryptoMath.reduce(temp1, point.curve.p, temp1)
+            if (cryptoMath.isZero(temp1) === false) {
+              return false
+            }
+          }
+
+          return true
+        }
+
+        function generatePrecomputationTableTed(npoints, point) {
+          var Q = point.clone(),
+            P2 = Q.clone(),
+            T = []
+
+          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++) {
+            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
+
+          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 = [],
+            kisNeg
+
+          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)
+
+            var mask = -(kDigits[i] >>> 31)
+
+            modSub(point.curve.p, L.x, tempk)
+            for (var m = 0; m < L.x.length; m++) {
+              L.x[m] = (L.x[m] & ~mask) | (tempk[m] & mask)
+            }
+
+            modSub(point.curve.p, L.td, tempk)
+            for (m = 0; m < L.td.length; m++) {
+              L.td[m] = (L.td[m] & ~mask) | (tempk[m] & mask)
+            }
+
+            addTedExtended(L, T, T)
+          }
+
+          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.')
+          }
+
+          cryptoMath.modMul(point.x, point.x, point.curve.p, temp0)
+
+          cryptoMath.modMul(point.y, point.y, point.curve.p, temp1)
+
+          cryptoMath.modMul(point.z, point.z, point.curve.p, point.ta)
+          modSub(temp1, temp0, outputPoint.tb)
+          modAdd(temp0, temp1, temp0)
+
+          modAdd(point.ta, point.ta, point.ta)
+
+          modAdd(point.y, point.y, point.y)
+
+          modSub(point.ta, temp0, temp1)
+
+          cryptoMath.modMul(point.x, point.y, point.curve.p, outputPoint.ta)
+
+          cryptoMath.modMul(temp0, outputPoint.tb, point.curve.p, outputPoint.y)
+
+          cryptoMath.modMul(temp1, outputPoint.ta, point.curve.p, outputPoint.x)
+
+          cryptoMath.modMul(temp0, temp1, point.curve.p, outputPoint.z)
+
+          return
+        }
+
+        function addTed(point1, point2, outputPoint) {
+          var cm = cryptoMath
+
+          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, point2, outputPoint) {
+          var cm = cryptoMath
+          var modulus = point2.curve.p
+
+          temp1 = []
+          temp2 = []
+          temp3 = []
+
+          cm.modMul(point2.z, qq.z, modulus, temp3)
+
+          cm.modMul(point2.ta, point2.tb, modulus, temp1)
+
+          modAdd(point2.x, point2.y, point2.ta)
+
+          cm.modMul(temp1, qq.td, modulus, temp2)
+
+          modAdd(qq.x, qq.y, point2.tb)
+
+          modSub(temp3, temp2, temp1)
+
+          modAdd(temp3, temp2, temp3)
+
+          cm.modMul(point2.ta, point2.tb, modulus, temp2)
+
+          cm.modMul(point2.x, qq.x, modulus, point2.z)
+
+          cm.modMul(point2.y, qq.y, modulus, point2.x)
+
+          modSub(temp2, point2.z, temp2)
+
+          modSub(point2.x, point2.z, outputPoint.ta)
+
+          modSub(temp2, point2.x, outputPoint.tb)
+
+          cm.modMul(outputPoint.ta, temp3, modulus, outputPoint.y)
+
+          cm.modMul(outputPoint.tb, temp1, modulus, outputPoint.x)
+
+          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
+
+          temp1 = [5]
+
+          modMul(a, temp1, p, temp2)
+
+          modSub(temp2, d, temp2)
+
+          modMul(d, temp1, p, temp3)
+
+          modSub(a, temp3, temp1)
+
+          modMul(tedPoint.y, temp1, p, temp3)
+
+          modAdd(temp3, temp2, temp2)
+
+          temp1 = [1]
+
+          modSub(temp1, tedPoint.y, temp3)
+
+          temp1 = [12]
+
+          modMul(temp1, temp3, p, temp4)
+
+          modInv(temp4, p, temp4, true)
+
+          modMul(tedPoint.x, temp3, p, temp1)
+
+          modAdd(temp1, temp1, temp3)
+
+          modAdd(temp3, temp3, temp3)
+
+          modInv(temp3, p, temp3, true)
+
+          modMul(temp4, temp2, p, wPoint.x)
+
+          temp1 = [1]
+
+          modAdd(tedPoint.y, temp1, temp1)
+
+          modSub(a, d, temp2)
+
+          modMul(temp1, temp2, p, temp4)
+
+          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)
+
+          modAdd(temp1, temp1, temp1)
+
+          modSub(temp1, a, temp2)
+
+          modSub(temp2, d, temp2)
+
+          modAdd(wPoint.y, wPoint.y, temp3)
+
+          modAdd(wPoint.y, temp3, temp3)
+
+          modAdd(temp3, temp3, temp3)
+
+          modInv(temp3, p, temp3, true)
+
+          modMul(temp2, temp3, p, tedPoint.x)
+
+          modAdd(temp1, temp1, temp1)
+
+          modAdd(temp1, d, temp2)
+
+          modAdd(temp1, a, temp1)
+
+          modAdd(a, a, temp3)
+
+          modSub(temp2, temp3, temp2)
+
+          modSub(temp2, temp3, temp2)
+
+          modSub(temp2, a, temp2)
+
+          modAdd(d, d, temp3)
+
+          modSub(temp1, temp3, temp1)
+
+          modSub(temp1, temp3, temp1)
+
+          modSub(temp1, d, temp1)
+
+          modInv(temp1, p, temp1, true)
+
+          modMul(temp1, temp2, p, tedPoint.y)
+
+          return
+        }
+
+        var methods = {
+          convertToMontgomeryForm: convertToMontgomeryForm,
+
+          convertToStandardForm: convertToStandardForm,
+
+          convertToAffineForm: convertToAffineForm,
+
+          convertToJacobianForm: convertToJacobianForm,
+
+          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.validatePoint = validatePointTed
+          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
+          methods.validatePoint = validatePoint
+        }
+
+        return methods
+      }
+
+      var sec1EncodingFp = function () {
+        return {
+          encodePoint: function (point) {
+            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)
+            } else {
+              var xOctetString = cryptoMath.digitsToBytes(point.x)
+              var yOctetString = cryptoMath.digitsToBytes(point.y)
+              var pOctetString = cryptoMath.digitsToBytes(point.curve.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)
+
+              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) {
+              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 {
+              throw new Error('Unsupported encoding format')
+            }
+          },
+        }
+      }
+
+      var ModularSquareRootSolver = function (modulus) {
+        var p = modulus
+
+        var specialK = []
+
+        if (typeof modulus === 'undefined') {
+          throw new Error('modulus')
+        }
+
+        if (cryptoMath.isEven(modulus)) {
+          throw new Error('Only odd moduli are supported')
+        }
+
+        var mul = cryptoMath.MontgomeryMultiplier(p)
+
+        if (p[0] % 4 === 3) {
+          cryptoMath.add(p, cryptoMath.One, specialK)
+          cryptoMath.shiftRight(specialK, specialK, 2)
+        } else {
+          specialK = null
+        }
+
+        var temp0 = new Array(p.length)
+        var temp1 = new Array(p.length)
+
+        function squareRootNistCurves(a) {
+          var beta = cryptoMath.intToDigits(0, 16)
+          mul.modExp(a, specialK, beta)
+
+          var aPrime = [0]
+          cryptoMath.modMul(beta, beta, mul.m, aPrime)
+
+          if (cryptoMath.compareDigits(a, aPrime) !== 0) {
+            return null
+          }
+
+          return beta
+        }
+
+        var publicMethods = {
+          squareRoot: function (a) {
+            if (specialK !== null) {
+              return squareRootNistCurves(a)
+            } else {
+              throw new Error('GeneralCase not supported.')
+            }
+          },
+
+          jacobiSymbol: function (a) {
+            var modEightMask = 0x7,
+              modFourMask = 0x3,
+              aPrime,
+              pPrime
+
+            aPrime = a.slice()
+            pPrime = p.slice()
+
+            cryptoMath.reduce(aPrime, pPrime, aPrime, temp0, temp1)
+
+            var t = 1
+
+            while (!cryptoMath.isZero(aPrime)) {
+              while (cryptoMath.isEven(aPrime)) {
+                cryptoMath.shiftRight(aPrime, aPrime)
+
+                var pMod8 = pPrime[0] & modEightMask
+                if (pMod8 === 3 || pMod8 === 5) {
+                  t = -t
+                }
+              }
+
+              var tmp = aPrime
+              aPrime = pPrime
+              pPrime = tmp
+
+              var aMod4 = aPrime[0] & modFourMask
+              var pMod4 = pPrime[0] & modFourMask
+              if (aMod4 === 3 && pMod4 === 3) {
+                t = -t
+              }
+
+              cryptoMath.reduce(aPrime, pPrime, aPrime, temp0, temp1)
+            }
+
+            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.')
+      }
+
+      var validateEccPoint = function (curveName, x, y, z) {
+        var curve = createCurve(curveName)
+        var point = new EllipticCurvePointFp(
+          curve,
+          false,
+          btd(x),
+          btd(y),
+          z && btd(z),
+          false,
+        )
+        var opp = new EllipticCurveOperatorFp(curve)
+        return opp.validatePoint(point)
+      }
+
+      return {
+        createCurve: createCurve,
+        curves: curvesInternal,
+        sec1EncodingFp: sec1EncodingFp,
+        validatePoint: validateEccPoint,
+        EllipticCurvePointFp: EllipticCurvePointFp,
+        EllipticCurveOperatorFp: EllipticCurveOperatorFp,
+        ModularSquareRootSolver: ModularSquareRootSolver,
+      }
+    }
+
+    var cryptoECC = cryptoECC || MsrcryptoECC()
+
+    var curve_P256 = {
+      name: 'P-256',
+      type: 0,
+      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,
+    }
+
+    var curve_P384 = {
+      name: 'P-384',
+      type: 0,
+      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,
+    }
+
+    var curve_P521 = {
+      name: 'P-521',
+      type: 0,
+      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,
+    }
+
+    if (typeof cryptoECC !== 'undefined') {
+      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,
+      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,
+    }
+
+    if (typeof cryptoECC !== 'undefined') {
+      cryptoECC.curves['BN-254'] = curve_BN254
+    }
+
+    var curve_numsp256d1 = {
+      info: ['numsp256d1', 256, 256, 256],
+      type: 0,
+      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(),
+      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(),
+      b: [0x81, 0x55, 0x02].reverse(),
+      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(),
+      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(),
+      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(),
+      cf: 1,
+    }
+
+    var curve_numsp256t1 = {
+      info: ['numsp256t1', 256, 255, 256],
+      name: 'numsp256t1',
+      type: 1,
+      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(),
+      a: [0x01],
+      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: [
+        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(),
+      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(),
+      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(),
+      cf: 4,
+    }
+
+    var curve_numsp384d1 = {
+      info: ['numsp384d1', 384, 384, 384],
+      name: 'numsp384d1',
+      type: 0,
+      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(),
+      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(),
+      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: [
+        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(),
+      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(),
+      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(),
+      cf: 1,
+    }
+
+    var curve_numsp384t1 = {
+      info: ['numsp384t1', 384, 382, 384],
+      name: 'numsp384t1',
+      type: 1,
+      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(),
+      a: [0x01],
+      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: [
+        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(),
+      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(),
+      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(),
+      cf: 4,
+    }
+
+    var curve_numsp512d1 = {
+      info: ['numsp512d1', 512, 512, 512],
+      name: 'numsp512d1',
+      type: 0,
+      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(),
+      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(),
+      b: [0x9b, 0xd9, 0x01].reverse(),
+      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(),
+      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(),
+      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(),
+      cf: 4,
+    }
+
+    var curve_numsp512t1 = {
+      info: ['numsp512t1', 512, 510, 512],
+      name: 'numsp512t1',
+      type: 1,
+      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(),
+      a: [0x01].reverse(),
+      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: [
+        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(),
+      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(),
+      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(),
+      cf: 4,
+    }
+
+    if (typeof cryptoECC !== 'undefined') {
+      cryptoECC.curves.NUMSP256D1 = curve_numsp256d1
+      cryptoECC.curves.NUMSP384D1 = curve_numsp384d1
+      cryptoECC.curves.NUMSP512D1 = curve_numsp512d1
+      cryptoECC.curves.NUMSP256T1 = curve_numsp256t1
+      cryptoECC.curves.NUMSP384T1 = curve_numsp384t1
+      cryptoECC.curves.NUMSP512T1 = curve_numsp512t1
+    }
+
+    var msrcryptoSha = function (
+      name,
+      der,
+      h,
+      k,
+      blockBytes,
+      blockFunction,
+      truncateTo,
+    ) {
+      var utils = msrcryptoUtilities
+
+      var hv = h.slice(),
+        w = new Array(blockBytes),
+        buffer = [],
+        blocksProcessed = 0
+
+      function hashBlocks(message) {
+        var blockCount = Math.floor(message.length / blockBytes)
+
+        for (var block = 0; block < blockCount; block++) {
+          blockFunction(message, block, hv, k, w)
+        }
+
+        blocksProcessed += blockCount
+
+        return message.slice(blockCount * blockBytes)
+      }
+
+      function hashToBytes() {
+        var hash = []
+
+        for (var i = 0; i < hv.length; i++) {
+          hash = hash.concat(utils.int32ToBytes(hv[i]))
+        }
+
+        hash.length = truncateTo / 8
+
+        return hash
+      }
+
+      function addPadding(messageBytes) {
+        var padLen = blockBytes - (messageBytes.length % blockBytes)
+
+        padLen <= blockBytes / 8 && (padLen += blockBytes)
+
+        var padding = utils.getVector(padLen)
+
+        padding[0] = 128
+
+        var messageLenBits =
+          (messageBytes.length + blocksProcessed * blockBytes) * 8
+
+        for (var i = 1; i <= 8; i++) {
+          padding[padLen - i] = messageLenBits % 0x100
+          messageLenBits = Math.floor(messageLenBits / 0x100)
+        }
+        return messageBytes.concat(padding)
+      }
+
+      function computeHash(messageBytes) {
+        buffer = hashBlocks(messageBytes)
+
+        return finish()
+      }
+
+      function process(messageBytes) {
+        buffer = buffer.concat(messageBytes)
+
+        if (buffer.length >= blockBytes) {
+          buffer = hashBlocks(buffer)
+        }
+
+        return
+      }
+
+      function finish() {
+        if (hashBlocks(addPadding(buffer)).length !== 0) {
+          throw new Error('buffer.length !== 0')
+        }
+
+        var result = hashToBytes()
+
+        buffer = []
+
+        hv = h.slice()
+
+        blocksProcessed = 0
+
+        return result
+      }
+
+      return {
+        name: name,
+        computeHash: computeHash,
+        process: process,
+        finish: finish,
+        der: der,
+        hashLen: truncateTo,
+        maxMessageSize: 0xffffffff,
+      }
+    }
+
+    var msrcryptoSha1 = (function () {
+      function hashBlock(message, blockIndex, hv, k, w) {
+        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]
+
+        for (i = 0; i < 16; i++) {
+          w[i] = utils.bytesToInt32(message, blockIndex * blockSize + i * 4)
+        }
+
+        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++) {
+          temp = (ra << 5) | (ra >>> 27)
+
+          temp +=
+            i >= 60
+              ? rb ^ rc ^ rd
+              : i >= 40
+              ? (rb & rc) ^ (rb & rd) ^ (rc & rd)
+              : i >= 20
+              ? rb ^ rc ^ rd
+              : (rb & rc) ^ (~rb & rd)
+
+          temp += re + k[i] + w[i]
+
+          re = rd
+          rd = rc
+          rc = (rb << 30) | (rb >>> 2)
+          rb = ra
+          ra = temp
+        }
+
+        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: function () {
+          return msrcryptoSha('SHA-1', der, h, k, 64, hashBlock, 160)
+        },
+      }
+    })()
+
+    if (typeof operations !== 'undefined') {
+      msrcryptoSha1.instances = {}
+
+      msrcryptoSha1.getInstance = function (id) {
+        return (
+          msrcryptoSha1.instances[id] ||
+          (msrcryptoSha1.instances[id] = msrcryptoSha1.sha1())
+        )
+      }
+
+      msrcryptoSha1.deleteInstance = function (id) {
+        msrcryptoSha1.instances[id] = null
+        delete msrcryptoSha1.instances[id]
+      }
+
+      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) {
+        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]
+
+        for (i = 0; i < 16; i++) {
+          w[i] = utils.bytesToInt32(message, blockIndex * blockSize + i * 4)
+        }
+
+        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
+          rg = rf
+          rf = re
+          re = rd
+          rd = rc
+          rc = rb
+          rb = ra
+          ra = temp
+        }
+
+        hv[0] = (hv[0] + ra) >>> 0
+        hv[1] = (hv[1] + rb) >>> 0
+        hv[2] = (hv[2] + rc) >>> 0
+        hv[3] = (hv[3] + rd) >>> 0
+        hv[4] = (hv[4] + re) >>> 0
+        hv[5] = (hv[5] + rf) >>> 0
+        hv[6] = (hv[6] + rg) >>> 0
+        hv[7] = (hv[7] + rh) >>> 0
+
+        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,
+      )
+
+      der224 = upd('MC0wDQYJYIZIAWUDBAIEBQAEHA')
+
+      der256 = upd('MDEwDQYJYIZIAWUDBAIBBQAEIA')
+
+      return {
+        sha224: function () {
+          return msrcryptoSha('SHA-224', der224, h224, k256, 64, hashBlock, 224)
+        },
+        sha256: function () {
+          return msrcryptoSha('SHA-256', der256, h256, k256, 64, hashBlock, 256)
+        },
+      }
+    })()
+
+    if (typeof operations !== 'undefined') {
+      msrcryptoSha256.instance224 =
+        msrcryptoSha256.instance224 || msrcryptoSha256.sha224()
+      msrcryptoSha256.instance256 =
+        msrcryptoSha256.instance256 || msrcryptoSha256.sha256()
+
+      msrcryptoSha256.instances = {}
+
+      msrcryptoSha256.getInstance224 = function (id) {
+        return (
+          msrcryptoSha256.instances[id] ||
+          (msrcryptoSha256.instances[id] = msrcryptoSha256.sha224())
+        )
+      }
+
+      msrcryptoSha256.getInstance256 = function (id) {
+        return (
+          msrcryptoSha256.instances[id] ||
+          (msrcryptoSha256.instances[id] = msrcryptoSha256.sha256())
+        )
+      }
+
+      msrcryptoSha256.deleteInstance = function (id) {
+        msrcryptoSha256.instances[id] = null
+        delete msrcryptoSha256.instances[id]
+      }
+
+      msrcryptoSha256.hash256 = function (p) {
+        if (p.operationSubType === 'process') {
+          msrcryptoSha256.getInstance256(p.workerid).process(p.buffer)
+          return null
+        }
+
+        if (p.operationSubType === 'finish') {
+          var result = msrcryptoSha256.getInstance256(p.workerid).finish()
+          msrcryptoSha256.deleteInstance(p.workerid)
+          return result
+        }
+
+        if (p.operationSubType === 'abort') {
+          msrcryptoSha256.deleteInstance(p.workerid)
+          return
+        }
+
+        return msrcryptoSha256.instance256.computeHash(p.buffer)
+      }
+
+      msrcryptoSha256.hash224 = function (p) {
+        if (p.operationSubType === 'process') {
+          msrcryptoSha256.getInstance224(p.workerid).process(p.buffer)
+          return
+        }
+
+        if (p.operationSubType === 'finish') {
+          var result = msrcryptoSha256.getInstance224(p.workerid).finish()
+        }
+
+        if (p.operationSubType === 'abort') {
+          msrcryptoSha224.deleteInstance(p.workerid)
+          return
+        }
+
+        return msrcryptoSha256.instance224.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) {
+        var lowSum = (x1 + y1) | 0
+
+        var carry = lowSum >>> 0 < y1 >>> 0
+
+        resultArray[0] = (x0 + y0 + carry) | 0
+        resultArray[1] = lowSum
+
+        return
+      }
+
+      function hashBlock(message, blockIndex, hv, k, w) {
+        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]
+        }
+
+        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) {
+          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))
+
+          tbh = (eh & fh) ^ (gh & ~eh)
+          tbl = (el & fl) ^ (gl & ~el)
+
+          add(hh, hl, tah, tal, tc)
+
+          add(tbh, tbl, k[i], k[i + 1], td)
+
+          add(tc[0], tc[1], w[i], w[i + 1], te)
+
+          add(td[0], td[1], te[0], te[1], te)
+
+          add(te[0], te[1], dh, dl, tc)
+          dh = tc[0]
+          dl = tc[1]
+
+          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)
+
+          add(te[0], te[1], tah, tal, tc)
+          tah = tc[0]
+          tal = tc[1]
+
+          add(tbh, tbl, tah, tal, tc)
+          tah = tc[0]
+          tal = tc[1]
+
+          hh = gh
+          hl = gl
+          gh = fh
+          gl = fl
+          fh = eh
+          fl = el
+          eh = dh
+          el = dl
+          dh = ch
+          dl = cl
+          ch = bh
+          cl = bl
+          bh = ah
+          bl = al
+          ah = tah
+          al = tal
+        }
+
+        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,
+      )
+
+      der384 = upd('MEEwDQYJYIZIAWUDBAICBQAEMA')
+      der512 = upd('MFEwDQYJYIZIAWUDBAIDBQAEQA')
+      der512_224 = upd('MC0wDQYJYIZIAWUDBAIFBQAEHA')
+      der512_256 = upd('MDEwDQYJYIZIAWUDBAIGBQAEIA')
+
+      return {
+        sha384: function () {
+          return msrcryptoSha(
+            'SHA-384',
+            der384,
+            h384,
+            k512,
+            128,
+            hashBlock,
+            384,
+          )
+        },
+        sha512: function () {
+          return msrcryptoSha(
+            'SHA-512',
+            der512,
+            h512,
+            k512,
+            128,
+            hashBlock,
+            512,
+          )
+        },
+        sha512_224: function () {
+          return msrcryptoSha(
+            'SHA-512.224',
+            der512_224,
+            h512,
+            k512,
+            128,
+            hashBlock,
+            224,
+          )
+        },
+        sha512_256: function () {
+          return msrcryptoSha(
+            'SHA-512.256',
+            der512_256,
+            h512,
+            k512,
+            128,
+            hashBlock,
+            256,
+          )
+        },
+      }
+    })()
+
+    if (typeof operations !== 'undefined') {
+      msrcryptoSha512.instances = {}
+
+      msrcryptoSha512.getInstance384 = function (id) {
+        return (
+          msrcryptoSha512.instances[id] ||
+          (msrcryptoSha512.instances[id] = msrcryptoSha512.sha384())
+        )
+      }
+
+      msrcryptoSha512.getInstance512 = function (id) {
+        return (
+          msrcryptoSha512.instances[id] ||
+          (msrcryptoSha512.instances[id] = msrcryptoSha512.sha512())
+        )
+      }
+
+      msrcryptoSha512.deleteInstance = function (id) {
+        msrcryptoSha512.instances[id] = null
+        delete msrcryptoSha512.instances[id]
+      }
+
+      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 (keyBytes, hashFunction) {
+      var blockSize =
+        {
+          384: 128,
+          512: 128,
+        }[hashFunction.name.replace(/SHA-/, '')] || 64
+      var ipad
+      var opad
+      var paddedKey = padKey()
+      var keyXorOpad
+      var keyXorIpad
+      var k0IpadText
+
+      function xorArrays(array1, array2) {
+        var newArray = new Array(array1)
+        for (var j = 0; j < array1.length; j++) {
+          newArray[j] = array1[j] ^ array2[j]
+        }
+        return newArray
+      }
+
+      function padZeros(bytes, paddedLength) {
+        var paddedArray = bytes.slice()
+        for (var j = bytes.length; j < paddedLength; j++) {
+          paddedArray.push(0)
+        }
+        return paddedArray
+      }
+
+      function padKey() {
+        if (keyBytes.length === blockSize) {
+          return keyBytes
+        }
+
+        if (keyBytes.length > blockSize) {
+          return padZeros(hashFunction.computeHash(keyBytes), blockSize)
+        }
+
+        return padZeros(keyBytes, blockSize)
+      }
+
+      function processHmac(messageBytes) {
+        if (!k0IpadText) {
+          k0IpadText = keyXorIpad.concat(messageBytes)
+          hashFunction.process(k0IpadText)
+        } 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
+      }
+
+      ipad = new Array(blockSize)
+      opad = new Array(blockSize)
+      for (var i = 0; i < blockSize; i++) {
+        ipad[i] = 0x36
+        opad[i] = 0x5c
+      }
+      keyXorIpad = xorArrays(paddedKey, ipad)
+      keyXorOpad = xorArrays(paddedKey, opad)
+      return {
+        computeHmac: function (dataBytes, key, hashAlgorithm) {
+          processHmac(dataBytes)
+          var result = finishHmac()
+          clearState()
+          return result
+        },
+
+        process: function (dataBytes, key, hashAlgorithm) {
+          processHmac(dataBytes)
+          return null
+        },
+
+        finish: function (key, hashAlgorithm) {
+          var result = finishHmac()
+          clearState()
+          return result
+        },
+      }
+    }
+
+    if (typeof operations !== 'undefined') {
+      var hmacInstances = {}
+
+      msrcryptoHmac.signHmac = function (p) {
+        var hashName = p.keyHandle.algorithm.hash.name.toUpperCase(),
+          hashAlg = msrcryptoHashFunctions[hashName](),
+          result,
+          id = p.workerid
+
+        if (!hmacInstances[id]) {
+          hmacInstances[id] = msrcryptoHmac(p.keyData, hashAlg)
+        }
+
+        if (p.operationSubType === 'process') {
+          hmacInstances[id].process(p.buffer)
+          return null
+        }
+
+        if (p.operationSubType === 'finish') {
+          result = hmacInstances[id].finish()
+          hmacInstances[id] = null
+          return result
+        }
+
+        result = hmacInstances[id].computeHmac(p.buffer)
+        hmacInstances[id] = null
+        return result
+      }
+
+      msrcryptoHmac.verifyHmac = function (p) {
+        var hashName = p.keyHandle.algorithm.hash.name.toUpperCase(),
+          hashAlg = msrcryptoHashFunctions[hashName](),
+          result,
+          id = p.workerid
+
+        if (!hmacInstances[id]) {
+          hmacInstances[id] = msrcryptoHmac(p.keyData, hashAlg)
+        }
+
+        if (p.operationSubType === 'process') {
+          hmacInstances[id].process(p.buffer)
+          return null
+        }
+
+        if (p.operationSubType === 'finish') {
+          result = hmacInstances[id].finish()
+          result = msrcryptoUtilities.arraysEqual(result, p.signature)
+          hmacInstances[id] = null
+          return result
+        }
+
+        result = hmacInstances[id].computeHmac(p.buffer)
+        result = msrcryptoUtilities.arraysEqual(result, p.signature)
+        hmacInstances[id] = null
+        return result
+      }
+
+      msrcryptoHmac.generateKey = function (p) {
+        var defaultKeyLengths = {
+          'SHA-1': 64,
+          'SHA-224': 64,
+          'SHA-256': 64,
+          'SHA-384': 128,
+          'SHA-512': 128,
+        }
+
+        var keyLength = p.algorithm.length
+
+        if (!keyLength) {
+          keyLength = defaultKeyLengths[p.algorithm.hash.name.toUpperCase()]
+        }
+
+        return {
+          type: 'keyGeneration',
+          keyData: msrcryptoPseudoRandom.getBytes(keyLength),
+          keyHandle: {
+            algorithm: p.algorithm,
+            extractable: p.extractable,
+            usages: null || p.usages,
+            type: 'secret',
+          },
+        }
+      }
+
+      msrcryptoHmac.importKey = function (p) {
+        var keyObject,
+          keyBits = p.keyData.length * 8
+
+        if (p.format === 'jwk') {
+          keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, ['k'])
+          keyObject.alg = keyObject.alg.replace('HS', 'SHA-')
+        } else if (p.format === 'raw') {
+          keyObject = {
+            k: msrcryptoUtilities.toArray(p.keyData),
+          }
+        } else {
+          throw new Error('unsupported import format')
+        }
+
+        return {
+          type: 'keyImport',
+          keyData: keyObject.k,
+          keyHandle: {
+            algorithm: {
+              name: 'HMAC',
+              hash: {
+                name: p.algorithm.hash.name,
+              },
+            },
+            extractable: p.extractable || keyObject.extractable,
+            usages: p.usages,
+            type: 'secret',
+          },
+        }
+      }
+
+      msrcryptoHmac.exportKey = function (p) {
+        if (p.format === 'jwk') {
+          return {
+            type: 'keyExport',
+            keyHandle: msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData),
+          }
+        }
+
+        if (p.format === 'raw') {
+          return {
+            type: 'keyExport',
+            keyHandle: p.keyData,
+          }
+        }
+
+        throw new Error('unsupported export format')
+      }
+
+      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,
+        x2,
+        x3,
+        x14,
+        x13,
+        x11,
+        x9,
+        sBoxTable,
+        invSBoxTable,
+        rConTable
+
+      return {
+        aes: function (keyBytes) {
+          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,
+            )
+            x2 = aesConstants[0]
+            x3 = aesConstants[1]
+            x14 = aesConstants[2]
+            x13 = aesConstants[3]
+            x11 = aesConstants[4]
+            x9 = aesConstants[5]
+            sBoxTable = aesConstants[6]
+            invSBoxTable = aesConstants[7]
+            rConTable = aesConstants[8]
+          }
+
+          var blockSize = 128,
+            keyLength,
+            nK,
+            nB = 4,
+            nR,
+            key
+
+          keyLength = keyBytes.length * 8
+
+          switch (keyLength) {
+            case 128:
+            case 192:
+            case 256:
+              break
+            default:
+              throw new Error('Unsupported keyLength')
+          }
+
+          nK = keyLength / 32
+          nR = nK + 6
+
+          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) {
+            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] = x2[a] ^ x3[b] ^ c ^ d
+            state[1] = a ^ x2[b] ^ x3[c] ^ d
+            state[2] = a ^ b ^ x2[c] ^ x3[d]
+            state[3] = x3[a] ^ b ^ c ^ x2[d]
+            state[4] = x2[e] ^ x3[f] ^ g ^ h
+            state[5] = e ^ x2[f] ^ x3[g] ^ h
+            state[6] = e ^ f ^ x2[g] ^ x3[h]
+            state[7] = x3[e] ^ f ^ g ^ x2[h]
+            state[8] = x2[i] ^ x3[j] ^ k ^ l
+            state[9] = i ^ x2[j] ^ x3[k] ^ l
+            state[10] = i ^ j ^ x2[k] ^ x3[l]
+            state[11] = x3[i] ^ j ^ k ^ x2[l]
+            state[12] = x2[m] ^ x3[n] ^ o ^ p
+            state[13] = m ^ x2[n] ^ x3[o] ^ p
+            state[14] = m ^ n ^ x2[o] ^ x3[p]
+            state[15] = x3[m] ^ n ^ o ^ x2[p]
+          }
+
+          var invMixColumns = function (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] = x14[a] ^ x11[b] ^ x13[c] ^ x9[d]
+            state[1] = x9[a] ^ x14[b] ^ x11[c] ^ x13[d]
+            state[2] = x13[a] ^ x9[b] ^ x14[c] ^ x11[d]
+            state[3] = x11[a] ^ x13[b] ^ x9[c] ^ x14[d]
+            state[4] = x14[e] ^ x11[f] ^ x13[g] ^ x9[h]
+            state[5] = x9[e] ^ x14[f] ^ x11[g] ^ x13[h]
+            state[6] = x13[e] ^ x9[f] ^ x14[g] ^ x11[h]
+            state[7] = x11[e] ^ x13[f] ^ x9[g] ^ x14[h]
+            state[8] = x14[i] ^ x11[j] ^ x13[k] ^ x9[l]
+            state[9] = x9[i] ^ x14[j] ^ x11[k] ^ x13[l]
+            state[10] = x13[i] ^ x9[j] ^ x14[k] ^ x11[l]
+            state[11] = x11[i] ^ x13[j] ^ x9[k] ^ x14[l]
+            state[12] = x14[m] ^ x11[n] ^ x13[o] ^ x9[p]
+            state[13] = x9[m] ^ x14[n] ^ x11[o] ^ x13[p]
+            state[14] = x13[m] ^ x9[n] ^ x14[o] ^ x11[p]
+            state[15] = x11[m] ^ x13[n] ^ x9[o] ^ x14[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 (keyIn) {
+            var temp,
+              res = [],
+              i = 0
+            while (i < 4 * nK) {
+              res.push(keyIn[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 () {},
+
+            keyLength: keyLength,
+
+            blockSize: blockSize,
+          }
+        },
+      }
+    })()
+
+    var msrcryptoPadding = msrcryptoPadding || {}
+
+    msrcryptoPadding.pkcsv7 = function (blockSize) {
+      function pad(messageBlocks) {
+        var lastIndex =
+          messageBlocks.length - 1 >= 0 ? messageBlocks.length - 1 : 0
+        var lastBlock = messageBlocks[lastIndex]
+        var lastBlockLength = lastBlock.length
+        var createNewBlock = lastBlockLength === blockSize
+
+        if (createNewBlock) {
+          var newBlock = []
+          var 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) {
+        var verified = true
+
+        if (messageBytes.length % blockSize !== 0) {
+          verified = false
+        }
+
+        var lastBlock = messageBytes.slice(-blockSize)
+
+        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)
+
+      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
+      }
+
+      function getBlocks(dataBytes) {
+        var blocks = []
+
+        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))
+        }
+
+        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()
+        },
+
+        encrypt: function (plainBytes) {
+          var result = encryptBlocks(getBlocks(plainBytes))
+          mResultBuffer = mResultBuffer.concat(mergeBlocks(result))
+
+          return this.finishEncrypt()
+        },
+
+        processEncrypt: function (plainBytes) {
+          var result = mergeBlocks(encryptBlocks(getBlocks(plainBytes)))
+
+          return result
+        },
+
+        finishEncrypt: function () {
+          var blocks = mBuffer.length === 1 ? [[mBuffer[0]]] : [mBuffer]
+
+          paddingScheme.pad(blocks)
+
+          var result = mResultBuffer.concat(mergeBlocks(encryptBlocks(blocks)))
+
+          clearState()
+
+          return result
+        },
+
+        decrypt: function (cipherBytes) {
+          this.processDecrypt(cipherBytes)
+
+          return this.finishDecrypt()
+        },
+
+        processDecrypt: function (cipherBytes) {
+          var result = decryptBlocks(getBlocks(cipherBytes))
+
+          mResultBuffer = mResultBuffer.concat(mergeBlocks(result))
+
+          return
+        },
+
+        finishDecrypt: function () {
+          var result = mResultBuffer
+
+          var verified = paddingScheme.unpad(result)
+
+          clearState()
+
+          return result
+        },
+      }
+    }
+
+    if (typeof operations !== 'undefined') {
+      var cbcInstances = {}
+
+      msrcryptoCbc.workerEncrypt = function (p) {
+        var result,
+          id = p.workerid
+
+        if (!cbcInstances[id]) {
+          cbcInstances[id] = msrcryptoCbc(msrcryptoBlockCipher.aes(p.keyData))
+          cbcInstances[id].init(p.algorithm.iv)
+        }
+
+        if (p.operationSubType === 'process') {
+          return cbcInstances[id].processEncrypt(p.buffer)
+        }
+
+        if (p.operationSubType === 'finish') {
+          result = cbcInstances[id].finishEncrypt()
+          cbcInstances[id] = null
+          return result
+        }
+
+        result = cbcInstances[id].encrypt(p.buffer)
+        cbcInstances[id] = null
+        return result
+      }
+
+      msrcryptoCbc.workerDecrypt = function (p) {
+        var result,
+          id = p.workerid
+
+        if (!cbcInstances[id]) {
+          cbcInstances[id] = msrcryptoCbc(msrcryptoBlockCipher.aes(p.keyData))
+          cbcInstances[id].init(p.algorithm.iv)
+        }
+
+        if (p.operationSubType === 'process') {
+          cbcInstances[id].processDecrypt(p.buffer)
+          return
+        }
+
+        if (p.operationSubType === 'finish') {
+          result = cbcInstances[id].finishDecrypt()
+          cbcInstances[id] = null
+          return result
+        }
+
+        result = cbcInstances[id].decrypt(p.buffer)
+        cbcInstances[id] = 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,
+            usages: null || p.usages,
+            type: 'secret',
+          },
+        }
+      }
+
+      msrcryptoCbc.importKey = function (p) {
+        var keyObject
+        var keyBits = p.keyData.length * 8
+
+        if (p.format === 'jwk') {
+          keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, ['k'])
+        } else if (p.format === 'raw') {
+          if (keyBits !== 128 && keyBits !== 192 && keyBits !== 256) {
+            throw new Error(
+              'invalid key length (should be 128, 192, or 256 bits)',
+            )
+          }
+          keyObject = {
+            k: msrcryptoUtilities.toArray(p.keyData),
+          }
+        } else {
+          throw new Error('unsupported import format')
+        }
+
+        p.algorithm.length = keyObject.k.length * 8
+
+        return {
+          keyData: keyObject.k,
+          keyHandle: {
+            algorithm: p.algorithm,
+            extractable: p.extractable || keyObject.extractable,
+            usages: null || p.usages,
+            type: 'secret',
+          },
+          type: 'keyImport',
+        }
+      }
+
+      msrcryptoCbc.exportKey = function (p) {
+        if (p.format === 'jwk') {
+          return {
+            type: 'keyExport',
+            keyHandle: msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData),
+          }
+        }
+
+        if (p.format === 'raw') {
+          return {
+            type: 'keyExport',
+            keyHandle: p.keyData,
+          }
+        }
+
+        throw new Error('unsupported export format')
+      }
+
+      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(0),
+          mask,
+          j,
+          i
+
+        for (i = 0; i < 128; i++) {
+          mask = -getBit(blockX, i) & 0xff
+
+          for (j = 0; j < 16; j++) {
+            z[j] = z[j] ^ (v[j] & mask)
+          }
+
+          mask = -(v[15] & 1) & 0xff
+
+          shiftRight(v)
+
+          v[0] ^= 0xe1 & mask
+        }
+
+        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 = []
+
+        if (mGctrCb !== icb) {
+          mGctrCb = icb.slice()
+        }
+
+        for (var block = 0; block < blockCount; block++) {
+          dataBlock = dataBytes.slice(block * 16, block * 16 + 16)
+
+          var e = blockCipher.encrypt(mGctrCb.slice())
+
+          result = result.concat(utils.xorVectors(dataBlock, e))
+
+          mGctrCb = inc(mGctrCb)
+        }
+
+        return result
+      }
+
+      function numberTo8Bytes(integer) {
+        return [
+          0,
+          0,
+          0,
+          0,
+          (integer >>> 24) & 255,
+          (integer >>> 16) & 255,
+          (integer >>> 8) & 255,
+          integer & 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)),
+          )
+
+          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) {
+        mBuffer = mBuffer.concat(plainBytes)
+
+        var fullBlocks = mBuffer.slice(0, Math.floor(mBuffer.length / 16) * 16)
+
+        mBytesProcessed += fullBlocks.length
+
+        mBuffer = mBuffer.slice(fullBlocks.length)
+
+        var c = gctr(mGctrCb || mJ0inc, fullBlocks)
+
+        mCipherText = mCipherText.concat(c)
+
+        ghash(mH, c)
+      }
+
+      function processDecrypt(cipherBytes) {
+        mBuffer = mBuffer.concat(cipherBytes)
+
+        var fullBlocks = mBuffer.slice(
+          0,
+          Math.floor((mBuffer.length - mTagLength / 8) / 16) * 16,
+        )
+
+        mBytesProcessed += fullBlocks.length
+
+        mBuffer = mBuffer.slice(fullBlocks.length)
+
+        var c = gctr(mGctrCb || mJ0inc, fullBlocks)
+
+        mCipherText = mCipherText.concat(c)
+
+        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 {
+          return null
+        }
+      }
+
+      return {
+        init: init,
+        encrypt: encrypt,
+        decrypt: decrypt,
+        processEncrypt: processEncrypt,
+        processDecrypt: processDecrypt,
+        finishEncrypt: finishEncrypt,
+        finishDecrypt: finishDecrypt,
+      }
+    }
+
+    if (typeof operations !== 'undefined') {
+      var gcmInstances = {}
+
+      msrcryptoGcm.encrypt = function (p) {
+        var result,
+          id = p.workerid
+
+        if (!gcmInstances[id]) {
+          gcmInstances[id] = msrcryptoGcm(msrcryptoBlockCipher.aes(p.keyData))
+          gcmInstances[id].init(
+            p.algorithm.iv,
+            p.algorithm.additionalData,
+            p.algorithm.tagLength,
+          )
+        }
+
+        if (p.operationSubType === 'process') {
+          gcmInstances[id].processEncrypt(p.buffer)
+          return
+        }
+
+        if (p.operationSubType === 'finish') {
+          result = gcmInstances[id].finishEncrypt()
+          gcmInstances[id] = null
+          return result
+        }
+
+        result = gcmInstances[id].encrypt(p.buffer)
+        gcmInstances[id] = null
+        return result
+      }
+
+      msrcryptoGcm.decrypt = function (p) {
+        var result,
+          id = p.workerid
+
+        if (!gcmInstances[id]) {
+          gcmInstances[id] = msrcryptoGcm(msrcryptoBlockCipher.aes(p.keyData))
+          gcmInstances[id].init(
+            p.algorithm.iv,
+            p.algorithm.additionalData,
+            p.algorithm.tagLength,
+          )
+        }
+
+        if (p.operationSubType === 'process') {
+          gcmInstances[id].processDecrypt(p.buffer)
+          return
+        }
+
+        if (p.operationSubType === 'finish') {
+          result = gcmInstances[id].finishDecrypt()
+          gcmInstances[id] = null
+          if (result === null) {
+            throw new Error('OperationError')
+          }
+          return result
+        }
+
+        var tagLength = p.algorithm.tagLength
+          ? Math.floor(p.algorithm.tagLength / 8)
+          : 16
+        var cipherBytes = p.buffer.slice(0, p.buffer.length - tagLength)
+        var tagBytes = p.buffer.slice(-tagLength)
+
+        result = gcmInstances[id].decrypt(cipherBytes, tagBytes)
+        gcmInstances[id] = null
+
+        if (result === null) {
+          throw new Error('OperationError')
+        }
+
+        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,
+            usages: null || p.usages,
+            type: 'secret',
+          },
+        }
+      }
+
+      msrcryptoGcm.importKey = function (p) {
+        var keyObject,
+          keyBits = p.keyData.length * 8
+
+        if (p.format === 'jwk') {
+          keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, ['k'])
+        } else if (p.format === 'raw') {
+          if (keyBits !== 128 && keyBits !== 192 && keyBits !== 256) {
+            throw new Error(
+              'invalid key length (should be 128, 192, or 256 bits)',
+            )
+          }
+          keyObject = {
+            k: msrcryptoUtilities.toArray(p.keyData),
+          }
+        } else {
+          throw new Error('unsupported import format')
+        }
+
+        return {
+          type: 'keyImport',
+          keyData: keyObject.k,
+          keyHandle: {
+            algorithm: p.algorithm,
+            extractable: p.extractable || keyObject.extractable,
+            usages: null || p.usages,
+            type: 'secret',
+          },
+        }
+      }
+
+      msrcryptoGcm.exportKey = function (p) {
+        if (p.format === 'jwk') {
+          return {
+            type: 'keyExport',
+            keyHandle: msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData),
+          }
+        }
+
+        if (p.format === 'raw') {
+          return {
+            type: 'keyExport',
+            keyHandle: p.keyData,
+          }
+        }
+
+        throw new Error('unsupported export format')
+      }
+
+      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() {
+      if (!(this instanceof MsrcryptoPrng)) {
+        throw new Error('create MsrcryptoPrng object with new keyword')
+      }
+
+      var initialized = false
+
+      var key
+      var v
+      var keyLen
+      var seedLen
+      var reseedCounter = 1
+      var reseedInterval = Math.pow(2, 48)
+
+      initialize()
+
+      function addOne(counter) {
+        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() {
+        key = msrcryptoUtilities.getVector(32)
+        v = msrcryptoUtilities.getVector(16)
+        keyLen = 32
+        seedLen = 48
+        reseedCounter = 1
+      }
+
+      function reseed(entropy, additionalEntropy) {
+        additionalEntropy = additionalEntropy || [0]
+        if (additionalEntropy.length > seedLen) {
+          throw new Error('Incorrect entropy or additionalEntropy length')
+        }
+        additionalEntropy = additionalEntropy.concat(
+          msrcryptoUtilities.getVector(seedLen - additionalEntropy.length),
+        )
+
+        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) {
+        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)
+          temp = temp.concat(outputBlock)
+        }
+        temp = msrcryptoUtilities.xorVectors(temp, providedData)
+        key = temp.slice(0, keyLen)
+        v = temp.slice(keyLen)
+      }
+
+      function generate(requestedBytes, additionalInput) {
+        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,
+        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 = []
+          var 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)
+        },
+        init: function (entropy, personalization) {
+          if (entropy.length < seedLen) {
+            throw new Error('Initial entropy length too short')
+          }
+          initialize()
+          reseed(entropy, personalization)
+          initialized = true
+        },
+      }
+    }
+
+    var msrcryptoPseudoRandom = new MsrcryptoPrng()
+
+    function MsrcryptoEntropy(global) {
+      var poolLength = 48
+      var collectorPool = []
+      var collectorPoolLength = 128
+      var collectorsRegistered = 0
+      var entropyPoolPrng = new MsrcryptoPrng()
+      var initialized = false
+      var cryptographicPRNGPresent = false
+      var globalScope = global
+
+      function collectEntropy() {
+        var headerList = [
+          'Cookie',
+          'RedirectUri',
+          'ETag',
+          'x-ms-client-antiforgery-id',
+          'x-ms-client-request-id',
+          'x-ms-client-session-id',
+          'SubscriptionPool',
+        ]
+
+        var i,
+          pool = []
+
+        for (i = 0; i < poolLength; i += 1) {
+          pool[i] = Math.floor(Math.random() * 256)
+        }
+
+        var prngCrypto = globalScope.crypto || globalScope.msCrypto
+        if (prngCrypto && typeof prngCrypto.getRandomValues === 'function') {
+          if (global.Uint8Array) {
+            var res = new global.Uint8Array(poolLength)
+            prngCrypto.getRandomValues(res)
+            pool = pool.concat(Array.apply(null, res))
+            cryptographicPRNGPresent = true
+          }
+        }
+
+        if (typeof XMLHttpRequest !== 'undefined') {
+          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) {}
+          }
+        }
+        if (!cryptographicPRNGPresent && canCollect) {
+          pool = pool.concat(collectorPool.splice(0, collectorPool.length))
+          collectors.startCollectors()
+        }
+
+        initialized ? entropyPoolPrng.reseed(pool) : entropyPoolPrng.init(pool)
+        initialized = true
+      }
+
+      function updatePool(entropyData) {
+        for (var i = 0; i < entropyData.length; ++i) {
+          collectorPool.push(entropyData[i])
+        }
+        if (collectorPool.length >= collectorPoolLength) {
+          collectors.stopCollectors()
+        }
+      }
+
+      var canCollect =
+        (global && global.addEventListener) ||
+        (typeof document !== 'undefined' && document.attachEvent)
+      var collectors = (function () {
+        return {
+          startCollectors: function () {
+            if (!this.collectorsRegistered) {
+              if (global.addEventListener) {
+                global.addEventListener(
+                  'mousemove',
+                  this.MouseEventCallBack,
+                  true,
+                )
+                global.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 (global.removeEventListener) {
+                global.removeEventListener(
+                  'mousemove',
+                  this.MouseEventCallBack,
+                  1,
+                )
+                global.removeEventListener('load', this.LoadTimeCallBack, 1)
+              } else if (global.detachEvent) {
+                global.detachEvent('onmousemove', this.MouseEventCallBack)
+                global.detachEvent('onload', this.LoadTimeCallBack)
+              }
+
+              this.collectorsRegistered = 0
+            }
+          },
+          MouseEventCallBack: function (eventData) {
+            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 () {
+            var d = new Date().valueOf()
+            var dateArray = [
+              d & 0x0ff,
+              (d >> 8) & 0x0ff,
+              (d >> 16) & 0x0ff,
+              (d >> 24) & 0x0ff,
+            ]
+
+            updatePool(dateArray)
+          },
+        }
+      })()
+
+      return {
+        init: function () {
+          collectEntropy()
+
+          if (
+            !cryptographicPRNGPresent &&
+            !collectorsRegistered &&
+            canCollect
+          ) {
+            try {
+              collectors.startCollectors()
+            } catch (e) {}
+          }
+        },
+
+        reseed: function (entropy) {
+          entropyPoolPrng.reseed(entropy)
+        },
+
+        read: function (length) {
+          if (!initialized) {
+            throw new Error('Entropy pool is not initialized.')
+          }
+
+          var ret = entropyPoolPrng.getBytes(length)
+
+          collectEntropy()
+
+          return ret
+        },
+      }
+    }
+
+    var prime = (function () {
+      var smallPrimes = []
+
+      var trialValues = []
+
+      var MAX_SMALL_PRIMES = 4096 * 4
+
+      function primeSieve(max) {
+        var numbers = new Array(max + 1),
+          results = [],
+          i,
+          j,
+          limit = Math.sqrt(max) | 0
+
+        for (i = 3; i <= limit; i += 2) {
+          for (j = i * i; j <= max; j += i * 2) {
+            numbers[j] = 0
+          }
+        }
+
+        for (i = 3; i <= max; i += 2) {
+          if (numbers[i] !== 0) {
+            results.push(i)
+          }
+        }
+
+        return results
+      }
+
+      function incrementalTrialDivision(increment) {
+        var i,
+          len = trialValues.length
+
+        for (i = 0; i < len; i++) {
+          if ((trialValues[i] + increment) % smallPrimes[i] === 0) {
+            return false
+          }
+        }
+
+        return true
+      }
+
+      function setupIncrementalTrialDivision(candidate) {
+        var i,
+          j,
+          r,
+          p,
+          y,
+          primeCount,
+          len = candidate.length - 1,
+          db = cryptoMath.DIGIT_BASE,
+          h = candidate[len]
+
+        if (smallPrimes.length === 0) {
+          smallPrimes = primeSieve(MAX_SMALL_PRIMES)
+        }
+        primeCount = smallPrimes.length
+
+        trialValues = new Array(primeCount)
+
+        for (i = 0; i < primeCount; i++) {
+          j = len
+          y = smallPrimes[i]
+
+          if (h < y) {
+            r = h
+            j--
+          } else {
+            r = 0
+          }
+
+          while (j >= 0) {
+            p = r * db + candidate[j--]
+            r = p - ((p / y) | 0) * y
+          }
+
+          trialValues[i] = r
+        }
+
+        return
+      }
+
+      function largestDivisibleByPowerOfTwo(number) {
+        var k = 0,
+          i = 0,
+          s = 0,
+          j
+        if (cryptoMath.isZero(number)) {
+          return 0
+        }
+        for (k = 0; number[k] === 0; k++) {}
+        for (i = 0, j = 2; number[k] % j === 0; j *= 2, i++) {}
+        return k * cryptoMath.DIGIT_BITS + i
+      }
+
+      function sizeInBits(digits) {
+        var k = 0,
+          i = 0,
+          j = 0
+        if (cryptoMath.isZero(digits)) {
+          return 0
+        }
+        for (k = digits.length - 1; digits[k] === 0; k--) {}
+        for (
+          i = cryptoMath.DIGIT_BITS - 1, j = 1 << i;
+          i > 0;
+          j = j >>> 1, i--
+        ) {
+          if ((digits[k] & j) !== 0) {
+            break
+          }
+        }
+        return k * cryptoMath.DIGIT_BITS + i
+      }
+
+      function millerRabin(number, iterations) {
+        var w = number
+        var wminus1 = []
+        cryptoMath.subtract(w, [1], wminus1)
+
+        var a = largestDivisibleByPowerOfTwo(wminus1)
+
+        var m = []
+        cryptoMath.shiftRight(wminus1, m, a)
+
+        var wlen = sizeInBits(w)
+        var b
+        var montmul = cryptoMath.MontgomeryMultiplier(w)
+
+        for (var i = 1; i <= iterations; i++) {
+          var status = false
+
+          do {
+            b = getRandomOddNumber(wlen)
+          } while (cryptoMath.compareDigits(b, wminus1) >= 0)
+
+          var z = []
+
+          montmul.modExp(b, m, z, true)
+
+          if (
+            cryptoMath.compareDigits(z, [1]) === 0 ||
+            cryptoMath.compareDigits(z, wminus1) === 0
+          ) {
+            continue
+          }
+
+          for (var j = 1; j < a; j++) {
+            montmul.montgomeryMultiply(z, z, z)
+
+            if (cryptoMath.compareDigits(z, wminus1) === 0) {
+              status = true
+              break
+            }
+
+            if (cryptoMath.compareDigits(z, [1]) === 0) {
+              return false
+            }
+          }
+
+          if (status === false) {
+            return false
+          }
+        }
+
+        return true
+      }
+
+      function generatePrime(bits) {
+        var candidate = getRandomOddNumber(bits),
+          inc = 0,
+          possiblePrime,
+          isPrime = false,
+          candidatePlusInc = []
+
+        setupIncrementalTrialDivision(candidate)
+
+        while (true) {
+          possiblePrime = incrementalTrialDivision(inc)
+
+          if (possiblePrime) {
+            cryptoMath.add(candidate, [inc], candidatePlusInc)
+            if (millerRabin(candidatePlusInc, 6) === true) {
+              return candidatePlusInc
+            }
+          }
+
+          inc += 2
+        }
+      }
+
+      function getRandomOddNumber(bits) {
+        var numBytes = Math.ceil(bits / 8),
+          bytes = msrcryptoPseudoRandom.getBytes(numBytes),
+          digits
+
+        bytes[0] |= 128
+        bytes[bytes.length - 1] |= 1
+
+        return cryptoMath.bytesToDigits(bytes)
+      }
+
+      return {
+        generatePrime: generatePrime,
+      }
+    })()
+
+    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)
+
+        utils.padFront(bytes, 0, modulusLength)
+
+        return bytes
+      }
+
+      function modExp(dataBytes, expBytes, modulusBytes) {
+        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 b2d = cryptoMath.bytesToDigits,
+          p = keyStruct.p,
+          q = keyStruct.q,
+          dp = keyStruct.dp,
+          dq = keyStruct.dq,
+          invQ = keyStruct.qi,
+          pDigits = b2d(p),
+          qDigits = b2d(q),
+          temp = new Array(pDigits.length + qDigits.length),
+          m1Digits = new Array(pDigits.length + 1),
+          m2Digits = new Array(qDigits.length + 1),
+          cDigits = b2d(cipherBytes),
+          mm = cryptoMath.MontgomeryMultiplier,
+          mmp = new mm(keyStruct.ctxp ? undefined : pDigits, keyStruct.ctxp),
+          mmq = new mm(keyStruct.ctxq ? undefined : qDigits, keyStruct.ctxq)
+
+        mmp.reduce(cDigits, temp)
+        mmp.modExp(temp, b2d(dp), m1Digits)
+
+        mmq.reduce(cDigits, temp)
+        mmq.modExp(temp, b2d(dq), m2Digits)
+
+        var carry = cryptoMath.subtract(m1Digits, m2Digits, temp)
+        if (carry !== 0) {
+          cryptoMath.subtract(m2Digits, m1Digits, temp)
+        }
+
+        cryptoMath.modMul(temp, b2d(invQ), pDigits, cDigits)
+        if (carry !== 0) {
+          cryptoMath.subtract(pDigits, cDigits, cDigits)
+        }
+
+        cryptoMath.multiply(cDigits, qDigits, temp)
+        cryptoMath.add(m2Digits, temp, m1Digits)
+
+        return toBytes(m1Digits)
+      }
+
+      return {
+        encrypt: function (messageBytes) {
+          var bytes = toBytes(
+            modExp(messageBytes, keyStruct.e, keyStruct.n, true),
+          )
+          return bytes
+        },
+
+        decrypt: function (cipherBytes) {
+          if (keyIsCrt) {
+            return decryptCrt(cipherBytes)
+          }
+
+          if (keyIsPrivate) {
+            return decryptModExp(cipherBytes)
+          }
+
+          throw new Error('missing private key')
+        },
+      }
+    }
+
+    var rsaShared = {
+      mgf1: function (seedBytes, maskLen, hashFunction) {
+        var t = [],
+          bytes,
+          hash,
+          counter,
+          hashByteLen = hashFunction.hashLen / 8
+
+        for (
+          counter = 0;
+          counter <= Math.floor(maskLen / hashByteLen);
+          counter += 1
+        ) {
+          bytes = [
+            (counter >>> 24) & 0xff,
+            (counter >>> 16) & 0xff,
+            (counter >>> 8) & 0xff,
+            counter & 0xff,
+          ]
+          hash = hashFunction.computeHash(seedBytes.concat(bytes))
+
+          t = t.concat(hash)
+        }
+
+        return t.slice(0, maskLen)
+      },
+
+      checkMessageVsMaxHash: function (messageBytes, hashFunction) {
+        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.')
+        }
+
+        if (label == null) {
+          label = []
+        }
+
+        lHash = hashFunction.computeHash(label)
+
+        psLen = size - message.length - 2 * lHash.length - 2
+        psArray = utils.getVector(psLen)
+
+        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 message
+      }
+
+      function unpad(encodedBytes, labelBytes) {
+        var lHash, maskedSeed, maskeddb, seedMask
+        var seed, dbMask, db
+        var lHashp,
+          i = 0
+        var valid = encodedBytes[0] === 0
+
+        if (!labelBytes) {
+          labelBytes = []
+        }
+
+        lHash = hashFunction.computeHash(labelBytes)
+
+        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)
+
+        valid = valid && utils.arraysEqual(lHash, lHashp)
+
+        db = db.slice(lHash.length)
+
+        while (!db[i++]) {}
+
+        return {
+          valid: valid,
+          data: db.slice(i),
+        }
+      }
+
+      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')
+        }
+
+        randomness = random.getNonZeroBytes(size - data.length - 3)
+
+        return [0, 2].concat(randomness, [0], data)
+      }
+
+      function validatePadding(paddedData) {
+        var paddingValid = paddedData[0] === 0 && paddedData[1] === 2
+
+        for (var i = 2; i < 10; i++) {
+          paddingValid = paddingValid && !!paddedData[i]
+        }
+
+        return paddingValid
+      }
+
+      function unpad(paddedData) {
+        var i,
+          paddingIsValid = validatePadding(paddedData),
+          startOfData = 0
+
+        for (i = 1; i < paddedData.length; i += 1) {
+          startOfData = startOfData || (+!paddedData[i] && i + 1)
+        }
+
+        startOfData = -paddingIsValid && startOfData
+
+        return {
+          data: paddedData.slice(startOfData),
+          valid: paddingIsValid,
+        }
+      }
+
+      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 modulusBits = cryptoMath.bitLength(keyStruct.n),
+          emBits = modulusBits - 1,
+          emLen = Math.ceil(emBits / 8),
+          mHash = hashFunction.computeHash(messageBytes)
+
+        saltLength = salt
+          ? salt.length
+          : saltLength == null
+          ? mHash.length
+          : saltLength
+
+        if (emLen < mHash.length + saltLength + 2) {
+          throw new Error('encoding error')
+        }
+
+        salt = salt || random.getBytes(saltLength)
+
+        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)
+
+        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 modulusBits = cryptoMath.bitLength(keyStruct.n)
+
+        var emBits = modulusBits - 1
+
+        var emLen = Math.ceil(emBits / 8)
+
+        var mHash = hashFunction.computeHash(messageBytes)
+
+        var hLen = mHash.length
+
+        saltLength = saltLength == null ? hLen : saltLength
+
+        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)
+
+        db[0] &= 0xff >>> (8 - (8 * emLen - emBits))
+
+        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(db.length - saltLength)
+
+        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
+          var encryptedData
+
+          if (paddingFunction !== null) {
+            paddedData = paddingFunction(dataBytes, labelBytes)
+          } else {
+            paddedData = dataBytes.slice()
+          }
+
+          encryptedData = rsaBase.encrypt(paddedData)
+
+          return encryptedData
+        },
+
+        decrypt: function (cipherBytes, labelBytes) {
+          var decryptedData = rsaBase.decrypt(cipherBytes)
+
+          if (unPaddingFunction !== null) {
+            decryptedData = unPaddingFunction(decryptedData, labelBytes)
+            if (decryptedData.valid === false) {
+              throw new Error('OperationError')
+            }
+
+            decryptedData = decryptedData.data
+          } 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)
+        },
+
+        generateKeyPair: function (bits) {
+          var keyPair = genRsaKeyFromRandom(bits)
+        },
+
+        mode: mode,
+      }
+
+      return returnObj
+    }
+
+    if (typeof operations !== 'undefined') {
+      msrcryptoRsa.sign = function (p) {
+        var rsaObj,
+          hashName = p.keyHandle.algorithm.hash.name,
+          hashFunc = msrcryptoHashFunctions[hashName.toUpperCase()](),
+          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.keyHandle.algorithm.hash.name,
+          hashFunc = msrcryptoHashFunctions[hashName.toUpperCase()](),
+          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.keyHandle.algorithm.hash.name
+            if (!hashName) {
+              throw new Error('unsupported hash algorithm')
+            }
+            hashFunc = msrcryptoHashFunctions[hashName.toUpperCase()]()
+            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.keyHandle.algorithm.hash.name
+            if (!hashName) {
+              throw new Error('unsupported hash algorithm')
+            }
+            hashFunc = msrcryptoHashFunctions[hashName.toUpperCase()]()
+            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
+
+        if (p.format === 'jwk') {
+          keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, [
+            'n',
+            'e',
+            'd',
+            'q',
+            'p',
+            'dq',
+            'dp',
+            'qi',
+          ])
+
+          if (keyObject.d) {
+            keyObject.ctxp = new cryptoMath.MontgomeryMultiplier(
+              cryptoMath.bytesToDigits(keyObject.p),
+            ).ctx
+            keyObject.ctxq = new cryptoMath.MontgomeryMultiplier(
+              cryptoMath.bytesToDigits(keyObject.q),
+            ).ctx
+          }
+        } else if (p.format === 'spki') {
+          var publicKeyInfo = asn1.parse(p.keyData)
+
+          if (publicKeyInfo == null) {
+            throw new Error('invalid key data.')
+          }
+
+          var bitString = publicKeyInfo[1]
+          var keySequence = asn1.parse(
+            bitString.data.slice(bitString.header + 1),
+            true,
+          )
+
+          if (keySequence == null) {
+            throw new Error('invalid key data.')
+          }
+
+          var n = keySequence[0],
+            e = keySequence[1]
+
+          if (n.type !== 'INTEGER' || e.type !== 'INTEGER') {
+            throw new Error('invalid key data.')
+          }
+
+          n = n.data.slice(n.header)
+          e = e.data.slice(e.header)
+
+          if (n[0] === 0 && n[1] & 128) {
+            n = n.slice(1)
+          }
+          if (e[0] === 0 && e[1] & 128) {
+            e = e.slice(1)
+          }
+
+          keyObject = {
+            n: n,
+            e: e,
+          }
+        } else {
+          throw new Error('unsupported key import format.')
+        }
+
+        return {
+          type: 'keyImport',
+          keyData: keyObject,
+          keyHandle: {
+            algorithm: p.algorithm,
+            extractable: p.extractable,
+            usages: p.usages,
+            type: keyObject.d || keyObject.dq ? 'private' : 'public',
+          },
+        }
+      }
+
+      msrcryptoRsa.exportKey = function (p) {
+        var jsonKeyStringArray = msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData)
+
+        return {
+          type: 'keyExport',
+          keyHandle: jsonKeyStringArray,
+        }
+      }
+
+      msrcryptoRsa.genRsaKeyFromRandom = function (bits, e) {
+        var exp = e ? cryptoMath.bytesToDigits(e) : [65537]
+
+        do {
+          var p = prime.generatePrime(bits / 2)
+
+          var q = prime.generatePrime(bits / 2)
+
+          if (cryptoMath.compareDigits(q, p) > 0) {
+            var t = p
+            p = q
+            q = t
+          }
+
+          var n = []
+          cryptoMath.multiply(p, q, n)
+
+          var p_1 = []
+          cryptoMath.subtract(p, [1], p_1)
+
+          var q_1 = []
+          cryptoMath.subtract(q, [1], q_1)
+
+          var p_1q_1 = []
+          cryptoMath.multiply(p_1, q_1, p_1q_1)
+
+          var gcd = []
+          cryptoMath.gcd(exp, p_1q_1, gcd)
+
+          var gcdEqual1 = cryptoMath.compareDigits(gcd, cryptoMath.One) === 0
+        } while (!gcdEqual1)
+
+        var d = []
+        cryptoMath.modInv(exp, p_1q_1, d)
+
+        var dp = []
+        cryptoMath.reduce(d, p_1, dp)
+
+        var dq = []
+        cryptoMath.reduce(d, q_1, dq)
+
+        var qi = []
+        cryptoMath.modInv(q, p, qi)
+
+        var d2b = cryptoMath.digitsToBytes
+
+        return {
+          privateKey: {
+            n: d2b(n),
+            e: d2b(exp),
+            d: d2b(d),
+            p: d2b(p),
+            q: d2b(q),
+            dp: d2b(dp),
+            dq: d2b(dq),
+            qi: d2b(qi),
+          },
+          publicKey: {
+            n: d2b(n),
+            e: d2b(exp),
+          },
+        }
+      }
+
+      msrcryptoRsa.generateKeyPair = function (p) {
+        if (typeof p.algorithm.modulusLength === 'undefined') {
+          throw new Error('missing modulusLength')
+        }
+
+        var keyPair
+        var b2d = cryptoMath.bytesToDigits
+
+        switch (p.algorithm.modulusLength) {
+          case 1024:
+          case 2048:
+          case 4096:
+            keyPair = msrcryptoRsa.genRsaKeyFromRandom(
+              p.algorithm.modulusLength,
+              p.algorithm.publicExponent,
+            )
+            break
+          default:
+            throw new Error('invalid modulusLength')
+        }
+
+        var pk = keyPair.privateKey
+        pk.ctxp = new cryptoMath.MontgomeryMultiplier(b2d(pk.p)).ctx
+        pk.ctxq = new cryptoMath.MontgomeryMultiplier(b2d(pk.q)).ctx
+
+        var algName = p.algorithm.name
+        var rsaKeyType = algName.slice(algName.indexOf('-') + 1).toUpperCase()
+
+        var publicUsage, privateUsage
+
+        if (algName === 'RSASSA-PKCS1-V1_5' || algName === 'RSA-PSS') {
+          publicUsage = ['verify']
+          privateUsage = ['sign']
+        } else {
+          publicUsage = ['encrypt']
+          privateUsage = ['decrypt']
+        }
+
+        return {
+          type: 'keyGeneration',
+          keyPair: {
+            publicKey: {
+              keyData: keyPair.publicKey,
+              keyHandle: {
+                algorithm: p.algorithm,
+                extractable: p.extractable,
+                usages: null || publicUsage,
+                type: 'public',
+              },
+            },
+            privateKey: {
+              keyData: keyPair.privateKey,
+              keyHandle: {
+                algorithm: p.algorithm,
+                extractable: p.extractable,
+                usages: null || privateUsage,
+                type: 'private',
+              },
+            },
+          },
+        }
+      }
+
+      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',
+        'RSAES-PKCS1-V1_5',
+        msrcryptoRsa.workerEncrypt,
+      )
+      operations.register(
+        'decrypt',
+        'RSAES-PKCS1-V1_5',
+        msrcryptoRsa.workerDecrypt,
+      )
+      operations.register('encrypt', 'RSA-OAEP', msrcryptoRsa.workerEncrypt)
+      operations.register('decrypt', 'RSA-OAEP', 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)
+
+      operations.register(
+        'generateKey',
+        'RSA-OAEP',
+        msrcryptoRsa.generateKeyPair,
+      )
+      operations.register(
+        'generateKey',
+        'RSAES-PKCS1-V1_5',
+        msrcryptoRsa.generateKeyPair,
+      )
+      operations.register(
+        'generateKey',
+        'RSASSA-PKCS1-V1_5',
+        msrcryptoRsa.generateKeyPair,
+      )
+      operations.register(
+        'generateKey',
+        'RSA-PSS',
+        msrcryptoRsa.generateKeyPair,
+      )
+    }
+
+    var msrcryptoConcatKdf = (function () {
+      function deriveBits(p) {
+        var hashName = p.algorithm.hash.name,
+          hashFunction = msrcryptoHashFunctions[hashName.toUpperCase()](),
+          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 reps = Math.ceil(p.length / hashFunction.hashLen),
+          counter = 1,
+          digest = p.keyData.concat(otherInfo),
+          output = []
+
+        for (var i = 0; i < reps; i++) {
+          var data = utils.int32ToBytes(counter++).concat(digest)
+          var h = hashFunction.computeHash(data)
+          output = output.concat(h)
+        }
+
+        return output.slice(0, p.length / 8)
+      }
+
+      return {
+        deriveBits: deriveBits,
+      }
+    })()
+
+    var msrcryptoConcatKdfInstance = null
+
+    if (typeof operations !== 'undefined') {
+      msrcryptoConcatKdf.importKey = function (p) {
+        var keyData
+
+        if (p.format === 'raw') {
+          keyData = msrcryptoUtilities.toArray(p.keyData)
+        } else {
+          throw new Error('unsupported import format')
+        }
+
+        if (p.extractable !== false) {
+          throw new Error('only extractable=false is supported.')
+        }
+
+        return {
+          type: 'keyImport',
+          keyData: keyData,
+          keyHandle: {
+            algorithm: {
+              name: 'CONCAT',
+            },
+            extractable: false,
+            usages: p.usages,
+            type: 'secret',
+          },
+        }
+      }
+
+      operations.register('deriveBits', 'CONCAT', msrcryptoConcatKdf.deriveBits)
+      operations.register('importKey', 'CONCAT', msrcryptoConcatKdf.importKey)
+    }
+
+    var msrcryptoPbkdf2 = (function () {
+      function deriveBits(p) {
+        var algorithm = p.algorithm,
+          keyBytes = p.keyData,
+          bits = p.length,
+          iterations = algorithm.iterations,
+          saltBytes = Array.apply(null, algorithm.salt),
+          byteLen = Math.ceil(bits / 8),
+          hLen,
+          blockCount,
+          output = []
+
+        switch (algorithm.hash.name.toUpperCase()) {
+          case 'SHA-1':
+            hLen = 20
+            break
+          case 'SHA-256':
+            hLen = 32
+            break
+          case 'SHA-384':
+            hLen = 48
+            break
+          case 'SHA-512':
+            hLen = 64
+            break
+          default:
+            throw new Error('Unsupported hash algorithm')
+        }
+
+        blockCount = Math.ceil(byteLen / hLen)
+
+        var hmacKey = msrcryptoHmac.importKey({
+          format: 'raw',
+          keyData: keyBytes,
+          algorithm: {
+            name: 'HMAC',
+            hash: algorithm.hash,
+          },
+        })
+
+        var hmacContext = {
+          algorithm: algorithm,
+          keyHandle: hmacKey.keyHandle,
+          keyData: hmacKey.keyData,
+          workerid: 0,
+          buffer: null,
+        }
+
+        function F(S, c, i) {
+          var result = [],
+            u = S.concat([
+              (i >>> 24) & 0xff,
+              (i >>> 16) & 0xff,
+              (i >>> 8) & 0xff,
+              i & 0xff,
+            ])
+
+          for (var j = 0; j < c; j++) {
+            hmacContext.buffer = u
+            u = msrcryptoHmac.signHmac(hmacContext)
+            for (var k = 0; k < hLen; k++) {
+              result[k] = ~~result[k] ^ u[k]
+            }
+          }
+
+          return result
+        }
+
+        for (var block = 1; block <= blockCount; block++) {
+          output = output.concat(F(saltBytes, iterations, block))
+        }
+
+        output.length = byteLen
+
+        return output
+      }
+
+      return {
+        deriveBits: deriveBits,
+      }
+    })()
+
+    var msrcryptoKdfInstance = null
+
+    if (typeof operations !== 'undefined') {
+      msrcryptoPbkdf2.importKey = function (p) {
+        var keyData
+
+        if (p.format === 'raw') {
+          keyData = msrcryptoUtilities.toArray(p.keyData)
+        } else {
+          throw new Error('unsupported import format')
+        }
+
+        if (p.extractable !== false) {
+          throw new Error('only extractable=false is supported.')
+        }
+
+        return {
+          type: 'keyImport',
+          keyData: keyData,
+          keyHandle: {
+            algorithm: {
+              name: 'PBKDF2',
+            },
+            extractable: false,
+            usages: p.usages,
+            type: 'secret',
+          },
+        }
+      }
+
+      operations.register('deriveBits', 'PBKDF2', msrcryptoPbkdf2.deriveBits)
+      operations.register('importKey', 'PBKDF2', msrcryptoPbkdf2.importKey)
+    }
+
+    var msrcryptoHkdf = (function () {
+      function deriveBits(p) {
+        var algorithm = p.algorithm,
+          keyBytes = p.keyData,
+          bits = p.length,
+          saltBytes = algorithm.salt,
+          byteLen = Math.ceil(bits / 8),
+          hLen,
+          output = [],
+          infoBytes = msrcryptoUtilities.toArray(algorithm.info),
+          t = [],
+          i,
+          hmacContext
+
+        switch (algorithm.hash.name.toUpperCase()) {
+          case 'SHA-1':
+            hLen = 20
+            break
+          case 'SHA-256':
+            hLen = 32
+            break
+          case 'SHA-384':
+            hLen = 48
+            break
+          case 'SHA-512':
+            hLen = 64
+            break
+          default:
+            throw new Error('Unsupported hash algorithm.')
+        }
+
+        if (algorithm.salt == null) {
+          throw new Error('HkdfParams: salt: Missing required property.')
+        }
+
+        if (algorithm.info == null) {
+          throw new Error('HkdfParams: info: Missing required property.')
+        }
+
+        if (bits % 8 !== 0) {
+          throw new Error(
+            'The length provided for HKDF is not a multiple of 8 bits.',
+          )
+        }
+
+        if (byteLen > 255 * hLen) {
+          throw new Error('The length provided for HKDF is too large.')
+        }
+
+        if (saltBytes.length === 0) {
+          saltBytes = msrcryptoUtilities.getVector(hLen)
+        }
+
+        hmacContext = {
+          workerid: 0,
+          keyHandle: {
+            algorithm: algorithm,
+          },
+          keyData: saltBytes,
+          buffer: keyBytes,
+        }
+
+        hmacContext.keyData = msrcryptoHmac.signHmac(hmacContext)
+
+        for (i = 0; i < Math.ceil(byteLen / hLen); i++) {
+          hmacContext.buffer = t.concat(infoBytes).concat([1 + i])
+          t = msrcryptoHmac.signHmac(hmacContext)
+          output = output.concat(t)
+        }
+
+        return output.slice(0, byteLen)
+      }
+
+      return {
+        deriveBits: deriveBits,
+      }
+    })()
+
+    var msrcryptoKdfInstance = null
+
+    if (typeof operations !== 'undefined') {
+      msrcryptoHkdf.importKey = function (p) {
+        var keyData
+
+        if (p.format === 'raw') {
+          keyData = msrcryptoUtilities.toArray(p.keyData)
+        } else {
+          throw new Error('unsupported import format')
+        }
+
+        if (p.extractable !== false) {
+          throw new Error('only extractable=false is supported.')
+        }
+
+        return {
+          type: 'keyImport',
+          keyData: keyData,
+          keyHandle: {
+            algorithm: {
+              name: 'HKDF',
+            },
+            extractable: false,
+            usages: p.usages,
+            type: 'secret',
+          },
+        }
+      }
+
+      operations.register('deriveBits', 'HKDF', msrcryptoHkdf.deriveBits)
+      operations.register('importKey', 'HKDF', msrcryptoHkdf.importKey)
+    }
+
+    var msrcryptoHkdfCtr = (function () {
+      function deriveBits(p) {
+        var algorithm = p.algorithm,
+          keyBytes = p.keyData,
+          bits = p.length,
+          labelBytes = algorithm.label,
+          contextBytes = algorithm.context,
+          byteLen = Math.ceil(bits / 8),
+          hLen,
+          output = [],
+          i,
+          hmacContext
+
+        switch (algorithm.hash.name.toUpperCase()) {
+          case 'SHA-1':
+            hLen = 20
+            break
+          case 'SHA-256':
+            hLen = 32
+            break
+          case 'SHA-384':
+            hLen = 48
+            break
+          case 'SHA-512':
+            hLen = 64
+            break
+          default:
+            throw new Error('Unsupported hash algorithm.')
+        }
+
+        if (algorithm.label == null) {
+          throw new Error('HkdfCtrParams: label: Missing required property.')
+        }
+
+        if (algorithm.context == null) {
+          throw new Error('HkdfCtrParams: context: Missing required property.')
+        }
+
+        if (bits % 8 !== 0) {
+          throw new Error(
+            'The length provided for HKDF-CTR is not a multiple of 8 bits.',
+          )
+        }
+
+        if (byteLen > 255 * hLen) {
+          throw new Error('The length provided for HKDF-CTR is too large.')
+        }
+
+        hmacContext = {
+          workerid: 0,
+          keyHandle: {
+            algorithm: algorithm,
+          },
+          keyData: keyBytes,
+          buffer: keyBytes,
+        }
+
+        var fixed = labelBytes.concat(
+          [0],
+          contextBytes,
+          utils.int32ToBytes(bits),
+        )
+
+        for (i = 1; i <= Math.ceil(byteLen / hLen); i++) {
+          hmacContext.buffer = utils.int32ToBytes(i).concat(fixed)
+          output = output.concat(msrcryptoHmac.signHmac(hmacContext))
+        }
+
+        return output.slice(0, byteLen)
+      }
+
+      return {
+        deriveBits: deriveBits,
+      }
+    })()
+
+    if (typeof operations !== 'undefined') {
+      msrcryptoHkdfCtr.importKey = function (p) {
+        var keyData
+
+        if (p.format === 'raw') {
+          keyData = msrcryptoUtilities.toArray(p.keyData)
+        } else {
+          throw new Error('unsupported import format')
+        }
+
+        if (p.extractable !== false) {
+          throw new Error('only extractable=false is supported.')
+        }
+
+        return {
+          type: 'keyImport',
+          keyData: keyData,
+          keyHandle: {
+            algorithm: {
+              name: 'HKDF-CTR',
+            },
+            extractable: false,
+            usages: p.usages,
+            type: 'secret',
+          },
+        }
+      }
+
+      operations.register('deriveBits', 'HKDF-CTR', msrcryptoHkdfCtr.deriveBits)
+      operations.register('importKey', 'HKDF-CTR', msrcryptoHkdfCtr.importKey)
+    }
+
+    var msrcryptoEcdh = function (curve) {
+      var btd = cryptoMath.bytesToDigits,
+        dtb = cryptoMath.digitsToBytes,
+        e = curve,
+        ecop = new cryptoECC.EllipticCurveOperatorFp(curve)
+
+      function generateKey(privateKeyBytes) {
+        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,
+          true,
+          publicKey.x.length,
+        )
+
+        if (length && secretBytes.length * 8 < length) {
+          throw new Error('DataError')
+        }
+
+        secretBytes = length
+          ? secretBytes.slice(0, Math.ceil(length / 8))
+          : secretBytes
+
+        var bits = length % 8
+        var mask = bits === 0 ? 0xff : 0xff00 >>> bits
+        secretBytes[secretBytes.length - 1] =
+          secretBytes[secretBytes.length - 1] & mask
+
+        return 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.deriveKey = function (p) {
+        throw new Error('not supported')
+
+        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,
+                usages: [],
+                type: 'public',
+              },
+            },
+            privateKey: {
+              keyData: keyPairData.privateKey,
+              keyHandle: {
+                algorithm: p.algorithm,
+                extractable: p.extractable,
+                usages: p.usages,
+                type: 'private',
+              },
+            },
+          },
+        }
+      }
+
+      msrcryptoEcdh.importKey = function (p) {
+        if (p.format === 'raw') {
+          var keyData = p.keyData
+
+          if (keyData[0] !== 4) {
+            throw new Error('DataError')
+          }
+
+          var elementSize = ~~((keyData.length - 1) / 2)
+
+          var curveName = p.algorithm.namedCurve.toUpperCase()
+
+          var x = keyData.slice(1, elementSize + 1),
+            y = keyData.slice(elementSize + 1)
+
+          if (cryptoECC.validatePoint(curveName, x, y) === false) {
+            throw new Error('DataError')
+          }
+
+          return {
+            type: 'keyImport',
+            keyData: {
+              x: x,
+              y: y,
+            },
+            keyHandle: {
+              algorithm: p.algorithm,
+              extractable: p.extractable || false,
+              usages: p.usages,
+              type: 'public',
+            },
+          }
+        }
+
+        if (p.format === 'jwk') {
+          var keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, [
+            'x',
+            'y',
+            'd',
+            'crv',
+          ])
+
+          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
+          }
+
+          if (
+            cryptoECC.validatePoint(
+              p.algorithm.namedCurve.toUpperCase(),
+              keyObject.x,
+              keyObject.y,
+            ) === false
+          ) {
+            throw new Error('DataError')
+          }
+
+          return {
+            type: 'keyImport',
+            keyData: keyObject,
+            keyHandle: {
+              algorithm: p.algorithm,
+              extractable: p.extractable || keyObject.extractable,
+              usages: p.usages,
+              type: keyObject.d ? 'private' : 'public',
+            },
+          }
+        }
+      }
+
+      msrcryptoEcdh.exportKey = function (p) {
+        if (p.format === 'raw' && p.keyHandle.type === 'public') {
+          var keyData = [4].concat(p.keyData.x, p.keyData.y)
+
+          return {
+            type: 'keyExport',
+            keyHandle: keyData,
+          }
+        }
+
+        if (p.format === 'jwk') {
+          var jsonKeyStringArray = msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData)
+          return {
+            type: 'keyExport',
+            keyHandle: jsonKeyStringArray,
+          }
+        }
+
+        throw new Error('unsupported export format.')
+      }
+
+      operations.register('importKey', 'ECDH', msrcryptoEcdh.importKey)
+      operations.register('exportKey', 'ECDH', msrcryptoEcdh.exportKey)
+      operations.register('generateKey', 'ECDH', msrcryptoEcdh.generateKey)
+      operations.register('deriveBits', 'ECDH', msrcryptoEcdh.deriveBits)
+      operations.register('deriveKey', 'ECDH', msrcryptoEcdh.deriveKey)
+    }
+
+    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 (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.modInvCT(k, curve.order, tmp)
+        cryptoMath.modMul(s, tmp, curve.order, s)
+
+        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) {
+        msrcryptoUtilities.checkParam(
+          p.algorithm.hash,
+          'Object',
+          'algorithm.hash',
+        )
+        msrcryptoUtilities.checkParam(
+          p.algorithm.hash.name,
+          'String',
+          'algorithm.hash.name',
+        )
+        msrcryptoUtilities.checkParam(
+          p.keyHandle.algorithm.namedCurve,
+          'String',
+          'p.keyHandle.algorithm.namedCurve',
+        )
+
+        var hashName = p.algorithm.hash.name,
+          curve = cryptoECC.createCurve(
+            p.keyHandle.algorithm.namedCurve.toUpperCase(),
+          ),
+          hashFunc = msrcryptoHashFunctions[hashName.toUpperCase()](),
+          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.toUpperCase()](),
+          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
+
+        function padTo8BytesIncrement(array) {
+          return array
+        }
+        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,
+                usages: ['verify'],
+                type: 'public',
+              },
+            },
+            privateKey: {
+              keyData: {
+                x: x,
+                y: y,
+                d: d,
+              },
+              keyHandle: {
+                algorithm: p.algorithm,
+                extractable: p.extractable,
+                usages: ['sign'],
+                type: 'private',
+              },
+            },
+          },
+        }
+      }
+
+      msrcryptoEcdsa.importKey = function (p) {
+        if (p.format === 'raw') {
+          var keyData = p.keyData
+
+          if (keyData[0] !== 4) {
+            throw new Error('DataError')
+          }
+
+          var elementSize = ~~((keyData.length - 1) / 2)
+
+          var curveName = p.algorithm.namedCurve.toUpperCase()
+
+          var x = keyData.slice(1, elementSize + 1),
+            y = keyData.slice(elementSize + 1)
+
+          if (cryptoECC.validatePoint(curveName, x, y) === false) {
+            throw new Error('DataError')
+          }
+
+          return {
+            type: 'keyImport',
+            keyData: {
+              x: x,
+              y: y,
+            },
+            keyHandle: {
+              algorithm: p.algorithm,
+              extractable: p.extractable || false,
+              usages: p.usages,
+              type: 'public',
+            },
+          }
+        }
+
+        if (p.format === 'jwk') {
+          var keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, [
+            'x',
+            'y',
+            'd',
+            'crv',
+          ])
+
+          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
+          }
+
+          if (
+            cryptoECC.validatePoint(
+              p.algorithm.namedCurve.toUpperCase(),
+              keyObject.x,
+              keyObject.y,
+            ) === false
+          ) {
+            throw new Error('DataError')
+          }
+
+          return {
+            type: 'keyImport',
+            keyData: keyObject,
+            keyHandle: {
+              algorithm: p.algorithm,
+              extractable: p.extractable || keyObject.extractable,
+              usages: null || p.usages,
+              type: keyObject.d ? 'private' : 'public',
+            },
+          }
+        }
+      }
+
+      msrcryptoEcdsa.exportKey = function (p) {
+        if (p.format === 'raw' && p.keyHandle.type === 'public') {
+          var keyData = [4].concat(p.keyData.x, p.keyData.y)
+
+          return {
+            type: 'keyExport',
+            keyHandle: keyData,
+          }
+        }
+
+        if (p.format === 'jwk') {
+          var jsonKeyStringArray = msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData)
+          return {
+            type: 'keyExport',
+            keyHandle: jsonKeyStringArray,
+          }
+        }
+
+        throw new Error('unsupported export format.')
+      }
+
+      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
+
+    var utils = msrcryptoUtilities
+
+    msrcryptoSubtle = (function () {
+      function syncWorker() {
+        var result
+
+        function postMessage(data) {
+          try {
+            data.workerid = this.id
+            result = msrcryptoWorker.jsCryptoRunner({
+              data: data,
+            })
+          } catch (ex) {
+            this.onerror({
+              data: ex,
+              type: 'error',
+            })
+            return
+          }
+
+          this.onmessage({
+            data: result,
+          })
+        }
+
+        return {
+          postMessage: postMessage,
+          onmessage: null,
+          onerror: null,
+          terminate: function () {},
+        }
+      }
+
+      var streamObject = function (op) {
+        return {
+          process: function (buffer) {
+            return op.process(buffer)
+          },
+          finish: function () {
+            return op.finish()
+          },
+          abort: function () {
+            return op.abort()
+          },
+        }
+      }
+
+      function baseOperation(processResults) {
+        var result = null,
+          oncompleteCallback = null,
+          onerrorCallback = null,
+          retObj,
+          promise,
+          resolveFunc,
+          rejectFunc
+
+        promise = new Promise(function (resolve, reject) {
+          resolveFunc = resolve
+          rejectFunc = reject
+        })
+
+        function opDispatchEvent(e) {
+          if (e.type === 'error') {
+            if (rejectFunc) {
+              rejectFunc.apply(promise, [e])
+            }
+            return
+          }
+
+          if (e.data.type === 'process') {
+            processResults(e.data.result, true)
+            return
+          }
+
+          if (e.data.type === 'finish') {
+            processResults(e.data.result, true)
+            return
+          }
+
+          this.result = processResults(e.data)
+          resolveFunc.apply(promise, [this.result])
+
+          return
+        }
+
+        retObj = {
+          dispatchEvent: opDispatchEvent,
+          promise: promise,
+          result: null,
+        }
+
+        return retObj
+      }
+
+      function keyOperation() {
+        function processResult(result) {
+          var publicKey, privateKey
+
+          switch (result.type) {
+            case 'keyGeneration':
+            case 'keyImport':
+            case 'keyDerive':
+              if (result.keyPair) {
+                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,
+                }
+              } else {
+                keys.add(result.keyHandle, result.keyData)
+                return result.keyHandle
+              }
+
+            case 'keyExport':
+              return result.keyHandle
+
+            case 'keyPairGeneration':
+              privateKey = result.keyPair.privateKey
+              publicKey = result.keyPair.publicKey
+              keys.add(publicKey.keyHandle, publicKey.keyData)
+              keys.add(privateKey.keyHandle, privateKey.keyData)
+              return {
+                publicKey: publicKey.keyHandle,
+                privateKey: privateKey.keyHandle,
+              }
+
+            default:
+              throw new Error('Unknown key operation')
+          }
+        }
+
+        return baseOperation(processResult)
+      }
+
+      function toArrayBufferIfSupported(dataArray) {
+        if (typedArraySupport && dataArray.pop) {
+          return new Uint8Array(dataArray).buffer
+        }
+
+        return dataArray
+      }
+
+      function cryptoOperation(cryptoContext) {
+        function processResult(result, isProcessCall) {
+          result = result && toArrayBufferIfSupported(result)
+
+          if (isProcessCall) {
+            promiseQueue.resolve(result)
+            return
+          }
+
+          return result
+        }
+
+        var promiseQueue = [],
+          op = baseOperation(processResult)
+
+        op.stream = cryptoContext.algorithm.stream
+
+        promiseQueue.add = function (label) {
+          var resolveFunc,
+            rejectFunc,
+            promise = new Promise(function (resolve, reject) {
+              resolveFunc = resolve
+              rejectFunc = reject
+            })
+
+          promise.label = label
+
+          promiseQueue.push({
+            resolve: resolveFunc,
+            reject: rejectFunc,
+            promise: promise,
+          })
+
+          return promise
+        }
+
+        promiseQueue.resolve = function (result) {
+          var queueItem = promiseQueue.shift()
+          queueItem.resolve.apply(queueItem.promise, [result])
+        }
+
+        op.process = function (buffer) {
+          cryptoContext.operationSubType = 'process'
+          cryptoContext.buffer = utils.toArray(buffer)
+          workerManager.continueJob(this, utils.clone(cryptoContext))
+
+          return promiseQueue.add('process')
+        }
+
+        op.finish = function () {
+          cryptoContext.operationSubType = 'finish'
+          cryptoContext.buffer = []
+          workerManager.continueJob(this, utils.clone(cryptoContext))
+
+          return promiseQueue.add('finish')
+        }
+
+        op.abort = function () {
+          workerManager.abortJob(this)
+        }
+        op.algorithm = cryptoContext.algorithm || null
+        op.key = cryptoContext.keyHandle || null
+
+        return op
+      }
+
+      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 += 1) {
+          if (keys[i].keyHandle === keyHandle) {
+            keys = keys.splice(i, 1)
+            return
+          }
+        }
+      }
+
+      keys.lookup = function (keyHandle) {
+        for (var i = 0; i < keys.length; i += 1) {
+          if (keys[i].keyHandle === keyHandle) {
+            return keys[i].keyData
+          }
+        }
+        return null
+      }
+
+      var workerManager = (function () {
+        var maxWorkers = 12
+
+        var maxFreeWorkers = 2
+
+        var workerPool = []
+
+        var jobQueue = []
+
+        var jobId = 0
+
+        var workerId = 0
+
+        var callbackQueue = []
+
+        var setFunction =
+          typeof setImmediate === 'undefined' ? setTimeout : setImmediate
+
+        function executeNextCallback() {
+          callbackQueue.shift()()
+        }
+
+        function queueCallback(callback) {
+          callbackQueue.push(callback)
+          setFunction(executeNextCallback, 0)
+        }
+
+        var workerStatus = webWorkerSupport ? 'available' : 'unavailable'
+
+        function getFreeWorker() {
+          purgeWorkerType(!asyncMode)
+
+          for (var i = 0; i < workerPool.length; i++) {
+            if (!workerPool[i].busy) {
+              return workerPool[i]
+            }
+          }
+
+          return null
+        }
+
+        function purgeWorkerType(webWorker) {
+          for (var i = workerPool.length - 1; i >= 0; i -= 1) {
+            if (workerPool[i].isWebWorker === webWorker) {
+              workerPool[i].terminate()
+              workerPool.splice(i, 1)
+            }
+          }
+        }
+
+        function freeWorkerCount() {
+          var freeWorkers = 0
+          for (var i = 0; i < workerPool.length; i++) {
+            if (!workerPool[i].busy) {
+              freeWorkers += 1
+            }
+          }
+          return freeWorkers
+        }
+
+        function addWorkerToPool(worker) {
+          workerPool.push(worker)
+        }
+
+        function removeWorkerFromPool(worker) {
+          for (var i = 0; i < workerPool.length; i++) {
+            if (workerPool[i] === worker) {
+              worker.terminate()
+              workerPool.splice(i, 1)
+              return
+            }
+          }
+        }
+
+        function lookupWorkerByOperation(operation) {
+          for (var i = 0; i < workerPool.length; i++) {
+            if (workerPool[i].operation === operation) {
+              return workerPool[i]
+            }
+          }
+          return null
+        }
+
+        function queueJob(operation, data) {
+          jobQueue.push({
+            operation: operation,
+            data: data,
+            id: jobId++,
+          })
+        }
+
+        function jobCompleted(worker) {
+          worker.busy = false
+
+          if (asyncMode) {
+            if (jobQueue.length > 0) {
+              var job = jobQueue.shift(),
+                i
+
+              continueJob(job.operation, job.data)
+
+              if (job.data.operationSubType === 'process') {
+                for (i = 0; i < jobQueue.length; i++) {
+                  if (job.operation === jobQueue[i].operation) {
+                    continueJob(jobQueue[i].operation, jobQueue[i].data)
+                  }
+                }
+                for (i = jobQueue.length - 1; i >= 0; i--) {
+                  if (job.operation === jobQueue[i].operation) {
+                    jobQueue.splice(i, 1)
+                  }
+                }
+              }
+            } else if (freeWorkerCount() > maxFreeWorkers) {
+              removeWorkerFromPool(worker)
+            }
+          }
+        }
+
+        function createNewWorker(operation) {
+          var worker
+
+          if (workerStatus === 'pending') {
+            throw new Error('Creating new worker while workerstatus=pending')
+          }
+
+          if (workerStatus === 'ready') {
+            try {
+              worker = new Worker(scriptUrl)
+              worker.postMessage({
+                prngSeed: msrcryptoPseudoRandom.getBytes(48),
+              })
+              worker.isWebWorker = true
+            } catch (ex) {
+              asyncMode = false
+              workerStatus = 'failed'
+              worker.terminate()
+              worker = syncWorker()
+              worker.isWebWorker = false
+            }
+          } else {
+            worker = syncWorker()
+            worker.isWebWorker = false
+          }
+
+          worker.operation = operation
+
+          worker.id = workerId++
+
+          worker.busy = false
+
+          worker.onmessage = function (e) {
+            if (e.data.initialized === true) {
+              return
+            }
+
+            var op = worker.operation
+
+            e.target ||
+              (e.target = {
+                data: worker.data,
+              })
+
+            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
+              }
+            }
+
+            if (!(e.data.hasOwnProperty('type') && e.data.type === 'process')) {
+              jobCompleted(worker)
+            }
+
+            op.dispatchEvent(e)
+          }
+
+          worker.onerror = function (e) {
+            var op = worker.operation
+
+            jobCompleted(worker)
+
+            op.dispatchEvent(e)
+          }
+
+          addWorkerToPool(worker)
+
+          return worker
+        }
+
+        function useWebWorkers(enable) {
+          if (workerStatus === 'unavailable') {
+            utils.consoleLog('web workers not available in this browser.')
+            return
+          }
+
+          if (enable === true && workerStatus === 'ready') {
+            return
+          }
+
+          if (enable === false && workerStatus === 'available') {
+            return
+          }
+
+          if (enable === false && workerStatus === 'ready') {
+            asyncMode = false
+            workerStatus = 'available'
+            utils.consoleLog('web workers disabled.')
+            return
+          }
+
+          if (workerStatus === 'pending') {
+            return
+          }
+
+          workerStatus = 'pending'
+
+          var worker = new Worker(scriptUrl)
+
+          function setWorkerStatus(e) {
+            var succeeded = !!(e.data && e.data.initialized === true)
+            worker.removeEventListener('message', setWorkerStatus, false)
+            worker.removeEventListener('error', setWorkerStatus, false)
+            worker.terminate()
+            workerStatus = succeeded ? 'ready' : 'failed'
+            asyncMode = succeeded
+            utils.consoleLog(
+              'web worker initialization ' +
+                (succeeded
+                  ? 'succeeded. Now using web workers.'
+                  : 'failed. running synchronously.' + (e.message || '')),
+            )
+            if (jobQueue.length > 0) {
+              var job = jobQueue.shift()
+              runJob(job.operation, job.data)
+            }
+            return
+          }
+
+          worker.addEventListener('message', setWorkerStatus, false)
+          worker.addEventListener('error', setWorkerStatus, false)
+
+          worker.postMessage({
+            prngSeed: msrcryptoPseudoRandom.getBytes(48),
+          })
+
+          return
+        }
+
+        function abortJob(cryptoOperationObject) {
+          var worker = lookupWorkerByOperation(cryptoOperationObject)
+          if (worker) {
+            removeWorkerFromPool(worker)
+          }
+        }
+
+        function runJob(operation, data) {
+          var worker = null
+
+          if (workerStatus === 'pending') {
+            queueJob(operation, data)
+            return
+          }
+
+          worker = getFreeWorker()
+
+          if (asyncMode && worker === null && workerPool.length >= maxWorkers) {
+            queueJob(operation, data)
+            return
+          }
+
+          if (worker === null) {
+            worker = createNewWorker(operation)
+          }
+
+          if (worker === null) {
+            queueJob(operation, data)
+            throw new Error('could not create new worker')
+          }
+
+          worker.operation = operation
+
+          worker.busy = true
+
+          data.workerid = worker.id
+
+          postMessageToWorker(worker, data)
+        }
+
+        function continueJob(operation, data) {
+          var worker = lookupWorkerByOperation(operation)
+
+          if (worker) {
+            postMessageToWorker(worker, data)
+            return
+          }
+
+          runJob(operation, data)
+        }
+
+        function postMessageToWorker(worker, data) {
+          data.workerid = worker.id
+
+          if (asyncMode) {
+            worker.postMessage(data)
+          } else {
+            var func = (function (postData) {
+              return function () {
+                return worker.postMessage(postData)
+              }
+            })(data)
+
+            queueCallback(func)
+          }
+
+          return
+        }
+
+        return {
+          runJob: runJob,
+          continueJob: continueJob,
+          abortJob: abortJob,
+          useWebWorkers: useWebWorkers,
+        }
+      })()
+
+      function checkOperation(operationType, algorithmName) {
+        if (!operations.exists(operationType, algorithmName)) {
+          throw new Error('unsupported algorithm')
+        }
+      }
+
+      var subtleParameters = [
+        {
+          name: 'algorithm',
+          type: 'Object',
+          required: true,
+        },
+        {
+          name: 'keyHandle',
+          type: 'Object',
+          required: true,
+        },
+        {
+          name: 'buffer',
+          type: 'Array',
+          required: false,
+        },
+        {
+          name: 'signature',
+          type: 'Array',
+          required: true,
+        },
+        {
+          name: 'format',
+          type: 'String',
+          required: true,
+        },
+        {
+          name: 'keyData',
+          type: 'Object',
+          required: true,
+        },
+        {
+          name: 'extractable',
+          type: 'Boolean',
+          required: false,
+        },
+        {
+          name: 'usages',
+          type: 'Array',
+          required: false,
+        },
+        {
+          name: 'derivedKeyType',
+          type: 'Object',
+          required: true,
+        },
+        {
+          name: 'length',
+          type: 'Number',
+          required: false,
+        },
+        {
+          name: 'extractable',
+          type: 'Boolean',
+          required: true,
+        },
+        {
+          name: 'usages',
+          type: 'Array',
+          required: true,
+        },
+        {
+          name: 'keyData',
+          type: 'Array',
+          required: true,
+        },
+      ]
+
+      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],
+        importKeyRaw: [4, 12, 0, 10, 11],
+        importKeyJwk: [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],
+      }
+
+      function lookupKeyData(handle) {
+        var data = keys.lookup(handle)
+
+        if (!data) {
+          throw new Error('key not found')
+        }
+
+        return data
+      }
+
+      function buildParameterCollection(operationName, parameterSet) {
+        var parameterCollection = {
+            operationType: operationName,
+          },
+          operationParameterSet,
+          expectedParam,
+          actualParam,
+          i
+
+        if (
+          operationName === 'importKey' &&
+          (parameterSet[0] === 'raw' || parameterSet[0] === 'spki')
+        ) {
+          operationName = 'importKeyRaw'
+        }
+
+        if (operationName === 'importKey' && parameterSet[0] === 'jwk') {
+          operationName = 'importKeyJwk'
+        }
+
+        operationParameterSet = subtleParametersSets[operationName]
+
+        for (i = 0; i < operationParameterSet.length; i += 1) {
+          expectedParam = subtleParameters[operationParameterSet[i]]
+          actualParam = parameterSet[i]
+
+          if (actualParam == null) {
+            if (expectedParam.required) {
+              throw new Error(expectedParam.name)
+            } else {
+              continue
+            }
+          }
+
+          if (actualParam.subarray) {
+            actualParam = utils.toArray(actualParam)
+          }
+
+          if (utils.getObjectType(actualParam) === 'ArrayBuffer') {
+            actualParam = utils.toArray(actualParam)
+          }
+
+          if (
+            msrcryptoUtilities.getObjectType(actualParam) !== expectedParam.type
+          ) {
+            throw new Error(expectedParam.name)
+          }
+
+          if (expectedParam.name === 'algorithm') {
+            actualParam.name = actualParam.name.toUpperCase()
+
+            if (actualParam.iv) {
+              actualParam.iv = utils.toArray(actualParam.iv)
+            }
+
+            if (actualParam.publicExponent) {
+              actualParam.publicExponent = utils.toArray(
+                actualParam.publicExponent,
+              )
+            }
+
+            if (actualParam.salt) {
+              actualParam.salt = utils.toArray(actualParam.salt)
+            }
+
+            if (actualParam.additionalData) {
+              actualParam.additionalData = utils.toArray(
+                actualParam.additionalData,
+              )
+            }
+
+            if (
+              actualParam.hash &&
+              !actualParam.hash.name &&
+              utils.getObjectType(actualParam.hash) === 'String'
+            ) {
+              actualParam.hash = {
+                name: actualParam.hash,
+              }
+            }
+          }
+
+          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)
+
+        checkOperation(operationName, pc.algorithm.name)
+
+        if (pc.keyHandle) {
+          pc.keyData = lookupKeyData(pc.keyHandle)
+        }
+
+        if (pc.keyHandle1) {
+          pc.keyData1 = lookupKeyData(pc.keyHandle1)
+        }
+
+        if (pc.algorithm && pc.algorithm.public) {
+          pc.additionalKeyData = lookupKeyData(pc.algorithm.public)
+        }
+
+        var op = keyFunc ? keyOperation(pc) : cryptoOperation(pc)
+
+        if (
+          keyFunc ||
+          pc.buffer ||
+          operationName === 'deriveBits' ||
+          operationName === 'wrapKey'
+        ) {
+          workerManager.runJob(op, pc)
+        }
+
+        if (op.stream) {
+          return Promise.resolve(streamObject(op))
+        }
+
+        return op.promise
+      }
+      var publicMethods = {
+        encrypt: function (algorithm, keyHandle, buffer) {
+          return executeOperation('encrypt', arguments, 0)
+        },
+
+        decrypt: function (algorithm, keyHandle, buffer) {
+          return executeOperation('decrypt', arguments, 0)
+        },
+
+        sign: function (algorithm, keyHandle, buffer) {
+          return executeOperation('sign', arguments, 0)
+        },
+
+        verify: function (algorithm, keyHandle, signature, buffer) {
+          return executeOperation('verify', arguments, 0)
+        },
+
+        digest: function (algorithm, buffer) {
+          return executeOperation('digest', arguments, 0)
+        },
+
+        generateKey: function (algorithm, extractable, keyUsage) {
+          return executeOperation('generateKey', arguments, 1)
+        },
+
+        deriveKey: function (
+          algorithm,
+          baseKey,
+          derivedKeyType,
+          extractable,
+          keyUsage,
+        ) {
+          var deriveBits = this.deriveBits,
+            importKey = this.importKey
+
+          return new Promise(function (resolve, reject) {
+            var keyLength
+
+            switch (derivedKeyType.name.toUpperCase()) {
+              case 'AES-CBC':
+              case 'AES-GCM':
+                keyLength = derivedKeyType.length
+                break
+              case 'HMAC':
+                keyLength =
+                  derivedKeyType.length ||
+                  {
+                    'SHA-1': 512,
+                    'SHA-224': 512,
+                    'SHA-256': 512,
+                    'SHA-384': 1024,
+                    'SHA-512': 1024,
+                  }[derivedKeyType.hash.name.toUpperCase()]
+                break
+              default:
+                reject(new Error('No Supported'))
+                return
+            }
+
+            deriveBits(algorithm, baseKey, keyLength)
+              .then(function (bits) {
+                return importKey(
+                  'raw',
+                  bits,
+                  derivedKeyType,
+                  extractable,
+                  keyUsage,
+                )
+              })
+              .then(function (key) {
+                resolve(key)
+              })
+              ['catch'](function (err) {
+                reject(err)
+              })
+          })
+        },
+
+        deriveBits: function (algorithm, baseKey, length) {
+          return executeOperation('deriveBits', arguments, 0)
+        },
+
+        importKey: function (
+          format,
+          keyData,
+          algorithm,
+          extractable,
+          keyUsage,
+        ) {
+          return executeOperation('importKey', arguments, 1)
+        },
+
+        exportKey: function (format, keyHandle) {
+          return executeOperation(
+            'exportKey',
+            [keyHandle.algorithm, format, keyHandle],
+            1,
+          )
+        },
+
+        wrapKey: function (format, key, wrappingKey, wrappingKeyAlgorithm) {
+          var encrypt = this.encrypt,
+            exportKey = this.exportKey
+
+          return new Promise(function (resolve, reject) {
+            if (
+              key.extractable === false ||
+              key.usages.indexOf('wrapKey') < 0 ||
+              wrappingKey.algorithm.name.toUpperCase() !==
+                wrappingKeyAlgorithm.name
+            ) {
+              reject(new Error('InvalidAccessError'))
+              return
+            }
+
+            exportKey(format, key)
+              .then(function (keyData) {
+                return encrypt(
+                  wrappingKeyAlgorithm,
+                  wrappingKey,
+                  format === 'jwk'
+                    ? utils.stringToBytes(JSON.stringify(keyData, null, 0))
+                    : keyData,
+                )
+              })
+
+              .then(function (cipherArrayBuffer) {
+                resolve(cipherArrayBuffer)
+              })
+
+              ['catch'](function (err) {
+                reject(err)
+              })
+          })
+        },
+
+        unwrapKey: function (
+          format,
+          wrappedKey,
+          unwrappingKey,
+          unwrapAlgorithm,
+          unwrappedKeyAlgorithm,
+          extractable,
+          keyUsages,
+        ) {
+          var decrypt = this.decrypt,
+            importKey = this.importKey
+
+          return new Promise(function (resolve, reject) {
+            if (
+              unwrappingKey.usages.indexOf('unwrapKey') < 0 ||
+              unwrappingKey.algorithm.name.toUpperCase() !==
+                unwrapAlgorithm.name
+            ) {
+              reject(new Error('InvalidAccessError'))
+              return
+            }
+
+            decrypt(unwrapAlgorithm, unwrappingKey, wrappedKey)
+              .then(function (keyPlain) {
+                return importKey(
+                  format,
+                  format === 'jwk'
+                    ? JSON.parse(utils.bytesToString(keyPlain))
+                    : keyPlain,
+                  unwrappedKeyAlgorithm,
+                  extractable,
+                  keyUsages,
+                )
+              })
+
+              .then(function (key) {
+                resolve(key)
+              })
+
+              ['catch'](function (err) {
+                reject(err)
+              })
+          })
+        },
+      }
+
+      var internalMethods = {
+        useWebWorkers: workerManager.useWebWorkers,
+      }
+
+      return {
+        publicMethods: publicMethods,
+        internalMethods: internalMethods,
+      }
+    })()
+
+    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.fromBase64
+
+        var keyDataJwk = JSON.parse(
+          String.fromCharCode.apply(null, params.buffer),
+        )
+
+        var header = utils.fromBase64(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,
+            usages: params.usages,
+            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 = {
+      subtle: msrcryptoSubtle ? msrcryptoSubtle.publicMethods : null,
+
+      getRandomValues: function (array) {
+        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) {
+        var entropyDataType = Object.prototype.toString.call(entropyData)
+
+        if (
+          entropyDataType !== '[object Array]' &&
+          entropyDataType !== '[object Uint8Array]'
+        ) {
+          throw new Error('entropyData must be a Array or Uint8Array')
+        }
+
+        entropyPool && entropyPool.reseed(entropyData)
+
+        msrcryptoPseudoRandom.reseed(entropyPool.read(48))
+        fprngEntropyProvided = true
+      },
+
+      toBase64: function (data, base64Url) {
+        return msrcryptoUtilities.toBase64(data, base64Url)
+      },
+
+      fromBase64: function (base64String) {
+        return msrcryptoUtilities.fromBase64(base64String)
+      },
+
+      textToBytes: function (text) {
+        return msrcryptoUtilities.stringToBytes(text)
+      },
+
+      bytesToText: function (byteArray) {
+        return msrcryptoUtilities.bytesToString(byteArray)
+      },
+
+      asn1: asn1,
+
+      url: scriptUrl,
+
+      version: msrCryptoVersion,
+
+      useWebWorkers: function (useWebWorkers) {
+        return msrcryptoSubtle
+          ? msrcryptoSubtle.internalMethods.useWebWorkers(useWebWorkers)
+          : null
+      },
+    }
+
+    var entropyPool
+
+    entropyPool = entropyPool || new MsrcryptoEntropy(global)
+
+    entropyPool.init()
+    var localEntropy = entropyPool.read(48)
+    msrcryptoPseudoRandom.init(localEntropy)
+    return publicMethods
+  }
+
+  return msrCrypto()
+})
+;(function (root, factory) {
+  if (typeof Promise !== 'undefined') {
+    return
+  }
+  root.Promise = factory()
+})(this, function () {
+  var Promise = function (executor, id) {
+    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 (successResult) {
+        thenFunctionResult = onCompleted(successResult.result)
+
+        if (thenFunctionResult && thenFunctionResult.then) {
+          return thenFunctionResult
+        }
+
+        return Promise.resolve(thenFunctionResult)
+      }
+
+      if (failReason) {
+        thenFunctionResult = onRejected
+          ? onRejected(failReason.result)
+          : failReason.result
+
+        if (thenFunctionResult && thenFunctionResult.then) {
+          return thenFunctionResult
+        }
+
+        return Promise.resolve(thenFunctionResult)
+      }
+
+      thenResolved.push(onCompleted)
+      if (onRejected) {
+        thenRejected.push(onRejected)
+      }
+
+      return new Promise(function (resolve, reject) {
+        resolveThenPromise.push(resolve)
+        rejectThenPromise.push(reject)
+      })
+    }
+
+    this['catch'] = function (onRejected) {
+      var catchFunctionResult
+
+      if (failReason) {
+        catchFunctionResult = onRejected(failReason.result)
+
+        if (catchFunctionResult && catchFunctionResult.then) {
+          return catchFunctionResult
+        }
+
+        return Promise.resolve(catchFunctionResult)
+      }
+
+      thenRejected.push(onRejected)
+
+      return new Promise(function (resolve, reject) {
+        resolveThenPromise.push(resolve)
+        rejectThenPromise.push(reject)
+      })
+    }
+
+    function resolve(param) {
+      var result, i
+
+      for (i = 0; i < thenResolved.length; i += 1) {
+        result = thenResolved[i](param)
+
+        if (result && result.then) {
+          result.then(resolveThenPromise[i])
+
+          if (rejectThenPromise[i]) {
+            result['catch'](rejectThenPromise[i])
+          }
+        } else {
+          if (resolveThenPromise[i]) {
+            resolveThenPromise[i](result)
+          }
+        }
+      }
+
+      successResult = {
+        result: param,
+      }
+
+      return
+    }
+
+    function reject(param) {
+      var reason, i
+
+      for (i = 0; i < thenRejected.length; i += 1) {
+        reason = thenRejected[i](param)
+
+        if (reason && reason.then) {
+          reason.then(resolveThenPromise[i], rejectThenPromise[i])
+        } else {
+          if (resolveThenPromise[i]) {
+            resolveThenPromise[i](reason)
+          }
+        }
+      }
+
+      failReason = {
+        result: param,
+      }
+
+      return
+    }
+
+    executor(resolve, reject)
+
+    return
+  }
+
+  Promise.all = function (promiseArray) {
+    var results = [],
+      resultCount = 0,
+      promiseAll
+
+    function then(index, resolve) {
+      return function (result) {
+        results[index] = result
+
+        resultCount += 1
+        if (resultCount === promiseArray.length) {
+          resolve(results)
+        }
+      }
+    }
+
+    promiseAll = new Promise(function (resolve, reject) {
+      var i
+
+      function r(reason) {
+        reject(reason)
+      }
+
+      for (i = 0; i < promiseArray.length; i += 1) {
+        if (promiseArray[i].then) {
+          promiseArray[i].then(then(i, resolve))
+          promiseArray[i]['catch'](r)
+          continue
+        }
+        Promise.resolve(promiseArray[i]).then(then(i, resolve))
+      }
+    })
+
+    return promiseAll
+  }
+
+  Promise.race = function (promiseArray) {
+    var resolved = false,
+      promiseRace
+
+    function then(resolveFunction) {
+      return function (result) {
+        if (!resolved) {
+          resolved = true
+          resolveFunction(result)
+        }
+      }
+    }
+
+    promiseRace = new Promise(function (resolve, reject) {
+      for (var i = 0; i < promiseArray.length; i += 1) {
+        promiseArray[i].then(then(resolve), then(reject))
+      }
+    })
+
+    return promiseRace
+  }
+
+  Promise.reject = function (rejectReason) {
+    return new Promise(function (resolve, reject) {
+      reject(rejectReason)
+    })
+  }
+
+  Promise.resolve = function (resolveResult) {
+    return new Promise(function (resolve, reject) {
+      resolve(resolveResult)
+    })
+  }
+
+  return Promise
+})