.coveralls.yml000064400000000015150736747750007360 0ustar00src_dir: lib .gitignore000064400000000177150736747750006566 0ustar00# Project and backup files *~ *.project .settings/* .idea # Composer vendor composer.lock # Test coverage reports clover.xml .styleci.yml000064400000000310150736747750007040 0ustar00preset: symfony linting: true disabled: - empty_return - phpdoc_align - blankline_after_open_tag - pre_increment - return - unalign_equals - unalign_double_arrow - phpdoc_separation .travis.yml000064400000000515150736747750006703 0ustar00language: php sudo: false php: - 7.0 - 7.1 - 7.2 cache: directories: - vendor before_script: - composer install script: - vendor/bin/phpunit --coverage-clover build/logs/clover.xml after_script: # code coverage currently does not seem to be supported on travis php 7 or hhvm - vendor/bin/coveralls --verbose CHANGELOG.md000064400000003062150736747750006403 0ustar00#### v.2.0.0 (2017-08) * rename `FG\ASN1\Object` to `FG\ASN1\ASNObject` because `Object` is a special class name in the next major PHP release - when you upgrade you have to adapt all corresponding `use` and `extends` statements as well as type hints and all usages of `Object::fromBinary(…)`. * generally drop PHP 5.6 support #### v.1.5.2 (2016-10-29) * allow empty octet strings #### v.1.5.1 (2015-10-02) * add keywords to composer.json (this is a version on its own so the keywords are found on a stable version at packagist.org) #### v.1.5.0 (2015-10-30) * 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 (2015-09-29) * fix a bug that would prevent you from decoding empty tagged objects [#57](https://github.com/fgrosse/PHPASN1/issues/57) #### v.1.4.1 * improve exception messages and general error handling [#55](https://github.com/fgrosse/PHPASN1/pull/55) #### v.1.4.0 * **require PHP 5.6** * support big integers (closes #1 and #37) * enforce one code style via [styleci.io][9] * track code coverage via [coveralls.io][10] * replace obsolete `FG\ASN1\Exception\GeneralException` with `\Exception` * `Construct` (`Sequence`, `Set`) does now implement `ArrayAccess`, `Countable` and `Iterator` so its easier to use * add [`TemplateParser`][11] LICENSE000064400000002106150736747750005575 0ustar00Copyright (c) 2012-2015 Friedrich Große Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. README.md000064400000014432150736747750006054 0ustar00PHPASN1 ======= [![Build Status](https://secure.travis-ci.org/fgrosse/PHPASN1.png?branch=master)](http://travis-ci.org/fgrosse/PHPASN1) [![PHP 7 ready](http://php7ready.timesplinter.ch/fgrosse/PHPASN1/badge.svg)](https://travis-ci.org/fgrosse/PHPASN1) [![Coverage Status](https://coveralls.io/repos/fgrosse/PHPASN1/badge.svg?branch=master&service=github)](https://coveralls.io/github/fgrosse/PHPASN1?branch=master) [![Latest Stable Version](https://poser.pugx.org/fgrosse/phpasn1/v/stable.png)](https://packagist.org/packages/fgrosse/phpasn1) [![Total Downloads](https://poser.pugx.org/fgrosse/phpasn1/downloads.png)](https://packagist.org/packages/fgrosse/phpasn1) [![Latest Unstable Version](https://poser.pugx.org/fgrosse/phpasn1/v/unstable.png)](https://packagist.org/packages/fgrosse/phpasn1) [![License](https://poser.pugx.org/fgrosse/phpasn1/license.png)](https://packagist.org/packages/fgrosse/phpasn1) A PHP Framework that allows you to encode and decode arbitrary [ASN.1][3] structures using the [ITU-T X.690 Encoding Rules][4]. This encoding is very frequently used in [X.509 PKI environments][5] or the communication between heterogeneous computer systems. The API allows you to encode ASN.1 structures to create binary data such as certificate signing requests (CSR), X.509 certificates or certificate revocation lists (CRL). PHPASN1 can also read [BER encoded][6] binary data into separate PHP objects that can be manipulated by the user and reencoded afterwards. The **changelog** can now be found at [CHANGELOG.md](CHANGELOG.md). ## Dependencies PHPASN1 requires at least `PHP 7.0` and the `gmp` extension. Support for older PHP versions (i.e. PHP 5.6) was dropped starting with `v2.0`. If you must use an outdated PHP version consider using [PHPASN v1.5][13]. For the loading of object identifier names directly from the web [curl][7] is used. ## Installation The preferred way to install this library is to rely on [Composer][2]: ```bash $ composer require fgrosse/phpasn1 ``` ## Usage ### Encoding ASN.1 Structures PHPASN1 offers you a class for each of the implemented ASN.1 universal types. The constructors should be pretty self explanatory so you should have no big trouble getting started. All data will be encoded using [DER encoding][8] ```php use FG\ASN1\OID; use FG\ASN1\Universal\Integer; use FG\ASN1\Universal\Boolean; use FG\ASN1\Universal\Enumerated; use FG\ASN1\Universal\IA5String; use FG\ASN1\Universal\ObjectIdentifier; use FG\ASN1\Universal\PrintableString; use FG\ASN1\Universal\Sequence; use FG\ASN1\Universal\Set; use FG\ASN1\Universal\NullObject; $integer = new Integer(123456); $boolean = new Boolean(true); $enum = new Enumerated(1); $ia5String = new IA5String('Hello world'); $asnNull = new NullObject(); $objectIdentifier1 = new ObjectIdentifier('1.2.250.1.16.9'); $objectIdentifier2 = new ObjectIdentifier(OID::RSA_ENCRYPTION); $printableString = new PrintableString('Foo bar'); $sequence = new Sequence($integer, $boolean, $enum, $ia5String); $set = new Set($sequence, $asnNull, $objectIdentifier1, $objectIdentifier2, $printableString); $myBinary = $sequence->getBinary(); $myBinary .= $set->getBinary(); echo base64_encode($myBinary); ``` ### Decoding binary data Decoding BER encoded binary data is just as easy as encoding it: ```php use FG\ASN1\ASNObject; $base64String = ... $binaryData = base64_decode($base64String); $asnObject = ASNObject::fromBinary($binaryData); // do stuff ``` If you already know exactly how your expected data should look like you can use the `FG\ASN1\TemplateParser`: ```php use FG\ASN1\TemplateParser; // first define your template $template = [ Identifier::SEQUENCE => [ Identifier::SET => [ Identifier::OBJECT_IDENTIFIER, Identifier::SEQUENCE => [ Identifier::INTEGER, Identifier::BITSTRING, ] ] ] ]; // if your binary data is not matching the template you provided this will throw an `\Exception`: $parser = new TemplateParser(); $object = $parser->parseBinary($data, $template); // there is also a convenience function if you parse binary data from base64: $object = $parser->parseBase64($data, $template); ``` You can use this function to make sure your data has exactly the format you are expecting. ### Navigating decoded data All constructed classes (i.e. `Sequence` and `Set`) can be navigated by array access or using an iterator. You can find examples [here](https://github.com/fgrosse/PHPASN1/blob/f6442cadda9d36f3518c737e32f28300a588b777/tests/ASN1/Universal/SequenceTest.php#L148-148), [here](https://github.com/fgrosse/PHPASN1/blob/f6442cadda9d36f3518c737e32f28300a588b777/tests/ASN1/Universal/SequenceTest.php#L121) and [here](https://github.com/fgrosse/PHPASN1/blob/f6442cadda9d36f3518c737e32f28300a588b777/tests/ASN1/TemplateParserTest.php#L45). ### Give me more examples! To see some example usage of the API classes or some generated output check out the [examples](https://github.com/fgrosse/PHPASN1/tree/master/examples). ### How do I contribute? If you found an issue or have a question submit a github issue with detailed information. In case you already know what caused the issue and feel in the mood to fix it, your code contributions are always welcome. Just fork the repository, implement your changes and make sure that you covered everything with tests. Afterwards submit a pull request via github and be a little patient :) I usually try to comment and/or merge as soon as possible. #### Mailing list New features or questions can be discussed in [this google group/mailing list][12]. ### Thanks To [all contributors][1] so far! ## License This library is distributed under the [MIT License](LICENSE). [1]: https://github.com/fgrosse/PHPASN1/graphs/contributors [2]: https://getcomposer.org/ [3]: http://www.itu.int/ITU-T/asn1/ [4]: http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=x.690 [5]: http://en.wikipedia.org/wiki/X.509 [6]: http://en.wikipedia.org/wiki/X.690#BER_encoding [7]: http://php.net/manual/en/book.curl.php [8]: http://en.wikipedia.org/wiki/X.690#DER_encoding [9]: https://styleci.io [10]: https://coveralls.io/github/fgrosse/PHPASN1 [11]: https://github.com/fgrosse/PHPASN1/blob/master/tests/ASN1/TemplateParserTest.php#L16 [12]: https://groups.google.com/d/forum/phpasn1 [13]: https://packagist.org/packages/fgrosse/phpasn1#1.5.2 composer.json000064400000002456150736747750007322 0ustar00{ "name": "fgrosse/phpasn1", "description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.", "type": "library", "homepage": "https://github.com/FGrosse/PHPASN1", "license": "MIT", "authors": [ { "name": "Friedrich Große", "email": "friedrich.grosse@gmail.com", "homepage": "https://github.com/FGrosse", "role": "Author" }, { "name": "All contributors", "homepage": "https://github.com/FGrosse/PHPASN1/contributors" } ], "keywords": [ "x690", "x.690", "x.509", "x509", "asn1", "asn.1", "ber", "der", "binary", "encoding", "decoding" ], "require": { "php": ">=7.0.0", "ext-gmp": "*" }, "require-dev": { "phpunit/phpunit": "~6.3", "satooshi/php-coveralls": "dev-master" }, "suggest": { "php-curl": "For loading OID information from the web if they have not bee defined statically" }, "autoload": { "psr-4": { "FG\\": "lib/" } }, "autoload-dev": { "psr-4": { "FG\\Test\\": "tests/" } }, "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } } } examples/Issue14.php000064400000014446150736747750010366 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require_once __DIR__.'/../vendor/autoload.php'; require_once 'shared.php'; use FG\ASN1\OID; use FG\ASN1\ASNObject; use FG\ASN1\Identifier; use FG\ASN1\Universal\ObjectIdentifier; use FG\ASN1\Universal\OctetString; use FG\ASN1\Universal\Sequence; $base64 = 'MIIINTCCBh2gAwIBAgIQbVVJc10C7UUjfYRHQ//7nTANBgkqhkiG9w0BAQsFADB0 MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDEtMCsGA1UECxMkQ2Vy dGlzaWduIENlcnRpZmljYWRvcmEgRGlnaXRhbCBTLkEuMSEwHwYDVQQDExhBQyBD ZXJ0aXNpZ24gTXVsdGlwbGEgRzUwHhcNMTIwNTI0MDAwMDAwWhcNMTMwNTIzMjM1 OTU5WjCBwDELMAkGA1UEBhMCQlIxEzARBgNVBAoUCklDUC1CcmFzaWwxIjAgBgNV BAsUGUF1dGVudGljYWRvIHBvciBBUiBEYXNjaGkxGzAZBgNVBAsUEkFzc2luYXR1 cmEgVGlwbyBBMTEVMBMGA1UECxQMSUQgLSAzMTYzNjAwMRwwGgYDVQQDExNGaW1h dGVjIFRleHRpbCBMdGRhMSYwJAYJKoZIhvcNAQkBFhdmZXJuYW5kb0BmaW1hdGVj LmNvbS5icjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOqNCGrNF3aV +lfImn2ji7SsyUbwqyddd2GRnZBUv3lOBQ9DhXq+YX1qjkb/EgGwC9EhIermj3Xw SZ4PjOweubXAVZ8BY2CrjZhjOOQRq5IKvbqStbsYSEOvOyVfbsDuYdralM6SJpyW wJRdjTprbKUARU2fJBLJgiGGyIwh0KqAIhp3OcwCW9t4B5RT01fcmPa6hhU808dQ JgqykA1i8ylQjtXzg52tinUOl6zWZZxxkg5y9l73Lf14eDFHcnI4lFv4kS3jkdk8 mwyll2K0nQX+aMcli8aN3K7C/Lj4VQIWuGarVNfzwg3wP3WzzOS+go0UFZ8ZSy52 3kpfnlsNBK0CAwEAAaOCA3QwggNwMIG6BgNVHREEgbIwga+gPQYFYEwBAwSgNAQy MjAxMDE5NTkwNTIyNTUzNzg4MDAwMDAwMDAwMDAwMDAwMDAwMDg4NzQzNzJ4c3Nw U1CgIQYFYEwBAwKgGAQWRmVybmFuZG8gSm9zZSBLYWlyYWxsYaAZBgVgTAEDA6AQ BA41ODcxNjUyMzAwMDExOaAXBgVgTAEDB6AOBAwwMDAwMDAwMDAwMDCBF2Zlcm5h bmRvQGZpbWF0ZWMuY29tLmJyMAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUnVDPvf8k yq+xM+sX4kJ6jmkqjlMwDgYDVR0PAQH/BAQDAgXgMIGJBgNVHSAEgYEwfzB9BgZg TAECAQswczBxBggrBgEFBQcCARZlaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWdu LmNvbS5ici9yZXBvc2l0b3Jpby9kcGMvQUNfQ2VydGlzaWduX011bHRpcGxhL0RQ Q19BQ19DZXJ0aVNpZ25NdWx0aXBsYS5wZGYwggElBgNVHR8EggEcMIIBGDBcoFqg WIZWaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWduLmNvbS5ici9yZXBvc2l0b3Jp by9sY3IvQUNDZXJ0aXNpZ25NdWx0aXBsYUc1L0xhdGVzdENSTC5jcmwwW6BZoFeG VWh0dHA6Ly9pY3AtYnJhc2lsLm91dHJhbGNyLmNvbS5ici9yZXBvc2l0b3Jpby9s Y3IvQUNDZXJ0aXNpZ25NdWx0aXBsYUc1L0xhdGVzdENSTC5jcmwwW6BZoFeGVWh0 dHA6Ly9yZXBvc2l0b3Jpby5pY3BicmFzaWwuZ292LmJyL2xjci9DZXJ0aXNpZ24v QUNDZXJ0aXNpZ25NdWx0aXBsYUc1L0xhdGVzdENSTC5jcmwwHQYDVR0lBBYwFAYI KwYBBQUHAwIGCCsGAQUFBwMEMIGgBggrBgEFBQcBAQSBkzCBkDBkBggrBgEFBQcw AoZYaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWduLmNvbS5ici9yZXBvc2l0b3Jp by9jZXJ0aWZpY2Fkb3MvQUNfQ2VydGlzaWduX011bHRpcGxhX0c1LnA3YzAoBggr BgEFBQcwAYYcaHR0cDovL29jc3AuY2VydGlzaWduLmNvbS5icjANBgkqhkiG9w0B AQsFAAOCAgEAyfdKIKDLGZyOhAsycAgJ7RZyn4/2tN29bd+rLwtkvp/9XJGRCAob fkLU8UACGzbVHT/bB7sOKIiHvp8CgGyoU/1veo5a1yGMFQobQWGcdEYBOd+6Ax6U FEOc+Ti+LsXsUA5tRbzTwfXJFFOIgFDgrXl7V4JGjSI3GdyoiEECR9OfM/UFYtr/ pD194Xh6G0lSCuP9xOKpOJ7qk24mcfObcGxooMxmktkt0bevzx5w84RtXJfbube+ p6o7JP06T6JJTGfIUfNKYU8wJ6CV9eUHEMDLHMoH6wnYK7d8rHcAbSoTJGMFlSYZ OqJrWAbKAPjraFNeNaH675u//Ck127kJVidcuRwtkLZV/OIlQaw/4QSp90QWQOQg AsOWC9+h8v/RcCYTJpWM22MCq3Xk67nz+mXO8e7LKpzHEh2sjX3gkfw4h80zYfT7 kTsNXVdxAHXiIahKNeRUT8fGhxvyOA0RqAXQBUaOyLyGYWRJ7Is5IqAAU6XiBHYe oJ3v8BTGYzIK2Ud5dZ23yzBp2ejdQzQX1ETpJoEdgELToHmfBXTkI+7ne59wSRkH beQXoK5y0U3gh1vIz/53GG0QMuCvq9r9xTMERcFJcpQUxJ2RjwxIFHlIFbNwCeiW I3DmZaXdR169kRoPIqkV3QIREs/yHBvkxXrwDt416stUnQ8KsxAEatE='; try { $binaryData = base64_decode($base64); //hexdump($binaryData, false); $rootObject = ASNObject::fromBinary($binaryData); //printObject($rootObject); // first navigate to the certificate extensions // (see ITU-T X.509 section 7 "Public-keys and public-key certificates" for cert structure) /* @var Sequence $rootObject */ assert($rootObject->getType() == Identifier::SEQUENCE); $topLevelContainer = $rootObject->getChildren(); $certificateInfo = $topLevelContainer[0]; /* @var Sequence $certificateInfo */ assert($certificateInfo->getType() == Identifier::SEQUENCE); // there need to be at least 8 child elements if the certificate extensions field is present assert($certificateInfo->getNumberofChildren() >= 8); $certInfoFields = $certificateInfo->getChildren(); $certExtensions = $certInfoFields[7]; // check if this is really the certificate extensions sequence /* @var \FG\ASN1\ASNObject $certExtensions */ $certExtensionsType = $certExtensions->getType(); assert(Identifier::isContextSpecificClass($certExtensionsType)); assert(Identifier::getTagNumber($certExtensions->getType()) == 3); // this should contain a sequence of extensions $certExtensions = $certExtensions->getFirstChild(); assert($certExtensions->getType() == Identifier::SEQUENCE); // now check all extensions and search for the SAN /** @var \FG\ASN1\ASNObject $extensionSequence */ foreach ($certExtensions as $extensionSequence) { assert($extensionSequence->getType() == Identifier::SEQUENCE); assert($extensionSequence->getNumberofChildren() >= 2); $extensionSequenceChildren = $extensionSequence->getChildren(); $objectIdentifier = $extensionSequenceChildren[0]; /* @var ObjectIdentifier $objectIdentifier */ assert($objectIdentifier->getType() == Identifier::OBJECT_IDENTIFIER); if ($objectIdentifier->getContent() == OID::CERT_EXT_SUBJECT_ALT_NAME) { // now we have the wanted octet string $octetString = $extensionSequenceChildren[1]; /* @var OctetString $octetString */ $octetStringBinary = $octetString->getBinaryContent(); // At this point you may want to create the sequence from the binary value of // the octet string and parse its structure like we did so far. // However a more general approach would be to understand the format of the // contained SAN fields and implement them in SubjectAlternativeNames. $sequence = Sequence::fromBinary($octetStringBinary); echo 'This is the parsed content of the SAN certificate extension field so far:'.PHP_EOL; printObject($sequence); // The following does not work yet because PHPASN1 SAn does only support DNS and IP //SubjectAlternativeNames::fromBinary($octetStringBinary); } } } catch (\Exception $exception) { echo '[ERROR] Caught exception:'.PHP_EOL.$exception->getMessage().PHP_EOL; } examples/Issue29.php000064400000001010150736747750010353 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use FG\ASN1\ASNObject; require_once __DIR__.'/../vendor/autoload.php'; require_once 'shared.php'; $hex = 'a02b302906092a864886f70d01090e311c301a30180603551d110411300f820d636f72766573706163652e6465'; $asn = ASNObject::fromBinary(hex2bin($hex)); printObject($asn); examples/Issue43.php000064400000006226150736747750010365 0ustar00 * * 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\Universal\Enumerated; use FG\ASN1\Universal\ObjectIdentifier; use FG\ASN1\Universal\Sequence; require_once __DIR__.'/../vendor/autoload.php'; // $data has been generated using https://pkijs.org/examples/OCSP_resp_complex_example.html $data = 'MIIEXgoBAKCCBFcwggRTBgkrBgEFBQcwAQEEggREMIIEQDBeoSAwHjEcMAkGA1UE BhMCUlUwDwYDVQQDHggAVABlAHMAdBgPMjAxNTA3MTYxNzU0MjFaMCkwJzASMAcG BSsOAwIaBAEBBAEBAgEBgAAYDzIwMTUwNzE2MTc1NDIxWjALBgkqhkiG9w0BAQUD ggEBAG5B1xOnhgzgpsnspWd9c4eLIeOY1XXl7q2DUO2kGji4WbBXtWDMEv7QQO9/ 8devmyWNTFlkScbhiMPIEfoES6AyW8lZu9aON2tMssgj/Ev9+H+A2Z2WHQdeDtv7 AS2mYa/7e3Ucsb63pAmBcxgDY55eHwmivBfZIWF6RjbzuJ0Uxz6NvrVEHlWz2KE0 1n/7qbcwbHx8nu1s/IdswFG9iePm5sTsHPsbvsMQ5ZfkvkuL/t5WlLONlRYunOA4 Nytf2lUIZbZznB0dvQG4vm1F9IQEcv1V0aU0D/ZU81I7lSPlPEv/DMTEo1vePTZv Gv8S1K+keDD/bfwM22d663Vfmt+gggLKMIICxjCCAsIwggGsoAMCAQICAQEwCwYJ KoZIhvcNAQEFMB4xHDAJBgNVBAYTAlJVMA8GA1UEAx4IAFQAZQBzAHQwHhcNMTMw MjAxMDAwMDAwWhcNMTYwMjAxMDAwMDAwWjAeMRwwCQYDVQQGEwJSVTAPBgNVBAMe CABUAGUAcwB0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsFlz/3yl mk+8uP4/x61UWBliqW08/1jMA83Qj2yuCAvhMhmuq8BvyWP4jnXoxTy3iTZFDXIj 5Vg2FlHWna0TMsU5jlmv8W/mu9U3+q7i1+339JrEZkrDLNDpJ23gqKBgC1OaNH7+ vicBRmoP5kj2X62XMaKT5TMFBZzTtSF7IaUjt9SVXQjG3aHIy7D1mOypvEw4LSS+ RvvJTezSQeCoLpX7HziRnoVUNkJWZHL2wG5rb/SJzpXwHkRa7R250vN8TFtSSsp4 YXzK6aks/qkxRJ1UkBLUAZlRasUD+zu8gyTIz0sNgFrOtx8EOzjZRiJ/vBiVOn88 Brf4i0D+fVXmJQIDAQABow8wDTALBgNVHQ8EBAMCAAIwCwYJKoZIhvcNAQEFA4IB AQBfubKPTvmDGrDxoqCbPwFoPRC0STwPL2GV8f5sD/Sbyc0NoJdygUO2DvquGGn5 6UJCfLo1u6Dn4zuuDs3m6if86HTpAf9Z3a72ok2Tor/NFwYt+vDOrFY5F4bXDZkf u4zuDLmjpj26Dk4te3BVohsLTXbvJ5a/TT2VanwNOyx85lXPxy3V8Rr1AwlmHZoz DDbUGbe/noUDJCgMjvaKKvLykIhIcW+g6W7SOcKRflw5H8kzDv816XFODSC3X1Uw o3aVy9du/0mH+g4HvyVVplO90tdoHD1gHUMZwuen4dbTzhWv4dtLFelWM5lGWbLE Wn7kJghclgIxv10nkGyfrowt'; // OCSP response status according to // https://tools.ietf.org/html/rfc6960#section-4.2.1 $validResponseStatuses = array( 0 => 'Response has valid confirmations', 1 => 'Illegal confirmation request', 2 => 'Internal error in issuer', 3 => 'Try again later', // (4) is not used 5 => 'Must sign the request', 6 => 'Request unauthorized', ); $ocspResponse = Sequence::fromBinary(base64_decode($data)); /* @var Enumerated $responseStatus */ $elements = $ocspResponse->getChildren(); $responseStatus = $elements[0]; $responseStatusCode = $responseStatus->getContent(); echo PHP_EOL; echo "OCSP response status: $responseStatusCode ({$validResponseStatuses[$responseStatusCode]})".PHP_EOL; /** @var ExplicitlyTaggedObject $responseBytes */ $responseBytes = $elements[1]; /** @var Sequence $responseBytesSequence */ $responseBytesSequence = $responseBytes->getContent(); /** @var ObjectIdentifier $responseType */ $responseType = $responseBytesSequence->getChildren()[0]; echo "ResponseType: {$responseType}".PHP_EOL; $response = $responseBytesSequence->getChildren()[1]; echo "Response (octet string): {$response}".PHP_EOL; examples/Issue57.php000064400000011165150736747750010370 0ustar00 * * 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\ASNObject; 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(ASNObject::fromBinary(base64_decode($input1))); } catch (Exception $exception) { echo "ERROR: " . $exception->getMessage(); } echo PHP_EOL; try { echo 'Input 2:' . PHP_EOL; printObject(ASNObject::fromBinary(base64_decode($input2))); } catch (Exception $exception) { echo "ERROR: " . $exception->getMessage(); } echo PHP_EOL; try { echo 'Input 3:' . PHP_EOL; printObject(ASNObject::fromBinary(base64_decode($input3))); } catch (Exception $exception) { echo "ERROR: " . $exception->getMessage(); } examples/allClasses.php000064400000017155150736747750011217 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require_once __DIR__.'/hexdump.php'; require_once __DIR__.'/../vendor/autoload.php'; use FG\ASN1\OID; use FG\ASN1\ASNObject; use FG\ASN1\Universal\Boolean; use FG\ASN1\Universal\Integer; use FG\ASN1\Universal\Enumerated; use FG\ASN1\Universal\PrintableString; use FG\ASN1\Universal\NumericString; use FG\ASN1\Universal\CharacterString; use FG\ASN1\Universal\UTF8String; use FG\ASN1\Universal\BitString; use FG\ASN1\Universal\OctetString; use FG\ASN1\Universal\ObjectIdentifier; use FG\ASN1\Universal\NullObject; use FG\ASN1\Universal\UTCTime; use FG\ASN1\Universal\GeneralizedTime; use FG\ASN1\Universal\Set; use FG\ASN1\Universal\Sequence; use FG\ASN1\Composite\AttributeTypeAndValue; use FG\ASN1\Composite\RDNString; use FG\ASN1\Composite\RelativeDistinguishedName; use FG\X509\CertificateSubject; use FG\X509\PublicKey; use FG\X509\AlgorithmIdentifier; use FG\X509\CSR\CSR; // check if openssl is installed on this system $openSSLVersionOutput = shell_exec('openssl version'); if (substr($openSSLVersionOutput, 0, 7) == 'OpenSSL') { $openSSLisAvailable = true; } else { $openSSLisAvailable = false; } function printVariableInfo(ASNObject $variable) { $className = get_class($variable); $stringValue = nl2br($variable->__toString()); $binaryData = $variable->getBinary(); $base64Binary = chunk_split(base64_encode($binaryData), 24); echo ''; echo "{$className}"; echo "\"{$stringValue}\""; echo "{$base64Binary}"; echo ''.hexdump($binaryData, true, true, true).''; global $openSSLisAvailable; if ($openSSLisAvailable) { $openSSLOutput = shell_exec("echo '{$base64Binary}' | openssl asn1parse -inform PEM -dump -i 2>&1"); echo "
{$openSSLOutput}
"; } echo ''; } ?> PHPASN1 Classes

