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.
Passed
Push — master ( 95c197...564e5e )
by Joni
04:32
created

Identifier   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 298
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

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

17 Methods

Rating   Name   Duplication   Size   Complexity  
A withTag() 0 5 1
A isContextSpecific() 0 3 1
A withClass() 0 5 1
A toDER() 0 22 4
A __construct() 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 19 4
A fromDER() 0 23 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 int|string $tag   Type tag number
67
     */
68 367
    public function __construct(int $class, int $pc, $tag)
69
    {
70 367
        $this->_class = 0b11 & $class;
71 367
        $this->_pc = 0b1 & $pc;
72 367
        $this->_tag = new BigInt($tag);
73 367
    }
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 253
    public static function fromDER(string $data, int &$offset = null): Identifier
89
    {
90 253
        $idx = $offset ?? 0;
91 253
        $datalen = strlen($data);
92 253
        if ($idx >= $datalen) {
93 1
            throw new DecodeException('Invalid offset.');
94
        }
95 252
        $byte = ord($data[$idx++]);
96
        // bits 8 and 7 (class)
97
        // 0 = universal, 1 = application, 2 = context-specific, 3 = private
98 252
        $class = (0b11000000 & $byte) >> 6;
99
        // bit 6 (0 = primitive / 1 = constructed)
100 252
        $pc = (0b00100000 & $byte) >> 5;
101
        // bits 5 to 1 (tag number)
102 252
        $tag = (0b00011111 & $byte);
103
        // long-form identifier
104 252
        if (0x1f === $tag) {
105 7
            $tag = self::_decodeLongFormTag($data, $idx);
106
        }
107 251
        if (isset($offset)) {
108 237
            $offset = $idx;
109
        }
110 251
        return new self($class, $pc, $tag);
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116 143
    public function toDER(): string
117
    {
118 143
        $bytes = [];
119 143
        $byte = $this->_class << 6 | $this->_pc << 5;
120 143
        $tag = $this->_tag->gmpObj();
121 143
        if ($tag < 0x1f) {
122 140
            $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 143
        return pack('C*', ...$bytes);
138
    }
139
140
    /**
141
     * Get class of the type.
142
     *
143
     * @return int
144
     */
145 236
    public function typeClass(): int
146
    {
147 236
        return $this->_class;
148
    }
149
150
    /**
151
     * Get P/C.
152
     *
153
     * @return int
154
     */
155 1
    public function pc(): int
156
    {
157 1
        return $this->_pc;
158
    }
159
160
    /**
161
     * Get the tag number.
162
     *
163
     * @return string Base 10 integer string
164
     */
165 5
    public function tag(): string
166
    {
167 5
        return $this->_tag->base10();
168
    }
169
170
    /**
171
     * Get the tag as an integer.
172
     *
173
     * @return int
174
     */
175 237
    public function intTag(): int
176
    {
177 237
        return $this->_tag->intVal();
178
    }
179
180
    /**
181
     * Check whether type is of an universal class.
182
     *
183
     * @return bool
184
     */
185 1
    public function isUniversal(): bool
186
    {
187 1
        return self::CLASS_UNIVERSAL === $this->_class;
188
    }
189
190
    /**
191
     * Check whether type is of an application class.
192
     *
193
     * @return bool
194
     */
195 1
    public function isApplication(): bool
196
    {
197 1
        return self::CLASS_APPLICATION === $this->_class;
198
    }
199
200
    /**
201
     * Check whether type is of a context specific class.
202
     *
203
     * @return bool
204
     */
205 1
    public function isContextSpecific(): bool
206
    {
207 1
        return self::CLASS_CONTEXT_SPECIFIC === $this->_class;
208
    }
209
210
    /**
211
     * Check whether type is of a private class.
212
     *
213
     * @return bool
214
     */
215 1
    public function isPrivate(): bool
216
    {
217 1
        return self::CLASS_PRIVATE === $this->_class;
218
    }
219
220
    /**
221
     * Check whether content is primitive type.
222
     *
223
     * @return bool
224
     */
225 91
    public function isPrimitive(): bool
226
    {
227 91
        return self::PRIMITIVE === $this->_pc;
228
    }
229
230
    /**
231
     * Check hether content is constructed type.
232
     *
233
     * @return bool
234
     */
235 222
    public function isConstructed(): bool
236
    {
237 222
        return self::CONSTRUCTED === $this->_pc;
238
    }
239
240
    /**
241
     * Get self with given type class.
242
     *
243
     * @param int $class One of `CLASS_*` enumerations
244
     *
245
     * @return self
246
     */
247 8
    public function withClass(int $class): Identifier
248
    {
249 8
        $obj = clone $this;
250 8
        $obj->_class = 0b11 & $class;
251 8
        return $obj;
252
    }
253
254
    /**
255
     * Get self with given type tag.
256
     *
257
     * @param int|string $tag Tag number
258
     *
259
     * @return self
260
     */
261 8
    public function withTag($tag): Identifier
262
    {
263 8
        $obj = clone $this;
264 8
        $obj->_tag = new BigInt($tag);
265 8
        return $obj;
266
    }
267
268
    /**
269
     * Get human readable name of the type class.
270
     *
271
     * @param int $class
272
     *
273
     * @return string
274
     */
275 8
    public static function classToName(int $class): string
276
    {
277 8
        if (!array_key_exists($class, self::MAP_CLASS_TO_NAME)) {
278 2
            return "CLASS {$class}";
279
        }
280 6
        return self::MAP_CLASS_TO_NAME[$class];
281
    }
282
283
    /**
284
     * Parse long form tag.
285
     *
286
     * @param string $data   DER data
287
     * @param int    $offset Reference to the variable containing offset to data
288
     *
289
     * @throws DecodeException If decoding fails
290
     *
291
     * @return string Tag number
292
     */
293 7
    private static function _decodeLongFormTag(string $data, int &$offset): string
294
    {
295 7
        $datalen = strlen($data);
296 7
        $tag = gmp_init(0, 10);
297 7
        while (true) {
298 7
            if ($offset >= $datalen) {
299 1
                throw new DecodeException(
300
                    'Unexpected end of data while decoding' .
301 1
                    ' long form identifier.');
302
            }
303 7
            $byte = ord($data[$offset++]);
304 7
            $tag <<= 7;
305 7
            $tag |= 0x7f & $byte;
306
            // last byte has bit 8 set to zero
307 7
            if (!(0x80 & $byte)) {
308 6
                break;
309
            }
310
        }
311 6
        return gmp_strval($tag, 10);
312
    }
313
}
314