Subversion Repositories uuid_mac_utils

Rev

Blame | Last modification | View Log | RSS feed

  1. <?php /* -*- coding: utf-8; indent-tabs-mode: t; tab-width: 4 -*-
  2. vim: ts=4 noet ai */
  3.  
  4. /**
  5.         Streamable SHA-3 for PHP 5.2+, with no lib/ext dependencies!
  6.  
  7.         Copyright © 2018  Desktopd Developers
  8.  
  9.         This program is free software: you can redistribute it and/or modify
  10.         it under the terms of the GNU Lesser General Public License as published by
  11.         the Free Software Foundation, either version 3 of the License, or
  12.         (at your option) any later version.
  13.  
  14.         This program is distributed in the hope that it will be useful,
  15.         but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.         GNU Lesser General Public License for more details.
  18.  
  19.         You should have received a copy of the GNU Lesser General Public License
  20.         along with this program.  If not, see <https://www.gnu.org/licenses/>.
  21.  
  22.         @license LGPL-3+
  23.         @file
  24. */
  25.  
  26. // Fixed small issues found by PHPstan by Daniel Marschall 02 August 2023
  27.  
  28.  
  29. /**
  30.         SHA-3 (FIPS-202) for PHP strings (byte arrays) (PHP 5.2.1+)
  31.         PHP 7.0 computes SHA-3 about 4 times faster than PHP 5.2 - 5.6 (on x86_64)
  32.  
  33.         Based on the reference implementations, which are under CC-0
  34.         Reference: http://keccak.noekeon.org/
  35.  
  36.         This uses PHP's native byte strings. Supports 32-bit as well as 64-bit
  37.         systems. Also for LE vs. BE systems.
  38. */
  39. class SHA3 {
  40.         const SHA3_224 = 1;
  41.         const SHA3_256 = 2;
  42.         const SHA3_384 = 3;
  43.         const SHA3_512 = 4;
  44.  
  45.         const SHAKE128 = 5;
  46.         const SHAKE256 = 6;
  47.  
  48.         private $blockSize = null; // added DM 2 Aug 2023
  49.  
  50.  
  51.         public static function init ($type = null) {
  52.                 switch ($type) {
  53.                         case self::SHA3_224: return new self (1152, 448, 0x06, 28);
  54.                         case self::SHA3_256: return new self (1088, 512, 0x06, 32);
  55.                         case self::SHA3_384: return new self (832, 768, 0x06, 48);
  56.                         case self::SHA3_512: return new self (576, 1024, 0x06, 64);
  57.                         case self::SHAKE128: return new self (1344, 256, 0x1f);
  58.                         case self::SHAKE256: return new self (1088, 512, 0x1f);
  59.                 }
  60.  
  61.                 throw new Exception ('Invalid operation type');
  62.         }
  63.  
  64.  
  65.         /**
  66.                 Feed input to SHA-3 "sponge"
  67.         */
  68.         public function absorb ($data) {
  69.                 if (self::PHASE_INPUT != $this->phase) {
  70.                         throw new Exception ('No more input accepted');
  71.                 }
  72.  
  73.                 $rateInBytes = $this->rateInBytes;
  74.                 $this->inputBuffer .= $data;
  75.                 while (strlen ($this->inputBuffer) >= $rateInBytes) {
  76.                         list ($input, $this->inputBuffer) = array (
  77.                                 substr ($this->inputBuffer, 0, $rateInBytes)
  78.                                 , substr ($this->inputBuffer, $rateInBytes));
  79.  
  80.                         $blockSize = $rateInBytes;
  81.                         for ($i = 0; $i < $blockSize; $i++) {
  82.                                 $this->state[$i] = $this->state[$i] ^ $input[$i];
  83.                         }
  84.  
  85.                         $this->state = self::keccakF1600Permute ($this->state);
  86.                         $this->blockSize = 0;
  87.                 }
  88.  
  89.                 return $this;
  90.         }
  91.  
  92.         /**
  93.                 Get hash output
  94.         */
  95.         public function squeeze ($length = null) {
  96.                 $outputLength = $this->outputLength; // fixed length output
  97.                 if ($length && 0 < $outputLength && $outputLength != $length) {
  98.                         throw new Exception ('Invalid length');
  99.                 }
  100.  
  101.                 if (self::PHASE_INPUT == $this->phase) {
  102.                         $this->finalizeInput ();
  103.                 }
  104.  
  105.                 if (self::PHASE_OUTPUT != $this->phase) {
  106.                         throw new Exception ('No more output allowed');
  107.                 }
  108.                 if (0 < $outputLength) {
  109.                         $this->phase = self::PHASE_DONE;
  110.                         return $this->getOutputBytes ($outputLength);
  111.                 }
  112.  
  113.                 $blockLength = $this->rateInBytes;
  114.                 list ($output, $this->outputBuffer) = array (
  115.                         substr ($this->outputBuffer, 0, $length)
  116.                         , substr ($this->outputBuffer, $length));
  117.                 $neededLength = $length - strlen ($output);
  118.                 $diff = $neededLength % $blockLength;
  119.                 if ($diff) {
  120.                         $readLength = (($neededLength - $diff) / $blockLength + 1)
  121.                                 * $blockLength;
  122.                 } else {
  123.                         $readLength = $neededLength;
  124.                 }
  125.  
  126.                 $read = $this->getOutputBytes ($readLength);
  127.                 $this->outputBuffer .= substr ($read, $neededLength);
  128.                 return $output . substr ($read, 0, $neededLength);
  129.         }
  130.  
  131.  
  132.         // internally used
  133.         const PHASE_INIT = 1;
  134.         const PHASE_INPUT = 2;
  135.         const PHASE_OUTPUT = 3;
  136.         const PHASE_DONE = 4;
  137.  
  138.         private $phase = self::PHASE_INIT;
  139.         private $state; // byte array (string)
  140.         private $rateInBytes; // positive integer
  141.         private $suffix; // 8-bit unsigned integer
  142.         private $inputBuffer = ''; // byte array (string): max length = rateInBytes
  143.         private $outputLength = 0;
  144.         private $outputBuffer = '';
  145.  
  146.  
  147.         /**
  148.         * @param int $rate
  149.         * @param int $capacity
  150.         * @param int $suffix
  151.         * @param int $length
  152.         */
  153.         public function __construct ($rate, $capacity, $suffix, $length = 0) {
  154.                 if (1600 != ($rate + $capacity)) {
  155.                         throw new Error ('Invalid parameters');
  156.                 }
  157.                 if (0 != ($rate % 8)) {
  158.                         throw new Error ('Invalid rate');
  159.                 }
  160.  
  161.                 $this->suffix = $suffix;
  162.                 $this->state = str_repeat ("\0", 200);
  163.                 $this->blockSize = 0;
  164.  
  165.                 $this->rateInBytes = $rate / 8;
  166.                 $this->outputLength = $length;
  167.                 $this->phase = self::PHASE_INPUT;
  168.                 return;
  169.         }
  170.  
  171.         protected function finalizeInput () {
  172.                 $this->phase = self::PHASE_OUTPUT;
  173.  
  174.                 $input = $this->inputBuffer;
  175.                 $inputLength = strlen ($input);
  176.                 if (0 < $inputLength) {
  177.                         $blockSize = $inputLength;
  178.                         for ($i = 0; $i < $blockSize; $i++) {
  179.                                 $this->state[$i] = $this->state[$i] ^ $input[$i];
  180.                         }
  181.  
  182.                         $this->blockSize = $blockSize;
  183.                 }
  184.  
  185.                 // Padding
  186.                 $rateInBytes = $this->rateInBytes;
  187.                 $this->state[$this->blockSize] = $this->state[$this->blockSize]
  188.                         ^ chr ($this->suffix);
  189.                 if (($this->suffix & 0x80) != 0
  190.                         && $this->blockSize == ($rateInBytes - 1)) {
  191.                         $this->state = self::keccakF1600Permute ($this->state);
  192.                 }
  193.                 $this->state[$rateInBytes - 1] = $this->state[$rateInBytes - 1] ^ "\x80";
  194.                 $this->state = self::keccakF1600Permute ($this->state);
  195.         }
  196.  
  197.         protected function getOutputBytes ($outputLength) {
  198.                 // Squeeze
  199.                 $output = '';
  200.                 while (0 < $outputLength) {
  201.                         $blockSize = min ($outputLength, $this->rateInBytes);
  202.                         $output .= substr ($this->state, 0, $blockSize);
  203.                         $outputLength -= $blockSize;
  204.                         if (0 < $outputLength) {
  205.                                 $this->state = self::keccakF1600Permute ($this->state);
  206.                         }
  207.                 }
  208.  
  209.                 return $output;
  210.         }
  211.  
  212.         /**
  213.                 1600-bit state version of Keccak's permutation
  214.         */
  215.         protected static function keccakF1600Permute ($state) {
  216.                 $lanes = str_split ($state, 8);
  217.                 $R = 1;
  218.                 $values = "\1\2\4\10\20\40\100\200";
  219.  
  220.                 for ($round = 0; $round < 24; $round++) {
  221.                         // θ step
  222.                         $C = array ();
  223.                         for ($x = 0; $x < 5; $x++) {
  224.                                 // (x, 0) (x, 1) (x, 2) (x, 3) (x, 4)
  225.                                 $C[$x] = $lanes[$x] ^ $lanes[$x + 5] ^ $lanes[$x + 10]
  226.                                         ^ $lanes[$x + 15] ^ $lanes[$x + 20];
  227.                         }
  228.                         for ($x = 0; $x < 5; $x++) {
  229.                                 //$D = $C[($x + 4) % 5] ^ self::rotL64 ($C[($x + 1) % 5], 1);
  230.                                 $D = $C[($x + 4) % 5] ^ self::rotL64One ($C[($x + 1) % 5]);
  231.                                 for ($y = 0; $y < 5; $y++) {
  232.                                         $idx = $x + 5 * $y; // x, y
  233.                                         $lanes[$idx] = $lanes[$idx] ^ $D;
  234.                                 }
  235.                         }
  236.                         unset ($C, $D);
  237.  
  238.                         // ρ and π steps
  239.                         $x = 1;
  240.                         $y = 0;
  241.                         $current = $lanes[1]; // x, y
  242.                         for ($t = 0; $t < 24; $t++) {
  243.                                 list ($x, $y) = array ($y, (2 * $x + 3 * $y) % 5);
  244.                                 $idx = $x + 5 * $y;
  245.                                 list ($current, $lanes[$idx]) = array ($lanes[$idx]
  246.                                         , self::rotL64 ($current
  247.                                                 , (($t + 1) * ($t + 2) / 2) % 64));
  248.                         }
  249.                         unset ($current);
  250.  
  251.                         // χ step
  252.                         $temp = array ();
  253.                         for ($y = 0; $y < 5; $y++) {
  254.                                 for ($x = 0; $x < 5; $x++) {
  255.                                         $temp[$x] = $lanes[$x + 5 * $y];
  256.                                 }
  257.                                 for ($x = 0; $x < 5; $x++) {
  258.                                         $lanes[$x + 5 * $y] = $temp[$x]
  259.                                                 ^ ((~ (string)$temp[($x + 1) % 5]) & $temp[($x + 2) % 5]);
  260.  
  261.                                 }
  262.                         }
  263.                         unset ($temp);
  264.  
  265.                         // ι step
  266.                         for ($j = 0; $j < 7; $j++) {
  267.                                 $R = (($R << 1) ^ (($R >> 7) * 0x71)) & 0xff;
  268.                                 if ($R & 2) {
  269.                                         $offset = (1 << $j) - 1;
  270.                                         $shift = $offset % 8;
  271.                                         $octetShift = ($offset - $shift) / 8;
  272.                                         $n = "\0\0\0\0\0\0\0\0";
  273.                                         $n[$octetShift] = $values[$shift];
  274.  
  275.                                         $lanes[0] = $lanes[0]
  276.                                                 ^ $n;
  277.                                                 //^ self::rotL64 ("\1\0\0\0\0\0\0\0", (1 << $j) - 1);
  278.                                 }
  279.                         }
  280.                 }
  281.  
  282.                 return implode ($lanes);
  283.         }
  284.  
  285.         protected static function rotL64_64 ($n, $offset) {
  286.                 return ($n << $offset) & ($n >> (64 - $offset));
  287.         }
  288.  
  289.         /**
  290.                 64-bit bitwise left rotation (Little endian)
  291.         */
  292.         protected static function rotL64 ($n, $offset) {
  293.  
  294.                 //$n = (binary) $n;
  295.                 //$offset = ((int) $offset) % 64;
  296.                 //if (8 != strlen ($n)) throw new Exception ('Invalid number');
  297.                 //if ($offset < 0) throw new Exception ('Invalid offset');
  298.  
  299.                 $shift = $offset % 8;
  300.                 $octetShift = ($offset - $shift) / 8;
  301.                 $n = substr ($n, - $octetShift) . substr ($n, 0, - $octetShift);
  302.  
  303.                 $overflow = 0x00;
  304.                 for ($i = 0; $i < 8; $i++) {
  305.                         $a = ord ($n[$i]) << $shift;
  306.                         $n[$i] = chr (0xff & $a | $overflow);
  307.                         $overflow = $a >> 8;
  308.                 }
  309.                 $n[0] = chr (ord ($n[0]) | $overflow);
  310.                 return $n;
  311.         }
  312.  
  313.         /**
  314.                 64-bit bitwise left rotation (Little endian)
  315.         */
  316.         protected static function rotL64One ($n) {
  317.                 list ($n[0], $n[1], $n[2], $n[3], $n[4], $n[5], $n[6], $n[7])
  318.                         = array (
  319.                                 chr (((ord ($n[0]) << 1) & 0xff) ^ (ord ($n[7]) >> 7))
  320.                                 ,chr (((ord ($n[1]) << 1) & 0xff) ^ (ord ($n[0]) >> 7))
  321.                                 ,chr (((ord ($n[2]) << 1) & 0xff) ^ (ord ($n[1]) >> 7))
  322.                                 ,chr (((ord ($n[3]) << 1) & 0xff) ^ (ord ($n[2]) >> 7))
  323.                                 ,chr (((ord ($n[4]) << 1) & 0xff) ^ (ord ($n[3]) >> 7))
  324.                                 ,chr (((ord ($n[5]) << 1) & 0xff) ^ (ord ($n[4]) >> 7))
  325.                                 ,chr (((ord ($n[6]) << 1) & 0xff) ^ (ord ($n[5]) >> 7))
  326.                                 ,chr (((ord ($n[7]) << 1) & 0xff) ^ (ord ($n[6]) >> 7)));
  327.                 return $n;
  328.         }
  329. }
  330.  
  331.  
  332. /*
  333. $hexMsg = '7c815c384eee0f288ece27cced52a01603127b079c007378bc5d1e6c5e9e6d1c735723acbbd5801ac49854b2b569d4472d33f40bbb8882956245c366dc3582d71696a97a4e19557e41e54dee482a14229005f93afd2c4a7d8614d10a97a9dfa07f7cd946fa45263063ddd29db8f9e34db60daa32684f0072ea2a9426ecebfa5239fb67f29c18cbaa2af6ed4bf4283936823ac1790164fec5457a9cba7c767ca59392d94cab7448f50eb34e9a93a80027471ce59736f099c886dea1ab4cba4d89f5fc7ae2f21ccd27f611eca4626b2d08dc22382e92c1efb2f6afdc8fdc3d2172604f5035c46b8197d3';
  334. $hexResult = 'dc2038c613a5f836bd3d7a4881b5b3bff3023da72d253e1b520bcad5162e181685662d40252bee982eb3214aa70ddf0a95c5d1031de9781266b1e0972fc9777d4a74164da68a5d4585f7a8e7438fe28d8af577306b8e2cbf6863c83431cc4c898dda50c94efd4925432fca36a6304790fbf4fefaeee279c01b8b6a8d1c275e3cb4e8bf17d880903fbaf27bfa65a2e3db8e285878a94955f6fc14f05a0fa2556994b8612bb7a494b4dd8b3cf1bc9e4bf833d4bfbf878c4d3bdc8fc70d26d7b7edaf0afe2f963dc6884c871c1475f4b92378b9824970e40da0a59780e84ac5138aa1efa46c1b50c3b045be59037c6a0c89e1d3cf246f1362794e8107b7cba74888f0bf4b905cfb9c33517f472bac16259809797f2fc883ffbdd7cede9518f891b9117de5ddc6d3e29fa56eb617f25e9eb1b66f7e46ed54c1d43ac07471d35c57b8c73bc68f5612ed042bff5e68634a4fb81e2ef0d92fff1e11e43fd6d9a935678d2fdd04e06061da3ba7de415b93c5a8db1653cf08de1866f5c3d33be32a3b8d2b7bb39e9745c6e88c782f220c367f945828b9b9250de71e8a14ec847bbeec2b1a486ce61731cef21b4a3a6353c2c705759fafa50ad33fb6abc23b45f28ee7736df6f59aaf38d59881547274cf9af2cfc8fc1ecadf81ab72e38abccd281df956f279bacc1796ad1f90d6930a5829bb95e94a8682a51a6743ae91b6c12c08e1465a';
  335. $msg = pack ('H*', $hexMsg);
  336. $result = pack ('H*', $hexResult);
  337. $sponge = SHA3::init (SHA3::SHAKE128);
  338. $sponge->absorb($msg);
  339. assert($result == $sponge->squeeze($outputLength = strlen($result)));
  340.  
  341. $hexMsg = 'fc424eeb27c18a11c01f39c555d8b78a805b88dba1dc2a42ed5e2c0ec737ff68b2456d80eb85e11714fa3f8eabfb906d3c17964cb4f5e76b29c1765db03d91be37fc';
  342. $hexResult = '66126e27da8c1600b68d0ed65e9f47c4165faa43dc4eb1b99ffeddc33e61e20b01b160c84740b0f9fe29fda1fb5eff2819d98c047cdd0cf8a0d396864e54a34657bd0c0355c75c77e5c3d9ad203e71fc2785a83d254b953277b262ee0a5bb7d0c24ed57faed4fdb96d5fd7820e6efeeb5a9e9df48c619c4872cf3b2516dbb28073273e2693544e271d6f0f64be8dc236ecd021c00039fd362a843dc3681b166cbc2407495e18903e469403807fe623f3648f799f18fbd60fff7705d07464e801e0aed4f2f0642b9a2c5cdd0c902b59b1da19a09375c1c13175b618091b8882a0e7205ee63a9219ecbcfa943a10d2d9a50c8c0b5d43b003f67ef0d52adbf9f659bb62fa6e00678bb8d4449648872a99eecdbb3dc381b5199fd500912afa93c63a6b23d00d0a416468fdab93aedd9115265be3a4440dd4029ff7f88d9755623e77f9430b934dae529be9a6b307b1b292ab5918eb24b14598554b4cc6269419c701494b7cba5b3d69f6cdcd5181fd03e0748d08e1e0aa5c4ec62c47877c1085873c016ef24e7e45da71d3db9db23b153cceda9a9ab5ccd8c5466cef29810098e976e4867075601f83a2d2cda1a476a1e990ce04c4567ffb99aac428922d9d8b25af68c36463d3aa4f689cd778f79e743e0bb5f935e6d45f978dcb2aed12dfcdca469556556e19f25d4c959c98785fb471d4bd1675d3b84742766d5ba4bff2a3f912';
  343. $msg = pack ('H*', $hexMsg);
  344. $result = pack ('H*', $hexResult);
  345. $sponge = SHA3::init (SHA3::SHAKE256);
  346. $sponge->absorb($msg);
  347. assert($result == $sponge->squeeze($outputLength = strlen($result)));
  348. */
  349.  
  350.