Files
2026-08-30 22:02:02 +00:00

187 lines
6.0 KiB
PHP

<?php
declare(strict_types=1);
/**
* Asn1Test.php
*
* @since 2026-07-15
* @category Library
* @package PdfSign
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf-sign
*
* This file is part of tc-lib-pdf-sign software library.
*/
namespace Test\Cms;
use Com\Tecnick\Pdf\Sign\Cms\Asn1;
use Com\Tecnick\Pdf\Sign\Exception;
use PHPUnit\Framework\TestCase;
/**
* Asn1 Test
*
* @since 2026-07-15
* @category Library
* @package PdfSign
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2026 Nicola Asuni - Tecnick.com LTD
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
* @link https://github.com/tecnickcom/tc-lib-pdf-sign
*/
class Asn1Test extends TestCase
{
private Asn1 $asn1;
protected function setUp(): void
{
$this->asn1 = new Asn1();
}
public function testEncodeLengthShortForm(): void
{
$this->assertSame("\x05", $this->asn1->encodeLength(5));
$this->assertSame("\x7F", $this->asn1->encodeLength(127));
}
public function testEncodeLengthLongForm(): void
{
$this->assertSame("\x81\x80", $this->asn1->encodeLength(128));
$this->assertSame("\x82\x01\x00", $this->asn1->encodeLength(256));
}
public function testEncodeInteger(): void
{
$this->assertSame("\x02\x01\x00", $this->asn1->encodeInteger(0));
$this->assertSame("\x02\x01\x7F", $this->asn1->encodeInteger(127));
$this->assertSame("\x02\x02\x00\xFF", $this->asn1->encodeInteger(255));
$this->assertSame("\x02\x02\x01\x00", $this->asn1->encodeInteger(256));
}
public function testEncodeIntegerBytesTrimsAndPads(): void
{
$this->assertSame("\x02\x01\x7F", $this->asn1->encodeIntegerBytes("\x00\x7F"));
$this->assertSame("\x02\x02\x00\x80", $this->asn1->encodeIntegerBytes("\x80"));
}
public function testEncodeBoolean(): void
{
$this->assertSame("\x01\x01\xFF", $this->asn1->encodeBoolean(true));
$this->assertSame("\x01\x01\x00", $this->asn1->encodeBoolean(false));
}
public function testEncodeNull(): void
{
$this->assertSame("\x05\x00", $this->asn1->encodeNull());
}
public function testEncodeOctetStringSequenceSet(): void
{
$this->assertSame("\x04\x02AB", $this->asn1->encodeOctetString('AB'));
$this->assertSame("\x30\x02AB", $this->asn1->encodeSequence('AB'));
$this->assertSame("\x31\x02AB", $this->asn1->encodeSet('AB'));
}
public function testEncodeContext(): void
{
$this->assertSame("\xA0\x02AB", $this->asn1->encodeContext(0, 'AB'));
$this->assertSame("\xA3\x02AB", $this->asn1->encodeContext(3, 'AB'));
}
public function testEncodeObjectIdentifier(): void
{
// sha256WithRSAEncryption: 1.2.840.113549.1.1.11
$this->assertSame(
'06092a864886f70d01010b',
\bin2hex($this->asn1->encodeObjectIdentifier('1.2.840.113549.1.1.11')),
);
}
public function testReadTlvRoundTrip(): void
{
$der = $this->asn1->encodeSequence($this->asn1->encodeInteger(256));
$offset = 0;
$tlv = $this->asn1->readTlv($der, $offset);
$this->assertSame(0x30, $tlv['tag']);
$this->assertSame(\strlen($der), $offset);
$this->assertSame($der, $tlv['raw']);
$inner = 0;
$intTlv = $this->asn1->readTlv($tlv['value'], $inner);
$this->assertSame(0x02, $intTlv['tag']);
$this->assertSame(256, $this->asn1->decodeInteger($intTlv['value']));
}
public function testReadTlvRejectsTruncatedData(): void
{
$this->expectException(Exception::class);
$offset = 0;
$this->asn1->readTlv("\x30\x05\x00", $offset);
}
public function testDecodeIntegerRejectsEmpty(): void
{
$this->expectException(Exception::class);
$this->asn1->decodeInteger('');
}
public function testEncodeIntegerBytesEmptyInputYieldsZero(): void
{
$this->assertSame("\x02\x01\x00", $this->asn1->encodeIntegerBytes(''));
}
public function testEncodeObjectIdentifierRejectsSingleArc(): void
{
$this->expectException(Exception::class);
$this->asn1->encodeObjectIdentifier('1');
}
public function testEncodeObjectIdentifierClampsNegativeArc(): void
{
$this->assertSame('06022a00', \bin2hex($this->asn1->encodeObjectIdentifier('1.2.-1')));
}
public function testReadTlvRejectsEmptyData(): void
{
$this->expectException(Exception::class);
$offset = 0;
$this->asn1->readTlv('', $offset);
}
public function testReadTlvRejectsMissingLength(): void
{
$this->expectException(Exception::class);
$offset = 0;
$this->asn1->readTlv("\x30", $offset);
}
public function testReadTlvRejectsUnsupportedLongFormLength(): void
{
$this->expectException(Exception::class);
$offset = 0;
// 0x85 announces a 5-octet length, which exceeds the supported 4 octets.
$this->asn1->readTlv("\x04\x85\x00\x00\x00\x00\x00", $offset);
}
public function testReadTlvHandlesLongFormLength(): void
{
// A 200-byte payload forces a multi-octet (long-form) DER length.
$payload = \str_repeat("\x41", 200);
$der = $this->asn1->encodeOctetString($payload);
$offset = 0;
$tlv = $this->asn1->readTlv($der, $offset);
$this->assertSame(0x04, $tlv['tag']);
$this->assertSame($payload, $tlv['value']);
$this->assertSame(\strlen($der), $offset);
}
// Coverage note: Asn1::encodeLength() throws on a length needing more than
// 127 octets to represent. That requires content larger than 2^1016 bytes,
// which is unrepresentable by a PHP int and unallocatable, so the guard is
// defensive and cannot be exercised in a unit test.
}