diff --git a/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr b/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr
index c36af03c..3bdc610e 100644
--- a/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr
+++ b/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr
@@ -389,6 +389,12 @@ uses
ClpMacUtilities in '..\..\CryptoLib\src\Crypto\Macs\ClpMacUtilities.pas',
ClpMiscObjectIdentifiers in '..\..\CryptoLib\src\Asn1\Misc\ClpMiscObjectIdentifiers.pas',
ClpMultipliers in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpMultipliers.pas',
+ ClpIFpFieldOps in '..\..\CryptoLib\src\Interfaces\Math\EC\Multiplier\ClpIFpFieldOps.pas',
+ ClpHomogeneousPoint in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpHomogeneousPoint.pas',
+ ClpFixedWindowCTMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpFixedWindowCTMultiplier.pas',
+ ClpIF2mFieldOps in '..\..\CryptoLib\src\Interfaces\Math\EC\Multiplier\ClpIF2mFieldOps.pas',
+ ClpLopezDahabLadder in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpLopezDahabLadder.pas',
+ ClpF2mMontgomeryLadderCTMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpF2mMontgomeryLadderCTMultiplier.pas',
ClpNistNamedCurves in '..\..\CryptoLib\src\Asn1\Nist\ClpNistNamedCurves.pas',
ClpNistObjectIdentifiers in '..\..\CryptoLib\src\Asn1\Nist\ClpNistObjectIdentifiers.pas',
ClpNoOpDigest in '..\..\CryptoLib\src\Crypto\Digests\ClpNoOpDigest.pas',
diff --git a/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dpr b/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dpr
index ce023fd7..af1217c3 100644
--- a/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dpr
+++ b/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dpr
@@ -406,6 +406,12 @@ uses
ClpMacUtilities in '..\..\CryptoLib\src\Crypto\Macs\ClpMacUtilities.pas',
ClpMiscObjectIdentifiers in '..\..\CryptoLib\src\Asn1\Misc\ClpMiscObjectIdentifiers.pas',
ClpMultipliers in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpMultipliers.pas',
+ ClpIFpFieldOps in '..\..\CryptoLib\src\Interfaces\Math\EC\Multiplier\ClpIFpFieldOps.pas',
+ ClpHomogeneousPoint in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpHomogeneousPoint.pas',
+ ClpFixedWindowCTMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpFixedWindowCTMultiplier.pas',
+ ClpIF2mFieldOps in '..\..\CryptoLib\src\Interfaces\Math\EC\Multiplier\ClpIF2mFieldOps.pas',
+ ClpLopezDahabLadder in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpLopezDahabLadder.pas',
+ ClpF2mMontgomeryLadderCTMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpF2mMontgomeryLadderCTMultiplier.pas',
ClpNistNamedCurves in '..\..\CryptoLib\src\Asn1\Nist\ClpNistNamedCurves.pas',
ClpNistObjectIdentifiers in '..\..\CryptoLib\src\Asn1\Nist\ClpNistObjectIdentifiers.pas',
ClpNoOpDigest in '..\..\CryptoLib\src\Crypto\Digests\ClpNoOpDigest.pas',
diff --git a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr
index 1118f259..14080409 100644
--- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr
+++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr
@@ -383,6 +383,12 @@ uses
ClpMacUtilities in '..\..\CryptoLib\src\Crypto\Macs\ClpMacUtilities.pas',
ClpMiscObjectIdentifiers in '..\..\CryptoLib\src\Asn1\Misc\ClpMiscObjectIdentifiers.pas',
ClpMultipliers in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpMultipliers.pas',
+ ClpIFpFieldOps in '..\..\CryptoLib\src\Interfaces\Math\EC\Multiplier\ClpIFpFieldOps.pas',
+ ClpHomogeneousPoint in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpHomogeneousPoint.pas',
+ ClpFixedWindowCTMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpFixedWindowCTMultiplier.pas',
+ ClpIF2mFieldOps in '..\..\CryptoLib\src\Interfaces\Math\EC\Multiplier\ClpIF2mFieldOps.pas',
+ ClpLopezDahabLadder in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpLopezDahabLadder.pas',
+ ClpF2mMontgomeryLadderCTMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpF2mMontgomeryLadderCTMultiplier.pas',
ClpNistNamedCurves in '..\..\CryptoLib\src\Asn1\Nist\ClpNistNamedCurves.pas',
ClpNistObjectIdentifiers in '..\..\CryptoLib\src\Asn1\Nist\ClpNistObjectIdentifiers.pas',
ClpNoOpDigest in '..\..\CryptoLib\src\Crypto\Digests\ClpNoOpDigest.pas',
@@ -929,6 +935,8 @@ uses
//FixedPointTests in '..\src\Math\EC\FixedPointTests.pas',
AESTests in '..\src\Crypto\AESTests.pas',
AesBitSlicedTests in '..\src\Crypto\AesBitSlicedTests.pas',
+ ECDHPrimeConstantTimeTests in '..\src\Crypto\ECDHPrimeConstantTimeTests.pas',
+ ECDHBinaryConstantTimeTests in '..\src\Crypto\ECDHBinaryConstantTimeTests.pas',
BlockCipherTestBase in '..\src\Crypto\BlockCipherTestBase.pas',
AesBlockCipherTestBase in '..\src\Crypto\AesBlockCipherTestBase.pas',
AesLightTests in '..\src\Crypto\AesLightTests.pas',
diff --git a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj
index 660cb15e..433ea020 100644
--- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj
+++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj
@@ -726,6 +726,12 @@
+
+
+
+
+
+
@@ -1268,6 +1274,8 @@
+
+
diff --git a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr
index 7d0f5f1f..1bea3bb2 100644
--- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr
+++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr
@@ -402,6 +402,12 @@ uses
ClpMacUtilities in '..\..\CryptoLib\src\Crypto\Macs\ClpMacUtilities.pas',
ClpMiscObjectIdentifiers in '..\..\CryptoLib\src\Asn1\Misc\ClpMiscObjectIdentifiers.pas',
ClpMultipliers in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpMultipliers.pas',
+ ClpIFpFieldOps in '..\..\CryptoLib\src\Interfaces\Math\EC\Multiplier\ClpIFpFieldOps.pas',
+ ClpHomogeneousPoint in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpHomogeneousPoint.pas',
+ ClpFixedWindowCTMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpFixedWindowCTMultiplier.pas',
+ ClpIF2mFieldOps in '..\..\CryptoLib\src\Interfaces\Math\EC\Multiplier\ClpIF2mFieldOps.pas',
+ ClpLopezDahabLadder in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpLopezDahabLadder.pas',
+ ClpF2mMontgomeryLadderCTMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpF2mMontgomeryLadderCTMultiplier.pas',
ClpNistNamedCurves in '..\..\CryptoLib\src\Asn1\Nist\ClpNistNamedCurves.pas',
ClpNistObjectIdentifiers in '..\..\CryptoLib\src\Asn1\Nist\ClpNistObjectIdentifiers.pas',
ClpNoOpDigest in '..\..\CryptoLib\src\Crypto\Digests\ClpNoOpDigest.pas',
@@ -950,6 +956,8 @@ uses
FixedPointTests in '..\src\Math\EC\FixedPointTests.pas',
AESTests in '..\src\Crypto\AESTests.pas',
AesBitSlicedTests in '..\src\Crypto\AesBitSlicedTests.pas',
+ ECDHPrimeConstantTimeTests in '..\src\Crypto\ECDHPrimeConstantTimeTests.pas',
+ ECDHBinaryConstantTimeTests in '..\src\Crypto\ECDHBinaryConstantTimeTests.pas',
BlockCipherTestBase in '..\src\Crypto\BlockCipherTestBase.pas',
AesBlockCipherTestBase in '..\src\Crypto\AesBlockCipherTestBase.pas',
AesLightTests in '..\src\Crypto\AesLightTests.pas',
diff --git a/CryptoLib.Tests/FreePascal.Tests/CryptoLib.Tests.lpi b/CryptoLib.Tests/FreePascal.Tests/CryptoLib.Tests.lpi
index f5889648..182f6106 100644
--- a/CryptoLib.Tests/FreePascal.Tests/CryptoLib.Tests.lpi
+++ b/CryptoLib.Tests/FreePascal.Tests/CryptoLib.Tests.lpi
@@ -79,7 +79,7 @@
-
+
@@ -901,6 +901,14 @@
+
+
+
+
+
+
+
+
diff --git a/CryptoLib.Tests/FreePascal.Tests/CryptoLib.lpr b/CryptoLib.Tests/FreePascal.Tests/CryptoLib.lpr
index 94960e3e..9673bdb0 100644
--- a/CryptoLib.Tests/FreePascal.Tests/CryptoLib.lpr
+++ b/CryptoLib.Tests/FreePascal.Tests/CryptoLib.lpr
@@ -40,9 +40,10 @@
AeadTestUtilities, GcmReorderTests, GCMTests, GcmSivTests, GMacTests,
Pkcs12Tests, Bip327MuSig2Tests, Bip340SchnorrTests, AlgorithmFinderTests,
MlKemTests, MlDsaTests, PqcPkcsTests, SlhDsaTests, Lib25519Tests,
- Asn1CipherBuilderWithKeyTests, AesBitSlicedTests, CryptoLibTestBase,
- PkixFoundationTests, PkixNameConstraintTests, PkixComparerTests,
- CertPathTests, CertPathValidatorTests, PkixPolicyMappingTests, OcspTests,
+ Asn1CipherBuilderWithKeyTests, AesBitSlicedTests, ECDHPrimeConstantTimeTests,
+ ECDHBinaryConstantTimeTests, CryptoLibTestBase, PkixFoundationTests,
+ PkixNameConstraintTests, PkixComparerTests, CertPathTests,
+ CertPathValidatorTests, PkixPolicyMappingTests, OcspTests,
CryptoLibHashSetTests, PkitsTestBase, NistCertPathTests, AttrCertPathTests,
CtrDrbgTests, DrbgTestSupport, HashDrbgTests, HMacDrbgTests,
SimdSelectSlotTests, BinaryPrimitivesTests, PkcsEncryptedPrivateKeyInfoTests,
diff --git a/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpi b/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpi
index a7d39284..05a88bf2 100644
--- a/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpi
+++ b/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpi
@@ -39,7 +39,7 @@
-
+
@@ -860,6 +860,14 @@
+
+
+
+
+
+
+
+
diff --git a/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpr b/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpr
index ff73c72d..a5845554 100644
--- a/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpr
+++ b/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpr
@@ -57,7 +57,8 @@
OpenSslVectors, PkcsVectors, CertVectors, PkitsVectors, PqcSampleCredentials,
CsvVectorLoaderBase, TestKeyBuilders, PemDerCodec, PemReaderVectors,
Argon2Vectors, PqcTestVectors, DrbgTestVectors, Int32Tests, Int64Tests,
- ByteUtilitiesTests, IPAddressUtilitiesTests, PemReaderTests;
+ ByteUtilitiesTests, IPAddressUtilitiesTests, PemReaderTests,
+ ECDHPrimeConstantTimeTests, ECDHBinaryConstantTimeTests;
type
diff --git a/CryptoLib.Tests/src/Crypto/AesBitSlicedTests.pas b/CryptoLib.Tests/src/Crypto/AesBitSlicedTests.pas
index ac44671a..43dc1451 100644
--- a/CryptoLib.Tests/src/Crypto/AesBitSlicedTests.pas
+++ b/CryptoLib.Tests/src/Crypto/AesBitSlicedTests.pas
@@ -79,6 +79,8 @@ TTestAesBitSliced = class(TAesBlockCipherTestBase)
procedure TestAesGcmEndToEnd;
procedure TestInvalidKeyLength;
procedure TestResetAndReInit;
+ procedure TestReInitDifferentKeySize;
+ procedure TestCallerKeyPreserved;
end;
implementation
@@ -444,6 +446,57 @@ procedure TTestAesBitSliced.TestResetAndReInit;
Fail('ProcessBlock after re-Init produced a different result');
end;
+procedure TTestAesBitSliced.TestReInitDifferentKeySize;
+var
+ LBlock, LOut: TBytes;
+ LEngine: IBlockCipher;
+begin
+ LBlock := DecodeHex(KAT_PLAIN);
+ System.SetLength(LOut, 16);
+ LEngine := TAesBitSlicedEngine.Create();
+
+ LEngine.Init(True, TKeyParameter.Create(DecodeHex(KAT_KEY_128)) as ICipherParameters);
+ LEngine.ProcessBlock(LBlock, 0, LOut, 0);
+ if not AreEqual(LOut, DecodeHex(KAT_CIPHER_128)) then
+ Fail('AES-128 encrypt wrong before re-key');
+
+ // Re-Init the same instance with a larger key: exercises the FSkey pre-wipe + realloc.
+ LEngine.Init(True, TKeyParameter.Create(DecodeHex(KAT_KEY_256)) as ICipherParameters);
+ LEngine.ProcessBlock(LBlock, 0, LOut, 0);
+ if not AreEqual(LOut, DecodeHex(KAT_CIPHER_256)) then
+ Fail('AES-256 encrypt wrong after cross-key-size re-Init');
+end;
+
+procedure TTestAesBitSliced.TestCallerKeyPreserved;
+var
+ LKey, LKeyBefore, LBad: TBytes;
+ LEngine: IBlockCipher;
+ LI: Int32;
+ LRaised: Boolean;
+begin
+ // A successful Init must not mutate the caller's key bytes.
+ LKey := DecodeHex(KAT_KEY_128);
+ LKeyBefore := System.Copy(LKey);
+ LEngine := TAesBitSlicedEngine.Create();
+ LEngine.Init(True, TKeyParameter.Create(LKey) as ICipherParameters);
+ if not AreEqual(LKey, LKeyBefore) then
+ Fail('caller key buffer was mutated on a successful Init');
+
+ // An invalid key length must raise (matching TAesEngine).
+ System.SetLength(LBad, 20);
+ for LI := 0 to System.Length(LBad) - 1 do
+ LBad[LI] := Byte($AA);
+ LRaised := False;
+ try
+ LEngine := TAesBitSlicedEngine.Create();
+ LEngine.Init(True, TKeyParameter.Create(LBad) as ICipherParameters);
+ except
+ on E: EArgumentCryptoLibException do
+ LRaised := True;
+ end;
+ CheckTrue(LRaised, 'invalid key length did not raise');
+end;
+
initialization
{$IFDEF FPC}
diff --git a/CryptoLib.Tests/src/Crypto/ECDHBinaryConstantTimeTests.pas b/CryptoLib.Tests/src/Crypto/ECDHBinaryConstantTimeTests.pas
new file mode 100644
index 00000000..f27471ec
--- /dev/null
+++ b/CryptoLib.Tests/src/Crypto/ECDHBinaryConstantTimeTests.pas
@@ -0,0 +1,198 @@
+{ *********************************************************************************** }
+{ * CryptoLib Library * }
+{ * Author - Ugochukwu Mmaduekwe * }
+{ * Github Repository * }
+{ * * }
+{ * 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 * }
+{ * the development of this library * }
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ECDHBinaryConstantTimeTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+ SysUtils,
+{$IFDEF FPC}
+ fpcunit,
+ testregistry,
+{$ELSE}
+ TestFramework,
+{$ENDIF FPC}
+ ClpSecureRandom,
+ ClpISecureRandom,
+ ClpBigInteger,
+ ClpCustomNamedCurves,
+ ClpIX9ECAsn1Objects,
+ ClpMultipliers,
+ ClpIECCommon,
+ ClpPlatformUtilities,
+ ClpCryptoLibTypes,
+ CryptoLibTestBase;
+
+type
+
+ ///
+ /// Verifies the constant-time López–Dahab ladder wired as the default
+ /// multiplier for the custom sect283k1 binary curve.
+ ///
+ TTestECDHBinaryConstantTime = class(TCryptoLibAlgorithmTestCase)
+ private
+ const
+ CurveName = String('sect283k1');
+ TestRounds = Int32(8);
+ var
+ FRandom: ISecureRandom;
+ function GetCurve: IX9ECParameters;
+ function RandomScalar(const AN: TBigInteger): TBigInteger;
+ procedure AssertPointsEqual(const AMsg: String; const AA, AB: IECPoint);
+ protected
+ procedure SetUp; override;
+ procedure TearDown; override;
+ published
+ procedure TestDefaultMultiplierIsConstantTime;
+ procedure TestParityWithWNaf;
+ procedure TestEdgeScalars;
+ procedure TestBlindingTransparency;
+ end;
+
+implementation
+
+{ TTestECDHBinaryConstantTime }
+
+procedure TTestECDHBinaryConstantTime.SetUp;
+begin
+ FRandom := TSecureRandom.Create();
+end;
+
+procedure TTestECDHBinaryConstantTime.TearDown;
+begin
+ inherited;
+end;
+
+function TTestECDHBinaryConstantTime.GetCurve: IX9ECParameters;
+begin
+ Result := TCustomNamedCurves.GetByName(CurveName);
+end;
+
+function TTestECDHBinaryConstantTime.RandomScalar(const AN: TBigInteger): TBigInteger;
+begin
+ Result := TBigInteger.Create(AN.BitLength, FRandom).&Mod(AN);
+ if Result.SignValue = 0 then
+ Result := TBigInteger.One;
+end;
+
+procedure TTestECDHBinaryConstantTime.AssertPointsEqual(const AMsg: String;
+ const AA, AB: IECPoint);
+begin
+ CheckEquals(True, AA.Equals(AB), AMsg);
+ CheckEquals(True, AB.Equals(AA), AMsg);
+end;
+
+procedure TTestECDHBinaryConstantTime.TestDefaultMultiplierIsConstantTime;
+var
+ LX9: IX9ECParameters;
+ LMul: IECMultiplier;
+begin
+ LX9 := GetCurve;
+ CheckTrue(LX9 <> nil, CurveName + ' not found');
+ LMul := LX9.Curve.Multiplier;
+ CheckEquals('TF2mMontgomeryLadderCTMultiplier', TPlatformUtilities.GetTypeName(LMul as TObject),
+ 'default multiplier for ' + CurveName + ' is not the constant-time ladder');
+end;
+
+procedure TTestECDHBinaryConstantTime.TestParityWithWNaf;
+var
+ LJ: Int32;
+ LX9: IX9ECParameters;
+ LCurve: IECCurve;
+ LWNaf, LCT: IECMultiplier;
+ LN, LR, LK: TBigInteger;
+ LQ, LRef, LGot: IECPoint;
+begin
+ LWNaf := TWNafL2RMultiplier.Create() as IECMultiplier;
+ LX9 := GetCurve;
+ LCurve := LX9.Curve;
+ LN := LX9.N;
+ LCT := LCurve.Multiplier;
+ for LJ := 0 to TestRounds - 1 do
+ begin
+ LR := RandomScalar(LN);
+ LQ := LWNaf.Multiply(LX9.G, LR).Normalize();
+ LK := RandomScalar(LN);
+ LRef := LWNaf.Multiply(LQ, LK).Normalize();
+ LGot := LCT.Multiply(LQ, LK).Normalize();
+ AssertPointsEqual('parity ' + CurveName, LRef, LGot);
+ end;
+end;
+
+procedure TTestECDHBinaryConstantTime.TestEdgeScalars;
+var
+ LJ: Int32;
+ LX9: IX9ECParameters;
+ LCurve: IECCurve;
+ LWNaf, LCT: IECMultiplier;
+ LN: TBigInteger;
+ LScalars: TCryptoLibGenericArray;
+ LRef, LGot: IECPoint;
+begin
+ LWNaf := TWNafL2RMultiplier.Create() as IECMultiplier;
+ LX9 := GetCurve;
+ LCurve := LX9.Curve;
+ LN := LX9.N;
+ LCT := LCurve.Multiplier;
+ LScalars := TCryptoLibGenericArray.Create(
+ TBigInteger.One,
+ TBigInteger.Two,
+ TBigInteger.ValueOf(7),
+ LN.Subtract(TBigInteger.One),
+ LN.Subtract(TBigInteger.Two));
+ for LJ := 0 to System.Length(LScalars) - 1 do
+ begin
+ LRef := LWNaf.Multiply(LX9.G, LScalars[LJ]).Normalize();
+ LGot := LCT.Multiply(LX9.G, LScalars[LJ]).Normalize();
+ AssertPointsEqual('edge idx ' + IntToStr(LJ), LRef, LGot);
+ end;
+end;
+
+procedure TTestECDHBinaryConstantTime.TestBlindingTransparency;
+var
+ LX9: IX9ECParameters;
+ LCT: IECMultiplier;
+ LN, LK: TBigInteger;
+ LI: Int32;
+ LFirst, LAgain: IECPoint;
+begin
+ // Randomized internals (scalar blind + projective coordinate) must not change the result.
+ LX9 := GetCurve;
+ LCT := LX9.Curve.Multiplier;
+ LN := LX9.N;
+ for LI := 0 to 5 do
+ begin
+ LK := RandomScalar(LN);
+ LFirst := LCT.Multiply(LX9.G, LK).Normalize();
+ LAgain := LCT.Multiply(LX9.G, LK).Normalize();
+ AssertPointsEqual('transparency', LFirst, LAgain);
+ end;
+end;
+
+initialization
+
+{$IFDEF FPC}
+ RegisterTest(TTestECDHBinaryConstantTime);
+{$ELSE}
+ RegisterTest(TTestECDHBinaryConstantTime.Suite);
+{$ENDIF FPC}
+
+end.
diff --git a/CryptoLib.Tests/src/Crypto/ECDHPrimeConstantTimeTests.pas b/CryptoLib.Tests/src/Crypto/ECDHPrimeConstantTimeTests.pas
new file mode 100644
index 00000000..b33f4715
--- /dev/null
+++ b/CryptoLib.Tests/src/Crypto/ECDHPrimeConstantTimeTests.pas
@@ -0,0 +1,427 @@
+{ *********************************************************************************** }
+{ * CryptoLib Library * }
+{ * Author - Ugochukwu Mmaduekwe * }
+{ * Github Repository * }
+{ * * }
+{ * 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 * }
+{ * the development of this library * }
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ECDHPrimeConstantTimeTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+ SysUtils,
+{$IFDEF FPC}
+ fpcunit,
+ testregistry,
+{$ELSE}
+ TestFramework,
+{$ENDIF FPC}
+ ClpSecureRandom,
+ ClpISecureRandom,
+ ClpBigInteger,
+ ClpNat,
+ ClpCustomNamedCurves,
+ ClpIX9ECAsn1Objects,
+ ClpMultipliers,
+ ClpECAlgorithms,
+ ClpIFpFieldOps,
+ ClpHomogeneousPoint,
+ ClpFixedWindowCTMultiplier,
+ ClpSecP256R1Custom,
+ ClpSecP256K1Custom,
+ ClpSecP384R1Custom,
+ ClpSecP521R1Custom,
+ ClpECParameters,
+ ClpIECParameters,
+ ClpECGenerators,
+ ClpIECGenerators,
+ ClpECDHBasicAgreement,
+ ClpIBasicAgreement,
+ ClpIAsymmetricCipherKeyPair,
+ ClpIAsymmetricCipherKeyPairGenerator,
+ ClpIKeyGenerationParameters,
+ ClpICipherParameters,
+ ClpIECCommon,
+ ClpIECFieldElement,
+ ClpPlatformUtilities,
+ ClpCryptoLibTypes,
+ ClpCryptoLibExceptions,
+ CryptoLibTestBase;
+
+type
+ TTestECDHPrimeConstantTime = class(TCryptoLibAlgorithmTestCase)
+ private
+ const
+ TestsPerCurve = Int32(16);
+ var
+ FRandom: ISecureRandom;
+ function CurveNames: TCryptoLibStringArray;
+ function MakeFieldOps(const AName: String; const ACurve: IECCurve): IFpFieldOps;
+ function RandomScalar(const AN: TBigInteger): TBigInteger;
+ procedure AssertPointsEqual(const AMsg: String; const AA, AB: IECPoint);
+ function HomogFromAffine(const AFO: IFpFieldOps; const AP: IECPoint): TCTHomogPoint;
+ function HomogToPoint(const AFO: IFpFieldOps; const ACurve: IECCurve;
+ const AP: TCTHomogPoint): IECPoint;
+ protected
+ procedure SetUp; override;
+ procedure TearDown; override;
+ published
+ procedure TestDefaultMultiplierIsConstantTime;
+ procedure TestParityWithWNaf;
+ procedure TestEdgeScalars;
+ procedure TestBlindingTransparency;
+ procedure TestExceptionalFormulas;
+ procedure TestECDHAgreement;
+ procedure TestScalarRangeGuard;
+ procedure TestBlindBitsValidation;
+ end;
+
+implementation
+
+{ TTestECDHPrimeConstantTime }
+
+procedure TTestECDHPrimeConstantTime.SetUp;
+begin
+ FRandom := TSecureRandom.Create();
+end;
+
+procedure TTestECDHPrimeConstantTime.TearDown;
+begin
+ inherited;
+end;
+
+function TTestECDHPrimeConstantTime.CurveNames: TCryptoLibStringArray;
+begin
+ Result := TCryptoLibStringArray.Create('secp256r1', 'secp384r1', 'secp521r1', 'secp256k1');
+end;
+
+function TTestECDHPrimeConstantTime.MakeFieldOps(const AName: String;
+ const ACurve: IECCurve): IFpFieldOps;
+begin
+ if AName = 'secp256r1' then
+ Result := TSecP256R1FpFieldOps.Create(ACurve.A, ACurve.B, ACurve.Order)
+ else if AName = 'secp384r1' then
+ Result := TSecP384R1FpFieldOps.Create(ACurve.A, ACurve.B, ACurve.Order)
+ else if AName = 'secp521r1' then
+ Result := TSecP521R1FpFieldOps.Create(ACurve.A, ACurve.B, ACurve.Order)
+ else
+ Result := TSecP256K1FpFieldOps.Create(ACurve.A, ACurve.B, ACurve.Order);
+end;
+
+function TTestECDHPrimeConstantTime.RandomScalar(const AN: TBigInteger): TBigInteger;
+begin
+ Result := TBigInteger.Create(AN.BitLength, FRandom).&Mod(AN);
+ if Result.SignValue = 0 then
+ Result := TBigInteger.One;
+end;
+
+procedure TTestECDHPrimeConstantTime.AssertPointsEqual(const AMsg: String;
+ const AA, AB: IECPoint);
+begin
+ CheckEquals(True, AA.Equals(AB), AMsg);
+ CheckEquals(True, AB.Equals(AA), AMsg);
+end;
+
+function TTestECDHPrimeConstantTime.HomogFromAffine(const AFO: IFpFieldOps;
+ const AP: IECPoint): TCTHomogPoint;
+var
+ LN: Int32;
+ LX, LY: TCryptoLibUInt32Array;
+ LQ: IECPoint;
+begin
+ LN := AFO.GetFieldInts;
+ LQ := AP.Normalize();
+ LX := TNat.Create(LN);
+ LY := TNat.Create(LN);
+ AFO.FieldFromBigInteger(LQ.AffineXCoord.ToBigInteger(), LX);
+ AFO.FieldFromBigInteger(LQ.AffineYCoord.ToBigInteger(), LY);
+ Result := TCTHomogeneousMath.FromAffine(AFO, LX, LY);
+end;
+
+function TTestECDHPrimeConstantTime.HomogToPoint(const AFO: IFpFieldOps;
+ const ACurve: IECCurve; const AP: TCTHomogPoint): IECPoint;
+var
+ LX, LY: TCryptoLibUInt32Array;
+ LIsInfinity: Boolean;
+begin
+ TCTHomogeneousMath.ToAffine(AFO, AP, LX, LY, LIsInfinity);
+ if LIsInfinity then
+ Result := ACurve.Infinity
+ else
+ Result := ACurve.CreateRawPoint(AFO.CreateFieldElement(LX), AFO.CreateFieldElement(LY));
+end;
+
+procedure TTestECDHPrimeConstantTime.TestDefaultMultiplierIsConstantTime;
+var
+ LNames: TCryptoLibStringArray;
+ LI: Int32;
+ LX9: IX9ECParameters;
+ LMul: IECMultiplier;
+begin
+ LNames := CurveNames;
+ for LI := 0 to System.Length(LNames) - 1 do
+ begin
+ LX9 := TCustomNamedCurves.GetByName(LNames[LI]);
+ CheckTrue(LX9 <> nil, LNames[LI] + ' not found');
+ LMul := LX9.Curve.Multiplier;
+ CheckEquals('TFixedWindowCTMultiplier', TPlatformUtilities.GetTypeName(LMul as TObject),
+ 'default multiplier for ' + LNames[LI] + ' is not constant-time');
+ end;
+end;
+
+procedure TTestECDHPrimeConstantTime.TestParityWithWNaf;
+var
+ LNames: TCryptoLibStringArray;
+ LI, LJ: Int32;
+ LX9: IX9ECParameters;
+ LCurve: IECCurve;
+ LWNaf, LCT: IECMultiplier;
+ LN, LR, LK: TBigInteger;
+ LQ, LRef, LGot: IECPoint;
+begin
+ LWNaf := TWNafL2RMultiplier.Create() as IECMultiplier;
+ LNames := CurveNames;
+ for LI := 0 to System.Length(LNames) - 1 do
+ begin
+ LX9 := TCustomNamedCurves.GetByName(LNames[LI]);
+ LCurve := LX9.Curve;
+ LN := LX9.N;
+ LCT := LCurve.Multiplier;
+ for LJ := 0 to TestsPerCurve - 1 do
+ begin
+ LR := RandomScalar(LN);
+ LQ := LWNaf.Multiply(LX9.G, LR).Normalize();
+ LK := RandomScalar(LN);
+ LRef := LWNaf.Multiply(LQ, LK).Normalize();
+ LGot := LCT.Multiply(LQ, LK).Normalize();
+ AssertPointsEqual('parity ' + LNames[LI], LRef, LGot);
+ end;
+ end;
+end;
+
+procedure TTestECDHPrimeConstantTime.TestEdgeScalars;
+var
+ LNames: TCryptoLibStringArray;
+ LI, LJ: Int32;
+ LX9: IX9ECParameters;
+ LCurve: IECCurve;
+ LWNaf, LCT: IECMultiplier;
+ LN: TBigInteger;
+ LScalars: TCryptoLibGenericArray;
+ LRef, LGot: IECPoint;
+begin
+ LWNaf := TWNafL2RMultiplier.Create() as IECMultiplier;
+ LNames := CurveNames;
+ for LI := 0 to System.Length(LNames) - 1 do
+ begin
+ LX9 := TCustomNamedCurves.GetByName(LNames[LI]);
+ LCurve := LX9.Curve;
+ LN := LX9.N;
+ LCT := LCurve.Multiplier;
+ LScalars := TCryptoLibGenericArray.Create(
+ TBigInteger.One,
+ TBigInteger.Two,
+ TBigInteger.ValueOf(7),
+ LN.Subtract(TBigInteger.One),
+ LN.Subtract(TBigInteger.Two));
+ for LJ := 0 to System.Length(LScalars) - 1 do
+ begin
+ LRef := LWNaf.Multiply(LX9.G, LScalars[LJ]).Normalize();
+ LGot := LCT.Multiply(LX9.G, LScalars[LJ]).Normalize();
+ AssertPointsEqual('edge ' + LNames[LI] + ' idx ' + IntToStr(LJ), LRef, LGot);
+ end;
+ end;
+end;
+
+procedure TTestECDHPrimeConstantTime.TestBlindingTransparency;
+var
+ LX9: IX9ECParameters;
+ LCT: IECMultiplier;
+ LN, LK: TBigInteger;
+ LI: Int32;
+ LFirst, LAgain: IECPoint;
+begin
+ // Randomized internals (scalar blind + projective coordinate) must not change the result.
+ LX9 := TCustomNamedCurves.GetByName('secp256r1');
+ LCT := LX9.Curve.Multiplier;
+ LN := LX9.N;
+ for LI := 0 to 7 do
+ begin
+ LK := RandomScalar(LN);
+ LFirst := LCT.Multiply(LX9.G, LK).Normalize();
+ LAgain := LCT.Multiply(LX9.G, LK).Normalize();
+ AssertPointsEqual('transparency', LFirst, LAgain);
+ end;
+end;
+
+procedure TTestECDHPrimeConstantTime.TestExceptionalFormulas;
+var
+ LX9: IX9ECParameters;
+ LCurve: IECCurve;
+ LFO: IFpFieldOps;
+ LWNaf: IECMultiplier;
+ LN: Int32;
+ LP, LDbl, LNeg, LSum, LInf: TCTHomogPoint;
+ LNegY, LZeroArr: TCryptoLibUInt32Array;
+ LG, LNegG, LRef2G: IECPoint;
+begin
+ LWNaf := TWNafL2RMultiplier.Create() as IECMultiplier;
+ LX9 := TCustomNamedCurves.GetByName('secp256r1');
+ LCurve := LX9.Curve;
+ LFO := MakeFieldOps('secp256r1', LCurve);
+ LN := LFO.GetFieldInts;
+
+ LG := LX9.G.Normalize();
+ LP := HomogFromAffine(LFO, LG);
+
+ // complete Add must handle P == Q (doubling): Add(P,P) == Double(P) == 2G
+ LDbl := TCTHomogeneousMath.Double(LFO, LP);
+ LSum := TCTHomogeneousMath.Add(LFO, LP, LP);
+ LRef2G := LWNaf.Multiply(LX9.G, TBigInteger.Two).Normalize();
+ AssertPointsEqual('Double(P)=2G', LRef2G, HomogToPoint(LFO, LCurve, LDbl));
+ AssertPointsEqual('Add(P,P)=2G', LRef2G, HomogToPoint(LFO, LCurve, LSum));
+
+ // P + (-P) == O
+ LZeroArr := TNat.Create(LN);
+ LNegY := TNat.Create(LN);
+ LFO.Sub(LZeroArr, HomogFromAffine(LFO, LG).Y, LNegY);
+ LNeg.X := HomogFromAffine(LFO, LG).X;
+ LNeg.Y := LNegY;
+ LNeg.Z := HomogFromAffine(LFO, LG).Z;
+ LSum := TCTHomogeneousMath.Add(LFO, LP, LNeg);
+ CheckEquals(True, HomogToPoint(LFO, LCurve, LSum).IsInfinity, 'P+(-P)=O');
+ // cross-check the affine (-P) really is the curve negation of P
+ LNegG := LX9.G.Negate().Normalize();
+ AssertPointsEqual('(-P) affine', LNegG, HomogToPoint(LFO, LCurve, LNeg));
+
+ // P + O == P and O + P == P
+ LInf := TCTHomogeneousMath.Infinity(LFO);
+ AssertPointsEqual('P+O=P', LG, HomogToPoint(LFO, LCurve, TCTHomogeneousMath.Add(LFO, LP, LInf)));
+ AssertPointsEqual('O+P=P', LG, HomogToPoint(LFO, LCurve, TCTHomogeneousMath.Add(LFO, LInf, LP)));
+
+ // O + O == O
+ CheckEquals(True, HomogToPoint(LFO, LCurve,
+ TCTHomogeneousMath.Add(LFO, LInf, LInf)).IsInfinity, 'O+O=O');
+end;
+
+procedure TTestECDHPrimeConstantTime.TestECDHAgreement;
+var
+ LNames: TCryptoLibStringArray;
+ LI: Int32;
+ LX9: IX9ECParameters;
+ LEC: IECDomainParameters;
+ LKpg: IAsymmetricCipherKeyPairGenerator;
+ LP1, LP2: IAsymmetricCipherKeyPair;
+ LE1, LE2: IBasicAgreement;
+ LK1, LK2: TBigInteger;
+begin
+ LNames := CurveNames;
+ for LI := 0 to System.Length(LNames) - 1 do
+ begin
+ LX9 := TCustomNamedCurves.GetByName(LNames[LI]);
+ LEC := TECDomainParameters.Create(LX9.Curve, LX9.G, LX9.N, LX9.H);
+ LKpg := TECKeyPairGenerator.Create();
+ LKpg.Init(TECKeyGenerationParameters.Create(LEC, FRandom) as IECKeyGenerationParameters);
+ LP1 := LKpg.GenerateKeyPair();
+ LP2 := LKpg.GenerateKeyPair();
+
+ LE1 := TECDHBasicAgreement.Create();
+ LE2 := TECDHBasicAgreement.Create();
+ LE1.Init(LP1.Private);
+ LE2.Init(LP2.Private);
+
+ LK1 := LE1.CalculateAgreement(LP2.Public as ICipherParameters);
+ LK2 := LE2.CalculateAgreement(LP1.Public as ICipherParameters);
+ CheckEquals(True, LK1.Equals(LK2), 'ECDH agreement mismatch ' + LNames[LI]);
+ end;
+end;
+
+procedure TTestECDHPrimeConstantTime.TestScalarRangeGuard;
+var
+ LNames: TCryptoLibStringArray;
+ LI, LOrderBits: Int32;
+ LX9: IX9ECParameters;
+ LMul: IECMultiplier;
+ LN: TBigInteger;
+
+ function Raises(const AK: TBigInteger): Boolean;
+ begin
+ Result := False;
+ try
+ LMul.Multiply(LX9.G, AK);
+ except
+ on E: EInvalidOperationCryptoLibException do
+ Result := True;
+ end;
+ end;
+
+begin
+ LNames := CurveNames;
+ for LI := 0 to System.Length(LNames) - 1 do
+ begin
+ LX9 := TCustomNamedCurves.GetByName(LNames[LI]);
+ LMul := LX9.Curve.Multiplier;
+ LN := LX9.N;
+ LOrderBits := LN.BitLength;
+ // k = n (BitLength = order bits) is in range and must not raise
+ CheckFalse(Raises(LN), 'k = n unexpectedly rejected for ' + LNames[LI]);
+ // scalars wider than the order must raise
+ CheckTrue(Raises(TBigInteger.One.ShiftLeft(LOrderBits)),
+ 'k = 2^orderbits not rejected for ' + LNames[LI]);
+ CheckTrue(Raises(TBigInteger.One.ShiftLeft(LOrderBits + 1)),
+ 'k = 2^(orderbits+1) not rejected for ' + LNames[LI]);
+ end;
+end;
+
+procedure TTestECDHPrimeConstantTime.TestBlindBitsValidation;
+var
+ LX9: IX9ECParameters;
+ LFO: IFpFieldOps;
+
+ function Rejects(ABlindBits: Int32): Boolean;
+ var
+ LMul: IECMultiplier;
+ begin
+ Result := False;
+ try
+ LMul := TFixedWindowCTMultiplier.Create(LFO, ABlindBits) as IECMultiplier;
+ except
+ on E: EArgumentCryptoLibException do
+ Result := True;
+ end;
+ end;
+
+begin
+ LX9 := TCustomNamedCurves.GetByName('secp256r1');
+ LFO := MakeFieldOps('secp256r1', LX9.Curve);
+ CheckTrue(Rejects(0), '0 accepted');
+ CheckTrue(Rejects(32), '32 (below floor) accepted');
+ CheckTrue(Rejects(48), '48 (non-multiple of 32) accepted');
+ CheckTrue(Rejects(544), '544 (above cap) accepted');
+ CheckFalse(Rejects(64), '64 rejected');
+ CheckFalse(Rejects(128), '128 rejected');
+end;
+
+initialization
+
+{$IFDEF FPC}
+ RegisterTest(TTestECDHPrimeConstantTime);
+{$ELSE}
+ RegisterTest(TTestECDHPrimeConstantTime.Suite);
+{$ENDIF FPC}
+
+end.
diff --git a/CryptoLib/src/Crypto/Engines/ClpAesBitSlicedEngine.pas b/CryptoLib/src/Crypto/Engines/ClpAesBitSlicedEngine.pas
index 19515ea1..f30e351d 100644
--- a/CryptoLib/src/Crypto/Engines/ClpAesBitSlicedEngine.pas
+++ b/CryptoLib/src/Crypto/Engines/ClpAesBitSlicedEngine.pas
@@ -133,6 +133,9 @@ TAesBitSlicedEngine = class sealed(TInterfacedObject, IAesEngine, IBlockCipher
/// Reset the cipher back to its post-Init state.
procedure Reset(); virtual;
+ /// Zeroize the expanded round-key schedule on teardown.
+ destructor Destroy; override;
+
/// The cipher name (AES[BitSliced]).
property AlgorithmName: String read GetAlgorithmName;
end;
@@ -602,87 +605,92 @@ procedure TAesBitSlicedEngine.KeySchedule(const AKey: TCryptoLibByteArray);
end;
end;
- LNk := LKeyLen shr 2;
- LNkf := (FRounds + 1) shl 2;
+ try
+ LNk := LKeyLen shr 2;
+ LNkf := (FRounds + 1) shl 2;
- LTmp := 0;
- for LI := 0 to LNk - 1 do
- begin
- LTmp := TPack.LE_To_UInt32(AKey, LI shl 2);
- LSkey32[LI] := LTmp;
- end;
-
- LJ := 0;
- LK := 0;
- for LI := LNk to LNkf - 1 do
- begin
- if LJ = 0 then
+ LTmp := 0;
+ for LI := 0 to LNk - 1 do
begin
- LTmp := TBitOperations.RotateLeft32(LTmp, 24);
- LTmp := SubWord(LTmp) xor RCon[LK];
- end
- else if (LNk > 6) and (LJ = 4) then
- begin
- LTmp := SubWord(LTmp);
+ LTmp := TPack.LE_To_UInt32(AKey, LI shl 2);
+ LSkey32[LI] := LTmp;
end;
- LTmp := LTmp xor LSkey32[LI - LNk];
- LSkey32[LI] := LTmp;
- System.Inc(LJ);
- if LJ = LNk then
+
+ LJ := 0;
+ LK := 0;
+ for LI := LNk to LNkf - 1 do
begin
- LJ := 0;
- System.Inc(LK);
+ if LJ = 0 then
+ begin
+ LTmp := TBitOperations.RotateLeft32(LTmp, 24);
+ LTmp := SubWord(LTmp) xor RCon[LK];
+ end
+ else if (LNk > 6) and (LJ = 4) then
+ begin
+ LTmp := SubWord(LTmp);
+ end;
+ LTmp := LTmp xor LSkey32[LI - LNk];
+ LSkey32[LI] := LTmp;
+ System.Inc(LJ);
+ if LJ = LNk then
+ begin
+ LJ := 0;
+ System.Inc(LK);
+ end;
end;
- end;
- // Compress round keys into two words per round key (one bit per nibble).
- System.SetLength(LCompSkey, (FRounds + 1) * 2);
- LI := 0;
- LJ := 0;
- while LI < LNkf do
- begin
- InterleaveIn(LSkey32[LI], LSkey32[LI + 1], LSkey32[LI + 2],
- LSkey32[LI + 3], LQ[0], LQ[4]);
- LQ[1] := LQ[0];
- LQ[2] := LQ[0];
- LQ[3] := LQ[0];
- LQ[5] := LQ[4];
- LQ[6] := LQ[4];
- LQ[7] := LQ[4];
- Ortho(LQ);
- LCompSkey[LJ + 0] := (LQ[0] and M1) or (LQ[1] and M2) or (LQ[2] and M4)
- or (LQ[3] and M8);
- LCompSkey[LJ + 1] := (LQ[4] and M1) or (LQ[5] and M2) or (LQ[6] and M4)
- or (LQ[7] and M8);
- System.Inc(LI, 4);
- System.Inc(LJ, 2);
- end;
+ // Compress round keys into two words per round key (one bit per nibble).
+ System.SetLength(LCompSkey, (FRounds + 1) * 2);
+ LI := 0;
+ LJ := 0;
+ while LI < LNkf do
+ begin
+ InterleaveIn(LSkey32[LI], LSkey32[LI + 1], LSkey32[LI + 2],
+ LSkey32[LI + 3], LQ[0], LQ[4]);
+ LQ[1] := LQ[0];
+ LQ[2] := LQ[0];
+ LQ[3] := LQ[0];
+ LQ[5] := LQ[4];
+ LQ[6] := LQ[4];
+ LQ[7] := LQ[4];
+ Ortho(LQ);
+ LCompSkey[LJ + 0] := (LQ[0] and M1) or (LQ[1] and M2) or (LQ[2] and M4)
+ or (LQ[3] and M8);
+ LCompSkey[LJ + 1] := (LQ[4] and M1) or (LQ[5] and M2) or (LQ[6] and M4)
+ or (LQ[7] and M8);
+ System.Inc(LI, 4);
+ System.Inc(LJ, 2);
+ end;
- // Expand the compressed round keys to the full bit-sliced form.
- System.SetLength(FSkey, (FRounds + 1) * 8);
- LN := (FRounds + 1) * 2;
- LV := 0;
- for LI := 0 to LN - 1 do
- begin
- LX0 := LCompSkey[LI];
- LX1 := LX0;
- LX2 := LX0;
- LX3 := LX0;
- LX0 := LX0 and M1;
- LX1 := LX1 and M2;
- LX2 := LX2 and M4;
- LX3 := LX3 and M8;
- LX1 := LX1 shr 1;
- LX2 := LX2 shr 2;
- LX3 := LX3 shr 3;
- FSkey[LV + 0] := (LX0 shl 4) - LX0;
- FSkey[LV + 1] := (LX1 shl 4) - LX1;
- FSkey[LV + 2] := (LX2 shl 4) - LX2;
- FSkey[LV + 3] := (LX3 shl 4) - LX3;
- System.Inc(LV, 4);
+ // Expand the compressed round keys to the full bit-sliced form.
+ TArrayUtilities.Fill(FSkey, 0, System.Length(FSkey), UInt64(0));
+ System.SetLength(FSkey, (FRounds + 1) * 8);
+ LN := (FRounds + 1) * 2;
+ LV := 0;
+ for LI := 0 to LN - 1 do
+ begin
+ LX0 := LCompSkey[LI];
+ LX1 := LX0;
+ LX2 := LX0;
+ LX3 := LX0;
+ LX0 := LX0 and M1;
+ LX1 := LX1 and M2;
+ LX2 := LX2 and M4;
+ LX3 := LX3 and M8;
+ LX1 := LX1 shr 1;
+ LX2 := LX2 shr 2;
+ LX3 := LX3 shr 3;
+ FSkey[LV + 0] := (LX0 shl 4) - LX0;
+ FSkey[LV + 1] := (LX1 shl 4) - LX1;
+ FSkey[LV + 2] := (LX2 shl 4) - LX2;
+ FSkey[LV + 3] := (LX3 shl 4) - LX3;
+ System.Inc(LV, 4);
+ end;
+ finally
+ TArrayUtilities.Fill(LCompSkey, 0, System.Length(LCompSkey), UInt64(0));
+ System.FillChar(LSkey32, System.SizeOf(LSkey32), 0);
+ System.FillChar(LQ, System.SizeOf(LQ), 0);
end;
-
- TArrayUtilities.Fill(AKey, 0, System.Length(AKey), Byte(0));
end;
procedure TAesBitSlicedEngine.BitsliceEncrypt(var AQ: TBitSliceState);
@@ -749,6 +757,12 @@ procedure TAesBitSlicedEngine.Reset;
// The bit-sliced engine keeps no per-block chaining state; nothing to reset.
end;
+destructor TAesBitSlicedEngine.Destroy;
+begin
+ TArrayUtilities.Fill(FSkey, 0, System.Length(FSkey), UInt64(0));
+ inherited Destroy;
+end;
+
function TAesBitSlicedEngine.ProcessBlock(const AInput: TCryptoLibByteArray;
AInOff: Int32; const AOutput: TCryptoLibByteArray; AOutOff: Int32): Int32;
var
diff --git a/CryptoLib/src/Interfaces/Math/EC/Multiplier/ClpIF2mFieldOps.pas b/CryptoLib/src/Interfaces/Math/EC/Multiplier/ClpIF2mFieldOps.pas
new file mode 100644
index 00000000..ea4acb66
--- /dev/null
+++ b/CryptoLib/src/Interfaces/Math/EC/Multiplier/ClpIF2mFieldOps.pas
@@ -0,0 +1,55 @@
+{ *********************************************************************************** }
+{ * CryptoLib Library * }
+{ * Author - Ugochukwu Mmaduekwe * }
+{ * Github Repository * }
+{ * * }
+{ * 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 * }
+{ * the development of this library * }
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIF2mFieldOps;
+
+{$I ..\..\..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+ ClpBigInteger,
+ ClpISecureRandom,
+ ClpIECFieldElement,
+ ClpCryptoLibTypes;
+
+type
+ ///
+ /// Constant-time GF(2^m) field arithmetic over a specific binary curve, exposing
+ /// only what the López–Dahab Montgomery ladder needs.
+ /// Each curve supplies an adapter over its existing (constant-time) binary field.
+ ///
+ IF2mFieldOps = interface(IInterface)
+ ['{2A9C7E14-6B3D-4F81-A0C5-7E2D9B4F1836}']
+ function GetFieldLongs: Int32;
+ function GetCardinalityBits: Int32;
+ procedure GetCardinality(const AZ: TCryptoLibUInt32Array; AInts: Int32);
+
+ procedure Mul(const AX, AY, AZ: TCryptoLibUInt64Array);
+ procedure Square(const AX, AZ: TCryptoLibUInt64Array);
+ procedure Add(const AX, AY, AZ: TCryptoLibUInt64Array);
+ procedure MulByB(const AX, AZ: TCryptoLibUInt64Array);
+ procedure Inv(const AX, AZ: TCryptoLibUInt64Array);
+ function IsZeroMask(const AX: TCryptoLibUInt64Array): UInt64;
+
+ procedure RandomNonZero(const ARandom: ISecureRandom; const AZ: TCryptoLibUInt64Array);
+ procedure FieldFromBigInteger(const AX: TBigInteger; const AZ: TCryptoLibUInt64Array);
+ function CreateFieldElement(const AX: TCryptoLibUInt64Array): IECFieldElement;
+ end;
+
+implementation
+
+end.
diff --git a/CryptoLib/src/Interfaces/Math/EC/Multiplier/ClpIFpFieldOps.pas b/CryptoLib/src/Interfaces/Math/EC/Multiplier/ClpIFpFieldOps.pas
new file mode 100644
index 00000000..ba2a24a7
--- /dev/null
+++ b/CryptoLib/src/Interfaces/Math/EC/Multiplier/ClpIFpFieldOps.pas
@@ -0,0 +1,58 @@
+{ *********************************************************************************** }
+{ * CryptoLib Library * }
+{ * Author - Ugochukwu Mmaduekwe * }
+{ * Github Repository * }
+{ * * }
+{ * 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 * }
+{ * the development of this library * }
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIFpFieldOps;
+
+{$I ..\..\..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+ ClpBigInteger,
+ ClpISecureRandom,
+ ClpIECFieldElement,
+ ClpCryptoLibTypes;
+
+type
+ ///
+ /// Constant-time field arithmetic over a specific prime-field short-Weierstrass
+ /// curve, exposing only what the homogeneous complete-formula layer needs. Each
+ /// curve supplies an adapter over its existing (constant-time) Nat field.
+ ///
+ IFpFieldOps = interface(IInterface)
+ ['{6F1B2A34-5C8D-4E90-B1A2-7C3D4E5F6A7B}']
+ function GetFieldInts: Int32;
+ function GetOrderBits: Int32;
+ procedure GetOrder(const AZ: TCryptoLibUInt32Array; AInts: Int32);
+
+ procedure Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure Square(const AX, AZ: TCryptoLibUInt32Array);
+ procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure Sub(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure MulByB3(const AX, AZ: TCryptoLibUInt32Array);
+ procedure MulByA(const AX, AZ: TCryptoLibUInt32Array);
+ procedure Inv(const AX, AZ: TCryptoLibUInt32Array);
+ function IsZero(const AX: TCryptoLibUInt32Array): Boolean;
+
+ procedure RandomMult(const ARandom: ISecureRandom; const AZ: TCryptoLibUInt32Array);
+ procedure FieldFromBigInteger(const AX: TBigInteger; const AZ: TCryptoLibUInt32Array);
+ function CreateFieldElement(const AX: TCryptoLibUInt32Array): IECFieldElement;
+ procedure FieldOne(const AZ: TCryptoLibUInt32Array);
+ end;
+
+implementation
+
+end.
diff --git a/CryptoLib/src/Math/EC/ClpECAlgorithms.pas b/CryptoLib/src/Math/EC/ClpECAlgorithms.pas
index 0e75d4c4..b39ea61c 100644
--- a/CryptoLib/src/Math/EC/ClpECAlgorithms.pas
+++ b/CryptoLib/src/Math/EC/ClpECAlgorithms.pas
@@ -257,13 +257,18 @@ class function TECAlgorithms.SumOfTwoMultiplies(const AP: IECPoint; const AK: TB
LQ: IECPoint;
LF2mCurve: IAbstractF2mCurve;
LGlv: IGlvEndomorphism;
+ LTau: IECMultiplier;
begin
LCurve := AP.Curve;
LQ := ImportPoint(LCurve, AQ);
+ // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick
if Supports(LCurve, IAbstractF2mCurve, LF2mCurve) and LF2mCurve.IsKoblitz then
begin
- Result := ImplCheckResult(AP.Multiply(AK).Add(LQ.Multiply(AL)));
+ //Result := ImplCheckResult(AP.Multiply(AK).Add(LQ.Multiply(AL)));
+ // name tau-NAF explicitly so it is not routed through the (constant-time) curve default
+ LTau := TWTauNafMultiplier.Create();
+ Result := ImplCheckResult(LTau.Multiply(AP, AK).Add(LTau.Multiply(LQ, AL)));
Exit;
end;
if Supports(LCurve.GetEndomorphism(), IGlvEndomorphism, LGlv) then
diff --git a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas
index 4646e7cb..0f2e40cb 100644
--- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas
+++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas
@@ -36,6 +36,8 @@ interface
ClpECFieldElement,
ClpECPoint,
ClpECLookupTables,
+ ClpIFpFieldOps,
+ ClpFixedWindowCTMultiplier,
ClpIECCommon,
ClpIECFieldElement,
ClpISecP256K1Custom,
@@ -191,11 +193,39 @@ TSecP256K1LookupTable = class sealed(TAbstractECLookupTable, IECLookupTable,
function RandomFieldElement(const ARandom: ISecureRandom): IECFieldElement; override;
function RandomFieldElementMult(const ARandom: ISecureRandom): IECFieldElement; override;
function SupportsCoordinateSystem(ACoord: Int32): Boolean; override;
+ function CreateDefaultMultiplier: IECMultiplier; override;
class property Q: TBigInteger read FQ;
class property SecP256K1AffineZs: TCryptoLibGenericArray read FSecP256K1AffineZs;
end;
+type
+ TSecP256K1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps)
+ strict private
+ const
+ FE_INTS = Int32(8);
+ var
+ FA, FB3, FOne, FN: TCryptoLibUInt32Array;
+ FOrderBits: Int32;
+ public
+ constructor Create(const AA, AB: IECFieldElement; const AOrder: TBigInteger);
+ function GetFieldInts: Int32;
+ function GetOrderBits: Int32;
+ procedure GetOrder(const AZ: TCryptoLibUInt32Array; AInts: Int32);
+ procedure Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure Square(const AX, AZ: TCryptoLibUInt32Array);
+ procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure Sub(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure MulByB3(const AX, AZ: TCryptoLibUInt32Array);
+ procedure MulByA(const AX, AZ: TCryptoLibUInt32Array);
+ procedure Inv(const AX, AZ: TCryptoLibUInt32Array);
+ function IsZero(const AX: TCryptoLibUInt32Array): Boolean;
+ procedure RandomMult(const ARandom: ISecureRandom; const AZ: TCryptoLibUInt32Array);
+ procedure FieldFromBigInteger(const AX: TBigInteger; const AZ: TCryptoLibUInt32Array);
+ function CreateFieldElement(const AX: TCryptoLibUInt32Array): IECFieldElement;
+ procedure FieldOne(const AZ: TCryptoLibUInt32Array);
+ end;
+
implementation
{ TSecP256K1Field }
@@ -1049,6 +1079,111 @@ function TSecP256K1Curve.SupportsCoordinateSystem(ACoord: Int32): Boolean;
end;
end;
+function TSecP256K1Curve.CreateDefaultMultiplier: IECMultiplier;
+var
+ LCurve: IECCurve;
+ LFieldOps: IFpFieldOps;
+begin
+ LCurve := Self as IECCurve;
+ LFieldOps := TSecP256K1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order);
+ Result := TFixedWindowCTMultiplier.Create(LFieldOps) as IECMultiplier;
+end;
+
+{ TSecP256K1FpFieldOps }
+
+constructor TSecP256K1FpFieldOps.Create(const AA, AB: IECFieldElement; const AOrder: TBigInteger);
+var
+ LB, LB3: TCryptoLibUInt32Array;
+begin
+ Inherited Create;
+ FOrderBits := AOrder.BitLength;
+ FN := TNat.FromBigInteger(FE_INTS * 32, AOrder);
+ FOne := TSecP256K1Field.FromBigInteger(TBigInteger.One);
+ FA := TNat.Copy(FE_INTS, (AA as ISecP256K1FieldElement).X);
+ LB := (AB as ISecP256K1FieldElement).X;
+ LB3 := TNat256.Create();
+ TSecP256K1Field.Add(LB, LB, LB3);
+ TSecP256K1Field.Add(LB3, LB, LB3);
+ FB3 := LB3;
+end;
+
+function TSecP256K1FpFieldOps.GetFieldInts: Int32;
+begin
+ Result := FE_INTS;
+end;
+
+function TSecP256K1FpFieldOps.GetOrderBits: Int32;
+begin
+ Result := FOrderBits;
+end;
+
+procedure TSecP256K1FpFieldOps.GetOrder(const AZ: TCryptoLibUInt32Array; AInts: Int32);
+begin
+ TNat.Copy(FE_INTS, FN, 0, AZ, 0);
+end;
+
+procedure TSecP256K1FpFieldOps.Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256K1Field.Multiply(AX, AY, AZ);
+end;
+
+procedure TSecP256K1FpFieldOps.Square(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256K1Field.Square(AX, AZ);
+end;
+
+procedure TSecP256K1FpFieldOps.Add(const AX, AY, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256K1Field.Add(AX, AY, AZ);
+end;
+
+procedure TSecP256K1FpFieldOps.Sub(const AX, AY, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256K1Field.Subtract(AX, AY, AZ);
+end;
+
+procedure TSecP256K1FpFieldOps.MulByB3(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256K1Field.Multiply(AX, FB3, AZ);
+end;
+
+procedure TSecP256K1FpFieldOps.MulByA(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256K1Field.Multiply(AX, FA, AZ);
+end;
+
+procedure TSecP256K1FpFieldOps.Inv(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256K1Field.Inv(AX, AZ);
+end;
+
+function TSecP256K1FpFieldOps.IsZero(const AX: TCryptoLibUInt32Array): Boolean;
+begin
+ Result := TSecP256K1Field.IsZero(AX) <> 0;
+end;
+
+procedure TSecP256K1FpFieldOps.RandomMult(const ARandom: ISecureRandom;
+ const AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256K1Field.RandomMult(ARandom, AZ);
+end;
+
+procedure TSecP256K1FpFieldOps.FieldFromBigInteger(const AX: TBigInteger;
+ const AZ: TCryptoLibUInt32Array);
+begin
+ TNat.Copy(FE_INTS, TSecP256K1Field.FromBigInteger(AX), 0, AZ, 0);
+end;
+
+function TSecP256K1FpFieldOps.CreateFieldElement(const AX: TCryptoLibUInt32Array): IECFieldElement;
+begin
+ Result := TSecP256K1FieldElement.Create(AX) as IECFieldElement;
+end;
+
+procedure TSecP256K1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array);
+begin
+ TNat.Copy(FE_INTS, FOne, 0, AZ, 0);
+end;
+
class function TSecP256K1FieldElement.GetQ: TBigInteger;
begin
Result := FQ;
diff --git a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas
index 8ef501c3..86ea10b4 100644
--- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas
+++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas
@@ -36,6 +36,8 @@ interface
ClpECFieldElement,
ClpECPoint,
ClpECLookupTables,
+ ClpIFpFieldOps,
+ ClpFixedWindowCTMultiplier,
ClpIECCommon,
ClpIECFieldElement,
ClpISecP256R1Custom,
@@ -192,11 +194,39 @@ TSecP256R1LookupTable = class sealed(TAbstractECLookupTable, IECLookupTable,
function RandomFieldElement(const ARandom: ISecureRandom): IECFieldElement; override;
function RandomFieldElementMult(const ARandom: ISecureRandom): IECFieldElement; override;
function SupportsCoordinateSystem(ACoord: Int32): Boolean; override;
+ function CreateDefaultMultiplier: IECMultiplier; override;
class property Q: TBigInteger read FQ;
class property SecP256R1AffineZs: TCryptoLibGenericArray read FSecP256R1AffineZs;
end;
+type
+ TSecP256R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps)
+ strict private
+ const
+ FE_INTS = Int32(8);
+ var
+ FA, FB3, FOne, FN: TCryptoLibUInt32Array;
+ FOrderBits: Int32;
+ public
+ constructor Create(const AA, AB: IECFieldElement; const AOrder: TBigInteger);
+ function GetFieldInts: Int32;
+ function GetOrderBits: Int32;
+ procedure GetOrder(const AZ: TCryptoLibUInt32Array; AInts: Int32);
+ procedure Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure Square(const AX, AZ: TCryptoLibUInt32Array);
+ procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure Sub(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure MulByB3(const AX, AZ: TCryptoLibUInt32Array);
+ procedure MulByA(const AX, AZ: TCryptoLibUInt32Array);
+ procedure Inv(const AX, AZ: TCryptoLibUInt32Array);
+ function IsZero(const AX: TCryptoLibUInt32Array): Boolean;
+ procedure RandomMult(const ARandom: ISecureRandom; const AZ: TCryptoLibUInt32Array);
+ procedure FieldFromBigInteger(const AX: TBigInteger; const AZ: TCryptoLibUInt32Array);
+ function CreateFieldElement(const AX: TCryptoLibUInt32Array): IECFieldElement;
+ procedure FieldOne(const AZ: TCryptoLibUInt32Array);
+ end;
+
implementation
{ TSecP256R1Field }
@@ -1205,4 +1235,109 @@ function TSecP256R1Curve.SupportsCoordinateSystem(ACoord: Int32): Boolean;
end;
end;
+function TSecP256R1Curve.CreateDefaultMultiplier: IECMultiplier;
+var
+ LCurve: IECCurve;
+ LFieldOps: IFpFieldOps;
+begin
+ LCurve := Self as IECCurve;
+ LFieldOps := TSecP256R1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order);
+ Result := TFixedWindowCTMultiplier.Create(LFieldOps) as IECMultiplier;
+end;
+
+{ TSecP256R1FpFieldOps }
+
+constructor TSecP256R1FpFieldOps.Create(const AA, AB: IECFieldElement; const AOrder: TBigInteger);
+var
+ LB, LB3: TCryptoLibUInt32Array;
+begin
+ Inherited Create;
+ FOrderBits := AOrder.BitLength;
+ FN := TNat.FromBigInteger(FE_INTS * 32, AOrder);
+ FOne := TSecP256R1Field.FromBigInteger(TBigInteger.One);
+ FA := TNat.Copy(FE_INTS, (AA as ISecP256R1FieldElement).X);
+ LB := (AB as ISecP256R1FieldElement).X;
+ LB3 := TNat256.Create();
+ TSecP256R1Field.Add(LB, LB, LB3);
+ TSecP256R1Field.Add(LB3, LB, LB3);
+ FB3 := LB3;
+end;
+
+function TSecP256R1FpFieldOps.GetFieldInts: Int32;
+begin
+ Result := FE_INTS;
+end;
+
+function TSecP256R1FpFieldOps.GetOrderBits: Int32;
+begin
+ Result := FOrderBits;
+end;
+
+procedure TSecP256R1FpFieldOps.GetOrder(const AZ: TCryptoLibUInt32Array; AInts: Int32);
+begin
+ TNat.Copy(FE_INTS, FN, 0, AZ, 0);
+end;
+
+procedure TSecP256R1FpFieldOps.Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256R1Field.Multiply(AX, AY, AZ);
+end;
+
+procedure TSecP256R1FpFieldOps.Square(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256R1Field.Square(AX, AZ);
+end;
+
+procedure TSecP256R1FpFieldOps.Add(const AX, AY, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256R1Field.Add(AX, AY, AZ);
+end;
+
+procedure TSecP256R1FpFieldOps.Sub(const AX, AY, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256R1Field.Subtract(AX, AY, AZ);
+end;
+
+procedure TSecP256R1FpFieldOps.MulByB3(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256R1Field.Multiply(AX, FB3, AZ);
+end;
+
+procedure TSecP256R1FpFieldOps.MulByA(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256R1Field.Multiply(AX, FA, AZ);
+end;
+
+procedure TSecP256R1FpFieldOps.Inv(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256R1Field.Inv(AX, AZ);
+end;
+
+function TSecP256R1FpFieldOps.IsZero(const AX: TCryptoLibUInt32Array): Boolean;
+begin
+ Result := TSecP256R1Field.IsZero(AX) <> 0;
+end;
+
+procedure TSecP256R1FpFieldOps.RandomMult(const ARandom: ISecureRandom;
+ const AZ: TCryptoLibUInt32Array);
+begin
+ TSecP256R1Field.RandomMult(ARandom, AZ);
+end;
+
+procedure TSecP256R1FpFieldOps.FieldFromBigInteger(const AX: TBigInteger;
+ const AZ: TCryptoLibUInt32Array);
+begin
+ TNat.Copy(FE_INTS, TSecP256R1Field.FromBigInteger(AX), 0, AZ, 0);
+end;
+
+function TSecP256R1FpFieldOps.CreateFieldElement(const AX: TCryptoLibUInt32Array): IECFieldElement;
+begin
+ Result := TSecP256R1FieldElement.Create(AX) as IECFieldElement;
+end;
+
+procedure TSecP256R1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array);
+begin
+ TNat.Copy(FE_INTS, FOne, 0, AZ, 0);
+end;
+
end.
diff --git a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas
index d91919cf..4c9e08c0 100644
--- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas
+++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas
@@ -36,6 +36,8 @@ interface
ClpECFieldElement,
ClpECPoint,
ClpECLookupTables,
+ ClpIFpFieldOps,
+ ClpFixedWindowCTMultiplier,
ClpIECCommon,
ClpIECFieldElement,
ClpISecP384R1Custom,
@@ -191,11 +193,39 @@ TSecP384R1LookupTable = class sealed(TAbstractECLookupTable, IECLookupTable,
function RandomFieldElement(const ARandom: ISecureRandom): IECFieldElement; override;
function RandomFieldElementMult(const ARandom: ISecureRandom): IECFieldElement; override;
function SupportsCoordinateSystem(ACoord: Int32): Boolean; override;
+ function CreateDefaultMultiplier: IECMultiplier; override;
class property Q: TBigInteger read FQ;
class property SecP384R1AffineZs: TCryptoLibGenericArray read FSecP384R1AffineZs;
end;
+type
+ TSecP384R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps)
+ strict private
+ const
+ FE_INTS = Int32(12);
+ var
+ FA, FB3, FOne, FN: TCryptoLibUInt32Array;
+ FOrderBits: Int32;
+ public
+ constructor Create(const AA, AB: IECFieldElement; const AOrder: TBigInteger);
+ function GetFieldInts: Int32;
+ function GetOrderBits: Int32;
+ procedure GetOrder(const AZ: TCryptoLibUInt32Array; AInts: Int32);
+ procedure Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure Square(const AX, AZ: TCryptoLibUInt32Array);
+ procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure Sub(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure MulByB3(const AX, AZ: TCryptoLibUInt32Array);
+ procedure MulByA(const AX, AZ: TCryptoLibUInt32Array);
+ procedure Inv(const AX, AZ: TCryptoLibUInt32Array);
+ function IsZero(const AX: TCryptoLibUInt32Array): Boolean;
+ procedure RandomMult(const ARandom: ISecureRandom; const AZ: TCryptoLibUInt32Array);
+ procedure FieldFromBigInteger(const AX: TBigInteger; const AZ: TCryptoLibUInt32Array);
+ function CreateFieldElement(const AX: TCryptoLibUInt32Array): IECFieldElement;
+ procedure FieldOne(const AZ: TCryptoLibUInt32Array);
+ end;
+
implementation
{ TSecP384R1Field }
@@ -1208,4 +1238,109 @@ function TSecP384R1Curve.SupportsCoordinateSystem(ACoord: Int32): Boolean;
end;
end;
+function TSecP384R1Curve.CreateDefaultMultiplier: IECMultiplier;
+var
+ LCurve: IECCurve;
+ LFieldOps: IFpFieldOps;
+begin
+ LCurve := Self as IECCurve;
+ LFieldOps := TSecP384R1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order);
+ Result := TFixedWindowCTMultiplier.Create(LFieldOps) as IECMultiplier;
+end;
+
+{ TSecP384R1FpFieldOps }
+
+constructor TSecP384R1FpFieldOps.Create(const AA, AB: IECFieldElement; const AOrder: TBigInteger);
+var
+ LB, LB3: TCryptoLibUInt32Array;
+begin
+ Inherited Create;
+ FOrderBits := AOrder.BitLength;
+ FN := TNat.FromBigInteger(FE_INTS * 32, AOrder);
+ FOne := TSecP384R1Field.FromBigInteger(TBigInteger.One);
+ FA := TNat.Copy(FE_INTS, (AA as ISecP384R1FieldElement).X);
+ LB := (AB as ISecP384R1FieldElement).X;
+ LB3 := TNat.Create(FE_INTS);
+ TSecP384R1Field.Add(LB, LB, LB3);
+ TSecP384R1Field.Add(LB3, LB, LB3);
+ FB3 := LB3;
+end;
+
+function TSecP384R1FpFieldOps.GetFieldInts: Int32;
+begin
+ Result := FE_INTS;
+end;
+
+function TSecP384R1FpFieldOps.GetOrderBits: Int32;
+begin
+ Result := FOrderBits;
+end;
+
+procedure TSecP384R1FpFieldOps.GetOrder(const AZ: TCryptoLibUInt32Array; AInts: Int32);
+begin
+ TNat.Copy(FE_INTS, FN, 0, AZ, 0);
+end;
+
+procedure TSecP384R1FpFieldOps.Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP384R1Field.Multiply(AX, AY, AZ);
+end;
+
+procedure TSecP384R1FpFieldOps.Square(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP384R1Field.Square(AX, AZ);
+end;
+
+procedure TSecP384R1FpFieldOps.Add(const AX, AY, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP384R1Field.Add(AX, AY, AZ);
+end;
+
+procedure TSecP384R1FpFieldOps.Sub(const AX, AY, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP384R1Field.Subtract(AX, AY, AZ);
+end;
+
+procedure TSecP384R1FpFieldOps.MulByB3(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP384R1Field.Multiply(AX, FB3, AZ);
+end;
+
+procedure TSecP384R1FpFieldOps.MulByA(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP384R1Field.Multiply(AX, FA, AZ);
+end;
+
+procedure TSecP384R1FpFieldOps.Inv(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP384R1Field.Inv(AX, AZ);
+end;
+
+function TSecP384R1FpFieldOps.IsZero(const AX: TCryptoLibUInt32Array): Boolean;
+begin
+ Result := TSecP384R1Field.IsZero(AX) <> 0;
+end;
+
+procedure TSecP384R1FpFieldOps.RandomMult(const ARandom: ISecureRandom;
+ const AZ: TCryptoLibUInt32Array);
+begin
+ TSecP384R1Field.RandomMult(ARandom, AZ);
+end;
+
+procedure TSecP384R1FpFieldOps.FieldFromBigInteger(const AX: TBigInteger;
+ const AZ: TCryptoLibUInt32Array);
+begin
+ TNat.Copy(FE_INTS, TSecP384R1Field.FromBigInteger(AX), 0, AZ, 0);
+end;
+
+function TSecP384R1FpFieldOps.CreateFieldElement(const AX: TCryptoLibUInt32Array): IECFieldElement;
+begin
+ Result := TSecP384R1FieldElement.Create(AX) as IECFieldElement;
+end;
+
+procedure TSecP384R1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array);
+begin
+ TNat.Copy(FE_INTS, FOne, 0, AZ, 0);
+end;
+
end.
diff --git a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas
index 585231a8..68164844 100644
--- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas
+++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas
@@ -36,6 +36,8 @@ interface
ClpECFieldElement,
ClpECPoint,
ClpECLookupTables,
+ ClpIFpFieldOps,
+ ClpFixedWindowCTMultiplier,
ClpIECCommon,
ClpIECFieldElement,
ClpISecP521R1Custom,
@@ -188,11 +190,39 @@ TSecP521R1LookupTable = class sealed(TAbstractECLookupTable, IECLookupTable,
function RandomFieldElement(const ARandom: ISecureRandom): IECFieldElement; override;
function RandomFieldElementMult(const ARandom: ISecureRandom): IECFieldElement; override;
function SupportsCoordinateSystem(ACoord: Int32): Boolean; override;
+ function CreateDefaultMultiplier: IECMultiplier; override;
class property Q: TBigInteger read FQ;
class property SecP521R1AffineZs: TCryptoLibGenericArray read FSecP521R1AffineZs;
end;
+type
+ TSecP521R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps)
+ strict private
+ const
+ FE_INTS = Int32(17);
+ var
+ FA, FB3, FOne, FN: TCryptoLibUInt32Array;
+ FOrderBits: Int32;
+ public
+ constructor Create(const AA, AB: IECFieldElement; const AOrder: TBigInteger);
+ function GetFieldInts: Int32;
+ function GetOrderBits: Int32;
+ procedure GetOrder(const AZ: TCryptoLibUInt32Array; AInts: Int32);
+ procedure Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure Square(const AX, AZ: TCryptoLibUInt32Array);
+ procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure Sub(const AX, AY, AZ: TCryptoLibUInt32Array);
+ procedure MulByB3(const AX, AZ: TCryptoLibUInt32Array);
+ procedure MulByA(const AX, AZ: TCryptoLibUInt32Array);
+ procedure Inv(const AX, AZ: TCryptoLibUInt32Array);
+ function IsZero(const AX: TCryptoLibUInt32Array): Boolean;
+ procedure RandomMult(const ARandom: ISecureRandom; const AZ: TCryptoLibUInt32Array);
+ procedure FieldFromBigInteger(const AX: TBigInteger; const AZ: TCryptoLibUInt32Array);
+ function CreateFieldElement(const AX: TCryptoLibUInt32Array): IECFieldElement;
+ procedure FieldOne(const AZ: TCryptoLibUInt32Array);
+ end;
+
implementation
{ TSecP521R1Field }
@@ -1042,4 +1072,109 @@ function TSecP521R1Curve.SupportsCoordinateSystem(ACoord: Int32): Boolean;
end;
end;
+function TSecP521R1Curve.CreateDefaultMultiplier: IECMultiplier;
+var
+ LCurve: IECCurve;
+ LFieldOps: IFpFieldOps;
+begin
+ LCurve := Self as IECCurve;
+ LFieldOps := TSecP521R1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order);
+ Result := TFixedWindowCTMultiplier.Create(LFieldOps) as IECMultiplier;
+end;
+
+{ TSecP521R1FpFieldOps }
+
+constructor TSecP521R1FpFieldOps.Create(const AA, AB: IECFieldElement; const AOrder: TBigInteger);
+var
+ LB, LB3: TCryptoLibUInt32Array;
+begin
+ Inherited Create;
+ FOrderBits := AOrder.BitLength;
+ FN := TNat.FromBigInteger(FE_INTS * 32, AOrder);
+ FOne := TSecP521R1Field.FromBigInteger(TBigInteger.One);
+ FA := TNat.Copy(FE_INTS, (AA as ISecP521R1FieldElement).X);
+ LB := (AB as ISecP521R1FieldElement).X;
+ LB3 := TNat.Create(FE_INTS);
+ TSecP521R1Field.Add(LB, LB, LB3);
+ TSecP521R1Field.Add(LB3, LB, LB3);
+ FB3 := LB3;
+end;
+
+function TSecP521R1FpFieldOps.GetFieldInts: Int32;
+begin
+ Result := FE_INTS;
+end;
+
+function TSecP521R1FpFieldOps.GetOrderBits: Int32;
+begin
+ Result := FOrderBits;
+end;
+
+procedure TSecP521R1FpFieldOps.GetOrder(const AZ: TCryptoLibUInt32Array; AInts: Int32);
+begin
+ TNat.Copy(FE_INTS, FN, 0, AZ, 0);
+end;
+
+procedure TSecP521R1FpFieldOps.Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP521R1Field.Multiply(AX, AY, AZ);
+end;
+
+procedure TSecP521R1FpFieldOps.Square(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP521R1Field.Square(AX, AZ);
+end;
+
+procedure TSecP521R1FpFieldOps.Add(const AX, AY, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP521R1Field.Add(AX, AY, AZ);
+end;
+
+procedure TSecP521R1FpFieldOps.Sub(const AX, AY, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP521R1Field.Subtract(AX, AY, AZ);
+end;
+
+procedure TSecP521R1FpFieldOps.MulByB3(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP521R1Field.Multiply(AX, FB3, AZ);
+end;
+
+procedure TSecP521R1FpFieldOps.MulByA(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP521R1Field.Multiply(AX, FA, AZ);
+end;
+
+procedure TSecP521R1FpFieldOps.Inv(const AX, AZ: TCryptoLibUInt32Array);
+begin
+ TSecP521R1Field.Inv(AX, AZ);
+end;
+
+function TSecP521R1FpFieldOps.IsZero(const AX: TCryptoLibUInt32Array): Boolean;
+begin
+ Result := TSecP521R1Field.IsZero(AX) <> 0;
+end;
+
+procedure TSecP521R1FpFieldOps.RandomMult(const ARandom: ISecureRandom;
+ const AZ: TCryptoLibUInt32Array);
+begin
+ TSecP521R1Field.RandomMult(ARandom, AZ);
+end;
+
+procedure TSecP521R1FpFieldOps.FieldFromBigInteger(const AX: TBigInteger;
+ const AZ: TCryptoLibUInt32Array);
+begin
+ TNat.Copy(FE_INTS, TSecP521R1Field.FromBigInteger(AX), 0, AZ, 0);
+end;
+
+function TSecP521R1FpFieldOps.CreateFieldElement(const AX: TCryptoLibUInt32Array): IECFieldElement;
+begin
+ Result := TSecP521R1FieldElement.Create(AX) as IECFieldElement;
+end;
+
+procedure TSecP521R1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array);
+begin
+ TNat.Copy(FE_INTS, FOne, 0, AZ, 0);
+end;
+
end.
diff --git a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecT283K1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecT283K1Custom.pas
index c645ce16..0dbe161b 100644
--- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecT283K1Custom.pas
+++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecT283K1Custom.pas
@@ -26,6 +26,7 @@ interface
ClpBitOperations,
ClpNat,
ClpNat320,
+ ClpPack,
ClpInterleave,
ClpEncoders,
ClpECCurve,
@@ -34,6 +35,9 @@ interface
ClpECPoint,
ClpECLookupTables,
ClpMultipliers,
+ ClpIF2mFieldOps,
+ ClpF2mMontgomeryLadderCTMultiplier,
+ ClpISecureRandom,
ClpIECCommon,
ClpIECFieldElement,
ClpISecT283K1Custom,
@@ -61,6 +65,8 @@ TSecT283Field = class sealed(TObject)
class procedure ImplMultiply(const AX, AY, AZZ: TCryptoLibUInt64Array); static;
class procedure ImplSquare(const AX, AZZ: TCryptoLibUInt64Array); static;
class constructor Create;
+ private
+ class procedure InvertCT(const AX, AZ: TCryptoLibUInt64Array); static;
public
class procedure Add(const AX, AY, AZ: TCryptoLibUInt64Array); static;
class procedure AddBothTo(const AX, AY, AZ: TCryptoLibUInt64Array); static;
@@ -204,6 +210,31 @@ TSecT283K1LookupTable = class sealed(TAbstractECLookupTable, IECLookupTable,
class property SecT283K1AffineZs: TCryptoLibGenericArray read FSecT283K1AffineZs;
end;
+type
+ TSecT283K1F2mFieldOps = class sealed(TInterfacedObject, IF2mFieldOps)
+ strict private
+ const
+ FE_LONGS = Int32(5);
+ M27 = UInt64($7FFFFFF);
+ var
+ FCard: TCryptoLibUInt32Array;
+ FCardLongs, FCardBits: Int32;
+ public
+ constructor Create(const AOrder, ACofactor: TBigInteger);
+ function GetFieldLongs: Int32;
+ function GetCardinalityBits: Int32;
+ procedure GetCardinality(const AZ: TCryptoLibUInt32Array; AInts: Int32);
+ procedure Mul(const AX, AY, AZ: TCryptoLibUInt64Array);
+ procedure Square(const AX, AZ: TCryptoLibUInt64Array);
+ procedure Add(const AX, AY, AZ: TCryptoLibUInt64Array);
+ procedure MulByB(const AX, AZ: TCryptoLibUInt64Array);
+ procedure Inv(const AX, AZ: TCryptoLibUInt64Array);
+ function IsZeroMask(const AX: TCryptoLibUInt64Array): UInt64;
+ procedure RandomNonZero(const ARandom: ISecureRandom; const AZ: TCryptoLibUInt64Array);
+ procedure FieldFromBigInteger(const AX: TBigInteger; const AZ: TCryptoLibUInt64Array);
+ function CreateFieldElement(const AX: TCryptoLibUInt64Array): IECFieldElement;
+ end;
+
implementation
{ TSecT283Field }
@@ -472,11 +503,17 @@ class procedure TSecT283Field.HalfTrace(const AX, AZ: TCryptoLibUInt64Array);
end;
class procedure TSecT283Field.Invert(const AX, AZ: TCryptoLibUInt64Array);
-var
- LT0, LT1: TCryptoLibUInt64Array;
begin
if TNat320.IsZero64(AX) then
raise EInvalidOperationCryptoLibException.CreateRes(@SDivisionByZeroInFieldInversion);
+ InvertCT(AX, AZ);
+end;
+
+class procedure TSecT283Field.InvertCT(const AX, AZ: TCryptoLibUInt64Array);
+var
+ LT0, LT1: TCryptoLibUInt64Array;
+begin
+ // guard-free Itoh-Tsujii chain a^(2^283 - 2); 0 is a fixed point so InvertCT(0)=0
SetLength(LT0, 5);
SetLength(LT1, 5);
Square(AX, LT0);
@@ -1226,8 +1263,13 @@ function TSecT283K1Curve.GetIsKoblitz: Boolean;
end;
function TSecT283K1Curve.CreateDefaultMultiplier: IECMultiplier;
+var
+ LCurve: IECCurve;
+ LFieldOps: IF2mFieldOps;
begin
- Result := TWTauNafMultiplier.Create() as IECMultiplier;
+ LCurve := Self as IECCurve;
+ LFieldOps := TSecT283K1F2mFieldOps.Create(LCurve.Order, LCurve.Cofactor);
+ Result := TF2mMontgomeryLadderCTMultiplier.Create(LFieldOps) as IECMultiplier;
end;
function TSecT283K1Curve.CloneCurve: IECCurve;
@@ -1311,4 +1353,90 @@ function TSecT283K1Curve.GetK3: Int32;
Result := 12;
end;
+{ TSecT283K1F2mFieldOps }
+
+constructor TSecT283K1F2mFieldOps.Create(const AOrder, ACofactor: TBigInteger);
+var
+ LCard: TBigInteger;
+begin
+ Inherited Create;
+ LCard := AOrder.Multiply(ACofactor);
+ FCardBits := LCard.BitLength;
+ FCardLongs := TNat.GetLengthForBits(FCardBits);
+ FCard := TNat.FromBigInteger(FCardBits, LCard);
+end;
+
+function TSecT283K1F2mFieldOps.GetFieldLongs: Int32;
+begin
+ Result := FE_LONGS;
+end;
+
+function TSecT283K1F2mFieldOps.GetCardinalityBits: Int32;
+begin
+ Result := FCardBits;
+end;
+
+procedure TSecT283K1F2mFieldOps.GetCardinality(const AZ: TCryptoLibUInt32Array; AInts: Int32);
+begin
+ TNat.Copy(FCardLongs, FCard, 0, AZ, 0);
+end;
+
+procedure TSecT283K1F2mFieldOps.Mul(const AX, AY, AZ: TCryptoLibUInt64Array);
+begin
+ TSecT283Field.Multiply(AX, AY, AZ);
+end;
+
+procedure TSecT283K1F2mFieldOps.Square(const AX, AZ: TCryptoLibUInt64Array);
+begin
+ TSecT283Field.Square(AX, AZ);
+end;
+
+procedure TSecT283K1F2mFieldOps.Add(const AX, AY, AZ: TCryptoLibUInt64Array);
+begin
+ TSecT283Field.Add(AX, AY, AZ);
+end;
+
+procedure TSecT283K1F2mFieldOps.MulByB(const AX, AZ: TCryptoLibUInt64Array);
+begin
+ // sect283k1 has b = 1: z := x
+ TNat.Copy64(FE_LONGS, AX, 0, AZ, 0);
+end;
+
+procedure TSecT283K1F2mFieldOps.Inv(const AX, AZ: TCryptoLibUInt64Array);
+begin
+ TSecT283Field.InvertCT(AX, AZ);
+end;
+
+function TSecT283K1F2mFieldOps.IsZeroMask(const AX: TCryptoLibUInt64Array): UInt64;
+var
+ LV: UInt64;
+begin
+ LV := AX[0] or AX[1] or AX[2] or AX[3] or AX[4];
+ Result := not UInt64(TBitOperations.Asr64(Int64(LV or (UInt64(0) - LV)), 63));
+end;
+
+procedure TSecT283K1F2mFieldOps.RandomNonZero(const ARandom: ISecureRandom;
+ const AZ: TCryptoLibUInt64Array);
+var
+ LBytes: TCryptoLibByteArray;
+begin
+ System.SetLength(LBytes, FE_LONGS * 8);
+ repeat
+ ARandom.NextBytes(LBytes);
+ TPack.LE_To_UInt64(LBytes, 0, AZ, 0, FE_LONGS);
+ AZ[FE_LONGS - 1] := AZ[FE_LONGS - 1] and M27;
+ until IsZeroMask(AZ) = 0;
+end;
+
+procedure TSecT283K1F2mFieldOps.FieldFromBigInteger(const AX: TBigInteger;
+ const AZ: TCryptoLibUInt64Array);
+begin
+ TNat.Copy64(FE_LONGS, TSecT283Field.FromBigInteger(AX), 0, AZ, 0);
+end;
+
+function TSecT283K1F2mFieldOps.CreateFieldElement(const AX: TCryptoLibUInt64Array): IECFieldElement;
+begin
+ Result := TSecT283FieldElement.Create(AX) as IECFieldElement;
+end;
+
end.
diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpF2mMontgomeryLadderCTMultiplier.pas b/CryptoLib/src/Math/EC/Multiplier/ClpF2mMontgomeryLadderCTMultiplier.pas
new file mode 100644
index 00000000..43127a5d
--- /dev/null
+++ b/CryptoLib/src/Math/EC/Multiplier/ClpF2mMontgomeryLadderCTMultiplier.pas
@@ -0,0 +1,186 @@
+{ *********************************************************************************** }
+{ * CryptoLib Library * }
+{ * Author - Ugochukwu Mmaduekwe * }
+{ * Github Repository * }
+{ * * }
+{ * 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 * }
+{ * the development of this library * }
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpF2mMontgomeryLadderCTMultiplier;
+
+{$I ..\..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+ SysUtils,
+ ClpBigInteger,
+ ClpNat,
+ ClpPack,
+ ClpMultipliers,
+ ClpIF2mFieldOps,
+ ClpLopezDahabLadder,
+ ClpISecureRandom,
+ ClpSecureRandom,
+ ClpIECFieldElement,
+ ClpIECCommon,
+ ClpCryptoLibTypes,
+ ClpCryptoLibExceptions;
+
+type
+ ///
+ /// Constant-time single-scalar variable-point multiplier for binary (GF(2^m))
+ /// curves — a plain López–Dahab Montgomery ladder (x-only), with a branch-free
+ /// conditional swap, a batched single field inversion for y-recovery, scalar
+ /// blinding by the group cardinality, independent randomized projective
+ /// coordinates, and a fixed processing length.
+ ///
+ TF2mMontgomeryLadderCTMultiplier = class sealed(TAbstractECMultiplier, IECMultiplier)
+ strict private
+ const
+ BLIND_BITS = Int32(64);
+ var
+ FFieldOps: IF2mFieldOps;
+ FRandom: ISecureRandom;
+ function GetRandom: ISecureRandom;
+ procedure GenerateBlind(const ARandom: ISecureRandom; const AZ: TCryptoLibUInt32Array);
+ strict protected
+ function MultiplyPositive(const AP: IECPoint; const AK: TBigInteger): IECPoint; override;
+ public
+ constructor Create(const AFieldOps: IF2mFieldOps); overload;
+ constructor Create(const AFieldOps: IF2mFieldOps; const ARandom: ISecureRandom); overload;
+ end;
+
+implementation
+
+resourcestring
+ SPointNotOnCurve = 'point is not a valid subgroup point for constant-time multiplication';
+
+{ TF2mMontgomeryLadderCTMultiplier }
+
+constructor TF2mMontgomeryLadderCTMultiplier.Create(const AFieldOps: IF2mFieldOps);
+begin
+ Inherited Create;
+ FFieldOps := AFieldOps;
+end;
+
+constructor TF2mMontgomeryLadderCTMultiplier.Create(const AFieldOps: IF2mFieldOps;
+ const ARandom: ISecureRandom);
+begin
+ Inherited Create;
+ FFieldOps := AFieldOps;
+ FRandom := ARandom;
+end;
+
+function TF2mMontgomeryLadderCTMultiplier.GetRandom: ISecureRandom;
+begin
+ if FRandom = nil then
+ FRandom := TSecureRandom.Create() as ISecureRandom;
+ Result := FRandom;
+end;
+
+procedure TF2mMontgomeryLadderCTMultiplier.GenerateBlind(const ARandom: ISecureRandom;
+ const AZ: TCryptoLibUInt32Array);
+var
+ LBytes: TCryptoLibByteArray;
+begin
+ System.SetLength(LBytes, BLIND_BITS div 8);
+ ARandom.NextBytes(LBytes);
+ TPack.LE_To_UInt32(LBytes, 0, AZ, 0, BLIND_BITS div 32);
+end;
+
+function TF2mMontgomeryLadderCTMultiplier.MultiplyPositive(const AP: IECPoint;
+ const AK: TBigInteger): IECPoint;
+var
+ LN, LScalarBits, LScalarInts, LI, LKbit, LPbit: Int32;
+ LRandom: ISecureRandom;
+ LAffine: IECPoint;
+ LBaseX, LBaseY, LLambda1, LLambda2, LXOut, LLOut: TCryptoLibUInt64Array;
+ LCard, LR, LProd, LK, LKPrime: TCryptoLibUInt32Array;
+ LQ1, LQ2: TLDPoint;
+ LIsInfinity: Boolean;
+ LXfe, LLfe: IECFieldElement;
+begin
+ if not AP.IsValid then
+ raise EInvalidOperationCryptoLibException.CreateRes(@SPointNotOnCurve);
+
+ LN := FFieldOps.GetFieldLongs;
+ LRandom := GetRandom;
+
+ // --- affine coordinates of the (public) input point ---
+ LAffine := AP.Normalize();
+ LBaseX := TNat.Create64(LN);
+ LBaseY := TNat.Create64(LN);
+ FFieldOps.FieldFromBigInteger(LAffine.AffineXCoord.ToBigInteger(), LBaseX);
+ FFieldOps.FieldFromBigInteger(LAffine.AffineYCoord.ToBigInteger(), LBaseY);
+ // defensive: x = 0 is the 2-torsion point, not in the odd-order subgroup
+ if FFieldOps.IsZeroMask(LBaseX) <> 0 then
+ raise EInvalidOperationCryptoLibException.CreateRes(@SPointNotOnCurve);
+
+ // --- scalar blinding by the cardinality: k' = k + r*(h*n) ---
+ LScalarBits := FFieldOps.GetCardinalityBits + BLIND_BITS + 1;
+ LScalarInts := TNat.GetLengthForBits(LScalarBits) + 1;
+ LCard := TNat.Create(LScalarInts);
+ LR := TNat.Create(LScalarInts);
+ LProd := TNat.Create(LScalarInts * 2);
+ FFieldOps.GetCardinality(LCard, LScalarInts);
+ GenerateBlind(LRandom, LR);
+ TNat.Mul(LScalarInts, LR, LCard, LProd);
+ LK := TNat.FromBigInteger(LScalarInts * 32, AK);
+ LKPrime := TNat.Create(LScalarInts);
+ TNat.Add(LScalarInts, LK, LProd, LKPrime);
+
+ try
+ // --- randomized projective init: Q1 = infinity = (lambda1 : 0), Q2 = P = (lambda2*x : lambda2) ---
+ LLambda1 := TNat.Create64(LN);
+ LLambda2 := TNat.Create64(LN);
+ FFieldOps.RandomNonZero(LRandom, LLambda1);
+ FFieldOps.RandomNonZero(LRandom, LLambda2);
+
+ LQ1.X := TNat.Create64(LN); LQ1.Z := TNat.Create64(LN);
+ LQ2.X := TNat.Create64(LN); LQ2.Z := TNat.Create64(LN);
+ TNat.Copy64(LN, LLambda1, LQ1.X); // Q1.X = lambda1, Q1.Z = 0 (infinity)
+ FFieldOps.Mul(LLambda2, LBaseX, LQ2.X); // Q2.X = lambda2 * x
+ TNat.Copy64(LN, LLambda2, LQ2.Z); // Q2.Z = lambda2
+
+ // --- fixed-length Montgomery ladder with branch-free conditional swap ---
+ LPbit := 0;
+ for LI := LScalarBits - 1 downto 0 do
+ begin
+ LKbit := Int32(TNat.GetBit(LKPrime, LI)) xor LPbit;
+ TNat.CSwap64(LN, LKbit, LQ1.X, LQ2.X);
+ TNat.CSwap64(LN, LKbit, LQ1.Z, LQ2.Z);
+ LQ2 := TLopezDahabLadder.MAdd(FFieldOps, LQ1, LQ2, LBaseX);
+ LQ1 := TLopezDahabLadder.MDouble(FFieldOps, LQ1);
+ LPbit := Int32(TNat.GetBit(LKPrime, LI));
+ end;
+ TNat.CSwap64(LN, LPbit, LQ1.X, LQ2.X);
+ TNat.CSwap64(LN, LPbit, LQ1.Z, LQ2.Z);
+
+ // --- y-recovery (batched single inversion) ---
+ LXOut := TNat.Create64(LN);
+ LLOut := TNat.Create64(LN);
+ TLopezDahabLadder.Recover(FFieldOps, LQ1, LQ2, LBaseX, LBaseY, LXOut, LLOut, LIsInfinity);
+ if LIsInfinity then
+ Exit(AP.Curve.Infinity);
+
+ LXfe := FFieldOps.CreateFieldElement(LXOut);
+ LLfe := FFieldOps.CreateFieldElement(LLOut);
+ Result := AP.Curve.CreateRawPoint(LXfe, LLfe);
+ finally
+ TNat.Zero(LScalarInts, LKPrime);
+ TNat.Zero(LScalarInts, LK);
+ TNat.Zero(LScalarInts, LR);
+ TNat.Zero(LScalarInts * 2, LProd);
+ end;
+end;
+
+end.
diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpFixedWindowCTMultiplier.pas b/CryptoLib/src/Math/EC/Multiplier/ClpFixedWindowCTMultiplier.pas
new file mode 100644
index 00000000..a333205f
--- /dev/null
+++ b/CryptoLib/src/Math/EC/Multiplier/ClpFixedWindowCTMultiplier.pas
@@ -0,0 +1,251 @@
+{ *********************************************************************************** }
+{ * CryptoLib Library * }
+{ * Author - Ugochukwu Mmaduekwe * }
+{ * Github Repository * }
+{ * * }
+{ * 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 * }
+{ * the development of this library * }
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpFixedWindowCTMultiplier;
+
+{$I ..\..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+ SysUtils,
+ ClpBigInteger,
+ ClpNat,
+ ClpPack,
+ ClpBitOperations,
+ ClpMultipliers,
+ ClpIFpFieldOps,
+ ClpHomogeneousPoint,
+ ClpISecureRandom,
+ ClpSecureRandom,
+ ClpIECFieldElement,
+ ClpIECCommon,
+ ClpCryptoLibTypes,
+ ClpCryptoLibExceptions;
+
+type
+ ///
+ /// Constant-time single-scalar variable-point multiplier for prime-field (Fp)
+ /// short-Weierstrass curves, driven by an IFpFieldOps adapter. Unsigned fixed
+ /// window over homogeneous complete formulas, masked table lookups only, one
+ /// unconditional addition per window; scalar blinding, randomized projective
+ /// coordinates and a fixed processing length as countermeasures.
+ ///
+ TFixedWindowCTMultiplier = class sealed(TAbstractECMultiplier, IECMultiplier)
+ strict private
+ const
+ WINDOW_BITS = Int32(4);
+ TABLE_SIZE = Int32(16);
+ DEFAULT_BLIND_BITS = Int32(64);
+ MAX_BLIND_BITS = Int32(512);
+ var
+ FFieldOps: IFpFieldOps;
+ FRandom: ISecureRandom;
+ FBlindBits: Int32;
+ function GetRandom: ISecureRandom;
+ procedure GenerateBlind(const ARandom: ISecureRandom; const AZ: TCryptoLibUInt32Array);
+ function SelectEntry(const ATable: TCryptoLibGenericArray;
+ AIndex: Int32): TCTHomogPoint;
+ strict protected
+ function MultiplyPositive(const AP: IECPoint; const AK: TBigInteger): IECPoint; override;
+ public
+ constructor Create(const AFieldOps: IFpFieldOps;
+ ABlindBits: Int32 = DEFAULT_BLIND_BITS); overload;
+ constructor Create(const AFieldOps: IFpFieldOps; const ARandom: ISecureRandom;
+ ABlindBits: Int32 = DEFAULT_BLIND_BITS); overload;
+ end;
+
+implementation
+
+resourcestring
+ SPointNotOnCurve = 'point is not a valid point on the curve for constant-time multiplication';
+ SScalarTooLarge = 'scalar is larger than the curve order';
+ SInvalidBlindBits = 'blinding length must be a multiple of 32 between 64 and 512';
+
+{ TFixedWindowCTMultiplier }
+
+constructor TFixedWindowCTMultiplier.Create(const AFieldOps: IFpFieldOps;
+ ABlindBits: Int32);
+begin
+ Inherited Create;
+ if (ABlindBits < DEFAULT_BLIND_BITS) or (ABlindBits > MAX_BLIND_BITS)
+ or ((ABlindBits and 31) <> 0) then
+ raise EArgumentCryptoLibException.CreateRes(@SInvalidBlindBits);
+ FFieldOps := AFieldOps;
+ FBlindBits := ABlindBits;
+end;
+
+constructor TFixedWindowCTMultiplier.Create(const AFieldOps: IFpFieldOps;
+ const ARandom: ISecureRandom; ABlindBits: Int32);
+begin
+ Inherited Create;
+ if (ABlindBits < DEFAULT_BLIND_BITS) or (ABlindBits > MAX_BLIND_BITS)
+ or ((ABlindBits and 31) <> 0) then
+ raise EArgumentCryptoLibException.CreateRes(@SInvalidBlindBits);
+ FFieldOps := AFieldOps;
+ FRandom := ARandom;
+ FBlindBits := ABlindBits;
+end;
+
+function TFixedWindowCTMultiplier.GetRandom: ISecureRandom;
+begin
+ if FRandom = nil then
+ FRandom := TSecureRandom.Create() as ISecureRandom;
+ Result := FRandom;
+end;
+
+procedure TFixedWindowCTMultiplier.GenerateBlind(const ARandom: ISecureRandom;
+ const AZ: TCryptoLibUInt32Array);
+var
+ LBytes: TCryptoLibByteArray;
+begin
+ // FBlindBits of fresh randomness in the low limbs of AZ
+ System.SetLength(LBytes, FBlindBits div 8);
+ ARandom.NextBytes(LBytes);
+ TPack.LE_To_UInt32(LBytes, 0, AZ, 0, FBlindBits div 32);
+end;
+
+function TFixedWindowCTMultiplier.SelectEntry(
+ const ATable: TCryptoLibGenericArray; AIndex: Int32): TCTHomogPoint;
+var
+ LN, LI, LJ: Int32;
+ LMask: UInt32;
+begin
+ LN := FFieldOps.GetFieldInts;
+ Result.X := TNat.Create(LN);
+ Result.Y := TNat.Create(LN);
+ Result.Z := TNat.Create(LN);
+ for LI := 0 to TABLE_SIZE - 1 do
+ begin
+ LMask := UInt32(TBitOperations.Asr32(((LI xor AIndex) - 1), 31));
+ for LJ := 0 to LN - 1 do
+ begin
+ Result.X[LJ] := Result.X[LJ] xor (ATable[LI].X[LJ] and LMask);
+ Result.Y[LJ] := Result.Y[LJ] xor (ATable[LI].Y[LJ] and LMask);
+ Result.Z[LJ] := Result.Z[LJ] xor (ATable[LI].Z[LJ] and LMask);
+ end;
+ end;
+end;
+
+function TFixedWindowCTMultiplier.MultiplyPositive(const AP: IECPoint;
+ const AK: TBigInteger): IECPoint;
+var
+ LFieldInts, LScalarBits, LScalarInts, LWindows, LI, LJ, LBit, LLimb, LShift, LDigit: Int32;
+ LTable: TCryptoLibGenericArray;
+ LBase, LAcc, LSel: TCTHomogPoint;
+ LLambda, LXa, LYa, LN, LR, LProd, LK, LKPrime: TCryptoLibUInt32Array;
+ LIsInfinity: Boolean;
+ LXfe, LYfe: IECFieldElement;
+ LAffine: IECPoint;
+ LRandom: ISecureRandom;
+begin
+ if not AP.IsValid then
+ raise EInvalidOperationCryptoLibException.CreateRes(@SPointNotOnCurve);
+
+ // reject scalars wider than the group order (the fixed-width buffer holds up to that)
+ if AK.BitLength > FFieldOps.GetOrderBits then
+ raise EInvalidOperationCryptoLibException.CreateRes(@SScalarTooLarge);
+
+ LFieldInts := FFieldOps.GetFieldInts;
+ LRandom := GetRandom;
+
+ // --- affine coordinates of the (public) input point ---
+ LAffine := AP.Normalize();
+ LXa := TNat.Create(LFieldInts);
+ LYa := TNat.Create(LFieldInts);
+ FFieldOps.FieldFromBigInteger(LAffine.AffineXCoord.ToBigInteger(), LXa);
+ FFieldOps.FieldFromBigInteger(LAffine.AffineYCoord.ToBigInteger(), LYa);
+
+ // --- randomized projective coordinates: base = (lambda*x, lambda*y, lambda) ---
+ LLambda := TNat.Create(LFieldInts);
+ FFieldOps.RandomMult(LRandom, LLambda);
+ LBase := TCTHomogeneousMath.ScaleRandom(FFieldOps,
+ TCTHomogeneousMath.FromAffine(FFieldOps, LXa, LYa), LLambda);
+
+ // --- projective precomputation table [0]=O, [i]=[i]*base ---
+ System.SetLength(LTable, TABLE_SIZE);
+ LTable[0] := TCTHomogeneousMath.Infinity(FFieldOps);
+ LTable[1] := LBase;
+ for LI := 2 to TABLE_SIZE - 1 do
+ LTable[LI] := TCTHomogeneousMath.Add(FFieldOps, LTable[LI - 1], LBase);
+
+ // --- scalar blinding in fixed-width Nat: k' = k + r*n ---
+ LScalarBits := FFieldOps.GetOrderBits + FBlindBits + 1;
+ LScalarInts := TNat.GetLengthForBits(LScalarBits) + 1;
+ LN := TNat.Create(LScalarInts);
+ LR := TNat.Create(LScalarInts);
+ LProd := TNat.Create(LScalarInts * 2);
+ FFieldOps.GetOrder(LN, LScalarInts);
+ GenerateBlind(LRandom, LR);
+ TNat.Mul(LScalarInts, LR, LN, LProd);
+ LK := TNat.FromBigInteger(LScalarInts * 32, AK);
+ LKPrime := TNat.Create(LScalarInts);
+ TNat.Add(LScalarInts, LK, LProd, LKPrime);
+
+ try
+ // --- fixed-length windowed ladder ---
+ LWindows := (LScalarBits + WINDOW_BITS - 1) div WINDOW_BITS;
+ LAcc := TCTHomogeneousMath.Infinity(FFieldOps);
+ for LI := LWindows - 1 downto 0 do
+ begin
+ for LJ := 0 to WINDOW_BITS - 1 do
+ LAcc := TCTHomogeneousMath.Double(FFieldOps, LAcc);
+
+ // WINDOW_BITS divides 32, so a digit never spans a limb boundary
+ LBit := LI * WINDOW_BITS;
+ LLimb := TBitOperations.Asr32(LBit, 5);
+ LShift := LBit and 31;
+ LDigit := Int32((LKPrime[LLimb] shr LShift) and UInt32(TABLE_SIZE - 1));
+
+ LSel := SelectEntry(LTable, LDigit);
+ LAcc := TCTHomogeneousMath.Add(FFieldOps, LAcc, LSel);
+ end;
+
+ TCTHomogeneousMath.ToAffine(FFieldOps, LAcc, LXa, LYa, LIsInfinity);
+ if LIsInfinity then
+ Exit(AP.Curve.Infinity);
+
+ LXfe := FFieldOps.CreateFieldElement(LXa);
+ LYfe := FFieldOps.CreateFieldElement(LYa);
+ Result := AP.Curve.CreateRawPoint(LXfe, LYfe);
+ finally
+ TNat.Zero(LScalarInts, LKPrime);
+ TNat.Zero(LScalarInts, LK);
+ TNat.Zero(LScalarInts, LR);
+ TNat.Zero(LScalarInts * 2, LProd);
+ TNat.Zero(LFieldInts, LLambda);
+ for LI := 0 to TABLE_SIZE - 1 do
+ begin
+ TNat.Zero(LFieldInts, LTable[LI].X);
+ TNat.Zero(LFieldInts, LTable[LI].Y);
+ TNat.Zero(LFieldInts, LTable[LI].Z);
+ end;
+ if LAcc.X <> nil then
+ begin
+ TNat.Zero(LFieldInts, LAcc.X);
+ TNat.Zero(LFieldInts, LAcc.Y);
+ TNat.Zero(LFieldInts, LAcc.Z);
+ end;
+ if LSel.X <> nil then
+ begin
+ TNat.Zero(LFieldInts, LSel.X);
+ TNat.Zero(LFieldInts, LSel.Y);
+ TNat.Zero(LFieldInts, LSel.Z);
+ end;
+ end;
+end;
+
+end.
diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpHomogeneousPoint.pas b/CryptoLib/src/Math/EC/Multiplier/ClpHomogeneousPoint.pas
new file mode 100644
index 00000000..d8c83913
--- /dev/null
+++ b/CryptoLib/src/Math/EC/Multiplier/ClpHomogeneousPoint.pas
@@ -0,0 +1,232 @@
+{ *********************************************************************************** }
+{ * CryptoLib Library * }
+{ * Author - Ugochukwu Mmaduekwe * }
+{ * Github Repository * }
+{ * * }
+{ * 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 * }
+{ * the development of this library * }
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpHomogeneousPoint;
+
+{$I ..\..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+ ClpNat,
+ ClpIFpFieldOps,
+ ClpCryptoLibTypes;
+
+type
+ ///
+ /// A point in homogeneous projective coordinates (X : Y : Z), x = X/Z, y = Y/Z,
+ /// identity = (0 : 1 : 0). Value aggregate over the three limb arrays.
+ ///
+ TCTHomogPoint = record
+ X, Y, Z: TCryptoLibUInt32Array;
+ end;
+
+ ///
+ /// Renes-Costello-Batina (EUROCRYPT 2016) complete addition formulas,
+ /// a = -3 specialization (Algorithms 4 and 6), homogeneous coordinates.
+ /// Exception-free for prime-order (cofactor 1) short-Weierstrass curves.
+ ///
+ TCTHomogeneousMath = class sealed(TObject)
+ public
+ class function Infinity(const AFO: IFpFieldOps): TCTHomogPoint; static;
+ class function FromAffine(const AFO: IFpFieldOps;
+ const AX, AY: TCryptoLibUInt32Array): TCTHomogPoint; static;
+ class function ScaleRandom(const AFO: IFpFieldOps; const AP: TCTHomogPoint;
+ const ALambda: TCryptoLibUInt32Array): TCTHomogPoint; static;
+ class function Add(const AFO: IFpFieldOps; const AP, AQ: TCTHomogPoint): TCTHomogPoint; static;
+ class function Double(const AFO: IFpFieldOps; const AP: TCTHomogPoint): TCTHomogPoint; static;
+ class procedure ToAffine(const AFO: IFpFieldOps; const AP: TCTHomogPoint;
+ out AX, AY: TCryptoLibUInt32Array; out AIsInfinity: Boolean); static;
+ end;
+
+implementation
+
+{ TCTHomogeneousMath }
+
+class function TCTHomogeneousMath.Infinity(const AFO: IFpFieldOps): TCTHomogPoint;
+var
+ LN: Int32;
+ LX, LY, LZ: TCryptoLibUInt32Array;
+begin
+ LN := AFO.GetFieldInts;
+ LX := TNat.Create(LN);
+ LY := TNat.Create(LN);
+ LZ := TNat.Create(LN);
+ AFO.FieldOne(LY);
+ Result.X := LX;
+ Result.Y := LY;
+ Result.Z := LZ;
+end;
+
+class function TCTHomogeneousMath.FromAffine(const AFO: IFpFieldOps;
+ const AX, AY: TCryptoLibUInt32Array): TCTHomogPoint;
+var
+ LN: Int32;
+ LX, LY, LZ: TCryptoLibUInt32Array;
+begin
+ LN := AFO.GetFieldInts;
+ LX := TNat.Copy(LN, AX);
+ LY := TNat.Copy(LN, AY);
+ LZ := TNat.Create(LN);
+ AFO.FieldOne(LZ);
+ Result.X := LX;
+ Result.Y := LY;
+ Result.Z := LZ;
+end;
+
+class function TCTHomogeneousMath.ScaleRandom(const AFO: IFpFieldOps;
+ const AP: TCTHomogPoint; const ALambda: TCryptoLibUInt32Array): TCTHomogPoint;
+var
+ LN: Int32;
+begin
+ LN := AFO.GetFieldInts;
+ Result.X := TNat.Create(LN);
+ Result.Y := TNat.Create(LN);
+ Result.Z := TNat.Create(LN);
+ AFO.Mul(AP.X, ALambda, Result.X);
+ AFO.Mul(AP.Y, ALambda, Result.Y);
+ AFO.Mul(AP.Z, ALambda, Result.Z);
+end;
+
+class function TCTHomogeneousMath.Add(const AFO: IFpFieldOps;
+ const AP, AQ: TCTHomogPoint): TCTHomogPoint;
+var
+ LN: Int32;
+ Lt0, Lt1, Lt2, Lt3, Lt4, Lt5, LX3, LY3, LZ3: TCryptoLibUInt32Array;
+ LX1, LY1, LZ1, LX2, LY2, LZ2: TCryptoLibUInt32Array;
+begin
+ LN := AFO.GetFieldInts;
+ LX1 := AP.X; LY1 := AP.Y; LZ1 := AP.Z;
+ LX2 := AQ.X; LY2 := AQ.Y; LZ2 := AQ.Z;
+
+ Lt0 := TNat.Create(LN); Lt1 := TNat.Create(LN); Lt2 := TNat.Create(LN);
+ Lt3 := TNat.Create(LN); Lt4 := TNat.Create(LN); Lt5 := TNat.Create(LN);
+ LX3 := TNat.Create(LN); LY3 := TNat.Create(LN); LZ3 := TNat.Create(LN);
+
+ // RCB2016 complete addition (Algorithm 1), explicit a and b3 = 3b
+ AFO.Mul(LX1, LX2, Lt0); // t0 = X1*X2
+ AFO.Mul(LY1, LY2, Lt1); // t1 = Y1*Y2
+ AFO.Mul(LZ1, LZ2, Lt2); // t2 = Z1*Z2
+ AFO.Add(LX1, LY1, Lt3); // t3 = X1+Y1
+ AFO.Add(LX2, LY2, Lt4); // t4 = X2+Y2
+ AFO.Mul(Lt3, Lt4, Lt3); // t3 = t3*t4
+ AFO.Add(Lt0, Lt1, Lt4); // t4 = t0+t1
+ AFO.Sub(Lt3, Lt4, Lt3); // t3 = t3-t4
+ AFO.Add(LX1, LZ1, Lt4); // t4 = X1+Z1
+ AFO.Add(LX2, LZ2, Lt5); // t5 = X2+Z2
+ AFO.Mul(Lt4, Lt5, Lt4); // t4 = t4*t5
+ AFO.Add(Lt0, Lt2, Lt5); // t5 = t0+t2
+ AFO.Sub(Lt4, Lt5, Lt4); // t4 = t4-t5
+ AFO.Add(LY1, LZ1, Lt5); // t5 = Y1+Z1
+ AFO.Add(LY2, LZ2, LX3); // X3 = Y2+Z2
+ AFO.Mul(Lt5, LX3, Lt5); // t5 = t5*X3
+ AFO.Add(Lt1, Lt2, LX3); // X3 = t1+t2
+ AFO.Sub(Lt5, LX3, Lt5); // t5 = t5-X3
+ AFO.MulByA(Lt4, LZ3); // Z3 = a*t4
+ AFO.MulByB3(Lt2, LX3); // X3 = b3*t2
+ AFO.Add(LX3, LZ3, LZ3); // Z3 = X3+Z3
+ AFO.Sub(Lt1, LZ3, LX3); // X3 = t1-Z3
+ AFO.Add(Lt1, LZ3, LZ3); // Z3 = t1+Z3
+ AFO.Mul(LX3, LZ3, LY3); // Y3 = X3*Z3
+ AFO.Add(Lt0, Lt0, Lt1); // t1 = t0+t0
+ AFO.Add(Lt1, Lt0, Lt1); // t1 = t1+t0
+ AFO.MulByA(Lt2, Lt2); // t2 = a*t2
+ AFO.MulByB3(Lt4, Lt4); // t4 = b3*t4
+ AFO.Add(Lt1, Lt2, Lt1); // t1 = t1+t2
+ AFO.Sub(Lt0, Lt2, Lt2); // t2 = t0-t2
+ AFO.MulByA(Lt2, Lt2); // t2 = a*t2
+ AFO.Add(Lt4, Lt2, Lt4); // t4 = t4+t2
+ AFO.Mul(Lt1, Lt4, Lt0); // t0 = t1*t4
+ AFO.Add(LY3, Lt0, LY3); // Y3 = Y3+t0
+ AFO.Mul(Lt5, Lt4, Lt0); // t0 = t5*t4
+ AFO.Mul(Lt3, LX3, LX3); // X3 = t3*X3
+ AFO.Sub(LX3, Lt0, LX3); // X3 = X3-t0
+ AFO.Mul(Lt3, Lt1, Lt0); // t0 = t3*t1
+ AFO.Mul(Lt5, LZ3, LZ3); // Z3 = t5*Z3
+ AFO.Add(LZ3, Lt0, LZ3); // Z3 = Z3+t0
+
+ Result.X := LX3; Result.Y := LY3; Result.Z := LZ3;
+end;
+
+class function TCTHomogeneousMath.Double(const AFO: IFpFieldOps;
+ const AP: TCTHomogPoint): TCTHomogPoint;
+var
+ LN: Int32;
+ Lt0, Lt1, Lt2, Lt3, LX3, LY3, LZ3: TCryptoLibUInt32Array;
+ LX, LY, LZ: TCryptoLibUInt32Array;
+begin
+ LN := AFO.GetFieldInts;
+ LX := AP.X; LY := AP.Y; LZ := AP.Z;
+
+ Lt0 := TNat.Create(LN); Lt1 := TNat.Create(LN); Lt2 := TNat.Create(LN);
+ Lt3 := TNat.Create(LN);
+ LX3 := TNat.Create(LN); LY3 := TNat.Create(LN); LZ3 := TNat.Create(LN);
+
+ // RCB2016 complete doubling (Algorithm 3), explicit a and b3 = 3b
+ AFO.Square(LX, Lt0); // t0 = X*X
+ AFO.Square(LY, Lt1); // t1 = Y*Y
+ AFO.Square(LZ, Lt2); // t2 = Z*Z
+ AFO.Mul(LX, LY, Lt3); // t3 = X*Y
+ AFO.Add(Lt3, Lt3, Lt3); // t3 = t3+t3
+ AFO.Mul(LX, LZ, LZ3); // Z3 = X*Z
+ AFO.Add(LZ3, LZ3, LZ3); // Z3 = Z3+Z3
+ AFO.MulByA(LZ3, LX3); // X3 = a*Z3
+ AFO.MulByB3(Lt2, LY3); // Y3 = b3*t2
+ AFO.Add(LX3, LY3, LY3); // Y3 = X3+Y3
+ AFO.Sub(Lt1, LY3, LX3); // X3 = t1-Y3
+ AFO.Add(Lt1, LY3, LY3); // Y3 = t1+Y3
+ AFO.Mul(LX3, LY3, LY3); // Y3 = X3*Y3
+ AFO.Mul(Lt3, LX3, LX3); // X3 = t3*X3
+ AFO.MulByB3(LZ3, LZ3); // Z3 = b3*Z3
+ AFO.MulByA(Lt2, Lt2); // t2 = a*t2
+ AFO.Sub(Lt0, Lt2, Lt3); // t3 = t0-t2
+ AFO.MulByA(Lt3, Lt3); // t3 = a*t3
+ AFO.Add(Lt3, LZ3, Lt3); // t3 = t3+Z3
+ AFO.Add(Lt0, Lt0, LZ3); // Z3 = t0+t0
+ AFO.Add(LZ3, Lt0, Lt0); // t0 = Z3+t0
+ AFO.Add(Lt0, Lt2, Lt0); // t0 = t0+t2
+ AFO.Mul(Lt0, Lt3, Lt0); // t0 = t0*t3
+ AFO.Add(LY3, Lt0, LY3); // Y3 = Y3+t0
+ AFO.Mul(LY, LZ, Lt2); // t2 = Y*Z
+ AFO.Add(Lt2, Lt2, Lt2); // t2 = t2+t2
+ AFO.Mul(Lt2, Lt3, Lt0); // t0 = t2*t3
+ AFO.Sub(LX3, Lt0, LX3); // X3 = X3-t0
+ AFO.Mul(Lt2, Lt1, LZ3); // Z3 = t2*t1
+ AFO.Add(LZ3, LZ3, LZ3); // Z3 = Z3+Z3
+ AFO.Add(LZ3, LZ3, LZ3); // Z3 = Z3+Z3
+
+ Result.X := LX3; Result.Y := LY3; Result.Z := LZ3;
+end;
+
+class procedure TCTHomogeneousMath.ToAffine(const AFO: IFpFieldOps;
+ const AP: TCTHomogPoint; out AX, AY: TCryptoLibUInt32Array; out AIsInfinity: Boolean);
+var
+ LN: Int32;
+ LZInv: TCryptoLibUInt32Array;
+begin
+ LN := AFO.GetFieldInts;
+ AIsInfinity := AFO.IsZero(AP.Z);
+ AX := TNat.Create(LN);
+ AY := TNat.Create(LN);
+ if AIsInfinity then
+ Exit;
+ LZInv := TNat.Create(LN);
+ AFO.Inv(AP.Z, LZInv);
+ AFO.Mul(AP.X, LZInv, AX);
+ AFO.Mul(AP.Y, LZInv, AY);
+end;
+
+end.
diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpLopezDahabLadder.pas b/CryptoLib/src/Math/EC/Multiplier/ClpLopezDahabLadder.pas
new file mode 100644
index 00000000..030d2308
--- /dev/null
+++ b/CryptoLib/src/Math/EC/Multiplier/ClpLopezDahabLadder.pas
@@ -0,0 +1,185 @@
+{ *********************************************************************************** }
+{ * CryptoLib Library * }
+{ * Author - Ugochukwu Mmaduekwe * }
+{ * Github Repository * }
+{ * * }
+{ * 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 * }
+{ * the development of this library * }
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpLopezDahabLadder;
+
+{$I ..\..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+ ClpNat,
+ ClpIF2mFieldOps,
+ ClpCryptoLibTypes;
+
+type
+ ///
+ /// A point in López–Dahab x-only projective coordinates (X : Z), x = X/Z,
+ /// point at infinity = (X : 0) with X != 0. Value aggregate over the limb arrays.
+ ///
+ TLDPoint = record
+ X, Z: TCryptoLibUInt64Array;
+ end;
+
+ ///
+ /// López–Dahab (1999) Montgomery-ladder x-only formulas over GF(2^m):
+ /// Mdouble, differential Madd, and Mxy y-recovery. See ClpLopezDahabLadder /
+ /// binary-ct plan for the exact formulas and the batched single-inversion
+ /// recovery. Exception-free through the infinity representation.
+ ///
+ TLopezDahabLadder = class sealed(TObject)
+ strict private
+ class procedure Sel(const AFO: IF2mFieldOps; AMask: UInt64;
+ const AIfTrue, AIfFalse, AZ: TCryptoLibUInt64Array); static;
+ public
+ // Mdouble: (X:Z) -> 2*(X:Z); Z' = X^2 Z^2, X' = X^4 + b Z^4
+ class function MDouble(const AFO: IF2mFieldOps; const AP: TLDPoint): TLDPoint; static;
+ // Madd: differential add of (X1:Z1),(X2:Z2) whose difference is the base point x.
+ // Z' = (X1 Z2 + X2 Z1)^2, X' = x Z' + (X1 Z2)(X2 Z1)
+ class function MAdd(const AFO: IF2mFieldOps; const AP1, AP2: TLDPoint;
+ const ABaseX: TCryptoLibUInt64Array): TLDPoint; static;
+ // Recover affine (x_k) and lambda (lambda_k) of the result from the final
+ // ladder state (Q1 = x(kP), Q2 = x((k+1)P)) and the base affine (x, y).
+ // Returns infinity when Z1 = 0. Batched single secret-path inversion.
+ class procedure Recover(const AFO: IF2mFieldOps; const AP1, AP2: TLDPoint;
+ const ABaseX, ABaseY: TCryptoLibUInt64Array;
+ const AXOut, ALOut: TCryptoLibUInt64Array; out AIsInfinity: Boolean); static;
+ end;
+
+implementation
+
+{ TLopezDahabLadder }
+
+class procedure TLopezDahabLadder.Sel(const AFO: IF2mFieldOps; AMask: UInt64;
+ const AIfTrue, AIfFalse, AZ: TCryptoLibUInt64Array);
+var
+ LN, LI: Int32;
+begin
+ // constant-time: AZ := AMask ? AIfTrue : AIfFalse (AMask is 0 or all-ones)
+ LN := AFO.GetFieldLongs;
+ for LI := 0 to LN - 1 do
+ AZ[LI] := (AIfTrue[LI] and AMask) or (AIfFalse[LI] and (not AMask));
+end;
+
+class function TLopezDahabLadder.MDouble(const AFO: IF2mFieldOps; const AP: TLDPoint): TLDPoint;
+var
+ LN: Int32;
+ Lt0, Lt1, LX3, LZ3: TCryptoLibUInt64Array;
+begin
+ LN := AFO.GetFieldLongs;
+ Lt0 := TNat.Create64(LN); Lt1 := TNat.Create64(LN);
+ LX3 := TNat.Create64(LN); LZ3 := TNat.Create64(LN);
+
+ AFO.Square(AP.X, Lt0); // t0 = X^2
+ AFO.Square(AP.Z, Lt1); // t1 = Z^2
+ AFO.Mul(Lt0, Lt1, LZ3); // Z3 = X^2 Z^2
+ AFO.Square(Lt0, Lt0); // t0 = X^4
+ AFO.Square(Lt1, Lt1); // t1 = Z^4
+ AFO.MulByB(Lt1, Lt1); // t1 = b Z^4
+ AFO.Add(Lt0, Lt1, LX3); // X3 = X^4 + b Z^4
+
+ Result.X := LX3; Result.Z := LZ3;
+end;
+
+class function TLopezDahabLadder.MAdd(const AFO: IF2mFieldOps; const AP1, AP2: TLDPoint;
+ const ABaseX: TCryptoLibUInt64Array): TLDPoint;
+var
+ LN: Int32;
+ LA, LB, LC, LD, LX3, LZ3: TCryptoLibUInt64Array;
+begin
+ LN := AFO.GetFieldLongs;
+ LA := TNat.Create64(LN); LB := TNat.Create64(LN); LC := TNat.Create64(LN);
+ LD := TNat.Create64(LN); LX3 := TNat.Create64(LN); LZ3 := TNat.Create64(LN);
+
+ AFO.Mul(AP1.X, AP2.Z, LA); // A = X1 Z2
+ AFO.Mul(AP2.X, AP1.Z, LB); // B = X2 Z1
+ AFO.Mul(LA, LB, LC); // C = A B
+ AFO.Add(LA, LB, LD); // D = A + B
+ AFO.Square(LD, LZ3); // Z3 = D^2
+ AFO.Mul(ABaseX, LZ3, LX3); // X3 = x Z3
+ AFO.Add(LX3, LC, LX3); // X3 = x Z3 + C
+
+ Result.X := LX3; Result.Z := LZ3;
+end;
+
+class procedure TLopezDahabLadder.Recover(const AFO: IF2mFieldOps; const AP1, AP2: TLDPoint;
+ const ABaseX, ABaseY: TCryptoLibUInt64Array;
+ const AXOut, ALOut: TCryptoLibUInt64Array; out AIsInfinity: Boolean);
+var
+ LN: Int32;
+ LZ1Z2, LP1, LU, LUinv, LInvP1, LInvZ1, LInvX1, LTmp, LTmp2: TCryptoLibUInt64Array;
+ LxZ1, LxZ2, Lxk, Ls, Lt, LN2, Lx2y, Lfrac, Lyk, LinvXk, Llamk: TCryptoLibUInt64Array;
+ LInvX, LxNeg, LlamNeg: TCryptoLibUInt64Array;
+ LmaskZ2: UInt64;
+begin
+ LN := AFO.GetFieldLongs;
+ AIsInfinity := AFO.IsZeroMask(AP1.Z) <> 0;
+ if AIsInfinity then
+ Exit; // kP = infinity (k == 0 mod ord P) - excluded for valid ECDH scalars
+
+ LZ1Z2 := TNat.Create64(LN); LP1 := TNat.Create64(LN); LU := TNat.Create64(LN);
+ LUinv := TNat.Create64(LN); LInvP1 := TNat.Create64(LN); LInvZ1 := TNat.Create64(LN);
+ LInvX1 := TNat.Create64(LN); LTmp := TNat.Create64(LN); LTmp2 := TNat.Create64(LN);
+ LxZ1 := TNat.Create64(LN); LxZ2 := TNat.Create64(LN); Lxk := TNat.Create64(LN);
+ Ls := TNat.Create64(LN); Lt := TNat.Create64(LN); LN2 := TNat.Create64(LN);
+ Lx2y := TNat.Create64(LN); Lfrac := TNat.Create64(LN); Lyk := TNat.Create64(LN);
+ LinvXk := TNat.Create64(LN); Llamk := TNat.Create64(LN);
+ LInvX := TNat.Create64(LN); LxNeg := TNat.Create64(LN); LlamNeg := TNat.Create64(LN);
+
+ // --- batched inversion: U = x*Z1*Z2*X1 (secret path, single guard-free inverse) ---
+ AFO.Mul(AP1.Z, AP2.Z, LZ1Z2); // Z1 Z2
+ AFO.Mul(ABaseX, LZ1Z2, LP1); // P1 = x Z1 Z2
+ AFO.Mul(LP1, AP1.X, LU); // U = P1 X1
+ AFO.Inv(LU, LUinv); // 1/U (0 if U = 0; only in the Z2=0 edge, selected away)
+ AFO.Mul(AP1.X, LUinv, LInvP1); // 1/(x Z1 Z2) = X1/U
+ AFO.Mul(ABaseX, AP2.Z, LTmp); // x Z2
+ AFO.Mul(LTmp, AP1.X, LTmp2); // x Z2 X1
+ AFO.Mul(LTmp2, LUinv, LInvZ1); // 1/Z1
+ AFO.Mul(LP1, LUinv, LInvX1); // 1/X1
+
+ // --- normal recovery ---
+ AFO.Mul(AP1.X, LInvZ1, Lxk); // x_k = X1/Z1
+ AFO.Mul(ABaseX, AP1.Z, LxZ1); // x Z1
+ AFO.Mul(ABaseX, AP2.Z, LxZ2); // x Z2
+ AFO.Add(AP1.X, LxZ1, Ls); // s = X1 + x Z1
+ AFO.Add(AP2.X, LxZ2, Lt); // t = X2 + x Z2
+ AFO.Mul(Ls, Lt, LN2); // s t
+ AFO.Square(ABaseX, Lx2y); // x^2
+ AFO.Add(Lx2y, ABaseY, Lx2y); // x^2 + y
+ AFO.Mul(Lx2y, LZ1Z2, LTmp); // (x^2+y) Z1 Z2
+ AFO.Add(LN2, LTmp, LN2); // N = s t + (x^2+y) Z1 Z2
+ AFO.Mul(LN2, LInvP1, Lfrac); // N / (x Z1 Z2)
+ AFO.Add(LxK, ABaseX, LTmp); // x_k + x
+ AFO.Mul(LTmp, Lfrac, Lyk); // (x_k+x) * frac
+ AFO.Add(Lyk, ABaseY, Lyk); // y_k = ... + y
+ AFO.Mul(AP1.Z, LInvX1, LinvXk); // 1/x_k = Z1/X1
+ AFO.Mul(Lyk, LinvXk, Llamk); // y_k / x_k
+ AFO.Add(Llamk, LxK, Llamk); // lambda_k = y_k/x_k + x_k
+
+ // --- -P candidate (public base; independent of the collapsed U) ---
+ AFO.Inv(ABaseX, LInvX); // 1/x (x is public, nonzero validated)
+ AFO.Add(ABaseX, ABaseY, LxNeg); // x + y (affine y of -P)
+ AFO.Mul(LxNeg, LInvX, LlamNeg); // (x+y)/x
+ AFO.Add(LlamNeg, ABaseX, LlamNeg); // lambda(-P) = (x+y)/x + x
+ // x-coordinate of -P is x itself; reuse ABaseX below.
+
+ // --- CT select the Z2=0 edge (result = -P) ---
+ LmaskZ2 := AFO.IsZeroMask(AP2.Z);
+ Sel(AFO, LmaskZ2, ABaseX, LxK, AXOut);
+ Sel(AFO, LmaskZ2, LlamNeg, Llamk, ALOut);
+end;
+
+end.
diff --git a/CryptoLib/src/Math/Raw/ClpNat.pas b/CryptoLib/src/Math/Raw/ClpNat.pas
index f388af56..cb2a9d90 100644
--- a/CryptoLib/src/Math/Raw/ClpNat.pas
+++ b/CryptoLib/src/Math/Raw/ClpNat.pas
@@ -64,6 +64,8 @@ TNat = class sealed
class function CAdd(ALen: Int32; AMask: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32; static;
class function CAddTo(ALen: Int32; AMask: Int32; const AX: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32; static;
class procedure CMov(ALen: Int32; AMask: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32); static;
+ class procedure CMov64(ALen: Int32; AMask: Int32; const AX: TCryptoLibUInt64Array; AZ: TCryptoLibUInt64Array); static;
+ class procedure CSwap64(ALen: Int32; AMask: Int32; const AX: TCryptoLibUInt64Array; const AZ: TCryptoLibUInt64Array); static;
class function Compare(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array): Int32; overload; static;
class function Compare(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32): Int32; overload; static;
class function Copy(ALen: Int32; const AX: TCryptoLibUInt32Array): TCryptoLibUInt32Array; overload; static;
@@ -623,6 +625,34 @@ class procedure TNat.CMov(ALen: Int32; AMask: Int32; const AX: TCryptoLibUInt32A
end;
end;
+class procedure TNat.CMov64(ALen: Int32; AMask: Int32; const AX: TCryptoLibUInt64Array; AZ: TCryptoLibUInt64Array);
+var
+ LMASK, LZI, LDiff: UInt64;
+ LI: Int32;
+begin
+ LMASK := UInt64(-Int64(AMask and 1));
+ for LI := 0 to ALen - 1 do
+ begin
+ LZI := AZ[LI];
+ LDiff := LZI xor AX[LI];
+ AZ[LI] := LZI xor (LDiff and LMASK);
+ end;
+end;
+
+class procedure TNat.CSwap64(ALen: Int32; AMask: Int32; const AX: TCryptoLibUInt64Array; const AZ: TCryptoLibUInt64Array);
+var
+ LMASK, LT: UInt64;
+ LI: Int32;
+begin
+ LMASK := UInt64(-Int64(AMask and 1));
+ for LI := 0 to ALen - 1 do
+ begin
+ LT := LMASK and (AX[LI] xor AZ[LI]);
+ AX[LI] := AX[LI] xor LT;
+ AZ[LI] := AZ[LI] xor LT;
+ end;
+end;
+
class function TNat.Compare(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array): Int32;
var
LI: Int32;
@@ -672,12 +702,10 @@ class function TNat.Compare(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff:
end;
class function TNat.Copy(ALen: Int32; const AX: TCryptoLibUInt32Array): TCryptoLibUInt32Array;
-var
- LZ: TCryptoLibUInt32Array;
begin
- SetLength(LZ, ALen);
- System.Move(AX[0], LZ[0], ALen * System.SizeOf(UInt32));
- Result := LZ;
+ Result := nil;
+ SetLength(Result, ALen);
+ System.Move(AX[0], Result[0], ALen * System.SizeOf(UInt32));
end;
class procedure TNat.Copy(ALen: Int32; const AX: TCryptoLibUInt32Array; const AZ: TCryptoLibUInt32Array);
@@ -691,12 +719,10 @@ class procedure TNat.Copy(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff: I
end;
class function TNat.Copy64(ALen: Int32; const AX: TCryptoLibUInt64Array): TCryptoLibUInt64Array;
-var
- LZ: TCryptoLibUInt64Array;
begin
- SetLength(LZ, ALen);
- System.Move(AX[0], LZ[0], ALen * System.SizeOf(UInt64));
- Result := LZ;
+ Result := nil;
+ SetLength(Result, ALen);
+ System.Move(AX[0], Result[0], ALen * System.SizeOf(UInt64));
end;
class procedure TNat.Copy64(ALen: Int32; const AX: TCryptoLibUInt64Array; const AZ: TCryptoLibUInt64Array);
@@ -711,12 +737,14 @@ class procedure TNat.Copy64(ALen: Int32; const AX: TCryptoLibUInt64Array; AXOff:
class function TNat.Create(ALen: Int32): TCryptoLibUInt32Array;
begin
+ Result := nil;
SetLength(Result, ALen);
Exit;
end;
class function TNat.Create64(ALen: Int32): TCryptoLibUInt64Array;
begin
+ Result := nil;
SetLength(Result, ALen);
Exit;
end;
@@ -1021,39 +1049,35 @@ class function TNat.EqualToZero64(ALen: Int32; const AX: TCryptoLibUInt64Array;
class function TNat.FromBigInteger(ABits: Int32; AX: TBigInteger): TCryptoLibUInt32Array;
var
LLen: Int32;
- LZ: TCryptoLibUInt32Array;
LI: Int32;
begin
LLen := GetLengthForBits(ABits);
- LZ := Create(LLen);
+ Result := Create(LLen);
if ((AX.SignValue < 0) or (AX.BitLength > ABits)) then
raise EArgumentCryptoLibException.CreateRes(@SValueOutOfRangeForBitLength);
- LZ[0] := UInt32(AX.Int32Value);
+ Result[0] := UInt32(AX.Int32Value);
for LI := 1 to LLen - 1 do
begin
AX := AX.ShiftRight(32);
- LZ[LI] := UInt32(AX.Int32Value);
+ Result[LI] := UInt32(AX.Int32Value);
end;
- Result := LZ;
end;
class function TNat.FromBigInteger64(ABits: Int32; AX: TBigInteger): TCryptoLibUInt64Array;
var
LLen: Int32;
- LZ: TCryptoLibUInt64Array;
LI: Int32;
begin
LLen := GetLengthForBits64(ABits);
- LZ := Create64(LLen);
+ Result := Create64(LLen);
if ((AX.SignValue < 0) or (AX.BitLength > ABits)) then
raise EArgumentCryptoLibException.CreateRes(@SValueOutOfRangeForBitLength);
- LZ[0] := UInt64(AX.Int64Value);
+ Result[0] := UInt64(AX.Int64Value);
for LI := 1 to LLen - 1 do
begin
AX := AX.ShiftRight(64);
- LZ[LI] := UInt64(AX.Int64Value);
+ Result[LI] := UInt64(AX.Int64Value);
end;
- Result := LZ;
end;
class function TNat.GetBit(const AX: TCryptoLibUInt32Array; ABit: Int32): UInt32;
diff --git a/CryptoLib/src/Math/Raw/ClpNat128.pas b/CryptoLib/src/Math/Raw/ClpNat128.pas
index fb14bb63..af9a6d22 100644
--- a/CryptoLib/src/Math/Raw/ClpNat128.pas
+++ b/CryptoLib/src/Math/Raw/ClpNat128.pas
@@ -247,21 +247,25 @@ class procedure TNat128.Copy64(const AX: TCryptoLibUInt64Array; AXOff: Int32; co
class function TNat128.Create: TCryptoLibUInt32Array;
begin
+ Result := nil;
System.SetLength(Result, 4);
end;
class function TNat128.Create64: TCryptoLibUInt64Array;
begin
+ Result := nil;
System.SetLength(Result, 2);
end;
class function TNat128.CreateExt: TCryptoLibUInt32Array;
begin
+ Result := nil;
System.SetLength(Result, 8);
end;
class function TNat128.CreateExt64: TCryptoLibUInt64Array;
begin
+ Result := nil;
System.SetLength(Result, 4);
end;
diff --git a/CryptoLib/src/Math/Raw/ClpNat160.pas b/CryptoLib/src/Math/Raw/ClpNat160.pas
index 4b77a9b3..dfd209c7 100644
--- a/CryptoLib/src/Math/Raw/ClpNat160.pas
+++ b/CryptoLib/src/Math/Raw/ClpNat160.pas
@@ -243,11 +243,13 @@ class procedure TNat160.Copy(const AX: TCryptoLibUInt32Array; AXOff: Int32; cons
class function TNat160.Create: TCryptoLibUInt32Array;
begin
+ Result := nil;
System.SetLength(Result, 5);
end;
class function TNat160.CreateExt: TCryptoLibUInt32Array;
begin
+ Result := nil;
System.SetLength(Result, 10);
end;
diff --git a/CryptoLib/src/Math/Raw/ClpNat192.pas b/CryptoLib/src/Math/Raw/ClpNat192.pas
index 3bd2e8e1..600f638b 100644
--- a/CryptoLib/src/Math/Raw/ClpNat192.pas
+++ b/CryptoLib/src/Math/Raw/ClpNat192.pas
@@ -238,24 +238,28 @@ class procedure TNat192.Copy64(const AX: TCryptoLibUInt64Array; AXOff: Int32; AZ
class function TNat192.Create(): TCryptoLibUInt32Array;
begin
+ Result := nil;
SetLength(Result, 6);
Exit;
end;
class function TNat192.Create64(): TCryptoLibUInt64Array;
begin
+ Result := nil;
SetLength(Result, 3);
Exit;
end;
class function TNat192.CreateExt(): TCryptoLibUInt32Array;
begin
+ Result := nil;
SetLength(Result, 12);
Exit;
end;
class function TNat192.CreateExt64(): TCryptoLibUInt64Array;
begin
+ Result := nil;
SetLength(Result, 6);
Exit;
end;
diff --git a/CryptoLib/src/Math/Raw/ClpNat224.pas b/CryptoLib/src/Math/Raw/ClpNat224.pas
index 2f4c053f..2439b327 100644
--- a/CryptoLib/src/Math/Raw/ClpNat224.pas
+++ b/CryptoLib/src/Math/Raw/ClpNat224.pas
@@ -299,12 +299,14 @@ class procedure TNat224.Copy(const AX: TCryptoLibUInt32Array; AXOff: Int32; AZ:
class function TNat224.Create(): TCryptoLibUInt32Array;
begin
+ Result := nil;
SetLength(Result, 7);
Exit;
end;
class function TNat224.CreateExt(): TCryptoLibUInt32Array;
begin
+ Result := nil;
SetLength(Result, 14);
Exit;
end;
diff --git a/CryptoLib/src/Math/Raw/ClpNat256.pas b/CryptoLib/src/Math/Raw/ClpNat256.pas
index e35ef204..32266667 100644
--- a/CryptoLib/src/Math/Raw/ClpNat256.pas
+++ b/CryptoLib/src/Math/Raw/ClpNat256.pas
@@ -363,24 +363,28 @@ class procedure TNat256.Copy64(const AX: TCryptoLibUInt64Array; AXOff: Int32; AZ
class function TNat256.Create(): TCryptoLibUInt32Array;
begin
+ Result := nil;
SetLength(Result, 8);
Exit;
end;
class function TNat256.Create64(): TCryptoLibUInt64Array;
begin
+ Result := nil;
SetLength(Result, 4);
Exit;
end;
class function TNat256.CreateExt(): TCryptoLibUInt32Array;
begin
+ Result := nil;
SetLength(Result, 16);
Exit;
end;
class function TNat256.CreateExt64(): TCryptoLibUInt64Array;
begin
+ Result := nil;
SetLength(Result, 8);
Exit;
end;
diff --git a/CryptoLib/src/Math/Raw/ClpNat320.pas b/CryptoLib/src/Math/Raw/ClpNat320.pas
index c9af6c5e..a2269ee5 100644
--- a/CryptoLib/src/Math/Raw/ClpNat320.pas
+++ b/CryptoLib/src/Math/Raw/ClpNat320.pas
@@ -54,12 +54,14 @@ class procedure TNat320.Copy64(const AX: TCryptoLibUInt64Array; AXOff: Int32; AZ
class function TNat320.Create64(): TCryptoLibUInt64Array;
begin
+ Result := nil;
SetLength(Result, 5);
Exit;
end;
class function TNat320.CreateExt64(): TCryptoLibUInt64Array;
begin
+ Result := nil;
SetLength(Result, 10);
Exit;
end;
diff --git a/CryptoLib/src/Math/Raw/ClpNat448.pas b/CryptoLib/src/Math/Raw/ClpNat448.pas
index abc8a251..8358ac5e 100644
--- a/CryptoLib/src/Math/Raw/ClpNat448.pas
+++ b/CryptoLib/src/Math/Raw/ClpNat448.pas
@@ -65,11 +65,13 @@ class procedure TNat448.Copy64(const AX: TCryptoLibUInt64Array; AXOff: Int32; AZ
class function TNat448.Create64(): TCryptoLibUInt64Array;
begin
+ Result := nil;
System.SetLength(Result, 7);
end;
class function TNat448.CreateExt64(): TCryptoLibUInt64Array;
begin
+ Result := nil;
System.SetLength(Result, 14);
end;
diff --git a/CryptoLib/src/Math/Raw/ClpNat576.pas b/CryptoLib/src/Math/Raw/ClpNat576.pas
index f6d9bf56..33b14ffb 100644
--- a/CryptoLib/src/Math/Raw/ClpNat576.pas
+++ b/CryptoLib/src/Math/Raw/ClpNat576.pas
@@ -55,11 +55,13 @@ class procedure TNat576.Copy64(const AX: TCryptoLibUInt64Array; AXOff: Int32; AZ
class function TNat576.Create64(): TCryptoLibUInt64Array;
begin
+ Result := nil;
System.SetLength(Result, 9);
end;
class function TNat576.CreateExt64(): TCryptoLibUInt64Array;
begin
+ Result := nil;
System.SetLength(Result, 18);
end;
diff --git a/CryptoLib/src/Packages/Delphi/CryptoLib4PascalPackage.dpk b/CryptoLib/src/Packages/Delphi/CryptoLib4PascalPackage.dpk
index 6c86f34d..b706a18e 100644
--- a/CryptoLib/src/Packages/Delphi/CryptoLib4PascalPackage.dpk
+++ b/CryptoLib/src/Packages/Delphi/CryptoLib4PascalPackage.dpk
@@ -397,6 +397,12 @@ contains
ClpMacUtilities in '..\..\Crypto\Macs\ClpMacUtilities.pas',
ClpMiscObjectIdentifiers in '..\..\Asn1\Misc\ClpMiscObjectIdentifiers.pas',
ClpMultipliers in '..\..\Math\EC\Multiplier\ClpMultipliers.pas',
+ ClpIFpFieldOps in '..\..\Interfaces\Math\EC\Multiplier\ClpIFpFieldOps.pas',
+ ClpHomogeneousPoint in '..\..\Math\EC\Multiplier\ClpHomogeneousPoint.pas',
+ ClpFixedWindowCTMultiplier in '..\..\Math\EC\Multiplier\ClpFixedWindowCTMultiplier.pas',
+ ClpIF2mFieldOps in '..\..\Interfaces\Math\EC\Multiplier\ClpIF2mFieldOps.pas',
+ ClpLopezDahabLadder in '..\..\Math\EC\Multiplier\ClpLopezDahabLadder.pas',
+ ClpF2mMontgomeryLadderCTMultiplier in '..\..\Math\EC\Multiplier\ClpF2mMontgomeryLadderCTMultiplier.pas',
ClpNistNamedCurves in '..\..\Asn1\Nist\ClpNistNamedCurves.pas',
ClpNistObjectIdentifiers in '..\..\Asn1\Nist\ClpNistObjectIdentifiers.pas',
ClpNoOpDigest in '..\..\Crypto\Digests\ClpNoOpDigest.pas',
diff --git a/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.lpk b/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.lpk
index 412629ed..e6c05270 100644
--- a/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.lpk
+++ b/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.lpk
@@ -31,7 +31,7 @@
Acknowledgements:
Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring the development of this library "/>
-
+
@@ -3465,6 +3465,30 @@ Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring the devel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.pas b/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.pas
index cbbff24f..7ed5edb3 100644
--- a/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.pas
+++ b/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.pas
@@ -279,7 +279,9 @@ interface
ClpPkixCrlRevocationChecker, ClpPkixRevocationChecker,
ClpPkixAttrCertChecker, ClpRfc3281CertPathUtilities,
ClpPkixAttrCertPathValidator, ClpPkixAttrCertPathBuilder,
- ClpCryptoLibExceptions, ClpAesBitSlicedEngine;
+ ClpCryptoLibExceptions, ClpAesBitSlicedEngine, ClpHomogeneousPoint,
+ ClpFixedWindowCTMultiplier, ClpIFpFieldOps, ClpIF2mFieldOps,
+ ClpLopezDahabLadder, ClpF2mMontgomeryLadderCTMultiplier;
implementation