//*******************************************************************************
//
// Copyright 2014 Microsoft
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//*******************************************************************************
//depot/incubations/msrcrypto/msrCrypto/scripts/cryptoMath.js#5 - edit change 1781 (text)
/// cryptoMath.js ==================================================================================
/// Implementation of math routines for cryptographic applications.
/// #region JSCop/JsHint
/* jshint -W016 */ /* allows bitwise operators */
/* jshint -W052 */ /* allows not operator */
///
///
///
/// alg,bitmasks,coord,De-montgomeryized,digitbits,digitmask,divrem,dont,Elt,endian-ness,endian,gcd,goto,gotta,Int,Jacobi,Jacobian,Legendre,mlen,Modm,modpow,montgomerized,montgomeryize,montgomeryized,montmul,mul,param,Pomerance,povar,precompute,Pseudocode,Tolga,typeof,Uint,unrollsentinel,wil,Xout,Xout-t,Yout,Zout
/// aequals,Eshift,idx,Lsbit,Minust,mult,myelement,myresult,naf,Neg,Nist,numcopy,Obj,onemontgomery,Precomputation,Res,swaptmp,Tmp,xbytes,ybytes
/// #endregion JSCop/JsHint
function MsrcryptoMath() {
// 'number' of bits per digit. Must be even.
var DIGIT_BITS = 24;
// 'number' of bytes per digit.
var DIGIT_NUM_BYTES = Math.floor(DIGIT_BITS / 8);
// digit mask.
var DIGIT_MASK = (1 << DIGIT_BITS) - 1;
// digit base.
var DIGIT_BASE = (1 << DIGIT_BITS);
// max digit value, unsigned
var DIGIT_MAX = DIGIT_MASK;
// Construct scaler for DIGIT_NUM_BYTES, so I don't have to multiply in the loop
var DIGIT_SCALER = [1, 256];
for (var i = 2; i <= DIGIT_NUM_BYTES; ++i) {
DIGIT_SCALER[i] = DIGIT_SCALER[i - 1] * 256;
}
// Number of trailing zero bits in numbers 0..15 (4 bits). [0] is for 0, [15] is for 15.
var Zero = [0];
var One = [1];
// Create an array, mimics the constructors for typed arrays.
function createArray(/*@dynamic*/parameter) {
var i, array = null;
if (!arguments.length || typeof arguments[0] === "number") {
// A number.
array = new Array(parameter);
for (i = 0; i < parameter; i += 1) {
array[i] = 0;
}
} else if (typeof arguments[0] === "object") {
// An array or other index-able object
array = new Array(parameter.length);
for (i = 0; i < parameter.length; i += 1) {
array[i] = parameter[i];
}
}
return array;
}
function swapEndianness(bytes) {
/// Swap big endian bytes to little endian bytes.
/// UInt8Array - representing a big-integer.
/// UInt8Array - the number with endianness swapped.
var out = new Array(bytes.length);
var i = 0;
while (i < bytes.length) {
out[i] = bytes[bytes.length - i - 1];
i += 1;
}
return out;
}
function stringToDigits(number, radix) {
/// Parse a String in a given base into a little endian digit array.
/// Input unsigned integer in a string.
/// Radix of the input. Must be >=2 and <=36. Default = 10.
/// Array of digits in little endian; [0] is LSW.
// skip leading and trailing whitespace.
number = number.replace(/^\s+|\s+$/g, '');
var num = [0];
var buffer = [0];
radix = radix || 10; // default radix is 10
for (var i = 0; i < number.length; i += 1) {
// Extract character
var char = parseInt(number[i], radix);
if (isNaN(char)) {
throw new Error("Failed to convert string to integer in radix " + radix.toString());
}
// 'buffer' = 'num' * 'radix'
multiply(num, radix, buffer);
// 'num' = 'buffer' + 'char'
add(buffer, [/*@static_cast(Number)*/char], num);
normalizeDigitArray(num);
}
return num;
}
function digitsToString(digits, radix) {
/// Convert a big-endian byte array to a number in string in radix.
/// A big integer as a little-endian digit array.
/// Radix from 2 to 26. Default = 10.
/// The number in base radix as a string.
radix = radix || 10;
if (DIGIT_BASE <= radix) {
throw new Error("DIGIT_BASE is smaller than RADIX; cannot convert.");
}
var wordLength = digits.length;
var quotient = [];
var remainder = [];
var temp1 = [];
var temp2 = [];
var divisor = [];
var a = [];
var i;
// Find the largest divisor that fits in a digit in radix
//divisor[0] = 10000; // Largest power of ten fitting in a digit
var sb = "";
var pad = "0";
divisor[0] = radix;
while (Math.floor(DIGIT_BASE / divisor[0]) >= radix) {
divisor[0] = divisor[0] * radix;
pad = pad.concat("0");
}
for (i = 0; i < wordLength; i += 1) {
a[i] = digits[i];
}
do {
var allZeros = true;
for (i = 0; i < a.length; i += 1) {
if (a[i] !== 0) {
allZeros = false;
break;
}
}
if (allZeros) {
break;
}
divRem(a, divisor, quotient, remainder, temp1, temp2);
normalizeDigitArray(quotient, a.length, true);
var /*@type(String) */ newDigits = remainder[0].toString(radix);
sb = pad.substring(0, pad.length - newDigits.length) + newDigits + sb;
var swap = a;
a = quotient;
quotient = swap;
} while (true);
// Trim leading zeros
while (sb.length !== 0 && sb[0] === "0") {
sb = sb.substring(1, sb.length);
}
if (sb.length === 0) {
sb = "0";
}
return sb;
}
function powerOfTwo(i) {
/// Given a positive integer i, return a big integer in big-endian format
/// equal to 2^i. This is useful for creating fields of certain size.
/// A positive integer.
/// UInt8Array - 2^i as a big-endian byte array.
var requiredBytes = Math.ceil((i + 1) / 8);
var out = createArray(requiredBytes);
out[0] = Math.pow(2, i % 8);
return out;
}
function computeBitArray(bytes) {
/// Given an array of bytes in big-endian format, compute UInt8Array with
/// one element for each bit (0 or 1), in little-endian order.
/// An array of bytes in big-endian format.
/// An array of 0's and 1's representing the bits in little-endian.
var out = createArray(bytes.length * 8);
var bitLength = 0;
var i = bytes.length - 1;
while (i >= 0) {
var j = 0;
while (j < 8) {
var mask = (1 << j);
var bit = ((bytes[i] & mask) === mask) ? 1 : 0;
var thisBitIndex = (8 * ((bytes.length - i) - 1)) + j;
if (bit === 1) {
bitLength = thisBitIndex + 1;
}
out[thisBitIndex] = bit;
j += 1;
}
i--;
}
return out.slice(0, bitLength);
}
function bitScanForward(value) {
/// Return the 0-based index of the first non-zero bit starting at the most significant bit position.
/// Value to scan.
/// Zero-based index of the first non-zero bit.
var mask = DIGIT_BASE >>> 1;
var index = DIGIT_BITS;
while (index-- > 0) {
if ((value & mask) === mask) {
break;
}
mask = mask >>> 1;
}
return index;
}
function highestSetBit(bytes) {
/// Returns the (1 indexed) index of the highest set bit.
/// A big-endian big integer byte array.
/// The index of the highest bit.
var i = 0;
var bitLength = 0;
while (i < bytes.length) {
if (bitLength === 0) {
// Look for highest set bit in this byte
var j = 7;
while (j >= 0 && bitLength === 0) {
var mask = (1 << j);
if ((bytes[i] & mask) === mask) {
bitLength = j + 1;
}
j--;
}
} else {
bitLength += 8;
}
i += 1;
}
return bitLength;
}
function computeNAF(k, w, temp, nafDigits) {
/// Compute the width-w Non-Adjacent-Form of the given positive integer, from
/// least to most significant digit.
/// A little-endian digit array representing the
/// number to be converted into NAF.
/// The width of the NAF window.
/// An array for temp storage, same size as k.
/// An array which receives the NAF form of
/// of the given number. Size should be equal to the
/// width of k (digits * 16) plus one. Not all elements will be
/// necessarily be used.
/// The number of valid digits.
// Get the number of bits
var bitWidth = k.length * DIGIT_BITS;
var i, j;
var s = k.length;
var sMinusOne = s - 1;
if (nafDigits.length !== bitWidth + 1) {
throw "nafDigits length must = bitwidth + 1";
}
if (w <= 1) {
throw "w must be greater than 1";
}
if (w >= DIGIT_BITS) {
throw "w must be less than " + DIGIT_BITS.toString();
}
// Clone digits because we are going to modify them during the
// computation.
for (i = 0; i < s; i += 1) {
temp[i] = k[i];
}
// Set number of NAF digits (out parameter) to begin
var numNafDigits = 0;
// Mask for taking w bits off the digits
var mask = (0x1 << w) - 1;
var wMinusOne = w - 1;
var twoToTheW = (0x1 << w);
var twoToTheWMinusOneMinusOne = (0x1 << (w - 1)) - 1;
// Number of pending zeros to write the NAF.
// They will be written IFF we encounter a non-zero value to write.
var zeroCount = 0;
var isTempZero = isZero(temp);
while (!isTempZero) {
/* If k is even */
if ((temp[0] & 0x1) !== 0x1) {
// 'k' <- k / 2
shiftRight(temp, temp);
zeroCount++;
} else {
// Grab remainder bits
var maskedBits = (temp[0] & mask);
// Bit shift w to the right
shiftRight(temp, temp, w, sMinusOne + 1);
if (maskedBits > twoToTheWMinusOneMinusOne) {
maskedBits -= twoToTheW;
}
if (maskedBits < 0) {
// ADD ONE TO K
var carry = temp[0] + 1;
temp[0] = carry & DIGIT_MASK;
carry = (carry >> DIGIT_BITS);
// Break early if possible
if (carry !== 0) {
for (j = 1; j < s; j += 1) {
carry += temp[j];
temp[j] = carry & DIGIT_MASK;
carry = (carry >> DIGIT_BITS);
if (carry === 0) {
break;
}
}
}
}
// Add any pending zeros and reset the zero counter
for (; zeroCount > 0; zeroCount--) {
nafDigits[numNafDigits] = 0;
numNafDigits++;
}
// 'k_i' = maskedBits
nafDigits[numNafDigits] = maskedBits;
numNafDigits++;
// Add zeros
for (j = 0; j < wMinusOne; j += 1) {
zeroCount++;
}
}
isTempZero = true;
for (i = 0; i < s; i += 1) {
if (temp[i] !== 0) {
isTempZero = false;
break;
}
}
}
return numNafDigits;
}
//// //// //// //// //// //// //// //// //// //// //// //// ///
/// Other utility functions
//// //// //// //// //// //// //// //// //// //// //// //// ///
function copyArray(/*@Array*/source, sourceIndex, /*@Array*/destination, destIndex, length) {
/// Copies a range of elements from one array to another array.
/// Source array to copy from.
/// The index in the source array at which copying begins.
/// The array that receives the data.
/// The index in the destination array at which storing begins.
/// The number of elements to copy.
while (length-- > 0) {
destination[destIndex + length] = source[sourceIndex + length];
}
}
function isZero(array) {
/// Check if an array is zero. All elements are zero.
/// UInt16Array - An array to be checked.
///
var i;
for (i = 0; i < array.length; i += 1) {
if (array[i] !== 0) {
return false;
}
}
return true;
}
function isEven(array) {
/// Returns true if this number is even.
///
///
return (array[0] & 0x1) === 0x0;
}
function sequenceEqual(left, right) {
/// Compare two indexable collections for sequence equality.
/// The left array.
/// The right array.
/// True if both arrays are the same.
if (left.length !== right.length) {
return false;
}
for (var i = 0; i < left.length; i += 1) {
if (left[i] !== right[i]) {
return false;
}
}
return true;
}
function bytesToDigits(bytes) {
/// Convert an unsigned number from big-endian bytes to little endian digits.
/// The number in unsigned big-endian byte format.
/// The digits in little-endian.
// Construct scaler for DIGIT_NUM_BYTES, so I don't have to multiply in the loop
var arrayLength = Math.floor((bytes.length + DIGIT_NUM_BYTES - 1) / DIGIT_NUM_BYTES);
var array = new Array(arrayLength);
array[0] = 0;
var digit = 0, index = 0, scIndex = 0;
for (var i = bytes.length - 1; i >= 0; --i) {
digit = digit + (DIGIT_SCALER[scIndex++] * (bytes[i] & 0x0ff));
if (DIGIT_SCALER[scIndex] === DIGIT_BASE) {
scIndex = 0;
array[index++] = digit;
digit = 0;
}
}
// Last digit (MSW), if there is a need
if (digit !== 0) {
array[index] = digit;
}
return array;
}
function digitsToBytes(digits, trim, minTrimLength) {
/// Construct a big endian array of bytes from a litte-endian array of digits.
/// Always returns at least one byte and trims leading zeros.
/// The digits in little-endian.
/// Remove the leading zeros from the result (default true)
/// Minimum length to trim down to. Valid only if trim is true. Default=1.
/// Encoded bytes in big-endian format.
var i, j, byte1;
var bytes = [0];
if (typeof trim === "undefined") {
trim = true;
}
for (i = 0; i < digits.length; i += 1) {
byte1 = digits[i];
for (j = 0; j < DIGIT_NUM_BYTES; j += 1) {
bytes[i * DIGIT_NUM_BYTES + j] = byte1 & 0x0FF;
byte1 = Math.floor(byte1 / 256);
}
}
bytes = swapEndianness(bytes);
if (minTrimLength === undefined) {
minTrimLength = 1;
}
if (trim) {
while (bytes.length > minTrimLength && bytes[0] === 0) {
bytes.shift();
}
}
return bytes;
}
function intToDigits(value, numDigits) {
/// Construct an array of digits from a positive integer.
/// A positive integer to be converted to digit form.
/// The number of digits to use for the digit form.
/// The given integer in digit form.
if (typeof numDigits === "undefined") {
if (value <= 1) {
numDigits = 1; // Special case <= 1
} else {
var numBits = Math.log(value) / Math.LN2;
numDigits = Math.ceil(numBits / DIGIT_BITS);
}
}
var digitRepresentation = [];
while (value > 0) {
digitRepresentation.push(value % DIGIT_BASE);
value = Math.floor(value / DIGIT_BASE);
}
while (digitRepresentation.length < numDigits) {
digitRepresentation.push(0);
}
return digitRepresentation;
}
function mswIndex(digits) {
/// Return the index of the most significant word of x, 0-indexed.
/// If x is zero (no significant index), then -1 is returned.
/// Digit array.
/// Index of the most significant word, or -1 if digits is zero.
for (var i = digits.length - 1; i >= 0; i--) {
if (digits[i] !== undefined && digits[i] !== 0) {
return i;
}
}
return (digits[0] === 0) ? -1 : 0;
}
function compareDigits(left, right) {
/// Compare two digit arrays by value. Returns an integer indicating the comparison result.
/// Digit arrays are in little endian.
/// The object on the left side of the comparison.
/// The object on the right side of the comparison.
/// A value that indicates the relative order of the objects
/// being compared. The value is 0 if the items are equal,
/// negative if the left object precedes the right object,
/// and positive otherwise.
var comparisonResult = 0;
var nLeft = mswIndex(left) + 1;
var nRight = mswIndex(right) + 1;
if (nLeft > nRight) {
comparisonResult = 1;
} else if (nRight > nLeft) {
comparisonResult = -1;
} else {
while ((nLeft-- > 0) && (comparisonResult === 0)) {
comparisonResult = left[nLeft] - right[nLeft];
}
}
return comparisonResult;
}
function normalizeDigitArray(digits, length, pad) {
/// Normalize a digit array by truncating any leading zeroes and adjusting its length.
/// Set the length if given, and pad it with zeroes to that length of padding is requested.
/// Normalization results with a zero-indexed length of the array such that the MSW is not zero.
/// If the final array length is zero and no non-zero digits are found, assign digits[0]=0 and set length to 1.
/// Optionally, pad with zeroes to the given length, and set the array length.
/// Digit array.
/// Output length to pad with zeroes.
/// Pad with zeroes to length if true [false].
/// Resized digits array; same input object.
// Trim. Find the trimmed length and the position to start padding from (if padding is requested).
var i = mswIndex(digits);
// set the length to the given length (if given) or the trimmed length
digits.length = length || i + 1;
// Pad to the length
if (pad) {
while (++i < digits.length) {
digits[i] = 0;
}
}
if (digits.length <= 0) {
// no non-zero digits found.
digits[0] = 0;
digits.length = 1;
}
return digits;
}
function shiftRight(source, destination, bits, length) {
/// Shift a big integer to the right by the given number of bits or 1 if bits is not specified,
/// effectively dividing by two (or 2^bits) and ignoring the remainder.
/// Source digit array.
/// Destination digit array. May be the same as source.
/// Number of bits to shift, must be less than DIGIT_BITS and greater or equal to zero. Default is 1.
/// Number of items to shift from he source array. Default is source.length.
/// This is a numerical shift. Integers are stored in arrays in little-endian format.
/// Thus, this function shifts an array from higher order indices into lower indices. [0] is LSW.
///
if (bits === undefined) {
bits = 1;
} else if (bits >= DIGIT_BITS || bits < 0) {
throw new Error("Invalid bit count for shiftRight");
}
if (length === undefined) {
length = source.length;
}
var n = length - 1;
var leftShiftBitCount = DIGIT_BITS - bits;
for (var i = 0; i < n; ++i) {
destination[i] = ((source[i + 1] << leftShiftBitCount) | (source[i] >>> bits)) & DIGIT_MASK;
//a[i] = high|low = low bits of a[i+1] | high bits of a[i]
}
destination[n] = source[n] >>> bits;
}
function shiftLeft(source, destination, bits, length) {
/// Shift a number array to the left by given bits, i.e., multiply by 2^bits.
/// Source digit array.
/// Destination digit array. May be the same as source.
/// Number of bits to shift, must be less than DIGIT_BITS and greater or equal to zero. Default is 1.
/// Number of items to shift from he source array. Default is source.length.
/// An additional MSW digit may be added if the leftshift out from the current MSW produces a non-zero result. [0] is LSW.
if (bits === undefined) {
bits = 1;
} else if (bits >= DIGIT_BITS || bits < 0) {
throw new Error("bit count must be smaller than DIGIT_BITS and positive in shiftLeft");
}
if (length === undefined) {
length = source.length;
}
var rightShiftBitCount = DIGIT_BITS - bits;
// The following line is correct. destination should remain undefined if there are no bits going into it.
destination[length] = (source[length - 1] >>> (DIGIT_BITS - bits)) || destination[length];
for (var i = length - 1; i > 0; i--) {
destination[i] = ((source[i] << bits) | ((source[i - 1] >>> rightShiftBitCount))) & DIGIT_MASK;
// a[i] = high|low = low bits of a[i] | high bits of a[i-1]
}
destination[0] = (source[0] << bits) & DIGIT_MASK;
}
//// //// //// //// //// //// //// //// //// //// //// //// //// /
// Low level math routines
//// //// //// //// //// //// //// //// //// //// //// //// //// /
function add(addend1, addend2, sum) {
/// Add two arrays of digits into a third array: sum = addend1 + addend2. Carry is recorded in the output if there is one.
/// The first addend.
/// The second added.
/// The output sum buffer addend1 + addend2.
/// If carry out then 1, otherwise 0.
// Determine which is shorter
var shortArray = addend1;
var longArray = addend2;
if (addend2.length < addend1.length) {
shortArray = addend2;
longArray = addend1;
}
// Perform the addition
var s = shortArray.length;
var carry = 0;
var i;
for (i = 0; i < s; i += 1) {
carry += shortArray[i] + longArray[i];
sum[i] = carry & DIGIT_MASK;
carry = (carry >> DIGIT_BITS);
}
for (i = s; i < longArray.length; i += 1) {
carry += longArray[i];
sum[i] = carry & DIGIT_MASK;
carry = (carry >> DIGIT_BITS);
}
// Set output length
sum.length = longArray.length;
// Is there a carry into the next digit?
if (carry !== 0) {
sum[i] = carry & DIGIT_MASK;
}
return carry;
}
function subtract(minuend, subtrahend, difference) {
/// Subtraction: difference = minuend - subtrahend. Condition: minuend.length <= subtrahend.length.
/// Minuend.
/// Subtrahend.
/// The difference.
/// Returns -1 if there is a borrow (minuend < subtrahend), or 0 if there isn't (minuend >= subtrahend).
var s = subtrahend.length;
if (minuend.length < subtrahend.length) {
s = mswIndex(subtrahend);
if (minuend.length < s) {
throw new Error("Subtrahend is longer than minuend, not supported.");
}
}
var i, carry = 0;
for (i = 0; i < s; i += 1) {
carry += minuend[i] - subtrahend[i];
difference[i] = carry & DIGIT_MASK;
carry = carry >> DIGIT_BITS;
}
// Propagate the carry by subtracting from minuend into difference
while (i < minuend.length) {
carry += minuend[i];
difference[i++] = carry & DIGIT_MASK;
carry = carry >> DIGIT_BITS;
}
return carry;
}
function multiply(multiplicant, /* @dynamic */multiplier, product) {
/// Multiply two arrays of digits into a third array using schoolbook.
/// Multiplicand.
/// Multiplier.
/// Product = multiplicant * multiplier.
/// The result buffer; same as the product argument.
// Single number or an array?
var mplr = (typeof multiplier === "number") ? [multiplier] : multiplier;
var s = Math.max(multiplicant.length, mplr.length);
var i, j, u;
// P <- 0
// We only have to do this for half of result
// since the upper half is over-written on the first i iteration.
for (i = 0; i < s; i += 1) {
product[i] = 0;
}
// For i from 0 by 1 to s - 1 do
for (i = 0; i < mplr.length; i += 1) {
// 'u <- 0'
u = 0;
// For j from 0 by 1 to s - 1 do
for (j = 0; j < multiplicant.length; j += 1) {
// '(u,v) <- a_j * b_i + p_(i+j) + u'
u += multiplicant[j] * mplr[i] + product[i + j];
// 'p_(i+j) <- v'
product[i + j] = (u & DIGIT_MASK) /* v */;
u = Math.floor(u / DIGIT_BASE); // 'v <- u, u <- 0'
}
product[multiplicant.length + i] = (u & DIGIT_MASK);
}
// set product length; there may still be leading zero digits after this
product.length = multiplicant.length + mplr.length;
return product;
}
function divRem(dividend, divisor, quotient, remainder, temp1, temp2) {
/// Computes the quotient q and remainder r when dividend is divided by
/// divisor.
/// The dividend.
/// The divisor.
/// Receives the quotient (n digits).
/// Receives the remainder (n digits).
/// Temporary storage (n digits).
/// Temporary storage (n digits).
/// This is an implementation of Figure 9-1 is Knuth's Algorithm D [Knu2 sec. 4.3.1].
/// Throws error on division by zero.
///
var m = mswIndex(dividend) + 1; // zero-based length
var n = mswIndex(divisor) + 1; // zero-based length
var qhat, rhat, carry, p, t, i, j;
// Check for quick results and clear out conditionals
if (m < n) {
// dividend < divisor. q=0, remainder=dividend
copyArray(dividend, 0, remainder, 0, dividend.length);
remainder.length = dividend.length;
normalizeDigitArray(remainder);
quotient[0] = 0;
quotient.length = 1;
return;
} else if (n === 0 || (n === 1 && divisor[n - 1] === 0)) { // self-explanatory
throw new Error("Division by zero.");
} else if (n === 1) {
// divisor is single digit; do a simpler division
t = divisor[0];
rhat = 0;
for (j = m - 1; j >= 0; --j) {
p = (rhat * DIGIT_BASE) + dividend[j];
quotient[j] = (p / t) & DIGIT_MASK;
rhat = (p - quotient[j] * t) & DIGIT_MASK;
}
quotient.length = m;
normalizeDigitArray(quotient);
remainder[0] = rhat;
remainder.length = 1;
return;
}
// Normalization step. Align dividend and divisor so that their
// most significant digits are at the same index.
// Shift divisor by so many bits (0..DIGIT_BITS-1) to make MSB non-zero.
var s = DIGIT_BITS - 1 - bitScanForward(divisor[n - 1]);
var vn = temp1 || [];
vn.length = n;
shiftLeft(divisor, vn, s, n);
var un = temp2 || [];
un.length = m;
shiftLeft(dividend, un, s, m);
un[m] = un[m] || 0; // must not be undefined
// Main division loop with quotient estimate qhat
quotient.length = m - n + 1;
remainder.length = n;
for (j = m - n; j >= 0; --j) {
// Estimate quotient qhat using two-digit by one-digit division
// because 3-digit by 2-digit division is more complex. Then, correct qhat after this.
qhat = Math.floor((un[j + n] * DIGIT_BASE + un[j + n - 1]) / vn[n - 1]);
rhat = (un[j + n] * DIGIT_BASE + un[j + n - 1]) - qhat * vn[n - 1];
// If the quotient estimate is large, reduce the quotient estimate till the following is satisfied:
// qhat = {un[j+n, j+n-1, j+n-2]} div {uv[n-1,n-2]}
while (true) {
if (qhat >= DIGIT_BASE || (qhat * vn[n - 2]) > ((rhat * DIGIT_BASE) + un[j + n - 2])) {
qhat = qhat - 1;
rhat = rhat + vn[n - 1];
if (rhat < DIGIT_BASE) {
continue;
}
}
break;
}
// Multiply the [shifted] divisor by the quotient estimate and subtract the product from the dividend
// un = un - qhat*vn
carry = 0;
for (i = 0; i < n; i++) {
p = qhat * vn[i];
t = un[i + j] - carry - (p & DIGIT_MASK);
un[i + j] = t & DIGIT_MASK;
//carry = (p >>> DIGIT_BITS) - (t >> DIGIT_BITS);
// Don't shift: integer shifts are defined over 32-bit numbers in JS.
carry = Math.floor(p / DIGIT_BASE) - Math.floor(t / DIGIT_BASE);
}
t = un[j + n] - carry;
un[j + n] = t & DIGIT_MASK;
// Store the estimated quotient digit (may need correction)
quotient[j] = qhat & DIGIT_MASK;
// Correction needed?
if (t < 0) {
// quotient too big (at most by 1 divisor)
// decrement the quotient, and add [shifted] divisor back to the running dividend (remainder)
quotient[j] = quotient[j] - 1;
// un = un + vn
carry = 0;
for (i = 0; i < n; i++) {
t = un[i + j] + vn[i] + carry;
un[i + j] = t & DIGIT_MASK;
carry = t >> DIGIT_BITS;
}
un[j + n] = (un[j + n] + carry) & DIGIT_MASK;
}
}
// Denormalize the remainder (shift right by s bits).
for (i = 0; i < n; i++) {
remainder[i] = ((un[i] >>> s) | (un[i + 1] << (DIGIT_BITS - s))) & DIGIT_MASK;
}
// Compute correct lengths for the quotient and remainder
normalizeDigitArray(quotient);
normalizeDigitArray(remainder);
}
function reduce(number, modulus, remainder, temp1, temp2) {
/// Integer reduction by a modulus to compute number mod modulus. This function uses division,
/// and should not be used for repetitive operations.
/// Input number to reduce.
/// Modulus to reduce the input by.
/// Output remainder = number mod modulus.
/// Temporary space, optional.
/// Temporary space, optional.
/// The resulting remainder is in 0..modulus-1; same as "remainder".
// TODO: More efficient reduction implementation
var quotient = [];
divRem(number, modulus, quotient, remainder, temp1, temp2);
return remainder;
}
function modMul(multiplicant, /*@dynamic*/multiplier, modulus, product, temp1, temp2) {
/// Moduler multiplication of two numbers for a modulus. This function uses multiply and divide method,
/// and should not be used for repetitive operations.
/// product can be same as multiplicant and multiplier.
/// Multiplicand.
/// Multiplier.
/// Modulus to reduce the product.
/// Output product = multiplicant * multiplier mod modulus.
/// Scratch space (optional).
/// Scratch space (optional).
/// The resulting product in in 0..modulus-1; same as product.
var quotient = [];
multiply(multiplicant, multiplier, quotient);
divRem(quotient, modulus, quotient, product, temp1, temp2);
return product;
}
function eea(a, b, upp, vpp, rpp) {
/// Extended Euclidean Algorithm, Berlekamp's version. On return
/// b*upp - a*vpp = (-1)(k-1)*rpp.
/// The first number a.
/// The second number b.
/// a^-1 mod b if gcd=1. Optional.
/// b^-1 mod a if gcd=1. Optional./
/// gcd(a,b).
/// k value.
/// Algebraic Coding Theory, Pages 24-30.
/// if k is odd
/// a*a^-1 = 1 mod b ---> a^-1 = b - vpp
/// b*b^-1 = 1 mod a ---> b^-1 = vpp
/// if k is even
/// a*a^-1 = 1 mod b ---> a^-1 = upp
/// b*b^-1 = 1 mod a ---> b^-1 = a - upp
///
// Initialize rpp and rp from two inputs a and b s.t. rpp >= rp
var rp; // initialized from a or b
if (isZero(a)) { // gcd = (0,b) = b
copyArray(b, 0, rpp, 0, b.length);
rpp.length = b.length;
return 0;
} else if (isZero(b)) { // gcd = (a,0) = a
copyArray(a, 0, rpp, 0, a.length);
rpp.length = a.length;
return 0;
} else if (compareDigits(a, b) < 0) {
rp = a.slice(0);
copyArray(b, 0, rpp, 0, b.length); rpp.length = b.length;
} else {
rp = b.slice(0);
copyArray(a, 0, rpp, 0, a.length); rpp.length = a.length;
}
normalizeDigitArray(rpp);
normalizeDigitArray(rp);
var q = new Array(rpp.length);
var r = new Array(rpp.length);
var v = new Array(rpp.length);
var vppPresent = vpp !== undefined;
var vp;
if (vppPresent) {
vp = new Array(rpp.length);
vp[0] = 1; vp.length = 1;
vpp[0] = 0; vpp.length = 1;
}
var up;
var u = new Array(rpp.length);
var uppPresent = upp !== undefined;
if (uppPresent) {
up = new Array(rpp.length);
up[0] = 0; up.length = 1;
upp[0] = 1; upp.length = 1;
}
// k starts at -1 so that on return, it is >=0.
// In the follwing discussion, assume aCompute greatest common divisor or a and b.
/// First integer input.
/// Second integer input.
/// GCD output (optional).
/// GCD(a,b), the same object as the output parameter if given or a new object otherwise.
var aa = a;
var bb = b;
if (compareDigits(a, b) > 0) {
aa = b;
bb = a;
}
eea(aa, bb, undefined, undefined, output);
return normalizeDigitArray(output);
}
function modInv(a, n, aInv) {
/// Modular multiplicative inverse a^-1 mod n.
/// The number to invert. Condition: a < n, or the result would be n^-1 mod a.
/// The modulus.
/// a^-1 mod n (optional).
/// a^-1 mod n. Same as the aInv parameter if the parameter is specified.
//var gcd = eea(a, n, inv);
var upp = new Array(n.length);
var vpp = new Array(n.length);
var rpp = new Array(n.length);
var k = eea(a, n, vpp, upp, rpp);
aInv = aInv || [];
if (compareDigits(rpp, One) !== 0) {
aInv[0] = NaN;
aInv.length = 1;
} else {
// gcd = 1, there is an inverse.
// Compute inverse from Berlekamp's EEA outputs.
if ((k & 1) === 1) {
subtract(n, upp, aInv);
} else {
copyArray(upp, 0, aInv, 0, upp.length); aInv.length = upp.length;
}
normalizeDigitArray(aInv);
}
return aInv;
}
function modExp(base, exponent, modulus, result) {
/// Modular exponentiation in an integer group.
/// The base of the exponentiation.
/// The exponent.
/// Modulus to reduce the result.
/// Output element that takes the modular exponentiation result (optional).
/// Modular exponentiation result, same as if not null, or a new object.
result = result || [];
// If exponent is 0 return 1
if (compareDigits(exponent, Zero) === 0) {
result[0] = 1;
} else if (compareDigits(exponent, One) === 0) {
// If exponent is 1 return valueElement
copyArray(base, 0, result, 0, base.length);
result.length = base.length;
} else {
var montmul = new MontgomeryMultiplier(modulus);
normalizeDigitArray(base, montmul.s, true);
montmul.modExp(
base,
exponent,
result);
result.length = modulus.length;
}
return result;
}
function MontgomeryMultiplier(modulus) {
/// Construct a new montgomeryMultiplier object with the given modulus.
/// A prime modulus in little-endian digit form
/// Montgomery Multiplier class
/// This class implements high performance montgomery multiplication using
/// CIOS, as well as modular exponentiation.
function computeM0Prime(m0) {
/// Compute m' = -(m^-1) mod b, 16 bit digits. Based on Tolga Acar's code.
/// Digit m.
/// Digit m'.
var m0Pr = 1;
var a = 2;
var b = 3;
var c = b & m0;
for (var i = 2; i <= DIGIT_BITS; i += 1) {
if (a < c) {
m0Pr += a;
}
a = a << 1;
b = (b << 1) | 1;
c = m0 * m0Pr & b;
}
var result = (~m0Pr & DIGIT_MASK) + 1;
return result;
}
function montgomeryMultiply(multiplicant, multiplier, result, ctx) {
/// Montgomery multiplication with the CIOS method.
/// Multiplicant.
/// Multiplier.
/// Computed result multiplicant * multiplier * r^-1 mod n.
/// Context (optional = this).
ctx = ctx || this;
// Upper digits of result
var resultHigh = 0;
// Precompute offsets
var s = ctx.m.length;
var sMinus1 = s - 1;
// Local cache of m0, m', digitmask, digitbits
var mPrime = ctx.mPrime;
var m0 = ctx.m0;
var left0 = multiplicant[0];
var uv = 0, rightI, q, i, j, k;
// Clear the result array
for (i = 0; i < s; i += 1) {
result[i] = 0;
}
for (i = 0; i < s; i += 1) {
rightI = multiplier[i]; // Cache array value
// 'u <- 0'
// ---- UNROLL FIRST ITERATION (j == 0) ----
uv = left0 * rightI + result[0];
result[0] = uv & DIGIT_MASK;
uv = Math.floor(uv / DIGIT_BASE);
// ---- REMAINING ITERATIONS ----
for (j = 1; j < s; j += 1) {
// '(u,v) <- a_j * b_i + z_j + u'
// uv = uv >>> DIGIT_BITS;
// Don't shift: JS supports shifts over 32-bit integers, only.
uv = multiplicant[j] * rightI + result[j] + uv;
result[j] = uv & DIGIT_MASK;
uv = Math.floor(uv / DIGIT_BASE);
}
// -------------------------------
// '(u,v) <- z_s + u'.
// 'z_s <- v'.
// 'z_s+1 <- u'.
resultHigh = resultHigh + uv;
// 'q <- z_0 * m' mod digitBase
q = (result[0] * mPrime) & DIGIT_MASK;
// '(u,v) <- z_0 + m_0 * q'
uv = Math.floor((result[0] + (m0 * q)) / DIGIT_BASE);
// For j from 1 by 1 to s-1
for (j = 1, k = 0; j < s; j += 1, k++) {
// '(u,v) <- m_j * q + z_j + u'
uv = ctx.m[j] * q + result[j] + uv;
// 'z_j-1 <- v'
result[k] = uv & DIGIT_MASK;
uv = Math.floor(uv / DIGIT_BASE);
}
// '(u,v) <- z_s + u'.
// 'z_s-1 <- v'.
// 'z_s <- z_s+1 + u'.
resultHigh += uv;
result[sMinus1] = resultHigh & DIGIT_MASK;
resultHigh = Math.floor(resultHigh / DIGIT_BASE);
}
// Quick check to see if Z < M
if (!(resultHigh === 0 && result[sMinus1] < ctx.m[sMinus1])) {
// Subtract modulus
var resultMinusM = ctx.temp1;
var carry = 0;
for (i = 0; i < s; i += 1) {
carry = result[i] - ctx.m[i] + (carry >> DIGIT_BITS);
resultMinusM[i] = carry & DIGIT_MASK;
}
carry = (resultHigh & DIGIT_MASK) + (carry >> DIGIT_BITS);
carry = (resultHigh >>> DIGIT_BITS) + (carry >> DIGIT_BITS);
if ((carry >> DIGIT_BITS) !== -1) {
// Return resultMinusM
for (i = 0; i < s; i += 1) {
result[i] = resultMinusM[i];
}
return;
}
}
}
function convertToMontgomeryForm(/*@type(Digits)*/digits) {
/// Convert the digits in standard form to Montgomery residue representation.
/// Input digits to convert, and also the output converted digits.
var result = createArray(digits.length);
this.montgomeryMultiply(digits, this.rSquaredModm, result);
for (var i = 0; i < this.s; i += 1) {
digits[i] = result[i];
}
}
function convertToStandardForm(digits) {
/// Convert from Montgomery residue representation to the standard form.
/// Input digits to convert, and also the output converted digits.
this.montgomeryMultiply(digits, this.one, this.temp1);
for (var i = 0; i < this.s; i += 1) {
digits[i] = this.temp1[i];
}
}
function modExp(base, exponent, result) {
/// Compute base to exponent mod m into result.
/// Base of length s in the context.
/// Exponent.
/// Output as base raised to exponent, and reduced to the modulus in the context.
/// result base^exponent mod m; the same result object.
// Skip leading zero bits in the exponent
// The total number of bits to scan in the exponent must be an integral multiple of
// the number of bits to use in the exponent.
var i;
var expBitsToScan = 2; // scan 2 bits at a time
var expMask = DIGIT_MASK >>> (DIGIT_BITS - expBitsToScan);
for (i = exponent.length - 1; i > 0 && exponent[i] === 0; --i) { }
var bitsToScan = i * DIGIT_BITS + bitScanForward(exponent[i]) + 1;
bitsToScan = bitsToScan + (expBitsToScan - (bitsToScan % expBitsToScan));
var shiftAmt = (bitsToScan % DIGIT_BITS) - expBitsToScan;
if (shiftAmt < 0) {
shiftAmt += DIGIT_BITS;
}
var mask = expMask << shiftAmt;
// Prepare the precomputation table of base for k bits
// base[0..3] = [r, r*base, r*base^2, r*base^3] mod m
for (i = 1; i < baseTable.length; ++i) {
modMul(baseTable[i - 1], base, this.m, baseTable[i], temp1, temp2);
normalizeDigitArray(baseTable[i], this.s, true);
}
// a is the running result: a = 1*r mod m
// TODO: Skip the first loop iteration below to avoid 1*1 mod m (minor optimization)
var fourthPower = new Array(this.s);
var squared = result;
var partialResult = temp2;
copyArray(this.rModM, 0, partialResult, 0, this.s);
// Scan the exponent expBitsToScan bits at a time
var tableIndex;
while (bitsToScan > 0) {
// result <- Mont(a, a);
// result <- Mont(result, a);
this.montgomeryMultiply(partialResult, partialResult, squared);
this.montgomeryMultiply(squared, squared, fourthPower);
// tableIndex <- the current bits of the scanned exponent
tableIndex = (exponent[Math.floor((bitsToScan - 1) / DIGIT_BITS)] & mask) >>> shiftAmt;
// aDigits = result * table[tableIndex]
this.montgomeryMultiply(fourthPower, baseTable[tableIndex], partialResult);
bitsToScan = bitsToScan - expBitsToScan;
shiftAmt = shiftAmt - expBitsToScan;
mask = mask >>> expBitsToScan;
if (mask === 0) {
mask = expMask << (DIGIT_BITS - expBitsToScan);
shiftAmt = DIGIT_BITS - expBitsToScan;
}
}
// result = Mont(a, 1)
this.montgomeryMultiply(partialResult, this.one, result);
return result;
}
// Modulus
var m = modulus;
// First digit of modulus
var m0 = m[0];
// Operand size (number of digits)
var s = m.length;
// The number one - used by modpow
var one = createArray(s);
one[0] = 1;
// Compute m' = -(m^-1) mod b used by CIOS
var mPrime = computeM0Prime(m0);
// Create r and compute r mod m
// Since we are base b integers of length s, we want
// 'r = b^n = b^s'.
var quotient = createArray(2 * s + 1);
var rRemainder = createArray(s + 1); // becomes rModM
var temp1 = createArray(2 * s + 1);
var temp2 = createArray(2 * s + 1);
var rDigits = rRemainder;
rDigits[s] = 1;
divRem(
rDigits,
m,
quotient,
rRemainder,
temp1,
temp2);
var rModM = normalizeDigitArray(rRemainder, s, true);
// Compute R^2 mod m
var rSquaredModm = createArray(2 * s + 1);
var rSquaredDigits = rSquaredModm;
rSquaredDigits[s * 2] = 1;
divRem(
rSquaredDigits,
m,
quotient,
rSquaredModm,
temp1,
temp2);
normalizeDigitArray(rSquaredModm, s, true);
// Ready to do MontMul now - compute R^3
var rCubedModm = createArray(s);
var ctx = { m: m, mPrime: mPrime, m0: m0, temp1: temp1 };
montgomeryMultiply(
rSquaredModm,
rSquaredModm,
rCubedModm,
ctx);
// Allocate space for multi-bit modular exponentiation
var baseTable = new Array(4);
baseTable[0] = rModM;
baseTable[1] = new Array(s);
baseTable[2] = new Array(s);
baseTable[3] = new Array(s);
// Return a per-instance context for Montgomery multiplier.
// There is no need to use the "new" keyword when using this function.
return {
// Modulus
m: modulus,
// First digit of modulus
m0: m0,
// Compute m' = -(m^-1) mod b used by CIOS
mPrime: mPrime,
rSquaredModm: rSquaredModm,
s: s,
rModM: rModM,
rCubedModm: rCubedModm,
one: one,
temp1: temp1,
// Functions
convertToMontgomeryForm: convertToMontgomeryForm,
convertToStandardForm: convertToStandardForm,
montgomeryMultiply: montgomeryMultiply,
modExp: modExp
};
}
function IntegerGroup (modulusBytes) {
/// Construct a new IntegerGroup object with the given modulus.
/// A big-endian number to represent the modulus in a byte array.
/// This class represents the set of integers mod n. It is meant to be used in
/// a variety of situations, for example to perform operations in the additive
/// or multiplicative groups mod n. The modulus can be an arbitrary integer and
/// in the case that it is a prime p then the integer group is the field Fp. The
/// user should be aware of what type of object the given modulus produces, and
/// thus which operations are valid.
// Modulus
var m_modulus = bytesToDigits(modulusBytes);
// Length of an element in digits
var m_digitWidth = m_modulus.length;
// Setup numeric constants
var m_zero = intToDigits(0, m_digitWidth);
var m_one = intToDigits(1, m_digitWidth);
// Temp storage.
// Allocation in js is very slow, we use these temp arrays to avoid it.
var temp0 = createArray(m_digitWidth);
var temp1 = createArray(m_digitWidth);
// Create montgomery multiplier object
var montmul = new MontgomeryMultiplier(m_modulus);
function createElementFromBytes(bytes) {
/// Create a new element object from a byte value.
/// Desired element in big-endian format in an array of bytes.
/// An element object representing the given element.
var digits = bytesToDigits(bytes);
// Check size of the new element
if (cryptoMath.compareDigits(digits, this.m_modulus) >= 0) {
// Too many digits
throw new Error("The number provided is not an element of this group");
}
// expand to the group modulus length
normalizeDigitArray(digits, this.m_digitWidth, true);
return IntegerGroupElement(digits, this);
}
function createElementFromInteger(integer) {
/// Create a new element object from an integer value.
/// Desired element as an integer.
/// An element object representing the given element.
var digits = intToDigits(integer, this.m_digitWidth);
return IntegerGroupElement(digits, this);
}
function createElementFromDigits(digits) {
/// Create a new element object from a digit array.
/// Desired element as a digit array.
/// Object initialized with the given value.
cryptoMath.normalizeDigitArray(digits, this.m_digitWidth, true);
return IntegerGroupElement(digits, this);
}
function equals(otherGroup) {
/// Return true if the given object is equivalent to this one.
/// )
/// True if the given objects are equivalent.
return compareDigits(this.m_modulus, otherGroup.m_modulus) === 0;
}
function add(addend1, addend2, sum) {
/// Add this element to another element.
///
///
///
var i;
var s = this.m_digitWidth;
var result = sum.m_digits;
cryptoMath.add(addend1.m_digits, addend2.m_digits, result);
var mask = compareDigits(result, this.m_modulus) >= 0 ? DIGIT_MASK : 0;
// Conditional reduction by the modulus (one subtraction, only) only if the sum>modulus in almost constant time.
// The result is unmodified if the computed sum < modulus already.
var carry = 0;
for (i = 0; i < s; i += 1) {
carry = result[i] - (this.m_modulus[i] & mask) + carry;
result[i] = carry & DIGIT_MASK;
carry = (carry >> DIGIT_BITS);
}
result.length = s;
}
function subtract(leftElement, rightElement, outputElement) {
/// Subtract an element from another element.
///
///
///
var i, s = this.m_digitWidth;
var result = outputElement.m_digits;
var carry = cryptoMath.subtract(leftElement.m_digits, rightElement.m_digits, outputElement.m_digits);
// Final borrow?
if (carry === -1) {
carry = 0;
for (i = 0; i < s; i += 1) {
carry += result[i] + this.m_modulus[i];
result[i] = carry & DIGIT_MASK;
carry = carry >> DIGIT_BITS;
}
}
}
function inverse(element, outputElement) {
/// Compute the modular inverse of the given element.
/// The element to be inverted.
/// Receives the inverse element.
cryptoMath.modInv(element.m_digits, this.m_modulus, outputElement.m_digits);
}
function multiply(multiplicant, multiplier, product) {
/// Multiply an element by another element in the integer group.
/// Multiplicand.
/// Multiplier.
/// Product reduced by the group modulus.
/// Same as .
return cryptoMath.modMul(multiplicant.m_digits, multiplier.m_digits, this.m_modulus, product.m_digits, temp0, temp1);
}
function modexp(valueElement, exponent, outputElement) {
/// Modular exponentiation in an integer group.
/// The base input to the exponentiation.
/// The exponentas an unsigned integer.
/// Output element that takes the modular exponentiation result.
/// Computed result. Same as if not null, a new object otherwise.
outputElement = outputElement || IntegerGroupElement([], this);
// If exponent is 0 return 1
if (compareDigits(exponent, m_zero) === 0) {
outputElement.m_digits = intToDigits(1, this.m_digitWidth);
} else if (compareDigits(exponent, m_one) === 0) {
// If exponent is 1 return valueElement
for (var i = 0; i < valueElement.m_digits.length; i++) {
outputElement.m_digits[i] = valueElement.m_digits[i];
}
outputElement.m_digits.length = valueElement.m_digits.length;
} else {
this.montmul.modExp(
valueElement.m_digits,
exponent,
outputElement.m_digits);
outputElement.m_digits.length = this.montmul.s;
}
return outputElement;
}
function IntegerGroupElement(digits, group) {
/// IntegerGroupElement inner class.
/// Create a new integer element mod n.
/// An array of digits representing the element.
/// The parent group to which this element belongs.
/// The value given in digits
/// must be >= 0 and &;lt; modulus. Note that the constructor should not be
/// visible to the user, user should use group.createElementFromDigits(). This way we
/// can use any digit size and endian-ness we wish internally, operating in
/// our chosen representation until such time as the user wishes to produce
/// a byte array as output, which will be done by calling
/// toByteArrayUnsigned(). Note that other properties and methods are meant
/// to be "public" of course and thus callable by the user.
return {
// Variables
m_digits: digits,
m_group: group,
// Functions
equals: function (element) {
/// Compare an elements to this for equality.
/// Element to compare.
/// True if elements are equal, false otherwise.
return (compareDigits(this.m_digits, element.m_digits) === 0) &&
this.m_group.equals(this.m_group, element.m_group);
}
};
}
return {
// Variables
m_modulus: m_modulus,
m_digitWidth: m_digitWidth,
montmul: montmul,
// Functions
createElementFromInteger: createElementFromInteger,
createElementFromBytes: createElementFromBytes,
createElementFromDigits: createElementFromDigits,
equals: equals,
add: add,
subtract: subtract,
multiply: multiply,
inverse: inverse,
modexp: modexp
};
}
return {
DIGIT_BITS: DIGIT_BITS,
DIGIT_NUM_BYTES: DIGIT_NUM_BYTES,
DIGIT_MASK: DIGIT_MASK,
DIGIT_BASE: DIGIT_BASE,
DIGIT_MAX: DIGIT_MAX,
Zero: Zero,
One: One,
normalizeDigitArray: normalizeDigitArray,
swapEndianness: swapEndianness,
bytesToDigits: bytesToDigits,
stringToDigits: stringToDigits,
digitsToString: digitsToString,
intToDigits: intToDigits,
digitsToBytes: digitsToBytes,
sequenceEqual: sequenceEqual,
isZero: isZero,
isEven: isEven,
powerOfTwo: powerOfTwo,
shiftRight: shiftRight,
shiftLeft : shiftLeft,
compareDigits: compareDigits,
computeBitArray: computeBitArray,
bitLength: highestSetBit,
computeNAF: computeNAF,
IntegerGroup: IntegerGroup,
add: add,
subtract: subtract,
multiply: multiply,
divRem: divRem,
reduce: reduce,
modInv: modInv,
modExp: modExp,
modMul: modMul,
MontgomeryMultiplier: MontgomeryMultiplier,
gcd: gcd
};
}
var cryptoMath = cryptoMath || MsrcryptoMath();