First release
This commit is contained in:
335
src/CborDecoder.php
Normal file
335
src/CborDecoder.php
Normal file
@@ -0,0 +1,335 @@
|
||||
<?php
|
||||
|
||||
namespace Laragear\WebAuthn;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Laragear\WebAuthn\Exceptions\DataException;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 Thomas Bleeker
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
* MIT License
|
||||
*
|
||||
* Copyright © 2021 Lukas Buchs
|
||||
* Copyright © 2018 Thomas Bleeker (CBOR & ByteBuffer part)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* This file has been modernized to fit Laravel.
|
||||
*
|
||||
* @author Lukas Buchs
|
||||
* @author Thomas Bleeker
|
||||
* @internal
|
||||
*/
|
||||
class CborDecoder
|
||||
{
|
||||
public const CBOR_MAJOR_UNSIGNED_INT = 0;
|
||||
public const CBOR_MAJOR_TEXT_STRING = 3;
|
||||
public const CBOR_MAJOR_FLOAT_SIMPLE = 7;
|
||||
public const CBOR_MAJOR_NEGATIVE_INT = 1;
|
||||
public const CBOR_MAJOR_ARRAY = 4;
|
||||
public const CBOR_MAJOR_TAG = 6;
|
||||
public const CBOR_MAJOR_MAP = 5;
|
||||
public const CBOR_MAJOR_BYTE_STRING = 2;
|
||||
|
||||
/**
|
||||
* Decodes the binary data.
|
||||
*
|
||||
* @param \Laragear\WebAuthn\ByteBuffer|string $encoded
|
||||
* @return \Laragear\WebAuthn\ByteBuffer|array|bool|float|int|string|null
|
||||
* @throws \Laragear\WebAuthn\Exceptions\DataException
|
||||
*/
|
||||
public static function decode(ByteBuffer|string $encoded): ByteBuffer|array|bool|float|int|string|null
|
||||
{
|
||||
if (is_string($encoded)) {
|
||||
$encoded = new ByteBuffer($encoded);
|
||||
}
|
||||
|
||||
$offset = 0;
|
||||
|
||||
$result = static::parseItem($encoded, $offset);
|
||||
|
||||
if ($offset !== $encoded->getDataLength()) {
|
||||
throw new DataException('CBOR: Unused bytes after data item.');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a portion of the Byte Buffer.
|
||||
*
|
||||
* @param ByteBuffer|string $bufOrBin
|
||||
* @param int $startOffset
|
||||
* @param int|null $endOffset
|
||||
* @return \Laragear\WebAuthn\ByteBuffer|array|bool|float|int|string|null
|
||||
* @throws \Laragear\WebAuthn\Exceptions\DataException
|
||||
*/
|
||||
public static function decodePortion(ByteBuffer|string $bufOrBin, int $startOffset, ?int &$endOffset = null): ByteBuffer|array|bool|float|int|string|null
|
||||
{
|
||||
$buf = $bufOrBin instanceof ByteBuffer ? $bufOrBin : new ByteBuffer($bufOrBin);
|
||||
|
||||
$offset = $startOffset;
|
||||
$data = static::parseItem($buf, $offset);
|
||||
$endOffset = $offset;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a single item of the Byte Buffer.
|
||||
*
|
||||
* @param ByteBuffer $buf
|
||||
* @param int $offset
|
||||
* @return \Laragear\WebAuthn\ByteBuffer|array|bool|float|int|string|null
|
||||
* @throws \Laragear\WebAuthn\Exceptions\DataException
|
||||
*/
|
||||
protected static function parseItem(ByteBuffer $buf, int &$offset): ByteBuffer|array|bool|float|int|string|null
|
||||
{
|
||||
$first = $buf->getByteVal($offset++);
|
||||
$type = $first >> 5;
|
||||
$val = $first & 0b11111;
|
||||
|
||||
if ($type === static::CBOR_MAJOR_FLOAT_SIMPLE) {
|
||||
return static::parseFloatSimple($val, $buf, $offset);
|
||||
}
|
||||
|
||||
$val = static::parseExtraLength($val, $buf, $offset);
|
||||
|
||||
try {
|
||||
return static::parseItemData($type, $val, $buf, $offset);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new DataException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a simple float value.
|
||||
*
|
||||
* @param int $val
|
||||
* @param \Laragear\WebAuthn\ByteBuffer $buf
|
||||
* @param int $offset
|
||||
* @return bool|float|null
|
||||
* @throws \Laragear\WebAuthn\Exceptions\DataException
|
||||
*/
|
||||
protected static function parseFloatSimple(int $val, ByteBuffer $buf, int &$offset): bool|float|null
|
||||
{
|
||||
switch ($val) {
|
||||
case 24:
|
||||
$val = $buf->getByteVal($offset);
|
||||
$offset++;
|
||||
return static::parseSimpleValue($val);
|
||||
case 25:
|
||||
$floatValue = $buf->getHalfFloatVal($offset);
|
||||
$offset += 2;
|
||||
return $floatValue;
|
||||
case 26:
|
||||
$floatValue = $buf->getFloatVal($offset);
|
||||
$offset += 4;
|
||||
return $floatValue;
|
||||
case 27:
|
||||
$floatValue = $buf->getDoubleVal($offset);
|
||||
$offset += 8;
|
||||
return $floatValue;
|
||||
case 28:
|
||||
case 29:
|
||||
case 30:
|
||||
throw new DataException('Reserved value used.');
|
||||
case 31:
|
||||
throw new DataException('Indefinite length is not supported.');
|
||||
default:
|
||||
return static::parseSimpleValue($val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a simple value from CBOR.
|
||||
*
|
||||
* @param int $val
|
||||
* @return bool|null
|
||||
* @throws \Laragear\WebAuthn\Exceptions\DataException
|
||||
*/
|
||||
protected static function parseSimpleValue(int $val): ?bool
|
||||
{
|
||||
return match ($val) {
|
||||
20 => false,
|
||||
21 => true,
|
||||
22 => null,
|
||||
default => throw new DataException(sprintf('Unsupported simple value %d.', $val))
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the CBOR extra length.
|
||||
*
|
||||
* @param int $val
|
||||
* @param \Laragear\WebAuthn\ByteBuffer $buf
|
||||
* @param int $offset
|
||||
* @return int
|
||||
* @throws \Laragear\WebAuthn\Exceptions\DataException
|
||||
*/
|
||||
protected static function parseExtraLength(int $val, ByteBuffer $buf, int &$offset): int
|
||||
{
|
||||
switch ($val) {
|
||||
case 24:
|
||||
$val = $buf->getByteVal($offset);
|
||||
$offset++;
|
||||
return $val;
|
||||
case 25:
|
||||
$val = $buf->getUint16Val($offset);
|
||||
$offset += 2;
|
||||
return $val;
|
||||
case 26:
|
||||
$val = $buf->getUint32Val($offset);
|
||||
$offset += 4;
|
||||
return $val;
|
||||
case 27:
|
||||
$val = $buf->getUint64Val($offset);
|
||||
$offset += 8;
|
||||
return $val;
|
||||
case 28:
|
||||
case 29:
|
||||
case 30:
|
||||
throw new DataException('Reserved value used.');
|
||||
case 31:
|
||||
throw new DataException('Indefinite length is not supported.');
|
||||
default:
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the data inside a Byte Buffer.
|
||||
*
|
||||
* @param int $type
|
||||
* @param int $val
|
||||
* @param \Laragear\WebAuthn\ByteBuffer $buf
|
||||
* @param $offset
|
||||
* @return \Laragear\WebAuthn\ByteBuffer|array|bool|float|int|string|null
|
||||
* @throws \Laragear\WebAuthn\Exceptions\DataException
|
||||
*/
|
||||
protected static function parseItemData(
|
||||
int $type,
|
||||
int $val,
|
||||
ByteBuffer $buf,
|
||||
&$offset
|
||||
): ByteBuffer|array|bool|float|int|string|null {
|
||||
switch ($type) {
|
||||
case static::CBOR_MAJOR_UNSIGNED_INT: // uint
|
||||
return $val;
|
||||
|
||||
case static::CBOR_MAJOR_NEGATIVE_INT:
|
||||
return -1 - $val;
|
||||
|
||||
case static::CBOR_MAJOR_BYTE_STRING:
|
||||
$data = $buf->getBytes($offset, $val);
|
||||
$offset += $val;
|
||||
return new ByteBuffer($data); // bytes
|
||||
|
||||
case static::CBOR_MAJOR_TEXT_STRING:
|
||||
$data = $buf->getBytes($offset, $val);
|
||||
$offset += $val;
|
||||
return $data; // UTF-8
|
||||
|
||||
case static::CBOR_MAJOR_ARRAY:
|
||||
return static::parseArray($buf, $offset, $val);
|
||||
|
||||
case static::CBOR_MAJOR_MAP:
|
||||
return static::parseMap($buf, $offset, $val);
|
||||
|
||||
case static::CBOR_MAJOR_TAG:
|
||||
return static::parseItem($buf, $offset); // 1 embedded data item
|
||||
}
|
||||
|
||||
throw new DataException(sprintf('Unknown major type %d.', $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an array with string keys.
|
||||
*
|
||||
* @param \Laragear\WebAuthn\ByteBuffer $buffer
|
||||
* @param int $offset
|
||||
* @param int $count
|
||||
* @return array<string, mixed>
|
||||
* @throws \Laragear\WebAuthn\Exceptions\DataException
|
||||
*/
|
||||
protected static function parseMap(ByteBuffer $buffer, int &$offset, int $count): array
|
||||
{
|
||||
$map = [];
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$mapKey = static::parseItem($buffer, $offset);
|
||||
$mapVal = static::parseItem($buffer, $offset);
|
||||
|
||||
if (!is_int($mapKey) && !is_string($mapKey)) {
|
||||
throw new DataException('Can only use strings or integers as map keys');
|
||||
}
|
||||
|
||||
$map[$mapKey] = $mapVal;
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an array from the byte buffer.
|
||||
*
|
||||
* @param \Laragear\WebAuthn\ByteBuffer $buf
|
||||
* @param int $offset
|
||||
* @param int $count
|
||||
* @return array
|
||||
* @throws \Laragear\WebAuthn\Exceptions\DataException
|
||||
*/
|
||||
protected static function parseArray(ByteBuffer $buf, int &$offset, int $count): array
|
||||
{
|
||||
$arr = [];
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$arr[] = static::parseItem($buf, $offset);
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user