From f034a2731f64966a61eacf48a01469e9c6ca61f3 Mon Sep 17 00:00:00 2001 From: Ugochukwu Mmaduekwe Date: Wed, 12 Aug 2026 18:22:49 +0100 Subject: [PATCH 1/5] Make TCipherKernelRegistry reads allocation-free The read path (Snapshot/GetSnapshot/TryAcquire*) allocated and refcounted a fresh factory array on every call. Register/Unregister now rebuild an immutable copy-on-write snapshot under the write lock; the read takes a single reference load of it - no per-call allocation. The lock is held only for that reference load (not the copy, not the factory TryCreate), so it stays uncontended and, unlike a lock-free read, keeps runtime registration concurrent with acquisition thread-safe - matching the guarantee the external-registration API implies. --- .../CipherKernels/ClpCipherKernelRegistry.pas | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) 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; From 44a951afd889c6fbb3cf2142f32960927f852d8d Mon Sep 17 00:00:00 2001 From: Ugochukwu Mmaduekwe Date: Thu, 13 Aug 2026 18:47:04 +0100 Subject: [PATCH 2/5] EC: value-type constant-time scalar multiplier for all Fp prime curves MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the array-based constant-time EC point multiplier with an allocation-free, value-type implementation over stack records, wired as the default multiplier for P-256, secp256k1, P-384 and P-521. Core layer (Math/EC/Multiplier): - ClpCTFieldValue: TFe/TFeExt/TFePoint fixed-size stack records (no heap). - ClpCTFieldOps: TCTFieldOpsBase, virtual/abstract class-method field ops overridden per curve — the one dispatch form that is refcount-free on FPC x86_64/i386 and Delphi. - ClpCTLadder: generic TCTLadder — RCB2016 complete add/double (Algorithms 1 & 3) over TFePoint. - ClpFpCTMultiplier: generic TFpCTMultiplier (IECMultiplier) — the hot loop (fixed 4-bit window, masked table lookup, one unconditional add per window, scalar blinding, randomized projective coords). Curve context (order, affine conversion, inverse) via IFpFieldOps. SIMD Fp multiply/square kernel (Simd/Facade + Simd/Backend + Include/Simd): - Width-general 64-bit-limb schoolbook mul/sqr for x86_64, i386, aarch64, behind an arch-neutral TFpKernelSimd.TryMul/TrySqr facade; scalar bridge in the caller when unavailable. Per-curve field ops gained TFe overloads over PUInt32 cores (kernel path alloc-free); TNat/TNat256 gained PUInt32 overloads (Add/Sub/Gte/Inc/Dec/ Eq/AddTo/ShiftDownBits/Mul33*/IncAt/DecAt/Add33To/Sub33From) with every array form delegating to the pointer core. P-521 mixed-width: no new asm — the 17 real limbs are padded to 18 uint32 (= 9 uint64) with a zeroed top limb so the even-width kernel applies; W[17] kept zero on every field-op output. Cleanup: retire the now-dead array multiplier and its math. - Delete ClpFixedWindowCTMultiplier and ClpHomogeneousPoint (TCTHomogeneous- Math); remove from all FPC and Delphi manifests. - Slim IFpFieldOps: drop Square/Add/MulByA/MulByB3 (unused after the move). - Retarget the exceptional-formula test to the live TCTLadder and the blind-bits test to TFpCTMultiplier. - Extend the dudect harness to all four prime curves. --- .../Delphi/CryptoLib.BenchmarkConsole.dpr | 9 +- .../src/Core/CtSubjects.pas | 53 +++- .../Delphi.Examples/CryptoLib.Examples.dpr | 9 +- .../Delphi.Tests/CryptoLib.Tests.Mobile.dpr | 9 +- .../Delphi.Tests/CryptoLib.Tests.Mobile.dproj | 9 +- .../Delphi.Tests/CryptoLib.Tests.dpr | 9 +- .../src/Crypto/ECDHPrimeConstantTimeTests.pas | 120 +++++--- .../Simd/FpKernel/FpKernel_aarch64.inc | 133 ++++++++ .../Include/Simd/FpKernel/FpKernel_i386.inc | 124 ++++++++ .../Include/Simd/FpKernel/FpKernel_x86_64.inc | 161 ++++++++++ .../Math/EC/Multiplier/ClpIFpFieldOps.pas | 4 - .../Math/EC/Custom/Sec/ClpSecP256K1Custom.pas | 223 +++++++++++--- .../Math/EC/Custom/Sec/ClpSecP256R1Custom.pas | 232 +++++++++++--- .../Math/EC/Custom/Sec/ClpSecP384R1Custom.pas | 231 +++++++++++--- .../Math/EC/Custom/Sec/ClpSecP521R1Custom.pas | 194 ++++++++++-- .../src/Math/EC/Multiplier/ClpCTFieldOps.pas | 52 ++++ .../Math/EC/Multiplier/ClpCTFieldValue.pas | 60 ++++ .../src/Math/EC/Multiplier/ClpCTLadder.pas | 141 +++++++++ ...CTMultiplier.pas => ClpFpCTMultiplier.pas} | 201 +++++++----- .../EC/Multiplier/ClpHomogeneousPoint.pas | 232 -------------- CryptoLib/src/Math/Raw/ClpNat.pas | 288 ++++++++---------- CryptoLib/src/Math/Raw/ClpNat256.pas | 262 +++++----------- CryptoLib/src/Misc/ClpX86SimdFeatures.pas | 50 +++ .../Delphi/CryptoLib4PascalPackage.dpk | 9 +- .../Packages/FPC/CryptoLib4PascalPackage.lpk | 58 ++-- .../Packages/FPC/CryptoLib4PascalPackage.pas | 8 +- .../Simd/Backend/ClpFpKernelArmBackend.pas | 97 ++++++ .../Simd/Backend/ClpFpKernelX86Backend.pas | 106 +++++++ CryptoLib/src/Simd/Facade/ClpFpKernelSimd.pas | 112 +++++++ 29 files changed, 2322 insertions(+), 874 deletions(-) create mode 100644 CryptoLib/src/Include/Simd/FpKernel/FpKernel_aarch64.inc create mode 100644 CryptoLib/src/Include/Simd/FpKernel/FpKernel_i386.inc create mode 100644 CryptoLib/src/Include/Simd/FpKernel/FpKernel_x86_64.inc create mode 100644 CryptoLib/src/Math/EC/Multiplier/ClpCTFieldOps.pas create mode 100644 CryptoLib/src/Math/EC/Multiplier/ClpCTFieldValue.pas create mode 100644 CryptoLib/src/Math/EC/Multiplier/ClpCTLadder.pas rename CryptoLib/src/Math/EC/Multiplier/{ClpFixedWindowCTMultiplier.pas => ClpFpCTMultiplier.pas} (54%) delete mode 100644 CryptoLib/src/Math/EC/Multiplier/ClpHomogeneousPoint.pas create mode 100644 CryptoLib/src/Simd/Backend/ClpFpKernelArmBackend.pas create mode 100644 CryptoLib/src/Simd/Backend/ClpFpKernelX86Backend.pas create mode 100644 CryptoLib/src/Simd/Facade/ClpFpKernelSimd.pas diff --git a/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr b/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr index ea31851c..ed683658 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,10 @@ 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', 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..d5ded07f 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,45 @@ 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; + function MakeAesBitsliced(ASeed: UInt64): TDudectOp; begin Result := TAesOp.Create(TAesBitSlicedEngine.Create as IBlockCipher, 16, ASeed); @@ -679,10 +718,10 @@ function MakeRow(const AName, ASubjectLabel, AControlLabel: string; function GetDudectRows: TCtRowArray; begin - System.SetLength(Result, 7); + System.SetLength(Result, 10); 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 +744,14 @@ 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))); 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..9c164496 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,10 @@ 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', 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.dpr b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr index 02f34750..0f2ca941 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,10 @@ 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', 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..19670b58 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,10 @@ - - + + + + diff --git a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr index 4213428f..b576e8a5 100644 --- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr +++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr @@ -406,8 +406,13 @@ 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', + 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..a66b4f90 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, + ClpCTLadder, + 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 (TCTLadder) 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); + TCTLadder.PointDouble(LP, LDbl); + TCTLadder.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)); + TCTLadder.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); + TCTLadder.PointAdd(LP, LInf, LSum); + AssertPointsEqual('P+O=P', LG, FePointToPoint(LFO, LCurve, LSum)); + TCTLadder.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'); + TCTLadder.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/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/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/Custom/Sec/ClpSecP256K1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas index 0f2e40cb..00a9c143 100644 --- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas +++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas @@ -25,6 +25,10 @@ interface ClpBigInteger, ClpNat256, ClpNat, + ClpFpKernelSimd, + ClpCTFieldValue, + ClpCTFieldOps, + ClpFpCTMultiplier, ClpMod, ClpPack, ClpEncoders, @@ -37,7 +41,6 @@ interface ClpECPoint, ClpECLookupTables, ClpIFpFieldOps, - ClpFixedWindowCTMultiplier, ClpIECCommon, ClpIECFieldElement, ClpISecP256K1Custom, @@ -56,9 +59,13 @@ TSecP256K1Field = class sealed(TObject) PInv33 = UInt32($3D1); class var FP, FPExt, FPExtInv: TCryptoLibUInt32Array; + FFa, FFb3: TFe; // a (=0) and b3 (=3b) field reps for the CT ladder + 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 +74,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; @@ -213,11 +227,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 +236,25 @@ TSecP256K1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps) procedure FieldOne(const AZ: TCryptoLibUInt32Array); end; +type + TSecP256K1Ops = class sealed(TCTFieldOpsBase) + 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 + LA, LB, LB3: TCryptoLibUInt32Array; begin FP := TCryptoLibUInt32Array.Create($FFFFFC2F, $FFFFFFFE, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF); @@ -239,6 +263,17 @@ implementation $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF); FPExtInv := TCryptoLibUInt32Array.Create($FFF16F5F, $FFFFF85D, $FFFFFFFE, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $000007A1, $00000002); + + // Value-type field constants for the CT ladder: a = 0, b3 = 3b mod p (b = 7). + System.FillChar(FFa, SizeOf(FFa), 0); + System.FillChar(FFb3, SizeOf(FFb3), 0); + LA := FromBigInteger(TBigInteger.Zero); + System.Move(LA[0], FFa.W[0], 8 * SizeOf(UInt32)); + 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 +285,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 +346,61 @@ 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 + Multiply(AX, FFa, AZ, ATT); +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,7 @@ 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; { TSecP256K1FpFieldOps } @@ -1127,31 +1246,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 +1288,42 @@ class function TSecP256K1FieldElement.GetQ: TBigInteger; Result := FQ; end; + +{ TSecP256K1Ops } + +class function TSecP256K1Ops.FieldLimbs: Int32; +begin + Result := 8; +end; + +class procedure TSecP256K1Ops.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); +begin + TSecP256K1Field.Multiply(AX, AY, AZ, ATT); +end; + +class procedure TSecP256K1Ops.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +begin + TSecP256K1Field.Square(AX, AZ, ATT); +end; + +class procedure TSecP256K1Ops.Add(const AX, AY: TFe; var AZ: TFe); +begin + TSecP256K1Field.Add(AX, AY, AZ); +end; + +class procedure TSecP256K1Ops.Sub(const AX, AY: TFe; var AZ: TFe); +begin + TSecP256K1Field.Subtract(AX, AY, AZ); +end; + +class procedure TSecP256K1Ops.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +begin + TSecP256K1Field.MulByA(AX, AZ, ATT); +end; + +class procedure TSecP256K1Ops.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..0849e983 100644 --- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas +++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas @@ -25,6 +25,10 @@ interface ClpBigInteger, ClpNat256, ClpNat, + ClpFpKernelSimd, + ClpCTFieldValue, + ClpCTFieldOps, + ClpFpCTMultiplier, ClpMod, ClpPack, ClpEncoders, @@ -37,7 +41,6 @@ interface ClpECPoint, ClpECLookupTables, ClpIFpFieldOps, - ClpFixedWindowCTMultiplier, ClpIECCommon, ClpIECFieldElement, ClpISecP256R1Custom, @@ -55,11 +58,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; + FFa, FFb3: TFe; // value-type field reps of a (= -3) and b3 (= 3b), for the CT ladder + 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 +77,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; @@ -214,11 +230,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 +239,52 @@ 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 ). + TSecP256R1Ops = class sealed(TCTFieldOpsBase) + 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 + LA, 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 constants for the CT ladder: a = -3 mod p, b3 = 3b mod p. + System.FillChar(FFa, SizeOf(FFa), 0); + System.FillChar(FFb3, SizeOf(FFb3), 0); + LA := FromBigInteger(TBigInteger.Create(1, THexEncoder.Decode('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC'))); + System.Move(LA[0], FFa.W[0], 8 * SizeOf(UInt32)); + 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 +320,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 +368,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 +428,61 @@ 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); +begin + Multiply(AX, FFa, AZ, ATT); +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 +519,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 +584,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 +635,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 +644,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 +679,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 +696,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 +716,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 +1375,7 @@ 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; { TSecP256R1FpFieldOps } @@ -1283,31 +1416,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 +1453,41 @@ procedure TSecP256R1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array); TNat.Copy(FE_INTS, FOne, 0, AZ, 0); end; +{ TSecP256R1Ops } + +class function TSecP256R1Ops.FieldLimbs: Int32; +begin + Result := 8; +end; + +class procedure TSecP256R1Ops.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); +begin + TSecP256R1Field.Multiply(AX, AY, AZ, ATT); +end; + +class procedure TSecP256R1Ops.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +begin + TSecP256R1Field.Square(AX, AZ, ATT); +end; + +class procedure TSecP256R1Ops.Add(const AX, AY: TFe; var AZ: TFe); +begin + TSecP256R1Field.Add(AX, AY, AZ); +end; + +class procedure TSecP256R1Ops.Sub(const AX, AY: TFe; var AZ: TFe); +begin + TSecP256R1Field.Subtract(AX, AY, AZ); +end; + +class procedure TSecP256R1Ops.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +begin + TSecP256R1Field.MulByA(AX, AZ, ATT); +end; + +class procedure TSecP256R1Ops.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..a203254b 100644 --- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas +++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas @@ -25,6 +25,10 @@ interface ClpBigInteger, ClpNat384, ClpNat, + ClpFpKernelSimd, + ClpCTFieldValue, + ClpCTFieldOps, + ClpFpCTMultiplier, ClpMod, ClpPack, ClpEncoders, @@ -37,7 +41,6 @@ interface ClpECPoint, ClpECLookupTables, ClpIFpFieldOps, - ClpFixedWindowCTMultiplier, ClpIECCommon, ClpIECFieldElement, ClpISecP384R1Custom, @@ -55,11 +58,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; + FFa, FFb3: TFe; // a (= p-3) and b3 (= 3b) field reps for the CT ladder + 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 +77,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; @@ -213,11 +229,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 +238,25 @@ TSecP384R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps) procedure FieldOne(const AZ: TCryptoLibUInt32Array); end; +type + TSecP384R1Ops = class sealed(TCTFieldOpsBase) + 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 + LA, LB, LB3: TCryptoLibUInt32Array; begin FP := TCryptoLibUInt32Array.Create($FFFFFFFF, $00000000, $00000000, $FFFFFFFF, $FFFFFFFE, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF); @@ -241,9 +267,25 @@ implementation FPExtInv := TCryptoLibUInt32Array.Create($FFFFFFFF, $00000001, $FFFFFFFF, $FFFFFFFD, $FFFFFFFF, $00000001, $FFFFFFFF, $FFFFFFFD, $FFFFFFFE, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $00000001, $FFFFFFFE, $FFFFFFFF, $00000001, $00000002); + + // Value-type field constants for the CT ladder: a = -3 mod p, b3 = 3b mod p. + System.FillChar(FFa, SizeOf(FFa), 0); + System.FillChar(FFb3, SizeOf(FFb3), 0); + LA := FromBigInteger(TBigInteger.Create(1, THexEncoder.Decode('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC'))); + System.Move(LA[0], FFa.W[0], 12 * SizeOf(UInt32)); + 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 +312,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 +351,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 +412,61 @@ 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); +begin + Multiply(AX, FFa, AZ, ATT); +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 +494,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 +571,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 +611,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 +620,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 +655,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 +672,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 +692,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 +1376,7 @@ 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; { TSecP384R1FpFieldOps } @@ -1286,31 +1417,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 +1454,42 @@ procedure TSecP384R1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array); TNat.Copy(FE_INTS, FOne, 0, AZ, 0); end; + +{ TSecP384R1Ops } + +class function TSecP384R1Ops.FieldLimbs: Int32; +begin + Result := 12; +end; + +class procedure TSecP384R1Ops.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); +begin + TSecP384R1Field.Multiply(AX, AY, AZ, ATT); +end; + +class procedure TSecP384R1Ops.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +begin + TSecP384R1Field.Square(AX, AZ, ATT); +end; + +class procedure TSecP384R1Ops.Add(const AX, AY: TFe; var AZ: TFe); +begin + TSecP384R1Field.Add(AX, AY, AZ); +end; + +class procedure TSecP384R1Ops.Sub(const AX, AY: TFe; var AZ: TFe); +begin + TSecP384R1Field.Subtract(AX, AY, AZ); +end; + +class procedure TSecP384R1Ops.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +begin + TSecP384R1Field.MulByA(AX, AZ, ATT); +end; + +class procedure TSecP384R1Ops.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..3a367372 100644 --- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas +++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas @@ -25,6 +25,10 @@ interface ClpBigInteger, ClpNat512, ClpNat, + ClpFpKernelSimd, + ClpCTFieldValue, + ClpCTFieldOps, + ClpFpCTMultiplier, ClpMod, ClpPack, ClpEncoders, @@ -37,7 +41,6 @@ interface ClpECPoint, ClpECLookupTables, ClpIFpFieldOps, - ClpFixedWindowCTMultiplier, ClpIECCommon, ClpIECFieldElement, ClpISecP521R1Custom, @@ -54,11 +57,13 @@ TSecP521R1Field = class sealed(TObject) P16 = UInt32($1FF); class var FP: TCryptoLibUInt32Array; + FFa, FFb3: TFe; // a (= p-3) and b3 (= 3b) field reps for the CT ladder 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 +71,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; @@ -210,11 +221,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 +230,40 @@ TSecP521R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps) procedure FieldOne(const AZ: TCryptoLibUInt32Array); end; +type + TSecP521R1Ops = class sealed(TCTFieldOpsBase) + 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 + LA, 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 constants for the CT ladder: a = -3 mod p, b3 = 3b mod p. + System.FillChar(FFa, SizeOf(FFa), 0); + System.FillChar(FFb3, SizeOf(FFb3), 0); + LA := FromBigInteger(TBigInteger.Create(1, THexEncoder.Decode('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC'))); + System.Move(LA[0], FFa.W[0], 17 * SizeOf(UInt32)); + 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 +307,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 +380,36 @@ 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); +begin + Multiply(AX, FFa, AZ, ATT); +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 +438,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 +451,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 +492,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 +559,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 +1193,7 @@ 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; { TSecP521R1FpFieldOps } @@ -1120,31 +1234,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 +1271,42 @@ procedure TSecP521R1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array); TNat.Copy(FE_INTS, FOne, 0, AZ, 0); end; + +{ TSecP521R1Ops } + +class function TSecP521R1Ops.FieldLimbs: Int32; +begin + Result := 17; +end; + +class procedure TSecP521R1Ops.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); +begin + TSecP521R1Field.Multiply(AX, AY, AZ, ATT); +end; + +class procedure TSecP521R1Ops.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +begin + TSecP521R1Field.Square(AX, AZ, ATT); +end; + +class procedure TSecP521R1Ops.Add(const AX, AY: TFe; var AZ: TFe); +begin + TSecP521R1Field.Add(AX, AY, AZ); +end; + +class procedure TSecP521R1Ops.Sub(const AX, AY: TFe; var AZ: TFe); +begin + TSecP521R1Field.Subtract(AX, AY, AZ); +end; + +class procedure TSecP521R1Ops.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +begin + TSecP521R1Field.MulByA(AX, AZ, ATT); +end; + +class procedure TSecP521R1Ops.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/ClpCTFieldOps.pas b/CryptoLib/src/Math/EC/Multiplier/ClpCTFieldOps.pas new file mode 100644 index 00000000..d771c26c --- /dev/null +++ b/CryptoLib/src/Math/EC/Multiplier/ClpCTFieldOps.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 ClpCTFieldOps; + +{$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 TCTLadder<TOps: TCTFieldOpsBase> dispatches to it. + /// + TCTFieldOpsBase = 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/ClpCTLadder.pas b/CryptoLib/src/Math/EC/Multiplier/ClpCTLadder.pas new file mode 100644 index 00000000..ee8d6b44 --- /dev/null +++ b/CryptoLib/src/Math/EC/Multiplier/ClpCTLadder.pas @@ -0,0 +1,141 @@ +{ *********************************************************************************** } +{ * 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 ClpCTLadder; + +{$I ..\..\..\Include\CryptoLib.inc} + +interface + +uses + ClpCTFieldValue, + ClpCTFieldOps; + +type + /// + /// Generic constant-time point arithmetic over a per-curve ops class: the + /// RCB2016 complete addition (Algorithm 1) and doubling (Algorithm 3) for Fp + /// short-Weierstrass curves in homogeneous coordinates. 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 windowed scalar loop that drives these lives in + /// TFpCTMultiplier. + /// + TCTLadder = 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; + end; + +implementation + +class procedure TCTLadder.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 TCTLadder.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; + +end. diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpFixedWindowCTMultiplier.pas b/CryptoLib/src/Math/EC/Multiplier/ClpFpCTMultiplier.pas similarity index 54% rename from CryptoLib/src/Math/EC/Multiplier/ClpFixedWindowCTMultiplier.pas rename to CryptoLib/src/Math/EC/Multiplier/ClpFpCTMultiplier.pas index a333205f..bb2dc7bb 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, + ClpCTFieldOps, + ClpCTLadder, 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 TCTLadder<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,13 @@ 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 OneFe(var AZ: TFe); + procedure Infinity(var AR: TFePoint); + procedure FromAffine(const AXa, AYa: TCryptoLibUInt32Array; var AR: TFePoint); + procedure ScaleRandom(const AP: TFePoint; const ALambda: TFe; var AR: TFePoint); + procedure ToAffine(const AP: TFePoint; const AXa, AYa: TCryptoLibUInt32Array; + out AIsInfinity: Boolean); + procedure SelectEntry(const ATable: array of TFePoint; AIndex: Int32; var AR: TFePoint); strict protected function MultiplyPositive(const AP: IECPoint; const AK: TBigInteger): IECPoint; override; public @@ -70,15 +85,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 +97,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 +109,115 @@ 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.OneFe(var AZ: TFe); +var + LArr: TCryptoLibUInt32Array; +begin + LArr := TNat.Create(FFieldOps.GetFieldInts); + FFieldOps.FieldOne(LArr); + FillChar(AZ, SizeOf(AZ), 0); + Move(LArr[0], AZ.W[0], FFieldOps.GetFieldInts * SizeOf(UInt32)); +end; + +procedure TFpCTMultiplier.Infinity(var AR: TFePoint); +begin + FillChar(AR, SizeOf(AR), 0); + OneFe(AR.Y); +end; + +procedure TFpCTMultiplier.FromAffine(const AXa, AYa: TCryptoLibUInt32Array; + var AR: TFePoint); +var + LN: Int32; +begin + LN := FFieldOps.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(AR.Z); +end; + +procedure TFpCTMultiplier.ScaleRandom(const AP: TFePoint; const ALambda: TFe; + var AR: TFePoint); +var + LTT: TFeExt; +begin + 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; + +procedure TFpCTMultiplier.ToAffine(const AP: TFePoint; + const AXa, AYa: TCryptoLibUInt32Array; out AIsInfinity: Boolean); +var + LN: Int32; + LZarr, LZInvArr: TCryptoLibUInt32Array; + LZInv, LTmp: TFe; + LTT: TFeExt; +begin + LN := FFieldOps.GetFieldInts; + LZarr := TNat.Create(LN); + Move(AP.Z.W[0], LZarr[0], LN * SizeOf(UInt32)); + AIsInfinity := FFieldOps.IsZero(LZarr); + if AIsInfinity then + Exit; + LZInvArr := TNat.Create(LN); + FFieldOps.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; + +procedure TFpCTMultiplier.SelectEntry(const ATable: array of TFePoint; + AIndex: Int32; var AR: TFePoint); var LN, LI, LJ: Int32; LMask: UInt32; + LEntry: TFePoint; begin LN := FFieldOps.GetFieldInts; - Result.X := TNat.Create(LN); - Result.Y := TNat.Create(LN); - Result.Z := TNat.Create(LN); + FillChar(AR, SizeOf(AR), 0); for LI := 0 to TABLE_SIZE - 1 do begin + LEntry := ATable[LI]; 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); + 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; -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 +226,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); + // 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)); + FromAffine(LXa, LYa, LBase); + ScaleRandom(LBase, LLambda, LBase); - // --- projective precomputation table [0]=O, [i]=[i]*base --- - System.SetLength(LTable, TABLE_SIZE); - LTable[0] := TCTHomogeneousMath.Infinity(FFieldOps); + // projective precomputation table [0]=O, [i]=[i]*base + SetLength(LTable, TABLE_SIZE); + Infinity(LTable[0]); LTable[1] := LBase; for LI := 2 to TABLE_SIZE - 1 do - LTable[LI] := TCTHomogeneousMath.Add(FFieldOps, LTable[LI - 1], LBase); + TCTLadder.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 +268,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); + Infinity(LAcc); for LI := LWindows - 1 downto 0 do begin for LJ := 0 to WINDOW_BITS - 1 do - LAcc := TCTHomogeneousMath.Double(FFieldOps, LAcc); + TCTLadder.PointDouble(LAcc, LAcc); // WINDOW_BITS divides 32, so a digit never spans a limb boundary LBit := LI * WINDOW_BITS; @@ -210,11 +282,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); + SelectEntry(LTable, LDigit, LSel); + TCTLadder.PointAdd(LAcc, LSel, LAcc); end; - TCTHomogeneousMath.ToAffine(FFieldOps, LAcc, LXa, LYa, LIsInfinity); + ToAffine(LAcc, LXa, LYa, LIsInfinity); if LIsInfinity then Exit(AP.Curve.Infinity); @@ -226,25 +298,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/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..1d246305 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,10 @@ 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', + ClpCTFieldOps in '..\..\Math\EC\Multiplier\ClpCTFieldOps.pas', + ClpCTLadder in '..\..\Math\EC\Multiplier\ClpCTLadder.pas', + ClpFpCTMultiplier in '..\..\Math\EC\Multiplier\ClpFpCTMultiplier.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..d7fc2eb5 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,61 @@ 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..5c6a0779 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, + ClpCryptoLibExceptions, ClpAesBitSlicedEngine, + ClpIFpFieldOps, ClpIF2mFieldOps, ClpLopezDahabLadder, ClpGaloisFieldUtilities, ClpAbstractBlockCipherMode, - ClpAbstractAeadCipher, ClpAbstractAeadBlockCipher; + ClpAbstractAeadCipher, ClpAbstractAeadBlockCipher, ClpFpKernelX86Backend, + ClpFpKernelSimd, ClpFpKernelArmBackend, ClpCTFieldValue, ClpCTFieldOps, + ClpCTLadder, ClpFpCTMultiplier; implementation diff --git a/CryptoLib/src/Simd/Backend/ClpFpKernelArmBackend.pas b/CryptoLib/src/Simd/Backend/ClpFpKernelArmBackend.pas new file mode 100644 index 00000000..3c6a8245 --- /dev/null +++ b/CryptoLib/src/Simd/Backend/ClpFpKernelArmBackend.pas @@ -0,0 +1,97 @@ +{ *********************************************************************************** } +{ * 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 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. From f8e2fe740c8eda0c811a940e940acecd39c511d2 Mon Sep 17 00:00:00 2001 From: Ugochukwu Mmaduekwe Date: Thu, 13 Aug 2026 23:09:27 +0100 Subject: [PATCH 3/5] EC: value-type fixed-base comb, a=-3 field specialization, CT point-layer refactor Continues the committed value-type constant-time scalar multiplier (44a951af) for the Fp prime curves. Fixed-base comb (ECDSA signing) - Add TFpCombMultiplier: a value-type Lim-Lee comb over stack TFePoint via the RCB complete formulas, reusing TFixedPointUtilities' generator table and caching the value-type table on the precomp-info object. Constant-time: masked table lookup, branchless recoding, fixed loop length. - Wire it in through a base-point hook rather than the variable-base default: IECCurve.GetBasePointMultiplier + TECCurve.CreateBasePointMultiplier (default TFixedPointCombMultiplier), overridden per custom curve; TECDsaSigner consumes it. Beats OpenSSL's generic [k]G on secp256k1/384/521. a = -3 / a = 0 field specialization - Fold the curve coefficient into MulByA: -(x+x+x) via field adds on P-256/384/521, zero on secp256k1 (a=0), instead of a full field multiply by a constant. ~1.2-1.4x on [k]G; the RCB add/double formulas are unchanged. Drop the now-dead FFa constant from all four curves. Constant-time point-layer refactor - Merge the group law and the point-representation helpers into a single TCTPoint (ClpCTLadder -> ClpCTPoint); "Ladder" was a misnomer for the RCB group law and clashed with the real TLopezDahabLadder on F2m. - Rename the value-type arithmetic ClpCTFieldOps/TCTFieldOpsBase -> ClpCTFieldArith/TCTFieldArithBase and per-curve TSecXXXOps -> TSecXXXFieldArith to end the name collision with the array-based IFpFieldOps adapter. The core is now a clean Value -> Arith -> Point trio. --- .../Delphi/CryptoLib.BenchmarkConsole.dpr | 1 + .../src/Core/CtSubjects.pas | 47 +- .../Delphi.Examples/CryptoLib.Examples.dpr | 1 + .../Delphi.Examples/CryptoLib.Examples.dproj | 1031 +++++++++++++++++ .../Delphi.Tests/CryptoLib.Tests.Mobile.dpr | 5 +- .../Delphi.Tests/CryptoLib.Tests.Mobile.dproj | 5 +- .../Delphi.Tests/CryptoLib.Tests.dpr | 5 +- .../src/Crypto/ECDHPrimeConstantTimeTests.pas | 18 +- .../src/Crypto/Signers/ClpECDsaSigner.pas | 2 +- .../src/Interfaces/Math/EC/ClpIECCommon.pas | 2 + CryptoLib/src/Math/EC/ClpECCurve.pas | 15 + .../Math/EC/Custom/Sec/ClpSecP256K1Custom.pas | 46 +- .../Math/EC/Custom/Sec/ClpSecP256R1Custom.pas | 51 +- .../Math/EC/Custom/Sec/ClpSecP384R1Custom.pas | 51 +- .../Math/EC/Custom/Sec/ClpSecP521R1Custom.pas | 51 +- ...{ClpCTFieldOps.pas => ClpCTFieldArith.pas} | 6 +- .../{ClpCTLadder.pas => ClpCTPoint.pas} | 134 ++- .../Math/EC/Multiplier/ClpFpCTMultiplier.pas | 104 +- .../EC/Multiplier/ClpFpCombMultiplier.pas | 157 +++ .../Delphi/CryptoLib4PascalPackage.dpk | 5 +- .../Packages/FPC/CryptoLib4PascalPackage.lpk | 14 +- .../Packages/FPC/CryptoLib4PascalPackage.pas | 12 +- 22 files changed, 1553 insertions(+), 210 deletions(-) create mode 100644 CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dproj rename CryptoLib/src/Math/EC/Multiplier/{ClpCTFieldOps.pas => ClpCTFieldArith.pas} (94%) rename CryptoLib/src/Math/EC/Multiplier/{ClpCTLadder.pas => ClpCTPoint.pas} (54%) create mode 100644 CryptoLib/src/Math/EC/Multiplier/ClpFpCombMultiplier.pas diff --git a/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr b/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr index ed683658..5adc82bb 100644 --- a/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr +++ b/CryptoLib.Benchmark/Delphi/CryptoLib.BenchmarkConsole.dpr @@ -400,6 +400,7 @@ uses 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 d5ded07f..ce4a9d14 100644 --- a/CryptoLib.ConstantTime/src/Core/CtSubjects.pas +++ b/CryptoLib.ConstantTime/src/Core/CtSubjects.pas @@ -628,6 +628,41 @@ function MakeSecp521r1WNaf(ASeed: UInt64): TDudectOp; 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); @@ -718,7 +753,7 @@ function MakeRow(const AName, ASubjectLabel, AControlLabel: string; function GetDudectRows: TCtRowArray; begin - System.SetLength(Result, 10); + System.SetLength(Result, 14); Result[0] := MakeRow('X25519', 'X25519 ladder', '', @MakeX25519, nil, MediumCfg(UInt64($0000000000000001))); Result[1] := MakeRow('P-256 [d]Q', 'value-type CT', 'wNAF (var-time)', @@ -752,6 +787,16 @@ function GetDudectRows: TCtRowArray; @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 9c164496..812cb6ff 100644 --- a/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dpr +++ b/CryptoLib.Examples/Delphi.Examples/CryptoLib.Examples.dpr @@ -417,6 +417,7 @@ uses 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 0f2ca941..10ea09b9 100644 --- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr +++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr @@ -391,9 +391,10 @@ uses ClpMultipliers in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpMultipliers.pas', ClpIFpFieldOps in '..\..\CryptoLib\src\Interfaces\Math\EC\Multiplier\ClpIFpFieldOps.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', + 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 19670b58..a2b8f40e 100644 --- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj +++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj @@ -734,9 +734,10 @@ - - + + + diff --git a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr index b576e8a5..3a7b4312 100644 --- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr +++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr @@ -407,9 +407,10 @@ uses ClpMultipliers in '..\..\CryptoLib\src\Math\EC\Multiplier\ClpMultipliers.pas', ClpIFpFieldOps in '..\..\CryptoLib\src\Interfaces\Math\EC\Multiplier\ClpIFpFieldOps.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', + 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', diff --git a/CryptoLib.Tests/src/Crypto/ECDHPrimeConstantTimeTests.pas b/CryptoLib.Tests/src/Crypto/ECDHPrimeConstantTimeTests.pas index a66b4f90..2ec564d2 100644 --- a/CryptoLib.Tests/src/Crypto/ECDHPrimeConstantTimeTests.pas +++ b/CryptoLib.Tests/src/Crypto/ECDHPrimeConstantTimeTests.pas @@ -39,7 +39,7 @@ interface ClpMultipliers, ClpIFpFieldOps, ClpCTFieldValue, - ClpCTLadder, + ClpCTPoint, ClpFpCTMultiplier, ClpSecP256R1Custom, ClpSecP256K1Custom, @@ -309,7 +309,7 @@ procedure TTestECDHPrimeConstantTime.TestExceptionalFormulas; LNegY, LZeroArr, LYtmp: TCryptoLibUInt32Array; LG, LNegG, LRef2G: IECPoint; begin - // Exercise the LIVE value-type complete-addition formulas (TCTLadder) on the + // 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'); @@ -321,8 +321,8 @@ procedure TTestECDHPrimeConstantTime.TestExceptionalFormulas; LP := FePointFromAffine(LFO, LG); // complete Add must handle P == Q (doubling): Add(P,P) == Double(P) == 2G - TCTLadder.PointDouble(LP, LDbl); - TCTLadder.PointAdd(LP, LP, LSum); + TCTPoint.PointDouble(LP, LDbl); + TCTPoint.PointAdd(LP, LP, LSum); LRef2G := LWNaf.Multiply(LX9.G, TBigInteger.Two).Normalize(); AssertPointsEqual('Double(P)=2G', LRef2G, FePointToPoint(LFO, LCurve, LDbl)); AssertPointsEqual('Add(P,P)=2G', LRef2G, FePointToPoint(LFO, LCurve, LSum)); @@ -335,7 +335,7 @@ procedure TTestECDHPrimeConstantTime.TestExceptionalFormulas; LNegY := TNat.Create(LN); LFO.Sub(LZeroArr, LYtmp, LNegY); System.Move(LNegY[0], LNeg.Y.W[0], LN * SizeOf(UInt32)); - TCTLadder.PointAdd(LP, LNeg, LSum); + 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(); @@ -343,13 +343,13 @@ procedure TTestECDHPrimeConstantTime.TestExceptionalFormulas; // P + O == P and O + P == P LInf := FeInfinity(LFO); - TCTLadder.PointAdd(LP, LInf, LSum); + TCTPoint.PointAdd(LP, LInf, LSum); AssertPointsEqual('P+O=P', LG, FePointToPoint(LFO, LCurve, LSum)); - TCTLadder.PointAdd(LInf, LP, LSum); + TCTPoint.PointAdd(LInf, LP, LSum); AssertPointsEqual('O+P=P', LG, FePointToPoint(LFO, LCurve, LSum)); // O + O == O - TCTLadder.PointAdd(LInf, LInf, LSum); + TCTPoint.PointAdd(LInf, LInf, LSum); CheckEquals(True, FePointToPoint(LFO, LCurve, LSum).IsInfinity, 'O+O=O'); end; @@ -433,7 +433,7 @@ procedure TTestECDHPrimeConstantTime.TestBlindBitsValidation; begin Result := False; try - LMul := TFpCTMultiplier.Create(LFO, ABlindBits); + LMul := TFpCTMultiplier.Create(LFO, ABlindBits); except on E: EArgumentCryptoLibException do Result := True; 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/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/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 00a9c143..2dc5bb9d 100644 --- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas +++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256K1Custom.pas @@ -27,8 +27,9 @@ interface ClpNat, ClpFpKernelSimd, ClpCTFieldValue, - ClpCTFieldOps, + ClpCTFieldArith, ClpFpCTMultiplier, + ClpFpCombMultiplier, ClpMod, ClpPack, ClpEncoders, @@ -59,7 +60,7 @@ TSecP256K1Field = class sealed(TObject) PInv33 = UInt32($3D1); class var FP, FPExt, FPExtInv: TCryptoLibUInt32Array; - FFa, FFb3: TFe; // a (=0) and b3 (=3b) field reps for the CT ladder + 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; @@ -208,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; @@ -237,7 +239,7 @@ TSecP256K1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps) end; type - TSecP256K1Ops = class sealed(TCTFieldOpsBase) + TSecP256K1FieldArith = class sealed(TCTFieldArithBase) public class function FieldLimbs: Int32; override; class procedure Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); override; @@ -254,7 +256,7 @@ implementation class constructor TSecP256K1Field.Create; var - LA, LB, LB3: TCryptoLibUInt32Array; + LB, LB3: TCryptoLibUInt32Array; begin FP := TCryptoLibUInt32Array.Create($FFFFFC2F, $FFFFFFFE, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF); @@ -264,11 +266,8 @@ implementation FPExtInv := TCryptoLibUInt32Array.Create($FFF16F5F, $FFFFF85D, $FFFFFFFE, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $000007A1, $00000002); - // Value-type field constants for the CT ladder: a = 0, b3 = 3b mod p (b = 7). - System.FillChar(FFa, SizeOf(FFa), 0); + // Value-type field constant for the CT ladder: b3 = 3b mod p (b = 7). System.FillChar(FFb3, SizeOf(FFb3), 0); - LA := FromBigInteger(TBigInteger.Zero); - System.Move(LA[0], FFa.W[0], 8 * SizeOf(UInt32)); LB := FromBigInteger(TBigInteger.Seven); LB3 := TNat256.Create; Add(LB, LB, LB3); @@ -393,7 +392,8 @@ class procedure TSecP256K1Field.Multiply(const AX, AY: TFe; var AZ: TFe; var ATT class procedure TSecP256K1Field.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin - Multiply(AX, FFa, AZ, ATT); + // a = 0 + System.FillChar(AZ, SizeOf(AZ), 0); end; class procedure TSecP256K1Field.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); @@ -1205,7 +1205,17 @@ function TSecP256K1Curve.CreateDefaultMultiplier: IECMultiplier; begin LCurve := Self as IECCurve; LFieldOps := TSecP256K1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order); - Result := TFpCTMultiplier.Create(LFieldOps); + 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 } @@ -1289,39 +1299,39 @@ class function TSecP256K1FieldElement.GetQ: TBigInteger; end; -{ TSecP256K1Ops } +{ TSecP256K1FieldArith } -class function TSecP256K1Ops.FieldLimbs: Int32; +class function TSecP256K1FieldArith.FieldLimbs: Int32; begin Result := 8; end; -class procedure TSecP256K1Ops.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP256K1FieldArith.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP256K1Field.Multiply(AX, AY, AZ, ATT); end; -class procedure TSecP256K1Ops.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP256K1FieldArith.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP256K1Field.Square(AX, AZ, ATT); end; -class procedure TSecP256K1Ops.Add(const AX, AY: TFe; var AZ: TFe); +class procedure TSecP256K1FieldArith.Add(const AX, AY: TFe; var AZ: TFe); begin TSecP256K1Field.Add(AX, AY, AZ); end; -class procedure TSecP256K1Ops.Sub(const AX, AY: TFe; var AZ: TFe); +class procedure TSecP256K1FieldArith.Sub(const AX, AY: TFe; var AZ: TFe); begin TSecP256K1Field.Subtract(AX, AY, AZ); end; -class procedure TSecP256K1Ops.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP256K1FieldArith.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP256K1Field.MulByA(AX, AZ, ATT); end; -class procedure TSecP256K1Ops.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP256K1FieldArith.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP256K1Field.MulByB3(AX, AZ, ATT); end; diff --git a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas index 0849e983..5f712936 100644 --- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas +++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP256R1Custom.pas @@ -27,8 +27,9 @@ interface ClpNat, ClpFpKernelSimd, ClpCTFieldValue, - ClpCTFieldOps, + ClpCTFieldArith, ClpFpCTMultiplier, + ClpFpCombMultiplier, ClpMod, ClpPack, ClpEncoders, @@ -58,7 +59,7 @@ TSecP256R1Field = class sealed(TObject) PExt15 = UInt32($FFFFFFFE); class var FP, FPExt: TCryptoLibUInt32Array; - FFa, FFb3: TFe; // value-type field reps of a (= -3) and b3 (= 3b), for the CT ladder + 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; @@ -211,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; @@ -242,7 +244,7 @@ TSecP256R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps) type /// P-256 field ops for the value-type constant-time ladder (the /// equivalent of ). - TSecP256R1Ops = class sealed(TCTFieldOpsBase) + TSecP256R1FieldArith = class sealed(TCTFieldArithBase) public class function FieldLimbs: Int32; override; class procedure Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); override; @@ -259,7 +261,7 @@ implementation class constructor TSecP256R1Field.Create; var - LA, LB, LB3: TCryptoLibUInt32Array; + LB, LB3: TCryptoLibUInt32Array; begin FP := TCryptoLibUInt32Array.Create($FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $00000000, $00000000, $00000000, $00000001, $FFFFFFFF); @@ -267,11 +269,8 @@ implementation $FFFFFFFF, $FFFFFFFF, $FFFFFFFE, $00000001, $FFFFFFFE, $00000001, $FFFFFFFE, $00000001, $00000001, $FFFFFFFE, $00000002, $FFFFFFFE); - // Value-type field constants for the CT ladder: a = -3 mod p, b3 = 3b mod p. - System.FillChar(FFa, SizeOf(FFa), 0); + // Value-type field constant for the CT ladder: b3 = 3b mod p. System.FillChar(FFb3, SizeOf(FFb3), 0); - LA := FromBigInteger(TBigInteger.Create(1, THexEncoder.Decode('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC'))); - System.Move(LA[0], FFa.W[0], 8 * SizeOf(UInt32)); LB := FromBigInteger(TBigInteger.Create(1, THexEncoder.Decode('5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B'))); LB3 := TNat256.Create; Add(LB, LB, LB3); @@ -474,8 +473,14 @@ class procedure TSecP256R1Field.Multiply(const AX, AY: TFe; var AZ: TFe; var ATT end; class procedure TSecP256R1Field.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +var + Lt, LZero: TFe; begin - Multiply(AX, FFa, AZ, ATT); + // 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); @@ -1375,7 +1380,17 @@ function TSecP256R1Curve.CreateDefaultMultiplier: IECMultiplier; begin LCurve := Self as IECCurve; LFieldOps := TSecP256R1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order); - Result := TFpCTMultiplier.Create(LFieldOps); + 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 } @@ -1453,39 +1468,39 @@ procedure TSecP256R1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array); TNat.Copy(FE_INTS, FOne, 0, AZ, 0); end; -{ TSecP256R1Ops } +{ TSecP256R1FieldArith } -class function TSecP256R1Ops.FieldLimbs: Int32; +class function TSecP256R1FieldArith.FieldLimbs: Int32; begin Result := 8; end; -class procedure TSecP256R1Ops.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP256R1FieldArith.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP256R1Field.Multiply(AX, AY, AZ, ATT); end; -class procedure TSecP256R1Ops.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP256R1FieldArith.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP256R1Field.Square(AX, AZ, ATT); end; -class procedure TSecP256R1Ops.Add(const AX, AY: TFe; var AZ: TFe); +class procedure TSecP256R1FieldArith.Add(const AX, AY: TFe; var AZ: TFe); begin TSecP256R1Field.Add(AX, AY, AZ); end; -class procedure TSecP256R1Ops.Sub(const AX, AY: TFe; var AZ: TFe); +class procedure TSecP256R1FieldArith.Sub(const AX, AY: TFe; var AZ: TFe); begin TSecP256R1Field.Subtract(AX, AY, AZ); end; -class procedure TSecP256R1Ops.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP256R1FieldArith.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP256R1Field.MulByA(AX, AZ, ATT); end; -class procedure TSecP256R1Ops.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP256R1FieldArith.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP256R1Field.MulByB3(AX, AZ, ATT); end; diff --git a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas index a203254b..2b5832a2 100644 --- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas +++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP384R1Custom.pas @@ -27,8 +27,9 @@ interface ClpNat, ClpFpKernelSimd, ClpCTFieldValue, - ClpCTFieldOps, + ClpCTFieldArith, ClpFpCTMultiplier, + ClpFpCombMultiplier, ClpMod, ClpPack, ClpEncoders, @@ -58,7 +59,7 @@ TSecP384R1Field = class sealed(TObject) PExt23 = UInt32($FFFFFFFF); class var FP, FPExt, FPExtInv: TCryptoLibUInt32Array; - FFa, FFb3: TFe; // a (= p-3) and b3 (= 3b) field reps for the CT ladder + 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; @@ -210,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; @@ -239,7 +241,7 @@ TSecP384R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps) end; type - TSecP384R1Ops = class sealed(TCTFieldOpsBase) + TSecP384R1FieldArith = class sealed(TCTFieldArithBase) public class function FieldLimbs: Int32; override; class procedure Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); override; @@ -256,7 +258,7 @@ implementation class constructor TSecP384R1Field.Create; var - LA, LB, LB3: TCryptoLibUInt32Array; + LB, LB3: TCryptoLibUInt32Array; begin FP := TCryptoLibUInt32Array.Create($FFFFFFFF, $00000000, $00000000, $FFFFFFFF, $FFFFFFFE, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF); @@ -268,11 +270,8 @@ implementation $FFFFFFFF, $00000001, $FFFFFFFF, $FFFFFFFD, $FFFFFFFE, $FFFFFFFF, $FFFFFFFF, $FFFFFFFF, $00000001, $FFFFFFFE, $FFFFFFFF, $00000001, $00000002); - // Value-type field constants for the CT ladder: a = -3 mod p, b3 = 3b mod p. - System.FillChar(FFa, SizeOf(FFa), 0); + // Value-type field constant for the CT ladder: b3 = 3b mod p. System.FillChar(FFb3, SizeOf(FFb3), 0); - LA := FromBigInteger(TBigInteger.Create(1, THexEncoder.Decode('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC'))); - System.Move(LA[0], FFa.W[0], 12 * SizeOf(UInt32)); LB := FromBigInteger(TBigInteger.Create(1, THexEncoder.Decode('B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF'))); LB3 := TNat.Create(12); Add(LB, LB, LB3); @@ -458,8 +457,14 @@ class procedure TSecP384R1Field.Multiply(const AX, AY: TFe; var AZ: TFe; var ATT end; class procedure TSecP384R1Field.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +var + Lt, LZero: TFe; begin - Multiply(AX, FFa, AZ, ATT); + // 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); @@ -1376,7 +1381,17 @@ function TSecP384R1Curve.CreateDefaultMultiplier: IECMultiplier; begin LCurve := Self as IECCurve; LFieldOps := TSecP384R1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order); - Result := TFpCTMultiplier.Create(LFieldOps); + 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 } @@ -1455,39 +1470,39 @@ procedure TSecP384R1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array); end; -{ TSecP384R1Ops } +{ TSecP384R1FieldArith } -class function TSecP384R1Ops.FieldLimbs: Int32; +class function TSecP384R1FieldArith.FieldLimbs: Int32; begin Result := 12; end; -class procedure TSecP384R1Ops.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP384R1FieldArith.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP384R1Field.Multiply(AX, AY, AZ, ATT); end; -class procedure TSecP384R1Ops.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP384R1FieldArith.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP384R1Field.Square(AX, AZ, ATT); end; -class procedure TSecP384R1Ops.Add(const AX, AY: TFe; var AZ: TFe); +class procedure TSecP384R1FieldArith.Add(const AX, AY: TFe; var AZ: TFe); begin TSecP384R1Field.Add(AX, AY, AZ); end; -class procedure TSecP384R1Ops.Sub(const AX, AY: TFe; var AZ: TFe); +class procedure TSecP384R1FieldArith.Sub(const AX, AY: TFe; var AZ: TFe); begin TSecP384R1Field.Subtract(AX, AY, AZ); end; -class procedure TSecP384R1Ops.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP384R1FieldArith.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP384R1Field.MulByA(AX, AZ, ATT); end; -class procedure TSecP384R1Ops.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP384R1FieldArith.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP384R1Field.MulByB3(AX, AZ, ATT); end; diff --git a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas index 3a367372..485d9a45 100644 --- a/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas +++ b/CryptoLib/src/Math/EC/Custom/Sec/ClpSecP521R1Custom.pas @@ -27,8 +27,9 @@ interface ClpNat, ClpFpKernelSimd, ClpCTFieldValue, - ClpCTFieldOps, + ClpCTFieldArith, ClpFpCTMultiplier, + ClpFpCombMultiplier, ClpMod, ClpPack, ClpEncoders, @@ -57,7 +58,7 @@ TSecP521R1Field = class sealed(TObject) P16 = UInt32($1FF); class var FP: TCryptoLibUInt32Array; - FFa, FFb3: TFe; // a (= p-3) and b3 (= 3b) field reps for the CT ladder + 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; @@ -202,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; @@ -231,7 +233,7 @@ TSecP521R1FpFieldOps = class sealed(TInterfacedObject, IFpFieldOps) end; type - TSecP521R1Ops = class sealed(TCTFieldOpsBase) + TSecP521R1FieldArith = class sealed(TCTFieldArithBase) public class function FieldLimbs: Int32; override; class procedure Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); override; @@ -248,17 +250,14 @@ implementation class constructor TSecP521R1Field.Create; var - LA, LB, LB3: TCryptoLibUInt32Array; + 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 constants for the CT ladder: a = -3 mod p, b3 = 3b mod p. - System.FillChar(FFa, SizeOf(FFa), 0); + // Value-type field constant for the CT ladder: b3 = 3b mod p. System.FillChar(FFb3, SizeOf(FFb3), 0); - LA := FromBigInteger(TBigInteger.Create(1, THexEncoder.Decode('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC'))); - System.Move(LA[0], FFa.W[0], 17 * SizeOf(UInt32)); LB := FromBigInteger(TBigInteger.Create(1, THexEncoder.Decode('0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00'))); LB3 := TNat.Create(17); Add(LB, LB, LB3); @@ -401,8 +400,14 @@ class procedure TSecP521R1Field.Multiply(const AX, AY: TFe; var AZ: TFe; var ATT end; class procedure TSecP521R1Field.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +var + Lt, LZero: TFe; begin - Multiply(AX, FFa, AZ, ATT); + // 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); @@ -1193,7 +1198,17 @@ function TSecP521R1Curve.CreateDefaultMultiplier: IECMultiplier; begin LCurve := Self as IECCurve; LFieldOps := TSecP521R1FpFieldOps.Create(LCurve.A, LCurve.B, LCurve.Order); - Result := TFpCTMultiplier.Create(LFieldOps); + 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 } @@ -1272,39 +1287,39 @@ procedure TSecP521R1FpFieldOps.FieldOne(const AZ: TCryptoLibUInt32Array); end; -{ TSecP521R1Ops } +{ TSecP521R1FieldArith } -class function TSecP521R1Ops.FieldLimbs: Int32; +class function TSecP521R1FieldArith.FieldLimbs: Int32; begin Result := 17; end; -class procedure TSecP521R1Ops.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP521R1FieldArith.Mul(const AX, AY: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP521R1Field.Multiply(AX, AY, AZ, ATT); end; -class procedure TSecP521R1Ops.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP521R1FieldArith.Sqr(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP521R1Field.Square(AX, AZ, ATT); end; -class procedure TSecP521R1Ops.Add(const AX, AY: TFe; var AZ: TFe); +class procedure TSecP521R1FieldArith.Add(const AX, AY: TFe; var AZ: TFe); begin TSecP521R1Field.Add(AX, AY, AZ); end; -class procedure TSecP521R1Ops.Sub(const AX, AY: TFe; var AZ: TFe); +class procedure TSecP521R1FieldArith.Sub(const AX, AY: TFe; var AZ: TFe); begin TSecP521R1Field.Subtract(AX, AY, AZ); end; -class procedure TSecP521R1Ops.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP521R1FieldArith.MulByA(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP521R1Field.MulByA(AX, AZ, ATT); end; -class procedure TSecP521R1Ops.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); +class procedure TSecP521R1FieldArith.MulByB3(const AX: TFe; var AZ: TFe; var ATT: TFeExt); begin TSecP521R1Field.MulByB3(AX, AZ, ATT); end; diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpCTFieldOps.pas b/CryptoLib/src/Math/EC/Multiplier/ClpCTFieldArith.pas similarity index 94% rename from CryptoLib/src/Math/EC/Multiplier/ClpCTFieldOps.pas rename to CryptoLib/src/Math/EC/Multiplier/ClpCTFieldArith.pas index d771c26c..06b8a6f3 100644 --- a/CryptoLib/src/Math/EC/Multiplier/ClpCTFieldOps.pas +++ b/CryptoLib/src/Math/EC/Multiplier/ClpCTFieldArith.pas @@ -14,7 +14,7 @@ (* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *) -unit ClpCTFieldOps; +unit ClpCTFieldArith; {$I ..\..\..\Include\CryptoLib.inc} @@ -27,9 +27,9 @@ interface /// /// Per-curve prime-field arithmetic as a base of virtual; abstract /// class methods over records; a curve overrides each op - /// and the generic TCTLadder<TOps: TCTFieldOpsBase> dispatches to it. + /// and the generic TCTPoint<TOps: TCTFieldArithBase> dispatches to it. /// - TCTFieldOpsBase = class + TCTFieldArithBase = class public /// uint32 limb count N for this curve (P-256 = 8). class function FieldLimbs: Int32; virtual; abstract; diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpCTLadder.pas b/CryptoLib/src/Math/EC/Multiplier/ClpCTPoint.pas similarity index 54% rename from CryptoLib/src/Math/EC/Multiplier/ClpCTLadder.pas rename to CryptoLib/src/Math/EC/Multiplier/ClpCTPoint.pas index ee8d6b44..222d7b61 100644 --- a/CryptoLib/src/Math/EC/Multiplier/ClpCTLadder.pas +++ b/CryptoLib/src/Math/EC/Multiplier/ClpCTPoint.pas @@ -14,28 +14,36 @@ (* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *) -unit ClpCTLadder; +unit ClpCTPoint; {$I ..\..\..\Include\CryptoLib.inc} interface uses + ClpNat, + ClpBitOperations, + ClpIFpFieldOps, + ClpIECFieldElement, ClpCTFieldValue, - ClpCTFieldOps; + ClpCTFieldArith, + ClpCryptoLibTypes; type /// - /// Generic constant-time point arithmetic over a per-curve ops class: the - /// RCB2016 complete addition (Algorithm 1) and doubling (Algorithm 3) for Fp - /// short-Weierstrass curves in homogeneous coordinates. 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 windowed scalar loop that drives these lives in - /// TFpCTMultiplier. + /// 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. /// - TCTLadder = class sealed + TCTPoint = class sealed public /// RCB2016 complete addition (Algorithm 1, explicit a and b3). /// AR := AP + AQ in homogeneous coordinates; all temporaries are stack @@ -43,11 +51,23 @@ TCTLadder = class sealed 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 TCTLadder.PointAdd(const AP, AQ: TFePoint; var AR: TFePoint); +class procedure TCTPoint.PointAdd(const AP, AQ: TFePoint; var AR: TFePoint); var Lt0, Lt1, Lt2, Lt3, Lt4, Lt5, LX3, LY3, LZ3: TFe; LTT: TFeExt; @@ -97,7 +117,7 @@ class procedure TCTLadder.PointAdd(const AP, AQ: TFePoint; var AR: TFePoin AR.Z := LZ3; end; -class procedure TCTLadder.PointDouble(const AP: TFePoint; var AR: TFePoint); +class procedure TCTPoint.PointDouble(const AP: TFePoint; var AR: TFePoint); var Lt0, Lt1, Lt2, Lt3, LX3, LY3, LZ3: TFe; LTT: TFeExt; @@ -138,4 +158,92 @@ class procedure TCTLadder.PointDouble(const AP: TFePoint; var AR: TFePoint 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/ClpFpCTMultiplier.pas b/CryptoLib/src/Math/EC/Multiplier/ClpFpCTMultiplier.pas index bb2dc7bb..14b9877c 100644 --- a/CryptoLib/src/Math/EC/Multiplier/ClpFpCTMultiplier.pas +++ b/CryptoLib/src/Math/EC/Multiplier/ClpFpCTMultiplier.pas @@ -33,8 +33,8 @@ interface ClpIECFieldElement, ClpIECCommon, ClpCTFieldValue, - ClpCTFieldOps, - ClpCTLadder, + ClpCTFieldArith, + ClpCTPoint, ClpCryptoLibTypes, ClpCryptoLibExceptions; @@ -50,11 +50,11 @@ interface /// projective coordinates, /// fixed processing length, masked table lookups, one unconditional add per /// window), but the hot loop runs over stack records via - /// the generic TCTLadder<TOps> — no per-operation heap allocation + /// 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. /// - TFpCTMultiplier = class sealed(TAbstractECMultiplier, IECMultiplier) + TFpCTMultiplier = class sealed(TAbstractECMultiplier, IECMultiplier) strict private const WINDOW_BITS = Int32(4); @@ -67,13 +67,7 @@ TFpCTMultiplier = class sealed(TAbstractECMultiplier, I FBlindBits: Int32; function GetRandom: ISecureRandom; procedure GenerateBlind(const ARandom: ISecureRandom; const AZ: TCryptoLibUInt32Array); - procedure OneFe(var AZ: TFe); - procedure Infinity(var AR: TFePoint); - procedure FromAffine(const AXa, AYa: TCryptoLibUInt32Array; var AR: TFePoint); procedure ScaleRandom(const AP: TFePoint; const ALambda: TFe; var AR: TFePoint); - procedure ToAffine(const AP: TFePoint; const AXa, AYa: TCryptoLibUInt32Array; - out AIsInfinity: Boolean); - procedure SelectEntry(const ATable: array of TFePoint; AIndex: Int32; var AR: TFePoint); strict protected function MultiplyPositive(const AP: IECPoint; const AK: TBigInteger): IECPoint; override; public @@ -126,34 +120,6 @@ procedure TFpCTMultiplier.GenerateBlind(const ARandom: ISecureRandom; TPack.LE_To_UInt32(LBytes, 0, AZ, 0, FBlindBits div 32); end; -procedure TFpCTMultiplier.OneFe(var AZ: TFe); -var - LArr: TCryptoLibUInt32Array; -begin - LArr := TNat.Create(FFieldOps.GetFieldInts); - FFieldOps.FieldOne(LArr); - FillChar(AZ, SizeOf(AZ), 0); - Move(LArr[0], AZ.W[0], FFieldOps.GetFieldInts * SizeOf(UInt32)); -end; - -procedure TFpCTMultiplier.Infinity(var AR: TFePoint); -begin - FillChar(AR, SizeOf(AR), 0); - OneFe(AR.Y); -end; - -procedure TFpCTMultiplier.FromAffine(const AXa, AYa: TCryptoLibUInt32Array; - var AR: TFePoint); -var - LN: Int32; -begin - LN := FFieldOps.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(AR.Z); -end; - procedure TFpCTMultiplier.ScaleRandom(const AP: TFePoint; const ALambda: TFe; var AR: TFePoint); var @@ -164,52 +130,6 @@ procedure TFpCTMultiplier.ScaleRandom(const AP: TFePoint; const ALambda: T TOps.Mul(AP.Z, ALambda, AR.Z, LTT); end; -procedure TFpCTMultiplier.ToAffine(const AP: TFePoint; - const AXa, AYa: TCryptoLibUInt32Array; out AIsInfinity: Boolean); -var - LN: Int32; - LZarr, LZInvArr: TCryptoLibUInt32Array; - LZInv, LTmp: TFe; - LTT: TFeExt; -begin - LN := FFieldOps.GetFieldInts; - LZarr := TNat.Create(LN); - Move(AP.Z.W[0], LZarr[0], LN * SizeOf(UInt32)); - AIsInfinity := FFieldOps.IsZero(LZarr); - if AIsInfinity then - Exit; - LZInvArr := TNat.Create(LN); - FFieldOps.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; - -procedure TFpCTMultiplier.SelectEntry(const ATable: array of TFePoint; - AIndex: Int32; var AR: TFePoint); -var - LN, LI, LJ: Int32; - LMask: UInt32; - LEntry: TFePoint; -begin - LN := FFieldOps.GetFieldInts; - FillChar(AR, SizeOf(AR), 0); - for LI := 0 to TABLE_SIZE - 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; - function TFpCTMultiplier.MultiplyPositive(const AP: IECPoint; const AK: TBigInteger): IECPoint; var @@ -244,15 +164,15 @@ function TFpCTMultiplier.MultiplyPositive(const AP: IECPoint; FFieldOps.RandomMult(LRandom, LLambdaArr); FillChar(LLambda, SizeOf(LLambda), 0); Move(LLambdaArr[0], LLambda.W[0], LFieldInts * SizeOf(UInt32)); - FromAffine(LXa, LYa, LBase); + TCTPoint.FromAffine(FFieldOps, LXa, LYa, LBase); ScaleRandom(LBase, LLambda, LBase); // projective precomputation table [0]=O, [i]=[i]*base SetLength(LTable, TABLE_SIZE); - Infinity(LTable[0]); + TCTPoint.Infinity(FFieldOps, LTable[0]); LTable[1] := LBase; for LI := 2 to TABLE_SIZE - 1 do - TCTLadder.PointAdd(LTable[LI - 1], LBase, LTable[LI]); + TCTPoint.PointAdd(LTable[LI - 1], LBase, LTable[LI]); // scalar blinding in fixed-width Nat: k' = k + r*n LScalarBits := FFieldOps.GetOrderBits + FBlindBits + 1; @@ -270,11 +190,11 @@ function TFpCTMultiplier.MultiplyPositive(const AP: IECPoint; try // fixed-length windowed ladder LWindows := (LScalarBits + WINDOW_BITS - 1) div WINDOW_BITS; - Infinity(LAcc); + TCTPoint.Infinity(FFieldOps, LAcc); for LI := LWindows - 1 downto 0 do begin for LJ := 0 to WINDOW_BITS - 1 do - TCTLadder.PointDouble(LAcc, LAcc); + TCTPoint.PointDouble(LAcc, LAcc); // WINDOW_BITS divides 32, so a digit never spans a limb boundary LBit := LI * WINDOW_BITS; @@ -282,11 +202,11 @@ function TFpCTMultiplier.MultiplyPositive(const AP: IECPoint; LShift := LBit and 31; LDigit := Int32((LKPrime[LLimb] shr LShift) and UInt32(TABLE_SIZE - 1)); - SelectEntry(LTable, LDigit, LSel); - TCTLadder.PointAdd(LAcc, LSel, LAcc); + TCTPoint.SelectEntry(FFieldOps, LTable, TABLE_SIZE, LDigit, LSel); + TCTPoint.PointAdd(LAcc, LSel, LAcc); end; - ToAffine(LAcc, LXa, LYa, LIsInfinity); + TCTPoint.ToAffine(FFieldOps, LAcc, LXa, LYa, LIsInfinity); if LIsInfinity then Exit(AP.Curve.Infinity); diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpFpCombMultiplier.pas b/CryptoLib/src/Math/EC/Multiplier/ClpFpCombMultiplier.pas new file mode 100644 index 00000000..f8833f12 --- /dev/null +++ b/CryptoLib/src/Math/EC/Multiplier/ClpFpCombMultiplier.pas @@ -0,0 +1,157 @@ +{ *********************************************************************************** } +{ * 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, + 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: IFixedPointPreCompInfo; + 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: 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; + + if LInfo <> FCachedInfo 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/Packages/Delphi/CryptoLib4PascalPackage.dpk b/CryptoLib/src/Packages/Delphi/CryptoLib4PascalPackage.dpk index 1d246305..d4e427d8 100644 --- a/CryptoLib/src/Packages/Delphi/CryptoLib4PascalPackage.dpk +++ b/CryptoLib/src/Packages/Delphi/CryptoLib4PascalPackage.dpk @@ -405,9 +405,10 @@ contains ClpMultipliers in '..\..\Math\EC\Multiplier\ClpMultipliers.pas', ClpIFpFieldOps in '..\..\Interfaces\Math\EC\Multiplier\ClpIFpFieldOps.pas', ClpCTFieldValue in '..\..\Math\EC\Multiplier\ClpCTFieldValue.pas', - ClpCTFieldOps in '..\..\Math\EC\Multiplier\ClpCTFieldOps.pas', - ClpCTLadder in '..\..\Math\EC\Multiplier\ClpCTLadder.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 d7fc2eb5..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 "/> - + @@ -3510,17 +3510,21 @@ 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 5c6a0779..ab0d3c54 100644 --- a/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.pas +++ b/CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.pas @@ -279,12 +279,12 @@ interface ClpPkixCrlRevocationChecker, ClpPkixRevocationChecker, ClpPkixAttrCertChecker, ClpRfc3281CertPathUtilities, ClpPkixAttrCertPathValidator, ClpPkixAttrCertPathBuilder, - ClpCryptoLibExceptions, ClpAesBitSlicedEngine, - ClpIFpFieldOps, ClpIF2mFieldOps, - ClpLopezDahabLadder, ClpGaloisFieldUtilities, ClpAbstractBlockCipherMode, - ClpAbstractAeadCipher, ClpAbstractAeadBlockCipher, ClpFpKernelX86Backend, - ClpFpKernelSimd, ClpFpKernelArmBackend, ClpCTFieldValue, ClpCTFieldOps, - ClpCTLadder, ClpFpCTMultiplier; + ClpCryptoLibExceptions, ClpAesBitSlicedEngine, ClpIFpFieldOps, + ClpIF2mFieldOps, ClpLopezDahabLadder, ClpGaloisFieldUtilities, + ClpAbstractBlockCipherMode, ClpAbstractAeadCipher, + ClpAbstractAeadBlockCipher, ClpFpKernelX86Backend, ClpFpKernelSimd, + ClpFpKernelArmBackend, ClpCTFieldValue, ClpCTFieldArith, ClpCTPoint, + ClpFpCTMultiplier, ClpFpCombMultiplier; implementation From 6adc5e1314d947a24aba272b6c0419edefc5841c Mon Sep 17 00:00:00 2001 From: Ugochukwu Mmaduekwe Date: Fri, 14 Aug 2026 05:07:20 +0100 Subject: [PATCH 4/5] Rsa Key valaidation speedup --- .../Crypto/Parameters/ClpRsaParameters.pas | 140 +++++++++++++++--- 1 file changed, 123 insertions(+), 17 deletions(-) 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; From 071ce38fa630e780cc4a4b47f2bdd5192d1fc70d Mon Sep 17 00:00:00 2001 From: Ugochukwu Mmaduekwe Date: Fri, 14 Aug 2026 08:44:22 +0100 Subject: [PATCH 5/5] Fix comb-multiplier leak + weak-ref idiom; X509 lazy-cache thread-safety - ClpFpCombMultiplier: hold the fixed-point precomp info as a TWeakRef to break the curve <-> comb reference cycle that leaked the curve/comb/adapter/table. - ClpPkixPolicyNode: use the implicit TWeakRef operators, matching convention. - ClpX509Certificate: thread-safe lazy caches (SigAlgName/PublicKey/ CachedEncoding) + Equals fast-path via a class-level critical section with double-checked locking. --- .../EC/Multiplier/ClpFpCombMultiplier.pas | 8 +- CryptoLib/src/Pkix/ClpPkixPolicyNode.pas | 6 +- CryptoLib/src/X509/ClpX509Certificate.pas | 126 +++++++++++++++--- 3 files changed, 112 insertions(+), 28 deletions(-) diff --git a/CryptoLib/src/Math/EC/Multiplier/ClpFpCombMultiplier.pas b/CryptoLib/src/Math/EC/Multiplier/ClpFpCombMultiplier.pas index f8833f12..396c85c5 100644 --- a/CryptoLib/src/Math/EC/Multiplier/ClpFpCombMultiplier.pas +++ b/CryptoLib/src/Math/EC/Multiplier/ClpFpCombMultiplier.pas @@ -28,6 +28,7 @@ interface ClpMultipliers, ClpFixedPointUtilities, ClpIFixedPointPreCompInfo, + ClpWeakRef, ClpIFpFieldOps, ClpIECFieldElement, ClpIECCommon, @@ -55,7 +56,7 @@ TFpCombMultiplier = class sealed(TAbstractECMultiplie IECMultiplier) strict private FFieldOps: IFpFieldOps; - FCachedInfo: IFixedPointPreCompInfo; + FCachedInfo: TWeakRef; FTable: TCryptoLibGenericArray; FOffset: TFePoint; strict protected @@ -79,7 +80,7 @@ function TFpCombMultiplier.MultiplyPositive(const AP: IECPoint; var LC: IECCurve; LSize, LWidth, LD, LFullComb, LN, LI, LJ: Int32; - LInfo: IFixedPointPreCompInfo; + LInfo, LCachedInfo: IFixedPointPreCompInfo; LLookup: IECLookupTable; LR, LSel: TFePoint; LK, LXa, LYa: TCryptoLibUInt32Array; @@ -98,7 +99,8 @@ function TFpCombMultiplier.MultiplyPositive(const AP: IECPoint; LD := (LSize + LWidth - 1) div LWidth; LFullComb := LD * LWidth; - if LInfo <> FCachedInfo then + 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 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 '' 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;