1 <?php 2 3 4 namespace lbuchs\WebAuthn\CBOR; 5 use lbuchs\WebAuthn\WebAuthnException; 6 use lbuchs\WebAuthn\Binary\ByteBuffer; 7 8 /** 9 * Modified version of https://github.com/madwizard-thomas/webauthn-server/blob/master/src/Format/CborDecoder.php 10 * Copyright © 2018 Thomas Bleeker - MIT licensed 11 * Modified by Lukas Buchs 12 * Thanks Thomas for your work! 13 */ 14 class CborDecoder { 15 const CBOR_MAJOR_UNSIGNED_INT = 0; 16 const CBOR_MAJOR_TEXT_STRING = 3; 17 const CBOR_MAJOR_FLOAT_SIMPLE = 7; 18 const CBOR_MAJOR_NEGATIVE_INT = 1; 19 const CBOR_MAJOR_ARRAY = 4; 20 const CBOR_MAJOR_TAG = 6; 21 const CBOR_MAJOR_MAP = 5; 22 const CBOR_MAJOR_BYTE_STRING = 2; 23 24 /** 25 * @param ByteBuffer|string $bufOrBin 26 * @return mixed 27 * @throws WebAuthnException 28 */ 29 public static function decode($bufOrBin) { 30 $buf = $bufOrBin instanceof ByteBuffer ? $bufOrBin : new ByteBuffer($bufOrBin); 31 32 $offset = 0; 33 $result = self::_parseItem($buf, $offset); 34 if ($offset !== $buf->getLength()) { 35 throw new WebAuthnException('Unused bytes after data item.', WebAuthnException::CBOR); 36 } 37 return $result; 38 } 39 40 /** 41 * @param ByteBuffer|string $bufOrBin 42 * @param int $startOffset 43 * @param int|null $endOffset 44 * @return mixed 45 */ 46 public static function decodeInPlace($bufOrBin, $startOffset, &$endOffset = null) { 47 $buf = $bufOrBin instanceof ByteBuffer ? $bufOrBin : new ByteBuffer($bufOrBin); 48 49 $offset = $startOffset; 50 $data = self::_parseItem($buf, $offset); 51 $endOffset = $offset; 52 return $data; 53 } 54 55 // --------------------- 56 // protected 57 // --------------------- 58 59 /** 60 * @param ByteBuffer $buf 61 * @param int $offset 62 * @return mixed 63 */ 64 protected static function _parseItem(ByteBuffer $buf, &$offset) { 65 $first = $buf->getByteVal($offset++); 66 $type = $first >> 5; 67 $val = $first & 0b11111; 68 69 if ($type === self::CBOR_MAJOR_FLOAT_SIMPLE) { 70 return self::_parseFloatSimple($val, $buf, $offset); 71 } 72 73 $val = self::_parseExtraLength($val, $buf, $offset); 74 75 return self::_parseItemData($type, $val, $buf, $offset); 76 } 77 78 protected static function _parseFloatSimple($val, ByteBuffer $buf, &$offset) { 79 switch ($val) { 80 case 24: 81 $val = $buf->getByteVal($offset); 82 $offset++; 83 return self::_parseSimple($val); 84 85 case 25: 86 $floatValue = $buf->getHalfFloatVal($offset); 87 $offset += 2; 88 return $floatValue; 89 90 case 26: 91 $floatValue = $buf->getFloatVal($offset); 92 $offset += 4; 93 return $floatValue; 94 95 case 27: 96 $floatValue = $buf->getDoubleVal($offset); 97 $offset += 8; 98 return $floatValue; 99 100 case 28: 101 case 29: 102 case 30: 103 throw new WebAuthnException('Reserved value used.', WebAuthnException::CBOR); 104 105 case 31: 106 throw new WebAuthnException('Indefinite length is not supported.', WebAuthnException::CBOR); 107 } 108 109 return self::_parseSimple($val); 110 } 111 112 /** 113 * @param int $val 114 * @return mixed 115 * @throws WebAuthnException 116 */ 117 protected static function _parseSimple($val) { 118 if ($val === 20) { 119 return false; 120 } 121 if ($val === 21) { 122 return true; 123 } 124 if ($val === 22) { 125 return null; 126 } 127 throw new WebAuthnException(sprintf('Unsupported simple value %d.', $val), WebAuthnException::CBOR); 128 } 129 130 protected static function _parseExtraLength($val, ByteBuffer $buf, &$offset) { 131 switch ($val) { 132 case 24: 133 $val = $buf->getByteVal($offset); 134 $offset++; 135 break; 136 137 case 25: 138 $val = $buf->getUint16Val($offset); 139 $offset += 2; 140 break; 141 142 case 26: 143 $val = $buf->getUint32Val($offset); 144 $offset += 4; 145 break; 146 147 case 27: 148 $val = $buf->getUint64Val($offset); 149 $offset += 8; 150 break; 151 152 case 28: 153 case 29: 154 case 30: 155 throw new WebAuthnException('Reserved value used.', WebAuthnException::CBOR); 156 157 case 31: 158 throw new WebAuthnException('Indefinite length is not supported.', WebAuthnException::CBOR); 159 } 160 161 return $val; 162 } 163 164 protected static function _parseItemData($type, $val, ByteBuffer $buf, &$offset) { 165 switch ($type) { 166 case self::CBOR_MAJOR_UNSIGNED_INT: // uint 167 return $val; 168 169 case self::CBOR_MAJOR_NEGATIVE_INT: 170 return -1 - $val; 171 172 case self::CBOR_MAJOR_BYTE_STRING: 173 $data = $buf->getBytes($offset, $val); 174 $offset += $val; 175 return new ByteBuffer($data); // bytes 176 177 case self::CBOR_MAJOR_TEXT_STRING: 178 $data = $buf->getBytes($offset, $val); 179 $offset += $val; 180 return $data; // UTF-8 181 182 case self::CBOR_MAJOR_ARRAY: 183 return self::_parseArray($buf, $offset, $val); 184 185 case self::CBOR_MAJOR_MAP: 186 return self::_parseMap($buf, $offset, $val); 187 188 case self::CBOR_MAJOR_TAG: 189 return self::_parseItem($buf, $offset); // 1 embedded data item 190 } 191 192 // This should never be reached 193 throw new WebAuthnException(sprintf('Unknown major type %d.', $type), WebAuthnException::CBOR); 194 } 195 196 protected static function _parseMap(ByteBuffer $buf, &$offset, $count) { 197 $map = array(); 198 199 for ($i = 0; $i < $count; $i++) { 200 $mapKey = self::_parseItem($buf, $offset); 201 $mapVal = self::_parseItem($buf, $offset); 202 203 if (!\is_int($mapKey) && !\is_string($mapKey)) { 204 throw new WebAuthnException('Can only use strings or integers as map keys', WebAuthnException::CBOR); 205 } 206 207 $map[$mapKey] = $mapVal; // todo dup 208 } 209 return $map; 210 } 211 212 protected static function _parseArray(ByteBuffer $buf, &$offset, $count) { 213 $arr = array(); 214 for ($i = 0; $i < $count; $i++) { 215 $arr[] = self::_parseItem($buf, $offset); 216 } 217 218 return $arr; 219 } 220 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body