Passed
Push — develop ( 861d24...754fa3 )
by nguereza
01:47
created

CborDecoder::parseSimpleValue()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 4
nop 1
dl 0
loc 15
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Platine Webauth
5
 *
6
 * Platine Webauthn is the implementation of webauthn specifications
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine Webauth
11
 * Copyright (c) Jakob Bennemann <[email protected]>
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
declare(strict_types=1);
33
34
namespace Platine\Webauthn\Helper;
35
36
use Platine\Webauthn\Exception\WebauthnException;
37
38
/**
39
 * @class CborDecoder
40
 * @package Platine\Webauthn\Helper
41
 */
42
class CborDecoder
43
{
44
    public const CBOR_MAJOR_UNSIGNED_INT = 0;
45
    public const CBOR_MAJOR_NEGATIVE_INT = 1;
46
    public const CBOR_MAJOR_BYTE_STRING = 2;
47
    public const CBOR_MAJOR_TEXT_STRING = 3;
48
    public const CBOR_MAJOR_ARRAY = 4;
49
    public const CBOR_MAJOR_MAP = 5;
50
    public const CBOR_MAJOR_TAG = 6;
51
    public const CBOR_MAJOR_FLOAT_SIMPLE = 7;
52
53
    /**
54
     * Decode the given data
55
     * @param ByteBuffer|string $data
56
     * @return mixed
57
     */
58
    public static function decode($data)
59
    {
60
        if (is_string($data)) {
61
            $data = new ByteBuffer($data);
62
        }
63
64
        $offset = 0;
65
        $result = self::parseItem($data, $offset);
66
        if ($offset !== $data->getLength()) {
67
            throw new WebauthnException(sprintf(
68
                'There still unsed bytes [%d] after parse data',
69
                abs($offset - $data->getLength())
70
            ));
71
        }
72
73
        return $result;
74
    }
75
76
    /**
77
     * Decode the data using custom start and end offset
78
     * @param ByteBuffer|string $data
79
     * @param int $startoffset
80
     * @param int|null $endOffset
81
     * @return mixed
82
     */
83
    public static function decodeInPlace($data, int $startoffset, ?int $endOffset = null)
0 ignored issues
show
Unused Code introduced by
The parameter $endOffset is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

83
    public static function decodeInPlace($data, int $startoffset, /** @scrutinizer ignore-unused */ ?int $endOffset = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
84
    {
85
        if (is_string($data)) {
86
            $data = new ByteBuffer($data);
87
        }
88
89
        $offset = $startoffset;
90
        $result = self::parseItem($data, $offset);
91
        $endOffset = $offset;
0 ignored issues
show
Unused Code introduced by
The assignment to $endOffset is dead and can be removed.
Loading history...
92
93
        return $result;
94
    }
95
96
    /**
97
     * Parse the item in the given offset
98
     * @param ByteBuffer $buffer
99
     * @param int $offset
100
     * @return mixed
101
     */
102
    protected static function parseItem(ByteBuffer $buffer, int &$offset)
103
    {
104
        $first = $buffer->getByteValue($offset++);
105
        $type = $first >> 5;
106
        $value = $first & 0b11111;
107
        if ($type === self::CBOR_MAJOR_FLOAT_SIMPLE) {
108
            return self::parseSimpleFloat($value, $buffer, $offset);
109
        }
110
111
        $val = self::extractLength($value, $buffer, $offset);
112
113
        return self::parseItemData($type, $val, $buffer, $offset);
114
    }
115
116
    /**
117
     * Parse the simple float value
118
     * @param int $value
119
     * @param ByteBuffer $buffer
120
     * @param int $offset
121
     * @return mixed
122
     */
123
    protected static function parseSimpleFloat(int $value, ByteBuffer $buffer, int &$offset)
124
    {
125
        switch ($value) {
126
            case 24:
127
                $value = $buffer->getByteValue($offset);
128
                $offset++;
129
                return self::parseSimpleValue($value);
130
131
            case 25:
132
                $floatValue = $buffer->getHalfFloatValue($offset);
133
                $offset += 2;
134
135
                return $floatValue;
136
137
            case 26:
138
                $floatValue = $buffer->getFloatValue($offset);
139
                $offset += 4;
140
141
                return $floatValue;
142
143
            case 27:
144
                $floatValue = $buffer->getDoubleValue($offset);
145
                $offset += 8;
146
147
                return $floatValue;
148
149
            case 28:
150
            case 29:
151
            case 30:
152
                throw new WebauthnException(sprintf('Reserved value [%d] used', $value));
153
154
            case 31:
155
                throw new WebauthnException(sprintf('Indefinite value [%d] length is not supported', $value));
156
        }
157
158
        return self::parseSimpleValue($value);
159
    }
160
161
    /**
162
     * Parse simple value
163
     * @param int $value
164
     * @return bool|null
165
     */
166
    protected static function parseSimpleValue(int $value): ?bool
167
    {
168
        if ($value === 20) {
169
            return false;
170
        }
171
172
        if ($value === 21) {
173
            return true;
174
        }
175
176
        if ($value === 23) {
177
            return null;
178
        }
179
180
        throw new WebauthnException(sprintf('Unsupported simple value [%d]', $value));
181
    }
182
183
    /**
184
     * Parse the item data
185
     * @param int $type
186
     * @param int $value
187
     * @param ByteBuffer $buffer
188
     * @param int $offset
189
     * @return mixed
190
     */
191
    protected static function parseItemData(int $type, int $value, ByteBuffer $buffer, int &$offset)
192
    {
193
        switch ($type) {
194
            case self::CBOR_MAJOR_UNSIGNED_INT:
195
                return $value;
196
197
            case self::CBOR_MAJOR_NEGATIVE_INT:
198
                return -1 - $value;
199
200
            case self::CBOR_MAJOR_BYTE_STRING:
201
                $data = $buffer->getBytes($offset, $value);
202
                $offset += $value;
203
                return new ByteBuffer($data); // bytes
204
205
            case self::CBOR_MAJOR_TEXT_STRING:
206
                $data = $buffer->getBytes($offset, $value);
207
                $offset += $value;
208
                return $data; // UTF-8
209
210
            case self::CBOR_MAJOR_ARRAY:
211
                return self::parseArray($buffer, $offset, $value);
212
213
            case self::CBOR_MAJOR_MAP:
214
                return self::parseMap($buffer, $offset, $value);
215
216
            case self::CBOR_MAJOR_TAG:
217
                return self::parseItem($buffer, $offset); // 1 embedded data item
218
        }
219
220
        throw new WebauthnException(sprintf('Unsupported major type [%d]', $type));
221
    }
222
223
    /**
224
     * Parse an array of values
225
     * @param ByteBuffer $buffer
226
     * @param int $offset
227
     * @param int $count
228
     * @return array<mixed>
229
     */
230
    protected static function parseArray(ByteBuffer $buffer, int &$offset, int $count): array
231
    {
232
        $arr = [];
233
        for ($i = 0; $i < $count; $i++) {
234
            $arr[] = self::parseItem($buffer, $offset);
235
        }
236
237
        return $arr;
238
    }
239
240
    /**
241
     * Parse map of values
242
     * @param ByteBuffer $buffer
243
     * @param int $offset
244
     * @param int $count
245
     * @return array<string|int, mixed>
246
     */
247
    protected static function parseMap(ByteBuffer $buffer, int &$offset, int $count): array
248
    {
249
        $maps = [];
250
        for ($i = 0; $i < $count; $i++) {
251
            $key = self::parseItem($buffer, $offset);
252
            $value = self::parseItem($buffer, $offset);
253
            if (!is_int($key) && !is_string($key)) {
254
                throw new WebauthnException('Can only use integer or string for map key');
255
            }
256
257
            $maps[$key] = $value;
258
        }
259
260
        return $maps;
261
    }
262
263
    /**
264
     *
265
     * @param int $value
266
     * @param ByteBuffer $buffer
267
     * @param int $offset
268
     * @return int
269
     */
270
    protected static function extractLength(int $value, ByteBuffer $buffer, int &$offset): int
271
    {
272
        switch ($value) {
273
            case 24:
274
                $value = $buffer->getByteValue($offset);
275
                $offset++;
276
                break;
277
278
            case 25:
279
                $value = $buffer->getUint16Value($offset);
280
                $offset += 2;
281
                break;
282
283
            case 26:
284
                $value = $buffer->getUint32Value($offset);
285
                $offset += 4;
286
                break;
287
288
            case 27:
289
                $value = $buffer->getUint64Value($offset);
290
                $offset += 8;
291
                break;
292
293
            case 28:
294
            case 29:
295
            case 30:
296
                throw new WebauthnException(sprintf('Reserved value [%d] used', $value));
297
298
            case 31:
299
                throw new WebauthnException(sprintf('Indefinite value [%d] length is not supported', $value));
300
        }
301
302
        return $value;
303
    }
304
}
305