diff --git a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr
index 2e7edcaf..dc9d03c7 100644
--- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr
+++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dpr
@@ -827,6 +827,8 @@ uses
ExternalTests in '..\src\Asn1\ExternalTests.pas',
GeneralizedTimeTests in '..\src\Asn1\GeneralizedTimeTests.pas',
InputStreamTests in '..\src\Asn1\InputStreamTests.pas',
+ DefiniteLengthAllocationTests in '..\src\Asn1\DefiniteLengthAllocationTests.pas',
+ Asn1StreamReadTests in '..\src\Asn1\Asn1StreamReadTests.pas',
OctetStringTests in '..\src\Asn1\OctetStringTests.pas',
OIDTests in '..\src\Asn1\OIDTests.pas',
ParseTests in '..\src\Asn1\ParseTests.pas',
@@ -920,6 +922,7 @@ uses
IetfUtilitiesTests in '..\src\Asn1\X500\IetfUtilitiesTests.pas',
GeneralNameTests in '..\src\Asn1\X509\GeneralNameTests.pas',
KeyUsageTests in '..\src\Asn1\X509\KeyUsageTests.pas',
+ KeyPurposeIDTests in '..\src\Asn1\X509\KeyPurposeIDTests.pas',
SubjectKeyIdentifierTests in '..\src\Asn1\X509\SubjectKeyIdentifierTests.pas',
X509AltTests in '..\src\Asn1\X509\X509AltTests.pas',
X509ExtensionsTests in '..\src\Asn1\X509\X509ExtensionsTests.pas',
diff --git a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj
index 91ad8636..0993ce34 100644
--- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj
+++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.Mobile.dproj
@@ -1170,6 +1170,8 @@
+
+
@@ -1258,6 +1260,7 @@
+
diff --git a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr
index 9bb65b5f..9080b797 100644
--- a/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr
+++ b/CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr
@@ -846,6 +846,8 @@ uses
ExternalTests in '..\src\Asn1\ExternalTests.pas',
GeneralizedTimeTests in '..\src\Asn1\GeneralizedTimeTests.pas',
InputStreamTests in '..\src\Asn1\InputStreamTests.pas',
+ DefiniteLengthAllocationTests in '..\src\Asn1\DefiniteLengthAllocationTests.pas',
+ Asn1StreamReadTests in '..\src\Asn1\Asn1StreamReadTests.pas',
OctetStringTests in '..\src\Asn1\OctetStringTests.pas',
OIDTests in '..\src\Asn1\OIDTests.pas',
ParseTests in '..\src\Asn1\ParseTests.pas',
@@ -939,6 +941,7 @@ uses
IetfUtilitiesTests in '..\src\Asn1\X500\IetfUtilitiesTests.pas',
GeneralNameTests in '..\src\Asn1\X509\GeneralNameTests.pas',
KeyUsageTests in '..\src\Asn1\X509\KeyUsageTests.pas',
+ KeyPurposeIDTests in '..\src\Asn1\X509\KeyPurposeIDTests.pas',
SubjectKeyIdentifierTests in '..\src\Asn1\X509\SubjectKeyIdentifierTests.pas',
X509AltTests in '..\src\Asn1\X509\X509AltTests.pas',
X509ExtensionsTests in '..\src\Asn1\X509\X509ExtensionsTests.pas',
diff --git a/CryptoLib.Tests/FreePascal.Tests/CryptoLib.Tests.lpi b/CryptoLib.Tests/FreePascal.Tests/CryptoLib.Tests.lpi
index 9c15e648..aebb8170 100644
--- a/CryptoLib.Tests/FreePascal.Tests/CryptoLib.Tests.lpi
+++ b/CryptoLib.Tests/FreePascal.Tests/CryptoLib.Tests.lpi
@@ -79,7 +79,7 @@
-
+
@@ -825,6 +825,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CryptoLib.Tests/FreePascal.Tests/CryptoLib.lpr b/CryptoLib.Tests/FreePascal.Tests/CryptoLib.lpr
index 2cb3a95b..19438c72 100644
--- a/CryptoLib.Tests/FreePascal.Tests/CryptoLib.lpr
+++ b/CryptoLib.Tests/FreePascal.Tests/CryptoLib.lpr
@@ -11,12 +11,12 @@
NamedCurveTests, SignerUtilitiesTests, SecureRandomTests,
DigestRandomNumberTests, FixedPointTests, AESTests, AesLightTests,
FusedExternalRegistrationTests, AesHardwareEngineTests, AESSICTests,
- SicBulkParityTests, EcbBulkParityTests, CbcBulkParityTests, GcmSivBulkParityTests,
- SpeckBlockCipherTestBase, SpeckLegacyTests, SpeckTests, IESCipherTests,
- HMacTests, Pkcs5Tests, HkdfGeneratorTests, ECIESTests, PascalCoinECIESTests,
- ECNRTests, PrimesTests, ECEncodingTests, GF256AesTests, PaddingTests,
- DSATests, DeterministicDsaTests, Salsa20Tests, XSalsa20Tests, ChaChaTests,
- ChaCha7539ProcessBlocks2Tests, HChaCha20Tests, XChaCha20Tests,
+ SicBulkParityTests, EcbBulkParityTests, CbcBulkParityTests,
+ GcmSivBulkParityTests, SpeckBlockCipherTestBase, SpeckLegacyTests, SpeckTests,
+ IESCipherTests, HMacTests, Pkcs5Tests, HkdfGeneratorTests, ECIESTests,
+ PascalCoinECIESTests, ECNRTests, PrimesTests, ECEncodingTests, GF256AesTests,
+ PaddingTests, DSATests, DeterministicDsaTests, Salsa20Tests, XSalsa20Tests,
+ ChaChaTests, ChaCha7539ProcessBlocks2Tests, HChaCha20Tests, XChaCha20Tests,
XChaCha20Poly1305Tests, StreamCipherResetTests, CTSTests, X25519Tests,
X448Tests, Ed25519Tests, Ed448Tests, X25519HigherLevelTests,
Ed25519HigherLevelTests, ShortenedDigestTests, Kdf1GeneratorTests,
@@ -27,15 +27,16 @@
DHTests, Asn1IntegerTests, BitStringTests, GeneralizedTimeTests,
OctetStringTests, RelativeOidTests, UtcTimeTests, InputStreamTests, SetTests,
X9Tests, PrivateKeyInfoTests, DerUtf8StringTests,
- EncryptedPrivateKeyInfoTests, Pkcs10CertRequestTests, DeltaCertificateTests,
+ EncryptedPrivateKeyInfoTests, DefiniteLengthAllocationTests,
+ Asn1StreamReadTests, Pkcs10CertRequestTests, DeltaCertificateTests,
RelatedCertificateTests, CcmParametersTests, GcmParametersTests,
CertificateTests, X509AltTests, X509ExtensionsTests,
AuthorityKeyIdentifierTests, IdpRelativeNameTests, IetfUtilitiesTests,
X509NameTests, SubjectKeyIdentifierTests, KeyUsageTests, GeneralNameTests,
- KMacTests, RSATests, PssTests, ISO9796Tests, RSABlindedTests,
- RSADigestSignerTests, X931SignerTests, CryptoIOStreamTests, CryptoIOSinkTests,
- OaepTests, RijndaelTests, BlowfishTests, Poly1305Tests, MacTests,
- ChaCha20Poly1305Tests, OcbTests, CcmTests, EaxTests, CMacTests,
+ KeyPurposeIDTests, KMacTests, RSATests, PssTests, ISO9796Tests,
+ RSABlindedTests, RSADigestSignerTests, X931SignerTests, CryptoIOStreamTests,
+ CryptoIOSinkTests, OaepTests, RijndaelTests, BlowfishTests, Poly1305Tests,
+ MacTests, ChaCha20Poly1305Tests, OcbTests, CcmTests, EaxTests, CMacTests,
AeadTestUtilities, GcmReorderTests, GCMTests, GcmSivTests, GMacTests,
Pkcs12Tests, Bip327MuSig2Tests, Bip340SchnorrTests, AlgorithmFinderTests,
MlKemTests, MlDsaTests, PqcPkcsTests, SlhDsaTests, Lib25519Tests,
diff --git a/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpi b/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpi
index 213a0319..d2d1e7eb 100644
--- a/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpi
+++ b/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpi
@@ -39,7 +39,7 @@
-
+
@@ -784,6 +784,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpr b/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpr
index 7dcf180a..f4dc5434 100644
--- a/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpr
+++ b/CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpr
@@ -12,12 +12,12 @@
NamedCurveTests, SignerUtilitiesTests, SecureRandomTests,
DigestRandomNumberTests, FixedPointTests, AESTests, AesLightTests,
FusedExternalRegistrationTests, AesHardwareEngineTests, AESSICTests,
- SicBulkParityTests, EcbBulkParityTests, CbcBulkParityTests, GcmSivBulkParityTests,
- SpeckBlockCipherTestBase, SpeckLegacyTests, SpeckTests, IESCipherTests,
- HMacTests, Pkcs5Tests, HkdfGeneratorTests, ECIESTests, PascalCoinECIESTests,
- ECNRTests, PrimesTests, ECEncodingTests, GF256AesTests, PaddingTests,
- DSATests, DeterministicDsaTests, Salsa20Tests, XSalsa20Tests, ChaChaTests,
- ChaCha7539ProcessBlocks2Tests, HChaCha20Tests, XChaCha20Tests,
+ SicBulkParityTests, EcbBulkParityTests, CbcBulkParityTests,
+ GcmSivBulkParityTests, SpeckBlockCipherTestBase, SpeckLegacyTests, SpeckTests,
+ IESCipherTests, HMacTests, Pkcs5Tests, HkdfGeneratorTests, ECIESTests,
+ PascalCoinECIESTests, ECNRTests, PrimesTests, ECEncodingTests, GF256AesTests,
+ PaddingTests, DSATests, DeterministicDsaTests, Salsa20Tests, XSalsa20Tests,
+ ChaChaTests, ChaCha7539ProcessBlocks2Tests, HChaCha20Tests, XChaCha20Tests,
XChaCha20Poly1305Tests, StreamCipherResetTests, CTSTests, X25519Tests,
X448Tests, Ed25519Tests, Ed448Tests, X25519HigherLevelTests,
Ed25519HigherLevelTests, Curve25519KeyUtilitiesTests, ShortenedDigestTests,
@@ -28,19 +28,20 @@
DHTests, Asn1IntegerTests, GeneralizedTimeTests, BitStringTests,
InputStreamTests, UtcTimeTests, RelativeOidTests, OctetStringTests, SetTests,
X9Tests, PrivateKeyInfoTests, DerUtf8StringTests,
- EncryptedPrivateKeyInfoTests, Pkcs10CertRequestTests, DeltaCertificateTests,
+ EncryptedPrivateKeyInfoTests, DefiniteLengthAllocationTests,
+ Asn1StreamReadTests, Pkcs10CertRequestTests, DeltaCertificateTests,
RelatedCertificateTests, CcmParametersTests, GcmParametersTests,
CertificateTests, X509AltTests, X509ExtensionsTests,
AuthorityKeyIdentifierTests, IdpRelativeNameTests, IetfUtilitiesTests,
X509NameTests, SubjectKeyIdentifierTests, KeyUsageTests, GeneralNameTests,
- KMacTests, PssTests, ISO9796Tests, RSABlindedTests, RSADigestSignerTests,
- RSATests, X931SignerTests, CryptoIOStreamTests, CryptoIOSinkTests, OaepTests,
- RijndaelTests, BlowfishTests, CcmTests, ChaCha20Poly1305Tests, CMacTests,
- EaxTests, OcbTests, MacTests, Poly1305Tests, AeadTestUtilities,
- GcmReorderTests, GCMTests, GcmSivTests, GMacTests, Pkcs12Tests,
- Bip327MuSig2Tests, Bip340SchnorrTests, AlgorithmFinderTests, MlKemTests,
- MlDsaTests, SlhDsaTests, PqcTestSampler, PqcPkcsTests, Lib25519Tests,
- Asn1CipherBuilderWithKeyTests, CryptoLibTestBase, CtrDrbgTests,
+ KeyPurposeIDTests, KMacTests, PssTests, ISO9796Tests, RSABlindedTests,
+ RSADigestSignerTests, RSATests, X931SignerTests, CryptoIOStreamTests,
+ CryptoIOSinkTests, OaepTests, RijndaelTests, BlowfishTests, CcmTests,
+ ChaCha20Poly1305Tests, CMacTests, EaxTests, OcbTests, MacTests, Poly1305Tests,
+ AeadTestUtilities, GcmReorderTests, GCMTests, GcmSivTests, GMacTests,
+ Pkcs12Tests, Bip327MuSig2Tests, Bip340SchnorrTests, AlgorithmFinderTests,
+ MlKemTests, MlDsaTests, SlhDsaTests, PqcTestSampler, PqcPkcsTests,
+ Lib25519Tests, Asn1CipherBuilderWithKeyTests, CryptoLibTestBase, CtrDrbgTests,
DrbgTestSupport, HashDrbgTests, HMacDrbgTests, SimdSelectSlotTests,
BinaryPrimitivesTests, PkcsEncryptedPrivateKeyInfoTests, Pkcs12StoreTests,
OpenSslReaderTests, OpenSslWriterTests, X509CertGenTests,
diff --git a/CryptoLib.Tests/src/Asn1/Asn1StreamReadTests.pas b/CryptoLib.Tests/src/Asn1/Asn1StreamReadTests.pas
new file mode 100644
index 00000000..b1a961b9
--- /dev/null
+++ b/CryptoLib.Tests/src/Asn1/Asn1StreamReadTests.pas
@@ -0,0 +1,483 @@
+{ *********************************************************************************** }
+{ * 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 Asn1StreamReadTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+ SysUtils,
+ Classes,
+ Math,
+{$IFDEF FPC}
+ fpcunit,
+ testregistry,
+{$ELSE}
+ TestFramework,
+{$ENDIF FPC}
+ ClpAsn1Objects,
+ ClpIAsn1Objects,
+ ClpAsn1Streams,
+ ClpAsn1Parsers,
+ ClpIAsn1Parsers,
+ ClpIAsn1Generators,
+ ClpAsn1Generators,
+ ClpCryptoLibTypes,
+ ClpArrayUtilities,
+ ClpStreams,
+ ClpStreamUtilities,
+ CryptoLibTestBase;
+
+type
+
+ TRecordingStream = class(TBaseInputStream)
+ strict private
+ FData: TCryptoLibByteArray;
+ FPos: Int32;
+ FFirstBulkReadLength: Int32;
+
+ procedure RecordFirstBulkReadLength(ALength: Int32);
+ public
+ constructor Create(const AData: TCryptoLibByteArray);
+
+ function Read(var ABuffer; ACount: LongInt): LongInt; override;
+ function Read(ABuffer: TCryptoLibByteArray; AOffset, ACount: LongInt): LongInt; override;
+ function ReadByte: Int32; override;
+
+ property FirstBulkReadLength: Int32 read FFirstBulkReadLength;
+ end;
+
+ TProbeConstructedOctetStream = class(TAsn1ConstructedOctetStream)
+ strict private
+ FReadByteCallCount: Int32;
+ public
+ constructor Create(const AParser: IAsn1StreamParser);
+
+ function Read(var ABuffer; ACount: LongInt): LongInt; override;
+ function Read(ABuffer: TCryptoLibByteArray; AOffset, ACount: LongInt): LongInt; override;
+ function ReadByte: Int32; override;
+
+ property ReadByteCallCount: Int32 read FReadByteCallCount;
+ end;
+
+ TProbeConstructedBitStream = class(TAsn1ConstructedBitStream)
+ strict private
+ FReadByteCallCount: Int32;
+ public
+ constructor Create(const AParser: IAsn1StreamParser; AOctetAligned: Boolean);
+
+ function Read(var ABuffer; ACount: LongInt): LongInt; override;
+ function Read(ABuffer: TCryptoLibByteArray; AOffset, ACount: LongInt): LongInt; override;
+ function ReadByte: Int32; override;
+
+ property ReadByteCallCount: Int32 read FReadByteCallCount;
+ end;
+
+ TAsn1StreamReadTest = class(TCryptoLibAlgorithmTestCase)
+ strict private
+ const
+ LargePayloadSize = 5000;
+ MaxReadByteCallsForBulkPath = 16;
+
+ function BuildBerIndefiniteOctetEncoding(const APayload: TCryptoLibByteArray): TCryptoLibByteArray;
+ function BuildBerIndefiniteConstructedBitString(const APayload: TCryptoLibByteArray): TCryptoLibByteArray;
+
+ published
+ procedure TestConstructedOctetReadAllRoundTrip;
+ procedure TestConstructedOctetReadAllUsesBulkVarRead;
+ procedure TestConstructedBitReadAllRoundTrip;
+ procedure TestConstructedBitReadAllUsesBulkVarRead;
+ procedure TestIndefiniteLengthBulkReadDelegatesToWrappedStream;
+ procedure TestIndefiniteLengthRespectsEndOfContentsMarker;
+ procedure TestIndefiniteLengthMalformedEndOfContentsRaises;
+ end;
+
+implementation
+
+{ TRecordingStream }
+
+constructor TRecordingStream.Create(const AData: TCryptoLibByteArray);
+begin
+ inherited Create;
+ FData := AData;
+ FPos := 0;
+ FFirstBulkReadLength := -1;
+end;
+
+procedure TRecordingStream.RecordFirstBulkReadLength(ALength: Int32);
+begin
+ if FFirstBulkReadLength < 0 then
+ FFirstBulkReadLength := ALength;
+end;
+
+function TRecordingStream.Read(var ABuffer; ACount: LongInt): LongInt;
+var
+ LToRead: Int32;
+begin
+ RecordFirstBulkReadLength(ACount);
+
+ if FPos >= System.Length(FData) then
+ begin
+ Result := 0;
+ Exit;
+ end;
+
+ LToRead := Min(ACount, System.Length(FData) - FPos);
+ Move(FData[FPos], ABuffer, LToRead);
+ Inc(FPos, LToRead);
+ Result := LToRead;
+end;
+
+function TRecordingStream.Read(ABuffer: TCryptoLibByteArray; AOffset, ACount: LongInt): LongInt;
+begin
+ TStreamUtilities.ValidateBufferArguments(ABuffer, AOffset, ACount);
+ Result := Read(ABuffer[AOffset], ACount);
+end;
+
+function TRecordingStream.ReadByte: Int32;
+begin
+ if FPos >= System.Length(FData) then
+ Result := -1
+ else
+ begin
+ Result := FData[FPos];
+ Inc(FPos);
+ end;
+end;
+
+{ TProbeConstructedOctetStream }
+
+constructor TProbeConstructedOctetStream.Create(const AParser: IAsn1StreamParser);
+begin
+ inherited Create(AParser);
+ FReadByteCallCount := 0;
+end;
+
+function TProbeConstructedOctetStream.Read(var ABuffer; ACount: LongInt): LongInt;
+begin
+ Result := inherited Read(ABuffer, ACount);
+end;
+
+function TProbeConstructedOctetStream.Read(ABuffer: TCryptoLibByteArray; AOffset, ACount: LongInt): LongInt;
+begin
+ TStreamUtilities.ValidateBufferArguments(ABuffer, AOffset, ACount);
+ Result := Read(ABuffer[AOffset], ACount);
+end;
+
+function TProbeConstructedOctetStream.ReadByte: Int32;
+begin
+ Inc(FReadByteCallCount);
+ Result := inherited ReadByte;
+end;
+
+{ TProbeConstructedBitStream }
+
+constructor TProbeConstructedBitStream.Create(const AParser: IAsn1StreamParser;
+ AOctetAligned: Boolean);
+begin
+ inherited Create(AParser, AOctetAligned);
+ FReadByteCallCount := 0;
+end;
+
+function TProbeConstructedBitStream.Read(var ABuffer; ACount: LongInt): LongInt;
+begin
+ Result := inherited Read(ABuffer, ACount);
+end;
+
+function TProbeConstructedBitStream.Read(ABuffer: TCryptoLibByteArray; AOffset, ACount: LongInt): LongInt;
+begin
+ TStreamUtilities.ValidateBufferArguments(ABuffer, AOffset, ACount);
+ Result := Read(ABuffer[AOffset], ACount);
+end;
+
+function TProbeConstructedBitStream.ReadByte: Int32;
+begin
+ Inc(FReadByteCallCount);
+ Result := inherited ReadByte;
+end;
+
+{ TAsn1StreamReadTest }
+
+function TAsn1StreamReadTest.BuildBerIndefiniteOctetEncoding
+ (const APayload: TCryptoLibByteArray): TCryptoLibByteArray;
+var
+ LBOut: TMemoryStream;
+ LOctGen: IBerOctetStringGenerator;
+ LOutStream: TStream;
+begin
+ LBOut := TMemoryStream.Create();
+ try
+ LOctGen := TBerOctetStringGenerator.Create(LBOut);
+ LOutStream := LOctGen.GetOctetOutputStream();
+ try
+ if System.Length(APayload) > 0 then
+ LOutStream.Write(APayload[0], System.Length(APayload));
+ finally
+ LOutStream.Free;
+ end;
+ LOctGen := nil;
+
+ LBOut.Position := 0;
+ System.SetLength(Result, LBOut.Size);
+ LBOut.Read(Result[0], System.Length(Result));
+ finally
+ LBOut.Free;
+ end;
+end;
+
+function TAsn1StreamReadTest.BuildBerIndefiniteConstructedBitString
+ (const APayload: TCryptoLibByteArray): TCryptoLibByteArray;
+var
+ LFragmentBodyLen, LFragmentLen, LTotalLen, LI: Int32;
+begin
+ LFragmentBodyLen := 1 + System.Length(APayload);
+ LFragmentLen := 1 + 3 + LFragmentBodyLen;
+ LTotalLen := 2 + LFragmentLen + 2;
+
+ System.SetLength(Result, LTotalLen);
+ LI := 0;
+ Result[LI] := $23;
+ Inc(LI);
+ Result[LI] := $80;
+ Inc(LI);
+
+ Result[LI] := $03;
+ Inc(LI);
+ Result[LI] := $82;
+ Inc(LI);
+ Result[LI] := Byte((LFragmentBodyLen shr 8) and $FF);
+ Inc(LI);
+ Result[LI] := Byte(LFragmentBodyLen and $FF);
+ Inc(LI);
+ Result[LI] := 0;
+ Inc(LI);
+
+ if System.Length(APayload) > 0 then
+ begin
+ Move(APayload[0], Result[LI], System.Length(APayload));
+ Inc(LI, System.Length(APayload));
+ end;
+
+ Result[LI] := 0;
+ Inc(LI);
+ Result[LI] := 0;
+end;
+
+procedure TAsn1StreamReadTest.TestConstructedOctetReadAllRoundTrip;
+var
+ LPayload, LEncoding, LActual: TCryptoLibByteArray;
+ LParser: IAsn1StreamParser;
+ LOctets: IBerOctetString;
+ LI: Int32;
+begin
+ System.SetLength(LPayload, LargePayloadSize);
+ for LI := 0 to LargePayloadSize - 1 do
+ LPayload[LI] := Byte(LI);
+
+ LEncoding := BuildBerIndefiniteOctetEncoding(LPayload);
+ LParser := TAsn1StreamParser.Create(LEncoding);
+ LOctets := TBerOctetStringParser.Parse(LParser);
+
+ CheckTrue(TArrayUtilities.AreEqual(LPayload, LOctets.GetOctets()),
+ 'constructed BER octet string did not round-trip through ReadAll');
+end;
+
+procedure TAsn1StreamReadTest.TestConstructedOctetReadAllUsesBulkVarRead;
+var
+ LPayload, LEncoding, LActual: TCryptoLibByteArray;
+ LParser: IAsn1StreamParser;
+ LProbe: TProbeConstructedOctetStream;
+ LI: Int32;
+begin
+ System.SetLength(LPayload, LargePayloadSize);
+ for LI := 0 to LargePayloadSize - 1 do
+ LPayload[LI] := Byte(LI);
+
+ LEncoding := BuildBerIndefiniteOctetEncoding(LPayload);
+ LParser := TAsn1StreamParser.Create(LEncoding);
+ LProbe := TProbeConstructedOctetStream.Create(LParser);
+ try
+ LActual := TStreamUtilities.ReadAll(LProbe);
+ CheckTrue(TArrayUtilities.AreEqual(LPayload, LActual),
+ 'constructed BER octet stream did not materialize payload');
+
+ if LProbe.ReadByteCallCount > MaxReadByteCallsForBulkPath then
+ Fail(Format('constructed octet stream used byte-at-a-time reads (%d ReadByte calls)',
+ [LProbe.ReadByteCallCount]));
+ finally
+ LProbe.Free;
+ end;
+end;
+
+procedure TAsn1StreamReadTest.TestConstructedBitReadAllRoundTrip;
+var
+ LPayload, LEncoding, LActual: TCryptoLibByteArray;
+ LParser: IAsn1StreamParser;
+ LBits: IBerBitString;
+ LI: Int32;
+begin
+ System.SetLength(LPayload, LargePayloadSize);
+ for LI := 0 to LargePayloadSize - 1 do
+ LPayload[LI] := Byte(LI xor $5A);
+
+ LEncoding := BuildBerIndefiniteConstructedBitString(LPayload);
+ LParser := TAsn1StreamParser.Create(LEncoding);
+ LBits := TBerBitStringParser.Parse(LParser);
+
+ LActual := LBits.GetBytes();
+ CheckTrue(TArrayUtilities.AreEqual(LPayload, LActual),
+ 'constructed BER bit string did not round-trip through ReadAll');
+end;
+
+procedure TAsn1StreamReadTest.TestConstructedBitReadAllUsesBulkVarRead;
+var
+ LPayload, LEncoding, LActual: TCryptoLibByteArray;
+ LParser: IAsn1StreamParser;
+ LProbe: TProbeConstructedBitStream;
+ LI: Int32;
+begin
+ System.SetLength(LPayload, LargePayloadSize);
+ for LI := 0 to LargePayloadSize - 1 do
+ LPayload[LI] := Byte(LI xor $5A);
+
+ LEncoding := BuildBerIndefiniteConstructedBitString(LPayload);
+ LParser := TAsn1StreamParser.Create(LEncoding);
+ LProbe := TProbeConstructedBitStream.Create(LParser, False);
+ try
+ LActual := TStreamUtilities.ReadAll(LProbe);
+ CheckTrue(TArrayUtilities.AreEqual(LPayload, LActual),
+ 'constructed BER bit stream did not materialize payload');
+
+ if LProbe.ReadByteCallCount > MaxReadByteCallsForBulkPath then
+ Fail(Format('constructed bit stream used byte-at-a-time reads (%d ReadByte calls)',
+ [LProbe.ReadByteCallCount]));
+ finally
+ LProbe.Free;
+ end;
+end;
+
+procedure TAsn1StreamReadTest.TestIndefiniteLengthBulkReadDelegatesToWrappedStream;
+var
+ LPayload: TCryptoLibByteArray;
+ LRecording: TRecordingStream;
+ LIndefinite: TAsn1IndefiniteLengthInputStream;
+ LBuffer: TCryptoLibByteArray;
+ LRead, LI: Int32;
+begin
+ System.SetLength(LPayload, LargePayloadSize);
+ for LI := 0 to LargePayloadSize - 1 do
+ LPayload[LI] := Byte(LI + 1);
+
+ LRecording := TRecordingStream.Create(LPayload);
+ try
+ LIndefinite := TAsn1IndefiniteLengthInputStream.Create(LRecording, LargePayloadSize);
+ try
+ LIndefinite.SetEofOn00(False);
+
+ System.SetLength(LBuffer, TStreamUtilities.DefaultBufferSize);
+ LRead := LIndefinite.Read(LBuffer[0], System.Length(LBuffer));
+
+ if LRead <= 1 then
+ Fail(Format('indefinite stream bulk read returned too few bytes (%d)', [LRead]));
+
+ if (LRecording.FirstBulkReadLength < 0) or (LRecording.FirstBulkReadLength <= 1) then
+ Fail(Format('indefinite stream did not delegate bulk read to wrapped stream (%d)',
+ [LRecording.FirstBulkReadLength]));
+ finally
+ LIndefinite.Free;
+ end;
+ finally
+ LRecording.Free;
+ end;
+end;
+
+procedure TAsn1StreamReadTest.TestIndefiniteLengthRespectsEndOfContentsMarker;
+var
+ LPayload: TCryptoLibByteArray;
+ LWrapped: TMemoryStream;
+ LIndefinite: TAsn1IndefiniteLengthInputStream;
+ LBuffer: TCryptoLibByteArray;
+ LRead, LI: Int32;
+begin
+ System.SetLength(LPayload, 4);
+ for LI := 0 to 3 do
+ LPayload[LI] := Byte(LI + 10);
+
+ LWrapped := TMemoryStream.Create();
+ try
+ LWrapped.WriteBuffer(LPayload[0], System.Length(LPayload));
+ LWrapped.WriteBuffer(TCryptoLibByteArray.Create(0, 0)[0], 2);
+ LWrapped.Position := 0;
+
+ LIndefinite := TAsn1IndefiniteLengthInputStream.Create(LWrapped, System.Length(LPayload) + 2);
+ try
+ System.SetLength(LBuffer, System.Length(LPayload));
+ LRead := TStreamUtilities.ReadFully(LIndefinite, LBuffer, 0, System.Length(LBuffer));
+ CheckEquals(System.Length(LPayload), LRead,
+ 'indefinite stream did not return payload before EOC');
+
+ CheckTrue(TArrayUtilities.AreEqual(LPayload, LBuffer),
+ 'indefinite stream payload mismatch before EOC');
+
+ LRead := LIndefinite.Read(LBuffer[0], System.Length(LBuffer));
+ CheckEquals(0, LRead, 'indefinite stream should be exhausted after EOC');
+ finally
+ LIndefinite.Free;
+ end;
+ finally
+ LWrapped.Free;
+ end;
+end;
+
+procedure TAsn1StreamReadTest.TestIndefiniteLengthMalformedEndOfContentsRaises;
+var
+ LWrapped: TMemoryStream;
+ LIndefinite: TAsn1IndefiniteLengthInputStream;
+begin
+ LWrapped := TMemoryStream.Create();
+ try
+ LWrapped.WriteBuffer(TCryptoLibByteArray.Create(0, 1)[0], 2);
+ LWrapped.Position := 0;
+
+ try
+ LIndefinite := TAsn1IndefiniteLengthInputStream.Create(LWrapped, 2);
+ try
+ Fail('expected malformed end-of-contents exception');
+ finally
+ LIndefinite.Free;
+ end;
+ except
+ on E: EIOCryptoLibException do
+ CheckEquals('malformed end-of-contents marker', E.Message);
+ end;
+ finally
+ LWrapped.Free;
+ end;
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TAsn1StreamReadTest);
+{$ELSE}
+RegisterTest(TAsn1StreamReadTest.Suite);
+{$ENDIF FPC}
+
+end.
diff --git a/CryptoLib.Tests/src/Asn1/DefiniteLengthAllocationTests.pas b/CryptoLib.Tests/src/Asn1/DefiniteLengthAllocationTests.pas
new file mode 100644
index 00000000..24a6b94f
--- /dev/null
+++ b/CryptoLib.Tests/src/Asn1/DefiniteLengthAllocationTests.pas
@@ -0,0 +1,210 @@
+{ *********************************************************************************** }
+{ * 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 DefiniteLengthAllocationTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+ SysUtils,
+ Classes,
+ Math,
+{$IFDEF FPC}
+ fpcunit,
+ testregistry,
+{$ELSE}
+ TestFramework,
+{$ENDIF FPC}
+ ClpAsn1Objects,
+ ClpIAsn1Objects,
+ ClpAsn1Streams,
+ ClpCryptoLibTypes,
+ ClpStreams,
+ ClpStreamUtilities,
+ CryptoLibTestBase;
+
+type
+
+ TRecordingStream = class(TBaseInputStream)
+ strict private
+ FData: TCryptoLibByteArray;
+ FPos: Int32;
+ FFirstBulkReadLength: Int32;
+
+ procedure RecordFirstBulkReadLength(ALength: Int32);
+ public
+ constructor Create(const AData: TCryptoLibByteArray);
+
+ function Read(var ABuffer; ACount: LongInt): LongInt; override;
+ function Read(ABuffer: TCryptoLibByteArray; AOffset, ACount: LongInt): LongInt; override;
+ function ReadByte: Int32; override;
+
+ property FirstBulkReadLength: Int32 read FFirstBulkReadLength;
+ end;
+
+ TDefiniteLengthAllocationTest = class(TCryptoLibAlgorithmTestCase)
+ published
+ procedure TestDeclaredLengthNotAllocatedUpFront;
+ procedure TestTruncatedObjectReportsExpectedMessage;
+ end;
+
+implementation
+
+{ TRecordingStream }
+
+constructor TRecordingStream.Create(const AData: TCryptoLibByteArray);
+begin
+ inherited Create;
+ FData := AData;
+ FPos := 0;
+ FFirstBulkReadLength := -1;
+end;
+
+procedure TRecordingStream.RecordFirstBulkReadLength(ALength: Int32);
+begin
+ if FFirstBulkReadLength < 0 then
+ FFirstBulkReadLength := ALength;
+end;
+
+function TRecordingStream.Read(var ABuffer; ACount: LongInt): LongInt;
+var
+ LToRead: Int32;
+begin
+ RecordFirstBulkReadLength(ACount);
+
+ if FPos >= System.Length(FData) then
+ begin
+ Result := 0;
+ Exit;
+ end;
+
+ LToRead := Min(ACount, System.Length(FData) - FPos);
+ Move(FData[FPos], ABuffer, LToRead);
+ Inc(FPos, LToRead);
+ Result := LToRead;
+end;
+
+function TRecordingStream.Read(ABuffer: TCryptoLibByteArray; AOffset, ACount: LongInt): LongInt;
+begin
+ TStreamUtilities.ValidateBufferArguments(ABuffer, AOffset, ACount);
+ Result := Read(ABuffer[AOffset], ACount);
+end;
+
+function TRecordingStream.ReadByte: Int32;
+begin
+ if FPos >= System.Length(FData) then
+ Result := -1
+ else
+ begin
+ Result := FData[FPos];
+ Inc(FPos);
+ end;
+end;
+
+{ TDefiniteLengthAllocationTest }
+
+procedure TDefiniteLengthAllocationTest.TestDeclaredLengthNotAllocatedUpFront;
+const
+ DeclaredLength = 1 shl 20;
+var
+ LInput: TCryptoLibByteArray;
+ LInStr: TRecordingStream;
+ LInputStream: TAsn1InputStream;
+ LOctets: IAsn1OctetString;
+ LFirstBulkReadLength: Int32;
+begin
+ System.SetLength(LInput, 5 + DeclaredLength);
+ LInput[0] := $04;
+ LInput[1] := $83;
+ LInput[2] := $10;
+ LInput[3] := $00;
+ LInput[4] := $00;
+
+ LInStr := TRecordingStream.Create(LInput);
+ try
+ LInputStream := TAsn1InputStream.Create(LInStr, True);
+ try
+ LOctets := TAsn1OctetString.GetInstance(LInputStream.ReadObject());
+ CheckEquals(DeclaredLength, LOctets.GetOctetsLength(),
+ 'octet string did not materialize to the declared length');
+
+ LFirstBulkReadLength := LInStr.FirstBulkReadLength;
+ if (LFirstBulkReadLength < 0) or (LFirstBulkReadLength >= DeclaredLength) then
+ Fail(Format('first bulk read requested the full declared length (%d)',
+ [LFirstBulkReadLength]));
+ finally
+ LInputStream.Free;
+ end;
+ finally
+ LInStr.Free;
+ end;
+end;
+
+procedure TDefiniteLengthAllocationTest.TestTruncatedObjectReportsExpectedMessage;
+const
+ DeclaredLength = 1 shl 20;
+ BodySupplied = 10;
+var
+ LInput: TCryptoLibByteArray;
+ LInStr: TRecordingStream;
+ LInputStream: TAsn1InputStream;
+ LExpected: String;
+begin
+ System.SetLength(LInput, 5 + BodySupplied);
+ LInput[0] := $04;
+ LInput[1] := $83;
+ LInput[2] := $10;
+ LInput[3] := $00;
+ LInput[4] := $00;
+
+ LExpected := Format('DEF length %d object truncated by %d',
+ [DeclaredLength, DeclaredLength - BodySupplied]);
+
+ LInStr := TRecordingStream.Create(LInput);
+ try
+ LInputStream := TAsn1InputStream.Create(LInStr, True);
+ try
+ try
+ LInputStream.ReadObject();
+ Fail('no exception on truncated definite-length object');
+ except
+ on E: EEndOfStreamCryptoLibException do
+ CheckEquals(LExpected, E.Message,
+ Format('unexpected truncation message: %s', [E.Message]));
+ on E: Exception do
+ Fail(Format('unexpected exception: %s', [E.Message]));
+ end;
+ finally
+ LInputStream.Free;
+ end;
+ finally
+ LInStr.Free;
+ end;
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TDefiniteLengthAllocationTest);
+{$ELSE}
+RegisterTest(TDefiniteLengthAllocationTest.Suite);
+{$ENDIF FPC}
+
+end.
diff --git a/CryptoLib.Tests/src/Asn1/X509/KeyPurposeIDTests.pas b/CryptoLib.Tests/src/Asn1/X509/KeyPurposeIDTests.pas
new file mode 100644
index 00000000..8e12bb56
--- /dev/null
+++ b/CryptoLib.Tests/src/Asn1/X509/KeyPurposeIDTests.pas
@@ -0,0 +1,86 @@
+{ *********************************************************************************** }
+{ * 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 KeyPurposeIDTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+ SysUtils,
+{$IFDEF FPC}
+ fpcunit,
+ testregistry,
+{$ELSE}
+ TestFramework,
+{$ENDIF FPC}
+ ClpX509Asn1Objects,
+ ClpAsn1Objects,
+ ClpIAsn1Objects,
+ CryptoLibTestBase;
+
+type
+
+ TKeyPurposeIDTest = class(TCryptoLibAlgorithmTestCase)
+ strict private
+ procedure CheckKeyPurposeID(const AKeyPurposeID: IDerObjectIdentifier;
+ const AExpectedID: String);
+
+ published
+ procedure TestKeyPurposeIDs;
+ end;
+
+implementation
+
+{ TKeyPurposeIDTest }
+
+procedure TKeyPurposeIDTest.CheckKeyPurposeID(const AKeyPurposeID: IDerObjectIdentifier;
+ const AExpectedID: String);
+var
+ LRecoveredOid: IDerObjectIdentifier;
+begin
+ if not SameText(AExpectedID, AKeyPurposeID.ID) then
+ Fail(Format('wrong ID for KeyPurposeID: expected %s but got %s',
+ [AExpectedID, AKeyPurposeID.ID]));
+
+ LRecoveredOid := TDerObjectIdentifier.GetInstance(AKeyPurposeID.GetEncoded());
+ if not SameText(AExpectedID, LRecoveredOid.ID) then
+ Fail(Format('KeyPurposeID did not round-trip: expected %s but got %s',
+ [AExpectedID, LRecoveredOid.ID]));
+end;
+
+procedure TKeyPurposeIDTest.TestKeyPurposeIDs;
+begin
+ CheckKeyPurposeID(TKeyPurposeId.IdKpDocumentSigning, '1.3.6.1.5.5.7.3.36');
+ CheckKeyPurposeID(TKeyPurposeId.IdKpImUri, '1.3.6.1.5.5.7.3.40');
+ CheckKeyPurposeID(TKeyPurposeId.IdKpConfigSigning, '1.3.6.1.5.5.7.3.41');
+ CheckKeyPurposeID(TKeyPurposeId.IdKpTrustAnchorConfigSigning, '1.3.6.1.5.5.7.3.42');
+ CheckKeyPurposeID(TKeyPurposeId.IdKpUpdatePackageSigning, '1.3.6.1.5.5.7.3.43');
+ CheckKeyPurposeID(TKeyPurposeId.IdKpSafetyCommunication, '1.3.6.1.5.5.7.3.44');
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TKeyPurposeIDTest);
+{$ELSE}
+RegisterTest(TKeyPurposeIDTest.Suite);
+{$ENDIF FPC}
+
+end.
diff --git a/CryptoLib.Tests/src/Pkcs/PkcsEncryptedPrivateKeyInfoTests.pas b/CryptoLib.Tests/src/Pkcs/PkcsEncryptedPrivateKeyInfoTests.pas
index 327a6b73..106983fa 100644
--- a/CryptoLib.Tests/src/Pkcs/PkcsEncryptedPrivateKeyInfoTests.pas
+++ b/CryptoLib.Tests/src/Pkcs/PkcsEncryptedPrivateKeyInfoTests.pas
@@ -45,6 +45,7 @@ interface
ClpPrivateKeyInfoFactory,
ClpPrivateKeyFactory,
ClpPbeUtilities,
+ ClpAsn1Objects,
ClpIPkcsAsn1Objects,
ClpPkcsAsn1Objects,
ClpCryptoLibTypes,
@@ -73,6 +74,8 @@ TTestPkcsEncryptedPrivateKeyInfo = class(TCryptoLibAlgorithmTestCase)
procedure TestOpensslPbes2AesDefaultKeys;
procedure TestPbkdf2IterationCountBound;
procedure TestPkcs5V1PbeIterationCountBound;
+ procedure TestPbeDefaultMaxIterationCount;
+ procedure TestPbes2DefaultMaxIterationCountBound;
end;
@@ -278,6 +281,70 @@ procedure TTestPkcsEncryptedPrivateKeyInfo.TestPkcs5V1PbeIterationCountBound;
end;
end;
+procedure TTestPkcsEncryptedPrivateKeyInfo.TestPbeDefaultMaxIterationCount;
+var
+ LOldMax: Int32;
+ LCount: Int32;
+begin
+ LOldMax := TPbeUtilities.MaxIterationCount;
+ try
+ TPbeUtilities.MaxIterationCount := -1;
+
+ LCount := TPbeUtilities.CheckPbeIterationCount(TDerInteger.ValueOf(5000000));
+ CheckEquals(5000000, LCount, 'default max iteration count should allow 5_000_000');
+
+ try
+ TPbeUtilities.CheckPbeIterationCount(TDerInteger.ValueOf(5000001));
+ Fail('iteration count above default max accepted');
+ except
+ on E: EArgumentCryptoLibException do
+ CheckTrue(Pos('greater than 5000000', E.Message) > 0,
+ 'unexpected message: ' + E.Message);
+ end;
+ finally
+ TPbeUtilities.MaxIterationCount := LOldMax;
+ end;
+end;
+
+procedure TTestPkcsEncryptedPrivateKeyInfo.TestPbes2DefaultMaxIterationCountBound;
+const
+ LExcessiveIterationCount = 5000001;
+var
+ LSalt, LIv: TCryptoLibByteArray;
+ LPassword: TCryptoLibCharArray;
+ LPbkdf2Params: IPbkdf2Params;
+ LKeyDerivFunc: IKeyDerivationFunc;
+ LEncScheme: IEncryptionScheme;
+ LPbeS2Params: IPbeS2Parameters;
+ LOldMax: Int32;
+begin
+ LPassword := StringToCharArray('password');
+ LSalt := DecodeHex('0102030405060708090A0B0C0D0E0F1011121314');
+ LIv := DecodeHex('0102030405060708090A0B0C0D0E0F10');
+
+ LPbkdf2Params := TPbkdf2Params.Create(LSalt, LExcessiveIterationCount);
+ LKeyDerivFunc := TKeyDerivationFunc.Create(TPkcsObjectIdentifiers.IdPbkdf2, LPbkdf2Params);
+ LEncScheme := TEncryptionScheme.Create(TNistObjectIdentifiers.IdAes256Cbc,
+ TDerOctetString.FromContents(LIv));
+ LPbeS2Params := TPbeS2Parameters.Create(LKeyDerivFunc, LEncScheme);
+
+ LOldMax := TPbeUtilities.MaxIterationCount;
+ try
+ TPbeUtilities.MaxIterationCount := -1;
+ try
+ TPbeUtilities.GenerateCipherParameters(TPkcsObjectIdentifiers.IdPbeS2,
+ LPassword, LPbeS2Params);
+ Fail('PBES2 derivation accepted excessive default-max iteration count');
+ except
+ on E: EArgumentCryptoLibException do
+ CheckTrue(Pos('greater than 5000000', E.Message) > 0,
+ 'unexpected message: ' + E.Message);
+ end;
+ finally
+ TPbeUtilities.MaxIterationCount := LOldMax;
+ end;
+end;
+
initialization
{$IFDEF FPC}
diff --git a/CryptoLib/src/Asn1/ClpAsn1Streams.pas b/CryptoLib/src/Asn1/ClpAsn1Streams.pas
index b36204b8..07c00507 100644
--- a/CryptoLib/src/Asn1/ClpAsn1Streams.pas
+++ b/CryptoLib/src/Asn1/ClpAsn1Streams.pas
@@ -123,6 +123,7 @@ TAsn1DefiniteLengthInputStream = class sealed(TAsn1LimitedInputStream)
///
/// Reads bytes from the stream into a buffer.
///
+ function Read(var ABuffer; ACount: LongInt): LongInt; override;
function Read(ABuffer: TCryptoLibByteArray; AOffset, ACount: LongInt)
: LongInt; override;
@@ -165,6 +166,7 @@ TAsn1IndefiniteLengthInputStream = class sealed(TAsn1LimitedInputStream)
///
/// Reads bytes from the stream into a buffer.
///
+ function Read(var ABuffer; ACount: LongInt): LongInt; override;
function Read(ABuffer: TCryptoLibByteArray; AOffset, ACount: LongInt)
: LongInt; override;
@@ -386,6 +388,7 @@ TAsn1ConstructedBitStream = class(TBaseInputStream)
///
/// Read bytes from the stream.
///
+ function Read(var ABuffer; ACount: LongInt): LongInt; override;
function Read(ABuffer: TCryptoLibByteArray; AOffset, ACount: LongInt)
: LongInt; override;
@@ -425,6 +428,7 @@ TAsn1ConstructedOctetStream = class(TBaseInputStream)
///
/// Read bytes from the stream.
///
+ function Read(var ABuffer; ACount: LongInt): LongInt; override;
function Read(ABuffer: TCryptoLibByteArray; AOffset, ACount: LongInt)
: LongInt; override;
@@ -573,6 +577,49 @@ implementation
ClpAsn1Parsers,
ClpAsn1Core;
+type
+ TLimitedCapacityMemoryStream = class(TMemoryStream)
+ strict private
+ FLimitedCapacity: Int32;
+ strict protected
+{$IFDEF FPC}
+ function Realloc(var NewCapacity: PtrInt): Pointer; override;
+{$ELSE}
+ procedure SetCapacity(NewCapacity: NativeInt); override;
+{$ENDIF}
+ public
+ constructor Create(ALimitedCapacity: Int32);
+ function GetBuffer: TCryptoLibByteArray;
+ end;
+
+{ TLimitedCapacityMemoryStream }
+
+constructor TLimitedCapacityMemoryStream.Create(ALimitedCapacity: Int32);
+begin
+ inherited Create;
+ FLimitedCapacity := ALimitedCapacity;
+end;
+
+{$IFDEF FPC}
+function TLimitedCapacityMemoryStream.Realloc(var NewCapacity: PtrInt): Pointer;
+begin
+ NewCapacity := Min(NewCapacity * 2, FLimitedCapacity);
+ Result := inherited Realloc(NewCapacity);
+end;
+{$ELSE}
+procedure TLimitedCapacityMemoryStream.SetCapacity(NewCapacity: NativeInt);
+begin
+ inherited SetCapacity(Min(NewCapacity * 2, FLimitedCapacity));
+end;
+{$ENDIF}
+
+function TLimitedCapacityMemoryStream.GetBuffer: TCryptoLibByteArray;
+begin
+ System.SetLength(Result, Size);
+ if Size > 0 then
+ Move(Memory^, Result[0], Size);
+end;
+
{ TAsn1InputStream }
class constructor TAsn1InputStream.Create;
@@ -678,8 +725,7 @@ function TAsn1DefiniteLengthInputStream.ReadByte: Int32;
end;
end;
-function TAsn1DefiniteLengthInputStream.Read(ABuffer: TCryptoLibByteArray;
- AOffset, ACount: LongInt): LongInt;
+function TAsn1DefiniteLengthInputStream.Read(var ABuffer; ACount: LongInt): LongInt;
var
LToRead, LNumRead: Int32;
begin
@@ -690,7 +736,7 @@ function TAsn1DefiniteLengthInputStream.Read(ABuffer: TCryptoLibByteArray;
end;
LToRead := Min(ACount, FRemaining);
- LNumRead := FIn.Read(ABuffer[AOffset], LToRead);
+ LNumRead := FIn.Read(ABuffer, LToRead);
if LNumRead < 1 then
raise EEndOfStreamCryptoLibException.CreateResFmt(@SDefLengthObjectTruncated, [FOriginalLength, FRemaining]);
@@ -704,6 +750,13 @@ function TAsn1DefiniteLengthInputStream.Read(ABuffer: TCryptoLibByteArray;
Result := LNumRead;
end;
+function TAsn1DefiniteLengthInputStream.Read(ABuffer: TCryptoLibByteArray;
+ AOffset, ACount: LongInt): LongInt;
+begin
+ TStreamUtilities.ValidateBufferArguments(ABuffer, AOffset, ACount);
+ Result := Read(ABuffer[AOffset], ACount);
+end;
+
procedure TAsn1DefiniteLengthInputStream.ReadAllIntoByteArray
(const ABuf: TCryptoLibByteArray);
var
@@ -727,6 +780,9 @@ procedure TAsn1DefiniteLengthInputStream.ReadAllIntoByteArray
end;
function TAsn1DefiniteLengthInputStream.ToArray: TCryptoLibByteArray;
+var
+ LBuf: TLimitedCapacityMemoryStream;
+ LThreshold: Int32;
begin
Result := nil;
if FRemaining = 0 then
@@ -736,6 +792,19 @@ function TAsn1DefiniteLengthInputStream.ToArray: TCryptoLibByteArray;
TAsn1InputStream.CheckLength(FRemaining, Limit);
+ LThreshold := TStreamUtilities.DefaultBufferSize * 8;
+ if FRemaining > LThreshold then
+ begin
+ LBuf := TLimitedCapacityMemoryStream.Create(FRemaining);
+ try
+ TStreamUtilities.CopyTo(Self, LBuf);
+ Result := LBuf.GetBuffer();
+ finally
+ LBuf.Free;
+ end;
+ Exit;
+ end;
+
System.SetLength(Result, FRemaining);
FRemaining := FRemaining - TStreamUtilities.ReadFully(FIn, Result, 0,
System.Length(Result));
@@ -796,15 +865,13 @@ function TAsn1IndefiniteLengthInputStream.ReadByte: Int32;
FLookAhead := RequireByte();
end;
-function TAsn1IndefiniteLengthInputStream.Read(ABuffer: TCryptoLibByteArray;
- AOffset, ACount: LongInt): LongInt;
+function TAsn1IndefiniteLengthInputStream.Read(var ABuffer; ACount: LongInt): LongInt;
var
LNumRead: Int32;
begin
- // Only use this optimisation if we aren't checking for 00
if FEofOn00 or (ACount <= 1) then
begin
- Result := inherited Read(ABuffer, AOffset, ACount);
+ Result := inherited Read(ABuffer, ACount);
Exit;
end;
@@ -814,16 +881,23 @@ function TAsn1IndefiniteLengthInputStream.Read(ABuffer: TCryptoLibByteArray;
Exit;
end;
- LNumRead := FIn.Read(ABuffer[AOffset + 1], ACount - 1);
+ LNumRead := FIn.Read(PByte(@ABuffer)[1], ACount - 1);
if LNumRead <= 0 then
raise EEndOfStreamCryptoLibException.CreateRes(@SUnexpectedEndOfStream);
- ABuffer[AOffset] := Byte(FLookAhead);
+ PByte(@ABuffer)[0] := Byte(FLookAhead);
FLookAhead := RequireByte();
Result := LNumRead + 1;
end;
+function TAsn1IndefiniteLengthInputStream.Read(ABuffer: TCryptoLibByteArray;
+ AOffset, ACount: LongInt): LongInt;
+begin
+ TStreamUtilities.ValidateBufferArguments(ABuffer, AOffset, ACount);
+ Result := Read(ABuffer[AOffset], ACount);
+end;
+
function TAsn1IndefiniteLengthInputStream.RequireByte: Int32;
var
LB: Int32;
@@ -1275,13 +1349,10 @@ function TAsn1ConstructedBitStream.GetNextParser(): IAsn1BitStringParser;
end;
end;
-function TAsn1ConstructedBitStream.Read(ABuffer: TCryptoLibByteArray;
- AOffset, ACount: LongInt): LongInt;
+function TAsn1ConstructedBitStream.Read(var ABuffer; ACount: LongInt): LongInt;
var
LTotalRead, LNumRead: Int32;
begin
- TStreamUtilities.ValidateBufferArguments(ABuffer, AOffset, ACount);
-
if ACount < 1 then
begin
Result := 0;
@@ -1311,7 +1382,7 @@ function TAsn1ConstructedBitStream.Read(ABuffer: TCryptoLibByteArray;
while True do
begin
- LNumRead := FCurrentStream.Read(ABuffer[AOffset + LTotalRead], ACount - LTotalRead);
+ LNumRead := FCurrentStream.Read(PByte(@ABuffer)[LTotalRead], ACount - LTotalRead);
if LNumRead > 0 then
begin
@@ -1340,6 +1411,13 @@ function TAsn1ConstructedBitStream.Read(ABuffer: TCryptoLibByteArray;
end;
end;
+function TAsn1ConstructedBitStream.Read(ABuffer: TCryptoLibByteArray;
+ AOffset, ACount: LongInt): LongInt;
+begin
+ TStreamUtilities.ValidateBufferArguments(ABuffer, AOffset, ACount);
+ Result := Read(ABuffer[AOffset], ACount);
+end;
+
function TAsn1ConstructedBitStream.ReadByte: Int32;
var
LB: Int32;
@@ -1427,13 +1505,10 @@ function TAsn1ConstructedOctetStream.GetNextParser(): IAsn1OctetStringParser;
end;
end;
-function TAsn1ConstructedOctetStream.Read(ABuffer: TCryptoLibByteArray;
- AOffset, ACount: LongInt): LongInt;
+function TAsn1ConstructedOctetStream.Read(var ABuffer; ACount: LongInt): LongInt;
var
LTotalRead, LNumRead: Int32;
begin
- TStreamUtilities.ValidateBufferArguments(ABuffer, AOffset, ACount);
-
if ACount < 1 then
begin
Result := 0;
@@ -1463,7 +1538,7 @@ function TAsn1ConstructedOctetStream.Read(ABuffer: TCryptoLibByteArray;
while True do
begin
- LNumRead := FCurrentStream.Read(ABuffer[AOffset + LTotalRead], ACount - LTotalRead);
+ LNumRead := FCurrentStream.Read(PByte(@ABuffer)[LTotalRead], ACount - LTotalRead);
if LNumRead > 0 then
begin
@@ -1491,6 +1566,13 @@ function TAsn1ConstructedOctetStream.Read(ABuffer: TCryptoLibByteArray;
end;
end;
+function TAsn1ConstructedOctetStream.Read(ABuffer: TCryptoLibByteArray;
+ AOffset, ACount: LongInt): LongInt;
+begin
+ TStreamUtilities.ValidateBufferArguments(ABuffer, AOffset, ACount);
+ Result := Read(ABuffer[AOffset], ACount);
+end;
+
function TAsn1ConstructedOctetStream.ReadByte: Int32;
var
LB: Int32;
diff --git a/CryptoLib/src/Asn1/X509/ClpX509Asn1Objects.pas b/CryptoLib/src/Asn1/X509/ClpX509Asn1Objects.pas
index caedd057..468ca9f4 100644
--- a/CryptoLib/src/Asn1/X509/ClpX509Asn1Objects.pas
+++ b/CryptoLib/src/Asn1/X509/ClpX509Asn1Objects.pas
@@ -801,6 +801,7 @@ TKeyPurposeId = class abstract(TObject)
FIdKpCmcRa: IDerObjectIdentifier;
FIdKpCmKga: IDerObjectIdentifier;
FIdKpDocumentSigning: IDerObjectIdentifier;
+ FIdKpImUri: IDerObjectIdentifier;
FIdKpConfigSigning: IDerObjectIdentifier;
FIdKpTrustAnchorConfigSigning: IDerObjectIdentifier;
FIdKpUpdatePackageSigning: IDerObjectIdentifier;
@@ -840,6 +841,7 @@ TKeyPurposeId = class abstract(TObject)
class function GetIdKpCmcRa: IDerObjectIdentifier; static; inline;
class function GetIdKpCmKga: IDerObjectIdentifier; static; inline;
class function GetIdKpDocumentSigning: IDerObjectIdentifier; static; inline;
+ class function GetIdKpImUri: IDerObjectIdentifier; static; inline;
class function GetIdKpConfigSigning: IDerObjectIdentifier; static; inline;
class function GetIdKpTrustAnchorConfigSigning: IDerObjectIdentifier; static; inline;
class function GetIdKpUpdatePackageSigning: IDerObjectIdentifier; static; inline;
@@ -882,6 +884,8 @@ TKeyPurposeId = class abstract(TObject)
class property IdKpCmKga: IDerObjectIdentifier read GetIdKpCmKga;
/// RFC 9336 sec. 3.1 ? signing documents (e.g. PDF, XML, JSON) for human consumption (id-kp-documentSigning, { id-kp 36 }).
class property IdKpDocumentSigning: IDerObjectIdentifier read GetIdKpDocumentSigning;
+ /// RFC 9734 sec. 3 ? proving the identity of an IM client whose IM URI (RFC 3860) or XMPP URI (RFC 6121) appears in the subjectAltName (id-kp-imUri, { id-kp 40 }).
+ class property IdKpImUri: IDerObjectIdentifier read GetIdKpImUri;
/// RFC 9809 sec. 3 ? signing general-purpose configuration files (id-kp-configSigning, { id-kp 41 }).
class property IdKpConfigSigning: IDerObjectIdentifier read GetIdKpConfigSigning;
/// RFC 9809 sec. 3 ? signing trust anchor configuration files (id-kp-trustAnchorConfigSigning, { id-kp 42 }).
@@ -6162,6 +6166,7 @@ function TExtensions.ToAsn1Object: IAsn1Object;
FIdKpCmcRa := TDerObjectIdentifier.Create(LIdKp.ID + '.28');
FIdKpCmKga := TDerObjectIdentifier.Create(LIdKp.ID + '.32');
FIdKpDocumentSigning := TDerObjectIdentifier.Create(LIdKp.ID + '.36');
+ FIdKpImUri := TDerObjectIdentifier.Create(LIdKp.ID + '.40');
FIdKpConfigSigning := TDerObjectIdentifier.Create(LIdKp.ID + '.41');
FIdKpTrustAnchorConfigSigning := TDerObjectIdentifier.Create(LIdKp.ID + '.42');
FIdKpUpdatePackageSigning := TDerObjectIdentifier.Create(LIdKp.ID + '.43');
@@ -6299,6 +6304,11 @@ class function TKeyPurposeId.GetIdKpDocumentSigning: IDerObjectIdentifier;
Result := FIdKpDocumentSigning;
end;
+class function TKeyPurposeId.GetIdKpImUri: IDerObjectIdentifier;
+begin
+ Result := FIdKpImUri;
+end;
+
class function TKeyPurposeId.GetIdKpConfigSigning: IDerObjectIdentifier;
begin
Result := FIdKpConfigSigning;
diff --git a/CryptoLib/src/Crypto/ClpPbeUtilities.pas b/CryptoLib/src/Crypto/ClpPbeUtilities.pas
index c90a1356..d74e7ecc 100644
--- a/CryptoLib/src/Crypto/ClpPbeUtilities.pas
+++ b/CryptoLib/src/Crypto/ClpPbeUtilities.pas
@@ -84,7 +84,7 @@ TPbeUtilities = class sealed(TObject)
Pkcs5S2 = 'Pkcs5S2';
Pkcs12 = 'Pkcs12';
OpenSsl = 'OpenSsl';
- DefaultMaxIterationCount = 10000000;
+ DefaultMaxIterationCount = 5000000;
class var
FAlgorithms: TDictionary;
FAlgorithmType: TDictionary;
@@ -94,7 +94,6 @@ TPbeUtilities = class sealed(TObject)
class destructor Destroy;
strict private
class function GetEffectiveMaxIterationCount: Int32; static;
- class function CheckPbeIterationCount(const AIterationCountObject: IDerInteger): Int32; static;
class function MakePbeGenerator(const AType: String; const ADigest: IDigest;
const AKey: TCryptoLibByteArray; const ASalt: TCryptoLibByteArray;
AIterationCount: Int32): IPbeParametersGenerator; static;
@@ -174,6 +173,12 @@ TPbeUtilities = class sealed(TObject)
const APassword: TCryptoLibCharArray; AWrongPkcs12Zero: Boolean;
const APbeParameters: IAsn1Encodable): ICipherParameters; overload; static;
+ ///
+ /// Validates a supplied PBE/PBKDF2 iteration count against
+ /// (or when unset).
+ ///
+ class function CheckPbeIterationCount(const AIterationCountObject: IDerInteger): Int32; static;
+
///
/// Maximum allowed PBE iteration count when decrypting PKCS#8 / PEM keys.
/// Unset (-1) or any negative value selects .
diff --git a/CryptoLib/src/IO/ClpStreamUtilities.pas b/CryptoLib/src/IO/ClpStreamUtilities.pas
index 0d979e57..8f47fce2 100644
--- a/CryptoLib/src/IO/ClpStreamUtilities.pas
+++ b/CryptoLib/src/IO/ClpStreamUtilities.pas
@@ -160,7 +160,7 @@ class function TStreamUtilities.PipeAllLimited(const AInStr: TStream;
var
LLimited: TLimitedInputStream;
begin
- LLimited := TLimitedInputStream.Create(AInStr, ALimit);
+ LLimited := TLimitedInputStream.Create(ALimit, AInStr, True);
try
CopyTo(LLimited, AOutStr, ABufferSize);
Result := ALimit - LLimited.CurrentLimit;
diff --git a/CryptoLib/src/IO/ClpStreams.pas b/CryptoLib/src/IO/ClpStreams.pas
index 0f856ae9..e57cade6 100644
--- a/CryptoLib/src/IO/ClpStreams.pas
+++ b/CryptoLib/src/IO/ClpStreams.pas
@@ -207,17 +207,21 @@ TLimitedInputStream = class sealed(TBaseInputStream)
strict private
var
- FLimit: Int64;
+ FCurrentLimit: Int64;
+ FLeaveOpen: Boolean;
FStream: TStream;
public
- constructor Create(const AStream: TStream; ALimit: Int64);
+ constructor Create(ALimit: Int64; const AStream: TStream;
+ ALeaveOpen: Boolean = False);
+
+ destructor Destroy; override;
function Read(var ABuffer; ACount: LongInt): LongInt; override;
function ReadByte(): Int32; override;
- property CurrentLimit: Int64 read FLimit;
+ property CurrentLimit: Int64 read FCurrentLimit;
end;
@@ -649,11 +653,20 @@ constructor TBufferedFilterStream.Create(const AStream: TStream;
{ TLimitedInputStream }
-constructor TLimitedInputStream.Create(const AStream: TStream; ALimit: Int64);
+constructor TLimitedInputStream.Create(ALimit: Int64; const AStream: TStream;
+ ALeaveOpen: Boolean);
begin
inherited Create();
FStream := AStream;
- FLimit := ALimit;
+ FCurrentLimit := ALimit;
+ FLeaveOpen := ALeaveOpen;
+end;
+
+destructor TLimitedInputStream.Destroy;
+begin
+ if not FLeaveOpen then
+ FStream.Free;
+ inherited Destroy;
end;
function TLimitedInputStream.Read(var ABuffer; ACount: LongInt): LongInt;
@@ -663,8 +676,8 @@ function TLimitedInputStream.Read(var ABuffer; ACount: LongInt): LongInt;
LNumRead := FStream.Read(ABuffer, ACount);
if LNumRead > 0 then
begin
- FLimit := FLimit - LNumRead;
- if FLimit < 0 then
+ FCurrentLimit := FCurrentLimit - LNumRead;
+ if FCurrentLimit < 0 then
raise EStreamOverflowCryptoLibException.CreateRes(@SDataOverflow);
end;
Result := LNumRead;
@@ -677,8 +690,8 @@ function TLimitedInputStream.ReadByte(): Int32;
LB := FStream.ReadByte();
if LB >= 0 then
begin
- System.Dec(FLimit);
- if FLimit < 0 then
+ System.Dec(FCurrentLimit);
+ if FCurrentLimit < 0 then
raise EStreamOverflowCryptoLibException.CreateRes(@SDataOverflow);
end;
Result := LB;