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
Branch indefinite (96169e)
by Joni
03:55
created

Length::expectFromDER()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 18
cts 18
cp 1
rs 8.8657
c 0
b 0
f 0
cc 6
nc 6
nop 3
crap 6
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 352
    public function __construct($length, bool $indefinite = false)
36
    {
37 352
        $this->_length = new BigInt($length);
38 352
        $this->_indefinite = $indefinite;
39 352
    }
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 245
    public static function fromDER(string $data, int &$offset = null): self
53
    {
54 245
        $idx = $offset ?? 0;
55 245
        $datalen = strlen($data);
56 245
        if ($idx >= $datalen) {
57 1
            throw new DecodeException(
58 1
                "Unexpected end of data while decoding length.");
59
        }
60 244
        $indefinite = false;
61 244
        $byte = ord($data[$idx++]);
62
        // bits 7 to 1
63 244
        $length = (0x7f & $byte);
64
        // long form
65 244
        if (0x80 & $byte) {
66 18
            if (!$length) {
67 10
                $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 242
        if (isset($offset)) {
77 232
            $offset = $idx;
78
        }
79 242
        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 232
    public static function expectFromDER(string $data, int &$offset,
125
        int $expected = null): self
126
    {
127 232
        $idx = $offset;
128 232
        $length = self::fromDER($data, $idx);
129
        // if certain length was expected
130 232
        if (isset($expected)) {
131 43
            if ($length->isIndefinite()) {
132 1
                throw new DecodeException('Expected length %d, got indefinite.',
133 1
                    $expected);
134
            }
135 42
            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 227
        if (!$length->isIndefinite() &&
143 227
            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 223
        $offset = $idx;
149 223
        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 138
    public function toDER(): string
159
    {
160 138
        $bytes = [];
161 138
        if ($this->_indefinite) {
162 2
            $bytes[] = 0x80;
163
        } else {
164 137
            $num = $this->_length->gmpObj();
165
            // long form
166 137
            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 130
                $bytes[] = gmp_intval($num);
182
            }
183
        }
184 136
        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 234
    public function intLength(): int
209
    {
210 234
        if ($this->_indefinite) {
211 1
            throw new \LogicException("Length is indefinite.");
212
        }
213 233
        return $this->_length->intVal();
214
    }
215
    
216
    /**
217
     * Whether length is indefinite.
218
     *
219
     * @return bool
220
     */
221 234
    public function isIndefinite(): bool
222
    {
223 234
        return $this->_indefinite;
224
    }
225
}
226