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.

Identifier   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 275
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 27
eloc 76
dl 0
loc 275
ccs 78
cts 78
cp 1
rs 10
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A toDER() 0 22 4
A __construct() 0 5 1
A fromDER() 0 23 4
A withTag() 0 5 1
A isContextSpecific() 0 3 1
A withClass() 0 5 1
A isConstructed() 0 3 1
A intTag() 0 3 1
A typeClass() 0 3 1
A pc() 0 3 1
A isUniversal() 0 3 1
A isPrimitive() 0 3 1
A isApplication() 0 3 1
A tag() 0 3 1
A _decodeLongFormTag() 0 18 4
A isPrivate() 0 3 1
A classToName() 0 6 2
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\ASN1\Component;
6
7
use Sop\ASN1\Exception\DecodeException;
8
use Sop\ASN1\Feature\Encodable;
9
use Sop\ASN1\Util\BigInt;
10
11
/**
12
 * Class to represent BER/DER identifier octets.
13
 */
14
class Identifier implements Encodable
15
{
16
    // Type class enumerations
17
    const CLASS_UNIVERSAL = 0b00;
18
    const CLASS_APPLICATION = 0b01;
19
    const CLASS_CONTEXT_SPECIFIC = 0b10;
20
    const CLASS_PRIVATE = 0b11;
21
22
    /**
23
     * Mapping from type class to human readable name.
24
     *
25
     * @internal
26
     *
27
     * @var array
28
     */
29
    const MAP_CLASS_TO_NAME = [
30
        self::CLASS_UNIVERSAL => 'UNIVERSAL',
31
        self::CLASS_APPLICATION => 'APPLICATION',
32
        self::CLASS_CONTEXT_SPECIFIC => 'CONTEXT SPECIFIC',
33
        self::CLASS_PRIVATE => 'PRIVATE',
34
    ];
35
36
    // P/C enumerations
37
    const PRIMITIVE = 0b0;
38
    const CONSTRUCTED = 0b1;
39
40
    /**
41
     * Type class.
42
     *
43
     * @var int
44
     */
45
    private $_class;
46
47
    /**
48
     * Primitive or Constructed.
49
     *
50
     * @var int
51
     */
52
    private $_pc;
53
54
    /**
55
     * Content type tag.
56
     *
57
     * @var BigInt
58
     */
59
    private $_tag;
60
61
    /**
62
     * Constructor.
63
     *
64
     * @param int             $class Type class
65
     * @param int             $pc    Primitive / Constructed
66
     * @param \GMP|int|string $tag   Type tag number
67
     */
68 416
    public function __construct(int $class, int $pc, $tag)
69
    {
70 416
        $this->_class = 0b11 & $class;
71 416
        $this->_pc = 0b1 & $pc;
72 416
        $this->_tag = new BigInt($tag);
73 416
    }
74
75
    /**
76
     * Decode identifier component from DER data.
77
     *
78
     * @param string   $data   DER encoded data
79
     * @param null|int $offset Reference to the variable that contains offset
80
     *                         into the data where to start parsing.
81
     *                         Variable is updated to the offset next to the
82
     *                         parsed identifier. If null, start from offset 0.
83
     *
84
     * @throws DecodeException If decoding fails
85
     *
86
     * @return self
87
     */
88 298
    public static function fromDER(string $data, ?int &$offset = null): Identifier
89
    {
90 298
        $idx = $offset ?? 0;
91 298
        $datalen = strlen($data);
92 298
        if ($idx >= $datalen) {
93 1
            throw new DecodeException('Invalid offset.');
94
        }
95 297
        $byte = ord($data[$idx++]);
96
        // bits 8 and 7 (class)
97
        // 0 = universal, 1 = application, 2 = context-specific, 3 = private
98 297
        $class = (0b11000000 & $byte) >> 6;
99
        // bit 6 (0 = primitive / 1 = constructed)
100 297
        $pc = (0b00100000 & $byte) >> 5;
101
        // bits 5 to 1 (tag number)
102 297
        $tag = (0b00011111 & $byte);
103
        // long-form identifier
104 297
        if (0x1f === $tag) {
105 7
            $tag = self::_decodeLongFormTag($data, $idx);
106
        }
107 296
        if (isset($offset)) {
108 282
            $offset = $idx;
109
        }
110 296
        return new self($class, $pc, $tag);
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116 188
    public function toDER(): string
117
    {
118 188
        $bytes = [];
119 188
        $byte = $this->_class << 6 | $this->_pc << 5;
120 188
        $tag = $this->_tag->gmpObj();
121 188
        if ($tag < 0x1f) {
122 185
            $bytes[] = $byte | $tag;
123
        }
124
        // long-form identifier
125
        else {
126 4
            $bytes[] = $byte | 0x1f;
127 4
            $octets = [];
128 4
            for (; $tag > 0; $tag >>= 7) {
129 4
                array_push($octets, gmp_intval(0x80 | ($tag & 0x7f)));
130
            }
131
            // last octet has bit 8 set to zero
132 4
            $octets[0] &= 0x7f;
133 4
            foreach (array_reverse($octets) as $octet) {
134 4
                $bytes[] = $octet;
135
            }
136
        }
137 188
        return pack('C*', ...$bytes);
138
    }
139
140
    /**
141
     * Get class of the type.
142
     */
143 281
    public function typeClass(): int
144
    {
145 281
        return $this->_class;
146
    }
147
148
    /**
149
     * Get P/C.
150
     */
151 1
    public function pc(): int
152
    {
153 1
        return $this->_pc;
154
    }
155
156
    /**
157
     * Get the tag number.
158
     *
159
     * @return string Base 10 integer string
160
     */
161 5
    public function tag(): string
162
    {
163 5
        return $this->_tag->base10();
164
    }
165
166
    /**
167
     * Get the tag as an integer.
168
     */
169 282
    public function intTag(): int
170
    {
171 282
        return $this->_tag->intVal();
172
    }
173
174
    /**
175
     * Check whether type is of an universal class.
176
     */
177 1
    public function isUniversal(): bool
178
    {
179 1
        return self::CLASS_UNIVERSAL === $this->_class;
180
    }
181
182
    /**
183
     * Check whether type is of an application class.
184
     */
185 1
    public function isApplication(): bool
186
    {
187 1
        return self::CLASS_APPLICATION === $this->_class;
188
    }
189
190
    /**
191
     * Check whether type is of a context specific class.
192
     */
193 1
    public function isContextSpecific(): bool
194
    {
195 1
        return self::CLASS_CONTEXT_SPECIFIC === $this->_class;
196
    }
197
198
    /**
199
     * Check whether type is of a private class.
200
     */
201 1
    public function isPrivate(): bool
202
    {
203 1
        return self::CLASS_PRIVATE === $this->_class;
204
    }
205
206
    /**
207
     * Check whether content is primitive type.
208
     */
209 109
    public function isPrimitive(): bool
210
    {
211 109
        return self::PRIMITIVE === $this->_pc;
212
    }
213
214
    /**
215
     * Check hether content is constructed type.
216
     */
217 268
    public function isConstructed(): bool
218
    {
219 268
        return self::CONSTRUCTED === $this->_pc;
220
    }
221
222
    /**
223
     * Get self with given type class.
224
     *
225
     * @param int $class One of `CLASS_*` enumerations
226
     *
227
     * @return self
228
     */
229 8
    public function withClass(int $class): Identifier
230
    {
231 8
        $obj = clone $this;
232 8
        $obj->_class = 0b11 & $class;
233 8
        return $obj;
234
    }
235
236
    /**
237
     * Get self with given type tag.
238
     *
239
     * @param \GMP|int|string $tag Tag number
240
     *
241
     * @return self
242
     */
243 8
    public function withTag($tag): Identifier
244
    {
245 8
        $obj = clone $this;
246 8
        $obj->_tag = new BigInt($tag);
247 8
        return $obj;
248
    }
249
250
    /**
251
     * Get human readable name of the type class.
252
     */
253 8
    public static function classToName(int $class): string
254
    {
255 8
        if (!array_key_exists($class, self::MAP_CLASS_TO_NAME)) {
256 2
            return "CLASS {$class}";
257
        }
258 6
        return self::MAP_CLASS_TO_NAME[$class];
259
    }
260
261
    /**
262
     * Parse long form tag.
263
     *
264
     * @param string $data   DER data
265
     * @param int    $offset Reference to the variable containing offset to data
266
     *
267
     * @throws DecodeException If decoding fails
268
     *
269
     * @return \GMP Tag number
270
     */
271 7
    private static function _decodeLongFormTag(string $data, int &$offset): \GMP
272
    {
273 7
        $datalen = strlen($data);
274 7
        $tag = gmp_init(0, 10);
275 7
        while (true) {
276 7
            if ($offset >= $datalen) {
277 1
                throw new DecodeException(
278 1
                    'Unexpected end of data while decoding long form identifier.');
279
            }
280 7
            $byte = ord($data[$offset++]);
281 7
            $tag <<= 7;
282 7
            $tag |= 0x7f & $byte;
283
            // last byte has bit 8 set to zero
284 7
            if (!(0x80 & $byte)) {
285 6
                break;
286
            }
287
        }
288 6
        return $tag;
289
    }
290
}
291