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 — php70 ( e15417...9941dc )
by Joni
03:18
created

Length::expectFromDER()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6

Importance

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