Skip to content

Commit 0930ded

Browse files
committed
[Core] Use JacobianModder for the Modding operation to replace the modpow to improve the efficiency for ECDH
1 parent 0ec9816 commit 0930ded

File tree

1 file changed

+165
-70
lines changed

1 file changed

+165
-70
lines changed

Lagrange.Core/Utility/Cryptography/EcdhProvider.cs

Lines changed: 165 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,25 @@
55

66
namespace Lagrange.Core.Utility.Cryptography;
77

8-
internal sealed class EcdhProvider
8+
public sealed class EcdhProvider
99
{
1010
private EllipticCurve Curve { get; }
11-
11+
1212
private BigInteger Secret { get; }
13-
13+
1414
private EllipticPoint Public { get; }
1515

16+
private readonly struct JacobianPoint(BigInteger x, BigInteger y, BigInteger z)
17+
{
18+
public BigInteger X { get; } = x;
19+
public BigInteger Y { get; } = y;
20+
public BigInteger Z { get; } = z;
21+
public bool IsInfinity => Z.IsZero;
22+
23+
public static JacobianPoint FromAffine(EllipticPoint p) =>
24+
p.IsDefault ? new JacobianPoint(0, 1, 0) : new JacobianPoint(p.X, p.Y, 1);
25+
}
26+
1627
public EcdhProvider(EllipticCurve curve)
1728
{
1829
Curve = curve;
@@ -37,14 +48,14 @@ public byte[] KeyExchange(byte[] ecPub, bool isHash)
3748
var shared = CreateShared(Secret, UnpackPublic(ecPub));
3849
return PackShared(shared, isHash);
3950
}
40-
51+
4152
public byte[] PackPublic(bool compress)
4253
{
4354
if (compress)
4455
{
4556
var result = new byte[Curve.Size + 1];
4657

47-
result[0] = (byte) (Public.Y.IsEven ^ Public.Y.Sign < 0 ? 0x02 : 0x03);
58+
result[0] = (byte)(Public.Y.IsEven ^ Public.Y.Sign < 0 ? 0x02 : 0x03);
4859
var xBytes = ToFixedBytes(Public.X, Curve.Size);
4960
xBytes.CopyTo(result, 1);
5061

@@ -63,7 +74,7 @@ public byte[] PackPublic(bool compress)
6374
return result;
6475
}
6576
}
66-
77+
6778
public byte[] PackSecret()
6879
{
6980
int rawLength = Secret.GetByteCount();
@@ -72,7 +83,7 @@ public byte[] PackSecret()
7283
result[3] = (byte)rawLength;
7384
return result[..(rawLength + 4)];
7485
}
75-
86+
7687
private byte[] PackShared(EllipticPoint ecShared, bool isHash)
7788
{
7889
var x = ToFixedBytes(ecShared.X, Curve.Size);
@@ -83,7 +94,7 @@ private EllipticPoint UnpackPublic(byte[] publicKey)
8394
{
8495
int length = publicKey.Length;
8596
if (length != Curve.Size * 2 + 1 && length != Curve.Size + 1) throw new Exception("Length does not match.");
86-
97+
8798
if (publicKey[0] == 0x04) // Not compressed
8899
{
89100
return new EllipticPoint(
@@ -109,17 +120,17 @@ private EllipticPoint UnpackPublic(byte[] publicKey)
109120
return new EllipticPoint(px, py);
110121
}
111122
}
112-
123+
113124
private static BigInteger UnpackSecret(byte[] ecSec)
114125
{
115126
int length = ecSec.Length - 4;
116127
if (length != ecSec[3]) throw new Exception("Length does not match.");
117-
128+
118129
return new BigInteger(ecSec.AsSpan(4, length), true, true);
119130
}
120-
131+
121132
private EllipticPoint CreatePublic() => CreateShared(Secret, Curve.G);
122-
133+
123134
private BigInteger CreateSecret()
124135
{
125136
BigInteger result;
@@ -133,69 +144,113 @@ private BigInteger CreateSecret()
133144

134145
return result;
135146
}
136-
147+
137148
private EllipticPoint CreateShared(BigInteger ecSec, EllipticPoint ecPub)
138149
{
139150
if (ecSec % Curve.N == 0 || ecPub.IsDefault) return default;
140151
if (ecSec < 0) return CreateShared(-ecSec, ecPub);
141152

142153
if (!Curve.CheckOn(ecPub)) throw new Exception("Public key does not correct, it is not on the curve.");
143154

144-
var pr = new EllipticPoint();
145-
var pa = ecPub;
155+
var pr = new JacobianPoint(0, 1, 0); // Point at infinity
156+
var pa = JacobianPoint.FromAffine(ecPub);
146157
var ps = ecSec;
158+
147159
while (ps > 0)
148160
{
149-
if ((ps & 1) > 0) pr = PointAdd(pr, pa);
150-
151-
pa = PointAdd(pa, pa);
161+
if ((ps & 1) > 0) pr = JacobianAdd(pr, pa);
162+
pa = JacobianDouble(pa);
152163
ps >>= 1;
153164
}
154165

155-
if (!Curve.CheckOn(pr)) throw new Exception("Calculated shared key is not on the curve.");
156-
157-
return pr;
166+
return JacobianToAffine(pr);
158167
}
159-
160-
private EllipticPoint PointAdd(EllipticPoint p1, EllipticPoint p2)
168+
169+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
170+
private JacobianPoint JacobianDouble(JacobianPoint p)
161171
{
162-
if (p1.IsDefault) return p2;
163-
if (p2.IsDefault) return p1;
164-
if (!Curve.CheckOn(p1) || !Curve.CheckOn(p2)) throw new InvalidDataException("Point is not on the curve.");
172+
if (p.IsInfinity) return p;
165173

166-
var x1 = p1.X;
167-
var x2 = p2.X;
168-
var y1 = p1.Y;
169-
var y2 = p2.Y;
170-
BigInteger m;
174+
var p2 = Curve.P;
175+
var x = p.X;
176+
var y = p.Y;
177+
var z = p.Z;
171178

172-
if (x1 == x2)
173-
{
174-
if (y1 == y2) m = (3 * x1 * x1 + Curve.A) * ModInverse(y1 << 1, Curve.P);
175-
else return default;
176-
}
177-
else
179+
var yy = Mod(y * y, p2);
180+
var s = Mod(4 * x * yy, p2);
181+
var m = Mod(3 * x * x + Curve.A * z * z * z * z, p2);
182+
var x3 = Mod(m * m - 2 * s, p2);
183+
var y3 = Mod(m * (s - x3) - 8 * yy * yy, p2);
184+
var z3 = Mod(2 * y * z, p2);
185+
186+
return new JacobianPoint(x3, y3, z3);
187+
}
188+
189+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
190+
private JacobianPoint JacobianAdd(JacobianPoint p1, JacobianPoint p2)
191+
{
192+
if (p1.IsInfinity) return p2;
193+
if (p2.IsInfinity) return p1;
194+
195+
var p = Curve.P;
196+
var z1z1 = Mod(p1.Z * p1.Z, p);
197+
var z2z2 = Mod(p2.Z * p2.Z, p);
198+
var u1 = Mod(p1.X * z2z2, p);
199+
var u2 = Mod(p2.X * z1z1, p);
200+
var s1 = Mod(p1.Y * p2.Z * z2z2, p);
201+
var s2 = Mod(p2.Y * p1.Z * z1z1, p);
202+
203+
if (u1 == u2)
178204
{
179-
m = (y1 - y2) * ModInverse(x1 - x2, Curve.P);
205+
if (s1 == s2) return JacobianDouble(p1);
206+
return new JacobianPoint(0, 1, 0); // Point at infinity
180207
}
181208

182-
var xr = Mod(m * m - x1 - x2, Curve.P);
183-
var yr = Mod(m * (x1 - xr) - y1, Curve.P);
184-
var pr = new EllipticPoint(xr, yr);
185-
186-
if (!Curve.CheckOn(pr)) throw new InvalidDataException("Calculated point is not on the curve.");
187-
return pr;
209+
var h = Mod(u2 - u1, p);
210+
var hh = Mod(h * h, p);
211+
var hhh = Mod(h * hh, p);
212+
var r = Mod(s2 - s1, p);
213+
var v = Mod(u1 * hh, p);
214+
215+
var x3 = Mod(r * r - hhh - 2 * v, p);
216+
var y3 = Mod(r * (v - x3) - s1 * hhh, p);
217+
var z3 = Mod(p1.Z * p2.Z * h, p);
218+
219+
return new JacobianPoint(x3, y3, z3);
188220
}
189221

222+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
223+
private EllipticPoint JacobianToAffine(JacobianPoint p)
224+
{
225+
if (p.IsInfinity) return default;
226+
227+
var zInv = ModInverse(p.Z, Curve.P);
228+
var zInv2 = Mod(zInv * zInv, Curve.P);
229+
var zInv3 = Mod(zInv2 * zInv, Curve.P);
230+
231+
return new EllipticPoint(Mod(p.X * zInv2, Curve.P), Mod(p.Y * zInv3, Curve.P));
232+
}
233+
234+
// Extended Euclidean Algorithm - faster than Fermat's little theorem
190235
[MethodImpl(MethodImplOptions.AggressiveInlining)]
191236
private static BigInteger ModInverse(BigInteger a, BigInteger p)
192237
{
193-
if (a < 0) return p - ModInverse(-a, p);
238+
if (a < 0) a = ((a % p) + p) % p;
239+
240+
BigInteger t = 0, newT = 1;
241+
BigInteger r = p, newR = a;
242+
243+
while (!newR.IsZero)
244+
{
245+
var quotient = r / newR;
246+
(t, newT) = (newT, t - quotient * newT);
247+
(r, newR) = (newR, r - quotient * newR);
248+
}
194249

195-
var g = BigInteger.GreatestCommonDivisor(a, p);
196-
if (g != 1) throw new Exception("Inverse does not exist.");
250+
if (r > 1) throw new Exception("Inverse does not exist.");
251+
if (t < 0) t += p;
197252

198-
return BigInteger.ModPow(a, p - 2, p);
253+
return t;
199254
}
200255

201256
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -225,7 +280,7 @@ private static byte[] ToFixedBytes(BigInteger value, int size)
225280
}
226281
}
227282

228-
internal readonly struct EllipticCurve
283+
public readonly struct EllipticCurve
229284
{
230285
public static readonly EllipticCurve Secp192K1 = new()
231286
{
@@ -238,42 +293,42 @@ internal readonly struct EllipticCurve
238293
B = 3,
239294
G = new EllipticPoint
240295
{
241-
X = new BigInteger(new byte[]
296+
X = new BigInteger(new byte[]
242297
{
243-
0x7D, 0x6C, 0xE0, 0xEA, 0xB1, 0xD1, 0xA5, 0x1D, 0x34, 0xF4, 0xB7, 0x80,
244-
0x02, 0x7D, 0xB0, 0x26, 0xAE, 0xE9, 0x57, 0xC0, 0x0E, 0xF1, 0x4F, 0xDB, 0
298+
0x7D, 0x6C, 0xE0, 0xEA, 0xB1, 0xD1, 0xA5, 0x1D, 0x34, 0xF4, 0xB7, 0x80,
299+
0x02, 0x7D, 0xB0, 0x26, 0xAE, 0xE9, 0x57, 0xC0, 0x0E, 0xF1, 0x4F, 0xDB, 0
245300
}),
246-
Y = new BigInteger(new byte[]
301+
Y = new BigInteger(new byte[]
247302
{
248-
0x9D, 0x2F, 0x5E, 0xD9, 0x88, 0xAA, 0x82, 0x40, 0x34, 0x86, 0xBE, 0x15,
249-
0xD0, 0x63, 0x41, 0x84, 0xA7, 0x28, 0x56, 0x9C, 0x6D, 0x2F, 0x2F, 0x9B, 0
303+
0x9D, 0x2F, 0x5E, 0xD9, 0x88, 0xAA, 0x82, 0x40, 0x34, 0x86, 0xBE, 0x15,
304+
0xD0, 0x63, 0x41, 0x84, 0xA7, 0x28, 0x56, 0x9C, 0x6D, 0x2F, 0x2F, 0x9B, 0
250305
})
251306
},
252-
N = new BigInteger(new byte[]
307+
N = new BigInteger(new byte[]
253308
{
254-
0x8D, 0xFD, 0xDE, 0x74, 0x6A, 0x46, 0x69, 0x0F, 0x17, 0xFC, 0xF2, 0x26,
255-
0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0
309+
0x8D, 0xFD, 0xDE, 0x74, 0x6A, 0x46, 0x69, 0x0F, 0x17, 0xFC, 0xF2, 0x26,
310+
0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0
256311
}),
257312
H = 1,
258313
PackSize = 24,
259314
Size = 24
260315
};
261-
316+
262317
public static readonly EllipticCurve Prime256V1 = new()
263318
{
264-
P = new BigInteger(new byte[]
319+
P = new BigInteger(new byte[]
265320
{
266321
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
267322
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0
268323
}),
269-
A = new BigInteger(new byte[]
324+
A = new BigInteger(new byte[]
270325
{
271-
0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
326+
0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
272327
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0
273328
}),
274-
B = new BigInteger(new byte[]
329+
B = new BigInteger(new byte[]
275330
{
276-
0x4B, 0x60, 0xD2, 0x27, 0x3E, 0x3C, 0xCE, 0x3B, 0xF6, 0xB0, 0x53, 0xCC, 0xB0, 0x06, 0x1D, 0x65,
331+
0x4B, 0x60, 0xD2, 0x27, 0x3E, 0x3C, 0xCE, 0x3B, 0xF6, 0xB0, 0x53, 0xCC, 0xB0, 0x06, 0x1D, 0x65,
277332
0xBC, 0x86, 0x98, 0x76, 0x55, 0xBD, 0xEB, 0xB3, 0xE7, 0x93, 0x3A, 0xAA, 0xD8, 0x35, 0xC6, 0x5A, 0
278333
}),
279334
G = new EllipticPoint
@@ -289,16 +344,56 @@ internal readonly struct EllipticCurve
289344
0x16, 0x9E, 0x0F, 0x7C, 0x4A, 0xEB, 0xE7, 0x8E, 0x9B, 0x7F, 0x1A, 0xFE, 0xE2, 0x42, 0xE3, 0x4F, 0
290345
})
291346
},
292-
N = new BigInteger(new byte[]
347+
N = new BigInteger(new byte[]
293348
{
294-
0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3, 0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC,
349+
0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3, 0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC,
295350
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0
296351
}),
297352
H = 1,
298353
Size = 32,
299354
PackSize = 16
300355
};
301356

357+
public static readonly EllipticCurve Secp224R1 = new()
358+
{
359+
P = new BigInteger(new byte[]
360+
{
361+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
362+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
363+
}),
364+
A = new BigInteger(new byte[]
365+
{
366+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
367+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE
368+
}),
369+
B = new BigInteger(new byte[]
370+
{
371+
0xB4, 0x05, 0x0A, 0x85, 0x0C, 0x04, 0xB3, 0xAB, 0xF5, 0x41, 0x32, 0x56,
372+
0x50, 0x44, 0xB0, 0xB7, 0xD7, 0xBF, 0xD8, 0xBA, 0x27, 0x0B, 0x39, 0x43, 0x23, 0x55, 0xFF, 0xB4
373+
}),
374+
G = new EllipticPoint
375+
{
376+
X = new BigInteger(new byte[]
377+
{
378+
0xB7, 0x0E, 0x0C, 0xBD, 0x6B, 0xB4, 0xBF, 0x7F, 0x32, 0x13, 0x90, 0xB9,
379+
0x4A, 0x03, 0xC1, 0xD3, 0x56, 0xC2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xD6, 0x11, 0x5C, 0x1D, 0x21
380+
}),
381+
Y = new BigInteger(new byte[]
382+
{
383+
0xBD, 0x37, 0x63, 0x88, 0xB5, 0xF7, 0x23, 0xFB, 0x4C, 0x22, 0xDF, 0xE6,
384+
0xCD, 0x43, 0x75, 0xA0, 0x5A, 0x07, 0x47, 0x64, 0x44, 0xD5, 0x81, 0x99, 0x85, 0x00, 0x7E, 0x34
385+
})
386+
},
387+
N = new BigInteger(new byte[]
388+
{
389+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x16, 0xA2, 0xE0,
390+
0xB8, 0xF0, 0x3E, 0x13, 0xDD, 0x29, 0x45, 0x5C, 0x5C, 0x2A, 0x3D, 0x16, 0x3C, 0xBF, 0x05, 0x6D
391+
}),
392+
H = 1,
393+
Size = 28,
394+
PackSize = 16
395+
};
396+
302397
public BigInteger P { get; private init; }
303398

304399
public BigInteger A { get; private init; }
@@ -314,18 +409,18 @@ internal readonly struct EllipticCurve
314409
public int Size { get; private init; }
315410

316411
public int PackSize { get; private init; }
317-
412+
318413
public bool CheckOn(EllipticPoint point) => (point.Y * point.Y - point.X * point.X * point.X - A * point.X - B) % P == 0;
319414
}
320415

321416
[DebuggerDisplay("ToString(),nq")]
322-
internal readonly struct EllipticPoint(BigInteger x, BigInteger y)
417+
public readonly struct EllipticPoint(BigInteger x, BigInteger y)
323418
{
324419
public BigInteger X { get; init; } = x;
325420

326421
public BigInteger Y { get; init; } = y;
327422

328423
public bool IsDefault => X.IsZero && Y.IsZero;
329-
424+
330425
public static EllipticPoint operator -(EllipticPoint p) => new(-p.X, -p.Y);
331-
}
426+
}

0 commit comments

Comments
 (0)