Browse Source

add curve25519

Ugochukwu Mmaduekwe 6 years ago
parent
commit
58d3fd834f

+ 7 - 7
CryptoLib.Tests/src/Math/PascalCoinECIESTests.pas

@@ -98,11 +98,11 @@ type
     function DoPascalCoinECIESDecrypt(keyType: TKeyType;
       const RawPrivateKey, PayloadToDecrypt: String): String;
 
-    procedure DoTestPascalCoinECIESDecrypt(id: String; keyType: TKeyType;
+    procedure DoTestPascalCoinECIESDecrypt(const id: String; keyType: TKeyType;
       const RawPrivateKey, PayloadToDecrypt, ExpectedOutput: String);
 
-    procedure DoTestPascalCoinECIESEncryptDecrypt(id: String; keyType: TKeyType;
-      const RawPrivateKey, RawAffineXCoord, RawAffineYCoord,
+    procedure DoTestPascalCoinECIESEncryptDecrypt(const id: String;
+      keyType: TKeyType; const RawPrivateKey, RawAffineXCoord, RawAffineYCoord,
       PayloadToEncrypt: String);
 
   protected
@@ -272,7 +272,7 @@ begin
   end;
 end;
 
-procedure TTestPascalCoinECIES.DoTestPascalCoinECIESDecrypt(id: String;
+procedure TTestPascalCoinECIES.DoTestPascalCoinECIESDecrypt(const id: String;
   keyType: TKeyType; const RawPrivateKey, PayloadToDecrypt,
   ExpectedOutput: String);
 var
@@ -285,9 +285,9 @@ begin
     ExpectedOutput, DecryptedPayload]));
 end;
 
-procedure TTestPascalCoinECIES.DoTestPascalCoinECIESEncryptDecrypt(id: String;
-  keyType: TKeyType; const RawPrivateKey, RawAffineXCoord, RawAffineYCoord,
-  PayloadToEncrypt: String);
+procedure TTestPascalCoinECIES.DoTestPascalCoinECIESEncryptDecrypt
+  (const id: String; keyType: TKeyType; const RawPrivateKey, RawAffineXCoord,
+  RawAffineYCoord, PayloadToEncrypt: String);
 var
   ActualOutput: String;
 begin

+ 45 - 4
CryptoLib.Tests/src/Others/ECTests.pas

@@ -35,6 +35,7 @@ uses
   ClpFixedSecureRandom,
   ClpSecureRandom,
   ClpISecureRandom,
+  ClpIX9ECParameters,
   ClpECDsaSigner,
   ClpIECDsaSigner,
   ClpIBasicAgreement,
@@ -53,6 +54,7 @@ uses
   ClpIECKeyGenerationParameters,
   ClpECKeyGenerationParameters,
   ClpIAsymmetricCipherKeyPair,
+  ClpCustomNamedCurves,
   ClpECC,
   ClpIECC,
   ClpEncoders,
@@ -115,12 +117,14 @@ type
     /// <summary>
     /// key generation test
     /// </summary>
-    procedure TestECDsaKeyGenTest();
+    procedure TestECDsaKeyGen();
 
     /// <summary>
     /// Basic Key Agreement Test
     /// </summary>
-    procedure TestECBasicAgreementTest();
+    procedure TestECBasicAgreement();
+
+    procedure TestECDHBasicAgreementCofactor();
 
   end;
 
@@ -604,7 +608,7 @@ begin
   end;
 end;
 
-procedure TTestEC.TestECDsaKeyGenTest;
+procedure TTestEC.TestECDsaKeyGen;
 var
   random: ISecureRandom;
   n: TBigInteger;
@@ -663,7 +667,7 @@ begin
   end;
 end;
 
-procedure TTestEC.TestECBasicAgreementTest;
+procedure TTestEC.TestECBasicAgreement;
 var
   random: ISecureRandom;
   n, k1, k2: TBigInteger;
@@ -738,6 +742,43 @@ begin
 
 end;
 
+procedure TTestEC.TestECDHBasicAgreementCofactor;
+var
+  random: ISecureRandom;
+  x9: IX9ECParameters;
+  ec: IECDomainParameters;
+  kpg: IECKeyPairGenerator;
+  p1, p2: IAsymmetricCipherKeyPair;
+  e1, e2: IBasicAgreement;
+  k1, k2: TBigInteger;
+begin
+  random := TSecureRandom.Create();
+
+  x9 := TCustomNamedCurves.GetByName('curve25519');
+  ec := TECDomainParameters.Create(x9.curve, x9.G, x9.n, x9.H, x9.GetSeed());
+
+  kpg := TECKeyPairGenerator.Create();
+  kpg.Init(TECKeyGenerationParameters.Create(ec, random)
+    as IECKeyGenerationParameters);
+
+  p1 := kpg.GenerateKeyPair();
+  p2 := kpg.GenerateKeyPair();
+
+  e1 := TECDHBasicAgreement.Create();
+  e2 := TECDHBasicAgreement.Create();
+
+  e1.Init(p1.Private);
+  e2.Init(p2.Private);
+
+  k1 := e1.CalculateAgreement(p2.Public);
+  k2 := e2.CalculateAgreement(p1.Public);
+
+  if (not(k1.Equals(k2))) then
+  begin
+    Fail('calculated agreement test failed');
+  end;
+end;
+
 initialization
 
 // Register any test cases with the test runner

+ 66 - 12
CryptoLib/src/Crypto/EC/ClpCustomNamedCurves.pas

@@ -41,6 +41,8 @@ uses
   ClpISecP521R1Custom,
   ClpSecT283Custom,
   ClpISecT283Custom,
+  ClpCurve25519Custom,
+  ClpICurve25519Custom,
   ClpIECC,
   ClpX9ECC,
   ClpIX9ECC,
@@ -67,8 +69,8 @@ type
 
     class function GetNames: TCryptoLibStringArray; static; inline;
 
-    // class procedure DefineCurve(const name: String;
-    // const holder: IX9ECParametersHolder); static; inline;
+    class procedure DefineCurve(const name: String;
+      const holder: IX9ECParametersHolder); static; inline;
 
     class procedure DefineCurveWithOid(const name: String;
       const oid: IDerObjectIdentifier; const holder: IX9ECParametersHolder);
@@ -116,6 +118,22 @@ type
     // */
     class property Names: TCryptoLibStringArray read GetNames;
 
+  type
+
+    /// <summary>
+    /// curve25519
+    /// </summary>
+    TCurve25519Holder = class sealed(TX9ECParametersHolder,
+      IX9ECParametersHolder)
+
+    strict protected
+      function CreateParameters(): IX9ECParameters; override;
+
+    public
+      class function Instance(): IX9ECParametersHolder; static;
+
+    end;
+
   type
 
     /// <summary>
@@ -202,16 +220,16 @@ implementation
 
 { TCustomNamedCurves }
 
