mlocati /
ocsp
| 1 | <?php |
||
| 2 | |||
| 3 | namespace Ocsp\Asn1\Der; |
||
| 4 | |||
| 5 | use DateTimeImmutable; |
||
| 6 | use DateTimeZone; |
||
| 7 | use Ocsp\Asn1\Decoder as DecoderInterface; |
||
| 8 | use Ocsp\Asn1\Element; |
||
| 9 | use Ocsp\Asn1\Element\BitString; |
||
| 10 | use Ocsp\Asn1\Element\GeneralizedTime; |
||
| 11 | use Ocsp\Asn1\Element\Integer; |
||
| 12 | use Ocsp\Asn1\Element\ObjectIdentifier; |
||
| 13 | use Ocsp\Asn1\Element\OctetString; |
||
| 14 | use Ocsp\Asn1\Element\PrintableString; |
||
| 15 | use Ocsp\Asn1\Element\RawConstructed; |
||
| 16 | use Ocsp\Asn1\Element\RawPrimitive; |
||
| 17 | use Ocsp\Asn1\Element\Sequence; |
||
| 18 | use Ocsp\Asn1\Element\Set; |
||
| 19 | use Ocsp\Asn1\Tag; |
||
| 20 | use Ocsp\Asn1\TaggableElement; |
||
| 21 | use Ocsp\Asn1\UniversalTagID; |
||
| 22 | use Ocsp\Exception\Asn1DecodingException; |
||
| 23 | use Ocsp\Service\Math; |
||
| 24 | |||
| 25 | /** |
||
| 26 | * Decoder from DER to ASN.1. |
||
| 27 | */ |
||
| 28 | class Decoder implements DecoderInterface |
||
| 29 | { |
||
| 30 | /** |
||
| 31 | * {@inheritdoc} |
||
| 32 | * |
||
| 33 | * @see \Ocsp\Asn1\Encoder::getEncodingHandle() |
||
| 34 | */ |
||
| 35 | 3 | public function getEncodingHandle() |
|
| 36 | { |
||
| 37 | 3 | return 'der'; |
|
| 38 | } |
||
| 39 | |||
| 40 | /** |
||
| 41 | * {@inheritdoc} |
||
| 42 | * |
||
| 43 | * @see \Ocsp\Asn1\Decoder::decodeElement() |
||
| 44 | */ |
||
| 45 | 3 | public function decodeElement($bytes) |
|
| 46 | { |
||
| 47 | 3 | $offset = 0; |
|
| 48 | |||
| 49 | 3 | return $this->decodeElementAt($bytes, $offset); |
|
| 50 | } |
||
| 51 | |||
| 52 | /** |
||
| 53 | * Decode an element at a specific position in a range of bytes. |
||
| 54 | * |
||
| 55 | * @param string $bytes |
||
| 56 | * @param int $offset |
||
| 57 | * |
||
| 58 | * @throws \Ocsp\Exception\Asn1DecodingException |
||
| 59 | * |
||
| 60 | * @return \Ocsp\Asn1\Element |
||
| 61 | */ |
||
| 62 | 3 | protected function decodeElementAt($bytes, &$offset) |
|
| 63 | { |
||
| 64 | 3 | list($typeID, $class, $isConstructed) = $this->decodeType($bytes, $offset); |
|
| 65 | 3 | $encodedValue = $this->extractEncodedValue($bytes, $offset); |
|
| 66 | |||
| 67 | 3 | return $isConstructed ? $this->decodeConstructed($typeID, $class, $encodedValue) : $this->decodePrimitive(/** @scrutinizer ignore-type */ $typeID, $class, $encodedValue); |
|
| 68 | } |
||
| 69 | |||
| 70 | /** |
||
| 71 | * Decode a CONSTRUCTED ASN.1 element. |
||
| 72 | * |
||
| 73 | * @param int|\phpseclib\Math\BigInteger|\phpseclib3\Math\BigInteger $typeID |
||
|
0 ignored issues
–
show
|
|||
| 74 | * @param string $class |
||
| 75 | * @param string $encodedValue |
||
| 76 | * |
||
| 77 | * @throws \Ocsp\Exception\Asn1DecodingException |
||
| 78 | * |
||
| 79 | * @return \Ocsp\Asn1\Element |
||
| 80 | */ |
||
| 81 | 3 | protected function decodeConstructed($typeID, $class, $encodedValue) |
|
| 82 | { |
||
| 83 | 3 | $offset = 0; |
|
| 84 | 3 | $encodedValueLength = strlen($encodedValue); |
|
| 85 | 3 | $elements = []; |
|
| 86 | 3 | while ($offset < $encodedValueLength) { |
|
| 87 | 3 | if ($encodedValue[$offset] === "\x00" && isset($encodedValue[$offset + 1]) && $encodedValue[$offset + 1] === "\x00") { |
|
| 88 | // end of elements in case the length is in indefinite form |
||
| 89 | break; |
||
| 90 | } |
||
| 91 | 3 | $elements[] = $this->decodeElementAt($encodedValue, $offset); |
|
| 92 | } |
||
| 93 | 3 | if (count($elements) === 1 && $class !== Element::CLASS_UNIVERSAL && $elements[0] instanceof TaggableElement) { |
|
| 94 | 3 | return $elements[0]->setTag(Tag::explicit($typeID, $class)); |
|
| 95 | } |
||
| 96 | 3 | if (is_int($typeID) && $class === Element::CLASS_UNIVERSAL) { |
|
| 97 | switch ($typeID) { |
||
| 98 | 3 | case UniversalTagID::SEQUENCE: |
|
| 99 | 3 | return Sequence::create($elements); |
|
| 100 | 3 | case UniversalTagID::SET: |
|
| 101 | 3 | return Set::create($elements); |
|
| 102 | } |
||
| 103 | } |
||
| 104 | |||
| 105 | return RawConstructed::create($this->getEncodingHandle(), $typeID, $class, $elements); |
||
| 106 | } |
||
| 107 | |||
| 108 | /** |
||
| 109 | * Decode a PRIMITIVE ASN.1 element. |
||
| 110 | * |
||
| 111 | * @param int $typeID |
||
| 112 | * @param string $class |
||
| 113 | * @param string $encodedValue |
||
| 114 | * |
||
| 115 | * @throws \Ocsp\Exception\Asn1DecodingException |
||
| 116 | * |
||
| 117 | * @return \Ocsp\Asn1\Element |
||
| 118 | */ |
||
| 119 | 3 | protected function decodePrimitive($typeID, $class, $encodedValue) |
|
| 120 | { |
||
| 121 | 3 | if ($class === Element::CLASS_UNIVERSAL) { |
|
| 122 | switch ($typeID) { |
||
| 123 | 3 | case UniversalTagID::INTEGER: |
|
| 124 | 3 | return Integer::create($this->decodeInteger($encodedValue)); |
|
| 125 | 3 | case UniversalTagID::BIT_STRING: |
|
| 126 | 3 | list($bytes, $numTrailingBits) = $this->decodeBitString($encodedValue); |
|
| 127 | |||
| 128 | 3 | return BitString::create($bytes, $numTrailingBits); |
|
| 129 | 3 | case UniversalTagID::OCTET_STRING: |
|
| 130 | 3 | return OctetString::create($this->decodeOctetString($encodedValue)); |
|
| 131 | 3 | case UniversalTagID::OBJECT_IDENTIFIER: |
|
| 132 | 3 | return ObjectIdentifier::create($this->decodeObjectIdentifier($encodedValue)); |
|
| 133 | 3 | case UniversalTagID::PRINTABLESTRING: |
|
| 134 | 3 | return PrintableString::create($this->decodePrintableString($encodedValue)); |
|
| 135 | 3 | case UniversalTagID::GENERALIZEDTIME: |
|
| 136 | 2 | return GeneralizedTime::create($this->decodeGeneralizedTime($encodedValue)); |
|
| 137 | } |
||
| 138 | } |
||
| 139 | |||
| 140 | 3 | return RawPrimitive::create($this->getEncodingHandle(), $typeID, $class, $encodedValue); |
|
| 141 | } |
||
| 142 | |||
| 143 | /** |
||
| 144 | * Extract the details about at a specific position in a range of bytes. |
||
| 145 | * |
||
| 146 | * @param string $bytes |
||
| 147 | * @param int $offset |
||
| 148 | * |
||
| 149 | * @throws \Ocsp\Exception\Asn1DecodingException |
||
| 150 | * |
||
| 151 | * @return array The first element is the type ID (int|\phpseclib\Math\BigInteger|\phpseclib3\Math\BigInteger), the second is the class (string), the third is true (if the type is constructed) or false (not constructed) |
||
| 152 | */ |
||
| 153 | 3 | protected function decodeType($bytes, &$offset) |
|
| 154 | { |
||
| 155 | 3 | if (!isset($bytes[$offset])) { |
|
| 156 | throw Asn1DecodingException::create(); |
||
| 157 | } |
||
| 158 | 3 | $byte = ord($bytes[$offset++]); |
|
| 159 | 3 | $isConstructed = ($byte & 0b100000) !== 0; |
|
| 160 | 3 | if (($byte & 0b11000000) === 0b11000000) { |
|
| 161 | $class = Element::CLASS_PRIVATE; |
||
| 162 | 3 | } elseif ($byte & 0b10000000) { |
|
| 163 | 3 | $class = Element::CLASS_CONTEXTSPECIFIC; |
|
| 164 | 3 | } elseif ($byte & 0b01000000) { |
|
| 165 | $class = Element::CLASS_APPLICATION; |
||
| 166 | } else { |
||
| 167 | 3 | $class = Element::CLASS_UNIVERSAL; |
|
| 168 | } |
||
| 169 | 3 | $typeID = $byte & 0b00011111; |
|
| 170 | 3 | if ($typeID === 0b00011111) { |
|
| 171 | $typeParts = []; |
||
| 172 | do { |
||
| 173 | if (!isset($bytes[$offset])) { |
||
| 174 | throw Asn1DecodingException::create(); |
||
| 175 | } |
||
| 176 | $byte = ord($bytes[$offset++]); |
||
| 177 | $typeParts[] = $byte & 0b01111111; |
||
| 178 | } while (($byte & 0b10000000) === 0); |
||
| 179 | $numTypeParts = count($typeParts); |
||
| 180 | if ($numTypeParts > PHP_INT_SIZE || ($numTypeParts === PHP_INT_SIZE && $typeParts[$numTypeParts - 1] & 0b10000000)) { |
||
| 181 | $typeIDBits = ''; |
||
| 182 | for ($i = 0; $i < $numTypeParts; $i++) { |
||
| 183 | $typeIDBits .= str_pad(decbin($typeParts[$i]), 7, '0', STR_PAD_LEFT); |
||
| 184 | } |
||
| 185 | $typeID = Math::createBigInteger($typeIDBits, 2); |
||
| 186 | } else { |
||
| 187 | $typeID = 0; |
||
| 188 | for ($i = $numTypeParts - 1; $i >= 0; $i--) { |
||
| 189 | $typeID = ($typeID << 7) + $typeParts[$i]; |
||
| 190 | } |
||
| 191 | } |
||
| 192 | } |
||
| 193 | |||
| 194 | 3 | return [$typeID, $class, $isConstructed]; |
|
| 195 | } |
||
| 196 | |||
| 197 | /** |
||
| 198 | * Extract the bytes representing the value of an element. |
||
| 199 | * |
||
| 200 | * @param string $bytes |
||
| 201 | * @param int $offset |
||
| 202 | * |
||
| 203 | * @throws \Ocsp\Exception\Asn1DecodingException |
||
| 204 | * |
||
| 205 | * @return string |
||
| 206 | */ |
||
| 207 | 3 | protected function extractEncodedValue($bytes, &$offset) |
|
| 208 | { |
||
| 209 | 3 | $length = $this->decodeLength($bytes, $offset); |
|
| 210 | 3 | if ($length === 0) { |
|
| 211 | 3 | return ''; |
|
| 212 | } |
||
| 213 | 3 | if ($offset + $length > strlen($bytes)) { |
|
| 214 | throw Asn1DecodingException::create(); |
||
| 215 | } |
||
| 216 | 3 | $encodedValue = substr($bytes, $offset, $length); |
|
| 217 | 3 | $offset += $length; |
|
| 218 | |||
| 219 | 3 | return $encodedValue; |
|
| 220 | } |
||
| 221 | |||
| 222 | /** |
||
| 223 | * Extract the length (in bytes) of the encoded value an element. |
||
| 224 | * |
||
| 225 | * @param string $bytes |
||
| 226 | * @param int $offset |
||
| 227 | * |
||
| 228 | * @throws \Ocsp\Exception\Asn1DecodingException |
||
| 229 | * |
||
| 230 | * @return int |
||
| 231 | */ |
||
| 232 | 3 | protected function decodeLength($bytes, &$offset) |
|
| 233 | { |
||
| 234 | 3 | if (!isset($bytes[$offset])) { |
|
| 235 | throw Asn1DecodingException::create(); |
||
| 236 | } |
||
| 237 | 3 | $byte = ord($bytes[$offset++]); |
|
| 238 | 3 | if (($byte & 0b10000000) === 0) { |
|
| 239 | // short form |
||
| 240 | 3 | return $byte; |
|
| 241 | } |
||
| 242 | 3 | if ($byte === 0b10000000) { |
|
| 243 | // indefinite form |
||
| 244 | return strlen($bytes) - $offset; |
||
| 245 | } |
||
| 246 | // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only |
||
| 247 | // support it up to four. |
||
| 248 | 3 | $numLenghtBytes = $byte & 0b01111111; |
|
| 249 | 3 | if ($numLenghtBytes === 0) { |
|
| 250 | throw Asn1DecodingException::create(); |
||
| 251 | } |
||
| 252 | 3 | $length = 0; |
|
| 253 | 3 | for ($i = 0; $i < $numLenghtBytes; $i++) { |
|
| 254 | 3 | if (!isset($bytes[$offset])) { |
|
| 255 | throw Asn1DecodingException::create(); |
||
| 256 | } |
||
| 257 | 3 | $byte = ord($bytes[$offset++]); |
|
| 258 | 3 | if ($i === PHP_INT_SIZE || ($i === PHP_INT_SIZE - 1 && $byte & 0b10000000)) { |
|
| 259 | throw Asn1DecodingException::create('Element length too long for this implementation'); |
||
| 260 | } |
||
| 261 | 3 | $length = ($length << 8) + $byte; |
|
| 262 | } |
||
| 263 | |||
| 264 | 3 | return $length; |
|
| 265 | } |
||
| 266 | |||
| 267 | /** |
||
| 268 | * Decode the value of an INTEGER element. |
||
| 269 | * |
||
| 270 | * @param string $bytes |
||
| 271 | * |
||
| 272 | * @return int|\phpseclib\Math\BigInteger|\phpseclib3\Math\BigInteger |
||
| 273 | */ |
||
| 274 | 3 | protected function decodeInteger($bytes) |
|
| 275 | { |
||
| 276 | 3 | $numBytes = strlen($bytes); |
|
| 277 | 3 | $firstByte = ord($bytes[0]); |
|
| 278 | 3 | $isNegative = ($firstByte & 0b10000000) !== 0; |
|
| 279 | 3 | if ($isNegative === false) { |
|
| 280 | switch ($numBytes) { |
||
| 281 | 3 | case 1: |
|
| 282 | 3 | return $firstByte; |
|
| 283 | 3 | case 2: |
|
| 284 | return current(unpack('n', $bytes)); |
||
| 285 | 3 | case 3: |
|
| 286 | return current(unpack('N', "\x00" . $bytes)); |
||
| 287 | 3 | case 4: |
|
| 288 | return current(unpack('N', $bytes)); |
||
| 289 | } |
||
| 290 | 3 | if ($numBytes <= 8 && PHP_INT_SIZE >= 8 && PHP_VERSION_ID >= 50603) { |
|
| 291 | return current(unpack('J', str_pad($bytes, 8, "\x00", STR_PAD_LEFT))); |
||
| 292 | } |
||
| 293 | } |
||
| 294 | |||
| 295 | 3 | return Math::createBigInteger($bytes, -256); |
|
| 296 | } |
||
| 297 | |||
| 298 | /** |
||
| 299 | * Decode the value of a BIT STRING element. |
||
| 300 | * |
||
| 301 | * @param string $bytes |
||
| 302 | * |
||
| 303 | * @return array The first element contains the bytes (a string), the second element the number of trailing bits (an integer) |
||
| 304 | */ |
||
| 305 | 3 | protected function decodeBitString($bytes) |
|
| 306 | { |
||
| 307 | 3 | $numTrailingBits = ord($bytes[0]) & 0b01111111; |
|
| 308 | 3 | $bytes = substr($bytes, 1); |
|
| 309 | 3 | if ($bytes === false) { |
|
| 310 | $bytes = ''; |
||
| 311 | } |
||
| 312 | |||
| 313 | 3 | return [$bytes, $numTrailingBits]; |
|
| 314 | } |
||
| 315 | |||
| 316 | /** |
||
| 317 | * Decode the value of a OCTET STRING element. |
||
| 318 | * |
||
| 319 | * @param string $bytes |
||
| 320 | * |
||
| 321 | * @return string |
||
| 322 | */ |
||
| 323 | 3 | protected function decodeOctetString($bytes) |
|
| 324 | { |
||
| 325 | 3 | return $bytes; |
|
| 326 | } |
||
| 327 | |||
| 328 | /** |
||
| 329 | * Decode the value of a OBJECT IDENTIFIER element. |
||
| 330 | * |
||
| 331 | * @param string $bytes |
||
| 332 | * |
||
| 333 | * @throws \Ocsp\Exception\Asn1DecodingException |
||
| 334 | * |
||
| 335 | * @return string |
||
| 336 | */ |
||
| 337 | 3 | protected function decodeObjectIdentifier($bytes) |
|
| 338 | { |
||
| 339 | 3 | $byte = ord($bytes[0]); |
|
| 340 | 3 | $result = sprintf('%d.%d', floor($byte / 40), $byte % 40); |
|
| 341 | 3 | $len = strlen($bytes); |
|
| 342 | 3 | $chunkBits = ''; |
|
| 343 | 3 | $maxIntBits = PHP_INT_SIZE * 8 - 1; |
|
| 344 | 3 | for ($offset = 1; $offset < $len; $offset++) { |
|
| 345 | 3 | $byte = ord($bytes[$offset]); |
|
| 346 | 3 | $chunkBits .= str_pad(decbin($byte & 0b01111111), 7, '0', STR_PAD_LEFT); |
|
| 347 | 3 | if (($byte & 0b10000000) === 0) { |
|
| 348 | 3 | $result .= '.'; |
|
| 349 | 3 | if (strlen($chunkBits) <= $maxIntBits) { |
|
| 350 | 3 | $result .= (string) bindec($chunkBits); |
|
| 351 | } else { |
||
| 352 | $result .= Math::createBigInteger($chunkBits, 2)->toString(); |
||
| 353 | } |
||
| 354 | 3 | $chunkBits = ''; |
|
| 355 | } |
||
| 356 | } |
||
| 357 | 3 | if ($chunkBits !== '') { |
|
| 358 | throw Asn1DecodingException::create(); |
||
| 359 | } |
||
| 360 | |||
| 361 | 3 | return $result; |
|
| 362 | } |
||
| 363 | |||
| 364 | /** |
||
| 365 | * Decode the value of a PrintableString element. |
||
| 366 | * |
||
| 367 | * @param string $bytes |
||
| 368 | * |
||
| 369 | * @return string |
||
| 370 | */ |
||
| 371 | 3 | protected function decodePrintableString($bytes) |
|
| 372 | { |
||
| 373 | 3 | return $bytes; |
|
| 374 | } |
||
| 375 | |||
| 376 | /** |
||
| 377 | * Decode the value of a GeneralizedTime element. |
||
| 378 | * |
||
| 379 | * @param string $bytes |
||
| 380 | * |
||
| 381 | * @throws \Ocsp\Exception\Asn1DecodingException |
||
| 382 | * |
||
| 383 | * @return \DateTimeImmutable |
||
| 384 | */ |
||
| 385 | 2 | protected function decodeGeneralizedTime($bytes) |
|
| 386 | { |
||
| 387 | 2 | $matches = null; |
|
| 388 | 2 | if (!preg_match('/(\d{4}\d{2}\d{2}\d{2}\d{2}\d{2})(?:\.(\d*))?Z$/', $bytes, $matches)) { |
|
| 389 | throw Asn1DecodingException::create(); |
||
| 390 | } |
||
| 391 | 2 | $dateTime = DateTimeImmutable::createFromFormat('!YmdHis.uT', $matches[1] . '.' . (isset($matches[2]) ? $matches[2] : '0') . 'UTC', new DateTimeZone('UTC')); |
|
| 392 | 2 | $result = $dateTime->setTimezone(new DateTimeZone(date_default_timezone_get())); |
|
| 393 | |||
| 394 | 2 | return $result; |
|
| 395 | } |
||
| 396 | } |
||
| 397 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths