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