Examples for all available PHPASN1 classes

Note: OpenSSL could not be found on this system. This is absolutely no problem for PHPASN1 but it could have been used to show you that the generated binaries are indeed correct :)

"; } ?> OpenSSL asn1parse output'; } // Primitives printVariableInfo(new Boolean(true)); printVariableInfo(new Boolean(false)); printVariableInfo(new Integer(123456)); printVariableInfo(new Integer(-546)); printVariableInfo(new Enumerated(1)); printVariableInfo(new PrintableString('Hello World')); printVariableInfo(new NumericString('123456')); printVariableInfo(new CharacterString('Foo')); printVariableInfo(new UTF8String('Hello ♥♥♥ World')); // there are more restricted character string classes available printVariableInfo(new BitString('3082010a02820101009e2a7')); printVariableInfo(new OctetString('x01020304AE123200A0')); printVariableInfo(new ObjectIdentifier(OID::PKCS9_EMAIL)); printVariableInfo(new NullObject()); printVariableInfo(new UTCTime('2012-09-30 14:33')); printVariableInfo(new GeneralizedTime('2008-07-01 22:35:17.024540+06:30')); // Constructed printVariableInfo(new Sequence( new ObjectIdentifier(OID::COMMON_NAME), new PrintableString('Friedrich') )); printVariableInfo(new Set( new ObjectIdentifier(OID::COUNTRY_NAME), new PrintableString('Germany') )); printVariableInfo(new AttributeTypeAndValue( OID::ORGANIZATION_NAME, new PrintableString('My Organization') )); printVariableInfo(new RelativeDistinguishedName( OID::RSA_ENCRYPTION, new Integer(62342) )); printVariableInfo(new RDNString( OID::COMMON_NAME, 'Hello World' )); // X509 printVariableInfo(new CertificateSubject( 'Friedrich Grosse', 'friedrich.grosse@gmail.com', 'PHPASN1', 'Berlin', 'Berlin', 'DE', 'Development Department' )); printVariableInfo(new PublicKey( '00 00 00 01 23 00 00 01 01 00 E7 40 F2 69 15 D5 4A DD 24 A1 C3 0C 93 38 03 1A 49 70 EB 76 B0 F5 1B BF 42 97 BA E1 F3 61 CC E1 63 0A BA 0F 66 6D 30 4E 31 67 65 39 E1 2E 93 A7 E7 3E 1E 76 10 81 5A 41 C6 DF 12 05 46 AC 61 A9 29 A3 BD 1A 16 35 38 DF 75 F6 0A 25 F7 3A F5 D3 C6 EB 84 7D 40 D4 1E 56 CA 0B 46 28 F7 79 D6 08 BB 1B 01 C7 2A 14 93 B2 F7 89 CB 11 F3 4B E6 5F 7A 2B 22 F4 14 CB 36 47 40 89 45 FC 14 03 1D 8B 31 AF 4C 57 63 7D 87 FD 95 0C ED 64 C1 BF 8F FC E2 76 6C 90 A3 4F 50 72 9D AB 01 1E 84 7A 63 95 9A 2E 0E 59 F1 AF A9 D0 6E AB 8E 45 96 80 AD 57 26 04 2F B7 B0 41 69 2B 7A BD F9 2F FE 60 99 A9 81 FF 94 A5 8E 5B 7F A8 C0 63 4B F3 79 F4 F7 3C 27 81 FF 90 5C C6 04 BC C2 C1 62 2E 47 8A EF 6F 95 53 89 99 34 7C 4C B4 AE A0 E8 AD C1 04 B8 B6 E2 02 BB 65 93 39 42 F9 FD 69 20 E3 CF EF 49 E9' )); printVariableInfo(new AlgorithmIdentifier(OID::SHA1_WITH_RSA_SIGNATURE)); printVariableInfo($csr = new CSR( 'Friedrich Grosse', 'friedrich.grosse@gmail.com', 'PHPASN1', 'Berlin', 'Berlin', 'DE', 'Development Department', '3082010a02820101009e2a7defae93720c91c43c46ff4a1f2e8eef7949289e281f788f3a07d9b94da26fb2e721009caceddd0e6b59daa596df20f871fc30a43f4b80798f94fa3d13cb2db79eb6d8f07b4065d0b09a541564ba3baa1201e20ee923ea16be31fa785c300635c4e881df7acb5b52c7c3d923067902cc55e77c00694f319d2b9e81edbbfe70ef1a462aef4960c567f33aa5264a05fdf24cd7bc36941cd7746fb767631a241b7a97fc4cdc42a68692b906406403599380c7586ce6f22fac34949caf1072c724ba5397e6440f957e2678c3a4bc92268fe6815d41fa210ab45364c11e3731c6c039832b54f54b51fdaf6afb351e1da9720b3c322f7fbaefb72d96d4ce5ec07b0203010001', '4adf270d347047192573cf245a94cd2e69594c1cdac1d7c99d7ed5856c926ee62c65188f21d893e634b213595cc4564d5a8d39bed0ca01e0b45e3182ab89310c129017f2a7a68d8603694ddc8d1c2ebfee39b3b5dfc9dbc2db667a089b1b51386f2cf7ec70140d185bae5c2f3b3148b9ef613ce068f94db13a230b1133e4b4a48ec5c8b4066d64a2199c0cfb6c4d0cfe105f21a89b2900d0a5c87bef5eded941ba93ae1b7e84aaeabcb46fa4a3844ffc683ebb4ee80717ff51cba5d82afe9d2633b760a66449e57e06d73eeeb151bc050a66825996d7f5ec821d31891c620a677c8271db13bbc22fcf91e1b7ac8f6f109eb8e3a2c61a3c8a4336b40a499e1404' )); ?>
Class toString() Binary (base64) Binary (hex)
examples/common.css000064400000000555150736747750010416 0ustar00table { border-collapse: collapse; } th { text-align: left; } td { border-top: black 1px solid; padding-right: 30px; } td.ASNclass { font-weight: bold; width: 150px; } td.openSSL { border-left: black 1px solid; } p.notice { margin-bottom: 30px; color: gray; } .red { color: red; } .monospace { font-family: monospace; } pre { margin: 7px } examples/getObjectIdentifier.php000064400000001727150736747750013040 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require_once __DIR__.'/../vendor/autoload.php'; use FG\ASN1\OID; function echoOIDRow($oidString) { $oidName = OID::getName($oidString); echo "{$oidString}{$oidName}"; } ?> PHPASN1 Examples
examples/hexdump.php000064400000004457150736747750010604 0ustar00 * @author Peter Waller * * @link http://aidanlister.com/2004/04/viewing-binary-data-as-a-hexdump-in-php/ * * @param string $data The string to be dumped * @param bool $htmloutput Set to false for non-HTML output * @param bool $uppercase Set to true for uppercase hex * @param bool $return Set to true to return the dump * * @return string */ function hexdump($data, $htmloutput = true, $uppercase = false, $return = false) { // Init $hexi = ''; $ascii = ''; $dump = ($htmloutput === true) ? '
' : '';
    $offset = 0;
    $len    = strlen($data);

    // Upper or lower case hexadecimal
    $x = ($uppercase === false) ? 'x' : 'X';

    // Iterate string
    for ($i = $j = 0; $i < $len; $i++) {
        // Convert to hexidecimal
        $hexi .= sprintf("%02$x ", ord($data[$i]));

        // Replace non-viewable bytes with '.'
        if (ord($data[$i]) >= 32) {
            $ascii .= ($htmloutput === true) ?
                            htmlentities($data[$i]) :
                            $data[$i];
        } else {
            $ascii .= '.';
        }

        // Add extra column spacing
        if ($j === 7) {
            $hexi  .= ' ';
            $ascii .= ' ';
        }

        // Add row
        if (++$j === 16 || $i === $len - 1) {
            // Join the hexi / ascii output
            $dump .= sprintf("%04$x  %-49s  %s", $offset, $hexi, $ascii);

            // Reset vars
            $hexi   = $ascii = '';
            $offset += 16;
            $j      = 0;

            // Add newline
            if ($i !== $len - 1) {
                $dump .= "\n";
            }
        }
    }

    // Finish dump
    $dump .= $htmloutput === true ?
                '
' : ''; $dump .= "\n"; // Output method if ($return === false) { echo $dump; } else { return $dump; } return null; } examples/index.html000064400000001033150736747750010401 0ustar00 PHPASN1 Examples Currently the following examples are available: examples/parseBinary.php000064400000005072150736747750011403 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ require_once __DIR__.'/../vendor/autoload.php'; require_once 'shared.php'; use FG\ASN1\ASNObject; $base64String = '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 = ASNObject::fromBinary($binaryData); ?> PHPASN1 Examples

Got byte of binary data:

