1<?php
2
3if (class_exists('ParagonIE_Sodium_Core_Salsa20', false)) {
4 return;
5}
6
7/**
8 * Class ParagonIE_Sodium_Core_Salsa20
9 */
10abstract class ParagonIE_Sodium_Core_Salsa20 extends ParagonIE_Sodium_Core_Util
11{
12 const ROUNDS = 20;
13
14 /**
15 * Calculate an salsa20 hash of a single block
16 *
17 * @internal You should not use this directly from another application
18 *
19 * @param string $in
20 * @param string $k
21 * @param string|null $c
22 * @return string
23 * @throws TypeError
24 */
25 public static function core_salsa20($in, $k, $c = null)
26 {
27 if (self::strlen($k) < 32) {
28 throw new RangeException('Key must be 32 bytes long');
29 }
30 if ($c === null) {
31 $j0 = $x0 = 0x61707865;
32 $j5 = $x5 = 0x3320646e;
33 $j10 = $x10 = 0x79622d32;
34 $j15 = $x15 = 0x6b206574;
35 } else {
36 $j0 = $x0 = self::load_4(self::substr($c, 0, 4));
37 $j5 = $x5 = self::load_4(self::substr($c, 4, 4));
38 $j10 = $x10 = self::load_4(self::substr($c, 8, 4));
39 $j15 = $x15 = self::load_4(self::substr($c, 12, 4));
40 }
41 $j1 = $x1 = self::load_4(self::substr($k, 0, 4));
42 $j2 = $x2 = self::load_4(self::substr($k, 4, 4));
43 $j3 = $x3 = self::load_4(self::substr($k, 8, 4));
44 $j4 = $x4 = self::load_4(self::substr($k, 12, 4));
45 $j6 = $x6 = self::load_4(self::substr($in, 0, 4));
46 $j7 = $x7 = self::load_4(self::substr($in, 4, 4));
47 $j8 = $x8 = self::load_4(self::substr($in, 8, 4));
48 $j9 = $x9 = self::load_4(self::substr($in, 12, 4));
49 $j11 = $x11 = self::load_4(self::substr($k, 16, 4));
50 $j12 = $x12 = self::load_4(self::substr($k, 20, 4));
51 $j13 = $x13 = self::load_4(self::substr($k, 24, 4));
52 $j14 = $x14 = self::load_4(self::substr($k, 28, 4));
53
54 for ($i = self::ROUNDS; $i > 0; $i -= 2) {
55 $x4 ^= self::rotate($x0 + $x12, 7);
56 $x8 ^= self::rotate($x4 + $x0, 9);
57 $x12 ^= self::rotate($x8 + $x4, 13);
58 $x0 ^= self::rotate($x12 + $x8, 18);
59
60 $x9 ^= self::rotate($x5 + $x1, 7);
61 $x13 ^= self::rotate($x9 + $x5, 9);
62 $x1 ^= self::rotate($x13 + $x9, 13);
63 $x5 ^= self::rotate($x1 + $x13, 18);
64
65 $x14 ^= self::rotate($x10 + $x6, 7);
66 $x2 ^= self::rotate($x14 + $x10, 9);
67 $x6 ^= self::rotate($x2 + $x14, 13);
68 $x10 ^= self::rotate($x6 + $x2, 18);
69
70 $x3 ^= self::rotate($x15 + $x11, 7);
71 $x7 ^= self::rotate($x3 + $x15, 9);
72 $x11 ^= self::rotate($x7 + $x3, 13);
73 $x15 ^= self::rotate($x11 + $x7, 18);
74
75 $x1 ^= self::rotate($x0 + $x3, 7);
76 $x2 ^= self::rotate($x1 + $x0, 9);
77 $x3 ^= self::rotate($x2 + $x1, 13);
78 $x0 ^= self::rotate($x3 + $x2, 18);
79
80 $x6 ^= self::rotate($x5 + $x4, 7);
81 $x7 ^= self::rotate($x6 + $x5, 9);
82 $x4 ^= self::rotate($x7 + $x6, 13);
83 $x5 ^= self::rotate($x4 + $x7, 18);
84
85 $x11 ^= self::rotate($x10 + $x9, 7);
86 $x8 ^= self::rotate($x11 + $x10, 9);
87 $x9 ^= self::rotate($x8 + $x11, 13);
88 $x10 ^= self::rotate($x9 + $x8, 18);
89
90 $x12 ^= self::rotate($x15 + $x14, 7);
91 $x13 ^= self::rotate($x12 + $x15, 9);
92 $x14 ^= self::rotate($x13 + $x12, 13);
93 $x15 ^= self::rotate($x14 + $x13, 18);
94 }
95
96 $x0 += $j0;
97 $x1 += $j1;
98 $x2 += $j2;
99 $x3 += $j3;
100 $x4 += $j4;
101 $x5 += $j5;
102 $x6 += $j6;
103 $x7 += $j7;
104 $x8 += $j8;
105 $x9 += $j9;
106 $x10 += $j10;
107 $x11 += $j11;
108 $x12 += $j12;
109 $x13 += $j13;
110 $x14 += $j14;
111 $x15 += $j15;
112
113 return self::store32_le($x0) .
114 self::store32_le($x1) .
115 self::store32_le($x2) .
116 self::store32_le($x3) .
117 self::store32_le($x4) .
118 self::store32_le($x5) .
119 self::store32_le($x6) .
120 self::store32_le($x7) .
121 self::store32_le($x8) .
122 self::store32_le($x9) .
123 self::store32_le($x10) .
124 self::store32_le($x11) .
125 self::store32_le($x12) .
126 self::store32_le($x13) .
127 self::store32_le($x14) .
128 self::store32_le($x15);
129 }
130
131 /**
132 * @internal You should not use this directly from another application
133 *
134 * @param int $len
135 * @param string $nonce
136 * @param string $key
137 * @return string
138 * @throws SodiumException
139 * @throws TypeError
140 */
141 public static function salsa20($len, $nonce, $key)
142 {
143 if (self::strlen($key) !== 32) {
144 throw new RangeException('Key must be 32 bytes long');
145 }
146 $kcopy = '' . $key;
147 $in = self::substr($nonce, 0, 8) . str_repeat("\0", 8);
148 $c = '';
149 while ($len >= 64) {
150 $c .= self::core_salsa20($in, $kcopy, null);
151 $u = 1;
152 // Internal counter.
153 for ($i = 8; $i < 16; ++$i) {
154 $u += self::chrToInt($in[$i]);
155 $in[$i] = self::intToChr($u & 0xff);
156 $u >>= 8;
157 }
158 $len -= 64;
159 }
160 if ($len > 0) {
161 $c .= self::substr(
162 self::core_salsa20($in, $kcopy, null),
163 0,
164 $len
165 );
166 }
167 try {
168 ParagonIE_Sodium_Compat::memzero($kcopy);
169 } catch (SodiumException $ex) {
170 $kcopy = null;
171 }
172 return $c;
173 }
174
175 /**
176 * @internal You should not use this directly from another application
177 *
178 * @param string $m
179 * @param string $n
180 * @param int $ic
181 * @param string $k
182 * @return string
183 * @throws SodiumException
184 * @throws TypeError
185 */
186 public static function salsa20_xor_ic($m, $n, $ic, $k)
187 {
188 $mlen = self::strlen($m);
189 if ($mlen < 1) {
190 return '';
191 }
192 $kcopy = self::substr($k, 0, 32);
193 $in = self::substr($n, 0, 8);
194 // Initialize the counter
195 $in .= ParagonIE_Sodium_Core_Util::store64_le($ic);
196
197 $c = '';
198 while ($mlen >= 64) {
199 $block = self::core_salsa20($in, $kcopy, null);
200 $c .= self::xorStrings(
201 self::substr($m, 0, 64),
202 self::substr($block, 0, 64)
203 );
204 $u = 1;
205 for ($i = 8; $i < 16; ++$i) {
206 $u += self::chrToInt($in[$i]);
207 $in[$i] = self::intToChr($u & 0xff);
208 $u >>= 8;
209 }
210
211 $mlen -= 64;
212 $m = self::substr($m, 64);
213 }
214
215 if ($mlen) {
216 $block = self::core_salsa20($in, $kcopy, null);
217 $c .= self::xorStrings(
218 self::substr($m, 0, $mlen),
219 self::substr($block, 0, $mlen)
220 );
221 }
222 try {
223 ParagonIE_Sodium_Compat::memzero($block);
224 ParagonIE_Sodium_Compat::memzero($kcopy);
225 } catch (SodiumException $ex) {
226 $block = null;
227 $kcopy = null;
228 }
229
230 return $c;
231 }
232
233 /**
234 * @internal You should not use this directly from another application
235 *
236 * @param string $message
237 * @param string $nonce
238 * @param string $key
239 * @return string
240 * @throws SodiumException
241 * @throws TypeError
242 */
243 public static function salsa20_xor($message, $nonce, $key)
244 {
245 return self::xorStrings(
246 $message,
247 self::salsa20(
248 self::strlen($message),
249 $nonce,
250 $key
251 )
252 );
253 }
254
255 /**
256 * @internal You should not use this directly from another application
257 *
258 * @param int $u
259 * @param int $c
260 * @return int
261 */
262 public static function rotate($u, $c)
263 {
264 $u &= 0xffffffff;
265 $c %= 32;
266 return (int) (0xffffffff & (
267 ($u << $c)
268 |
269 ($u >> (32 - $c))
270 )
271 );
272 }
273}
274