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 ( cafc8c...f882cd )
by Joni
04:19
created

TBSCertificate::fromASN1()   B

Complexity

Conditions 6
Paths 18

Size

Total Lines 48
Code Lines 42

Duplication

Lines 12
Ratio 25 %

Code Coverage

Tests 43
CRAP Score 6.0031

Importance

Changes 0
Metric Value
dl 12
loc 48
ccs 43
cts 45
cp 0.9556
rs 8.551
c 0
b 0
f 0
cc 6
eloc 42
nc 18
nop 1
crap 6.0031
1
<?php
2
3
namespace X509\Certificate;
4
5
use ASN1\Element;
6
use ASN1\Type\Constructed\Sequence;
7
use ASN1\Type\Primitive\Integer;
8
use ASN1\Type\Tagged\ExplicitlyTaggedType;
9
use ASN1\Type\Tagged\ImplicitlyTaggedType;
10
use CryptoUtil\ASN1\AlgorithmIdentifier;
11
use CryptoUtil\ASN1\AlgorithmIdentifier\Feature\SignatureAlgorithmIdentifier;
12
use CryptoUtil\ASN1\PrivateKeyInfo;
13
use CryptoUtil\ASN1\PublicKeyInfo;
14
use CryptoUtil\Crypto\Crypto;
15
use X501\ASN1\Name;
16
use X509\Certificate\Extension\AuthorityKeyIdentifierExtension;
17
use X509\Certificate\Extension\Extension;
18
use X509\Certificate\Extension\SubjectKeyIdentifierExtension;
19
use X509\CertificationRequest\CertificationRequest;
20
21
22
/**
23
 * Implements <i>TBSCertificate</i> ASN.1 type.
24
 *
25
 * @link https://tools.ietf.org/html/rfc5280#section-4.1.2
26
 */
