diff options
Diffstat (limited to 'vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php')
-rw-r--r-- | vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php b/vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php new file mode 100644 index 0000000..3b7f162 --- /dev/null +++ b/vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php @@ -0,0 +1,355 @@ +<?php +/* + * This file is part of the PHPASN1 library. + * + * Copyright © Friedrich Große <friedrich.grosse@gmail.com> + * + * 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 FG\Utility\BigInteger; +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 = BigInteger::create(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->shiftLeft(8)->add(ord($binaryData[$offsetIndex++])); + } + + if ($contentLength->compare(PHP_INT_MAX) > 0) { + throw new ParserException("Can not parse content length from data: length > maximum integer", $offsetIndex); + } + + $contentLength = $contentLength->toInteger(); + } + + if ($contentLength < $minimumLength) { + throw new ParserException('A '.get_called_class()." should have a content length of at least {$minimumLength}. Extracted length was {$contentLength}", $offsetIndex); + } + + $lenDataRemaining = strlen($binaryData) - $offsetIndex; + + if ($lenDataRemaining < $contentLength) { + throw new ParserException("Content length {$contentLength} exceeds remaining data length {$lenDataRemaining}", $offsetIndex); + } + + return $contentLength; + } +} |