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 ( efe956...a640e0 )
by Joni
03:38
created

Element::isType()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
ccs 10
cts 10
cp 1
rs 8.8571
cc 5
eloc 10
nc 5
nop 1
crap 5
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
	 */
186
	abstract public function typeClass();
187
	
188
	/**
189
	 *
190
	 * @see \ASN1\Feature\ElementBase::isConstructed()
191
	 */
192
	abstract public function isConstructed();
193
	
194
	/**
195
	 * Get content encoded in DER.
196
	 *
197
	 * Returns the DER encoded content without identifier and length header
198
	 * octets.
199
	 *
200
	 * @return string
201
	 */
202
	abstract protected function _encodedContentDER();
203
	
204
	/**
205
	 * Decode type-specific element from DER.
206
	 *
207
	 * @param Identifier $identifier Pre-parsed identifier
208
	 * @param string $data DER data
209
	 * @param int $offset Offset in data to the next byte after identifier
210
	 * @throws DecodeException If decoding fails
211
	 * @return Element
212
	 */
213 1
	protected static function _decodeFromDER(Identifier $identifier, $data, 
214
			&$offset) {
215 1
		throw new \BadMethodCallException(
216 1
			__METHOD__ . " must be implemented in derived class.");
217
	}
218
	
219
	/**
220
	 *
221
	 * @see \ASN1\Feature\ElementBase::tag()
222
	 */
223 63
	public function tag() {
224 63
		return $this->_typeTag;
225
	}
226
	
227
	/**
228
	 * Decode element from DER data.
229
	 *
230
	 * @param string $data DER encoded data
231
	 * @param int|null $offset Reference to the variable that contains offset
232
	 *        into the data where to start parsing. Variable is updated to
233
	 *        the offset next to the parsed element. If null, start from offset
234
	 *        0.
235
	 * @throws DecodeException If decoding fails
236
	 * @throws \UnexpectedValueException If called in the context of an expected
237
	 *         type, but decoding yields another type
238
	 * @return self
239
	 */
240 194
	public static function fromDER($data, &$offset = null) {
241 194
		assert('is_string($data)', "got " . gettype($data));
242
		// decode identifier
243 193
		$idx = $offset ? $offset : 0;
244 193
		$identifier = Identifier::fromDER($data, $idx);
245
		// determine class that implements type specific decoding
246 193
		$cls = self::_determineImplClass($identifier);
247
		try {
248
			// decode remaining element
249 191
			$element = $cls::_decodeFromDER($identifier, $data, $idx);
250 191
		} catch (\LogicException $e) {
251
			// rethrow as a RuntimeException for unified exception handling
252 1
			throw new DecodeException(
253 1
				"Error while decoding " . self::tagToName($identifier->tag()) .
254 1
					 ".", 0, $e);
255
		}
256
		// if called in the context of a concrete class, check
257
		// that decoded type matches the type of a calling class
258 155
		$called_class = get_called_class();
259 155
		if (__CLASS__ != $called_class) {
260 152
			if (!($element instanceof $called_class)) {
261 1
				throw new \UnexpectedValueException(
262 1
					"$called_class expected, got " . get_class($element) . ".");
263
			}
264 151
		}
265
		// update offset for the caller
266 154
		if (isset($offset)) {
267 16
			$offset = $idx;
268 16
		}
269 154
		return $element;
270
	}
271
	
272
	/**
273
	 * Determine the class that implements the type.
274
	 *
275
	 * @param Identifier $identifier
276
	 * @return string Class name
277
	 */
278 193
	protected static function _determineImplClass(Identifier $identifier) {
279
		// tagged type
280 193
		if ($identifier->isContextSpecific()) {
281 23
			return TaggedType::class;
282
		}
283
		// universal class
284 185
		if ($identifier->isUniversal()) {
285 184
			return self::_determineUniversalImplClass(
286 184
				intval($identifier->tag()));
287
		}
288 1
		throw new \UnexpectedValueException(
289 1
			Identifier::classToName($identifier->typeClass()) . " " .
290 1
				 $identifier->tag() . " not implemented.");
291
	}
292
	
293
	/**
294
	 * Determine the class that implements an universal type of the given tag.
295
	 *
296
	 * @param int $tag
297
	 * @throws \UnexpectedValueException
298
	 * @return string Class name
299
	 */
