Subversion Repositories oidplus

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

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