|
1 <?php |
|
2 |
|
3 if (class_exists('ParagonIE_Sodium_Core_Salsa20', false)) { |
|
4 return; |
|
5 } |
|
6 |
|
7 /** |
|
8 * Class ParagonIE_Sodium_Core_Salsa20 |
|
9 */ |
|
10 abstract 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 } |