1*927933f5SAndreas Gohr<?php 2*927933f5SAndreas Gohrdeclare(strict_types=1); 3*927933f5SAndreas Gohrnamespace ParagonIE\ConstantTime; 4*927933f5SAndreas Gohr 5*927933f5SAndreas Gohruse RangeException; 6*927933f5SAndreas Gohruse TypeError; 7*927933f5SAndreas Gohr 8*927933f5SAndreas Gohr/** 9*927933f5SAndreas Gohr * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. 10*927933f5SAndreas Gohr * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) 11*927933f5SAndreas Gohr * 12*927933f5SAndreas Gohr * Permission is hereby granted, free of charge, to any person obtaining a copy 13*927933f5SAndreas Gohr * of this software and associated documentation files (the "Software"), to deal 14*927933f5SAndreas Gohr * in the Software without restriction, including without limitation the rights 15*927933f5SAndreas Gohr * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16*927933f5SAndreas Gohr * copies of the Software, and to permit persons to whom the Software is 17*927933f5SAndreas Gohr * furnished to do so, subject to the following conditions: 18*927933f5SAndreas Gohr * 19*927933f5SAndreas Gohr * The above copyright notice and this permission notice shall be included in all 20*927933f5SAndreas Gohr * copies or substantial portions of the Software. 21*927933f5SAndreas Gohr * 22*927933f5SAndreas Gohr * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23*927933f5SAndreas Gohr * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24*927933f5SAndreas Gohr * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25*927933f5SAndreas Gohr * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26*927933f5SAndreas Gohr * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27*927933f5SAndreas Gohr * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28*927933f5SAndreas Gohr * SOFTWARE. 29*927933f5SAndreas Gohr */ 30*927933f5SAndreas Gohr 31*927933f5SAndreas Gohr/** 32*927933f5SAndreas Gohr * Class Hex 33*927933f5SAndreas Gohr * @package ParagonIE\ConstantTime 34*927933f5SAndreas Gohr */ 35*927933f5SAndreas Gohrabstract class Hex implements EncoderInterface 36*927933f5SAndreas Gohr{ 37*927933f5SAndreas Gohr /** 38*927933f5SAndreas Gohr * Convert a binary string into a hexadecimal string without cache-timing 39*927933f5SAndreas Gohr * leaks 40*927933f5SAndreas Gohr * 41*927933f5SAndreas Gohr * @param string $binString (raw binary) 42*927933f5SAndreas Gohr * @return string 43*927933f5SAndreas Gohr * @throws TypeError 44*927933f5SAndreas Gohr */ 45*927933f5SAndreas Gohr public static function encode(string $binString): string 46*927933f5SAndreas Gohr { 47*927933f5SAndreas Gohr $hex = ''; 48*927933f5SAndreas Gohr $len = Binary::safeStrlen($binString); 49*927933f5SAndreas Gohr for ($i = 0; $i < $len; ++$i) { 50*927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 51*927933f5SAndreas Gohr $chunk = \unpack('C', $binString[$i]); 52*927933f5SAndreas Gohr $c = $chunk[1] & 0xf; 53*927933f5SAndreas Gohr $b = $chunk[1] >> 4; 54*927933f5SAndreas Gohr 55*927933f5SAndreas Gohr $hex .= \pack( 56*927933f5SAndreas Gohr 'CC', 57*927933f5SAndreas Gohr (87 + $b + ((($b - 10) >> 8) & ~38)), 58*927933f5SAndreas Gohr (87 + $c + ((($c - 10) >> 8) & ~38)) 59*927933f5SAndreas Gohr ); 60*927933f5SAndreas Gohr } 61*927933f5SAndreas Gohr return $hex; 62*927933f5SAndreas Gohr } 63*927933f5SAndreas Gohr 64*927933f5SAndreas Gohr /** 65*927933f5SAndreas Gohr * Convert a binary string into a hexadecimal string without cache-timing 66*927933f5SAndreas Gohr * leaks, returning uppercase letters (as per RFC 4648) 67*927933f5SAndreas Gohr * 68*927933f5SAndreas Gohr * @param string $binString (raw binary) 69*927933f5SAndreas Gohr * @return string 70*927933f5SAndreas Gohr * @throws TypeError 71*927933f5SAndreas Gohr */ 72*927933f5SAndreas Gohr public static function encodeUpper(string $binString): string 73*927933f5SAndreas Gohr { 74*927933f5SAndreas Gohr $hex = ''; 75*927933f5SAndreas Gohr $len = Binary::safeStrlen($binString); 76*927933f5SAndreas Gohr 77*927933f5SAndreas Gohr for ($i = 0; $i < $len; ++$i) { 78*927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 79*927933f5SAndreas Gohr $chunk = \unpack('C', $binString[$i]); 80*927933f5SAndreas Gohr $c = $chunk[1] & 0xf; 81*927933f5SAndreas Gohr $b = $chunk[1] >> 4; 82*927933f5SAndreas Gohr 83*927933f5SAndreas Gohr $hex .= \pack( 84*927933f5SAndreas Gohr 'CC', 85*927933f5SAndreas Gohr (55 + $b + ((($b - 10) >> 8) & ~6)), 86*927933f5SAndreas Gohr (55 + $c + ((($c - 10) >> 8) & ~6)) 87*927933f5SAndreas Gohr ); 88*927933f5SAndreas Gohr } 89*927933f5SAndreas Gohr return $hex; 90*927933f5SAndreas Gohr } 91*927933f5SAndreas Gohr 92*927933f5SAndreas Gohr /** 93*927933f5SAndreas Gohr * Convert a hexadecimal string into a binary string without cache-timing 94*927933f5SAndreas Gohr * leaks 95*927933f5SAndreas Gohr * 96*927933f5SAndreas Gohr * @param string $encodedString 97*927933f5SAndreas Gohr * @param bool $strictPadding 98*927933f5SAndreas Gohr * @return string (raw binary) 99*927933f5SAndreas Gohr * @throws RangeException 100*927933f5SAndreas Gohr */ 101*927933f5SAndreas Gohr public static function decode( 102*927933f5SAndreas Gohr string $encodedString, 103*927933f5SAndreas Gohr bool $strictPadding = false 104*927933f5SAndreas Gohr ): string { 105*927933f5SAndreas Gohr $hex_pos = 0; 106*927933f5SAndreas Gohr $bin = ''; 107*927933f5SAndreas Gohr $c_acc = 0; 108*927933f5SAndreas Gohr $hex_len = Binary::safeStrlen($encodedString); 109*927933f5SAndreas Gohr $state = 0; 110*927933f5SAndreas Gohr if (($hex_len & 1) !== 0) { 111*927933f5SAndreas Gohr if ($strictPadding) { 112*927933f5SAndreas Gohr throw new RangeException( 113*927933f5SAndreas Gohr 'Expected an even number of hexadecimal characters' 114*927933f5SAndreas Gohr ); 115*927933f5SAndreas Gohr } else { 116*927933f5SAndreas Gohr $encodedString = '0' . $encodedString; 117*927933f5SAndreas Gohr ++$hex_len; 118*927933f5SAndreas Gohr } 119*927933f5SAndreas Gohr } 120*927933f5SAndreas Gohr 121*927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 122*927933f5SAndreas Gohr $chunk = \unpack('C*', $encodedString); 123*927933f5SAndreas Gohr while ($hex_pos < $hex_len) { 124*927933f5SAndreas Gohr ++$hex_pos; 125*927933f5SAndreas Gohr $c = $chunk[$hex_pos]; 126*927933f5SAndreas Gohr $c_num = $c ^ 48; 127*927933f5SAndreas Gohr $c_num0 = ($c_num - 10) >> 8; 128*927933f5SAndreas Gohr $c_alpha = ($c & ~32) - 55; 129*927933f5SAndreas Gohr $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8; 130*927933f5SAndreas Gohr 131*927933f5SAndreas Gohr if (($c_num0 | $c_alpha0) === 0) { 132*927933f5SAndreas Gohr throw new RangeException( 133*927933f5SAndreas Gohr 'Expected hexadecimal character' 134*927933f5SAndreas Gohr ); 135*927933f5SAndreas Gohr } 136*927933f5SAndreas Gohr $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0); 137*927933f5SAndreas Gohr if ($state === 0) { 138*927933f5SAndreas Gohr $c_acc = $c_val * 16; 139*927933f5SAndreas Gohr } else { 140*927933f5SAndreas Gohr $bin .= \pack('C', $c_acc | $c_val); 141*927933f5SAndreas Gohr } 142*927933f5SAndreas Gohr $state ^= 1; 143*927933f5SAndreas Gohr } 144*927933f5SAndreas Gohr return $bin; 145*927933f5SAndreas Gohr } 146*927933f5SAndreas Gohr} 147