AttributeCertificateInfo::holder()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
nc 1
cc 1
nop 0
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\Primitive\Integer;
10
use Sop\CryptoBridge\Crypto;
11
use Sop\CryptoTypes\AlgorithmIdentifier\AlgorithmIdentifier;
12
use Sop\CryptoTypes\AlgorithmIdentifier\Feature\SignatureAlgorithmIdentifier;
13
use Sop\CryptoTypes\Asymmetric\PrivateKeyInfo;
14
use X509\Certificate\Extensions;
15
use X509\Certificate\UniqueIdentifier;
16
use X509\Certificate\Extension\Extension;
17
18
/**
19
 * Implements <i>AttributeCertificateInfo</i> ASN.1 type.
20
 *
21
 * @link https://tools.ietf.org/html/rfc5755#section-4.1
22
 */
23
class AttributeCertificateInfo
24
{
25
    const VERSION_2 = 1;
26
    
27
    /**
28
     * AC version.
29
     *
30
     * @var int $_version
31
     */
32
    protected $_version;
33
    
34
    /**
35
     * AC holder.
36
     *
37
     * @var Holder $_holder
38
     */
39
    protected $_holder;
40
    
41
    /**
42
     * AC issuer.
43
     *
44
     * @var AttCertIssuer $_issuer
45
     */
46
    protected $_issuer;
47
    
48
    /**
49
     * Signature algorithm identifier.
50
     *
51
     * @var SignatureAlgorithmIdentifier $_signature
52
     */
53
    protected $_signature;
54
    
55
    /**
56
     * AC serial number.
57
     *
58
     * @var string $_serialNumber
59
     */
60
    protected $_serialNumber;
61
    
62
    /**
63
     * Validity period.
64
     *
65
     * @var AttCertValidityPeriod $_attrCertValidityPeriod
66
     */
67
    protected $_attrCertValidityPeriod;
68
    
69
    /**
70
     * Attributes.
71
     *
72
     * @var Attributes $_attributes
73
     */
74
    protected $_attributes;
75
    
76
    /**
77
     * Issuer unique identifier.
78
     *
79
     * @var UniqueIdentifier|null $_issuerUniqueID
80
     */
81
    protected $_issuerUniqueID;
82
    
83
    /**
84
     * Extensions.
85
     *
86
     * @var Extensions $_extensions
87
     */
88
    protected $_extensions;
89
    
90
    /**
91
     * Constructor.
92
     *
93
     * @param Holder $holder AC holder
94
     * @param AttCertIssuer $issuer AC issuer
95
     * @param AttCertValidityPeriod $validity Validity
96
     * @param Attributes $attribs Attributes
97
     */
98 8
    public function __construct(Holder $holder, AttCertIssuer $issuer,
99
        AttCertValidityPeriod $validity, Attributes $attribs)
100
    {
101 8
        $this->_version = self::VERSION_2;
102 8
        $this->_holder = $holder;
103 8
        $this->_issuer = $issuer;
104 8
        $this->_attrCertValidityPeriod = $validity;
105 8
        $this->_attributes = $attribs;
106 8
        $this->_extensions = new Extensions();
107 8
    }
108
    
109
    /**
110
     * Initialize from ASN.1.
111
     *
112
     * @param Sequence $seq
113
     * @throws \UnexpectedValueException
114
     * @return self
115
     */
116 7
    public static function fromASN1(Sequence $seq): self
117
    {
118 7
        $version = $seq->at(0)
119 7
            ->asInteger()
120 7
            ->intNumber();
121 7
        if ($version != self::VERSION_2) {
122 1
            throw new \UnexpectedValueException("Version must be 2.");
123
        }
124 6
        $holder = Holder::fromASN1($seq->at(1)->asSequence());
125 6
        $issuer = AttCertIssuer::fromASN1($seq->at(2));
126 6
        $signature = AlgorithmIdentifier::fromASN1($seq->at(3)->asSequence());
127 6
        if (!$signature instanceof SignatureAlgorithmIdentifier) {
128 1
            throw new \UnexpectedValueException(
129 1
                "Unsupported signature algorithm " . $signature->oid() . ".");
130
        }
131 5
        $serial = $seq->at(4)
132 5
            ->asInteger()
133 5
            ->number();
134 5
        $validity = AttCertValidityPeriod::fromASN1($seq->at(5)->asSequence());
135 5
        $attribs = Attributes::fromASN1($seq->at(6)->asSequence());
136 5
        $obj = new self($holder, $issuer, $validity, $attribs);
137 5
        $obj->_signature = $signature;
138 5
        $obj->_serialNumber = $serial;
139 5
        $idx = 7;
140 5
        if ($seq->has($idx, Element::TYPE_BIT_STRING)) {
141 1
            $obj->_issuerUniqueID = UniqueIdentifier::fromASN1(
142 1
                $seq->at($idx++)->asBitString());
143
        }
144 5
        if ($seq->has($idx, Element::TYPE_SEQUENCE)) {
145 3
            $obj->_extensions = Extensions::fromASN1(
146 3
                $seq->at($idx++)->asSequence());
147
        }
148 5
        return $obj;
149
    }
150
    
151
    /**
152
     * Get self with holder.
153
     *
154
     * @param Holder $holder
155
     * @return self
156
     */
157 1
    public function withHolder(Holder $holder): self
158
    {
159 1
        $obj = clone $this;
160 1
        $obj->_holder = $holder;
161 1
        return $obj;
162
    }
163
    
164
    /**
165
     * Get self with issuer.
166
     *
167
     * @param AttCertIssuer $issuer
168
     * @return self
169
     */
170 1
    public function withIssuer(AttCertIssuer $issuer): self
171
    {
172 1
        $obj = clone $this;
173 1
        $obj->_issuer = $issuer;
174 1
        return $obj;
175
    }
176
    
177
    /**
178
     * Get self with signature algorithm identifier.
179
     *
180
     * @param SignatureAlgorithmIdentifier $algo
181
     * @return self
182
     */
183 3
    public function withSignature(SignatureAlgorithmIdentifier $algo): self
184
    {
185 3
        $obj = clone $this;
186 3
        $obj->_signature = $algo;
187 3
        return $obj;
188
    }
189
    
190
    /**
191
     * Get self with serial number.
192
     *
193
     * @param int|string $serial
194
     * @return self
195
     */
196 4
    public function withSerialNumber($serial): self
197
    {
198 4
        $obj = clone $this;
199 4
        $obj->_serialNumber = strval($serial);
200 4
        return $obj;
201
    }
202
    
203
    /**
204
     * Get self with random positive serial number.
205
     *
206
     * @param int $size Number of random bytes
207
     * @return self
208
     */
209 1
    public function withRandomSerialNumber(int $size = 16): self
210
    {
211
        // ensure that first byte is always non-zero and having first bit unset
212 1
        $num = gmp_init(mt_rand(1, 0x7f), 10);
213 1
        for ($i = 1; $i < $size; ++$i) {
214 1
            $num <<= 8;
215 1
            $num += mt_rand(0, 0xff);
216
        }
217 1
        return $this->withSerialNumber(gmp_strval($num, 10));
218
    }
219
    
220
    /**
221
     * Get self with validity period.
222
     *
223
     * @param AttCertValidityPeriod $validity
224
     * @return self
225
     */
226 1
    public function withValidity(AttCertValidityPeriod $validity): self
227
    {
228 1
        $obj = clone $this;
229 1
        $obj->_attrCertValidityPeriod = $validity;
230 1
        return $obj;
231
    }
232
    
233
    /**
234
     * Get self with attributes.
235
     *
236
     * @param Attributes $attribs
237
     * @return self
238
     */
239 1
    public function withAttributes(Attributes $attribs): self
240
    {
241 1
        $obj = clone $this;
242 1
        $obj->_attributes = $attribs;
243 1
        return $obj;
244
    }
245
    
246
    /**
247
     * Get self with issuer unique identifier.
248
     *
249
     * @param UniqueIdentifier $uid
250
     * @return self
251
     */
252 2
    public function withIssuerUniqueID(UniqueIdentifier $uid): self
253
    {
254 2
        $obj = clone $this;
255 2
        $obj->_issuerUniqueID = $uid;
256 2
        return $obj;
257
    }
258
    
259
    /**
260
     * Get self with extensions.
261
     *
262
     * @param Extensions $extensions
263
     * @return self
264
     */
265 2
    public function withExtensions(Extensions $extensions): self
266
    {
267 2
        $obj = clone $this;
268 2
        $obj->_extensions = $extensions;
269 2
        return $obj;
270
    }
271
    
272
    /**
273
     * Get self with extensions added.
274
     *
275
     * @param Extension ...$exts One or more Extension objects
276
     * @return self
277
     */
278 1
    public function withAdditionalExtensions(Extension ...$exts): self
279
    {
280 1
        $obj = clone $this;
281 1
        $obj->_extensions = $obj->_extensions->withExtensions(...$exts);
282 1
        return $obj;
283
    }
284
    
285
    /**
286
     * Get version.
287
     *
288
     * @return int
289
     */
290 1
    public function version(): int
291
    {
292 1
        return $this->_version;
293
    }
294
    
295
    /**
296
     * Get AC holder.
297
     *
298
     * @return Holder
299
     */
300 14
    public function holder(): Holder
301
    {
302 14
        return $this->_holder;
303
    }
304
    
305
    /**
306
     * Get AC issuer.
307
     *
308
     * @return AttCertIssuer
309
     */
310 12
    public function issuer(): AttCertIssuer
311
    {
312 12
        return $this->_issuer;
313
    }
314
    
315
    /**
316
     * Check whether signature is set.
317
     *
318
     * @return bool
319
     */
320 21
    public function hasSignature(): bool
321
    {
322 21
        return isset($this->_signature);
323
    }
324
    
325
    /**
326
     * Get signature algorithm identifier.
327
     *
328
     * @return SignatureAlgorithmIdentifier
329
     */
330 21
    public function signature(): SignatureAlgorithmIdentifier
331
    {
332 21
        if (!$this->hasSignature()) {
333 1
            throw new \LogicException("signature not set.");
334
        }
335 20
        return $this->_signature;
336
    }
337
    
338
    /**
339
     * Check whether serial number is present.
340
     *
341
     * @return bool
342
     */
343 22
    public function hasSerialNumber(): bool
344
    {
345 22
        return isset($this->_serialNumber);
346
    }
347
    
348
    /**
349
     * Get AC serial number.
350
     *
351
     * @return string
352
     */
353 22
    public function serialNumber(): string
354
    {
355 22
        if (!$this->hasSerialNumber()) {
356 1
            throw new \LogicException("serialNumber not set.");
357
        }
358 21
        return $this->_serialNumber;
359
    }
360
    
361
    /**
362
     * Get validity period.
363
     *
364
     * @return AttCertValidityPeriod
365
     */
366 6
    public function validityPeriod(): AttCertValidityPeriod
367
    {
368 6
        return $this->_attrCertValidityPeriod;
369
    }
370
    
371
    /**
372
     * Get attributes.
373
     *
374
     * @return Attributes
375
     */
376 1
    public function attributes(): Attributes
377
    {
378 1
        return $this->_attributes;
379
    }
380
    
381
    /**
382
     * Check whether issuer unique identifier is present.
383
     *
384
     * @return bool
385
     */
386 2
    public function hasIssuerUniqueID(): bool
387
    {
388 2
        return isset($this->_issuerUniqueID);
389
    }
390
    
391
    /**
392
     * Get issuer unique identifier.
393
     *
394
     * @return UniqueIdentifier
395
     */
396 2
    public function issuerUniqueID(): UniqueIdentifier
397
    {
398 2
        if (!$this->hasIssuerUniqueID()) {
399 1
            throw new \LogicException("issuerUniqueID not set.");
400
        }
401 1
        return $this->_issuerUniqueID;
402
    }
403
    
404
    /**
405
     * Get extensions.
406
     *
407
     * @return Extensions
408
     */
409 4
    public function extensions(): Extensions
410
    {
411 4
        return $this->_extensions;
412
    }
413
    
414
    /**
415
     * Get ASN.1 structure.
416
     *
417
     * @return Sequence
418
     */
419 19
    public function toASN1(): Sequence
420
    {
421 19
        $elements = array(new Integer($this->_version), $this->_holder->toASN1(),
422 19
            $this->_issuer->toASN1(), $this->signature()->toASN1(),
423 19
            new Integer($this->serialNumber()),
424 19
            $this->_attrCertValidityPeriod->toASN1(),
425 19
            $this->_attributes->toASN1());
426 19
        if (isset($this->_issuerUniqueID)) {
427 3
            $elements[] = $this->_issuerUniqueID->toASN1();
428
        }
429 19
        if (count($this->_extensions)) {
430 8
            $elements[] = $this->_extensions->toASN1();
431
        }
432 19
        return new Sequence(...$elements);
433
    }
434
    
435
    /**
436
     * Create signed attribute certificate.
437
     *
438
     * @param SignatureAlgorithmIdentifier $algo Signature algorithm
439
     * @param PrivateKeyInfo $privkey_info Private key
440
     * @param Crypto|null $crypto Crypto engine, use default if not set
441
     * @return AttributeCertificate
442
     */
443 1
    public function sign(SignatureAlgorithmIdentifier $algo,
444
        PrivateKeyInfo $privkey_info, Crypto $crypto = null): AttributeCertificate
445
    {
446 1
        $crypto = $crypto ?: Crypto::getDefault();
447 1
        $aci = clone $this;
448 1
        if (!isset($aci->_serialNumber)) {
449 1
            $aci->_serialNumber = "0";
450
        }
451 1
        $aci->_signature = $algo;
452 1
        $data = $aci->toASN1()->toDER();
453 1
        $signature = $crypto->sign($data, $privkey_info, $algo);
454 1
        return new AttributeCertificate($aci, $algo, $signature);
455
    }
456
}
457