Extension   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 284
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 13
lcom 1
cbo 7
dl 0
loc 284
ccs 42
cts 42
cp 1
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
_valueASN1() 0 1 ?
A _fromDER() 0 5 1
A __construct() 0 5 1
A fromASN1() 0 21 3
A oid() 0 4 1
A isCritical() 0 4 1
A toASN1() 0 9 2
A _extnValue() 0 4 1
A extensionName() 0 7 2
A __toString() 0 4 1
1
<?php
2
declare(strict_types = 1);
3
4
namespace X509\Certificate\Extension;
5
6
use ASN1\Element;
7
use ASN1\Type\Constructed\Sequence;
8
use ASN1\Type\Primitive\Boolean;
9
use ASN1\Type\Primitive\ObjectIdentifier;
10
use ASN1\Type\Primitive\OctetString;
11
12
/**
13
 * Base class for certificate extensions.
14
 *
15
 * @link https://tools.ietf.org/html/rfc5280#section-4.2
16
 * @link https://tools.ietf.org/html/rfc5280#section-4.1
17
 */
18
abstract class Extension
19
{
20
    // OID's from standard certificate extensions
21
    const OID_OBSOLETE_AUTHORITY_KEY_IDENTIFIER = "2.5.29.1";
22
    const OID_OBSOLETE_KEY_ATTRIBUTES = "2.5.29.2";
23
    const OID_OBSOLETE_CERTIFICATE_POLICIES = "2.5.29.3";
24
    const OID_OBSOLETE_KEY_USAGE_RESTRICTION = "2.5.29.4";
25
    const OID_OBSOLETE_POLICY_MAPPING = "2.5.29.5";
26
    const OID_OBSOLETE_SUBTREES_CONSTRAINT = "2.5.29.6";
27
    const OID_OBSOLETE_SUBJECT_ALT_NAME = "2.5.29.7";
28
    const OID_OBSOLETE_ISSUER_ALT_NAME = "2.5.29.8";
29
    const OID_SUBJECT_DIRECTORY_ATTRIBUTES = "2.5.29.9";
30
    const OID_OBSOLETE_BASIC_CONSTRAINTS = "2.5.29.10";
31
    const OID_SUBJECT_KEY_IDENTIFIER = "2.5.29.14";
32
    const OID_KEY_USAGE = "2.5.29.15";
33
    const OID_PRIVATE_KEY_USAGE_PERIOD = "2.5.29.16";
34
    const OID_SUBJECT_ALT_NAME = "2.5.29.17";
35
    const OID_ISSUER_ALT_NAME = "2.5.29.18";
36
    const OID_BASIC_CONSTRAINTS = "2.5.29.19";
37
    const OID_CRL_NUMBER = "2.5.29.20";
38
    const OID_REASON_CODE = "2.5.29.21";
39
    const OID_OBSOLETE_EXPIRATION_DATE = "2.5.29.22";
40
    const OID_INSTRUCTION_CODE = "2.5.29.23";
41
    const OID_INVALIDITY_DATE = "2.5.29.24";
42
    const OID_OBSOLETE_CRL_DISTRIBUTION_POINTS = "2.5.29.25";
43
    const OID_OBSOLETE_ISSUING_DISTRIBUTION_POINT = "2.5.29.26";
44
    const OID_DELTA_CRL_INDICATOR = "2.5.29.27";
45
    const OID_ISSUING_DISTRIBUTION_POINT = "2.5.29.28";
46
    const OID_CERTIFICATE_ISSUER = "2.5.29.29";
47
    const OID_NAME_CONSTRAINTS = "2.5.29.30";
48
    const OID_CRL_DISTRIBUTION_POINTS = "2.5.29.31";
49
    const OID_CERTIFICATE_POLICIES = "2.5.29.32";
50
    const OID_POLICY_MAPPINGS = "2.5.29.33";
51
    const OID_OBSOLETE_POLICY_CONSTRAINTS = "2.5.29.34";
52
    const OID_AUTHORITY_KEY_IDENTIFIER = "2.5.29.35";
53
    const OID_POLICY_CONSTRAINTS = "2.5.29.36";
54
    const OID_EXT_KEY_USAGE = "2.5.29.37";
55
    const OID_AUTHORITY_ATTRIBUTE_IDENTIFIER = "2.5.29.38";
56
    const OID_ROLE_SPEC_CERT_IDENTIFIER = "2.5.29.39";
57
    const OID_CRL_STREAM_IDENTIFIER = "2.5.29.40";
58
    const OID_BASIC_ATT_CONSTRAINTS = "2.5.29.41";
59
    const OID_DELEGATED_NAME_CONSTRAINTS = "2.5.29.42";
60
    const OID_TIME_SPECIFICATION = "2.5.29.43";
61
    const OID_CRL_SCOPE = "2.5.29.44";
62
    const OID_STATUS_REFERRALS = "2.5.29.45";
63
    const OID_FRESHEST_CRL = "2.5.29.46";
64
    const OID_ORDERED_LIST = "2.5.29.47";
65
    const OID_ATTRIBUTE_DESCRIPTOR = "2.5.29.48";
66
    const OID_USER_NOTICE = "2.5.29.49";
67
    const OID_SOA_IDENTIFIER = "2.5.29.50";
68
    const OID_BASE_UPDATE_TIME = "2.5.29.51";
69
    const OID_ACCEPTABLE_CERT_POLICIES = "2.5.29.52";
70
    const OID_DELTA_INFO = "2.5.29.53";
71
    const OID_INHIBIT_ANY_POLICY = "2.5.29.54";
72
    const OID_TARGET_INFORMATION = "2.5.29.55";
73
    const OID_NO_REV_AVAIL = "2.5.29.56";
74
    const OID_ACCEPTABLE_PRIVILEGE_POLICIES = "2.5.29.57";
75
    const OID_TO_BE_REVOKED = "2.5.29.58";
76
    const OID_REVOKED_GROUPS = "2.5.29.59";
77
    const OID_EXPIRED_CERTS_ON_CRL = "2.5.29.60";
78
    const OID_INDIRECT_ISSUER = "2.5.29.61";
79
    const OID_NO_ASSERTION = "2.5.29.62";
80
    const OID_AA_ISSUING_DISTRIBUTION_POINT = "2.5.29.63";
81
    const OID_ISSUED_ON_BEHALF_OF = "2.5.29.64";
82
    const OID_SINGLE_USE = "2.5.29.65";
83
    const OID_GROUP_AC = "2.5.29.66";
84
    const OID_ALLOWED_ATT_ASS = "2.5.29.67";
85
    const OID_ATTRIBUTE_MAPPINGS = "2.5.29.68";
86
    const OID_HOLDER_NAME_CONSTRAINTS = "2.5.29.69";
87
    
88
    // OID's from private certificate extensions arc
89
    const OID_AUTHORITY_INFORMATION_ACCESS = "1.3.6.1.5.5.7.1.1";
90
    const OID_AA_CONTROLS = "1.3.6.1.5.5.7.1.6";
91
    const OID_SUBJECT_INFORMATION_ACCESS = "1.3.6.1.5.5.7.1.11";
92
    const OID_LOGOTYPE = "1.3.6.1.5.5.7.1.12";
93
    
94
    /**
95
     * Mapping from extension ID to implementation class name.
96
     *
97
     * @internal
98
     *
99
     * @var array
100
     */
101
    const MAP_OID_TO_CLASS = array(
102
        /* @formatter:off */
103
        self::OID_AUTHORITY_KEY_IDENTIFIER => AuthorityKeyIdentifierExtension::class,
104
        self::OID_SUBJECT_KEY_IDENTIFIER => SubjectKeyIdentifierExtension::class,
105
        self::OID_KEY_USAGE => KeyUsageExtension::class,
106
        self::OID_CERTIFICATE_POLICIES => CertificatePoliciesExtension::class,
107
        self::OID_POLICY_MAPPINGS => PolicyMappingsExtension::class,
108
        self::OID_SUBJECT_ALT_NAME => SubjectAlternativeNameExtension::class,
109
        self::OID_ISSUER_ALT_NAME => IssuerAlternativeNameExtension::class,
110
        self::OID_SUBJECT_DIRECTORY_ATTRIBUTES => SubjectDirectoryAttributesExtension::class,
111
        self::OID_BASIC_CONSTRAINTS => BasicConstraintsExtension::class,
112
        self::OID_NAME_CONSTRAINTS => NameConstraintsExtension::class,
113
        self::OID_POLICY_CONSTRAINTS => PolicyConstraintsExtension::class,
114
        self::OID_EXT_KEY_USAGE => ExtendedKeyUsageExtension::class,
115
        self::OID_CRL_DISTRIBUTION_POINTS => CRLDistributionPointsExtension::class,
116
        self::OID_INHIBIT_ANY_POLICY => InhibitAnyPolicyExtension::class,
117
        self::OID_FRESHEST_CRL => FreshestCRLExtension::class,
118
        self::OID_NO_REV_AVAIL => NoRevocationAvailableExtension::class,
119
        self::OID_TARGET_INFORMATION => TargetInformationExtension::class,
120
        self::OID_AUTHORITY_INFORMATION_ACCESS => AuthorityInformationAccessExtension::class,
121
        self::OID_AA_CONTROLS => AAControlsExtension::class,
122
        self::OID_SUBJECT_INFORMATION_ACCESS => SubjectInformationAccessExtension::class
123
        /* @formatter:on */
124
    );
125
    
126
    /**
127
     * Mapping from extensions ID to short name.
128
     *
129
     * @internal
130
     *
131
     * @var array
132
     */
133
    const MAP_OID_TO_NAME = array(
134
        /* @formatter:off */
135
        self::OID_AUTHORITY_KEY_IDENTIFIER => "authorityKeyIdentifier",
136
        self::OID_SUBJECT_KEY_IDENTIFIER => "subjectKeyIdentifier",
137
        self::OID_KEY_USAGE => "keyUsage",
138
        self::OID_PRIVATE_KEY_USAGE_PERIOD => "privateKeyUsagePeriod",
139
        self::OID_CERTIFICATE_POLICIES => "certificatePolicies",
140
        self::OID_POLICY_MAPPINGS => "policyMappings",
141
        self::OID_SUBJECT_ALT_NAME => "subjectAltName",
142
        self::OID_ISSUER_ALT_NAME => "issuerAltName",
143
        self::OID_SUBJECT_DIRECTORY_ATTRIBUTES => "subjectDirectoryAttributes",
144
        self::OID_BASIC_CONSTRAINTS => "basicConstraints",
145
        self::OID_NAME_CONSTRAINTS => "nameConstraints",
146
        self::OID_POLICY_CONSTRAINTS => "policyConstraints",
147
        self::OID_EXT_KEY_USAGE => "extKeyUsage",
148
        self::OID_CRL_DISTRIBUTION_POINTS => "cRLDistributionPoints",
149
        self::OID_INHIBIT_ANY_POLICY => "inhibitAnyPolicy",
150
        self::OID_FRESHEST_CRL => "freshestCRL",
151
        self::OID_NO_REV_AVAIL => "noRevAvail",
152
        self::OID_TARGET_INFORMATION => "targetInformation",
153
        self::OID_AUTHORITY_INFORMATION_ACCESS => "authorityInfoAccess",
154
        self::OID_AA_CONTROLS => "aaControls",
155
        self::OID_SUBJECT_INFORMATION_ACCESS => "subjectInfoAccess",
156
        self::OID_LOGOTYPE => "logotype"
157
        /* @formatter:on */
158
    );
159
    
160
    /**
161
     * Extension's OID.
162
     *
163
     * @var string $_oid
164
     */
165
    protected $_oid;
166
    
167
    /**
168
     * Whether extension is critical.
169
     *
170
     * @var bool $_critical
171
     */
172
    protected $_critical;
173
    
174
    /**
175
     * Get ASN.1 structure of the extension value.
176
     *
177
     * @return Element
178
     */
179
    abstract protected function _valueASN1();
180
    
181
    /**
182
     * Parse extension value from DER.
183
     *
184
     * @param string $data DER data
185
     * @param bool $critical Whether extension is critical
186
     * @throws \BadMethodCallException
187
     * @return self
188
     */
189 1
    protected static function _fromDER(string $data, bool $critical)
190
    {
191 1
        throw new \BadMethodCallException(
192 1
            __FUNCTION__ . " must be implemented in derived class.");
193
    }
194
    
195
    /**
196
     * Constructor.
197
     *
198
     * @param string $oid Extension OID
199
     * @param bool $critical Whether extension is critical
200
     */
201 98
    public function __construct(string $oid, bool $critical)
202
    {
203 98
        $this->_oid = $oid;
204 98
        $this->_critical = $critical;
205 98
    }
206
    
207
    /**
208
     * Initialize from ASN.1.
209
     *
210
     * @param Sequence $seq
211
     * @return self
212
     */
213 51
    public static function fromASN1(Sequence $seq): Extension
214
    {
215 51
        $extnID = $seq->at(0)
216 51
            ->asObjectIdentifier()
217 51
            ->oid();
218 51
        $critical = false;
219 51
        $idx = 1;
220 51
        if ($seq->has($idx, Element::TYPE_BOOLEAN)) {
221 22
            $critical = $seq->at($idx++)
222 22
                ->asBoolean()
223 22
                ->value();
224
        }
225 51
        $data = $seq->at($idx)
226 51
            ->asOctetString()
227 51
            ->string();
228 51
        if (array_key_exists($extnID, self::MAP_OID_TO_CLASS)) {
229 50
            $cls = self::MAP_OID_TO_CLASS[$extnID];
230 50
            return $cls::_fromDER($data, $critical);
231
        }
232 1
        return UnknownExtension::fromRawString($extnID, $critical, $data);
233
    }
234
    
235
    /**
236
     * Get extension OID.
237
     *
238
     * @return string
239
     */
240 66
    public function oid(): string
241
    {
242 66
        return $this->_oid;
243
    }
244
    
245
    /**
246
     * Check whether extension is critical.
247
     *
248
     * @return bool
249
     */
250 20
    public function isCritical(): bool
251
    {
252 20
        return $this->_critical;
253
    }
254
    
255
    /**
256
     * Generate ASN.1 structure.
257
     *
258
     * @return Sequence
259
     */
260 88
    public function toASN1(): Sequence
261
    {
262 88
        $elements = array(new ObjectIdentifier($this->_oid));
263 88
        if ($this->_critical) {
264 58
            $elements[] = new Boolean(true);
265
        }
266 88
        $elements[] = $this->_extnValue();
267 83
        return new Sequence(...$elements);
268
    }
269
    
270
    /**
271
     * Get the extnValue element.
272
     *
273
     * @return OctetString
274
     */
275 86
    protected function _extnValue(): OctetString
276
    {
277 86
        return new OctetString($this->_valueASN1()->toDER());
278
    }
279
    
280
    /**
281
     * Get short name of the extension.
282
     *
283
     * @return string
284
     */
285 3
    public function extensionName(): string
286
    {
287 3
        if (array_key_exists($this->_oid, self::MAP_OID_TO_NAME)) {
288 2
            return self::MAP_OID_TO_NAME[$this->_oid];
289
        }
290 1
        return $this->oid();
291
    }
292
    
293
    /**
294
     *
295
     * @return string
296
     */
297 1
    public function __toString(): string
298
    {
299 1
        return $this->extensionName();
300
    }
301
}
302