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|\InvalidArgumentException */ 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 * @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; } }