diff --git a/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr b/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr
index ea31851c..5adc82bb 100644
--- a/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr
+++ b/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr
@@ -208,6 +208,9 @@ uses
ClpChaChaSimd in '..\..\CryptoLib\src\Simd\Facade\ClpChaChaSimd.pas',
ClpChaChaX86Backend in '..\..\CryptoLib\src\Simd\Backend\ClpChaChaX86Backend.pas',
ClpChaChaArmBackend in '..\..\CryptoLib\src\Simd\Backend\ClpChaChaArmBackend.pas',
+ ClpFpKernelSimd in '..\..\CryptoLib\src\Simd\Facade\ClpFpKernelSimd.pas',
+ ClpFpKernelX86Backend in '..\..\CryptoLib\src\Simd\Backend\ClpFpKernelX86Backend.pas',
+ ClpFpKernelArmBackend in '..\..\CryptoLib\src\Simd\Backend\ClpFpKernelArmBackend.pas',
ClpSalsaSimd in '..\..\CryptoLib\src\Simd\Facade\ClpSalsaSimd.pas',
ClpSalsaX86Backend in '..\..\CryptoLib\src\Simd\Backend\ClpSalsaX86Backend.pas',
ClpSalsaArmBackend in '..\..\CryptoLib\src\Simd\Backend\ClpSalsaArmBackend.pas',
@@ -393,8 +396,11 @@ uses
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',
+ ClpCTFieldValue in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpCTFieldValue.pas',
+ ClpCTFieldOps in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpCTFieldOps.pas',
+ ClpCTLadder in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpCTLadder.pas',
+ ClpFpCTMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpFpCTMultiplier.pas',
+ ClpFpCombMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpFpCombMultiplier.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',
diff --git a/CryptoLib.ConstantTime/src/Core/CtSubjects.pas b/CryptoLib.ConstantTime/src/Core/CtSubjects.pas
index 4f84fef5..ce4a9d14 100644
--- a/CryptoLib.ConstantTime/src/Core/CtSubjects.pas
+++ b/CryptoLib.ConstantTime/src/Core/CtSubjects.pas
@@ -20,7 +20,7 @@
# Subject (must stay clean) Control (must fire)
1 TX25519 ladder (none - clean baseline)
- 2 P-256 default (TFixedWindowCTMultiplier) TWNafL2RMultiplier, same curve
+ 2 P-256 default (TFpCTMultiplier value-type) TWNafL2RMultiplier, same curve
3 sect283k1 default (F2m Montgomery ladder)TWTauNafMultiplier, same curve
4 TAesBitSlicedEngine block TAesEngine (T-table) block
5 TBasicGcmMultiplier (ImplMul64) GHASH TTables4kGcmMultiplier GHASH
@@ -589,6 +589,80 @@ function MakeSect283WTau(ASeed: UInt64): TDudectOp;
Result := BuildEcOp('sect283k1', TWTauNafMultiplier.Create as IECMultiplier, ASeed);
end;
+function MakeSecp256k1CT(ASeed: UInt64): TDudectOp;
+var
+ LX9: IX9ECParameters;
+begin
+ LX9 := TCustomNamedCurves.GetByName('secp256k1');
+ Result := TECMulOp.Create(LX9.Curve.Multiplier, LX9.G, LX9.N, ASeed);
+end;
+
+function MakeSecp256k1WNaf(ASeed: UInt64): TDudectOp;
+begin
+ Result := BuildEcOp('secp256k1', TWNafL2RMultiplier.Create as IECMultiplier, ASeed);
+end;
+
+function MakeSecp384r1CT(ASeed: UInt64): TDudectOp;
+var
+ LX9: IX9ECParameters;
+begin
+ LX9 := TCustomNamedCurves.GetByName('secp384r1');
+ Result := TECMulOp.Create(LX9.Curve.Multiplier, LX9.G, LX9.N, ASeed);
+end;
+
+function MakeSecp384r1WNaf(ASeed: UInt64): TDudectOp;
+begin
+ Result := BuildEcOp('secp384r1', TWNafL2RMultiplier.Create as IECMultiplier, ASeed);
+end;
+
+function MakeSecp521r1CT(ASeed: UInt64): TDudectOp;
+var
+ LX9: IX9ECParameters;
+begin
+ LX9 := TCustomNamedCurves.GetByName('secp521r1');
+ Result := TECMulOp.Create(LX9.Curve.Multiplier, LX9.G, LX9.N, ASeed);
+end;
+
+function MakeSecp521r1WNaf(ASeed: UInt64): TDudectOp;
+begin
+ Result := BuildEcOp('secp521r1', TWNafL2RMultiplier.Create as IECMultiplier, ASeed);
+end;
+
+{ Fixed-base comb subjects: the same TFpPointOps CT primitives as [d]Q, driven
+ through the fixed-base multiplier ([k]G on the reused generator). Paired with
+ the existing wNAF-on-G controls. }
+function MakeSecp256r1Comb(ASeed: UInt64): TDudectOp;
+var
+ LX9: IX9ECParameters;
+begin
+ LX9 := TCustomNamedCurves.GetByName('secp256r1');
+ Result := TECMulOp.Create(LX9.Curve.BasePointMultiplier, LX9.G, LX9.N, ASeed);
+end;
+
+function MakeSecp256k1Comb(ASeed: UInt64): TDudectOp;
+var
+ LX9: IX9ECParameters;
+begin
+ LX9 := TCustomNamedCurves.GetByName('secp256k1');
+ Result := TECMulOp.Create(LX9.Curve.BasePointMultiplier, LX9.G, LX9.N, ASeed);
+end;
+
+function MakeSecp384r1Comb(ASeed: UInt64): TDudectOp;
+var
+ LX9: IX9ECParameters;
+begin
+ LX9 := TCustomNamedCurves.GetByName('secp384r1');
+ Result := TECMulOp.Create(LX9.Curve.BasePointMultiplier, LX9.G, LX9.N, ASeed);
+end;
+
+function MakeSecp521r1Comb(ASeed: UInt64): TDudectOp;
+var
+ LX9: IX9ECParameters;
+begin
+ LX9 := TCustomNamedCurves.GetByName('secp521r1');
+ Result := TECMulOp.Create(LX9.Curve.BasePointMultiplier, LX9.G, LX9.N, ASeed);
+end;
+
function MakeAesBitsliced(ASeed: UInt64): TDudectOp;
begin
Result := TAesOp.Create(TAesBitSlicedEngine.Create as IBlockCipher, 16, ASeed);
@@ -679,10 +753,10 @@ function MakeRow(const AName, ASubjectLabel, AControlLabel: string;
function GetDudectRows: TCtRowArray;
begin
- System.SetLength(Result, 7);
+ System.SetLength(Result, 14);
Result[0] := MakeRow('X25519', 'X25519 ladder', '',
@MakeX25519, nil, MediumCfg(UInt64($0000000000000001)));
- Result[1] := MakeRow('P-256 [d]Q', 'FixedWindow CT', 'wNAF (var-time)',
+ Result[1] := MakeRow('P-256 [d]Q', 'value-type CT', 'wNAF (var-time)',
@MakeP256CT, @MakeP256WNaf, ExpensiveCfg(UInt64($0000000000000002)));
Result[2] := MakeRow('sect283k1 [d]Q', 'F2m Montgomery CT', 'WTauNAF (var-time)',
@MakeSect283CT, @MakeSect283WTau, ExpensiveCfg(UInt64($0000000000000003)));
@@ -705,6 +779,24 @@ function GetDudectRows: TCtRowArray;
@MakeModInvSafe, @MakeModInvVar, MediumCfg(UInt64($0000000000000006)));
Result[6] := MakeRow('mod-inv (wrapper)', 'safegcd wrapper (CT)', 'variable-time wrapper',
@MakeModInvWrapperSafe, @MakeModInvWrapperVar, MediumCfg(UInt64($0000000000000007)));
+ // The remaining prime curves also run the value-type CT multiplier; measure each
+ // explicitly against its wNAF (variable-time) control.
+ Result[7] := MakeRow('secp256k1 [d]Q', 'value-type CT', 'wNAF (var-time)',
+ @MakeSecp256k1CT, @MakeSecp256k1WNaf, ExpensiveCfg(UInt64($0000000000000008)));
+ Result[8] := MakeRow('secp384r1 [d]Q', 'value-type CT', 'wNAF (var-time)',
+ @MakeSecp384r1CT, @MakeSecp384r1WNaf, ExpensiveCfg(UInt64($0000000000000009)));
+ Result[9] := MakeRow('secp521r1 [d]Q', 'value-type CT', 'wNAF (var-time)',
+ @MakeSecp521r1CT, @MakeSecp521r1WNaf, ExpensiveCfg(UInt64($000000000000000A)));
+ // Fixed-base comb [k]G on the reused generator: same CT primitives as [d]Q,
+ // paired against wNAF-on-G (variable-time) controls.
+ Result[10] := MakeRow('secp256r1 [k]G comb', 'value-type comb (CT)', 'wNAF (var-time)',
+ @MakeSecp256r1Comb, @MakeP256WNaf, ExpensiveCfg(UInt64($000000000000000B)));
+ Result[11] := MakeRow('secp256k1 [k]G comb', 'value-type comb (CT)', 'wNAF (var-time)',
+ @MakeSecp256k1Comb, @MakeSecp256k1WNaf, ExpensiveCfg(UInt64($000000000000000C)));
+ Result[12] := MakeRow('secp384r1 [k]G comb', 'value-type comb (CT)', 'wNAF (var-time)',
+ @MakeSecp384r1Comb, @MakeSecp384r1WNaf, ExpensiveCfg(UInt64($000000000000000D)));
+ Result[13] := MakeRow('secp521r1 [k]G comb', 'value-type comb (CT)', 'wNAF (var-time)',
+ @MakeSecp521r1Comb, @MakeSecp521r1WNaf, ExpensiveCfg(UInt64($000000000000000E)));
end;
function MakeVg(const AName: string; AMake: TCtOpFactory;
diff --git a/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dpr b/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dpr
index 8ccdbff3..812cb6ff 100644
--- a/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dpr
+++ b/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dpr
@@ -227,6 +227,9 @@ uses
ClpChaChaSimd in '..\..\CryptoLib\src\Simd\Facade\ClpChaChaSimd.pas',
ClpChaChaX86Backend in '..\..\CryptoLib\src\Simd\Backend\ClpChaChaX86Backend.pas',
ClpChaChaArmBackend in '..\..\CryptoLib\src\Simd\Backend\ClpChaChaArmBackend.pas',
+ ClpFpKernelSimd in '..\..\CryptoLib\src\Simd\Facade\ClpFpKernelSimd.pas',
+ ClpFpKernelX86Backend in '..\..\CryptoLib\src\Simd\Backend\ClpFpKernelX86Backend.pas',
+ ClpFpKernelArmBackend in '..\..\CryptoLib\src\Simd\Backend\ClpFpKernelArmBackend.pas',
ClpSalsaSimd in '..\..\CryptoLib\src\Simd\Facade\ClpSalsaSimd.pas',
ClpSalsaX86Backend in '..\..\CryptoLib\src\Simd\Backend\ClpSalsaX86Backend.pas',
ClpSalsaArmBackend in '..\..\CryptoLib\src\Simd\Backend\ClpSalsaArmBackend.pas',
@@ -410,8 +413,11 @@ uses
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',
+ ClpCTFieldValue in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpCTFieldValue.pas',
+ ClpCTFieldOps in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpCTFieldOps.pas',
+ ClpCTLadder in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpCTLadder.pas',
+ ClpFpCTMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpFpCTMultiplier.pas',
+ ClpFpCombMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpFpCombMultiplier.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',
diff --git a/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dproj b/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dproj
new file mode 100644
index 00000000..4d00d6ac
--- /dev/null
+++ b/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dproj
@@ -0,0 +1,1031 @@
+
+
+ {79684263-1D11-4F41-9FE6-F4C3FD17A96E}
+ CryptoLib.Examples.dpr
+ True
+ Release
+ 3
+ Console
+ None
+ 19.2
+ Win64
+
+
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Cfg_2
+ true
+ true
+
+
+ false
+ false
+ false
+ false
+ false
+ 00400000
+ CryptoLib_Examples
+ 1033
+ CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=
+ System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace)
+ $(BDS)\bin\delphi_PROJECTICON.ico
+ $(BDS)\bin\delphi_PROJECTICNS.icns
+ C:\DelphiDevLibs\HashLib4Pascal;C:\DelphiDevLibs\SimpleBaseLib4Pascal;$(DCC_UnitSearchPath)
+
+
+ System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
+ Debug
+ CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)
+ 1033
+ CryptoLib.Examples_Icon.ico
+
+
+ CryptoLib.Examples_Icon.ico
+ System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)
+ Debug
+ CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
+
+
+ RELEASE;$(DCC_Define)
+ 0
+ false
+ 0
+
+
+ DEBUG;$(DCC_Define)
+ false
+ true
+
+
+ 1033
+ CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)
+ CryptoLib.Examples_Icon.ico
+ (None)
+
+
+
+ MainSource
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Cfg_2
+ Base
+
+
+ Base
+
+
+ Cfg_1
+ Base
+
+
+
+ Delphi.Personality.12
+
+
+
+
+ CryptoLib.Examples.dpr
+
+
+ Microsoft Office 2000 Sample Automation Server Wrapper Components
+ Microsoft Office XP Sample Automation Server Wrapper Components
+
+
+
+ True
+ True
+
+
+ 12
+
+
+
+
diff --git a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr
index 02f34750..10ea09b9 100644
--- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr
+++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr
@@ -204,6 +204,9 @@ uses
ClpChaChaSimd in '..\..\CryptoLib\src\Simd\Facade\ClpChaChaSimd.pas',
ClpChaChaX86Backend in '..\..\CryptoLib\src\Simd\Backend\ClpChaChaX86Backend.pas',
ClpChaChaArmBackend in '..\..\CryptoLib\src\Simd\Backend\ClpChaChaArmBackend.pas',
+ ClpFpKernelSimd in '..\..\CryptoLib\src\Simd\Facade\ClpFpKernelSimd.pas',
+ ClpFpKernelX86Backend in '..\..\CryptoLib\src\Simd\Backend\ClpFpKernelX86Backend.pas',
+ ClpFpKernelArmBackend in '..\..\CryptoLib\src\Simd\Backend\ClpFpKernelArmBackend.pas',
ClpSalsaSimd in '..\..\CryptoLib\src\Simd\Facade\ClpSalsaSimd.pas',
ClpSalsaX86Backend in '..\..\CryptoLib\src\Simd\Backend\ClpSalsaX86Backend.pas',
ClpSalsaArmBackend in '..\..\CryptoLib\src\Simd\Backend\ClpSalsaArmBackend.pas',
@@ -387,8 +390,11 @@ uses
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',
+ ClpCTFieldValue in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpCTFieldValue.pas',
+ ClpCTFieldArith in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpCTFieldArith.pas',
+ ClpCTPoint in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpCTPoint.pas',
+ ClpFpCTMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpFpCTMultiplier.pas',
+ ClpFpCombMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpFpCombMultiplier.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',
diff --git a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj
index b1e6c920..a2b8f40e 100644
--- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj
+++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj
@@ -547,6 +547,9 @@
+
+
+
@@ -730,8 +733,11 @@
-
-
+
+
+
+
+
diff --git a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr
index 4213428f..3a7b4312 100644
--- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr
+++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr
@@ -406,8 +406,14 @@ uses
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',
+ ClpCTFieldValue in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpCTFieldValue.pas',
+ ClpCTFieldArith in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpCTFieldArith.pas',
+ ClpCTPoint in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpCTPoint.pas',
+ ClpFpCTMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpFpCTMultiplier.pas',
+ ClpFpCombMultiplier in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpFpCombMultiplier.pas',
+ ClpFpKernelSimd in '..\..\CryptoLib\src\Simd\Facade\ClpFpKernelSimd.pas',
+ ClpFpKernelX86Backend in '..\..\CryptoLib\src\Simd\Backend\ClpFpKernelX86Backend.pas',
+ ClpFpKernelArmBackend in '..\..\CryptoLib\src\Simd\Backend\ClpFpKernelArmBackend.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',
diff --git a/CryptoLib.Tests/src/Crypto/ECDHPrimeConstantTimeTests.pas b/CryptoLib.Tests/src/Crypto/ECDHPrimeConstantTimeTests.pas
index c39d86ec..2ec564d2 100644
--- a/CryptoLib.Tests/src/Crypto/ECDHPrimeConstantTimeTests.pas
+++ b/CryptoLib.Tests/src/Crypto/ECDHPrimeConstantTimeTests.pas
@@ -38,8 +38,9 @@ interface
ClpIX9ECAsn1Objects,
ClpMultipliers,
ClpIFpFieldOps,
- ClpHomogeneousPoint,
- ClpFixedWindowCTMultiplier,
+ ClpCTFieldValue,
+ ClpCTPoint,
+ ClpFpCTMultiplier,
ClpSecP256R1Custom,
ClpSecP256K1Custom,
ClpSecP384R1Custom,
@@ -70,9 +71,10 @@ TTestECDHPrimeConstantTime = class(TCryptoLibAlgorithmTestCase)
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;
+ function FePointFromAffine(const AFO: IFpFieldOps; const AP: IECPoint): TFePoint;
+ function FePointToPoint(const AFO: IFpFieldOps; const ACurve: IECCurve;
+ const AP: TFePoint): IECPoint;
+ function FeInfinity(const AFO: IFpFieldOps): TFePoint;
protected
procedure SetUp; override;
procedure TearDown; override;
@@ -133,33 +135,62 @@ procedure TTestECDHPrimeConstantTime.AssertPointsEqual(const AMsg: String;
CheckEquals(True, AB.Equals(AA), AMsg);
end;
-function TTestECDHPrimeConstantTime.HomogFromAffine(const AFO: IFpFieldOps;
- const AP: IECPoint): TCTHomogPoint;
+function TTestECDHPrimeConstantTime.FePointFromAffine(const AFO: IFpFieldOps;
+ const AP: IECPoint): TFePoint;
var
LN: Int32;
- LX, LY: TCryptoLibUInt32Array;
+ LX, LY, LOne: TCryptoLibUInt32Array;
LQ: IECPoint;
begin
LN := AFO.GetFieldInts;
LQ := AP.Normalize();
LX := TNat.Create(LN);
LY := TNat.Create(LN);
+ LOne := TNat.Create(LN);
AFO.FieldFromBigInteger(LQ.AffineXCoord.ToBigInteger(), LX);
AFO.FieldFromBigInteger(LQ.AffineYCoord.ToBigInteger(), LY);
- Result := TCTHomogeneousMath.FromAffine(AFO, LX, LY);
+ AFO.FieldOne(LOne);
+ System.FillChar(Result, SizeOf(Result), 0);
+ System.Move(LX[0], Result.X.W[0], LN * SizeOf(UInt32));
+ System.Move(LY[0], Result.Y.W[0], LN * SizeOf(UInt32));
+ System.Move(LOne[0], Result.Z.W[0], LN * SizeOf(UInt32));
end;
-function TTestECDHPrimeConstantTime.HomogToPoint(const AFO: IFpFieldOps;
- const ACurve: IECCurve; const AP: TCTHomogPoint): IECPoint;
+function TTestECDHPrimeConstantTime.FeInfinity(const AFO: IFpFieldOps): TFePoint;
var
- LX, LY: TCryptoLibUInt32Array;
- LIsInfinity: Boolean;
+ LN: Int32;
+ LOne: TCryptoLibUInt32Array;
begin
- TCTHomogeneousMath.ToAffine(AFO, AP, LX, LY, LIsInfinity);
- if LIsInfinity then
- Result := ACurve.Infinity
- else
- Result := ACurve.CreateRawPoint(AFO.CreateFieldElement(LX), AFO.CreateFieldElement(LY));
+ // identity in homogeneous coords is (0 : 1 : 0)
+ LN := AFO.GetFieldInts;
+ LOne := TNat.Create(LN);
+ AFO.FieldOne(LOne);
+ System.FillChar(Result, SizeOf(Result), 0);
+ System.Move(LOne[0], Result.Y.W[0], LN * SizeOf(UInt32));
+end;
+
+function TTestECDHPrimeConstantTime.FePointToPoint(const AFO: IFpFieldOps;
+ const ACurve: IECCurve; const AP: TFePoint): IECPoint;
+var
+ LN: Int32;
+ LZ, LZInv, LXtmp, LYtmp, LXa, LYa: TCryptoLibUInt32Array;
+begin
+ LN := AFO.GetFieldInts;
+ LZ := TNat.Create(LN);
+ System.Move(AP.Z.W[0], LZ[0], LN * SizeOf(UInt32));
+ if AFO.IsZero(LZ) then
+ Exit(ACurve.Infinity);
+ LZInv := TNat.Create(LN);
+ LXtmp := TNat.Create(LN);
+ LYtmp := TNat.Create(LN);
+ LXa := TNat.Create(LN);
+ LYa := TNat.Create(LN);
+ System.Move(AP.X.W[0], LXtmp[0], LN * SizeOf(UInt32));
+ System.Move(AP.Y.W[0], LYtmp[0], LN * SizeOf(UInt32));
+ AFO.Inv(LZ, LZInv);
+ AFO.Mul(LXtmp, LZInv, LXa);
+ AFO.Mul(LYtmp, LZInv, LYa);
+ Result := ACurve.CreateRawPoint(AFO.CreateFieldElement(LXa), AFO.CreateFieldElement(LYa));
end;
procedure TTestECDHPrimeConstantTime.TestDefaultMultiplierIsConstantTime;
@@ -168,6 +199,7 @@ procedure TTestECDHPrimeConstantTime.TestDefaultMultiplierIsConstantTime;
LI: Int32;
LX9: IX9ECParameters;
LMul: IECMultiplier;
+ LTypeName: String;
begin
LNames := CurveNames;
for LI := 0 to System.Length(LNames) - 1 do
@@ -175,8 +207,9 @@ procedure TTestECDHPrimeConstantTime.TestDefaultMultiplierIsConstantTime;
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');
+ LTypeName := TPlatformUtilities.GetTypeName(LMul as TObject);
+ CheckTrue(Pos('CTMultiplier', LTypeName) > 0,
+ 'default multiplier for ' + LNames[LI] + ' is not constant-time (' + LTypeName + ')');
end;
end;
@@ -272,10 +305,12 @@ procedure TTestECDHPrimeConstantTime.TestExceptionalFormulas;
LFO: IFpFieldOps;
LWNaf: IECMultiplier;
LN: Int32;
- LP, LDbl, LNeg, LSum, LInf: TCTHomogPoint;
- LNegY, LZeroArr: TCryptoLibUInt32Array;
+ LP, LDbl, LNeg, LSum, LInf: TFePoint;
+ LNegY, LZeroArr, LYtmp: TCryptoLibUInt32Array;
LG, LNegG, LRef2G: IECPoint;
begin
+ // Exercise the LIVE value-type complete-addition formulas (TCTPoint) on the
+ // exceptional inputs the end-to-end [d]Q test does not deterministically hit.
LWNaf := TWNafL2RMultiplier.Create() as IECMultiplier;
LX9 := TCustomNamedCurves.GetByName('secp256r1');
LCurve := LX9.Curve;
@@ -283,36 +318,39 @@ procedure TTestECDHPrimeConstantTime.TestExceptionalFormulas;
LN := LFO.GetFieldInts;
LG := LX9.G.Normalize();
- LP := HomogFromAffine(LFO, LG);
+ LP := FePointFromAffine(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);
+ TCTPoint.PointDouble(LP, LDbl);
+ TCTPoint.PointAdd(LP, LP, LSum);
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));
+ AssertPointsEqual('Double(P)=2G', LRef2G, FePointToPoint(LFO, LCurve, LDbl));
+ AssertPointsEqual('Add(P,P)=2G', LRef2G, FePointToPoint(LFO, LCurve, LSum));
- // P + (-P) == O
+ // P + (-P) == O (-P = (X : -Y : Z))
+ LNeg := LP;
+ LYtmp := TNat.Create(LN);
+ System.Move(LP.Y.W[0], LYtmp[0], LN * SizeOf(UInt32));
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');
+ LFO.Sub(LZeroArr, LYtmp, LNegY);
+ System.Move(LNegY[0], LNeg.Y.W[0], LN * SizeOf(UInt32));
+ TCTPoint.PointAdd(LP, LNeg, LSum);
+ CheckEquals(True, FePointToPoint(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));
+ AssertPointsEqual('(-P) affine', LNegG, FePointToPoint(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)));
+ LInf := FeInfinity(LFO);
+ TCTPoint.PointAdd(LP, LInf, LSum);
+ AssertPointsEqual('P+O=P', LG, FePointToPoint(LFO, LCurve, LSum));
+ TCTPoint.PointAdd(LInf, LP, LSum);
+ AssertPointsEqual('O+P=P', LG, FePointToPoint(LFO, LCurve, LSum));
// O + O == O
- CheckEquals(True, HomogToPoint(LFO, LCurve,
- TCTHomogeneousMath.Add(LFO, LInf, LInf)).IsInfinity, 'O+O=O');
+ TCTPoint.PointAdd(LInf, LInf, LSum);
+ CheckEquals(True, FePointToPoint(LFO, LCurve, LSum).IsInfinity, 'O+O=O');
end;
procedure TTestECDHPrimeConstantTime.TestECDHAgreement;
@@ -395,7 +433,7 @@ procedure TTestECDHPrimeConstantTime.TestBlindBitsValidation;
begin
Result := False;
try
- LMul := TFixedWindowCTMultiplier.Create(LFO, ABlindBits) as IECMultiplier;
+ LMul := TFpCTMultiplier.Create(LFO, ABlindBits);
except
on E: EArgumentCryptoLibException do
Result := True;
diff --git a/CryptoLib/src/Crypto/CipherKernels/ClpCipherKernelRegistry.pas b/CryptoLib/src/Crypto/CipherKernels/ClpCipherKernelRegistry.pas
index 89bfeaf6..d26f9315 100644
--- a/CryptoLib/src/Crypto/CipherKernels/ClpCipherKernelRegistry.pas
+++ b/CryptoLib/src/Crypto/CipherKernels/ClpCipherKernelRegistry.pas
@@ -61,15 +61,27 @@ TCipherKernelGate = class sealed(TObject)
/// family with Supports() and calls its typed TryCreate. An external
/// consumer registers its own I<X>KernelFactory and resolves it
/// through the public GetSnapshot + a Supports walk - no framework edit and
- /// no central enum. Thread-safe: one TCriticalSection guards mutation and
- /// snapshotting; TryAcquireX snapshots under the lock then releases it before
- /// invoking factory TryCreate, so factory work never serialises other call
- /// sites.
+ /// no central enum.
+ ///
+ /// Thread-safety / read path: the mutable factory list (FFactories) is guarded
+ /// by FLock and touched only by Register/Unregister, which after each mutation
+ /// publish a fresh immutable snapshot array (copy-on-write). TryAcquireX and
+ /// GetSnapshot read that published array under FLock but do a single reference
+ /// load only - no per-call allocation - then release the lock before invoking
+ /// factory TryCreate, so factory work never serialises other call sites and the
+ /// hot path allocates nothing. The short lock keeps runtime registration safe
+ /// against concurrent acquisition (registration remains a setup-time operation
+ /// in practice - every built-in factory registers during unit initialisation).
///
TCipherKernelRegistry = class sealed(TObject)
strict private
class var FLock: TCriticalSection;
class var FFactories: TList;
+ // Immutable, copy-on-write snapshot published by Register/Unregister. Read by
+ // every acquire path via one reference load (no allocation). Never mutated in
+ // place after publication.
+ class var FSnapshot: TCryptoLibGenericArray;
+ class procedure RebuildSnapshot; static;
class function Snapshot: TCryptoLibGenericArray; static;
class constructor Create;
class destructor Destroy;
@@ -119,14 +131,29 @@ implementation
begin
FLock := TCriticalSection.Create;
FFactories := TList.Create;
+ FSnapshot := nil;
end;
class destructor TCipherKernelRegistry.Destroy;
begin
+ FSnapshot := nil;
FFactories.Free;
FLock.Free;
end;
+// Rebuild and publish the immutable snapshot (copy-on-write); caller holds FLock.
+class procedure TCipherKernelRegistry.RebuildSnapshot;
+var
+ LI, LCount: Int32;
+ LNew: TCryptoLibGenericArray;
+begin
+ LCount := FFactories.Count;
+ System.SetLength(LNew, LCount);
+ for LI := 0 to LCount - 1 do
+ LNew[LI] := FFactories[LI];
+ FSnapshot := LNew;
+end;
+
class procedure TCipherKernelRegistry.Register(const AFactory: ICipherKernelFactory);
var
LI, LPriority: Int32;
@@ -149,6 +176,7 @@ class procedure TCipherKernelRegistry.Register(const AFactory: ICipherKernelFact
end;
if not LInserted then
FFactories.Add(AFactory);
+ RebuildSnapshot;
finally
FLock.Leave;
end;
@@ -163,22 +191,24 @@ class procedure TCipherKernelRegistry.Unregister(const AFactory: ICipherKernelFa
try
LIdx := FFactories.IndexOf(AFactory);
if LIdx >= 0 then
+ begin
FFactories.Delete(LIdx);
+ RebuildSnapshot;
+ end;
finally
FLock.Leave;
end;
end;
+// Allocation-free read: one reference load of the published immutable snapshot
+// under FLock. The lock is held only for the load (no copy, no factory work), so it
+// stays uncontended, keeps runtime registration safe, and the returned reference
+// keeps that array alive for the caller's iteration after the lock is released.
class function TCipherKernelRegistry.Snapshot: TCryptoLibGenericArray;
-var
- LI, LCount: Int32;
begin
FLock.Enter;
try
- LCount := FFactories.Count;
- System.SetLength(Result, LCount);
- for LI := 0 to LCount - 1 do
- Result[LI] := FFactories[LI];
+ Result := FSnapshot;
finally
FLock.Leave;
end;
diff --git a/CryptoLib/src/Crypto/Parameters/ClpRsaParameters.pas b/CryptoLib/src/Crypto/Parameters/ClpRsaParameters.pas
index d45d7d8b..a5d9b2de 100644
--- a/CryptoLib/src/Crypto/Parameters/ClpRsaParameters.pas
+++ b/CryptoLib/src/Crypto/Parameters/ClpRsaParameters.pas
@@ -21,6 +21,7 @@
interface
uses
+ SyncObjs,
SysUtils,
ClpBigInteger,
ClpBigIntegerUtilities,
@@ -31,6 +32,7 @@ interface
ClpKeyGenerationParameters,
ClpISecureRandom,
ClpCryptoLibConfig,
+ ClpCryptoLibTypes,
ClpCryptoLibExceptions;
resourcestring
@@ -50,10 +52,38 @@ interface
TRsaKeyParameters = class(TAsymmetricKeyParameter, IRsaKeyParameters)
strict private
+ type
+ // a bounded set of moduli that already passed validation: a ring buffer that overwrites
+ // the oldest entry when full, so the memory footprint stays fixed. Not self-locking - the
+ // owner serialises access (the Miller-Rabin runs unlocked, only the ring ops are guarded).
+ TValidatedModulusRing = record
+ strict private
+ const
+ // upper bound on remembered moduli; a ring buffer overwrites the oldest entry when
+ // full, so the memory footprint stays fixed under many distinct peers
+ ValidatedModuliCacheSize = 32;
+ var
+ FEntries: TCryptoLibGenericArray;
+ FCount: Int32;
+ FNext: Int32;
+ public
+ class function Init: TValidatedModulusRing; static;
+ function Contains(const AModulus: TBigInteger): Boolean;
+ procedure Remember(const AModulus: TBigInteger);
+ end;
+
+ strict private
+ class var
+ FValidated: TValidatedModulusRing;
+ FValidatedLock: TCriticalSection;
var
FModulus: TBigInteger;
FExponent: TBigInteger;
+ class constructor Create;
+ class destructor Destroy;
+ class function IsModulusValidated(const AModulus: TBigInteger): Boolean; static;
+ class procedure MarkModulusValidated(const AModulus: TBigInteger); static;
class function Validate(const AModulus: TBigInteger; AIsInternal: Boolean): TBigInteger; static;
class function GetEffectiveMaxMRTests(ABits: Int32): Int32; static;
class function GetMRIterations(ABits: Int32): Int32; static;
@@ -179,8 +209,76 @@ TRsaBlindingParameters = class(TInterfacedObject, IRsaBlindingParameters)
implementation
+{ TRsaKeyParameters.TValidatedModulusRing }
+
+class function TRsaKeyParameters.TValidatedModulusRing.Init: TValidatedModulusRing;
+begin
+ System.SetLength(Result.FEntries, ValidatedModuliCacheSize);
+ Result.FCount := 0;
+ Result.FNext := 0;
+end;
+
+function TRsaKeyParameters.TValidatedModulusRing.Contains(
+ const AModulus: TBigInteger): Boolean;
+var
+ LI: Int32;
+begin
+ Result := False;
+ for LI := 0 to FCount - 1 do
+ if FEntries[LI].Equals(AModulus) then
+ Exit(True);
+end;
+
+procedure TRsaKeyParameters.TValidatedModulusRing.Remember(
+ const AModulus: TBigInteger);
+var
+ LI: Int32;
+begin
+ // another thread may have remembered the same modulus between the caller's check and now
+ for LI := 0 to FCount - 1 do
+ if FEntries[LI].Equals(AModulus) then
+ Exit;
+ FEntries[FNext] := AModulus;
+ FNext := (FNext + 1) mod System.Length(FEntries);
+ if FCount < System.Length(FEntries) then
+ Inc(FCount);
+end;
+
{ TRsaKeyParameters }
+class constructor TRsaKeyParameters.Create;
+begin
+ FValidated := TValidatedModulusRing.Init;
+ FValidatedLock := TCriticalSection.Create;
+end;
+
+class destructor TRsaKeyParameters.Destroy;
+begin
+ FValidatedLock.Free;
+end;
+
+class function TRsaKeyParameters.IsModulusValidated(
+ const AModulus: TBigInteger): Boolean;
+begin
+ FValidatedLock.Enter;
+ try
+ Result := FValidated.Contains(AModulus);
+ finally
+ FValidatedLock.Leave;
+ end;
+end;
+
+class procedure TRsaKeyParameters.MarkModulusValidated(
+ const AModulus: TBigInteger);
+begin
+ FValidatedLock.Enter;
+ try
+ FValidated.Remember(AModulus);
+ finally
+ FValidatedLock.Leave;
+ end;
+end;
+
class function TRsaKeyParameters.GetEffectiveMaxMRTests(ABits: Int32): Int32;
begin
if TCryptoLibConfig.Rsa.MaxMRTests.HasValue then
@@ -213,25 +311,33 @@ class function TRsaKeyParameters.Validate(const AModulus: TBigInteger;
LIterations: Int32;
LMR: TPrimes.IMROutput;
begin
- if not AIsInternal then
+ Result := AModulus;
+ if AIsInternal then
+ Exit;
+
+ // the cheap invariants always run: they are microseconds and depend on live config (MaxSize),
+ // so a modulus is still rejected when the limits change even after it was seen before
+ if not AModulus.TestBit(0) then
+ raise EArgumentCryptoLibException.CreateRes(@SRsaModulusIsEven);
+ if AModulus.BitLength > TCryptoLibConfig.Rsa.MaxSize then
+ raise EArgumentCryptoLibException.CreateRes(@SRsaModulusOutOfRange);
+ if TBigIntegerUtilities.HasAnySmallFactors(AModulus) then
+ raise EArgumentCryptoLibException.CreateRes(@SRsaModulusHasSmallPrimeFactor);
+
+ // only the expensive Miller-Rabin is memoized: a modulus that already passed it does not need
+ // it again - the server's own key across signs, a peer/root certificate across handshakes
+ if IsModulusValidated(AModulus) then
+ Exit;
+
+ LIterations := GetEffectiveMaxMRTests(AModulus.BitLength div 2);
+ if LIterations > 0 then
begin
- if not AModulus.TestBit(0) then
- raise EArgumentCryptoLibException.CreateRes(@SRsaModulusIsEven);
- if AModulus.BitLength > TCryptoLibConfig.Rsa.MaxSize then
- raise EArgumentCryptoLibException.CreateRes(@SRsaModulusOutOfRange);
- if TBigIntegerUtilities.HasAnySmallFactors(AModulus) then
- raise EArgumentCryptoLibException.CreateRes(@SRsaModulusHasSmallPrimeFactor);
-
- LIterations := GetEffectiveMaxMRTests(AModulus.BitLength div 2);
- if LIterations > 0 then
- begin
- LMR := TPrimes.EnhancedMRProbablePrimeTest(AModulus,
- TCryptoServicesRegistrar.GetSecureRandom(), LIterations);
- if not LMR.IsProvablyComposite then
- raise EArgumentCryptoLibException.CreateRes(@SRsaModulusIsNotComposite);
- end;
+ LMR := TPrimes.EnhancedMRProbablePrimeTest(AModulus,
+ TCryptoServicesRegistrar.GetSecureRandom(), LIterations);
+ if not LMR.IsProvablyComposite then
+ raise EArgumentCryptoLibException.CreateRes(@SRsaModulusIsNotComposite);
+ MarkModulusValidated(AModulus);
end;
- Result := AModulus;
end;
constructor TRsaKeyParameters.Create(AIsPrivate: Boolean;
diff --git a/CryptoLib/src/Crypto/Signers/ClpECDsaSigner.pas b/CryptoLib/src/Crypto/Signers/ClpECDsaSigner.pas
index 335eaaef..443ed377 100644
--- a/CryptoLib/src/Crypto/Signers/ClpECDsaSigner.pas
+++ b/CryptoLib/src/Crypto/Signers/ClpECDsaSigner.pas
@@ -160,7 +160,7 @@ constructor TECDsaSigner.Create(const AKCalculator: IDsaKCalculator);
function TECDsaSigner.CreateBasePointMultiplier: IECMultiplier;
begin
- Result := TFixedPointCombMultiplier.Create();
+ Result := FKey.Parameters.Curve.GetBasePointMultiplier;
end;
class constructor TECDsaSigner.ECDsaSigner;
diff --git a/CryptoLib/src/Include/Simd/FpKernel/FpKernel_aarch64.inc b/CryptoLib/src/Include/Simd/FpKernel/FpKernel_aarch64.inc
new file mode 100644
index 00000000..343cc64f
--- /dev/null
+++ b/CryptoLib/src/Include/Simd/FpKernel/FpKernel_aarch64.inc
@@ -0,0 +1,133 @@
+// Prime-field (Fp) big-integer 64-bit-limb multiply/square kernels (AArch64).
+// -------------------------------------------------------------------
+//
+// One selector-driven file per arch; exactly one of the following must be
+// defined before including this file:
+//
+// CRYPTOLIB_FP_MUL (ClpSimdProc4Begin: x0 = PX, x1 = PY, x2 = PZ, x3 = N)
+// - schoolbook Z[0..2N-1] := X[0..N-1] * Y[0..N-1] over 64-bit limbs;
+// callers reinterpret 32-bit-limb field arrays (uint32[2N] == uint64[N]).
+// CRYPTOLIB_FP_SQR (ClpSimdProc3Begin: x0 = PX, x1 = PZ, x2 = N)
+// - dedicated schoolbook square Z[0..2N-1] := X[0..N-1]^2 (off-diagonal
+// sum, double, then add the diagonal X[i]^2 terms).
+//
+// Constant-time. Uses only caller-saved x4..x15 plus the argument registers,
+// so no callee-saved (x19..x28) preservation is required. Base ARMv8-A
+// integer instructions (mul/umulh/adds/adc/ldr/str) are emitted as mnemonics.
+{$IFDEF CRYPTOLIB_FP_MUL}
+ // x0=X, x1=Y, x2=Z, x3=N
+ lsl x10, x3, #1 // 2N
+ mov x5, xzr // k
+.LFpMulZero:
+ str xzr, [x2, x5, lsl #3]
+ add x5, x5, #1
+ cmp x5, x10
+ b.lo .LFpMulZero
+ mov x4, xzr // i
+.LFpMulRow:
+ ldr x6, [x0, x4, lsl #3] // X[i]
+ mov x7, xzr // carry
+ mov x5, xzr // j
+.LFpMulCol:
+ ldr x11, [x1, x5, lsl #3] // Y[j]
+ mul x8, x6, x11 // lo
+ umulh x9, x6, x11 // hi
+ adds x8, x8, x7 // lo += carry
+ adc x9, x9, xzr
+ add x12, x4, x5 // i+j
+ ldr x13, [x2, x12, lsl #3]
+ adds x8, x8, x13 // lo += Z[i+j]
+ adc x9, x9, xzr
+ str x8, [x2, x12, lsl #3]
+ mov x7, x9 // carry
+ add x5, x5, #1
+ cmp x5, x3
+ b.lo .LFpMulCol
+ add x12, x4, x3 // i+N
+ str x7, [x2, x12, lsl #3]
+ add x4, x4, #1
+ cmp x4, x3
+ b.lo .LFpMulRow
+{$ELSE}
+ {$IFDEF CRYPTOLIB_FP_SQR}
+ // x0=X, x1=Z, x2=N
+ lsl x10, x2, #1 // 2N
+ mov x5, xzr
+.LFpSqrZero:
+ str xzr, [x1, x5, lsl #3]
+ add x5, x5, #1
+ cmp x5, x10
+ b.lo .LFpSqrZero
+ // phase 1: off-diagonal sum (i in 0..N-2, j in i+1..N-1)
+ mov x4, xzr // i
+.LFpSqrP1Row:
+ sub x14, x2, #1 // N-1
+ cmp x4, x14
+ b.hs .LFpSqrP1Done
+ ldr x6, [x0, x4, lsl #3] // X[i]
+ mov x7, xzr // carry
+ add x5, x4, #1 // j = i+1
+.LFpSqrP1Col:
+ ldr x11, [x0, x5, lsl #3] // X[j]
+ mul x8, x6, x11
+ umulh x9, x6, x11
+ adds x8, x8, x7
+ adc x9, x9, xzr
+ add x12, x4, x5 // i+j
+ ldr x13, [x1, x12, lsl #3]
+ adds x8, x8, x13
+ adc x9, x9, xzr
+ str x8, [x1, x12, lsl #3]
+ mov x7, x9
+ add x5, x5, #1
+ cmp x5, x2
+ b.lo .LFpSqrP1Col
+ add x12, x4, x2 // i+N
+ str x7, [x1, x12, lsl #3]
+ add x4, x4, #1
+ b .LFpSqrP1Row
+.LFpSqrP1Done:
+ // phase 2: Z := 2*Z
+ mov x7, xzr // carry bit
+ lsl x10, x2, #1 // 2N
+ mov x5, xzr
+.LFpSqrP2:
+ ldr x8, [x1, x5, lsl #3]
+ lsr x9, x8, #63
+ lsl x8, x8, #1
+ orr x8, x8, x7
+ str x8, [x1, x5, lsl #3]
+ mov x7, x9
+ add x5, x5, #1
+ cmp x5, x10
+ b.lo .LFpSqrP2
+ // phase 3: add diagonal X[i]^2 at position 2i
+ mov x7, xzr // carry-in word (0/1)
+ mov x4, xzr // i
+.LFpSqrP3:
+ ldr x6, [x0, x4, lsl #3]
+ mul x8, x6, x6 // lo
+ umulh x9, x6, x6 // hi
+ lsl x12, x4, #1 // 2i
+ ldr x13, [x1, x12, lsl #3]
+ mov x14, xzr
+ adds x13, x13, x8
+ adc x14, x14, xzr
+ adds x13, x13, x7
+ adc x14, x14, xzr
+ str x13, [x1, x12, lsl #3]
+ add x15, x12, #1
+ ldr x13, [x1, x15, lsl #3]
+ mov x7, xzr
+ adds x13, x13, x9
+ adc x7, x7, xzr
+ adds x13, x13, x14
+ adc x7, x7, xzr
+ str x13, [x1, x15, lsl #3]
+ add x4, x4, #1
+ cmp x4, x2
+ b.lo .LFpSqrP3
+ {$ELSE}
+ {$FATAL Exactly one of CRYPTOLIB_FP_MUL, CRYPTOLIB_FP_SQR must be defined before including this file}
+ {$ENDIF}
+{$ENDIF}
diff --git a/CryptoLib/src/Include/Simd/FpKernel/FpKernel_i386.inc b/CryptoLib/src/Include/Simd/FpKernel/FpKernel_i386.inc
new file mode 100644
index 00000000..efd7c39b
--- /dev/null
+++ b/CryptoLib/src/Include/Simd/FpKernel/FpKernel_i386.inc
@@ -0,0 +1,124 @@
+// Prime-field (Fp) big-integer 64-bit-limb multiply/square kernels (i386).
+// -------------------------------------------------------------------
+//
+// One selector-driven file per arch; exactly one of the following must be
+// defined before including this file:
+//
+// CRYPTOLIB_FP_MUL (ClpSimdProc4Begin: ebx = PX, esi = PY, edi = PZ, eax = N (uint64 limbs))
+// - schoolbook Z[0..2N-1] := X[0..N-1] * Y[0..N-1] over 64-bit limbs;
+// callers reinterpret 32-bit-limb field arrays (uint32[2N] == uint64[N]).
+// CRYPTOLIB_FP_SQR (ClpSimdProc3Begin: ebx = PX, esi = PZ, edi = N)
+// - i386 has no dedicated square: Z := X^2 is computed via the multiply
+// kernel (X*X), reusing its body after a 3-register entry shuffle.
+//
+// Constant-time. The field is worked in M = 2N 32-bit limbs; loop variables
+// live on a small stack frame (i386 has too few GPRs to keep them in
+// registers). The epilogue frees the frame and pops the Proc-prologue GPRs.
+{$IFDEF CRYPTOLIB_FP_MUL}
+ push ebp // callee-saved; reused below as the carry accumulator
+ sub esp, 16 // [esp]=M, [esp+4]=i, [esp+8]=j, [esp+12]=Xi
+ lea ecx, [eax + eax] // M = 2N
+ mov [esp], ecx
+ lea edx, [ecx + ecx] // 2M
+ xor eax, eax
+ xor ecx, ecx // k
+@@FpMulZero:
+ mov [edi + ecx*4], eax
+ inc ecx
+ cmp ecx, edx
+ jb @@FpMulZero
+ mov dword ptr [esp+4], 0 // i = 0
+@@FpMulRow:
+ mov ecx, [esp+4] // i
+ mov eax, [ebx + ecx*4] // X[i]
+ mov [esp+12], eax // Xi
+ xor ebp, ebp // carry
+ mov dword ptr [esp+8], 0 // j = 0
+@@FpMulCol:
+ mov eax, [esp+12] // Xi
+ mov ecx, [esp+8] // j
+ mul dword ptr [esi + ecx*4] // edx:eax = Xi * Y[j]
+ add eax, ebp // + carry
+ adc edx, 0
+ mov ecx, [esp+4] // i
+ add ecx, [esp+8] // i+j
+ add eax, [edi + ecx*4] // + Z[i+j]
+ adc edx, 0
+ mov [edi + ecx*4], eax
+ mov ebp, edx // carry
+ mov ecx, [esp+8]
+ inc ecx
+ mov [esp+8], ecx // j++
+ cmp ecx, [esp] // j < M
+ jb @@FpMulCol
+ mov ecx, [esp+4]
+ add ecx, [esp] // i+M
+ mov [edi + ecx*4], ebp // Z[i+M] = carry
+ mov ecx, [esp+4]
+ inc ecx
+ mov [esp+4], ecx // i++
+ cmp ecx, [esp] // i < M
+ jb @@FpMulRow
+ add esp, 16
+ pop ebp
+ pop edi
+ pop esi
+ pop ebx
+{$ELSE}
+ {$IFDEF CRYPTOLIB_FP_SQR}
+ mov eax, edi // N
+ mov edi, esi // PZ
+ mov esi, ebx // PY = PX (square via the multiply kernel)
+ push ebp // callee-saved; reused below as the carry accumulator
+ sub esp, 16 // [esp]=M, [esp+4]=i, [esp+8]=j, [esp+12]=Xi
+ lea ecx, [eax + eax] // M = 2N
+ mov [esp], ecx
+ lea edx, [ecx + ecx] // 2M
+ xor eax, eax
+ xor ecx, ecx // k
+@@FpMulZero:
+ mov [edi + ecx*4], eax
+ inc ecx
+ cmp ecx, edx
+ jb @@FpMulZero
+ mov dword ptr [esp+4], 0 // i = 0
+@@FpMulRow:
+ mov ecx, [esp+4] // i
+ mov eax, [ebx + ecx*4] // X[i]
+ mov [esp+12], eax // Xi
+ xor ebp, ebp // carry
+ mov dword ptr [esp+8], 0 // j = 0
+@@FpMulCol:
+ mov eax, [esp+12] // Xi
+ mov ecx, [esp+8] // j
+ mul dword ptr [esi + ecx*4] // edx:eax = Xi * Y[j]
+ add eax, ebp // + carry
+ adc edx, 0
+ mov ecx, [esp+4] // i
+ add ecx, [esp+8] // i+j
+ add eax, [edi + ecx*4] // + Z[i+j]
+ adc edx, 0
+ mov [edi + ecx*4], eax
+ mov ebp, edx // carry
+ mov ecx, [esp+8]
+ inc ecx
+ mov [esp+8], ecx // j++
+ cmp ecx, [esp] // j < M
+ jb @@FpMulCol
+ mov ecx, [esp+4]
+ add ecx, [esp] // i+M
+ mov [edi + ecx*4], ebp // Z[i+M] = carry
+ mov ecx, [esp+4]
+ inc ecx
+ mov [esp+4], ecx // i++
+ cmp ecx, [esp] // i < M
+ jb @@FpMulRow
+ add esp, 16
+ pop ebp
+ pop edi
+ pop esi
+ pop ebx
+ {$ELSE}
+ {$FATAL Exactly one of CRYPTOLIB_FP_MUL, CRYPTOLIB_FP_SQR must be defined before including this file}
+ {$ENDIF}
+{$ENDIF}
diff --git a/CryptoLib/src/Include/Simd/FpKernel/FpKernel_x86_64.inc b/CryptoLib/src/Include/Simd/FpKernel/FpKernel_x86_64.inc
new file mode 100644
index 00000000..25988cf8
--- /dev/null
+++ b/CryptoLib/src/Include/Simd/FpKernel/FpKernel_x86_64.inc
@@ -0,0 +1,161 @@
+// Prime-field (Fp) big-integer 64-bit-limb multiply/square kernels (x86-64).
+// -------------------------------------------------------------------
+//
+// One selector-driven file per arch; exactly one of the following must be
+// defined before including this file:
+//
+// CRYPTOLIB_FP_MUL (ClpSimdProc4Begin: rcx = PX, rdx = PY, r8 = PZ, r9 = N)
+// - schoolbook Z[0..2N-1] := X[0..N-1] * Y[0..N-1] over 64-bit limbs;
+// callers reinterpret 32-bit-limb field arrays (uint32[2N] == uint64[N]).
+// CRYPTOLIB_FP_SQR (ClpSimdProc3Begin: rcx = PX, rdx = PZ, r8 = N)
+// - dedicated schoolbook square Z[0..2N-1] := X[0..N-1]^2 (off-diagonal
+// sum, double, then add the diagonal X[i]^2 terms).
+//
+// Constant-time: no data-dependent branches or memory addresses. The
+// multiply and square each save and restore the callee-saved GPRs they
+// use (the Proc-prologue itself pushes none on x86-64).
+{$IFDEF CRYPTOLIB_FP_MUL}
+ push rbx
+ push rsi
+ push rdi
+ push r12
+ push r13
+ mov rsi, rcx // PX
+ mov rdi, rdx // PY
+ // r8 = PZ, r9 = N
+ lea r10, [r9 + r9] // 2N
+ xor rax, rax
+ xor r11, r11
+@@FpMulZero:
+ mov [r8 + r11*8], rax
+ inc r11
+ cmp r11, r10
+ jb @@FpMulZero
+ xor r10, r10 // i
+@@FpMulRow:
+ mov rbx, [rsi + r10*8] // X[i]
+ xor r12, r12 // carry
+ xor r11, r11 // j
+@@FpMulCol:
+ mov rax, rbx
+ mul qword ptr [rdi + r11*8] // rdx:rax = X[i]*Y[j]
+ add rax, r12
+ adc rdx, 0
+ mov r13, r10
+ add r13, r11 // i+j
+ add rax, [r8 + r13*8]
+ adc rdx, 0
+ mov [r8 + r13*8], rax
+ mov r12, rdx // carry
+ inc r11
+ cmp r11, r9
+ jb @@FpMulCol
+ mov r13, r10
+ add r13, r9 // i+N
+ mov [r8 + r13*8], r12
+ inc r10
+ cmp r10, r9
+ jb @@FpMulRow
+ pop r13
+ pop r12
+ pop rdi
+ pop rsi
+ pop rbx
+{$ELSE}
+ {$IFDEF CRYPTOLIB_FP_SQR}
+ push rbx
+ push rsi
+ push rdi
+ push r12
+ push r13
+ push r14
+ mov rsi, rcx // PX
+ mov r9, r8 // N
+ mov r8, rdx // PZ
+ lea r10, [r9 + r9] // 2N
+ xor rax, rax
+ xor r11, r11
+@@FpSqrZero:
+ mov [r8 + r11*8], rax
+ inc r11
+ cmp r11, r10
+ jb @@FpSqrZero
+ // phase 1: off-diagonal sum (i in 0..N-2, j in i+1..N-1)
+ xor r10, r10 // i
+@@FpSqrP1Row:
+ mov rcx, r9
+ dec rcx // N-1
+ cmp r10, rcx
+ jae @@FpSqrP1Done
+ mov rbx, [rsi + r10*8] // X[i]
+ xor r12, r12 // carry
+ lea r11, [r10 + 1] // j = i+1
+@@FpSqrP1Col:
+ mov rax, rbx
+ mul qword ptr [rsi + r11*8] // X[i]*X[j]
+ add rax, r12
+ adc rdx, 0
+ mov r13, r10
+ add r13, r11 // i+j
+ add rax, [r8 + r13*8]
+ adc rdx, 0
+ mov [r8 + r13*8], rax
+ mov r12, rdx
+ inc r11
+ cmp r11, r9
+ jb @@FpSqrP1Col
+ mov r13, r10
+ add r13, r9 // i+N
+ mov [r8 + r13*8], r12
+ inc r10
+ jmp @@FpSqrP1Row
+@@FpSqrP1Done:
+ // phase 2: Z := 2*Z (shift-left by 1 across 2N words)
+ xor r12, r12 // carry bit
+ lea r10, [r9 + r9] // 2N
+ xor r11, r11 // k
+@@FpSqrP2:
+ mov rax, [r8 + r11*8]
+ mov r13, rax
+ shr r13, 63
+ shl rax, 1
+ or rax, r12
+ mov [r8 + r11*8], rax
+ mov r12, r13
+ inc r11
+ cmp r11, r10
+ jb @@FpSqrP2
+ // phase 3: add diagonal X[i]^2 at position 2i
+ xor r12, r12 // carry-in word (0/1)
+ xor r10, r10 // i
+@@FpSqrP3:
+ mov rax, [rsi + r10*8]
+ mul rax // rdx:rax = X[i]^2
+ lea r11, [r10 + r10] // 2i
+ xor r14, r14
+ mov r13, [r8 + r11*8]
+ add r13, rax
+ adc r14, 0
+ add r13, r12
+ adc r14, 0
+ mov [r8 + r11*8], r13
+ xor r12, r12
+ mov r13, [r8 + r11*8 + 8]
+ add r13, rdx
+ adc r12, 0
+ add r13, r14
+ adc r12, 0
+ mov [r8 + r11*8 + 8], r13
+ inc r10
+ cmp r10, r9
+ jb @@FpSqrP3
+ pop r14
+ pop r13
+ pop r12
+ pop rdi
+ pop rsi
+ pop rbx
+ {$ELSE}
+ {$FATAL Exactly one of CRYPTOLIB_FP_MUL, CRYPTOLIB_FP_SQR must be defined before including this file}
+ {$ENDIF}
+{$ENDIF}
diff --git a/CryptoLib/src/Interfaces/Math/EC/ClpIECCommon.pas b/CryptoLib/src/Interfaces/Math/EC/ClpIECCommon.pas
index 4778ac5e..33d275f2 100644
--- a/CryptoLib/src/Interfaces/Math/EC/ClpIECCommon.pas
+++ b/CryptoLib/src/Interfaces/Math/EC/ClpIECCommon.pas
@@ -52,6 +52,7 @@ interface
function GetA: IECFieldElement;
function GetB: IECFieldElement;
function GetMultiplier: IECMultiplier;
+ function GetBasePointMultiplier: IECMultiplier;
function Equals(const AOther: IECCurve): Boolean;
property FieldSize: Int32 read GetFieldSize;
property FieldElementEncodingLength: Int32 read GetFieldElementEncodingLength;
@@ -63,6 +64,7 @@ interface
property A: IECFieldElement read GetA;
property B: IECFieldElement read GetB;
property Multiplier: IECMultiplier read GetMultiplier;
+ property BasePointMultiplier: IECMultiplier read GetBasePointMultiplier;
function FromBigInteger(const AX: TBigInteger): IECFieldElement;
function CreatePoint(const AX, AY: TBigInteger): IECPoint;
function CreateRawPoint(const AX, AY: IECFieldElement): IECPoint; overload;
diff --git a/CryptoLib/src/Interfaces/Math/EC/Multiplier/ClpIFpFieldOps.pas b/CryptoLib/src/Interfaces/Math/EC/Multiplier/ClpIFpFieldOps.pas
index ba2a24a7..c0409be4 100644
--- a/CryptoLib/src/Interfaces/Math/EC/Multiplier/ClpIFpFieldOps.pas
+++ b/CryptoLib/src/Interfaces/Math/EC/Multiplier/ClpIFpFieldOps.pas
@@ -39,11 +39,7 @@ interface
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;
diff --git a/CryptoLib/src/Math/EC/ClpECCurve.pas b/CryptoLib/src/Math/EC/ClpECCurve.pas
index 11346cbb..d0efc31e 100644
--- a/CryptoLib/src/Math/EC/ClpECCurve.pas
+++ b/CryptoLib/src/Math/EC/ClpECCurve.pas
@@ -92,12 +92,14 @@ TDefaultLookupTable = class sealed(TAbstractECLookupTable, IECLookupTable)
FOrder, FCofactor: TBigInteger;
FCoord: Int32;
FMultiplier: IECMultiplier;
+ FBasePointMultiplier: IECMultiplier;
FEndomorphism: IECEndomorphism;
FPreCompTable: TDictionary;
FLock: TCriticalSection;
FTableLock: TCriticalSection;
function CreateDefaultMultiplier(): IECMultiplier; virtual;
+ function CreateBasePointMultiplier(): IECMultiplier; virtual;
procedure CheckPoint(const APoint: IECPoint); virtual;
procedure CheckPoints(const APoints: TCryptoLibGenericArray); overload; virtual;
procedure CheckPoints(const APoints: TCryptoLibGenericArray;
@@ -135,6 +137,7 @@ TECCurveConfig = class sealed(TInterfacedObject, IECCurveConfig)
function GetA: IECFieldElement; virtual;
function GetB: IECFieldElement; virtual;
function GetMultiplier: IECMultiplier; virtual;
+ function GetBasePointMultiplier: IECMultiplier; virtual;
function GetEndomorphism: IECEndomorphism; virtual;
function FromBigInteger(const AX: TBigInteger): IECFieldElement; virtual; abstract;
@@ -463,6 +466,11 @@ function TECCurve.CreateDefaultMultiplier: IECMultiplier;
Result := TWNafL2RMultiplier.Create() as IECMultiplier;
end;
+function TECCurve.CreateBasePointMultiplier: IECMultiplier;
+begin
+ Result := TFixedPointCombMultiplier.Create() as IECMultiplier;
+end;
+
procedure TECCurve.CheckPoint(const APoint: IECPoint);
begin
if (APoint = nil) or (Self as IECCurve <> APoint.Curve) then
@@ -529,6 +537,13 @@ function TECCurve.GetMultiplier: IECMultiplier;
Result := FMultiplier;
end;
+function TECCurve.GetBasePointMultiplier: IECMultiplier;
+begin
+ if FBasePointMultiplier = nil then
+ FBasePointMultiplier := CreateBasePointMultiplier();
+ Result := FBasePointMultiplier;
+end;
+
function TECCurve.CreatePoint(const AX, AY: TBigInteger): IECPoint;
begin
Result := CreateRawPoint(FromBigInteger(AX), FromBigInteger(AY));
diff --git a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas
index 0f2e40cb..2dc5bb9d 100644
--- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas
+++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas
@@ -25,6 +25,11 @@ interface
ClpBigInteger,
ClpNat256,
ClpNat,
+ ClpFpKernelSimd,
+ ClpCTFieldValue,
+ ClpCTFieldArith,
+ ClpFpCTMultiplier,
+ ClpFpCombMultiplier,
ClpMod,
ClpPack,
ClpEncoders,
@@ -37,7 +42,6 @@ interface
ClpECPoint,
ClpECLookupTables,
ClpIFpFieldOps,
- ClpFixedWindowCTMultiplier,
ClpIECCommon,
ClpIECFieldElement,
ClpISecP256K1Custom,
@@ -56,9 +60,13 @@ TSecP256K1Field = class sealed(TObject)
PInv33 = UInt32($3D1);
class var
FP, FPExt, FPExtInv: TCryptoLibUInt32Array;
+ FFb3: TFe; // b3 (= 3b) field rep for the CT ladder (a = 0 folded into MulByA)
+ class procedure MulExt(const AX, AY, AZz: TCryptoLibUInt32Array); static; inline;
+ class procedure SqrExt(const AX, AZz: TCryptoLibUInt32Array); static; inline;
class constructor Create;
public
- class procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array); static;
+ class procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Add(const AX, AY: TFe; var AZ: TFe); overload; static;
class procedure AddExt(const AXX, AYY, AZZ: TCryptoLibUInt32Array); static;
class procedure AddOne(const AX, AZ: TCryptoLibUInt32Array); static;
class function FromBigInteger(const AX: TBigInteger): TCryptoLibUInt32Array; static;
@@ -67,19 +75,26 @@ TSecP256K1Field = class sealed(TObject)
class function IsZero(const AX: TCryptoLibUInt32Array): Int32; static;
class procedure Multiply(const AX, AY, AZ: TCryptoLibUInt32Array); overload; static;
class procedure Multiply(const AX, AY, AZ, ATT: TCryptoLibUInt32Array); overload; static;
+ class procedure Multiply(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); overload; static;
+ class procedure MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); static;
+ class procedure MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); static;
class procedure MultiplyAddToExt(const AX, AY, AZZ: TCryptoLibUInt32Array); static;
class procedure Negate(const AX, AZ: TCryptoLibUInt32Array); static;
class procedure Random(const AR: ISecureRandom; const AZ: TCryptoLibUInt32Array); static;
class procedure RandomMult(const AR: ISecureRandom; const AZ: TCryptoLibUInt32Array); static;
- class procedure Reduce(const AXX, AZ: TCryptoLibUInt32Array); static;
- class procedure Reduce32(AX: UInt32; const AZ: TCryptoLibUInt32Array); static;
+ class procedure Reduce(const AXX, AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Reduce(AXX, AZ: PUInt32); overload; static;
+ class procedure Reduce32(AX: UInt32; const AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Reduce32(AX: UInt32; AZ: PUInt32); overload; static;
class procedure Square(const AX, AZ: TCryptoLibUInt32Array); overload; static;
class procedure Square(const AX, AZ, ATT: TCryptoLibUInt32Array); overload; static;
+ class procedure Square(const AX: TFe; var AZ: TFe; var ATT: TFeExt); overload; static;
class procedure SquareN(const AX: TCryptoLibUInt32Array; AN: Int32;
const AZ: TCryptoLibUInt32Array); overload; static;
class procedure SquareN(const AX: TCryptoLibUInt32Array; AN: Int32;
const AZ, ATT: TCryptoLibUInt32Array); overload; static;
- class procedure Subtract(const AX, AY, AZ: TCryptoLibUInt32Array); static;
+ class procedure Subtract(const AX, AY, AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Subtract(const AX, AY: TFe; var AZ: TFe); overload; static;
class procedure SubtractExt(const AXX, AYY, AZZ: TCryptoLibUInt32Array); static;
class procedure Twice(const AX, AZ: TCryptoLibUInt32Array); static;
@@ -194,6 +209,7 @@ TSecP256K1LookupTable = class sealed(TAbstractECLookupTable, IECLookupTable,
function RandomFieldElementMult(const ARandom: ISecureRandom): IECFieldElement; override;
function SupportsCoordinateSystem(ACoord: Int32): Boolean; override;
function CreateDefaultMultiplier: IECMultiplier; override;
+ function CreateBasePointMultiplier: IECMultiplier; override;
class property Q: TBigInteger read FQ;
class property SecP256K1AffineZs: TCryptoLibGenericArray read FSecP256K1AffineZs;
@@ -213,11 +229,7 @@ TSecP256K1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps)
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);
@@ -226,11 +238,25 @@ TSecP256K1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps)
procedure FieldOne(const AZ: TCryptoLibUInt32Array);
end;
+type
+ TSecP256K1FieldArith = class sealed(TCTFieldArithBase)
+ public
+ class function FieldLimbs: Int32; override;
+ class procedure Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ class procedure Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ class procedure Add(const AX, AY: TFe; var AZ: TFe); override;
+ class procedure Sub(const AX, AY: TFe; var AZ: TFe); override;
+ class procedure MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ class procedure MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ end;
+
implementation
{ TSecP256K1Field }
class constructor TSecP256K1Field.Create;
+var
+ LB, LB3: TCryptoLibUInt32Array;
begin
FP := TCryptoLibUInt32Array.Create($FFFFFC2F, $FFFFFFFE, $FFFFFFFF, $FFFFFFFF,
$FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF);
@@ -239,6 +265,14 @@ implementation
$FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF);
FPExtInv := TCryptoLibUInt32Array.Create($FFF16F5F, $FFFFF85D, $FFFFFFFE, $FFFFFFFF,
$FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $000007A1, $00000002);
+
+ // Value-type field constant for the CT ladder: b3 = 3b mod p (b = 7).
+ System.FillChar(FFb3, SizeOf(FFb3), 0);
+ LB := FromBigInteger(TBigInteger.Seven);
+ LB3 := TNat256.Create;
+ Add(LB, LB, LB3);
+ Add(LB3, LB, LB3);
+ System.Move(LB3[0], FFb3.W[0], 8 * SizeOf(UInt32));
end;
class procedure TSecP256K1Field.Add(const AX, AY, AZ: TCryptoLibUInt32Array);
@@ -250,6 +284,15 @@ class procedure TSecP256K1Field.Add(const AX, AY, AZ: TCryptoLibUInt32Array);
TNat.Add33To(8, PInv33, AZ);
end;
+class procedure TSecP256K1Field.Add(const AX, AY: TFe; var AZ: TFe);
+var
+ LC: UInt32;
+begin
+ LC := TNat256.Add(PUInt32(@AX.W[0]), PUInt32(@AY.W[0]), PUInt32(@AZ.W[0]));
+ if (LC <> 0) or ((AZ.W[7] = P7) and TNat256.Gte(PUInt32(@AZ.W[0]), PUInt32(@FP[0]))) then
+ TNat.Add33To(8, PInv33, PUInt32(@AZ.W[0]));
+end;
+
class procedure TSecP256K1Field.AddExt(const AXX, AYY, AZZ: TCryptoLibUInt32Array);
var
LC: UInt32;
@@ -302,21 +345,62 @@ class function TSecP256K1Field.IsZero(const AX: TCryptoLibUInt32Array): Int32;
Result := Int32(TNat.EqualToZero(8, AX, 0));
end;
+class procedure TSecP256K1Field.MulExt(const AX, AY, AZz: TCryptoLibUInt32Array);
+begin
+ if not TFpKernelSimd.TryMul(AX, AY, AZz, 8) then
+ TNat256.Mul(AX, AY, AZz);
+end;
+
+class procedure TSecP256K1Field.SqrExt(const AX, AZz: TCryptoLibUInt32Array);
+begin
+ if not TFpKernelSimd.TrySqr(AX, AZz, 8) then
+ TNat256.Square(AX, AZz);
+end;
+
class procedure TSecP256K1Field.Multiply(const AX, AY, AZ: TCryptoLibUInt32Array);
var
LTT: TCryptoLibUInt32Array;
begin
LTT := TNat256.CreateExt();
- TNat256.Mul(AX, AY, LTT);
+ MulExt(AX, AY, LTT);
Reduce(LTT, AZ);
end;
class procedure TSecP256K1Field.Multiply(const AX, AY, AZ, ATT: TCryptoLibUInt32Array);
begin
- TNat256.Mul(AX, AY, ATT);
+ MulExt(AX, AY, ATT);
Reduce(ATT, AZ);
end;
+class procedure TSecP256K1Field.Multiply(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt);
+var
+ LX, LY, LZ: TCryptoLibUInt32Array;
+begin
+ if TFpKernelSimd.TryMul(PUInt32(@AX.W[0]), PUInt32(@AY.W[0]), PUInt32(@ATT.W[0]), 8) then
+ Reduce(PUInt32(@ATT.W[0]), PUInt32(@AZ.W[0]))
+ else
+ begin
+ LX := TNat256.Create;
+ LY := TNat256.Create;
+ LZ := TNat256.Create;
+ System.Move(AX.W[0], LX[0], 8 * SizeOf(UInt32));
+ System.Move(AY.W[0], LY[0], 8 * SizeOf(UInt32));
+ Multiply(LX, LY, LZ);
+ System.Move(LZ[0], AZ.W[0], 8 * SizeOf(UInt32));
+ end;
+end;
+
+class procedure TSecP256K1Field.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ // a = 0
+ System.FillChar(AZ, SizeOf(AZ), 0);
+end;
+
+class procedure TSecP256K1Field.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ Multiply(AX, FFb3, AZ, ATT);
+end;
+
class procedure TSecP256K1Field.MultiplyAddToExt(const AX, AY, AZZ: TCryptoLibUInt32Array);
var
LC: UInt32;
@@ -354,23 +438,33 @@ class procedure TSecP256K1Field.RandomMult(const AR: ISecureRandom; const AZ: TC
end;
class procedure TSecP256K1Field.Reduce(const AXX, AZ: TCryptoLibUInt32Array);
+begin
+ Reduce(PUInt32(@AXX[0]), PUInt32(@AZ[0]));
+end;
+
+class procedure TSecP256K1Field.Reduce(AXX, AZ: PUInt32);
var
LCc: UInt64;
LC: UInt32;
begin
- LCc := TNat256.Mul33Add(PInv33, AXX, 8, AXX, 0, AZ, 0);
- LC := TNat256.Mul33DWordAdd(PInv33, LCc, AZ, 0);
+ LCc := TNat256.Mul33Add(PInv33, AXX + 8, AXX, AZ);
+ LC := TNat256.Mul33DWordAdd(PInv33, LCc, AZ);
{$IFDEF DEBUG}
System.Assert((LC = 0) or (LC = 1));
{$ENDIF DEBUG}
- if (LC <> 0) or ((AZ[7] = P7) and TNat256.Gte(AZ, FP)) then
+ if (LC <> 0) or ((AZ[7] = P7) and TNat256.Gte(AZ, PUInt32(@FP[0]))) then
TNat.Add33To(8, PInv33, AZ);
end;
class procedure TSecP256K1Field.Reduce32(AX: UInt32; const AZ: TCryptoLibUInt32Array);
begin
- if ((AX <> 0) and (TNat256.Mul33WordAdd(PInv33, AX, AZ, 0) <> 0)) or
- ((AZ[7] = P7) and TNat256.Gte(AZ, FP)) then
+ Reduce32(AX, PUInt32(@AZ[0]));
+end;
+
+class procedure TSecP256K1Field.Reduce32(AX: UInt32; AZ: PUInt32);
+begin
+ if ((AX <> 0) and (TNat256.Mul33WordAdd(PInv33, AX, AZ) <> 0)) or
+ ((AZ[7] = P7) and TNat256.Gte(AZ, PUInt32(@FP[0]))) then
TNat.Add33To(8, PInv33, AZ);
end;
@@ -379,16 +473,32 @@ class procedure TSecP256K1Field.Square(const AX, AZ: TCryptoLibUInt32Array);
LTT: TCryptoLibUInt32Array;
begin
LTT := TNat256.CreateExt();
- TNat256.Square(AX, LTT);
+ SqrExt(AX, LTT);
Reduce(LTT, AZ);
end;
class procedure TSecP256K1Field.Square(const AX, AZ, ATT: TCryptoLibUInt32Array);
begin
- TNat256.Square(AX, ATT);
+ SqrExt(AX, ATT);
Reduce(ATT, AZ);
end;
+class procedure TSecP256K1Field.Square(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+var
+ LX, LZ: TCryptoLibUInt32Array;
+begin
+ if TFpKernelSimd.TrySqr(PUInt32(@AX.W[0]), PUInt32(@ATT.W[0]), 8) then
+ Reduce(PUInt32(@ATT.W[0]), PUInt32(@AZ.W[0]))
+ else
+ begin
+ LX := TNat256.Create;
+ LZ := TNat256.Create;
+ System.Move(AX.W[0], LX[0], 8 * SizeOf(UInt32));
+ Square(LX, LZ);
+ System.Move(LZ[0], AZ.W[0], 8 * SizeOf(UInt32));
+ end;
+end;
+
class procedure TSecP256K1Field.SquareN(const AX: TCryptoLibUInt32Array; AN: Int32;
const AZ: TCryptoLibUInt32Array);
var
@@ -398,12 +508,12 @@ class procedure TSecP256K1Field.SquareN(const AX: TCryptoLibUInt32Array; AN: Int
System.Assert(AN > 0);
{$ENDIF DEBUG}
LTT := TNat256.CreateExt();
- TNat256.Square(AX, LTT);
+ SqrExt(AX, LTT);
Reduce(LTT, AZ);
Dec(AN);
while AN > 0 do
begin
- TNat256.Square(AZ, LTT);
+ SqrExt(AZ, LTT);
Reduce(LTT, AZ);
Dec(AN);
end;
@@ -415,12 +525,12 @@ class procedure TSecP256K1Field.SquareN(const AX: TCryptoLibUInt32Array; AN: Int
{$IFDEF DEBUG}
System.Assert(AN > 0);
{$ENDIF DEBUG}
- TNat256.Square(AX, ATT);
+ SqrExt(AX, ATT);
Reduce(ATT, AZ);
Dec(AN);
while AN > 0 do
begin
- TNat256.Square(AZ, ATT);
+ SqrExt(AZ, ATT);
Reduce(ATT, AZ);
Dec(AN);
end;
@@ -435,6 +545,15 @@ class procedure TSecP256K1Field.Subtract(const AX, AY, AZ: TCryptoLibUInt32Array
TNat.Sub33From(8, PInv33, AZ);
end;
+class procedure TSecP256K1Field.Subtract(const AX, AY: TFe; var AZ: TFe);
+var
+ LC: Int32;
+begin
+ LC := TNat256.Sub(PUInt32(@AX.W[0]), PUInt32(@AY.W[0]), PUInt32(@AZ.W[0]));
+ if LC <> 0 then
+ TNat.Sub33From(8, PInv33, PUInt32(@AZ.W[0]));
+end;
+
class procedure TSecP256K1Field.SubtractExt(const AXX, AYY, AZZ: TCryptoLibUInt32Array);
var
LC: Int32;
@@ -1086,7 +1205,17 @@ function TSecP256K1Curve.CreateDefaultMultiplier: IECMultiplier;
begin
LCurve := Self as IECCurve;
LFieldOps := TSecP256K1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order);
- Result := TFixedWindowCTMultiplier.Create(LFieldOps) as IECMultiplier;
+ Result := TFpCTMultiplier.Create(LFieldOps);
+end;
+
+function TSecP256K1Curve.CreateBasePointMultiplier: IECMultiplier;
+var
+ LCurve: IECCurve;
+ LFieldOps: IFpFieldOps;
+begin
+ LCurve := Self as IECCurve;
+ LFieldOps := TSecP256K1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order);
+ Result := TFpCombMultiplier.Create(LFieldOps);
end;
{ TSecP256K1FpFieldOps }
@@ -1127,31 +1256,11 @@ procedure TSecP256K1FpFieldOps.Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
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);
@@ -1189,4 +1298,42 @@ class function TSecP256K1FieldElement.GetQ: TBigInteger;
Result := FQ;
end;
+
+{ TSecP256K1FieldArith }
+
+class function TSecP256K1FieldArith.FieldLimbs: Int32;
+begin
+ Result := 8;
+end;
+
+class procedure TSecP256K1FieldArith.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP256K1Field.Multiply(AX, AY, AZ, ATT);
+end;
+
+class procedure TSecP256K1FieldArith.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP256K1Field.Square(AX, AZ, ATT);
+end;
+
+class procedure TSecP256K1FieldArith.Add(const AX, AY: TFe; var AZ: TFe);
+begin
+ TSecP256K1Field.Add(AX, AY, AZ);
+end;
+
+class procedure TSecP256K1FieldArith.Sub(const AX, AY: TFe; var AZ: TFe);
+begin
+ TSecP256K1Field.Subtract(AX, AY, AZ);
+end;
+
+class procedure TSecP256K1FieldArith.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP256K1Field.MulByA(AX, AZ, ATT);
+end;
+
+class procedure TSecP256K1FieldArith.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP256K1Field.MulByB3(AX, AZ, ATT);
+end;
+
end.
diff --git a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas
index 86ea10b4..5f712936 100644
--- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas
+++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas
@@ -25,6 +25,11 @@ interface
ClpBigInteger,
ClpNat256,
ClpNat,
+ ClpFpKernelSimd,
+ ClpCTFieldValue,
+ ClpCTFieldArith,
+ ClpFpCTMultiplier,
+ ClpFpCombMultiplier,
ClpMod,
ClpPack,
ClpEncoders,
@@ -37,7 +42,6 @@ interface
ClpECPoint,
ClpECLookupTables,
ClpIFpFieldOps,
- ClpFixedWindowCTMultiplier,
ClpIECCommon,
ClpIECFieldElement,
ClpISecP256R1Custom,
@@ -55,11 +59,17 @@ TSecP256R1Field = class sealed(TObject)
PExt15 = UInt32($FFFFFFFE);
class var
FP, FPExt: TCryptoLibUInt32Array;
- class procedure AddPInvTo(const AZ: TCryptoLibUInt32Array); static;
- class procedure SubPInvFrom(const AZ: TCryptoLibUInt32Array); static;
+ FFb3: TFe; // value-type field rep of b3 (= 3b) for the CT ladder (a = -3 folded into MulByA)
+ class procedure AddPInvTo(const AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure AddPInvTo(AZ: PUInt32); overload; static;
+ class procedure SubPInvFrom(const AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure SubPInvFrom(AZ: PUInt32); overload; static;
+ class procedure MulExt(const AX, AY, AZz: TCryptoLibUInt32Array); static; inline;
+ class procedure SqrExt(const AX, AZz: TCryptoLibUInt32Array); static; inline;
class constructor Create;
public
- class procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array); static;
+ class procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Add(const AX, AY: TFe; var AZ: TFe); overload; static;
class procedure AddExt(const AXX, AYY, AZZ: TCryptoLibUInt32Array); static;
class procedure AddOne(const AX, AZ: TCryptoLibUInt32Array); static;
class function FromBigInteger(const AX: TBigInteger): TCryptoLibUInt32Array; static;
@@ -68,19 +78,26 @@ TSecP256R1Field = class sealed(TObject)
class function IsZero(const AX: TCryptoLibUInt32Array): Int32; static;
class procedure Multiply(const AX, AY, AZ: TCryptoLibUInt32Array); overload; static;
class procedure Multiply(const AX, AY, AZ, ATT: TCryptoLibUInt32Array); overload; static;
+ class procedure Multiply(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); overload; static;
+ class procedure MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); static;
+ class procedure MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); static;
class procedure MultiplyAddToExt(const AX, AY, AZZ: TCryptoLibUInt32Array); static;
class procedure Negate(const AX, AZ: TCryptoLibUInt32Array); static;
class procedure Random(const AR: ISecureRandom; const AZ: TCryptoLibUInt32Array); static;
class procedure RandomMult(const AR: ISecureRandom; const AZ: TCryptoLibUInt32Array); static;
- class procedure Reduce(const AXX, AZ: TCryptoLibUInt32Array); static;
- class procedure Reduce32(AX: UInt32; const AZ: TCryptoLibUInt32Array); static;
+ class procedure Reduce(const AXX, AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Reduce(AXX, AZ: PUInt32); overload; static;
+ class procedure Reduce32(AX: UInt32; const AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Reduce32(AX: UInt32; AZ: PUInt32); overload; static;
class procedure Square(const AX, AZ: TCryptoLibUInt32Array); overload; static;
class procedure Square(const AX, AZ, ATT: TCryptoLibUInt32Array); overload; static;
+ class procedure Square(const AX: TFe; var AZ: TFe; var ATT: TFeExt); overload; static;
class procedure SquareN(const AX: TCryptoLibUInt32Array; AN: Int32;
const AZ: TCryptoLibUInt32Array); overload; static;
class procedure SquareN(const AX: TCryptoLibUInt32Array; AN: Int32;
const AZ, ATT: TCryptoLibUInt32Array); overload; static;
- class procedure Subtract(const AX, AY, AZ: TCryptoLibUInt32Array); static;
+ class procedure Subtract(const AX, AY, AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Subtract(const AX, AY: TFe; var AZ: TFe); overload; static;
class procedure SubtractExt(const AXX, AYY, AZZ: TCryptoLibUInt32Array); static;
class procedure Twice(const AX, AZ: TCryptoLibUInt32Array); static;
@@ -195,6 +212,7 @@ TSecP256R1LookupTable = class sealed(TAbstractECLookupTable, IECLookupTable,
function RandomFieldElementMult(const ARandom: ISecureRandom): IECFieldElement; override;
function SupportsCoordinateSystem(ACoord: Int32): Boolean; override;
function CreateDefaultMultiplier: IECMultiplier; override;
+ function CreateBasePointMultiplier: IECMultiplier; override;
class property Q: TBigInteger read FQ;
class property SecP256R1AffineZs: TCryptoLibGenericArray read FSecP256R1AffineZs;
@@ -214,11 +232,7 @@ TSecP256R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps)
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);
@@ -227,20 +241,49 @@ TSecP256R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps)
procedure FieldOne(const AZ: TCryptoLibUInt32Array);
end;
+type
+ /// P-256 field ops for the value-type constant-time ladder (the
+ /// equivalent of ).
+ TSecP256R1FieldArith = class sealed(TCTFieldArithBase)
+ public
+ class function FieldLimbs: Int32; override;
+ class procedure Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ class procedure Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ class procedure Add(const AX, AY: TFe; var AZ: TFe); override;
+ class procedure Sub(const AX, AY: TFe; var AZ: TFe); override;
+ class procedure MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ class procedure MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ end;
+
implementation
{ TSecP256R1Field }
class constructor TSecP256R1Field.Create;
+var
+ LB, LB3: TCryptoLibUInt32Array;
begin
FP := TCryptoLibUInt32Array.Create($FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $00000000,
$00000000, $00000000, $00000001, $FFFFFFFF);
FPExt := TCryptoLibUInt32Array.Create($00000001, $00000000, $00000000, $FFFFFFFE,
$FFFFFFFF, $FFFFFFFF, $FFFFFFFE, $00000001, $FFFFFFFE, $00000001, $FFFFFFFE,
$00000001, $00000001, $FFFFFFFE, $00000002, $FFFFFFFE);
+
+ // Value-type field constant for the CT ladder: b3 = 3b mod p.
+ System.FillChar(FFb3, SizeOf(FFb3), 0);
+ LB := FromBigInteger(TBigInteger.Create(1, THexEncoder.Decode('5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B')));
+ LB3 := TNat256.Create;
+ Add(LB, LB, LB3);
+ Add(LB3, LB, LB3);
+ System.Move(LB3[0], FFb3.W[0], 8 * SizeOf(UInt32));
end;
class procedure TSecP256R1Field.AddPInvTo(const AZ: TCryptoLibUInt32Array);
+begin
+ AddPInvTo(PUInt32(@AZ[0]));
+end;
+
+class procedure TSecP256R1Field.AddPInvTo(AZ: PUInt32);
var
LC: Int64;
begin
@@ -276,6 +319,11 @@ class procedure TSecP256R1Field.AddPInvTo(const AZ: TCryptoLibUInt32Array);
end;
class procedure TSecP256R1Field.SubPInvFrom(const AZ: TCryptoLibUInt32Array);
+begin
+ SubPInvFrom(PUInt32(@AZ[0]));
+end;
+
+class procedure TSecP256R1Field.SubPInvFrom(AZ: PUInt32);
var
LC: Int64;
begin
@@ -319,6 +367,15 @@ class procedure TSecP256R1Field.Add(const AX, AY, AZ: TCryptoLibUInt32Array);
AddPInvTo(AZ);
end;
+class procedure TSecP256R1Field.Add(const AX, AY: TFe; var AZ: TFe);
+var
+ LC: UInt32;
+begin
+ LC := TNat256.Add(PUInt32(@AX.W[0]), PUInt32(@AY.W[0]), PUInt32(@AZ.W[0]));
+ if (LC <> 0) or ((AZ.W[7] = P7) and TNat256.Gte(PUInt32(@AZ.W[0]), PUInt32(@FP[0]))) then
+ AddPInvTo(PUInt32(@AZ.W[0]));
+end;
+
class procedure TSecP256R1Field.AddExt(const AXX, AYY, AZZ: TCryptoLibUInt32Array);
var
LC: UInt32;
@@ -370,21 +427,67 @@ class function TSecP256R1Field.IsZero(const AX: TCryptoLibUInt32Array): Int32;
Result := Int32(TNat.EqualToZero(8, AX, 0));
end;
+class procedure TSecP256R1Field.MulExt(const AX, AY, AZz: TCryptoLibUInt32Array);
+begin
+ if not TFpKernelSimd.TryMul(AX, AY, AZz, 8) then
+ TNat256.Mul(AX, AY, AZz);
+end;
+
+class procedure TSecP256R1Field.SqrExt(const AX, AZz: TCryptoLibUInt32Array);
+begin
+ if not TFpKernelSimd.TrySqr(AX, AZz, 8) then
+ TNat256.Square(AX, AZz);
+end;
+
class procedure TSecP256R1Field.Multiply(const AX, AY, AZ: TCryptoLibUInt32Array);
var
LTT: TCryptoLibUInt32Array;
begin
LTT := TNat256.CreateExt();
- TNat256.Mul(AX, AY, LTT);
+ MulExt(AX, AY, LTT);
Reduce(LTT, AZ);
end;
class procedure TSecP256R1Field.Multiply(const AX, AY, AZ, ATT: TCryptoLibUInt32Array);
begin
- TNat256.Mul(AX, AY, ATT);
+ MulExt(AX, AY, ATT);
Reduce(ATT, AZ);
end;
+class procedure TSecP256R1Field.Multiply(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt);
+var
+ LX, LY, LZ: TCryptoLibUInt32Array;
+begin
+ if TFpKernelSimd.TryMul(PUInt32(@AX.W[0]), PUInt32(@AY.W[0]), PUInt32(@ATT.W[0]), 8) then
+ Reduce(PUInt32(@ATT.W[0]), PUInt32(@AZ.W[0]))
+ else
+ begin
+ LX := TNat256.Create;
+ LY := TNat256.Create;
+ LZ := TNat256.Create;
+ System.Move(AX.W[0], LX[0], 8 * SizeOf(UInt32));
+ System.Move(AY.W[0], LY[0], 8 * SizeOf(UInt32));
+ Multiply(LX, LY, LZ);
+ System.Move(LZ[0], AZ.W[0], 8 * SizeOf(UInt32));
+ end;
+end;
+
+class procedure TSecP256R1Field.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+var
+ Lt, LZero: TFe;
+begin
+ // a = -3: -(x+x+x) via field adds, cheaper than a full multiply by FFa.
+ Add(AX, AX, Lt);
+ Add(Lt, AX, Lt);
+ System.FillChar(LZero, SizeOf(LZero), 0);
+ Subtract(LZero, Lt, AZ);
+end;
+
+class procedure TSecP256R1Field.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ Multiply(AX, FFb3, AZ, ATT);
+end;
+
class procedure TSecP256R1Field.MultiplyAddToExt(const AX, AY, AZZ: TCryptoLibUInt32Array);
var
LC: UInt32;
@@ -421,6 +524,11 @@ class procedure TSecP256R1Field.RandomMult(const AR: ISecureRandom; const AZ: TC
end;
class procedure TSecP256R1Field.Reduce(const AXX, AZ: TCryptoLibUInt32Array);
+begin
+ Reduce(PUInt32(@AXX[0]), PUInt32(@AZ[0]));
+end;
+
+class procedure TSecP256R1Field.Reduce(AXX, AZ: PUInt32);
var
LXX08, LXX09, LXX10, LXX11, LXX12, LXX13, LXX14, LXX15: Int64;
LT0, LT1, LT2, LT3, LT4, LT5, LT6, LT7: Int64;
@@ -481,6 +589,11 @@ class procedure TSecP256R1Field.Reduce(const AXX, AZ: TCryptoLibUInt32Array);
end;
class procedure TSecP256R1Field.Reduce32(AX: UInt32; const AZ: TCryptoLibUInt32Array);
+begin
+ Reduce32(AX, PUInt32(@AZ[0]));
+end;
+
+class procedure TSecP256R1Field.Reduce32(AX: UInt32; AZ: PUInt32);
var
LCc: Int64;
LXX08: Int64;
@@ -527,7 +640,7 @@ class procedure TSecP256R1Field.Reduce32(AX: UInt32; const AZ: TCryptoLibUInt32A
{$ENDIF DEBUG}
end;
- if (LCc <> 0) or ((AZ[7] = P7) and TNat256.Gte(AZ, FP)) then
+ if (LCc <> 0) or ((AZ[7] = P7) and TNat256.Gte(AZ, PUInt32(@FP[0]))) then
AddPInvTo(AZ);
end;
@@ -536,16 +649,32 @@ class procedure TSecP256R1Field.Square(const AX, AZ: TCryptoLibUInt32Array);
LTT: TCryptoLibUInt32Array;
begin
LTT := TNat256.CreateExt();
- TNat256.Square(AX, LTT);
+ SqrExt(AX, LTT);
Reduce(LTT, AZ);
end;
class procedure TSecP256R1Field.Square(const AX, AZ, ATT: TCryptoLibUInt32Array);
begin
- TNat256.Square(AX, ATT);
+ SqrExt(AX, ATT);
Reduce(ATT, AZ);
end;
+class procedure TSecP256R1Field.Square(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+var
+ LX, LZ: TCryptoLibUInt32Array;
+begin
+ if TFpKernelSimd.TrySqr(PUInt32(@AX.W[0]), PUInt32(@ATT.W[0]), 8) then
+ Reduce(PUInt32(@ATT.W[0]), PUInt32(@AZ.W[0]))
+ else
+ begin
+ LX := TNat256.Create;
+ LZ := TNat256.Create;
+ System.Move(AX.W[0], LX[0], 8 * SizeOf(UInt32));
+ Square(LX, LZ);
+ System.Move(LZ[0], AZ.W[0], 8 * SizeOf(UInt32));
+ end;
+end;
+
class procedure TSecP256R1Field.SquareN(const AX: TCryptoLibUInt32Array; AN: Int32;
const AZ: TCryptoLibUInt32Array);
var
@@ -555,12 +684,12 @@ class procedure TSecP256R1Field.SquareN(const AX: TCryptoLibUInt32Array; AN: Int
Assert(AN > 0);
{$ENDIF DEBUG}
LTT := TNat256.CreateExt();
- TNat256.Square(AX, LTT);
+ SqrExt(AX, LTT);
Reduce(LTT, AZ);
Dec(AN);
while AN > 0 do
begin
- TNat256.Square(AZ, LTT);
+ SqrExt(AZ, LTT);
Reduce(LTT, AZ);
Dec(AN);
end;
@@ -572,12 +701,12 @@ class procedure TSecP256R1Field.SquareN(const AX: TCryptoLibUInt32Array; AN: Int
{$IFDEF DEBUG}
Assert(AN > 0);
{$ENDIF DEBUG}
- TNat256.Square(AX, ATT);
+ SqrExt(AX, ATT);
Reduce(ATT, AZ);
Dec(AN);
while AN > 0 do
begin
- TNat256.Square(AZ, ATT);
+ SqrExt(AZ, ATT);
Reduce(ATT, AZ);
Dec(AN);
end;
@@ -592,6 +721,15 @@ class procedure TSecP256R1Field.Subtract(const AX, AY, AZ: TCryptoLibUInt32Array
SubPInvFrom(AZ);
end;
+class procedure TSecP256R1Field.Subtract(const AX, AY: TFe; var AZ: TFe);
+var
+ LC: Int32;
+begin
+ LC := TNat256.Sub(PUInt32(@AX.W[0]), PUInt32(@AY.W[0]), PUInt32(@AZ.W[0]));
+ if LC <> 0 then
+ SubPInvFrom(PUInt32(@AZ.W[0]));
+end;
+
class procedure TSecP256R1Field.SubtractExt(const AXX, AYY, AZZ: TCryptoLibUInt32Array);
var
LC: Int32;
@@ -1242,7 +1380,17 @@ function TSecP256R1Curve.CreateDefaultMultiplier: IECMultiplier;
begin
LCurve := Self as IECCurve;
LFieldOps := TSecP256R1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order);
- Result := TFixedWindowCTMultiplier.Create(LFieldOps) as IECMultiplier;
+ Result := TFpCTMultiplier.Create(LFieldOps);
+end;
+
+function TSecP256R1Curve.CreateBasePointMultiplier: IECMultiplier;
+var
+ LCurve: IECCurve;
+ LFieldOps: IFpFieldOps;
+begin
+ LCurve := Self as IECCurve;
+ LFieldOps := TSecP256R1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order);
+ Result := TFpCombMultiplier.Create(LFieldOps);
end;
{ TSecP256R1FpFieldOps }
@@ -1283,31 +1431,11 @@ procedure TSecP256R1FpFieldOps.Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
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);
@@ -1340,4 +1468,41 @@ procedure TSecP256R1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array);
TNat.Copy(FE_INTS, FOne, 0, AZ, 0);
end;
+{ TSecP256R1FieldArith }
+
+class function TSecP256R1FieldArith.FieldLimbs: Int32;
+begin
+ Result := 8;
+end;
+
+class procedure TSecP256R1FieldArith.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP256R1Field.Multiply(AX, AY, AZ, ATT);
+end;
+
+class procedure TSecP256R1FieldArith.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP256R1Field.Square(AX, AZ, ATT);
+end;
+
+class procedure TSecP256R1FieldArith.Add(const AX, AY: TFe; var AZ: TFe);
+begin
+ TSecP256R1Field.Add(AX, AY, AZ);
+end;
+
+class procedure TSecP256R1FieldArith.Sub(const AX, AY: TFe; var AZ: TFe);
+begin
+ TSecP256R1Field.Subtract(AX, AY, AZ);
+end;
+
+class procedure TSecP256R1FieldArith.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP256R1Field.MulByA(AX, AZ, ATT);
+end;
+
+class procedure TSecP256R1FieldArith.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP256R1Field.MulByB3(AX, AZ, ATT);
+end;
+
end.
diff --git a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas
index 4c9e08c0..2b5832a2 100644
--- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas
+++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas
@@ -25,6 +25,11 @@ interface
ClpBigInteger,
ClpNat384,
ClpNat,
+ ClpFpKernelSimd,
+ ClpCTFieldValue,
+ ClpCTFieldArith,
+ ClpFpCTMultiplier,
+ ClpFpCombMultiplier,
ClpMod,
ClpPack,
ClpEncoders,
@@ -37,7 +42,6 @@ interface
ClpECPoint,
ClpECLookupTables,
ClpIFpFieldOps,
- ClpFixedWindowCTMultiplier,
ClpIECCommon,
ClpIECFieldElement,
ClpISecP384R1Custom,
@@ -55,11 +59,17 @@ TSecP384R1Field = class sealed(TObject)
PExt23 = UInt32($FFFFFFFF);
class var
FP, FPExt, FPExtInv: TCryptoLibUInt32Array;
- class procedure AddPInvTo(const AZ: TCryptoLibUInt32Array); static;
- class procedure SubPInvFrom(const AZ: TCryptoLibUInt32Array); static;
+ FFb3: TFe; // b3 (= 3b) field rep for the CT ladder (a = -3 folded into MulByA)
+ class procedure AddPInvTo(const AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure AddPInvTo(AZ: PUInt32); overload; static;
+ class procedure SubPInvFrom(const AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure SubPInvFrom(AZ: PUInt32); overload; static;
+ class procedure MulExt(const AX, AY, AZz: TCryptoLibUInt32Array); static; inline;
+ class procedure SqrExt(const AX, AZz: TCryptoLibUInt32Array); static; inline;
class constructor Create;
public
- class procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array); static;
+ class procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Add(const AX, AY: TFe; var AZ: TFe); overload; static;
class procedure AddExt(const AXX, AYY, AZZ: TCryptoLibUInt32Array); static;
class procedure AddOne(const AX, AZ: TCryptoLibUInt32Array); static;
class function FromBigInteger(const AX: TBigInteger): TCryptoLibUInt32Array; static;
@@ -68,18 +78,25 @@ TSecP384R1Field = class sealed(TObject)
class function IsZero(const AX: TCryptoLibUInt32Array): Int32; static;
class procedure Multiply(const AX, AY, AZ: TCryptoLibUInt32Array); overload; static;
class procedure Multiply(const AX, AY, AZ, ATT: TCryptoLibUInt32Array); overload; static;
+ class procedure Multiply(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); overload; static;
+ class procedure MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); static;
+ class procedure MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); static;
class procedure Negate(const AX, AZ: TCryptoLibUInt32Array); static;
class procedure Random(const AR: ISecureRandom; const AZ: TCryptoLibUInt32Array); static;
class procedure RandomMult(const AR: ISecureRandom; const AZ: TCryptoLibUInt32Array); static;
- class procedure Reduce(const AXX, AZ: TCryptoLibUInt32Array); static;
- class procedure Reduce32(AX: UInt32; const AZ: TCryptoLibUInt32Array); static;
+ class procedure Reduce(const AXX, AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Reduce(AXX, AZ: PUInt32); overload; static;
+ class procedure Reduce32(AX: UInt32; const AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Reduce32(AX: UInt32; AZ: PUInt32); overload; static;
class procedure Square(const AX, AZ: TCryptoLibUInt32Array); overload; static;
class procedure Square(const AX, AZ, ATT: TCryptoLibUInt32Array); overload; static;
+ class procedure Square(const AX: TFe; var AZ: TFe; var ATT: TFeExt); overload; static;
class procedure SquareN(const AX: TCryptoLibUInt32Array; AN: Int32;
const AZ: TCryptoLibUInt32Array); overload; static;
class procedure SquareN(const AX: TCryptoLibUInt32Array; AN: Int32;
const AZ, ATT: TCryptoLibUInt32Array); overload; static;
- class procedure Subtract(const AX, AY, AZ: TCryptoLibUInt32Array); static;
+ class procedure Subtract(const AX, AY, AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Subtract(const AX, AY: TFe; var AZ: TFe); overload; static;
class procedure SubtractExt(const AXX, AYY, AZZ: TCryptoLibUInt32Array); static;
class procedure Twice(const AX, AZ: TCryptoLibUInt32Array); static;
@@ -194,6 +211,7 @@ TSecP384R1LookupTable = class sealed(TAbstractECLookupTable, IECLookupTable,
function RandomFieldElementMult(const ARandom: ISecureRandom): IECFieldElement; override;
function SupportsCoordinateSystem(ACoord: Int32): Boolean; override;
function CreateDefaultMultiplier: IECMultiplier; override;
+ function CreateBasePointMultiplier: IECMultiplier; override;
class property Q: TBigInteger read FQ;
class property SecP384R1AffineZs: TCryptoLibGenericArray read FSecP384R1AffineZs;
@@ -213,11 +231,7 @@ TSecP384R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps)
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);
@@ -226,11 +240,25 @@ TSecP384R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps)
procedure FieldOne(const AZ: TCryptoLibUInt32Array);
end;
+type
+ TSecP384R1FieldArith = class sealed(TCTFieldArithBase)
+ public
+ class function FieldLimbs: Int32; override;
+ class procedure Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ class procedure Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ class procedure Add(const AX, AY: TFe; var AZ: TFe); override;
+ class procedure Sub(const AX, AY: TFe; var AZ: TFe); override;
+ class procedure MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ class procedure MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ end;
+
implementation
{ TSecP384R1Field }
class constructor TSecP384R1Field.Create;
+var
+ LB, LB3: TCryptoLibUInt32Array;
begin
FP := TCryptoLibUInt32Array.Create($FFFFFFFF, $00000000, $00000000, $FFFFFFFF,
$FFFFFFFE, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF);
@@ -241,9 +269,22 @@ implementation
FPExtInv := TCryptoLibUInt32Array.Create($FFFFFFFF, $00000001, $FFFFFFFF, $FFFFFFFD,
$FFFFFFFF, $00000001, $FFFFFFFF, $FFFFFFFD, $FFFFFFFE, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF,
$00000001, $FFFFFFFE, $FFFFFFFF, $00000001, $00000002);
+
+ // Value-type field constant for the CT ladder: b3 = 3b mod p.
+ System.FillChar(FFb3, SizeOf(FFb3), 0);
+ LB := FromBigInteger(TBigInteger.Create(1, THexEncoder.Decode('B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF')));
+ LB3 := TNat.Create(12);
+ Add(LB, LB, LB3);
+ Add(LB3, LB, LB3);
+ System.Move(LB3[0], FFb3.W[0], 12 * SizeOf(UInt32));
end;
class procedure TSecP384R1Field.AddPInvTo(const AZ: TCryptoLibUInt32Array);
+begin
+ AddPInvTo(PUInt32(@AZ[0]));
+end;
+
+class procedure TSecP384R1Field.AddPInvTo(AZ: PUInt32);
var
LC: Int64;
begin
@@ -270,6 +311,11 @@ class procedure TSecP384R1Field.AddPInvTo(const AZ: TCryptoLibUInt32Array);
end;
class procedure TSecP384R1Field.SubPInvFrom(const AZ: TCryptoLibUInt32Array);
+begin
+ SubPInvFrom(PUInt32(@AZ[0]));
+end;
+
+class procedure TSecP384R1Field.SubPInvFrom(AZ: PUInt32);
var
LC: Int64;
begin
@@ -304,6 +350,15 @@ class procedure TSecP384R1Field.Add(const AX, AY, AZ: TCryptoLibUInt32Array);
AddPInvTo(AZ);
end;
+class procedure TSecP384R1Field.Add(const AX, AY: TFe; var AZ: TFe);
+var
+ LC: UInt32;
+begin
+ LC := TNat.Add(12, PUInt32(@AX.W[0]), PUInt32(@AY.W[0]), PUInt32(@AZ.W[0]));
+ if (LC <> 0) or ((AZ.W[11] = P11) and TNat.Gte(12, PUInt32(@AZ.W[0]), PUInt32(@FP[0]))) then
+ AddPInvTo(PUInt32(@AZ.W[0]));
+end;
+
class procedure TSecP384R1Field.AddExt(const AXX, AYY, AZZ: TCryptoLibUInt32Array);
var
LC: UInt32;
@@ -356,21 +411,67 @@ class function TSecP384R1Field.IsZero(const AX: TCryptoLibUInt32Array): Int32;
Result := Int32(TNat.EqualToZero(12, AX, 0));
end;
+class procedure TSecP384R1Field.MulExt(const AX, AY, AZz: TCryptoLibUInt32Array);
+begin
+ if not TFpKernelSimd.TryMul(AX, AY, AZz, 12) then
+ TNat384.Mul(AX, AY, AZz);
+end;
+
+class procedure TSecP384R1Field.SqrExt(const AX, AZz: TCryptoLibUInt32Array);
+begin
+ if not TFpKernelSimd.TrySqr(AX, AZz, 12) then
+ TNat384.Square(AX, AZz);
+end;
+
class procedure TSecP384R1Field.Multiply(const AX, AY, AZ: TCryptoLibUInt32Array);
var
LTT: TCryptoLibUInt32Array;
begin
LTT := TNat.Create(24);
- TNat384.Mul(AX, AY, LTT);
+ MulExt(AX, AY, LTT);
Reduce(LTT, AZ);
end;
class procedure TSecP384R1Field.Multiply(const AX, AY, AZ, ATT: TCryptoLibUInt32Array);
begin
- TNat384.Mul(AX, AY, ATT);
+ MulExt(AX, AY, ATT);
Reduce(ATT, AZ);
end;
+class procedure TSecP384R1Field.Multiply(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt);
+var
+ LX, LY, LZ: TCryptoLibUInt32Array;
+begin
+ if TFpKernelSimd.TryMul(PUInt32(@AX.W[0]), PUInt32(@AY.W[0]), PUInt32(@ATT.W[0]), 12) then
+ Reduce(PUInt32(@ATT.W[0]), PUInt32(@AZ.W[0]))
+ else
+ begin
+ LX := TNat.Create(12);
+ LY := TNat.Create(12);
+ LZ := TNat.Create(12);
+ System.Move(AX.W[0], LX[0], 12 * SizeOf(UInt32));
+ System.Move(AY.W[0], LY[0], 12 * SizeOf(UInt32));
+ Multiply(LX, LY, LZ);
+ System.Move(LZ[0], AZ.W[0], 12 * SizeOf(UInt32));
+ end;
+end;
+
+class procedure TSecP384R1Field.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+var
+ Lt, LZero: TFe;
+begin
+ // a = -3: -(x+x+x) via field adds, cheaper than a full multiply by FFa.
+ Add(AX, AX, Lt);
+ Add(Lt, AX, Lt);
+ System.FillChar(LZero, SizeOf(LZero), 0);
+ Subtract(LZero, Lt, AZ);
+end;
+
+class procedure TSecP384R1Field.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ Multiply(AX, FFb3, AZ, ATT);
+end;
+
class procedure TSecP384R1Field.Negate(const AX, AZ: TCryptoLibUInt32Array);
begin
if IsZero(AX) <> 0 then
@@ -398,6 +499,11 @@ class procedure TSecP384R1Field.RandomMult(const AR: ISecureRandom; const AZ: TC
end;
class procedure TSecP384R1Field.Reduce(const AXX, AZ: TCryptoLibUInt32Array);
+begin
+ Reduce(PUInt32(@AXX[0]), PUInt32(@AZ[0]));
+end;
+
+class procedure TSecP384R1Field.Reduce(AXX, AZ: PUInt32);
var
LXX16, LXX17, LXX18, LXX19, LXX20, LXX21, LXX22, LXX23: Int64;
LT0, LT1, LT2, LT3, LT4, LT5, LT6, LT7: Int64;
@@ -470,6 +576,11 @@ class procedure TSecP384R1Field.Reduce(const AXX, AZ: TCryptoLibUInt32Array);
end;
class procedure TSecP384R1Field.Reduce32(AX: UInt32; const AZ: TCryptoLibUInt32Array);
+begin
+ Reduce32(AX, PUInt32(@AZ[0]));
+end;
+
+class procedure TSecP384R1Field.Reduce32(AX: UInt32; AZ: PUInt32);
var
LCc: Int64;
LXX12: Int64;
@@ -505,7 +616,7 @@ class procedure TSecP384R1Field.Reduce32(AX: UInt32; const AZ: TCryptoLibUInt32A
end;
if ((LCc <> 0) and (TNat.IncAt(12, AZ, 5) <> 0)) or
- ((AZ[11] = P11) and TNat.Gte(12, AZ, FP)) then
+ ((AZ[11] = P11) and TNat.Gte(12, AZ, PUInt32(@FP[0]))) then
AddPInvTo(AZ);
end;
@@ -514,16 +625,32 @@ class procedure TSecP384R1Field.Square(const AX, AZ: TCryptoLibUInt32Array);
LTT: TCryptoLibUInt32Array;
begin
LTT := TNat.Create(24);
- TNat384.Square(AX, LTT);
+ SqrExt(AX, LTT);
Reduce(LTT, AZ);
end;
class procedure TSecP384R1Field.Square(const AX, AZ, ATT: TCryptoLibUInt32Array);
begin
- TNat384.Square(AX, ATT);
+ SqrExt(AX, ATT);
Reduce(ATT, AZ);
end;
+class procedure TSecP384R1Field.Square(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+var
+ LX, LZ: TCryptoLibUInt32Array;
+begin
+ if TFpKernelSimd.TrySqr(PUInt32(@AX.W[0]), PUInt32(@ATT.W[0]), 12) then
+ Reduce(PUInt32(@ATT.W[0]), PUInt32(@AZ.W[0]))
+ else
+ begin
+ LX := TNat.Create(12);
+ LZ := TNat.Create(12);
+ System.Move(AX.W[0], LX[0], 12 * SizeOf(UInt32));
+ Square(LX, LZ);
+ System.Move(LZ[0], AZ.W[0], 12 * SizeOf(UInt32));
+ end;
+end;
+
class procedure TSecP384R1Field.SquareN(const AX: TCryptoLibUInt32Array; AN: Int32;
const AZ: TCryptoLibUInt32Array);
var
@@ -533,12 +660,12 @@ class procedure TSecP384R1Field.SquareN(const AX: TCryptoLibUInt32Array; AN: Int
Assert(AN > 0);
{$ENDIF DEBUG}
LTT := TNat.Create(24);
- TNat384.Square(AX, LTT);
+ SqrExt(AX, LTT);
Reduce(LTT, AZ);
Dec(AN);
while AN > 0 do
begin
- TNat384.Square(AZ, LTT);
+ SqrExt(AZ, LTT);
Reduce(LTT, AZ);
Dec(AN);
end;
@@ -550,12 +677,12 @@ class procedure TSecP384R1Field.SquareN(const AX: TCryptoLibUInt32Array; AN: Int
{$IFDEF DEBUG}
Assert(AN > 0);
{$ENDIF DEBUG}
- TNat384.Square(AX, ATT);
+ SqrExt(AX, ATT);
Reduce(ATT, AZ);
Dec(AN);
while AN > 0 do
begin
- TNat384.Square(AZ, ATT);
+ SqrExt(AZ, ATT);
Reduce(ATT, AZ);
Dec(AN);
end;
@@ -570,6 +697,15 @@ class procedure TSecP384R1Field.Subtract(const AX, AY, AZ: TCryptoLibUInt32Array
SubPInvFrom(AZ);
end;
+class procedure TSecP384R1Field.Subtract(const AX, AY: TFe; var AZ: TFe);
+var
+ LC: Int32;
+begin
+ LC := TNat.Sub(12, PUInt32(@AX.W[0]), PUInt32(@AY.W[0]), PUInt32(@AZ.W[0]));
+ if LC <> 0 then
+ SubPInvFrom(PUInt32(@AZ.W[0]));
+end;
+
class procedure TSecP384R1Field.SubtractExt(const AXX, AYY, AZZ: TCryptoLibUInt32Array);
var
LC: Int32;
@@ -1245,7 +1381,17 @@ function TSecP384R1Curve.CreateDefaultMultiplier: IECMultiplier;
begin
LCurve := Self as IECCurve;
LFieldOps := TSecP384R1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order);
- Result := TFixedWindowCTMultiplier.Create(LFieldOps) as IECMultiplier;
+ Result := TFpCTMultiplier.Create(LFieldOps);
+end;
+
+function TSecP384R1Curve.CreateBasePointMultiplier: IECMultiplier;
+var
+ LCurve: IECCurve;
+ LFieldOps: IFpFieldOps;
+begin
+ LCurve := Self as IECCurve;
+ LFieldOps := TSecP384R1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order);
+ Result := TFpCombMultiplier.Create(LFieldOps);
end;
{ TSecP384R1FpFieldOps }
@@ -1286,31 +1432,11 @@ procedure TSecP384R1FpFieldOps.Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
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);
@@ -1343,4 +1469,42 @@ procedure TSecP384R1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array);
TNat.Copy(FE_INTS, FOne, 0, AZ, 0);
end;
+
+{ TSecP384R1FieldArith }
+
+class function TSecP384R1FieldArith.FieldLimbs: Int32;
+begin
+ Result := 12;
+end;
+
+class procedure TSecP384R1FieldArith.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP384R1Field.Multiply(AX, AY, AZ, ATT);
+end;
+
+class procedure TSecP384R1FieldArith.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP384R1Field.Square(AX, AZ, ATT);
+end;
+
+class procedure TSecP384R1FieldArith.Add(const AX, AY: TFe; var AZ: TFe);
+begin
+ TSecP384R1Field.Add(AX, AY, AZ);
+end;
+
+class procedure TSecP384R1FieldArith.Sub(const AX, AY: TFe; var AZ: TFe);
+begin
+ TSecP384R1Field.Subtract(AX, AY, AZ);
+end;
+
+class procedure TSecP384R1FieldArith.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP384R1Field.MulByA(AX, AZ, ATT);
+end;
+
+class procedure TSecP384R1FieldArith.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP384R1Field.MulByB3(AX, AZ, ATT);
+end;
+
end.
diff --git a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas
index 68164844..485d9a45 100644
--- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas
+++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas
@@ -25,6 +25,11 @@ interface
ClpBigInteger,
ClpNat512,
ClpNat,
+ ClpFpKernelSimd,
+ ClpCTFieldValue,
+ ClpCTFieldArith,
+ ClpFpCTMultiplier,
+ ClpFpCombMultiplier,
ClpMod,
ClpPack,
ClpEncoders,
@@ -37,7 +42,6 @@ interface
ClpECPoint,
ClpECLookupTables,
ClpIFpFieldOps,
- ClpFixedWindowCTMultiplier,
ClpIECCommon,
ClpIECFieldElement,
ClpISecP521R1Custom,
@@ -54,11 +58,13 @@ TSecP521R1Field = class sealed(TObject)
P16 = UInt32($1FF);
class var
FP: TCryptoLibUInt32Array;
+ FFb3: TFe; // b3 (= 3b) field rep for the CT ladder (a = -3 folded into MulByA)
class procedure ImplMultiply(const AX, AY, AZZ: TCryptoLibUInt32Array); static;
class procedure ImplSquare(const AX, AZZ: TCryptoLibUInt32Array); static;
class constructor Create;
public
- class procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array); static;
+ class procedure Add(const AX, AY, AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Add(const AX, AY: TFe; var AZ: TFe); overload; static;
class procedure AddOne(const AX, AZ: TCryptoLibUInt32Array); static;
class function FromBigInteger(const AX: TBigInteger): TCryptoLibUInt32Array; static;
class procedure Half(const AX, AZ: TCryptoLibUInt32Array); static;
@@ -66,18 +72,24 @@ TSecP521R1Field = class sealed(TObject)
class function IsZero(const AX: TCryptoLibUInt32Array): Int32; static;
class procedure Multiply(const AX, AY, AZ: TCryptoLibUInt32Array); overload; static;
class procedure Multiply(const AX, AY, AZ, ATT: TCryptoLibUInt32Array); overload; static;
+ class procedure Multiply(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); overload; static;
+ class procedure MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); static;
+ class procedure MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); static;
class procedure Negate(const AX, AZ: TCryptoLibUInt32Array); static;
class procedure Random(const AR: ISecureRandom; const AZ: TCryptoLibUInt32Array); static;
class procedure RandomMult(const AR: ISecureRandom; const AZ: TCryptoLibUInt32Array); static;
- class procedure Reduce(const AXX, AZ: TCryptoLibUInt32Array); static;
+ class procedure Reduce(const AXX, AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Reduce(AXX, AZ: PUInt32); overload; static;
class procedure Reduce23(const AZ: TCryptoLibUInt32Array); static;
class procedure Square(const AX, AZ: TCryptoLibUInt32Array); overload; static;
class procedure Square(const AX, AZ, ATT: TCryptoLibUInt32Array); overload; static;
+ class procedure Square(const AX: TFe; var AZ: TFe; var ATT: TFeExt); overload; static;
class procedure SquareN(const AX: TCryptoLibUInt32Array; AN: Int32;
const AZ: TCryptoLibUInt32Array); overload; static;
class procedure SquareN(const AX: TCryptoLibUInt32Array; AN: Int32;
const AZ, ATT: TCryptoLibUInt32Array); overload; static;
- class procedure Subtract(const AX, AY, AZ: TCryptoLibUInt32Array); static;
+ class procedure Subtract(const AX, AY, AZ: TCryptoLibUInt32Array); overload; static;
+ class procedure Subtract(const AX, AY: TFe; var AZ: TFe); overload; static;
class procedure Twice(const AX, AZ: TCryptoLibUInt32Array); static;
class property P: TCryptoLibUInt32Array read FP;
@@ -191,6 +203,7 @@ TSecP521R1LookupTable = class sealed(TAbstractECLookupTable, IECLookupTable,
function RandomFieldElementMult(const ARandom: ISecureRandom): IECFieldElement; override;
function SupportsCoordinateSystem(ACoord: Int32): Boolean; override;
function CreateDefaultMultiplier: IECMultiplier; override;
+ function CreateBasePointMultiplier: IECMultiplier; override;
class property Q: TBigInteger read FQ;
class property SecP521R1AffineZs: TCryptoLibGenericArray read FSecP521R1AffineZs;
@@ -210,11 +223,7 @@ TSecP521R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps)
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);
@@ -223,15 +232,37 @@ TSecP521R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps)
procedure FieldOne(const AZ: TCryptoLibUInt32Array);
end;
+type
+ TSecP521R1FieldArith = class sealed(TCTFieldArithBase)
+ public
+ class function FieldLimbs: Int32; override;
+ class procedure Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ class procedure Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ class procedure Add(const AX, AY: TFe; var AZ: TFe); override;
+ class procedure Sub(const AX, AY: TFe; var AZ: TFe); override;
+ class procedure MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ class procedure MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); override;
+ end;
+
implementation
{ TSecP521R1Field }
class constructor TSecP521R1Field.Create;
+var
+ LB, LB3: TCryptoLibUInt32Array;
begin
FP := TCryptoLibUInt32Array.Create($FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF,
$FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF,
$FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $1FF);
+
+ // Value-type field constant for the CT ladder: b3 = 3b mod p.
+ System.FillChar(FFb3, SizeOf(FFb3), 0);
+ LB := FromBigInteger(TBigInteger.Create(1, THexEncoder.Decode('0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00')));
+ LB3 := TNat.Create(17);
+ Add(LB, LB, LB3);
+ Add(LB3, LB, LB3);
+ System.Move(LB3[0], FFb3.W[0], 17 * SizeOf(UInt32));
end;
class procedure TSecP521R1Field.ImplMultiply(const AX, AY, AZZ: TCryptoLibUInt32Array);
@@ -275,6 +306,21 @@ class procedure TSecP521R1Field.Add(const AX, AY, AZ: TCryptoLibUInt32Array);
AZ[16] := LC;
end;
+class procedure TSecP521R1Field.Add(const AX, AY: TFe; var AZ: TFe);
+var
+ LC: UInt32;
+begin
+ LC := TNat.Add(16, PUInt32(@AX.W[0]), PUInt32(@AY.W[0]), PUInt32(@AZ.W[0]))
+ + AX.W[16] + AY.W[16];
+ if (LC > P16) or ((LC = P16) and TNat.Eq(16, PUInt32(@AZ.W[0]), PUInt32(@FP[0]))) then
+ begin
+ LC := LC + TNat.Inc(16, PUInt32(@AZ.W[0]));
+ LC := LC and P16;
+ end;
+ AZ.W[16] := LC;
+ AZ.W[17] := 0;
+end;
+
class procedure TSecP521R1Field.AddOne(const AX, AZ: TCryptoLibUInt32Array);
var
LC: UInt32;
@@ -333,6 +379,42 @@ class procedure TSecP521R1Field.Multiply(const AX, AY, AZ, ATT: TCryptoLibUInt32
Reduce(ATT, AZ);
end;
+class procedure TSecP521R1Field.Multiply(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt);
+var
+ LX, LY, LZ: TCryptoLibUInt32Array;
+begin
+ // 17 real limbs padded to 18 (= 9 uint64) with AX.W[17] = AY.W[17] = 0.
+ if TFpKernelSimd.TryMul(PUInt32(@AX.W[0]), PUInt32(@AY.W[0]), PUInt32(@ATT.W[0]), 18) then
+ Reduce(PUInt32(@ATT.W[0]), PUInt32(@AZ.W[0]))
+ else
+ begin
+ LX := TNat.Create(17);
+ LY := TNat.Create(17);
+ LZ := TNat.Create(17);
+ System.Move(AX.W[0], LX[0], 17 * SizeOf(UInt32));
+ System.Move(AY.W[0], LY[0], 17 * SizeOf(UInt32));
+ Multiply(LX, LY, LZ);
+ System.Move(LZ[0], AZ.W[0], 17 * SizeOf(UInt32));
+ end;
+ AZ.W[17] := 0;
+end;
+
+class procedure TSecP521R1Field.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+var
+ Lt, LZero: TFe;
+begin
+ // a = -3: -(x+x+x) via field adds, cheaper than a full multiply by FFa.
+ Add(AX, AX, Lt);
+ Add(Lt, AX, Lt);
+ System.FillChar(LZero, SizeOf(LZero), 0);
+ Subtract(LZero, Lt, AZ);
+end;
+
+class procedure TSecP521R1Field.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ Multiply(AX, FFb3, AZ, ATT);
+end;
+
class procedure TSecP521R1Field.Negate(const AX, AZ: TCryptoLibUInt32Array);
begin
if IsZero(AX) <> 0 then
@@ -361,6 +443,11 @@ class procedure TSecP521R1Field.RandomMult(const AR: ISecureRandom; const AZ: TC
end;
class procedure TSecP521R1Field.Reduce(const AXX, AZ: TCryptoLibUInt32Array);
+begin
+ Reduce(PUInt32(@AXX[0]), PUInt32(@AZ[0]));
+end;
+
+class procedure TSecP521R1Field.Reduce(AXX, AZ: PUInt32);
var
LXX32: UInt32;
LC: UInt32;
@@ -369,10 +456,10 @@ class procedure TSecP521R1Field.Reduce(const AXX, AZ: TCryptoLibUInt32Array);
Assert(AXX[32] shr 18 = 0);
{$ENDIF DEBUG}
LXX32 := AXX[32];
- LC := TNat.ShiftDownBits(16, AXX, 16, 9, LXX32, AZ, 0) shr 23;
+ LC := TNat.ShiftDownBits(16, AXX + 16, 9, LXX32, AZ) shr 23;
LC := LC + (LXX32 shr 9);
LC := LC + TNat.AddTo(16, AXX, AZ);
- if (LC > P16) or ((LC = P16) and TNat.Eq(16, AZ, FP)) then
+ if (LC > P16) or ((LC = P16) and TNat.Eq(16, AZ, PUInt32(@FP[0]))) then
begin
LC := LC + TNat.Inc(16, AZ);
LC := LC and P16;
@@ -410,6 +497,23 @@ class procedure TSecP521R1Field.Square(const AX, AZ, ATT: TCryptoLibUInt32Array)
Reduce(ATT, AZ);
end;
+class procedure TSecP521R1Field.Square(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+var
+ LX, LZ: TCryptoLibUInt32Array;
+begin
+ if TFpKernelSimd.TrySqr(PUInt32(@AX.W[0]), PUInt32(@ATT.W[0]), 18) then
+ Reduce(PUInt32(@ATT.W[0]), PUInt32(@AZ.W[0]))
+ else
+ begin
+ LX := TNat.Create(17);
+ LZ := TNat.Create(17);
+ System.Move(AX.W[0], LX[0], 17 * SizeOf(UInt32));
+ Square(LX, LZ);
+ System.Move(LZ[0], AZ.W[0], 17 * SizeOf(UInt32));
+ end;
+ AZ.W[17] := 0;
+end;
+
class procedure TSecP521R1Field.SquareN(const AX: TCryptoLibUInt32Array; AN: Int32;
const AZ: TCryptoLibUInt32Array);
var
@@ -460,6 +564,21 @@ class procedure TSecP521R1Field.Subtract(const AX, AY, AZ: TCryptoLibUInt32Array
AZ[16] := UInt32(LC);
end;
+class procedure TSecP521R1Field.Subtract(const AX, AY: TFe; var AZ: TFe);
+var
+ LC: Int32;
+begin
+ LC := Int32(TNat.Sub(16, PUInt32(@AX.W[0]), PUInt32(@AY.W[0]), PUInt32(@AZ.W[0])))
+ + Int32(AX.W[16] - AY.W[16]);
+ if LC < 0 then
+ begin
+ LC := LC + Int32(TNat.Dec(16, PUInt32(@AZ.W[0])));
+ LC := LC and Int32(P16);
+ end;
+ AZ.W[16] := UInt32(LC);
+ AZ.W[17] := 0;
+end;
+
class procedure TSecP521R1Field.Twice(const AX, AZ: TCryptoLibUInt32Array);
var
LX16: UInt32;
@@ -1079,7 +1198,17 @@ function TSecP521R1Curve.CreateDefaultMultiplier: IECMultiplier;
begin
LCurve := Self as IECCurve;
LFieldOps := TSecP521R1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order);
- Result := TFixedWindowCTMultiplier.Create(LFieldOps) as IECMultiplier;
+ Result := TFpCTMultiplier.Create(LFieldOps);
+end;
+
+function TSecP521R1Curve.CreateBasePointMultiplier: IECMultiplier;
+var
+ LCurve: IECCurve;
+ LFieldOps: IFpFieldOps;
+begin
+ LCurve := Self as IECCurve;
+ LFieldOps := TSecP521R1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order);
+ Result := TFpCombMultiplier.Create(LFieldOps);
end;
{ TSecP521R1FpFieldOps }
@@ -1120,31 +1249,11 @@ procedure TSecP521R1FpFieldOps.Mul(const AX, AY, AZ: TCryptoLibUInt32Array);
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);
@@ -1177,4 +1286,42 @@ procedure TSecP521R1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array);
TNat.Copy(FE_INTS, FOne, 0, AZ, 0);
end;
+
+{ TSecP521R1FieldArith }
+
+class function TSecP521R1FieldArith.FieldLimbs: Int32;
+begin
+ Result := 17;
+end;
+
+class procedure TSecP521R1FieldArith.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP521R1Field.Multiply(AX, AY, AZ, ATT);
+end;
+
+class procedure TSecP521R1FieldArith.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP521R1Field.Square(AX, AZ, ATT);
+end;
+
+class procedure TSecP521R1FieldArith.Add(const AX, AY: TFe; var AZ: TFe);
+begin
+ TSecP521R1Field.Add(AX, AY, AZ);
+end;
+
+class procedure TSecP521R1FieldArith.Sub(const AX, AY: TFe; var AZ: TFe);
+begin
+ TSecP521R1Field.Subtract(AX, AY, AZ);
+end;
+
+class procedure TSecP521R1FieldArith.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP521R1Field.MulByA(AX, AZ, ATT);
+end;
+
+class procedure TSecP521R1FieldArith.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt);
+begin
+ TSecP521R1Field.MulByB3(AX, AZ, ATT);
+end;
+
end.
diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpCTFieldArith.pas b/CryptoLib/src/Math/EC/Multiplier/ClpCTFieldArith.pas
new file mode 100644
index 00000000..06b8a6f3
--- /dev/null
+++ b/CryptoLib/src/Math/EC/Multiplier/ClpCTFieldArith.pas
@@ -0,0 +1,52 @@
+{ *********************************************************************************** }
+{ * 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 ClpCTFieldArith;
+
+{$I ..\..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+ ClpCTFieldValue;
+
+type
+ ///
+ /// Per-curve prime-field arithmetic as a base of virtual; abstract
+ /// class methods over records; a curve overrides each op
+ /// and the generic TCTPoint<TOps: TCTFieldArithBase> dispatches to it.
+ ///
+ TCTFieldArithBase = class
+ public
+ /// uint32 limb count N for this curve (P-256 = 8).
+ class function FieldLimbs: Int32; virtual; abstract;
+ /// AZ := AX * AY mod p. ATT is caller-owned 2N scratch.
+ class procedure Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); virtual; abstract;
+ /// AZ := AX^2 mod p.
+ class procedure Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); virtual; abstract;
+ /// AZ := AX + AY mod p.
+ class procedure Add(const AX, AY: TFe; var AZ: TFe); virtual; abstract;
+ /// AZ := AX - AY mod p.
+ class procedure Sub(const AX, AY: TFe; var AZ: TFe); virtual; abstract;
+ /// AZ := a * AX mod p (curve coefficient a).
+ class procedure MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); virtual; abstract;
+ /// AZ := 3b * AX mod p (b3 = 3 * curve coefficient b).
+ class procedure MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); virtual; abstract;
+ end;
+
+implementation
+
+end.
diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpCTFieldValue.pas b/CryptoLib/src/Math/EC/Multiplier/ClpCTFieldValue.pas
new file mode 100644
index 00000000..93f930b4
--- /dev/null
+++ b/CryptoLib/src/Math/EC/Multiplier/ClpCTFieldValue.pas
@@ -0,0 +1,60 @@
+{ *********************************************************************************** }
+{ * 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 ClpCTFieldValue;
+
+{$I ..\..\..\Include\CryptoLib.inc}
+
+interface
+
+const
+ /// Widest field this value-type layer serves. P-521's 17 real uint32
+ /// limbs are padded to 18 (= 9 uint64) so the even-width Fp kernel can multiply
+ /// it; the 18th limb is kept zero. Smaller curves use W[0..N-1] and leave the
+ /// tail zero.
+ MAX_CT_FE_LIMBS = 18;
+
+type
+ ///
+ /// One prime-field element as a fixed-size, allocation-free stack record (the
+ /// same little-endian 32-bit-limb representation as the heap arrays, just
+ /// inline). Sized for the widest curve; a given curve uses W[0..N-1]. Passed
+ /// const/var only — never returned by value on a secret-bearing
+ /// path (a by-value copy scatters unscrubbable secrets on the stack).
+ ///
+ TFe = record
+ W: array [0 .. MAX_CT_FE_LIMBS - 1] of UInt32;
+ end;
+
+ ///
+ /// Double-width (2N) multiply/square scratch, stack-resident. Reused across a
+ /// whole point formula rather than allocated per field op.
+ ///
+ TFeExt = record
+ W: array [0 .. 2 * MAX_CT_FE_LIMBS - 1] of UInt32;
+ end;
+
+ ///
+ /// A point in homogeneous projective coordinates with inline field-element
+ /// coordinates (no heap). Value aggregate over three .
+ ///
+ TFePoint = record
+ X, Y, Z: TFe;
+ end;
+
+implementation
+
+end.
diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpCTPoint.pas b/CryptoLib/src/Math/EC/Multiplier/ClpCTPoint.pas
new file mode 100644
index 00000000..222d7b61
--- /dev/null
+++ b/CryptoLib/src/Math/EC/Multiplier/ClpCTPoint.pas
@@ -0,0 +1,249 @@
+{ *********************************************************************************** }
+{ * 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 ClpCTPoint;
+
+{$I ..\..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+ ClpNat,
+ ClpBitOperations,
+ ClpIFpFieldOps,
+ ClpIECFieldElement,
+ ClpCTFieldValue,
+ ClpCTFieldArith,
+ ClpCryptoLibTypes;
+
+type
+ ///
+ /// Generic constant-time value-type point operations for Fp short-Weierstrass
+ /// curves in homogeneous coordinates: the RCB2016 complete group law (addition
+ /// = Algorithm 1, doubling = Algorithm 3) plus the representation helpers
+ /// (affine <-> projective conversion and the masked table lookup) that the
+ /// multipliers build on. One body serves every curve; TOps supplies the
+ /// field arithmetic via the virtual class
+ /// methods, so the formulas run with no interface dispatch and every temporary
+ /// is a stack . The curve context needed by the conversions
+ /// (field width, one, is-zero, inverse) comes from the IFpFieldOps
+ /// adapter threaded in as a parameter. The windowed/comb scalar loops that
+ /// drive these live in TFpCTMultiplier / TFpCombMultiplier.
+ ///
+ TCTPoint = class sealed
+ public
+ /// RCB2016 complete addition (Algorithm 1, explicit a and b3).
+ /// AR := AP + AQ in homogeneous coordinates; all temporaries are stack
+ /// records. AR may alias AP or AQ.
+ class procedure PointAdd(const AP, AQ: TFePoint; var AR: TFePoint); static;
+ /// RCB2016 complete doubling (Algorithm 3). AR := 2*AP.
+ class procedure PointDouble(const AP: TFePoint; var AR: TFePoint); static;
+ class procedure OneFe(const AFieldOps: IFpFieldOps; var AZ: TFe); static;
+ class procedure Infinity(const AFieldOps: IFpFieldOps; var AR: TFePoint); static;
+ class procedure FromAffine(const AFieldOps: IFpFieldOps;
+ const AXa, AYa: TCryptoLibUInt32Array; var AR: TFePoint); static;
+ class procedure FromAffineElt(const AFieldOps: IFpFieldOps;
+ const AX, AY: IECFieldElement; var AR: TFePoint); static;
+ class procedure ToAffine(const AFieldOps: IFpFieldOps; const AP: TFePoint;
+ const AXa, AYa: TCryptoLibUInt32Array; out AIsInfinity: Boolean); static;
+ /// Constant-time masked lookup: AR := ATable[AIndex], scanning all
+ /// ACount entries so the access pattern is scalar-independent.
+ class procedure SelectEntry(const AFieldOps: IFpFieldOps;
+ const ATable: array of TFePoint; ACount, AIndex: Int32; var AR: TFePoint); static;
+ end;
+
+implementation
+
+class procedure TCTPoint.PointAdd(const AP, AQ: TFePoint; var AR: TFePoint);
+var
+ Lt0, Lt1, Lt2, Lt3, Lt4, Lt5, LX3, LY3, LZ3: TFe;
+ LTT: TFeExt;
+begin
+ TOps.Mul(AP.X, AQ.X, Lt0, LTT); // t0 = X1*X2
+ TOps.Mul(AP.Y, AQ.Y, Lt1, LTT); // t1 = Y1*Y2
+ TOps.Mul(AP.Z, AQ.Z, Lt2, LTT); // t2 = Z1*Z2
+ TOps.Add(AP.X, AP.Y, Lt3); // t3 = X1+Y1
+ TOps.Add(AQ.X, AQ.Y, Lt4); // t4 = X2+Y2
+ TOps.Mul(Lt3, Lt4, Lt3, LTT); // t3 = t3*t4
+ TOps.Add(Lt0, Lt1, Lt4); // t4 = t0+t1
+ TOps.Sub(Lt3, Lt4, Lt3); // t3 = t3-t4
+ TOps.Add(AP.X, AP.Z, Lt4); // t4 = X1+Z1
+ TOps.Add(AQ.X, AQ.Z, Lt5); // t5 = X2+Z2
+ TOps.Mul(Lt4, Lt5, Lt4, LTT); // t4 = t4*t5
+ TOps.Add(Lt0, Lt2, Lt5); // t5 = t0+t2
+ TOps.Sub(Lt4, Lt5, Lt4); // t4 = t4-t5
+ TOps.Add(AP.Y, AP.Z, Lt5); // t5 = Y1+Z1
+ TOps.Add(AQ.Y, AQ.Z, LX3); // X3 = Y2+Z2
+ TOps.Mul(Lt5, LX3, Lt5, LTT); // t5 = t5*X3
+ TOps.Add(Lt1, Lt2, LX3); // X3 = t1+t2
+ TOps.Sub(Lt5, LX3, Lt5); // t5 = t5-X3
+ TOps.MulByA(Lt4, LZ3, LTT); // Z3 = a*t4
+ TOps.MulByB3(Lt2, LX3, LTT); // X3 = b3*t2
+ TOps.Add(LX3, LZ3, LZ3); // Z3 = X3+Z3
+ TOps.Sub(Lt1, LZ3, LX3); // X3 = t1-Z3
+ TOps.Add(Lt1, LZ3, LZ3); // Z3 = t1+Z3
+ TOps.Mul(LX3, LZ3, LY3, LTT); // Y3 = X3*Z3
+ TOps.Add(Lt0, Lt0, Lt1); // t1 = t0+t0
+ TOps.Add(Lt1, Lt0, Lt1); // t1 = t1+t0
+ TOps.MulByA(Lt2, Lt2, LTT); // t2 = a*t2
+ TOps.MulByB3(Lt4, Lt4, LTT); // t4 = b3*t4
+ TOps.Add(Lt1, Lt2, Lt1); // t1 = t1+t2
+ TOps.Sub(Lt0, Lt2, Lt2); // t2 = t0-t2
+ TOps.MulByA(Lt2, Lt2, LTT); // t2 = a*t2
+ TOps.Add(Lt4, Lt2, Lt4); // t4 = t4+t2
+ TOps.Mul(Lt1, Lt4, Lt0, LTT); // t0 = t1*t4
+ TOps.Add(LY3, Lt0, LY3); // Y3 = Y3+t0
+ TOps.Mul(Lt5, Lt4, Lt0, LTT); // t0 = t5*t4
+ TOps.Mul(Lt3, LX3, LX3, LTT); // X3 = t3*X3
+ TOps.Sub(LX3, Lt0, LX3); // X3 = X3-t0
+ TOps.Mul(Lt3, Lt1, Lt0, LTT); // t0 = t3*t1
+ TOps.Mul(Lt5, LZ3, LZ3, LTT); // Z3 = t5*Z3
+ TOps.Add(LZ3, Lt0, LZ3); // Z3 = Z3+t0
+ AR.X := LX3;
+ AR.Y := LY3;
+ AR.Z := LZ3;
+end;
+
+class procedure TCTPoint.PointDouble(const AP: TFePoint; var AR: TFePoint);
+var
+ Lt0, Lt1, Lt2, Lt3, LX3, LY3, LZ3: TFe;
+ LTT: TFeExt;
+begin
+ TOps.Sqr(AP.X, Lt0, LTT); // t0 = X*X
+ TOps.Sqr(AP.Y, Lt1, LTT); // t1 = Y*Y
+ TOps.Sqr(AP.Z, Lt2, LTT); // t2 = Z*Z
+ TOps.Mul(AP.X, AP.Y, Lt3, LTT); // t3 = X*Y
+ TOps.Add(Lt3, Lt3, Lt3); // t3 = t3+t3
+ TOps.Mul(AP.X, AP.Z, LZ3, LTT); // Z3 = X*Z
+ TOps.Add(LZ3, LZ3, LZ3); // Z3 = Z3+Z3
+ TOps.MulByA(LZ3, LX3, LTT); // X3 = a*Z3
+ TOps.MulByB3(Lt2, LY3, LTT); // Y3 = b3*t2
+ TOps.Add(LX3, LY3, LY3); // Y3 = X3+Y3
+ TOps.Sub(Lt1, LY3, LX3); // X3 = t1-Y3
+ TOps.Add(Lt1, LY3, LY3); // Y3 = t1+Y3
+ TOps.Mul(LX3, LY3, LY3, LTT); // Y3 = X3*Y3
+ TOps.Mul(Lt3, LX3, LX3, LTT); // X3 = t3*X3
+ TOps.MulByB3(LZ3, LZ3, LTT); // Z3 = b3*Z3
+ TOps.MulByA(Lt2, Lt2, LTT); // t2 = a*t2
+ TOps.Sub(Lt0, Lt2, Lt3); // t3 = t0-t2
+ TOps.MulByA(Lt3, Lt3, LTT); // t3 = a*t3
+ TOps.Add(Lt3, LZ3, Lt3); // t3 = t3+Z3
+ TOps.Add(Lt0, Lt0, LZ3); // Z3 = t0+t0
+ TOps.Add(LZ3, Lt0, Lt0); // t0 = Z3+t0
+ TOps.Add(Lt0, Lt2, Lt0); // t0 = t0+t2
+ TOps.Mul(Lt0, Lt3, Lt0, LTT); // t0 = t0*t3
+ TOps.Add(LY3, Lt0, LY3); // Y3 = Y3+t0
+ TOps.Mul(AP.Y, AP.Z, Lt2, LTT); // t2 = Y*Z
+ TOps.Add(Lt2, Lt2, Lt2); // t2 = t2+t2
+ TOps.Mul(Lt2, Lt3, Lt0, LTT); // t0 = t2*t3
+ TOps.Sub(LX3, Lt0, LX3); // X3 = X3-t0
+ TOps.Mul(Lt2, Lt1, LZ3, LTT); // Z3 = t2*t1
+ TOps.Add(LZ3, LZ3, LZ3); // Z3 = Z3+Z3
+ TOps.Add(LZ3, LZ3, LZ3); // Z3 = Z3+Z3
+ AR.X := LX3;
+ AR.Y := LY3;
+ AR.Z := LZ3;
+end;
+
+class procedure TCTPoint.OneFe(const AFieldOps: IFpFieldOps; var AZ: TFe);
+var
+ LArr: TCryptoLibUInt32Array;
+begin
+ LArr := TNat.Create(AFieldOps.GetFieldInts);
+ AFieldOps.FieldOne(LArr);
+ FillChar(AZ, SizeOf(AZ), 0);
+ Move(LArr[0], AZ.W[0], AFieldOps.GetFieldInts * SizeOf(UInt32));
+end;
+
+class procedure TCTPoint.Infinity(const AFieldOps: IFpFieldOps; var AR: TFePoint);
+begin
+ FillChar(AR, SizeOf(AR), 0);
+ OneFe(AFieldOps, AR.Y);
+end;
+
+class procedure TCTPoint.FromAffine(const AFieldOps: IFpFieldOps;
+ const AXa, AYa: TCryptoLibUInt32Array; var AR: TFePoint);
+var
+ LN: Int32;
+begin
+ LN := AFieldOps.GetFieldInts;
+ FillChar(AR, SizeOf(AR), 0);
+ Move(AXa[0], AR.X.W[0], LN * SizeOf(UInt32));
+ Move(AYa[0], AR.Y.W[0], LN * SizeOf(UInt32));
+ OneFe(AFieldOps, AR.Z);
+end;
+
+class procedure TCTPoint.FromAffineElt(const AFieldOps: IFpFieldOps;
+ const AX, AY: IECFieldElement; var AR: TFePoint);
+var
+ LN: Int32;
+ LXa, LYa: TCryptoLibUInt32Array;
+begin
+ LN := AFieldOps.GetFieldInts;
+ LXa := TNat.Create(LN);
+ LYa := TNat.Create(LN);
+ AFieldOps.FieldFromBigInteger(AX.ToBigInteger(), LXa);
+ AFieldOps.FieldFromBigInteger(AY.ToBigInteger(), LYa);
+ FromAffine(AFieldOps, LXa, LYa, AR);
+end;
+
+class procedure TCTPoint.ToAffine(const AFieldOps: IFpFieldOps;
+ const AP: TFePoint; const AXa, AYa: TCryptoLibUInt32Array; out AIsInfinity: Boolean);
+var
+ LN: Int32;
+ LZarr, LZInvArr: TCryptoLibUInt32Array;
+ LZInv, LTmp: TFe;
+ LTT: TFeExt;
+begin
+ LN := AFieldOps.GetFieldInts;
+ LZarr := TNat.Create(LN);
+ Move(AP.Z.W[0], LZarr[0], LN * SizeOf(UInt32));
+ AIsInfinity := AFieldOps.IsZero(LZarr);
+ if AIsInfinity then
+ Exit;
+ LZInvArr := TNat.Create(LN);
+ AFieldOps.Inv(LZarr, LZInvArr);
+ FillChar(LZInv, SizeOf(LZInv), 0);
+ Move(LZInvArr[0], LZInv.W[0], LN * SizeOf(UInt32));
+ TOps.Mul(AP.X, LZInv, LTmp, LTT);
+ Move(LTmp.W[0], AXa[0], LN * SizeOf(UInt32));
+ TOps.Mul(AP.Y, LZInv, LTmp, LTT);
+ Move(LTmp.W[0], AYa[0], LN * SizeOf(UInt32));
+end;
+
+class procedure TCTPoint.SelectEntry(const AFieldOps: IFpFieldOps;
+ const ATable: array of TFePoint; ACount, AIndex: Int32; var AR: TFePoint);
+var
+ LN, LI, LJ: Int32;
+ LMask: UInt32;
+ LEntry: TFePoint;
+begin
+ LN := AFieldOps.GetFieldInts;
+ FillChar(AR, SizeOf(AR), 0);
+ for LI := 0 to ACount - 1 do
+ begin
+ LEntry := ATable[LI];
+ LMask := UInt32(TBitOperations.Asr32(((LI xor AIndex) - 1), 31));
+ for LJ := 0 to LN - 1 do
+ begin
+ AR.X.W[LJ] := AR.X.W[LJ] xor (LEntry.X.W[LJ] and LMask);
+ AR.Y.W[LJ] := AR.Y.W[LJ] xor (LEntry.Y.W[LJ] and LMask);
+ AR.Z.W[LJ] := AR.Z.W[LJ] xor (LEntry.Z.W[LJ] and LMask);
+ end;
+ end;
+end;
+
+end.
diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpFixedWindowCTMultiplier.pas b/CryptoLib/src/Math/EC/Multiplier/ClpFpCTMultiplier.pas
similarity index 61%
rename from CryptoLib/src/Math/EC/Multiplier/ClpFixedWindowCTMultiplier.pas
rename to CryptoLib/src/Math/EC/Multiplier/ClpFpCTMultiplier.pas
index a333205f..14b9877c 100644
--- a/CryptoLib/src/Math/EC/Multiplier/ClpFixedWindowCTMultiplier.pas
+++ b/CryptoLib/src/Math/EC/Multiplier/ClpFpCTMultiplier.pas
@@ -14,7 +14,7 @@
(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
-unit ClpFixedWindowCTMultiplier;
+unit ClpFpCTMultiplier;
{$I ..\..\..\Include\CryptoLib.inc}
@@ -28,23 +28,33 @@ interface
ClpBitOperations,
ClpMultipliers,
ClpIFpFieldOps,
- ClpHomogeneousPoint,
ClpISecureRandom,
ClpSecureRandom,
ClpIECFieldElement,
ClpIECCommon,
+ ClpCTFieldValue,
+ ClpCTFieldArith,
+ ClpCTPoint,
ClpCryptoLibTypes,
ClpCryptoLibExceptions;
+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';
+
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.
+ /// Value-type constant-time single-scalar variable-point multiplier for Fp
+ /// short-Weierstrass curves. Countermeasures: scalar blinding, randomized
+ /// projective coordinates,
+ /// fixed processing length, masked table lookups, one unconditional add per
+ /// window), but the hot loop runs over stack records via
+ /// the generic TCTPoint<TOps> — no per-operation heap allocation
+ /// and no interface dispatch. The curve context (order, affine conversion,
+ /// inverse, field-element boxing) comes from the IFpFieldOps adapter.
///
- TFixedWindowCTMultiplier = class sealed(TAbstractECMultiplier, IECMultiplier)
+ TFpCTMultiplier = class sealed(TAbstractECMultiplier, IECMultiplier)
strict private
const
WINDOW_BITS = Int32(4);
@@ -57,8 +67,7 @@ TFixedWindowCTMultiplier = class sealed(TAbstractECMultiplier, IECMultiplier)
FBlindBits: Int32;
function GetRandom: ISecureRandom;
procedure GenerateBlind(const ARandom: ISecureRandom; const AZ: TCryptoLibUInt32Array);
- function SelectEntry(const ATable: TCryptoLibGenericArray;
- AIndex: Int32): TCTHomogPoint;
+ procedure ScaleRandom(const AP: TFePoint; const ALambda: TFe; var AR: TFePoint);
strict protected
function MultiplyPositive(const AP: IECPoint; const AK: TBigInteger): IECPoint; override;
public
@@ -70,15 +79,9 @@ TFixedWindowCTMultiplier = class sealed(TAbstractECMultiplier, IECMultiplier)
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 }
+{ TFpCTMultiplier }
-constructor TFixedWindowCTMultiplier.Create(const AFieldOps: IFpFieldOps;
- ABlindBits: Int32);
+constructor TFpCTMultiplier.Create(const AFieldOps: IFpFieldOps; ABlindBits: Int32);
begin
Inherited Create;
if (ABlindBits < DEFAULT_BLIND_BITS) or (ABlindBits > MAX_BLIND_BITS)
@@ -88,7 +91,7 @@ constructor TFixedWindowCTMultiplier.Create(const AFieldOps: IFpFieldOps;
FBlindBits := ABlindBits;
end;
-constructor TFixedWindowCTMultiplier.Create(const AFieldOps: IFpFieldOps;
+constructor TFpCTMultiplier.Create(const AFieldOps: IFpFieldOps;
const ARandom: ISecureRandom; ABlindBits: Int32);
begin
Inherited Create;
@@ -100,53 +103,41 @@ constructor TFixedWindowCTMultiplier.Create(const AFieldOps: IFpFieldOps;
FBlindBits := ABlindBits;
end;
-function TFixedWindowCTMultiplier.GetRandom: ISecureRandom;
+function TFpCTMultiplier.GetRandom: ISecureRandom;
begin
if FRandom = nil then
FRandom := TSecureRandom.Create() as ISecureRandom;
Result := FRandom;
end;
-procedure TFixedWindowCTMultiplier.GenerateBlind(const ARandom: ISecureRandom;
+procedure TFpCTMultiplier.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);
+ 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;
+procedure TFpCTMultiplier.ScaleRandom(const AP: TFePoint; const ALambda: TFe;
+ var AR: TFePoint);
var
- LN, LI, LJ: Int32;
- LMask: UInt32;
+ LTT: TFeExt;
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;
+ TOps.Mul(AP.X, ALambda, AR.X, LTT);
+ TOps.Mul(AP.Y, ALambda, AR.Y, LTT);
+ TOps.Mul(AP.Z, ALambda, AR.Z, LTT);
end;
-function TFixedWindowCTMultiplier.MultiplyPositive(const AP: IECPoint;
+function TFpCTMultiplier.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;
+ LTable: array of TFePoint;
+ LBase, LAcc, LSel: TFePoint;
+ LLambda: TFe;
+ LLambdaArr, LXa, LYa, LN, LR, LProd, LK, LKPrime: TCryptoLibUInt32Array;
LIsInfinity: Boolean;
LXfe, LYfe: IECFieldElement;
LAffine: IECPoint;
@@ -155,34 +146,35 @@ function TFixedWindowCTMultiplier.MultiplyPositive(const AP: IECPoint;
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 ---
+ // 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);
+ // randomized projective coordinates: base = (lambda*x, lambda*y, lambda)
+ LLambdaArr := TNat.Create(LFieldInts);
+ FFieldOps.RandomMult(LRandom, LLambdaArr);
+ FillChar(LLambda, SizeOf(LLambda), 0);
+ Move(LLambdaArr[0], LLambda.W[0], LFieldInts * SizeOf(UInt32));
+ TCTPoint.FromAffine(FFieldOps, LXa, LYa, LBase);
+ ScaleRandom(LBase, LLambda, LBase);
+
+ // projective precomputation table [0]=O, [i]=[i]*base
+ SetLength(LTable, TABLE_SIZE);
+ TCTPoint.Infinity(FFieldOps, LTable[0]);
LTable[1] := LBase;
for LI := 2 to TABLE_SIZE - 1 do
- LTable[LI] := TCTHomogeneousMath.Add(FFieldOps, LTable[LI - 1], LBase);
+ TCTPoint.PointAdd(LTable[LI - 1], LBase, LTable[LI]);
- // --- scalar blinding in fixed-width Nat: k' = k + r*n ---
+ // scalar blinding in fixed-width Nat: k' = k + r*n
LScalarBits := FFieldOps.GetOrderBits + FBlindBits + 1;
LScalarInts := TNat.GetLengthForBits(LScalarBits) + 1;
LN := TNat.Create(LScalarInts);
@@ -196,13 +188,13 @@ function TFixedWindowCTMultiplier.MultiplyPositive(const AP: IECPoint;
TNat.Add(LScalarInts, LK, LProd, LKPrime);
try
- // --- fixed-length windowed ladder ---
+ // fixed-length windowed ladder
LWindows := (LScalarBits + WINDOW_BITS - 1) div WINDOW_BITS;
- LAcc := TCTHomogeneousMath.Infinity(FFieldOps);
+ TCTPoint.Infinity(FFieldOps, LAcc);
for LI := LWindows - 1 downto 0 do
begin
for LJ := 0 to WINDOW_BITS - 1 do
- LAcc := TCTHomogeneousMath.Double(FFieldOps, LAcc);
+ TCTPoint.PointDouble(LAcc, LAcc);
// WINDOW_BITS divides 32, so a digit never spans a limb boundary
LBit := LI * WINDOW_BITS;
@@ -210,11 +202,11 @@ function TFixedWindowCTMultiplier.MultiplyPositive(const AP: IECPoint;
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);
+ TCTPoint.SelectEntry(FFieldOps, LTable, TABLE_SIZE, LDigit, LSel);
+ TCTPoint.PointAdd(LAcc, LSel, LAcc);
end;
- TCTHomogeneousMath.ToAffine(FFieldOps, LAcc, LXa, LYa, LIsInfinity);
+ TCTPoint.ToAffine(FFieldOps, LAcc, LXa, LYa, LIsInfinity);
if LIsInfinity then
Exit(AP.Curve.Infinity);
@@ -226,25 +218,12 @@ function TFixedWindowCTMultiplier.MultiplyPositive(const AP: IECPoint;
TNat.Zero(LScalarInts, LK);
TNat.Zero(LScalarInts, LR);
TNat.Zero(LScalarInts * 2, LProd);
- TNat.Zero(LFieldInts, LLambda);
+ FillChar(LLambda, SizeOf(LLambda), 0);
+ FillChar(LBase, SizeOf(LBase), 0);
+ FillChar(LAcc, SizeOf(LAcc), 0);
+ FillChar(LSel, SizeOf(LSel), 0);
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;
+ FillChar(LTable[LI], SizeOf(LTable[LI]), 0);
end;
end;
diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpFpCombMultiplier.pas b/CryptoLib/src/Math/EC/Multiplier/ClpFpCombMultiplier.pas
new file mode 100644
index 00000000..396c85c5
--- /dev/null
+++ b/CryptoLib/src/Math/EC/Multiplier/ClpFpCombMultiplier.pas
@@ -0,0 +1,159 @@
+{ *********************************************************************************** }
+{ * 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 ClpFpCombMultiplier;
+
+{$I ..\..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+ SysUtils,
+ ClpBigInteger,
+ ClpNat,
+ ClpBitOperations,
+ ClpMultipliers,
+ ClpFixedPointUtilities,
+ ClpIFixedPointPreCompInfo,
+ ClpWeakRef,
+ ClpIFpFieldOps,
+ ClpIECFieldElement,
+ ClpIECCommon,
+ ClpCTFieldValue,
+ ClpCTFieldArith,
+ ClpCTPoint,
+ ClpCryptoLibTypes,
+ ClpCryptoLibExceptions;
+
+resourcestring
+ SFixedPointCombNotSupported =
+ 'fixed-point comb doesn''t support scalars larger than the curve order';
+
+type
+ ///
+ /// Value-type constant-time fixed-base (comb) multiplier for Fp short-Weierstrass
+ /// curves. Same comb structure, recoding and masked table lookup as the
+ /// array-based TFixedPointCombMultiplier, but the per-column point
+ /// arithmetic runs over stack records via the RCB complete
+ /// formulas (TCTPoint<TOps>) - no per-operation heap allocation.
+ /// The precomputed generator table (public) is reused via
+ /// TFixedPointUtilities; only the online phase touches the secret scalar.
+ ///
+ TFpCombMultiplier = class sealed(TAbstractECMultiplier,
+ IECMultiplier)
+ strict private
+ FFieldOps: IFpFieldOps;
+ FCachedInfo: TWeakRef;
+ FTable: TCryptoLibGenericArray;
+ FOffset: TFePoint;
+ strict protected
+ function MultiplyPositive(const AP: IECPoint; const AK: TBigInteger): IECPoint; override;
+ public
+ constructor Create(const AFieldOps: IFpFieldOps);
+ end;
+
+implementation
+
+{ TFpCombMultiplier }
+
+constructor TFpCombMultiplier.Create(const AFieldOps: IFpFieldOps);
+begin
+ Inherited Create;
+ FFieldOps := AFieldOps;
+end;
+
+function TFpCombMultiplier.MultiplyPositive(const AP: IECPoint;
+ const AK: TBigInteger): IECPoint;
+var
+ LC: IECCurve;
+ LSize, LWidth, LD, LFullComb, LN, LI, LJ: Int32;
+ LInfo, LCachedInfo: IFixedPointPreCompInfo;
+ LLookup: IECLookupTable;
+ LR, LSel: TFePoint;
+ LK, LXa, LYa: TCryptoLibUInt32Array;
+ LSecretIndex, LSecretBit: UInt32;
+ LIsInfinity: Boolean;
+ LAff: IECPoint;
+begin
+ LC := AP.Curve;
+ LSize := TFixedPointUtilities.GetCombSize(LC);
+
+ if AK.BitLength > LSize then
+ raise EInvalidOperationCryptoLibException.CreateRes(@SFixedPointCombNotSupported);
+
+ LInfo := TFixedPointUtilities.Precompute(AP);
+ LWidth := LInfo.Width;
+ LD := (LSize + LWidth - 1) div LWidth;
+ LFullComb := LD * LWidth;
+
+ LCachedInfo := FCachedInfo;
+ if LInfo <> LCachedInfo then
+ begin
+ // Build the value-type table once per base point. The precomp info is cached
+ // on the point, so this rebuilds only when the base point changes (never for
+ // the reused generator). LookupVar is fine - the table contents are public;
+ // only the online index is secret.
+ LLookup := LInfo.LookupTable;
+ LN := LLookup.GetSize;
+ SetLength(FTable, LN);
+ for LI := 0 to LN - 1 do
+ begin
+ LAff := LLookup.LookupVar(LI);
+ TCTPoint.FromAffineElt(FFieldOps, LAff.RawXCoord, LAff.RawYCoord, FTable[LI]);
+ end;
+ LAff := LInfo.Offset.Normalize();
+ TCTPoint.FromAffineElt(FFieldOps, LAff.AffineXCoord, LAff.AffineYCoord, FOffset);
+ FCachedInfo := LInfo;
+ end;
+ LN := System.Length(FTable);
+
+ LK := TNat.FromBigInteger(LFullComb, AK);
+
+ TCTPoint.Infinity(FFieldOps, LR);
+ for LI := 1 to LD do
+ begin
+ LSecretIndex := 0;
+
+ LJ := LFullComb - LI;
+ while LJ >= 0 do
+ begin
+ LSecretBit := LK[TBitOperations.Asr32(LJ, 5)] shr (LJ and $1F);
+ LSecretIndex := LSecretIndex xor (LSecretBit shr 1);
+ LSecretIndex := LSecretIndex shl 1;
+ LSecretIndex := LSecretIndex xor LSecretBit;
+ LJ := LJ - LD;
+ end;
+
+ TCTPoint.SelectEntry(FFieldOps, FTable, LN, Int32(LSecretIndex), LSel);
+ // R := 2*R + Sel
+ TCTPoint.PointDouble(LR, LR);
+ TCTPoint.PointAdd(LR, LSel, LR);
+ end;
+
+ // R := R + Offset
+ TCTPoint.PointAdd(LR, FOffset, LR);
+
+ LXa := TNat.Create(FFieldOps.GetFieldInts);
+ LYa := TNat.Create(FFieldOps.GetFieldInts);
+ TCTPoint.ToAffine(FFieldOps, LR, LXa, LYa, LIsInfinity);
+ if LIsInfinity then
+ Exit(AP.Curve.Infinity);
+
+ Result := AP.Curve.CreateRawPoint(FFieldOps.CreateFieldElement(LXa),
+ FFieldOps.CreateFieldElement(LYa));
+end;
+
+end.
diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpHomogeneousPoint.pas b/CryptoLib/src/Math/EC/Multiplier/ClpHomogeneousPoint.pas
deleted file mode 100644
index d8c83913..00000000
--- a/CryptoLib/src/Math/EC/Multiplier/ClpHomogeneousPoint.pas
+++ /dev/null
@@ -1,232 +0,0 @@
-{ *********************************************************************************** }
-{ * 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/Raw/ClpNat.pas b/CryptoLib/src/Math/Raw/ClpNat.pas
index cb2a9d90..78802e24 100644
--- a/CryptoLib/src/Math/Raw/ClpNat.pas
+++ b/CryptoLib/src/Math/Raw/ClpNat.pas
@@ -40,10 +40,12 @@ TNat = class sealed
private
const M: UInt64 = UInt64($FFFFFFFF);
public
- class function Add(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32; static;
+ class function Add(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32; overload; static;
+ class function Add(ALen: Int32; AX, AY, AZ: PUInt32): UInt32; overload; static;
class function Add33At(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array; AZPos: Int32): UInt32; overload; static;
class function Add33At(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array; AZOff: Int32; AZPos: Int32): UInt32; overload; static;
class function Add33To(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array): UInt32; overload; static;
+ class function Add33To(ALen: Int32; AX: UInt32; AZ: PUInt32): UInt32; overload; static;
class function Add33To(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; overload; static;
class function AddBothTo(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32; overload; static;
class function AddBothTo(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; overload; static;
@@ -52,6 +54,7 @@ TNat = class sealed
class function AddDWordTo(ALen: Int32; AX: UInt64; AZ: TCryptoLibUInt32Array): UInt32; overload; static;
class function AddDWordTo(ALen: Int32; AX: UInt64; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; overload; static;
class function AddTo(ALen: Int32; const AX: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32; overload; static;
+ class function AddTo(ALen: Int32; AX, AZ: PUInt32): UInt32; overload; static;
class function AddTo(ALen: Int32; const AX: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array; ACIn: UInt32): UInt32; overload; static;
class function AddTo(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; overload; static;
class function AddTo(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32; ACIn: UInt32): UInt32; overload; static;
@@ -79,10 +82,13 @@ TNat = class sealed
class function CSub(ALen: Int32; AMask: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): Int32; overload; static;
class function CSub(ALen: Int32; AMask: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): Int32; overload; static;
class function Dec(ALen: Int32; const AZ: TCryptoLibUInt32Array): Int32; overload; static;
+ class function Dec(ALen: Int32; AZ: PUInt32): Int32; overload; static;
class function Dec(ALen: Int32; const AX: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): Int32; overload; static;
class function DecAt(ALen: Int32; const AZ: TCryptoLibUInt32Array; AZPos: Int32): Int32; overload; static;
+ class function DecAt(ALen: Int32; AZ: PUInt32; AZPos: Int32): Int32; overload; static;
class function DecAt(ALen: Int32; const AZ: TCryptoLibUInt32Array; AZOff: Int32; AZPos: Int32): Int32; overload; static;
- class function Eq(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array): Boolean; static;
+ class function Eq(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array): Boolean; overload; static;
+ class function Eq(ALen: Int32; AX, AY: PUInt32): Boolean; overload; static;
class function CZero(AX: UInt32): UInt32; static;
class function CZero64(AX: UInt64): UInt64; static;
class function EqualTo(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; AY: UInt32): UInt32; overload; static;
@@ -107,10 +113,13 @@ TNat = class sealed
class function GetBitLength64(ALen: Int32; const AX: TCryptoLibUInt64Array; AXOff: Int32): Int32; overload; static;
class function GetLengthForBits(ABits: Int32): Int32; static;
class function GetLengthForBits64(ABits: Int32): Int32; static;
- class function Gte(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array): Boolean; static;
+ class function Gte(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array): Boolean; overload; static;
+ class function Gte(ALen: Int32; AX, AY: PUInt32): Boolean; overload; static;
class function Inc(ALen: Int32; const AZ: TCryptoLibUInt32Array): UInt32; overload; static;
+ class function Inc(ALen: Int32; AZ: PUInt32): UInt32; overload; static;
class function Inc(ALen: Int32; const AX: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32; overload; static;
class function IncAt(ALen: Int32; const AZ: TCryptoLibUInt32Array; AZPos: Int32): UInt32; overload; static;
+ class function IncAt(ALen: Int32; AZ: PUInt32; AZPos: Int32): UInt32; overload; static;
class function IncAt(ALen: Int32; const AZ: TCryptoLibUInt32Array; AZOff: Int32; AZPos: Int32): UInt32; overload; static;
class function IsOne(ALen: Int32; const AX: TCryptoLibUInt32Array): Boolean; static;
class function IsZero(ALen: Int32; const AX: TCryptoLibUInt32Array): Boolean; static;
@@ -134,6 +143,7 @@ TNat = class sealed
class function ShiftDownBits(ALen: Int32; AZ: TCryptoLibUInt32Array; ABits: Int32; AC: UInt32): UInt32; overload; static;
class function ShiftDownBits(ALen: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32; ABits: Int32; AC: UInt32): UInt32; overload; static;
class function ShiftDownBits(ALen: Int32; const AX: TCryptoLibUInt32Array; ABits: Int32; AC: UInt32; AZ: TCryptoLibUInt32Array): UInt32; overload; static;
+ class function ShiftDownBits(ALen: Int32; AX: PUInt32; ABits: Int32; AC: UInt32; AZ: PUInt32): UInt32; overload; static;
class function ShiftDownBits(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; ABits: Int32; AC: UInt32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; overload; static;
class function ShiftDownBits64(ALen: Int32; AZ: TCryptoLibUInt64Array; AZOff: Int32; ABits: Int32; AC: UInt64): UInt64; static;
class function ShiftDownWord(ALen: Int32; AZ: TCryptoLibUInt32Array; AC: UInt32): UInt32; static;
@@ -160,10 +170,12 @@ TNat = class sealed
class function SquareWordAddTo(const AX: TCryptoLibUInt32Array; AXPos: Int32; AZ: TCryptoLibUInt32Array): UInt32; overload; static;
class function SquareWordAddTo(const AX: TCryptoLibUInt32Array; AXOff: Int32; AXPos: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; overload; static;
class function Sub(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): Int32; overload; static;
+ class function Sub(ALen: Int32; AX, AY, AZ: PUInt32): Int32; overload; static;
class function Sub(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): Int32; overload; static;
class function Sub33At(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array; AZPos: Int32): Int32; overload; static;
class function Sub33At(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array; AZOff: Int32; AZPos: Int32): Int32; overload; static;
class function Sub33From(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array): Int32; overload; static;
+ class function Sub33From(ALen: Int32; AX: UInt32; AZ: PUInt32): Int32; overload; static;
class function Sub33From(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array; AZOff: Int32): Int32; overload; static;
class function SubBothFrom(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): Int32; overload; static;
class function SubBothFrom(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): Int32; overload; static;
@@ -213,6 +225,11 @@ implementation
ClpNat512;
class function TNat.Add(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32;
+begin
+ Result := Add(ALen, PUInt32(@AX[0]), PUInt32(@AY[0]), PUInt32(@AZ[0]));
+end;
+
+class function TNat.Add(ALen: Int32; AX, AY, AZ: PUInt32): UInt32;
var
LC: UInt64;
LI: Int32;
@@ -274,43 +291,13 @@ class function TNat.Add33At(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array;
end;
class function TNat.Add33To(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array): UInt32;
-var
- LC: UInt64;
begin
- LC := UInt64(AZ[0]) + AX;
- AZ[0] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + UInt64(AZ[1]) + 1;
- AZ[1] := UInt32(LC);
- LC := LC shr 32;
- if (LC = 0) then
- begin
- Result := 0;
- end
- else
- begin
- Result := IncAt(ALen, AZ, 2);
- end;
+ Result := Add33To(ALen, AX, PUInt32(@AZ[0]));
end;
class function TNat.Add33To(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32;
-var
- LC: UInt64;
begin
- LC := UInt64(AZ[AZOff + 0]) + AX;
- AZ[AZOff + 0] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + UInt64(AZ[AZOff + 1]) + 1;
- AZ[AZOff + 1] := UInt32(LC);
- LC := LC shr 32;
- if (LC = 0) then
- begin
- Result := 0;
- end
- else
- begin
- Result := IncAt(ALen, AZ, AZOff, 2);
- end;
+ Result := Add33To(ALen, AX, PUInt32(@AZ[AZOff]));
end;
class function TNat.AddBothTo(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32;
@@ -430,6 +417,11 @@ class function TNat.AddDWordTo(ALen: Int32; AX: UInt64; AZ: TCryptoLibUInt32Arra
end;
class function TNat.AddTo(ALen: Int32; const AX: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32;
+begin
+ Result := AddTo(ALen, PUInt32(@AX[0]), PUInt32(@AZ[0]));
+end;
+
+class function TNat.AddTo(ALen: Int32; AX, AZ: PUInt32): UInt32;
var
LC: UInt64;
LI: Int32;
@@ -450,18 +442,8 @@ class function TNat.AddTo(ALen: Int32; const AX: TCryptoLibUInt32Array; AZ: TCry
end;
class function TNat.AddTo(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32;
-var
- LC: UInt64;
- LI: Int32;
begin
- LC := 0;
- for LI := 0 to ALen - 1 do
- begin
- LC := LC + UInt64(AX[AXOff + LI]) + AZ[AZOff + LI];
- AZ[AZOff + LI] := UInt32(LC);
- LC := LC shr 32;
- end;
- Result := UInt32(LC);
+ Result := AddTo(ALen, PUInt32(@AX[AXOff]), PUInt32(@AZ[AZOff]));
end;
class function TNat.AddTo(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32; ACIn: UInt32): UInt32;
@@ -784,6 +766,11 @@ class function TNat.CSub(ALen: Int32; AMask: Int32; const AX: TCryptoLibUInt32Ar
end;
class function TNat.Dec(ALen: Int32; const AZ: TCryptoLibUInt32Array): Int32;
+begin
+ Result := Dec(ALen, PUInt32(@AZ[0]));
+end;
+
+class function TNat.Dec(ALen: Int32; AZ: PUInt32): Int32;
var
LI: Int32;
begin
@@ -791,10 +778,7 @@ class function TNat.Dec(ALen: Int32; const AZ: TCryptoLibUInt32Array): Int32;
begin
System.Dec(AZ[LI]);
if AZ[LI] <> UInt32.MaxValue then
- begin
- Result := 0;
- Exit;
- end;
+ Exit(0);
end;
Result := -1;
end;
@@ -821,55 +805,33 @@ class function TNat.Dec(ALen: Int32; const AX: TCryptoLibUInt32Array; AZ: TCrypt
end;
class function TNat.DecAt(ALen: Int32; const AZ: TCryptoLibUInt32Array; AZPos: Int32): Int32;
-var
- LI: Int32;
begin
{$IFDEF DEBUG}
System.Assert(AZPos <= ALen);
{$ENDIF}
- for LI := AZPos to ALen - 1 do
- begin
- System.Dec(AZ[LI]);
- if AZ[LI] <> UInt32.MaxValue then
- begin
- Result := 0;
- Exit;
- end;
- end;
- Result := -1;
+ Result := DecAt(ALen, PUInt32(@AZ[0]), AZPos);
end;
class function TNat.DecAt(ALen: Int32; const AZ: TCryptoLibUInt32Array; AZOff: Int32; AZPos: Int32): Int32;
-var
- LI: Int32;
begin
{$IFDEF DEBUG}
System.Assert(AZPos <= ALen);
{$ENDIF}
- for LI := AZPos to ALen - 1 do
- begin
- System.Dec(AZ[AZOff + LI]);
- if AZ[AZOff + LI] <> UInt32.MaxValue then
- begin
- Result := 0;
- Exit;
- end;
- end;
- Result := -1;
+ Result := DecAt(ALen, PUInt32(@AZ[AZOff]), AZPos);
end;
class function TNat.Eq(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array): Boolean;
+begin
+ Result := Eq(ALen, PUInt32(@AX[0]), PUInt32(@AY[0]));
+end;
+
+class function TNat.Eq(ALen: Int32; AX, AY: PUInt32): Boolean;
var
LI: Int32;
begin
for LI := ALen - 1 downto 0 do
- begin
if AX[LI] <> AY[LI] then
- begin
- Result := False;
- Exit;
- end;
- end;
+ Exit(False);
Result := True;
end;
@@ -1171,6 +1133,11 @@ class function TNat.GetLengthForBits64(ABits: Int32): Int32;
end;
class function TNat.Gte(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array): Boolean;
+begin
+ Result := Gte(ALen, PUInt32(@AX[0]), PUInt32(@AY[0]));
+end;
+
+class function TNat.Gte(ALen: Int32; AX, AY: PUInt32): Boolean;
var
LI: Int32;
LXI: UInt32;
@@ -1195,6 +1162,11 @@ class function TNat.Gte(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY:
end;
class function TNat.Inc(ALen: Int32; const AZ: TCryptoLibUInt32Array): UInt32;
+begin
+ Result := Inc(ALen, PUInt32(@AZ[0]));
+end;
+
+class function TNat.Inc(ALen: Int32; AZ: PUInt32): UInt32;
var
LI: Int32;
begin
@@ -1202,10 +1174,7 @@ class function TNat.Inc(ALen: Int32; const AZ: TCryptoLibUInt32Array): UInt32;
begin
System.Inc(AZ[LI]);
if AZ[LI] <> UInt32(0) then
- begin
- Result := 0;
- Exit;
- end;
+ Exit(0);
end;
Result := 1;
end;
@@ -1232,41 +1201,19 @@ class function TNat.Inc(ALen: Int32; const AX: TCryptoLibUInt32Array; AZ: TCrypt
end;
class function TNat.IncAt(ALen: Int32; const AZ: TCryptoLibUInt32Array; AZPos: Int32): UInt32;
-var
- LI: Int32;
begin
{$IFDEF DEBUG}
System.Assert(AZPos <= ALen);
{$ENDIF}
- for LI := AZPos to ALen - 1 do
- begin
- System.Inc(AZ[LI]);
- if AZ[LI] <> UInt32(0) then
- begin
- Result := 0;
- Exit;
- end;
- end;
- Result := 1;
+ Result := IncAt(ALen, PUInt32(@AZ[0]), AZPos);
end;
class function TNat.IncAt(ALen: Int32; const AZ: TCryptoLibUInt32Array; AZOff: Int32; AZPos: Int32): UInt32;
-var
- LI: Int32;
begin
{$IFDEF DEBUG}
System.Assert(AZPos <= ALen);
{$ENDIF}
- for LI := AZPos to ALen - 1 do
- begin
- System.Inc(AZ[AZOff + LI]);
- if AZ[AZOff + LI] <> UInt32(0) then
- begin
- Result := 0;
- Exit;
- end;
- end;
- Result := 1;
+ Result := IncAt(ALen, PUInt32(@AZ[AZOff]), AZPos);
end;
class function TNat.IsOne(ALen: Int32; const AX: TCryptoLibUInt32Array): Boolean;
@@ -1616,6 +1563,11 @@ class function TNat.ShiftDownBits(ALen: Int32; AZ: TCryptoLibUInt32Array; AZOff:
end;
class function TNat.ShiftDownBits(ALen: Int32; const AX: TCryptoLibUInt32Array; ABits: Int32; AC: UInt32; AZ: TCryptoLibUInt32Array): UInt32;
+begin
+ Result := ShiftDownBits(ALen, PUInt32(@AX[0]), ABits, AC, PUInt32(@AZ[0]));
+end;
+
+class function TNat.ShiftDownBits(ALen: Int32; AX: PUInt32; ABits: Int32; AC: UInt32; AZ: PUInt32): UInt32;
var
LI: Int32;
LNext: UInt32;
@@ -1635,22 +1587,8 @@ class function TNat.ShiftDownBits(ALen: Int32; const AX: TCryptoLibUInt32Array;
end;
class function TNat.ShiftDownBits(ALen: Int32; const AX: TCryptoLibUInt32Array; AXOff: Int32; ABits: Int32; AC: UInt32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32;
-var
- LI: Int32;
- LNext: UInt32;
begin
- {$IFDEF DEBUG}
- System.Assert((ABits > 0) and (ABits < 32));
- {$ENDIF}
- LI := ALen;
- while LI > 0 do
- begin
- System.Dec(LI);
- LNext := AX[AXOff + LI];
- AZ[AZOff + LI] := ((LNext shr ABits)) or (TBitOperations.NegativeLeftShift32(AC, -ABits));
- AC := LNext;
- end;
- Result := TBitOperations.NegativeLeftShift32(AC, -ABits);
+ Result := ShiftDownBits(ALen, PUInt32(@AX[AXOff]), ABits, AC, PUInt32(@AZ[AZOff]));
end;
class function TNat.ShiftDownBits64(ALen: Int32; AZ: TCryptoLibUInt64Array; AZOff: Int32; ABits: Int32; AC: UInt64): UInt64;
@@ -2381,6 +2319,11 @@ class function TNat.SquareWordAddTo(const AX: TCryptoLibUInt32Array; AXOff: Int3
end;
class function TNat.Sub(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): Int32;
+begin
+ Result := Sub(ALen, PUInt32(@AX[0]), PUInt32(@AY[0]), PUInt32(@AZ[0]));
+end;
+
+class function TNat.Sub(ALen: Int32; AX, AY, AZ: PUInt32): Int32;
var
LC: Int64;
LI: Int32;
@@ -2457,43 +2400,13 @@ class function TNat.Sub33At(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array;
end;
class function TNat.Sub33From(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array): Int32;
-var
- LC: Int64;
begin
- LC := Int64(AZ[0]) - Int64(AX);
- AZ[0] := UInt32(LC);
- LC := TBitOperations.Asr64(LC, 32);
- LC := LC + (Int64(AZ[1]) - 1);
- AZ[1] := UInt32(LC);
- LC := TBitOperations.Asr64(LC, 32);
- if (LC = 0) then
- begin
- Result := 0;
- end
- else
- begin
- Result := DecAt(ALen, AZ, 2);
- end;
+ Result := Sub33From(ALen, AX, PUInt32(@AZ[0]));
end;
class function TNat.Sub33From(ALen: Int32; AX: UInt32; AZ: TCryptoLibUInt32Array; AZOff: Int32): Int32;
-var
- LC: Int64;
begin
- LC := Int64(AZ[AZOff + 0]) - Int64(AX);
- AZ[AZOff + 0] := UInt32(LC);
- LC := TBitOperations.Asr64(LC, 32);
- LC := LC + (Int64(AZ[AZOff + 1]) - 1);
- AZ[AZOff + 1] := UInt32(LC);
- LC := TBitOperations.Asr64(LC, 32);
- if (LC = 0) then
- begin
- Result := 0;
- end
- else
- begin
- Result := DecAt(ALen, AZ, AZOff, 2);
- end;
+ Result := Sub33From(ALen, AX, PUInt32(@AZ[AZOff]));
end;
class function TNat.SubBothFrom(ALen: Int32; const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): Int32;
@@ -3293,4 +3206,69 @@ class procedure TNat.Zero64(ALen: Int32; AZ: TCryptoLibUInt64Array; AZOff: Int32
end;
end;
+
+class function TNat.IncAt(ALen: Int32; AZ: PUInt32; AZPos: Int32): UInt32;
+var
+ LI: Int32;
+begin
+ for LI := AZPos to ALen - 1 do
+ begin
+ System.Inc(AZ[LI]);
+ if AZ[LI] <> UInt32(0) then
+ begin
+ Result := 0;
+ Exit;
+ end;
+ end;
+ Result := 1;
+end;
+
+class function TNat.DecAt(ALen: Int32; AZ: PUInt32; AZPos: Int32): Int32;
+var
+ LI: Int32;
+begin
+ for LI := AZPos to ALen - 1 do
+ begin
+ System.Dec(AZ[LI]);
+ if AZ[LI] <> UInt32.MaxValue then
+ begin
+ Result := 0;
+ Exit;
+ end;
+ end;
+ Result := -1;
+end;
+
+class function TNat.Add33To(ALen: Int32; AX: UInt32; AZ: PUInt32): UInt32;
+var
+ LC: UInt64;
+begin
+ LC := UInt64(AZ[0]) + AX;
+ AZ[0] := UInt32(LC);
+ LC := LC shr 32;
+ LC := LC + UInt64(AZ[1]) + 1;
+ AZ[1] := UInt32(LC);
+ LC := LC shr 32;
+ if (LC = 0) then
+ Result := 0
+ else
+ Result := IncAt(ALen, AZ, 2);
+end;
+
+class function TNat.Sub33From(ALen: Int32; AX: UInt32; AZ: PUInt32): Int32;
+var
+ LC: Int64;
+begin
+ LC := Int64(AZ[0]) - Int64(AX);
+ AZ[0] := UInt32(LC);
+ LC := TBitOperations.Asr64(LC, 32);
+ LC := LC + (Int64(AZ[1]) - 1);
+ AZ[1] := UInt32(LC);
+ LC := TBitOperations.Asr64(LC, 32);
+ if (LC = 0) then
+ Result := 0
+ else
+ Result := DecAt(ALen, AZ, 2);
+end;
+
end.
diff --git a/CryptoLib/src/Math/Raw/ClpNat256.pas b/CryptoLib/src/Math/Raw/ClpNat256.pas
index 32266667..621a709b 100644
--- a/CryptoLib/src/Math/Raw/ClpNat256.pas
+++ b/CryptoLib/src/Math/Raw/ClpNat256.pas
@@ -36,6 +36,7 @@ TNat256 = class sealed
M: UInt64 = $FFFFFFFF;
public
class function Add(const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32; overload; static;
+ class function Add(AX, AY, AZ: PUInt32): UInt32; overload; static;
class function Add(const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; overload; static;
class function AddBothTo(const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32; overload; static;
class function AddBothTo(const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; overload; static;
@@ -55,6 +56,7 @@ TNat256 = class sealed
class function Eq64(const AX: TCryptoLibUInt64Array; const AY: TCryptoLibUInt64Array): Boolean; static;
class function GetBit(const AX: TCryptoLibUInt32Array; ABit: Int32): UInt32; static;
class function Gte(const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array): Boolean; overload; static;
+ class function Gte(AX, AY: PUInt32): Boolean; overload; static;
class function Gte(const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32): Boolean; overload; static;
class function IsOne(const AX: TCryptoLibUInt32Array): Boolean; static;
class function IsOne64(const AX: TCryptoLibUInt64Array): Boolean; static;
@@ -64,17 +66,21 @@ TNat256 = class sealed
class procedure Mul128(const AX: TCryptoLibUInt32Array; const AY128: TCryptoLibUInt32Array; AZz: TCryptoLibUInt32Array); static;
class function MulAddTo(const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZz: TCryptoLibUInt32Array): UInt32; overload; static;
class function MulAddTo(const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZz: TCryptoLibUInt32Array; AZzOff: Int32): UInt32; overload; static;
- class function Mul33Add(AW: UInt32; const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt64; static;
+ class function Mul33Add(AW: UInt32; const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt64; overload; static;
+ class function Mul33Add(AW: UInt32; AX, AY, AZ: PUInt32): UInt64; overload; static;
class function MulByWord(AX: UInt32; AZ: TCryptoLibUInt32Array): UInt32; static;
class function MulByWordAddTo(AX: UInt32; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32; static;
class function MulWordAddTo(AX: UInt32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; static;
- class function Mul33DWordAdd(AX: UInt32; AY: UInt64; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; static;
- class function Mul33WordAdd(AX: UInt32; AY: UInt32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; static;
+ class function Mul33DWordAdd(AX: UInt32; AY: UInt64; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; overload; static;
+ class function Mul33DWordAdd(AX: UInt32; AY: UInt64; AZ: PUInt32): UInt32; overload; static;
+ class function Mul33WordAdd(AX: UInt32; AY: UInt32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; overload; static;
+ class function Mul33WordAdd(AX: UInt32; AY: UInt32; AZ: PUInt32): UInt32; overload; static;
class function MulWordDwordAdd(AX: UInt32; AY: UInt64; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; static;
class function MulWord(AX: UInt32; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32; static;
class procedure Square(const AX: TCryptoLibUInt32Array; AZz: TCryptoLibUInt32Array); overload; static;
class procedure Square(const AX: TCryptoLibUInt32Array; AXOff: Int32; AZz: TCryptoLibUInt32Array; AZzOff: Int32); overload; static;
class function Sub(const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): Int32; overload; static;
+ class function Sub(AX, AY, AZ: PUInt32): Int32; overload; static;
class function Sub(const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): Int32; overload; static;
class function SubBothFrom(const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): Int32; static;
class function SubFrom(const AX: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array; ACIn: Int32): Int32; overload; static;
@@ -110,6 +116,11 @@ TNat256 = class sealed
implementation
class function TNat256.Add(const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32;
+begin
+ Result := Add(PUInt32(@AX[0]), PUInt32(@AY[0]), PUInt32(@AZ[0]));
+end;
+
+class function TNat256.Add(AX, AY, AZ: PUInt32): UInt32;
var
LC: UInt64;
begin
@@ -142,35 +153,8 @@ class function TNat256.Add(const AX: TCryptoLibUInt32Array; const AY: TCryptoLib
end;
class function TNat256.Add(const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32;
-var
- LC: UInt64;
begin
- LC := 0;
- LC := LC + (UInt64(AX[AXOff + 0]) + AY[AYOff + 0]);
- AZ[AZOff + 0] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + (UInt64(AX[AXOff + 1]) + AY[AYOff + 1]);
- AZ[AZOff + 1] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + (UInt64(AX[AXOff + 2]) + AY[AYOff + 2]);
- AZ[AZOff + 2] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + (UInt64(AX[AXOff + 3]) + AY[AYOff + 3]);
- AZ[AZOff + 3] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + (UInt64(AX[AXOff + 4]) + AY[AYOff + 4]);
- AZ[AZOff + 4] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + (UInt64(AX[AXOff + 5]) + AY[AYOff + 5]);
- AZ[AZOff + 5] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + (UInt64(AX[AXOff + 6]) + AY[AYOff + 6]);
- AZ[AZOff + 6] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + (UInt64(AX[AXOff + 7]) + AY[AYOff + 7]);
- AZ[AZOff + 7] := UInt32(LC);
- LC := LC shr 32;
- Result := UInt32(LC);
+ Result := Add(PUInt32(@AX[AXOff]), PUInt32(@AY[AYOff]), PUInt32(@AZ[AZOff]));
end;
class function TNat256.AddBothTo(const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): UInt32;
@@ -456,6 +440,11 @@ class function TNat256.GetBit(const AX: TCryptoLibUInt32Array; ABit: Int32): UIn
end;
class function TNat256.Gte(const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array): Boolean;
+begin
+ Result := Gte(PUInt32(@AX[0]), PUInt32(@AY[0]));
+end;
+
+class function TNat256.Gte(AX, AY: PUInt32): Boolean;
var
LX_i: UInt32;
LY_i: UInt32;
@@ -480,27 +469,8 @@ class function TNat256.Gte(const AX: TCryptoLibUInt32Array; const AY: TCryptoLib
end;
class function TNat256.Gte(const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32): Boolean;
-var
- LX_i: UInt32;
- LY_i: UInt32;
- LI: Int32;
begin
- for LI := 7 downto 0 do
- begin
- LX_i := AX[AXOff + LI];
- LY_i := AY[AYOff + LI];
- if LX_i < LY_i then
- begin
- Result := False;
- Exit;
- end;
- if LX_i > LY_i then
- begin
- Result := True;
- Exit;
- end;
- end;
- Result := True;
+ Result := Gte(PUInt32(@AX[AXOff]), PUInt32(@AY[AYOff]));
end;
class function TNat256.IsOne(const AX: TCryptoLibUInt32Array): Boolean;
@@ -931,55 +901,8 @@ class function TNat256.MulAddTo(const AX: TCryptoLibUInt32Array; AXOff: Int32; c
end;
class function TNat256.Mul33Add(AW: UInt32; const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt64;
-var
- LC: UInt64;
- LWVal: UInt64;
- LX0: UInt64;
- LX1: UInt64;
- LX2: UInt64;
- LX3: UInt64;
- LX4: UInt64;
- LX5: UInt64;
- LX6: UInt64;
- LX7: UInt64;
begin
- System.Assert(AW shr 31 = 0);
- LC := 0;
- LWVal := AW;
- LX0 := AX[AXOff + 0];
- LC := LC + (LWVal * LX0 + AY[AYOff + 0]);
- AZ[AZOff + 0] := UInt32(LC);
- LC := LC shr 32;
- LX1 := AX[AXOff + 1];
- LC := LC + (LWVal * LX1 + LX0 + AY[AYOff + 1]);
- AZ[AZOff + 1] := UInt32(LC);
- LC := LC shr 32;
- LX2 := AX[AXOff + 2];
- LC := LC + (LWVal * LX2 + LX1 + AY[AYOff + 2]);
- AZ[AZOff + 2] := UInt32(LC);
- LC := LC shr 32;
- LX3 := AX[AXOff + 3];
- LC := LC + (LWVal * LX3 + LX2 + AY[AYOff + 3]);
- AZ[AZOff + 3] := UInt32(LC);
- LC := LC shr 32;
- LX4 := AX[AXOff + 4];
- LC := LC + (LWVal * LX4 + LX3 + AY[AYOff + 4]);
- AZ[AZOff + 4] := UInt32(LC);
- LC := LC shr 32;
- LX5 := AX[AXOff + 5];
- LC := LC + (LWVal * LX5 + LX4 + AY[AYOff + 5]);
- AZ[AZOff + 5] := UInt32(LC);
- LC := LC shr 32;
- LX6 := AX[AXOff + 6];
- LC := LC + (LWVal * LX6 + LX5 + AY[AYOff + 6]);
- AZ[AZOff + 6] := UInt32(LC);
- LC := LC shr 32;
- LX7 := AX[AXOff + 7];
- LC := LC + (LWVal * LX7 + LX6 + AY[AYOff + 7]);
- AZ[AZOff + 7] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + (LX7);
- Result := LC;
+ Result := Mul33Add(AW, PUInt32(@AX[AXOff]), PUInt32(@AY[AYOff]), PUInt32(@AZ[AZOff]));
end;
class function TNat256.MulByWord(AX: UInt32; AZ: TCryptoLibUInt32Array): UInt32;
@@ -1085,70 +1008,21 @@ class function TNat256.MulWordAddTo(AX: UInt32; const AY: TCryptoLibUInt32Array;
end;
class function TNat256.Mul33DWordAdd(AX: UInt32; AY: UInt64; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32;
-var
- LC: UInt64;
- LXVal: UInt64;
- LY00: UInt64;
- LY01: UInt64;
begin
{$IFDEF DEBUG}
System.Assert(AX shr 31 = 0);
System.Assert(AZOff <= 4);
{$ENDIF}
- LC := 0;
- LXVal := AX;
- LY00 := AY and M;
- LC := LC + (LXVal * LY00 + AZ[AZOff + 0]);
- AZ[AZOff + 0] := UInt32(LC);
- LC := LC shr 32;
- LY01 := AY shr 32;
- LC := LC + (LXVal * LY01 + LY00 + AZ[AZOff + 1]);
- AZ[AZOff + 1] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + (LY01 + AZ[AZOff + 2]);
- AZ[AZOff + 2] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + (AZ[AZOff + 3]);
- AZ[AZOff + 3] := UInt32(LC);
- LC := LC shr 32;
- if (LC = 0) then
- begin
- Result := 0;
- end
- else
- begin
- Result := TNat.IncAt(8, AZ, AZOff, 4);
- end;
+ Result := Mul33DWordAdd(AX, AY, PUInt32(@AZ[AZOff]));
end;
class function TNat256.Mul33WordAdd(AX: UInt32; AY: UInt32; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32;
-var
- LC: UInt64;
- LYVal: UInt64;
begin
{$IFDEF DEBUG}
System.Assert(AX shr 31 = 0);
System.Assert(AZOff <= 5);
{$ENDIF}
- LC := 0;
- LYVal := AY;
- LC := LC + (LYVal * AX + AZ[AZOff + 0]);
- AZ[AZOff + 0] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + (LYVal + AZ[AZOff + 1]);
- AZ[AZOff + 1] := UInt32(LC);
- LC := LC shr 32;
- LC := LC + (AZ[AZOff + 2]);
- AZ[AZOff + 2] := UInt32(LC);
- LC := LC shr 32;
- if (LC = 0) then
- begin
- Result := 0;
- end
- else
- begin
- Result := TNat.IncAt(8, AZ, AZOff, 3);
- end;
+ Result := Mul33WordAdd(AX, AY, PUInt32(@AZ[AZOff]));
end;
class function TNat256.MulWordDwordAdd(AX: UInt32; AY: UInt64; AZ: TCryptoLibUInt32Array; AZOff: Int32): UInt32;
@@ -1561,6 +1435,11 @@ class procedure TNat256.Square(const AX: TCryptoLibUInt32Array; AXOff: Int32; AZ
end;
class function TNat256.Sub(const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): Int32;
+begin
+ Result := Sub(PUInt32(@AX[0]), PUInt32(@AY[0]), PUInt32(@AZ[0]));
+end;
+
+class function TNat256.Sub(AX, AY, AZ: PUInt32): Int32;
var
LC: Int64;
begin
@@ -1593,35 +1472,8 @@ class function TNat256.Sub(const AX: TCryptoLibUInt32Array; const AY: TCryptoLib
end;
class function TNat256.Sub(const AX: TCryptoLibUInt32Array; AXOff: Int32; const AY: TCryptoLibUInt32Array; AYOff: Int32; AZ: TCryptoLibUInt32Array; AZOff: Int32): Int32;
-var
- LC: Int64;
begin
- LC := 0;
- LC := LC + (Int64(AX[AXOff + 0]) - Int64(AY[AYOff + 0]));
- AZ[AZOff + 0] := UInt32(LC);
- LC := TBitOperations.Asr64(LC, 32);
- LC := LC + (Int64(AX[AXOff + 1]) - Int64(AY[AYOff + 1]));
- AZ[AZOff + 1] := UInt32(LC);
- LC := TBitOperations.Asr64(LC, 32);
- LC := LC + (Int64(AX[AXOff + 2]) - Int64(AY[AYOff + 2]));
- AZ[AZOff + 2] := UInt32(LC);
- LC := TBitOperations.Asr64(LC, 32);
- LC := LC + (Int64(AX[AXOff + 3]) - Int64(AY[AYOff + 3]));
- AZ[AZOff + 3] := UInt32(LC);
- LC := TBitOperations.Asr64(LC, 32);
- LC := LC + (Int64(AX[AXOff + 4]) - Int64(AY[AYOff + 4]));
- AZ[AZOff + 4] := UInt32(LC);
- LC := TBitOperations.Asr64(LC, 32);
- LC := LC + (Int64(AX[AXOff + 5]) - Int64(AY[AYOff + 5]));
- AZ[AZOff + 5] := UInt32(LC);
- LC := TBitOperations.Asr64(LC, 32);
- LC := LC + (Int64(AX[AXOff + 6]) - Int64(AY[AYOff + 6]));
- AZ[AZOff + 6] := UInt32(LC);
- LC := TBitOperations.Asr64(LC, 32);
- LC := LC + (Int64(AX[AXOff + 7]) - Int64(AY[AYOff + 7]));
- AZ[AZOff + 7] := UInt32(LC);
- LC := TBitOperations.Asr64(LC, 32);
- Result := Int32(LC);
+ Result := Sub(PUInt32(@AX[AXOff]), PUInt32(@AY[AYOff]), PUInt32(@AZ[AZOff]));
end;
class function TNat256.SubBothFrom(const AX: TCryptoLibUInt32Array; const AY: TCryptoLibUInt32Array; AZ: TCryptoLibUInt32Array): Int32;
@@ -1961,4 +1813,58 @@ class procedure TNat256.Zero64(AZ: TCryptoLibUInt64Array; AZOff: Int32);
TArrayUtilities.Fill(AZ, AZOff, AZOff + 4, UInt64(0));
end;
+
+class function TNat256.Mul33Add(AW: UInt32; AX, AY, AZ: PUInt32): UInt64;
+var
+ LC, LWVal, LX0, LX1, LX2, LX3, LX4, LX5, LX6, LX7: UInt64;
+begin
+ System.Assert(AW shr 31 = 0);
+ LC := 0;
+ LWVal := AW;
+ LX0 := AX[0]; LC := LC + (LWVal * LX0 + AY[0]); AZ[0] := UInt32(LC); LC := LC shr 32;
+ LX1 := AX[1]; LC := LC + (LWVal * LX1 + LX0 + AY[1]); AZ[1] := UInt32(LC); LC := LC shr 32;
+ LX2 := AX[2]; LC := LC + (LWVal * LX2 + LX1 + AY[2]); AZ[2] := UInt32(LC); LC := LC shr 32;
+ LX3 := AX[3]; LC := LC + (LWVal * LX3 + LX2 + AY[3]); AZ[3] := UInt32(LC); LC := LC shr 32;
+ LX4 := AX[4]; LC := LC + (LWVal * LX4 + LX3 + AY[4]); AZ[4] := UInt32(LC); LC := LC shr 32;
+ LX5 := AX[5]; LC := LC + (LWVal * LX5 + LX4 + AY[5]); AZ[5] := UInt32(LC); LC := LC shr 32;
+ LX6 := AX[6]; LC := LC + (LWVal * LX6 + LX5 + AY[6]); AZ[6] := UInt32(LC); LC := LC shr 32;
+ LX7 := AX[7]; LC := LC + (LWVal * LX7 + LX6 + AY[7]); AZ[7] := UInt32(LC); LC := LC shr 32;
+ LC := LC + LX7;
+ Result := LC;
+end;
+
+class function TNat256.Mul33DWordAdd(AX: UInt32; AY: UInt64; AZ: PUInt32): UInt32;
+var
+ LC, LXVal, LY00, LY01: UInt64;
+begin
+ LC := 0;
+ LXVal := AX;
+ LY00 := AY and M;
+ LC := LC + (LXVal * LY00 + AZ[0]); AZ[0] := UInt32(LC); LC := LC shr 32;
+ LY01 := AY shr 32;
+ LC := LC + (LXVal * LY01 + LY00 + AZ[1]); AZ[1] := UInt32(LC); LC := LC shr 32;
+ LC := LC + (LY01 + AZ[2]); AZ[2] := UInt32(LC); LC := LC shr 32;
+ LC := LC + (AZ[3]); AZ[3] := UInt32(LC); LC := LC shr 32;
+ if (LC = 0) then
+ Result := 0
+ else
+ Result := TNat.IncAt(8, AZ, 4);
+end;
+
+
+class function TNat256.Mul33WordAdd(AX: UInt32; AY: UInt32; AZ: PUInt32): UInt32;
+var
+ LC, LYVal: UInt64;
+begin
+ LC := 0;
+ LYVal := AY;
+ LC := LC + (LYVal * AX + AZ[0]); AZ[0] := UInt32(LC); LC := LC shr 32;
+ LC := LC + (LYVal + AZ[1]); AZ[1] := UInt32(LC); LC := LC shr 32;
+ LC := LC + (AZ[2]); AZ[2] := UInt32(LC); LC := LC shr 32;
+ if (LC = 0) then
+ Result := 0
+ else
+ Result := TNat.IncAt(8, AZ, 3);
+end;
+
end.
diff --git a/CryptoLib/src/Misc/ClpX86SimdFeatures.pas b/CryptoLib/src/Misc/ClpX86SimdFeatures.pas
index 91858cc5..b438f052 100644
--- a/CryptoLib/src/Misc/ClpX86SimdFeatures.pas
+++ b/CryptoLib/src/Misc/ClpX86SimdFeatures.pas
@@ -32,6 +32,8 @@ TX86SimdFeatures = class sealed
FHasPCLMULQDQ: Boolean;
FHasVPCLMULQDQ: Boolean;
FHasAESNI: Boolean;
+ FHasBMI2: Boolean;
+ FHasADX: Boolean;
strict private
class function CPUHasSSE2(): Boolean; static;
@@ -44,6 +46,8 @@ TX86SimdFeatures = class sealed
class function CPUHasPCLMULQDQ(): Boolean; static;
class function CPUHasVPCLMULQDQ(): Boolean; static;
class function CPUHasAESNI(): Boolean; static;
+ class function CPUHasBMI2(): Boolean; static;
+ class function CPUHasADX(): Boolean; static;
// Clears all the "extra" CPU feature flags (SHA-NI, PCLMULQDQ,
// VPCLMULQDQ, AES-NI). Used by ApplyBuildOverrides to give every
@@ -66,6 +70,8 @@ TX86SimdFeatures = class sealed
class function HasPCLMULQDQ(): Boolean; static;
class function HasVPCLMULQDQ(): Boolean; static;
class function HasAESNI(): Boolean; static;
+ class function HasBMI2(): Boolean; static;
+ class function HasADX(): Boolean; static;
// Picks the highest declared tier in ATiers that is <= the cached
// FActiveSimdLevel. Falls back to TX86SimdLevel.Scalar when no tier
@@ -264,12 +270,44 @@ class function TX86SimdFeatures.CPUHasAESNI(): Boolean;
{$ENDIF}
end;
+class function TX86SimdFeatures.CPUHasBMI2(): Boolean;
+{$IFDEF CRYPTOLIB_X86_SIMD}
+var
+ LCpuId: TCpuIdResult;
+{$ENDIF}
+begin
+{$IFDEF CRYPTOLIB_X86_SIMD}
+ CpuIdQuery(7, 0, LCpuId);
+ // BMI2 (MULX): leaf 7 subleaf 0, EBX bit 8
+ Result := (LCpuId.RegEBX and (1 shl 8)) <> 0;
+{$ELSE}
+ Result := False;
+{$ENDIF}
+end;
+
+class function TX86SimdFeatures.CPUHasADX(): Boolean;
+{$IFDEF CRYPTOLIB_X86_SIMD}
+var
+ LCpuId: TCpuIdResult;
+{$ENDIF}
+begin
+{$IFDEF CRYPTOLIB_X86_SIMD}
+ CpuIdQuery(7, 0, LCpuId);
+ // ADX (ADCX/ADOX): leaf 7 subleaf 0, EBX bit 19
+ Result := (LCpuId.RegEBX and (1 shl 19)) <> 0;
+{$ELSE}
+ Result := False;
+{$ENDIF}
+end;
+
class procedure TX86SimdFeatures.DisableAllExtraFeatures();
begin
FHasSHANI := False;
FHasPCLMULQDQ := False;
FHasVPCLMULQDQ := False;
FHasAESNI := False;
+ FHasBMI2 := False;
+ FHasADX := False;
end;
class procedure TX86SimdFeatures.ProbeHardwareAndCache();
@@ -305,6 +343,8 @@ class procedure TX86SimdFeatures.ProbeHardwareAndCache();
FHasSHANI := CPUHasSHANI();
FHasPCLMULQDQ := CPUHasPCLMULQDQ();
FHasVPCLMULQDQ := CPUHasVPCLMULQDQ() and LHasAVX2; // VPCLMULQDQ needs AVX/AVX2 lanes
+ FHasBMI2 := CPUHasBMI2();
+ FHasADX := CPUHasADX();
end;
class procedure TX86SimdFeatures.ApplyBuildOverrides();
@@ -390,6 +430,16 @@ class function TX86SimdFeatures.HasAESNI(): Boolean;
Result := FHasAESNI;
end;
+class function TX86SimdFeatures.HasBMI2(): Boolean;
+begin
+ Result := FHasBMI2;
+end;
+
+class function TX86SimdFeatures.HasADX(): Boolean;
+begin
+ Result := FHasADX;
+end;
+
class function TX86SimdFeatures.SelectSlot(const ATiers
: array of TX86SimdLevel): TX86SimdLevel;
begin
diff --git a/CryptoLib/src/Packages/Delphi/CryptoLib4PascalPackage.dpk b/CryptoLib/src/Packages/Delphi/CryptoLib4PascalPackage.dpk
index 336e5545..d4e427d8 100644
--- a/CryptoLib/src/Packages/Delphi/CryptoLib4PascalPackage.dpk
+++ b/CryptoLib/src/Packages/Delphi/CryptoLib4PascalPackage.dpk
@@ -218,6 +218,9 @@ contains
ClpChaChaSimd in '..\..\Simd\Facade\ClpChaChaSimd.pas',
ClpChaChaX86Backend in '..\..\Simd\Backend\ClpChaChaX86Backend.pas',
ClpChaChaArmBackend in '..\..\Simd\Backend\ClpChaChaArmBackend.pas',
+ ClpFpKernelSimd in '..\..\Simd\Facade\ClpFpKernelSimd.pas',
+ ClpFpKernelX86Backend in '..\..\Simd\Backend\ClpFpKernelX86Backend.pas',
+ ClpFpKernelArmBackend in '..\..\Simd\Backend\ClpFpKernelArmBackend.pas',
ClpSalsaSimd in '..\..\Simd\Facade\ClpSalsaSimd.pas',
ClpSalsaX86Backend in '..\..\Simd\Backend\ClpSalsaX86Backend.pas',
ClpSalsaArmBackend in '..\..\Simd\Backend\ClpSalsaArmBackend.pas',
@@ -401,8 +404,11 @@ contains
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',
+ ClpCTFieldValue in '..\..\Math\EC\Multiplier\ClpCTFieldValue.pas',
+ ClpCTFieldArith in '..\..\Math\EC\Multiplier\ClpCTFieldArith.pas',
+ ClpCTPoint in '..\..\Math\EC\Multiplier\ClpCTPoint.pas',
+ ClpFpCTMultiplier in '..\..\Math\EC\Multiplier\ClpFpCTMultiplier.pas',
+ ClpFpCombMultiplier in '..\..\Math\EC\Multiplier\ClpFpCombMultiplier.pas',
ClpIF2mFieldOps in '..\..\Interfaces\Math\EC\Multiplier\ClpIF2mFieldOps.pas',
ClpLopezDahabLadder in '..\..\Math\EC\Multiplier\ClpLopezDahabLadder.pas',
ClpF2mMontgomeryLadderCTMultiplier in '..\..\Math\EC\Multiplier\ClpF2mMontgomeryLadderCTMultiplier.pas',
diff --git a/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.lpk b/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.lpk
index 6ebe0219..a432b061 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 "/>
-
+
@@ -3466,41 +3466,65 @@ 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 fd55dd81..ab0d3c54 100644
--- a/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.pas
+++ b/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.pas
@@ -279,10 +279,12 @@ interface
ClpPkixCrlRevocationChecker, ClpPkixRevocationChecker,
ClpPkixAttrCertChecker, ClpRfc3281CertPathUtilities,
ClpPkixAttrCertPathValidator, ClpPkixAttrCertPathBuilder,
- ClpCryptoLibExceptions, ClpAesBitSlicedEngine, ClpHomogeneousPoint,
- ClpFixedWindowCTMultiplier, ClpIFpFieldOps, ClpIF2mFieldOps,
- ClpLopezDahabLadder, ClpGaloisFieldUtilities, ClpAbstractBlockCipherMode,
- ClpAbstractAeadCipher, ClpAbstractAeadBlockCipher;
+ ClpCryptoLibExceptions, ClpAesBitSlicedEngine, ClpIFpFieldOps,
+ ClpIF2mFieldOps, ClpLopezDahabLadder, ClpGaloisFieldUtilities,
+ ClpAbstractBlockCipherMode, ClpAbstractAeadCipher,
+ ClpAbstractAeadBlockCipher, ClpFpKernelX86Backend, ClpFpKernelSimd,
+ ClpFpKernelArmBackend, ClpCTFieldValue, ClpCTFieldArith, ClpCTPoint,
+ ClpFpCTMultiplier, ClpFpCombMultiplier;
implementation
diff --git a/CryptoLib/src/Pkix/ClpPkixPolicyNode.pas b/CryptoLib/src/Pkix/ClpPkixPolicyNode.pas
index 724fafc4..379fe9b4 100644
--- a/CryptoLib/src/Pkix/ClpPkixPolicyNode.pas
+++ b/CryptoLib/src/Pkix/ClpPkixPolicyNode.pas
@@ -93,7 +93,7 @@ constructor TPkixPolicyNode.Create(const AChildren: TCryptoLibGenericArray * }
+{ * * }
+{ * 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 ClpFpKernelArmBackend;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+{$IFDEF CRYPTOLIB_AARCH64_ASM}
+ ClpCpuFeatures,
+ ClpSimdLevels,
+{$ENDIF}
+ ClpCryptoLibTypes;
+
+type
+ ///
+ /// AArch64 big-integer kernel backend for prime-field (Fp) multiplication/
+ /// square. A leaf: capability probe plus the hot schoolbook multiply/square.
+ /// The arch-neutral dispatch and the scalar fallback live in
+ /// TFpKernelSimd / the field units. Hot paths are in
+ /// Include/Simd/FpKernel/.
+ ///
+ TFpKernelArmBackend = class sealed
+ public
+ class function IsSupported: Boolean; static;
+ /// Z[0..2N-1] := X * Y, ALimbs64 = uint64 limb count N.
+ class procedure Mul(PX, PY, PZ: PUInt64; ALimbs64: NativeInt); static;
+ /// Z[0..2N-1] := X^2.
+ class procedure Sqr(PX, PZ: PUInt64; ALimbs64: NativeInt); static;
+ end;
+
+implementation
+
+{$IFDEF CRYPTOLIB_AARCH64_ASM}
+
+// Z[0..2N-1] := X[0..N-1] * Y[0..N-1].
+procedure FpKernelMulAsm(PX, PY, PZ: PUInt64; ALimbs64: NativeInt);
+{$DEFINE CRYPTOLIB_FP_MUL}
+{$I ..\..\Include\Simd\Common\ClpSimdProc4Begin_aarch64.inc}
+{$I ..\..\Include\Simd\FpKernel\FpKernel_aarch64.inc}
+{$UNDEF CRYPTOLIB_FP_MUL}
+end;
+
+// Z[0..2N-1] := X[0..N-1]^2.
+procedure FpKernelSqrAsm(PX, PZ: PUInt64; ALimbs64: NativeInt);
+{$DEFINE CRYPTOLIB_FP_SQR}
+{$I ..\..\Include\Simd\Common\ClpSimdProc3Begin_aarch64.inc}
+{$I ..\..\Include\Simd\FpKernel\FpKernel_aarch64.inc}
+{$UNDEF CRYPTOLIB_FP_SQR}
+end;
+
+{$ENDIF}
+
+{ TFpKernelArmBackend }
+
+class function TFpKernelArmBackend.IsSupported: Boolean;
+begin
+{$IFDEF CRYPTOLIB_AARCH64_ASM}
+ // Base kernel uses plain integer ops (no CPU-feature dependency); gate only on
+ // "not forced scalar" so a CRYPTOLIB_FORCE_SCALAR build (which pins the active
+ // level to Scalar) falls back to the scalar path.
+ Result := TCpuFeatures.Arm.GetActiveSimdLevel() <> TArmSimdLevel.Scalar;
+{$ELSE}
+ Result := False;
+{$ENDIF}
+end;
+
+class procedure TFpKernelArmBackend.Mul(PX, PY, PZ: PUInt64; ALimbs64: NativeInt);
+begin
+{$IFDEF CRYPTOLIB_AARCH64_ASM}
+ FpKernelMulAsm(PX, PY, PZ, ALimbs64);
+{$ENDIF}
+end;
+
+class procedure TFpKernelArmBackend.Sqr(PX, PZ: PUInt64; ALimbs64: NativeInt);
+begin
+{$IFDEF CRYPTOLIB_AARCH64_ASM}
+ FpKernelSqrAsm(PX, PZ, ALimbs64);
+{$ENDIF}
+end;
+
+end.
diff --git a/CryptoLib/src/Simd/Backend/ClpFpKernelX86Backend.pas b/CryptoLib/src/Simd/Backend/ClpFpKernelX86Backend.pas
new file mode 100644
index 00000000..030477cf
--- /dev/null
+++ b/CryptoLib/src/Simd/Backend/ClpFpKernelX86Backend.pas
@@ -0,0 +1,106 @@
+{ *********************************************************************************** }
+{ * 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 ClpFpKernelX86Backend;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+{$IFDEF CRYPTOLIB_X86_SIMD}
+ ClpCpuFeatures,
+ ClpSimdLevels,
+{$ENDIF}
+ ClpCryptoLibTypes;
+
+type
+ ///
+ /// x86 (i386 + x86-64) big-integer kernel backend for prime-field (Fp)
+ /// multiplication/square. A leaf: capability probe plus the hot schoolbook
+ /// multiply/square. The arch-neutral dispatch and the scalar fallback live in
+ /// TFpKernelSimd / the field units. Hot paths are in
+ /// Include/Simd/FpKernel/.
+ ///
+ TFpKernelX86Backend = class sealed
+ public
+ class function IsSupported: Boolean; static;
+ class procedure Mul(PX, PY, PZ: PUInt64; ALimbs64: NativeInt); static;
+ class procedure Sqr(PX, PZ: PUInt64; ALimbs64: NativeInt); static;
+ end;
+
+implementation
+
+{$IFDEF CRYPTOLIB_X86_SIMD}
+
+procedure FpKernelMulAsm(PX, PY, PZ: PUInt64; ALimbs64: NativeInt);
+{$DEFINE CRYPTOLIB_FP_MUL}
+{$IFDEF CRYPTOLIB_X86_64_ASM}
+{$I ..\..\Include\Simd\Common\ClpSimdProc4Begin_x86_64.inc}
+{$I ..\..\Include\Simd\FpKernel\FpKernel_x86_64.inc}
+{$ENDIF}
+{$IFDEF CRYPTOLIB_I386_ASM}
+{$I ..\..\Include\Simd\Common\ClpSimdProc4Begin_i386.inc}
+{$I ..\..\Include\Simd\FpKernel\FpKernel_i386.inc}
+{$ENDIF}
+{$UNDEF CRYPTOLIB_FP_MUL}
+end;
+
+// Square Z := X^2 (FP_SQR selector).
+procedure FpKernelSqrAsm(PX, PZ: PUInt64; ALimbs64: NativeInt);
+{$DEFINE CRYPTOLIB_FP_SQR}
+{$IFDEF CRYPTOLIB_X86_64_ASM}
+{$I ..\..\Include\Simd\Common\ClpSimdProc3Begin_x86_64.inc}
+{$I ..\..\Include\Simd\FpKernel\FpKernel_x86_64.inc}
+{$ENDIF}
+{$IFDEF CRYPTOLIB_I386_ASM}
+{$I ..\..\Include\Simd\Common\ClpSimdProc3Begin_i386.inc}
+{$I ..\..\Include\Simd\FpKernel\FpKernel_i386.inc}
+{$ENDIF}
+{$UNDEF CRYPTOLIB_FP_SQR}
+end;
+
+{$ENDIF}
+
+{ TFpKernelX86Backend }
+
+class function TFpKernelX86Backend.IsSupported: Boolean;
+begin
+{$IFDEF CRYPTOLIB_X86_SIMD}
+ // Base kernel uses plain integer ops (no CPU-feature dependency); gate only on
+ // "not forced scalar" so a CRYPTOLIB_FORCE_SCALAR build (which pins the active
+ // level to Scalar) falls back to the scalar path.
+ Result := TCpuFeatures.X86.GetActiveSimdLevel() <> TX86SimdLevel.Scalar;
+{$ELSE}
+ Result := False;
+{$ENDIF}
+end;
+
+class procedure TFpKernelX86Backend.Mul(PX, PY, PZ: PUInt64; ALimbs64: NativeInt);
+begin
+{$IFDEF CRYPTOLIB_X86_SIMD}
+ FpKernelMulAsm(PX, PY, PZ, ALimbs64);
+{$ENDIF}
+end;
+
+class procedure TFpKernelX86Backend.Sqr(PX, PZ: PUInt64; ALimbs64: NativeInt);
+begin
+{$IFDEF CRYPTOLIB_X86_SIMD}
+ FpKernelSqrAsm(PX, PZ, ALimbs64);
+{$ENDIF}
+end;
+
+end.
diff --git a/CryptoLib/src/Simd/Facade/ClpFpKernelSimd.pas b/CryptoLib/src/Simd/Facade/ClpFpKernelSimd.pas
new file mode 100644
index 00000000..0f5763ee
--- /dev/null
+++ b/CryptoLib/src/Simd/Facade/ClpFpKernelSimd.pas
@@ -0,0 +1,112 @@
+{ *********************************************************************************** }
+{ * 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 ClpFpKernelSimd;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+{$IFDEF CRYPTOLIB_X86_SIMD}
+ ClpFpKernelX86Backend,
+{$ENDIF}
+{$IFDEF CRYPTOLIB_AARCH64_ASM}
+ ClpFpKernelArmBackend,
+{$ENDIF}
+ ClpCryptoLibTypes;
+
+type
+ ///
+ /// Arch-neutral facade over the prime-field (Fp) big-integer multiply/square
+ /// kernel. TryMul/TrySqr reinterpret the caller's little-endian
+ /// 32-bit-limb field arrays as 64-bit limbs and run the wide multiply, returning
+ /// False when no fast path applies (unsupported arch, a forced-scalar
+ /// build, or an odd 32-bit limb count such as P-521's 17); the caller then uses its
+ /// existing 32-bit path.
+ ///
+ TFpKernelSimd = class sealed
+ public
+ /// AZz[0..2*ALimbs32-1] := AX * AY (both ALimbs32 uint32 limbs).
+ /// Returns False if unsupported (caller falls back).
+ class function TryMul(const AX, AY, AZz: TCryptoLibUInt32Array;
+ ALimbs32: Int32): Boolean; overload; static;
+ class function TryMul(APX, APY, APZz: PUInt32; ALimbs32: Int32): Boolean; overload; static;
+ /// AZz[0..2*ALimbs32-1] := AX^2. Returns False if unsupported.
+ class function TrySqr(const AX, AZz: TCryptoLibUInt32Array;
+ ALimbs32: Int32): Boolean; overload; static;
+ class function TrySqr(APX, APZz: PUInt32; ALimbs32: Int32): Boolean; overload; static;
+ end;
+
+implementation
+
+{ TFpKernelSimd }
+
+class function TFpKernelSimd.TryMul(const AX, AY, AZz: TCryptoLibUInt32Array;
+ ALimbs32: Int32): Boolean;
+begin
+ Result := TryMul(PUInt32(@AX[0]), PUInt32(@AY[0]), PUInt32(@AZz[0]), ALimbs32);
+end;
+
+class function TFpKernelSimd.TryMul(APX, APY, APZz: PUInt32; ALimbs32: Int32): Boolean;
+begin
+{$IF DEFINED(CRYPTOLIB_X86_SIMD) OR DEFINED(CRYPTOLIB_AARCH64_ASM)}
+ // Even 32-bit limb count only: uint32[2N] == uint64[N]. Odd widths (P-521 = 17)
+ // are not byte-identical to any uint64[k] and need a mixed-width kernel.
+ if (ALimbs32 and 1) <> 0 then
+ Exit(False);
+ {$IFDEF CRYPTOLIB_X86_SIMD}
+ if not TFpKernelX86Backend.IsSupported then
+ Exit(False);
+ TFpKernelX86Backend.Mul(PUInt64(APX), PUInt64(APY), PUInt64(APZz), ALimbs32 shr 1);
+ {$ELSE}
+ if not TFpKernelArmBackend.IsSupported then
+ Exit(False);
+ TFpKernelArmBackend.Mul(PUInt64(APX), PUInt64(APY), PUInt64(APZz), ALimbs32 shr 1);
+ {$ENDIF}
+ Result := True;
+{$ELSE}
+ Result := False;
+{$IFEND}
+end;
+
+class function TFpKernelSimd.TrySqr(const AX, AZz: TCryptoLibUInt32Array;
+ ALimbs32: Int32): Boolean;
+begin
+ Result := TrySqr(PUInt32(@AX[0]), PUInt32(@AZz[0]), ALimbs32);
+end;
+
+class function TFpKernelSimd.TrySqr(APX, APZz: PUInt32; ALimbs32: Int32): Boolean;
+begin
+{$IF DEFINED(CRYPTOLIB_X86_SIMD) OR DEFINED(CRYPTOLIB_AARCH64_ASM)}
+ if (ALimbs32 and 1) <> 0 then
+ Exit(False);
+ {$IFDEF CRYPTOLIB_X86_SIMD}
+ if not TFpKernelX86Backend.IsSupported then
+ Exit(False);
+ TFpKernelX86Backend.Sqr(PUInt64(APX), PUInt64(APZz), ALimbs32 shr 1);
+ {$ELSE}
+ if not TFpKernelArmBackend.IsSupported then
+ Exit(False);
+ TFpKernelArmBackend.Sqr(PUInt64(APX), PUInt64(APZz), ALimbs32 shr 1);
+ {$ENDIF}
+ Result := True;
+{$ELSE}
+ Result := False;
+{$IFEND}
+end;
+
+end.
diff --git a/CryptoLib/src/X509/ClpX509Certificate.pas b/CryptoLib/src/X509/ClpX509Certificate.pas
index ca0697b1..dbf04422 100644
--- a/CryptoLib/src/X509/ClpX509Certificate.pas
+++ b/CryptoLib/src/X509/ClpX509Certificate.pas
@@ -23,6 +23,7 @@ interface
uses
SysUtils,
Classes,
+ SyncObjs,
Rtti,
Math,
Generics.Collections,
@@ -111,6 +112,9 @@ TCachedEncoding = class(TInterfacedObject, ICachedEncoding)
FHashValueSet: Boolean;
FHashValue: Int32;
+ class var
+ FLazyLock: TCriticalSection;
+
function GetCachedEncoding: ICachedEncoding;
function CreateCachedEncoding(const ACert: IX509CertificateStructure): ICachedEncoding;
function CreatePublicKey(const ACert: IX509CertificateStructure): IAsymmetricKeyParameter;
@@ -124,6 +128,9 @@ TCachedEncoding = class(TInterfacedObject, ICachedEncoding)
function CheckSignatureValid(const AVerifier: IVerifierFactory): Boolean; virtual;
public
+ class constructor Create;
+ class destructor Destroy;
+
constructor Create(const ACertData: TCryptoLibByteArray); overload;
constructor Create(const ACertificate: IX509CertificateStructure); overload;
@@ -220,6 +227,16 @@ function TX509Certificate.TCachedEncoding.GetEncoded: TCryptoLibByteArray;
{ TX509Certificate }
+class constructor TX509Certificate.Create;
+begin
+ FLazyLock := TCriticalSection.Create;
+end;
+
+class destructor TX509Certificate.Destroy;
+begin
+ FLazyLock.Free;
+end;
+
constructor TX509Certificate.Create(const ACertData: TCryptoLibByteArray);
begin
Create(TX509CertificateStructure.GetInstance(ACertData));
@@ -367,12 +384,23 @@ function TX509Certificate.GetSignature: TCryptoLibByteArray;
end;
function TX509Certificate.GetSigAlgName: String;
+var
+ LComputed: String;
begin
- if FSigAlgName = '' then
- begin
- FSigAlgName := TX509SignatureUtilities.GetSignatureName(SignatureAlgorithm);
- end;
Result := FSigAlgName;
+ if Result <> '' then
+ Exit;
+
+ LComputed := TX509SignatureUtilities.GetSignatureName(SignatureAlgorithm);
+
+ FLazyLock.Acquire;
+ try
+ if FSigAlgName = '' then
+ FSigAlgName := LComputed;
+ Result := FSigAlgName;
+ finally
+ FLazyLock.Release;
+ end;
end;
function TX509Certificate.GetIsCritical: Boolean;
@@ -615,13 +643,23 @@ function TX509Certificate.GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
end;
function TX509Certificate.GetPublicKey: IAsymmetricKeyParameter;
+var
+ LComputed: IAsymmetricKeyParameter;
begin
- // Cache the public key to support repeated-use optimizations
- if FPublicKeyValue = nil then
- begin
- FPublicKeyValue := CreatePublicKey(FCertificateStructure);
- end;
Result := FPublicKeyValue;
+ if Result <> nil then
+ Exit;
+
+ LComputed := CreatePublicKey(FCertificateStructure);
+
+ FLazyLock.Acquire;
+ try
+ if FPublicKeyValue = nil then
+ FPublicKeyValue := LComputed;
+ Result := FPublicKeyValue;
+ finally
+ FLazyLock.Release;
+ end;
end;
function TX509Certificate.CreatePublicKey(const ACert: IX509CertificateStructure): IAsymmetricKeyParameter;
@@ -635,12 +673,23 @@ function TX509Certificate.GetEncoded: TCryptoLibByteArray;
end;
function TX509Certificate.GetCachedEncoding: ICachedEncoding;
+var
+ LComputed: ICachedEncoding;
begin
- if FCachedEncoding = nil then
- begin
- FCachedEncoding := CreateCachedEncoding(FCertificateStructure);
- end;
Result := FCachedEncoding;
+ if Result <> nil then
+ Exit;
+
+ LComputed := CreateCachedEncoding(FCertificateStructure);
+
+ FLazyLock.Acquire;
+ try
+ if FCachedEncoding = nil then
+ FCachedEncoding := LComputed;
+ Result := FCachedEncoding;
+ finally
+ FLazyLock.Release;
+ end;
end;
function TX509Certificate.CreateCachedEncoding(const ACert: IX509CertificateStructure): ICachedEncoding;
@@ -667,6 +716,8 @@ function TX509Certificate.Equals(AOther: IX509Certificate): Boolean;
LThat: TX509Certificate;
LThisEncoding, LThatEncoding: TCryptoLibByteArray;
LSignature: IDerBitString;
+ LThisHashSet, LThatHashSet, LThisNoEncoding, LThatNoEncoding: Boolean;
+ LThisHash, LThatHash: Int32;
begin
if (Self AS IX509Certificate) = AOther then
begin
@@ -682,15 +733,27 @@ function TX509Certificate.Equals(AOther: IX509Certificate): Boolean;
LThat := AOther as TX509Certificate;
- if FHashValueSet and LThat.FHashValueSet then
+ FLazyLock.Acquire;
+ try
+ LThisHashSet := FHashValueSet;
+ LThatHashSet := LThat.FHashValueSet;
+ LThisHash := FHashValue;
+ LThatHash := LThat.FHashValue;
+ LThisNoEncoding := FCachedEncoding = nil;
+ LThatNoEncoding := LThat.FCachedEncoding = nil;
+ finally
+ FLazyLock.Release;
+ end;
+
+ if LThisHashSet and LThatHashSet then
begin
- if FHashValue <> LThat.FHashValue then
+ if LThisHash <> LThatHash then
begin
Result := False;
Exit;
end;
end
- else if (FCachedEncoding = nil) or (LThat.FCachedEncoding = nil) then
+ else if LThisNoEncoding or LThatNoEncoding then
begin
LSignature := FCertificateStructure.Signature;
if (LSignature <> nil) and (not LSignature.Equals(LThat.FCertificateStructure.Signature)) then
@@ -712,14 +775,33 @@ function TX509Certificate.Equals(AOther: IX509Certificate): Boolean;
function TX509Certificate.GetHashCode: {$IFDEF DELPHI}Int32; {$ELSE}PtrInt; {$ENDIF DELPHI}
var
LEncoding: TCryptoLibByteArray;
+ LHash: Int32;
+ LNeedCompute: Boolean;
begin
- if not FHashValueSet then
- begin
- LEncoding := GetCachedEncoding().GetEncoding();
- FHashValue := TArrayUtilities.GetArrayHashCode(LEncoding);
- FHashValueSet := True;
+ FLazyLock.Acquire;
+ try
+ LNeedCompute := not FHashValueSet;
+ Result := FHashValue;
+ finally
+ FLazyLock.Release;
+ end;
+ if not LNeedCompute then
+ Exit;
+
+ LEncoding := GetCachedEncoding().GetEncoding();
+ LHash := TArrayUtilities.GetArrayHashCode(LEncoding);
+
+ FLazyLock.Acquire;
+ try
+ if not FHashValueSet then
+ begin
+ FHashValue := LHash;
+ FHashValueSet := True;
+ end;
+ Result := FHashValue;
+ finally
+ FLazyLock.Release;
end;
- Result := FHashValue;
end;
function TX509Certificate.ToString: String;