27
class TBSCertificate
28
{
29
	// Certificate version enumerations
30
	const VERSION_1 = 0;
31
	const VERSION_2 = 1;
32
	const VERSION_3 = 2;
33
	
34
	/**
35
	 * Certificate version.
36
	 *
37
	 * @var int
38
	 */
39
	protected $_version;
40
	
41
	/**
42
	 * Serial number.
43
	 *
44
	 * @var int|string
45
	 */
46
	protected $_serialNumber;
47
	
48
	/**
49
	 * Signature algorithm.
50
	 *
51
	 * @var SignatureAlgorithmIdentifier
52
	 */
53
	protected $_signature;
54
	
55
	/**
56
	 * Certificate issuer.
57
	 *
58
	 * @var Name $_issuer
59
	 */
60
	protected $_issuer;
61
	
62
	/**
63
	 * Certificate validity period.
64
	 *
65
	 * @var Validity $_validity
66
	 */
67
	protected $_validity;
68
	
69
	/**
70
	 * Certificate subject.
71
	 *
72
	 * @var Name $_subject
73
	 */
74
	protected $_subject;
75
	
76
	/**
77
	 * Subject public key.
78
	 *
79
	 * @var PublicKeyInfo $_subjectPublicKeyInfo
80
	 */
81
	protected $_subjectPublicKeyInfo;
82
	
83
	/**
84
	 * Issuer unique identifier.
85
	 *
86
	 * @var UniqueIdentifier|null $_issuerUniqueID
87
	 */
88
	protected $_issuerUniqueID;
89
	
90
	/**
91
	 * Subject unique identifier.
92
	 *
93
	 * @var UniqueIdentifier|null $_subjectUniqueID
94
	 */
95
	protected $_subjectUniqueID;
96
	
97
	/**
98
	 * Extensions.
99
	 *
100
	 * @var Extensions $_extensions
101
	 */
102
	protected $_extensions;
103
	
104
	/**
105
	 * Constructor
106
	 *
107
	 * @param Name $subject Certificate subject
108
	 * @param PublicKeyInfo $pki Subject public key
109
	 * @param Name $issuer Certificate issuer
110
	 * @param Validity $validity Validity period
111
	 */
112 18
	public function __construct(Name $subject, PublicKeyInfo $pki, Name $issuer, 
113
			Validity $validity) {
114 18
		$this->_subject = $subject;
115 18
		$this->_subjectPublicKeyInfo = $pki;
116 18
		$this->_issuer = $issuer;
117 18
		$this->_validity = $validity;
118 18
		$this->_extensions = new Extensions();
119 18
	}
120
	
121
	/**
122
	 * Initialize from ASN.1.
123
	 *
124
	 * @param Sequence $seq
125
	 * @return self
126
	 */
127 12
	public static function fromASN1(Sequence $seq) {
128 12
		$idx = 0;
129 12
		if ($seq->hasTagged(0)) {
130 9
			$idx++;
131 9
			$version = intval(
132 9
				$seq->getTagged(0)
133 9
					->asExplicit()
134 9
					->asInteger()
135 9
					->number());
136 9
		} else {
137 3
			$version = self::VERSION_1;
138
		}
139 12
		$serial = $seq->at($idx++)
140 12
			->asInteger()
141 12
			->number();
142 12
		$algo = AlgorithmIdentifier::fromASN1($seq->at($idx++)->asSequence());
143 12
		if (!$algo instanceof SignatureAlgorithmIdentifier) {
144
			throw new \UnexpectedValueException(
145
				"Unsupported signature algorithm " . $algo->oid() . ".");
146
		}
147 12
		$issuer = Name::fromASN1($seq->at($idx++)->asSequence());
148 12
		$validity = Validity::fromASN1($seq->at($idx++)->asSequence());
149 12
		$subject = Name::fromASN1($seq->at($idx++)->asSequence());
150 12
		$pki = PublicKeyInfo::fromASN1($seq->at($idx++)->asSequence());
151 12
		$tbs_cert = new self($subject, $pki, $issuer, $validity);
152 12
		$tbs_cert->_version = $version;
153 12
		$tbs_cert->_serialNumber = $serial;
154 12
		$tbs_cert->_signature = $algo;
155 12 View Code Duplication
		if ($seq->hasTagged(1)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
156 1
			$tbs_cert->_issuerUniqueID = UniqueIdentifier::fromASN1(
157 1
				$seq->getTagged(1)
158 1
					->asImplicit(Element::TYPE_BIT_STRING)
159 1
					->asBitString());
160 1
		}
161 12 View Code Duplication
		if ($seq->hasTagged(2)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162 1
			$tbs_cert->_subjectUniqueID = UniqueIdentifier::fromASN1(
163 1
				$seq->getTagged(2)
164 1
					->asImplicit(Element::TYPE_BIT_STRING)
165 1
					->asBitString());
166 1
		}
167 12
		if ($seq->hasTagged(3)) {
168 9
			$tbs_cert->_extensions = Extensions::fromASN1(
169 9
				$seq->getTagged(3)
170 9
					->asExplicit()
171 9
					->asSequence());
172 9
		}
173 12
		return $tbs_cert;
174
	}
175
	
176
	/**
177
	 * Initialize from certification request.
178
	 *
179
	 * Note that signature is not verified and must be done by the caller.
180
	 *
181
	 * @param CertificationRequest $cr
182
	 * @return self
183
	 */
184 1
	public static function fromCSR(CertificationRequest $cr) {
185 1
		$cri = $cr->certificationRequestInfo();
186 1
		$tbs_cert = new self($cri->subject(), $cri->subjectPKInfo(), new Name(), 
187 1
			Validity::fromStrings(null, null));
188
		// if CSR has Extension Request attribute
189 1
		if ($cri->hasAttributes()) {
190 1
			$attribs = $cri->attributes();
191 1
			if ($attribs->hasExtensionRequest()) {
192 1
				$tbs_cert = $tbs_cert->withExtensions(
193 1
					$attribs->extensionRequest()
194 1
						->extensions());
195 1
			}
196 1
		}
197
		// add Subject Key Identifier extension
198 1
		$tbs_cert = $tbs_cert->withAdditionalExtensions(
199 1
			new SubjectKeyIdentifierExtension(false, 
200 1
				$cri->subjectPKInfo()
201 1
					->keyIdentifier()));
202 1
		return $tbs_cert;
203
	}
204
	
205
	/**
206
	 * Get self with fields set from the issuer's certificate.
207
	 *
208
	 * Issuer shall be set to issuing certificate's subject.
209
	 * Authority key identifier extensions shall be added with a key identifier
210
	 * set to issuing certificate's public key identifier.
211
	 *
212
	 * @param Certificate $cert Issuing party's certificate
213
	 * @return self
214
	 */
215 1
	public function withIssuerCertificate(Certificate $cert) {
216 1
		$obj = clone $this;
217
		// set issuer DN from cert's subject
218 1
		$obj->_issuer = $cert->tbsCertificate()->subject();
219
		// add authority key identifier extension
220 1
		$key_id = $cert->tbsCertificate()
221 1
			->subjectPublicKeyInfo()
222 1
			->keyIdentifier();
223 1
		$obj->_extensions = $obj->_extensions->withExtensions(
224 1
			new AuthorityKeyIdentifierExtension(false, $key_id));
225 1
		return $obj;
226
	}
227
	
228
	/**
229
	 * Get self with given version.
230
	 *
231
	 * If version is not set, appropriate version is automatically
232
	 * determined during signing.
233
	 *
234
	 * @param int $version
235
	 * @return self
236
	 */
237 4
	public function withVersion($version) {
238 4
		$obj = clone $this;
239 4
		$obj->_version = $version;
240 4
		return $obj;
241
	}
242
	
243
	/**
244
	 * Get self with given serial number.
245
	 *
246
	 * @param int|string $serial Base 10 number
247
	 * @return self
248
	 */
249 5
	public function withSerialNumber($serial) {
250 5
		$obj = clone $this;
251 5
		$obj->_serialNumber = $serial;
252 5
		return $obj;
253
	}
254
	
255
	/**
256
	 * Get self with random positive serial number.
257
	 *
258
	 * @param int $size Number of random bytes
259
	 * @return self
260
	 */
261 1 View Code Duplication
	public function withRandomSerialNumber($size = 16) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
262
		// ensure that first byte is always non-zero and having first bit unset
263 1
		$num = gmp_init(mt_rand(1, 0x7f), 10);
264 1
		for ($i = 1; $i < $size; ++$i) {
265 1
			$num <<= 8;
266 1
			$num += mt_rand(0, 0xff);
267 1
		}
268 1
		return $this->withSerialNumber(gmp_strval($num, 10));
269
	}
