GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 06daff...a25647 )
by Joni
03:03
created

Element::isConstructed()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 1
ccs 0
cts 0
cp 0
c 1
b 0
f 0
nc 1
1
<?php
2
3
namespace ASN1;
4
5
use ASN1\Component\Identifier;
6
use ASN1\Component\Length;
7
use ASN1\Exception\DecodeException;
8
use ASN1\Feature\ElementBase;
9
use ASN1\Type\Constructed\Sequence;
10
use ASN1\Type\Constructed\Set;
11
use ASN1\Type\Primitive\BitString;
12
use ASN1\Type\Primitive\BMPString;
13
use ASN1\Type\Primitive\Boolean;
14
use ASN1\Type\Primitive\CharacterString;
15
use ASN1\Type\Primitive\Enumerated;
16
use ASN1\Type\Primitive\GeneralizedTime;
17
use ASN1\Type\Primitive\GeneralString;
18
use ASN1\Type\Primitive\GraphicString;
19
use ASN1\Type\Primitive\IA5String;
20
use ASN1\Type\Primitive\Integer;
21
use ASN1\Type\Primitive\NullType;
22
use ASN1\Type\Primitive\NumericString;
23
use ASN1\Type\Primitive\ObjectDescriptor;
24
use ASN1\Type\Primitive\ObjectIdentifier;
25
use ASN1\Type\Primitive\OctetString;
26
use ASN1\Type\Primitive\PrintableString;
27
use ASN1\Type\Primitive\Real;
28
use ASN1\Type\Primitive\RelativeOID;
29
use ASN1\Type\Primitive\T61String;
30
use ASN1\Type\Primitive\UniversalString;
31
use ASN1\Type\Primitive\UTCTime;
32
use ASN1\Type\Primitive\UTF8String;
33
use ASN1\Type\Primitive\VideotexString;
34
use ASN1\Type\Primitive\VisibleString;
35
use ASN1\Type\StringType;
36
use ASN1\Type\TaggedType;
37
use ASN1\Type\TimeType;
38
39
40
/**
41
 * Base class for all ASN.1 type elements.
42
 */
