SHA3.php 8.8 KB

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