-// class procedure TCustomNamedCurves.DefineCurve(const name: String;
-// const holder: IX9ECParametersHolder);
-// var
-// LName: string;
-// begin
-// LName := name;
-// Fnames.Add(LName);
-// LName := UpperCase(LName);
-// FnameToCurve.Add(LName, holder);
-// end;
+class procedure TCustomNamedCurves.DefineCurve(const name: String;
+  const holder: IX9ECParametersHolder);
+var
+  LName: string;
+begin
+  LName := name;
+  Fnames.Add(LName);
+  LName := UpperCase(LName);
+  FnameToCurve.Add(LName, holder);
+end;
 
 class procedure TCustomNamedCurves.DefineCurveWithOid(const name: String;
   const oid: IDerObjectIdentifier; const holder: IX9ECParametersHolder);
@@ -335,6 +353,8 @@ begin
 
   Fnames := TList<String>.Create();
 
+  DefineCurve('curve25519', TCurve25519Holder.Instance);
+
   DefineCurveWithOid('secp256k1', TSecObjectIdentifiers.SecP256k1,
     TSecP256K1Holder.Instance);
 
@@ -357,6 +377,40 @@ begin
   DefineCurveAlias('P-521', TSecObjectIdentifiers.SecP521r1);
 end;
 
+{ TCustomNamedCurves.TCurve25519Holder }
+
+function TCustomNamedCurves.TCurve25519Holder.CreateParameters: IX9ECParameters;
+var
+  curve: IECCurve;
+  G: IX9ECPoint;
+  S: TCryptoLibByteArray;
+begin
+  S := Nil;
+  curve := ConfigureCurve(TCurve25519.Create() as ICurve25519);
+
+  { *
+    * NOTE: Curve25519 was specified in Montgomery form. Rewriting in Weierstrass form
+    * involves substitution of variables, so the base-point x coordinate is 9 + (486662 / 3).
+    *
+    * The Curve25519 paper doesn't say which of the two possible y values the base
+    * point has. The choice here is guided by language in the Ed25519 paper.
+    *
+    * (The other possible y value is 5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14)
+    * }
+  G := TX9ECPoint.Create(curve,
+    THex.Decode('04' +
+    '2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD245A' +
+    '20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9'));
+
+  result := TX9ECParameters.Create(curve, G, curve.Order, curve.Cofactor, S);
+end;
+
+class function TCustomNamedCurves.TCurve25519Holder.Instance
+  : IX9ECParametersHolder;
+begin
+  result := TCurve25519Holder.Create();
+end;
+
 { TCustomNamedCurves.TSecP256K1Holder }
 
 function TCustomNamedCurves.TSecP256K1Holder.CreateParameters: IX9ECParameters;

+ 64 - 0
CryptoLib/src/Interfaces/ClpICurve25519Custom.pas

@@ -0,0 +1,64 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpICurve25519Custom;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpBigInteger,
+  ClpIECC,
+  ClpCryptoLibTypes;
+
+type
+  ICurve25519FieldElement = Interface(IAbstractFpFieldElement)
+    ['{50046C65-BACE-4E68-9AEF-09AAD33DFD62}']
+
+    function GetX: TCryptoLibUInt32Array;
+    property X: TCryptoLibUInt32Array read GetX;
+  end;
+
+type
+  ICurve25519Point = Interface(IAbstractFpPoint)
+    ['{49280930-32AC-4F84-BBCE-C9A9DF18E71E}']
+
+    function CalculateJacobianModifiedW(const z: ICurve25519FieldElement;
+      const ZSquared: TCryptoLibUInt32Array): ICurve25519FieldElement;
+    function GetJacobianModifiedW(): ICurve25519FieldElement;
+    function TwiceJacobianModified(calculateW: Boolean): ICurve25519Point;
+
+  end;
+
+type
+  ICurve25519 = Interface(IAbstractFpCurve)
+    ['{56BB2C20-454C-4C42-A603-AD7429362D82}']
+
+    function GetQ: TBigInteger;
+    property Q: TBigInteger read GetQ;
+
+  end;
+
+type
+  ICurve25519LookupTable = Interface(IECLookupTable)
+    ['{79FE1276-3D22-4A20-A4F1-58F0C0532BAC}']
+  end;
+
+implementation
+
+end.

+ 1378 - 0
CryptoLib/src/Math/EC/Custom/Djb/ClpCurve25519Custom.pas

