Project import generated by Copybara.
GitOrigin-RevId: 63746295f1a5ab5a619056791995793d65529e62
diff --git a/src/lib/WebAuthn/CBOR/CborDecoder.php b/src/lib/WebAuthn/CBOR/CborDecoder.php
new file mode 100644
index 0000000..e6b5427
--- /dev/null
+++ b/src/lib/WebAuthn/CBOR/CborDecoder.php
@@ -0,0 +1,220 @@
+<?php
+
+
+namespace lbuchs\WebAuthn\CBOR;
+use lbuchs\WebAuthn\WebAuthnException;
+use lbuchs\WebAuthn\Binary\ByteBuffer;
+
+/**
+ * Modified version of https://github.com/madwizard-thomas/webauthn-server/blob/master/src/Format/CborDecoder.php
+ * Copyright © 2018 Thomas Bleeker - MIT licensed
+ * Modified by Lukas Buchs
+ * Thanks Thomas for your work!
+ */
+class CborDecoder {
+ const CBOR_MAJOR_UNSIGNED_INT = 0;
+ const CBOR_MAJOR_TEXT_STRING = 3;
+ const CBOR_MAJOR_FLOAT_SIMPLE = 7;
+ const CBOR_MAJOR_NEGATIVE_INT = 1;
+ const CBOR_MAJOR_ARRAY = 4;
+ const CBOR_MAJOR_TAG = 6;
+ const CBOR_MAJOR_MAP = 5;
+ const CBOR_MAJOR_BYTE_STRING = 2;
+
+ /**
+ * @param ByteBuffer|string $bufOrBin
+ * @return mixed
+ * @throws WebAuthnException
+ */
+ public static function decode($bufOrBin) {
+ $buf = $bufOrBin instanceof ByteBuffer ? $bufOrBin : new ByteBuffer($bufOrBin);
+
+ $offset = 0;
+ $result = self::_parseItem($buf, $offset);
+ if ($offset !== $buf->getLength()) {
+ throw new WebAuthnException('Unused bytes after data item.', WebAuthnException::CBOR);
+ }
+ return $result;
+ }
+
+ /**
+ * @param ByteBuffer|string $bufOrBin
+ * @param int $startOffset
+ * @param int|null $endOffset
+ * @return mixed
+ */
+ public static function decodeInPlace($bufOrBin, $startOffset, &$endOffset = null) {
+ $buf = $bufOrBin instanceof ByteBuffer ? $bufOrBin : new ByteBuffer($bufOrBin);
+
+ $offset = $startOffset;
+ $data = self::_parseItem($buf, $offset);
+ $endOffset = $offset;
+ return $data;
+ }
+
+ // ---------------------
+ // protected
+ // ---------------------
+
+ /**
+ * @param ByteBuffer $buf
+ * @param int $offset
+ * @return mixed
+ */
+ protected static function _parseItem(ByteBuffer $buf, &$offset) {
+ $first = $buf->getByteVal($offset++);
+ $type = $first >> 5;
+ $val = $first & 0b11111;
+
+ if ($type === self::CBOR_MAJOR_FLOAT_SIMPLE) {
+ return self::_parseFloatSimple($val, $buf, $offset);
+ }
+
+ $val = self::_parseExtraLength($val, $buf, $offset);
+
+ return self::_parseItemData($type, $val, $buf, $offset);
+ }
+
+ protected static function _parseFloatSimple($val, ByteBuffer $buf, &$offset) {
+ switch ($val) {
+ case 24:
+ $val = $buf->getByteVal($offset);
+ $offset++;
+ return self::_parseSimple($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 WebAuthnException('Reserved value used.', WebAuthnException::CBOR);
+
+ case 31:
+ throw new WebAuthnException('Indefinite length is not supported.', WebAuthnException::CBOR);
+ }
+
+ return self::_parseSimple($val);
+ }
+
+ /**
+ * @param int $val
+ * @return mixed
+ * @throws WebAuthnException
+ */
+ protected static function _parseSimple($val) {
+ if ($val === 20) {
+ return false;
+ }
+ if ($val === 21) {
+ return true;
+ }
+ if ($val === 22) {
+ return null;
+ }
+ throw new WebAuthnException(sprintf('Unsupported simple value %d.', $val), WebAuthnException::CBOR);
+ }
+
+ protected static function _parseExtraLength($val, ByteBuffer $buf, &$offset) {
+ switch ($val) {
+ case 24:
+ $val = $buf->getByteVal($offset);
+ $offset++;
+ break;
+
+ case 25:
+ $val = $buf->getUint16Val($offset);
+ $offset += 2;
+ break;
+
+ case 26:
+ $val = $buf->getUint32Val($offset);
+ $offset += 4;
+ break;
+
+ case 27:
+ $val = $buf->getUint64Val($offset);
+ $offset += 8;
+ break;
+
+ case 28:
+ case 29:
+ case 30:
+ throw new WebAuthnException('Reserved value used.', WebAuthnException::CBOR);
+
+ case 31:
+ throw new WebAuthnException('Indefinite length is not supported.', WebAuthnException::CBOR);
+ }
+
+ return $val;
+ }
+
+ protected static function _parseItemData($type, $val, ByteBuffer $buf, &$offset) {
+ switch ($type) {
+ case self::CBOR_MAJOR_UNSIGNED_INT: // uint
+ return $val;
+
+ case self::CBOR_MAJOR_NEGATIVE_INT:
+ return -1 - $val;
+
+ case self::CBOR_MAJOR_BYTE_STRING:
+ $data = $buf->getBytes($offset, $val);
+ $offset += $val;
+ return new ByteBuffer($data); // bytes
+
+ case self::CBOR_MAJOR_TEXT_STRING:
+ $data = $buf->getBytes($offset, $val);
+ $offset += $val;
+ return $data; // UTF-8
+
+ case self::CBOR_MAJOR_ARRAY:
+ return self::_parseArray($buf, $offset, $val);
+
+ case self::CBOR_MAJOR_MAP:
+ return self::_parseMap($buf, $offset, $val);
+
+ case self::CBOR_MAJOR_TAG:
+ return self::_parseItem($buf, $offset); // 1 embedded data item
+ }
+
+ // This should never be reached
+ throw new WebAuthnException(sprintf('Unknown major type %d.', $type), WebAuthnException::CBOR);
+ }
+
+ protected static function _parseMap(ByteBuffer $buf, &$offset, $count) {
+ $map = array();
+
+ for ($i = 0; $i < $count; $i++) {
+ $mapKey = self::_parseItem($buf, $offset);
+ $mapVal = self::_parseItem($buf, $offset);
+
+ if (!\is_int($mapKey) && !\is_string($mapKey)) {
+ throw new WebAuthnException('Can only use strings or integers as map keys', WebAuthnException::CBOR);
+ }
+
+ $map[$mapKey] = $mapVal; // todo dup
+ }
+ return $map;
+ }
+
+ protected static function _parseArray(ByteBuffer $buf, &$offset, $count) {
+ $arr = array();
+ for ($i = 0; $i < $count; $i++) {
+ $arr[] = self::_parseItem($buf, $offset);
+ }
+
+ return $arr;
+ }
+}