300 192
	protected static function _determineUniversalImplClass($tag) {
301 192
		if (!array_key_exists($tag, self::MAP_TAG_TO_CLASS)) {
302 1
			throw new \UnexpectedValueException(
303 1
				"Universal tag $tag not implemented.");
304
		}
305 191
		return self::MAP_TAG_TO_CLASS[$tag];
306
	}
307
	
308
	/**
309
	 *
310
	 * @see \ASN1\Feature\Encodable::toDER()
311
	 */
312 109
	public function toDER() {
313 109
		$identifier = new Identifier($this->typeClass(), 
314 109
			$this->isConstructed() ? Identifier::CONSTRUCTED : Identifier::PRIMITIVE, 
315 109
			$this->_typeTag);
316 109
		$content = $this->_encodedContentDER();
317 109
		$length = new Length(strlen($content));
318 109
		return $identifier->toDER() . $length->toDER() . $content;
319
	}
320
	
321
	/**
322
	 * Check whether the element is a concrete type of a given tag.
323
	 *
324
	 * @param int $tag
325
	 * @return bool
326
	 */
327 13
	private function _isConcreteType($tag) {
328
		// if tag doesn't match
329 13
		if ($this->tag() != $tag) {
330 4
			return false;
331
		}
332
		// if type is universal check that instance is of a correct class
333 9
		if ($this->typeClass() == Identifier::CLASS_UNIVERSAL) {
334 9
			$cls = self::_determineUniversalImplClass($tag);
335 9
			if (!($this instanceof $cls)) {
336 1
				return false;
337
			}
338 8
		}
339 8
		return true;
340
	}
341
	
342
	/**
343
	 *
344
	 * @see \ASN1\Feature\ElementBase::isType()
345
	 */
346 19
	public function isType($tag) {
347
		// if element is context specific
348 19
		if ($this->typeClass() == Identifier::CLASS_CONTEXT_SPECIFIC) {
349 2
			return false;
350
		}
351
		// concrete type
352 17
		if ($tag >= 0) {
353 13
			return $this->_isConcreteType($tag);
354 4
		} else if (self::TYPE_STRING == $tag) { // string type
355 1
			return $this instanceof StringType;
356 3
		} else if (self::TYPE_TIME == $tag) { // time type
357 2
			return $this instanceof TimeType;
358
		}
359 1
		return false;
360
	}
361
	
362
	/**
363
	 *
364
	 * @see \ASN1\Feature\ElementBase::isTagged()
365
	 */
366 13
	public function isTagged() {
367 13
		return $this instanceof TaggedType;
368
	}
369
	
370
	/**
371
	 *
372
	 * @see \ASN1\Feature\ElementBase::expectType()
373
	 */
374 14
	public function expectType($tag) {
375 14
		if (!$this->isType($tag)) {
376 6
			throw new \UnexpectedValueException(
377 6
				self::tagToName($tag) . " expected, got " .
378 6
					 $this->_typeDescriptorString() . ".");
379
		}
380 8
		return $this;
381
	}
382
	
383
	/**
384
	 *
385
	 * @see \ASN1\Feature\ElementBase::expectTagged()
386
	 */
387 8
	public function expectTagged($tag = null) {
388 8
		if (!$this->isTagged()) {
389 1
			throw new \UnexpectedValueException(
390
				"Context specific element expected, got " .
391 1
					 Identifier::classToName($this->typeClass()) . ".");
392
		}
393 7
		if (isset($tag) && $this->tag() != $tag) {
394 3
			throw new \UnexpectedValueException(
395 3
				"Tag $tag expected, got " . $this->tag() . ".");
396
		}
397 4
		return $this;
398
	}
399
	
400
	/**
401
	 * Get textual description of the type for debugging purposes.
402
	 *
403
	 * @return string
404
	 */
405 6
	protected function _typeDescriptorString() {
406 6
		if ($this->typeClass() == Identifier::CLASS_UNIVERSAL) {
407 4
			return self::tagToName($this->_typeTag);
408
		}
409 2
		return Identifier::classToName($this->typeClass()) . " TAG " .
410 2
			 $this->_typeTag;
411
	}
412
	
413
	/**
414
	 * Get human readable name for an universal tag.
415
	 *
416
	 * @param int $tag
417
	 * @return string
418
	 */
419 16
	public static function tagToName($tag) {
420 16
		if (!array_key_exists($tag, self::MAP_TYPE_TO_NAME)) {
421 1
			return "TAG $tag";
422
		}
423 15
		return self::MAP_TYPE_TO_NAME[$tag];
424
	}
425
}
426