xref: /dokuwiki/vendor/paragonie/constant_time_encoding/src/Hex.php (revision 850e662095111529d7d330745fee3207907c4aee)
1927933f5SAndreas Gohr<?php
2927933f5SAndreas Gohrdeclare(strict_types=1);
3927933f5SAndreas Gohrnamespace ParagonIE\ConstantTime;
4927933f5SAndreas Gohr
5927933f5SAndreas Gohruse RangeException;
6927933f5SAndreas Gohruse TypeError;
7927933f5SAndreas Gohr
8927933f5SAndreas Gohr/**
9927933f5SAndreas Gohr *  Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
10927933f5SAndreas Gohr *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
11927933f5SAndreas Gohr *
12927933f5SAndreas Gohr *  Permission is hereby granted, free of charge, to any person obtaining a copy
13927933f5SAndreas Gohr *  of this software and associated documentation files (the "Software"), to deal
14927933f5SAndreas Gohr *  in the Software without restriction, including without limitation the rights
15927933f5SAndreas Gohr *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16927933f5SAndreas Gohr *  copies of the Software, and to permit persons to whom the Software is
17927933f5SAndreas Gohr *  furnished to do so, subject to the following conditions:
18927933f5SAndreas Gohr *
19927933f5SAndreas Gohr *  The above copyright notice and this permission notice shall be included in all
20927933f5SAndreas Gohr *  copies or substantial portions of the Software.
21927933f5SAndreas Gohr *
22927933f5SAndreas Gohr *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23927933f5SAndreas Gohr *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24927933f5SAndreas Gohr *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25927933f5SAndreas Gohr *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26927933f5SAndreas Gohr *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27927933f5SAndreas Gohr *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28927933f5SAndreas Gohr *  SOFTWARE.
29927933f5SAndreas Gohr */
30927933f5SAndreas Gohr
31927933f5SAndreas Gohr/**
32927933f5SAndreas Gohr * Class Hex
33927933f5SAndreas Gohr * @package ParagonIE\ConstantTime
34927933f5SAndreas Gohr */
35927933f5SAndreas Gohrabstract class Hex implements EncoderInterface
36927933f5SAndreas Gohr{
37927933f5SAndreas Gohr    /**
38927933f5SAndreas Gohr     * Convert a binary string into a hexadecimal string without cache-timing
39927933f5SAndreas Gohr     * leaks
40927933f5SAndreas Gohr     *
41927933f5SAndreas Gohr     * @param string $binString (raw binary)
42927933f5SAndreas Gohr     * @return string
43927933f5SAndreas Gohr     * @throws TypeError
44927933f5SAndreas Gohr     */
45*850e6620SAndreas Gohr    public static function encode(
46*850e6620SAndreas Gohr        #[\SensitiveParameter]
47*850e6620SAndreas Gohr        string $binString
48*850e6620SAndreas Gohr    ): string {
49927933f5SAndreas Gohr        $hex = '';
50927933f5SAndreas Gohr        $len = Binary::safeStrlen($binString);
51927933f5SAndreas Gohr        for ($i = 0; $i < $len; ++$i) {
52927933f5SAndreas Gohr            /** @var array<int, int> $chunk */
53927933f5SAndreas Gohr            $chunk = \unpack('C', $binString[$i]);
54927933f5SAndreas Gohr            $c = $chunk[1] & 0xf;
55927933f5SAndreas Gohr            $b = $chunk[1] >> 4;
56927933f5SAndreas Gohr
57927933f5SAndreas Gohr            $hex .= \pack(
58927933f5SAndreas Gohr                'CC',
59927933f5SAndreas Gohr                (87 + $b + ((($b - 10) >> 8) & ~38)),
60927933f5SAndreas Gohr                (87 + $c + ((($c - 10) >> 8) & ~38))
61927933f5SAndreas Gohr            );
62927933f5SAndreas Gohr        }
63927933f5SAndreas Gohr        return $hex;
64927933f5SAndreas Gohr    }
65927933f5SAndreas Gohr
66927933f5SAndreas Gohr    /**
67927933f5SAndreas Gohr     * Convert a binary string into a hexadecimal string without cache-timing
68927933f5SAndreas Gohr     * leaks, returning uppercase letters (as per RFC 4648)
69927933f5SAndreas Gohr     *
70927933f5SAndreas Gohr     * @param string $binString (raw binary)
71927933f5SAndreas Gohr     * @return string
72927933f5SAndreas Gohr     * @throws TypeError
73927933f5SAndreas Gohr     */
74*850e6620SAndreas Gohr    public static function encodeUpper(
75*850e6620SAndreas Gohr        #[\SensitiveParameter]
76*850e6620SAndreas Gohr        string $binString
77*850e6620SAndreas Gohr    ): string {
78927933f5SAndreas Gohr        $hex = '';
79927933f5SAndreas Gohr        $len = Binary::safeStrlen($binString);
80927933f5SAndreas Gohr
81927933f5SAndreas Gohr        for ($i = 0; $i < $len; ++$i) {
82927933f5SAndreas Gohr            /** @var array<int, int> $chunk */
83927933f5SAndreas Gohr            $chunk = \unpack('C', $binString[$i]);
84927933f5SAndreas Gohr            $c = $chunk[1] & 0xf;
85927933f5SAndreas Gohr            $b = $chunk[1] >> 4;
86927933f5SAndreas Gohr
87927933f5SAndreas Gohr            $hex .= \pack(
88927933f5SAndreas Gohr                'CC',
89927933f5SAndreas Gohr                (55 + $b + ((($b - 10) >> 8) & ~6)),
90927933f5SAndreas Gohr                (55 + $c + ((($c - 10) >> 8) & ~6))
91927933f5SAndreas Gohr            );
92927933f5SAndreas Gohr        }
93927933f5SAndreas Gohr        return $hex;
94927933f5SAndreas Gohr    }
95927933f5SAndreas Gohr
96927933f5SAndreas Gohr    /**
97927933f5SAndreas Gohr     * Convert a hexadecimal string into a binary string without cache-timing
98927933f5SAndreas Gohr     * leaks
99927933f5SAndreas Gohr     *
100927933f5SAndreas Gohr     * @param string $encodedString
101927933f5SAndreas Gohr     * @param bool $strictPadding
102927933f5SAndreas Gohr     * @return string (raw binary)
103927933f5SAndreas Gohr     * @throws RangeException
104927933f5SAndreas Gohr     */
105927933f5SAndreas Gohr    public static function decode(
106*850e6620SAndreas Gohr        #[\SensitiveParameter]
107927933f5SAndreas Gohr        string $encodedString,
108927933f5SAndreas Gohr        bool $strictPadding = false
109927933f5SAndreas Gohr    ): string {
110927933f5SAndreas Gohr        $hex_pos = 0;
111927933f5SAndreas Gohr        $bin = '';
112927933f5SAndreas Gohr        $c_acc = 0;
113927933f5SAndreas Gohr        $hex_len = Binary::safeStrlen($encodedString);
114927933f5SAndreas Gohr        $state = 0;
115927933f5SAndreas Gohr        if (($hex_len & 1) !== 0) {
116927933f5SAndreas Gohr            if ($strictPadding) {
117927933f5SAndreas Gohr                throw new RangeException(
118927933f5SAndreas Gohr                    'Expected an even number of hexadecimal characters'
119927933f5SAndreas Gohr                );
120927933f5SAndreas Gohr            } else {
121927933f5SAndreas Gohr                $encodedString = '0' . $encodedString;
122927933f5SAndreas Gohr                ++$hex_len;
123927933f5SAndreas Gohr            }
124927933f5SAndreas Gohr        }
125927933f5SAndreas Gohr
126927933f5SAndreas Gohr        /** @var array<int, int> $chunk */
127927933f5SAndreas Gohr        $chunk = \unpack('C*', $encodedString);
128927933f5SAndreas Gohr        while ($hex_pos < $hex_len) {
129927933f5SAndreas Gohr            ++$hex_pos;
130927933f5SAndreas Gohr            $c = $chunk[$hex_pos];
131927933f5SAndreas Gohr            $c_num = $c ^ 48;
132927933f5SAndreas Gohr            $c_num0 = ($c_num - 10) >> 8;
133927933f5SAndreas Gohr            $c_alpha = ($c & ~32) - 55;
134927933f5SAndreas Gohr            $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8;
135927933f5SAndreas Gohr
136927933f5SAndreas Gohr            if (($c_num0 | $c_alpha0) === 0) {
137927933f5SAndreas Gohr                throw new RangeException(
138927933f5SAndreas Gohr                    'Expected hexadecimal character'
139927933f5SAndreas Gohr                );
140927933f5SAndreas Gohr            }
141927933f5SAndreas Gohr            $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0);
142927933f5SAndreas Gohr            if ($state === 0) {
143927933f5SAndreas Gohr                $c_acc = $c_val * 16;
144927933f5SAndreas Gohr            } else {
145927933f5SAndreas Gohr                $bin .= \pack('C', $c_acc | $c_val);
146927933f5SAndreas Gohr            }
147927933f5SAndreas Gohr            $state ^= 1;
148927933f5SAndreas Gohr        }
149927933f5SAndreas Gohr        return $bin;
150927933f5SAndreas Gohr    }
151927933f5SAndreas Gohr}
152