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.
Completed
Push — master ( 9e3709...6a1987 )
by Joni
03:09
created

Length::fromDER()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 17
cts 17
cp 1
rs 8.439
c 0
b 0
f 0
cc 6
eloc 18
nc 8
nop 2
crap 6
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace ASN1\Component;
6
7
use ASN1\Exception\DecodeException;
8
use ASN1\Feature\Encodable;
9
use ASN1\Util\BigInt;
10
11
/**
12
 * Class to represent BER/DER length octets.
13
 */
14
class Length implements Encodable
15
{
16
    /**
17
     * Length.
18
     *
19
     * @var BigInt
20
     */
21
    private $_length;
22
    
23
    /**
24
     * Whether length is indefinite.
25
     *
26
     * @var bool
27
     */
28
    private $_indefinite;
29
    
30
    /**
31
     * Constructor.
32
     *
33
     * @param int|string $length Length
34
     * @param bool $indefinite Whether length is indefinite
35
     */
36 328
    public function __construct($length, bool $indefinite = false)
37
    {
38 328
        $this->_length = new BigInt($length);
39 328
        $this->_indefinite = $indefinite;
40 328
    }
41
    
42
    /**
43
     * Decode length component from DER data.
44
     *
45
     * @param string $data DER encoded data
46
     * @param int|null $offset Reference to the variable that contains offset
47
     *        into the data where to start parsing. Variable is updated to
48
     *        the offset next to the parsed length component. If null, start
49
     *        from offset 0.
50
     * @throws DecodeException If decoding fails
51
     * @return self
52
     */
53 227
    public static function fromDER(string $data, int &$offset = null): self
54
    {
55 227
        $idx = $offset ?? 0;
56 227
        $datalen = strlen($data);
57 227
        if ($idx >= $datalen) {
58 1
            throw new DecodeException("Invalid offset.");
59
        }
60 226
        $indefinite = false;
61 226
        $byte = ord($data[$idx++]);
62
        // bits 7 to 1
63 226
        $length = (0x7f & $byte);
64
        // long form
65 226
        if (0x80 & $byte) {
66 12
            if (!$length) {
67 4
                $indefinite = true;
68
            } else {
69 8
                if ($idx + $length > $datalen) {
70 1
                    throw new DecodeException("Too many length octets.");
71
                }
72 7
                $length = self::_decodeLongFormLength($length, $data, $idx);
73
            }
74
        }
75 224
        if (isset($offset)) {
76 214
            $offset = $idx;
77
        }
78 224
        return new self($length, $indefinite);
79
    }
80
    
81
    /**
82
     * Decode long form length.
83
     *
84
     * @param int $length Number of octets
85
     * @param string $data Data
86
     * @param int $offset Reference to the variable containing offset to the
87
     *        data.
88
     * @throws DecodeException If decoding fails
89
     * @return string Integer as a string
90
     */
91 7
    private static function _decodeLongFormLength(int $length, string $data,
92
        int &$offset): string
93
    {
94
        // first octet must not be 0xff (spec 8.1.3.5c)
95 7
        if ($length == 127) {
96 1
            throw new DecodeException("Invalid number of length octets.");
97
        }
98 6
        $num = gmp_init(0, 10);
99 6
        while (--$length >= 0) {
100 6
            $byte = ord($data[$offset++]);
101 6
            $num <<= 8;
102 6
            $num |= $byte;
103
        }
104 6
        return gmp_strval($num);
105
    }
106
    
107
    /**
108
     * Decode length from DER.
109
     *
110
     * Throws an exception if length doesn't match with expected or if data
111
     * doesn't contain enough bytes.
112
     *
113
     * @see self::fromDER
114
     * @param string $data DER data
115
     * @param int $offset Reference to the offset variable
116
     * @param int|null $expected Expected length, null to bypass checking
117
     * @throws DecodeException If decoding or expectation fails
118
     * @return self
119
     */
120 214
    public static function expectFromDER(string $data, int &$offset,
121
        int $expected = null): self
122
    {
123 214
        $idx = $offset;
124 214
        $length = self::fromDER($data, $idx);
125
        // DER encoding must have definite length (spec 10.1)
126 214
        if ($length->isIndefinite()) {
127 1
            throw new DecodeException("DER encoding must have definite length.");
128
        }
129
        // if certain length was expected
130 213
        if (isset($expected) && $expected != $length->intLength()) {
131 3
            throw new DecodeException(
132 3
                sprintf("Expected length %d, got %d.", $expected,
133 3
                    $length->intLength()));
134
        }
135
        // check that enough data is available
136 210
        if (strlen($data) < $idx + $length->intLength()) {
137 4
            throw new DecodeException(
138 4
                sprintf("Length %d overflows data, %d bytes left.",
139 4
                    $length->intLength(), strlen($data) - $idx));
140
        }
141 206
        $offset = $idx;
142 206
        return $length;
143
    }
144
    
145
    /**
146
     *
147
     * @see Encodable::toDER()
148
     * @throws \DomainException If length is too large to encode
149
     * @return string
150
     */
151 124
    public function toDER(): string
152
    {
153 124
        $bytes = [];
154 124
        if ($this->_indefinite) {
155 1
            $bytes[] = 0x80;
156
        } else {
157 123
            $num = $this->_length->gmpObj();
158
            // long form
159 123
            if ($num > 127) {
160 7
                $octets = [];
161 7
                for (; $num > 0; $num >>= 8) {
162 7
                    $octets[] = gmp_intval(0xff & $num);
163
                }
164 7
                $count = count($octets);
165
                // first octet must not be 0xff
166 7
                if ($count >= 127) {
167 2
                    throw new \DomainException("Too many length octets.");
168
                }
169 5
                $bytes[] = 0x80 | $count;
170 5
                foreach (array_reverse($octets) as $octet) {
171 5
                    $bytes[] = $octet;
172
                }
173
            } else { // short form
174 116
                $bytes[] = gmp_intval($num);
175
            }
176
        }
177 122
        return pack("C*", ...$bytes);
178
    }
179
    
180
    /**
181
     * Get the length.
182
     *
183
     * @throws \LogicException If length is indefinite
184
     * @return string Length as an integer string
185
     */
186 5
    public function length(): string
187
    {
188 5
        if ($this->_indefinite) {
189 1
            throw new \LogicException("Length is indefinite.");
190
        }
191 4
        return $this->_length->base10();
192
    }
193
    
194
    /**
195
     * Get the length as an integer.
196
     *
197
     * @throws \LogicException If length is indefinite
198
     * @throws \RuntimeException If length overflows integer size
199
     * @return int
200
     */
201 217
    public function intLength(): int
202
    {
203 217
        if ($this->_indefinite) {
204 1
            throw new \LogicException("Length is indefinite.");
205
        }
206 216
        return $this->_length->intVal();
207
    }
208
    
209
    /**
210
     * Whether length is indefinite.
211
     *
212
     * @return bool
213
     */
214 216
    public function isIndefinite(): bool
215
    {
216 216
        return $this->_indefinite;
217
    }
218
}
219