Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions CryptoLib.Tests/src/Crypto/RSATests.pas
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ TTestRSA = class(TCryptoLibAlgorithmTestCase)
procedure TestRsaSignature;
procedure TestRsaPublicKeyInfoEncodingHasNullParameters;
procedure TestSubjectPublicKeyInfoFactoryRsaConsistency;
procedure TestMaxSizeRejectsOversizedModulus;
procedure TestMaxMRTestsZeroSkipsCompositeCheck;
procedure TestMaxSizeMaxMRTestsUnsetDefault;

end;

Expand Down Expand Up @@ -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}
Expand Down
7 changes: 5 additions & 2 deletions CryptoLib/src/Crypto/Generators/ClpDHGenerators.pas
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,13 @@ function TDHParametersGenerator.GenerateParameters: IDHParameters;
LSafePrimes: TCryptoLibGenericArray<TBigInteger>;
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;

Expand Down
192 changes: 93 additions & 99 deletions CryptoLib/src/Crypto/Generators/ClpDHParametersHelper.pas
Original file line number Diff line number Diff line change
Expand Up @@ -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<TBigInteger>;
Expand All @@ -43,34 +47,24 @@ TDHParametersHelper = class sealed(TObject)
class function ConstructBigPrimeProducts(const APrimeProducts
: TCryptoLibInt32Array): TCryptoLibGenericArray<TBigInteger>; static;

class function HasAnySmallFactorsSafe(const X: TBigInteger): Boolean; static;

class procedure Boot(); static;

class constructor DHParametersHelper();

public

/// <summary>
/// <para>
/// Finds a pair of prime BigInteger's {p, q: p = 2q + 1}
/// </para>
/// <para>
/// (see: Handbook of Applied Cryptography 4.86)
/// </para>
/// </summary>
class function GenerateSafePrimes(ASize, ACertainty: Int32;
const ARandom: ISecureRandom): TCryptoLibGenericArray<TBigInteger>; static;

/// <summary>
/// <para>
/// Select a high order element of the multiplicative group Zp*
/// </para>
/// <para>
/// p and q must be s.t. p = 2*q + 1, where p and q are prime (see
/// generateSafePrimes)
/// </para>
/// Finds a pair of prime BigInteger's {p, q: p = 2q + 1}.
/// </summary>
class function SelectGenerator(const AP, AQ: TBigInteger;
const ARandom: ISecureRandom): TBigInteger; static;
/// <remarks>
/// 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).
/// </remarks>
class function GenerateSafePrimes(ABitLength, ACertainty: Int32;
const ARandom: ISecureRandom; AForGenerator2: Boolean)
: TCryptoLibGenericArray<TBigInteger>; static;
end;

implementation
Expand All @@ -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;
Expand Down Expand Up @@ -112,105 +108,103 @@ class function TDHParametersHelper.ConstructBigPrimeProducts(const APrimeProduct
Result := LBpp;
end;

class function TDHParametersHelper.GenerateSafePrimes(ASize, ACertainty: Int32;
const ARandom: ISecureRandom): TCryptoLibGenericArray<TBigInteger>;
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<TBigInteger>;
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<TBigInteger>.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<TBigInteger>.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
Expand All @@ -225,6 +219,6 @@ class function TDHParametersHelper.SelectGenerator(const AP, AQ: TBigInteger;

Result := LG;
end;
{$ENDIF}
}

end.
9 changes: 3 additions & 6 deletions CryptoLib/src/Crypto/Generators/ClpRsaGenerators.pas
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
Loading
Loading