source: vendor/paragonie/constant_time_encoding/src/Base32.php@ f9c482b

Last change on this file since f9c482b was f9c482b, checked in by Vlado 222039 <vlado.popovski@…>, 7 days ago

Upload new project files

  • Property mode set to 100644
File size: 17.4 KB
Line 
1<?php
2declare(strict_types=1);
3namespace ParagonIE\ConstantTime;
4
5use InvalidArgumentException;
6use RangeException;
7use TypeError;
8
9/**
10 * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
11 * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
12 *
13 * Permission is hereby granted, free of charge, to any person obtaining a copy
14 * of this software and associated documentation files (the "Software"), to deal
15 * in the Software without restriction, including without limitation the rights
16 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 * copies of the Software, and to permit persons to whom the Software is
18 * furnished to do so, subject to the following conditions:
19 *
20 * The above copyright notice and this permission notice shall be included in all
21 * copies or substantial portions of the Software.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 * SOFTWARE.
30 */
31
32/**
33 * Class Base32
34 * [A-Z][2-7]
35 *
36 * @package ParagonIE\ConstantTime
37 */
38abstract class Base32 implements EncoderInterface
39{
40 /**
41 * Decode a Base32-encoded string into raw binary
42 *
43 * @param string $encodedString
44 * @param bool $strictPadding
45 * @return string
46 */
47 public static function decode(
48 #[\SensitiveParameter]
49 string $encodedString,
50 bool $strictPadding = false
51 ): string {
52 return static::doDecode($encodedString, false, $strictPadding);
53 }
54
55 /**
56 * Decode an uppercase Base32-encoded string into raw binary
57 *
58 * @param string $src
59 * @param bool $strictPadding
60 * @return string
61 */
62 public static function decodeUpper(
63 #[\SensitiveParameter]
64 string $src,
65 bool $strictPadding = false
66 ): string {
67 return static::doDecode($src, true, $strictPadding);
68 }
69
70 /**
71 * Encode into Base32 (RFC 4648)
72 *
73 * @param string $binString
74 * @return string
75 * @throws TypeError
76 */
77 public static function encode(
78 #[\SensitiveParameter]
79 string $binString
80 ): string {
81 return static::doEncode($binString, false, true);
82 }
83
84 /**
85 * Encode into Base32 (RFC 4648)
86 *
87 * @param string $src
88 * @return string
89 * @throws TypeError
90 */
91 public static function encodeUnpadded(
92 #[\SensitiveParameter]
93 string $src
94 ): string {
95 return static::doEncode($src, false, false);
96 }
97
98 /**
99 * Encode into uppercase Base32 (RFC 4648)
100 *
101 * @param string $src
102 * @return string
103 * @throws TypeError
104 */
105 public static function encodeUpper(
106 #[\SensitiveParameter]
107 string $src
108 ): string {
109 return static::doEncode($src, true, true);
110 }
111
112 /**
113 * Encode into uppercase Base32 (RFC 4648)
114 *
115 * @param string $src
116 * @return string
117 * @throws TypeError
118 */
119 public static function encodeUpperUnpadded(
120 #[\SensitiveParameter]
121 string $src
122 ): string {
123 return static::doEncode($src, true, false);
124 }
125
126 /**
127 * Uses bitwise operators instead of table-lookups to turn 5-bit integers
128 * into 8-bit integers.
129 *
130 * @param int $src
131 * @return int
132 */
133 protected static function decode5Bits(int $src): int
134 {
135 $ret = -1;
136
137 // if ($src > 96 && $src < 123) $ret += $src - 97 + 1; // -64
138 $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 96);
139
140 // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23
141 $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23);
142
143 return $ret;
144 }
145
146 /**
147 * Uses bitwise operators instead of table-lookups to turn 5-bit integers
148 * into 8-bit integers.
149 *
150 * Uppercase variant.
151 *
152 * @param int $src
153 * @return int
154 */
155 protected static function decode5BitsUpper(int $src): int
156 {
157 $ret = -1;
158
159 // if ($src > 64 && $src < 91) $ret += $src - 65 + 1; // -64
160 $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
161
162 // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23
163 $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23);
164
165 return $ret;
166 }
167
168 /**
169 * Uses bitwise operators instead of table-lookups to turn 8-bit integers
170 * into 5-bit integers.
171 *
172 * @param int $src
173 * @return string
174 */
175 protected static function encode5Bits(int $src): string
176 {
177 $diff = 0x61;
178
179 // if ($src > 25) $ret -= 72;
180 $diff -= ((25 - $src) >> 8) & 73;
181
182 return \pack('C', $src + $diff);
183 }
184
185 /**
186 * Uses bitwise operators instead of table-lookups to turn 8-bit integers
187 * into 5-bit integers.
188 *
189 * Uppercase variant.
190 *
191 * @param int $src
192 * @return string
193 */
194 protected static function encode5BitsUpper(int $src): string
195 {
196 $diff = 0x41;
197
198 // if ($src > 25) $ret -= 40;
199 $diff -= ((25 - $src) >> 8) & 41;
200
201 return \pack('C', $src + $diff);
202 }
203
204 /**
205 * @param string $encodedString
206 * @param bool $upper
207 * @return string
208 */
209 public static function decodeNoPadding(
210 #[\SensitiveParameter]
211 string $encodedString,
212 bool $upper = false
213 ): string {
214 $srcLen = Binary::safeStrlen($encodedString);
215 if ($srcLen === 0) {
216 return '';
217 }
218 if (($srcLen & 7) === 0) {
219 for ($j = 0; $j < 7 && $j < $srcLen; ++$j) {
220 if ($encodedString[$srcLen - $j - 1] === '=') {
221 throw new InvalidArgumentException(
222 "decodeNoPadding() doesn't tolerate padding"
223 );
224 }
225 }
226 }
227 return static::doDecode(
228 $encodedString,
229 $upper,
230 true
231 );
232 }
233
234 /**
235 * Base32 decoding
236 *
237 * @param string $src
238 * @param bool $upper
239 * @param bool $strictPadding
240 * @return string
241 *
242 * @throws TypeError
243 */
244 protected static function doDecode(
245 #[\SensitiveParameter]
246 string $src,
247 bool $upper = false,
248 bool $strictPadding = false
249 ): string {
250 // We do this to reduce code duplication:
251 $method = $upper
252 ? 'decode5BitsUpper'
253 : 'decode5Bits';
254
255 // Remove padding
256 $srcLen = Binary::safeStrlen($src);
257 if ($srcLen === 0) {
258 return '';
259 }
260 if ($strictPadding) {
261 if (($srcLen & 7) === 0) {
262 for ($j = 0; $j < 7; ++$j) {
263 if ($src[$srcLen - 1] === '=') {
264 $srcLen--;
265 } else {
266 break;
267 }
268 }
269 }
270 if (($srcLen & 7) === 1) {
271 throw new RangeException(
272 'Incorrect padding'
273 );
274 }
275 } else {
276 $src = \rtrim($src, '=');
277 $srcLen = Binary::safeStrlen($src);
278 }
279
280 $err = 0;
281 $dest = '';
282 // Main loop (no padding):
283 for ($i = 0; $i + 8 <= $srcLen; $i += 8) {
284 /** @var array<int, int> $chunk */
285 $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 8));
286 /** @var int $c0 */
287 $c0 = static::$method($chunk[1]);
288 /** @var int $c1 */
289 $c1 = static::$method($chunk[2]);
290 /** @var int $c2 */
291 $c2 = static::$method($chunk[3]);
292 /** @var int $c3 */
293 $c3 = static::$method($chunk[4]);
294 /** @var int $c4 */
295 $c4 = static::$method($chunk[5]);
296 /** @var int $c5 */
297 $c5 = static::$method($chunk[6]);
298 /** @var int $c6 */
299 $c6 = static::$method($chunk[7]);
300 /** @var int $c7 */
301 $c7 = static::$method($chunk[8]);
302
303 $dest .= \pack(
304 'CCCCC',
305 (($c0 << 3) | ($c1 >> 2) ) & 0xff,
306 (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
307 (($c3 << 4) | ($c4 >> 1) ) & 0xff,
308 (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff,
309 (($c6 << 5) | ($c7 ) ) & 0xff
310 );
311 $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6 | $c7) >> 8;
312 }
313 // The last chunk, which may have padding:
314 if ($i < $srcLen) {
315 /** @var array<int, int> $chunk */
316 $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
317 /** @var int $c0 */
318 $c0 = static::$method($chunk[1]);
319
320 if ($i + 6 < $srcLen) {
321 /** @var int $c1 */
322 $c1 = static::$method($chunk[2]);
323 /** @var int $c2 */
324 $c2 = static::$method($chunk[3]);
325 /** @var int $c3 */
326 $c3 = static::$method($chunk[4]);
327 /** @var int $c4 */
328 $c4 = static::$method($chunk[5]);
329 /** @var int $c5 */
330 $c5 = static::$method($chunk[6]);
331 /** @var int $c6 */
332 $c6 = static::$method($chunk[7]);
333
334 $dest .= \pack(
335 'CCCC',
336 (($c0 << 3) | ($c1 >> 2) ) & 0xff,
337 (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
338 (($c3 << 4) | ($c4 >> 1) ) & 0xff,
339 (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff
340 );
341 $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6) >> 8;
342 if ($strictPadding) {
343 $err |= ($c6 << 5) & 0xff;
344 }
345 } elseif ($i + 5 < $srcLen) {
346 /** @var int $c1 */
347 $c1 = static::$method($chunk[2]);
348 /** @var int $c2 */
349 $c2 = static::$method($chunk[3]);
350 /** @var int $c3 */
351 $c3 = static::$method($chunk[4]);
352 /** @var int $c4 */
353 $c4 = static::$method($chunk[5]);
354 /** @var int $c5 */
355 $c5 = static::$method($chunk[6]);
356
357 $dest .= \pack(
358 'CCCC',
359 (($c0 << 3) | ($c1 >> 2) ) & 0xff,
360 (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
361 (($c3 << 4) | ($c4 >> 1) ) & 0xff,
362 (($c4 << 7) | ($c5 << 2) ) & 0xff
363 );
364 $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5) >> 8;
365 } elseif ($i + 4 < $srcLen) {
366 /** @var int $c1 */
367 $c1 = static::$method($chunk[2]);
368 /** @var int $c2 */
369 $c2 = static::$method($chunk[3]);
370 /** @var int $c3 */
371 $c3 = static::$method($chunk[4]);
372 /** @var int $c4 */
373 $c4 = static::$method($chunk[5]);
374
375 $dest .= \pack(
376 'CCC',
377 (($c0 << 3) | ($c1 >> 2) ) & 0xff,
378 (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
379 (($c3 << 4) | ($c4 >> 1) ) & 0xff
380 );
381 $err |= ($c0 | $c1 | $c2 | $c3 | $c4) >> 8;
382 if ($strictPadding) {
383 $err |= ($c4 << 7) & 0xff;
384 }
385 } elseif ($i + 3 < $srcLen) {
386 /** @var int $c1 */
387 $c1 = static::$method($chunk[2]);
388 /** @var int $c2 */
389 $c2 = static::$method($chunk[3]);
390 /** @var int $c3 */
391 $c3 = static::$method($chunk[4]);
392
393 $dest .= \pack(
394 'CC',
395 (($c0 << 3) | ($c1 >> 2) ) & 0xff,
396 (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff
397 );
398 $err |= ($c0 | $c1 | $c2 | $c3) >> 8;
399 if ($strictPadding) {
400 $err |= ($c3 << 4) & 0xff;
401 }
402 } elseif ($i + 2 < $srcLen) {
403 /** @var int $c1 */
404 $c1 = static::$method($chunk[2]);
405 /** @var int $c2 */
406 $c2 = static::$method($chunk[3]);
407
408 $dest .= \pack(
409 'CC',
410 (($c0 << 3) | ($c1 >> 2) ) & 0xff,
411 (($c1 << 6) | ($c2 << 1) ) & 0xff
412 );
413 $err |= ($c0 | $c1 | $c2) >> 8;
414 if ($strictPadding) {
415 $err |= ($c2 << 6) & 0xff;
416 }
417 } elseif ($i + 1 < $srcLen) {
418 /** @var int $c1 */
419 $c1 = static::$method($chunk[2]);
420
421 $dest .= \pack(
422 'C',
423 (($c0 << 3) | ($c1 >> 2) ) & 0xff
424 );
425 $err |= ($c0 | $c1) >> 8;
426 if ($strictPadding) {
427 $err |= ($c1 << 6) & 0xff;
428 }
429 } else {
430 $dest .= \pack(
431 'C',
432 (($c0 << 3) ) & 0xff
433 );
434 $err |= ($c0) >> 8;
435 }
436 }
437 $check = ($err === 0);
438 if (!$check) {
439 throw new RangeException(
440 'Base32::doDecode() only expects characters in the correct base32 alphabet'
441 );
442 }
443 return $dest;
444 }
445
446 /**
447 * Base32 Encoding
448 *
449 * @param string $src
450 * @param bool $upper
451 * @param bool $pad
452 * @return string
453 * @throws TypeError
454 */
455 protected static function doEncode(
456 #[\SensitiveParameter]
457 string $src,
458 bool $upper = false,
459 $pad = true
460 ): string {
461 // We do this to reduce code duplication:
462 $method = $upper
463 ? 'encode5BitsUpper'
464 : 'encode5Bits';
465
466 $dest = '';
467 $srcLen = Binary::safeStrlen($src);
468
469 // Main loop (no padding):
470 for ($i = 0; $i + 5 <= $srcLen; $i += 5) {
471 /** @var array<int, int> $chunk */
472 $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 5));
473 $b0 = $chunk[1];
474 $b1 = $chunk[2];
475 $b2 = $chunk[3];
476 $b3 = $chunk[4];
477 $b4 = $chunk[5];
478 $dest .=
479 static::$method( ($b0 >> 3) & 31) .
480 static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
481 static::$method((($b1 >> 1) ) & 31) .
482 static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
483 static::$method((($b2 << 1) | ($b3 >> 7)) & 31) .
484 static::$method((($b3 >> 2) ) & 31) .
485 static::$method((($b3 << 3) | ($b4 >> 5)) & 31) .
486 static::$method( $b4 & 31);
487 }
488 // The last chunk, which may have padding:
489 if ($i < $srcLen) {
490 /** @var array<int, int> $chunk */
491 $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
492 $b0 = $chunk[1];
493 if ($i + 3 < $srcLen) {
494 $b1 = $chunk[2];
495 $b2 = $chunk[3];
496 $b3 = $chunk[4];
497 $dest .=
498 static::$method( ($b0 >> 3) & 31) .
499 static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
500 static::$method((($b1 >> 1) ) & 31) .
501 static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
502 static::$method((($b2 << 1) | ($b3 >> 7)) & 31) .
503 static::$method((($b3 >> 2) ) & 31) .
504 static::$method((($b3 << 3) ) & 31);
505 if ($pad) {
506 $dest .= '=';
507 }
508 } elseif ($i + 2 < $srcLen) {
509 $b1 = $chunk[2];
510 $b2 = $chunk[3];
511 $dest .=
512 static::$method( ($b0 >> 3) & 31) .
513 static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
514 static::$method((($b1 >> 1) ) & 31) .
515 static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
516 static::$method((($b2 << 1) ) & 31);
517 if ($pad) {
518 $dest .= '===';
519 }
520 } elseif ($i + 1 < $srcLen) {
521 $b1 = $chunk[2];
522 $dest .=
523 static::$method( ($b0 >> 3) & 31) .
524 static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
525 static::$method((($b1 >> 1) ) & 31) .
526 static::$method((($b1 << 4) ) & 31);
527 if ($pad) {
528 $dest .= '====';
529 }
530 } else {
531 $dest .=
532 static::$method( ($b0 >> 3) & 31) .
533 static::$method( ($b0 << 2) & 31);
534 if ($pad) {
535 $dest .= '======';
536 }
537 }
538 }
539 return $dest;
540 }
541}
Note: See TracBrowser for help on using the repository browser.