270
	
271
	/**
272
	 * Get self with given signature algorithm.
273
	 *
274
	 * @param SignatureAlgorithmIdentifier $algo
275
	 * @return self
276
	 */
277 4
	public function withSignature(SignatureAlgorithmIdentifier $algo) {
278 4
		$obj = clone $this;
279 4
		$obj->_signature = $algo;
280 4
		return $obj;
281
	}
282
	
283
	/**
284
	 * Get self with given issuer.
285
	 *
286
	 * @param Name $issuer
287
	 * @return self
288
	 */
289 1
	public function withIssuer(Name $issuer) {
290 1
		$obj = clone $this;
291 1
		$obj->_issuer = $issuer;
292 1
		return $obj;
293
	}
294
	
295
	/**
296
	 * Get self with given validity.
297
	 *
298
	 * @param Validity $validity
299
	 * @return self
300
	 */
301 2
	public function withValidity(Validity $validity) {
302 2
		$obj = clone $this;
303 2
		$obj->_validity = $validity;
304 2
		return $obj;
305
	}
306
	
307
	/**
308
	 * Get self with given subject.
309
	 *
310
	 * @param Name $subject
311
	 * @return self
312
	 */
313 1
	public function withSubject(Name $subject) {
314 1
		$obj = clone $this;
315 1
		$obj->_subject = $subject;
316 1
		return $obj;
317
	}
318
	
319
	/**
320
	 * Get self with given subject public key info.
321
	 *
322
	 * @param PublicKeyInfo $pub_key_info
323
	 * @return self
324
	 */
325 1
	public function withSubjectPublicKeyInfo(PublicKeyInfo $pub_key_info) {
326 1
		$obj = clone $this;
327 1
		$obj->_subjectPublicKeyInfo = $pub_key_info;
328 1
		return $obj;
329
	}
330
	
331
	/**
332
	 * Get self with issuer unique ID.
333
	 *
334
	 * @param UniqueIdentifier $id
335
	 * @return self
336
	 */
