1 <?php 2 3 4 namespace lbuchs\WebAuthn\Binary; 5 use lbuchs\WebAuthn\WebAuthnException; 6 7 /** 8 * Modified version of https://github.com/madwizard-thomas/webauthn-server/blob/master/src/Format/ByteBuffer.php 9 * Copyright © 2018 Thomas Bleeker - MIT licensed 10 * Modified by Lukas Buchs 11 * Thanks Thomas for your work! 12 */ 13 class ByteBuffer implements \JsonSerializable, \Serializable { 14 /** 15 * @var bool 16 */ 17 public static $useBase64UrlEncoding = false; 18 19 /** 20 * @var string 21 */ 22 private $_data; 23 24 /** 25 * @var int 26 */ 27 private $_length; 28 29 public function __construct($binaryData) { 30 $this->_data = (string)$binaryData; 31 $this->_length = \strlen($binaryData); 32 } 33 34 35 // ----------------------- 36 // PUBLIC STATIC 37 // ----------------------- 38 39 /** 40 * create a ByteBuffer from a base64 url encoded string 41 * @param string $base64url 42 * @return ByteBuffer 43 */ 44 public static function fromBase64Url($base64url): ByteBuffer { 45 $bin = self::_base64url_decode($base64url); 46 if ($bin === false) { 47 throw new WebAuthnException('ByteBuffer: Invalid base64 url string', WebAuthnException::BYTEBUFFER); 48 } 49 return new ByteBuffer($bin); 50 } 51 52 /** 53 * create a ByteBuffer from a base64 url encoded string 54 * @param string $hex 55 * @return ByteBuffer 56 */ 57 public static function fromHex($hex): ByteBuffer { 58 $bin = \hex2bin($hex); 59 if ($bin === false) { 60 throw new WebAuthnException('ByteBuffer: Invalid hex string', WebAuthnException::BYTEBUFFER); 61 } 62 return new ByteBuffer($bin); 63 } 64 65 /** 66 * create a random ByteBuffer 67 * @param string $length 68 * @return ByteBuffer 69 */ 70 public static function randomBuffer($length): ByteBuffer { 71 if (\function_exists('random_bytes')) { // >PHP 7.0 72 return new ByteBuffer(\random_bytes($length)); 73 74 } else if (\function_exists('openssl_random_pseudo_bytes')) { 75 return new ByteBuffer(\openssl_random_pseudo_bytes($length)); 76 77 } else { 78 throw new WebAuthnException('ByteBuffer: cannot generate random bytes', WebAuthnException::BYTEBUFFER); 79 } 80 } 81 82 // ----------------------- 83 // PUBLIC 84 // ----------------------- 85 86 public function getBytes($offset, $length): string { 87 if ($offset < 0 || $length < 0 || ($offset + $length > $this->_length)) { 88 throw new WebAuthnException('ByteBuffer: Invalid offset or length', WebAuthnException::BYTEBUFFER); 89 } 90 return \substr($this->_data, $offset, $length); 91 } 92 93 public function getByteVal($offset): int { 94 if ($offset < 0 || $offset >= $this->_length) { 95 throw new WebAuthnException('ByteBuffer: Invalid offset', WebAuthnException::BYTEBUFFER); 96 } 97 return \ord(\substr($this->_data, $offset, 1)); 98 } 99 100 public function getJson($jsonFlags=0) { 101 $data = \json_decode($this->getBinaryString(), null, 512, $jsonFlags); 102 if (\json_last_error() !== JSON_ERROR_NONE) { 103 throw new WebAuthnException(\json_last_error_msg(), WebAuthnException::BYTEBUFFER); 104 } 105 return $data; 106 } 107 108 public function getLength(): int { 109 return $this->_length; 110 } 111 112 public function getUint16Val($offset) { 113 if ($offset < 0 || ($offset + 2) > $this->_length) { 114 throw new WebAuthnException('ByteBuffer: Invalid offset', WebAuthnException::BYTEBUFFER); 115 } 116 return unpack('n', $this->_data, $offset)[1]; 117 } 118 119 public function getUint32Val($offset) { 120 if ($offset < 0 || ($offset + 4) > $this->_length) { 121 throw new WebAuthnException('ByteBuffer: Invalid offset', WebAuthnException::BYTEBUFFER); 122 } 123 $val = unpack('N', $this->_data, $offset)[1]; 124 125 // Signed integer overflow causes signed negative numbers 126 if ($val < 0) { 127 throw new WebAuthnException('ByteBuffer: Value out of integer range.', WebAuthnException::BYTEBUFFER); 128 } 129 return $val; 130 } 131 132 public function getUint64Val($offset) { 133 if (PHP_INT_SIZE < 8) { 134 throw new WebAuthnException('ByteBuffer: 64-bit values not supported by this system', WebAuthnException::BYTEBUFFER); 135 } 136 if ($offset < 0 || ($offset + 8) > $this->_length) { 137 throw new WebAuthnException('ByteBuffer: Invalid offset', WebAuthnException::BYTEBUFFER); 138 } 139 $val = unpack('J', $this->_data, $offset)[1]; 140 141 // Signed integer overflow causes signed negative numbers 142 if ($val < 0) { 143 throw new WebAuthnException('ByteBuffer: Value out of integer range.', WebAuthnException::BYTEBUFFER); 144 } 145 146 return $val; 147 } 148 149 public function getHalfFloatVal($offset) { 150 //FROM spec pseudo decode_half(unsigned char *halfp) 151 $half = $this->getUint16Val($offset); 152 153 $exp = ($half >> 10) & 0x1f; 154 $mant = $half & 0x3ff; 155 156 if ($exp === 0) { 157 $val = $mant * (2 ** -24); 158 } elseif ($exp !== 31) { 159 $val = ($mant + 1024) * (2 ** ($exp - 25)); 160 } else { 161 $val = ($mant === 0) ? INF : NAN; 162 } 163 164 return ($half & 0x8000) ? -$val : $val; 165 } 166 167 public function getFloatVal($offset) { 168 if ($offset < 0 || ($offset + 4) > $this->_length) { 169 throw new WebAuthnException('ByteBuffer: Invalid offset', WebAuthnException::BYTEBUFFER); 170 } 171 return unpack('G', $this->_data, $offset)[1]; 172 } 173 174 public function getDoubleVal($offset) { 175 if ($offset < 0 || ($offset + 8) > $this->_length) { 176 throw new WebAuthnException('ByteBuffer: Invalid offset', WebAuthnException::BYTEBUFFER); 177 } 178 return unpack('E', $this->_data, $offset)[1]; 179 } 180 181 /** 182 * @return string 183 */ 184 public function getBinaryString(): string { 185 return $this->_data; 186 } 187 188 /** 189 * @param string|ByteBuffer $buffer 190 * @return bool 191 */ 192 public function equals($buffer): bool { 193 if (is_object($buffer) && $buffer instanceof ByteBuffer) { 194 return $buffer->getBinaryString() === $this->getBinaryString(); 195 196 } else if (is_string($buffer)) { 197 return $buffer === $this->getBinaryString(); 198 } 199 200 return false; 201 } 202 203 /** 204 * @return string 205 */ 206 public function getHex(): string { 207 return \bin2hex($this->_data); 208 } 209 210 /** 211 * @return bool 212 */ 213 public function isEmpty(): bool { 214 return $this->_length === 0; 215 } 216 217 218 /** 219 * jsonSerialize interface 220 * return binary data in RFC 1342-Like serialized string 221 * @return string 222 */ 223 public function jsonSerialize(): string { 224 if (ByteBuffer::$useBase64UrlEncoding) { 225 return self::_base64url_encode($this->_data); 226 227 } else { 228 return '=?BINARY?B?' . \base64_encode($this->_data) . '?='; 229 } 230 } 231 232 /** 233 * Serializable-Interface 234 * @return string 235 */ 236 public function serialize(): string { 237 return \serialize($this->_data); 238 } 239 240 /** 241 * Serializable-Interface 242 * @param string $serialized 243 */ 244 public function unserialize($serialized) { 245 $this->_data = \unserialize($serialized); 246 $this->_length = \strlen($this->_data); 247 } 248 249 /** 250 * (PHP 8 deprecates Serializable-Interface) 251 * @return array 252 */ 253 public function __serialize(): array { 254 return [ 255 'data' => \serialize($this->_data) 256 ]; 257 } 258 259 /** 260 * object to string 261 * @return string 262 */ 263 public function __toString(): string { 264 return $this->getHex(); 265 } 266 267 /** 268 * (PHP 8 deprecates Serializable-Interface) 269 * @param array $data 270 * @return void 271 */ 272 public function __unserialize($data) { 273 if ($data && isset($data['data'])) { 274 $this->_data = \unserialize($data['data']); 275 $this->_length = \strlen($this->_data); 276 } 277 } 278 279 // ----------------------- 280 // PROTECTED STATIC 281 // ----------------------- 282 283 /** 284 * base64 url decoding 285 * @param string $data 286 * @return string 287 */ 288 protected static function _base64url_decode($data): string { 289 return \base64_decode(\strtr($data, '-_', '+/') . \str_repeat('=', 3 - (3 + \strlen($data)) % 4)); 290 } 291 292 /** 293 * base64 url encoding 294 * @param string $data 295 * @return string 296 */ 297 protected static function _base64url_encode($data): string { 298 return \rtrim(\strtr(\base64_encode($data), '+/', '-_'), '='); 299 } 300 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body