]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/ParagonIE/ConstantTime/Base32.php
Merge branch 'nightly' into 'master'
[quix0rs-gnu-social.git] / extlib / ParagonIE / ConstantTime / Base32.php
1 <?php
2 namespace ParagonIE\ConstantTime;
3
4 /**
5  *  Copyright (c) 2016 Paragon Initiative Enterprises.
6  *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
7  *
8  *  Permission is hereby granted, free of charge, to any person obtaining a copy
9  *  of this software and associated documentation files (the "Software"), to deal
10  *  in the Software without restriction, including without limitation the rights
11  *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  *  copies of the Software, and to permit persons to whom the Software is
13  *  furnished to do so, subject to the following conditions:
14  *
15  *  The above copyright notice and this permission notice shall be included in all
16  *  copies or substantial portions of the Software.
17  *
18  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  *  SOFTWARE.
25  */
26
27 /**
28  * Class Base32
29  * [A-Z][2-7]
30  *
31  * @package ParagonIE\ConstantTime
32  */
33 abstract class Base32 implements EncoderInterface
34 {
35     /**
36      * Decode a Base32-encoded string into raw binary
37      *
38      * @param string $src
39      * @return string
40      */
41     public static function decode($src)
42     {
43         return static::doDecode($src, false);
44     }
45
46     /**
47      * Decode an uppercase Base32-encoded string into raw binary
48      *
49      * @param string $src
50      * @return string
51      */
52     public static function decodeUpper($src)
53     {
54         return static::doDecode($src, true);
55     }
56
57     /**
58      * Encode into Base32 (RFC 4648)
59      *
60      * @param string $src
61      * @return string
62      */
63     public static function encode($src)
64     {
65         return static::doEncode($src, false);
66     }
67
68     /**
69      * Encode into uppercase Base32 (RFC 4648)
70      *
71      * @param string $src
72      * @return string
73      */
74     public static function encodeUpper($src)
75     {
76         return static::doEncode($src, true);
77     }
78
79     /**
80      * Uses bitwise operators instead of table-lookups to turn 5-bit integers
81      * into 8-bit integers.
82      *
83      * @param int $src
84      * @return int
85      */
86     protected static function decode5Bits($src)
87     {
88         $ret = -1;
89
90         // if ($src > 96 && $src < 123) $ret += $src - 97 + 1; // -64
91         $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 96);
92
93         // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23
94         $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23);
95
96         return $ret;
97     }
98
99     /**
100      * Uses bitwise operators instead of table-lookups to turn 5-bit integers
101      * into 8-bit integers.
102      *
103      * Uppercase variant.
104      *
105      * @param int $src
106      * @return int
107      */
108     protected static function decode5BitsUpper($src)
109     {
110         $ret = -1;
111
112         // if ($src > 64 && $src < 91) $ret += $src - 65 + 1; // -64
113         $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
114
115         // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23
116         $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23);
117
118         return $ret;
119     }
120
121     /**
122      * Uses bitwise operators instead of table-lookups to turn 8-bit integers
123      * into 5-bit integers.
124      *
125      * @param $src
126      * @return string
127      */
128     protected static function encode5Bits($src)
129     {
130         $diff = 0x61;
131
132         // if ($src > 25) $ret -= 72;
133         $diff -= ((25 - $src) >> 8) & 73;
134
135         return \pack('C', $src + $diff);
136     }
137
138     /**
139      * Uses bitwise operators instead of table-lookups to turn 8-bit integers
140      * into 5-bit integers.
141      *
142      * Uppercase variant.
143      *
144      * @param $src
145      * @return string
146      */
147     protected static function encode5BitsUpper($src)
148     {
149         $diff = 0x41;
150
151         // if ($src > 25) $ret -= 40;
152         $diff -= ((25 - $src) >> 8) & 41;
153
154         return \pack('C', $src + $diff);
155     }
156
157
158     /**
159      * Base32 decoding
160      *
161      * @param $src
162      * @param bool $upper
163      * @return string
164      */
165     protected static function doDecode($src, $upper = false)
166     {
167         // We do this to reduce code duplication:
168         $method = $upper
169             ? 'decode5BitsUpper'
170             : 'decode5Bits';
171
172         // Remove padding
173         $srcLen = Binary::safeStrlen($src);
174         if ($srcLen === 0) {
175             return '';
176         }
177         if (($srcLen & 7) === 0) {
178             for ($j = 0; $j < 7; ++$j) {
179                 if ($src[$srcLen - 1] === '=') {
180                     $srcLen--;
181                 } else {
182                     break;
183                 }
184             }
185         }
186         if (($srcLen & 7) === 1) {
187             throw new \RangeException(
188                 'Incorrect padding'
189             );
190         }
191
192         $err = 0;
193         $dest = '';
194         // Main loop (no padding):
195         for ($i = 0; $i + 8 <= $srcLen; $i += 8) {
196             $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 8));
197             $c0 = static::$method($chunk[1]);
198             $c1 = static::$method($chunk[2]);
199             $c2 = static::$method($chunk[3]);
200             $c3 = static::$method($chunk[4]);
201             $c4 = static::$method($chunk[5]);
202             $c5 = static::$method($chunk[6]);
203             $c6 = static::$method($chunk[7]);
204             $c7 = static::$method($chunk[8]);
205
206             $dest .= \pack(
207                 'CCCCC',
208                 (($c0 << 3) | ($c1 >> 2)             ) & 0xff,
209                 (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
210                 (($c3 << 4) | ($c4 >> 1)             ) & 0xff,
211                 (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff,
212                 (($c6 << 5) | ($c7     )             ) & 0xff
213             );
214             $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6 | $c7) >> 8;
215         }
216         // The last chunk, which may have padding:
217         if ($i < $srcLen) {
218             $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
219             $c0 = static::$method($chunk[1]);
220
221             if ($i + 6 < $srcLen) {
222                 $c1 = static::$method($chunk[2]);
223                 $c2 = static::$method($chunk[3]);
224                 $c3 = static::$method($chunk[4]);
225                 $c4 = static::$method($chunk[5]);
226                 $c5 = static::$method($chunk[6]);
227                 $c6 = static::$method($chunk[7]);
228
229                 $dest .= \pack(
230                     'CCCC',
231                     (($c0 << 3) | ($c1 >> 2)             ) & 0xff,
232                     (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
233                     (($c3 << 4) | ($c4 >> 1)             ) & 0xff,
234                     (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff
235                 );
236                 $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6) >> 8;
237             } elseif ($i + 5 < $srcLen) {
238                 $c1 = static::$method($chunk[2]);
239                 $c2 = static::$method($chunk[3]);
240                 $c3 = static::$method($chunk[4]);
241                 $c4 = static::$method($chunk[5]);
242                 $c5 = static::$method($chunk[6]);
243
244                 $dest .= \pack(
245                     'CCCC',
246                     (($c0 << 3) | ($c1 >> 2)             ) & 0xff,
247                     (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
248                     (($c3 << 4) | ($c4 >> 1)             ) & 0xff,
249                     (($c4 << 7) | ($c5 << 2)             ) & 0xff
250                 );
251                 $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5) >> 8;
252             } elseif ($i + 4 < $srcLen) {
253                 $c1 = static::$method($chunk[2]);
254                 $c2 = static::$method($chunk[3]);
255                 $c3 = static::$method($chunk[4]);
256                 $c4 = static::$method($chunk[5]);
257
258                 $dest .= \pack(
259                     'CCC',
260                     (($c0 << 3) | ($c1 >> 2)             ) & 0xff,
261                     (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
262                     (($c3 << 4) | ($c4 >> 1)             ) & 0xff
263                 );
264                 $err |= ($c0 | $c1 | $c2 | $c3 | $c4) >> 8;
265             } elseif ($i + 3 < $srcLen) {
266                 $c1 = static::$method($chunk[2]);
267                 $c2 = static::$method($chunk[3]);
268                 $c3 = static::$method($chunk[4]);
269
270                 $dest .= \pack(
271                     'CC',
272                     (($c0 << 3) | ($c1 >> 2)             ) & 0xff,
273                     (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff
274                 );
275                 $err |= ($c0 | $c1 | $c2 | $c3) >> 8;
276             } elseif ($i + 2 < $srcLen) {
277                 $c1 = static::$method($chunk[2]);
278                 $c2 = static::$method($chunk[3]);
279
280                 $dest .= \pack(
281                     'CC',
282                     (($c0 << 3) | ($c1 >> 2)             ) & 0xff,
283                     (($c1 << 6) | ($c2 << 1)             ) & 0xff
284                 );
285                 $err |= ($c0 | $c1 | $c2) >> 8;
286             } elseif ($i + 1 < $srcLen) {
287                 $c1 = static::$method($chunk[2]);
288
289                 $dest .= \pack(
290                     'C',
291                     (($c0 << 3) | ($c1 >> 2)             ) & 0xff
292                 );
293                 $err |= ($c0 | $c1) >> 8;
294             } else {
295                 $dest .= \pack(
296                     'C',
297                     (($c0 << 3)                          ) & 0xff
298                 );
299                 $err |= ($c0) >> 8;
300             }
301         }
302         if ($err !== 0) {
303             throw new \RangeException(
304                 'Base32::doDecode() only expects characters in the correct base32 alphabet'
305             );
306         }
307         return $dest;
308     }
309
310     /**
311      * Base32 Decoding
312      *
313      * @param string $src
314      * @param bool $upper
315      * @return string
316      */
317     protected static function doEncode($src, $upper = false)
318     {
319         // We do this to reduce code duplication:
320         $method = $upper
321             ? 'encode5BitsUpper'
322             : 'encode5Bits';
323         
324         $dest = '';
325         $srcLen = Binary::safeStrlen($src);
326
327         // Main loop (no padding):
328         for ($i = 0; $i + 5 <= $srcLen; $i += 5) {
329             $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 5));
330             $b0 = $chunk[1];
331             $b1 = $chunk[2];
332             $b2 = $chunk[3];
333             $b3 = $chunk[4];
334             $b4 = $chunk[5];
335             $dest .=
336                 static::$method(              ($b0 >> 3)  & 31) .
337                 static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
338                 static::$method((($b1 >> 1)             ) & 31) .
339                 static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
340                 static::$method((($b2 << 1) | ($b3 >> 7)) & 31) .
341                 static::$method((($b3 >> 2)             ) & 31) .
342                 static::$method((($b3 << 3) | ($b4 >> 5)) & 31) .
343                 static::$method(  $b4                     & 31);
344         }
345         // The last chunk, which may have padding:
346         if ($i < $srcLen) {
347             $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
348             $b0 = $chunk[1];
349             if ($i + 3 < $srcLen) {
350                 $b1 = $chunk[2];
351                 $b2 = $chunk[3];
352                 $b3 = $chunk[4];
353                 $dest .=
354                     static::$method(              ($b0 >> 3)  & 31) .
355                     static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
356                     static::$method((($b1 >> 1)             ) & 31) .
357                     static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
358                     static::$method((($b2 << 1) | ($b3 >> 7)) & 31) .
359                     static::$method((($b3 >> 2)             ) & 31) .
360                     static::$method((($b3 << 3)             ) & 31) .
361                     '=';
362             } elseif ($i + 2 < $srcLen) {
363                 $b1 = $chunk[2];
364                 $b2 = $chunk[3];
365                 $dest .=
366                     static::$method(              ($b0 >> 3)  & 31) .
367                     static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
368                     static::$method((($b1 >> 1)             ) & 31) .
369                     static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
370                     static::$method((($b2 << 1)             ) & 31) .
371                     '===';
372             } elseif ($i + 1 < $srcLen) {
373                 $b1 = $chunk[2];
374                 $dest .=
375                     static::$method(              ($b0 >> 3)  & 31) .
376                     static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
377                     static::$method((($b1 >> 1)             ) & 31) .
378                     static::$method((($b1 << 4)             ) & 31) .
379                     '====';
380             } else {
381                 $dest .=
382                     static::$method(              ($b0 >> 3)  & 31) .
383                     static::$method( ($b0 << 2)               & 31) .
384                     '======';
385             }
386         }
387         return $dest;
388     }
389 }