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
Branch php70 (9ac8e7)
by Joni
10:16 queued 03:54
created

PolicyTree::_processPolicy()   A

Complexity

Conditions 6
Paths 12

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 24
ccs 15
cts 15
cp 1
rs 9.2222
c 0
b 0
f 0
cc 6
nc 12
nop 2
crap 6
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace X509\CertificationPath\Policy;
6
7
use X509\Certificate\Certificate;
8
use X509\Certificate\Extension\CertificatePolicy\PolicyInformation;
9
use X509\CertificationPath\PathValidation\ValidatorState;
10
11
class PolicyTree
12
{
13
    /**
14
     * Root node at depth zero.
15
     *
16
     * @var PolicyNode|null
17
     */
18
    protected $_root;
19
    
20
    /**
21
     * Constructor.
22
     *
23
     * @param PolicyNode $root Initial root node
24
     */
25 49
    public function __construct(PolicyNode $root)
26
    {
27 49
        $this->_root = $root;
28 49
    }
29
    
30
    /**
31
     * Process policy information from the certificate.
32
     *
33
     * Certificate policies extension must be present.
34
     *
35
     * @param ValidatorState $state
36
     * @param Certificate $cert
37
     * @return ValidatorState
38
     */
39 14
    public function processPolicies(ValidatorState $state, Certificate $cert): ValidatorState
40
    {
41 14
        $policies = $cert->tbsCertificate()
42 14
            ->extensions()
43 14
            ->certificatePolicies();
44 14
        $tree = clone $this;
45
        // (d.1) for each policy P not equal to anyPolicy
46 14
        foreach ($policies as $policy) {
47 14
            if ($policy->isAnyPolicy()) {
48 6
                $tree->_processAnyPolicy($policy, $cert, $state);
49
            } else {
50 14
                $tree->_processPolicy($policy, $state);
51
            }
52
        }
53
        // if whole tree is pruned
54 14
        if (!$tree->_pruneTree($state->index() - 1)) {
55 1
            return $state->withoutValidPolicyTree();
56
        }
57 14
        return $state->withValidPolicyTree($tree);
58
    }
59
    
60
    /**
61
     * Process policy mappings from the certificate.
62
     *
63
     * @param ValidatorState $state
64
     * @param Certificate $cert
65
     * @return ValidatorState
66
     */
67 3
    public function processMappings(ValidatorState $state, Certificate $cert): ValidatorState
68
    {
69 3
        $tree = clone $this;
70 3
        if ($state->policyMapping() > 0) {
71 2
            $tree->_applyMappings($cert, $state);
72 1
        } else if ($state->policyMapping() == 0) {
73 1
            $tree->_deleteMappings($cert, $state);
74
        }
75
        // if whole tree is pruned
76 3
        if (!$tree->_root) {
77 1
            return $state->withoutValidPolicyTree();
78
        }
79 2
        return $state->withValidPolicyTree($tree);
80
    }
81
    
82
    /**
83
     * Calculate policy intersection as specified in Wrap-Up Procedure 6.1.5.g.
84
     *
85
     * @param ValidatorState $state
86
     * @param array $policies
87
     * @return ValidatorState
88
     */
89 6
    public function calculateIntersection(ValidatorState $state, array $policies): ValidatorState
90
    {
91 6
        $tree = clone $this;
92 6
        $valid_policy_node_set = $tree->_validPolicyNodeSet();
93
        // 2. If the valid_policy of any node in the valid_policy_node_set
94
        // is not in the user-initial-policy-set and is not anyPolicy,
95
        // delete this node and all its children.
96 6
        $valid_policy_node_set = array_filter($valid_policy_node_set,
97 6
            function (PolicyNode $node) use ($policies) {
98 6
                if ($node->isAnyPolicy()) {
99 4
                    return true;
100
                }
101 5
                if (in_array($node->validPolicy(), $policies)) {
102 3
                    return true;
103
                }
104 3
                $node->remove();
105 3
                return false;
106 6
            });
107
        // array of valid policy OIDs
108 6
        $valid_policy_set = array_map(
109 6
            function (PolicyNode $node) {
110 5
                return $node->validPolicy();
111 6
            }, $valid_policy_node_set);
112
        // 3. If the valid_policy_tree includes a node of depth n with
113
        // the valid_policy anyPolicy and the user-initial-policy-set 
114
        // is not any-policy
115 6
        foreach ($tree->_nodesAtDepth($state->index()) as $node) {
116 4
            if ($node->hasParent() && $node->isAnyPolicy()) {
117
                // a. Set P-Q to the qualifier_set in the node of depth n
118
                // with valid_policy anyPolicy.
119 1
                $pq = $node->qualifiers();
120
                // b. For each P-OID in the user-initial-policy-set that is not
121
                // the valid_policy of a node in the valid_policy_node_set,
122
                // create a child node whose parent is the node of depth n-1
123
                // with the valid_policy anyPolicy.
124 1
                $poids = array_diff($policies, $valid_policy_set);
125 1
                foreach ($tree->_nodesAtDepth($state->index() - 1) as $parent) {
126 1
                    if ($parent->isAnyPolicy()) {
127
                        // Set the values in the child node as follows: 
128
                        // set the valid_policy to P-OID, set the qualifier_set
129
                        // to P-Q, and set the expected_policy_set to {P-OID}.
130 1
                        foreach ($poids as $poid) {
131 1
                            $parent->addChild(
132 1
                                new PolicyNode($poid, $pq, array($poid)));
133
                        }
134 1
                        break;
135
                    }
136
                }
137
                // c. Delete the node of depth n with the
138
                // valid_policy anyPolicy.
139 4
                $node->remove();
140
            }
141
        }
142
        // 4. If there is a node in the valid_policy_tree of depth n-1 or less
143
        // without any child nodes, delete that node. Repeat this step until
144
        // there are no nodes of depth n-1 or less without children.
145 6
        if (!$tree->_pruneTree($state->index() - 1)) {
146 2
            return $state->withoutValidPolicyTree();
147
        }
148 4
        return $state->withValidPolicyTree($tree);
149
    }
150
    
151
    /**
152
     * Get policies at given policy tree depth.
153
     *
154
     * @param int $i Depth in range 1..n
155
     * @return PolicyInformation[]
156
     */
157 6
    public function policiesAtDepth(int $i): array
158
    {
159 6
        $policies = array();
160 6
        foreach ($this->_nodesAtDepth($i) as $node) {
161 5
            $policies[] = new PolicyInformation($node->validPolicy(),
162 5
                ...$node->qualifiers());
163
        }
164 6
        return $policies;
165
    }
166
    
167
    /**
168
     * Process single policy information.
169
     *
170
     * @param PolicyInformation $policy
171
     * @param ValidatorState $state
172
     */
173 12
    protected function _processPolicy(PolicyInformation $policy,
174
        ValidatorState $state)
175
    {
176 12
        $p_oid = $policy->oid();
177 12
        $i = $state->index();
178 12
        $match_count = 0;
179
        // (d.1.i) for each node of depth i-1 in the valid_policy_tree...
180 12
        foreach ($this->_nodesAtDepth($i - 1) as $node) {
181
            // ...where P-OID is in the expected_policy_set
182 12
            if ($node->hasExpectedPolicy($p_oid)) {
183 6
                $node->addChild(
184 6
                    new PolicyNode($p_oid, $policy->qualifiers(), array($p_oid)));
185 12
                ++$match_count;
186
            }
187
        }
188
        // (d.1.ii) if there was no match in step (i)...
189 12
        if (!$match_count) {
190
            // ...and the valid_policy_tree includes a node of depth i-1 with
191
            // the valid_policy anyPolicy
192 11
            foreach ($this->_nodesAtDepth($i - 1) as $node) {
193 11
                if ($node->isAnyPolicy()) {
194 11
                    $node->addChild(
195 11
                        new PolicyNode($p_oid, $policy->qualifiers(),
196 11
                            array($p_oid)));
197
                }
198
            }
199
        }
200 12
    }
201
    
202
    /**
203
     * Process anyPolicy policy information.
204
     *
205
     * @param PolicyInformation $policy
206
     * @param Certificate $cert
207
     * @param ValidatorState $state
208
     */
209 6
    protected function _processAnyPolicy(PolicyInformation $policy,
210
        Certificate $cert, ValidatorState $state)
211
    {
212 6
        $i = $state->index();
213
        // if (a) inhibit_anyPolicy is greater than 0 or
214
        // (b) i<n and the certificate is self-issued
215 6
        if (!($state->inhibitAnyPolicy() > 0 ||
216 6
             ($i < $state->pathLength() && $cert->isSelfIssued()))) {
217 1
            return;
218
        }
219
        // for each node in the valid_policy_tree of depth i-1
220 6
        foreach ($this->_nodesAtDepth($i - 1) as $node) {
221
            // for each value in the expected_policy_set
222 6
            foreach ($node->expectedPolicies() as $p_oid) {
223
                // that does not appear in a child node
224 6
                if (!$node->hasChildWithValidPolicy($p_oid)) {
225 6
                    $node->addChild(
226 6
                        new PolicyNode($p_oid, $policy->qualifiers(),
227 6
                            array($p_oid)));
228
                }
229
            }
230
        }
231 6
    }
232
    
233
    /**
234
     * Apply policy mappings to the policy tree.
235
     *
236
     * @param Certificate $cert
237
     * @param ValidatorState $state
238
     */
239 2
    protected function _applyMappings(Certificate $cert, ValidatorState $state)
240
    {
241 2
        $policy_mappings = $cert->tbsCertificate()
242 2
            ->extensions()
243 2
            ->policyMappings();
244
        // (6.1.4. b.1.) for each node in the valid_policy_tree of depth i...
245 2
        foreach ($policy_mappings->flattenedMappings() as $idp => $sdps) {
246 2
            $match_count = 0;
247 2
            foreach ($this->_nodesAtDepth($state->index()) as $node) {
248
                // ...where ID-P is the valid_policy
249 2
                if ($node->validPolicy() == $idp) {
250
                    // set expected_policy_set to the set of subjectDomainPolicy
251
                    // values that are specified as equivalent to ID-P by
252
                    // the policy mappings extension
253 1
                    $node->setExpectedPolicies(...$sdps);
254 2
                    ++$match_count;
255
                }
256
            }
257
            // if no node of depth i in the valid_policy_tree has
258
            // a valid_policy of ID-P...
259 2
            if (!$match_count) {
260 2
                $this->_applyAnyPolicyMapping($cert, $state, $idp, $sdps);
261
            }
262
        }
263 2
    }
264
    
265
    /**
266
     * Apply anyPolicy mapping to the policy tree as specified in 6.1.4 (b)(1).
267
     *
268
     * @param Certificate $cert
269
     * @param ValidatorState $state
270
     * @param string $idp OID of the issuer domain policy
271
     * @param array $sdps Array of subject domain policy OIDs
272
     */
273 2
    protected function _applyAnyPolicyMapping(Certificate $cert,
274
        ValidatorState $state, $idp, array $sdps)
275
    {
276
        // (6.1.4. b.1.) ...but there is a node of depth i with
277
        // a valid_policy of anyPolicy
278 2
        foreach ($this->_nodesAtDepth($state->index()) as $node) {
279 2
            if ($node->isAnyPolicy()) {
280
                // then generate a child node of the node of depth i-1
281
                // that has a valid_policy of anyPolicy as follows...
282 2
                foreach ($this->_nodesAtDepth($state->index() - 1) as $node) {
0 ignored issues
show
Comprehensibility Bug introduced by
$node is overwriting a variable from outer foreach loop.
Loading history...
283 2
                    if ($node->isAnyPolicy()) {
284
                        // try to fetch qualifiers of anyPolicy certificate policy
285 2
                        $qualifiers = array();
0 ignored issues
show
Unused Code introduced by
The assignment to $qualifiers is dead and can be removed.
Loading history...
286
                        try {
287 2
                            $qualifiers = $cert->tbsCertificate()
288 2
                                ->extensions()
289 2
                                ->certificatePolicies()
290 2
                                ->anyPolicy()
291 1
                                ->qualifiers();
292 1
                        } catch (\LogicException $e) {
293
                            // if there's no policies or no qualifiers
294
                        }
295 2
                        $node->addChild(
296 2
                            new PolicyNode($idp, $qualifiers, $sdps));
297
                        // bail after first anyPolicy has been processed
298 2
                        break;
299
                    }
300
                }
301
                // bail after first anyPolicy has been processed
302 2
                break;
303
            }
304
        }
305 2
    }
306
    
307
    /**
308
     * Delete nodes as specified in 6.1.4 (b)(2).
309
     *
310
     * @param Certificate $cert
311
     * @param ValidatorState $state
312
     */
313 1
    protected function _deleteMappings(Certificate $cert, ValidatorState $state)
314
    {
315 1
        $idps = $cert->tbsCertificate()
316 1
            ->extensions()
317 1
            ->policyMappings()
318 1
            ->issuerDomainPolicies();
319
        // delete each node of depth i in the valid_policy_tree
320
        // where ID-P is the valid_policy
321 1
        foreach ($this->_nodesAtDepth($state->index()) as $node) {
322 1
            if (in_array($node->validPolicy(), $idps)) {
323 1
                $node->remove();
324
            }
325
        }
326 1
        $this->_pruneTree($state->index() - 1);
327 1
    }
328
    
329
    /**
330
     * Prune tree starting from given depth.
331
     *
332
     * @param int $depth
333
     * @return int The number of nodes left in a tree
334
     */
335 14
    protected function _pruneTree(int $depth): int
336
    {
337 14
        for ($i = $depth; $i > 0; --$i) {
338 11
            foreach ($this->_nodesAtDepth($i) as $node) {
339 11
                if (!count($node)) {
340 11
                    $node->remove();
341
                }
342
            }
343
        }
344
        // if root has no children left
345 14
        if (!count($this->_root)) {
346 4
            $this->_root = null;
347 4
            return 0;
348
        }
349 14
        return $this->_root->nodeCount();
0 ignored issues
show
Bug introduced by
The method nodeCount() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

349
        return $this->_root->/** @scrutinizer ignore-call */ nodeCount();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
350
    }
351
    
352
    /**
353
     * Get all nodes at given depth.
354
     *
355
     * @param int $i
356
     * @return PolicyNode[]
357
     */
358 16
    protected function _nodesAtDepth(int $i): array
359
    {
360 16
        if (!$this->_root) {
361 1
            return array();
362
        }
363 15
        $depth = 0;
364 15
        $nodes = array($this->_root);
365 15
        while ($depth < $i) {
366 12
            $nodes = self::_gatherChildren(...$nodes);
367 12
            if (!count($nodes)) {
368 2
                break;
369
            }
370 12
            ++$depth;
371
        }
372 15
        return $nodes;
373
    }
374
    
375
    /**
376
     * Get the valid policy node set as specified in spec 6.1.5.(g)(iii)1.
377
     *
378
     * @return PolicyNode[]
379
     */
380 7
    protected function _validPolicyNodeSet(): array
381
    {
382
        // 1. Determine the set of policy nodes whose parent nodes have
383
        // a valid_policy of anyPolicy. This is the valid_policy_node_set.
384 7
        $set = array();
385 7
        if (!$this->_root) {
386 1
            return $set;
387
        }
388
        // for each node in a tree
389 6
        $this->_root->walkNodes(
390 6
            function (PolicyNode $node) use (&$set) {
391 6
                $parents = $node->parents();
392
                // node has parents
393 6
                if (count($parents)) {
394
                    // check that each ancestor is an anyPolicy node
395 6
                    foreach ($parents as $ancestor) {
396 6
                        if (!$ancestor->isAnyPolicy()) {
397 6
                            return;
398
                        }
399
                    }
400 6
                    $set[] = $node;
401
                }
402 6
            });
403 6
        return $set;
404
    }
405
    
406
    /**
407
     * Gather all children of given nodes to a flattened array.
408
     *
409
     * @param PolicyNode ...$nodes
410
     * @return PolicyNode[]
411
     */
412 12
    private static function _gatherChildren(PolicyNode ...$nodes): array
413
    {
414 12
        $children = array();
415 12
        foreach ($nodes as $node) {
416 12
            $children = array_merge($children, $node->children());
417
        }
418 12
        return $children;
419
    }
420
}
421