From fa75e69a46cf5d43b2eb68b4cbafd235903fe8a2 Mon Sep 17 00:00:00 2001 From: Ugochukwu Mmaduekwe Date: Thu, 28 May 2026 00:37:41 +0100 Subject: [PATCH] Improve DH safe-prime generation and RSA modulus validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve DH safe-prime generation and RSA modulus validation. Move small-prime screening and CreateRandomPrime into BigIntegerUtilities, add external RSA modulus checks (size, small factors, enhanced MR) with AIsInternal for generated keys, and expose MaxSize/MaxMRTests as static class properties. Rewrite DH safe-prime generation for g=2 with Pocklington/Rabin–Miller screening. --- CryptoLib.Tests/src/Crypto/RSATests.pas | 63 ++++++ .../src/Crypto/Generators/ClpDHGenerators.pas | 7 +- .../Generators/ClpDHParametersHelper.pas | 192 +++++++++--------- .../Crypto/Generators/ClpRsaGenerators.pas | 9 +- .../Crypto/Parameters/ClpRsaParameters.pas | 155 ++++++++++---- CryptoLib/src/Math/ClpBigIntegerUtilities.pas | 47 +++++ 6 files changed, 321 insertions(+), 152 deletions(-) diff --git a/CryptoLib.Tests/src/Crypto/RSATests.pas b/CryptoLib.Tests/src/Crypto/RSATests.pas index a70439fa..4b2f9374 100644 --- a/CryptoLib.Tests/src/Crypto/RSATests.pas +++ b/CryptoLib.Tests/src/Crypto/RSATests.pas @@ -89,6 +89,9 @@ TTestRSA = class(TCryptoLibAlgorithmTestCase) procedure TestRsaSignature; procedure TestRsaPublicKeyInfoEncodingHasNullParameters; procedure TestSubjectPublicKeyInfoFactoryRsaConsistency; + procedure TestMaxSizeRejectsOversizedModulus; + procedure TestMaxMRTestsZeroSkipsCompositeCheck; + procedure TestMaxSizeMaxMRTestsUnsetDefault; end; @@ -443,6 +446,66 @@ procedure TTestRSA.TestSubjectPublicKeyInfoFactoryRsaConsistency; 'RSA AlgorithmIdentifier parameters should be DerNull.'); end; +procedure TTestRSA.TestMaxSizeRejectsOversizedModulus; +var + LOldMaxSize, LOldMaxMRTests: Int32; +begin + LOldMaxSize := TRsaKeyParameters.MaxSize; + LOldMaxMRTests := TRsaKeyParameters.MaxMRTests; + try + TRsaKeyParameters.MaxSize := 512; + CheckTrue(FModulus.BitLength > 512, 'test modulus must exceed MaxSize cap'); + try + TRsaKeyParameters.Create(False, FModulus, FPubExp); + Fail('expected EArgumentCryptoLibException for oversized modulus'); + except + on E: EArgumentCryptoLibException do + CheckEquals('RSA modulus out of range', E.Message); + end; + finally + TRsaKeyParameters.MaxSize := LOldMaxSize; + TRsaKeyParameters.MaxMRTests := LOldMaxMRTests; + end; +end; + +procedure TTestRSA.TestMaxMRTestsZeroSkipsCompositeCheck; +var + LOldMaxSize, LOldMaxMRTests: Int32; + LParams: TRsaKeyParameters; +begin + LOldMaxSize := TRsaKeyParameters.MaxSize; + LOldMaxMRTests := TRsaKeyParameters.MaxMRTests; + try + TRsaKeyParameters.MaxMRTests := 0; + LParams := TRsaKeyParameters.Create(False, FModulus, FPubExp); + CheckTrue(LParams.Modulus.Equals(FModulus), 'modulus should be accepted when MR is disabled'); + finally + TRsaKeyParameters.MaxSize := LOldMaxSize; + TRsaKeyParameters.MaxMRTests := LOldMaxMRTests; + end; +end; + +procedure TTestRSA.TestMaxSizeMaxMRTestsUnsetDefault; +var + LOldMaxSize, LOldMaxMRTests: Int32; + LParams: TRsaKeyParameters; +begin + LOldMaxSize := TRsaKeyParameters.MaxSize; + LOldMaxMRTests := TRsaKeyParameters.MaxMRTests; + try + TRsaKeyParameters.MaxSize := -1; + TRsaKeyParameters.MaxMRTests := -1; + CheckEquals(-1, TRsaKeyParameters.MaxSize, 'unset MaxSize should be -1'); + CheckEquals(-1, TRsaKeyParameters.MaxMRTests, 'unset MaxMRTests should be -1'); + LParams := TRsaKeyParameters.Create(False, FModulus, FPubExp); + CheckTrue(LParams.Modulus.Equals(FModulus), + 'default limits should accept standard test modulus'); + finally + TRsaKeyParameters.MaxSize := LOldMaxSize; + TRsaKeyParameters.MaxMRTests := LOldMaxMRTests; + end; +end; + initialization {$IFDEF FPC} diff --git a/CryptoLib/src/Crypto/Generators/ClpDHGenerators.pas b/CryptoLib/src/Crypto/Generators/ClpDHGenerators.pas index 80b91886..962c1dea 100644 --- a/CryptoLib/src/Crypto/Generators/ClpDHGenerators.pas +++ b/CryptoLib/src/Crypto/Generators/ClpDHGenerators.pas @@ -115,10 +115,13 @@ function TDHParametersGenerator.GenerateParameters: IDHParameters; LSafePrimes: TCryptoLibGenericArray; LP, LQ, LG: TBigInteger; begin - LSafePrimes := TDHParametersHelper.GenerateSafePrimes(FSize, FCertainty, FRandom); + LSafePrimes := TDHParametersHelper.GenerateSafePrimes(FSize, FCertainty, FRandom, True); LP := LSafePrimes[0]; LQ := LSafePrimes[1]; - LG := TDHParametersHelper.SelectGenerator(LP, LQ, FRandom); +{$IFDEF DEBUG} + Assert((LP.Int32ValueExact and 7) = 7); +{$ENDIF DEBUG} + LG := TBigInteger.Two; Result := TDHParameters.Create(LP, LG, LQ, TBigInteger.Two, nil); end; diff --git a/CryptoLib/src/Crypto/Generators/ClpDHParametersHelper.pas b/CryptoLib/src/Crypto/Generators/ClpDHParametersHelper.pas index 92df68c7..64438182 100644 --- a/CryptoLib/src/Crypto/Generators/ClpDHParametersHelper.pas +++ b/CryptoLib/src/Crypto/Generators/ClpDHParametersHelper.pas @@ -23,18 +23,22 @@ interface uses ClpISecureRandom, ClpBigInteger, - ClpBigIntegerUtilities, ClpWNafUtilities, ClpBitOperations, ClpCryptoLibTypes; +resourcestring + SSizeTooSmall = 'size < 64'; + type TDHParametersHelper = class sealed(TObject) strict private class var - FSix: TBigInteger; + FTwo: TBigInteger; + FTwelve: TBigInteger; + FTwentyFour: TBigInteger; FPrimeProducts: TCryptoLibInt32Array; FPrimeLists: TCryptoLibMatrixInt32Array; FBigPrimeProducts: TCryptoLibGenericArray; @@ -43,6 +47,8 @@ TDHParametersHelper = class sealed(TObject) class function ConstructBigPrimeProducts(const APrimeProducts : TCryptoLibInt32Array): TCryptoLibGenericArray; static; + class function HasAnySmallFactorsSafe(const X: TBigInteger): Boolean; static; + class procedure Boot(); static; class constructor DHParametersHelper(); @@ -50,27 +56,15 @@ TDHParametersHelper = class sealed(TObject) public /// - /// - /// Finds a pair of prime BigInteger's {p, q: p = 2q + 1} - /// - /// - /// (see: Handbook of Applied Cryptography 4.86) - /// - /// - class function GenerateSafePrimes(ASize, ACertainty: Int32; - const ARandom: ISecureRandom): TCryptoLibGenericArray; static; - - /// - /// - /// Select a high order element of the multiplicative group Zp* - /// - /// - /// p and q must be s.t. p = 2*q + 1, where p and q are prime (see - /// generateSafePrimes) - /// + /// Finds a pair of prime BigInteger's {p, q: p = 2q + 1}. /// - class function SelectGenerator(const AP, AQ: TBigInteger; - const ARandom: ISecureRandom): TBigInteger; static; + /// + /// See: Handbook of Applied Cryptography 4.86. If AForGenerator2 is true, the + /// returned p will also have 2 as a quadratic residue (p === 7 mod 8). + /// + class function GenerateSafePrimes(ABitLength, ACertainty: Int32; + const ARandom: ISecureRandom; AForGenerator2: Boolean) + : TCryptoLibGenericArray; static; end; implementation @@ -81,7 +75,9 @@ class procedure TDHParametersHelper.Boot; begin if not FIsBooted then begin - FSix := TBigInteger.ValueOf(6); + FTwo := TBigInteger.Two; + FTwelve := TBigInteger.ValueOf(12); + FTwentyFour := TBigInteger.ValueOf(24); FPrimeLists := TBigInteger.primeLists; FPrimeProducts := TBigInteger.primeProducts; @@ -112,105 +108,103 @@ class function TDHParametersHelper.ConstructBigPrimeProducts(const APrimeProduct Result := LBpp; end; -class function TDHParametersHelper.GenerateSafePrimes(ASize, ACertainty: Int32; - const ARandom: ISecureRandom): TCryptoLibGenericArray; +class function TDHParametersHelper.HasAnySmallFactorsSafe(const X: TBigInteger): Boolean; var - LP, LQ: TBigInteger; - LQLength, LMinWeight, LI, LTest, LRem3, LDiff, LJ, LPrime, LQRem: Int32; - LRetryFlag: Boolean; + LI, LJ, LR, LPrime: Int32; LPrimeList: TCryptoLibInt32Array; begin - LRetryFlag := False; - LQLength := ASize - 1; - LMinWeight := TBitOperations.Asr32(ASize, 2); - - if ASize <= 32 then + for LI := 0 to System.Pred(System.Length(FPrimeLists)) do begin - while True do + LR := X.Remainder(FBigPrimeProducts[LI]).Int32ValueExact; + + LPrimeList := FPrimeLists[LI]; + for LJ := 0 to System.Pred(System.Length(LPrimeList)) do begin - LQ := TBigInteger.Create(LQLength, 2, ARandom); + LPrime := LPrimeList[LJ]; + if (LR mod LPrime) < 2 then + Exit(True); + end; + end; - LP := LQ.ShiftLeft(1).Add(TBigInteger.One); + Result := False; +end; - if not LP.IsProbablePrime(ACertainty, True) then - Continue; +class function TDHParametersHelper.GenerateSafePrimes(ABitLength, ACertainty: Int32; + const ARandom: ISecureRandom; AForGenerator2: Boolean) + : TCryptoLibGenericArray; +var + LP, LQ, LStep: TBigInteger; + LLowBitsSet, LInc3, LMinWeight, LByteLength, LExtraBits, LCount, LPMod3: Int32; + LBytes: TCryptoLibByteArray; +begin + if ABitLength < 64 then + raise EArgumentCryptoLibException.CreateRes(@SSizeTooSmall); - if (ACertainty > 2) and (not LQ.IsProbablePrime(ACertainty, True)) then - Continue; + LLowBitsSet := $03; + LInc3 := 4; + LStep := FTwelve; - Break; - end; - end - else + if AForGenerator2 then begin - while True do - begin - LQ := TBigInteger.Create(LQLength, 0, ARandom); + LLowBitsSet := $07; + LInc3 := -8; + LStep := FTwentyFour; + end; - LI := 0; - while LI < System.Length(FPrimeLists) do - begin - LTest := LQ.Remainder(FBigPrimeProducts[LI]).Int32Value; + LMinWeight := TBitOperations.Asr32(ABitLength, 2); + LByteLength := (ABitLength + 7) div 8; + LExtraBits := LByteLength * 8 - ABitLength; - if LI = 0 then - begin - LRem3 := LTest mod 3; - if LRem3 <> 2 then - begin - LDiff := (2 * LRem3) + 2; - LQ := LQ.Add(TBigInteger.ValueOf(LDiff)); - LTest := (LTest + LDiff) mod FPrimeProducts[LI]; - end; - end; + System.SetLength(LBytes, LByteLength); - LPrimeList := FPrimeLists[LI]; - for LJ := 0 to System.Pred(System.Length(LPrimeList)) do - begin - LPrime := LPrimeList[LJ]; - LQRem := LTest mod LPrime; - if (LQRem = 0) or (LQRem = TBitOperations.Asr32(LPrime, 1)) then - begin - LQ := LQ.Add(FSix); - LRetryFlag := True; - Break; - end; - end; - - if LRetryFlag then - begin - LI := 0; - LRetryFlag := False; - end - else - System.Inc(LI); - end; + while True do + begin + ARandom.NextBytes(LBytes); - if LQ.BitLength <> LQLength then - Continue; + LBytes[0] := (LBytes[0] and Byte($FF shr LExtraBits)) or Byte($80 shr LExtraBits); + LBytes[System.Pred(LByteLength)] := LBytes[System.Pred(LByteLength)] or Byte(LLowBitsSet); - if not LQ.RabinMillerTest(2, ARandom, True) then - Continue; + LP := TBigInteger.Create(1, LBytes); - LP := LQ.ShiftLeft(1).Add(TBigInteger.One); + LPMod3 := LP.&Mod(TBigInteger.Three).Int32ValueExact; + if LPMod3 <> 2 then + LP := LP.Add(TBigInteger.ValueOf((2 - LPMod3) * LInc3)); - if not LP.RabinMillerTest(ACertainty, ARandom, True) then - Continue; + LCount := 0; + while LCount < 256 do + begin + System.Inc(LCount); + if LP.BitLength <> ABitLength then + Break; - if (ACertainty > 2) and (not LQ.RabinMillerTest(ACertainty - 2, ARandom, True)) then - Continue; + if not HasAnySmallFactorsSafe(LP) then + begin + // NOTE: Pocklington criterion: Fermat test suffices to prove p prime given q is prime + if FTwo.ModPow(LP, LP).Equals(FTwo) then + begin + LQ := LP.ShiftRight(1); + if LQ.RabinMillerTest(ACertainty, ARandom, True) then + begin + if TWNafUtilities.GetNafWeight(LP) >= LMinWeight then + begin + Result := TCryptoLibGenericArray.Create(LP, LQ); + Exit; + end; + end; + end; - if TWNafUtilities.GetNafWeight(LP) < LMinWeight then - Continue; + Break; + end; - Break; + LP := LP.Add(LStep); end; end; - - Result := TCryptoLibGenericArray.Create(LP, LQ); end; -{$IFNDEF _FIXINSIGHT_} - +{ +// Select a high order element of the multiplicative group Zp* +// (see generateSafePrimes). Superseded by fixed generator g = 2 when +// GenerateSafePrimes is called with AForGenerator2 = true. class function TDHParametersHelper.SelectGenerator(const AP, AQ: TBigInteger; const ARandom: ISecureRandom): TBigInteger; var @@ -225,6 +219,6 @@ class function TDHParametersHelper.SelectGenerator(const AP, AQ: TBigInteger; Result := LG; end; -{$ENDIF} +} end. diff --git a/CryptoLib/src/Crypto/Generators/ClpRsaGenerators.pas b/CryptoLib/src/Crypto/Generators/ClpRsaGenerators.pas index 32aeb849..d7ef0787 100644 --- a/CryptoLib/src/Crypto/Generators/ClpRsaGenerators.pas +++ b/CryptoLib/src/Crypto/Generators/ClpRsaGenerators.pas @@ -112,11 +112,9 @@ function TRsaKeyPairGenerator.ChooseRandomPrime(ABitLength: Int32; (TArrayUtilities.Contains(FSpecialEValues, AE.Int32Value)); while True do begin - LP := TBigInteger.Create(ABitLength, 1, FParam.Random); + LP := TBigIntegerUtilities.CreateRandomPrime(ABitLength, FParam.Certainty, FParam.Random); if LP.&Mod(AE).Equals(TBigInteger.One) then Continue; - if not LP.IsProbablePrime(FParam.Certainty, True) then - Continue; if not LEIsKnownOddPrime then begin LPSub1 := LP.Subtract(TBigInteger.One); @@ -183,9 +181,8 @@ function TRsaKeyPairGenerator.GenerateKeyPair: IAsymmetricCipherKeyPair; LDP := LD.Remainder(LPSub1); LDQ := LD.Remainder(LQSub1); LQInv := TBigIntegerUtilities.ModOddInverse(LP, LQ); - LPubKey := TRsaKeyParameters.Create(False, LN, LE) as IRsaKeyParameters; - LPrivKey := TRsaPrivateCrtKeyParameters.Create(LN, LE, LD, LP, LQ, LDP, LDQ, LQInv) - as IRsaPrivateCrtKeyParameters; + LPubKey := TRsaKeyParameters.Create(False, LN, LE, True); + LPrivKey := TRsaPrivateCrtKeyParameters.Create(LN, LE, LD, LP, LQ, LDP, LDQ, LQInv, True); Result := TAsymmetricCipherKeyPair.Create(LPubKey, LPrivKey); Exit; end; diff --git a/CryptoLib/src/Crypto/Parameters/ClpRsaParameters.pas b/CryptoLib/src/Crypto/Parameters/ClpRsaParameters.pas index dd031734..4d768436 100644 --- a/CryptoLib/src/Crypto/Parameters/ClpRsaParameters.pas +++ b/CryptoLib/src/Crypto/Parameters/ClpRsaParameters.pas @@ -24,6 +24,8 @@ interface SysUtils, ClpBigInteger, ClpBigIntegerUtilities, + ClpPrimes, + ClpCryptoServicesRegistrar, ClpIRsaParameters, ClpAsymmetricKeyParameter, ClpKeyGenerationParameters, @@ -38,6 +40,7 @@ interface SRsaModulusIsEven = 'RSA modulus is even'; SRsaModulusOutOfRange = 'RSA modulus out of range'; SRsaModulusHasSmallPrimeFactor = 'RSA modulus has a small prime factor'; + SRsaModulusIsNotComposite = 'RSA modulus is not composite'; SRsaPublicExponentIsEven = 'RSA publicExponent is even'; SNotValidRsaPValue = 'Not a valid RSA P value'; SNotValidRsaQValue = 'Not a valid RSA Q value'; @@ -52,26 +55,50 @@ interface TRsaKeyParameters = class(TAsymmetricKeyParameter, IRsaKeyParameters) strict private - class var - FSmallPrimesProduct: TBigInteger; - - class constructor CreateRsaKeyParameters(); - - class function HasAnySmallFactors(const AModulus: TBigInteger): Boolean; static; - class function Validate(const AModulus: TBigInteger): TBigInteger; static; - - strict private - var + /// + /// Default maximum RSA modulus bit length when is unset (-1). + /// + const + DefaultMaxBitLength = 16384; + class var + FMaxSize: Int32; + FMaxMRTests: Int32; + var FModulus: TBigInteger; FExponent: TBigInteger; + class constructor Create; + + class function Validate(const AModulus: TBigInteger; AIsInternal: Boolean): TBigInteger; static; + class function GetEffectiveMaxSize: Int32; static; + class function GetEffectiveMaxMRTests(ABits: Int32): Int32; static; + class function GetMRIterations(ABits: Int32): Int32; static; + strict protected function GetModulus: TBigInteger; function GetExponent: TBigInteger; public + + class function ValidateModulus(const AModulus: TBigInteger): TBigInteger; static; + + /// + /// Maximum allowed RSA modulus bit length for externally supplied keys. + /// Unset (-1) or any negative value selects . + /// + class property MaxSize: Int32 read FMaxSize write FMaxSize; + + /// + /// Enhanced Miller-Rabin iteration count for externally supplied moduli. + /// Unset (-1) or any negative value selects a bit-length-dependent default; + /// 0 disables composite testing. + /// + class property MaxMRTests: Int32 read FMaxMRTests write FMaxMRTests; + + constructor Create(AIsPrivate: Boolean; + const AModulus, AExponent: TBigInteger); overload; constructor Create(AIsPrivate: Boolean; - const AModulus, AExponent: TBigInteger); + const AModulus, AExponent: TBigInteger; AIsInternal: Boolean); overload; function Equals(const AOther: IRsaKeyParameters): Boolean; reintroduce; overload; @@ -107,7 +134,9 @@ TRsaPrivateCrtKeyParameters = class(TRsaKeyParameters, IRsaPrivateCrtKeyParame public constructor Create(const AModulus, APublicExponent, APrivateExponent, - AP, AQ, ADP, ADQ, AQInv: TBigInteger); + AP, AQ, ADP, ADQ, AQInv: TBigInteger); overload; + constructor Create(const AModulus, APublicExponent, APrivateExponent, + AP, AQ, ADP, ADQ, AQInv: TBigInteger; AIsInternal: Boolean); overload; function Equals(const AOther: IRsaPrivateCrtKeyParameters): Boolean; reintroduce; overload; @@ -179,53 +208,83 @@ TRsaBlindingParameters = class(TInterfacedObject, IRsaBlindingParameters) implementation -const - SmallPrimesProductHex = - '8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f' + - '73d893fa424cd2edc8636a6c3285e022b0e3866a565ae8108eed8591cd4fe8d2' + - 'ce86165a978d719ebf647f362d33fca29cd179fb42401cbaf3df0c614056f9c8' + - 'f3cfd51e474afb6bc6974f78db8aba8e9e517fded658591ab7502bd41849462f'; - DefaultMaxBitLength = 16384; - { TRsaKeyParameters } -class constructor TRsaKeyParameters.CreateRsaKeyParameters; +class constructor TRsaKeyParameters.Create; begin - FSmallPrimesProduct := TBigInteger.Create(SmallPrimesProductHex, 16); + FMaxSize := -1; + FMaxMRTests := -1; end; -class function TRsaKeyParameters.HasAnySmallFactors( - const AModulus: TBigInteger): Boolean; -var - LM, LX: TBigInteger; +class function TRsaKeyParameters.GetEffectiveMaxSize: Int32; begin - if AModulus.BitLength < FSmallPrimesProduct.BitLength then - begin - LM := FSmallPrimesProduct; - LX := AModulus; - end + if FMaxSize < 0 then + Result := DefaultMaxBitLength else - begin - LM := AModulus; - LX := FSmallPrimesProduct; - end; - Result := not TBigIntegerUtilities.ModOddIsCoprimeVar(LM, LX); + Result := FMaxSize; +end; + +class function TRsaKeyParameters.GetEffectiveMaxMRTests(ABits: Int32): Int32; +begin + if FMaxMRTests < 0 then + Result := GetMRIterations(ABits) + else + Result := FMaxMRTests; +end; + +class function TRsaKeyParameters.GetMRIterations(ABits: Int32): Int32; +begin + if ABits >= 1536 then + Result := 3 + else if ABits >= 1024 then + Result := 4 + else if ABits >= 512 then + Result := 7 + else + Result := 50; end; -class function TRsaKeyParameters.Validate( +class function TRsaKeyParameters.ValidateModulus( const AModulus: TBigInteger): TBigInteger; begin - if (AModulus.IsEven) then - raise EArgumentCryptoLibException.CreateRes(@SRsaModulusIsEven); - if (AModulus.BitLength > DefaultMaxBitLength) then - raise EArgumentCryptoLibException.CreateRes(@SRsaModulusOutOfRange); - if HasAnySmallFactors(AModulus) then - raise EArgumentCryptoLibException.CreateRes(@SRsaModulusHasSmallPrimeFactor); + Result := Validate(AModulus, False); +end; + +class function TRsaKeyParameters.Validate(const AModulus: TBigInteger; + AIsInternal: Boolean): TBigInteger; +var + LIterations: Int32; + LMR: TPrimes.IMROutput; +begin + if not AIsInternal then + begin + if not AModulus.TestBit(0) then + raise EArgumentCryptoLibException.CreateRes(@SRsaModulusIsEven); + if AModulus.BitLength > GetEffectiveMaxSize 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; + end; Result := AModulus; end; constructor TRsaKeyParameters.Create(AIsPrivate: Boolean; const AModulus, AExponent: TBigInteger); +begin + Create(AIsPrivate, AModulus, AExponent, False); +end; + +constructor TRsaKeyParameters.Create(AIsPrivate: Boolean; + const AModulus, AExponent: TBigInteger; AIsInternal: Boolean); begin inherited Create(AIsPrivate); if not AModulus.IsInitialized then @@ -238,7 +297,7 @@ constructor TRsaKeyParameters.Create(AIsPrivate: Boolean; raise EArgumentCryptoLibException.CreateRes(@SNotValidRsaExponent); if (not AIsPrivate) and (AExponent.IsEven) then raise EArgumentCryptoLibException.CreateRes(@SRsaPublicExponentIsEven); - FModulus := Validate(AModulus); + FModulus := Validate(AModulus, AIsInternal); FExponent := AExponent; end; @@ -287,7 +346,13 @@ class procedure TRsaPrivateCrtKeyParameters.ValidateValue(const AX: TBigInteger; constructor TRsaPrivateCrtKeyParameters.Create(const AModulus, APublicExponent, APrivateExponent, AP, AQ, ADP, ADQ, AQInv: TBigInteger); begin - inherited Create(True, AModulus, APrivateExponent); + Create(AModulus, APublicExponent, APrivateExponent, AP, AQ, ADP, ADQ, AQInv, False); +end; + +constructor TRsaPrivateCrtKeyParameters.Create(const AModulus, APublicExponent, + APrivateExponent, AP, AQ, ADP, ADQ, AQInv: TBigInteger; AIsInternal: Boolean); +begin + inherited Create(True, AModulus, APrivateExponent, AIsInternal); ValidateValue(APublicExponent, 'publicExponent', 'exponent'); ValidateValue(AP, 'p', 'P value'); ValidateValue(AQ, 'q', 'Q value'); diff --git a/CryptoLib/src/Math/ClpBigIntegerUtilities.pas b/CryptoLib/src/Math/ClpBigIntegerUtilities.pas index 5854e7f4..7e0cd3a6 100644 --- a/CryptoLib/src/Math/ClpBigIntegerUtilities.pas +++ b/CryptoLib/src/Math/ClpBigIntegerUtilities.pas @@ -64,6 +64,7 @@ TBigIntegerUtilities = class sealed(TObject) class var FBigIntegerEqualityComparer: IEqualityComparer; + FSmallPrimesProduct: TBigInteger; class constructor Create; class function GetZero: TBigInteger; static; @@ -111,6 +112,12 @@ TBigIntegerUtilities = class sealed(TObject) /// a random BigInteger value. class function CreateRandomBigInteger(const ABitLength: Int32; const ASecureRandom: ISecureRandom): TBigInteger; static; + /// + /// Creates a random probable prime BigInteger of a given bit length. + /// + class function CreateRandomPrime(ABitLength, ACertainty: Int32; + const ARandom: ISecureRandom): TBigInteger; static; + /// /// Return a random BigInteger not less than 'min' and not greater than 'max' /// @@ -150,6 +157,11 @@ TBigIntegerUtilities = class sealed(TObject) /// the unsigned byte length. class function GetUnsignedByteLength(const AN: TBigInteger): Int32; static; + /// + /// Returns true if the value has any small prime factors (including if even). + /// + class function HasAnySmallFactors(const AX: TBigInteger): Boolean; static; + /// /// Write the passed in value as unsigned bytes to the specified stream. /// @@ -192,6 +204,13 @@ TBigIntegerUtilities = class sealed(TObject) implementation +const + SmallPrimesProductHex = + '8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f' + + '73d893fa424cd2edc8636a6c3285e022b0e3866a565ae8108eed8591cd4fe8d2' + + 'ce86165a978d719ebf647f362d33fca29cd179fb42401cbaf3df0c614056f9c8' + + 'f3cfd51e474afb6bc6974f78db8aba8e9e517fded658591ab7502bd41849462f'; + { TBigIntegerEqualityComparer } {$IFDEF CRYPTOLIB_FPC_HAS_CONSTREF_GENERIC_COMPARER} @@ -217,6 +236,7 @@ function TBigIntegerEqualityComparer.GetHashCode(const AValue: TBigInteger): {$I class constructor TBigIntegerUtilities.Create; begin FBigIntegerEqualityComparer := TBigIntegerEqualityComparer.Create(); + FSmallPrimesProduct := TBigInteger.Create(SmallPrimesProductHex, 16); end; class function TBigIntegerUtilities.GetZero: TBigInteger; @@ -280,6 +300,12 @@ class function TBigIntegerUtilities.CreateRandomBigInteger(const ABitLength: Int Result := TBigInteger.Create(ABitLength, ASecureRandom); end; +class function TBigIntegerUtilities.CreateRandomPrime(ABitLength, ACertainty: Int32; + const ARandom: ISecureRandom): TBigInteger; +begin + Result := TBigInteger.Create(ABitLength, ACertainty, ARandom); +end; + class function TBigIntegerUtilities.CreateRandomInRange(const AMin, AMax: TBigInteger; const ARandom: ISecureRandom): TBigInteger; var LCmp: Int32; @@ -336,6 +362,27 @@ class function TBigIntegerUtilities.GetUnsignedByteLength(const AN: TBigInteger) Result := AN.GetLengthofByteArrayUnsigned(); end; +class function TBigIntegerUtilities.HasAnySmallFactors(const AX: TBigInteger): Boolean; +var + LM, LX: TBigInteger; +begin + if not AX.TestBit(0) then + Exit(True); + + if AX.BitLength < FSmallPrimesProduct.BitLength then + begin + LM := FSmallPrimesProduct; + LX := AX; + end + else + begin + LM := AX; + LX := FSmallPrimesProduct; + end; + + Result := not ModOddIsCoprimeVar(LM, LX); +end; + class procedure TBigIntegerUtilities.WriteUnsignedByteArray(const AOutStr: TStream; const AN: TBigInteger); var LBuffer: TCryptoLibByteArray;