337 4
	public function withIssuerUniqueID(UniqueIdentifier $id) {
338 4
		$obj = clone $this;
339 4
		$obj->_issuerUniqueID = $id;
340 4
		return $obj;
341
	}
342
	
343
	/**
344
	 * Get self with subject unique ID.
345
	 *
346
	 * @param UniqueIdentifier $id
347
	 * @return self
348
	 */
349 4
	public function withSubjectUniqueID(UniqueIdentifier $id) {
350 4
		$obj = clone $this;
351 4
		$obj->_subjectUniqueID = $id;
352 4
		return $obj;
353
	}
354
	
355
	/**
356
	 * Get self with given extensions.
357
	 *
358
	 * @param Extensions $extensions
359
	 * @return self
360
	 */
361 4
	public function withExtensions(Extensions $extensions) {
362 4
		$obj = clone $this;
363 4
		$obj->_extensions = $extensions;
364 4
		return $obj;
365
	}
366
	
367
	/**
368
	 * Get self with extensions added.
369
	 *
370
	 * @param Extension ...$exts One or more Extension objects
371
	 * @return self
372
	 */
373 3
	public function withAdditionalExtensions(Extension ...$exts) {
374 3
		$obj = clone $this;
375 3
		$obj->_extensions = $obj->_extensions->withExtensions(...$exts);
376 3
		return $obj;
377
	}
378
	
379
	/**
380
	 * Check whether version is set.
381
	 *
382
	 * @return bool
383
	 */
384 41
	public function hasVersion() {
385 41
		return isset($this->_version);
386
	}
387
	
388
	/**
389
	 * Get certificate version.
390
	 *
391
	 * @return int
392
	 */
393 41
	public function version() {
394 41
		if (!$this->hasVersion()) {
395 1
			throw new \LogicException("version not set.");
396
		}
397 40
		return $this->_version;
398
	}
399
	
400
	/**
401
	 * Check whether serial number is set.
402
	 *
403
	 * @return bool
404
	 */
405 44
	public function hasSerialNumber() {
406 44
		return isset($this->_serialNumber);
407
	}
408
	
409
	/**
410
	 * Get serial number.
411
	 *
412
	 * @return int|string Base 10 integer
413
	 */
414 44
	public function serialNumber() {
415 44
		if (!$this->hasSerialNumber()) {
416 1
			throw new \LogicException("serialNumber not set.");
417
		}
418 43
		return $this->_serialNumber;
419
	}
420
	
421
	/**
422
	 * Check whether signature algorithm is set.
423
	 *
424
	 * @return bool
425
	 */
426 41
	public function hasSignature() {
427 41
		return isset($this->_signature);
428
	}
429
	
430
	/**
431
	 * Get signature algorithm.
432
	 *
433
	 * @return SignatureAlgorithmIdentifier
434
	 */
435 41
	public function signature() {
436 41
		if (!$this->hasSignature()) {
437 1
			throw new \LogicException("signature not set.");
438
		}
439 40
		return $this->_signature;
440
	}
441
	
442
	/**
443
	 * Get issuer.
444
	 *
445
	 * @return Name
446
	 */
447 37
	public function issuer() {
448 37
		return $this->_issuer;
449
	}
450
	
451
	/**
452
	 * Get validity period.
453
	 *
454
	 * @return Validity
455
	 */
456 25
	public function validity() {
457 25
		return $this->_validity;
458
	}
459
	
460
	/**
461
	 * Get subject.
462
	 *
463
	 * @return Name
464
	 */
465 36
	public function subject() {
466 36
		return $this->_subject;
467
	}
468
	
469
	/**
470
	 * Get subject public key.
471
	 *
472
	 * @return PublicKeyInfo
473
	 */
474 31
	public function subjectPublicKeyInfo() {
475 31
		return $this->_subjectPublicKeyInfo;
476
	}
477
	
478
	/**
479
	 * Whether issuer unique identifier is present.
480
	 *
481
	 * @return bool
482
	 */
483 3
	public function hasIssuerUniqueID() {
484 3
		return isset($this->_issuerUniqueID);
485
	}
486
	
487
	/**
488
	 * Get issuerUniqueID.
489
	 *
490
	 * @return UniqueIdentifier
491
	 */
