123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- <?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;
- }
- }
|