examples/readmeExample.php000064400000001722150736747750011673 0ustar00getBinary(); $myBinary .= $set->getBinary(); echo base64_encode($myBinary); examples/shared.php000064400000001052150736747750010364 0ustar00 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); } } } lib/ASN1/ASNObject.php000064400000030427150736747750010330 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1; use FG\ASN1\Exception\ParserException; use FG\ASN1\Universal\BitString; use FG\ASN1\Universal\Boolean; use FG\ASN1\Universal\Enumerated; use FG\ASN1\Universal\GeneralizedTime; use FG\ASN1\Universal\Integer; use FG\ASN1\Universal\NullObject; use FG\ASN1\Universal\ObjectIdentifier; use FG\ASN1\Universal\RelativeObjectIdentifier; use FG\ASN1\Universal\OctetString; use FG\ASN1\Universal\Sequence; use FG\ASN1\Universal\Set; use FG\ASN1\Universal\UTCTime; use FG\ASN1\Universal\IA5String; use FG\ASN1\Universal\PrintableString; use FG\ASN1\Universal\NumericString; use FG\ASN1\Universal\UTF8String; use FG\ASN1\Universal\UniversalString; use FG\ASN1\Universal\CharacterString; use FG\ASN1\Universal\GeneralString; use FG\ASN1\Universal\VisibleString; use FG\ASN1\Universal\GraphicString; use FG\ASN1\Universal\BMPString; use FG\ASN1\Universal\T61String; use FG\ASN1\Universal\ObjectDescriptor; use LogicException; /** * Class ASNObject is the base class for all concrete ASN.1 objects. */ abstract class ASNObject implements Parsable { private $contentLength; private $nrOfLengthOctets; /** * Must return the number of octets of the content part. * * @return int */ abstract protected function calculateContentLength(); /** * Encode the object using DER encoding. * * @see http://en.wikipedia.org/wiki/X.690#DER_encoding * * @return string the binary representation of an objects value */ abstract protected function getEncodedValue(); /** * Return the content of this object in a non encoded form. * This can be used to print the value in human readable form. * * @return mixed */ abstract public function getContent(); /** * Return the object type octet. * This should use the class constants of Identifier. * * @see Identifier * * @return int */ abstract public function getType(); /** * Returns all identifier octets. If an inheriting class models a tag with * the long form identifier format, it MUST reimplement this method to * return all octets of the identifier. * * @throws LogicException If the identifier format is long form * * @return string Identifier as a set of octets */ public function getIdentifier() { $firstOctet = $this->getType(); if (Identifier::isLongForm($firstOctet)) { throw new LogicException(sprintf('Identifier of %s uses the long form and must therefor override "ASNObject::getIdentifier()".', get_class($this))); } return chr($firstOctet); } /** * Encode this object using DER encoding. * * @return string the full binary representation of the complete object */ public function getBinary() { $result = $this->getIdentifier(); $result .= $this->createLengthPart(); $result .= $this->getEncodedValue(); return $result; } private function createLengthPart() { $contentLength = $this->getContentLength(); $nrOfLengthOctets = $this->getNumberOfLengthOctets($contentLength); if ($nrOfLengthOctets == 1) { return chr($contentLength); } else { // the first length octet determines the number subsequent length octets $lengthOctets = chr(0x80 | ($nrOfLengthOctets - 1)); for ($shiftLength = 8 * ($nrOfLengthOctets - 2); $shiftLength >= 0; $shiftLength -= 8) { $lengthOctets .= chr($contentLength >> $shiftLength); } return $lengthOctets; } } protected function getNumberOfLengthOctets($contentLength = null) { if (!isset($this->nrOfLengthOctets)) { if ($contentLength == null) { $contentLength = $this->getContentLength(); } $this->nrOfLengthOctets = 1; if ($contentLength > 127) { do { // long form $this->nrOfLengthOctets++; $contentLength = $contentLength >> 8; } while ($contentLength > 0); } } return $this->nrOfLengthOctets; } protected function getContentLength() { if (!isset($this->contentLength)) { $this->contentLength = $this->calculateContentLength(); } return $this->contentLength; } protected function setContentLength($newContentLength) { $this->contentLength = $newContentLength; $this->getNumberOfLengthOctets($newContentLength); } /** * Returns the length of the whole object (including the identifier and length octets). */ public function getObjectLength() { $nrOfIdentifierOctets = strlen($this->getIdentifier()); $contentLength = $this->getContentLength(); $nrOfLengthOctets = $this->getNumberOfLengthOctets($contentLength); return $nrOfIdentifierOctets + $nrOfLengthOctets + $contentLength; } public function __toString() { return $this->getContent(); } /** * Returns the name of the ASN.1 Type of this object. * * @see Identifier::getName() */ public function getTypeName() { return Identifier::getName($this->getType()); } /** * @param string $binaryData * @param int $offsetIndex * * @throws ParserException * * @return \FG\ASN1\ASNObject */ public static function fromBinary(&$binaryData, &$offsetIndex = 0) { if (strlen($binaryData) <= $offsetIndex) { throw new ParserException('Can not parse binary from data: Offset index larger than input size', $offsetIndex); } $identifierOctet = ord($binaryData[$offsetIndex]); if (Identifier::isContextSpecificClass($identifierOctet) && Identifier::isConstructed($identifierOctet)) { return ExplicitlyTaggedObject::fromBinary($binaryData, $offsetIndex); } switch ($identifierOctet) { case Identifier::BITSTRING: return BitString::fromBinary($binaryData, $offsetIndex); case Identifier::BOOLEAN: return Boolean::fromBinary($binaryData, $offsetIndex); case Identifier::ENUMERATED: return Enumerated::fromBinary($binaryData, $offsetIndex); case Identifier::INTEGER: return Integer::fromBinary($binaryData, $offsetIndex); case Identifier::NULL: return NullObject::fromBinary($binaryData, $offsetIndex); case Identifier::OBJECT_IDENTIFIER: return ObjectIdentifier::fromBinary($binaryData, $offsetIndex); case Identifier::RELATIVE_OID: return RelativeObjectIdentifier::fromBinary($binaryData, $offsetIndex); case Identifier::OCTETSTRING: return OctetString::fromBinary($binaryData, $offsetIndex); case Identifier::SEQUENCE: return Sequence::fromBinary($binaryData, $offsetIndex); case Identifier::SET: return Set::fromBinary($binaryData, $offsetIndex); case Identifier::UTC_TIME: return UTCTime::fromBinary($binaryData, $offsetIndex); case Identifier::GENERALIZED_TIME: return GeneralizedTime::fromBinary($binaryData, $offsetIndex); case Identifier::IA5_STRING: return IA5String::fromBinary($binaryData, $offsetIndex); case Identifier::PRINTABLE_STRING: return PrintableString::fromBinary($binaryData, $offsetIndex); case Identifier::NUMERIC_STRING: return NumericString::fromBinary($binaryData, $offsetIndex); case Identifier::UTF8_STRING: return UTF8String::fromBinary($binaryData, $offsetIndex); case Identifier::UNIVERSAL_STRING: return UniversalString::fromBinary($binaryData, $offsetIndex); case Identifier::CHARACTER_STRING: return CharacterString::fromBinary($binaryData, $offsetIndex); case Identifier::GENERAL_STRING: return GeneralString::fromBinary($binaryData, $offsetIndex); case Identifier::VISIBLE_STRING: return VisibleString::fromBinary($binaryData, $offsetIndex); case Identifier::GRAPHIC_STRING: return GraphicString::fromBinary($binaryData, $offsetIndex); case Identifier::BMP_STRING: return BMPString::fromBinary($binaryData, $offsetIndex); case Identifier::T61_STRING: return T61String::fromBinary($binaryData, $offsetIndex); case Identifier::OBJECT_DESCRIPTOR: return ObjectDescriptor::fromBinary($binaryData, $offsetIndex); default: // At this point the identifier may be >1 byte. if (Identifier::isConstructed($identifierOctet)) { return new UnknownConstructedObject($binaryData, $offsetIndex); } else { $identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex); $lengthOfUnknownObject = self::parseContentLength($binaryData, $offsetIndex); $offsetIndex += $lengthOfUnknownObject; return new UnknownObject($identifier, $lengthOfUnknownObject); } } } protected static function parseIdentifier($identifierOctet, $expectedIdentifier, $offsetForExceptionHandling) { if (is_string($identifierOctet) || is_numeric($identifierOctet) == false) { $identifierOctet = ord($identifierOctet); } if ($identifierOctet != $expectedIdentifier) { $message = 'Can not create an '.Identifier::getName($expectedIdentifier).' from an '.Identifier::getName($identifierOctet); throw new ParserException($message, $offsetForExceptionHandling); } } protected static function parseBinaryIdentifier($binaryData, &$offsetIndex) { if (strlen($binaryData) <= $offsetIndex) { throw new ParserException('Can not parse identifier from data: Offset index larger than input size', $offsetIndex); } $identifier = $binaryData[$offsetIndex++]; if (Identifier::isLongForm(ord($identifier)) == false) { return $identifier; } while (true) { if (strlen($binaryData) <= $offsetIndex) { throw new ParserException('Can not parse identifier (long form) from data: Offset index larger than input size', $offsetIndex); } $nextOctet = $binaryData[$offsetIndex++]; $identifier .= $nextOctet; if ((ord($nextOctet) & 0x80) === 0) { // the most significant bit is 0 to we have reached the end of the identifier break; } } return $identifier; } protected static function parseContentLength(&$binaryData, &$offsetIndex, $minimumLength = 0) { if (strlen($binaryData) <= $offsetIndex) { throw new ParserException('Can not parse content length from data: Offset index larger than input size', $offsetIndex); } $contentLength = ord($binaryData[$offsetIndex++]); if (($contentLength & 0x80) != 0) { // bit 8 is set -> this is the long form $nrOfLengthOctets = $contentLength & 0x7F; $contentLength = 0x00; for ($i = 0; $i < $nrOfLengthOctets; $i++) { if (strlen($binaryData) <= $offsetIndex) { throw new ParserException('Can not parse content length (long form) from data: Offset index larger than input size', $offsetIndex); } $contentLength = ($contentLength << 8) + ord($binaryData[$offsetIndex++]); } } if ($contentLength < $minimumLength) { throw new ParserException('A '.get_called_class()." should have a content length of at least {$minimumLength}. Extracted length was {$contentLength}", $offsetIndex); } return $contentLength; } } lib/ASN1/AbstractString.php000064400000006505150736747750011512 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1; use Exception; abstract class AbstractString extends ASNObject implements Parsable { /** @var string */ protected $value; private $checkStringForIllegalChars = true; private $allowedCharacters = []; /** * The abstract base class for ASN.1 classes which represent some string of character. * * @param string $string */ public function __construct($string) { $this->value = $string; } public function getContent() { return $this->value; } protected function allowCharacter($character) { $this->allowedCharacters[] = $character; } protected function allowCharacters(...$characters) { foreach ($characters as $character) { $this->allowedCharacters[] = $character; } } protected function allowNumbers() { foreach (range('0', '9') as $char) { $this->allowedCharacters[] = (string) $char; } } protected function allowAllLetters() { $this->allowSmallLetters(); $this->allowCapitalLetters(); } protected function allowSmallLetters() { foreach (range('a', 'z') as $char) { $this->allowedCharacters[] = $char; } } protected function allowCapitalLetters() { foreach (range('A', 'Z') as $char) { $this->allowedCharacters[] = $char; } } protected function allowSpaces() { $this->allowedCharacters[] = ' '; } protected function allowAll() { $this->checkStringForIllegalChars = false; } protected function calculateContentLength() { return strlen($this->value); } protected function getEncodedValue() { if ($this->checkStringForIllegalChars) { $this->checkString(); } return $this->value; } protected function checkString() { $stringLength = $this->getContentLength(); for ($i = 0; $i < $stringLength; $i++) { if (in_array($this->value[$i], $this->allowedCharacters) == false) { $typeName = Identifier::getName($this->getType()); throw new Exception("Could not create a {$typeName} from the character sequence '{$this->value}'."); } } } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { $parsedObject = new static(''); self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex); $string = substr($binaryData, $offsetIndex, $contentLength); $offsetIndex += $contentLength; $parsedObject->value = $string; $parsedObject->setContentLength($contentLength); return $parsedObject; } public static function isValid($string) { $testObject = new static($string); try { $testObject->checkString(); return true; } catch (Exception $exception) { return false; } } } lib/ASN1/AbstractTime.php000064400000004421150736747750011135 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1; use DateInterval; use DateTime; use DateTimeZone; use Exception; abstract class AbstractTime extends ASNObject { /** @var DateTime */ protected $value; public function __construct($dateTime = null, $dateTimeZone = 'UTC') { if ($dateTime == null || is_string($dateTime)) { $timeZone = new DateTimeZone($dateTimeZone); $dateTimeObject = new DateTime($dateTime, $timeZone); if ($dateTimeObject == false) { $errorMessage = $this->getLastDateTimeErrors(); $className = Identifier::getName($this->getType()); throw new Exception(sprintf("Could not create %s from date time string '%s': %s", $className, $dateTime, $errorMessage)); } $dateTime = $dateTimeObject; } elseif (!$dateTime instanceof DateTime) { throw new Exception('Invalid first argument for some instance of AbstractTime constructor'); } $this->value = $dateTime; } public function getContent() { return $this->value; } protected function getLastDateTimeErrors() { $messages = ''; $lastErrors = DateTime::getLastErrors(); foreach ($lastErrors['errors'] as $errorMessage) { $messages .= "{$errorMessage}, "; } return substr($messages, 0, -2); } public function __toString() { return $this->value->format("Y-m-d\tH:i:s"); } protected static function extractTimeZoneData(&$binaryData, &$offsetIndex, DateTime $dateTime) { $sign = $binaryData[$offsetIndex++]; $timeOffsetHours = intval(substr($binaryData, $offsetIndex, 2)); $timeOffsetMinutes = intval(substr($binaryData, $offsetIndex + 2, 2)); $offsetIndex += 4; $interval = new DateInterval("PT{$timeOffsetHours}H{$timeOffsetMinutes}M"); if ($sign == '+') { $dateTime->sub($interval); } else { $dateTime->add($interval); } return $dateTime; } } lib/ASN1/Base128.php000064400000003345150736747750007664 0ustar00 0) { $octets .= chr(gmp_strval(gmp_or(0x80, gmp_and($value, 0x7f)), 10)); $value = $rightShift($value, 7); } return strrev($octets); } /** * @param string $octets * * @throws InvalidArgumentException if the given octets represent a malformed base-128 value or the decoded value would exceed the the maximum integer length * * @return int */ public static function decode($octets) { $bitsPerOctet = 7; $value = gmp_init(0, 10); $i = 0; $leftShift = function ($number, $positions) { return gmp_mul($number, gmp_pow(2, $positions)); }; while (true) { if (!isset($octets[$i])) { throw new InvalidArgumentException(sprintf('Malformed base-128 encoded value (0x%s).', strtoupper(bin2hex($octets)) ?: '0')); } $octet = gmp_init(ord($octets[$i++]), 10); $l1 = $leftShift($value, $bitsPerOctet); $r1 = gmp_and($octet, 0x7f); $value = gmp_add($l1, $r1); if (0 === gmp_cmp(gmp_and($octet, 0x80), 0)) { break; } } return gmp_strval($value); } } lib/ASN1/Composite/AttributeTypeAndValue.php000064400000001626150736747750014746 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Composite; use FG\ASN1\ASNObject; use FG\ASN1\Universal\Sequence; use FG\ASN1\Universal\ObjectIdentifier; class AttributeTypeAndValue extends Sequence { /** * @param ObjectIdentifier|string $objIdentifier * @param \FG\ASN1\ASNObject $value */ public function __construct($objIdentifier, ASNObject $value) { if ($objIdentifier instanceof ObjectIdentifier == false) { $objIdentifier = new ObjectIdentifier($objIdentifier); } parent::__construct($objIdentifier, $value); } public function __toString() { return $this->children[0].': '.$this->children[1]; } } lib/ASN1/Composite/RDNString.php000064400000002010150736747750012317 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Composite; use FG\ASN1\Universal\PrintableString; use FG\ASN1\Universal\IA5String; use FG\ASN1\Universal\UTF8String; class RDNString extends RelativeDistinguishedName { /** * @param string|\FG\ASN1\Universal\ObjectIdentifier $objectIdentifierString * @param string|\FG\ASN1\ASNObject $value */ public function __construct($objectIdentifierString, $value) { if (PrintableString::isValid($value)) { $value = new PrintableString($value); } else { if (IA5String::isValid($value)) { $value = new IA5String($value); } else { $value = new UTF8String($value); } } parent::__construct($objectIdentifierString, $value); } } lib/ASN1/Composite/RelativeDistinguishedName.php000064400000003152150736747750015615 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Composite; use FG\ASN1\Exception\NotImplementedException; use FG\ASN1\ASNObject; use FG\ASN1\Universal\Set; class RelativeDistinguishedName extends Set { /** * @param string|\FG\ASN1\Universal\ObjectIdentifier $objIdentifierString * @param \FG\ASN1\ASNObject $value */ public function __construct($objIdentifierString, ASNObject $value) { // TODO: This does only support one element in the RelativeDistinguishedName Set but it it is defined as follows: // RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue parent::__construct(new AttributeTypeAndValue($objIdentifierString, $value)); } public function getContent() { /** @var \FG\ASN1\ASNObject $firstObject */ $firstObject = $this->children[0]; return $firstObject->__toString(); } /** * At the current version this code can not work since the implementation of Construct requires * the class to support a constructor without arguments. * * @deprecated this function is not yet implemented! Feel free to submit a pull request on github * @param string $binaryData * @param int $offsetIndex * @throws NotImplementedException */ public static function fromBinary(&$binaryData, &$offsetIndex = 0) { throw new NotImplementedException(); } } lib/ASN1/Construct.php000064400000010157150736747750010542 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1; use ArrayAccess; use ArrayIterator; use Countable; use Iterator; abstract class Construct extends ASNObject implements Countable, ArrayAccess, Iterator, Parsable { /** @var \FG\ASN1\ASNObject[] */ protected $children; private $iteratorPosition; /** * @param \FG\ASN1\ASNObject[] $children the variadic type hint is commented due to https://github.com/facebook/hhvm/issues/4858 */ public function __construct(/* HH_FIXME[4858]: variadic + strict */ ...$children) { $this->children = $children; $this->iteratorPosition = 0; } public function getContent() { return $this->children; } public function rewind() { $this->iteratorPosition = 0; } public function current() { return $this->children[$this->iteratorPosition]; } public function key() { return $this->iteratorPosition; } public function next() { $this->iteratorPosition++; } public function valid() { return isset($this->children[$this->iteratorPosition]); } public function offsetExists($offset) { return array_key_exists($offset, $this->children); } public function offsetGet($offset) { return $this->children[$offset]; } public function offsetSet($offset, $value) { if ($offset === null) { $offset = count($this->children); } $this->children[$offset] = $value; } public function offsetUnset($offset) { unset($this->children[$offset]); } protected function calculateContentLength() { $length = 0; foreach ($this->children as $component) { $length += $component->getObjectLength(); } return $length; } protected function getEncodedValue() { $result = ''; foreach ($this->children as $component) { $result .= $component->getBinary(); } return $result; } public function addChild(ASNObject $child) { $this->children[] = $child; } public function addChildren(array $children) { foreach ($children as $child) { $this->addChild($child); } } public function __toString() { $nrOfChildren = $this->getNumberOfChildren(); $childString = $nrOfChildren == 1 ? 'child' : 'children'; return "[{$nrOfChildren} {$childString}]"; } public function getNumberOfChildren() { return count($this->children); } /** * @return \FG\ASN1\ASNObject[] */ public function getChildren() { return $this->children; } /** * @return \FG\ASN1\ASNObject */ public function getFirstChild() { return $this->children[0]; } /** * @param string $binaryData * @param int $offsetIndex * * @throws Exception\ParserException * * @return Construct|static */ public static function fromBinary(&$binaryData, &$offsetIndex = 0) { $parsedObject = new static(); self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex); $children = []; $octetsToRead = $contentLength; while ($octetsToRead > 0) { $newChild = ASNObject::fromBinary($binaryData, $offsetIndex); $octetsToRead -= $newChild->getObjectLength(); $children[] = $newChild; } $parsedObject->addChildren($children); $parsedObject->setContentLength($contentLength); return $parsedObject; } public function count($mode = COUNT_NORMAL) { return count($this->children, $mode); } public function getIterator() { return new ArrayIterator($this->children); } } lib/ASN1/Exception/NotImplementedException.php000064400000000521150736747750015311 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Exception; class NotImplementedException extends \Exception { } lib/ASN1/Exception/ParserException.php000064400000001277150736747750013632 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Exception; class ParserException extends \Exception { private $errorMessage; private $offset; public function __construct($errorMessage, $offset) { $this->errorMessage = $errorMessage; $this->offset = $offset; parent::__construct("ASN.1 Parser Exception at offset {$this->offset}: {$this->errorMessage}"); } public function getOffset() { return $this->offset; } } lib/ASN1/ExplicitlyTaggedObject.php000064400000010325150736747750013144 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1; use FG\ASN1\Exception\ParserException; /** * Class ExplicitlyTaggedObject decorate an inner object with an additional tag that gives information about * its context specific meaning. * * Explanation taken from A Layman's Guide to a Subset of ASN.1, BER, and DER: * >>> An RSA Laboratories Technical Note * >>> Burton S. Kaliski Jr. * >>> Revised November 1, 1993 * * [...] * Explicitly tagged types are derived from other types by adding an outer tag to the underlying type. * In effect, explicitly tagged types are structured types consisting of one component, the underlying type. * Explicit tagging is denoted by the ASN.1 keywords [class number] EXPLICIT (see Section 5.2). * [...] * * @see http://luca.ntop.org/Teaching/Appunti/asn1.html */ class ExplicitlyTaggedObject extends ASNObject { /** @var \FG\ASN1\ASNObject[] */ private $decoratedObjects; private $tag; /** * @param int $tag * @param \FG\ASN1\ASNObject $objects,... */ public function __construct($tag, /* HH_FIXME[4858]: variadic + strict */ ...$objects) { $this->tag = $tag; $this->decoratedObjects = $objects; } protected function calculateContentLength() { $length = 0; foreach ($this->decoratedObjects as $object) { $length += $object->getObjectLength(); } return $length; } protected function getEncodedValue() { $encoded = ''; foreach ($this->decoratedObjects as $object) { $encoded .= $object->getBinary(); } return $encoded; } public function getContent() { return $this->decoratedObjects; } public function __toString() { 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}]"; } } public function getType() { return ord($this->getIdentifier()); } public function getIdentifier() { $identifier = Identifier::create(Identifier::CLASS_CONTEXT_SPECIFIC, true, $this->tag); return is_int($identifier) ? chr($identifier) : $identifier; } public function getTag() { return $this->tag; } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { $identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex); $firstIdentifierOctet = ord($identifier); 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); $totalContentLength = self::parseContentLength($binaryData, $offsetIndex); $remainingContentLength = $totalContentLength; $offsetIndexOfDecoratedObject = $offsetIndex; $decoratedObjects = []; while ($remainingContentLength > 0) { $nextObject = ASNObject::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, ...$decoratedObjects); $parsedObject->setContentLength($totalContentLength); return $parsedObject; } } lib/ASN1/Identifier.php000064400000025106150736747750010640 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1; use Exception; /** * The Identifier encodes the ASN.1 tag (class and number) of the type of a data value. * * Every identifier whose number is in the range 0 to 30 has the following structure: * * Bits: 8 7 6 5 4 3 2 1 * | Class | P/C | Tag number | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Bits 8 and 7 define the class of this type ( Universal, Application, Context-specific or Private). * Bit 6 encoded whether this type is primitive or constructed * The remaining bits 5 - 1 encode the tag number */ class Identifier { const CLASS_UNIVERSAL = 0x00; const CLASS_APPLICATION = 0x01; const CLASS_CONTEXT_SPECIFIC = 0x02; const CLASS_PRIVATE = 0x03; const EOC = 0x00; // unsupported for now const BOOLEAN = 0x01; const INTEGER = 0x02; const BITSTRING = 0x03; const OCTETSTRING = 0x04; const NULL = 0x05; const OBJECT_IDENTIFIER = 0x06; const OBJECT_DESCRIPTOR = 0x07; const EXTERNAL = 0x08; // unsupported for now const REAL = 0x09; // unsupported for now const ENUMERATED = 0x0A; const EMBEDDED_PDV = 0x0B; // unsupported for now const UTF8_STRING = 0x0C; const RELATIVE_OID = 0x0D; // value 0x0E and 0x0F are reserved for future use const SEQUENCE = 0x30; const SET = 0x31; const NUMERIC_STRING = 0x12; const PRINTABLE_STRING = 0x13; const T61_STRING = 0x14; // sometimes referred to as TeletextString const VIDEOTEXT_STRING = 0x15; const IA5_STRING = 0x16; const UTC_TIME = 0x17; const GENERALIZED_TIME = 0x18; const GRAPHIC_STRING = 0x19; const VISIBLE_STRING = 0x1A; const GENERAL_STRING = 0x1B; const UNIVERSAL_STRING = 0x1C; const CHARACTER_STRING = 0x1D; // Unrestricted character type const BMP_STRING = 0x1E; const LONG_FORM = 0x1F; const IS_CONSTRUCTED = 0x20; /** * Creates an identifier. Short form identifiers are returned as integers * for BC, long form identifiers will be returned as a string of octets. * * @param int $class * @param bool $isConstructed * @param int $tagNumber * * @throws Exception if the given arguments are invalid * * @return int|string */ public static function create($class, $isConstructed, $tagNumber) { if (!is_numeric($class) || $class < self::CLASS_UNIVERSAL || $class > self::CLASS_PRIVATE) { throw new Exception(sprintf('Invalid class %d given', $class)); } if (!is_bool($isConstructed)) { throw new Exception("\$isConstructed must be a boolean value ($isConstructed given)"); } $tagNumber = self::makeNumeric($tagNumber); if ($tagNumber < 0) { throw new Exception(sprintf('Invalid $tagNumber %d given. You can only use positive integers.', $tagNumber)); } if ($tagNumber < self::LONG_FORM) { return ($class << 6) | ($isConstructed << 5) | $tagNumber; } $firstOctet = ($class << 6) | ($isConstructed << 5) | self::LONG_FORM; // Tag numbers formatted in long form are base-128 encoded. See X.609#8.1.2.4 return chr($firstOctet).Base128::encode($tagNumber); } public static function isConstructed($identifierOctet) { return ($identifierOctet & self::IS_CONSTRUCTED) === self::IS_CONSTRUCTED; } public static function isLongForm($identifierOctet) { return ($identifierOctet & self::LONG_FORM) === self::LONG_FORM; } /** * Return the name of the mapped ASN.1 type with a preceding "ASN.1 ". * * Example: ASN.1 Octet String * * @see Identifier::getShortName() * * @param int|string $identifier * * @return string */ public static function getName($identifier) { $identifierOctet = self::makeNumeric($identifier); $typeName = static::getShortName($identifier); if (($identifierOctet & self::LONG_FORM) < self::LONG_FORM) { $typeName = "ASN.1 {$typeName}"; } return $typeName; } /** * Return the short version of the type name. * * If the given identifier octet can be mapped to a known universal type this will * return its name. Else Identifier::getClassDescription() is used to retrieve * information about the identifier. * * @see Identifier::getName() * @see Identifier::getClassDescription() * * @param int|string $identifier * * @return string */ public static function getShortName($identifier) { $identifierOctet = self::makeNumeric($identifier); switch ($identifierOctet) { case self::EOC: return 'End-of-contents octet'; case self::BOOLEAN: return 'Boolean'; case self::INTEGER: return 'Integer'; case self::BITSTRING: return 'Bit String'; case self::OCTETSTRING: return 'Octet String'; case self::NULL: return 'NULL'; case self::OBJECT_IDENTIFIER: return 'Object Identifier'; case self::OBJECT_DESCRIPTOR: return 'Object Descriptor'; case self::EXTERNAL: return 'External Type'; case self::REAL: return 'Real'; case self::ENUMERATED: return 'Enumerated'; case self::EMBEDDED_PDV: return 'Embedded PDV'; case self::UTF8_STRING: return 'UTF8 String'; case self::RELATIVE_OID: return 'Relative OID'; case self::SEQUENCE: return 'Sequence'; case self::SET: return 'Set'; case self::NUMERIC_STRING: return 'Numeric String'; case self::PRINTABLE_STRING: return 'Printable String'; case self::T61_STRING: return 'T61 String'; case self::VIDEOTEXT_STRING: return 'Videotext String'; case self::IA5_STRING: return 'IA5 String'; case self::UTC_TIME: return 'UTC Time'; case self::GENERALIZED_TIME: return 'Generalized Time'; case self::GRAPHIC_STRING: return 'Graphic String'; case self::VISIBLE_STRING: return 'Visible String'; case self::GENERAL_STRING: return 'General String'; case self::UNIVERSAL_STRING: return 'Universal String'; case self::CHARACTER_STRING: return 'Character String'; case self::BMP_STRING: return 'BMP String'; case 0x0E: return 'RESERVED (0x0E)'; case 0x0F: return 'RESERVED (0x0F)'; case self::LONG_FORM: default: $classDescription = self::getClassDescription($identifier); if (is_int($identifier)) { $identifier = chr($identifier); } return "$classDescription (0x".strtoupper(bin2hex($identifier)).')'; } } /** * Returns a textual description of the information encoded in a given identifier octet. * * The first three (most significant) bytes are evaluated to determine if this is a * constructed or primitive type and if it is either universal, application, context-specific or * private. * * Example: * Constructed context-specific * Primitive universal * * @param int|string $identifier * * @return string */ public static function getClassDescription($identifier) { $identifierOctet = self::makeNumeric($identifier); if (self::isConstructed($identifierOctet)) { $classDescription = 'Constructed '; } else { $classDescription = 'Primitive '; } $classBits = $identifierOctet >> 6; switch ($classBits) { case self::CLASS_UNIVERSAL: $classDescription .= 'universal'; break; case self::CLASS_APPLICATION: $classDescription .= 'application'; break; case self::CLASS_CONTEXT_SPECIFIC: $tagNumber = self::getTagNumber($identifier); $classDescription = "[$tagNumber] Context-specific"; break; case self::CLASS_PRIVATE: $classDescription .= 'private'; break; default: return "INVALID IDENTIFIER OCTET: {$identifierOctet}"; } return $classDescription; } /** * @param int|string $identifier * * @return int */ public static function getTagNumber($identifier) { $firstOctet = self::makeNumeric($identifier); $tagNumber = $firstOctet & self::LONG_FORM; if ($tagNumber < self::LONG_FORM) { return $tagNumber; } if (is_numeric($identifier)) { $identifier = chr($identifier); } return Base128::decode(substr($identifier, 1)); } public static function isUniversalClass($identifier) { $identifier = self::makeNumeric($identifier); return $identifier >> 6 == self::CLASS_UNIVERSAL; } public static function isApplicationClass($identifier) { $identifier = self::makeNumeric($identifier); return $identifier >> 6 == self::CLASS_APPLICATION; } public static function isContextSpecificClass($identifier) { $identifier = self::makeNumeric($identifier); return $identifier >> 6 == self::CLASS_CONTEXT_SPECIFIC; } public static function isPrivateClass($identifier) { $identifier = self::makeNumeric($identifier); return $identifier >> 6 == self::CLASS_PRIVATE; } private static function makeNumeric($identifierOctet) { if (!is_numeric($identifierOctet)) { return ord($identifierOctet); } else { return $identifierOctet; } } } lib/ASN1/OID.php000064400000020047150736747750007170 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1; class OID { const RSA_ENCRYPTION = '1.2.840.113549.1.1.1'; const MD5_WITH_RSA_ENCRYPTION = '1.2.840.113549.1.1.4'; const SHA1_WITH_RSA_SIGNATURE = '1.2.840.113549.1.1.5'; const PKCS9_EMAIL = '1.2.840.113549.1.9.1'; const PKCS9_UNSTRUCTURED_NAME = '1.2.840.113549.1.9.2'; const PKCS9_CONTENT_TYPE = '1.2.840.113549.1.9.3'; const PKCS9_MESSAGE_DIGEST = '1.2.840.113549.1.9.4'; const PKCS9_SIGNING_TIME = '1.2.840.113549.1.9.5'; const PKCS9_EXTENSION_REQUEST = '1.2.840.113549.1.9.14'; // certificate extension identifier const CERT_EXT_SUBJECT_DIRECTORY_ATTR = '2.5.29.9'; const CERT_EXT_SUBJECT_KEY_IDENTIFIER = '2.5.29.14'; const CERT_EXT_KEY_USAGE = '2.5.29.15'; const CERT_EXT_PRIVATE_KEY_USAGE_PERIOD = '2.5.29.16'; const CERT_EXT_SUBJECT_ALT_NAME = '2.5.29.17'; const CERT_EXT_ISSUER_ALT_NAME = '2.5.29.18'; const CERT_EXT_BASIC_CONSTRAINTS = '2.5.29.19'; const CERT_EXT_CRL_NUMBER = '2.5.29.20'; const CERT_EXT_REASON_CODE = '2.5.29.21'; const CERT_EXT_INVALIDITY_DATE = '2.5.29.24'; const CERT_EXT_DELTA_CRL_INDICATOR = '2.5.29.27'; const CERT_EXT_ISSUING_DIST_POINT = '2.5.29.28'; const CERT_EXT_CERT_ISSUER = '2.5.29.29'; const CERT_EXT_NAME_CONSTRAINTS = '2.5.29.30'; const CERT_EXT_CRL_DISTRIBUTION_POINTS = '2.5.29.31'; const CERT_EXT_CERT_POLICIES = '2.5.29.32'; const CERT_EXT_AUTHORITY_KEY_IDENTIFIER = '2.5.29.35'; const CERT_EXT_EXTENDED_KEY_USAGE = '2.5.29.37'; // standard certificate files const COMMON_NAME = '2.5.4.3'; const SURNAME = '2.5.4.4'; const SERIAL_NUMBER = '2.5.4.5'; const COUNTRY_NAME = '2.5.4.6'; const LOCALITY_NAME = '2.5.4.7'; const STATE_OR_PROVINCE_NAME = '2.5.4.8'; const STREET_ADDRESS = '2.5.4.9'; const ORGANIZATION_NAME = '2.5.4.10'; const OU_NAME = '2.5.4.11'; const TITLE = '2.5.4.12'; const DESCRIPTION = '2.5.4.13'; const POSTAL_ADDRESS = '2.5.4.16'; const POSTAL_CODE = '2.5.4.17'; const AUTHORITY_REVOCATION_LIST = '2.5.4.38'; const AUTHORITY_INFORMATION_ACCESS = '1.3.6.1.5.5.7.1.1'; /** * Returns the name of the given object identifier. * * Some OIDs are saved as class constants in this class. * If the wanted oidString is not among them, this method will * query http://oid-info.com for the right name. * This behavior can be suppressed by setting the second method parameter to false. * * @param string $oidString * @param bool $loadFromWeb * * @see self::loadFromWeb($oidString) * * @return string */ public static function getName($oidString, $loadFromWeb = true) { switch ($oidString) { case self::RSA_ENCRYPTION: return 'RSA Encryption'; case self::MD5_WITH_RSA_ENCRYPTION: return 'MD5 with RSA Encryption'; case self::SHA1_WITH_RSA_SIGNATURE: return 'SHA-1 with RSA Signature'; case self::PKCS9_EMAIL: return 'PKCS #9 Email Address'; case self::PKCS9_UNSTRUCTURED_NAME: return 'PKCS #9 Unstructured Name'; case self::PKCS9_CONTENT_TYPE: return 'PKCS #9 Content Type'; case self::PKCS9_MESSAGE_DIGEST: return 'PKCS #9 Message Digest'; case self::PKCS9_SIGNING_TIME: return 'PKCS #9 Signing Time'; case self::COMMON_NAME: return 'Common Name'; case self::SURNAME: return 'Surname'; case self::SERIAL_NUMBER: return 'Serial Number'; case self::COUNTRY_NAME: return 'Country Name'; case self::LOCALITY_NAME: return 'Locality Name'; case self::STATE_OR_PROVINCE_NAME: return 'State or Province Name'; case self::STREET_ADDRESS: return 'Street Address'; case self::ORGANIZATION_NAME: return 'Organization Name'; case self::OU_NAME: return 'Organization Unit Name'; case self::TITLE: return 'Title'; case self::DESCRIPTION: return 'Description'; case self::POSTAL_ADDRESS: return 'Postal Address'; case self::POSTAL_CODE: return 'Postal Code'; case self::AUTHORITY_REVOCATION_LIST: return 'Authority Revocation List'; case self::CERT_EXT_SUBJECT_DIRECTORY_ATTR: return 'Subject directory attributes'; case self::CERT_EXT_SUBJECT_KEY_IDENTIFIER: return 'Subject key identifier'; case self::CERT_EXT_KEY_USAGE: return 'Key usage certificate extension'; case self::CERT_EXT_PRIVATE_KEY_USAGE_PERIOD: return 'Private key usage'; case self::CERT_EXT_SUBJECT_ALT_NAME: return 'Subject alternative name (SAN)'; case self::CERT_EXT_ISSUER_ALT_NAME: return 'Issuer alternative name'; case self::CERT_EXT_BASIC_CONSTRAINTS: return 'Basic constraints'; case self::CERT_EXT_CRL_NUMBER: return 'CRL number'; case self::CERT_EXT_REASON_CODE: return 'Reason code'; case self::CERT_EXT_INVALIDITY_DATE: return 'Invalidity code'; case self::CERT_EXT_DELTA_CRL_INDICATOR: return 'Delta CRL indicator'; case self::CERT_EXT_ISSUING_DIST_POINT: return 'Issuing distribution point'; case self::CERT_EXT_CERT_ISSUER: return 'Certificate issuer'; case self::CERT_EXT_NAME_CONSTRAINTS: return 'Name constraints'; case self::CERT_EXT_CRL_DISTRIBUTION_POINTS: return 'CRL distribution points'; case self::CERT_EXT_CERT_POLICIES: return 'Certificate policies '; case self::CERT_EXT_AUTHORITY_KEY_IDENTIFIER: return 'Authority key identifier'; case self::CERT_EXT_EXTENDED_KEY_USAGE: return 'Extended key usage'; case self::AUTHORITY_INFORMATION_ACCESS: return 'Certificate Authority Information Access (AIA)'; default: if ($loadFromWeb) { return self::loadFromWeb($oidString); } else { return $oidString; } } } public static function loadFromWeb($oidString) { $ch = curl_init("http://oid-info.com/get/{$oidString}"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); $contents = curl_exec($ch); curl_close($ch); // This pattern needs to be updated as soon as the website layout of oid-info.com changes preg_match_all('#(.+)\(\d+\)#si', $contents, $oidName); if (empty($oidName[1])) { return "{$oidString} (unknown)"; } $oidName = ucfirst(strtolower(preg_replace('/([A-Z][a-z])/', ' $1', $oidName[1][0]))); $oidName = str_replace('-', ' ', $oidName); return "{$oidName} ({$oidString})"; } } lib/ASN1/Parsable.php000064400000001771150736747750010311 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1; use FG\ASN1\Exception\ParserException; /** * The Parsable interface describes classes that can be parsed from their binary DER representation. */ interface Parsable { /** * Parse an instance of this class from its binary DER encoded representation. * * @param string $binaryData * @param int $offsetIndex the offset at which parsing of the $binaryData is started. This parameter ill be modified * to contain the offset index of the next object after this object has been parsed * * @throws ParserException if the given binary data is either invalid or not currently supported * * @return static */ public static function fromBinary(&$binaryData, &$offsetIndex = null); } lib/ASN1/TemplateParser.php000064400000003577150736747750011516 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1; use Exception; use FG\ASN1\Exception\ParserException; use FG\ASN1\Universal\Sequence; class TemplateParser { /** * @param string $data * @param array $template * @return \FG\ASN1\ASNObject|Sequence * @throws ParserException if there was an issue parsing */ public function parseBase64($data, array $template) { // TODO test with invalid data return $this->parseBinary(base64_decode($data), $template); } /** * @param string $binary * @param array $template * @return \FG\ASN1\ASNObject|Sequence * @throws ParserException if there was an issue parsing */ public function parseBinary($binary, array $template) { $parsedObject = ASNObject::fromBinary($binary); foreach ($template as $key => $value) { $this->validate($parsedObject, $key, $value); } return $parsedObject; } private function validate(ASNObject $object, $key, $value) { if (is_array($value)) { $this->assertTypeId($key, $object); /* @var Construct $object */ foreach ($value as $key => $child) { $this->validate($object->current(), $key, $child); $object->next(); } } else { $this->assertTypeId($value, $object); } } private function assertTypeId($expectedTypeId, ASNObject $object) { $actualType = $object->getType(); if ($expectedTypeId != $actualType) { throw new Exception("Expected type ($expectedTypeId) does not match actual type ($actualType"); } } } lib/ASN1/Universal/BMPString.php000064400000002071150736747750012327 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractString; use FG\ASN1\Identifier; class BMPString extends AbstractString { /** * Creates a new ASN.1 BMP String. * * BMPString is a subtype of UniversalString that has its own * unique tag and contains only the characters in the * Basic Multilingual Plane (those corresponding to the first * 64K-2 cells, less cells whose encoding is used to address * characters outside the Basic Multilingual Plane) of ISO/IEC 10646-1. * * TODO The encodable characters of this type are not yet checked. * * @param string $string */ public function __construct($string) { $this->value = $string; $this->allowAll(); } public function getType() { return Identifier::BMP_STRING; } } lib/ASN1/Universal/BitString.php000064400000004605150736747750012434 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use Exception; use FG\ASN1\Parsable; use FG\ASN1\Identifier; class BitString extends OctetString implements Parsable { private $nrOfUnusedBits; /** * Creates a new ASN.1 BitString object. * * @param string|int $value Either the hexadecimal value as a string (spaces are allowed - leading 0x is optional) or a numeric value * @param int $nrOfUnusedBits the number of unused bits in the last octet [optional]. * * @throws Exception if the second parameter is no positive numeric value */ public function __construct($value, $nrOfUnusedBits = 0) { parent::__construct($value); if (!is_numeric($nrOfUnusedBits) || $nrOfUnusedBits < 0) { throw new Exception('BitString: second parameter needs to be a positive number (or zero)!'); } $this->nrOfUnusedBits = $nrOfUnusedBits; } public function getType() { return Identifier::BITSTRING; } protected function calculateContentLength() { // add one to the length for the first octet which encodes the number of unused bits in the last octet return parent::calculateContentLength() + 1; } protected function getEncodedValue() { // the first octet determines the number of unused bits $nrOfUnusedBitsOctet = chr($this->nrOfUnusedBits); $actualContent = parent::getEncodedValue(); return $nrOfUnusedBitsOctet.$actualContent; } public function getNumberOfUnusedBits() { return $this->nrOfUnusedBits; } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], Identifier::BITSTRING, $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex, 2); $nrOfUnusedBits = ord($binaryData[$offsetIndex]); $value = substr($binaryData, $offsetIndex + 1, $contentLength - 1); $offsetIndex += $contentLength; $parsedObject = new self(bin2hex($value), $nrOfUnusedBits); $parsedObject->setContentLength($contentLength); return $parsedObject; } } lib/ASN1/Universal/Boolean.php000064400000003403150736747750012101 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\ASNObject; use FG\ASN1\Parsable; use FG\ASN1\Identifier; use FG\ASN1\Exception\ParserException; class Boolean extends ASNObject implements Parsable { private $value; /** * @param bool $value */ public function __construct($value) { $this->value = $value; } public function getType() { return Identifier::BOOLEAN; } protected function calculateContentLength() { return 1; } protected function getEncodedValue() { if ($this->value == false) { return chr(0x00); } else { return chr(0xFF); } } public function getContent() { if ($this->value == true) { return 'TRUE'; } else { return 'FALSE'; } } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], Identifier::BOOLEAN, $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex); if ($contentLength != 1) { throw new ParserException("An ASN.1 Boolean should not have a length other than one. Extracted length was {$contentLength}", $offsetIndex); } $value = ord($binaryData[$offsetIndex++]); $booleanValue = $value == 0xFF ? true : false; $parsedObject = new self($booleanValue); $parsedObject->setContentLength($contentLength); return $parsedObject; } } lib/ASN1/Universal/CharacterString.php000064400000001112150736747750013600 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractString; use FG\ASN1\Identifier; class CharacterString extends AbstractString { public function __construct($string) { $this->value = $string; $this->allowAll(); } public function getType() { return Identifier::CHARACTER_STRING; } } lib/ASN1/Universal/Enumerated.php000064400000000653150736747750012617 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\Identifier; class Enumerated extends Integer { public function getType() { return Identifier::ENUMERATED; } } lib/ASN1/Universal/GeneralString.php000064400000001353150736747750013270 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractString; use FG\ASN1\Identifier; class GeneralString extends AbstractString { /** * Creates a new ASN.1 GeneralString. * TODO The encodable characters of this type are not yet checked. * * @param string $string */ public function __construct($string) { $this->value = $string; $this->allowAll(); } public function getType() { return Identifier::GENERAL_STRING; } } lib/ASN1/Universal/GeneralizedTime.php000064400000011422150736747750013572 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractTime; use FG\ASN1\Parsable; use FG\ASN1\Identifier; use FG\ASN1\Exception\ParserException; /** * This ASN.1 universal type contains date and time information according to ISO 8601. * * The type consists of values representing: * a) a calendar date, as defined in ISO 8601; and * b) a time of day, to any of the precisions defined in ISO 8601, except for the hours value 24 which shall not be used; and * c) the local time differential factor as defined in ISO 8601. * * Decoding of this type will accept the Basic Encoding Rules (BER) * The encoding will comply with the Distinguished Encoding Rules (DER). */ class GeneralizedTime extends AbstractTime implements Parsable { private $microseconds; public function __construct($dateTime = null, $dateTimeZone = 'UTC') { parent::__construct($dateTime, $dateTimeZone); $this->microseconds = $this->value->format('u'); if ($this->containsFractionalSecondsElement()) { // DER requires us to remove trailing zeros $this->microseconds = preg_replace('/([1-9]+)0+$/', '$1', $this->microseconds); } } public function getType() { return Identifier::GENERALIZED_TIME; } protected function calculateContentLength() { $contentSize = 15; // YYYYMMDDHHmmSSZ if ($this->containsFractionalSecondsElement()) { $contentSize += 1 + strlen($this->microseconds); } return $contentSize; } public function containsFractionalSecondsElement() { return intval($this->microseconds) > 0; } protected function getEncodedValue() { $encodedContent = $this->value->format('YmdHis'); if ($this->containsFractionalSecondsElement()) { $encodedContent .= ".{$this->microseconds}"; } return $encodedContent.'Z'; } public function __toString() { if ($this->containsFractionalSecondsElement()) { return $this->value->format("Y-m-d\tH:i:s.uP"); } else { return $this->value->format("Y-m-d\tH:i:sP"); } } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], Identifier::GENERALIZED_TIME, $offsetIndex++); $lengthOfMinimumTimeString = 14; // YYYYMMDDHHmmSS $contentLength = self::parseContentLength($binaryData, $offsetIndex, $lengthOfMinimumTimeString); $maximumBytesToRead = $contentLength; $format = 'YmdGis'; $content = substr($binaryData, $offsetIndex, $contentLength); $dateTimeString = substr($content, 0, $lengthOfMinimumTimeString); $offsetIndex += $lengthOfMinimumTimeString; $maximumBytesToRead -= $lengthOfMinimumTimeString; if ($contentLength == $lengthOfMinimumTimeString) { $localTimeZone = new \DateTimeZone(date_default_timezone_get()); $dateTime = \DateTime::createFromFormat($format, $dateTimeString, $localTimeZone); } else { if ($binaryData[$offsetIndex] == '.') { $maximumBytesToRead--; // account for the '.' $nrOfFractionalSecondElements = 1; // account for the '.' while ($maximumBytesToRead > 0 && $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != '+' && $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != '-' && $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != 'Z') { $nrOfFractionalSecondElements++; $maximumBytesToRead--; } $dateTimeString .= substr($binaryData, $offsetIndex, $nrOfFractionalSecondElements); $offsetIndex += $nrOfFractionalSecondElements; $format .= '.u'; } $dateTime = \DateTime::createFromFormat($format, $dateTimeString, new \DateTimeZone('UTC')); if ($maximumBytesToRead > 0) { if ($binaryData[$offsetIndex] == '+' || $binaryData[$offsetIndex] == '-') { $dateTime = static::extractTimeZoneData($binaryData, $offsetIndex, $dateTime); } elseif ($binaryData[$offsetIndex++] != 'Z') { throw new ParserException('Invalid ISO 8601 Time String', $offsetIndex); } } } $parsedObject = new self($dateTime); $parsedObject->setContentLength($contentLength); return $parsedObject; } } lib/ASN1/Universal/GraphicString.php000064400000001354150736747750013271 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractString; use FG\ASN1\Identifier; class GraphicString extends AbstractString { /** * Creates a new ASN.1 Graphic String. * TODO The encodable characters of this type are not yet checked. * * @param string $string */ public function __construct($string) { $this->value = $string; $this->allowAll(); } public function getType() { return Identifier::GRAPHIC_STRING; } } lib/ASN1/Universal/IA5String.php000064400000001437150736747750012274 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractString; use FG\ASN1\Identifier; /** * The International Alphabet No.5 (IA5) references the encoding of the ASCII characters. * * Each character in the data is encoded as 1 byte. */ class IA5String extends AbstractString { public function __construct($string) { parent::__construct($string); for ($i = 1; $i < 128; $i++) { $this->allowCharacter(chr($i)); } } public function getType() { return Identifier::IA5_STRING; } } lib/ASN1/Universal/Integer.php000064400000006000150736747750012113 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use Exception; use FG\ASN1\ASNObject; use FG\ASN1\Parsable; use FG\ASN1\Identifier; class Integer extends ASNObject implements Parsable { /** @var int */ private $value; /** * @param int $value * * @throws Exception if the value is not numeric */ public function __construct($value) { if (is_numeric($value) == false) { throw new Exception("Invalid VALUE [{$value}] for ASN1_INTEGER"); } $this->value = $value; } public function getType() { return Identifier::INTEGER; } public function getContent() { return $this->value; } protected function calculateContentLength() { $nrOfOctets = 1; // we need at least one octet $tmpValue = gmp_abs(gmp_init($this->value, 10)); while (gmp_cmp($tmpValue, 127) > 0) { $tmpValue = $this->rightShift($tmpValue, 8); $nrOfOctets++; } return $nrOfOctets; } /** * @param resource|\GMP $number * @param int $positions * * @return resource|\GMP */ private function rightShift($number, $positions) { // Shift 1 right = div / 2 return gmp_div($number, gmp_pow(2, (int) $positions)); } protected function getEncodedValue() { $numericValue = gmp_init($this->value, 10); $contentLength = $this->getContentLength(); if (gmp_sign($numericValue) < 0) { $numericValue = gmp_add($numericValue, (gmp_sub(gmp_pow(2, 8 * $contentLength), 1))); $numericValue = gmp_add($numericValue, 1); } $result = ''; for ($shiftLength = ($contentLength - 1) * 8; $shiftLength >= 0; $shiftLength -= 8) { $octet = gmp_strval(gmp_mod($this->rightShift($numericValue, $shiftLength), 256)); $result .= chr($octet); } return $result; } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { $parsedObject = new static(0); self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex, 1); $isNegative = (ord($binaryData[$offsetIndex]) & 0x80) != 0x00; $number = gmp_init(ord($binaryData[$offsetIndex++]) & 0x7F, 10); for ($i = 0; $i < $contentLength - 1; $i++) { $number = gmp_or(gmp_mul($number, 0x100), ord($binaryData[$offsetIndex++])); } if ($isNegative) { $number = gmp_sub($number, gmp_pow(2, 8 * $contentLength - 1)); } $parsedObject = new static(gmp_strval($number, 10)); $parsedObject->setContentLength($contentLength); return $parsedObject; } } lib/ASN1/Universal/Null.php000064400000000640150736747750011434 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; /** * @deprecated Deprecated for future PHP 7 compatibility. Use {@link NullObject} instead. */ class Null extends NullObject { } lib/ASN1/Universal/NullObject.php000064400000002440150736747750012563 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\ASNObject; use FG\ASN1\Parsable; use FG\ASN1\Identifier; use FG\ASN1\Exception\ParserException; class NullObject extends ASNObject implements Parsable { public function getType() { return Identifier::NULL; } protected function calculateContentLength() { return 0; } protected function getEncodedValue() { return null; } public function getContent() { return 'NULL'; } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], Identifier::NULL, $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex); if ($contentLength != 0) { throw new ParserException("An ASN.1 Null should not have a length other than zero. Extracted length was {$contentLength}", $offsetIndex); } $parsedObject = new self(); $parsedObject->setContentLength(0); return $parsedObject; } } lib/ASN1/Universal/NumericString.php000064400000001511150736747750013311 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractString; use FG\ASN1\Identifier; class NumericString extends AbstractString { /** * Creates a new ASN.1 NumericString. * * The following characters are permitted: * Digits 0,1, ... 9 * SPACE (space) * * @param string $string */ public function __construct($string) { $this->value = $string; $this->allowNumbers(); $this->allowSpaces(); } public function getType() { return Identifier::NUMERIC_STRING; } } lib/ASN1/Universal/ObjectDescriptor.php000064400000001060150736747750013764 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\Identifier; class ObjectDescriptor extends GraphicString { public function __construct($objectDescription) { parent::__construct($objectDescription); } public function getType() { return Identifier::OBJECT_DESCRIPTOR; } } lib/ASN1/Universal/ObjectIdentifier.php000064400000007477150736747750013752 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use Exception; use FG\ASN1\Base128; use FG\ASN1\OID; use FG\ASN1\ASNObject; use FG\ASN1\Parsable; use FG\ASN1\Identifier; use FG\ASN1\Exception\ParserException; class ObjectIdentifier extends ASNObject implements Parsable { protected $subIdentifiers; protected $value; public function __construct($value) { $this->subIdentifiers = explode('.', $value); $nrOfSubIdentifiers = count($this->subIdentifiers); for ($i = 0; $i < $nrOfSubIdentifiers; $i++) { if (is_numeric($this->subIdentifiers[$i])) { // enforce the integer type $this->subIdentifiers[$i] = intval($this->subIdentifiers[$i]); } else { throw new Exception("[{$value}] is no valid object identifier (sub identifier ".($i + 1).' is not numeric)!'); } } // Merge the first to arcs of the OID registration tree (per ASN definition!) if ($nrOfSubIdentifiers >= 2) { $this->subIdentifiers[1] = ($this->subIdentifiers[0] * 40) + $this->subIdentifiers[1]; unset($this->subIdentifiers[0]); } $this->value = $value; } public function getContent() { return $this->value; } public function getType() { return Identifier::OBJECT_IDENTIFIER; } protected function calculateContentLength() { $length = 0; foreach ($this->subIdentifiers as $subIdentifier) { do { $subIdentifier = $subIdentifier >> 7; $length++; } while ($subIdentifier > 0); } return $length; } protected function getEncodedValue() { $encodedValue = ''; foreach ($this->subIdentifiers as $subIdentifier) { $encodedValue .= Base128::encode($subIdentifier); } return $encodedValue; } public function __toString() { return OID::getName($this->value); } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], Identifier::OBJECT_IDENTIFIER, $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex, 1); $firstOctet = ord($binaryData[$offsetIndex++]); $oidString = floor($firstOctet / 40).'.'.($firstOctet % 40); $oidString .= '.'.self::parseOid($binaryData, $offsetIndex, $contentLength - 1); $parsedObject = new self($oidString); $parsedObject->setContentLength($contentLength); return $parsedObject; } /** * Parses an object identifier except for the first octet, which is parsed * differently. This way relative object identifiers can also be parsed * using this. * * @param $binaryData * @param $offsetIndex * @param $octetsToRead * * @throws ParserException * * @return string */ protected static function parseOid(&$binaryData, &$offsetIndex, $octetsToRead) { $oid = ''; while ($octetsToRead > 0) { $octets = ''; do { if (0 === $octetsToRead) { throw new ParserException('Malformed ASN.1 Object Identifier', $offsetIndex - 1); } $octetsToRead--; $octet = $binaryData[$offsetIndex++]; $octets .= $octet; } while (ord($octet) & 0x80); $oid .= sprintf('%d.', Base128::decode($octets)); } // Remove trailing '.' return substr($oid, 0, -1) ?: ''; } } lib/ASN1/Universal/OctetString.php000064400000004411150736747750012767 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use Exception; use FG\ASN1\ASNObject; use FG\ASN1\Parsable; use FG\ASN1\Identifier; class OctetString extends ASNObject implements Parsable { protected $value; public function __construct($value) { if (is_string($value)) { // remove gaps between hex digits $value = preg_replace('/\s|0x/', '', $value); } elseif (is_numeric($value)) { $value = dechex($value); } elseif ($value === null) { return; } else { throw new Exception('OctetString: unrecognized input type!'); } if (strlen($value) % 2 != 0) { // transform values like 1F2 to 01F2 $value = '0'.$value; } $this->value = $value; } public function getType() { return Identifier::OCTETSTRING; } protected function calculateContentLength() { return strlen($this->value) / 2; } protected function getEncodedValue() { $value = $this->value; $result = ''; //Actual content while (strlen($value) >= 2) { // get the hex value byte by byte from the string and and add it to binary result $result .= chr(hexdec(substr($value, 0, 2))); $value = substr($value, 2); } return $result; } public function getContent() { return strtoupper($this->value); } public function getBinaryContent() { return $this->getEncodedValue(); } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], Identifier::OCTETSTRING, $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex); $value = substr($binaryData, $offsetIndex, $contentLength); $offsetIndex += $contentLength; $parsedObject = new self(bin2hex($value)); $parsedObject->setContentLength($contentLength); return $parsedObject; } } lib/ASN1/Universal/PrintableString.php000064400000002577150736747750013644 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractString; use FG\ASN1\Identifier; class PrintableString extends AbstractString { /** * Creates a new ASN.1 PrintableString. * * The ITU-T X.680 Table 8 permits the following characters: * Latin capital letters A,B, ... Z * Latin small letters a,b, ... z * Digits 0,1, ... 9 * SPACE (space) * APOSTROPHE ' * LEFT PARENTHESIS ( * RIGHT PARENTHESIS ) * PLUS SIGN + * COMMA , * HYPHEN-MINUS - * FULL STOP . * SOLIDUS / * COLON : * EQUALS SIGN = * QUESTION MARK ? * * @param string $string */ public function __construct($string) { $this->value = $string; $this->allowNumbers(); $this->allowAllLetters(); $this->allowSpaces(); $this->allowCharacters("'", '(', ')', '+', '-', '.', ',', '/', ':', '=', '?'); } public function getType() { return Identifier::PRINTABLE_STRING; } } lib/ASN1/Universal/RelativeObjectIdentifier.php000064400000003460150736747750015432 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use Exception; use FG\ASN1\Parsable; use FG\ASN1\Identifier; use FG\ASN1\Exception\ParserException; class RelativeObjectIdentifier extends ObjectIdentifier implements Parsable { public function __construct($subIdentifiers) { $this->value = $subIdentifiers; $this->subIdentifiers = explode('.', $subIdentifiers); $nrOfSubIdentifiers = count($this->subIdentifiers); for ($i = 0; $i < $nrOfSubIdentifiers; $i++) { if (is_numeric($this->subIdentifiers[$i])) { // enforce the integer type $this->subIdentifiers[$i] = intval($this->subIdentifiers[$i]); } else { throw new Exception("[{$subIdentifiers}] is no valid object identifier (sub identifier ".($i + 1).' is not numeric)!'); } } } public function getType() { return Identifier::RELATIVE_OID; } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], Identifier::RELATIVE_OID, $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex, 1); try { $oidString = self::parseOid($binaryData, $offsetIndex, $contentLength); } catch (ParserException $e) { throw new ParserException('Malformed ASN.1 Relative Object Identifier', $e->getOffset()); } $parsedObject = new self($oidString); $parsedObject->setContentLength($contentLength); return $parsedObject; } } lib/ASN1/Universal/Sequence.php000064400000000752150736747750012276 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\Construct; use FG\ASN1\Parsable; use FG\ASN1\Identifier; class Sequence extends Construct implements Parsable { public function getType() { return Identifier::SEQUENCE; } } lib/ASN1/Universal/Set.php000064400000000636150736747750011262 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\Identifier; class Set extends Sequence { public function getType() { return Identifier::SET; } } lib/ASN1/Universal/T61String.php000064400000001431150736747750012262 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractString; use FG\ASN1\Identifier; class T61String extends AbstractString { /** * Creates a new ASN.1 T61 String. * TODO The encodable characters of this type are not yet checked. * * @see http://en.wikipedia.org/wiki/ITU_T.61 * * @param string $string */ public function __construct($string) { $this->value = $string; $this->allowAll(); } public function getType() { return Identifier::T61_STRING; } } lib/ASN1/Universal/UTCTime.php000064400000004601150736747750011775 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractTime; use FG\ASN1\Parsable; use FG\ASN1\Identifier; use FG\ASN1\Exception\ParserException; /** * This ASN.1 universal type contains the calendar date and time. * * The precision is one minute or one second and optionally a * local time differential from coordinated universal time. * * Decoding of this type will accept the Basic Encoding Rules (BER) * The encoding will comply with the Distinguished Encoding Rules (DER). */ class UTCTime extends AbstractTime implements Parsable { public function getType() { return Identifier::UTC_TIME; } protected function calculateContentLength() { return 13; // Content is a string o the following format: YYMMDDhhmmssZ (13 octets) } protected function getEncodedValue() { return $this->value->format('ymdHis').'Z'; } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], Identifier::UTC_TIME, $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex, 11); $format = 'ymdGi'; $dateTimeString = substr($binaryData, $offsetIndex, 10); $offsetIndex += 10; // extract optional seconds part if ($binaryData[$offsetIndex] != 'Z' && $binaryData[$offsetIndex] != '+' && $binaryData[$offsetIndex] != '-') { $dateTimeString .= substr($binaryData, $offsetIndex, 2); $offsetIndex += 2; $format .= 's'; } $dateTime = \DateTime::createFromFormat($format, $dateTimeString, new \DateTimeZone('UTC')); // extract time zone settings if ($binaryData[$offsetIndex] == '+' || $binaryData[$offsetIndex] == '-') { $dateTime = static::extractTimeZoneData($binaryData, $offsetIndex, $dateTime); } elseif ($binaryData[$offsetIndex++] != 'Z') { throw new ParserException('Invalid UTC String', $offsetIndex); } $parsedObject = new self($dateTime); $parsedObject->setContentLength($contentLength); return $parsedObject; } } lib/ASN1/Universal/UTF8String.php000064400000001350150736747750012436 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractString; use FG\ASN1\Identifier; class UTF8String extends AbstractString { /** * Creates a new ASN.1 Universal String. * TODO The encodable characters of this type are not yet checked. * * @param string $string */ public function __construct($string) { $this->value = $string; $this->allowAll(); } public function getType() { return Identifier::UTF8_STRING; } } lib/ASN1/Universal/UniversalString.php000064400000001472150736747750013665 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractString; use FG\ASN1\Identifier; class UniversalString extends AbstractString { /** * Creates a new ASN.1 Universal String. * TODO The encodable characters of this type are not yet checked. * * @see http://en.wikipedia.org/wiki/Universal_Character_Set * * @param string $string */ public function __construct($string) { $this->value = $string; $this->allowAll(); } public function getType() { return Identifier::UNIVERSAL_STRING; } } lib/ASN1/Universal/VisibleString.php000064400000001354150736747750013311 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1\Universal; use FG\ASN1\AbstractString; use FG\ASN1\Identifier; class VisibleString extends AbstractString { /** * Creates a new ASN.1 Visible String. * TODO The encodable characters of this type are not yet checked. * * @param string $string */ public function __construct($string) { $this->value = $string; $this->allowAll(); } public function getType() { return Identifier::VISIBLE_STRING; } } lib/ASN1/UnknownConstructedObject.php000064400000002621150736747750013557 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1; class UnknownConstructedObject extends Construct { private $identifier; private $contentLength; /** * @param string $binaryData * @param int $offsetIndex * * @throws \FG\ASN1\Exception\ParserException */ public function __construct($binaryData, &$offsetIndex) { $this->identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex); $this->contentLength = self::parseContentLength($binaryData, $offsetIndex); $children = []; $octetsToRead = $this->contentLength; while ($octetsToRead > 0) { $newChild = ASNObject::fromBinary($binaryData, $offsetIndex); $octetsToRead -= $newChild->getObjectLength(); $children[] = $newChild; } parent::__construct(...$children); } public function getType() { return ord($this->identifier); } public function getIdentifier() { return $this->identifier; } protected function calculateContentLength() { return $this->contentLength; } protected function getEncodedValue() { return ''; } } lib/ASN1/UnknownObject.php000064400000002421150736747750011337 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\ASN1; class UnknownObject extends ASNObject { /** @var string */ private $value; private $identifier; /** * @param string|int $identifier Either the first identifier octet as int or all identifier bytes as a string * @param int $contentLength */ public function __construct($identifier, $contentLength) { if (is_int($identifier)) { $identifier = chr($identifier); } $this->identifier = $identifier; $this->value = "Unparsable Object ({$contentLength} bytes)"; $this->setContentLength($contentLength); } public function getContent() { return $this->value; } public function getType() { return ord($this->identifier[0]); } public function getIdentifier() { return $this->identifier; } protected function calculateContentLength() { return $this->getContentLength(); } protected function getEncodedValue() { return ''; } } lib/X509/AlgorithmIdentifier.php000064400000001053150736747750012445 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\X509; use FG\ASN1\Universal\NullObject; use FG\ASN1\Composite\AttributeTypeAndValue; class AlgorithmIdentifier extends AttributeTypeAndValue { public function __construct($objectIdentifierString) { parent::__construct($objectIdentifierString, new NullObject()); } } lib/X509/CSR/Attributes.php000064400000004445150736747750011301 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\X509\CSR; use FG\ASN1\ASNObject; use FG\X509\CertificateExtensions; use FG\ASN1\OID; use FG\ASN1\Parsable; use FG\ASN1\Construct; use FG\ASN1\Identifier; use FG\ASN1\Universal\Set; use FG\ASN1\Universal\Sequence; use FG\ASN1\Universal\ObjectIdentifier; class Attributes extends Construct implements Parsable { public function getType() { return 0xA0; } public function addAttribute($objectIdentifier, Set $attribute) { if (is_string($objectIdentifier)) { $objectIdentifier = new ObjectIdentifier($objectIdentifier); } $attributeSequence = new Sequence($objectIdentifier, $attribute); $attributeSequence->getNumberOfLengthOctets(); // length and number of length octets is calculated $this->addChild($attributeSequence); } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], 0xA0, $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex); $octetsToRead = $contentLength; $parsedObject = new self(); while ($octetsToRead > 0) { $initialOffset = $offsetIndex; // used to calculate how much bits have been read self::parseIdentifier($binaryData[$offsetIndex], Identifier::SEQUENCE, $offsetIndex++); self::parseContentLength($binaryData, $offsetIndex); $objectIdentifier = ObjectIdentifier::fromBinary($binaryData, $offsetIndex); $oidString = $objectIdentifier->getContent(); if ($oidString == OID::PKCS9_EXTENSION_REQUEST) { $attribute = CertificateExtensions::fromBinary($binaryData, $offsetIndex); } else { $attribute = ASNObject::fromBinary($binaryData, $offsetIndex); } $parsedObject->addAttribute($objectIdentifier, $attribute); $octetsToRead -= ($offsetIndex - $initialOffset); } $parsedObject->setContentLength($contentLength); return $parsedObject; } } lib/X509/CSR/CSR.php000064400000006632150736747750007602 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\X509\CSR; use FG\ASN1\OID; use FG\ASN1\Universal\Integer; use FG\ASN1\Universal\BitString; use FG\ASN1\Universal\Sequence; use FG\X509\CertificateSubject; use FG\X509\AlgorithmIdentifier; use FG\X509\PublicKey; class CSR extends Sequence { const CSR_VERSION_NR = 0; protected $subject; protected $publicKey; protected $signature; protected $signatureAlgorithm; protected $startSequence; /** * @param string $commonName * @param string $email * @param string $organization * @param string $locality * @param string $state * @param string $country * @param string $organizationalUnit * @param string $publicKey * @param string $signature * @param string $signatureAlgorithm */ public function __construct($commonName, $email, $organization, $locality, $state, $country, $organizationalUnit, $publicKey, $signature, $signatureAlgorithm = OID::SHA1_WITH_RSA_SIGNATURE) { $this->subject = new CertificateSubject( $commonName, $email, $organization, $locality, $state, $country, $organizationalUnit ); $this->publicKey = $publicKey; $this->signature = $signature; $this->signatureAlgorithm = $signatureAlgorithm; $this->createCSRSequence(); } protected function createCSRSequence() { $versionNr = new Integer(self::CSR_VERSION_NR); $publicKey = new PublicKey($this->publicKey); $signature = new BitString($this->signature); $signatureAlgorithm = new AlgorithmIdentifier($this->signatureAlgorithm); $certRequestInfo = new Sequence($versionNr, $this->subject, $publicKey); $this->addChild($certRequestInfo); $this->addChild($signatureAlgorithm); $this->addChild($signature); } public function __toString() { $tmp = base64_encode($this->getBinary()); for ($i = 0; $i < strlen($tmp); $i++) { if (($i + 2) % 65 == 0) { $tmp = substr($tmp, 0, $i + 1)."\n".substr($tmp, $i + 1); } } $result = '-----BEGIN CERTIFICATE REQUEST-----'.PHP_EOL; $result .= $tmp.PHP_EOL; $result .= '-----END CERTIFICATE REQUEST-----'; return $result; } public function getVersion() { return self::CSR_VERSION_NR; } public function getOrganizationName() { return $this->subject->getOrganization(); } public function getLocalName() { return $this->subject->getLocality(); } public function getState() { return $this->subject->getState(); } public function getCountry() { return $this->subject->getCountry(); } public function getOrganizationalUnit() { return $this->subject->getOrganizationalUnit(); } public function getPublicKey() { return $this->publicKey; } public function getSignature() { return $this->signature; } public function getSignatureAlgorithm() { return $this->signatureAlgorithm; } } lib/X509/CertificateExtensions.php000064400000007117150736747750013025 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\X509; use FG\ASN1\Exception\ParserException; use FG\ASN1\OID; use FG\ASN1\ASNObject; use FG\ASN1\Parsable; use FG\ASN1\Identifier; use FG\ASN1\Universal\OctetString; use FG\ASN1\Universal\Set; use FG\ASN1\Universal\Sequence; use FG\ASN1\Universal\ObjectIdentifier; use FG\X509\SAN\SubjectAlternativeNames; class CertificateExtensions extends Set implements Parsable { private $innerSequence; private $extensions = []; public function __construct() { $this->innerSequence = new Sequence(); parent::__construct($this->innerSequence); } public function addSubjectAlternativeNames(SubjectAlternativeNames $sans) { $this->addExtension(OID::CERT_EXT_SUBJECT_ALT_NAME, $sans); } private function addExtension($oidString, ASNObject $extension) { $sequence = new Sequence(); $sequence->addChild(new ObjectIdentifier($oidString)); $sequence->addChild($extension); $this->innerSequence->addChild($sequence); $this->extensions[] = $extension; } public function getContent() { return $this->extensions; } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], Identifier::SET, $offsetIndex++); self::parseContentLength($binaryData, $offsetIndex); $tmpOffset = $offsetIndex; $extensions = Sequence::fromBinary($binaryData, $offsetIndex); $tmpOffset += 1 + $extensions->getNumberOfLengthOctets(); $parsedObject = new self(); foreach ($extensions as $extension) { if ($extension->getType() != Identifier::SEQUENCE) { //FIXME wrong offset index throw new ParserException('Could not parse Certificate Extensions: Expected ASN.1 Sequence but got '.$extension->getTypeName(), $offsetIndex); } $tmpOffset += 1 + $extension->getNumberOfLengthOctets(); $children = $extension->getChildren(); if (count($children) < 2) { throw new ParserException('Could not parse Certificate Extensions: Needs at least two child elements per extension sequence (object identifier and octet string)', $tmpOffset); } /** @var \FG\ASN1\ASNObject $objectIdentifier */ $objectIdentifier = $children[0]; /** @var OctetString $octetString */ $octetString = $children[1]; if ($objectIdentifier->getType() != Identifier::OBJECT_IDENTIFIER) { throw new ParserException('Could not parse Certificate Extensions: Expected ASN.1 Object Identifier but got '.$extension->getTypeName(), $tmpOffset); } $tmpOffset += $objectIdentifier->getObjectLength(); if ($objectIdentifier->getContent() == OID::CERT_EXT_SUBJECT_ALT_NAME) { $sans = SubjectAlternativeNames::fromBinary($binaryData, $tmpOffset); $parsedObject->addSubjectAlternativeNames($sans); } else { // can now only parse SANs. There might be more in the future $tmpOffset += $octetString->getObjectLength(); } } $parsedObject->getBinary(); // Determine the number of content octets and object sizes once (just to let the equality unit tests pass :/ ) return $parsedObject; } } lib/X509/CertificateSubject.php000064400000005626150736747750012270 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\X509; use FG\ASN1\Composite\RelativeDistinguishedName; use FG\ASN1\Identifier; use FG\ASN1\OID; use FG\ASN1\Parsable; use FG\ASN1\Composite\RDNString; use FG\ASN1\Universal\Sequence; class CertificateSubject extends Sequence implements Parsable { private $commonName; private $email; private $organization; private $locality; private $state; private $country; private $organizationalUnit; /** * @param string $commonName * @param string $email * @param string $organization * @param string $locality * @param string $state * @param string $country * @param string $organizationalUnit */ public function __construct($commonName, $email, $organization, $locality, $state, $country, $organizationalUnit) { parent::__construct( new RDNString(OID::COUNTRY_NAME, $country), new RDNString(OID::STATE_OR_PROVINCE_NAME, $state), new RDNString(OID::LOCALITY_NAME, $locality), new RDNString(OID::ORGANIZATION_NAME, $organization), new RDNString(OID::OU_NAME, $organizationalUnit), new RDNString(OID::COMMON_NAME, $commonName), new RDNString(OID::PKCS9_EMAIL, $email) ); $this->commonName = $commonName; $this->email = $email; $this->organization = $organization; $this->locality = $locality; $this->state = $state; $this->country = $country; $this->organizationalUnit = $organizationalUnit; } public function getCommonName() { return $this->commonName; } public function getEmail() { return $this->email; } public function getOrganization() { return $this->organization; } public function getLocality() { return $this->locality; } public function getState() { return $this->state; } public function getCountry() { return $this->country; } public function getOrganizationalUnit() { return $this->organizationalUnit; } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], Identifier::SEQUENCE, $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex); $names = []; $octetsToRead = $contentLength; while ($octetsToRead > 0) { $relativeDistinguishedName = RelativeDistinguishedName::fromBinary($binaryData, $offsetIndex); $octetsToRead -= $relativeDistinguishedName->getObjectLength(); $names[] = $relativeDistinguishedName; } } } lib/X509/PrivateKey.php000064400000001603150736747750010600 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\X509; use FG\ASN1\OID; use FG\ASN1\Universal\NullObject; use FG\ASN1\Universal\Sequence; use FG\ASN1\Universal\BitString; use FG\ASN1\Universal\ObjectIdentifier; class PrivateKey extends Sequence { /** * @param string $hexKey * @param \FG\ASN1\ASNObject|string $algorithmIdentifierString */ public function __construct($hexKey, $algorithmIdentifierString = OID::RSA_ENCRYPTION) { parent::__construct( new Sequence( new ObjectIdentifier($algorithmIdentifierString), new NullObject() ), new BitString($hexKey) ); } } lib/X509/PublicKey.php000064400000001602150736747750010403 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\X509; use FG\ASN1\OID; use FG\ASN1\Universal\NullObject; use FG\ASN1\Universal\Sequence; use FG\ASN1\Universal\BitString; use FG\ASN1\Universal\ObjectIdentifier; class PublicKey extends Sequence { /** * @param string $hexKey * @param \FG\ASN1\ASNObject|string $algorithmIdentifierString */ public function __construct($hexKey, $algorithmIdentifierString = OID::RSA_ENCRYPTION) { parent::__construct( new Sequence( new ObjectIdentifier($algorithmIdentifierString), new NullObject() ), new BitString($hexKey) ); } } lib/X509/SAN/DNSName.php000064400000001171150736747750010363 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\X509\SAN; use FG\ASN1\Universal\GeneralString; class DNSName extends GeneralString { const IDENTIFIER = 0x82; // not sure yet why this is the identifier used in SAN extensions public function __construct($dnsNameString) { parent::__construct($dnsNameString); } public function getType() { return self::IDENTIFIER; } } lib/X509/SAN/IPAddress.php000064400000003727150736747750010765 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\X509\SAN; use FG\ASN1\ASNObject; use FG\ASN1\Parsable; use FG\ASN1\Exception\ParserException; class IPAddress extends ASNObject implements Parsable { const IDENTIFIER = 0x87; // not sure yet why this is the identifier used in SAN extensions /** @var string */ private $value; public function __construct($ipAddressString) { $this->value = $ipAddressString; } public function getType() { return self::IDENTIFIER; } public function getContent() { return $this->value; } protected function calculateContentLength() { return 4; } protected function getEncodedValue() { $ipParts = explode('.', $this->value); $binary = chr($ipParts[0]); $binary .= chr($ipParts[1]); $binary .= chr($ipParts[2]); $binary .= chr($ipParts[3]); return $binary; } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], self::IDENTIFIER, $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex); if ($contentLength != 4) { throw new ParserException("A FG\\X509\SAN\IPAddress should have a content length of 4. Extracted length was {$contentLength}", $offsetIndex); } $ipAddressString = ord($binaryData[$offsetIndex++]).'.'; $ipAddressString .= ord($binaryData[$offsetIndex++]).'.'; $ipAddressString .= ord($binaryData[$offsetIndex++]).'.'; $ipAddressString .= ord($binaryData[$offsetIndex++]); $parsedObject = new self($ipAddressString); $parsedObject->getObjectLength(); return $parsedObject; } } lib/X509/SAN/SubjectAlternativeNames.php000064400000006247150736747750013731 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\X509\SAN; use FG\ASN1\Exception\ParserException; use FG\ASN1\ASNObject; use FG\ASN1\OID; use FG\ASN1\Parsable; use FG\ASN1\Identifier; use FG\ASN1\Universal\Sequence; /** * See section 8.3.2.1 of ITU-T X.509. */ class SubjectAlternativeNames extends ASNObject implements Parsable { private $alternativeNamesSequence; public function __construct() { $this->alternativeNamesSequence = new Sequence(); } protected function calculateContentLength() { return $this->alternativeNamesSequence->getObjectLength(); } public function getType() { return Identifier::OCTETSTRING; } public function addDomainName(DNSName $domainName) { $this->alternativeNamesSequence->addChild($domainName); } public function addIP(IPAddress $ip) { $this->alternativeNamesSequence->addChild($ip); } public function getContent() { return $this->alternativeNamesSequence->getContent(); } protected function getEncodedValue() { return $this->alternativeNamesSequence->getBinary(); } public static function fromBinary(&$binaryData, &$offsetIndex = 0) { self::parseIdentifier($binaryData[$offsetIndex], Identifier::OCTETSTRING, $offsetIndex++); $contentLength = self::parseContentLength($binaryData, $offsetIndex); if ($contentLength < 2) { throw new ParserException('Can not parse Subject Alternative Names: The Sequence within the octet string after the Object identifier '.OID::CERT_EXT_SUBJECT_ALT_NAME." is too short ({$contentLength} octets)", $offsetIndex); } $offsetOfSequence = $offsetIndex; $sequence = Sequence::fromBinary($binaryData, $offsetIndex); $offsetOfSequence += $sequence->getNumberOfLengthOctets() + 1; if ($sequence->getObjectLength() != $contentLength) { throw new ParserException('Can not parse Subject Alternative Names: The Sequence length does not match the length of the surrounding octet string', $offsetIndex); } $parsedObject = new self(); /** @var \FG\ASN1\ASNObject $object */ foreach ($sequence as $object) { if ($object->getType() == DNSName::IDENTIFIER) { $domainName = DNSName::fromBinary($binaryData, $offsetOfSequence); $parsedObject->addDomainName($domainName); } elseif ($object->getType() == IPAddress::IDENTIFIER) { $ip = IPAddress::fromBinary($binaryData, $offsetOfSequence); $parsedObject->addIP($ip); } else { throw new ParserException('Could not parse Subject Alternative Name: Only DNSName and IP SANs are currently supported', $offsetIndex); } } $parsedObject->getBinary(); // Determine the number of content octets and object sizes once (just to let the equality unit tests pass :/ ) return $parsedObject; } } phpunit.xml.dist000064400000001512150736747750007743 0ustar00 ./tests ./ ./tests ./examples ./vendor tests/ASN1/Base128Test.php000064400000005145150736747750011120 0ustar00assertEquals($expected, Base128::encode($value)); } /** * @dataProvider getDataToDecode */ public function testDecode($expected, $octets) { $this->assertEquals($expected, Base128::decode($octets)); } public function getDataToDecode() { return [ [0x00000000, "\x00"], // self explanatory [0x0000003F, "\x3F"], // binary: 00111111 // septets: 0111111 // padded: 00111111 // hex: 0x3F [0x0000007F, "\x7F"], // binary: 01111111 // septets: 1111111 // padded: 01111111 // hex: 0x7F [0x00000080, "\x81\x00"], // binary: 10000000 // septets: 0000001 00000000 // padded: 10000001 00000000 // hex: 0x81 0x00 [0x00004001, "\x81\x80\x01"], // binary: 01000000 00000001 // septets: 01 0000000 0000001 // padded: 10000001 10000000 00000001 // hex: 0x81 0x80 0x01 [0x00002000, "\xC0\x00"], // binary: 00100000 00000000 // septets: 1000000 0000000 // padded: 11000000 00000000 // hex: 0xC0 0x00 [0x001FFFFF, "\xFF\xFF\x7F"], // binary: 00011111 11111111 11111111 // septet: 1111111 1111111 1111111 // padded: 11111111 11111111 01111111 // hex: 0xFF 0xFF 0x7F [0x0FFFFFFF, "\xFF\xFF\xFF\x7F"], // binary: 00001111 11111111 11111111 11111111 // septet: 1111111 1111111 1111111 1111111 // padded: 11111111 11111111 11111111 01111111 // hex: 0xFF 0xFF 0xFF 0x7F [0xA34B253, "\xD1\xD2\xE4\x53"], // binary: 00001010 00110100 10110010 01010011 // septet: 1010001 1010010 1100100 1010011 // padded: 11010001 11010010 11100100 01010011 // hex: 0xD1 0xD2 0xE4 0x53 ]; } /** * @expectedException \InvalidArgumentException * @expectedExceptionMessage Malformed base-128 encoded value (0xFFFF). */ public function testDecodeFailsIfLastOctetSignificantBitSet() { Base128::decode("\xFF\xFF"); } } tests/ASN1/Composite/RelativeDistinguishedNameTest.php000064400000002322150736747750017047 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Composite; use FG\Test\ASN1TestCase; use FG\ASN1\OID; use FG\ASN1\Identifier; use FG\ASN1\Universal\UTF8String; use FG\ASN1\Composite\RelativeDistinguishedName; class RelativeDistinguishedNameTest extends ASN1TestCase { public function testGetType() { $object = new RelativeDistinguishedName(OID::COMMON_NAME, new UTF8String('Friedrich Große')); $this->assertEquals(Identifier::SET, $object->getType()); } public function testGetIdentifier() { $object = new RelativeDistinguishedName(OID::COMMON_NAME, new UTF8String('Friedrich Große')); $this->assertEquals(chr(Identifier::SET), $object->getIdentifier()); } public function testContent() { $oid = OID::COMMON_NAME; $string = 'Friedrich Große'; $object = new RelativeDistinguishedName($oid, new UTF8String($string)); $this->assertEquals(OID::getName($oid).": {$string}", $object->getContent()); } } tests/ASN1/ExplicitlyTaggedObjectTest.php000064400000010056150736747750014401 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ 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; class ExplicitlyTaggedObjectTest extends ASN1TestCase { public function testGetType() { $asn = new ExplicitlyTaggedObject(0x1E, new PrintableString('test')); $expectedType = Identifier::create(Identifier::CLASS_CONTEXT_SPECIFIC, $isConstructed = true, 0x1E); $this->assertEquals($expectedType, $asn->getType()); } public function testGetIdentifier() { $asn = new ExplicitlyTaggedObject(0x1E, new PrintableString('test')); $expectedIdentifier = chr(Identifier::create(Identifier::CLASS_CONTEXT_SPECIFIC, $isConstructed = true, 0x1E)); $this->assertEquals($expectedIdentifier, $asn->getIdentifier()); } public function testGetTag() { $object = new ExplicitlyTaggedObject(0, new PrintableString('test')); $this->assertEquals(0, $object->getTag()); $object = new ExplicitlyTaggedObject(1, new PrintableString('test')); $this->assertEquals(1, $object->getTag()); } public function testGetLength() { $string = new PrintableString('test'); $object = new ExplicitlyTaggedObject(0, $string); $this->assertEquals($string->getObjectLength() + 2, $object->getObjectLength()); } public function testGetContent() { $string = new PrintableString('test'); $object = new ExplicitlyTaggedObject(0, $string); $this->assertEquals([$string], $object->getContent()); } public function testGetBinary() { $tag = 0x01; $string = new PrintableString('test'); $expectedType = chr(Identifier::create(Identifier::CLASS_CONTEXT_SPECIFIC, $isConstructed = true, $tag)); $expectedLength = chr($string->getObjectLength()); $encodedStringObject = $string->getBinary(); $object = new ExplicitlyTaggedObject($tag, $string); $this->assertBinaryEquals($expectedType.$expectedLength.$encodedStringObject, $object->getBinary()); } /** * @dataProvider getTags * @depends testGetBinary */ public function testFromBinary($originalTag) { $originalStringObject = new PrintableString('test'); $originalObject = new ExplicitlyTaggedObject($originalTag, $originalStringObject); $binaryData = $originalObject->getBinary(); $parsedObject = ExplicitlyTaggedObject::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } public function getTags() { return [ [0x02], [0x00004002], ]; } public function testFromBinaryWithZeroContent() { $data = hex2bin('A000'); $object = ExplicitlyTaggedObject::fromBinary($data); $this->assertEquals(2, $object->getObjectLength()); $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()); } } tests/ASN1/IdentifierTest.php000064400000014756150736747750012105 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; class IdentifierTest extends ASN1TestCase { public function testCreate() { $this->assertEquals(0x02, Identifier::create(Identifier::CLASS_UNIVERSAL, $isConstructed = false, 0x02)); $this->assertEquals(0x30, Identifier::create(Identifier::CLASS_UNIVERSAL, $isConstructed = true, 0x10)); $this->assertEquals(0xB3, Identifier::create(Identifier::CLASS_CONTEXT_SPECIFIC, $isConstructed = true, 0x13)); $this->assertEquals("\x1F\x1F", Identifier::create(Identifier::CLASS_UNIVERSAL, $isConstructed = false, 0x1F)); $this->assertEquals("\x1F\x81\x7F", Identifier::create(Identifier::CLASS_UNIVERSAL, $isConstructed = false, 0xFF)); } public function testGetIdentifierName() { $this->assertEquals('ASN.1 End-of-contents octet', Identifier::getName(Identifier::EOC)); $this->assertEquals('ASN.1 Boolean', Identifier::getName(Identifier::BOOLEAN)); $this->assertEquals('ASN.1 Integer', Identifier::getName(Identifier::INTEGER)); $this->assertEquals('ASN.1 Bit String', Identifier::getName(Identifier::BITSTRING)); $this->assertEquals('ASN.1 Octet String', Identifier::getName(Identifier::OCTETSTRING)); $this->assertEquals('ASN.1 NULL', Identifier::getName(Identifier::NULL)); $this->assertEquals('ASN.1 Object Identifier', Identifier::getName(Identifier::OBJECT_IDENTIFIER)); $this->assertEquals('ASN.1 Object Descriptor', Identifier::getName(Identifier::OBJECT_DESCRIPTOR)); $this->assertEquals('ASN.1 External Type', Identifier::getName(Identifier::EXTERNAL)); $this->assertEquals('ASN.1 Real', Identifier::getName(Identifier::REAL)); $this->assertEquals('ASN.1 Enumerated', Identifier::getName(Identifier::ENUMERATED)); $this->assertEquals('ASN.1 Embedded PDV', Identifier::getName(Identifier::EMBEDDED_PDV)); $this->assertEquals('ASN.1 UTF8 String', Identifier::getName(Identifier::UTF8_STRING)); $this->assertEquals('ASN.1 Relative OID', Identifier::getName(Identifier::RELATIVE_OID)); $this->assertEquals('ASN.1 Sequence', Identifier::getName(Identifier::SEQUENCE)); $this->assertEquals('ASN.1 Set', Identifier::getName(Identifier::SET)); $this->assertEquals('ASN.1 Numeric String', Identifier::getName(Identifier::NUMERIC_STRING)); $this->assertEquals('ASN.1 Printable String', Identifier::getName(Identifier::PRINTABLE_STRING)); $this->assertEquals('ASN.1 T61 String', Identifier::getName(Identifier::T61_STRING)); $this->assertEquals('ASN.1 Videotext String', Identifier::getName(Identifier::VIDEOTEXT_STRING)); $this->assertEquals('ASN.1 IA5 String', Identifier::getName(Identifier::IA5_STRING)); $this->assertEquals('ASN.1 UTC Time', Identifier::getName(Identifier::UTC_TIME)); $this->assertEquals('ASN.1 Generalized Time', Identifier::getName(Identifier::GENERALIZED_TIME)); $this->assertEquals('ASN.1 Graphic String', Identifier::getName(Identifier::GRAPHIC_STRING)); $this->assertEquals('ASN.1 Visible String', Identifier::getName(Identifier::VISIBLE_STRING)); $this->assertEquals('ASN.1 General String', Identifier::getName(Identifier::GENERAL_STRING)); $this->assertEquals('ASN.1 Universal String', Identifier::getName(Identifier::UNIVERSAL_STRING)); $this->assertEquals('ASN.1 Character String', Identifier::getName(Identifier::CHARACTER_STRING)); $this->assertEquals('ASN.1 BMP String', Identifier::getName(Identifier::BMP_STRING)); $this->assertEquals('ASN.1 RESERVED (0x0E)', Identifier::getName(0x0E)); $this->assertEquals('ASN.1 RESERVED (0x0F)', Identifier::getName(0x0F)); $this->assertEquals('Constructed private (0xFF7F)', Identifier::getName("\xFF\x7F")); } public function testGetIdentifierNameWithBinaryInput() { $this->assertEquals('ASN.1 Numeric String', Identifier::getName(chr(Identifier::NUMERIC_STRING))); } public function testGetTagNumber() { $this->assertEquals(1, Identifier::getTagNumber((Identifier::CLASS_CONTEXT_SPECIFIC << 6) | 0x01)); $this->assertEquals(3, Identifier::getTagNumber((Identifier::CLASS_CONTEXT_SPECIFIC << 6) | 0x03)); $this->assertEquals(0xFF, Identifier::getTagNumber("\x1F\x81\x7F")); } /** * @expectedException \InvalidArgumentException * @expectedExceptionMessage Malformed base-128 encoded value (0x0). */ public function testGetTagNumberFailsIfLongFormIdentifierMissingSubsequentOctets() { Identifier::getTagNumber(Identifier::LONG_FORM); } public function testIsSpecificClass() { $identifier1 = Identifier::INTEGER; $this->assertTrue(Identifier::isUniversalClass($identifier1)); $this->assertFalse(Identifier::isApplicationClass($identifier1)); $this->assertFalse(Identifier::isContextSpecificClass($identifier1)); $this->assertFalse(Identifier::isPrivateClass($identifier1)); $identifier2 = 0x41; $this->assertFalse(Identifier::isUniversalClass($identifier2)); $this->assertTrue(Identifier::isApplicationClass($identifier2)); $this->assertFalse(Identifier::isContextSpecificClass($identifier2)); $this->assertFalse(Identifier::isPrivateClass($identifier2)); $identifier3 = 0x83; $this->assertFalse(Identifier::isUniversalClass($identifier3)); $this->assertFalse(Identifier::isApplicationClass($identifier3)); $this->assertTrue(Identifier::isContextSpecificClass($identifier3)); $this->assertFalse(Identifier::isPrivateClass($identifier3)); $identifier4 = 0xC3; $this->assertFalse(Identifier::isUniversalClass($identifier4)); $this->assertFalse(Identifier::isApplicationClass($identifier4)); $this->assertFalse(Identifier::isContextSpecificClass($identifier4)); $this->assertTrue(Identifier::isPrivateClass($identifier4)); $identifier5 = 0xA3; $this->assertFalse(Identifier::isUniversalClass($identifier5)); $this->assertFalse(Identifier::isApplicationClass($identifier5)); $this->assertTrue(Identifier::isContextSpecificClass($identifier5)); $this->assertFalse(Identifier::isPrivateClass($identifier5)); } } tests/ASN1/ObjectTest.php000064400000031017150736747750011216 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1; use FG\ASN1\ExplicitlyTaggedObject; use FG\ASN1\Universal\GeneralizedTime; use FG\Test\ASN1TestCase; use FG\ASN1\ASNObject; use FG\ASN1\UnknownConstructedObject; use FG\ASN1\UnknownObject; use FG\ASN1\Identifier; use FG\ASN1\Universal\BitString; use FG\ASN1\Universal\Boolean; use FG\ASN1\Universal\Enumerated; use FG\ASN1\Universal\Integer; use FG\ASN1\Universal\NullObject; use FG\ASN1\Universal\ObjectIdentifier; use FG\ASN1\Universal\OctetString; use FG\ASN1\Universal\Sequence; use FG\ASN1\Universal\IA5String; use FG\ASN1\Universal\PrintableString; class ObjectTest extends ASN1TestCase { public function testCalculateNumberOfLengthOctets() { $object = $this->getMockForAbstractClass('\FG\ASN1\ASNObject'); $calculatedNrOfLengthOctets = $this->callMethod($object, 'getNumberOfLengthOctets', 32); $this->assertEquals(1, $calculatedNrOfLengthOctets); $object = $this->getMockForAbstractClass('\FG\ASN1\ASNObject'); $calculatedNrOfLengthOctets = $this->callMethod($object, 'getNumberOfLengthOctets', 0); $this->assertEquals(1, $calculatedNrOfLengthOctets); $object = $this->getMockForAbstractClass('\FG\ASN1\ASNObject'); $calculatedNrOfLengthOctets = $this->callMethod($object, 'getNumberOfLengthOctets', 127); $this->assertEquals(1, $calculatedNrOfLengthOctets); $object = $this->getMockForAbstractClass('\FG\ASN1\ASNObject'); $calculatedNrOfLengthOctets = $this->callMethod($object, 'getNumberOfLengthOctets', 128); $this->assertEquals(2, $calculatedNrOfLengthOctets); $object = $this->getMockForAbstractClass('\FG\ASN1\ASNObject'); $calculatedNrOfLengthOctets = $this->callMethod($object, 'getNumberOfLengthOctets', 255); $this->assertEquals(2, $calculatedNrOfLengthOctets); $object = $this->getMockForAbstractClass('\FG\ASN1\ASNObject'); $calculatedNrOfLengthOctets = $this->callMethod($object, 'getNumberOfLengthOctets', 1025); $this->assertEquals(3, $calculatedNrOfLengthOctets); } /** * For the real parsing tests look in the test cases of each single ASN object. */ public function testFromBinary() { /* @var BitString $parsedObject */ $binaryData = chr(Identifier::BITSTRING); $binaryData .= chr(0x03); $binaryData .= chr(0x05); $binaryData .= chr(0xFF); $binaryData .= chr(0xA0); $expectedObject = new BitString(0xFFA0, 5); $parsedObject = ASNObject::fromBinary($binaryData); $this->assertTrue($parsedObject instanceof BitString); $this->assertEquals($expectedObject->getContent(), $parsedObject->getContent()); $this->assertEquals($expectedObject->getNumberOfUnusedBits(), $parsedObject->getNumberOfUnusedBits()); /* @var OctetString $parsedObject */ $binaryData = chr(Identifier::OCTETSTRING); $binaryData .= chr(0x02); $binaryData .= chr(0xFF); $binaryData .= chr(0xA0); $expectedObject = new OctetString(0xFFA0); $parsedObject = ASNObject::fromBinary($binaryData); $this->assertTrue($parsedObject instanceof OctetString); $this->assertEquals($expectedObject->getContent(), $parsedObject->getContent()); /* @var \FG\ASN1\Universal\Boolean $parsedObject */ $binaryData = chr(Identifier::BOOLEAN); $binaryData .= chr(0x01); $binaryData .= chr(0xFF); $expectedObject = new Boolean(true); $parsedObject = ASNObject::fromBinary($binaryData); $this->assertTrue($parsedObject instanceof Boolean); $this->assertEquals($expectedObject->getContent(), $parsedObject->getContent()); /* @var Enumerated $parsedObject */ $binaryData = chr(Identifier::ENUMERATED); $binaryData .= chr(0x01); $binaryData .= chr(0x03); $expectedObject = new Enumerated(3); $parsedObject = ASNObject::fromBinary($binaryData); $this->assertTrue($parsedObject instanceof Enumerated); $this->assertEquals($expectedObject->getContent(), $parsedObject->getContent()); /* @var IA5String $parsedObject */ $string = 'Hello Foo World!!!11EinsEins!1'; $binaryData = chr(Identifier::IA5_STRING); $binaryData .= chr(strlen($string)); $binaryData .= $string; $expectedObject = new IA5String($string); $parsedObject = ASNObject::fromBinary($binaryData); $this->assertTrue($parsedObject instanceof IA5String); $this->assertEquals($expectedObject->getContent(), $parsedObject->getContent()); /* @var \FG\ASN1\Universal\Integer $parsedObject */ $binaryData = chr(Identifier::INTEGER); $binaryData .= chr(0x01); $binaryData .= chr(123); $expectedObject = new Integer(123); $parsedObject = ASNObject::fromBinary($binaryData); $this->assertTrue($parsedObject instanceof Integer); $this->assertEquals($expectedObject->getContent(), $parsedObject->getContent()); /* @var \FG\ASN1\Universal\NullObject $parsedObject */ $binaryData = chr(Identifier::NULL); $binaryData .= chr(0x00); $expectedObject = new NullObject(); $parsedObject = ASNObject::fromBinary($binaryData); $this->assertTrue($parsedObject instanceof NullObject); $this->assertEquals($expectedObject->getContent(), $parsedObject->getContent()); /* @var ObjectIdentifier $parsedObject */ $binaryData = chr(Identifier::OBJECT_IDENTIFIER); $binaryData .= chr(0x02); $binaryData .= chr(1 * 40 + 2); $binaryData .= chr(3); $expectedObject = new ObjectIdentifier('1.2.3'); $parsedObject = ASNObject::fromBinary($binaryData); $this->assertTrue($parsedObject instanceof ObjectIdentifier); $this->assertEquals($expectedObject->getContent(), $parsedObject->getContent()); /* @var PrintableString $parsedObject */ $string = 'This is a test string. #?!%&""'; $binaryData = chr(Identifier::PRINTABLE_STRING); $binaryData .= chr(strlen($string)); $binaryData .= $string; $expectedObject = new PrintableString($string); $parsedObject = ASNObject::fromBinary($binaryData); $this->assertTrue($parsedObject instanceof PrintableString); $this->assertEquals($expectedObject->getContent(), $parsedObject->getContent()); /* @var GeneralizedTime $parsedObject */ $binaryData = chr(Identifier::GENERALIZED_TIME); $binaryData .= chr(15); $binaryData .= '20120923202316Z'; $expectedObject = new GeneralizedTime('2012-09-23 20:23:16', 'UTC'); $parsedObject = ASNObject::fromBinary($binaryData); $this->assertTrue($parsedObject instanceof GeneralizedTime); $this->assertEquals($expectedObject->getContent(), $parsedObject->getContent()); /* @var Sequence $parsedObject */ $binaryData = chr(Identifier::SEQUENCE); $binaryData .= chr(0x06); $binaryData .= chr(Identifier::BOOLEAN); $binaryData .= chr(0x01); $binaryData .= chr(0x00); $binaryData .= chr(Identifier::INTEGER); $binaryData .= chr(0x01); $binaryData .= chr(0x03); $expectedChild1 = new Boolean(false); $expectedChild2 = new Integer(0x03); $parsedObject = ASNObject::fromBinary($binaryData); $this->assertTrue($parsedObject instanceof Sequence); $this->assertEquals(2, $parsedObject->getNumberOfChildren()); $children = $parsedObject->getChildren(); $child1 = $children[0]; $child2 = $children[1]; $this->assertEquals($expectedChild1->getContent(), $child1->getContent()); $this->assertEquals($expectedChild2->getContent(), $child2->getContent()); /* @var ExplicitlyTaggedObject $parsedObject */ $taggedObject = new ExplicitlyTaggedObject(0x01, new PrintableString('Hello tagged world')); $binaryData = $taggedObject->getBinary(); $parsedObject = ASNObject::fromBinary($binaryData); $this->assertTrue($parsedObject instanceof ExplicitlyTaggedObject); // An unknown constructed object containing 2 integer children, // first 3 bytes are the identifier. $binaryData = "\x3F\x81\x7F\x06".chr(Identifier::INTEGER)."\x01\x42".chr(Identifier::INTEGER)."\x01\x69"; $offsetIndex = 0; $parsedObject = ASNObject::fromBinary($binaryData, $offsetIndex); $this->assertTrue($parsedObject instanceof UnknownConstructedObject); $this->assertEquals(substr($binaryData, 0, 3), $parsedObject->getIdentifier()); $this->assertCount(2, $parsedObject->getContent()); $this->assertEquals(strlen($binaryData), $offsetIndex); $this->assertEquals(10, $parsedObject->getObjectLength()); // First 3 bytes are the identifier $binaryData = "\x1F\x81\x7F\x01\xFF"; $offsetIndex = 0; $parsedObject = ASNObject::fromBinary($binaryData, $offsetIndex); $this->assertTrue($parsedObject instanceof UnknownObject); $this->assertEquals(substr($binaryData, 0, 3), $parsedObject->getIdentifier()); $this->assertEquals('Unparsable Object (1 bytes)', $parsedObject->getContent()); $this->assertEquals(strlen($binaryData), $offsetIndex); $this->assertEquals(5, $parsedObject->getObjectLength()); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 10: Can not parse binary from data: Offset index larger than input size * @depends testFromBinary */ public function testFromBinaryThrowsException() { $binaryData = 0x0; $offset = 10; ASNObject::fromBinary($binaryData, $offset); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 0: Can not parse binary from data: Offset index larger than input size * @depends testFromBinary */ public function testFromBinaryWithEmptyStringThrowsException() { $data = ''; ASNObject::fromBinary($data); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 2: Can not parse binary from data: Offset index larger than input size * @depends testFromBinary */ public function testFromBinaryWithSpacyStringThrowsException() { $data = ' '; ASNObject::fromBinary($data); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 1: Can not parse content length from data: Offset index larger than input size * @depends testFromBinary */ public function testFromBinaryWithNumberStringThrowsException() { $data = '1'; ASNObject::fromBinary($data); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 25: Can not parse content length from data: Offset index larger than input size * @depends testFromBinary */ public function testFromBinaryWithGarbageStringThrowsException() { $data = 'certainly no asn.1 object'; ASNObject::fromBinary($data); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 1: Can not parse identifier (long form) from data: Offset index larger than input size * @depends testFromBinary */ public function testFromBinaryUnknownObjectMissingLength() { $data = hex2bin('1f'); ASNObject::fromBinary($data); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 4: Can not parse content length (long form) from data: Offset index larger than input size * @depends testFromBinary */ public function testFromBinaryInalidLongFormContentLength() { $binaryData = chr(Identifier::INTEGER); $binaryData .= chr(0x8f); //denotes a long-form content length with 15 length-octets $binaryData .= chr(0x1); //only give one content-length-octet $binaryData .= chr(0x1); //this is needed to reach the code to be tested ASNObject::fromBinary($binaryData); } } tests/ASN1/TemplateParserTest.php000064400000002717150736747750012745 0ustar00getBinary()); $template = [ Identifier::SEQUENCE => [ Identifier::SET => [ Identifier::OBJECT_IDENTIFIER, Identifier::SEQUENCE => [ Identifier::INTEGER, Identifier::BITSTRING, ], ], ], ]; $parser = new TemplateParser(); $object = $parser->parseBase64($data, $template); $this->assertInstanceOf(Set::class, $object[0]); $this->assertInstanceOf(ObjectIdentifier::class, $object[0][0]); $this->assertInstanceOf(Sequence::class, $object[0][1]); $this->assertInstanceOf(Integer::class, $object[0][1][0]); $this->assertInstanceOf(BitString::class, $object[0][1][1]); } } tests/ASN1/Universal/BMPStringTest.php000064400000004713150736747750013570 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\BMPString; class BMPStringTest extends ASN1TestCase { public function testGetType() { $object = new BMPString('Hello World'); $this->assertEquals(Identifier::BMP_STRING, $object->getType()); } public function testGetIdentifier() { $object = new BMPString('Hello World'); $this->assertEquals(chr(Identifier::BMP_STRING), $object->getIdentifier()); } public function testContent() { $object = new BMPString('Hello World'); $this->assertEquals('Hello World', $object->getContent()); } public function testGetObjectLength() { $string = 'Hello World'; $object = new BMPString($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = 'Hello World'; $expectedType = chr(Identifier::BMP_STRING); $expectedLength = chr(strlen($string)); $object = new BMPString($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new BMPString('Hello World'); $binaryData = $originalObject->getBinary(); $parsedObject = BMPString::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new BMPString('Hello '); $originalObject2 = new BMPString(' World'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = BMPString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = BMPString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } } tests/ASN1/Universal/BitStringTest.php000064400000012605150736747750013667 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\BitString; class BitStringTest extends ASN1TestCase { public function testGetType() { $object = new BitString('A0 12 00 43'); $this->assertEquals(Identifier::BITSTRING, $object->getType()); } public function testGetIdentifier() { $object = new BitString('A0 12 00 43'); $this->assertEquals(chr(Identifier::BITSTRING), $object->getIdentifier()); } public function testContent() { $object = new BitString('A01200C3'); $this->assertEquals('A01200C3', $object->getContent()); $object = new BitString('A0 12 00 C3'); $this->assertEquals('A01200C3', $object->getContent()); $object = new BitString('a0 12 00 c3'); $this->assertEquals('A01200C3', $object->getContent()); $object = new BitString('A0 12 00 c3'); $this->assertEquals('A01200C3', $object->getContent()); $object = new BitString(0xA01200C3); $this->assertEquals('A01200C3', $object->getContent()); } public function testGetObjectLength() { $object = new BitString(0x00); $this->assertEquals(4, $object->getObjectLength()); $object = new BitString(0xFF); $this->assertEquals(4, $object->getObjectLength()); $object = new BitString(0xA000); $this->assertEquals(5, $object->getObjectLength()); $object = new BitString(0x3F2001); $this->assertEquals(6, $object->getObjectLength()); } public function testGetObjectLengthWithVeryLongBitString() { $hexString = '0x'.str_repeat('FF', 1024); $object = new BitString($hexString); $this->assertEquals(1 + 3 + 1 + 1024, $object->getObjectLength()); } public function testGetBinary() { $expectedType = chr(Identifier::BITSTRING); $expectedLength = chr(0x02); $object = new BitString(0xFF); $expectedContent = chr(0x00); $expectedContent .= chr(0xFF); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new BitString(0xFFA034); $expectedLength = chr(0x04); $expectedContent = chr(0x00); $expectedContent .= chr(0xFF); $expectedContent .= chr(0xA0); $expectedContent .= chr(0x34); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new BitString(0xA8, 3); $expectedLength = chr(0x02); $expectedContent = chr(0x03); $expectedContent .= chr(0xA8); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } public function testGetBinaryForLargeBitStrings() { $nrOfBytes = 1024; $hexString = '0x'.str_repeat('FF', $nrOfBytes); $object = new BitString($hexString); $expectedType = chr(Identifier::BITSTRING); $expectedLength = chr(0x80 | 0x02); // long length form: 2 length octets $expectedLength .= chr(1025 >> 8); // first 8 bit of 1025 $expectedLength .= chr(1025 & 0xFF); // last 8 bit of 1025 $expectedContent = chr(0x00); // number of unused bits for ($i = 0; $i < $nrOfBytes; $i++) { $expectedContent .= chr(0xFF); // content } $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new BitString(0x12); $binaryData = $originalObject->getBinary(); $parsedObject = BitString::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); $originalObject = new BitString(0x010203A0, 3); $binaryData = $originalObject->getBinary(); $parsedObject = BitString::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new BitString(0xA0); $originalObject2 = new BitString(0x314510); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = BitString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(4, $offset); $parsedObject = BitString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(10, $offset); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 2: A FG\ASN1\Universal\BitString should have a content length of at least 2. Extracted length was 1 * @depends testFromBinary */ public function testFromBinaryWithInvalidLength01() { $binaryData = chr(Identifier::BITSTRING); $binaryData .= chr(0x01); $binaryData .= chr(0x00); BitString::fromBinary($binaryData); } } tests/ASN1/Universal/BooleanTest.php000064400000007465150736747750013351 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\Boolean; class BooleanTest extends ASN1TestCase { public function testGetType() { $object = new Boolean(true); $this->assertEquals(Identifier::BOOLEAN, $object->getType()); } public function testGetIdentifier() { $object = new Boolean(true); $this->assertEquals(chr(Identifier::BOOLEAN), $object->getIdentifier()); } public function testContent() { $object = new Boolean(true); $this->assertEquals('TRUE', $object->getContent()); $object = new Boolean(false); $this->assertEquals('FALSE', $object->getContent()); } public function testGetObjectLength() { $object = new Boolean(true); $this->assertEquals(3, $object->getObjectLength()); $object = new Boolean(false); $this->assertEquals(3, $object->getObjectLength()); } public function testGetBinary() { $expectedType = chr(Identifier::BOOLEAN); $expectedLength = chr(0x01); $object = new Boolean(true); $expectedContent = chr(0xFF); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new Boolean(false); $expectedContent = chr(0x00); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new Boolean(true); $binaryData = $originalObject->getBinary(); $parsedObject = Boolean::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); $originalObject = new Boolean(false); $binaryData = $originalObject->getBinary(); $parsedObject = Boolean::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new Boolean(true); $originalObject2 = new Boolean(false); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = Boolean::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(3, $offset); $parsedObject = Boolean::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(6, $offset); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 2: An ASN.1 Boolean should not have a length other than one. Extracted length was 2 * @depends testFromBinary */ public function testFromBinaryWithInvalidLength01() { $binaryData = chr(Identifier::BOOLEAN); $binaryData .= chr(0x02); $binaryData .= chr(0xFF); Boolean::fromBinary($binaryData); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 2: An ASN.1 Boolean should not have a length other than one. Extracted length was 0 * @depends testFromBinary */ public function testFromBinaryWithInvalidLength02() { $binaryData = chr(Identifier::BOOLEAN); $binaryData .= chr(0x00); $binaryData .= chr(0xFF); Boolean::fromBinary($binaryData); } } tests/ASN1/Universal/CharacterStringTest.php000064400000005053150736747750015044 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\CharacterString; class CharacterStringTest extends ASN1TestCase { public function testGetType() { $object = new CharacterString('Hello World'); $this->assertEquals(Identifier::CHARACTER_STRING, $object->getType()); } public function testGetIdentifier() { $object = new CharacterString('Hello World'); $this->assertEquals(chr(Identifier::CHARACTER_STRING), $object->getIdentifier()); } public function testContent() { $object = new CharacterString('Hello World'); $this->assertEquals('Hello World', $object->getContent()); } public function testGetObjectLength() { $string = 'Hello World'; $object = new CharacterString($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = 'Hello World'; $expectedType = chr(Identifier::CHARACTER_STRING); $expectedLength = chr(strlen($string)); $object = new CharacterString($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new CharacterString('Hello World'); $binaryData = $originalObject->getBinary(); $parsedObject = CharacterString::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new CharacterString('Hello '); $originalObject2 = new CharacterString(' World'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = CharacterString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = CharacterString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } } tests/ASN1/Universal/EnumeratedTest.php000064400000007036150736747750014055 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\Enumerated; class EnumeratedTest extends ASN1TestCase { public function testGetType() { $object = new Enumerated(1); $this->assertEquals(Identifier::ENUMERATED, $object->getType()); } public function testGetIdentifier() { $object = new Enumerated(1); $this->assertEquals(chr(Identifier::ENUMERATED), $object->getIdentifier()); } public function testContent() { $object = new Enumerated(0); $this->assertEquals(0, $object->getContent()); $object = new Enumerated(1); $this->assertEquals(1, $object->getContent()); $object = new Enumerated(512); $this->assertEquals(512, $object->getContent()); } public function testGetObjectLength() { $object = new Enumerated(0); $this->assertEquals(3, $object->getObjectLength()); $object = new Enumerated(127); $this->assertEquals(3, $object->getObjectLength()); $object = new Enumerated(128); $this->assertEquals(4, $object->getObjectLength()); } public function testGetBinary() { $expectedType = chr(Identifier::ENUMERATED); $expectedLength = chr(0x01); $object = new Enumerated(0); $expectedContent = chr(0x00); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new Enumerated(127); $expectedContent = chr(0x7F); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new Enumerated(7420); $expectedLength = chr(0x02); $expectedContent = chr(0x1C); $expectedContent .= chr(0xFC); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new Enumerated(0); $binaryData = $originalObject->getBinary(); $parsedObject = Enumerated::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); $originalObject = new Enumerated(127); $binaryData = $originalObject->getBinary(); $parsedObject = Enumerated::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); $originalObject = new Enumerated(200); $binaryData = $originalObject->getBinary(); $parsedObject = Enumerated::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new Enumerated(1); $originalObject2 = new Enumerated(2); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = Enumerated::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(3, $offset); $parsedObject = Enumerated::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(6, $offset); } } tests/ASN1/Universal/GeneralStringTest.php000064400000005351150736747750014526 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\GeneralString; class GeneralStringTest extends ASN1TestCase { public function testGetType() { $object = new GeneralString('Hello World'); $this->assertEquals(Identifier::GENERAL_STRING, $object->getType()); } public function testGetIdentifier() { $object = new GeneralString('Hello World'); $this->assertEquals(chr(Identifier::GENERAL_STRING), $object->getIdentifier()); } public function testContent() { $object = new GeneralString('Hello World'); $this->assertEquals('Hello World', $object->getContent()); $object = new GeneralString(''); $this->assertEquals('', $object->getContent()); $object = new GeneralString(' '); $this->assertEquals(' ', $object->getContent()); } public function testGetObjectLength() { $string = 'Hello World'; $object = new GeneralString($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = 'Hello World'; $expectedType = chr(Identifier::GENERAL_STRING); $expectedLength = chr(strlen($string)); $object = new GeneralString($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new GeneralString('Hello World'); $binaryData = $originalObject->getBinary(); $parsedObject = GeneralString::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new GeneralString('Hello '); $originalObject2 = new GeneralString(' World'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = GeneralString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = GeneralString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } } tests/ASN1/Universal/GeneralizedTimeTest.php000064400000014514150736747750015033 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use DateTime; use DateTimeZone; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\GeneralizedTime; class GeneralizedTimeTest extends ASN1TestCase { private $UTC; public function setUp() { $this->UTC = new DateTimeZone('UTC'); } public function testGetType() { $object = new GeneralizedTime(); $this->assertEquals(Identifier::GENERALIZED_TIME, $object->getType()); } public function testGetIdentifier() { $object = new GeneralizedTime(); $this->assertEquals(chr(Identifier::GENERALIZED_TIME), $object->getIdentifier()); } public function testGetContent() { $now = new DateTime(); $now->setTimezone($this->UTC); $object = new GeneralizedTime($now, 'UTC'); $content = $object->getContent(); $this->assertTrue($content instanceof DateTime); $this->assertEquals($now->format(DATE_RFC3339), $content->format(DATE_RFC3339)); $timeString = '2012-09-23 20:27'; $dateTime = new DateTime($timeString, $this->UTC); $object = new GeneralizedTime($timeString); $content = $object->getContent(); $this->assertTrue($content instanceof DateTime); $this->assertEquals($dateTime->format(DATE_RFC3339), $content->format(DATE_RFC3339)); } public function testGetObjectLength() { $object = new GeneralizedTime('1987-01-15 12:12'); $expectedSize = 2 + 15; // Identifier + length + YYYYMMDDHHmmSSZ $this->assertEquals($expectedSize, $object->getObjectLength()); // without specified daytime $object = new GeneralizedTime('2012-09-23'); $this->assertEquals($expectedSize, $object->getObjectLength()); // with fractional-seconds elements $object = new GeneralizedTime('2012-09-23 22:21:03.5435440'); $this->assertEquals($expectedSize + 7, $object->getObjectLength()); } public function testGetBinary() { $now = new DateTime('1987-01-15 12:12'); $now->setTimezone($this->UTC); $expectedType = chr(Identifier::GENERALIZED_TIME); $expectedLength = chr(15); // YYYYMMDDHHmmSSZ $object = new GeneralizedTime($now); $expectedContent = $now->format('YmdHis').'Z'; $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $dateString = '2012-09-23'; $object = new GeneralizedTime($dateString); $expectedContent = '20120923000000Z'; $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $dateString = '1987-01-15 12:12'; $object = new GeneralizedTime($dateString); $expectedContent = '19870115121200Z'; $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $dateString = '2008-07-01 22:35:17.02'; $expectedLength = chr(18); $object = new GeneralizedTime($dateString); $expectedContent = '20080701223517.02Z'; $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $dateString = '2008-07-01 22:35:17.024540'; $expectedLength = chr(21); $object = new GeneralizedTime($dateString); $expectedContent = '20080701223517.02454Z'; $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinaryWithDEREncoding() { $dateTime = new DateTime('2012-09-23 20:23:16', $this->UTC); $binaryData = chr(Identifier::GENERALIZED_TIME); $binaryData .= chr(15); $binaryData .= '20120923202316Z'; $parsedObject = GeneralizedTime::fromBinary($binaryData); $this->assertEquals($dateTime, $parsedObject->getContent()); } /** * @depends testGetBinary */ public function testFromBinaryWithDEREncodingAndFractionalSecondsPart() { $dateTime = new DateTime('2012-09-23 22:21:03.5435440', $this->UTC); $binaryData = chr(Identifier::GENERALIZED_TIME); $binaryData .= chr(22); $binaryData .= '20120923222103.543544Z'; $parsedObject = GeneralizedTime::fromBinary($binaryData); $this->assertEquals($dateTime, $parsedObject->getContent()); } /** * @depends testGetBinary */ public function testFromBinaryWithBEREncodingWithLocalTimeZone() { $dateTime = new DateTime('2012-09-23 20:23:16'); $binaryData = chr(Identifier::GENERALIZED_TIME); $binaryData .= chr(14); $binaryData .= '20120923202316'; $parsedObject = GeneralizedTime::fromBinary($binaryData); $this->assertEquals($dateTime, $parsedObject->getContent()); } /** * @depends testGetBinary */ public function testFromBinaryWithBEREncodingWithOtherTimeZone() { $dateTime = new DateTime('2012-09-23 22:13:59', $this->UTC); $binaryData = chr(Identifier::GENERALIZED_TIME); $binaryData .= chr(19); $binaryData .= '20120923161359-0600'; $parsedObject = GeneralizedTime::fromBinary($binaryData); $this->assertEquals($dateTime, $parsedObject->getContent()); $dateTime = new DateTime('2012-09-23 22:13:59', $this->UTC); $binaryData = chr(Identifier::GENERALIZED_TIME); $binaryData .= chr(19); $binaryData .= '20120924021359+0400'; $parsedObject = GeneralizedTime::fromBinary($binaryData); $this->assertEquals($dateTime, $parsedObject->getContent()); } /** * @depends testFromBinaryWithDEREncodingAndFractionalSecondsPart */ public function testFromBinaryWithBEREncodingWithFractionalSecondsPartAndOtherTimeZone() { $dateTime = new DateTime('2012-09-23 22:13:59.525', $this->UTC); $binaryData = chr(Identifier::GENERALIZED_TIME); $binaryData .= chr(23); $binaryData .= '20120923161359.525-0600'; $parsedObject = GeneralizedTime::fromBinary($binaryData); $this->assertEquals($dateTime, $parsedObject->getContent()); } } tests/ASN1/Universal/GraphicStringTest.php000064400000005351150736747750014526 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\GraphicString; class GraphicStringTest extends ASN1TestCase { public function testGetType() { $object = new GraphicString('Hello World'); $this->assertEquals(Identifier::GRAPHIC_STRING, $object->getType()); } public function testGetIdentifier() { $object = new GraphicString('Hello World'); $this->assertEquals(chr(Identifier::GRAPHIC_STRING), $object->getIdentifier()); } public function testContent() { $object = new GraphicString('Hello World'); $this->assertEquals('Hello World', $object->getContent()); $object = new GraphicString(''); $this->assertEquals('', $object->getContent()); $object = new GraphicString(' '); $this->assertEquals(' ', $object->getContent()); } public function testGetObjectLength() { $string = 'Hello World'; $object = new GraphicString($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = 'Hello World'; $expectedType = chr(Identifier::GRAPHIC_STRING); $expectedLength = chr(strlen($string)); $object = new GraphicString($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new GraphicString('Hello World'); $binaryData = $originalObject->getBinary(); $parsedObject = GraphicString::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new GraphicString('Hello '); $originalObject2 = new GraphicString(' World'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = GraphicString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = GraphicString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } } tests/ASN1/Universal/IA5StringTest.php000064400000005241150736747750013525 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\IA5String; class IA5StringTest extends ASN1TestCase { public function testGetType() { $object = new IA5String('Hello World'); $this->assertEquals(Identifier::IA5_STRING, $object->getType()); } public function testGetIdentifier() { $object = new IA5String('Hello World'); $this->assertEquals(chr(Identifier::IA5_STRING), $object->getIdentifier()); } public function testContent() { $object = new IA5String('Hello World'); $this->assertEquals('Hello World', $object->getContent()); $object = new IA5String(''); $this->assertEquals('', $object->getContent()); $object = new IA5String(' '); $this->assertEquals(' ', $object->getContent()); } public function testGetObjectLength() { $string = 'Hello World'; $object = new IA5String($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = 'Hello World'; $expectedType = chr(Identifier::IA5_STRING); $expectedLength = chr(strlen($string)); $object = new IA5String($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new IA5String('Hello World'); $binaryData = $originalObject->getBinary(); $parsedObject = IA5String::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new IA5String('Hello '); $originalObject2 = new IA5String(' World'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = IA5String::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = IA5String::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } } tests/ASN1/Universal/IntegerTest.php000064400000022313150736747750013354 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\ASN1\ASNObject; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\Integer; class IntegerTest extends ASN1TestCase { public function testGetType() { $object = new Integer(123); $this->assertEquals(Identifier::INTEGER, $object->getType()); } public function testGetIdentifier() { $object = new Integer(123); $this->assertEquals(chr(Identifier::INTEGER), $object->getIdentifier()); } /** * @expectedException \Exception */ public function testCreateInstanceCanFail() { new Integer('a'); } public function testContent() { $object = new Integer(1234); $this->assertEquals(1234, $object->getContent()); $object = new Integer(-1234); $this->assertEquals(-1234, $object->getContent()); $object = new Integer(0); $this->assertEquals(0, $object->getContent()); // test with maximum integer value $object = new Integer(PHP_INT_MAX); $this->assertEquals(PHP_INT_MAX, $object->getContent()); // test with minimum integer value by negating the max value $object = new Integer(~PHP_INT_MAX); $this->assertEquals(~PHP_INT_MAX, $object->getContent()); } public function testGetObjectLength() { $positiveObj = new Integer(0); $expectedSize = 2 + 1; $this->assertEquals($expectedSize, $positiveObj->getObjectLength()); $positiveObj = new Integer(127); $negativeObj = new Integer(-127); $expectedSize = 2 + 1; $this->assertEquals($expectedSize, $positiveObj->getObjectLength()); $this->assertEquals($expectedSize, $negativeObj->getObjectLength()); $positiveObj = new Integer(128); $negativeObj = new Integer(-128); $expectedSize = 2 + 2; $this->assertEquals($expectedSize, $positiveObj->getObjectLength()); $this->assertEquals($expectedSize, $negativeObj->getObjectLength()); $positiveObj = new Integer(0x7FFF); $negativeObj = new Integer(-0x7FFF); $expectedSize = 2 + 2; $this->assertEquals($expectedSize, $positiveObj->getObjectLength()); $this->assertEquals($expectedSize, $negativeObj->getObjectLength()); $positiveObj = new Integer(0x8000); $negativeObj = new Integer(-0x8000); $expectedSize = 2 + 3; $this->assertEquals($expectedSize, $positiveObj->getObjectLength()); $this->assertEquals($expectedSize, $negativeObj->getObjectLength()); $positiveObj = new Integer(0x7FFFFF); $negativeObj = new Integer(-0x7FFFFF); $expectedSize = 2 + 3; $this->assertEquals($expectedSize, $positiveObj->getObjectLength()); $this->assertEquals($expectedSize, $negativeObj->getObjectLength()); $positiveObj = new Integer(0x800000); $negativeObj = new Integer(-0x800000); $expectedSize = 2 + 4; $this->assertEquals($expectedSize, $positiveObj->getObjectLength()); $this->assertEquals($expectedSize, $negativeObj->getObjectLength()); $positiveObj = new Integer(0x7FFFFFFF); $negativeObj = new Integer(-0x7FFFFFFF); $expectedSize = 2 + 4; $this->assertEquals($expectedSize, $positiveObj->getObjectLength()); $this->assertEquals($expectedSize, $negativeObj->getObjectLength()); } public function testGetBinary() { $expectedType = chr(Identifier::INTEGER); $expectedLength = chr(0x01); $object = new Integer(0); $expectedContent = chr(0x00); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new Integer(127); $expectedContent = chr(0x7F); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new Integer(-127); $expectedContent = chr(0x81); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new Integer(200); $expectedLength = chr(0x02); $expectedContent = chr(0x00); $expectedContent .= chr(0xC8); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new Integer(-546); $expectedLength = chr(0x02); $expectedContent = chr(0xFD); $expectedContent .= chr(0xDE); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new Integer(7420); $expectedLength = chr(0x02); $expectedContent = chr(0x1C); $expectedContent .= chr(0xFC); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new Integer(-1891004); $expectedLength = chr(0x03); $expectedContent = chr(0xE3); $expectedContent .= chr(0x25); $expectedContent .= chr(0x44); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } public function testBigIntegerSupport() { // Positive bigint $expectedType = chr(Identifier::INTEGER); $expectedLength = chr(0x20); $expectedContent = "\x7f\xff\xff\xff\xff\xff\xff\xff"; $expectedContent .= "\xff\xff\xff\xff\xff\xff\xff\xff"; $expectedContent .= "\xff\xff\xff\xff\xff\xff\xff\xff"; $expectedContent .= "\xff\xff\xff\xff\xff\xff\xff\xff"; $bigint = gmp_strval(gmp_sub(gmp_pow(2, 255), 1)); $object = new Integer($bigint); $binary = $object->getBinary(); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $binary); $obj = ASNObject::fromBinary($binary); $this->assertEquals($obj, $object); // Test a negative number $expectedLength = chr(0x21); $expectedContent = "\x00\x80\x00\x00\x00\x00\x00\x00\x00"; $expectedContent .= "\x00\x00\x00\x00\x00\x00\x00\x00"; $expectedContent .= "\x00\x00\x00\x00\x00\x00\x00\x00"; $expectedContent .= "\x00\x00\x00\x00\x00\x00\x00\x00"; $bigint = gmp_strval(gmp_pow(2, 255)); $object = new Integer($bigint); $binary = $object->getBinary(); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $binary); $obj = ASNObject::fromBinary($binary); $this->assertEquals($object, $obj); } /** * @dataProvider bigIntegersProvider */ public function testSerializeBigIntegers($i) { $object = new Integer($i); $binary = $object->getBinary(); $obj = ASNObject::fromBinary($binary); $this->assertEquals($obj->getContent(), $object->getContent()); } public function bigIntegersProvider() { for ($i = 1; $i <= 256; $i *= 2) { // 2 ^ n [0, 256] large positive numbers yield [gmp_strval(gmp_pow(2, $i), 10)]; } for ($i = 1; $i <= 256; $i *= 2) { // 0 - 2 ^ n [0, 256] large negative numbers yield [gmp_strval(gmp_sub(0, gmp_pow(2, $i)), 10)]; } } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new Integer(200); $binaryData = $originalObject->getBinary(); $parsedObject = Integer::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); $originalObject = new Integer(12345); $binaryData = $originalObject->getBinary(); $parsedObject = Integer::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); $originalObject = new Integer(-1891004); $binaryData = $originalObject->getBinary(); $parsedObject = Integer::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new Integer(12345); $originalObject2 = new Integer(67890); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = Integer::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(4, $offset); $parsedObject = Integer::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(9, $offset); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 2: A FG\ASN1\Universal\Integer should have a content length of at least 1. Extracted length was 0 * @depends testFromBinary */ public function testFromBinaryWithInvalidLength01() { $binaryData = chr(Identifier::INTEGER); $binaryData .= chr(0x00); $binaryData .= chr(0xA0); Integer::fromBinary($binaryData); } } tests/ASN1/Universal/NullObjectTest.php000064400000005237150736747750014026 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\NullObject; class NullObjectTest extends ASN1TestCase { public function testGetType() { $object = new NullObject(); $this->assertEquals(Identifier::NULL, $object->getType()); } public function testGetIdentifier() { $object = new NullObject(); $this->assertEquals(chr(Identifier::NULL), $object->getIdentifier()); } public function testContent() { $object = new NullObject(); $this->assertEquals('NULL', $object->getContent()); } public function testGetObjectLength() { $object = new NullObject(); $this->assertEquals(2, $object->getObjectLength()); } public function testGetBinary() { $object = new NullObject(); $expectedType = chr(Identifier::NULL); $expectedLength = chr(0x00); $this->assertEquals($expectedType.$expectedLength, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new NullObject(); $binaryData = $originalObject->getBinary(); $parsedObject = NullObject::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new NullObject(); $originalObject2 = new NullObject(); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = NullObject::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(2, $offset); $parsedObject = NullObject::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(4, $offset); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 2: An ASN.1 Null should not have a length other than zero. Extracted length was 1 * @depends testFromBinary */ public function testFromBinaryWithInvalidLength01() { $binaryData = chr(Identifier::NULL); $binaryData .= chr(0x01); NullObject::fromBinary($binaryData); } } tests/ASN1/Universal/NumericStringTest.php000064400000011162150736747750014550 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\NumericString; class NumericStringTest extends ASN1TestCase { public function testGetType() { $object = new NumericString('1234'); $this->assertEquals(Identifier::NUMERIC_STRING, $object->getType()); } public function testGetIdentifier() { $object = new NumericString('1234'); $this->assertEquals(chr(Identifier::NUMERIC_STRING), $object->getIdentifier()); } public function testContent() { $object = new NumericString('123 45 67890'); $this->assertEquals('123 45 67890', $object->getContent()); $object = new NumericString(' '); $this->assertEquals(' ', $object->getContent()); } public function testGetObjectLength() { $string = '123 4 55677 0987'; $object = new NumericString($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = '123 4 55677 0987'; $expectedType = chr(Identifier::NUMERIC_STRING); $expectedLength = chr(strlen($string)); $object = new NumericString($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new NumericString('123 45 5322'); $binaryData = $originalObject->getBinary(); $parsedObject = NumericString::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new NumericString('1324 0'); $originalObject2 = new NumericString('1 2 3 '); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = NumericString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = NumericString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } public function testCreateStringWithValidCharacters() { $object = new NumericString('1234'); $this->assertEquals(pack('H*', '120431323334'), $object->getBinary()); $object = new NumericString('321 98 76'); $this->assertEquals(pack('H*', '1209333231203938203736'), $object->getBinary()); } public function testCreateStringWithInvalidCharacters() { $invalidString = 'Hello World'; try { $object = new NumericString($invalidString); $object->getBinary(); $this->fail('Should have thrown an exception'); } catch (\Exception $exception) { $this->assertEquals("Could not create a ASN.1 Numeric String from the character sequence '{$invalidString}'.", $exception->getMessage()); } $invalidString = '123,456'; try { $object = new NumericString($invalidString); $object->getBinary(); $this->fail('Should have thrown an exception'); } catch (\Exception $exception) { $this->assertEquals("Could not create a ASN.1 Numeric String from the character sequence '{$invalidString}'.", $exception->getMessage()); } $invalidString = '+123456'; try { $object = new NumericString($invalidString); $object->getBinary(); $this->fail('Should have thrown an exception'); } catch (\Exception $exception) { $this->assertEquals("Could not create a ASN.1 Numeric String from the character sequence '{$invalidString}'.", $exception->getMessage()); } $invalidString = '-123456'; try { $object = new NumericString($invalidString); $object->getBinary(); $this->fail('Should have thrown an exception'); } catch (\Exception $exception) { $this->assertEquals("Could not create a ASN.1 Numeric String from the character sequence '{$invalidString}'.", $exception->getMessage()); } } } tests/ASN1/Universal/ObjectDescriptorTest.php000064400000005502150736747750015225 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\ObjectDescriptor; class ObjectDescriptorTest extends ASN1TestCase { public function testGetType() { $object = new ObjectDescriptor('NumericString character abstract syntax'); $this->assertEquals(Identifier::OBJECT_DESCRIPTOR, $object->getType()); } public function testGetIdentifier() { $object = new ObjectDescriptor('NumericString character abstract syntax'); $this->assertEquals(chr(Identifier::OBJECT_DESCRIPTOR), $object->getIdentifier()); } public function testContent() { $object = new ObjectDescriptor('PrintableString character abstract syntax'); $this->assertEquals('PrintableString character abstract syntax', $object->getContent()); } public function testGetObjectLength() { $string = 'Basic Encoding of a single ASN.1 type'; $object = new ObjectDescriptor($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = 'Basic Encoding of a single ASN.1 type'; $expectedType = chr(Identifier::OBJECT_DESCRIPTOR); $expectedLength = chr(strlen($string)); $object = new ObjectDescriptor($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new ObjectDescriptor('PrintableString character abstract syntax'); $binaryData = $originalObject->getBinary(); $parsedObject = ObjectDescriptor::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new ObjectDescriptor('NumericString character abstract syntax'); $originalObject2 = new ObjectDescriptor('Basic Encoding of a single ASN.1 type'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = ObjectDescriptor::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(41, $offset); $parsedObject = ObjectDescriptor::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(80, $offset); } } tests/ASN1/Universal/ObjectIdentifierTest.php000064400000007630150736747750015175 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\ObjectIdentifier; class ObjectIdentifierTest extends ASN1TestCase { /** * @expectedException \Exception * @expectedExceptionMessage [1.Foo.3] is no valid object identifier (sub identifier 2 is not numeric)! */ public function testCreateWithInvalidObjectIdentifier() { new ObjectIdentifier('1.Foo.3'); } public function testGetType() { $object = new ObjectIdentifier('1.2.3'); $this->assertEquals(Identifier::OBJECT_IDENTIFIER, $object->getType()); } public function testGetIdentifier() { $object = new ObjectIdentifier('1.2.3'); $this->assertEquals(chr(Identifier::OBJECT_IDENTIFIER), $object->getIdentifier()); } public function testContent() { $object = new ObjectIdentifier('1.2.3'); $this->assertEquals('1.2.3', $object->getContent()); } public function testGetObjectLength() { $object = new ObjectIdentifier('1.2.3'); $this->assertEquals(4, $object->getObjectLength()); $object = new ObjectIdentifier('1.2.250.1.16.9'); $this->assertEquals(8, $object->getObjectLength()); } public function testGetBinary() { $object = new ObjectIdentifier('1.2.3'); $expectedType = chr(Identifier::OBJECT_IDENTIFIER); $expectedLength = chr(0x02); $expectedContent = chr(1 * 40 + 2); $expectedContent .= chr(3); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new ObjectIdentifier('1.2.250.1.16.9'); $expectedLength = chr(0x06); $expectedContent = chr(1 * 40 + 2); // 1.2 $expectedContent .= chr(128 | 1); // 250 $expectedContent .= chr(122); // $expectedContent .= chr(1); // 1 $expectedContent .= chr(16); // 16 $expectedContent .= chr(9); // 9 $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new ObjectIdentifier('1.2.250.1.16.9'); $binaryData = $originalObject->getBinary(); $parsedObject = ObjectIdentifier::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new ObjectIdentifier('1.2.3'); $originalObject2 = new ObjectIdentifier('1.2.250.1.16.9'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = ObjectIdentifier::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(4, $offset); $parsedObject = ObjectIdentifier::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(12, $offset); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 4: Malformed ASN.1 Object Identifier * @depends testFromBinary */ public function testFromBinaryWithMalformedOID() { $binaryData = chr(Identifier::OBJECT_IDENTIFIER); $binaryData .= chr(0x03); $binaryData .= chr(42); $binaryData .= chr(128 | 1); $binaryData .= chr(128 | 1); ObjectIdentifier::fromBinary($binaryData); } } tests/ASN1/Universal/OctetStringTest.php000064400000012041150736747750014221 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\OctetString; class OctetStringTest extends ASN1TestCase { public function testGetType() { $object = new OctetString('30 14 06 08 2B 06 01 05 05 07 03 01 06 08 2B 06 01 05 05 07 03 02'); $this->assertEquals(Identifier::OCTETSTRING, $object->getType()); } public function testGetIdentifier() { $object = new OctetString('30 14 06 08 2B 06 01 05 05 07 03 01 06 08 2B 06 01 05 05 07 03 02'); $this->assertEquals(chr(Identifier::OCTETSTRING), $object->getIdentifier()); } public function testContent() { $object = new OctetString('A01200C3'); $this->assertEquals('A01200C3', $object->getContent()); $object = new OctetString('A0 12 00 C3'); $this->assertEquals('A01200C3', $object->getContent()); $object = new OctetString('a0 12 00 c3'); $this->assertEquals('A01200C3', $object->getContent()); $object = new OctetString('A0 12 00 c3'); $this->assertEquals('A01200C3', $object->getContent()); $object = new OctetString(0xA01200C3); $this->assertEquals('A01200C3', $object->getContent()); } public function testGetObjectLength() { $object = new OctetString(0x00); $this->assertEquals(3, $object->getObjectLength()); $object = new OctetString(0xFF); $this->assertEquals(3, $object->getObjectLength()); $object = new OctetString(0xA000); $this->assertEquals(4, $object->getObjectLength()); $object = new OctetString(0x3F2001); $this->assertEquals(5, $object->getObjectLength()); } public function testGetObjectLengthWithVeryLongOctetString() { $hexString = '0x'.str_repeat('FF', 1024); $object = new OctetString($hexString); $this->assertEquals(1 + 3 + 1024, $object->getObjectLength()); } public function testGetBinary() { $expectedType = chr(Identifier::OCTETSTRING); $expectedLength = chr(0x01); $object = new OctetString(0xFF); $expectedContent = chr(0xFF); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new OctetString(0xFFA034); $expectedLength = chr(0x03); $expectedContent = chr(0xFF); $expectedContent .= chr(0xA0); $expectedContent .= chr(0x34); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $object = new OctetString(null); $expectedLength = chr(0x00); $this->assertEquals($expectedType.$expectedLength, $object->getBinary()); } public function testGetBinaryForLargeOctetStrings() { $nrOfBytes = 1024; $hexString = '0x'.str_repeat('FF', $nrOfBytes); $object = new OctetString($hexString); $expectedType = chr(Identifier::OCTETSTRING); $expectedLength = chr(0x80 | 0x02); // long length form: 2 length octets $expectedLength .= chr(1024 >> 8); // first 8 bit of 1025 $expectedLength .= chr(1024 & 0xFF); // last 8 bit of 1025 $expectedContent = ''; for ($i = 0; $i < $nrOfBytes; $i++) { $expectedContent .= chr(0xFF); // content } $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new OctetString(0x12); $binaryData = $originalObject->getBinary(); $parsedObject = OctetString::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); $originalObject = new OctetString(0x010203A0); $binaryData = $originalObject->getBinary(); $parsedObject = OctetString::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new OctetString(0xA0); $originalObject2 = new OctetString(0x314510); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = OctetString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(3, $offset); $parsedObject = OctetString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(8, $offset); } public function testEmptyValue() { $object = new OctetString(null); $this->assertEmpty($object->getContent()); $this->assertEquals(2, $object->getObjectLength()); } } tests/ASN1/Universal/PrintableStringTest.php000064400000010337150736747750015071 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\PrintableString; class PrintableStringTest extends ASN1TestCase { public function testGetType() { $object = new PrintableString('Hello World'); $this->assertEquals(Identifier::PRINTABLE_STRING, $object->getType()); } public function testGetIdentifier() { $object = new PrintableString('Hello World'); $this->assertEquals(chr(Identifier::PRINTABLE_STRING), $object->getIdentifier()); } public function testContent() { $object = new PrintableString('Hello World'); $this->assertEquals('Hello World', $object->getContent()); $object = new PrintableString(''); $this->assertEquals('', $object->getContent()); $object = new PrintableString(' '); $this->assertEquals(' ', $object->getContent()); } public function testGetObjectLength() { $string = 'Hello World'; $object = new PrintableString($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = 'Hello World'; $expectedType = chr(Identifier::PRINTABLE_STRING); $expectedLength = chr(strlen($string)); $object = new PrintableString($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new PrintableString('Hello World'); $binaryData = $originalObject->getBinary(); $parsedObject = PrintableString::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new PrintableString('Hello '); $originalObject2 = new PrintableString(' World'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = PrintableString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = PrintableString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } public function testCreateStringWithValidCharacters() { $object = new PrintableString('Hello World'); $this->assertEquals(pack('H*', '130b48656c6c6f20576f726c64'), $object->getBinary()); $object = new PrintableString('Hello, World?'); $this->assertEquals(pack('H*', '130d48656c6c6f2c20576f726c643f'), $object->getBinary()); $object = new PrintableString("(Hello) 0001100 'World'?"); $this->assertEquals(pack('H*', '13182848656c6c6f2920303030313130302027576f726c64273f'), $object->getBinary()); $object = new PrintableString('Hello := World'); $this->assertEquals(pack('H*', '130e48656c6c6f203a3d20576f726c64'), $object->getBinary()); } public function testCreateStringWithInvalidCharacters() { $invalidString = 'Hello ♥♥♥ World'; try { $object = new PrintableString($invalidString); $object->getBinary(); $this->fail('Should have thrown an exception'); } catch (\Exception $exception) { $this->assertEquals("Could not create a ASN.1 Printable String from the character sequence '{$invalidString}'.", $exception->getMessage()); } } public function testIsPrintableString() { $validString = 'Hello World'; $this->assertTrue(PrintableString::isValid($validString)); $invalidString = 'Hello ♥♥♥ World'; $this->assertFalse(PrintableString::isValid($invalidString)); } } tests/ASN1/Universal/RelativeObjectIdentifierTest.php000064400000006525150736747750016673 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\RelativeObjectIdentifier; class RelativeObjectIdentifierTest extends ASN1TestCase { public function testGetType() { $object = new RelativeObjectIdentifier('8751.3.2'); $this->assertEquals(Identifier::RELATIVE_OID, $object->getType()); } public function testGetIdentifier() { $object = new RelativeObjectIdentifier('8751.3.2'); $this->assertEquals(chr(Identifier::RELATIVE_OID), $object->getIdentifier()); } public function testContent() { $object = new RelativeObjectIdentifier('8751.3.2'); $this->assertEquals('8751.3.2', $object->getContent()); } public function testGetObjectLength() { $object = new RelativeObjectIdentifier('1.2.3'); $this->assertEquals(2 + 3, $object->getObjectLength()); $object = new RelativeObjectIdentifier('1.2.250.1.16.9'); $this->assertEquals(2 + 7, $object->getObjectLength()); } public function testGetBinary() { $object = new RelativeObjectIdentifier('8571.3.2'); $expectedType = chr(Identifier::RELATIVE_OID); $expectedLength = chr(0x04); $expectedContent = chr(0xC2); $expectedContent .= chr(0x7B); $expectedContent .= chr(0x03); $expectedContent .= chr(0x02); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new RelativeObjectIdentifier('8571.3.2'); $binaryData = $originalObject->getBinary(); $parsedObject = RelativeObjectIdentifier::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new RelativeObjectIdentifier('8571.3.2'); $originalObject2 = new RelativeObjectIdentifier('45.2.3455.1'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = RelativeObjectIdentifier::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(6, $offset); $parsedObject = RelativeObjectIdentifier::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(13, $offset); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 4: Malformed ASN.1 Relative Object Identifier * @depends testFromBinary */ public function testFromBinaryWithMalformedOID() { $binaryData = chr(Identifier::RELATIVE_OID); $binaryData .= chr(0x03); $binaryData .= chr(42); $binaryData .= chr(128 | 1); $binaryData .= chr(128 | 1); RelativeObjectIdentifier::fromBinary($binaryData); } } tests/ASN1/Universal/SequenceTest.php000064400000011657150736747750013540 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\Sequence; use FG\ASN1\Universal\Integer; use FG\ASN1\Universal\PrintableString; use FG\ASN1\Universal\Boolean; class SequenceTest extends ASN1TestCase { public function testGetType() { $object = new Sequence(); $this->assertEquals(Identifier::SEQUENCE, $object->getType()); } public function testGetIdentifier() { $object = new Sequence(); $this->assertEquals(chr(Identifier::SEQUENCE), $object->getIdentifier()); } public function testContent() { $child1 = new Integer(123); $child2 = new PrintableString('Hello Wold'); $child3 = new Boolean(true); $object = new Sequence($child1, $child2, $child3); $this->assertEquals([$child1, $child2, $child3], $object->getContent()); } public function testGetObjectLength() { $child1 = new Boolean(true); $object = new Sequence($child1); $this->assertEquals(5, $object->getObjectLength()); $child1 = new Integer(123); $child2 = new Boolean(true); $object = new Sequence($child1, $child2); $this->assertEquals(8, $object->getObjectLength()); $child1 = new Integer(123); $child2 = new PrintableString('Hello Wold'); $child3 = new Boolean(true); $object = new Sequence($child1, $child2, $child3); $this->assertEquals(20, $object->getObjectLength()); } public function testGetBinary() { $child1 = new Boolean(true); $object = new Sequence($child1); $expectedType = chr(Identifier::SEQUENCE); $expectedLength = chr(0x03); $expectedContent = $child1->getBinary(); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $child1 = new Integer(123); $child2 = new Boolean(true); $object = new Sequence($child1, $child2); $expectedLength = chr(0x06); $expectedContent = $child1->getBinary(); $expectedContent .= $child2->getBinary(); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new Sequence( new Boolean(true), new Integer(1234567) ); $binaryData = $originalObject->getBinary(); $parsedObject = Sequence::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new Sequence( new Boolean(true), new Integer(123) ); $originalObject2 = new Sequence( new Integer(64), new Boolean(false) ); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = Sequence::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = Sequence::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } public function testSequenceAsArray() { $sequence = new Sequence(); $child1 = new Integer(123); $child2 = new PrintableString('Hello Wold'); $child3 = new Boolean(true); $child4 = new Integer(1234567); $sequence[] = $child1; $sequence[] = $child2; $sequence['foo'] = $child3; $this->assertEquals($child1, $sequence[0]); $this->assertEquals($child2, $sequence[1]); $this->assertEquals($child3, $sequence['foo']); $this->assertEquals(3, count($sequence)); unset($sequence[1]); $sequence['bar'] = $child4; $this->assertEquals($child1, $sequence[0]); $this->assertFalse(isset($sequence[1])); $this->assertEquals($child3, $sequence['foo']); $this->assertEquals($child4, $sequence['bar']); $this->assertEquals(3, count($sequence)); } public function testIterateSequence() { $sequence = new Sequence( new Integer(1), new Integer(2), new Integer(3) ); foreach ($sequence as $object) { $this->assertInstanceOf(Integer::class, $object); } } public function testCreateEmptySequence() { $sequence = new Sequence(); // this should be legal $this->assertEmpty($sequence); } } tests/ASN1/Universal/SetTest.php000064400000010541150736747750012512 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\Set; use FG\ASN1\Universal\Integer; use FG\ASN1\Universal\PrintableString; use FG\ASN1\Universal\Boolean; class SetTest extends ASN1TestCase { public function testGetType() { $object = new Set(); $this->assertEquals(Identifier::SET, $object->getType()); } public function testGetIdentifier() { $object = new Set(); $this->assertEquals(chr(Identifier::SET), $object->getIdentifier()); } public function testContent() { $child1 = new Integer(123); $child2 = new PrintableString('Hello Wold'); $child3 = new Boolean(true); $object = new Set($child1, $child2, $child3); $this->assertEquals([$child1, $child2, $child3], $object->getContent()); } public function testGetObjectLength() { $child1 = new Boolean(true); $object = new Set($child1); $this->assertEquals(5, $object->getObjectLength()); $child1 = new Integer(123); $child2 = new Boolean(true); $object = new Set($child1, $child2); $this->assertEquals(8, $object->getObjectLength()); $child1 = new Integer(123); $child2 = new PrintableString('Hello Wold'); $child3 = new Boolean(true); $object = new Set($child1, $child2, $child3); $this->assertEquals(20, $object->getObjectLength()); } public function testGetBinary() { $child1 = new Boolean(true); $object = new Set($child1); $expectedType = chr(Identifier::SET); $expectedLength = chr(0x03); $expectedContent = $child1->getBinary(); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $child1 = new Integer(123); $child2 = new Boolean(true); $object = new Set($child1, $child2); $expectedLength = chr(0x06); $expectedContent = $child1->getBinary(); $expectedContent .= $child2->getBinary(); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new Set( new Boolean(true), new Integer(1234567) ); $binaryData = $originalObject->getBinary(); $parsedObject = Set::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /* * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new Set( new Boolean(true), new Integer(123) ); $originalObject2 = new Set( new Integer(64), new Boolean(false) ); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = Set::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = Set::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } public function testSetAsArray() { $object = new Set(); $child1 = new Integer(123); $child2 = new PrintableString('Hello Wold'); $child3 = new Boolean(true); $child4 = new Integer(1234567); $object[] = $child1; $object[] = $child2; $object['foo'] = $child3; $this->assertEquals($child1, $object[0]); $this->assertEquals($child2, $object[1]); $this->assertEquals($child3, $object['foo']); $this->assertEquals(3, count($object)); unset($object[1]); $object['bar'] = $child4; $this->assertEquals($child1, $object[0]); $this->assertFalse(isset($object[1])); $this->assertEquals($child3, $object['foo']); $this->assertEquals($child4, $object['bar']); $this->assertEquals(3, count($object)); } } tests/ASN1/Universal/T61StringTest.php000064400000004713150736747750013524 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\T61String; class T61StringTest extends ASN1TestCase { public function testGetType() { $object = new T61String('Hello World'); $this->assertEquals(Identifier::T61_STRING, $object->getType()); } public function testGetIdentifier() { $object = new T61String('Hello World'); $this->assertEquals(chr(Identifier::T61_STRING), $object->getIdentifier()); } public function testContent() { $object = new T61String('Hello World'); $this->assertEquals('Hello World', $object->getContent()); } public function testGetObjectLength() { $string = 'Hello World'; $object = new T61String($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = 'Hello World'; $expectedType = chr(Identifier::T61_STRING); $expectedLength = chr(strlen($string)); $object = new T61String($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new T61String('Hello World'); $binaryData = $originalObject->getBinary(); $parsedObject = T61String::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new T61String('Hello '); $originalObject2 = new T61String(' World'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = T61String::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = T61String::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } } tests/ASN1/Universal/UTCTimeTest.php000064400000016044150736747750013235 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\UTCTime; class UTCTimeTest extends ASN1TestCase { private $UTC; public function setUp() { $this->UTC = new \DateTimeZone('UTC'); } public function testGetType() { $object = new UTCTime(); $this->assertEquals(Identifier::UTC_TIME, $object->getType()); } public function testGetIdentifier() { $object = new UTCTime(); $this->assertEquals(chr(Identifier::UTC_TIME), $object->getIdentifier()); } public function testGetContent() { $now = new \DateTime(); $now->setTimezone($this->UTC); $object = new UTCTime(); $content = $object->getContent(); $this->assertTrue($content instanceof \DateTime); $this->assertEquals($now->format(DATE_RFC3339), $content->format(DATE_RFC3339)); $timeString = '2012-09-23 20:27'; $dateTime = new \DateTime($timeString, $this->UTC); $object = new UTCTime($timeString); $content = $object->getContent(); $this->assertTrue($content instanceof \DateTime); $this->assertEquals($dateTime->format(DATE_RFC3339), $content->format(DATE_RFC3339)); } public function testGetObjectLength() { $object = new UTCTime(); $expectedSize = 2 + 13; // Identifier + length + YYMMDDHHmmssZ $this->assertEquals($expectedSize, $object->getObjectLength()); $object = new UTCTime('2012-09-23'); $this->assertEquals($expectedSize, $object->getObjectLength()); $object = new UTCTime('1987-01-15 12:12:16'); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $expectedType = chr(Identifier::UTC_TIME); $expectedLength = chr(13); $object = new UTCTime(); $now = new \DateTime(); $now->setTimezone($this->UTC); $expectedContent = $now->format('ymdHis').'Z'; $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $dateString = '2012-09-23'; $object = new UTCTime($dateString); $date = new \DateTime($dateString, $this->UTC); $expectedContent = $date->format('ymdHis').'Z'; $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); $dateString = '1987-01-15 12:12'; $object = new UTCTime($dateString); $date = new \DateTime($dateString, $this->UTC); $expectedContent = $date->format('ymdHis').'Z'; $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinaryWithDEREncoding() { $dateTime = new \DateTime('2012-09-23 20:23:16', $this->UTC); $binaryData = chr(Identifier::UTC_TIME); $binaryData .= chr(13); $binaryData .= '120923202316Z'; $parsedObject = UTCTime::fromBinary($binaryData); $this->assertEquals($dateTime, $parsedObject->getContent()); } /** * @depends testGetBinary */ public function testFromBinaryWithBEREncodingWithoutSecondsInUTC() { $dateTime = new \DateTime('1987-01-15 13:15:00', $this->UTC); $binaryData = chr(Identifier::UTC_TIME); $binaryData .= chr(13); $binaryData .= '8701151315Z'; $parsedObject = UTCTime::fromBinary($binaryData); $this->assertEquals($dateTime, $parsedObject->getContent()); } /** * @depends testGetBinary */ public function testFromBinaryWithBEREncodingWithoutSecondsInOtherTimeZone() { $dateTime = new \DateTime('2012-09-23 22:13:00', $this->UTC); $binaryData = chr(Identifier::UTC_TIME); $binaryData .= chr(13); $binaryData .= '1209231613-0600'; $parsedObject = UTCTime::fromBinary($binaryData); $this->assertEquals($dateTime, $parsedObject->getContent()); $dateTime = new \DateTime('2012-09-23 22:13:00', $this->UTC); $binaryData = chr(Identifier::UTC_TIME); $binaryData .= chr(13); $binaryData .= '1209240213+0400'; $parsedObject = UTCTime::fromBinary($binaryData); $this->assertEquals($dateTime, $parsedObject->getContent()); } /** * @depends testGetBinary */ public function testFromBinaryWithBEREncodingWithSecondsInOtherTimeZone() { $dateTime = new \DateTime('2012-09-23 22:13:32', $this->UTC); $binaryData = chr(Identifier::UTC_TIME); $binaryData .= chr(13); $binaryData .= '120923161332-0600'; $parsedObject = UTCTime::fromBinary($binaryData); $this->assertEquals($dateTime, $parsedObject->getContent()); $dateTime = new \DateTime('2012-09-23 22:13:32', $this->UTC); $binaryData = chr(Identifier::UTC_TIME); $binaryData .= chr(13); $binaryData .= '120924021332+0400'; $parsedObject = UTCTime::fromBinary($binaryData); $this->assertEquals($dateTime, $parsedObject->getContent()); } /** * @depends testFromBinaryWithDEREncoding * @depends testFromBinaryWithBEREncodingWithoutSecondsInUTC * @depends testFromBinaryWithBEREncodingWithoutSecondsInOtherTimeZone * @depends testFromBinaryWithBEREncodingWithSecondsInOtherTimeZone */ public function testFromBinaryWithOffset() { $binaryData = chr(Identifier::UTC_TIME); $binaryData .= chr(11); $binaryData .= '1209231613Z'; $dateTime1 = new \DateTime('2012-09-23 16:13:00', $this->UTC); $binaryData .= chr(Identifier::UTC_TIME); $binaryData .= chr(13); $binaryData .= '120923180030Z'; $dateTime2 = new \DateTime('2012-09-23 18:00:30', $this->UTC); $binaryData .= chr(Identifier::UTC_TIME); $binaryData .= chr(17); $binaryData .= '120924021332+0400'; $dateTime3 = new \DateTime('2012-09-23 22:13:32', $this->UTC); $offset = 0; $parsedObject = UTCTime::fromBinary($binaryData, $offset); $this->assertEquals($dateTime1, $parsedObject->getContent()); $this->assertEquals(13, $offset); $parsedObject = UTCTime::fromBinary($binaryData, $offset); $this->assertEquals($dateTime2, $parsedObject->getContent()); $this->assertEquals(28, $offset); $parsedObject = UTCTime::fromBinary($binaryData, $offset); $this->assertEquals($dateTime3, $parsedObject->getContent()); $this->assertEquals(47, $offset); } public function testToString() { $object = new UTCTime('2012-09-23'); $this->assertEquals("2012-09-23\t00:00:00", $object->__toString()); $object = new UTCTime('2012-09-23 22:13:43'); $this->assertEquals("2012-09-23\t22:13:43", $object->__toString()); } } tests/ASN1/Universal/UTF8StringTest.php000064400000004733150736747750013702 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\UTF8String; class UTF8StringTest extends ASN1TestCase { public function testGetType() { $object = new UTF8String('Hello World'); $this->assertEquals(Identifier::UTF8_STRING, $object->getType()); } public function testGetIdentifier() { $object = new UTF8String('Hello World'); $this->assertEquals(chr(Identifier::UTF8_STRING), $object->getIdentifier()); } public function testContent() { $object = new UTF8String('Hello World'); $this->assertEquals('Hello World', $object->getContent()); } public function testGetObjectLength() { $string = 'Hello World'; $object = new UTF8String($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = 'Hello World'; $expectedType = chr(Identifier::UTF8_STRING); $expectedLength = chr(strlen($string)); $object = new UTF8String($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new UTF8String('Hello World'); $binaryData = $originalObject->getBinary(); $parsedObject = UTF8String::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new UTF8String('Hello '); $originalObject2 = new UTF8String(' World'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = UTF8String::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = UTF8String::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } } tests/ASN1/Universal/UniversalStringTest.php000064400000005053150736747750015120 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\UniversalString; class UniversalStringTest extends ASN1TestCase { public function testGetType() { $object = new UniversalString('Hello World'); $this->assertEquals(Identifier::UNIVERSAL_STRING, $object->getType()); } public function testGetIdentifier() { $object = new UniversalString('Hello World'); $this->assertEquals(chr(Identifier::UNIVERSAL_STRING), $object->getIdentifier()); } public function testContent() { $object = new UniversalString('Hello World'); $this->assertEquals('Hello World', $object->getContent()); } public function testGetObjectLength() { $string = 'Hello World'; $object = new UniversalString($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = 'Hello World'; $expectedType = chr(Identifier::UNIVERSAL_STRING); $expectedLength = chr(strlen($string)); $object = new UniversalString($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new UniversalString('Hello World'); $binaryData = $originalObject->getBinary(); $parsedObject = UniversalString::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new UniversalString('Hello '); $originalObject2 = new UniversalString(' World'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = UniversalString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = UniversalString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } } tests/ASN1/Universal/VisibleStringTest.php000064400000005351150736747750014546 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\ASN1\Universal; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\ASN1\Universal\VisibleString; class VisibleStringTest extends ASN1TestCase { public function testGetType() { $object = new VisibleString('Hello World'); $this->assertEquals(Identifier::VISIBLE_STRING, $object->getType()); } public function testGetIdentifier() { $object = new VisibleString('Hello World'); $this->assertEquals(chr(Identifier::VISIBLE_STRING), $object->getIdentifier()); } public function testContent() { $object = new VisibleString('Hello World'); $this->assertEquals('Hello World', $object->getContent()); $object = new VisibleString(''); $this->assertEquals('', $object->getContent()); $object = new VisibleString(' '); $this->assertEquals(' ', $object->getContent()); } public function testGetObjectLength() { $string = 'Hello World'; $object = new VisibleString($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = 'Hello World'; $expectedType = chr(Identifier::VISIBLE_STRING); $expectedLength = chr(strlen($string)); $object = new VisibleString($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new VisibleString('Hello World'); $binaryData = $originalObject->getBinary(); $parsedObject = VisibleString::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new VisibleString('Hello '); $originalObject2 = new VisibleString(' World'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = VisibleString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(8, $offset); $parsedObject = VisibleString::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(16, $offset); } } tests/ASN1TestCase.php000064400000002741150736747750010606 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test; use PHPUnit\Framework\TestCase; abstract class ASN1TestCase extends TestCase { /** * Call a method on an object regardless of the visibility. * * This is useful if you want to test a protected or private method. * Note: This does not support passing parameters by reference. * * @param object $object The object on which the method will be called * @param string $methodName The name of the method * @param string $arguments optional arguments of the called method. Multiple arguments can be passed as variable-length argument list. * * @return mixed */ protected function callMethod($object, $methodName, $arguments = null) { $className = get_class($object); $class = new \ReflectionClass($className); $method = $class->getMethod($methodName); $method->setAccessible(true); $arguments = func_get_args(); array_shift($arguments); array_shift($arguments); return $method->invokeArgs($object, $arguments); } protected function assertBinaryEquals($expected, $actual) { // TODO add logic to make the error output readable $this->assertEquals($expected, $actual); } } tests/DocumentationExamplesTest.php000064400000003637150736747750013625 0ustar00expectOutputString('MBgCAwHiQAEB/woBARYLSGVsbG8gd29ybGQxODAYAgMB4kABAf8KAQEWC0hlbGxvIHdvcmxkBQAGBiqBegEQCQYJKoZIhvcNAQEBEwdGb28gYmFy'); $integer = new Integer(123456); $boolean = new Boolean(true); $enum = new Enumerated(1); $ia5String = new IA5String('Hello world'); $asnNull = new NullObject(); $objectIdentifier1 = new ObjectIdentifier('1.2.250.1.16.9'); $objectIdentifier2 = new ObjectIdentifier(OID::RSA_ENCRYPTION); $printableString = new PrintableString('Foo bar'); $sequence = new Sequence($integer, $boolean, $enum, $ia5String); $set = new Set($sequence, $asnNull, $objectIdentifier1, $objectIdentifier2, $printableString); $myBinary = $sequence->getBinary(); $myBinary .= $set->getBinary(); echo base64_encode($myBinary); } public function testREADME_decoding() { $base64String = 'MBgCAwHiQAEB/woBARYLSGVsbG8gd29ybGQxODAYAgMB4kABAf8KAQEWC0hlbGxvIHdvcmxkBQAGBiqBegEQCQYJKoZIhvcNAQEBEwdGb28gYmFy'; $binaryData = base64_decode($base64String); $asnObject = ASNObject::fromBinary($binaryData); $this->assertInstanceOf(Sequence::class, $asnObject); } } tests/X509/CSR/AttributesTest.php000064400000005272150736747750012534 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\X509\CSR; use FG\Test\ASN1TestCase; use FG\ASN1\OID; use FG\ASN1\Universal\Sequence; use FG\ASN1\Universal\ObjectIdentifier; use FG\X509\CSR\Attributes; use FG\X509\CertificateExtensions; use FG\X509\SAN\DNSName; use FG\X509\SAN\SubjectAlternativeNames; class AttributesTest extends ASN1TestCase { public function testGetType() { $object = new Attributes(); $this->assertEquals(0xa0, $object->getType()); // Identifier indicates this object is context specific and constructed } public function testGetIdentifier() { $object = new Attributes(); $this->assertEquals(chr(0xa0), $object->getIdentifier()); } public function testGetContent() { $attributes = new Attributes(); $content = $attributes->getContent(); $this->assertTrue(is_array($content)); $this->assertEquals(0, count($content)); $sans = new SubjectAlternativeNames(); $sans->addDomainName(new DNSName('corvespace.de')); $extensionRequest = new CertificateExtensions(); $extensionRequest->addSubjectAlternativeNames($sans); $attributes->addAttribute(OID::PKCS9_EXTENSION_REQUEST, $extensionRequest); $content = $attributes->getContent(); $this->assertTrue(is_array($content)); $this->assertEquals(1, count($content)); /** @var Sequence $attribute */ $attribute = $content[0]; $this->assertTrue($attribute instanceof Sequence); $this->assertEquals(2, $attribute->getNumberofChildren()); $attributeArray = $attribute->getChildren(); $objectIdentifier = $attributeArray[0]; $this->assertTrue($objectIdentifier instanceof ObjectIdentifier); $this->assertEquals(OID::PKCS9_EXTENSION_REQUEST, $objectIdentifier->getContent()); $this->assertEquals($extensionRequest, $attributeArray[1]); } public function testFromBinary() { $originalObject = new Attributes(); $sans = new SubjectAlternativeNames(); $sans->addDomainName(new DNSName('corvespace.de')); $extensionRequest = new CertificateExtensions(); $extensionRequest->addSubjectAlternativeNames($sans); $originalObject->addAttribute(OID::PKCS9_EXTENSION_REQUEST, $extensionRequest); $binaryData = $originalObject->getBinary(); $parsedObject = Attributes::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } } tests/X509/CertificateExtensionTest.php000064400000012017150736747750014071 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\X509; use FG\Test\ASN1TestCase; use FG\ASN1\OID; use FG\ASN1\Identifier; use FG\ASN1\Universal\ObjectIdentifier; use FG\X509\CertificateExtensions; use FG\X509\SAN\DNSName; use FG\X509\SAN\IPAddress; use FG\X509\SAN\SubjectAlternativeNames; class CertificateExtensionTest extends ASN1TestCase { public function testGetType() { $object = new CertificateExtensions(); $this->assertEquals(Identifier::SET, $object->getType()); } public function testGetIdentifier() { $object = new CertificateExtensions(); $this->assertEquals(chr(Identifier::SET), $object->getIdentifier()); } public function testGetContent() { $object = new CertificateExtensions(); $content = $object->getContent(); $this->assertTrue(is_array($content)); $this->assertTrue(count($content) == 0); $sans = new SubjectAlternativeNames(); $sans->addDomainName(new DNSName('corvespace.de')); $object->addSubjectAlternativeNames($sans); $this->assertTrue(count($object->getContent()) == 1); $this->assertContains($sans, $object->getContent()); } public function testGetObjectLength() { $object = new CertificateExtensions(); $objectIdentifier = new ObjectIdentifier(OID::CERT_EXT_SUBJECT_ALT_NAME); $sans = new SubjectAlternativeNames(); $sans->addDomainName(new DNSName('corvespace.de')); $object->addSubjectAlternativeNames($sans); $sizeOfFirstExtensionSequence = 2 + $objectIdentifier->getObjectLength() + $sans->getObjectLength(); $expectedSize = 2 + 2 + $sizeOfFirstExtensionSequence; // Extensions are sequences of Object identifiers and octet strings that are contained in a sequence which is contained in a set $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $object = new CertificateExtensions(); $objectIdentifier = new ObjectIdentifier(OID::CERT_EXT_SUBJECT_ALT_NAME); $sans = new SubjectAlternativeNames(); $sans->addDomainName(new DNSName('corvespace.de')); $object->addSubjectAlternativeNames($sans); $expectedType = chr(Identifier::SET); $expectedLength = chr(2 + 2 + $objectIdentifier->getObjectLength() + $sans->getObjectLength()); $expectedContent = chr(Identifier::SEQUENCE); $expectedContent .= chr(2 + $objectIdentifier->getObjectLength() + $sans->getObjectLength()); $expectedContent .= chr(Identifier::SEQUENCE); $expectedContent .= chr($objectIdentifier->getObjectLength() + $sans->getObjectLength()); $expectedContent .= $objectIdentifier->getBinary(); $expectedContent .= $sans->getBinary(); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new CertificateExtensions(); $sans = new SubjectAlternativeNames(); $sans->addDomainName(new DNSName('corvespace.de')); $sans->addIP(new IPAddress('192.168.0.1')); $originalObject->addSubjectAlternativeNames($sans); $binaryData = $originalObject->getBinary(); $parsedObject = CertificateExtensions::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $objectIdentifier = new ObjectIdentifier(OID::CERT_EXT_SUBJECT_ALT_NAME); $originalObject1 = new CertificateExtensions(); $sans1 = new SubjectAlternativeNames(); $sans1->addDomainName(new DNSName('corvespace.de')); $sans1->addIP(new IPAddress('192.168.0.1')); $originalObject1->addSubjectAlternativeNames($sans1); $originalObject2 = new CertificateExtensions(); $sans2 = new SubjectAlternativeNames(); $sans2->addDomainName(new DNSName('google.com')); $originalObject2->addSubjectAlternativeNames($sans2); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = CertificateExtensions::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $offsetAfterFirstObject = $sans1->getObjectLength() + $objectIdentifier->getObjectLength() + 2 + 2 + 2; $this->assertEquals($offsetAfterFirstObject, $offset); $parsedObject = CertificateExtensions::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals($offsetAfterFirstObject + $sans2->getObjectLength() + $objectIdentifier->getObjectLength() + 2 + 2 + 2, $offset); } } tests/X509/CertificateSubjectTest.php000064400000005722150736747750013521 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\X509; use FG\ASN1\OID; use FG\ASN1\Universal\ObjectIdentifier; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\X509\CertificateExtensions; use FG\X509\CertificateSubject; use FG\X509\SAN\DNSName; use FG\X509\SAN\IPAddress; use FG\X509\SAN\SubjectAlternativeNames; class CertificateSubjectTest extends ASN1TestCase { public function testGetType() { $object = new CertificateSubject('Friedrich Große', 'friedrich.grosse@foo.de', 'Organization', 'Locality', 'State', 'Country', 'OrgaUnit'); $this->assertEquals(Identifier::SEQUENCE, $object->getType()); } public function testGetIdentifier() { $object = new CertificateSubject('Friedrich Große', 'friedrich.grosse@foo.de', 'Organization', 'Locality', 'State', 'Country', 'OrgaUnit'); $this->assertEquals(chr(Identifier::SEQUENCE), $object->getIdentifier()); } public function testFromBinary() { $this->markTestIncomplete('Not implemented'); $originalObject = new CertificateSubject('Friedrich Große', 'friedrich.grosse@foo.de', 'Organization', 'Locality', 'State', 'Country', 'OrgaUnit'); $binaryData = $originalObject->getBinary(); $parsedObject = CertificateSubject::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $objectIdentifier = new ObjectIdentifier(OID::CERT_EXT_SUBJECT_ALT_NAME); $originalObject1 = new CertificateExtensions(); $sans1 = new SubjectAlternativeNames(); $sans1->addDomainName(new DNSName('corvespace.de')); $sans1->addIP(new IPAddress('192.168.0.1')); $originalObject1->addSubjectAlternativeNames($sans1); $originalObject2 = new CertificateExtensions(); $sans2 = new SubjectAlternativeNames(); $sans2->addDomainName(new DNSName('google.com')); $originalObject2->addSubjectAlternativeNames($sans2); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = CertificateExtensions::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $offsetAfterFirstObject = $sans1->getObjectLength() + $objectIdentifier->getObjectLength() + 2 + 2 + 2; $this->assertEquals($offsetAfterFirstObject, $offset); $parsedObject = CertificateExtensions::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals($offsetAfterFirstObject + $sans2->getObjectLength() + $objectIdentifier->getObjectLength() + 2 + 2 + 2, $offset); } } tests/X509/DNSNameTest.php000064400000004635150736747750011206 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\X509; use FG\Test\ASN1TestCase; use FG\X509\SAN\DNSName; class DNSNameTest extends ASN1TestCase { public function testGetType() { $object = new DNSName('test.corvespace.de'); $this->assertEquals(0x82, $object->getType()); } public function testGetIdentifier() { $object = new DNSName('test.corvespace.de'); $this->assertEquals(chr(0x82), $object->getIdentifier()); } public function testGetContent() { $object = new DNSName('test.corvespace.de'); $this->assertEquals('test.corvespace.de', $object->getContent()); } public function testGetObjectLength() { $string = 'test.corvespace.de'; $object = new DNSName($string); $expectedSize = 2 + strlen($string); $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $string = 'test.corvespace.de'; $expectedType = chr(0x82); $expectedLength = chr(strlen($string)); $object = new DNSName($string); $this->assertEquals($expectedType.$expectedLength.$string, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new DNSName('test.corvespace.de'); $binaryData = $originalObject->getBinary(); $parsedObject = DNSName::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new DNSName('test.corvespace.de'); $originalObject2 = new DNSName('superdomain.com'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = DNSName::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(20, $offset); $parsedObject = DNSName::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(37, $offset); } } tests/X509/IPAddressTest.php000064400000005677150736747750011606 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\X509; use FG\Test\ASN1TestCase; use FG\X509\SAN\IPAddress; class IPAddressTest extends ASN1TestCase { public function testGetType() { $object = new IPAddress('192.168.0.1'); $this->assertEquals(0x87, $object->getType()); } public function testGetIdentifier() { $object = new IPAddress('192.168.0.1'); $this->assertEquals(chr(0x87), $object->getIdentifier()); } public function testGetContent() { $object = new IPAddress('192.168.0.1'); $this->assertEquals('192.168.0.1', $object->getContent()); } public function testGetObjectLength() { $object = new IPAddress('192.168.0.1'); $expectedSize = 6; // Type + Length + 4 Byte $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $expectedType = chr(0x87); $expectedLength = chr(4); $ipAddress = chr(192); $ipAddress .= chr(168); $ipAddress .= chr(0); $ipAddress .= chr(1); $object = new IPAddress('192.168.0.1'); $this->assertEquals($expectedType.$expectedLength.$ipAddress, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new IPAddress('192.168.0.1'); $binaryData = $originalObject->getBinary(); $parsedObject = IPAddress::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @expectedException \FG\ASN1\Exception\ParserException * @expectedExceptionMessage ASN.1 Parser Exception at offset 2: A FG\X509\SAN\IPAddress should have a content length of 4. Extracted length was 2 * @depends testFromBinary */ public function testFromBinaryWithWrongLengthThrowsException() { $binaryData = chr(0x87); $binaryData .= chr(2); $binaryData .= chr(192); $binaryData .= chr(168); IPAddress::fromBinary($binaryData); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new IPAddress('192.168.0.1'); $originalObject2 = new IPAddress('10.65.32.123'); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = IPAddress::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(6, $offset); $parsedObject = IPAddress::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(12, $offset); } } tests/X509/SubjectAlternativeNamesTest.php000064400000007525150736747750014544 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace FG\Test\X509; use FG\Test\ASN1TestCase; use FG\ASN1\Identifier; use FG\X509\SAN\DNSName; use FG\X509\SAN\IPAddress; use FG\X509\SAN\SubjectAlternativeNames; class SubjectAlternativeNamesTest extends ASN1TestCase { public function testGetType() { $object = new SubjectAlternativeNames(); $this->assertEquals(Identifier::OCTETSTRING, $object->getType()); } public function testGetIdentifier() { $object = new SubjectAlternativeNames(); $this->assertEquals(chr(Identifier::OCTETSTRING), $object->getIdentifier()); } public function testGetContent() { $object = new SubjectAlternativeNames(); $content = $object->getContent(); $this->assertTrue(is_array($content)); $this->assertTrue(count($content) == 0); $dnsName = new DNSName('corvespace.de'); $object->addDomainName($dnsName); $this->assertTrue(count($object->getContent()) == 1); $this->assertContains($dnsName, $object->getContent()); $ipAddress = new IPAddress('192.168.0.1'); $object->addIP($ipAddress); $this->assertTrue(count($object->getContent()) == 2); $this->assertContains($ipAddress, $object->getContent()); } public function testGetObjectLength() { $dnsName = new DNSName('example.dhl.com'); $object = new SubjectAlternativeNames(); $object->addDomainName($dnsName); $expectedSize = 2 + 2 + $dnsName->getObjectLength(); // all registered SANs are encapsulated in a sequence which is encapsulated in a octet string $this->assertEquals($expectedSize, $object->getObjectLength()); } public function testGetBinary() { $dnsName = new DNSName('example.dhl.com'); $object = new SubjectAlternativeNames(); $object->addDomainName($dnsName); $expectedType = chr(Identifier::OCTETSTRING); $expectedLength = chr(0x13); $expectedContent = chr(Identifier::SEQUENCE); $expectedContent .= chr(0x11); $expectedContent .= $dnsName->getBinary(); $this->assertEquals($expectedType.$expectedLength.$expectedContent, $object->getBinary()); } /** * @depends testGetBinary */ public function testFromBinary() { $originalObject = new SubjectAlternativeNames(); $originalObject->addDomainName(new DNSName('corvespace.de')); $originalObject->addIP(new IPAddress('192.168.0.1')); $binaryData = $originalObject->getBinary(); $parsedObject = SubjectAlternativeNames::fromBinary($binaryData); $this->assertEquals($originalObject, $parsedObject); } /** * @depends testFromBinary */ public function testFromBinaryWithOffset() { $originalObject1 = new SubjectAlternativeNames(); $originalObject1->addDomainName(new DNSName('corvespace.de')); $originalObject1->addIP(new IPAddress('192.168.0.1')); $originalObject1->addIP(new IPAddress('10.218.0.1')); $originalObject2 = new SubjectAlternativeNames(); $originalObject2->addDomainName(new DNSName('google.com')); $binaryData = $originalObject1->getBinary(); $binaryData .= $originalObject2->getBinary(); $offset = 0; $parsedObject = SubjectAlternativeNames::fromBinary($binaryData, $offset); $this->assertEquals($originalObject1, $parsedObject); $this->assertEquals(31, $offset); $parsedObject = SubjectAlternativeNames::fromBinary($binaryData, $offset); $this->assertEquals($originalObject2, $parsedObject); $this->assertEquals(47, $offset); } }