492 2
	public function issuerUniqueID() {
493 2
		if (!$this->hasIssuerUniqueID()) {
494 1
			throw new \LogicException("issuerUniqueID not set.");
495
		}
496 1
		return $this->_issuerUniqueID;
497
	}
498
	
499
	/**
500
	 * Whether subject unique identifier is present.
501
	 *
502
	 * @return bool
503
	 */
504 2
	public function hasSubjectUniqueID() {
505 2
		return isset($this->_subjectUniqueID);
506
	}
507
	
508
	/**
509
	 * Get subjectUniqueID.
510
	 *
511
	 * @return UniqueIdentifier
512
	 */
513 2
	public function subjectUniqueID() {
514 2
		if (!$this->hasSubjectUniqueID()) {
515 1
			throw new \LogicException("subjectUniqueID not set.");
516
		}
517 1
		return $this->_subjectUniqueID;
518
	}
519
	
520
	/**
521
	 * Get extensions.
522
	 *
523
	 * @return Extensions
524
	 */
525 35
	public function extensions() {
526 35
		return $this->_extensions;
527
	}
528
	
529
	/**
530
	 * Generate ASN.1 structure.
531
	 *
532
	 * @return Sequence
533
	 */
534 38
	public function toASN1() {
535 38
		$elements = array();
536 38
		$version = $this->version();
537
		// if version is not default
538 38
		if ($version != self::VERSION_1) {
539 29
			$elements[] = new ExplicitlyTaggedType(0, new Integer($version));
540 29
		}
541 38
		$serial = $this->serialNumber();
542 38
		$signature = $this->signature();
543
		// add required elements
544 38
		array_push($elements, new Integer($serial), $signature->toASN1(), 
545 38
			$this->_issuer->toASN1(), $this->_validity->toASN1(), 
546 38
			$this->_subject->toASN1(), $this->_subjectPublicKeyInfo->toASN1());
547 38
		if (isset($this->_issuerUniqueID)) {
548 3
			$elements[] = new ImplicitlyTaggedType(1, 
549 3
				$this->_issuerUniqueID->toASN1());
550 3
		}
551 38
		if (isset($this->_subjectUniqueID)) {
552 3
			$elements[] = new ImplicitlyTaggedType(2, 
553 3
				$this->_subjectUniqueID->toASN1());
554 3
		}
555 38
		if (count($this->_extensions)) {
556 25
			$elements[] = new ExplicitlyTaggedType(3, 
557 25
				$this->_extensions->toASN1());
558 25
		}
559 38
		return new Sequence(...$elements);
560
	}
561
	
562
	/**
563
	 * Create signed certificate.
564
	 *
565
	 * @param Crypto $crypto Crypto engine
566
	 * @param SignatureAlgorithmIdentifier $algo Algorithm used for signing
567
	 * @param PrivateKeyInfo $privkey_info Private key used for signing
568
	 * @return Certificate
569
	 */
570 9
	public function sign(Crypto $crypto, SignatureAlgorithmIdentifier $algo, 
571
			PrivateKeyInfo $privkey_info) {
572 9
		$tbsCert = clone $this;
573 9
		if (!isset($tbsCert->_version)) {
574 9
			$tbsCert->_version = $tbsCert->_determineVersion();
575 9
		}
576 9
		if (!isset($tbsCert->_serialNumber)) {
577 9
			$tbsCert->_serialNumber = 0;
578 9
		}
579 9
		$tbsCert->_signature = $algo;
580 9
		$data = $tbsCert->toASN1()->toDER();
581 9
		$signature = $crypto->sign($data, $privkey_info, $algo);
582 9
		return new Certificate($tbsCert, $algo, $signature);
583
	}
584
	
585
	/**
586
	 * Determine minimum version for the certificate.
587
	 *
588
	 * @return int
589
	 */
590 9
	protected function _determineVersion() {
591
		// if extensions are present
592 9
		if (count($this->_extensions)) {
593 3
			return self::VERSION_3;
594
		}
595
		// if UniqueIdentifier is present
596 6
		if (isset($this->_issuerUniqueID) || isset($this->_subjectUniqueID)) {
597 3
			return self::VERSION_2;
598
		}
599 3
		return self::VERSION_1;
600
	}
601
}
602