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 ( 96d802...e5ec4d )
by Joni
04:39
created

TBSCertificate::withSubjectUniqueID()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4285
nc 1
cc 1
eloc 4
nop 1
crap 1
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 19
	public function __construct(Name $subject, PublicKeyInfo $pki, Name $issuer, 
113
			Validity $validity) {
114 19
		$this->_subject = $subject;
115 19
		$this->_subjectPublicKeyInfo = $pki;
116 19
		$this->_issuer = $issuer;
117 19
		$this->_validity = $validity;
118 19
		$this->_extensions = new Extensions();
119 19
	}
120
	
121
	/**
122
	 * Initialize from ASN.1.
123
	 *
124
	 * @param Sequence $seq
125
	 * @return self
126
	 */
127 14
	public static function fromASN1(Sequence $seq) {
128 14
		$idx = 0;
129 14
		if ($seq->hasTagged(0)) {
130 10
			$idx++;
131 10
			$version = intval(
132 10
				$seq->getTagged(0)
133 10
					->asExplicit()
134 10
					->asInteger()
135 10
					->number());
136 10
		} else {
137 4
			$version = self::VERSION_1;
138
		}
139 14
		$serial = $seq->at($idx++)
140 14
			->asInteger()
141 14
			->number();
142 14
		$algo = AlgorithmIdentifier::fromASN1($seq->at($idx++)->asSequence());
143 14
		if (!$algo instanceof SignatureAlgorithmIdentifier) {
144 1
			throw new \UnexpectedValueException(
145 1
				"Unsupported signature algorithm " . $algo->oid() . ".");
146
		}
147 13
		$issuer = Name::fromASN1($seq->at($idx++)->asSequence());
148 13
		$validity = Validity::fromASN1($seq->at($idx++)->asSequence());
149 13
		$subject = Name::fromASN1($seq->at($idx++)->asSequence());
150 13
		$pki = PublicKeyInfo::fromASN1($seq->at($idx++)->asSequence());
151 13
		$tbs_cert = new self($subject, $pki, $issuer, $validity);
152 13
		$tbs_cert->_version = $version;
153 13
		$tbs_cert->_serialNumber = $serial;
154 13
		$tbs_cert->_signature = $algo;
155 13 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 13 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 13
		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 13
		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 44
	public function hasVersion() {
385 44
		return isset($this->_version);
386
	}
387
	
388
	/**
389
	 * Get certificate version.
390
	 *
391
	 * @return int
392
	 */
393 44
	public function version() {
394 44
		if (!$this->hasVersion()) {
395 1
			throw new \LogicException("version not set.");
396
		}
397 43
		return $this->_version;
398
	}
399
	
400
	/**
401
	 * Check whether serial number is set.
402
	 *
403
	 * @return bool
404
	 */
405 47
	public function hasSerialNumber() {
406 47
		return isset($this->_serialNumber);
407
	}
408
	
409
	/**
410
	 * Get serial number.
411
	 *
412
	 * @return int|string Base 10 integer
413
	 */
414 47
	public function serialNumber() {
415 47
		if (!$this->hasSerialNumber()) {
416 1
			throw new \LogicException("serialNumber not set.");
417
		}
418 46
		return $this->_serialNumber;
419
	}
420
	
421
	/**
422
	 * Check whether signature algorithm is set.
423
	 *
424
	 * @return bool
425
	 */
426 44
	public function hasSignature() {
427 44
		return isset($this->_signature);
428
	}
429
	
430
	/**
431
	 * Get signature algorithm.
432
	 *
433
	 * @return SignatureAlgorithmIdentifier
434
	 */
435 44
	public function signature() {
436 44
		if (!$this->hasSignature()) {
437 1
			throw new \LogicException("signature not set.");
438
		}
439 43
		return $this->_signature;
440
	}
441
	
442
	/**
443
	 * Get issuer.
444
	 *
445
	 * @return Name
446
	 */
447 39
	public function issuer() {
448 39
		return $this->_issuer;
449
	}
450
	
451
	/**
452
	 * Get validity period.
453
	 *
454
	 * @return Validity
455
	 */
456 26
	public function validity() {
457 26
		return $this->_validity;
458
	}
459
	
460
	/**
461
	 * Get subject.
462
	 *
463
	 * @return Name
464
	 */
465 37
	public function subject() {
466 37
		return $this->_subject;
467
	}
468
	
469
	/**
470
	 * Get subject public key.
471
	 *
472
	 * @return PublicKeyInfo
473
	 */
474 33
	public function subjectPublicKeyInfo() {
475 33
		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 36
	public function extensions() {
526 36
		return $this->_extensions;
527
	}
528
	
529
	/**
530
	 * Generate ASN.1 structure.
531
	 *
532
	 * @return Sequence
533
	 */
534 41
	public function toASN1() {
535 41
		$elements = array();
536 41
		$version = $this->version();
537
		// if version is not default
538 41
		if ($version != self::VERSION_1) {
539 31
			$elements[] = new ExplicitlyTaggedType(0, new Integer($version));
540 31
		}
541 41
		$serial = $this->serialNumber();
542 41
		$signature = $this->signature();
543
		// add required elements
544 41
		array_push($elements, new Integer($serial), $signature->toASN1(), 
545 41
			$this->_issuer->toASN1(), $this->_validity->toASN1(), 
546 41
			$this->_subject->toASN1(), $this->_subjectPublicKeyInfo->toASN1());
547 41
		if (isset($this->_issuerUniqueID)) {
548 4
			$elements[] = new ImplicitlyTaggedType(1, 
549 4
				$this->_issuerUniqueID->toASN1());
550 4
		}
551 41
		if (isset($this->_subjectUniqueID)) {
552 4
			$elements[] = new ImplicitlyTaggedType(2, 
553 4
				$this->_subjectUniqueID->toASN1());
554 4
		}
555 41
		if (count($this->_extensions)) {
556 27
			$elements[] = new ExplicitlyTaggedType(3, 
557 27
				$this->_extensions->toASN1());
558 27
		}
559 41
		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