PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 4.0.0
WP 2FA – Two-factor authentication for WordPress v4.0.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Admin / Methods / passkeys / class-byte-buffer.php
wp-2fa / includes / classes / Admin / Methods / passkeys Last commit date
assets 1 day ago format 1 day ago helpers 1 day ago class-ajax-passkeys.php 1 day ago class-api-register.php 1 day ago class-api-signin.php 1 day ago class-attestation-object.php 1 day ago class-authenticate-server.php 1 day ago class-authenticator-data.php 1 day ago class-byte-buffer.php 1 day ago class-chor-decoder.php 1 day ago class-passkeys-endpoints.php 1 day ago class-passkeys-user-profile.php 1 day ago class-passkeys-wizard-steps.php 1 day ago class-passkeys.php 1 day ago class-pending-2fa-helper.php 1 day ago class-source-repository.php 1 day ago class-web-authn-exception.php 1 day ago class-web-authn.php 1 day ago index.php 1 day ago
class-byte-buffer.php
505 lines
1 <?php
2 /**
3 * Responsible for the Passkeys extension plugin settings
4 *
5 * @package wp2fa
6 * @subpackage passkeys
7 * @since 3.0.0
8 * @copyright 2026 Melapress
9 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
10 * @link https://wordpress.org/plugins/wp-2fa/
11 */
12
13 declare(strict_types=1);
14
15 namespace WP2FA\Methods\Passkeys;
16
17 defined( 'ABSPATH' ) || exit; // Exit if accessed directly.
18
19 /**
20 * Passkeys Byte_Buffer
21 */
22 if ( ! class_exists( '\WP2FA\Methods\Passkeys\Byte_Buffer' ) ) {
23
24 /**
25 * Modified version of https://github.com/madwizard-thomas/Web_Authn-server/blob/master/src/Format/Byte_Buffer.php
26 * Copyright © 2018 Thomas Bleeker - MIT licensed
27 * Modified by Lukas Buchs
28 * Thanks Thomas for your work!
29 */
30 class Byte_Buffer implements \JsonSerializable {
31
32 /**
33 * Undocumented variable
34 *
35 * @var boolean
36 *
37 * @since 3.0.0
38 */
39 public static $use_base_64_url_encoding = false;
40
41 /**
42 * Undocumented variable
43 *
44 * @var string
45 *
46 * @since 3.0.0
47 */
48 private $data;
49
50 /**
51 * Undocumented variable
52 *
53 * @var int
54 *
55 * @since 3.0.0
56 */
57 private $length;
58
59 /**
60 * Default constructor
61 *
62 * @param string $binary_data - String with binary data.
63 *
64 * @since 3.0.0
65 */
66 public function __construct( $binary_data ) {
67 $this->data = (string) $binary_data;
68 $this->length = \strlen( $binary_data );
69 }
70
71 // -----------------------
72 // PUBLIC STATIC
73 // -----------------------
74
75 /**
76 * Create a Byte_Buffer from a base64 url encoded string
77 *
78 * @param string $base64url - The Url Encode.
79 *
80 * @return Byte_Buffer
81 *
82 * @throws Web_Authn_Exception - When url string is invalid.
83 *
84 * @since 3.0.0
85 */
86 public static function fromBase64Url( $base64url ): Byte_Buffer {
87 $bin = self::_base64url_decode( $base64url );
88 if ( false === $bin ) {
89 throw new Web_Authn_Exception( 'Byte_Buffer: Invalid base64 url string', Web_Authn_Exception::BYTE_BUFFER );
90 }
91 return new Byte_Buffer( $bin );
92 }
93
94 /**
95 * Create a Byte_Buffer from a base64 url encoded string
96 *
97 * @param string $hex - Hex string.
98 *
99 * @return Byte_Buffer
100 *
101 * @throws Web_Authn_Exception - When url string is invalid.
102 *
103 * @since 3.0.0
104 */
105 public static function fromHex( $hex ): Byte_Buffer {
106 $bin = \hex2bin( $hex );
107 if ( false === $bin ) {
108 throw new Web_Authn_Exception( 'Byte_Buffer: Invalid hex string', Web_Authn_Exception::BYTE_BUFFER );
109 }
110 return new Byte_Buffer( $bin );
111 }
112
113 /**
114 * Create a random Byte_Buffer
115 *
116 * @param int $length - The size.
117 *
118 * @return Byte_Buffer
119 *
120 * @throws Web_Authn_Exception - When url string is invalid.
121 *
122 * @since 3.0.0
123 */
124 public static function randomBuffer( $length ): Byte_Buffer {
125 if ( \function_exists( 'random_bytes' ) ) { // >PHP 7.0
126 return new Byte_Buffer( \random_bytes( $length ) );
127
128 } elseif ( \function_exists( 'openssl_random_pseudo_bytes' ) ) {
129 return new Byte_Buffer( \openssl_random_pseudo_bytes( $length ) );
130
131 } else {
132 throw new Web_Authn_Exception( 'Byte_Buffer: cannot generate random bytes', Web_Authn_Exception::BYTE_BUFFER );
133 }
134 }
135
136 // -----------------------
137 // PUBLIC
138 // -----------------------
139
140 /**
141 * Returns the bytes
142 *
143 * @param int $offset - Offset.
144 * @param int $length - Length.
145 *
146 * @return string
147 *
148 * @throws Web_Authn_Exception - When url string is invalid.
149 *
150 * @since 3.0.0
151 */
152 public function getBytes( $offset, $length ): string {
153 if ( $offset < 0 || $length < 0 || ( $offset + $length > $this->length ) ) {
154 throw new Web_Authn_Exception( 'Byte_Buffer: Invalid offset or length', Web_Authn_Exception::BYTE_BUFFER );
155 }
156 return \substr( $this->data, $offset, $length );
157 }
158
159 /**
160 * Returns the bytes
161 *
162 * @param int $offset - Offset.
163 *
164 * @return string
165 *
166 * @throws Web_Authn_Exception - When url string is invalid.
167 *
168 * @since 3.0.0
169 */
170 public function getByteVal( $offset ): int {
171 if ( $offset < 0 || $offset >= $this->length ) {
172 throw new Web_Authn_Exception( 'Byte_Buffer: Invalid offset', Web_Authn_Exception::BYTE_BUFFER );
173 }
174 return \ord( \substr( $this->data, $offset, 1 ) );
175 }
176
177 /**
178 * Returns the bytes
179 *
180 * @param int $json_flags - Json flags.
181 *
182 * @return string
183 *
184 * @throws Web_Authn_Exception - When url string is invalid.
185 *
186 * @since 3.0.0
187 */
188 public function getJson( $json_flags = 0 ) {
189 $data = \json_decode( $this->getBinaryString(), null, 512, $json_flags );
190 if ( \json_last_error() !== JSON_ERROR_NONE ) {
191 throw new Web_Authn_Exception( \json_last_error_msg(), Web_Authn_Exception::BYTE_BUFFER );
192 }
193 return $data;
194 }
195
196 /**
197 * Returns the length
198 *
199 * @return int
200 *
201 * @since 3.0.0
202 */
203 public function getLength(): int {
204 return $this->length;
205 }
206
207 /**
208 * Returns the bytes
209 *
210 * @param int $offset - The offsets.
211 *
212 * @return string
213 *
214 * @throws Web_Authn_Exception - When url string is invalid.
215 *
216 * @since 3.0.0
217 */
218 public function getUint16Val( $offset ) {
219 if ( $offset < 0 || ( $offset + 2 ) > $this->length ) {
220 throw new Web_Authn_Exception( 'Byte_Buffer: Invalid offset', Web_Authn_Exception::BYTE_BUFFER );
221 }
222 return unpack( 'n', $this->data, $offset )[1];
223 }
224
225 /**
226 * Returns the bytes
227 *
228 * @param int $offset - The offsets.
229 *
230 * @return string
231 *
232 * @throws Web_Authn_Exception - When url string is invalid.
233 *
234 * @since 3.0.0
235 */
236 public function getUint32Val( $offset ) {
237 if ( $offset < 0 || ( $offset + 4 ) > $this->length ) {
238 throw new Web_Authn_Exception( 'Byte_Buffer: Invalid offset', Web_Authn_Exception::BYTE_BUFFER );
239 }
240 $val = unpack( 'N', $this->data, $offset )[1];
241
242 // Signed integer overflow causes signed negative numbers.
243 if ( $val < 0 ) {
244 throw new Web_Authn_Exception( 'Byte_Buffer: Value out of integer range.', Web_Authn_Exception::BYTE_BUFFER );
245 }
246 return $val;
247 }
248
249 /**
250 * Returns the bytes
251 *
252 * @param int $offset - The offsets.
253 *
254 * @return string
255 *
256 * @throws Web_Authn_Exception - When url string is invalid.
257 *
258 * @since 3.0.0
259 */
260 public function getUint64Val( $offset ) {
261 if ( PHP_INT_SIZE < 8 ) {
262 throw new Web_Authn_Exception( 'Byte_Buffer: 64-bit values not supported by this system', Web_Authn_Exception::BYTE_BUFFER );
263 }
264 if ( $offset < 0 || ( $offset + 8 ) > $this->length ) {
265 throw new Web_Authn_Exception( 'Byte_Buffer: Invalid offset', Web_Authn_Exception::BYTE_BUFFER );
266 }
267 $val = unpack( 'J', $this->data, $offset )[1];
268
269 // Signed integer overflow causes signed negative numbers.
270 if ( $val < 0 ) {
271 throw new Web_Authn_Exception( 'Byte_Buffer: Value out of integer range.', Web_Authn_Exception::BYTE_BUFFER );
272 }
273
274 return $val;
275 }
276
277 /**
278 * Returns the bytes
279 *
280 * @param int $offset - The offsets.
281 *
282 * @return string
283 *
284 * @throws Web_Authn_Exception - When url string is invalid.
285 *
286 * @since 3.0.0
287 */
288 public function getHalfFloatVal( $offset ) {
289 // FROM spec pseudo decode_half(unsigned char *halfp).
290 $half = $this->getUint16Val( $offset );
291
292 $exp = ( $half >> 10 ) & 0x1f;
293 $mant = $half & 0x3ff;
294
295 if ( 0 === $exp ) {
296 $val = $mant * ( 2 ** -24 );
297 } elseif ( 31 !== $exp ) {
298 $val = ( $mant + 1024 ) * ( 2 ** ( $exp - 25 ) );
299 } else {
300 $val = ( 0 === $mant ) ? INF : NAN;
301 }
302
303 return ( $half & 0x8000 ) ? -$val : $val;
304 }
305
306 /**
307 * Returns the bytes
308 *
309 * @param int $offset - The offsets.
310 *
311 * @return string
312 *
313 * @throws Web_Authn_Exception - When url string is invalid.
314 *
315 * @since 3.0.0
316 */
317 public function getFloatVal( $offset ) {
318 if ( $offset < 0 || ( $offset + 4 ) > $this->length ) {
319 throw new Web_Authn_Exception( 'Byte_Buffer: Invalid offset', Web_Authn_Exception::BYTE_BUFFER );
320 }
321 return unpack( 'G', $this->data, $offset )[1];
322 }
323
324 /**
325 * Returns the bytes
326 *
327 * @param int $offset - The offsets.
328 *
329 * @return string
330 *
331 * @throws Web_Authn_Exception - When url string is invalid.
332 *
333 * @since 3.0.0
334 */
335 public function getDoubleVal( $offset ) {
336 if ( $offset < 0 || ( $offset + 8 ) > $this->length ) {
337 throw new Web_Authn_Exception( 'Byte_Buffer: Invalid offset', Web_Authn_Exception::BYTE_BUFFER );
338 }
339 return unpack( 'E', $this->data, $offset )[1];
340 }
341
342 /**
343 * Returns the bytes
344 *
345 * @return string
346 *
347 * @since 3.0.0
348 */
349 public function getBinaryString(): string {
350 return $this->data;
351 }
352
353 /**
354 * Returns the bytes
355 *
356 * @param string|Byte_Buffer $buffer - The buffer.
357 *
358 * @return bool
359 *
360 * @since 3.0.0
361 */
362 public function equals( $buffer ): bool {
363 if ( is_object( $buffer ) && $buffer instanceof Byte_Buffer ) {
364 return $buffer->getBinaryString() === $this->getBinaryString();
365
366 } elseif ( is_string( $buffer ) ) {
367 return $buffer === $this->getBinaryString();
368 }
369
370 return false;
371 }
372
373 /**
374 * Returns the bytes
375 *
376 * @return string
377 *
378 * @since 3.0.0
379 */
380 public function getHex(): string {
381 return \bin2hex( $this->data );
382 }
383
384 /**
385 * Returns the bytes
386 *
387 * @return bool
388 *
389 * @since 3.0.0
390 */
391 public function isEmpty(): bool {
392 return 0 === $this->length;
393 }
394
395
396 /**
397 * JsonSerialize interface
398 * return binary data in RFC 1342-Like serialized string
399 *
400 * @return string
401 */
402 public function jsonSerialize(): string {
403 if ( self::$use_base_64_url_encoding ) {
404 return self::_base64url_encode( $this->data );
405
406 } else {
407 return '=?BINARY?B?' . \base64_encode( $this->data ) . '?=';
408 }
409 }
410
411 /**
412 * Serializable-Interface
413 *
414 * @return string
415 *
416 * @since 3.0.0
417 */
418 public function serialize(): string {
419 return \serialize( $this->data );
420 }
421
422 /**
423 * Serializable-Interface
424 *
425 * @param string $serialized - Serialized string.
426 *
427 * @since 3.0.0
428 */
429 public function unserialize( $serialized ) {
430 $this->data = \unserialize( $serialized, array( 'allowed_classes' => false ) );
431 $this->length = \strlen( $this->data );
432 }
433
434 /**
435 * (PHP 8 deprecates Serializable-Interface)
436 *
437 * @return array
438 *
439 * @since 3.0.0
440 */
441 public function __serialize(): array {
442 return array(
443 'data' => \serialize( $this->data ),
444 );
445 }
446
447 /**
448 * Object to string
449 *
450 * @return string
451 *
452 * @since 3.0.0
453 */
454 public function __toString(): string {
455 return $this->getHex();
456 }
457
458 /**
459 * (PHP 8 deprecates Serializable-Interface)
460 *
461 * @param array $data - The data.
462 *
463 * @return void
464 *
465 * @since 3.0.0
466 */
467 public function __unserialize( $data ) {
468 if ( $data && isset( $data['data'] ) ) {
469 $this->data = \unserialize( $data['data'], array( 'allowed_classes' => false ) );
470 $this->length = \strlen( $this->data );
471 }
472 }
473
474 // -----------------------
475 // PROTECTED STATIC
476 // -----------------------
477
478 /**
479 * Base64 url decoding
480 *
481 * @param string $data - The data.
482 *
483 * @return string
484 *
485 * @since 3.0.0
486 */
487 protected static function _base64url_decode( $data ): string {
488 return \base64_decode( \strtr( $data, '-_', '+/' ) . \str_repeat( '=', 3 - ( 3 + \strlen( $data ) ) % 4 ) );
489 }
490
491 /**
492 * Base64 url encoding
493 *
494 * @param string $data - The data.
495 *
496 * @return string
497 *
498 * @since 3.0.0
499 */
500 protected static function _base64url_encode( $data ): string {
501 return \rtrim( \strtr( \base64_encode( $data ), '+/', '-_' ), '=' );
502 }
503 }
504 }
505