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
14 changes: 14 additions & 0 deletions src/FreeDSx/Asn1/Encoder/BerEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use FreeDSx\Asn1\Exception\EncoderException;
use FreeDSx\Asn1\Exception\InvalidArgumentException;
use FreeDSx\Asn1\Exception\PartialPduException;
use FreeDSx\Asn1\Exception\PduLengthException;
use FreeDSx\Asn1\Type\AbstractTimeType;
use FreeDSx\Asn1\Type\AbstractType;
use FreeDSx\Asn1\Type as EncodedType;
Expand Down Expand Up @@ -176,6 +177,8 @@ public function __construct(protected EncoderOptions $options = new EncoderOptio
* {@inheritdoc}
*
* @return AbstractType<mixed>
*
* @throws PduLengthException
*/
public function decode(
string $binary,
Expand Down Expand Up @@ -364,6 +367,7 @@ protected function stopEncoding(): void
*
* @throws EncoderException
* @throws PartialPduException
* @throws PduLengthException
*/
protected function decodeBytes(bool $isRoot = false, $tagType = null, $length = null, $isConstructed = null, $class = null): AbstractType
{
Expand Down Expand Up @@ -397,6 +401,16 @@ protected function decodeBytes(bool $isRoot = false, $tagType = null, $length =
if ($length > 128) {
$length = $this->decodeLongDefiniteLength($length);
}

# Reject an oversized root PDU on its declared length alone, before its body is buffered or decoded.
if ($isRoot && $this->options->maxLength > 0 && $length > $this->options->maxLength) {
throw new PduLengthException(sprintf(
'The PDU length %d exceeds the maximum allowed length of %d.',
$length,
$this->options->maxLength,
));
}

$tagType = ($class === AbstractType::TAG_CLASS_UNIVERSAL) ? $tagNumber : ($this->tmpTagMap[$class][$tagNumber] ?? null);

if (($this->maxLen - $this->pos) < $length) {
Expand Down
2 changes: 2 additions & 0 deletions src/FreeDSx/Asn1/Encoder/EncoderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use FreeDSx\Asn1\Exception\EncoderException;
use FreeDSx\Asn1\Exception\PartialPduException;
use FreeDSx\Asn1\Exception\PduLengthException;
use FreeDSx\Asn1\Type\AbstractType;
use FreeDSx\Asn1\Type\IncompleteType;

Expand Down Expand Up @@ -51,6 +52,7 @@ public function complete(IncompleteType $type, int $tagType, array $tagMap = [])
*
* @throws EncoderException
* @throws PartialPduException
* @throws PduLengthException
*/
public function decode(
string $binary,
Expand Down
4 changes: 4 additions & 0 deletions src/FreeDSx/Asn1/Encoder/EncoderOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@
*/
final readonly class EncoderOptions
{
/**
* @param int $maxLength Reject a root PDU whose declared length exceeds this many bytes; 0 disables the limit.
*/
public function __construct(
public string $bitstringPadding = '0',
public int $maxLength = 0,
) {
}
}
20 changes: 20 additions & 0 deletions src/FreeDSx/Asn1/Exception/PduLengthException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
/**
* This file is part of the FreeDSx ASN1 package.
*
* (c) Chad Sikorra <Chad.Sikorra@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FreeDSx\Asn1\Exception;

/**
* Thrown when a root PDU's declared length exceeds the configured maximum.
*
* @author Chad Sikorra <Chad.Sikorra@gmail.com>
*/
class PduLengthException extends EncoderException
{
}
28 changes: 28 additions & 0 deletions tests/unit/Encoder/BerEncoderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use FreeDSx\Asn1\Encoder\EncoderOptions;
use FreeDSx\Asn1\Exception\EncoderException;
use FreeDSx\Asn1\Exception\PartialPduException;
use FreeDSx\Asn1\Exception\PduLengthException;
use FreeDSx\Asn1\Type\AbstractType;
use FreeDSx\Asn1\Type\BitStringType;
use FreeDSx\Asn1\Type\BmpStringType;
Expand Down Expand Up @@ -86,6 +87,33 @@ public function test_it_should_accept_options_via_the_constructor(): void
);
}

public function test_it_should_reject_a_root_pdu_whose_declared_length_exceeds_the_max_length(): void
{
$subject = new BerEncoder(new EncoderOptions(maxLength: 100));

$this->expectException(PduLengthException::class);

$subject->decode(hex2bin('3084000000c8'));
}

public function test_it_should_decode_a_root_pdu_within_the_max_length(): void
{
$subject = new BerEncoder(new EncoderOptions(maxLength: 1000));
$encoded = $subject->encode(new OctetStringType('foo'));

self::assertEquals(
new OctetStringType('foo'),
$subject->decode($encoded),
);
}

public function test_it_should_treat_an_oversized_declared_length_as_partial_when_no_max_length_is_set(): void
{
$this->expectException(PartialPduException::class);

$this->subject->decode(hex2bin('3084000000c8'));
}

public function test_it_should_decode_long_definite_length_when_the_length_is_the_exact_size_of_the_payload(): void
{
$tagAndLength = hex2bin('3084000000350201');
Expand Down
Loading