CertificateBundle   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 207
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 6
dl 0
loc 207
ccs 63
cts 63
cp 1
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A __clone() 0 4 1
A fromPEMs() 0 8 1
A fromPEMBundle() 0 4 1
A withCertificates() 0 6 1
A withPEMBundle() 0 8 2
A withPEM() 0 6 1
A contains() 0 15 4
A allBySubjectKeyIdentifier() 0 8 2
A all() 0 4 1
A _getKeyIdMap() 0 15 4
A _getCertKeyId() 0 10 2
A count() 0 4 1
A getIterator() 0 4 1
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace X509\Certificate;
6
7
use Sop\CryptoEncoding\PEM;
8
use Sop\CryptoEncoding\PEMBundle;
9
10
/**
11
 * Implements a list of certificates.
12
 */
13
class CertificateBundle implements \Countable, \IteratorAggregate
14
{
15
    /**
16
     * Certificates.
17
     *
18
     * @var Certificate[] $_certs
19
     */
20
    protected $_certs;
21
    
22
    /**
23
     * Mapping from public key id to array of certificates.
24
     *
25
     * @var null|(Certificate[])[]
26
     */
27
    private $_keyIdMap;
28
    
29
    /**
30
     * Constructor.
31
     *
32
     * @param Certificate ...$certs Certificate objects
33
     */
34 17
    public function __construct(Certificate ...$certs)
35
    {
36 17
        $this->_certs = $certs;
37 17
    }
38
    
39
    /**
40
     * Reset internal cached variables on clone.
41
     */
42 1
    public function __clone()
43
    {
44 1
        $this->_keyIdMap = null;
45 1
    }
46
    
47
    /**
48
     * Initialize from PEMs.
49
     *
50
     * @param PEM ...$pems PEM objects
51
     * @return self
52
     */
53 2
    public static function fromPEMs(PEM ...$pems): self
54
    {
55 2
        $certs = array_map(
56 2
            function ($pem) {
57 2
                return Certificate::fromPEM($pem);
58 2
            }, $pems);
59 2
        return new self(...$certs);
60
    }
61
    
62
    /**
63
     * Initialize from PEM bundle.
64
     *
65
     * @param PEMBundle $pem_bundle
66
     * @return self
67
     */
68 1
    public static function fromPEMBundle(PEMBundle $pem_bundle): self
69
    {
70 1
        return self::fromPEMs(...$pem_bundle->all());
71
    }
72
    
73
    /**
74
     * Get self with certificates added.
75
     *
76
     * @param Certificate ...$cert
77
     * @return self
78
     */
79 1
    public function withCertificates(Certificate ...$cert): self
80
    {
81 1
        $obj = clone $this;
82 1
        $obj->_certs = array_merge($obj->_certs, $cert);
83 1
        return $obj;
84
    }
85
    
86
    /**
87
     * Get self with certificates from PEMBundle added.
88
     *
89
     * @param PEMBundle $pem_bundle
90
     * @return self
91
     */
92 1
    public function withPEMBundle(PEMBundle $pem_bundle): self
93
    {
94 1
        $certs = $this->_certs;
95 1
        foreach ($pem_bundle as $pem) {
96 1
            $certs[] = Certificate::fromPEM($pem);
97
        }
98 1
        return new self(...$certs);
99
    }
100
    
101
    /**
102
     * Get self with single certificate from PEM added.
103
     *
104
     * @param PEM $pem
105
     * @return self
106
     */
107 1
    public function withPEM(PEM $pem): self
108
    {
109 1
        $certs = $this->_certs;
110 1
        $certs[] = Certificate::fromPEM($pem);
111 1
        return new self(...$certs);
112
    }
113
    
114
    /**
115
     * Check whether bundle contains a given certificate.
116
     *
117
     * @param Certificate $cert
118
     * @return bool
119
     */
120 3
    public function contains(Certificate $cert): bool
121
    {
122 3
        $id = self::_getCertKeyId($cert);
123 3
        $map = $this->_getKeyIdMap();
124 3
        if (!isset($map[$id])) {
125 1
            return false;
126
        }
127 2
        foreach ($map[$id] as $c) {
128
            /** @var Certificate $c */
129 2
            if ($cert->equals($c)) {
130 2
                return true;
131
            }
132
        }
133 1
        return false;
134
    }
135
    
136
    /**
137
     * Get all certificates that have given subject key identifier.
138
     *
139
     * @param string $id
140
     * @return Certificate[]
141
     */
142 11
    public function allBySubjectKeyIdentifier(string $id): array
143
    {
144 11
        $map = $this->_getKeyIdMap();
145 11
        if (!isset($map[$id])) {
146 7
            return array();
147
        }
148 9
        return $map[$id];
149
    }
150
    
151
    /**
152
     * Get all certificates in a bundle.
153
     *
154
     * @return Certificate[]
155
     */
156 1
    public function all(): array
157
    {
158 1
        return $this->_certs;
159
    }
160
    
161
    /**
162
     * Get certificate mapping by public key id.
163
     *
164
     * @return (Certificate[])[]
165
     */
166 14
    private function _getKeyIdMap(): array
167
    {
168
        // lazily build mapping
169 14
        if (!isset($this->_keyIdMap)) {
170 13
            $this->_keyIdMap = array();
171 13
            foreach ($this->_certs as $cert) {
172 13
                $id = self::_getCertKeyId($cert);
173 13
                if (!isset($this->_keyIdMap[$id])) {
174 13
                    $this->_keyIdMap[$id] = array();
175
                }
176 13
                array_push($this->_keyIdMap[$id], $cert);
177
            }
178
        }
179 14
        return $this->_keyIdMap;
180
    }
181
    
182
    /**
183
     * Get public key id for the certificate.
184
     *
185
     * @param Certificate $cert
186
     * @return string
187
     */
188 13
    private static function _getCertKeyId(Certificate $cert): string
189
    {
190 13
        $exts = $cert->tbsCertificate()->extensions();
191 13
        if ($exts->hasSubjectKeyIdentifier()) {
192 11
            return $exts->subjectKeyIdentifier()->keyIdentifier();
193
        }
194 2
        return $cert->tbsCertificate()
195 2
            ->subjectPublicKeyInfo()
196 2
            ->keyIdentifier();
197
    }
198
    
199
    /**
200
     *
201
     * @see \Countable::count()
202
     * @return int
203
     */
204 4
    public function count(): int
205
    {
206 4
        return count($this->_certs);
207
    }
208
    
209
    /**
210
     * Get iterator for certificates.
211
     *
212
     * @see \IteratorAggregate::getIterator()
213
     * @return \ArrayIterator
214
     */
215 1
    public function getIterator(): \ArrayIterator
216
    {
217 1
        return new \ArrayIterator($this->_certs);
218
    }
219
}
220