55
66namespace 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