2 namespace ParagonIE\ConstantTime;
5 * Copyright (c) 2016 Paragon Initiative Enterprises.
6 * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
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:
15 * The above copyright notice and this permission notice shall be included in all
16 * copies or substantial portions of the Software.
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
31 * @package ParagonIE\ConstantTime
33 abstract class Base32 implements EncoderInterface
36 * Decode a Base32-encoded string into raw binary
41 public static function decode($src)
43 return static::doDecode($src, false);
47 * Decode an uppercase Base32-encoded string into raw binary
52 public static function decodeUpper($src)
54 return static::doDecode($src, true);
58 * Encode into Base32 (RFC 4648)
63 public static function encode($src)
65 return static::doEncode($src, false);
69 * Encode into uppercase Base32 (RFC 4648)
74 public static function encodeUpper($src)
76 return static::doEncode($src, true);
80 * Uses bitwise operators instead of table-lookups to turn 5-bit integers
81 * into 8-bit integers.
86 protected static function decode5Bits($src)
90 // if ($src > 96 && $src < 123) $ret += $src - 97 + 1; // -64
91 $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 96);
93 // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23
94 $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23);
100 * Uses bitwise operators instead of table-lookups to turn 5-bit integers
101 * into 8-bit integers.
108 protected static function decode5BitsUpper($src)
112 // if ($src > 64 && $src < 91) $ret += $src - 65 + 1; // -64
113 $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
115 // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23
116 $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23);
122 * Uses bitwise operators instead of table-lookups to turn 8-bit integers
123 * into 5-bit integers.
128 protected static function encode5Bits($src)
132 // if ($src > 25) $ret -= 72;
133 $diff -= ((25 - $src) >> 8) & 73;
135 return \pack('C', $src + $diff);
139 * Uses bitwise operators instead of table-lookups to turn 8-bit integers
140 * into 5-bit integers.
147 protected static function encode5BitsUpper($src)
151 // if ($src > 25) $ret -= 40;
152 $diff -= ((25 - $src) >> 8) & 41;
154 return \pack('C', $src + $diff);
165 protected static function doDecode($src, $upper = false)
167 // We do this to reduce code duplication:
173 $srcLen = Binary::safeStrlen($src);
177 if (($srcLen & 7) === 0) {
178 for ($j = 0; $j < 7; ++$j) {
179 if ($src[$srcLen - 1] === '=') {
186 if (($srcLen & 7) === 1) {
187 throw new \RangeException(
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]);
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
214 $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6 | $c7) >> 8;
216 // The last chunk, which may have padding:
218 $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
219 $c0 = static::$method($chunk[1]);
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]);
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
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]);
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
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]);
260 (($c0 << 3) | ($c1 >> 2) ) & 0xff,
261 (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
262 (($c3 << 4) | ($c4 >> 1) ) & 0xff
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]);
272 (($c0 << 3) | ($c1 >> 2) ) & 0xff,
273 (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff
275 $err |= ($c0 | $c1 | $c2 | $c3) >> 8;
276 } elseif ($i + 2 < $srcLen) {
277 $c1 = static::$method($chunk[2]);
278 $c2 = static::$method($chunk[3]);
282 (($c0 << 3) | ($c1 >> 2) ) & 0xff,
283 (($c1 << 6) | ($c2 << 1) ) & 0xff
285 $err |= ($c0 | $c1 | $c2) >> 8;
286 } elseif ($i + 1 < $srcLen) {
287 $c1 = static::$method($chunk[2]);
291 (($c0 << 3) | ($c1 >> 2) ) & 0xff
293 $err |= ($c0 | $c1) >> 8;
303 throw new \RangeException(
304 'Base32::doDecode() only expects characters in the correct base32 alphabet'
317 protected static function doEncode($src, $upper = false)
319 // We do this to reduce code duplication:
325 $srcLen = Binary::safeStrlen($src);
327 // Main loop (no padding):
328 for ($i = 0; $i + 5 <= $srcLen; $i += 5) {
329 $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 5));
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);
345 // The last chunk, which may have padding:
347 $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
349 if ($i + 3 < $srcLen) {
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) .
362 } elseif ($i + 2 < $srcLen) {
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) .
372 } elseif ($i + 1 < $srcLen) {
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) .
382 static::$method( ($b0 >> 3) & 31) .
383 static::$method( ($b0 << 2) & 31) .