Holder::withObjectDigestInfo()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
nc 1
cc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace X509\AttributeCertificate;
6
7
use ASN1\Element;
8
use ASN1\Type\Constructed\Sequence;
9
use ASN1\Type\Tagged\ImplicitlyTaggedType;
10
use X509\Certificate\Certificate;
11
use X509\GeneralName\DirectoryName;
12
use X509\GeneralName\GeneralName;
13
use X509\GeneralName\GeneralNames;
14
15
/**
16
 * Implements <i>Holder</i> ASN.1 type.
17
 *
18
 * @link https://tools.ietf.org/html/rfc5755#section-4.1
19
 */
20
class Holder
21
{
22
    /**
23
     * Holder PKC's issuer and serial.
24
     *
25
     * @var IssuerSerial|null $_baseCertificateID
26
     */
27
    protected $_baseCertificateID;
28
    
29
    /**
30
     * Holder PKC's subject.
31
     *
32
     * @var GeneralNames|null $_entityName
33
     */
34
    protected $_entityName;
35
    
36
    /**
37
     * Linked object.
38
     *
39
     * @var ObjectDigestInfo|null $_objectDigestInfo
40
     */
41
    protected $_objectDigestInfo;
42
    
43
    /**
44
     * Constructor.
45
     *
46
     * @param IssuerSerial|null $issuer_serial
47
     * @param GeneralNames|null $entity_name
48
     */
49 21
    public function __construct(IssuerSerial $issuer_serial = null,
50
        GeneralNames $entity_name = null)
51
    {
52 21
        $this->_baseCertificateID = $issuer_serial;
53 21
        $this->_entityName = $entity_name;
54 21
    }
55
    
56
    /**
57
     * Initialize from a holder's public key certificate.
58
     *
59
     * @param Certificate $cert
60
     * @return self
61
     */
62 1
    public static function fromPKC(Certificate $cert): self
63
    {
64 1
        return new self(IssuerSerial::fromPKC($cert));
65
    }
66
    
67
    /**
68
     * Initialize from ASN.1.
69
     *
70
     * @param Sequence $seq
71
     */
72 7
    public static function fromASN1(Sequence $seq): self
73
    {
74 7
        $cert_id = null;
75 7
        $entity_name = null;
76 7
        $digest_info = null;
77 7
        if ($seq->hasTagged(0)) {
78 7
            $cert_id = IssuerSerial::fromASN1(
79 7
                $seq->getTagged(0)
80 7
                    ->asImplicit(Element::TYPE_SEQUENCE)
81 7
                    ->asSequence());
82
        }
83 7
        if ($seq->hasTagged(1)) {
84 3
            $entity_name = GeneralNames::fromASN1(
85 3
                $seq->getTagged(1)
86 3
                    ->asImplicit(Element::TYPE_SEQUENCE)
87 3
                    ->asSequence());
88
        }
89 7
        if ($seq->hasTagged(2)) {
90 1
            $digest_info = ObjectDigestInfo::fromASN1(
91 1
                $seq->getTagged(2)
92 1
                    ->asImplicit(Element::TYPE_SEQUENCE)
93 1
                    ->asSequence());
94
        }
95 7
        $obj = new self($cert_id, $entity_name);
96 7
        $obj->_objectDigestInfo = $digest_info;
97 7
        return $obj;
98
    }
99
    
100
    /**
101
     * Get self with base certificate ID.
102
     *
103
     * @param IssuerSerial $issuer
104
     * @return self
105
     */
106 1
    public function withBaseCertificateID(IssuerSerial $issuer): self
107
    {
108 1
        $obj = clone $this;
109 1
        $obj->_baseCertificateID = $issuer;
110 1
        return $obj;
111
    }
112
    
113
    /**
114
     * Get self with entity name.
115
     *
116
     * @param GeneralNames $names
117
     * @return self
118
     */
119 1
    public function withEntityName(GeneralNames $names): self
120
    {
121 1
        $obj = clone $this;
122 1
        $obj->_entityName = $names;
123 1
        return $obj;
124
    }
125
    
126
    /**
127
     * Get self with object digest info.
128
     *
129
     * @param ObjectDigestInfo $odi
130
     * @return self
131
     */
132 2
    public function withObjectDigestInfo(ObjectDigestInfo $odi): self
133
    {
134 2
        $obj = clone $this;
135 2
        $obj->_objectDigestInfo = $odi;
136 2
        return $obj;
137
    }
138
    
139
    /**
140
     * Check whether base certificate ID is present.
141
     *
142
     * @return bool
143
     */
144 2
    public function hasBaseCertificateID(): bool
145
    {
146 2
        return isset($this->_baseCertificateID);
147
    }
148
    
149
    /**
150
     * Get base certificate ID.
151
     *
152
     * @throws \LogicException
153
     * @return IssuerSerial
154
     */
155 2
    public function baseCertificateID(): IssuerSerial
156
    {
157 2
        if (!$this->hasBaseCertificateID()) {
158 1
            throw new \LogicException("baseCertificateID not set.");
159
        }
160 1
        return $this->_baseCertificateID;
161
    }
162
    
163
    /**
164
     * Check whether entity name is present.
165
     *
166
     * @return bool
167
     */
168 2
    public function hasEntityName(): bool
169
    {
170 2
        return isset($this->_entityName);
171
    }
172
    
173
    /**
174
     * Get entity name.
175
     *
176
     * @throws \LogicException
177
     * @return GeneralNames
178
     */
179 2
    public function entityName(): GeneralNames
180
    {
181 2
        if (!$this->hasEntityName()) {
182 1
            throw new \LogicException("entityName not set.");
183
        }
184 1
        return $this->_entityName;
185
    }
186
    
187
    /**
188
     * Check whether object digest info is present.
189
     *
190
     * @return bool
191
     */
192 2
    public function hasObjectDigestInfo(): bool
193
    {
194 2
        return isset($this->_objectDigestInfo);
195
    }
196
    
197
    /**
198
     * Get object digest info.
199
     *
200
     * @throws \LogicException
201
     * @return ObjectDigestInfo
202
     */
203 2
    public function objectDigestInfo(): ObjectDigestInfo
204
    {
205 2
        if (!$this->hasObjectDigestInfo()) {
206 1
            throw new \LogicException("objectDigestInfo not set.");
207
        }
208 1
        return $this->_objectDigestInfo;
209
    }
210
    
211
    /**
212
     * Generate ASN.1 structure.
213
     *
214
     * @return Sequence
215
     */
216 20
    public function toASN1(): Sequence
217
    {
218 20
        $elements = [];
219 20
        if (isset($this->_baseCertificateID)) {
220 20
            $elements[] = new ImplicitlyTaggedType(0,
221 20
                $this->_baseCertificateID->toASN1());
222
        }
223 20
        if (isset($this->_entityName)) {
224 4
            $elements[] = new ImplicitlyTaggedType(1,
225 4
                $this->_entityName->toASN1());
226
        }
227 20
        if (isset($this->_objectDigestInfo)) {
228 1
            $elements[] = new ImplicitlyTaggedType(2,
229 1
                $this->_objectDigestInfo->toASN1());
230
        }
231 20
        return new Sequence(...$elements);
232
    }
233
    
234
    /**
235
     * Check whether Holder identifies given certificate.
236
     *
237
     * @param Certificate $cert
238
     * @return boolean
239
     */
240 19
    public function identifiesPKC(Certificate $cert): bool
241
    {
242
        // if neither baseCertificateID nor entityName are present
243 19
        if (!$this->_baseCertificateID && !$this->_entityName) {
244 1
            return false;
245
        }
246
        // if baseCertificateID is present, but doesn't match
247 18
        if ($this->_baseCertificateID &&
248 18
             !$this->_baseCertificateID->identifiesPKC($cert)) {
249 3
            return false;
250
        }
251
        // if entityName is present, but doesn't match
252 15
        if ($this->_entityName && !$this->_checkEntityName($cert)) {
253 1
            return false;
254
        }
255 14
        return true;
256
    }
257
    
258
    /**
259
     * Check whether entityName matches the given certificate.
260
     *
261
     * @param Certificate $cert
262
     * @return boolean
263
     */
264 4
    private function _checkEntityName(Certificate $cert): bool
265
    {
266 4
        $name = $this->_entityName->firstDN();
267 4
        if ($cert->tbsCertificate()
268 4
            ->subject()
269 4
            ->equals($name)) {
270 2
            return true;
271
        }
272 2
        $exts = $cert->tbsCertificate()->extensions();
273 2
        if ($exts->hasSubjectAlternativeName()) {
274 2
            $ext = $exts->subjectAlternativeName();
275 2
            if ($this->_checkEntityAlternativeNames($ext->names())) {
276 1
                return true;
277
            }
278
        }
279 1
        return false;
280
    }
281
    
282
    /**
283
     * Check whether any of the subject alternative names match entityName.
284
     *
285
     * @param GeneralNames $san
286
     * @return boolean
287
     */
288 2
    private function _checkEntityAlternativeNames(GeneralNames $san): bool
289
    {
290
        // only directory names supported for now
291 2
        $name = $this->_entityName->firstDN();
292 2
        foreach ($san->allOf(GeneralName::TAG_DIRECTORY_NAME) as $dn) {
293 2
            if ($dn instanceof DirectoryName && $dn->dn()->equals($name)) {
294 2
                return true;
295
            }
296
        }
297 1
        return false;
298
    }
299
}
300