Subversion Repositories oidplus

Rev

Rev 827 | Details | Compare with Previous | Last modification | View Log | RSS feed

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