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