xref: /dokuwiki/vendor/paragonie/constant_time_encoding/src/Hex.php (revision 927933f55f286c8bea68959a13975cbcb59eb8ee)
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