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.
Passed
Branch master (ad32f8)
by Joni
03:11
created

Element   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 421
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 38
c 3
b 0
f 0
lcom 1
cbo 4
dl 0
loc 421
ccs 96
cts 96
cp 1
rs 8.3999

19 Methods

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