@@ -0,0 +1,1378 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpCurve25519Custom;
+
+{$I ..\..\..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpECC,
+  ClpIECC,
+  ClpNat,
+  ClpMod,
+  ClpNat256,
+  ClpBigInteger,
+  ClpICurve25519Custom,
+  ClpECCurveConstants,
+  ClpBits,
+  ClpEncoders,
+  ClpArrayUtils,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SInvalidValueForCurve25519FieldElement =
+    'Value Invalid for Curve25519FieldElement "%s"';
+  SOneOfECFieldElementIsNil = 'Exactly One of the Field Elements is Nil';
+
+type
+  // 2^255 - 2^4 - 2^1 - 1
+  TCurve25519Field = class sealed(TObject)
+
+  strict private
+  const
+    P7 = UInt32($7FFFFFFF);
+    PInv = UInt32($13);
+
+    class var
+
+      FP, FPExt: TCryptoLibUInt32Array;
+
+    class function AddPTo(const z: TCryptoLibUInt32Array): UInt32;
+      static; inline;
+
+    class function AddPExtTo(const zz: TCryptoLibUInt32Array): UInt32;
+      static; inline;
+
+    class function SubPFrom(const z: TCryptoLibUInt32Array): Int32;
+      static; inline;
+
+    class function SubPExtFrom(const zz: TCryptoLibUInt32Array): Int32;
+      static; inline;
+
+    class function GetP: TCryptoLibUInt32Array; static; inline;
+
+    class procedure Boot(); static;
+    class constructor Curve25519Field();
+
+  public
+    class procedure Add(const x, y, z: TCryptoLibUInt32Array); static; inline;
+    class procedure AddExt(const xx, yy, zz: TCryptoLibUInt32Array);
+      static; inline;
+    class procedure AddOne(const x, z: TCryptoLibUInt32Array); static; inline;
+    class function FromBigInteger(const x: TBigInteger): TCryptoLibUInt32Array;
+      static; inline;
+    class procedure Half(const x, z: TCryptoLibUInt32Array); static; inline;
+    class procedure Multiply(const x, y, z: TCryptoLibUInt32Array);
+      static; inline;
+    class procedure MultiplyAddToExt(const x, y, zz: TCryptoLibUInt32Array);
+      static; inline;
+    class procedure Negate(const x, z: TCryptoLibUInt32Array); static; inline;
+    class procedure Reduce(const xx, z: TCryptoLibUInt32Array); static; inline;
+    class procedure Reduce27(x: UInt32; const z: TCryptoLibUInt32Array);
+      static; inline;
+    class procedure Square(const x, z: TCryptoLibUInt32Array); static; inline;
+    class procedure SquareN(const x: TCryptoLibUInt32Array; n: Int32;
+      const z: TCryptoLibUInt32Array); static; inline;
+    class procedure Subtract(const x, y, z: TCryptoLibUInt32Array);
+      static; inline;
+    class procedure SubtractExt(const xx, yy, zz: TCryptoLibUInt32Array);
+      static; inline;
+    class procedure Twice(const x, z: TCryptoLibUInt32Array); static; inline;
+
+    class property P: TCryptoLibUInt32Array read GetP;
+
+  end;
+
+type
+  TCurve25519FieldElement = class(TAbstractFpFieldElement,
+    ICurve25519FieldElement)
+
+  strict private
+    class var
+
+      FPRECOMP_POW2: TCryptoLibUInt32Array;
+
+    function Equals(const other: ICurve25519FieldElement): Boolean;
+      reintroduce; overload;
+
+    class function GetQ: TBigInteger; static; inline;
+
+    class procedure Boot(); static;
+    class constructor Curve25519FieldElement();
+
+  strict protected
+  var
+    Fx: TCryptoLibUInt32Array;
+
+    function GetFieldName: string; override;
+    function GetFieldSize: Int32; override;
+    function GetIsOne: Boolean; override;
+    function GetIsZero: Boolean; override;
+
+    function GetX: TCryptoLibUInt32Array; inline;
+    property x: TCryptoLibUInt32Array read GetX;
+
+  public
+    constructor Create(); overload;
+    constructor Create(const x: TBigInteger); overload;
+    constructor Create(const x: TCryptoLibUInt32Array); overload;
+
+    function TestBitZero: Boolean; override;
+    function ToBigInteger(): TBigInteger; override;
+
+    function Add(const b: IECFieldElement): IECFieldElement; override;
+    function AddOne(): IECFieldElement; override;
+    function Subtract(const b: IECFieldElement): IECFieldElement; override;
+
+    function Multiply(const b: IECFieldElement): IECFieldElement; override;
+    function Divide(const b: IECFieldElement): IECFieldElement; override;
+    function Negate(): IECFieldElement; override;
+    function Square(): IECFieldElement; override;
+
+    function Invert(): IECFieldElement; override;
+
+    /// <summary>
+    /// return a sqrt root - the routine verifies that the calculation
+    /// returns the right value - if <br />none exists it returns null.
+    /// </summary>
+    function Sqrt(): IECFieldElement; override;
+
+    function Equals(const other: IECFieldElement): Boolean; overload; override;
+
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}override;
+
+    property IsZero: Boolean read GetIsZero;
+    property IsOne: Boolean read GetIsOne;
+    property FieldName: string read GetFieldName;
+    property FieldSize: Int32 read GetFieldSize;
+
+    class property Q: TBigInteger read GetQ;
+
+  end;
+
+type
+  TCurve25519Point = class sealed(TAbstractFpPoint, ICurve25519Point)
+
+  strict protected
+    function Detach(): IECPoint; override;
+    function CalculateJacobianModifiedW(const z: ICurve25519FieldElement;
+      const ZSquared: TCryptoLibUInt32Array): ICurve25519FieldElement; virtual;
+    function GetJacobianModifiedW(): ICurve25519FieldElement; virtual;
+    function TwiceJacobianModified(calculateW: Boolean)
+      : ICurve25519Point; virtual;
+
+  public
+
+    /// <summary>
+    /// Create a point which encodes without point compression.
+    /// </summary>
+    /// <param name="curve">
+    /// the curve to use
+    /// </param>
+    /// <param name="x">
+    /// affine x co-ordinate
+    /// </param>
+    /// <param name="y">
+    /// affine y co-ordinate
+    /// </param>
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement);
+      overload; deprecated 'Use ECCurve.createPoint to construct points';
+
+    /// <summary>
+    /// Create a point that encodes with or without point compresion.
+    /// </summary>
+    /// <param name="curve">
+    /// the curve to use
+    /// </param>
+    /// <param name="x">
+    /// affine x co-ordinate
+    /// </param>
+    /// <param name="y">
+    /// affine y co-ordinate
+    /// </param>
+    /// <param name="withCompression">
+    /// if true encode with point compression
+    /// </param>
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      withCompression: Boolean); overload;
+      deprecated
+      'Per-point compression property will be removed, see GetEncoded(boolean)';
+
+    constructor Create(const curve: IECCurve; const x, y: IECFieldElement;
+      const zs: TCryptoLibGenericArray<IECFieldElement>;
+      withCompression: Boolean); overload;
+
+    function Add(const b: IECPoint): IECPoint; override;
+    function Negate(): IECPoint; override;
+
+    function Twice(): IECPoint; override;
+    function TwicePlus(const b: IECPoint): IECPoint; override;
+
+    function ThreeTimes(): IECPoint; override;
+
+    function GetZCoord(index: Int32): IECFieldElement; override;
+
+  end;
+
+type
+  TCurve25519 = class sealed(TAbstractFpCurve, ICurve25519)
+
+  strict private
+
+  type
+    TCurve25519LookupTable = class sealed(TInterfacedObject,
+      ICurve25519LookupTable, IECLookupTable)
+
+    strict private
+    var
+      Fm_outer: ICurve25519;
+      Fm_table: TCryptoLibUInt32Array;
+      Fm_size: Int32;
+
+      function GetSize: Int32; virtual;
+
+    public
+
+      constructor Create(const outer: ICurve25519;
+        const table: TCryptoLibUInt32Array; size: Int32);
+
+      function Lookup(index: Int32): IECPoint; virtual;
+
+      property size: Int32 read GetSize;
+
+    end;
+
+  const
+    Curve25519_DEFAULT_COORDS = Int32
+      (TECCurveConstants.COORD_JACOBIAN_MODIFIED);
+    CURVE25519_FE_INTS = Int32(8);
+
+  var
+    Fq: TBigInteger;
+
+    class function GetCurve25519_Q: TBigInteger; static; inline;
+
+  strict protected
+  var
+    Fm_infinity: ICurve25519Point;
+
+    function GetQ: TBigInteger; virtual;
+    function GetFieldSize: Int32; override;
+    function GetInfinity: IECPoint; override;
+
+    function CloneCurve(): IECCurve; override;
+
+    function CreateRawPoint(const x, y: IECFieldElement;
+      withCompression: Boolean): IECPoint; overload; override;
+
+    function CreateRawPoint(const x, y: IECFieldElement;
+      const zs: TCryptoLibGenericArray<IECFieldElement>;
+      withCompression: Boolean): IECPoint; overload; override;
+
+  public
+    constructor Create();
+    function FromBigInteger(const x: TBigInteger): IECFieldElement; override;
+
+    function SupportsCoordinateSystem(coord: Int32): Boolean; override;
+
+    function CreateCacheSafeLookupTable(const points
+      : TCryptoLibGenericArray<IECPoint>; off, len: Int32)
+      : IECLookupTable; override;
+
+    property Q: TBigInteger read GetQ;
+    property Infinity: IECPoint read GetInfinity;
+    property FieldSize: Int32 read GetFieldSize;
+
+    class property Curve25519_Q: TBigInteger read GetCurve25519_Q;
+
+  end;
+
+implementation
+
+{ TCurve25519Field }
+
+class function TCurve25519Field.AddPTo(const z: TCryptoLibUInt32Array): UInt32;
+var
+  c: Int64;
+begin
+  c := Int64(z[0]) - PInv;
+  z[0] := UInt32(c);
+  c := c shr 32;
+  if (c <> 0) then
+  begin
+    c := TNat.DecAt(7, z, 1);
+  end;
+  c := c + (Int64(z[7]) + (P7 + 1));
+  z[7] := UInt32(c);
+  c := c shr 32;
+  result := UInt32(c);
+end;
+
+class function TCurve25519Field.AddPExtTo
+  (const zz: TCryptoLibUInt32Array): UInt32;
+var
+  c: Int64;
+begin
+  c := Int64(zz[0]) + FPExt[0];
+  zz[0] := UInt32(c);
+  c := c shr 32;
+  if (c <> 0) then
+  begin
+    c := TNat.IncAt(8, zz, 1);
+  end;
+  c := c + (Int64(zz[8]) - PInv);
+  zz[8] := UInt32(c);
+  c := c shr 32;
+  if (c <> 0) then
+  begin
+    c := TNat.DecAt(15, zz, 9);
+  end;
+  c := c + (Int64(zz[15]) + (FPExt[15] + 1));
+  zz[15] := UInt32(c);
+  c := c shr 32;
+  result := UInt32(c);
+end;
+
+class function TCurve25519Field.SubPFrom(const z: TCryptoLibUInt32Array): Int32;
+var
+  c: Int64;
+begin
+  c := Int64(z[0]) + PInv;
+  z[0] := UInt32(c);
+  c := c shr 32;
+  if (c <> 0) then
+  begin
+    c := TNat.IncAt(7, z, 1);
+  end;
+  c := c + (Int64(z[7]) - (P7 + 1));
+  z[7] := UInt32(c);
+  c := c shr 32;
+  result := Int32(c);
+end;
+
+class function TCurve25519Field.SubPExtFrom
+  (const zz: TCryptoLibUInt32Array): Int32;
+var
+  c: Int64;
+begin
+  c := Int64(zz[0]) - FPExt[0];
+  zz[0] := UInt32(c);
+  c := c shr 32;
+  if (c <> 0) then
+  begin
+    c := TNat.DecAt(8, zz, 1);
+  end;
+  c := c + (Int64(zz[8]) + PInv);
+  zz[8] := UInt32(c);
+  c := c shr 32;
+  if (c <> 0) then
+  begin
+    c := TNat.IncAt(15, zz, 9);
+  end;
+  c := c + (Int64(zz[15]) - (FPExt[15] + 1));
+  zz[15] := UInt32(c);
+  c := c shr 32;
+  result := Int32(c);
+end;
+
+class constructor TCurve25519Field.Curve25519Field;
+begin
+  TCurve25519Field.Boot;
+end;
+
+class function TCurve25519Field.GetP: TCryptoLibUInt32Array;
+begin
+  result := FP;
+end;
+
+class procedure TCurve25519Field.Add(const x, y, z: TCryptoLibUInt32Array);
+begin
+  TNat256.Add(x, y, z);
+  if (TNat256.Gte(z, P)) then
+  begin
+    SubPFrom(z);
+  end;
+end;
+
+class procedure TCurve25519Field.AddExt(const xx, yy,
+  zz: TCryptoLibUInt32Array);
+begin
+  TNat.Add(16, xx, yy, zz);
+  if (TNat.Gte(16, zz, FPExt)) then
+  begin
+    SubPExtFrom(zz);
+  end;
+end;
+
+class procedure TCurve25519Field.AddOne(const x, z: TCryptoLibUInt32Array);
+begin
+  TNat.Inc(8, x, z);
+  if (TNat256.Gte(z, P)) then
+  begin
+    SubPFrom(z);
+  end;
+end;
+
+class procedure TCurve25519Field.Boot;
+begin
+  FP := TCryptoLibUInt32Array.Create($FFFFFFED, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF,
+    $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $7FFFFFFF);
+  FPExt := TCryptoLibUInt32Array.Create($00000169, $00000000, $00000000,
+    $00000000, $00000000, $00000000, $00000000, $00000000, $FFFFFFED, $FFFFFFFF,
+    $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $3FFFFFFF);
+end;
+
+class function TCurve25519Field.FromBigInteger(const x: TBigInteger)
+  : TCryptoLibUInt32Array;
+var
+  z: TCryptoLibUInt32Array;
+begin
+  z := TNat256.FromBigInteger(x);
+  while (TNat256.Gte(z, P)) do
+  begin
+    TNat256.SubFrom(P, z);
+  end;
+  result := z;
+end;
+
+class procedure TCurve25519Field.Half(const x, z: TCryptoLibUInt32Array);
+begin
+  if ((x[0] and 1) = 0) then
+  begin
+    TNat.ShiftDownBit(8, x, 0, z);
+  end
+  else
+  begin
+    TNat256.Add(x, P, z);
+    TNat.ShiftDownBit(8, z, 0);
+  end;
+end;
+
+class procedure TCurve25519Field.Reduce(const xx, z: TCryptoLibUInt32Array);
+var
+  xx07, c, z7: UInt32;
+begin
+{$IFDEF DEBUG}
+  System.Assert((xx[15] shr 30) = 0);
+{$ENDIF DEBUG}
+  xx07 := xx[7];
+  TNat.ShiftUpBit(8, xx, 8, xx07, z, 0);
+  c := TNat256.MulByWordAddTo(PInv, xx, z) shl 1;
+  z7 := z[7];
+  c := c + ((z7 shr 31) - (xx07 shr 31));
+  z7 := z7 and P7;
+  z7 := z7 + (TNat.AddWordTo(7, c * PInv, z));
+  z[7] := z7;
+  if ((z7 >= P7) and (TNat256.Gte(z, P))) then
+  begin
+    SubPFrom(z);
+  end;
+end;
+
+class procedure TCurve25519Field.Multiply(const x, y, z: TCryptoLibUInt32Array);
+var
+  tt: TCryptoLibUInt32Array;
+begin
+  tt := TNat256.CreateExt();
+  TNat256.Mul(x, y, tt);
+  Reduce(tt, z);
+end;
+
+class procedure TCurve25519Field.MultiplyAddToExt(const x, y,
+  zz: TCryptoLibUInt32Array);
+begin
+  TNat256.MulAddTo(x, y, zz);
+  if (TNat.Gte(16, zz, FPExt)) then
+  begin
+    SubPExtFrom(zz);
+  end;
+end;
+
+class procedure TCurve25519Field.Negate(const x, z: TCryptoLibUInt32Array);
+begin
+  if (TNat256.IsZero(x)) then
+  begin
+    TNat256.Zero(z);
+  end
+  else
+  begin
+    TNat256.Sub(P, x, z);
+  end;
+end;
+
+class procedure TCurve25519Field.Reduce27(x: UInt32;
+  const z: TCryptoLibUInt32Array);
+var
+  z7, c: UInt32;
+begin
+{$IFDEF DEBUG}
+  System.Assert(((x shr 26) = 0));
+{$ENDIF DEBUG}
+  z7 := z[7];
+  c := ((x shl 1) or (z7 shr 31));
+  z7 := z7 and P7;
+  z7 := z7 + (TNat.AddWordTo(7, c * PInv, z));
+  z[7] := z7;
+  if ((z7 >= P7) and (TNat256.Gte(z, P))) then
+  begin
+    SubPFrom(z);
+  end;
+end;
+
+class procedure TCurve25519Field.Square(const x, z: TCryptoLibUInt32Array);
+var
+  tt: TCryptoLibUInt32Array;
+begin
+  tt := TNat256.CreateExt();
+  TNat256.Square(x, tt);
+  Reduce(tt, z);
+end;
+
+class procedure TCurve25519Field.SquareN(const x: TCryptoLibUInt32Array;
+  n: Int32; const z: TCryptoLibUInt32Array);
+var
+  tt: TCryptoLibUInt32Array;
+begin
+{$IFDEF DEBUG}
+  System.Assert(n > 0);
+{$ENDIF DEBUG}
+  tt := TNat256.CreateExt();
+  TNat256.Square(x, tt);
+  Reduce(tt, z);
+  System.Dec(n);
+  while (n > 0) do
+  begin
+    TNat256.Square(z, tt);
+    Reduce(tt, z);
+    System.Dec(n);
+  end;
+end;
+
+class procedure TCurve25519Field.Subtract(const x, y, z: TCryptoLibUInt32Array);
+var
+  c: Int32;
+begin
+  c := TNat256.Sub(x, y, z);
+  if (c <> 0) then
+  begin
+    AddPTo(z);
+  end;
+end;
+
+class procedure TCurve25519Field.SubtractExt(const xx, yy,
+  zz: TCryptoLibUInt32Array);
+var
+  c: Int32;
+begin
+  c := TNat.Sub(16, xx, yy, zz);
+  if (c <> 0) then
+  begin
+    AddPExtTo(zz);
+  end;
+end;
+
+class procedure TCurve25519Field.Twice(const x, z: TCryptoLibUInt32Array);
+begin
+  TNat.ShiftUpBit(8, x, 0, z);
+  if (TNat256.Gte(z, P)) then
+  begin
+    SubPFrom(z);
+  end;
+end;
+
+{ TCurve25519FieldElement }
+
+class function TCurve25519FieldElement.GetQ: TBigInteger;
+begin
+  result := TCurve25519.Curve25519_Q;
+end;
+
+class procedure TCurve25519FieldElement.Boot;
+begin
+  // Calculated as TBigInteger.ValueOf(2).modPow(Q.shiftRight(2), Q)
+  FPRECOMP_POW2 := TCryptoLibUInt32Array.Create($4A0EA0B0, $C4EE1B27, $AD2FE478,
+    $2F431806, $3DFBD7A7, $2B4D0099, $4FC1DF0B, $2B832480);
+end;
+
+class constructor TCurve25519FieldElement.Curve25519FieldElement;
+begin
+  TCurve25519FieldElement.Boot();
+end;
+
+function TCurve25519FieldElement.GetX: TCryptoLibUInt32Array;
+begin
+  result := Fx;
+end;
+
+constructor TCurve25519FieldElement.Create;
+begin
+  Inherited Create();
+  Fx := TNat256.Create();
+end;
+
+constructor TCurve25519FieldElement.Create(const x: TBigInteger);
+begin
+  if ((not(x.IsInitialized)) or (x.SignValue < 0) or (x.CompareTo(Q) >= 0)) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt
+      (@SInvalidValueForCurve25519FieldElement, ['x']);
+  end;
+  Inherited Create();
+  Fx := TCurve25519Field.FromBigInteger(x);
+end;
+
+constructor TCurve25519FieldElement.Create(const x: TCryptoLibUInt32Array);
+begin
+  Inherited Create();
+  Fx := x;
+end;
+
+function TCurve25519FieldElement.GetFieldName: string;
+begin
+  result := 'Curve25519Field';
+end;
+
+function TCurve25519FieldElement.GetFieldSize: Int32;
+begin
+  result := Q.BitLength;
+end;
+
+function TCurve25519FieldElement.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt;
+{$ENDIF DELPHI}
+begin
+  result := Q.GetHashCode() xor TArrayUtils.GetArrayHashCode(Fx, 0, 8);
+end;
+
+function TCurve25519FieldElement.GetIsOne: Boolean;
+begin
+  result := TNat256.IsOne(Fx);
+end;
+
+function TCurve25519FieldElement.GetIsZero: Boolean;
+begin
+  result := TNat256.IsZero(Fx);
+end;
+
+function TCurve25519FieldElement.Invert: IECFieldElement;
+var
+  z: TCryptoLibUInt32Array;
+begin
+  z := TNat256.Create();
+  TMod.Invert(TCurve25519Field.P, Fx, z);
+  result := TCurve25519FieldElement.Create(z);
+end;
+
+function TCurve25519FieldElement.Multiply(const b: IECFieldElement)
+  : IECFieldElement;
+var
+  z: TCryptoLibUInt32Array;
+begin
+  z := TNat256.Create();
+  TCurve25519Field.Multiply(Fx, (b as ICurve25519FieldElement).x, z);
+  result := TCurve25519FieldElement.Create(z);
+end;
+
+function TCurve25519FieldElement.Negate: IECFieldElement;
+var
+  z: TCryptoLibUInt32Array;
+begin
+  z := TNat256.Create();
+  TCurve25519Field.Negate(Fx, z);
+  result := TCurve25519FieldElement.Create(z);
+end;
+
+function TCurve25519FieldElement.Sqrt: IECFieldElement;
+var
+  x1, x2, x3, x4, x7, x11, x15, x30, x60, x120, x131, x251, t1,
+    t2: TCryptoLibUInt32Array;
+begin
+
+  (*
+    * Q == 8m + 5, so we use Pocklington's method for this case.
+    *
+    * First, raise this element to the exponent 2^252 - 2^1 (i.e. m + 1)
+    * 
+    * Breaking up the exponent's binary representation into "repunits", we get:
+    * { 251 1s } { 1 0s }
+    * 
+    * Therefore we need an addition chain containing 251 (the lengths of the repunits)
+    * We use: 1, 2, 3, 4, 7, 11, 15, 30, 60, 120, 131, [251]
+  *)
+
+  x1 := Fx;
+  if ((TNat256.IsZero(x1)) or (TNat256.IsOne(x1))) then
+  begin
+    result := Self as IECFieldElement;
+    Exit;
+  end;
+
+  x2 := TNat256.Create();
+  TCurve25519Field.Square(x1, x2);
+  TCurve25519Field.Multiply(x2, x1, x2);
+  x3 := x2;
+  TCurve25519Field.Square(x2, x3);
+  TCurve25519Field.Multiply(x3, x1, x3);
+  x4 := TNat256.Create();
+  TCurve25519Field.Square(x3, x4);
+  TCurve25519Field.Multiply(x4, x1, x4);
+  x7 := TNat256.Create();
+  TCurve25519Field.SquareN(x4, 3, x7);
+  TCurve25519Field.Multiply(x7, x3, x7);
+  x11 := x3;
+  TCurve25519Field.SquareN(x7, 4, x11);
+  TCurve25519Field.Multiply(x11, x4, x11);
+  x15 := x7;
+  TCurve25519Field.SquareN(x11, 4, x15);
+  TCurve25519Field.Multiply(x15, x4, x15);
+  x30 := x4;
+  TCurve25519Field.SquareN(x15, 15, x30);
+  TCurve25519Field.Multiply(x30, x15, x30);
+  x60 := x15;
+  TCurve25519Field.SquareN(x30, 30, x60);
+  TCurve25519Field.Multiply(x60, x30, x60);
+  x120 := x30;
+  TCurve25519Field.SquareN(x60, 60, x120);
+  TCurve25519Field.Multiply(x120, x60, x120);
+  x131 := x60;
+  TCurve25519Field.SquareN(x120, 11, x131);
+  TCurve25519Field.Multiply(x131, x11, x131);
+  x251 := x11;
+  TCurve25519Field.SquareN(x131, 120, x251);
+  TCurve25519Field.Multiply(x251, x120, x251);
+
+  t1 := x251;
+  TCurve25519Field.Square(t1, t1);
+
+  t2 := x120;
+  TCurve25519Field.Square(t1, t2);
+
+  if (TNat256.Eq(x1, t2)) then
+  begin
+    result := TCurve25519FieldElement.Create(t1);
+    Exit;
+  end;
+
+  (*
+    * If the first guess is incorrect, we multiply by a precomputed power of 2 to get the second guess,
+    * which is ((4x)^(m + 1))/2 mod Q
+  *)
+  TCurve25519Field.Multiply(t1, FPRECOMP_POW2, t1);
+
+  TCurve25519Field.Square(t1, t2);
+
+  if (TNat256.Eq(x1, t2)) then
+  begin
+    result := TCurve25519FieldElement.Create(t1);
+    Exit;
+  end;
+
+  result := Nil;
+
+end;
+
+function TCurve25519FieldElement.Square: IECFieldElement;
+var
+  z: TCryptoLibUInt32Array;
+begin
+  z := TNat256.Create();
+  TCurve25519Field.Square(Fx, z);
+  result := TCurve25519FieldElement.Create(z);
+end;
+
+function TCurve25519FieldElement.Subtract(const b: IECFieldElement)
+  : IECFieldElement;
+var
+  z: TCryptoLibUInt32Array;
+begin
+  z := TNat256.Create();
+  TCurve25519Field.Subtract(Fx, (b as ICurve25519FieldElement).x, z);
+  result := TCurve25519FieldElement.Create(z);
+end;
+
+function TCurve25519FieldElement.TestBitZero: Boolean;
+begin
+  result := TNat256.GetBit(Fx, 0) = 1;
+end;
+
+function TCurve25519FieldElement.ToBigInteger: TBigInteger;
+begin
+  result := TNat256.ToBigInteger(Fx);
+end;
+
+function TCurve25519FieldElement.Add(const b: IECFieldElement): IECFieldElement;
+var
+  z: TCryptoLibUInt32Array;
+begin
+  z := TNat256.Create();
+  TCurve25519Field.Add(x, (b as ICurve25519FieldElement).x, z);
+  result := TCurve25519FieldElement.Create(z);
+end;
+
+function TCurve25519FieldElement.AddOne: IECFieldElement;
+var
+  z: TCryptoLibUInt32Array;
+begin
+  z := TNat256.Create();
+  TCurve25519Field.AddOne(Fx, z);
+  result := TCurve25519FieldElement.Create(z);
+end;
+
+function TCurve25519FieldElement.Divide(const b: IECFieldElement)
+  : IECFieldElement;
+var
+  z: TCryptoLibUInt32Array;
+begin
+  z := TNat256.Create();
+  TMod.Invert(TCurve25519Field.P, (b as ICurve25519FieldElement).x, z);
+  TCurve25519Field.Multiply(z, Fx, z);
+  result := TCurve25519FieldElement.Create(z);
+end;
+
+function TCurve25519FieldElement.Equals(const other
+  : ICurve25519FieldElement): Boolean;
+begin
+  if ((Self as ICurve25519FieldElement) = other) then
+  begin
+    result := true;
+    Exit;
+  end;
+  if (other = Nil) then
+  begin
+    result := false;
+    Exit;
+  end;
+  result := TNat256.Eq(Fx, other.x);
+end;
+
+function TCurve25519FieldElement.Equals(const other: IECFieldElement): Boolean;
+begin
+  result := Equals(other as ICurve25519FieldElement);
+end;
+
+{ TCurve25519Point }
+
+function TCurve25519Point.Detach: IECPoint;
+begin
+  result := TCurve25519Point.Create(Nil, AffineXCoord, AffineYCoord);
+end;
+
+function TCurve25519Point.CalculateJacobianModifiedW
+  (const z: ICurve25519FieldElement; const ZSquared: TCryptoLibUInt32Array)
+  : ICurve25519FieldElement;
+var
+  a4, W: ICurve25519FieldElement;
+  LZSquared: TCryptoLibUInt32Array;
+begin
+  a4 := curve.A as ICurve25519FieldElement;
+  if (z.IsOne) then
+  begin
+    result := a4;
+    Exit;
+  end;
+
+  W := TCurve25519FieldElement.Create();
+  LZSquared := ZSquared;
+  if (LZSquared = Nil) then
+  begin
+    LZSquared := W.x;
+    TCurve25519Field.Square(z.x, LZSquared);
+  end;
+  TCurve25519Field.Square(LZSquared, W.x);
+  TCurve25519Field.Multiply(W.x, a4.x, W.x);
+  result := W;
+end;
+
+function TCurve25519Point.GetJacobianModifiedW: ICurve25519FieldElement;
+var
+  zz: TCryptoLibGenericArray<IECFieldElement>;
+  W: ICurve25519FieldElement;
+begin
+  zz := RawZCoords;
+  W := zz[1] as ICurve25519FieldElement;
+  if (W = Nil) then
+  begin
+    // NOTE: Rarely, TwicePlus will result in the need for a lazy W1 calculation here
+    W := CalculateJacobianModifiedW(zz[0] as ICurve25519FieldElement, Nil);
+    zz[1] := W;
+  end;
+  result := W;
+end;
+
+function TCurve25519Point.TwiceJacobianModified(calculateW: Boolean)
+  : ICurve25519Point;
+var
+  x1, Y1, Z1, W1, x3, Y3, Z3, W3: ICurve25519FieldElement;
+  c: UInt32;
+  M, _2Y1, _2Y1Squared, S, _8T: TCryptoLibUInt32Array;
+begin
+  x1 := RawXCoord as ICurve25519FieldElement;
+  Y1 := RawYCoord as ICurve25519FieldElement;
+  Z1 := RawZCoords[0] as ICurve25519FieldElement;
+  W1 := GetJacobianModifiedW();
+
+  M := TNat256.Create();
+  TCurve25519Field.Square(x1.x, M);
+  c := TNat256.AddBothTo(M, M, M);
+  c := c + TNat256.AddTo(W1.x, M);
+  TCurve25519Field.Reduce27(c, M);
+
+  _2Y1 := TNat256.Create();
+  TCurve25519Field.Twice(Y1.x, _2Y1);
+
+  _2Y1Squared := TNat256.Create();
+  TCurve25519Field.Multiply(_2Y1, Y1.x, _2Y1Squared);
+
+  S := TNat256.Create();
+  TCurve25519Field.Multiply(_2Y1Squared, x1.x, S);
+  TCurve25519Field.Twice(S, S);
+
+  _8T := TNat256.Create();
+  TCurve25519Field.Square(_2Y1Squared, _8T);
+  TCurve25519Field.Twice(_8T, _8T);
+
+  x3 := TCurve25519FieldElement.Create(_2Y1Squared);
+  TCurve25519Field.Square(M, x3.x);
+  TCurve25519Field.Subtract(x3.x, S, x3.x);
+  TCurve25519Field.Subtract(x3.x, S, x3.x);
+
+  Y3 := TCurve25519FieldElement.Create(S);
+  TCurve25519Field.Subtract(S, x3.x, Y3.x);
+  TCurve25519Field.Multiply(Y3.x, M, Y3.x);
+  TCurve25519Field.Subtract(Y3.x, _8T, Y3.x);
+
+  Z3 := TCurve25519FieldElement.Create(_2Y1);
+  if (not(TNat256.IsOne(Z1.x))) then
+  begin
+    TCurve25519Field.Multiply(Z3.x, Z1.x, Z3.x);
+  end;
+
+  W3 := Nil;
+  if (calculateW) then
+  begin
+    W3 := TCurve25519FieldElement.Create(_8T);
+    TCurve25519Field.Multiply(W3.x, W1.x, W3.x);
+    TCurve25519Field.Twice(W3.x, W3.x);
+  end;
+
+  result := TCurve25519Point.Create(curve, x3, Y3,
+    TCryptoLibGenericArray<IECFieldElement>.Create(Z3, W3), IsCompressed);
+end;
+
+function TCurve25519Point.GetZCoord(index: Int32): IECFieldElement;
+begin
+  if (index = 1) then
+  begin
+    result := GetJacobianModifiedW();
+    Exit;
+  end;
+
+  result := Inherited GetZCoord(index);
+end;
+
+function TCurve25519Point.Add(const b: IECPoint): IECPoint;
+var
+  LCurve: IECCurve;
+  x1, Y1, Z1, x2, Y2, Z2, x3, Y3, Z3, W3: ICurve25519FieldElement;
+  c: UInt32;
+  tt1, t2, t3, t4, U2, S2, U1, S1, H, R, G, V, HSquared,
+    Z3Squared: TCryptoLibUInt32Array;
+  zs: TCryptoLibGenericArray<IECFieldElement>;
+  Z1IsOne, Z2IsOne: Boolean;
+begin
+  if (IsInfinity) then
+  begin
+    result := b;
+    Exit;
+  end;
+  if (b.IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+  if ((Self as IECPoint) = b) then
+  begin
+    result := Twice();
+    Exit;
+  end;
+
+  LCurve := curve;
+
+  x1 := RawXCoord as ICurve25519FieldElement;
+  Y1 := RawYCoord as ICurve25519FieldElement;
+  Z1 := RawZCoords[0] as ICurve25519FieldElement;
+  x2 := b.RawXCoord as ICurve25519FieldElement;
+  Y2 := b.RawYCoord as ICurve25519FieldElement;
+  Z2 := b.RawZCoords[0] as ICurve25519FieldElement;
+
+  tt1 := TNat256.CreateExt();
+  t2 := TNat256.Create();
+  t3 := TNat256.Create();
+  t4 := TNat256.Create();
+
+  Z1IsOne := Z1.IsOne;
+  if (Z1IsOne) then
+  begin
+    U2 := x2.x;
+    S2 := Y2.x;
+  end
+  else
+  begin
+    S2 := t3;
+    TCurve25519Field.Square(Z1.x, S2);
+
+    U2 := t2;
+    TCurve25519Field.Multiply(S2, x2.x, U2);
+
+    TCurve25519Field.Multiply(S2, Z1.x, S2);
+    TCurve25519Field.Multiply(S2, Y2.x, S2);
+  end;
+
+  Z2IsOne := Z2.IsOne;
+
+  if (Z2IsOne) then
+  begin
+    U1 := x1.x;
+    S1 := Y1.x;
+  end
+  else
+  begin
+    S1 := t4;
+    TCurve25519Field.Square(Z2.x, S1);
+
+    U1 := tt1;
+    TCurve25519Field.Multiply(S1, x1.x, U1);
+
+    TCurve25519Field.Multiply(S1, Z2.x, S1);
+    TCurve25519Field.Multiply(S1, Y1.x, S1);
+  end;
+
+  H := TNat256.Create();
+  TCurve25519Field.Subtract(U1, U2, H);
+
+  R := t2;
+  TCurve25519Field.Subtract(S1, S2, R);
+
+  // Check if b = Self or b = -Self
+  if (TNat256.IsZero(H)) then
+  begin
+    if (TNat256.IsZero(R)) then
+    begin
+      // this == b, i.e. this must be doubled
+      result := Twice();
+      Exit;
+    end;
+
+    // Self = -b, i.e. the result is the point at infinity
+    result := LCurve.Infinity;
+    Exit;
+  end;
+
+  HSquared := TNat256.Create();
+  TCurve25519Field.Square(H, HSquared);
+
+  G := TNat256.Create();
+  TCurve25519Field.Multiply(HSquared, H, G);
+
+  V := t3;
+  TCurve25519Field.Multiply(HSquared, U1, V);
+
+  TCurve25519Field.Negate(G, G);
+  TNat256.Mul(S1, G, tt1);
+
+  c := TNat256.AddBothTo(V, V, G);
+  TCurve25519Field.Reduce27(c, G);
+
+  x3 := TCurve25519FieldElement.Create(t4);
+  TCurve25519Field.Square(R, x3.x);
+  TCurve25519Field.Subtract(x3.x, G, x3.x);
+
+  Y3 := TCurve25519FieldElement.Create(G);
+  TCurve25519Field.Subtract(V, x3.x, Y3.x);
+  TCurve25519Field.MultiplyAddToExt(Y3.x, R, tt1);
+  TCurve25519Field.Reduce(tt1, Y3.x);
+
+  Z3 := TCurve25519FieldElement.Create(H);
+  if (not(Z1IsOne)) then
+  begin
+    TCurve25519Field.Multiply(Z3.x, Z1.x, Z3.x);
+  end;
+  if (not(Z2IsOne)) then
+  begin
+    TCurve25519Field.Multiply(Z3.x, Z2.x, Z3.x);
+  end;
+
+  if ((Z1IsOne) and (Z2IsOne)) then
+  begin
+    Z3Squared := HSquared;
+  end
+  else
+  begin
+    Z3Squared := Nil;
+  end;
+
+  // TODO If the result will only be used in a subsequent addition, we don't need W3
+  W3 := CalculateJacobianModifiedW(Z3, Z3Squared);
+
+  zs := TCryptoLibGenericArray<IECFieldElement>.Create(Z3, W3);
+
+  result := TCurve25519Point.Create(LCurve, x3, Y3, zs, IsCompressed);
+end;
+
+constructor TCurve25519Point.Create(const curve: IECCurve;
+  const x, y: IECFieldElement;
+  const zs: TCryptoLibGenericArray<IECFieldElement>; withCompression: Boolean);
+begin
+  Inherited Create(curve, x, y, zs, withCompression);
+end;
+
+constructor TCurve25519Point.Create(const curve: IECCurve;
+  const x, y: IECFieldElement; withCompression: Boolean);
+begin
+  Inherited Create(curve, x, y, withCompression);
+  if ((x = Nil) <> (y = Nil)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SOneOfECFieldElementIsNil);
+  end;
+end;
+
+constructor TCurve25519Point.Create(const curve: IECCurve;
+  const x, y: IECFieldElement);
+begin
+  Create(curve, x, y, false);
+end;
+
+function TCurve25519Point.Negate: IECPoint;
+begin
+  if (IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  result := TCurve25519Point.Create(curve, RawXCoord, RawYCoord.Negate(),
+    RawZCoords, IsCompressed);
+end;
+
+function TCurve25519Point.ThreeTimes: IECPoint;
+begin
+  if ((IsInfinity) or (RawYCoord.IsZero)) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  result := TwiceJacobianModified(false).Add(Self as ICurve25519Point);
+end;
+
+function TCurve25519Point.Twice: IECPoint;
+var
+  LCurve: IECCurve;
+  Y1: IECFieldElement;
+begin
+  if (IsInfinity) then
+  begin
+    result := Self;
+    Exit;
+  end;
+
+  LCurve := curve;
+
+  Y1 := RawYCoord;
+  if (Y1.IsZero) then
+  begin
+    result := LCurve.Infinity;
+    Exit;
+  end;
+
+  result := TwiceJacobianModified(true);
+end;
+
+function TCurve25519Point.TwicePlus(const b: IECPoint): IECPoint;
+var
+  Y1: IECFieldElement;
+begin
+  if ((Self as IECPoint) = b) then
+  begin
+    result := ThreeTimes();
+    Exit;
+  end;
+  if (IsInfinity) then
+  begin
+    result := b;
+    Exit;
+  end;
+  if (b.IsInfinity) then
+  begin
+    result := Twice();
+    Exit;
+  end;
+
+  Y1 := RawYCoord;
+  if (Y1.IsZero) then
+  begin
+    result := b;
+    Exit;
+  end;
+
+  result := TwiceJacobianModified(false).Add(b);
+end;
+
+{ TCurve25519 }
+
+class function TCurve25519.GetCurve25519_Q: TBigInteger;
+begin
+  result := TNat256.ToBigInteger(TCurve25519Field.P);
+end;
+
+constructor TCurve25519.Create;
+begin
+  Fq := Curve25519_Q;
+  Inherited Create(Fq);
+  Fm_infinity := TCurve25519Point.Create(Self as IECCurve, Nil, Nil);
+
+  Fm_a := FromBigInteger(TBigInteger.Create(1,
+    THex.Decode
+    ('2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA984914A144')));
+  Fm_b := FromBigInteger(TBigInteger.Create(1,
+    THex.Decode
+    ('7B425ED097B425ED097B425ED097B425ED097B425ED097B4260B5E9C7710C864')));
+  Fm_order := TBigInteger.Create(1,
+    THex.Decode
+    ('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED'));
+  Fm_cofactor := TBigInteger.ValueOf(8);
+  Fm_coord := Curve25519_DEFAULT_COORDS;
+end;
+
+function TCurve25519.CloneCurve: IECCurve;
+begin
+  result := TCurve25519.Create();
+end;
+
+function TCurve25519.CreateCacheSafeLookupTable(const points
+  : TCryptoLibGenericArray<IECPoint>; off, len: Int32): IECLookupTable;
+var
+  table: TCryptoLibUInt32Array;
+  pos, i: Int32;
+  P: IECPoint;
+begin
+  System.SetLength(table, len * CURVE25519_FE_INTS * 2);
+
+  pos := 0;
+  for i := 0 to System.Pred(len) do
+  begin
+    P := points[off + i];
+    TNat256.Copy((P.RawXCoord as ICurve25519FieldElement).x, 0, table, pos);
+    pos := pos + CURVE25519_FE_INTS;
+    TNat256.Copy((P.RawYCoord as ICurve25519FieldElement).x, 0, table, pos);
+    pos := pos + CURVE25519_FE_INTS;
+  end;
+
+  result := TCurve25519LookupTable.Create(Self as ICurve25519, table, len);
+end;
+
+function TCurve25519.CreateRawPoint(const x, y: IECFieldElement;
+  withCompression: Boolean): IECPoint;
+begin
+  result := TCurve25519Point.Create(Self as IECCurve, x, y, withCompression);
+end;
+
+function TCurve25519.CreateRawPoint(const x, y: IECFieldElement;
+  const zs: TCryptoLibGenericArray<IECFieldElement>; withCompression: Boolean)
+  : IECPoint;
+begin
+  result := TCurve25519Point.Create(Self as IECCurve, x, y, zs,
+    withCompression);
+end;
+
+function TCurve25519.FromBigInteger(const x: TBigInteger): IECFieldElement;
+begin
+  result := TCurve25519FieldElement.Create(x);
+end;
+
+function TCurve25519.GetFieldSize: Int32;
+begin
+  result := Fq.BitLength;
+end;
+
+function TCurve25519.GetInfinity: IECPoint;
+begin
+  result := Fm_infinity;
+end;
+
+function TCurve25519.GetQ: TBigInteger;
+begin
+  result := Fq;
+end;
+
+function TCurve25519.SupportsCoordinateSystem(coord: Int32): Boolean;
+begin
+  case coord of
+    TECCurveConstants.COORD_JACOBIAN_MODIFIED:
+      result := true
+  else
+    result := false;
+  end;
+end;
+
+{ TCurve25519.TCurve25519LookupTable }
+
+constructor TCurve25519.TCurve25519LookupTable.Create(const outer: ICurve25519;
+  const table: TCryptoLibUInt32Array; size: Int32);
+begin
+  Inherited Create();
+  Fm_outer := outer;
+  Fm_table := table;
+  Fm_size := size;
+end;
+
+function TCurve25519.TCurve25519LookupTable.GetSize: Int32;
+begin
+  result := Fm_size;
+end;
+
+function TCurve25519.TCurve25519LookupTable.Lookup(index: Int32): IECPoint;
+var
+  x, y: TCryptoLibUInt32Array;
+  pos, i, J: Int32;
+  MASK: UInt32;
+begin
+  x := TNat256.Create();
+  y := TNat256.Create();
+  pos := 0;
+
+  for i := 0 to System.Pred(Fm_size) do
+  begin
+    MASK := UInt32(TBits.Asr32((i xor index) - 1, 31));
+
+    for J := 0 to System.Pred(CURVE25519_FE_INTS) do
+    begin
+      x[J] := x[J] xor (Fm_table[pos + J] and MASK);
+      y[J] := y[J] xor (Fm_table[pos + CURVE25519_FE_INTS + J] and MASK);
+    end;
+
+    pos := pos + (CURVE25519_FE_INTS * 2);
+  end;
+
+  result := Fm_outer.CreateRawPoint(TCurve25519FieldElement.Create(x)
+    as ICurve25519FieldElement, TCurve25519FieldElement.Create(y)
+    as ICurve25519FieldElement, false);
+end;
+
+end.

+ 10 - 0
CryptoLib/src/Utils/ClpCryptoLibTypes.pas

@@ -207,6 +207,16 @@ type
   TCryptoLibMatrixUInt64Array = array of TCryptoLibUInt64Array;
 {$ENDIF DELPHIXE_UP}
 
+  TCustomArrayBuffer<T> = record
+  public
+    Data: TCryptoLibGenericArray<T>;
+    Length: Int32;
+    IsNil: Boolean;
+  end;
+
+const
+  EmptyBytesNotNil: TCustomArrayBuffer<Byte> = (Data: Nil; Length: 0; IsNil: False);
+
 implementation
 
 {$IFDEF FPC}