1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace X509\CertificationPath\PathValidation; |
4
|
|
|
|
5
|
|
|
use CryptoUtil\Crypto\Crypto; |
6
|
|
|
use X509\Certificate\Certificate; |
7
|
|
|
use X509\Certificate\Extension\Extension; |
8
|
|
|
use X509\Certificate\TBSCertificate; |
9
|
|
|
use X509\CertificationPath\Exception\PathValidationException; |
10
|
|
|
|
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* Implements certification path validation. |
14
|
|
|
* |
15
|
|
|
* @link https://tools.ietf.org/html/rfc5280#section-6 |
16
|
|
|
*/ |
17
|
|
|
class PathValidator |
18
|
|
|
{ |
19
|
|
|
/** |
20
|
|
|
* Crypto engine. |
21
|
|
|
* |
22
|
|
|
* @var Crypto $_crypto |
23
|
|
|
*/ |
24
|
|
|
protected $_crypto; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Path validation configuration. |
28
|
|
|
* |
29
|
|
|
* @var PathValidationConfig $_config |
30
|
|
|
*/ |
31
|
|
|
protected $_config; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Certification path. |
35
|
|
|
* |
36
|
|
|
* @var Certificate[] $_certificates |
37
|
|
|
*/ |
38
|
|
|
protected $_certificates; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Certification path trust anchor. |
42
|
|
|
* |
43
|
|
|
* @var Certificate $_trustAnchor |
44
|
|
|
*/ |
45
|
|
|
protected $_trustAnchor; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Constructor |
49
|
|
|
* |
50
|
|
|
* @param Crypto $crypto |
51
|
|
|
* @param PathValidationConfig $config |
52
|
|
|
* @param Certificate ...$certificates |
53
|
|
|
*/ |
54
|
23 |
|
public function __construct(Crypto $crypto, PathValidationConfig $config, |
55
|
|
|
Certificate ...$certificates) { |
56
|
23 |
|
if (!count($certificates)) { |
57
|
|
|
throw new \LogicException("No certificates."); |
58
|
|
|
} |
59
|
23 |
|
$this->_crypto = $crypto; |
60
|
23 |
|
$this->_config = $config; |
61
|
23 |
|
$this->_certificates = $certificates; |
62
|
|
|
// if trust anchor is explicitly given in configuration |
63
|
23 |
|
$this->_trustAnchor = $config->hasTrustAnchor() ? $config->trustAnchor() : reset( |
|
|
|
|
64
|
23 |
|
$certificates); |
65
|
23 |
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Validate certification path. |
69
|
|
|
* |
70
|
|
|
* @throws PathValidationException |
71
|
|
|
* @return PathValidationResult |
72
|
|
|
*/ |
73
|
23 |
|
public function validate() { |
74
|
23 |
|
$n = count($this->_certificates); |
75
|
23 |
|
if (!$n) { |
76
|
|
|
throw new \LogicException("No certificates."); |
77
|
|
|
} |
78
|
23 |
|
$state = ValidatorState::initialize($this->_config, $this->_trustAnchor, |
79
|
23 |
|
$n); |
80
|
23 |
|
for ($i = 0; $i < $n; ++$i) { |
81
|
|
|
// whether processing final certificate |
82
|
23 |
|
$state = $state->withIsFinal($i === $n - 1); |
83
|
23 |
|
$cert = $this->_certificates[$i]; |
84
|
|
|
// process certificate (section 6.1.3.) |
85
|
23 |
|
$state = $this->_processCertificate($state, $cert); |
86
|
22 |
|
if (!$state->isFinal()) { |
87
|
|
|
// prepare next certificate (section 6.1.4.) |
88
|
22 |
|
$state = $this->_prepareNext($state, $cert); |
89
|
18 |
|
} |
90
|
18 |
|
} |
91
|
|
|
// wrap-up (section 6.1.5.) |
92
|
12 |
|
$this->_wrapUp($state, $cert); |
|
|
|
|
93
|
|
|
// return outputs |
94
|
11 |
|
return new PathValidationResult($cert, $state->validPolicyTree(), |
95
|
11 |
|
$state->workingPublicKey(), $state->workingPublicKeyAlgorithm(), |
96
|
11 |
|
$state->workingPublicKeyParameters()); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Apply basic certificate processing according to RFC 5280 section 6.1.3. |
101
|
|
|
* |
102
|
|
|
* @link https://tools.ietf.org/html/rfc5280#section-6.1.3 |
103
|
|
|
* @param ValidatorState $state |
104
|
|
|
* @param Certificate $cert |
105
|
|
|
* @throws PathValidationException |
106
|
|
|
* @return ValidatorState |
107
|
|
|
*/ |
108
|
23 |
|
protected function _processCertificate(ValidatorState $state, |
109
|
|
|
Certificate $cert) { |
110
|
|
|
// (a.1) verify signature |
111
|
23 |
|
$this->_verifySignature($state, $cert); |
112
|
|
|
// (a.2) check validity period |
113
|
23 |
|
$this->_checkValidity($cert); |
114
|
|
|
// (a.3) check that certificate is not revoked |
115
|
22 |
|
$this->_checkRevocation($cert); |
116
|
|
|
// (a.4) check issuer |
117
|
22 |
|
$this->_checkIssuer($state, $cert); |
118
|
|
|
// (b)(c) if certificate is self-issued and it is not |
119
|
|
|
// the final certificate in the path, skip this step |
120
|
22 |
|
if (!($cert->isSelfIssued() && !$state->isFinal())) { |
121
|
|
|
// (b) check permitted subtrees |
122
|
15 |
|
$this->_checkPermittedSubtrees($state, $cert); |
|
|
|
|
123
|
|
|
// (c) check excluded subtrees |
124
|
15 |
|
$this->_checkExcludedSubtrees($state, $cert); |
|
|
|
|
125
|
15 |
|
} |
126
|
22 |
|
$extensions = $cert->tbsCertificate()->extensions(); |
127
|
22 |
|
if ($extensions->hasCertificatePolicies()) { |
128
|
|
|
// (d) process policy information |
129
|
7 |
|
if ($state->hasValidPolicyTree()) { |
130
|
6 |
|
$state = $this->_processPolicyInformation($state, $cert); |
131
|
6 |
|
} |
132
|
7 |
|
} else { |
133
|
|
|
// (e) certificate policies extension not present, |
134
|
|
|
// set the valid_policy_tree to NULL |
135
|
18 |
|
$state = $state->withValidPolicyTree(null); |
136
|
|
|
} |
137
|
|
|
// (f) check that explicit_policy > 0 or valid_policy_tree is set |
138
|
22 |
|
if (!($state->explicitPolicy() > 0 || $state->hasValidPolicyTree())) { |
139
|
1 |
|
throw new PathValidationException("Policy error."); |
140
|
|
|
} |
141
|
22 |
|
return $state; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Apply preparation for certificate i+1 according to rfc5280 section 6.1.4. |
146
|
|
|
* |
147
|
|
|
* @link https://tools.ietf.org/html/rfc5280#section-6.1.4 |
148
|
|
|
* @param ValidatorState $state |
149
|
|
|
* @param Certificate $cert |
150
|
|
|
* @return ValidatorState |
151
|
|
|
*/ |
152
|
22 |
|
protected function _prepareNext(ValidatorState $state, Certificate $cert) { |
153
|
22 |
|
$tbs_cert = $cert->tbsCertificate(); |
154
|
22 |
|
$extensions = $tbs_cert->extensions(); |
155
|
|
|
// (a)(b) if policy mappings extension is present |
156
|
22 |
|
if ($extensions->hasPolicyMappings()) { |
157
|
|
|
// (a) verify that anyPolicy mapping is not used |
158
|
3 |
|
if ($extensions->policyMappings()->hasAnyPolicyMapping()) { |
159
|
1 |
|
throw new PathValidationException("anyPolicy mapping found."); |
160
|
|
|
} |
161
|
|
|
// (b) process policy mappings |
162
|
2 |
|
$state = $this->_processPolicyMappings($state, $cert); |
163
|
2 |
|
} |
164
|
|
|
// (c) assign working_issuer_name |
165
|
21 |
|
$state = $state->withWorkingIssuerName($tbs_cert->subject()); |
166
|
|
|
// (d)(e)(f) |
|
|
|
|
167
|
21 |
|
$state = $this->_setPublicKeyState($state, $cert); |
168
|
|
|
// (g) if name constraints extension is present |
169
|
21 |
|
if ($extensions->hasNameConstraints()) { |
170
|
1 |
|
$state = $this->_processNameConstraints($state, $cert); |
171
|
1 |
|
} |
172
|
|
|
// (h) if certificate is not self-issued |
173
|
21 |
|
if (!$cert->isSelfIssued()) { |
174
|
|
|
// (h.1) |
175
|
6 |
|
if ($state->explicitPolicy() > 0) { |
176
|
5 |
|
$state = $state->withExplicitPolicy( |
177
|
5 |
|
$state->explicitPolicy() - 1); |
178
|
5 |
|
} |
179
|
|
|
// (h.2) |
180
|
6 |
|
if ($state->policyMapping() > 0) { |
181
|
5 |
|
$state = $state->withPolicyMapping($state->policyMapping() - 1); |
182
|
5 |
|
} |
183
|
|
|
// (h.3) |
184
|
6 |
|
if ($state->inhibitAnyPolicy() > 0) { |
185
|
6 |
|
$state = $state->withInhibitAnyPolicy( |
186
|
6 |
|
$state->inhibitAnyPolicy() - 1); |
187
|
6 |
|
} |
188
|
6 |
|
} |
189
|
|
|
// (i) if policy constraints extension is present |
190
|
21 |
|
if ($extensions->hasPolicyConstraints()) { |
191
|
2 |
|
$ext = $extensions->policyConstraints(); |
192
|
|
|
// (i.1) |
193
|
2 |
|
if ($ext->hasRequireExplicitPolicy() && |
194
|
2 |
|
$ext->requireExplicitPolicy() < $state->explicitPolicy()) { |
195
|
2 |
|
$state = $state->withExplicitPolicy( |
196
|
2 |
|
$ext->requireExplicitPolicy()); |
197
|
2 |
|
} |
198
|
|
|
// (i.2) |
199
|
2 |
|
if ($ext->hasInhibitPolicyMapping() && |
200
|
2 |
|
$ext->inhibitPolicyMapping() < $state->policyMapping()) { |
201
|
1 |
|
$state = $state->withPolicyMapping($ext->inhibitPolicyMapping()); |
202
|
1 |
|
} |
203
|
2 |
|
} |
204
|
|
|
// (j) if inhibit any policy extension is present |
205
|
21 |
|
if ($extensions->hasInhibitAnyPolicy()) { |
206
|
1 |
|
$ext = $extensions->inhibitAnyPolicy(); |
207
|
1 |
|
if ($ext->skipCerts() < $state->inhibitAnyPolicy()) { |
208
|
1 |
|
$state = $state->withInhibitAnyPolicy($ext->skipCerts()); |
209
|
1 |
|
} |
210
|
1 |
|
} |
211
|
|
|
// (k) check basic constraints |
212
|
21 |
|
$this->_processBasicContraints($cert); |
213
|
|
|
// (l) verify max_path_length |
214
|
19 |
|
if (!$cert->isSelfIssued()) { |
215
|
6 |
|
if ($state->maxPathLength() <= 0) { |
216
|
2 |
|
throw new PathValidationException( |
217
|
2 |
|
"Certification path length exceeded."); |
218
|
|
|
} |
219
|
4 |
|
$state = $state->withMaxPathLength($state->maxPathLength() - 1); |
220
|
4 |
|
} |
221
|
|
|
// (m) check pathLenContraint |
222
|
19 |
|
$state = $this->_processPathLengthContraint($state, $cert); |
223
|
|
|
// (n) check key usage |
224
|
19 |
|
if ($extensions->hasKeyUsage()) { |
225
|
7 |
|
$ext = $extensions->keyUsage(); |
226
|
7 |
|
if (!$ext->isKeyCertSign()) { |
227
|
1 |
|
throw new PathValidationException("keyCertSign usage not set."); |
228
|
|
|
} |
229
|
6 |
|
} |
230
|
|
|
// (o) process relevant extensions |
231
|
18 |
|
$state = $this->_processExtensions($state, $cert); |
232
|
18 |
|
return $state; |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* Apply wrap-up procedure according to RFC 5280 section 6.1.5. |
237
|
|
|
* |
238
|
|
|
* @link https://tools.ietf.org/html/rfc5280#section-6.1.5 |
239
|
|
|
* @param ValidatorState $state |
240
|
|
|
* @param Certificate $cert |
241
|
|
|
* @throws PathValidationException |
242
|
|
|
*/ |
243
|
12 |
|
protected function _wrapUp(ValidatorState $state, Certificate $cert) { |
244
|
12 |
|
$tbs_cert = $cert->tbsCertificate(); |
245
|
12 |
|
$extensions = $tbs_cert->extensions(); |
246
|
|
|
// (a) |
247
|
12 |
|
if ($state->explicitPolicy() > 0) { |
248
|
9 |
|
$state = $state->withExplicitPolicy($state->explicitPolicy() - 1); |
249
|
9 |
|
} |
250
|
|
|
// (b) |
251
|
12 |
|
if ($extensions->hasPolicyConstraints()) { |
252
|
2 |
|
$ext = $extensions->policyConstraints(); |
253
|
2 |
|
if ($ext->hasRequireExplicitPolicy() && |
254
|
2 |
|
$ext->requireExplicitPolicy() == 0) { |
255
|
1 |
|
$state = $state->withExplicitPolicy(0); |
256
|
1 |
|
} |
257
|
2 |
|
} |
258
|
|
|
// (c)(d)(e) |
|
|
|
|
259
|
12 |
|
$state = $this->_setPublicKeyState($state, $cert); |
260
|
|
|
// (f) process relevant extensions |
261
|
12 |
|
$state = $this->_processExtensions($state, $cert); |
262
|
|
|
// (g) intersection of valid_policy_tree and the initial-policy-set |
263
|
12 |
|
$state = $this->_calculatePolicyIntersection($state); |
264
|
|
|
// check that explicit_policy > 0 or valid_policy_tree is set |
265
|
12 |
|
if (!($state->explicitPolicy() > 0 || $state->hasValidPolicyTree())) { |
266
|
1 |
|
throw new PathValidationException("Policy error."); |
267
|
|
|
} |
268
|
|
|
// path validation succeeded |
269
|
11 |
|
} |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* Update working_public_key, working_public_key_parameters and |
273
|
|
|
* working_public_key_algorithm state variables from certificate. |
274
|
|
|
* |
275
|
|
|
* @param ValidatorState $state |
276
|
|
|
* @param Certificate $cert |
277
|
|
|
* @return ValidatorState |
278
|
|
|
*/ |
279
|
21 |
|
protected function _setPublicKeyState(ValidatorState $state, |
280
|
|
|
Certificate $cert) { |
281
|
21 |
|
$pk_info = $cert->tbsCertificate()->subjectPublicKeyInfo(); |
282
|
|
|
// assign working_public_key |
283
|
21 |
|
$state = $state->withWorkingPublicKey($pk_info); |
284
|
|
|
// assign working_public_key_parameters |
285
|
21 |
|
$params = ValidatorState::getAlgorithmParameters( |
286
|
21 |
|
$pk_info->algorithmIdentifier()); |
287
|
21 |
|
if (null !== $params) { |
288
|
21 |
|
$state = $state->withWorkingPublicKeyParameters($params); |
289
|
21 |
|
} else { |
290
|
|
|
// if algorithms differ, set parameters to null |
291
|
1 |
|
if ($pk_info->algorithmIdentifier()->oid() !== |
292
|
1 |
|
$state->workingPublicKeyAlgorithm()->oid()) { |
293
|
1 |
|
$state = $state->withWorkingPublicKeyParameters(null); |
294
|
1 |
|
} |
295
|
|
|
} |
296
|
|
|
// assign working_public_key_algorithm |
297
|
21 |
|
$state = $state->withWorkingPublicKeyAlgorithm( |
298
|
21 |
|
$pk_info->algorithmIdentifier()); |
299
|
21 |
|
return $state; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Verify certificate signature. |
304
|
|
|
* |
305
|
|
|
* @param ValidatorState $state |
306
|
|
|
* @param Certificate $cert |
307
|
|
|
* @throws PathValidationException |
308
|
|
|
*/ |
309
|
23 |
|
protected function _verifySignature(ValidatorState $state, Certificate $cert) { |
310
|
23 |
|
if (!$cert->verify($this->_crypto, $state->workingPublicKey())) { |
311
|
1 |
|
throw new PathValidationException( |
312
|
1 |
|
"Certificate signature doesn't match."); |
313
|
|
|
} |
314
|
23 |
|
} |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* Check certificate validity. |
318
|
|
|
* |
319
|
|
|
* @param Certificate $cert |
320
|
|
|
* @throws PathValidationException |
321
|
|
|
*/ |
322
|
23 |
|
protected function _checkValidity(Certificate $cert) { |
323
|
23 |
|
$refdt = $this->_config->dateTime(); |
324
|
23 |
|
$validity = $cert->tbsCertificate()->validity(); |
325
|
23 |
|
if ($validity->notBefore() |
326
|
23 |
|
->dateTime() |
327
|
23 |
|
->diff($refdt)->invert) { |
328
|
1 |
|
throw new PathValidationException( |
329
|
1 |
|
"Certificate validity period has not started."); |
330
|
|
|
} |
331
|
22 |
|
if ($refdt->diff($validity->notAfter() |
332
|
22 |
|
->dateTime())->invert) { |
333
|
1 |
|
throw new PathValidationException("Certificate has expired."); |
334
|
|
|
} |
335
|
22 |
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* Check certificate revocation. |
339
|
|
|
* |
340
|
|
|
* @param Certificate $cert |
341
|
|
|
*/ |
342
|
22 |
|
protected function _checkRevocation(Certificate $cert) { |
|
|
|
|
343
|
|
|
// @todo Implement CRL handling |
344
|
22 |
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* Check certificate issuer. |
348
|
|
|
* |
349
|
|
|
* @param ValidatorState $state |
350
|
|
|
* @param Certificate $cert |
351
|
|
|
* @throws PathValidationException |
352
|
|
|
*/ |
353
|
22 |
|
protected function _checkIssuer(ValidatorState $state, Certificate $cert) { |
354
|
22 |
|
if (!$cert->tbsCertificate() |
355
|
22 |
|
->issuer() |
356
|
22 |
|
->equals($state->workingIssuerName())) { |
|
|
|
|
357
|
1 |
|
throw new PathValidationException("Certification issuer mismatch."); |
358
|
|
|
} |
359
|
22 |
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* |
363
|
|
|
* @param ValidatorState $state |
364
|
|
|
* @param Certificate $cert |
365
|
|
|
*/ |
366
|
15 |
|
protected function _checkPermittedSubtrees(ValidatorState $state, |
367
|
|
|
Certificate $cert) { |
|
|
|
|
368
|
|
|
// @todo Implement |
369
|
15 |
|
$state->permittedSubtrees(); |
|
|
|
|
370
|
15 |
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* |
374
|
|
|
* @param ValidatorState $state |
375
|
|
|
* @param Certificate $cert |
376
|
|
|
*/ |
377
|
15 |
|
protected function _checkExcludedSubtrees(ValidatorState $state, |
378
|
|
|
Certificate $cert) { |
|
|
|
|
379
|
|
|
// @todo Implement |
380
|
15 |
|
$state->excludedSubtrees(); |
|
|
|
|
381
|
15 |
|
} |
382
|
|
|
|
383
|
|
|
/** |
384
|
|
|
* |
385
|
|
|
* @param ValidatorState $state |
386
|
|
|
* @param Certificate $cert |
387
|
|
|
* @return ValidatorState |
388
|
|
|
*/ |
389
|
6 |
|
protected function _processPolicyInformation(ValidatorState $state, |
390
|
|
|
Certificate $cert) { |
|
|
|
|
391
|
|
|
// @todo Implement |
392
|
6 |
|
return $state; |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
/** |
396
|
|
|
* |
397
|
|
|
* @param ValidatorState $state |
398
|
|
|
* @param Certificate $cert |
399
|
|
|
* @return ValidatorState |
400
|
|
|
*/ |
401
|
1 |
|
protected function _processNameConstraints(ValidatorState $state, |
402
|
|
|
Certificate $cert) { |
|
|
|
|
403
|
|
|
// @todo Implement |
404
|
1 |
|
return $state; |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* Process basic constraints extension. |
409
|
|
|
* |
410
|
|
|
* @param Certificate $cert |
411
|
|
|
* @throws PathValidationException |
412
|
|
|
*/ |
413
|
21 |
|
protected function _processBasicContraints(Certificate $cert) { |
414
|
21 |
|
if ($cert->tbsCertificate()->version() == TBSCertificate::VERSION_3) { |
415
|
18 |
|
$extensions = $cert->tbsCertificate()->extensions(); |
416
|
18 |
|
if (!$extensions->hasBasicConstraints()) { |
417
|
1 |
|
throw new PathValidationException( |
418
|
1 |
|
"v3 certificate must have basicConstraints extension."); |
419
|
|
|
} |
420
|
|
|
// verify that cA is set to TRUE |
421
|
17 |
|
if (!$extensions->basicConstraints()->isCA()) { |
422
|
1 |
|
throw new PathValidationException( |
423
|
1 |
|
"Certificate is not a CA certificate."); |
424
|
|
|
} |
425
|
16 |
|
} |
426
|
19 |
|
} |
427
|
|
|
|
428
|
|
|
/** |
429
|
|
|
* Process pathLenConstraint. |
430
|
|
|
* |
431
|
|
|
* @param ValidatorState $state |
432
|
|
|
* @param Certificate $cert |
433
|
|
|
* @return ValidatorState |
434
|
|
|
*/ |
435
|
19 |
|
protected function _processPathLengthContraint(ValidatorState $state, |
436
|
|
|
Certificate $cert) { |
437
|
19 |
|
$extensions = $cert->tbsCertificate()->extensions(); |
438
|
19 |
|
if ($extensions->hasBasicConstraints()) { |
439
|
16 |
|
$ext = $extensions->basicConstraints(); |
440
|
16 |
|
if ($ext->hasPathLen()) { |
441
|
12 |
|
if ($ext->pathLen() < $state->maxPathLength()) { |
442
|
8 |
|
$state = $state->withMaxPathLength($ext->pathLen()); |
443
|
8 |
|
} |
444
|
12 |
|
} |
445
|
16 |
|
} |
446
|
19 |
|
return $state; |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
/** |
450
|
|
|
* Process policy mappings extension. |
451
|
|
|
* |
452
|
|
|
* @param ValidatorState $state |
453
|
|
|
* @param Certificate $cert |
454
|
|
|
* @return ValidatorState |
455
|
|
|
*/ |
456
|
2 |
|
protected function _processPolicyMappings(ValidatorState $state, |
457
|
|
|
Certificate $cert) { |
|
|
|
|
458
|
|
|
// @todo Implement |
459
|
2 |
|
return $state; |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
/** |
463
|
|
|
* |
464
|
|
|
* @param ValidatorState $state |
465
|
|
|
* @param Certificate $cert |
466
|
|
|
* @return ValidatorState |
467
|
|
|
*/ |
468
|
18 |
|
protected function _processExtensions(ValidatorState $state, |
469
|
|
|
Certificate $cert) { |
|
|
|
|
470
|
|
|
// @todo Implement |
471
|
18 |
|
return $state; |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
/** |
475
|
|
|
* |
476
|
|
|
* @param ValidatorState $state |
477
|
|
|
* @return ValidatorState |
478
|
|
|
*/ |
479
|
12 |
|
protected function _calculatePolicyIntersection(ValidatorState $state) { |
480
|
|
|
// @todo Implement |
481
|
12 |
|
return $state; |
482
|
|
|
} |
483
|
|
|
} |
484
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.