source: vendor/paragonie/constant_time_encoding/src/Base64.php

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

Upload project files

  • Property mode set to 100644
File size: 9.9 KB
RevLine 
[e3d4e0a]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 Base64
34 * [A-Z][a-z][0-9]+/
35 *
36 * @package ParagonIE\ConstantTime
37 */
38abstract class Base64 implements EncoderInterface
39{
40 /**
41 * Encode into Base64
42 *
43 * Base64 character set "[A-Z][a-z][0-9]+/"
44 *
45 * @param string $binString
46 * @return string
47 *
48 * @throws TypeError
49 */
50 public static function encode(
51 #[\SensitiveParameter]
52 string $binString
53 ): string {
54 return static::doEncode($binString, true);
55 }
56
57 /**
58 * Encode into Base64, no = padding
59 *
60 * Base64 character set "[A-Z][a-z][0-9]+/"
61 *
62 * @param string $src
63 * @return string
64 *
65 * @throws TypeError
66 */
67 public static function encodeUnpadded(
68 #[\SensitiveParameter]
69 string $src
70 ): string {
71 return static::doEncode($src, false);
72 }
73
74 /**
75 * @param string $src
76 * @param bool $pad Include = padding?
77 * @return string
78 *
79 * @throws TypeError
80 */
81 protected static function doEncode(
82 #[\SensitiveParameter]
83 string $src,
84 bool $pad = true
85 ): string {
86 $dest = '';
87 $srcLen = Binary::safeStrlen($src);
88 // Main loop (no padding):
89 for ($i = 0; $i + 3 <= $srcLen; $i += 3) {
90 /** @var array<int, int> $chunk */
91 $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 3));
92 $b0 = $chunk[1];
93 $b1 = $chunk[2];
94 $b2 = $chunk[3];
95
96 $dest .=
97 static::encode6Bits( $b0 >> 2 ) .
98 static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
99 static::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) .
100 static::encode6Bits( $b2 & 63);
101 }
102 // The last chunk, which may have padding:
103 if ($i < $srcLen) {
104 /** @var array<int, int> $chunk */
105 $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
106 $b0 = $chunk[1];
107 if ($i + 1 < $srcLen) {
108 $b1 = $chunk[2];
109 $dest .=
110 static::encode6Bits($b0 >> 2) .
111 static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
112 static::encode6Bits(($b1 << 2) & 63);
113 if ($pad) {
114 $dest .= '=';
115 }
116 } else {
117 $dest .=
118 static::encode6Bits( $b0 >> 2) .
119 static::encode6Bits(($b0 << 4) & 63);
120 if ($pad) {
121 $dest .= '==';
122 }
123 }
124 }
125 return $dest;
126 }
127
128 /**
129 * decode from base64 into binary
130 *
131 * Base64 character set "./[A-Z][a-z][0-9]"
132 *
133 * @param string $encodedString
134 * @param bool $strictPadding
135 * @return string
136 *
137 * @throws RangeException
138 * @throws TypeError
139 */
140 public static function decode(
141 #[\SensitiveParameter]
142 string $encodedString,
143 bool $strictPadding = false
144 ): string {
145 // Remove padding
146 $srcLen = Binary::safeStrlen($encodedString);
147 if ($srcLen === 0) {
148 return '';
149 }
150
151 if ($strictPadding) {
152 if (($srcLen & 3) === 0) {
153 if ($encodedString[$srcLen - 1] === '=') {
154 $srcLen--;
155 if ($encodedString[$srcLen - 1] === '=') {
156 $srcLen--;
157 }
158 }
159 }
160 if (($srcLen & 3) === 1) {
161 throw new RangeException(
162 'Incorrect padding'
163 );
164 }
165 if ($encodedString[$srcLen - 1] === '=') {
166 throw new RangeException(
167 'Incorrect padding'
168 );
169 }
170 } else {
171 $encodedString = \rtrim($encodedString, '=');
172 $srcLen = Binary::safeStrlen($encodedString);
173 }
174
175 $err = 0;
176 $dest = '';
177 // Main loop (no padding):
178 for ($i = 0; $i + 4 <= $srcLen; $i += 4) {
179 /** @var array<int, int> $chunk */
180 $chunk = \unpack('C*', Binary::safeSubstr($encodedString, $i, 4));
181 $c0 = static::decode6Bits($chunk[1]);
182 $c1 = static::decode6Bits($chunk[2]);
183 $c2 = static::decode6Bits($chunk[3]);
184 $c3 = static::decode6Bits($chunk[4]);
185
186 $dest .= \pack(
187 'CCC',
188 ((($c0 << 2) | ($c1 >> 4)) & 0xff),
189 ((($c1 << 4) | ($c2 >> 2)) & 0xff),
190 ((($c2 << 6) | $c3 ) & 0xff)
191 );
192 $err |= ($c0 | $c1 | $c2 | $c3) >> 8;
193 }
194 // The last chunk, which may have padding:
195 if ($i < $srcLen) {
196 /** @var array<int, int> $chunk */
197 $chunk = \unpack('C*', Binary::safeSubstr($encodedString, $i, $srcLen - $i));
198 $c0 = static::decode6Bits($chunk[1]);
199
200 if ($i + 2 < $srcLen) {
201 $c1 = static::decode6Bits($chunk[2]);
202 $c2 = static::decode6Bits($chunk[3]);
203 $dest .= \pack(
204 'CC',
205 ((($c0 << 2) | ($c1 >> 4)) & 0xff),
206 ((($c1 << 4) | ($c2 >> 2)) & 0xff)
207 );
208 $err |= ($c0 | $c1 | $c2) >> 8;
209 if ($strictPadding) {
210 $err |= ($c2 << 6) & 0xff;
211 }
212 } elseif ($i + 1 < $srcLen) {
213 $c1 = static::decode6Bits($chunk[2]);
214 $dest .= \pack(
215 'C',
216 ((($c0 << 2) | ($c1 >> 4)) & 0xff)
217 );
218 $err |= ($c0 | $c1) >> 8;
219 if ($strictPadding) {
220 $err |= ($c1 << 4) & 0xff;
221 }
222 } elseif ($strictPadding) {
223 $err |= 1;
224 }
225 }
226 $check = ($err === 0);
227 if (!$check) {
228 throw new RangeException(
229 'Base64::decode() only expects characters in the correct base64 alphabet'
230 );
231 }
232 return $dest;
233 }
234
235 /**
236 * @param string $encodedString
237 * @return string
238 */
239 public static function decodeNoPadding(
240 #[\SensitiveParameter]
241 string $encodedString
242 ): string {
243 $srcLen = Binary::safeStrlen($encodedString);
244 if ($srcLen === 0) {
245 return '';
246 }
247 if (($srcLen & 3) === 0) {
248 // If $strLen is not zero, and it is divisible by 4, then it's at least 4.
249 if ($encodedString[$srcLen - 1] === '=' || $encodedString[$srcLen - 2] === '=') {
250 throw new InvalidArgumentException(
251 "decodeNoPadding() doesn't tolerate padding"
252 );
253 }
254 }
255 return static::decode(
256 $encodedString,
257 true
258 );
259 }
260
261 /**
262 * Uses bitwise operators instead of table-lookups to turn 6-bit integers
263 * into 8-bit integers.
264 *
265 * Base64 character set:
266 * [A-Z] [a-z] [0-9] + /
267 * 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f
268 *
269 * @param int $src
270 * @return int
271 */
272 protected static function decode6Bits(int $src): int
273 {
274 $ret = -1;
275
276 // if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64
277 $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
278
279 // if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70
280 $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);
281
282 // if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5
283 $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);
284
285 // if ($src == 0x2b) $ret += 62 + 1;
286 $ret += (((0x2a - $src) & ($src - 0x2c)) >> 8) & 63;
287
288 // if ($src == 0x2f) ret += 63 + 1;
289 $ret += (((0x2e - $src) & ($src - 0x30)) >> 8) & 64;
290
291 return $ret;
292 }
293
294 /**
295 * Uses bitwise operators instead of table-lookups to turn 8-bit integers
296 * into 6-bit integers.
297 *
298 * @param int $src
299 * @return string
300 */
301 protected static function encode6Bits(int $src): string
302 {
303 $diff = 0x41;
304
305 // if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6
306 $diff += ((25 - $src) >> 8) & 6;
307
308 // if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75
309 $diff -= ((51 - $src) >> 8) & 75;
310
311 // if ($src > 61) $diff += 0x2b - 0x30 - 10; // -15
312 $diff -= ((61 - $src) >> 8) & 15;
313
314 // if ($src > 62) $diff += 0x2f - 0x2b - 1; // 3
315 $diff += ((62 - $src) >> 8) & 3;
316
317 return \pack('C', $src + $diff);
318 }
319}
Note: See TracBrowser for help on using the repository browser.