PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / trunk
JetBackup – Backup, Restore & Migrate vtrunk
3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 1.6.11 1.6.12 1.6.13 1.6.15 1.6.5.1 1.6.8.8 1.6.9 1.6.9.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7.5 2.0.8.7 2.0.9.11 2.0.9.14 2.0.9.15 2.0.9.6 2.0.9.7 2.0.9.9 3.1.10.7 3.1.11.1 3.1.12.3 3.1.13.4 3.1.14.17 3.1.15.4 3.1.16.1 3.1.17.5 3.1.18.10 3.1.18.8 3.1.18.9 3.1.19.8 3.1.20.3 3.1.21.3 3.1.7.9 3.1.9.2 trunk 1.1.90 1.1.91 1.2.0 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2
backup / src / JetBackup / 3rdparty / phpseclib3 / Common / Functions / Strings.php
backup / src / JetBackup / 3rdparty / phpseclib3 / Common / Functions Last commit date
.htaccess 1 year ago Strings.php 1 year ago index.html 1 year ago web.config 1 year ago
Strings.php
462 lines
1 <?php
2
3 /**
4 * Common String Functions
5 *
6 * PHP version 5
7 *
8 * @author Jim Wigginton <terrafrost@php.net>
9 * @copyright 2016 Jim Wigginton
10 * @license http://www.opensource.org/licenses/mit-license.html MIT License
11 * @link http://phpseclib.sourceforge.net
12 */
13
14 declare(strict_types=1);
15
16 namespace phpseclib3\Common\Functions;
17
18 use ParagonIE\ConstantTime\Base64;
19 use ParagonIE\ConstantTime\Base64UrlSafe;
20 use ParagonIE\ConstantTime\Hex;
21 use phpseclib3\Exception\InvalidArgumentException;
22 use phpseclib3\Exception\LengthException;
23 use phpseclib3\Exception\RuntimeException;
24 use phpseclib3\Math\BigInteger;
25 use phpseclib3\Math\Common\FiniteField;
26
27 /**
28 * Common String Functions
29 *
30 * @author Jim Wigginton <terrafrost@php.net>
31 */
32 abstract class Strings
33 {
34 /**
35 * String Shift
36 *
37 * Inspired by array_shift
38 */
39 public static function shift(string &$string, int $index = 1): string
40 {
41 $substr = substr($string, 0, $index);
42 $string = substr($string, $index);
43 return $substr;
44 }
45
46 /**
47 * String Pop
48 *
49 * Inspired by array_pop
50 */
51 public static function pop(string &$string, int $index = 1): string
52 {
53 $substr = substr($string, -$index);
54 $string = substr($string, 0, -$index);
55 return $substr;
56 }
57
58 /**
59 * Parse SSH2-style string
60 *
61 * Returns either an array or a boolean if $data is malformed.
62 *
63 * Valid characters for $format are as follows:
64 *
65 * C = byte
66 * b = boolean (true/false)
67 * N = uint32
68 * Q = uint64
69 * s = string
70 * i = mpint
71 * L = name-list
72 *
73 * uint64 is not supported.
74 */
75 public static function unpackSSH2(string $format, string &$data): array
76 {
77 $format = self::formatPack($format);
78 $result = [];
79 for ($i = 0; $i < strlen($format); $i++) {
80 switch ($format[$i]) {
81 case 'C':
82 case 'b':
83 if (!strlen($data)) {
84 throw new LengthException('At least one byte needs to be present for successful C / b decodes');
85 }
86 break;
87 case 'N':
88 case 'i':
89 case 's':
90 case 'L':
91 if (strlen($data) < 4) {
92 throw new LengthException('At least four byte needs to be present for successful N / i / s / L decodes');
93 }
94 break;
95 case 'Q':
96 if (strlen($data) < 8) {
97 throw new LengthException('At least eight byte needs to be present for successful N / i / s / L decodes');
98 }
99 break;
100
101 default:
102 throw new InvalidArgumentException('$format contains an invalid character');
103 }
104 switch ($format[$i]) {
105 case 'C':
106 $result[] = ord(self::shift($data));
107 continue 2;
108 case 'b':
109 $result[] = ord(self::shift($data)) != 0;
110 continue 2;
111 case 'N':
112 [, $temp] = unpack('N', self::shift($data, 4));
113 $result[] = $temp;
114 continue 2;
115 case 'Q':
116 // pack() added support for Q in PHP 5.6.3 and PHP 5.6 is phpseclib 3's minimum version
117 // so in theory we could support this BUT, "64-bit format codes are not available for
118 // 32-bit versions" and phpseclib works on 32-bit installs. on 32-bit installs
119 // 64-bit floats can be used to get larger numbers then 32-bit signed ints would allow
120 // for. sure, you're not gonna get the full precision of 64-bit numbers but just because
121 // you need > 32-bit precision doesn't mean you need the full 64-bit precision
122 extract(unpack('Nupper/Nlower', self::shift($data, 8)));
123 $temp = $upper ? 4294967296 * $upper : 0;
124 $temp += $lower < 0 ? ($lower & 0x7FFFFFFFF) + 0x80000000 : $lower;
125 // $temp = hexdec(bin2hex(self::shift($data, 8)));
126 $result[] = $temp;
127 continue 2;
128 }
129 [, $length] = unpack('N', self::shift($data, 4));
130 if (strlen($data) < $length) {
131 throw new LengthException("$length bytes needed; " . strlen($data) . ' bytes available');
132 }
133 $temp = self::shift($data, $length);
134 switch ($format[$i]) {
135 case 'i':
136 $result[] = new BigInteger($temp, -256);
137 break;
138 case 's':
139 $result[] = $temp;
140 break;
141 case 'L':
142 $result[] = explode(',', $temp);
143 }
144 }
145
146 return $result;
147 }
148
149 /**
150 * Create SSH2-style string
151 *
152 * @param string|int|float|array|bool ...$elements
153 */
154 public static function packSSH2(string $format, ...$elements): string
155 {
156 $format = self::formatPack($format);
157 if (strlen($format) != count($elements)) {
158 throw new InvalidArgumentException('There must be as many arguments as there are characters in the $format string');
159 }
160 $result = '';
161 for ($i = 0; $i < strlen($format); $i++) {
162 $element = $elements[$i];
163 switch ($format[$i]) {
164 case 'C':
165 if (!is_int($element)) {
166 throw new InvalidArgumentException('Bytes must be represented as an integer between 0 and 255, inclusive.');
167 }
168 $result .= pack('C', $element);
169 break;
170 case 'b':
171 if (!is_bool($element)) {
172 throw new InvalidArgumentException('A boolean parameter was expected.');
173 }
174 $result .= $element ? "\1" : "\0";
175 break;
176 case 'Q':
177 if (!is_int($element) && !is_float($element)) {
178 throw new InvalidArgumentException('An integer was expected.');
179 }
180 // 4294967296 == 1 << 32
181 $result .= pack('NN', $element / 4294967296, $element);
182 break;
183 case 'N':
184 if (is_float($element)) {
185 $element = (int) $element;
186 }
187 if (!is_int($element)) {
188 throw new InvalidArgumentException('An integer was expected.');
189 }
190 $result .= pack('N', $element);
191 break;
192 case 's':
193 if (!self::is_stringable($element)) {
194 throw new InvalidArgumentException('A string was expected.');
195 }
196 $result .= pack('Na*', strlen($element), $element);
197 break;
198 case 'i':
199 if (!$element instanceof BigInteger && !$element instanceof FiniteField\Integer) {
200 throw new InvalidArgumentException('A phpseclib3\Math\BigInteger or phpseclib3\Math\Common\FiniteField\Integer object was expected.');
201 }
202 $element = $element->toBytes(true);
203 $result .= pack('Na*', strlen($element), $element);
204 break;
205 case 'L':
206 if (!is_array($element)) {
207 throw new InvalidArgumentException('An array was expected.');
208 }
209 $element = implode(',', $element);
210 $result .= pack('Na*', strlen($element), $element);
211 break;
212 default:
213 throw new InvalidArgumentException('$format contains an invalid character');
214 }
215 }
216 return $result;
217 }
218
219 /**
220 * Expand a pack string
221 *
222 * Converts C5 to CCCCC, for example.
223 */
224 private static function formatPack(string $format): string
225 {
226 $parts = preg_split('#(\d+)#', $format, -1, PREG_SPLIT_DELIM_CAPTURE);
227 $format = '';
228 for ($i = 1; $i < count($parts); $i += 2) {
229 $format .= substr($parts[$i - 1], 0, -1) . str_repeat($parts[$i - 1][-1], (int) $parts[$i]);
230 }
231 $format .= $parts[$i - 1];
232
233 return $format;
234 }
235
236 /**
237 * Convert binary data into bits
238 *
239 * bin2hex / hex2bin refer to base-256 encoded data as binary, whilst
240 * decbin / bindec refer to base-2 encoded data as binary. For the purposes
241 * of this function, bin refers to base-256 encoded data whilst bits refers
242 * to base-2 encoded data
243 */
244 public static function bits2bin(string $x): string
245 {
246 /*
247 // the pure-PHP approach is faster than the GMP approach
248 if (function_exists('gmp_export')) {
249 return strlen($x) ? gmp_export(gmp_init($x, 2)) : gmp_init(0);
250 }
251 */
252
253 if (preg_match('#[^01]#', $x)) {
254 throw new RuntimeException('The only valid characters are 0 and 1');
255 }
256
257 if (!defined('PHP_INT_MIN')) {
258 define('PHP_INT_MIN', ~PHP_INT_MAX);
259 }
260
261 $length = strlen($x);
262 if (!$length) {
263 return '';
264 }
265 $block_size = PHP_INT_SIZE << 3;
266 $pad = $block_size - ($length % $block_size);
267 if ($pad != $block_size) {
268 $x = str_repeat('0', $pad) . $x;
269 }
270
271 $parts = str_split($x, $block_size);
272 $str = '';
273 foreach ($parts as $part) {
274 $xor = $part[0] == '1' ? PHP_INT_MIN : 0;
275 $part[0] = '0';
276 $str .= pack(
277 PHP_INT_SIZE == 4 ? 'N' : 'J',
278 $xor ^ eval('return 0b' . $part . ';')
279 );
280 }
281 return ltrim($str, "\0");
282 }
283
284 /**
285 * Convert bits to binary data
286 */
287 public static function bin2bits(string $x, bool $trim = true): string
288 {
289 /*
290 // the pure-PHP approach is slower than the GMP approach BUT
291 // i want to the pure-PHP version to be easily unit tested as well
292 if (function_exists('gmp_import')) {
293 return gmp_strval(gmp_import($x), 2);
294 }
295 */
296
297 $len = strlen($x);
298 $mod = $len % PHP_INT_SIZE;
299 if ($mod) {
300 $x = str_pad($x, $len + PHP_INT_SIZE - $mod, "\0", STR_PAD_LEFT);
301 }
302
303 $bits = '';
304 if (PHP_INT_SIZE == 4) {
305 $digits = unpack('N*', $x);
306 foreach ($digits as $digit) {
307 $bits .= sprintf('%032b', $digit);
308 }
309 } else {
310 $digits = unpack('J*', $x);
311 foreach ($digits as $digit) {
312 $bits .= sprintf('%064b', $digit);
313 }
314 }
315
316 return $trim ? ltrim($bits, '0') : $bits;
317 }
318
319 /**
320 * Switch Endianness Bit Order
321 */
322 public static function switchEndianness(string $x): string
323 {
324 $r = '';
325 for ($i = strlen($x) - 1; $i >= 0; $i--) {
326 $b = ord($x[$i]);
327 if (PHP_INT_SIZE === 8) {
328 // 3 operations
329 // from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64BitsDiv
330 $r .= chr((($b * 0x0202020202) & 0x010884422010) % 1023);
331 } else {
332 // 7 operations
333 // from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits
334 $p1 = ($b * 0x0802) & 0x22110;
335 $p2 = ($b * 0x8020) & 0x88440;
336 $r .= chr(
337 (($p1 | $p2) * 0x10101) >> 16
338 );
339 }
340 }
341 return $r;
342 }
343
344 /**
345 * Increment the current string
346 */
347 public static function increment_str(string &$var): string
348 {
349 if (function_exists('sodium_increment')) {
350 $var = strrev($var);
351 sodium_increment($var);
352 $var = strrev($var);
353 return $var;
354 }
355
356 for ($i = 4; $i <= strlen($var); $i += 4) {
357 $temp = substr($var, -$i, 4);
358 switch ($temp) {
359 case "\xFF\xFF\xFF\xFF":
360 $var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4);
361 break;
362 case "\x7F\xFF\xFF\xFF":
363 $var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4);
364 return $var;
365 default:
366 $temp = unpack('Nnum', $temp);
367 $var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4);
368 return $var;
369 }
370 }
371
372 $remainder = strlen($var) % 4;
373
374 if ($remainder == 0) {
375 return $var;
376 }
377
378 $temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT));
379 $temp = substr(pack('N', $temp['num'] + 1), -$remainder);
380 $var = substr_replace($var, $temp, 0, $remainder);
381
382 return $var;
383 }
384
385 /**
386 * Find whether the type of a variable is string (or could be converted to one)
387 *
388 * @psalm-assert-if-true string|\Stringable $var
389 */
390 public static function is_stringable($var): bool
391 {
392 return is_string($var) || (is_object($var) && method_exists($var, '__toString'));
393 }
394
395 /**
396 * Constant Time Base64-decoding
397 *
398 * ParagoneIE\ConstantTime doesn't use libsodium if it's available so we'll do so
399 * ourselves. see https://github.com/paragonie/constant_time_encoding/issues/39
400 */
401 public static function base64_decode(string $data): string
402 {
403 return function_exists('sodium_base642bin') ?
404 sodium_base642bin($data, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING, '=') :
405 Base64::decode($data);
406 }
407
408 /**
409 * Constant Time Base64-decoding (URL safe)
410 */
411 public static function base64url_decode(string $data): string
412 {
413 // return self::base64_decode(str_replace(['-', '_'], ['+', '/'], $data));
414
415 return function_exists('sodium_base642bin') ?
416 sodium_base642bin($data, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING, '=') :
417 Base64UrlSafe::decode($data);
418 }
419
420 /**
421 * Constant Time Base64-encoding
422 */
423 public static function base64_encode(string $data): string
424 {
425 return function_exists('sodium_bin2base64') ?
426 sodium_bin2base64($data, SODIUM_BASE64_VARIANT_ORIGINAL) :
427 Base64::encode($data);
428 }
429
430 /**
431 * Constant Time Base64-encoding (URL safe)
432 */
433 public static function base64url_encode(string $data): string
434 {
435 // return str_replace(['+', '/'], ['-', '_'], self::base64_encode($data));
436
437 return function_exists('sodium_bin2base64') ?
438 sodium_bin2base64($data, SODIUM_BASE64_VARIANT_URLSAFE) :
439 Base64UrlSafe::encode($data);
440 }
441
442 /**
443 * Constant Time Hex Decoder
444 */
445 public static function hex2bin(string $data): string
446 {
447 return function_exists('sodium_hex2bin') ?
448 sodium_hex2bin($data) :
449 Hex::decode($data);
450 }
451
452 /**
453 * Constant Time Hex Encoder
454 */
455 public static function bin2hex(string $data): string
456 {
457 return function_exists('sodium_bin2hex') ?
458 sodium_bin2hex($data) :
459 Hex::encode($data);
460 }
461 }
462