1996 lines
73 KiB
JavaScript
1996 lines
73 KiB
JavaScript
|
|
//*******************************************************************************
|
|||
|
|
//
|
|||
|
|
// 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.
|
|||
|
|
//
|
|||
|
|
//*******************************************************************************
|
|||
|
|
|
|||
|
|
/// cryptoECC.js ==================================================================================
|
|||
|
|
/// Implementation of Elliptic Curve math routines for cryptographic applications.
|
|||
|
|
|
|||
|
|
/// #region JSCop/JsHint
|
|||
|
|
/* global createProperty */
|
|||
|
|
/* global cryptoMath */
|
|||
|
|
/* jshint -W016 */ /* allows bitwise operators */
|
|||
|
|
/* jshint -W052 */ /* allows not operator */
|
|||
|
|
|
|||
|
|
/// <reference path="jsCopDefs.js" />
|
|||
|
|
/// <reference path="global.js" />
|
|||
|
|
/// <reference path="random.js" />
|
|||
|
|
|
|||
|
|
/// <dictionary>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</dictionary>
|
|||
|
|
/// <dictionary>aequals,Eshift,idx,Lsbit,Minust,mult,myelement,myresult,naf,Neg,Nist,numcopy,Obj,onemontgomery,Precomputation,Res,swaptmp,Tmp,xbytes,ybytes</dictionary>
|
|||
|
|
|
|||
|
|
/// #endregion JSCop/JsHint
|
|||
|
|
|
|||
|
|
function MsrcryptoECC() {
|
|||
|
|
/// <summary>Elliptic Curve Cryptography (ECC) funcions.</summary>
|
|||
|
|
|
|||
|
|
// 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 = [];
|
|||
|
|
for (i = 0; i < parameter; i += 1) {
|
|||
|
|
array[i] = 0;
|
|||
|
|
}
|
|||
|
|
} else if (typeof arguments[0] === "object") {
|
|||
|
|
// An array or other index-able object
|
|||
|
|
array = [];
|
|||
|
|
for (i = 0; i < parameter.length; i += 1) {
|
|||
|
|
array[i] = parameter[i];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return array;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var btd = cryptoMath.bytesToDigits;
|
|||
|
|
var utils = msrcryptoUtilities;
|
|||
|
|
|
|||
|
|
var EllipticCurveFp = function (p1, a1, b1, order, gx, gy) {
|
|||
|
|
/// <param name="p1" type="Digits"/>
|
|||
|
|
/// <param name="a1" type="Digits"/>
|
|||
|
|
/// <param name="b1" type="Digits"/>
|
|||
|
|
/// <param name="order" type="Digits"/>
|
|||
|
|
/// <param name="gx" type="Digits"/>
|
|||
|
|
/// <param name="gy" type="Digits"/>
|
|||
|
|
/// <returns type="EllipticCurveFp"/>
|
|||
|
|
|
|||
|
|
var fieldStorageBitLength = p1.length;
|
|||
|
|
|
|||
|
|
var generator = EllipticCurvePointFp(this, false, gx, gy, null, false);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
p: p1, // field prime
|
|||
|
|
a: a1, // Weierstrass coefficient a
|
|||
|
|
b: b1, // Weierstrass coefficient b
|
|||
|
|
order: order, // EC group order
|
|||
|
|
generator: generator, // EC group generator
|
|||
|
|
allocatePointStorage: function () {
|
|||
|
|
return EllipticCurvePointFp(
|
|||
|
|
this,
|
|||
|
|
false,
|
|||
|
|
cryptoMath.intToDigits(0, fieldStorageBitLength),
|
|||
|
|
cryptoMath.intToDigits(0, fieldStorageBitLength)
|
|||
|
|
);
|
|||
|
|
},
|
|||
|
|
createPointAtInfinity: function () {
|
|||
|
|
return EllipticCurvePointFp(
|
|||
|
|
this,
|
|||
|
|
true,
|
|||
|
|
cryptoMath.intToDigits(0, fieldStorageBitLength),
|
|||
|
|
cryptoMath.intToDigits(0, fieldStorageBitLength)
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
var createANeg3Curve = function (p, b, order, gx, gy) {
|
|||
|
|
/// <param name="p" type="Digits"/>
|
|||
|
|
/// <param name="b" type="Digits"/>
|
|||
|
|
/// <param name="order" type="Digits"/>
|
|||
|
|
/// <param name="gx" type="Digits"/>
|
|||
|
|
/// <param name="gy" type="Digits"/>
|
|||
|
|
|
|||
|
|
var a = cryptoMath.intToDigits(3, p.length);
|
|||
|
|
cryptoMath.subtract(p, a, a);
|
|||
|
|
var curve = EllipticCurveFp(p, a, b, order, gx, gy);
|
|||
|
|
curve.generator.curve = curve;
|
|||
|
|
return curve;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
var curvesData = {
|
|||
|
|
"256": { size: 32, data: "/////wAAAAEAAAAAAAAAAAAAAAD///////////////9axjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgS/////8AAAAA//////////+85vqtpxeehPO5ysL8YyVRaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9Q=="},
|
|||
|
|
"384": { size: 48, data: "//////////////////////////////////////////7/////AAAAAAAAAAD/////szEvp+I+5+SYjgVr4/gtGRgdnG7+gUESAxQIj1ATh1rGVjmNii7RnSqFyO3T7Crv////////////////////////////////x2NNgfQ3Ld9YGg2ySLCneuzsGWrMxSlzqofKIr6LBTeOscce8yCtdG4dO2KLp5uYWfdB4IJUKjhVAvJdv1UpbDpUXjhydgq3NhfeSpYmLG9dnpi/kpLcKfj0Hb0omhR86doxE7XwuMAKYLHOHX6BnXpDHXyQ6g5f"},
|
|||
|
|
"521": { size: 32, data: "Af//////////////////////////////////////////////////////////////////////////////////////UZU+uWGOHJofkpohoLaFQO6i2nJbmbMV87i0iZGO8QnhVhk5Uex+k3sWUsC9O7G/BzVz34g9LDTx70Uf1GtQPwAB///////////////////////////////////////////6UYaHg78vlmt/zAFI9wml0Du1ybiJnEeuu2+3HpE4ZAnGhY4GtwQE6c2ePstmI5W0QpxkgTkFP7Uh+CivYGtNPbqhS1537+dZKP4dwSei/6jeM0izwYVqQpv5fn4xwuW9ZgEYOSlqeJo7wARcil+0LH0b2Zj1RElXm0RoF6+9Fyc+ZiyX7nKZXvQmQMVQuQE/rQdhNTxwhqJywkCIvpR2n9FmUA==" }
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
var createP256 = function () {
|
|||
|
|
return createPCurve("256");
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
var createP384 = function () {
|
|||
|
|
return createPCurve("384");
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
var createP521 = function () {
|
|||
|
|
return createPCurve("521");
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
var createPCurve = function (curveSize) {
|
|||
|
|
|
|||
|
|
var cd = utils.unpackData(curvesData[curveSize].data, curvesData[curveSize].size);
|
|||
|
|
|
|||
|
|
var newCurve = createANeg3Curve(
|
|||
|
|
btd(cd[0]), // P
|
|||
|
|
btd(cd[1]), // B
|
|||
|
|
btd(cd[2]), // Order
|
|||
|
|
btd(cd[3]), // gX
|
|||
|
|
btd(cd[4]) // gy
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
newCurve.name = "P-" + curveSize;
|
|||
|
|
|
|||
|
|
return newCurve;
|
|||
|
|
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
var createBN254 = function () {
|
|||
|
|
|
|||
|
|
return EllipticCurveFp(
|
|||
|
|
cryptoMath.stringToDigits("16798108731015832284940804142231733909889187121439069848933715426072753864723", 10), // 'p'
|
|||
|
|
cryptoMath.intToDigits(0, 16), // 'a'
|
|||
|
|
cryptoMath.intToDigits(2, 16), // 'b'
|
|||
|
|
cryptoMath.stringToDigits("16798108731015832284940804142231733909759579603404752749028378864165570215949", 10), // 'order'
|
|||
|
|
cryptoMath.stringToDigits("16798108731015832284940804142231733909889187121439069848933715426072753864722", 10), // 'gx = -1'
|
|||
|
|
cryptoMath.intToDigits(1, 16) // 'gy = 1'
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
var EllipticCurvePointFp = function (curve, isInfinity, x, y, z, isInMontgomeryForm) {
|
|||
|
|
/// <param name="curve" type="EllipticCurveFp"/>
|
|||
|
|
/// <param name="isInfinity" type="Boolean"/>
|
|||
|
|
/// <param name="x" type="Digits"/>
|
|||
|
|
/// <param name="y" type="Digits"/>
|
|||
|
|
/// <param name="z" type="Digits" optional="true"/>
|
|||
|
|
/// <param name="isInMontgomeryForm" type="Boolean" optional="true"/>
|
|||
|
|
/// <returns type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
var returnObj;
|
|||
|
|
|
|||
|
|
// 'optional' parameters
|
|||
|
|
if (typeof z === "undefined") {
|
|||
|
|
z = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (typeof isInMontgomeryForm === "undefined") {
|
|||
|
|
isInMontgomeryForm = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function equals(/*@type(EllipticCurvePointFp)*/ellipticCurvePointFp) {
|
|||
|
|
/// <param name="ellipticCurvePointFp" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
// If null
|
|||
|
|
if (!ellipticCurvePointFp) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Infinity == infinity
|
|||
|
|
if (returnObj.isInfinity && ellipticCurvePointFp.isInfinity) {
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Otherwise its member-wise comparison
|
|||
|
|
|
|||
|
|
if (returnObj.z === null && ellipticCurvePointFp.z !== null) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (returnObj.z !== null && ellipticCurvePointFp.z === null) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (returnObj.z === null) {
|
|||
|
|
return (cryptoMath.sequenceEqual(returnObj.x, ellipticCurvePointFp.x) &&
|
|||
|
|
cryptoMath.sequenceEqual(returnObj.y, ellipticCurvePointFp.y) &&
|
|||
|
|
returnObj.isInMontgomeryForm === ellipticCurvePointFp.isInMontgomeryForm);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (cryptoMath.sequenceEqual(returnObj.x, ellipticCurvePointFp.x) &&
|
|||
|
|
cryptoMath.sequenceEqual(returnObj.y, ellipticCurvePointFp.y) &&
|
|||
|
|
cryptoMath.sequenceEqual(returnObj.z, ellipticCurvePointFp.z) &&
|
|||
|
|
returnObj.isInMontgomeryForm === ellipticCurvePointFp.isInMontgomeryForm);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function copyTo(/*@type(EllipticCurvePointFp)*/ source, /*@type(EllipticCurvePointFp)*/ destination) {
|
|||
|
|
/// <param name="source" type="EllipticCurvePointFp"/>
|
|||
|
|
/// <param name="destination" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
destination.curve = source.curve;
|
|||
|
|
destination.x = source.x.slice();
|
|||
|
|
destination.y = source.y.slice();
|
|||
|
|
|
|||
|
|
if (source.z !== null) {
|
|||
|
|
destination.z = source.z.slice();
|
|||
|
|
} else {
|
|||
|
|
destination.z = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
setterSupport || (destination.isAffine = source.isAffine);
|
|||
|
|
destination.isInMontgomeryForm = source.isInMontgomeryForm;
|
|||
|
|
destination.isInfinity = source.isInfinity;
|
|||
|
|
|
|||
|
|
if (!destination.equals(source)) {
|
|||
|
|
throw new Error("Instances should be equal.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function clone() {
|
|||
|
|
if (returnObj.z === null) { // isAffine
|
|||
|
|
|
|||
|
|
return EllipticCurvePointFp(
|
|||
|
|
returnObj.curve,
|
|||
|
|
returnObj.isInfinity,
|
|||
|
|
createArray(returnObj.x),
|
|||
|
|
createArray(returnObj.y),
|
|||
|
|
null,
|
|||
|
|
returnObj.isInMontgomeryForm);
|
|||
|
|
} else {
|
|||
|
|
|
|||
|
|
return EllipticCurvePointFp(
|
|||
|
|
returnObj.curve,
|
|||
|
|
returnObj.isInfinity,
|
|||
|
|
createArray(returnObj.x),
|
|||
|
|
createArray(returnObj.y),
|
|||
|
|
createArray(returnObj.z),
|
|||
|
|
returnObj.isInMontgomeryForm);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
returnObj = /*@static_cast(EllipticCurvePointFp)*/ {
|
|||
|
|
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 (/*@type(EllipticCurveFp)*/curve) {
|
|||
|
|
/// <param name="curve" type="EllipticCurveFp"/>
|
|||
|
|
|
|||
|
|
// Store a reference to the curve.
|
|||
|
|
var m_curve = curve;
|
|||
|
|
|
|||
|
|
var fieldElementWidth = curve.p.length;
|
|||
|
|
|
|||
|
|
var montgomeryMultiplier = cryptoMath.MontgomeryMultiplier(curve.p);
|
|||
|
|
|
|||
|
|
// Pre-compute and store the montgomeryized form of A, and set our
|
|||
|
|
// zero flag to determine whether or not we should use implementations
|
|||
|
|
// optimized for A = 0.
|
|||
|
|
var montgomerizedA = curve.a.slice();
|
|||
|
|
montgomeryMultiplier.convertToMontgomeryForm(montgomerizedA);
|
|||
|
|
|
|||
|
|
var aequalsZero = cryptoMath.isZero(curve.a);
|
|||
|
|
|
|||
|
|
var one = cryptoMath.One;
|
|||
|
|
|
|||
|
|
var onemontgomery = createArray(fieldElementWidth);
|
|||
|
|
onemontgomery[0] = 1;
|
|||
|
|
montgomeryMultiplier.convertToMontgomeryForm(onemontgomery);
|
|||
|
|
|
|||
|
|
var group = cryptoMath.IntegerGroup(cryptoMath.digitsToBytes(montgomeryMultiplier.m), true);
|
|||
|
|
|
|||
|
|
// Setup temp storage.
|
|||
|
|
var temp0 = createArray(fieldElementWidth);
|
|||
|
|
var temp1 = createArray(fieldElementWidth);
|
|||
|
|
var temp2 = createArray(fieldElementWidth);
|
|||
|
|
var temp3 = createArray(fieldElementWidth);
|
|||
|
|
var temp4 = createArray(fieldElementWidth);
|
|||
|
|
var temp5 = createArray(fieldElementWidth);
|
|||
|
|
var temp6 = createArray(fieldElementWidth);
|
|||
|
|
var temp7 = createArray(fieldElementWidth);
|
|||
|
|
var swap0 = createArray(fieldElementWidth);
|
|||
|
|
|
|||
|
|
// Some additional temp storage used in point conversion routines.
|
|||
|
|
var conversionTemp0 = createArray(fieldElementWidth);
|
|||
|
|
var conversionTemp1 = createArray(fieldElementWidth);
|
|||
|
|
var conversionTemp2 = createArray(fieldElementWidth);
|
|||
|
|
|
|||
|
|
function modSub(left, right, result) {
|
|||
|
|
var resultElement = group.createElementFromInteger(0);
|
|||
|
|
resultElement.m_digits = result;
|
|||
|
|
group.subtract(
|
|||
|
|
group.createElementFromDigits(left),
|
|||
|
|
group.createElementFromDigits(right),
|
|||
|
|
resultElement);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function modAdd(left, right, result) {
|
|||
|
|
var resultElement = group.createElementFromInteger(0);
|
|||
|
|
resultElement.m_digits = result;
|
|||
|
|
group.add(
|
|||
|
|
group.createElementFromDigits(left),
|
|||
|
|
group.createElementFromDigits(right),
|
|||
|
|
resultElement);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function modInv(number, result) {
|
|||
|
|
cryptoMath.modInv(number, m_curve.p, result);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function modDivByTwo( /*@type(Digits)*/ dividend, /*@type(Digits)*/ result) {
|
|||
|
|
|
|||
|
|
var s = dividend.length;
|
|||
|
|
|
|||
|
|
var modulus = curve.p;
|
|||
|
|
|
|||
|
|
// If dividend is odd, add modulus
|
|||
|
|
if ((dividend[0] & 0x1) === 0x1) {
|
|||
|
|
var carry = 0;
|
|||
|
|
|
|||
|
|
for (var i = 0; i < s; i += 1) {
|
|||
|
|
carry += dividend[i] + modulus[i];
|
|||
|
|
result[i] = carry & cryptoMath.DIGIT_MASK;
|
|||
|
|
carry = (carry >>> cryptoMath.DIGIT_BITS);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Put carry bit into position for masking in
|
|||
|
|
carry = carry << (cryptoMath.DIGIT_BITS - 1);
|
|||
|
|
|
|||
|
|
// Bit shift
|
|||
|
|
cryptoMath.shiftRight(result, result);
|
|||
|
|
|
|||
|
|
// Mask in the carry bit
|
|||
|
|
result[s - 1] |= carry;
|
|||
|
|
} else {
|
|||
|
|
// Shift directly into result
|
|||
|
|
cryptoMath.shiftRight(dividend, result);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function montgomeryMultiply(left, right, result) {
|
|||
|
|
montgomeryMultiplier.montgomeryMultiply(
|
|||
|
|
left,
|
|||
|
|
right,
|
|||
|
|
result);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function montgomerySquare(left, result) {
|
|||
|
|
montgomeryMultiplier.montgomeryMultiply(
|
|||
|
|
left,
|
|||
|
|
left,
|
|||
|
|
result);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function correctInversion(digits) {
|
|||
|
|
/// <param name="digits" type="Digits"/>
|
|||
|
|
var results = createArray(digits.length);
|
|||
|
|
montgomeryMultiply(digits, montgomeryMultiplier.rCubedModm, results);
|
|||
|
|
for (var i = 0; i < results.length; i += 1) {
|
|||
|
|
digits[i] = results[i];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function doubleAequalsNeg3(point, outputPoint) {
|
|||
|
|
/// <param name="point" type="EllipticCurvePointFp"/>
|
|||
|
|
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
// If point = infinity then outputPoint := infinity.
|
|||
|
|
if (point.isInfinity) {
|
|||
|
|
outputPoint.isInfinity = true;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 't4:=Z1^2;'
|
|||
|
|
montgomerySquare(point.z, temp4);
|
|||
|
|
|
|||
|
|
// 't3:=Y1^2;'
|
|||
|
|
montgomerySquare(point.y, temp3);
|
|||
|
|
|
|||
|
|
// 't1:=X1+t4;'
|
|||
|
|
modAdd(point.x, temp4, temp1);
|
|||
|
|
|
|||
|
|
// 't4:=X1-t4;'
|
|||
|
|
modSub(point.x, temp4, temp4);
|
|||
|
|
|
|||
|
|
// 't0:=3*t4;'
|
|||
|
|
modAdd(temp4, temp4, temp0);
|
|||
|
|
modAdd(temp0, temp4, temp0);
|
|||
|
|
|
|||
|
|
// 't5:=X1*t3;'
|
|||
|
|
montgomeryMultiply(point.x, temp3, temp5);
|
|||
|
|
|
|||
|
|
// 't4:=t1*t0;'
|
|||
|
|
montgomeryMultiply(temp1, temp0, temp4);
|
|||
|
|
|
|||
|
|
// 't0:=t3^2;'
|
|||
|
|
montgomerySquare(temp3, temp0);
|
|||
|
|
|
|||
|
|
// 't1:=t4/2'
|
|||
|
|
modDivByTwo(temp4, temp1);
|
|||
|
|
|
|||
|
|
// 't3:=t1^2;'
|
|||
|
|
montgomerySquare(temp1, temp3);
|
|||
|
|
|
|||
|
|
// 'Z_out:=Y1*Z1;'
|
|||
|
|
montgomeryMultiply(point.y, point.z, swap0);
|
|||
|
|
for (var i = 0; i < swap0.length; i += 1) {
|
|||
|
|
outputPoint.z[i] = swap0[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'X_out:=t3-2*t5;'
|
|||
|
|
modSub(temp3, temp5, outputPoint.x);
|
|||
|
|
modSub(outputPoint.x, temp5, outputPoint.x);
|
|||
|
|
|
|||
|
|
// 't3:=t5-X_out;'
|
|||
|
|
modSub(temp5, outputPoint.x, temp3);
|
|||
|
|
|
|||
|
|
// 't5:=t1*t3'
|
|||
|
|
montgomeryMultiply(temp1, temp3, temp5);
|
|||
|
|
|
|||
|
|
// 'Y_out:=t5-t0;'
|
|||
|
|
modSub(temp5, temp0, outputPoint.y);
|
|||
|
|
|
|||
|
|
// Finalize the flags on the output point.
|
|||
|
|
outputPoint.isInfinity = false;
|
|||
|
|
outputPoint.isInMontgomeryForm = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function doubleAequals0(point, outputPoint) {
|
|||
|
|
/// <param name="point" type="EllipticCurvePointFp"/>
|
|||
|
|
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
// If point = infinity then outputPoint := infinity.
|
|||
|
|
if (point.isInfinity) {
|
|||
|
|
outputPoint.isInfinity = true;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 't3:=Y1^2;'
|
|||
|
|
montgomerySquare(point.y, temp3);
|
|||
|
|
|
|||
|
|
// 't4:=X1^2;'
|
|||
|
|
montgomerySquare(point.x, temp4);
|
|||
|
|
|
|||
|
|
// 't4:=3*t4;'
|
|||
|
|
modAdd(temp4, temp4, temp0);
|
|||
|
|
modAdd(temp0, temp4, temp4);
|
|||
|
|
|
|||
|
|
// 't5:=X1*t3;'
|
|||
|
|
montgomeryMultiply(point.x, temp3, temp5);
|
|||
|
|
|
|||
|
|
// 't0:=t3^2;'
|
|||
|
|
montgomerySquare(temp3, temp0);
|
|||
|
|
|
|||
|
|
// 't1:=t4/2;'
|
|||
|
|
modDivByTwo(temp4, temp1);
|
|||
|
|
|
|||
|
|
// 't3:=t1^2;'
|
|||
|
|
montgomerySquare(temp1, temp3);
|
|||
|
|
|
|||
|
|
// 'Z_out:=Y1*Z1;'
|
|||
|
|
montgomeryMultiply(point.y, point.z, swap0);
|
|||
|
|
for (var i = 0; i < swap0.length; i += 1) {
|
|||
|
|
outputPoint.z[i] = swap0[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'X_out:=t3-2*t5;'
|
|||
|
|
modSub(temp3, temp5, outputPoint.x);
|
|||
|
|
modSub(outputPoint.x, temp5, outputPoint.x);
|
|||
|
|
|
|||
|
|
// 't4:=t5-X_out;'
|
|||
|
|
modSub(temp5, outputPoint.x, temp4);
|
|||
|
|
|
|||
|
|
// 't2:=t1*t4;'
|
|||
|
|
montgomeryMultiply(temp1, temp4, temp2);
|
|||
|
|
|
|||
|
|
// 'Y_out:=t2-t0;'
|
|||
|
|
modSub(temp2, temp0, outputPoint.y);
|
|||
|
|
|
|||
|
|
// Finalize the flags on the output point.
|
|||
|
|
outputPoint.isInfinity = false;
|
|||
|
|
outputPoint.isInMontgomeryForm = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function generatePrecomputationTable(w, generatorPoint) {
|
|||
|
|
|
|||
|
|
if (w < 4) {
|
|||
|
|
throw new Error("This pre-computation algorithm assumes w >= 4");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!generatorPoint.isInMontgomeryForm) {
|
|||
|
|
throw new Error("Generator point must be in montgomery form");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!generatorPoint.isAffine) {
|
|||
|
|
throw new Error("Generator point must be in affine form");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Currently we support only two curve types, those with A=-3, and
|
|||
|
|
// those with A=0. In the future we will implement general support.
|
|||
|
|
// For now we switch here, assuming that the curve was validated in
|
|||
|
|
// the constructor.
|
|||
|
|
if (aequalsZero) {
|
|||
|
|
return generatePrecomputationTableAequals0(w, generatorPoint);
|
|||
|
|
} else {
|
|||
|
|
return generatePrecomputationTableAequalsNeg3(w, generatorPoint);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Given a point P on an elliptic curve, return a table of
|
|||
|
|
// size 2^(w-2) filled with pre-computed values for
|
|||
|
|
// P, 3P, 5P, ... Etc.
|
|||
|
|
|
|||
|
|
function generatePrecomputationTableAequalsNeg3(w, generatorPoint) {
|
|||
|
|
/// <param name="w" type="Number">The "width" of the table to use. The should match the width used to generate the NAF.</param>
|
|||
|
|
/// <param name="generatorPoint">The point P in affine, montgomery form.</param>
|
|||
|
|
/// <returns>A table of size 2^(w-2) filled with pre-computed values for P, 3P, 5P, ... Etc in De-montgomeryized Affine Form.</returns>
|
|||
|
|
|
|||
|
|
// Width of our field elements.
|
|||
|
|
var s = curve.p.length;
|
|||
|
|
|
|||
|
|
// Initialize table
|
|||
|
|
// The first element is set to our generator povar initially.
|
|||
|
|
// The rest are dummy points to be filled in by the pre-computation
|
|||
|
|
// algorithm.
|
|||
|
|
var tableSize = (1 << (w - 2)); // 2^(w-2)
|
|||
|
|
var t = []; // Of EllipticCurvePointFp
|
|||
|
|
var i;
|
|||
|
|
t[0] = generatorPoint.clone();
|
|||
|
|
|
|||
|
|
for (i = 1; i < tableSize; i += 1) {
|
|||
|
|
var newPoint = EllipticCurvePointFp(
|
|||
|
|
curve,
|
|||
|
|
false,
|
|||
|
|
createArray(s),
|
|||
|
|
createArray(s),
|
|||
|
|
createArray(s),
|
|||
|
|
true
|
|||
|
|
);
|
|||
|
|
t[i] = newPoint;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Initialize temp tables for povar recovery.
|
|||
|
|
var d = [];
|
|||
|
|
var e = [];
|
|||
|
|
|
|||
|
|
for (i = 0; i < tableSize - 2; i += 1) {
|
|||
|
|
d[i] = createArray(s);
|
|||
|
|
e[i] = createArray(s);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Alias temp7 to Z for readability.
|
|||
|
|
var z = temp7;
|
|||
|
|
|
|||
|
|
// Pseudocode Note:
|
|||
|
|
// Arrays of points use element 1 for X, element 2 for Y
|
|||
|
|
// so e.g. T[0][1] === T[0].x.
|
|||
|
|
|
|||
|
|
// SETUP -----------------------------------------------------------
|
|||
|
|
|
|||
|
|
// Compute T[0] = 2*P and T[1] = P such that both are in Jacobian
|
|||
|
|
// form with the same Z. These values are then used to compute
|
|||
|
|
// T[0] = 3P, T[1] = 5P, ..., T[n] = (3 + 2*n) * P.
|
|||
|
|
|
|||
|
|
// 't1 := T[0].x^2; '
|
|||
|
|
montgomerySquare(t[0].x, temp1);
|
|||
|
|
|
|||
|
|
// 't3 := T[0].y^2;'
|
|||
|
|
montgomerySquare(t[0].y, temp3);
|
|||
|
|
|
|||
|
|
// 't1 := (3*t1 + A)/2;'
|
|||
|
|
modAdd(temp1, temp1, temp2);
|
|||
|
|
modAdd(temp2, temp1, temp1);
|
|||
|
|
modAdd(temp1, montgomerizedA, temp1);
|
|||
|
|
modDivByTwo(temp1, temp1);
|
|||
|
|
|
|||
|
|
// 'T[2].x := t3 * T[0].x;'
|
|||
|
|
montgomeryMultiply(temp3, t[0].x, t[2].x);
|
|||
|
|
|
|||
|
|
// 'T[2].y := t3^2;'
|
|||
|
|
montgomerySquare(temp3, t[2].y);
|
|||
|
|
|
|||
|
|
// 'Z := T[0].y;'
|
|||
|
|
for (i = 0; i < s; i += 1) {
|
|||
|
|
z[i] = t[0].y[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'T[1].x := t1^2;'
|
|||
|
|
montgomerySquare(temp1, t[1].x);
|
|||
|
|
|
|||
|
|
// 'T[1].x := T[1].x - 2 * T[2].x;'
|
|||
|
|
// PERF: Implementing DBLSUB here (and everywhere where we compute A - 2*B)
|
|||
|
|
// may possibly result in some performance gain.
|
|||
|
|
modSub(t[1].x, t[2].x, t[1].x);
|
|||
|
|
modSub(t[1].x, t[2].x, t[1].x);
|
|||
|
|
|
|||
|
|
// 't2 := T[2].x - T[1].x;'
|
|||
|
|
modSub(t[2].x, t[1].x, temp2);
|
|||
|
|
|
|||
|
|
// 't1 := t1 * t2;'
|
|||
|
|
// NOTE: Using temp0 as target since montmul is destructive.
|
|||
|
|
montgomeryMultiply(temp1, temp2, temp0);
|
|||
|
|
|
|||
|
|
// 'T[1].y := t1 - T[2].y;'
|
|||
|
|
modSub(temp0, t[2].y, t[1].y);
|
|||
|
|
|
|||
|
|
// First iteration ------------------------------------------------
|
|||
|
|
|
|||
|
|
// 't1 := T[2].x - T[1].x;'
|
|||
|
|
modSub(t[2].x, t[1].x, temp1);
|
|||
|
|
|
|||
|
|
// 't2 := T[2].y - T[1].y;'
|
|||
|
|
modSub(t[2].y, t[1].y, temp2);
|
|||
|
|
|
|||
|
|
// 'Z := Z * t1;'
|
|||
|
|
montgomeryMultiply(z, temp1, temp0);
|
|||
|
|
var idx;
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
z[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'd[0] := t1^2;'
|
|||
|
|
montgomerySquare(temp1, d[0]);
|
|||
|
|
|
|||
|
|
// 't3 := t2^2;'
|
|||
|
|
montgomerySquare(temp2, temp3);
|
|||
|
|
|
|||
|
|
// 'T[2].x := d[0] * T[1].x;'
|
|||
|
|
montgomeryMultiply(d[0], t[1].x, t[2].x);
|
|||
|
|
|
|||
|
|
// 't3 := t3 - 2 * T[2].x;'
|
|||
|
|
modSub(temp3, t[2].x, temp3);
|
|||
|
|
modSub(temp3, t[2].x, temp3);
|
|||
|
|
|
|||
|
|
// 'd[0] := d[0] * t1;'
|
|||
|
|
montgomeryMultiply(d[0], temp1, temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
d[0][idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'T[1].x := t3 - d[0];'
|
|||
|
|
modSub(temp3, d[0], t[1].x);
|
|||
|
|
|
|||
|
|
// 't1 := T[2].x - T[1].x;'
|
|||
|
|
modSub(t[2].x, t[1].x, temp1);
|
|||
|
|
|
|||
|
|
// 't1 := t1 * t2;'
|
|||
|
|
// NOTE: Using t0 as target due to destructive multiply.
|
|||
|
|
montgomeryMultiply(temp1, temp2, temp0);
|
|||
|
|
|
|||
|
|
// 'T[2].y := T[1].y*d[0];'
|
|||
|
|
montgomeryMultiply(t[1].y, d[0], t[2].y);
|
|||
|
|
|
|||
|
|
// 'T[1].y := t1 - T[2].y;'
|
|||
|
|
// NOTE: Reusing t0 result from above.
|
|||
|
|
modSub(temp0, t[2].y, t[1].y);
|
|||
|
|
|
|||
|
|
// INNER ITERATIONS ------------------------------------------------
|
|||
|
|
var j, k, l;
|
|||
|
|
|
|||
|
|
for (i = 0; i < tableSize - 3; i += 1) {
|
|||
|
|
j = i + 1;
|
|||
|
|
k = i + 2;
|
|||
|
|
l = i + 3;
|
|||
|
|
|
|||
|
|
// 't1 := T[j].x - T[k].x;'
|
|||
|
|
modSub(t[j].x, t[k].x, temp1);
|
|||
|
|
|
|||
|
|
// 't2 := T[j].y - T[k].y;'
|
|||
|
|
modSub(t[j].y, t[k].y, temp2);
|
|||
|
|
|
|||
|
|
// 'Z := Z * t1;'
|
|||
|
|
// NOTE: Using temp0 as target since multiply is destructive.
|
|||
|
|
montgomeryMultiply(z, temp1, temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
z[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'd[i] := t1^2;'
|
|||
|
|
montgomerySquare(temp1, d[i]);
|
|||
|
|
|
|||
|
|
// 't3 := t2^2;'
|
|||
|
|
montgomerySquare(temp2, temp3);
|
|||
|
|
|
|||
|
|
// 'T[l].x := d[i] * T[k].x;'
|
|||
|
|
montgomeryMultiply(d[i], t[k].x, t[l].x);
|
|||
|
|
|
|||
|
|
// 't3 := t3 - 2 * T[l].x;'
|
|||
|
|
modSub(temp3, t[l].x, temp3);
|
|||
|
|
modSub(temp3, t[l].x, temp3);
|
|||
|
|
|
|||
|
|
// 'e[i] := d[i] * t1;'
|
|||
|
|
montgomeryMultiply(d[i], temp1, e[i]);
|
|||
|
|
|
|||
|
|
// 'T[k].x := t3 - e[i];'
|
|||
|
|
modSub(temp3, e[i], t[k].x);
|
|||
|
|
|
|||
|
|
// 't1 := T[l].x - T[k].x;'
|
|||
|
|
// NOTE: Using temp0 as target so we can multiply into temp1 below.
|
|||
|
|
modSub(t[l].x, t[k].x, temp0);
|
|||
|
|
|
|||
|
|
// 't1 := t1 * t2;'
|
|||
|
|
// NOTE: Using temp0 result from above.
|
|||
|
|
montgomeryMultiply(temp0, temp2, temp1);
|
|||
|
|
|
|||
|
|
// 'T[l].y := T[k].y*e[i];'
|
|||
|
|
montgomeryMultiply(t[k].y, e[i], t[l].y);
|
|||
|
|
|
|||
|
|
// 'T[k].y := t1 - T[l].y;'
|
|||
|
|
modSub(temp1, t[l].y, t[k].y);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// FINAL ITERATION -------------------------------------------------
|
|||
|
|
// {
|
|||
|
|
i = tableSize - 3;
|
|||
|
|
j = i + 1;
|
|||
|
|
k = i + 2; // 't1 := T[j].x - T[k].x;'
|
|||
|
|
modSub(t[j].x, t[k].x, temp1);
|
|||
|
|
|
|||
|
|
// 't2 := T[j].y - T[k].y;'
|
|||
|
|
modSub(t[j].y, t[k].y, temp2);
|
|||
|
|
|
|||
|
|
// 'Z := Z * t1;'
|
|||
|
|
montgomeryMultiply(z, temp1, temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
z[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'd[i] := t1^2;'
|
|||
|
|
montgomerySquare(temp1, d[i]);
|
|||
|
|
|
|||
|
|
// 't3 := t2^2;'
|
|||
|
|
montgomerySquare(temp2, temp3);
|
|||
|
|
|
|||
|
|
// 'e[i] := d[i] * t1;'
|
|||
|
|
montgomeryMultiply(d[i], temp1, e[i]);
|
|||
|
|
|
|||
|
|
// 't1 := d[i] * T[k].x;'
|
|||
|
|
montgomeryMultiply(d[i], t[k].x, temp1);
|
|||
|
|
|
|||
|
|
// 't3 := t3 - 2 * t1;'
|
|||
|
|
modSub(temp3, temp1, temp3);
|
|||
|
|
modSub(temp3, temp1, temp3);
|
|||
|
|
|
|||
|
|
// 'T[k].x := t3 - e[i];'
|
|||
|
|
modSub(temp3, e[i], t[k].x);
|
|||
|
|
|
|||
|
|
// 't1 := t1 - T[k].x;'
|
|||
|
|
// NOTE: Using temp0 as target so we can multiply into temp1 below.
|
|||
|
|
modSub(temp1, t[k].x, temp0);
|
|||
|
|
|
|||
|
|
// 't1 := t1 * t2;'
|
|||
|
|
// NOTE: Reusing temp0 to multiply into temp1.
|
|||
|
|
montgomeryMultiply(temp0, temp2, temp1);
|
|||
|
|
|
|||
|
|
// 'T[k].y := T[k].y * e[i];'
|
|||
|
|
// NOTE: Using temp3 as target due to destructive multiply.
|
|||
|
|
montgomeryMultiply(t[k].y, e[i], temp3);
|
|||
|
|
|
|||
|
|
// 'T[k].y := t1 - T[k].y;'
|
|||
|
|
// NOTE: Using temp3 instead of T[k].y.
|
|||
|
|
modSub(temp1, temp3, t[k].y);
|
|||
|
|
|
|||
|
|
// POST ITERATIONS - INVERT Z AND PREPARE TO RECOVER TABLE ENTRIES ---------------
|
|||
|
|
|
|||
|
|
// 'Z := 1/Z;'
|
|||
|
|
// NOTE: Z is in montgomery form at this point, i.e. Z*R. After
|
|||
|
|
// inversion we will have 1/(Z*R) but we want (1/Z)*R (the
|
|||
|
|
// montgomery form of Z inverse) so we use the inversion
|
|||
|
|
// correction, which does a montgomery multiplication by R^3
|
|||
|
|
// yielding the correct result.
|
|||
|
|
modInv(z, z);
|
|||
|
|
correctInversion(z);
|
|||
|
|
|
|||
|
|
// 't1 := Z^2;'
|
|||
|
|
montgomerySquare(z, temp1);
|
|||
|
|
|
|||
|
|
// 't2 := t1 * Z;'
|
|||
|
|
montgomeryMultiply(temp1, z, temp2);
|
|||
|
|
|
|||
|
|
// 'T[k].x := T[k].x * t1;'
|
|||
|
|
montgomeryMultiply(t[k].x, temp1, temp0);
|
|||
|
|
|
|||
|
|
// Copy temp0 to T[k].x.
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
t[k].x[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'T[k].y := T[k].y * t2;'
|
|||
|
|
montgomeryMultiply(t[k].y, temp2, temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
t[k].y[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
// } FINAL ITERATION
|
|||
|
|
|
|||
|
|
// RECOVER POINTS FROM TABLE ---------------------------------------
|
|||
|
|
|
|||
|
|
// For i in [(2^(w-2)-2)..1 by -1] do
|
|||
|
|
for (i = tableSize - 3; i >= 0; i--) {
|
|||
|
|
// 'j := i + 1;'
|
|||
|
|
j = i + 1; // 't1 := t1 * d[i];'
|
|||
|
|
montgomeryMultiply(temp1, d[i], temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
temp1[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 't2 := t2 * e[i];'
|
|||
|
|
montgomeryMultiply(temp2, e[i], temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
temp2[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'T[j].x := T[j].x * t1;'
|
|||
|
|
montgomeryMultiply(t[j].x, temp1, temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
t[j].x[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'T[j].y := T[j].y * t2;'
|
|||
|
|
montgomeryMultiply(t[j].y, temp2, temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
t[j].y[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// End for;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Points are now in affine form, set Z coord to null (== 1).
|
|||
|
|
for (i = 0; i < t.length; i += 1) {
|
|||
|
|
t[i].z = null;
|
|||
|
|
setterSupport || (t[i].isAffine = true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (i = 0; i < t.length; i += 1) {
|
|||
|
|
|
|||
|
|
if (!t[i].isAffine) {
|
|||
|
|
throw new Error("Non-affine povar found in precomputation table");
|
|||
|
|
}
|
|||
|
|
if (!t[i].isInMontgomeryForm) {
|
|||
|
|
convertToMontgomeryForm(t[i]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return t;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Given a povar P on an elliptic curve, return a table of
|
|||
|
|
// size 2^(w-2) filled with pre-computed values for
|
|||
|
|
// P, 3P, 5P, ... Etc.
|
|||
|
|
|
|||
|
|
function generatePrecomputationTableAequals0(w, generatorPoint) {
|
|||
|
|
/// <param name="w" type="Number">The "width" of the table to use. The should match
|
|||
|
|
/// the width used to generate the NAF.</param>
|
|||
|
|
/// <param name="generatorPoint" type="EllipticCurvePointFp">The povar P in affine, montgomery form.</param>
|
|||
|
|
/// <returns>A table of
|
|||
|
|
/// size 2^(w-2) filled with pre-computed values for
|
|||
|
|
/// P, 3P, 5P, ... Etc in De-montgomeryized Affine Form.</returns>
|
|||
|
|
|
|||
|
|
// Width of our field elements.
|
|||
|
|
var s = curve.p.length;
|
|||
|
|
|
|||
|
|
// Initialize table
|
|||
|
|
// The first element is set to our generator povar initially.
|
|||
|
|
// The rest are dummy points to be filled in by the pre-computation
|
|||
|
|
// algorithm.
|
|||
|
|
var tableSize = (1 << (w - 2)); // '2^(w-2)'
|
|||
|
|
var t = []; // Of EllipticCurvePointFp
|
|||
|
|
t[0] = generatorPoint.clone();
|
|||
|
|
var i;
|
|||
|
|
for (i = 1; i < tableSize; i += 1) {
|
|||
|
|
var newPoint = EllipticCurvePointFp(
|
|||
|
|
curve,
|
|||
|
|
false,
|
|||
|
|
createArray(s),
|
|||
|
|
createArray(s),
|
|||
|
|
createArray(s),
|
|||
|
|
true
|
|||
|
|
);
|
|||
|
|
t[i] = newPoint;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Initialize temp tables for povar recovery.
|
|||
|
|
var d = [];
|
|||
|
|
var e = [];
|
|||
|
|
|
|||
|
|
for (i = 0; i < tableSize - 2; i += 1) {
|
|||
|
|
d[i] = createArray(s);
|
|||
|
|
e[i] = createArray(s);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Alias temp7 to Z for readability.
|
|||
|
|
var z = temp7;
|
|||
|
|
|
|||
|
|
// Pseudocode Note:
|
|||
|
|
// Arrays of points use element 1 for X, element 2 for Y
|
|||
|
|
// so e.g. T[0][1] === T[0].x.
|
|||
|
|
|
|||
|
|
// SETUP -----------------------------------------------------------
|
|||
|
|
|
|||
|
|
// Compute T[0] = 2*P and T[1] = P such that both are in Jacobian
|
|||
|
|
// form with the same Z. These values are then used to compute
|
|||
|
|
// T[0] = 3P, T[1] = 5P, ..., T[n] = (3 + 2*n) * P.
|
|||
|
|
|
|||
|
|
// 't1 := T[0].x^2; '
|
|||
|
|
montgomerySquare(t[0].x, temp1);
|
|||
|
|
|
|||
|
|
// 't3 := T[0].y^2;'
|
|||
|
|
montgomerySquare(t[0].y, temp3);
|
|||
|
|
|
|||
|
|
// 't1 := (3*t1)/2;'
|
|||
|
|
modAdd(temp1, temp1, temp2);
|
|||
|
|
modAdd(temp2, temp1, temp1);
|
|||
|
|
modDivByTwo(temp1, temp1);
|
|||
|
|
|
|||
|
|
// 'T[2].x := t3 * T[0].x;'
|
|||
|
|
montgomeryMultiply(temp3, t[0].x, t[2].x);
|
|||
|
|
|
|||
|
|
// 'T[2].y := t3^2;'
|
|||
|
|
montgomerySquare(temp3, t[2].y);
|
|||
|
|
|
|||
|
|
// 'Z := T[0].y;'
|
|||
|
|
for (i = 0; i < s; i += 1) {
|
|||
|
|
z[i] = t[0].y[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'T[1].x := t1^2;'
|
|||
|
|
montgomerySquare(temp1, t[1].x);
|
|||
|
|
|
|||
|
|
// 'T[1].x := T[1].x - 2 * T[2].x;'
|
|||
|
|
// PERF: Implementing DBLSUB here (and everywhere where we compute A - 2*B)
|
|||
|
|
// may possibly result in some performance gain.
|
|||
|
|
modSub(t[1].x, t[2].x, t[1].x);
|
|||
|
|
modSub(t[1].x, t[2].x, t[1].x);
|
|||
|
|
|
|||
|
|
// 't2 := T[2].x - T[1].x;'
|
|||
|
|
modSub(t[2].x, t[1].x, temp2);
|
|||
|
|
|
|||
|
|
// 't1 := t1 * t2;'
|
|||
|
|
// NOTE: Using temp0 as target since montmul is destructive.
|
|||
|
|
montgomeryMultiply(temp1, temp2, temp0);
|
|||
|
|
|
|||
|
|
// 'T[1].y := t1 - T[2].y;'
|
|||
|
|
modSub(temp0, t[2].y, t[1].y);
|
|||
|
|
|
|||
|
|
// First iteration ------------------------------------------------
|
|||
|
|
|
|||
|
|
// 't1 := T[2].x - T[1].x;'
|
|||
|
|
modSub(t[2].x, t[1].x, temp1);
|
|||
|
|
|
|||
|
|
// 't2 := T[2].y - T[1].y;'
|
|||
|
|
modSub(t[2].y, t[1].y, temp2);
|
|||
|
|
|
|||
|
|
// 'Z := Z * t1;'
|
|||
|
|
montgomeryMultiply(z, temp1, temp0);
|
|||
|
|
var idx;
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
z[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'd[0] := t1^2;'
|
|||
|
|
montgomerySquare(temp1, d[0]);
|
|||
|
|
|
|||
|
|
// 't3 := t2^2;'
|
|||
|
|
montgomerySquare(temp2, temp3);
|
|||
|
|
|
|||
|
|
// 'T[2].x := d[0] * T[1].x;'
|
|||
|
|
montgomeryMultiply(d[0], t[1].x, t[2].x);
|
|||
|
|
|
|||
|
|
// 't3 := t3 - 2 * T[2].x;'
|
|||
|
|
modSub(temp3, t[2].x, temp3);
|
|||
|
|
modSub(temp3, t[2].x, temp3);
|
|||
|
|
|
|||
|
|
// 'd[0] := d[0] * t1;'
|
|||
|
|
montgomeryMultiply(d[0], temp1, temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
d[0][idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'T[1].x := t3 - d[0];'
|
|||
|
|
modSub(temp3, d[0], t[1].x);
|
|||
|
|
|
|||
|
|
// 't1 := T[2].x - T[1].x;'
|
|||
|
|
modSub(t[2].x, t[1].x, temp1);
|
|||
|
|
|
|||
|
|
// 't1 := t1 * t2;'
|
|||
|
|
// NOTE: Using t0 as target due to destructive multiply.
|
|||
|
|
montgomeryMultiply(temp1, temp2, temp0);
|
|||
|
|
|
|||
|
|
// 'T[2].y := T[1].y*d[0];'
|
|||
|
|
montgomeryMultiply(t[1].y, d[0], t[2].y);
|
|||
|
|
|
|||
|
|
// 'T[1].y := t1 - T[2].y;'
|
|||
|
|
// NOTE: Reusing t0 result from above.
|
|||
|
|
modSub(temp0, t[2].y, t[1].y);
|
|||
|
|
|
|||
|
|
var j, k, l;
|
|||
|
|
// INNER ITERATIONS ------------------------------------------------
|
|||
|
|
for (i = 0; i < tableSize - 3; i += 1) {
|
|||
|
|
j = i + 1;
|
|||
|
|
k = i + 2;
|
|||
|
|
l = i + 3;
|
|||
|
|
|
|||
|
|
// 't1 := T[j].x - T[k].x;'
|
|||
|
|
modSub(t[j].x, t[k].x, temp1);
|
|||
|
|
|
|||
|
|
// 't2 := T[j].y - T[k].y;'
|
|||
|
|
modSub(t[j].y, t[k].y, temp2);
|
|||
|
|
|
|||
|
|
// 'Z := Z * t1;'
|
|||
|
|
// NOTE: Using temp0 as target since multiply is destructive.
|
|||
|
|
montgomeryMultiply(z, temp1, temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
z[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'd[i] := t1^2;'
|
|||
|
|
montgomerySquare(temp1, d[i]);
|
|||
|
|
|
|||
|
|
// 't3 := t2^2;'
|
|||
|
|
montgomerySquare(temp2, temp3);
|
|||
|
|
|
|||
|
|
// 'T[l].x := d[i] * T[k].x;'
|
|||
|
|
montgomeryMultiply(d[i], t[k].x, t[l].x);
|
|||
|
|
|
|||
|
|
// 't3 := t3 - 2 * T[l].x;'
|
|||
|
|
modSub(temp3, t[l].x, temp3);
|
|||
|
|
modSub(temp3, t[l].x, temp3);
|
|||
|
|
|
|||
|
|
// 'e[i] := d[i] * t1;'
|
|||
|
|
montgomeryMultiply(d[i], temp1, e[i]);
|
|||
|
|
|
|||
|
|
// 'T[k].x := t3 - e[i];'
|
|||
|
|
modSub(temp3, e[i], t[k].x);
|
|||
|
|
|
|||
|
|
// 't1 := T[l].x - T[k].x;'
|
|||
|
|
// NOTE: Using temp0 as target so we can multiply into temp1 below.
|
|||
|
|
modSub(t[l].x, t[k].x, temp0);
|
|||
|
|
|
|||
|
|
// 't1 := t1 * t2;'
|
|||
|
|
// NOTE: Using temp0 result from above.
|
|||
|
|
montgomeryMultiply(temp0, temp2, temp1);
|
|||
|
|
|
|||
|
|
// 'T[l].y := T[k].y*e[i];'
|
|||
|
|
montgomeryMultiply(t[k].y, e[i], t[l].y);
|
|||
|
|
|
|||
|
|
// 'T[k].y := t1 - T[l].y;'
|
|||
|
|
modSub(temp1, t[l].y, t[k].y);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// FINAL ITERATION -------------------------------------------------
|
|||
|
|
// {
|
|||
|
|
i = tableSize - 3;
|
|||
|
|
j = i + 1;
|
|||
|
|
k = i + 2;
|
|||
|
|
|
|||
|
|
// 't1 := T[j].x - T[k].x;'
|
|||
|
|
modSub(t[j].x, t[k].x, temp1);
|
|||
|
|
|
|||
|
|
// 't2 := T[j].y - T[k].y;'
|
|||
|
|
modSub(t[j].y, t[k].y, temp2);
|
|||
|
|
|
|||
|
|
// 'Z := Z * t1;'
|
|||
|
|
montgomeryMultiply(z, temp1, temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
z[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'd[i] := t1^2;'
|
|||
|
|
montgomerySquare(temp1, d[i]);
|
|||
|
|
|
|||
|
|
// 't3 := t2^2;'
|
|||
|
|
montgomerySquare(temp2, temp3);
|
|||
|
|
|
|||
|
|
// 'e[i] := d[i] * t1;'
|
|||
|
|
montgomeryMultiply(d[i], temp1, e[i]);
|
|||
|
|
|
|||
|
|
// 't1 := d[i] * T[k].x;'
|
|||
|
|
montgomeryMultiply(d[i], t[k].x, temp1);
|
|||
|
|
|
|||
|
|
// 't3 := t3 - 2 * t1;'
|
|||
|
|
modSub(temp3, temp1, temp3);
|
|||
|
|
modSub(temp3, temp1, temp3);
|
|||
|
|
|
|||
|
|
// 'T[k].x := t3 - e[i];'
|
|||
|
|
modSub(temp3, e[i], t[k].x);
|
|||
|
|
|
|||
|
|
// 't1 := t1 - T[k].x;'
|
|||
|
|
// NOTE: Using temp0 as target so we can multiply into temp1 below.
|
|||
|
|
modSub(temp1, t[k].x, temp0);
|
|||
|
|
|
|||
|
|
// 't1 := t1 * t2;'
|
|||
|
|
// NOTE: Reusing temp0 to multiply into temp1.
|
|||
|
|
montgomeryMultiply(temp0, temp2, temp1);
|
|||
|
|
|
|||
|
|
// 'T[k].y := T[k].y*e[i];'
|
|||
|
|
// NOTE: Using temp3 as target due to destructive multiply.
|
|||
|
|
montgomeryMultiply(t[k].y, e[i], temp3);
|
|||
|
|
|
|||
|
|
// 'T[k].y := t1 - T[k].y;'
|
|||
|
|
// NOTE: Using temp3 instead of T[k].y.
|
|||
|
|
modSub(temp1, temp3, t[k].y);
|
|||
|
|
|
|||
|
|
// POST ITERATIONS - INVERT Z AND PREPARE TO RECOVER TABLE ENTRIES ---------------
|
|||
|
|
|
|||
|
|
// 'Z := 1/Z;'
|
|||
|
|
// NOTE: Z is in montgomery form at this point, i.e. Z*R. After
|
|||
|
|
// inversion we will have 1/(Z*R) but we want (1/Z)*R (the
|
|||
|
|
// montgomery form of Z inverse) so we use the inversion
|
|||
|
|
// correction, which does a montgomery multiplication by R^3
|
|||
|
|
// yielding the correct result.
|
|||
|
|
modInv(z, z);
|
|||
|
|
correctInversion(z);
|
|||
|
|
|
|||
|
|
// 't1 := Z^2;'
|
|||
|
|
montgomerySquare(z, temp1);
|
|||
|
|
|
|||
|
|
// 't2 := t1 * Z;'
|
|||
|
|
montgomeryMultiply(temp1, z, temp2);
|
|||
|
|
|
|||
|
|
// 'T[k].x := T[k].x * t1;'
|
|||
|
|
montgomeryMultiply(t[k].x, temp1, temp0);
|
|||
|
|
|
|||
|
|
// Copy temp0 to T[k].x.
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
t[k].x[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'T[k].y := T[k].y * t2;'
|
|||
|
|
montgomeryMultiply(t[k].y, temp2, temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
t[k].y[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// RECOVER POINTS FROM TABLE ---------------------------------------
|
|||
|
|
|
|||
|
|
// For i in [(2^(w-2)-2)..1 by -1] do
|
|||
|
|
for (i = tableSize - 3; i >= 0; i--) {
|
|||
|
|
// 'j := i + 1;'
|
|||
|
|
j = i + 1;
|
|||
|
|
|
|||
|
|
// 't1 := t1 * d[i];'
|
|||
|
|
montgomeryMultiply(temp1, d[i], temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
temp1[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 't2 := t2 * e[i];'
|
|||
|
|
montgomeryMultiply(temp2, e[i], temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
temp2[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'T[j].x := T[j].x * t1;'
|
|||
|
|
montgomeryMultiply(t[j].x, temp1, temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
t[j].x[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'T[j].y := T[j].y * t2;'
|
|||
|
|
montgomeryMultiply(t[j].y, temp2, temp0);
|
|||
|
|
|
|||
|
|
for (idx = 0; idx < s; idx++) {
|
|||
|
|
t[j].y[idx] = temp0[idx];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// End for;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Points are now in affine form, set Z coord to null (== 1).
|
|||
|
|
for (i = 0; i < t.length; i += 1) {
|
|||
|
|
t[i].z = null;
|
|||
|
|
setterSupport || (t[i].isAffine = true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (i = 0; i < t.length; i += 1) {
|
|||
|
|
|
|||
|
|
if (!t[i].isAffine) {
|
|||
|
|
throw new Error("Non-affine povar found in precomputation table");
|
|||
|
|
}
|
|||
|
|
if (!t[i].isInMontgomeryForm) {
|
|||
|
|
convertToMontgomeryForm(t[i]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return t;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function convertToMontgomeryForm(point) {
|
|||
|
|
/// <param name="point" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
if (point.isInMontgomeryForm) {
|
|||
|
|
throw new Error("The given point is already in montgomery form.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!point.isInfinity) {
|
|||
|
|
montgomeryMultiplier.convertToMontgomeryForm(point.x);
|
|||
|
|
montgomeryMultiplier.convertToMontgomeryForm(point.y);
|
|||
|
|
|
|||
|
|
if (point.z !== null) {
|
|||
|
|
montgomeryMultiplier.convertToMontgomeryForm(point.z);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
point.isInMontgomeryForm = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
|
|||
|
|
double: function (point, outputPoint) {
|
|||
|
|
/// <param name="point" type="EllipticCurvePointFp"/>
|
|||
|
|
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
if (typeof point === "undefined") {
|
|||
|
|
throw new Error("point undefined");
|
|||
|
|
}
|
|||
|
|
if (typeof outputPoint === "undefined") {
|
|||
|
|
throw new Error("outputPoint undefined");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//// if (!point.curve.equals(outputPoint.curve)) {
|
|||
|
|
//// throw new Error("point and outputPoint must be from the same curve object.");
|
|||
|
|
//// }
|
|||
|
|
|
|||
|
|
if (point.isAffine) {
|
|||
|
|
throw new Error("Given point was in Affine form. Use convertToJacobian() first.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!point.isInMontgomeryForm) {
|
|||
|
|
throw new Error("Given point must be in montgomery form. Use montgomeryize() first.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (outputPoint.isAffine) {
|
|||
|
|
throw new Error("Given output point was in Affine form. Use convertToJacobian() first.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Currently we support only two curve types, those with A=-3, and
|
|||
|
|
// those with A=0. In the future we will implement general support.
|
|||
|
|
// For now we switch here, assuming that the curve was validated in
|
|||
|
|
// the constructor.
|
|||
|
|
if (aequalsZero) {
|
|||
|
|
doubleAequals0(point, outputPoint);
|
|||
|
|
} else {
|
|||
|
|
doubleAequalsNeg3(point, outputPoint);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
mixedDoubleAdd: function (jacobianPoint, affinePoint, outputPoint) {
|
|||
|
|
/// <param name="jacobianPoint" type="EllipticCurvePointFp"/>
|
|||
|
|
/// <param name="affinePoint" type="EllipticCurvePointFp"/>
|
|||
|
|
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
if (jacobianPoint.isInfinity) {
|
|||
|
|
affinePoint.copy(outputPoint);
|
|||
|
|
this.convertToJacobianForm(outputPoint);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (affinePoint.isInfinity) {
|
|||
|
|
jacobianPoint.copy(outputPoint);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Ok then we do the full double and add.
|
|||
|
|
|
|||
|
|
// Note: in pseudo-code the capital X,Y,Z is Jacobian point, lower
|
|||
|
|
// case x, y, z is Affine point.
|
|||
|
|
|
|||
|
|
// 't5:=Z1^ 2;'
|
|||
|
|
montgomerySquare(jacobianPoint.z, temp5);
|
|||
|
|
|
|||
|
|
// 't6:=Z1*t5;'
|
|||
|
|
montgomeryMultiply(jacobianPoint.z, temp5, temp6);
|
|||
|
|
|
|||
|
|
// 't4:=x2*t5;'
|
|||
|
|
montgomeryMultiply(affinePoint.x, temp5, temp4);
|
|||
|
|
|
|||
|
|
// 't5:=y2*t6;'
|
|||
|
|
montgomeryMultiply(affinePoint.y, temp6, temp5);
|
|||
|
|
|
|||
|
|
// 't1:=t4-X1;'
|
|||
|
|
modSub(temp4, jacobianPoint.x, temp1);
|
|||
|
|
|
|||
|
|
// 't2:=t5-Y1;'
|
|||
|
|
modSub(temp5, jacobianPoint.y, temp2);
|
|||
|
|
|
|||
|
|
// 't4:=t2^2;'
|
|||
|
|
montgomerySquare(temp2, temp4);
|
|||
|
|
|
|||
|
|
// 't6:=t1^2;'
|
|||
|
|
montgomerySquare(temp1, temp6);
|
|||
|
|
|
|||
|
|
// 't5:=t6*X1;'
|
|||
|
|
montgomeryMultiply(temp6, jacobianPoint.x, temp5);
|
|||
|
|
|
|||
|
|
// 't0:=t1*t6;'
|
|||
|
|
montgomeryMultiply(temp1, temp6, temp0);
|
|||
|
|
|
|||
|
|
// 't3:=t4-2*t5;'
|
|||
|
|
modSub(temp4, temp5, temp3);
|
|||
|
|
modSub(temp3, temp5, temp3);
|
|||
|
|
|
|||
|
|
// 't4:=Z1*t1;'
|
|||
|
|
montgomeryMultiply(jacobianPoint.z, temp1, temp4);
|
|||
|
|
|
|||
|
|
// 't3:=t3-t5;'
|
|||
|
|
modSub(temp3, temp5, temp3);
|
|||
|
|
|
|||
|
|
// 't6:=t0*Y1;'
|
|||
|
|
montgomeryMultiply(temp0, jacobianPoint.y, temp6);
|
|||
|
|
|
|||
|
|
// 't3:=t3-t0;'
|
|||
|
|
modSub(temp3, temp0, temp3);
|
|||
|
|
|
|||
|
|
// 't1:=2*t6;'
|
|||
|
|
modAdd(temp6, temp6, temp1);
|
|||
|
|
|
|||
|
|
// 'Zout:=t4*t3;'
|
|||
|
|
montgomeryMultiply(temp4, temp3, outputPoint.z);
|
|||
|
|
|
|||
|
|
// 't4:=t2*t3;'
|
|||
|
|
montgomeryMultiply(temp2, temp3, temp4);
|
|||
|
|
|
|||
|
|
// 't0:=t3^2;'
|
|||
|
|
montgomerySquare(temp3, temp0);
|
|||
|
|
|
|||
|
|
// 't1:=t1+t4;'
|
|||
|
|
modAdd(temp1, temp4, temp1);
|
|||
|
|
|
|||
|
|
// 't4:=t0*t5;'
|
|||
|
|
montgomeryMultiply(temp0, temp5, temp4);
|
|||
|
|
|
|||
|
|
// 't7:=t1^2;'
|
|||
|
|
montgomerySquare(temp1, temp7);
|
|||
|
|
|
|||
|
|
// 't4:=t0*t5;'
|
|||
|
|
montgomeryMultiply(temp0, temp3, temp5);
|
|||
|
|
|
|||
|
|
// 'Xout:=t7-2*t4;'
|
|||
|
|
modSub(temp7, temp4, outputPoint.x);
|
|||
|
|
modSub(outputPoint.x, temp4, outputPoint.x);
|
|||
|
|
|
|||
|
|
// 'Xout:=Xout-t5;'
|
|||
|
|
modSub(outputPoint.x, temp5, outputPoint.x);
|
|||
|
|
|
|||
|
|
// 't3:=Xout-t4;'
|
|||
|
|
modSub(outputPoint.x, temp4, temp3);
|
|||
|
|
|
|||
|
|
// 't0:=t5*t6;'
|
|||
|
|
montgomeryMultiply(temp5, temp6, temp0);
|
|||
|
|
|
|||
|
|
// 't4:=t1*t3;'
|
|||
|
|
montgomeryMultiply(temp1, temp3, temp4);
|
|||
|
|
|
|||
|
|
// 'Yout:=t4-t0;'
|
|||
|
|
modSub(temp4, temp0, outputPoint.y);
|
|||
|
|
|
|||
|
|
outputPoint.isInfinity = false;
|
|||
|
|
outputPoint.isInMontgomeryForm = true;
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
mixedAdd: function (jacobianPoint, affinePoint, outputPoint) {
|
|||
|
|
/// <param name="jacobianPoint" type="EllipticCurvePointFp"/>
|
|||
|
|
/// <param name="affinePoint" type="EllipticCurvePointFp"/>
|
|||
|
|
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
if (jacobianPoint === null) {
|
|||
|
|
throw new Error("jacobianPoint");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (affinePoint === null) {
|
|||
|
|
throw new Error("affinePoint");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (outputPoint === null) {
|
|||
|
|
throw new Error("outputPoint");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (jacobianPoint.curve !== affinePoint.curve ||
|
|||
|
|
jacobianPoint.curve !== outputPoint.curve) {
|
|||
|
|
throw new Error("All points must be from the same curve object.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (jacobianPoint.isAffine) {
|
|||
|
|
throw new Error(
|
|||
|
|
"Given jacobianPoint was in Affine form. Use ConvertToJacobian() before calling DoubleJacobianAddAffinePoints().");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!affinePoint.isAffine) {
|
|||
|
|
throw new Error(
|
|||
|
|
"Given affinePoint was in Jacobian form. Use ConvertToAffine() before calling DoubleJacobianAddAffinePoints().");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (outputPoint.isAffine) {
|
|||
|
|
throw new Error(
|
|||
|
|
"Given jacobianPoint was in Jacobian form. Use ConvertToJacobian() before calling DoubleJacobianAddAffinePoints().");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!jacobianPoint.isInMontgomeryForm) {
|
|||
|
|
throw new Error("Jacobian point must be in Montgomery form");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!affinePoint.isInMontgomeryForm) {
|
|||
|
|
throw new Error("Affine point must be in Montgomery form");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (jacobianPoint.isInfinity) {
|
|||
|
|
affinePoint.copy(outputPoint);
|
|||
|
|
this.convertToJacobianForm(outputPoint);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (affinePoint.isInfinity) {
|
|||
|
|
jacobianPoint.copy(outputPoint);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Ok then we do the full double and add.
|
|||
|
|
|
|||
|
|
// Note: in pseudo-code the capital X1,Y1,Z1 is Jacobian point,
|
|||
|
|
// lower case x2, y2, z2 is Affine point.
|
|||
|
|
// 't1 := Z1^2;'.
|
|||
|
|
montgomerySquare(jacobianPoint.z, temp1);
|
|||
|
|
|
|||
|
|
// 't2 := t1 * Z1;'
|
|||
|
|
montgomeryMultiply(temp1, jacobianPoint.z, temp2);
|
|||
|
|
|
|||
|
|
// 't3 := t1 * x2;'
|
|||
|
|
montgomeryMultiply(temp1, affinePoint.x, temp3);
|
|||
|
|
|
|||
|
|
// 't4 := t2 * y2;'
|
|||
|
|
montgomeryMultiply(temp2, affinePoint.y, temp4);
|
|||
|
|
|
|||
|
|
// 't1 := t3 - X1;'
|
|||
|
|
modSub(temp3, jacobianPoint.x, temp1);
|
|||
|
|
|
|||
|
|
// 't2 := t4 - Y1;'
|
|||
|
|
modSub(temp4, jacobianPoint.y, temp2);
|
|||
|
|
|
|||
|
|
// If t1 != 0 then
|
|||
|
|
var i;
|
|||
|
|
for (i = 0; i < temp1.length; i += 1) {
|
|||
|
|
if (temp1[i] !== 0) {
|
|||
|
|
|
|||
|
|
// 'Zout := Z1 * t1;'
|
|||
|
|
montgomeryMultiply(jacobianPoint.z, temp1, temp0);
|
|||
|
|
for (var j = 0; j < fieldElementWidth; j += 1) {
|
|||
|
|
outputPoint.z[j] = temp0[j];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 't3 := t1^2;'
|
|||
|
|
montgomerySquare(temp1, temp3);
|
|||
|
|
|
|||
|
|
// 't4 := t3 * t1;'
|
|||
|
|
montgomeryMultiply(temp3, temp1, temp4);
|
|||
|
|
|
|||
|
|
// 't5 := t3 * X1;'
|
|||
|
|
montgomeryMultiply(temp3, jacobianPoint.x, temp5);
|
|||
|
|
|
|||
|
|
// 't1 := 2 * t5;'
|
|||
|
|
modAdd(temp5, temp5, temp1);
|
|||
|
|
|
|||
|
|
// 'Xout := t2^2;'
|
|||
|
|
montgomerySquare(temp2, outputPoint.x);
|
|||
|
|
|
|||
|
|
// 'Xout := Xout - t1;'
|
|||
|
|
modSub(outputPoint.x, temp1, outputPoint.x);
|
|||
|
|
|
|||
|
|
// 'Xout := Xout - t4;'
|
|||
|
|
modSub(outputPoint.x, temp4, outputPoint.x);
|
|||
|
|
|
|||
|
|
// 't3 := t5 - Xout;'
|
|||
|
|
modSub(temp5, outputPoint.x, temp3);
|
|||
|
|
|
|||
|
|
// 't5 := t3*t2;'
|
|||
|
|
montgomeryMultiply(temp2, temp3, temp5);
|
|||
|
|
|
|||
|
|
// 't6 := t4*Y1;'
|
|||
|
|
montgomeryMultiply(jacobianPoint.y, temp4, temp6);
|
|||
|
|
|
|||
|
|
// 'Yout := t5-t6;'
|
|||
|
|
modSub(temp5, temp6, outputPoint.y);
|
|||
|
|
|
|||
|
|
outputPoint.isInfinity = false;
|
|||
|
|
outputPoint.isInMontgomeryForm = true;
|
|||
|
|
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Else if T2 != 0 then
|
|||
|
|
for (i = 0; i < temp2.length; i += 1) {
|
|||
|
|
if (temp2[i] !== 0) {
|
|||
|
|
// Return infinity
|
|||
|
|
outputPoint.isInfinity = true;
|
|||
|
|
outputPoint.isInMontgomeryForm = true;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Else use DBL routine to return 2(x2, y2, 1)
|
|||
|
|
affinePoint.copy(outputPoint);
|
|||
|
|
this.convertToJacobianForm(outputPoint);
|
|||
|
|
this.double(outputPoint, outputPoint);
|
|||
|
|
outputPoint.isInMontgomeryForm = true;
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
scalarMultiply: function (k, point, outputPoint) {
|
|||
|
|
/// <param name="k" type="Digits"/>
|
|||
|
|
/// <param name="point" type="EllipticCurvePointFp"/>
|
|||
|
|
/// <param name="outputPoint" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
// Special case for the point at infinity or k == 0
|
|||
|
|
if (point.isInfinity || cryptoMath.isZero(k)) {
|
|||
|
|
outputPoint.isInfinity = true;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Runtime check for 1 <= k < order to ensure we dont get hit by
|
|||
|
|
// subgroup attacks. Since k is a FixedWidth it is a positive integer
|
|||
|
|
// and we already checked for zero above. So it must be >= 1 already.
|
|||
|
|
if (cryptoMath.compareDigits(k, curve.order) >= 0) {
|
|||
|
|
throw new Error("The scalar k must be in the range 1 <= k < order.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var digit;
|
|||
|
|
|
|||
|
|
// Change w based on the size of the digits,
|
|||
|
|
// 5 is good for 256 bits, use 6 for bigger sizes.
|
|||
|
|
var w = (fieldElementWidth <= 8) ? 5 : 6;
|
|||
|
|
// Generate wNAF representation.
|
|||
|
|
// Using an Array because we want to allow negative numbers.
|
|||
|
|
var nafDigits = msrcryptoUtilities.getVector(k.length * cryptoMath.DIGIT_BITS + 1);
|
|||
|
|
var numNafDigits = cryptoMath.computeNAF(k, w, temp0, nafDigits);
|
|||
|
|
|
|||
|
|
// Generate pre-computation table.
|
|||
|
|
var table = generatePrecomputationTable(w, point);
|
|||
|
|
|
|||
|
|
// Setup output point as Infinity, Jacobian, montgomery.
|
|||
|
|
outputPoint.isInfinity = true;
|
|||
|
|
|
|||
|
|
// Main algorithm.
|
|||
|
|
for (var i1 = numNafDigits - 1; i1 >= 0; i1--) {
|
|||
|
|
if (nafDigits[i1] === 0) {
|
|||
|
|
this.double(outputPoint, outputPoint);
|
|||
|
|
} else if (nafDigits[i1] < 0) {
|
|||
|
|
digit = (-nafDigits[i1] >> 1);
|
|||
|
|
|
|||
|
|
// Negate Y coord before doing the add.
|
|||
|
|
this.negate(table[digit], table[digit]);
|
|||
|
|
this.mixedDoubleAdd(outputPoint, table[digit], outputPoint);
|
|||
|
|
this.negate(table[digit], table[digit]);
|
|||
|
|
} else if (nafDigits[i1] > 0) {
|
|||
|
|
digit = (nafDigits[i1] >> 1);
|
|||
|
|
this.mixedDoubleAdd(outputPoint, table[digit], outputPoint);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
negate: function (point, outputPoint) {
|
|||
|
|
/// <param name="point" type="EllipticCurvePointFp">Input point to negate.</param>
|
|||
|
|
/// <param name="outputPoint" type="EllipticCurvePointFp">(x, p - y).</param>
|
|||
|
|
|
|||
|
|
if (point !== outputPoint) {
|
|||
|
|
point.copy(outputPoint);
|
|||
|
|
}
|
|||
|
|
cryptoMath.subtract(point.curve.p, point.y, outputPoint.y);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
convertToMontgomeryForm: function (point) {
|
|||
|
|
/// <param name="point" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
if (point.isInMontgomeryForm) {
|
|||
|
|
throw new Error("The given point is already in montgomery form.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!point.isInfinity) {
|
|||
|
|
montgomeryMultiplier.convertToMontgomeryForm(point.x);
|
|||
|
|
montgomeryMultiplier.convertToMontgomeryForm(point.y);
|
|||
|
|
|
|||
|
|
if (point.z !== null) {
|
|||
|
|
montgomeryMultiplier.convertToMontgomeryForm(point.z);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
point.isInMontgomeryForm = true;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
convertToStandardForm: function (point) {
|
|||
|
|
/// <param name="point" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
if (!point.isInMontgomeryForm) {
|
|||
|
|
throw new Error("The given point is not in montgomery form.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!point.isInfinity) {
|
|||
|
|
montgomeryMultiplier.convertToStandardForm(point.x);
|
|||
|
|
montgomeryMultiplier.convertToStandardForm(point.y);
|
|||
|
|
if (point.z !== null) {
|
|||
|
|
montgomeryMultiplier.convertToStandardForm(point.z);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
point.isInMontgomeryForm = false;
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
convertToAffineForm: function (point) {
|
|||
|
|
/// <param name="point" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
if (point.isInfinity) {
|
|||
|
|
point.z = null;
|
|||
|
|
setterSupport || (point.isAffine = true);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// DETERMINE 1/Z IN MONTGOMERY FORM --------------------------------
|
|||
|
|
|
|||
|
|
// Call out to the basic inversion function, not the one in this class.
|
|||
|
|
cryptoMath.modInv(point.z, curve.p, conversionTemp2);
|
|||
|
|
|
|||
|
|
if (point.isInMontgomeryForm) {
|
|||
|
|
montgomeryMultiply(conversionTemp2, montgomeryMultiplier.rCubedModm, conversionTemp1);
|
|||
|
|
var swap = conversionTemp2;
|
|||
|
|
conversionTemp2 = conversionTemp1;
|
|||
|
|
conversionTemp1 = swap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CONVERT TO AFFINE COORDS ----------------------------------------
|
|||
|
|
|
|||
|
|
// 'temp0 <- 1/z^2'
|
|||
|
|
montgomerySquare(conversionTemp2, conversionTemp0);
|
|||
|
|
|
|||
|
|
// Compute point.x = x / z^2 mod p
|
|||
|
|
// NOTE: We cannot output directly to the X digit array since it is
|
|||
|
|
// used for input to the multiplication routine, so we output to temp1
|
|||
|
|
// and copy.
|
|||
|
|
montgomeryMultiply(point.x, conversionTemp0, conversionTemp1);
|
|||
|
|
for (var i = 0; i < fieldElementWidth; i += 1) {
|
|||
|
|
point.x[i] = conversionTemp1[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Compute point.y = y / z^3 mod p
|
|||
|
|
// temp1 <- y * 1/z^2.
|
|||
|
|
montgomeryMultiply(point.y, conversionTemp0, conversionTemp1);
|
|||
|
|
// 'y <- temp1 * temp2 (which == 1/z)'
|
|||
|
|
montgomeryMultiply(conversionTemp1, conversionTemp2, point.y);
|
|||
|
|
|
|||
|
|
// Finally, point.z = z / z mod p = 1
|
|||
|
|
// We use z = NULL for this case to make detecting Jacobian form
|
|||
|
|
// faster (otherwise we would have to scan the entire Z digit array).
|
|||
|
|
point.z = null;
|
|||
|
|
setterSupport || (point.isAffine = true);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
convertToJacobianForm: function (point) {
|
|||
|
|
/// <param name="point" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
if (!point.isAffine) {
|
|||
|
|
throw new Error("The given point is not in Affine form.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
setterSupport || (point.isAffine = false);
|
|||
|
|
|
|||
|
|
var clonedDigits;
|
|||
|
|
var i;
|
|||
|
|
if (point.isInMontgomeryForm) {
|
|||
|
|
|
|||
|
|
// Z = 1 (montgomery form)
|
|||
|
|
clonedDigits = createArray(onemontgomery.length);
|
|||
|
|
for (i = 0; i < onemontgomery.length; i += 1) {
|
|||
|
|
clonedDigits[i] = onemontgomery[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
point.z = clonedDigits;
|
|||
|
|
} else {
|
|||
|
|
|
|||
|
|
// Z = 1 (standard form)
|
|||
|
|
clonedDigits = createArray(one.length);
|
|||
|
|
for (i = 0; i < one.length; i += 1) {
|
|||
|
|
clonedDigits[i] = one[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
point.z = clonedDigits;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// For tests
|
|||
|
|
generatePrecomputationTable: function (w, generatorPoint) {
|
|||
|
|
/// <param name="w" type="Number"/>
|
|||
|
|
/// <param name="generatorPoint" type="EllipticCurvePointFp"/>
|
|||
|
|
|
|||
|
|
return generatePrecomputationTable(w, generatorPoint);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
var sec1EncodingFp = function () {
|
|||
|
|
return {
|
|||
|
|
encodePoint: function (/*@type(EllipticCurvePointFp)*/ point) {
|
|||
|
|
/// <summary>Encode an EC point without compression.
|
|||
|
|
/// This function encodes a given points into a bytes array containing 0x04 | X | Y, where X and Y are big endian bytes of x and y coordinates.</summary>
|
|||
|
|
/// <param name="point" type="EllipticCurvePointFp">Input EC point to encode.</param>
|
|||
|
|
/// <returns type="Array">A bytes array containing 0x04 | X | Y, where X and Y are big endian encoded x and y coordinates.</returns>
|
|||
|
|
|
|||
|
|
if (!point) {
|
|||
|
|
throw new Error("point");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!point.isAffine) {
|
|||
|
|
throw new Error("Point must be in affine form.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (point.isInMontgomeryForm) {
|
|||
|
|
throw new Error("Point must not be in Montgomery form.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (point.isInfinity) {
|
|||
|
|
return createArray(1); /* [0] */
|
|||
|
|
} else {
|
|||
|
|
var xOctetString = cryptoMath.digitsToBytes(point.x);
|
|||
|
|
var yOctetString = cryptoMath.digitsToBytes(point.y);
|
|||
|
|
var pOctetString = cryptoMath.digitsToBytes(point.curve.p); // just to get byte length of p
|
|||
|
|
var mlen = pOctetString.length;
|
|||
|
|
if (mlen < xOctetString.length || mlen < yOctetString.length) {
|
|||
|
|
throw new Error("Point coordinate(s) are bigger than the field order.");
|
|||
|
|
}
|
|||
|
|
var output = createArray(2 * mlen + 1); // for encoded x and y
|
|||
|
|
|
|||
|
|
output[0] = 0x04;
|
|||
|
|
var offset = mlen - xOctetString.length;
|
|||
|
|
for (var i = 0; i < xOctetString.length; i++) {
|
|||
|
|
output[i + 1 + offset] = xOctetString[i];
|
|||
|
|
}
|
|||
|
|
offset = mlen - yOctetString.length;
|
|||
|
|
for (i = 0; i < yOctetString.length; i++) {
|
|||
|
|
output[mlen + i + 1 + offset] = yOctetString[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return output;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
decodePoint: function (encoded, curve) {
|
|||
|
|
/// <param name="encoded" type="Digits"/>
|
|||
|
|
/// <param name="curve" type="EllipticCurveFp"/>
|
|||
|
|
|
|||
|
|
if (encoded.length < 1) {
|
|||
|
|
throw new Error("Byte array must have non-zero length");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var pOctetString = cryptoMath.digitsToBytes(curve.p);
|
|||
|
|
var mlen = pOctetString.length;
|
|||
|
|
|
|||
|
|
if (encoded[0] === 0x0 && encoded.length === 1) {
|
|||
|
|
return curve.createPointAtInfinity();
|
|||
|
|
} else if (encoded[0] === 0x04 && encoded.length === 1 + 2 * mlen) {
|
|||
|
|
// Standard encoding.
|
|||
|
|
// Each point is a big endian string of bytes of length.
|
|||
|
|
// 'ceiling(log_2(Q)/8)'
|
|||
|
|
// Zero-padded and representing the magnitude of the coordinate.
|
|||
|
|
var xbytes = createArray(mlen);
|
|||
|
|
var ybytes = createArray(mlen);
|
|||
|
|
|
|||
|
|
for (var i = 0; i < mlen; i++) {
|
|||
|
|
xbytes[i] = encoded[i + 1];
|
|||
|
|
ybytes[i] = encoded[mlen + i + 1];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var x = cryptoMath.bytesToDigits(xbytes);
|
|||
|
|
var y = cryptoMath.bytesToDigits(ybytes);
|
|||
|
|
|
|||
|
|
return EllipticCurvePointFp(curve, false, x, y);
|
|||
|
|
} else {
|
|||
|
|
// We don't support other encoding features such as compression
|
|||
|
|
throw new Error("Unsupported encoding format");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
var ModularSquareRootSolver = function (modulus) {
|
|||
|
|
/// <param name="modulus" type="Digits"/>
|
|||
|
|
|
|||
|
|
// The modulus we are going to use.
|
|||
|
|
var p = modulus;
|
|||
|
|
|
|||
|
|
// Special-K not just for breakfast anymore! This is k = (p-3)/4 + 1
|
|||
|
|
// which is used for NIST curves (or any curve of with P= 3 mod 4).
|
|||
|
|
// This field is null if p is not of the special form, or k if it is.
|
|||
|
|
var specialK = [];
|
|||
|
|
|
|||
|
|
if (typeof modulus === "undefined") {
|
|||
|
|
throw new Error("modulus");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Support for odd moduli, only.
|
|||
|
|
if (cryptoMath.isEven(modulus)) {
|
|||
|
|
throw new Error("Only odd moduli are supported");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// A montgomery multiplier object for doing fast squaring.
|
|||
|
|
var mul = cryptoMath.MontgomeryMultiplier(p);
|
|||
|
|
|
|||
|
|
// 'p === 3 mod 4' then we can use the special super fast version.
|
|||
|
|
// Otherwise we must use the slower general case algorithm.
|
|||
|
|
if (p[0] % 4 === 3) {
|
|||
|
|
// 'special k = (p + 1) / 4'
|
|||
|
|
cryptoMath.add(p, cryptoMath.One, specialK);
|
|||
|
|
cryptoMath.shiftRight(specialK, specialK, 2);
|
|||
|
|
} else {
|
|||
|
|
specialK = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Temp storage
|
|||
|
|
var temp0 = new Array(p.length);
|
|||
|
|
var temp1 = new Array(p.length);
|
|||
|
|
|
|||
|
|
function squareRootNistCurves(a) {
|
|||
|
|
/// <summary>Given a number a, returns a solution x to x^2 = a (mod p).</summary>
|
|||
|
|
/// <param name="a" type="Array">An integer a.</param>
|
|||
|
|
/// <returns type="Array">The square root of the number a modulo p, if it exists,
|
|||
|
|
/// otherwise null.</returns>
|
|||
|
|
|
|||
|
|
// beta = a^k mod n where k=(n+1)/4 for n == 3 mod 4, thus a^(1/2) mod n
|
|||
|
|
var beta = cryptoMath.intToDigits(0, 16);
|
|||
|
|
mul.modExp(a, specialK, beta);
|
|||
|
|
|
|||
|
|
// Okay now we gotta double check by squaring.
|
|||
|
|
var aPrime = [0];
|
|||
|
|
cryptoMath.modMul(beta, beta, mul.m, aPrime);
|
|||
|
|
|
|||
|
|
// If a != x^2 then a has no square root
|
|||
|
|
if (cryptoMath.compareDigits(a, aPrime) !== 0) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return beta;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var publicMethods = {
|
|||
|
|
|
|||
|
|
squareRoot: function (a) {
|
|||
|
|
if (specialK !== null) {
|
|||
|
|
// Use the special case fast code
|
|||
|
|
return squareRootNistCurves(a);
|
|||
|
|
} else {
|
|||
|
|
// Use the general case code
|
|||
|
|
throw new Error("GeneralCase not supported.");
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// Given an integer a, this routine returns the Jacobi symbol (a/p),
|
|||
|
|
// where p is the modulus given in the constructor, which for p an
|
|||
|
|
// odd prime is also the Legendre symbol. From "Prime Numbers, A
|
|||
|
|
// Computational Perspective" by Crandall and Pomerance, alg. 2.3.5.
|
|||
|
|
// The Legendre symbol is defined as:
|
|||
|
|
// 0 if a === 0 mod p.
|
|||
|
|
// 1 if a is a quadratic residue (mod p).
|
|||
|
|
// -1 if a is a quadratic non-reside (mod p).
|
|||
|
|
jacobiSymbol: function (a) {
|
|||
|
|
/// <param name="a">An integer a.</param>
|
|||
|
|
|
|||
|
|
var modEightMask = 0x7,
|
|||
|
|
modFourMask = 0x3,
|
|||
|
|
aPrime,
|
|||
|
|
pPrime;
|
|||
|
|
|
|||
|
|
// Clone our inputs, we are going to destroy them
|
|||
|
|
aPrime = a.slice();
|
|||
|
|
pPrime = p.slice();
|
|||
|
|
|
|||
|
|
// 'a = a mod p'.
|
|||
|
|
cryptoMath.reduce(aPrime, pPrime, aPrime, temp0, temp1);
|
|||
|
|
|
|||
|
|
// 't = 1'
|
|||
|
|
var t = 1;
|
|||
|
|
|
|||
|
|
// While (a != 0)
|
|||
|
|
while (!cryptoMath.isZero(aPrime)) {
|
|||
|
|
// While a is even
|
|||
|
|
while (cryptoMath.isEven(aPrime)) {
|
|||
|
|
// 'a <- a / 2'
|
|||
|
|
cryptoMath.shiftRight(aPrime, aPrime);
|
|||
|
|
|
|||
|
|
// If (p mod 8 in {3,5}) t = -t;
|
|||
|
|
var pMod8 = (pPrime[0] & modEightMask);
|
|||
|
|
if (pMod8 === 3 || pMod8 === 5) {
|
|||
|
|
t = -t;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Swap variables
|
|||
|
|
// (a, p) = (p, a).
|
|||
|
|
var tmp = aPrime;
|
|||
|
|
aPrime = pPrime;
|
|||
|
|
pPrime = tmp;
|
|||
|
|
|
|||
|
|
// If (a === p === 3 (mod 4)) t = -t;
|
|||
|
|
var aMod4 = (aPrime[0] & modFourMask);
|
|||
|
|
var pMod4 = (pPrime[0] & modFourMask);
|
|||
|
|
if (aMod4 === 3 && pMod4 === 3) {
|
|||
|
|
t = -t;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 'a = a mod p'
|
|||
|
|
cryptoMath.reduce(aPrime, pPrime, aPrime, temp0, temp1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// If (p == 1) return t else return 0
|
|||
|
|
if (cryptoMath.compareDigits(pPrime, cryptoMath.One) === 0) {
|
|||
|
|
return t;
|
|||
|
|
} else {
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return publicMethods;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
createP256: createP256,
|
|||
|
|
createP384: createP384,
|
|||
|
|
createBN254: createBN254,
|
|||
|
|
createANeg3Curve: createANeg3Curve,
|
|||
|
|
sec1EncodingFp: sec1EncodingFp,
|
|||
|
|
EllipticCurvePointFp: EllipticCurvePointFp,
|
|||
|
|
EllipticCurveOperatorFp: EllipticCurveOperatorFp,
|
|||
|
|
ModularSquareRootSolver: ModularSquareRootSolver
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var cryptoECC = cryptoECC || MsrcryptoECC();
|