Salsa20.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. <?php
  2. namespace ACES\Common\Salsa20;
  3. /**
  4. * Salsa20, HSalsa20 and XSalsa20
  5. *
  6. * Assembled from:
  7. * - http://tweetnacl.cr.yp.to/
  8. *
  9. *
  10. * @link https://github.com/devi/Salt
  11. *
  12. */
  13. class Salsa20 {
  14. /* Salsa20, HSalsa20, XSalsa20 */
  15. const salsa20_KEY = 32;
  16. const salsa20_NONCE = 8;
  17. const salsa20_INPUT = 16;
  18. const salsa20_OUTPUT = 64;
  19. const salsa20_CONST = 16;
  20. const hsalsa20_KEY = 32;
  21. const hsalsa20_INPUT = 16;
  22. const hsalsa20_OUTPUT = 32;
  23. const hsalsa20_CONST = 16;
  24. const xsalsa20_KEY = 32;
  25. const xsalsa20_NONCE = 24;
  26. /* Stream salsa20, salsa20_xor */
  27. const stream_salsa20_KEY = 32;
  28. const stream_salsa20_NONCE = 24;
  29. public static $sigma = array(101,120,112,97,110,100,32,51,50,45,98,121,116,101,32,107);
  30. /* Lazy load */
  31. private static $instance;
  32. public static function instance() {
  33. if (!isset(static::$instance)) {
  34. static::$instance = new Salsa20();
  35. }
  36. return static::$instance;
  37. }
  38. function load($x, $offset = 0) {
  39. return
  40. $x[$offset] |
  41. ($x[1+$offset] << 8) |
  42. ($x[2+$offset] << 16) |
  43. ($x[3+$offset] << 24);
  44. }
  45. function store($x, $offset = 0, $u) {
  46. $x[$offset] = $u & 0xff; $u >>= 8;
  47. $x[1+$offset] = $u & 0xff; $u >>= 8;
  48. $x[2+$offset] = $u & 0xff; $u >>= 8;
  49. $x[3+$offset] = $u & 0xff;
  50. }
  51. function rotate(&$x, $y, $z, $c) {
  52. $u = $y + $z;
  53. $u &= 0xffffffff;
  54. $x ^= ($u << $c) | ($u >> (32 - $c));
  55. $x &= 0xffffffff;
  56. }
  57. function core($out, $in, $k, $c, $salsa20 = true) {
  58. $x0 = $this->load($c, 0);
  59. $x1 = $this->load($k, 0);
  60. $x2 = $this->load($k, 4);
  61. $x3 = $this->load($k, 8);
  62. $x4 = $this->load($k, 12);
  63. $x5 = $this->load($c, 4);
  64. $x6 = $this->load($in, 0);
  65. $x7 = $this->load($in, 4);
  66. $x8 = $this->load($in, 8);
  67. $x9 = $this->load($in, 12);
  68. $x10 = $this->load($c, 8);
  69. $x11 = $this->load($k, 16);
  70. $x12 = $this->load($k, 20);
  71. $x13 = $this->load($k, 24);
  72. $x14 = $this->load($k, 28);
  73. $x15 = $this->load($c, 12);
  74. if ($salsa20) {
  75. $j0 = $x0; $j1 = $x1; $j2 = $x2; $j3 = $x3; $j4 = $x4;
  76. $j5 = $x5; $j6 = $x6; $j7 = $x7; $j8 = $x8; $j9 = $x9;
  77. $j10 = $x10; $j11 = $x11; $j12 = $x12; $j13 = $x13;
  78. $j14 = $x14; $j15 = $x15;
  79. }
  80. for ($i = 0; $i < 20; $i += 2) {
  81. $this->rotate($x4, $x0, $x12, 7);
  82. $this->rotate($x8, $x4, $x0, 9);
  83. $this->rotate($x12, $x8, $x4, 13);
  84. $this->rotate($x0, $x12, $x8, 18);
  85. $this->rotate($x9, $x5, $x1, 7);
  86. $this->rotate($x13, $x9, $x5, 9);
  87. $this->rotate($x1, $x13, $x9, 13);
  88. $this->rotate($x5, $x1, $x13, 18);
  89. $this->rotate($x14, $x10, $x6, 7);
  90. $this->rotate($x2, $x14, $x10, 9);
  91. $this->rotate($x6, $x2, $x14, 13);
  92. $this->rotate($x10, $x6, $x2, 18);
  93. $this->rotate($x3, $x15, $x11, 7);
  94. $this->rotate($x7, $x3, $x15, 9);
  95. $this->rotate($x11, $x7, $x3, 13);
  96. $this->rotate($x15, $x11, $x7, 18);
  97. $this->rotate($x1, $x0, $x3, 7);
  98. $this->rotate($x2, $x1, $x0, 9);
  99. $this->rotate($x3, $x2, $x1, 13);
  100. $this->rotate($x0, $x3, $x2, 18);
  101. $this->rotate($x6, $x5, $x4, 7);
  102. $this->rotate($x7, $x6, $x5, 9);
  103. $this->rotate($x4, $x7, $x6, 13);
  104. $this->rotate($x5, $x4, $x7, 18);
  105. $this->rotate($x11, $x10, $x9, 7);
  106. $this->rotate($x8, $x11, $x10, 9);
  107. $this->rotate($x9, $x8, $x11, 13);
  108. $this->rotate($x10, $x9, $x8, 18);
  109. $this->rotate($x12, $x15, $x14, 7);
  110. $this->rotate($x13, $x12, $x15, 9);
  111. $this->rotate($x14, $x13, $x12, 13);
  112. $this->rotate($x15, $x14, $x13, 18);
  113. }
  114. if ($salsa20) {
  115. $x0 += $j0; $x1 += $j1; $x2 += $j2; $x3 += $j3; $x4 += $j4;
  116. $x5 += $j5; $x6 += $j6; $x7 += $j7; $x8 += $j8; $x9 += $j9;
  117. $x10 += $j10; $x11 += $j11; $x12 += $j12; $x13 += $j13;
  118. $x14 += $j14; $x15 += $j15;
  119. $this->store($out, 0, $x0);
  120. $this->store($out, 4, $x1);
  121. $this->store($out, 8, $x2);
  122. $this->store($out, 12, $x3);
  123. $this->store($out, 16, $x4);
  124. $this->store($out, 20, $x5);
  125. $this->store($out, 24, $x6);
  126. $this->store($out, 28, $x7);
  127. $this->store($out, 32, $x8);
  128. $this->store($out, 36, $x9);
  129. $this->store($out, 40, $x10);
  130. $this->store($out, 44, $x11);
  131. $this->store($out, 48, $x12);
  132. $this->store($out, 52, $x13);
  133. $this->store($out, 56, $x14);
  134. $this->store($out, 60, $x15);
  135. } else {
  136. $this->store($out, 0, $x0);
  137. $this->store($out, 4, $x5);
  138. $this->store($out, 8, $x10);
  139. $this->store($out, 12, $x15);
  140. $this->store($out, 16, $x6);
  141. $this->store($out, 20, $x7);
  142. $this->store($out, 24, $x8);
  143. $this->store($out, 28, $x9);
  144. }
  145. }
  146. function stream($out, $m, $mlen ,$n, $k) {
  147. if (!$mlen) return false;
  148. $z = new \SplFixedArray(16);
  149. $x = new \SplFixedArray(64);
  150. for ($i = 0;$i < 8;++$i) $z[$i] = $n[$i];
  151. if ($m) {
  152. $l = count($m);
  153. if ($l < $mlen) {
  154. for ($i = $l;$i < $mlen;++$i) $m[$i] = 0;
  155. }
  156. $mpos = 0;
  157. }
  158. $outpos = 0;
  159. while ($mlen >= 64) {
  160. $this->core($x, $z, $k, Salsa20::$sigma);
  161. for ($i = 0;$i < 64;++$i) {
  162. $out[$i+$outpos] = ($m?$m[$i+$mpos]:0) ^ $x[$i];
  163. }
  164. $u = 1;
  165. for ($i = 8;$i < 16;++$i) {
  166. $u += $z[$i];
  167. $z[$i] = $u;
  168. $u >>= 8;
  169. }
  170. $mlen -= 64;
  171. $outpos += 64;
  172. if ($m) $mpos += 64;
  173. }
  174. if ($mlen) {
  175. $this->core($x, $z, $k, Salsa20::$sigma);
  176. for ($i = 0;$i < $mlen;++$i) {
  177. $out[$i+$outpos] = ($m?$m[$i+$mpos]:0) ^ $x[$i];
  178. }
  179. }
  180. }
  181. public function crypto_core_salsa20($in, $key, $const) {
  182. $out = new FieldElement(32);
  183. $this->core($out, $in, $key, $const);
  184. return $out;
  185. }
  186. public function crypto_core_hsalsa20($in, $key, $const) {
  187. $out = new FieldElement(32);
  188. $this->core($out, $in, $key, $const, false);
  189. return $out;
  190. }
  191. public function crypto_stream_salsa20($length, $nonce, $key) {
  192. $out = new FieldElement($length);
  193. $this->stream($out, false, $length, $nonce, $key);
  194. return $out;
  195. }
  196. public function crypto_stream_salsa20_xor($in, $length, $nonce, $key) {
  197. $out = new FieldElement($length);
  198. $this->stream($out, $in, $length, $nonce, $key);
  199. return $out;
  200. }
  201. public function crypto_stream_xsalsa20($length, $nonce, $key) {
  202. $subkey = $this->crypto_core_hsalsa20($nonce, $key, Salsa20::$sigma);
  203. return $this->crypto_stream_salsa20($length, $nonce->slice(16), $subkey);
  204. }
  205. public function crypto_stream_xsalsa20_xor($in, $length, $nonce, $key) {
  206. $subkey = $this->crypto_core_hsalsa20($nonce, $key, Salsa20::$sigma);
  207. return $this->crypto_stream_salsa20_xor($in, $length, $nonce->slice(16), $subkey);
  208. }
  209. public function crypto_stream($length, $nonce, $key) {
  210. return $this->crypto_stream_xsalsa20($length, $nonce, $key);
  211. }
  212. public function crypto_stream_xor($in, $length, $nonce, $key) {
  213. return $this->crypto_stream_xsalsa20_xor($in, $length, $nonce, $key);
  214. }
  215. }