43
abstract class Element implements ElementBase
44
{
45
	// Universal type tags
46
	const TYPE_EOC = 0x00;
47
	const TYPE_BOOLEAN = 0x01;
48
	const TYPE_INTEGER = 0x02;
49
	const TYPE_BIT_STRING = 0x03;
50
	const TYPE_OCTET_STRING = 0x04;
51
	const TYPE_NULL = 0x05;
52
	const TYPE_OBJECT_IDENTIFIER = 0x06;
53
	const TYPE_OBJECT_DESCRIPTOR = 0x07;
54
	const TYPE_EXTERNAL = 0x08;
55
	const TYPE_REAL = 0x09;
56
	const TYPE_ENUMERATED = 0x0a;
57
	const TYPE_EMBEDDED_PDV = 0x0b;
58
	const TYPE_UTF8_STRING = 0x0c;
59
	const TYPE_RELATIVE_OID = 0x0d;
60
	const TYPE_SEQUENCE = 0x10;
61
	const TYPE_SET = 0x11;
62
	const TYPE_NUMERIC_STRING = 0x12;
63
	const TYPE_PRINTABLE_STRING = 0x13;
64
	const TYPE_T61_STRING = 0x14;
65
	const TYPE_VIDEOTEX_STRING = 0x15;
66
	const TYPE_IA5_STRING = 0x16;
67
	const TYPE_UTC_TIME = 0x17;
68
	const TYPE_GENERALIZED_TIME = 0x18;
69
	const TYPE_GRAPHIC_STRING = 0x19;
70
	const TYPE_VISIBLE_STRING = 0x1a;
71
	const TYPE_GENERAL_STRING = 0x1b;
72
	const TYPE_UNIVERSAL_STRING = 0x1c;
73
	const TYPE_CHARACTER_STRING = 0x1d;
74
	const TYPE_BMP_STRING = 0x1e;
75
	
76
	/**
77
	 * Mapping from universal type tag to implementation class name.
78
	 *
79
	 * @internal
80
	 *
81
	 * @var array
82
	 */
83
	const MAP_TAG_TO_CLASS = array(
84
		/* @formatter:off */
85
		self::TYPE_BOOLEAN => Boolean::class,
86
		self::TYPE_INTEGER => Integer::class,
87
		self::TYPE_BIT_STRING => BitString::class,
88
		self::TYPE_OCTET_STRING => OctetString::class,
89
		self::TYPE_NULL => NullType::class,
90
		self::TYPE_OBJECT_IDENTIFIER => ObjectIdentifier::class,
91
		self::TYPE_OBJECT_DESCRIPTOR => ObjectDescriptor::class,
92
		self::TYPE_REAL => Real::class,
93
		self::TYPE_ENUMERATED => Enumerated::class,
94
		self::TYPE_UTF8_STRING => UTF8String::class,
95
		self::TYPE_RELATIVE_OID => RelativeOID::class,
96
		self::TYPE_SEQUENCE => Sequence::class,
97
		self::TYPE_SET => Set::class,
98
		self::TYPE_NUMERIC_STRING => NumericString::class,
99
		self::TYPE_PRINTABLE_STRING => PrintableString::class,
100
		self::TYPE_T61_STRING => T61String::class,
101
		self::TYPE_VIDEOTEX_STRING => VideotexString::class,
102
		self::TYPE_IA5_STRING => IA5String::class,
103
		self::TYPE_UTC_TIME => UTCTime::class,
104
		self::TYPE_GENERALIZED_TIME => GeneralizedTime::class,
105
		self::TYPE_GRAPHIC_STRING => GraphicString::class,
106
		self::TYPE_VISIBLE_STRING => VisibleString::class,
107
		self::TYPE_GENERAL_STRING => GeneralString::class,
108
		self::TYPE_UNIVERSAL_STRING => UniversalString::class,
109
		self::TYPE_CHARACTER_STRING => CharacterString::class,
110
		self::TYPE_BMP_STRING => BMPString::class
111
		/* @formatter:on */
112
	);
113
	
114
	/**
115
	 * Pseudotype for all string types.
116
	 *
117
	 * May be used as an expectation parameter.
118
	 *
119
	 * @var int
120
	 */
121
	const TYPE_STRING = -1;
122
	
123
	/**
124
	 * Pseudotype for all time types.
125
	 *
126
	 * May be used as an expectation parameter.
127
	 *
128
	 * @var int
129
	 */
130
	const TYPE_TIME = -2;
131
	
132
	/**
133
	 * Mapping from universal type tag to human readable name.
134
	 *
135
	 * @internal
136
	 *
137
	 * @var array
138
	 */
139
	const MAP_TYPE_TO_NAME = array(
140
		/* @formatter:off */
141
		self::TYPE_EOC => "EOC",
142
		self::TYPE_BOOLEAN => "BOOLEAN",
143
		self::TYPE_INTEGER => "INTEGER",
144
		self::TYPE_BIT_STRING => "BIT STRING",
145
		self::TYPE_OCTET_STRING => "OCTET STRING",
146
		self::TYPE_NULL => "NULL",
147
		self::TYPE_OBJECT_IDENTIFIER => "OBJECT IDENTIFIER",
148
		self::TYPE_OBJECT_DESCRIPTOR => "ObjectDescriptor",
149
		self::TYPE_EXTERNAL => "EXTERNAL",
150
		self::TYPE_REAL => "REAL",
151
		self::TYPE_ENUMERATED => "ENUMERATED",
152
		self::TYPE_EMBEDDED_PDV => "EMBEDDED PDV",
153
		self::TYPE_UTF8_STRING => "UTF8String",
154
		self::TYPE_RELATIVE_OID => "RELATIVE-OID",
155
		self::TYPE_SEQUENCE => "SEQUENCE",
156
		self::TYPE_SET => "SET",
157
		self::TYPE_NUMERIC_STRING => "NumericString",
158
		self::TYPE_PRINTABLE_STRING => "PrintableString",
159
		self::TYPE_T61_STRING => "T61String",
160
		self::TYPE_VIDEOTEX_STRING => "VideotexString",
161
		self::TYPE_IA5_STRING => "IA5String",
162
		self::TYPE_UTC_TIME => "UTCTime",
163
		self::TYPE_GENERALIZED_TIME => "GeneralizedTime",
164
		self::TYPE_GRAPHIC_STRING => "GraphicString",
165
		self::TYPE_VISIBLE_STRING => "VisibleString",
166
		self::TYPE_GENERAL_STRING => "GeneralString",
167
		self::TYPE_UNIVERSAL_STRING => "UniversalString",
168
		self::TYPE_CHARACTER_STRING => "CHARACTER STRING",
169
		self::TYPE_BMP_STRING => "BMPString",
170
		self::TYPE_STRING => "Any String",
171
		self::TYPE_TIME => "Any Time"
172
		/* @formatter:on */
173
	);
174
	
175
	/**
176
	 * Element's type tag.
177
	 *
178
	 * @var int
179
	 */
180
	protected $_typeTag;
181
	
182
	/**
183
	 *
184
	 * @see \ASN1\Feature\ElementBase::typeClass()
185
	 * @return int
186
	 */
187
	abstract public function typeClass();
188
	
189
	/**
190
	 *
191
	 * @see \ASN1\Feature\ElementBase::isConstructed()
192
	 * @return bool
193
	 */
194
	abstract public function isConstructed();
195
	
196
	/**
197
	 * Get the content encoded in DER.
198
	 *
199
	 * Returns the DER encoded content without identifier and length header
200
	 * octets.
201
	 *
202
	 * @return string
203
	 */
204
	abstract protected function _encodedContentDER();
205
	
206
	/**
207
	 * Decode type-specific element from DER.
208
	 *
209
	 * @param Identifier $identifier Pre-parsed identifier
210
	 * @param string $data DER data
211
	 * @param int $offset Offset in data to the next byte after identifier
212
	 * @throws DecodeException If decoding fails
213
	 * @return Element
214
	 */
215 1
	protected static function _decodeFromDER(Identifier $identifier, $data, 
216
			&$offset) {
217 1
		throw new \BadMethodCallException(
218 1
			__METHOD__ . " must be implemented in derived class.");
219
	}
220
	
221
	/**
222
	 * Decode element from DER data.
223
	 *
224
	 * @param string $data DER encoded data
225
	 * @param int|null $offset Reference to the variable that contains offset
226
	 *        into the data where to start parsing. Variable is updated to
227
	 *        the offset next to the parsed element. If null, start from offset
228
	 *        0.
229
	 * @throws DecodeException If decoding fails
230
	 * @throws \UnexpectedValueException If called in the context of an expected
231
	 *         type, but decoding yields another type
232
	 * @return self
233
	 */
234 194
	public static function fromDER($data, &$offset = null) {
235 194
		assert('is_string($data)', "got " . gettype($data));
236
		// decode identifier
237 193
		$idx = $offset ? $offset : 0;
238 193
		$identifier = Identifier::fromDER($data, $idx);
239
		// determine class that implements type specific decoding
240 193
		$cls = self::_determineImplClass($identifier);
241
		try {
242
			// decode remaining element
243 191
			$element = $cls::_decodeFromDER($identifier, $data, $idx);
244 191
		} catch (\LogicException $e) {
245
			// rethrow as a RuntimeException for unified exception handling
246 1
			throw new DecodeException(
247 1
				"Error while decoding " . self::tagToName($identifier->tag()) .
248 1
					 ".", 0, $e);
249
		}
250
		// if called in the context of a concrete class, check
251
		// that decoded type matches the type of a calling class
252 155
		$called_class = get_called_class();
253 155
		if (__CLASS__ != $called_class) {
254 152
			if (!$element instanceof $called_class) {
255 1
				throw new \UnexpectedValueException(
256 1
					"$called_class expected, got " . get_class($element) . ".");
257
			}
258 151
		}
259
		// update offset for the caller
260 154
		if (isset($offset)) {
261 16
			$offset = $idx;
262 16
		}
263 154
		return $element;
264
	}
265
	
266
	/**
267
	 *
268
	 * @see \ASN1\Feature\Encodable::toDER()
269
	 * @return string
270
	 */
271 110
	public function toDER() {
272 110
		$identifier = new Identifier($this->typeClass(), 
273 110
			$this->isConstructed() ? Identifier::CONSTRUCTED : Identifier::PRIMITIVE, 
274 110
			$this->_typeTag);
275 110
		$content = $this->_encodedContentDER();
276 110
		$length = new Length(strlen($content));
277 110
		return $identifier->toDER() . $length->toDER() . $content;
278
	}
279
	
280
	/**
281
	 *
282
	 * @see \ASN1\Feature\ElementBase::tag()
283
	 * @return int
284
	 */
285 101
	public function tag() {
286 101
		return $this->_typeTag;
287
	}
288
	
289
	/**
290
	 *
291
	 * @see \ASN1\Feature\ElementBase::isType()
292
	 * @return bool
293
	 */
294 22
	public function isType($tag) {
295
		// if element is context specific
296 22
		if ($this->typeClass() == Identifier::CLASS_CONTEXT_SPECIFIC) {
297 2
			return false;
298
		}
299
		// negative tags identify an abstract pseudotype
300 20
		if ($tag < 0) {
301 4
			return $this->_isPseudoType($tag);
302
		}
303 16
		return $this->_isConcreteType($tag);
304
	}
305
	
306
	/**
307
	 *
308
	 * @see \ASN1\Feature\ElementBase::expectType()
309
	 * @return ElementBase
310
	 */
311 16
	public function expectType($tag) {
312 16
		if (!$this->isType($tag)) {
313 6
			throw new \UnexpectedValueException(
314 6
				self::tagToName($tag) . " expected, got " .
315 6
					 $this->_typeDescriptorString() . ".");
316
		}
317 10
		return $this;
318
	}
319
	
320
	/**
321
	 * Check whether the element is a concrete type of a given tag.
322
	 *
323
	 * @param int $tag
324
	 * @return bool
325
	 */
326 16
	private function _isConcreteType($tag) {
327
		// if tag doesn't match
328 16
		if ($this->tag() != $tag) {
329 4
			return false;
330
		}
331
		// if type is universal check that instance is of a correct class
332 12
		if ($this->typeClass() == Identifier::CLASS_UNIVERSAL) {
333 12
			$cls = self::_determineUniversalImplClass($tag);
334 12
			if (!$this instanceof $cls) {
335 1
				return false;
336
			}
337 11
		}
338 11
		return true;
339
	}
340
	
341
	/**
342
	 * Check whether the element is a pseudotype.
343
	 *
344
	 * @param int $tag
345
	 * @return bool
346
	 */
347 4
	private function _isPseudoType($tag) {
348
		switch ($tag) {
349 4
		case self::TYPE_STRING:
350 1
			return $this instanceof StringType;
351 3
		case self::TYPE_TIME:
352 2
			return $this instanceof TimeType;
353
		}
354 1
		return false;
355
	}
356
	
357
	/**
358
	 *
359
	 * @see \ASN1\Feature\ElementBase::isTagged()
360
	 * @return bool
361
	 */
362 19
	public function isTagged() {
363 19
		return $this instanceof TaggedType;
364
	}
365
	
366
	/**
367
	 *
368
	 * @see \ASN1\Feature\ElementBase::expectTagged()
369
	 * @return TaggedType
370
	 */
371 13
	public function expectTagged($tag = null) {
372 13
		if (!$this->isTagged()) {
373 1
			throw new \UnexpectedValueException(
374
				"Context specific element expected, got " .
375 1
					 Identifier::classToName($this->typeClass()) . ".");
376
		}
377 12
		if (isset($tag) && $this->tag() != $tag) {
378 5
			throw new \UnexpectedValueException(
379 5
				"Tag $tag expected, got " . $this->tag() . ".");
380
		}
381 7
		return $this;
382
	}
383
	
384
	/**
385
	 * Determine the class that implements the type.
386
	 *
387
	 * @param Identifier $identifier
388
	 * @return string Class name
389
	 */
390 193
	protected static function _determineImplClass(Identifier $identifier) {
391
		// tagged type
392 193
		if ($identifier->isContextSpecific()) {
393 23
			return TaggedType::class;
394
		}
395
		// universal class
396 185
		if ($identifier->isUniversal()) {
397 184
			return self::_determineUniversalImplClass(
398 184
				intval($identifier->tag()));
399
		}
400 1
		throw new \UnexpectedValueException(
401 1
			Identifier::classToName($identifier->typeClass()) . " " .
402 1
				 $identifier->tag() . " not implemented.");
403
	}
404
	
405
	/**
406
	 * Determine the class that implements an universal type of the given tag.
407
	 *
408
	 * @param int $tag
409
	 * @throws \UnexpectedValueException
410
	 * @return string Class name
411
	 */
412 195
	protected static function _determineUniversalImplClass($tag) {
413 195
		if (!array_key_exists($tag, self::MAP_TAG_TO_CLASS)) {
414 1
			throw new \UnexpectedValueException(
415 1
				"Universal tag $tag not implemented.");
416
		}
417 194
		return self::MAP_TAG_TO_CLASS[$tag];
418
	}
419
	
420
	/**
421
	 * Get textual description of the type for debugging purposes.
422
	 *
423
	 * @return string
424
	 */
425 6
	protected function _typeDescriptorString() {
426 6
		if ($this->typeClass() == Identifier::CLASS_UNIVERSAL) {
427 4
			return self::tagToName($this->_typeTag);
428
		}
429 2
		return Identifier::classToName($this->typeClass()) . " TAG " .
430 2
			 $this->_typeTag;
431
	}
432
	
433
	/**
434
	 * Get human readable name for an universal tag.
435
	 *
436
	 * @param int $tag
437
	 * @return string
438
	 */
439 46
	public static function tagToName($tag) {
440 46
		if (!array_key_exists($tag, self::MAP_TYPE_TO_NAME)) {
441 1
			return "TAG $tag";
442
		}
443 45
		return self::MAP_TYPE_TO_NAME[$tag];
444
	}
445
}
446