//https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates#Mixed_Addition_(with_affine_point)_(8M_+_3S) //U1 = X1 [*Z2^2] //U2 = X2*Z1^2 //S1 = Y1 [*Z2^3] //S2 = Y2*Z1^3 //if (U1 == U2) // if (S1 != S2) // return POINT_AT_INFINITY // else // return POINT_DOUBLE(X1, Y1, Z1) //H = U2 - U1 //R = S2 - S1 //X3 = R^2 - H^3 - 2*U1*H^2 //Y3 = R*(U1*H^2 - X3) - S1*H^3 //Z3 = H*Z1*Z2 //return (X3, Y3, Z3) //http://www.hyperelliptic.org/EFD/oldefd/jacobian.html#mADD //Z2 = [p.Z]^2 //S1 = [q.X] * Z2 - [p.X] //R1 = [+/-q.Y] * [p.Z] * Z2 - [p.Y] //S2 = S1^2 //Z3 = ([p.Z]+S1)^2 - S2 - Z2 //S2 *= 4 //S1 *= S2 //R1 = 2*R1 //Y3 = [p.X]*S2 //X3 = R1^2-S1-2*Y3 //Y3 = R1*(Y3-X3)-2*[p.y]*S1 public void Add(ref JacobianECPoint p, OwnECPoint q, bool subtract = false) { //Mixed addition returns p + q in "7M + 4S" if (p.IsInfinity || q.IsInfinity) { if (!q.IsInfinity) { p.SetFrom(q.X, q.Y, One, q.IsInfinity); if (subtract) { SubtractModP(P, p.Y, p.Y); } } return; } Let(Z2, p.Z); SquareModP(Z2); //Z2 = [p.Z]^2 Let(S1, q.X); MultiplyModP(S1, Z2); SubModP(S1, p.X); //S1 = [q.X] * Z2 - [p.X] Let(R1, q.Y); if (subtract) { SubtractModP(P, R1, R1); } MultiplyModP(R1, p.Z); MultiplyModP(R1, Z2); SubModP(R1, p.Y); //R1 = [+/-q.Y] * [p.Z] * Z2 - [p.Y] if (IsZero(S1)) { if (IsZero(R1)) { //now: p == q GetDouble(ref p); return; } p.SetInfinity(); return; } Let(S2, S1); SquareModP(S2); //S2 = S1^2 Let(Z3, p.Z); AddModP(Z3, S1); SquareModP(Z3); SubModP(Z3, S2); SubModP(Z3, Z2); //Z3 = ([p.Z]+S1)^2 - S2 - Z2 AddScaledModP(S2, S2, 3); //S2 *= 4 MultiplyModP(S1, S2); //S1 *= S2 AddModP(R1, R1); //R1 = 2*R1 Let(y1, p.X); MultiplyModP(y1, S2); //Y3 = [p.X]*S2 Let(x1, R1); SquareModP(x1); SubModP(x1, S1); SubScaledModP(x1, y1, 2); //X3 = R1^2-S1-2*Y3 MultiplyModP(S1, p.Y); SubModP(y1, x1); MultiplyModP(y1, R1); SubScaledModP(y1, S1, 2); //Y3 = R1*(Y3-X3)-2*[p.y]*S1 p.SetFrom(x1, y1, Z3, false); }
//return: P * factor on the elliptic curves domain. public OwnECPoint Multiply(OwnECPoint p, ulong[] factor) { ulong[] exp = new ulong[factor.Length + 1]; ulong[] exp3 = new ulong[factor.Length + 1]; factor.CopyTo(exp, 0); exp3[factor.Length] = AsmX64Operations.MultiplyDigitAndAdd(exp, 3, exp3, factor.Length); for (int i = exp.Length; --i >= 0;) { exp3[i] ^= exp[i]; //exp3= (exp*3) ^ exp } int high = HighBit(exp3); if (high < 0) { return(new OwnECPoint(p.X, p.Y, true)); } JacobianECPoint x = new JacobianECPoint(p.X, p.Y, true); ulong mask = 1UL << (high & 63); for (int i = high; --i >= 0;) { GetDouble(ref x); if ((exp3[(i + 1) >> 6] & mask) != 0) { if ((exp[(i + 1) >> 6] & mask) != 0) { Subtract(ref x, p); } else { Add(ref x, p); } } mask = (mask >> 1) | (mask << 63); } OwnECPoint result = x.ToECPoint(this); return(result); }
public bool UnitTest() { OwnECPoint S = new OwnECPoint( FromHex(@"000001d5 c693f66c 08ed03ad 0f031f93 7443458f 601fd098 d3d0227b 4bf62873 af50740b 0bb84aa1 57fc847b cf8dc16a 8b2b8bfd 8e2d0a7d 39af04b0 89930ef6 dad5c1b4"), FromHex(@"00000144 b7770963 c63a3924 8865ff36 b074151e ac33549b 224af5c8 664c5401 2b818ed0 37b2b7c1 a63ac89e baa11e07 db89fcee 5b556e49 764ee3fa 66ea7ae6 1ac01823"), false); OwnECPoint T = new OwnECPoint( FromHex(@"000000f4 11f2ac2e b971a267 b80297ba 67c322db a4bb21ce c8b70073 bf88fc1c a5fde3ba 09e5df6d 39acb2c0 762c03d7 bc224a3e 197feaf7 60d63240 06fe3be9 a548c7d5"), FromHex(@"000001fd f842769c 707c93c6 30df6d02 eff399a0 6f1b36fb 9684f0b3 73ed0648 89629abb 92b1ae32 8fdb4553 42683849 43f0e922 2afe0325 9b32274d 35d1b958 4c65e305"), false); OwnECPoint ST = new OwnECPoint( FromHex(@"00000126 4ae115ba 9cbc2ee5 6e6f0059 e24b52c8 04632160 2c59a339 cfb757c8 9a59c358 a9a8e1f8 6d384b3f 3b255ea3 f73670c6 dc9f45d4 6b6a196d c37bbe0f 6b2dd9e9"), FromHex(@"00000062 a9c72b8f 9f88a271 690bfa01 7a6466c3 1b9cadc2 fc544744 aeb81707 2349cfdd c5ad0e81 b03f1897 bd9c8c6e fbdf6823 7dc3bb00 445979fb 373b20c9 a967ac55"), false); OwnECPoint STSub = new OwnECPoint( FromHex(@"00000129 2cb58b17 95ba4770 63fef7cd 22e42c20 f57ae94c eaad86e0 d21ff229 18b0dd3b 076d63be 253de24b c20c6da2 90fa54d8 3771a225 deecf914 9f79a8e6 14c3c4cd"), FromHex(@"00000169 5e3821e7 2c7cacaa dcf62909 cd83463a 21c6d033 93c527c6 43b36239 c46af117 ab7c7ad1 9a4c8cf0 ae95ed51 72988546 1aa2ce27 00a6365b ca3733d2 920b2267"), false); OwnECPoint S2X = new OwnECPoint( FromHex(@"00000128 79442f24 50c119e7 119a5f73 8be1f1eb a9e9d7c6 cf41b325 d9ce6d64 3106e9d6 1124a91a 96bcf201 305a9dee 55fa7913 6dc70083 1e54c3ca 4ff2646b d3c36bc6"), FromHex(@"00000198 64a8b885 5c2479cb efe375ae 553e2393 271ed36f adfc4494 fc0583f6 bd035988 96f39854 abeae5f9 a6515a02 1e2c0eef 139e71de 610143f5 3382f410 4dccb543"), false); JacobianECPoint ST1 = S.ToJacobian; JacobianECPoint ST2 = S.ToJacobian; JacobianECPoint S2 = S.ToJacobian; Add(ref ST1, T, false); Add(ref ST2, T, true); GetDouble(ref S2); OwnECPoint ST1a = ST1.ToECPoint(this); OwnECPoint ST2a = ST2.ToECPoint(this); OwnECPoint S2a = S2.ToECPoint(this); if (!IsEqual(ST1a.X, ST.X) || !IsEqual(ST1a.Y, ST.Y)) { return(false); } if (!IsEqual(ST2a.X, STSub.X) || !IsEqual(ST2a.Y, STSub.Y)) { return(false); } if (!IsEqual(S2a.X, S2X.X) || !IsEqual(S2a.Y, S2X.Y)) { return(false); } ulong[] factorD = FromHex(@"000001eb 7f81785c 9629f136 a7e8f8c6 74957109 73555411 1a2a866f a5a16669 9419bfa9 936c78b6 2653964d f0d6da94 0a695c72 94d41b2d 6600de6d fcf0edcf c89fdcb1"); OwnECPoint dS = new OwnECPoint( FromHex(@"00000091 b15d09d0 ca0353f8 f96b93cd b13497b0 a4bb582a e9ebefa3 5eee61bf 7b7d041b 8ec34c6c 00c0c067 1c4ae063 318fb75b e87af4fe 859608c9 5f0ab477 4f8c95bb"), FromHex(@"00000130 f8f8b5e1 abb4dd94 f6baaf65 4a2d5810 411e77b7 423965e0 c7fd79ec 1ae563c2 07bd255e e9828eb7 a03fed56 5240d2cc 80ddd2ce cbb2eb50 f0951f75 ad87977f"), false); OwnECPoint rsd = Multiply(S, factorD); if (!IsEqual(rsd.X, dS.X) || !IsEqual(rsd.Y, dS.Y)) { return(false); } ulong[] factorE = FromHex(@"00000137 e6b73d38 f153c3a7 57561581 2608f2ba b3229c92 e21c0d1c 83cfad92 61dbb17b b77a6368 2000031b 9122c2f0 cdab2af7 2314be95 254de429 1a8f85f7 c70412e3"); OwnECPoint dSeT = new OwnECPoint( FromHex(@"0000009d 3802642b 3bea152b eb9e05fb a247790f 7fc16807 2d363340 133402f2 585588dc 1385d40e bcb8552f 8db02b23 d687cae4 6185b275 28adb1bf 9729716e 4eba653d"), FromHex(@"0000000f e44344e7 9da6f49d 87c10637 44e5957d 9ac0a505 bafa8281 c9ce9ff2 5ad53f8d a084a2de b0923e46 501de579 7850c61b 229023dd 9cf7fc7f 04cd35eb b026d89d"), false); OwnECPoint ret = Multiply(T, factorE); Add(ref ret, rsd, false); if (!IsEqual(ret.X, dSeT.X) || !IsEqual(ret.Y, dSeT.Y)) { return(false); } return(true); }
//return: P * factor on the elliptic curves domain. public OwnECPoint DualMultiply(OwnECPoint p1, BigInteger k1, OwnECPoint p2, BigInteger k2) { if (k1 < 0) { p1 = Negative(p1); k1 = -k1; } if (k2 < 0) { p2 = Negative(p2); k2 = -k2; } BigInteger exp1 = k1 ^ (k1 * 3); BigInteger exp2 = k2 ^ (k2 * 3); BigInteger max = BigInteger.Max(exp1, exp2); int high = max <= 0 ? 0 : 1 + (int)Math.Ceiling(BigInteger.Log(max, 2)); JacobianECPoint x = new JacobianECPoint(p1.X, p1.Y, true); OwnECPoint cacheAdd = p1.Copy(); OwnECPoint cacheSub = p1.Copy(); Add(ref cacheAdd, p2); Subtract(ref cacheSub, p2); BigInteger bit = BigInteger.One << high; for (int i = high; --i >= 0; bit >>= 1) { GetDouble(ref x); if ((exp1 & bit) != 0 && (exp2 & bit) != 0) { int branch = ((k1 & bit) != 0 ? 0 : 1) + ((k2 & bit) != 0 ? 0 : 2); switch (branch) { case 0: Subtract(ref x, cacheAdd); break; case 1: Add(ref x, cacheSub); break; case 2: Subtract(ref x, cacheSub); break; case 3: Add(ref x, cacheAdd); break; } continue; } if ((exp1 & bit) != 0) { if ((k1 & bit) != 0) { Subtract(ref x, p1); } else { Add(ref x, p1); } continue; } if ((exp2 & bit) != 0) { if ((k2 & bit) != 0) { Subtract(ref x, p2); } else { Add(ref x, p2); } continue; } } OwnECPoint result = x.ToECPoint(this); return(result); }
public void GetDouble(ref JacobianECPoint p) { //return: 2 * p in "3M + 4S" for a = 0 and "3M + 5S" for a = -3. if (p.IsInfinity) { return; } if (IsZero(p.Y)) { p.SetInfinity(); return; } if (AIsZero) { //https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates //if (Y == 0) // return POINT_AT_INFINITY //S = 4 * X * Y^2 //M = 3*X^2 [+ a* Z^4] //X' = M^2 - 2*S //Y' = M*(S - X') - 8*Y^4 //Z' = 2*Y*Z = (Y+Z)^2 - Y^2 - Z^2 //return (X', Y', Z') Let(R1, p.Y); SquareModP(R1); AddModP(R1, R1); //R1 = 2 * Y^2 Let(Z3, R1); SquareModP(Z3); //Z3 = 4 * Y^4 Let(S1, p.X); MultiplyModP(S1, R1); AddModP(S1, S1); //S1 = S = 4 * X * Y^2 Let(S2, p.X); SquareModP(S2); AddScaledModP(S2, S2, 2); //S2 = M = 3*X^2 [+ a* Z^4] because a == 0. Let(x1, S2); SquareModP(x1); SubScaledModP(x1, S1, 2); //X' = M^2 - 2*S Let(y1, S1); SubModP(y1, x1); MultiplyModP(y1, S2); SubScaledModP(y1, Z3, 2); //Y' = M*(S - X') - 8*Y^4 Let(Z3, p.Y); AddModP(Z3, Z3); MultiplyModP(Z3, p.Z); //Z' = 2*Y*Z p.SetFrom(x1, y1, Z3, false); } else if (AIsMinus3) { //http://www.hyperelliptic.org/EFD/oldefd/jacobian.html#a3DBL //delta:=Z1^2; //gamma:=Y1^2; //beta:=4*X1*gamma; //alpha:=3*(X1-delta)*(X1+delta); //X3:=alpha^2-2*beta; //Z3:=(Y1+Z1)^2-gamma-delta; //Y3:=alpha*(beta-X3)-8*gamma^2; Let(Z2, p.Z); SquareModP(Z2); //Z2:= delta:=Z1^2; Let(y1, p.Y); SquareModP(y1); //y1:= gamma:=Y1^2; Let(R1, p.X); MultiplyModP(R1, y1); AddScaledModP(R1, R1, 3); //R1:= beta:=4*X1*gamma; SubtractModP(p.X, Z2, S1); AdditionModP(p.X, Z2, S2); MultiplyModP(S1, S2); AddScaledModP(S1, S1, 2); //S1:= alpha:=3*(X1-delta)*(X1+delta) because a == -3 AddModP(Z2, y1); //delta = delta + gamma Let(x1, S1); SquareModP(x1); SubScaledModP(x1, R1, 2); //X3:= alpha^2-2*beta; Let(Z3, p.Y); AddModP(Z3, p.Z); SquareModP(Z3); SubModP(Z3, Z2); //Z3 = (Y1+Z1)^2-delta SubModP(R1, x1); MultiplyModP(R1, S1); SquareModP(y1); SubScaledModP(R1, y1, 8); //Y3 = alpha*(beta-X3)-8*gamma^2 p.SetFrom(x1, R1, Z3, false); } else { throw new NotImplementedException(); } }
public void Subtract(ref JacobianECPoint p, OwnECPoint q) { Add(ref p, q, true); }