221 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			221 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?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;
 | |
|     }
 | |
| }
 | 
