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());
+ }
}