|
1 <?php |
|
2 |
|
3 if (class_exists('ParagonIE_Sodium_Core_ChaCha20', false)) { |
|
4 return; |
|
5 } |
|
6 |
|
7 /** |
|
8 * Class ParagonIE_Sodium_Core_ChaCha20 |
|
9 */ |
|
10 class ParagonIE_Sodium_Core_ChaCha20 extends ParagonIE_Sodium_Core_Util |
|
11 { |
|
12 /** |
|
13 * Bitwise left rotation |
|
14 * |
|
15 * @internal You should not use this directly from another application |
|
16 * |
|
17 * @param int $v |
|
18 * @param int $n |
|
19 * @return int |
|
20 */ |
|
21 public static function rotate($v, $n) |
|
22 { |
|
23 $v &= 0xffffffff; |
|
24 $n &= 31; |
|
25 return (int) ( |
|
26 0xffffffff & ( |
|
27 ($v << $n) |
|
28 | |
|
29 ($v >> (32 - $n)) |
|
30 ) |
|
31 ); |
|
32 } |
|
33 |
|
34 /** |
|
35 * The ChaCha20 quarter round function. Works on four 32-bit integers. |
|
36 * |
|
37 * @internal You should not use this directly from another application |
|
38 * |
|
39 * @param int $a |
|
40 * @param int $b |
|
41 * @param int $c |
|
42 * @param int $d |
|
43 * @return array<int, int> |
|
44 */ |
|
45 protected static function quarterRound($a, $b, $c, $d) |
|
46 { |
|
47 # a = PLUS(a,b); d = ROTATE(XOR(d,a),16); |
|
48 /** @var int $a */ |
|
49 $a = ($a + $b) & 0xffffffff; |
|
50 $d = self::rotate($d ^ $a, 16); |
|
51 |
|
52 # c = PLUS(c,d); b = ROTATE(XOR(b,c),12); |
|
53 /** @var int $c */ |
|
54 $c = ($c + $d) & 0xffffffff; |
|
55 $b = self::rotate($b ^ $c, 12); |
|
56 |
|
57 # a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); |
|
58 /** @var int $a */ |
|
59 $a = ($a + $b) & 0xffffffff; |
|
60 $d = self::rotate($d ^ $a, 8); |
|
61 |
|
62 # c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); |
|
63 /** @var int $c */ |
|
64 $c = ($c + $d) & 0xffffffff; |
|
65 $b = self::rotate($b ^ $c, 7); |
|
66 return array((int) $a, (int) $b, (int) $c, (int) $d); |
|
67 } |
|
68 |
|
69 /** |
|
70 * @internal You should not use this directly from another application |
|
71 * |
|
72 * @param ParagonIE_Sodium_Core_ChaCha20_Ctx $ctx |
|
73 * @param string $message |
|
74 * |
|
75 * @return string |
|
76 * @throws TypeError |
|
77 * @throws SodiumException |
|
78 */ |
|
79 public static function encryptBytes( |
|
80 ParagonIE_Sodium_Core_ChaCha20_Ctx $ctx, |
|
81 $message = '' |
|
82 ) { |
|
83 $bytes = self::strlen($message); |
|
84 |
|
85 /* |
|
86 j0 = ctx->input[0]; |
|
87 j1 = ctx->input[1]; |
|
88 j2 = ctx->input[2]; |
|
89 j3 = ctx->input[3]; |
|
90 j4 = ctx->input[4]; |
|
91 j5 = ctx->input[5]; |
|
92 j6 = ctx->input[6]; |
|
93 j7 = ctx->input[7]; |
|
94 j8 = ctx->input[8]; |
|
95 j9 = ctx->input[9]; |
|
96 j10 = ctx->input[10]; |
|
97 j11 = ctx->input[11]; |
|
98 j12 = ctx->input[12]; |
|
99 j13 = ctx->input[13]; |
|
100 j14 = ctx->input[14]; |
|
101 j15 = ctx->input[15]; |
|
102 */ |
|
103 $j0 = (int) $ctx[0]; |
|
104 $j1 = (int) $ctx[1]; |
|
105 $j2 = (int) $ctx[2]; |
|
106 $j3 = (int) $ctx[3]; |
|
107 $j4 = (int) $ctx[4]; |
|
108 $j5 = (int) $ctx[5]; |
|
109 $j6 = (int) $ctx[6]; |
|
110 $j7 = (int) $ctx[7]; |
|
111 $j8 = (int) $ctx[8]; |
|
112 $j9 = (int) $ctx[9]; |
|
113 $j10 = (int) $ctx[10]; |
|
114 $j11 = (int) $ctx[11]; |
|
115 $j12 = (int) $ctx[12]; |
|
116 $j13 = (int) $ctx[13]; |
|
117 $j14 = (int) $ctx[14]; |
|
118 $j15 = (int) $ctx[15]; |
|
119 |
|
120 $c = ''; |
|
121 for (;;) { |
|
122 if ($bytes < 64) { |
|
123 $message .= str_repeat("\x00", 64 - $bytes); |
|
124 } |
|
125 |
|
126 $x0 = (int) $j0; |
|
127 $x1 = (int) $j1; |
|
128 $x2 = (int) $j2; |
|
129 $x3 = (int) $j3; |
|
130 $x4 = (int) $j4; |
|
131 $x5 = (int) $j5; |
|
132 $x6 = (int) $j6; |
|
133 $x7 = (int) $j7; |
|
134 $x8 = (int) $j8; |
|
135 $x9 = (int) $j9; |
|
136 $x10 = (int) $j10; |
|
137 $x11 = (int) $j11; |
|
138 $x12 = (int) $j12; |
|
139 $x13 = (int) $j13; |
|
140 $x14 = (int) $j14; |
|
141 $x15 = (int) $j15; |
|
142 |
|
143 # for (i = 20; i > 0; i -= 2) { |
|
144 for ($i = 20; $i > 0; $i -= 2) { |
|
145 # QUARTERROUND( x0, x4, x8, x12) |
|
146 list($x0, $x4, $x8, $x12) = self::quarterRound($x0, $x4, $x8, $x12); |
|
147 |
|
148 # QUARTERROUND( x1, x5, x9, x13) |
|
149 list($x1, $x5, $x9, $x13) = self::quarterRound($x1, $x5, $x9, $x13); |
|
150 |
|
151 # QUARTERROUND( x2, x6, x10, x14) |
|
152 list($x2, $x6, $x10, $x14) = self::quarterRound($x2, $x6, $x10, $x14); |
|
153 |
|
154 # QUARTERROUND( x3, x7, x11, x15) |
|
155 list($x3, $x7, $x11, $x15) = self::quarterRound($x3, $x7, $x11, $x15); |
|
156 |
|
157 # QUARTERROUND( x0, x5, x10, x15) |
|
158 list($x0, $x5, $x10, $x15) = self::quarterRound($x0, $x5, $x10, $x15); |
|
159 |
|
160 # QUARTERROUND( x1, x6, x11, x12) |
|
161 list($x1, $x6, $x11, $x12) = self::quarterRound($x1, $x6, $x11, $x12); |
|
162 |
|
163 # QUARTERROUND( x2, x7, x8, x13) |
|
164 list($x2, $x7, $x8, $x13) = self::quarterRound($x2, $x7, $x8, $x13); |
|
165 |
|
166 # QUARTERROUND( x3, x4, x9, x14) |
|
167 list($x3, $x4, $x9, $x14) = self::quarterRound($x3, $x4, $x9, $x14); |
|
168 } |
|
169 /* |
|
170 x0 = PLUS(x0, j0); |
|
171 x1 = PLUS(x1, j1); |
|
172 x2 = PLUS(x2, j2); |
|
173 x3 = PLUS(x3, j3); |
|
174 x4 = PLUS(x4, j4); |
|
175 x5 = PLUS(x5, j5); |
|
176 x6 = PLUS(x6, j6); |
|
177 x7 = PLUS(x7, j7); |
|
178 x8 = PLUS(x8, j8); |
|
179 x9 = PLUS(x9, j9); |
|
180 x10 = PLUS(x10, j10); |
|
181 x11 = PLUS(x11, j11); |
|
182 x12 = PLUS(x12, j12); |
|
183 x13 = PLUS(x13, j13); |
|
184 x14 = PLUS(x14, j14); |
|
185 x15 = PLUS(x15, j15); |
|
186 */ |
|
187 /** @var int $x0 */ |
|
188 $x0 = ($x0 & 0xffffffff) + $j0; |
|
189 /** @var int $x1 */ |
|
190 $x1 = ($x1 & 0xffffffff) + $j1; |
|
191 /** @var int $x2 */ |
|
192 $x2 = ($x2 & 0xffffffff) + $j2; |
|
193 /** @var int $x3 */ |
|
194 $x3 = ($x3 & 0xffffffff) + $j3; |
|
195 /** @var int $x4 */ |
|
196 $x4 = ($x4 & 0xffffffff) + $j4; |
|
197 /** @var int $x5 */ |
|
198 $x5 = ($x5 & 0xffffffff) + $j5; |
|
199 /** @var int $x6 */ |
|
200 $x6 = ($x6 & 0xffffffff) + $j6; |
|
201 /** @var int $x7 */ |
|
202 $x7 = ($x7 & 0xffffffff) + $j7; |
|
203 /** @var int $x8 */ |
|
204 $x8 = ($x8 & 0xffffffff) + $j8; |
|
205 /** @var int $x9 */ |
|
206 $x9 = ($x9 & 0xffffffff) + $j9; |
|
207 /** @var int $x10 */ |
|
208 $x10 = ($x10 & 0xffffffff) + $j10; |
|
209 /** @var int $x11 */ |
|
210 $x11 = ($x11 & 0xffffffff) + $j11; |
|
211 /** @var int $x12 */ |
|
212 $x12 = ($x12 & 0xffffffff) + $j12; |
|
213 /** @var int $x13 */ |
|
214 $x13 = ($x13 & 0xffffffff) + $j13; |
|
215 /** @var int $x14 */ |
|
216 $x14 = ($x14 & 0xffffffff) + $j14; |
|
217 /** @var int $x15 */ |
|
218 $x15 = ($x15 & 0xffffffff) + $j15; |
|
219 |
|
220 /* |
|
221 x0 = XOR(x0, LOAD32_LE(m + 0)); |
|
222 x1 = XOR(x1, LOAD32_LE(m + 4)); |
|
223 x2 = XOR(x2, LOAD32_LE(m + 8)); |
|
224 x3 = XOR(x3, LOAD32_LE(m + 12)); |
|
225 x4 = XOR(x4, LOAD32_LE(m + 16)); |
|
226 x5 = XOR(x5, LOAD32_LE(m + 20)); |
|
227 x6 = XOR(x6, LOAD32_LE(m + 24)); |
|
228 x7 = XOR(x7, LOAD32_LE(m + 28)); |
|
229 x8 = XOR(x8, LOAD32_LE(m + 32)); |
|
230 x9 = XOR(x9, LOAD32_LE(m + 36)); |
|
231 x10 = XOR(x10, LOAD32_LE(m + 40)); |
|
232 x11 = XOR(x11, LOAD32_LE(m + 44)); |
|
233 x12 = XOR(x12, LOAD32_LE(m + 48)); |
|
234 x13 = XOR(x13, LOAD32_LE(m + 52)); |
|
235 x14 = XOR(x14, LOAD32_LE(m + 56)); |
|
236 x15 = XOR(x15, LOAD32_LE(m + 60)); |
|
237 */ |
|
238 $x0 ^= self::load_4(self::substr($message, 0, 4)); |
|
239 $x1 ^= self::load_4(self::substr($message, 4, 4)); |
|
240 $x2 ^= self::load_4(self::substr($message, 8, 4)); |
|
241 $x3 ^= self::load_4(self::substr($message, 12, 4)); |
|
242 $x4 ^= self::load_4(self::substr($message, 16, 4)); |
|
243 $x5 ^= self::load_4(self::substr($message, 20, 4)); |
|
244 $x6 ^= self::load_4(self::substr($message, 24, 4)); |
|
245 $x7 ^= self::load_4(self::substr($message, 28, 4)); |
|
246 $x8 ^= self::load_4(self::substr($message, 32, 4)); |
|
247 $x9 ^= self::load_4(self::substr($message, 36, 4)); |
|
248 $x10 ^= self::load_4(self::substr($message, 40, 4)); |
|
249 $x11 ^= self::load_4(self::substr($message, 44, 4)); |
|
250 $x12 ^= self::load_4(self::substr($message, 48, 4)); |
|
251 $x13 ^= self::load_4(self::substr($message, 52, 4)); |
|
252 $x14 ^= self::load_4(self::substr($message, 56, 4)); |
|
253 $x15 ^= self::load_4(self::substr($message, 60, 4)); |
|
254 |
|
255 /* |
|
256 j12 = PLUSONE(j12); |
|
257 if (!j12) { |
|
258 j13 = PLUSONE(j13); |
|
259 } |
|
260 */ |
|
261 ++$j12; |
|
262 if ($j12 & 0xf0000000) { |
|
263 throw new SodiumException('Overflow'); |
|
264 } |
|
265 |
|
266 /* |
|
267 STORE32_LE(c + 0, x0); |
|
268 STORE32_LE(c + 4, x1); |
|
269 STORE32_LE(c + 8, x2); |
|
270 STORE32_LE(c + 12, x3); |
|
271 STORE32_LE(c + 16, x4); |
|
272 STORE32_LE(c + 20, x5); |
|
273 STORE32_LE(c + 24, x6); |
|
274 STORE32_LE(c + 28, x7); |
|
275 STORE32_LE(c + 32, x8); |
|
276 STORE32_LE(c + 36, x9); |
|
277 STORE32_LE(c + 40, x10); |
|
278 STORE32_LE(c + 44, x11); |
|
279 STORE32_LE(c + 48, x12); |
|
280 STORE32_LE(c + 52, x13); |
|
281 STORE32_LE(c + 56, x14); |
|
282 STORE32_LE(c + 60, x15); |
|
283 */ |
|
284 $block = self::store32_le((int) ($x0 & 0xffffffff)) . |
|
285 self::store32_le((int) ($x1 & 0xffffffff)) . |
|
286 self::store32_le((int) ($x2 & 0xffffffff)) . |
|
287 self::store32_le((int) ($x3 & 0xffffffff)) . |
|
288 self::store32_le((int) ($x4 & 0xffffffff)) . |
|
289 self::store32_le((int) ($x5 & 0xffffffff)) . |
|
290 self::store32_le((int) ($x6 & 0xffffffff)) . |
|
291 self::store32_le((int) ($x7 & 0xffffffff)) . |
|
292 self::store32_le((int) ($x8 & 0xffffffff)) . |
|
293 self::store32_le((int) ($x9 & 0xffffffff)) . |
|
294 self::store32_le((int) ($x10 & 0xffffffff)) . |
|
295 self::store32_le((int) ($x11 & 0xffffffff)) . |
|
296 self::store32_le((int) ($x12 & 0xffffffff)) . |
|
297 self::store32_le((int) ($x13 & 0xffffffff)) . |
|
298 self::store32_le((int) ($x14 & 0xffffffff)) . |
|
299 self::store32_le((int) ($x15 & 0xffffffff)); |
|
300 |
|
301 /* Partial block */ |
|
302 if ($bytes < 64) { |
|
303 $c .= self::substr($block, 0, $bytes); |
|
304 break; |
|
305 } |
|
306 |
|
307 /* Full block */ |
|
308 $c .= $block; |
|
309 $bytes -= 64; |
|
310 if ($bytes <= 0) { |
|
311 break; |
|
312 } |
|
313 $message = self::substr($message, 64); |
|
314 } |
|
315 /* end for(;;) loop */ |
|
316 |
|
317 $ctx[12] = $j12; |
|
318 $ctx[13] = $j13; |
|
319 return $c; |
|
320 } |
|
321 |
|
322 /** |
|
323 * @internal You should not use this directly from another application |
|
324 * |
|
325 * @param int $len |
|
326 * @param string $nonce |
|
327 * @param string $key |
|
328 * @return string |
|
329 * @throws SodiumException |
|
330 * @throws TypeError |
|
331 */ |
|
332 public static function stream($len = 64, $nonce = '', $key = '') |
|
333 { |
|
334 return self::encryptBytes( |
|
335 new ParagonIE_Sodium_Core_ChaCha20_Ctx($key, $nonce), |
|
336 str_repeat("\x00", $len) |
|
337 ); |
|
338 } |
|
339 |
|
340 /** |
|
341 * @internal You should not use this directly from another application |
|
342 * |
|
343 * @param int $len |
|
344 * @param string $nonce |
|
345 * @param string $key |
|
346 * @return string |
|
347 * @throws SodiumException |
|
348 * @throws TypeError |
|
349 */ |
|
350 public static function ietfStream($len, $nonce = '', $key = '') |
|
351 { |
|
352 return self::encryptBytes( |
|
353 new ParagonIE_Sodium_Core_ChaCha20_IetfCtx($key, $nonce), |
|
354 str_repeat("\x00", $len) |
|
355 ); |
|
356 } |
|
357 |
|
358 /** |
|
359 * @internal You should not use this directly from another application |
|
360 * |
|
361 * @param string $message |
|
362 * @param string $nonce |
|
363 * @param string $key |
|
364 * @param string $ic |
|
365 * @return string |
|
366 * @throws SodiumException |
|
367 * @throws TypeError |
|
368 */ |
|
369 public static function ietfStreamXorIc($message, $nonce = '', $key = '', $ic = '') |
|
370 { |
|
371 return self::encryptBytes( |
|
372 new ParagonIE_Sodium_Core_ChaCha20_IetfCtx($key, $nonce, $ic), |
|
373 $message |
|
374 ); |
|
375 } |
|
376 |
|
377 /** |
|
378 * @internal You should not use this directly from another application |
|
379 * |
|
380 * @param string $message |
|
381 * @param string $nonce |
|
382 * @param string $key |
|
383 * @param string $ic |
|
384 * @return string |
|
385 * @throws SodiumException |
|
386 * @throws TypeError |
|
387 */ |
|
388 public static function streamXorIc($message, $nonce = '', $key = '', $ic = '') |
|
389 { |
|
390 return self::encryptBytes( |
|
391 new ParagonIE_Sodium_Core_ChaCha20_Ctx($key, $nonce, $ic), |
|
392 $message |
|
393 ); |
|
394 } |
|
395 } |