Subversion Repositories oidplus

Rev

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

  1. <?php
  2. declare(strict_types=1);
  3. namespace ParagonIE\ConstantTime;
  4.  
  5. use RangeException;
  6. use TypeError;
  7.  
  8. /**
  9.  *  Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
  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
  43.      * @throws TypeError
  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 */
  51.             $chunk = \unpack('C', $binString[$i]);
  52.             $c = $chunk[1] & 0xf;
  53.             $b = $chunk[1] >> 4;
  54.  
  55.             $hex .= \pack(
  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
  70.      * @throws TypeError
  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 */
  79.             $chunk = \unpack('C', $binString[$i]);
  80.             $c = $chunk[1] & 0xf;
  81.             $b = $chunk[1] >> 4;
  82.  
  83.             $hex .= \pack(
  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)
  99.      * @throws RangeException
  100.      */
  101.     public static function decode(
  102.         string $encodedString,
  103.         bool $strictPadding = false
  104.     ): string {
  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) {
  112.                 throw new RangeException(
  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) {
  132.                 throw new RangeException(
  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. }
  147.