10144 lines
276 KiB
JavaScript
10144 lines
276 KiB
JavaScript
// 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
|
|
})
|