Subversion Repositories oidplus

Rev

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

  1. <?php
  2.  
  3. # https://github.com/0xbb/php-sha3/blob/master/src/Sha3.php
  4.  
  5. namespace bb\Sha3;
  6.  
  7. final class Sha3
  8. {
  9.     const KECCAK_ROUNDS = 24;
  10.     private static $keccakf_rotc = [1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44];
  11.     private static $keccakf_piln = [10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12,2, 20, 14, 22, 9, 6, 1];
  12.  
  13.     private static function keccakf64(&$st, $rounds)
  14.     {
  15.         $keccakf_rndc = [
  16.             [0x00000000, 0x00000001], [0x00000000, 0x00008082], [0x80000000, 0x0000808a], [0x80000000, 0x80008000],
  17.             [0x00000000, 0x0000808b], [0x00000000, 0x80000001], [0x80000000, 0x80008081], [0x80000000, 0x00008009],
  18.             [0x00000000, 0x0000008a], [0x00000000, 0x00000088], [0x00000000, 0x80008009], [0x00000000, 0x8000000a],
  19.             [0x00000000, 0x8000808b], [0x80000000, 0x0000008b], [0x80000000, 0x00008089], [0x80000000, 0x00008003],
  20.             [0x80000000, 0x00008002], [0x80000000, 0x00000080], [0x00000000, 0x0000800a], [0x80000000, 0x8000000a],
  21.             [0x80000000, 0x80008081], [0x80000000, 0x00008080], [0x00000000, 0x80000001], [0x80000000, 0x80008008]
  22.         ];
  23.  
  24.         $bc = [];
  25.         for ($round = 0; $round < $rounds; $round++) {
  26.  
  27.             // Theta
  28.             for ($i = 0; $i < 5; $i++) {
  29.                 $bc[$i] = [
  30.                     $st[$i][0] ^ $st[$i + 5][0] ^ $st[$i + 10][0] ^ $st[$i + 15][0] ^ $st[$i + 20][0],
  31.                     $st[$i][1] ^ $st[$i + 5][1] ^ $st[$i + 10][1] ^ $st[$i + 15][1] ^ $st[$i + 20][1]
  32.                 ];
  33.             }
  34.  
  35.             for ($i = 0; $i < 5; $i++) {
  36.                 $t = [
  37.                     $bc[($i + 4) % 5][0] ^ (($bc[($i + 1) % 5][0] << 1) | ($bc[($i + 1) % 5][1] >> 31)) & (0xFFFFFFFF),
  38.                     $bc[($i + 4) % 5][1] ^ (($bc[($i + 1) % 5][1] << 1) | ($bc[($i + 1) % 5][0] >> 31)) & (0xFFFFFFFF)
  39.                 ];
  40.  
  41.                 for ($j = 0; $j < 25; $j += 5) {
  42.                     $st[$j + $i] = [
  43.                         $st[$j + $i][0] ^ $t[0],
  44.                         $st[$j + $i][1] ^ $t[1]
  45.                     ];
  46.                 }
  47.             }
  48.  
  49.             // Rho Pi
  50.             $t = $st[1];
  51.             for ($i = 0; $i < 24; $i++) {
  52.                 $j = self::$keccakf_piln[$i];
  53.  
  54.                 $bc[0] = $st[$j];
  55.  
  56.                 $n = self::$keccakf_rotc[$i];
  57.                 $hi = $t[0];
  58.                 $lo = $t[1];
  59.                 if ($n >= 32) {
  60.                     $n -= 32;
  61.                     $hi = $t[1];
  62.                     $lo = $t[0];
  63.                 }
  64.  
  65.                 $st[$j] =[
  66.                     (($hi << $n) | ($lo >> (32 - $n))) & (0xFFFFFFFF),
  67.                     (($lo << $n) | ($hi >> (32 - $n))) & (0xFFFFFFFF)
  68.                 ];
  69.  
  70.                 $t = $bc[0];
  71.             }
  72.  
  73.             //  Chi
  74.             for ($j = 0; $j < 25; $j += 5) {
  75.                 for ($i = 0; $i < 5; $i++) {
  76.                     $bc[$i] = $st[$j + $i];
  77.                 }
  78.                 for ($i = 0; $i < 5; $i++) {
  79.                     $st[$j + $i] = [
  80.                         $st[$j + $i][0] ^ ~$bc[($i + 1) % 5][0] & $bc[($i + 2) % 5][0],
  81.                         $st[$j + $i][1] ^ ~$bc[($i + 1) % 5][1] & $bc[($i + 2) % 5][1]
  82.                     ];
  83.                 }
  84.             }
  85.  
  86.             // Iota
  87.             $st[0] = [
  88.                 $st[0][0] ^ $keccakf_rndc[$round][0],
  89.                 $st[0][1] ^ $keccakf_rndc[$round][1]
  90.             ];
  91.         }
  92.     }
  93.  
  94.     private static function keccak64($in_raw, $capacity, $outputlength, $suffix, $raw_output)
  95.     {
  96.         $capacity /= 8;
  97.  
  98.         $inlen = self::ourStrlen($in_raw);
  99.  
  100.         $rsiz = 200 - 2 * $capacity;
  101.         $rsizw = $rsiz / 8;
  102.  
  103.         $st = [];
  104.         for ($i = 0; $i < 25; $i++) {
  105.             $st[] = [0, 0];
  106.         }
  107.  
  108.         for ($in_t = 0; $inlen >= $rsiz; $inlen -= $rsiz, $in_t += $rsiz) {
  109.             for ($i = 0; $i < $rsizw; $i++) {
  110.                 $t = unpack('V*', self::ourSubstr($in_raw, $i * 8 + $in_t, 8));
  111.  
  112.                 $st[$i] = [
  113.                     $st[$i][0] ^ $t[2],
  114.                     $st[$i][1] ^ $t[1]
  115.                 ];
  116.             }
  117.  
  118.             self::keccakf64($st, self::KECCAK_ROUNDS);
  119.         }
  120.  
  121.         $temp = self::ourSubstr($in_raw, $in_t, $inlen);
  122.         $temp = str_pad($temp, $rsiz, "\x0", STR_PAD_RIGHT);
  123.  
  124.         $temp[$inlen] = chr($suffix);
  125.         $temp[$rsiz - 1] = chr(ord($temp[$rsiz - 1]) | 0x80);
  126.  
  127.         for ($i = 0; $i < $rsizw; $i++) {
  128.             $t = unpack('V*', self::ourSubstr($temp, $i * 8, 8));
  129.  
  130.             $st[$i] = [
  131.                 $st[$i][0] ^ $t[2],
  132.                 $st[$i][1] ^ $t[1]
  133.             ];
  134.         }
  135.  
  136.         self::keccakf64($st, self::KECCAK_ROUNDS);
  137.  
  138.         $out = '';
  139.         for ($i = 0; $i < 25; $i++) {
  140.             $out .= $t = pack('V*', $st[$i][1], $st[$i][0]);
  141.         }
  142.         $r = self::ourSubstr($out, 0, $outputlength / 8);
  143.  
  144.         return $raw_output ? $r : bin2hex($r);
  145.     }
  146.  
  147.     private static function keccakf32(&$st, $rounds)
  148.     {
  149.         $keccakf_rndc = [
  150.             [0x0000, 0x0000, 0x0000, 0x0001], [0x0000, 0x0000, 0x0000, 0x8082], [0x8000, 0x0000, 0x0000, 0x0808a], [0x8000, 0x0000, 0x8000, 0x8000],
  151.             [0x0000, 0x0000, 0x0000, 0x808b], [0x0000, 0x0000, 0x8000, 0x0001], [0x8000, 0x0000, 0x8000, 0x08081], [0x8000, 0x0000, 0x0000, 0x8009],
  152.             [0x0000, 0x0000, 0x0000, 0x008a], [0x0000, 0x0000, 0x0000, 0x0088], [0x0000, 0x0000, 0x8000, 0x08009], [0x0000, 0x0000, 0x8000, 0x000a],
  153.             [0x0000, 0x0000, 0x8000, 0x808b], [0x8000, 0x0000, 0x0000, 0x008b], [0x8000, 0x0000, 0x0000, 0x08089], [0x8000, 0x0000, 0x0000, 0x8003],
  154.             [0x8000, 0x0000, 0x0000, 0x8002], [0x8000, 0x0000, 0x0000, 0x0080], [0x0000, 0x0000, 0x0000, 0x0800a], [0x8000, 0x0000, 0x8000, 0x000a],
  155.             [0x8000, 0x0000, 0x8000, 0x8081], [0x8000, 0x0000, 0x0000, 0x8080], [0x0000, 0x0000, 0x8000, 0x00001], [0x8000, 0x0000, 0x8000, 0x8008]
  156.         ];
  157.  
  158.         $bc = [];
  159.         for ($round = 0; $round < $rounds; $round++) {
  160.  
  161.             // Theta
  162.             for ($i = 0; $i < 5; $i++) {
  163.                 $bc[$i] = [
  164.                     $st[$i][0] ^ $st[$i + 5][0] ^ $st[$i + 10][0] ^ $st[$i + 15][0] ^ $st[$i + 20][0],
  165.                     $st[$i][1] ^ $st[$i + 5][1] ^ $st[$i + 10][1] ^ $st[$i + 15][1] ^ $st[$i + 20][1],
  166.                     $st[$i][2] ^ $st[$i + 5][2] ^ $st[$i + 10][2] ^ $st[$i + 15][2] ^ $st[$i + 20][2],
  167.                     $st[$i][3] ^ $st[$i + 5][3] ^ $st[$i + 10][3] ^ $st[$i + 15][3] ^ $st[$i + 20][3]
  168.                 ];
  169.             }
  170.  
  171.             for ($i = 0; $i < 5; $i++) {
  172.                 $t = [
  173.                     $bc[($i + 4) % 5][0] ^ ((($bc[($i + 1) % 5][0] << 1) | ($bc[($i + 1) % 5][1] >> 15)) & (0xFFFF)),
  174.                     $bc[($i + 4) % 5][1] ^ ((($bc[($i + 1) % 5][1] << 1) | ($bc[($i + 1) % 5][2] >> 15)) & (0xFFFF)),
  175.                     $bc[($i + 4) % 5][2] ^ ((($bc[($i + 1) % 5][2] << 1) | ($bc[($i + 1) % 5][3] >> 15)) & (0xFFFF)),
  176.                     $bc[($i + 4) % 5][3] ^ ((($bc[($i + 1) % 5][3] << 1) | ($bc[($i + 1) % 5][0] >> 15)) & (0xFFFF))
  177.                 ];
  178.  
  179.                 for ($j = 0; $j < 25; $j += 5) {
  180.                     $st[$j + $i] = [
  181.                         $st[$j + $i][0] ^ $t[0],
  182.                         $st[$j + $i][1] ^ $t[1],
  183.                         $st[$j + $i][2] ^ $t[2],
  184.                         $st[$j + $i][3] ^ $t[3]
  185.                     ];
  186.                 }
  187.             }
  188.  
  189.             // Rho Pi
  190.             $t = $st[1];
  191.             for ($i = 0; $i < 24; $i++) {
  192.                 $j = self::$keccakf_piln[$i];
  193.                 $bc[0] = $st[$j];
  194.  
  195.  
  196.                 $n = self::$keccakf_rotc[$i] >> 4;
  197.                 $m = self::$keccakf_rotc[$i] % 16;
  198.  
  199.                 $st[$j] =  [
  200.                     ((($t[(0+$n) %4] << $m) | ($t[(1+$n) %4] >> (16-$m))) & (0xFFFF)),
  201.                     ((($t[(1+$n) %4] << $m) | ($t[(2+$n) %4] >> (16-$m))) & (0xFFFF)),
  202.                     ((($t[(2+$n) %4] << $m) | ($t[(3+$n) %4] >> (16-$m))) & (0xFFFF)),
  203.                     ((($t[(3+$n) %4] << $m) | ($t[(0+$n) %4] >> (16-$m))) & (0xFFFF))
  204.                 ];
  205.  
  206.                 $t = $bc[0];
  207.             }
  208.  
  209.             //  Chi
  210.             for ($j = 0; $j < 25; $j += 5) {
  211.                 for ($i = 0; $i < 5; $i++) {
  212.                     $bc[$i] = $st[$j + $i];
  213.                 }
  214.                 for ($i = 0; $i < 5; $i++) {
  215.                     $st[$j + $i] = [
  216.                         $st[$j + $i][0] ^ ~$bc[($i + 1) % 5][0] & $bc[($i + 2) % 5][0],
  217.                         $st[$j + $i][1] ^ ~$bc[($i + 1) % 5][1] & $bc[($i + 2) % 5][1],
  218.                         $st[$j + $i][2] ^ ~$bc[($i + 1) % 5][2] & $bc[($i + 2) % 5][2],
  219.                         $st[$j + $i][3] ^ ~$bc[($i + 1) % 5][3] & $bc[($i + 2) % 5][3]
  220.                     ];
  221.                 }
  222.             }
  223.  
  224.             // Iota
  225.             $st[0] = [
  226.                 $st[0][0] ^ $keccakf_rndc[$round][0],
  227.                 $st[0][1] ^ $keccakf_rndc[$round][1],
  228.                 $st[0][2] ^ $keccakf_rndc[$round][2],
  229.                 $st[0][3] ^ $keccakf_rndc[$round][3]
  230.             ];
  231.         }
  232.     }
  233.  
  234.     private static function keccak32($in_raw, $capacity, $outputlength, $suffix, $raw_output)
  235.     {
  236.         $capacity /= 8;
  237.  
  238.         $inlen = self::ourStrlen($in_raw);
  239.  
  240.         $rsiz = 200 - 2 * $capacity;
  241.         $rsizw = $rsiz / 8;
  242.  
  243.         $st = [];
  244.         for ($i = 0; $i < 25; $i++) {
  245.             $st[] = [0, 0, 0, 0];
  246.         }
  247.  
  248.         for ($in_t = 0; $inlen >= $rsiz; $inlen -= $rsiz, $in_t += $rsiz) {
  249.             for ($i = 0; $i < $rsizw; $i++) {
  250.                 $t = unpack('v*', self::ourSubstr($in_raw, $i * 8 + $in_t, 8));
  251.  
  252.                 $st[$i] = [
  253.                     $st[$i][0] ^ $t[4],
  254.                     $st[$i][1] ^ $t[3],
  255.                     $st[$i][2] ^ $t[2],
  256.                     $st[$i][3] ^ $t[1]
  257.                 ];
  258.             }
  259.  
  260.             self::keccakf32($st, self::KECCAK_ROUNDS);
  261.         }
  262.  
  263.         $temp = self::ourSubstr($in_raw, $in_t, $inlen);
  264.         $temp = str_pad($temp, $rsiz, "\x0", STR_PAD_RIGHT);
  265.  
  266.         $temp[$inlen] = chr($suffix);
  267.         $temp[$rsiz - 1] = chr($temp[$rsiz - 1] | 0x80);
  268.  
  269.         for ($i = 0; $i < $rsizw; $i++) {
  270.             $t = unpack('v*', self::ourSubstr($temp, $i * 8, 8));
  271.  
  272.             $st[$i] = [
  273.                 $st[$i][0] ^ $t[4],
  274.                 $st[$i][1] ^ $t[3],
  275.                 $st[$i][2] ^ $t[2],
  276.                 $st[$i][3] ^ $t[1]
  277.             ];
  278.         }
  279.  
  280.         self::keccakf32($st, self::KECCAK_ROUNDS);
  281.  
  282.         $out = '';
  283.         for ($i = 0; $i < 25; $i++) {
  284.             $out .= $t = pack('v*', $st[$i][3],$st[$i][2], $st[$i][1], $st[$i][0]);
  285.         }
  286.         $r = self::ourSubstr($out, 0, $outputlength / 8);
  287.  
  288.         return $raw_output ? $r: bin2hex($r);
  289.     }
  290.  
  291.     // 0 = not run, 1 = 64 bit passed, 2 = 32 bit passed, 3 = failed
  292.     private static $test_state = 0;
  293.     private static function selfTest()
  294.     {
  295.         if(self::$test_state === 1 || self::$test_state === 2){
  296.             return;
  297.         }
  298.  
  299.         if(self::$test_state === 3){
  300.             throw new \Exception('Sha3 previous self test failed!');
  301.         }
  302.  
  303.         $in = '';
  304.         $md = '6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7';
  305.         if(self::keccak64($in, 224, 224, 0x06, false) === $md){
  306.             self::$test_state = 1;
  307.             return;
  308.         }
  309.  
  310.         if(self::keccak32($in, 224, 224, 0x06, false) === $md){
  311.             self::$test_state = 2;
  312.             return;
  313.         }
  314.  
  315.         self::$test_state = 3;
  316.         throw new \Exception('Sha3 self test failed!');
  317.     }
  318.  
  319.     private static function keccak($in_raw, $capacity, $outputlength, $suffix, $raw_output)
  320.     {
  321.         self::selfTest();
  322.  
  323.         if(self::$test_state === 1) {
  324.             return self::keccak64($in_raw, $capacity, $outputlength, $suffix, $raw_output);
  325.         }
  326.  
  327.         return self::keccak32($in_raw, $capacity, $outputlength, $suffix, $raw_output);
  328.     }
  329.  
  330.     public static function hash($in, $mdlen, $raw_output = false)
  331.     {
  332.         if( ! in_array($mdlen, [224, 256, 384, 512], true)) {
  333.             throw new \Exception('Unsupported Sha3 Hash output size.');
  334.         }
  335.  
  336.         return self::keccak($in, $mdlen, $mdlen, 0x06, $raw_output);
  337.     }
  338.  
  339.     public static function shake($in, $security_level, $outlen, $raw_output = false)
  340.     {
  341.         if( ! in_array($security_level, [128, 256], true)) {
  342.             throw new \Exception('Unsupported Sha3 Shake security level.');
  343.         }
  344.  
  345.         return self::keccak($in, $security_level, $outlen, 0x1f, $raw_output);
  346.     }
  347.  
  348.     /**
  349.      *  Multi-byte-safe string functions borrowed from https://github.com/sarciszewski/php-future
  350.      */
  351.  
  352.     /**
  353.      * Multi-byte-safe string length calculation
  354.      *
  355.      * @param string $str
  356.      * @return int
  357.      */
  358.     private static function ourStrlen($str)
  359.     {
  360.         // Premature optimization: cache the function_exists() result
  361.         static $exists = null;
  362.         if ($exists === null) {
  363.             $exists = \function_exists('\\mb_strlen');
  364.         }
  365.         // If it exists, we need to make sure we're using 8bit mode
  366.         if ($exists) {
  367.             $length =  \mb_strlen($str, '8bit');
  368.             if ($length === false) {
  369.                 throw new \Exception('mb_strlen() failed.');
  370.             }
  371.             return $length;
  372.         }
  373.  
  374.         return \strlen($str);
  375.     }
  376.  
  377.     /**
  378.      * Multi-byte-safe substring calculation
  379.      *
  380.      * @param string $str
  381.      * @param int $start
  382.      * @param int $length (optional)
  383.      * @return string
  384.      */
  385.     private static function ourSubstr($str, $start = 0, $length = null)
  386.     {
  387.         // Premature optimization: cache the function_exists() result
  388.         static $exists = null;
  389.         if ($exists === null) {
  390.             $exists = \function_exists('\\mb_substr');
  391.         }
  392.         // If it exists, we need to make sure we're using 8bit mode
  393.         if ($exists) {
  394.             return \mb_substr($str, $start, $length, '8bit');
  395.         } elseif ($length !== null) {
  396.             return \substr($str, $start, $length);
  397.         }
  398.         return \substr($str, $start);
  399.     }
  400. }
  401.