diff --git a/README.md b/README.md index 923c764..632ed81 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,11 @@ PHPASN1 can also read [BER encoded][6] binary data into separate PHP objects tha ## Changelog +#### v.1.5.0 +* fix a bug that would prevent you from decoding context specific tags on multiple objects [#57](https://github.com/fgrosse/PHPASN1/issues/57) + - `ExplicitlyTaggedObject::__construct` does now accept multiple objects to be tagged with a single tag + - `ExplicitlyTaggedObject::getContent` will now always return an array (even if only one object is tagged) + #### v.1.4.2 * fix a bug that would prevent you from decoding empty tagged objects [#57](https://github.com/fgrosse/PHPASN1/issues/57) diff --git a/examples/Issue14.php b/examples/Issue14.php index c3b2c1a..a630ebe 100644 --- a/examples/Issue14.php +++ b/examples/Issue14.php @@ -9,6 +9,7 @@ */ require_once __DIR__.'/../vendor/autoload.php'; +require_once 'shared.php'; use FG\ASN1\OID; use FG\ASN1\Object; @@ -17,27 +18,6 @@ use FG\ASN1\Universal\OctetString; use FG\ASN1\Universal\Sequence; -function printObject(Object $object, $depth = 0) -{ - $treeSymbol = ''; - $depthString = str_repeat('─', $depth); - if ($depth > 0) { - $treeSymbol = '├'; - } - - $name = Identifier::getShortName($object->getType()); - echo "{$treeSymbol}{$depthString}{$name} : "; - - echo $object->__toString().PHP_EOL; - - $content = $object->getContent(); - if (is_array($content)) { - foreach ($object as $child) { - printObject($child, $depth + 1); - } - } -} - $base64 = 'MIIINTCCBh2gAwIBAgIQbVVJc10C7UUjfYRHQ//7nTANBgkqhkiG9w0BAQsFADB0 MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDEtMCsGA1UECxMkQ2Vy dGlzaWduIENlcnRpZmljYWRvcmEgRGlnaXRhbCBTLkEuMSEwHwYDVQQDExhBQyBD diff --git a/examples/Issue29.php b/examples/Issue29.php index 56e96a8..faa2cec 100644 --- a/examples/Issue29.php +++ b/examples/Issue29.php @@ -9,34 +9,11 @@ */ use FG\ASN1\Object; -use FG\ASN1\Identifier; require_once __DIR__.'/../vendor/autoload.php'; +require_once 'shared.php'; $hex = 'a02b302906092a864886f70d01090e311c301a30180603551d110411300f820d636f72766573706163652e6465'; $asn = Object::fromBinary(hex2bin($hex)); -function printObject(Object $object, $depth = 0) -{ - $name = strtoupper(Identifier::getShortName($object->getType())); - $treeSymbol = ''; - $depthString = str_repeat('━', $depth); - if ($depth > 0) { - $treeSymbol = '┣'; - $name = ' '.$name; - } - - echo "{$treeSymbol}{$depthString}{$name} : "; - echo $object->__toString().PHP_EOL; - - $content = $object->getContent(); - if ($content instanceof Object) { - printObject($content, $depth + 1); - } elseif (is_array($content)) { - foreach ($object as $child) { - printObject($child, $depth + 1); - } - } -} - printObject($asn); diff --git a/examples/Issue57.php b/examples/Issue57.php new file mode 100644 index 0000000..75f61e1 --- /dev/null +++ b/examples/Issue57.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use FG\ASN1\ExplicitlyTaggedObject; +use FG\ASN1\Object; +use FG\ASN1\Universal\Enumerated; +use FG\ASN1\Universal\ObjectIdentifier; +use FG\ASN1\Universal\Sequence; + +require_once __DIR__.'/../vendor/autoload.php'; +require_once 'shared.php'; + +$input1 = ' +MIICWDCCAhgCAQAwVzEYMBYGA1UEAwwPY3J5cHRvZ3JhcGh5LmlvMQ0wCwYDVQQK +DARQeUNBMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxDzANBgNVBAcMBkF1 +c3RpbjCCAbYwggErBgcqhkjOOAQBMIIBHgKBgQCNf628CeKEqvppFUzqJBdwBJCe +UZ+LNdaFzeW07NyVg+dNNwoPiK2pjwJvJ3Yvs9XaeDb5ht/Ns1ieW5Jb6hFN78A+ ++B2uMMJLvG3z1YjpNCe7pkID1KWxaHsrXjtkPUxhSXb4n5WjjT5MiQZfupdRTCLF +Ctu/KJFjp0tUhZs1twIVAINd5WvQfPf4LiAy/niUmu0ReqLvAoGAH3F7Wgd4L8Lk +5o4xH+qRpU7dNrhqxjTRTwWmipfq6dLvMfse895Cw9EA35ymT1vcKux7/ftHTPgx +/qBYU7XgWfLSSYCgrEY/HoGK81I+PLeaOdRfqScxiXdShCRpz4VAsBSRAk6q+85g +GOih9GWMND9Lp8CyHlN2oh9L64SRlh4DgYQAAoGABxPwdkH2Npu1qVRSdKLUwBmY +Nn+zcbueE0NjY2cu1o+CF0wt4FyOg5vG3laN1QuijY2dhxlCOq7FVX3xDXc6si1t +Zcu4eASml7yP2WW5Uvn36FDt8TyKzbXXU7bRDlngtXMuPIK6+hQDQrxKO7oWvQaB +yKai27t+/mziuEY7FwugADAJBgcqhkjOOAQDAy8AMCwCFGHVjcAo0BEIGKfYF9dC +NXJ8Ss/fAhQJe1LhmOzpXeFyc/CpJN8jzp2BiA=='; + +$input2 = ' +MIIFGDCCAwACAQAwOjEWMBQGCgmSJomT8ixkARkWBnNlY3VyZTEgMB4GA1UEAxMX +LlNlY3VyZSBFbnRlcnByaXNlIENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQCzgEpL+Za7a3y7YpURDrxlGIBlks25fD0tHaZIYkBTaXA5h+9MWoXn +FA7AlIUt8pbBvXdJbOCmGaeQmBfBH0Qy9vTbx/DR2IOwzqy2ZHuurI5bPL12ceE2 +Mxa9xgY/i7U6MAUtoA3amEd7cKj2fz9EWZruRladOX0DXv9KexSan+45QjCWH+u2 +Cxem2zH9ZDNPGBuAF9YsAvkdHdAoX8aSm05ZAjUiO2e/+L57whh7zZiDY3WIhin7 +N/2JNTKVO6lx50S8a34XUKBt3SKgSR941hcLrBYUNftUYsTPo40bzKKcWqemiH+w +jQiDrln4V2b5EbVeoGWe4UDPXCVmC6UPklG7iYfF0eeK4ujV8uc9PtV2LvGLOFdm +AYE3+FAba5byQATw/DY8EJKQ7ptPigJhVe47NNeJlsKwk1haJ9k8ZazjS+vT45B5 +pqe0yBFAEon8TFnOLnAOblmKO12i0zqMUNAAlmr1c8jNjLr+dhruS+QropZmzZ24 +mAnFG+Y0qpfhMzAxTGQyVjyGwDfRK/ARmtrGpmROjj5+6VuMmZ6Ljf3xN09epmtH +gJe+lYNBlpfUYg16tm+OusnziYnXL6nIo2ChOY/7GNJJif9fjvvaPDCC98K64av5 +5rpIx7N/XH4hwHeQQkEQangExE+8UMyBNFNmvPnIHVHUZdYo4SLsYwIDAQABoIGY +MBsGCisGAQQBgjcNAgMxDRYLNi4zLjk2MDAuMi4weQYJKoZIhvcNAQkOMWwwajAQ +BgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU5nEIMEUT5mMd1WepmviwgK7dIzww +GQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAKZl6bAeaID3b/ic4aztL8ZZI7vi +D3A9otUKx6v1Xe63zDPR+DiWSnxb9m+l8OPtnWkcLkzEIM/IMWorHKUAJ/J871D0 +Qx+0/HbkcrjMtVu/dNrtb9Z9CXup66ZvxTPcpEziq0/n2yw8QdBaa+lli65Qcwcy +tzMQK6WQTRYfvVCIX9AKcPKxwx1DLH+7hL/bERB1lUDu59Jx6fQfqJrFVOY2N8c0 +MGvurfoHGmEoyCMIyvmIMu4+/wSNEE/sSDp4lZ6zuF6rf1m0GiLdTX2XJE+gfvep +JTFmp4S3WFqkszKvaxBIT+jV0XKTNDwnO+dpExwU4jZUh18CdEFkIUuQb0gFF8B7 +WJFVpNdsRqZRPBz83BW1Kjo0yAmaoTrGNmG0p6Qf3K2zbk1+Jik3VZq4rvKoTi20 +6RvLA2//cMNfkYPsuqvoHGe2e0GOLtIB63wJzloWROpb72ohEHsvCKullIJVSuiS +9sfTBAenHCyndgAEd4T3npTUdaiNumVEm5ilZId7LAYekJhkgFu3vlcl8blBJKjE +skVTp7JpBmdXCL/G/6H2SFjca4JMOAy3DxwlGdgneIaXazHs5nBK/BgKPIyPzZ4w +secxBTTCNgI48YezK3GDkn65cmlnkt6F6Mf0MwoDaXTuB88Jycbwb5ihKnHEJIsO +draiRBZruwMPwPIP +'; + +$input3 = ' +MIIC4zCCAcsCAQAwcjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx +ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDENMAsGA1UEAwwEdGVz +dDEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAL7t6CR90kOZ0m3qJx9koHBFdXnunEq9ELSUvsZ7oFiw +6zJsyi18S9znVRLEgsW4v2rTsLiJmKnMF2Kyd9AQ2X2zKUK49CTMqG1p/4lF0oLa +nQgNmjYC4VL61wTte6CUAJX7wZA8LL00QfCdgILKodP12E3c7Yp3yw5TAOBdA3Av +KnpsUiNLmQN3HSn9dwk9rCHulrP8LL97l//H2KSCvOvSfk1XeN7ADSyWMC6ZPBD/ +7WVnQo7GgMAA0Qhz+nIIFA5AyuFEbRiff+L5EEl+Bg6R9tgx+QUt9JXxF260dkAb +iKBmPcJldl2/GEmz6RAXhMwJnN35tbdzw0LVLzsypr0CAwEAAaAsMBMGCSqGSIb3 +DQEJAjEGDAR0ZXN0MBUGCSqGSIb3DQEJBzEIDAYxMjM0NTYwDQYJKoZIhvcNAQEF +BQADggEBACqZ5+3GP7QbTJAqnUloMRBJGYZl0/EyW6dAMoq7E+RiR2OlJGXgP+ga +DPv7Wa4jZiYmf2CZCWKP+WvTvcg4AQq5h7hhAN+u+s8kk0lIjYjnkKRAedGYKDUO +fPD9WowISru3RB1xyxlvgeZS6WoA6TD8REVa1UyFoNzUewvsrNVkKSHh1xk/+ePx +2Ovvrcg9pAWY4K8FvMRdFQKnEud9CAoMxqz3kszhxDW6rcr2mgFPSrKi5WNj+Scg +Tqod8xbB753JWjEbG6Hui9LIMihTX3ZJ0c2GB0buhEgz49y83X/byFHSGGSQpzxX +qXDFVov9UZ+sGy8CJ5ahII79yrfKpxY= +'; + +try { + echo 'Input 1:' . PHP_EOL; + printObject(Object::fromBinary(base64_decode($input1))); +} catch (Exception $exception) { + echo "ERROR: " . $exception->getMessage(); +} + +echo PHP_EOL; + +try { + echo 'Input 2:' . PHP_EOL; + printObject(Object::fromBinary(base64_decode($input2))); +} catch (Exception $exception) { + echo "ERROR: " . $exception->getMessage(); +} + +echo PHP_EOL; + +try { + echo 'Input 3:' . PHP_EOL; + printObject(Object::fromBinary(base64_decode($input3))); +} catch (Exception $exception) { + echo "ERROR: " . $exception->getMessage(); +} diff --git a/examples/parseBinary.php b/examples/parseBinary.php index cdf21a6..5bf7f07 100644 --- a/examples/parseBinary.php +++ b/examples/parseBinary.php @@ -9,55 +9,43 @@ */ require_once __DIR__.'/../vendor/autoload.php'; +require_once 'shared.php'; use FG\ASN1\Object; -use FG\ASN1\Identifier; $base64String = -'MIIDfjCCAuegAwIBAgIKZGgbPQAAAABnVTANBgkqhkiG9w0BAQUFADBGMQswCQYD -VQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAxMZR29vZ2xlIElu -dGVybmV0IEF1dGhvcml0eTAeFw0xMjA4MTYxMjI2MTlaFw0xMzA2MDcxOTQzMjda -MGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N -b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMRYwFAYDVQQDEw13d3cu -Z29vZ2xlLmRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEoGt7UvjRfcHM -tHNixkq3marhniRadVnpzux9iqTfB8sLIf39djxjoxOmaP6ddk7ZE8UaZ2eI22Kv -Yk9CLC8RLBMWkiql03gjzZ9D0fxUUT0Usp42mR8IoELELqru5f6OLLEBZxdKNZzr -9vrMLJypM61AMTfuLD9MvtlGASnHKwIDAQABo4IBUDCCAUwwHQYDVR0lBBYwFAYI -KwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBS7E76yPlQGezzNghLX64dAkcH1 -HTAfBgNVHSMEGDAWgBS/wDDr9UMRPme6npH7/Gra42sSJDBbBgNVHR8EVDBSMFCg -TqBMhkpodHRwOi8vd3d3LmdzdGF0aWMuY29tL0dvb2dsZUludGVybmV0QXV0aG9y -aXR5L0dvb2dsZUludGVybmV0QXV0aG9yaXR5LmNybDBmBggrBgEFBQcBAQRaMFgw -VgYIKwYBBQUHMAKGSmh0dHA6Ly93d3cuZ3N0YXRpYy5jb20vR29vZ2xlSW50ZXJu -ZXRBdXRob3JpdHkvR29vZ2xlSW50ZXJuZXRBdXRob3JpdHkuY3J0MAwGA1UdEwEB -/wQCMAAwGAYDVR0RBBEwD4INd3d3Lmdvb2dsZS5kZTANBgkqhkiG9w0BAQUFAAOB -gQBOBSVeQj4EiCzcgVUmO9BIzQqOHgOQq2ePbgrUln9aX+0SCLvJf/38HrT884Jf -CznPwmTKJmjLRIq6v6RPvYC9O45EM7kjB1YXcCCKiPK7IGmJf8dwZAO4MKLtJnv4 -D0k6lc6/SWpmbg33TqEDjl8OsvMUzV6S8XRz2L/rqZ1z1g=='; +'MIIFGDCCAwACAQAwOjEWMBQGCgmSJomT8ixkARkWBnNlY3VyZTEgMB4GA1UEAxMX +LlNlY3VyZSBFbnRlcnByaXNlIENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQCzgEpL+Za7a3y7YpURDrxlGIBlks25fD0tHaZIYkBTaXA5h+9MWoXn +FA7AlIUt8pbBvXdJbOCmGaeQmBfBH0Qy9vTbx/DR2IOwzqy2ZHuurI5bPL12ceE2 +Mxa9xgY/i7U6MAUtoA3amEd7cKj2fz9EWZruRladOX0DXv9KexSan+45QjCWH+u2 +Cxem2zH9ZDNPGBuAF9YsAvkdHdAoX8aSm05ZAjUiO2e/+L57whh7zZiDY3WIhin7 +N/2JNTKVO6lx50S8a34XUKBt3SKgSR941hcLrBYUNftUYsTPo40bzKKcWqemiH+w +jQiDrln4V2b5EbVeoGWe4UDPXCVmC6UPklG7iYfF0eeK4ujV8uc9PtV2LvGLOFdm +AYE3+FAba5byQATw/DY8EJKQ7ptPigJhVe47NNeJlsKwk1haJ9k8ZazjS+vT45B5 +pqe0yBFAEon8TFnOLnAOblmKO12i0zqMUNAAlmr1c8jNjLr+dhruS+QropZmzZ24 +mAnFG+Y0qpfhMzAxTGQyVjyGwDfRK/ARmtrGpmROjj5+6VuMmZ6Ljf3xN09epmtH +gJe+lYNBlpfUYg16tm+OusnziYnXL6nIo2ChOY/7GNJJif9fjvvaPDCC98K64av5 +5rpIx7N/XH4hwHeQQkEQangExE+8UMyBNFNmvPnIHVHUZdYo4SLsYwIDAQABoIGY +MBsGCisGAQQBgjcNAgMxDRYLNi4zLjk2MDAuMi4weQYJKoZIhvcNAQkOMWwwajAQ +BgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU5nEIMEUT5mMd1WepmviwgK7dIzww +GQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAKZl6bAeaID3b/ic4aztL8ZZI7vi +D3A9otUKx6v1Xe63zDPR+DiWSnxb9m+l8OPtnWkcLkzEIM/IMWorHKUAJ/J871D0 +Qx+0/HbkcrjMtVu/dNrtb9Z9CXup66ZvxTPcpEziq0/n2yw8QdBaa+lli65Qcwcy +tzMQK6WQTRYfvVCIX9AKcPKxwx1DLH+7hL/bERB1lUDu59Jx6fQfqJrFVOY2N8c0 +MGvurfoHGmEoyCMIyvmIMu4+/wSNEE/sSDp4lZ6zuF6rf1m0GiLdTX2XJE+gfvep +JTFmp4S3WFqkszKvaxBIT+jV0XKTNDwnO+dpExwU4jZUh18CdEFkIUuQb0gFF8B7 +WJFVpNdsRqZRPBz83BW1Kjo0yAmaoTrGNmG0p6Qf3K2zbk1+Jik3VZq4rvKoTi20 +6RvLA2//cMNfkYPsuqvoHGe2e0GOLtIB63wJzloWROpb72ohEHsvCKullIJVSuiS +9sfTBAenHCyndgAEd4T3npTUdaiNumVEm5ilZId7LAYekJhkgFu3vlcl8blBJKjE +skVTp7JpBmdXCL/G/6H2SFjca4JMOAy3DxwlGdgneIaXazHs5nBK/BgKPIyPzZ4w +secxBTTCNgI48YezK3GDkn65cmlnkt6F6Mf0MwoDaXTuB88Jycbwb5ihKnHEJIsO +draiRBZruwMPwPIP'; $binaryData = base64_decode($base64String); $asnObject = Object::fromBinary($binaryData); -function printObject(Object $object, $depth = 0) -{ - $treeSymbol = ''; - $depthString = str_repeat('━', $depth); - if ($depth > 0) { - $treeSymbol = '┣'; - } - - $name = strtoupper(Identifier::getShortName($object->getType())); - echo "{$treeSymbol}{$depthString}{$name} : "; - - echo $object->__toString().'
'; - - $content = $object->getContent(); - if (is_array($content)) { - foreach ($object as $child) { - printObject($child, $depth + 1); - } - } -} - ?> diff --git a/examples/shared.php b/examples/shared.php new file mode 100644 index 0000000..76c8e86 --- /dev/null +++ b/examples/shared.php @@ -0,0 +1,25 @@ + 0) { + $treeSymbol = '├'; + } + + $name = Identifier::getShortName($object->getType()); + echo "{$treeSymbol}{$depthString}{$name} : "; + + echo $object->__toString().PHP_EOL; + + $content = $object->getContent(); + if (is_array($content)) { + foreach ($object as $child) { + printObject($child, $depth + 1); + } + } +} diff --git a/lib/ASN1/ExplicitlyTaggedObject.php b/lib/ASN1/ExplicitlyTaggedObject.php index 67611ab..7c4b9d1 100644 --- a/lib/ASN1/ExplicitlyTaggedObject.php +++ b/lib/ASN1/ExplicitlyTaggedObject.php @@ -31,49 +31,55 @@ */ class ExplicitlyTaggedObject extends Object { - private $decoratedObject; + /** @var \FG\ASN1\Object[] */ + private $decoratedObjects; private $tag; /** * @param int $tag - * @param \FG\ASN1\Object|null $object + * @param \FG\ASN1\Object $objects,... */ - public function __construct($tag, Object $object = null) + public function __construct($tag, /* HH_FIXME[4858]: variadic + strict */ ...$objects) { $this->tag = $tag; - $this->decoratedObject = $object; + $this->decoratedObjects = $objects; } protected function calculateContentLength() { - if (isset($this->decoratedObject)) { - return $this->decoratedObject->getObjectLength(); - } else { - return 0; + $length = 0; + foreach ($this->decoratedObjects as $object) { + $length += $object->getObjectLength(); } + + return $length; } protected function getEncodedValue() { - if (isset($this->decoratedObject)) { - return $this->decoratedObject->getBinary(); - } else { - return ''; + $encoded = ''; + foreach ($this->decoratedObjects as $object) { + $encoded .= $object->getBinary(); } + + return $encoded; } public function getContent() { - return $this->decoratedObject; + return $this->decoratedObjects; } public function __toString() { - if (isset($this->decoratedObject)) { - $decoratedType = Identifier::getShortName($this->decoratedObject->getType()); - return "Context specific $decoratedType with tag [{$this->tag}]"; - } else { + switch ($length = count($this->decoratedObjects)) { + case 0: return "Context specific empty object with tag [{$this->tag}]"; + case 1: + $decoratedType = Identifier::getShortName($this->decoratedObjects[0]->getType()); + return "Context specific $decoratedType with tag [{$this->tag}]"; + default: + return "$length context specific objects with tag [{$this->tag}]"; } } @@ -98,24 +104,28 @@ public static function fromBinary(&$binaryData, &$offsetIndex = 0) { $identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex); $firstIdentifierOctet = ord($identifier); - assert(Identifier::isContextSpecificClass($firstIdentifierOctet)); - assert(Identifier::isConstructed($firstIdentifierOctet)); + assert(Identifier::isContextSpecificClass($firstIdentifierOctet), 'identifier octet should indicate context specific class'); + assert(Identifier::isConstructed($firstIdentifierOctet), 'identifier octet should indicate constructed object'); $tag = Identifier::getTagNumber($identifier); - $contentLength = self::parseContentLength($binaryData, $offsetIndex); - if ($contentLength == 0) { - return new self($tag); - } + $totalContentLength = self::parseContentLength($binaryData, $offsetIndex); + $remainingContentLength = $totalContentLength; $offsetIndexOfDecoratedObject = $offsetIndex; - $decoratedObject = Object::fromBinary($binaryData, $offsetIndex); - $decoratedObjectLength = $decoratedObject->getObjectLength(); - if ($decoratedObjectLength != $contentLength) { - throw new ParserException("Context-Specific explicitly tagged object [$tag] starting at offset $offsetIndexOfDecoratedObject is ".($decoratedObjectLength < $contentLength ? 'shorter than required' : 'longer than allowed').' in the outer tag', $offsetIndexOfDecoratedObject); + $decoratedObjects = []; + + while ($remainingContentLength > 0) { + $nextObject = Object::fromBinary($binaryData, $offsetIndex); + $remainingContentLength -= $nextObject->getObjectLength(); + $decoratedObjects[] = $nextObject; + } + + if ($remainingContentLength != 0) { + throw new ParserException("Context-Specific explicitly tagged object [$tag] starting at offset $offsetIndexOfDecoratedObject specifies a length of $totalContentLength octets but $remainingContentLength remain after parsing the content", $offsetIndexOfDecoratedObject); } - $parsedObject = new self($tag, $decoratedObject); - $parsedObject->setContentLength($contentLength); + $parsedObject = new self($tag, ...$decoratedObjects); + $parsedObject->setContentLength($totalContentLength); return $parsedObject; } } diff --git a/tests/ASN1/ExplicitlyTaggedObjectTest.php b/tests/ASN1/ExplicitlyTaggedObjectTest.php index 035729f..d1acfe7 100644 --- a/tests/ASN1/ExplicitlyTaggedObjectTest.php +++ b/tests/ASN1/ExplicitlyTaggedObjectTest.php @@ -11,6 +11,8 @@ namespace FG\Test\ASN1; use FG\ASN1\Identifier; +use FG\ASN1\Universal\Boolean; +use FG\ASN1\Universal\Integer; use FG\Test\ASN1TestCase; use FG\ASN1\ExplicitlyTaggedObject; use FG\ASN1\Universal\PrintableString; @@ -51,7 +53,7 @@ public function testGetContent() { $string = new PrintableString('test'); $object = new ExplicitlyTaggedObject(0, $string); - $this->assertEquals($string, $object->getContent()); + $this->assertEquals([$string], $object->getContent()); } public function testGetBinary() @@ -88,37 +90,29 @@ public function getTags() ]; } - /** - * @expectedException \FG\ASN1\Exception\ParserException - * @expectedExceptionMessage ASN.1 Parser Exception at offset 2: Context-Specific explicitly tagged object [1] starting at offset 2 is shorter than required in the outer tag - * @depends testFromBinary - */ - public function testFromBinaryWithInvalidOuterLengthThrowsException1() - { - $data = hex2bin('a104040101'); - // ^- this is wrong. correct would be "3" - ExplicitlyTaggedObject::fromBinary($data); - } - - /** - * @expectedException \FG\ASN1\Exception\ParserException - * @expectedExceptionMessage ASN.1 Parser Exception at offset 2: Context-Specific explicitly tagged object [1] starting at offset 2 is longer than allowed in the outer tag - * @depends testFromBinary - */ - public function testFromBinaryWithInvalidOuterLengthThrowsException2() - { - $data = hex2bin('a102040101'); - // ^- this is wrong. correct would be "3" - ExplicitlyTaggedObject::fromBinary($data); - } - public function testFromBinaryWithZeroContent() { $data = hex2bin('A000'); $object = ExplicitlyTaggedObject::fromBinary($data); $this->assertEquals(2, $object->getObjectLength()); - $this->assertNull($object->getContent()); + $this->assertEquals([], $object->getContent()); $this->assertEquals('Context specific empty object with tag [0]', $object->__toString()); $this->assertEquals($data, $object->getBinary()); } + + public function testFromBinaryWithMultipleObjects() + { + $object1 = new Boolean(true); + $object2 = new Integer(42); + + $identifier = 0xA0; + $length = $object1->getObjectLength()+$object2->getObjectLength(); + $data = chr($identifier).chr($length).$object1->getBinary().$object2->getBinary(); + + $object = ExplicitlyTaggedObject::fromBinary($data); + $this->assertEquals(2+$length, $object->getObjectLength()); + $this->assertEquals([$object1, $object2], $object->getContent()); + $this->assertEquals($data, $object->getBinary()); + $this->assertEquals('2 context specific objects with tag [0]', $object->__toString()); + } }