|
1 <?php |
|
2 |
|
3 if (class_exists('ParagonIE_Sodium_Crypto32', false)) { |
|
4 return; |
|
5 } |
|
6 |
|
7 /** |
|
8 * Class ParagonIE_Sodium_Crypto |
|
9 * |
|
10 * ATTENTION! |
|
11 * |
|
12 * If you are using this library, you should be using |
|
13 * ParagonIE_Sodium_Compat in your code, not this class. |
|
14 */ |
|
15 abstract class ParagonIE_Sodium_Crypto32 |
|
16 { |
|
17 const aead_chacha20poly1305_KEYBYTES = 32; |
|
18 const aead_chacha20poly1305_NSECBYTES = 0; |
|
19 const aead_chacha20poly1305_NPUBBYTES = 8; |
|
20 const aead_chacha20poly1305_ABYTES = 16; |
|
21 |
|
22 const aead_chacha20poly1305_IETF_KEYBYTES = 32; |
|
23 const aead_chacha20poly1305_IETF_NSECBYTES = 0; |
|
24 const aead_chacha20poly1305_IETF_NPUBBYTES = 12; |
|
25 const aead_chacha20poly1305_IETF_ABYTES = 16; |
|
26 |
|
27 const aead_xchacha20poly1305_IETF_KEYBYTES = 32; |
|
28 const aead_xchacha20poly1305_IETF_NSECBYTES = 0; |
|
29 const aead_xchacha20poly1305_IETF_NPUBBYTES = 24; |
|
30 const aead_xchacha20poly1305_IETF_ABYTES = 16; |
|
31 |
|
32 const box_curve25519xsalsa20poly1305_SEEDBYTES = 32; |
|
33 const box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32; |
|
34 const box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32; |
|
35 const box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32; |
|
36 const box_curve25519xsalsa20poly1305_NONCEBYTES = 24; |
|
37 const box_curve25519xsalsa20poly1305_MACBYTES = 16; |
|
38 const box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16; |
|
39 const box_curve25519xsalsa20poly1305_ZEROBYTES = 32; |
|
40 |
|
41 const onetimeauth_poly1305_BYTES = 16; |
|
42 const onetimeauth_poly1305_KEYBYTES = 32; |
|
43 |
|
44 const secretbox_xsalsa20poly1305_KEYBYTES = 32; |
|
45 const secretbox_xsalsa20poly1305_NONCEBYTES = 24; |
|
46 const secretbox_xsalsa20poly1305_MACBYTES = 16; |
|
47 const secretbox_xsalsa20poly1305_BOXZEROBYTES = 16; |
|
48 const secretbox_xsalsa20poly1305_ZEROBYTES = 32; |
|
49 |
|
50 const secretbox_xchacha20poly1305_KEYBYTES = 32; |
|
51 const secretbox_xchacha20poly1305_NONCEBYTES = 24; |
|
52 const secretbox_xchacha20poly1305_MACBYTES = 16; |
|
53 const secretbox_xchacha20poly1305_BOXZEROBYTES = 16; |
|
54 const secretbox_xchacha20poly1305_ZEROBYTES = 32; |
|
55 |
|
56 const stream_salsa20_KEYBYTES = 32; |
|
57 |
|
58 /** |
|
59 * AEAD Decryption with ChaCha20-Poly1305 |
|
60 * |
|
61 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
62 * |
|
63 * @param string $message |
|
64 * @param string $ad |
|
65 * @param string $nonce |
|
66 * @param string $key |
|
67 * @return string |
|
68 * @throws SodiumException |
|
69 * @throws TypeError |
|
70 */ |
|
71 public static function aead_chacha20poly1305_decrypt( |
|
72 $message = '', |
|
73 $ad = '', |
|
74 $nonce = '', |
|
75 $key = '' |
|
76 ) { |
|
77 /** @var int $len - Length of message (ciphertext + MAC) */ |
|
78 $len = ParagonIE_Sodium_Core32_Util::strlen($message); |
|
79 |
|
80 /** @var int $clen - Length of ciphertext */ |
|
81 $clen = $len - self::aead_chacha20poly1305_ABYTES; |
|
82 |
|
83 /** @var int $adlen - Length of associated data */ |
|
84 $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); |
|
85 |
|
86 /** @var string $mac - Message authentication code */ |
|
87 $mac = ParagonIE_Sodium_Core32_Util::substr( |
|
88 $message, |
|
89 $clen, |
|
90 self::aead_chacha20poly1305_ABYTES |
|
91 ); |
|
92 |
|
93 /** @var string $ciphertext - The encrypted message (sans MAC) */ |
|
94 $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 0, $clen); |
|
95 |
|
96 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ |
|
97 $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream( |
|
98 32, |
|
99 $nonce, |
|
100 $key |
|
101 ); |
|
102 |
|
103 /* Recalculate the Poly1305 authentication tag (MAC): */ |
|
104 $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0); |
|
105 try { |
|
106 ParagonIE_Sodium_Compat::memzero($block0); |
|
107 } catch (SodiumException $ex) { |
|
108 $block0 = null; |
|
109 } |
|
110 $state->update($ad); |
|
111 $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen)); |
|
112 $state->update($ciphertext); |
|
113 $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen)); |
|
114 $computed_mac = $state->finish(); |
|
115 |
|
116 /* Compare the given MAC with the recalculated MAC: */ |
|
117 if (!ParagonIE_Sodium_Core32_Util::verify_16($computed_mac, $mac)) { |
|
118 throw new SodiumException('Invalid MAC'); |
|
119 } |
|
120 |
|
121 // Here, we know that the MAC is valid, so we decrypt and return the plaintext |
|
122 return ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( |
|
123 $ciphertext, |
|
124 $nonce, |
|
125 $key, |
|
126 ParagonIE_Sodium_Core32_Util::store64_le(1) |
|
127 ); |
|
128 } |
|
129 |
|
130 /** |
|
131 * AEAD Encryption with ChaCha20-Poly1305 |
|
132 * |
|
133 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
134 * |
|
135 * @param string $message |
|
136 * @param string $ad |
|
137 * @param string $nonce |
|
138 * @param string $key |
|
139 * @return string |
|
140 * @throws SodiumException |
|
141 * @throws TypeError |
|
142 */ |
|
143 public static function aead_chacha20poly1305_encrypt( |
|
144 $message = '', |
|
145 $ad = '', |
|
146 $nonce = '', |
|
147 $key = '' |
|
148 ) { |
|
149 /** @var int $len - Length of the plaintext message */ |
|
150 $len = ParagonIE_Sodium_Core32_Util::strlen($message); |
|
151 |
|
152 /** @var int $adlen - Length of the associated data */ |
|
153 $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); |
|
154 |
|
155 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ |
|
156 $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream( |
|
157 32, |
|
158 $nonce, |
|
159 $key |
|
160 ); |
|
161 $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0); |
|
162 try { |
|
163 ParagonIE_Sodium_Compat::memzero($block0); |
|
164 } catch (SodiumException $ex) { |
|
165 $block0 = null; |
|
166 } |
|
167 |
|
168 /** @var string $ciphertext - Raw encrypted data */ |
|
169 $ciphertext = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( |
|
170 $message, |
|
171 $nonce, |
|
172 $key, |
|
173 ParagonIE_Sodium_Core32_Util::store64_le(1) |
|
174 ); |
|
175 |
|
176 $state->update($ad); |
|
177 $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen)); |
|
178 $state->update($ciphertext); |
|
179 $state->update(ParagonIE_Sodium_Core32_Util::store64_le($len)); |
|
180 return $ciphertext . $state->finish(); |
|
181 } |
|
182 |
|
183 /** |
|
184 * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) |
|
185 * |
|
186 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
187 * |
|
188 * @param string $message |
|
189 * @param string $ad |
|
190 * @param string $nonce |
|
191 * @param string $key |
|
192 * @return string |
|
193 * @throws SodiumException |
|
194 * @throws TypeError |
|
195 */ |
|
196 public static function aead_chacha20poly1305_ietf_decrypt( |
|
197 $message = '', |
|
198 $ad = '', |
|
199 $nonce = '', |
|
200 $key = '' |
|
201 ) { |
|
202 /** @var int $adlen - Length of associated data */ |
|
203 $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); |
|
204 |
|
205 /** @var int $len - Length of message (ciphertext + MAC) */ |
|
206 $len = ParagonIE_Sodium_Core32_Util::strlen($message); |
|
207 |
|
208 /** @var int $clen - Length of ciphertext */ |
|
209 $clen = $len - self::aead_chacha20poly1305_IETF_ABYTES; |
|
210 |
|
211 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ |
|
212 $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream( |
|
213 32, |
|
214 $nonce, |
|
215 $key |
|
216 ); |
|
217 |
|
218 /** @var string $mac - Message authentication code */ |
|
219 $mac = ParagonIE_Sodium_Core32_Util::substr( |
|
220 $message, |
|
221 $len - self::aead_chacha20poly1305_IETF_ABYTES, |
|
222 self::aead_chacha20poly1305_IETF_ABYTES |
|
223 ); |
|
224 |
|
225 /** @var string $ciphertext - The encrypted message (sans MAC) */ |
|
226 $ciphertext = ParagonIE_Sodium_Core32_Util::substr( |
|
227 $message, |
|
228 0, |
|
229 $len - self::aead_chacha20poly1305_IETF_ABYTES |
|
230 ); |
|
231 |
|
232 /* Recalculate the Poly1305 authentication tag (MAC): */ |
|
233 $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0); |
|
234 try { |
|
235 ParagonIE_Sodium_Compat::memzero($block0); |
|
236 } catch (SodiumException $ex) { |
|
237 $block0 = null; |
|
238 } |
|
239 $state->update($ad); |
|
240 $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); |
|
241 $state->update($ciphertext); |
|
242 $state->update(str_repeat("\x00", (0x10 - $clen) & 0xf)); |
|
243 $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen)); |
|
244 $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen)); |
|
245 $computed_mac = $state->finish(); |
|
246 |
|
247 /* Compare the given MAC with the recalculated MAC: */ |
|
248 if (!ParagonIE_Sodium_Core32_Util::verify_16($computed_mac, $mac)) { |
|
249 throw new SodiumException('Invalid MAC'); |
|
250 } |
|
251 |
|
252 // Here, we know that the MAC is valid, so we decrypt and return the plaintext |
|
253 return ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( |
|
254 $ciphertext, |
|
255 $nonce, |
|
256 $key, |
|
257 ParagonIE_Sodium_Core32_Util::store64_le(1) |
|
258 ); |
|
259 } |
|
260 |
|
261 /** |
|
262 * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) |
|
263 * |
|
264 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
265 * |
|
266 * @param string $message |
|
267 * @param string $ad |
|
268 * @param string $nonce |
|
269 * @param string $key |
|
270 * @return string |
|
271 * @throws SodiumException |
|
272 * @throws TypeError |
|
273 */ |
|
274 public static function aead_chacha20poly1305_ietf_encrypt( |
|
275 $message = '', |
|
276 $ad = '', |
|
277 $nonce = '', |
|
278 $key = '' |
|
279 ) { |
|
280 /** @var int $len - Length of the plaintext message */ |
|
281 $len = ParagonIE_Sodium_Core32_Util::strlen($message); |
|
282 |
|
283 /** @var int $adlen - Length of the associated data */ |
|
284 $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); |
|
285 |
|
286 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ |
|
287 $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream( |
|
288 32, |
|
289 $nonce, |
|
290 $key |
|
291 ); |
|
292 $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0); |
|
293 try { |
|
294 ParagonIE_Sodium_Compat::memzero($block0); |
|
295 } catch (SodiumException $ex) { |
|
296 $block0 = null; |
|
297 } |
|
298 |
|
299 /** @var string $ciphertext - Raw encrypted data */ |
|
300 $ciphertext = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( |
|
301 $message, |
|
302 $nonce, |
|
303 $key, |
|
304 ParagonIE_Sodium_Core32_Util::store64_le(1) |
|
305 ); |
|
306 |
|
307 $state->update($ad); |
|
308 $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); |
|
309 $state->update($ciphertext); |
|
310 $state->update(str_repeat("\x00", ((0x10 - $len) & 0xf))); |
|
311 $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen)); |
|
312 $state->update(ParagonIE_Sodium_Core32_Util::store64_le($len)); |
|
313 return $ciphertext . $state->finish(); |
|
314 } |
|
315 |
|
316 /** |
|
317 * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) |
|
318 * |
|
319 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
320 * |
|
321 * @param string $message |
|
322 * @param string $ad |
|
323 * @param string $nonce |
|
324 * @param string $key |
|
325 * @return string |
|
326 * @throws SodiumException |
|
327 * @throws TypeError |
|
328 */ |
|
329 public static function aead_xchacha20poly1305_ietf_decrypt( |
|
330 $message = '', |
|
331 $ad = '', |
|
332 $nonce = '', |
|
333 $key = '' |
|
334 ) { |
|
335 $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20( |
|
336 ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), |
|
337 $key |
|
338 ); |
|
339 $nonceLast = "\x00\x00\x00\x00" . |
|
340 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); |
|
341 |
|
342 return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey); |
|
343 } |
|
344 |
|
345 /** |
|
346 * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) |
|
347 * |
|
348 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
349 * |
|
350 * @param string $message |
|
351 * @param string $ad |
|
352 * @param string $nonce |
|
353 * @param string $key |
|
354 * @return string |
|
355 * @throws SodiumException |
|
356 * @throws TypeError |
|
357 */ |
|
358 public static function aead_xchacha20poly1305_ietf_encrypt( |
|
359 $message = '', |
|
360 $ad = '', |
|
361 $nonce = '', |
|
362 $key = '' |
|
363 ) { |
|
364 $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20( |
|
365 ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), |
|
366 $key |
|
367 ); |
|
368 $nonceLast = "\x00\x00\x00\x00" . |
|
369 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); |
|
370 |
|
371 return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey); |
|
372 } |
|
373 |
|
374 /** |
|
375 * HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512) |
|
376 * |
|
377 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
378 * |
|
379 * @param string $message |
|
380 * @param string $key |
|
381 * @return string |
|
382 * @throws TypeError |
|
383 */ |
|
384 public static function auth($message, $key) |
|
385 { |
|
386 return ParagonIE_Sodium_Core32_Util::substr( |
|
387 hash_hmac('sha512', $message, $key, true), |
|
388 0, |
|
389 32 |
|
390 ); |
|
391 } |
|
392 |
|
393 /** |
|
394 * HMAC-SHA-512-256 validation. Constant-time via hash_equals(). |
|
395 * |
|
396 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
397 * |
|
398 * @param string $mac |
|
399 * @param string $message |
|
400 * @param string $key |
|
401 * @return bool |
|
402 * @throws SodiumException |
|
403 * @throws TypeError |
|
404 */ |
|
405 public static function auth_verify($mac, $message, $key) |
|
406 { |
|
407 return ParagonIE_Sodium_Core32_Util::hashEquals( |
|
408 $mac, |
|
409 self::auth($message, $key) |
|
410 ); |
|
411 } |
|
412 |
|
413 /** |
|
414 * X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption |
|
415 * |
|
416 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
417 * |
|
418 * @param string $plaintext |
|
419 * @param string $nonce |
|
420 * @param string $keypair |
|
421 * @return string |
|
422 * @throws SodiumException |
|
423 * @throws TypeError |
|
424 */ |
|
425 public static function box($plaintext, $nonce, $keypair) |
|
426 { |
|
427 return self::secretbox( |
|
428 $plaintext, |
|
429 $nonce, |
|
430 self::box_beforenm( |
|
431 self::box_secretkey($keypair), |
|
432 self::box_publickey($keypair) |
|
433 ) |
|
434 ); |
|
435 } |
|
436 |
|
437 /** |
|
438 * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair. |
|
439 * |
|
440 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
441 * |
|
442 * @param string $message |
|
443 * @param string $publicKey |
|
444 * @return string |
|
445 * @throws SodiumException |
|
446 * @throws TypeError |
|
447 */ |
|
448 public static function box_seal($message, $publicKey) |
|
449 { |
|
450 /** @var string $ephemeralKeypair */ |
|
451 $ephemeralKeypair = self::box_keypair(); |
|
452 |
|
453 /** @var string $ephemeralSK */ |
|
454 $ephemeralSK = self::box_secretkey($ephemeralKeypair); |
|
455 |
|
456 /** @var string $ephemeralPK */ |
|
457 $ephemeralPK = self::box_publickey($ephemeralKeypair); |
|
458 |
|
459 /** @var string $nonce */ |
|
460 $nonce = self::generichash( |
|
461 $ephemeralPK . $publicKey, |
|
462 '', |
|
463 24 |
|
464 ); |
|
465 |
|
466 /** @var string $keypair - The combined keypair used in crypto_box() */ |
|
467 $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey); |
|
468 |
|
469 /** @var string $ciphertext Ciphertext + MAC from crypto_box */ |
|
470 $ciphertext = self::box($message, $nonce, $keypair); |
|
471 try { |
|
472 ParagonIE_Sodium_Compat::memzero($ephemeralKeypair); |
|
473 ParagonIE_Sodium_Compat::memzero($ephemeralSK); |
|
474 ParagonIE_Sodium_Compat::memzero($nonce); |
|
475 } catch (SodiumException $ex) { |
|
476 $ephemeralKeypair = null; |
|
477 $ephemeralSK = null; |
|
478 $nonce = null; |
|
479 } |
|
480 return $ephemeralPK . $ciphertext; |
|
481 } |
|
482 |
|
483 /** |
|
484 * Opens a message encrypted via box_seal(). |
|
485 * |
|
486 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
487 * |
|
488 * @param string $message |
|
489 * @param string $keypair |
|
490 * @return string |
|
491 * @throws SodiumException |
|
492 * @throws TypeError |
|
493 */ |
|
494 public static function box_seal_open($message, $keypair) |
|
495 { |
|
496 /** @var string $ephemeralPK */ |
|
497 $ephemeralPK = ParagonIE_Sodium_Core32_Util::substr($message, 0, 32); |
|
498 |
|
499 /** @var string $ciphertext (ciphertext + MAC) */ |
|
500 $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 32); |
|
501 |
|
502 /** @var string $secretKey */ |
|
503 $secretKey = self::box_secretkey($keypair); |
|
504 |
|
505 /** @var string $publicKey */ |
|
506 $publicKey = self::box_publickey($keypair); |
|
507 |
|
508 /** @var string $nonce */ |
|
509 $nonce = self::generichash( |
|
510 $ephemeralPK . $publicKey, |
|
511 '', |
|
512 24 |
|
513 ); |
|
514 |
|
515 /** @var string $keypair */ |
|
516 $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK); |
|
517 |
|
518 /** @var string $m */ |
|
519 $m = self::box_open($ciphertext, $nonce, $keypair); |
|
520 try { |
|
521 ParagonIE_Sodium_Compat::memzero($secretKey); |
|
522 ParagonIE_Sodium_Compat::memzero($ephemeralPK); |
|
523 ParagonIE_Sodium_Compat::memzero($nonce); |
|
524 } catch (SodiumException $ex) { |
|
525 $secretKey = null; |
|
526 $ephemeralPK = null; |
|
527 $nonce = null; |
|
528 } |
|
529 return $m; |
|
530 } |
|
531 |
|
532 /** |
|
533 * Used by crypto_box() to get the crypto_secretbox() key. |
|
534 * |
|
535 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
536 * |
|
537 * @param string $sk |
|
538 * @param string $pk |
|
539 * @return string |
|
540 * @throws SodiumException |
|
541 * @throws TypeError |
|
542 */ |
|
543 public static function box_beforenm($sk, $pk) |
|
544 { |
|
545 return ParagonIE_Sodium_Core32_HSalsa20::hsalsa20( |
|
546 str_repeat("\x00", 16), |
|
547 self::scalarmult($sk, $pk) |
|
548 ); |
|
549 } |
|
550 |
|
551 /** |
|
552 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
553 * |
|
554 * @return string |
|
555 * @throws Exception |
|
556 * @throws SodiumException |
|
557 * @throws TypeError |
|
558 */ |
|
559 public static function box_keypair() |
|
560 { |
|
561 $sKey = random_bytes(32); |
|
562 $pKey = self::scalarmult_base($sKey); |
|
563 return $sKey . $pKey; |
|
564 } |
|
565 |
|
566 /** |
|
567 * @param string $seed |
|
568 * @return string |
|
569 * @throws SodiumException |
|
570 * @throws TypeError |
|
571 */ |
|
572 public static function box_seed_keypair($seed) |
|
573 { |
|
574 $sKey = ParagonIE_Sodium_Core32_Util::substr( |
|
575 hash('sha512', $seed, true), |
|
576 0, |
|
577 32 |
|
578 ); |
|
579 $pKey = self::scalarmult_base($sKey); |
|
580 return $sKey . $pKey; |
|
581 } |
|
582 |
|
583 /** |
|
584 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
585 * |
|
586 * @param string $sKey |
|
587 * @param string $pKey |
|
588 * @return string |
|
589 * @throws TypeError |
|
590 */ |
|
591 public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey) |
|
592 { |
|
593 return ParagonIE_Sodium_Core32_Util::substr($sKey, 0, 32) . |
|
594 ParagonIE_Sodium_Core32_Util::substr($pKey, 0, 32); |
|
595 } |
|
596 |
|
597 /** |
|
598 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
599 * |
|
600 * @param string $keypair |
|
601 * @return string |
|
602 * @throws RangeException |
|
603 * @throws TypeError |
|
604 */ |
|
605 public static function box_secretkey($keypair) |
|
606 { |
|
607 if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== 64) { |
|
608 throw new RangeException( |
|
609 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.' |
|
610 ); |
|
611 } |
|
612 return ParagonIE_Sodium_Core32_Util::substr($keypair, 0, 32); |
|
613 } |
|
614 |
|
615 /** |
|
616 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
617 * |
|
618 * @param string $keypair |
|
619 * @return string |
|
620 * @throws RangeException |
|
621 * @throws TypeError |
|
622 */ |
|
623 public static function box_publickey($keypair) |
|
624 { |
|
625 if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { |
|
626 throw new RangeException( |
|
627 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.' |
|
628 ); |
|
629 } |
|
630 return ParagonIE_Sodium_Core32_Util::substr($keypair, 32, 32); |
|
631 } |
|
632 |
|
633 /** |
|
634 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
635 * |
|
636 * @param string $sKey |
|
637 * @return string |
|
638 * @throws RangeException |
|
639 * @throws SodiumException |
|
640 * @throws TypeError |
|
641 */ |
|
642 public static function box_publickey_from_secretkey($sKey) |
|
643 { |
|
644 if (ParagonIE_Sodium_Core32_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) { |
|
645 throw new RangeException( |
|
646 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.' |
|
647 ); |
|
648 } |
|
649 return self::scalarmult_base($sKey); |
|
650 } |
|
651 |
|
652 /** |
|
653 * Decrypt a message encrypted with box(). |
|
654 * |
|
655 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
656 * |
|
657 * @param string $ciphertext |
|
658 * @param string $nonce |
|
659 * @param string $keypair |
|
660 * @return string |
|
661 * @throws SodiumException |
|
662 * @throws TypeError |
|
663 */ |
|
664 public static function box_open($ciphertext, $nonce, $keypair) |
|
665 { |
|
666 return self::secretbox_open( |
|
667 $ciphertext, |
|
668 $nonce, |
|
669 self::box_beforenm( |
|
670 self::box_secretkey($keypair), |
|
671 self::box_publickey($keypair) |
|
672 ) |
|
673 ); |
|
674 } |
|
675 |
|
676 /** |
|
677 * Calculate a BLAKE2b hash. |
|
678 * |
|
679 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
680 * |
|
681 * @param string $message |
|
682 * @param string|null $key |
|
683 * @param int $outlen |
|
684 * @return string |
|
685 * @throws RangeException |
|
686 * @throws SodiumException |
|
687 * @throws TypeError |
|
688 */ |
|
689 public static function generichash($message, $key = '', $outlen = 32) |
|
690 { |
|
691 // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized |
|
692 ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); |
|
693 |
|
694 $k = null; |
|
695 if (!empty($key)) { |
|
696 /** @var SplFixedArray $k */ |
|
697 $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key); |
|
698 if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) { |
|
699 throw new RangeException('Invalid key size'); |
|
700 } |
|
701 } |
|
702 |
|
703 /** @var SplFixedArray $in */ |
|
704 $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message); |
|
705 |
|
706 /** @var SplFixedArray $ctx */ |
|
707 $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outlen); |
|
708 ParagonIE_Sodium_Core32_BLAKE2b::update($ctx, $in, $in->count()); |
|
709 |
|
710 /** @var SplFixedArray $out */ |
|
711 $out = new SplFixedArray($outlen); |
|
712 $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($ctx, $out); |
|
713 |
|
714 /** @var array<int, int> */ |
|
715 $outArray = $out->toArray(); |
|
716 return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray); |
|
717 } |
|
718 |
|
719 /** |
|
720 * Finalize a BLAKE2b hashing context, returning the hash. |
|
721 * |
|
722 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
723 * |
|
724 * @param string $ctx |
|
725 * @param int $outlen |
|
726 * @return string |
|
727 * @throws SodiumException |
|
728 * @throws TypeError |
|
729 */ |
|
730 public static function generichash_final($ctx, $outlen = 32) |
|
731 { |
|
732 if (!is_string($ctx)) { |
|
733 throw new TypeError('Context must be a string'); |
|
734 } |
|
735 $out = new SplFixedArray($outlen); |
|
736 |
|
737 /** @var SplFixedArray $context */ |
|
738 $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx); |
|
739 |
|
740 /** @var SplFixedArray $out */ |
|
741 $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($context, $out); |
|
742 |
|
743 /** @var array<int, int> */ |
|
744 $outArray = $out->toArray(); |
|
745 return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray); |
|
746 } |
|
747 |
|
748 /** |
|
749 * Initialize a hashing context for BLAKE2b. |
|
750 * |
|
751 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
752 * |
|
753 * @param string $key |
|
754 * @param int $outputLength |
|
755 * @return string |
|
756 * @throws RangeException |
|
757 * @throws SodiumException |
|
758 * @throws TypeError |
|
759 */ |
|
760 public static function generichash_init($key = '', $outputLength = 32) |
|
761 { |
|
762 // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized |
|
763 ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); |
|
764 |
|
765 $k = null; |
|
766 if (!empty($key)) { |
|
767 $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key); |
|
768 if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) { |
|
769 throw new RangeException('Invalid key size'); |
|
770 } |
|
771 } |
|
772 |
|
773 /** @var SplFixedArray $ctx */ |
|
774 $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength); |
|
775 |
|
776 return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx); |
|
777 } |
|
778 |
|
779 /** |
|
780 * Update a hashing context for BLAKE2b with $message |
|
781 * |
|
782 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
783 * |
|
784 * @param string $ctx |
|
785 * @param string $message |
|
786 * @return string |
|
787 * @throws SodiumException |
|
788 * @throws TypeError |
|
789 */ |
|
790 public static function generichash_update($ctx, $message) |
|
791 { |
|
792 // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized |
|
793 ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); |
|
794 |
|
795 /** @var SplFixedArray $context */ |
|
796 $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx); |
|
797 |
|
798 /** @var SplFixedArray $in */ |
|
799 $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message); |
|
800 |
|
801 ParagonIE_Sodium_Core32_BLAKE2b::update($context, $in, $in->count()); |
|
802 |
|
803 return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($context); |
|
804 } |
|
805 |
|
806 /** |
|
807 * Libsodium's crypto_kx(). |
|
808 * |
|
809 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
810 * |
|
811 * @param string $my_sk |
|
812 * @param string $their_pk |
|
813 * @param string $client_pk |
|
814 * @param string $server_pk |
|
815 * @return string |
|
816 * @throws SodiumException |
|
817 * @throws TypeError |
|
818 */ |
|
819 public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk) |
|
820 { |
|
821 return self::generichash( |
|
822 self::scalarmult($my_sk, $their_pk) . |
|
823 $client_pk . |
|
824 $server_pk |
|
825 ); |
|
826 } |
|
827 |
|
828 /** |
|
829 * ECDH over Curve25519 |
|
830 * |
|
831 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
832 * |
|
833 * @param string $sKey |
|
834 * @param string $pKey |
|
835 * @return string |
|
836 * |
|
837 * @throws SodiumException |
|
838 * @throws TypeError |
|
839 */ |
|
840 public static function scalarmult($sKey, $pKey) |
|
841 { |
|
842 $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey); |
|
843 self::scalarmult_throw_if_zero($q); |
|
844 return $q; |
|
845 } |
|
846 |
|
847 /** |
|
848 * ECDH over Curve25519, using the basepoint. |
|
849 * Used to get a secret key from a public key. |
|
850 * |
|
851 * @param string $secret |
|
852 * @return string |
|
853 * |
|
854 * @throws SodiumException |
|
855 * @throws TypeError |
|
856 */ |
|
857 public static function scalarmult_base($secret) |
|
858 { |
|
859 $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10_base($secret); |
|
860 self::scalarmult_throw_if_zero($q); |
|
861 return $q; |
|
862 } |
|
863 |
|
864 /** |
|
865 * This throws an Error if a zero public key was passed to the function. |
|
866 * |
|
867 * @param string $q |
|
868 * @return void |
|
869 * @throws SodiumException |
|
870 * @throws TypeError |
|
871 */ |
|
872 protected static function scalarmult_throw_if_zero($q) |
|
873 { |
|
874 $d = 0; |
|
875 for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) { |
|
876 $d |= ParagonIE_Sodium_Core32_Util::chrToInt($q[$i]); |
|
877 } |
|
878 |
|
879 /* branch-free variant of === 0 */ |
|
880 if (-(1 & (($d - 1) >> 8))) { |
|
881 throw new SodiumException('Zero public key is not allowed'); |
|
882 } |
|
883 } |
|
884 |
|
885 /** |
|
886 * XSalsa20-Poly1305 authenticated symmetric-key encryption. |
|
887 * |
|
888 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
889 * |
|
890 * @param string $plaintext |
|
891 * @param string $nonce |
|
892 * @param string $key |
|
893 * @return string |
|
894 * @throws SodiumException |
|
895 * @throws TypeError |
|
896 */ |
|
897 public static function secretbox($plaintext, $nonce, $key) |
|
898 { |
|
899 /** @var string $subkey */ |
|
900 $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); |
|
901 |
|
902 /** @var string $block0 */ |
|
903 $block0 = str_repeat("\x00", 32); |
|
904 |
|
905 /** @var int $mlen - Length of the plaintext message */ |
|
906 $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext); |
|
907 $mlen0 = $mlen; |
|
908 if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) { |
|
909 $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES; |
|
910 } |
|
911 $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0); |
|
912 |
|
913 /** @var string $block0 */ |
|
914 $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor( |
|
915 $block0, |
|
916 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), |
|
917 $subkey |
|
918 ); |
|
919 |
|
920 /** @var string $c */ |
|
921 $c = ParagonIE_Sodium_Core32_Util::substr( |
|
922 $block0, |
|
923 self::secretbox_xsalsa20poly1305_ZEROBYTES |
|
924 ); |
|
925 if ($mlen > $mlen0) { |
|
926 $c .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic( |
|
927 ParagonIE_Sodium_Core32_Util::substr( |
|
928 $plaintext, |
|
929 self::secretbox_xsalsa20poly1305_ZEROBYTES |
|
930 ), |
|
931 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), |
|
932 1, |
|
933 $subkey |
|
934 ); |
|
935 } |
|
936 $state = new ParagonIE_Sodium_Core32_Poly1305_State( |
|
937 ParagonIE_Sodium_Core32_Util::substr( |
|
938 $block0, |
|
939 0, |
|
940 self::onetimeauth_poly1305_KEYBYTES |
|
941 ) |
|
942 ); |
|
943 try { |
|
944 ParagonIE_Sodium_Compat::memzero($block0); |
|
945 ParagonIE_Sodium_Compat::memzero($subkey); |
|
946 } catch (SodiumException $ex) { |
|
947 $block0 = null; |
|
948 $subkey = null; |
|
949 } |
|
950 |
|
951 $state->update($c); |
|
952 |
|
953 /** @var string $c - MAC || ciphertext */ |
|
954 $c = $state->finish() . $c; |
|
955 unset($state); |
|
956 |
|
957 return $c; |
|
958 } |
|
959 |
|
960 /** |
|
961 * Decrypt a ciphertext generated via secretbox(). |
|
962 * |
|
963 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
964 * |
|
965 * @param string $ciphertext |
|
966 * @param string $nonce |
|
967 * @param string $key |
|
968 * @return string |
|
969 * @throws SodiumException |
|
970 * @throws TypeError |
|
971 */ |
|
972 public static function secretbox_open($ciphertext, $nonce, $key) |
|
973 { |
|
974 /** @var string $mac */ |
|
975 $mac = ParagonIE_Sodium_Core32_Util::substr( |
|
976 $ciphertext, |
|
977 0, |
|
978 self::secretbox_xsalsa20poly1305_MACBYTES |
|
979 ); |
|
980 |
|
981 /** @var string $c */ |
|
982 $c = ParagonIE_Sodium_Core32_Util::substr( |
|
983 $ciphertext, |
|
984 self::secretbox_xsalsa20poly1305_MACBYTES |
|
985 ); |
|
986 |
|
987 /** @var int $clen */ |
|
988 $clen = ParagonIE_Sodium_Core32_Util::strlen($c); |
|
989 |
|
990 /** @var string $subkey */ |
|
991 $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); |
|
992 |
|
993 /** @var string $block0 */ |
|
994 $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20( |
|
995 64, |
|
996 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), |
|
997 $subkey |
|
998 ); |
|
999 $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify( |
|
1000 $mac, |
|
1001 $c, |
|
1002 ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32) |
|
1003 ); |
|
1004 if (!$verified) { |
|
1005 try { |
|
1006 ParagonIE_Sodium_Compat::memzero($subkey); |
|
1007 } catch (SodiumException $ex) { |
|
1008 $subkey = null; |
|
1009 } |
|
1010 throw new SodiumException('Invalid MAC'); |
|
1011 } |
|
1012 |
|
1013 /** @var string $m - Decrypted message */ |
|
1014 $m = ParagonIE_Sodium_Core32_Util::xorStrings( |
|
1015 ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES), |
|
1016 ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES) |
|
1017 ); |
|
1018 if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) { |
|
1019 // We had more than 1 block, so let's continue to decrypt the rest. |
|
1020 $m .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic( |
|
1021 ParagonIE_Sodium_Core32_Util::substr( |
|
1022 $c, |
|
1023 self::secretbox_xsalsa20poly1305_ZEROBYTES |
|
1024 ), |
|
1025 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), |
|
1026 1, |
|
1027 (string) $subkey |
|
1028 ); |
|
1029 } |
|
1030 return $m; |
|
1031 } |
|
1032 |
|
1033 /** |
|
1034 * XChaCha20-Poly1305 authenticated symmetric-key encryption. |
|
1035 * |
|
1036 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
1037 * |
|
1038 * @param string $plaintext |
|
1039 * @param string $nonce |
|
1040 * @param string $key |
|
1041 * @return string |
|
1042 * @throws SodiumException |
|
1043 * @throws TypeError |
|
1044 */ |
|
1045 public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key) |
|
1046 { |
|
1047 /** @var string $subkey */ |
|
1048 $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20( |
|
1049 ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), |
|
1050 $key |
|
1051 ); |
|
1052 $nonceLast = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); |
|
1053 |
|
1054 /** @var string $block0 */ |
|
1055 $block0 = str_repeat("\x00", 32); |
|
1056 |
|
1057 /** @var int $mlen - Length of the plaintext message */ |
|
1058 $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext); |
|
1059 $mlen0 = $mlen; |
|
1060 if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) { |
|
1061 $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES; |
|
1062 } |
|
1063 $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0); |
|
1064 |
|
1065 /** @var string $block0 */ |
|
1066 $block0 = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( |
|
1067 $block0, |
|
1068 $nonceLast, |
|
1069 $subkey |
|
1070 ); |
|
1071 |
|
1072 /** @var string $c */ |
|
1073 $c = ParagonIE_Sodium_Core32_Util::substr( |
|
1074 $block0, |
|
1075 self::secretbox_xchacha20poly1305_ZEROBYTES |
|
1076 ); |
|
1077 if ($mlen > $mlen0) { |
|
1078 $c .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( |
|
1079 ParagonIE_Sodium_Core32_Util::substr( |
|
1080 $plaintext, |
|
1081 self::secretbox_xchacha20poly1305_ZEROBYTES |
|
1082 ), |
|
1083 $nonceLast, |
|
1084 $subkey, |
|
1085 ParagonIE_Sodium_Core32_Util::store64_le(1) |
|
1086 ); |
|
1087 } |
|
1088 $state = new ParagonIE_Sodium_Core32_Poly1305_State( |
|
1089 ParagonIE_Sodium_Core32_Util::substr( |
|
1090 $block0, |
|
1091 0, |
|
1092 self::onetimeauth_poly1305_KEYBYTES |
|
1093 ) |
|
1094 ); |
|
1095 try { |
|
1096 ParagonIE_Sodium_Compat::memzero($block0); |
|
1097 ParagonIE_Sodium_Compat::memzero($subkey); |
|
1098 } catch (SodiumException $ex) { |
|
1099 $block0 = null; |
|
1100 $subkey = null; |
|
1101 } |
|
1102 |
|
1103 $state->update($c); |
|
1104 |
|
1105 /** @var string $c - MAC || ciphertext */ |
|
1106 $c = $state->finish() . $c; |
|
1107 unset($state); |
|
1108 |
|
1109 return $c; |
|
1110 } |
|
1111 |
|
1112 /** |
|
1113 * Decrypt a ciphertext generated via secretbox_xchacha20poly1305(). |
|
1114 * |
|
1115 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
1116 * |
|
1117 * @param string $ciphertext |
|
1118 * @param string $nonce |
|
1119 * @param string $key |
|
1120 * @return string |
|
1121 * @throws SodiumException |
|
1122 * @throws TypeError |
|
1123 */ |
|
1124 public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key) |
|
1125 { |
|
1126 /** @var string $mac */ |
|
1127 $mac = ParagonIE_Sodium_Core32_Util::substr( |
|
1128 $ciphertext, |
|
1129 0, |
|
1130 self::secretbox_xchacha20poly1305_MACBYTES |
|
1131 ); |
|
1132 |
|
1133 /** @var string $c */ |
|
1134 $c = ParagonIE_Sodium_Core32_Util::substr( |
|
1135 $ciphertext, |
|
1136 self::secretbox_xchacha20poly1305_MACBYTES |
|
1137 ); |
|
1138 |
|
1139 /** @var int $clen */ |
|
1140 $clen = ParagonIE_Sodium_Core32_Util::strlen($c); |
|
1141 |
|
1142 /** @var string $subkey */ |
|
1143 $subkey = ParagonIE_Sodium_Core32_HChaCha20::hchacha20($nonce, $key); |
|
1144 |
|
1145 /** @var string $block0 */ |
|
1146 $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream( |
|
1147 64, |
|
1148 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), |
|
1149 $subkey |
|
1150 ); |
|
1151 $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify( |
|
1152 $mac, |
|
1153 $c, |
|
1154 ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32) |
|
1155 ); |
|
1156 |
|
1157 if (!$verified) { |
|
1158 try { |
|
1159 ParagonIE_Sodium_Compat::memzero($subkey); |
|
1160 } catch (SodiumException $ex) { |
|
1161 $subkey = null; |
|
1162 } |
|
1163 throw new SodiumException('Invalid MAC'); |
|
1164 } |
|
1165 |
|
1166 /** @var string $m - Decrypted message */ |
|
1167 $m = ParagonIE_Sodium_Core32_Util::xorStrings( |
|
1168 ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES), |
|
1169 ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES) |
|
1170 ); |
|
1171 |
|
1172 if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) { |
|
1173 // We had more than 1 block, so let's continue to decrypt the rest. |
|
1174 $m .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( |
|
1175 ParagonIE_Sodium_Core32_Util::substr( |
|
1176 $c, |
|
1177 self::secretbox_xchacha20poly1305_ZEROBYTES |
|
1178 ), |
|
1179 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), |
|
1180 (string) $subkey, |
|
1181 ParagonIE_Sodium_Core32_Util::store64_le(1) |
|
1182 ); |
|
1183 } |
|
1184 return $m; |
|
1185 } |
|
1186 |
|
1187 /** |
|
1188 * Detached Ed25519 signature. |
|
1189 * |
|
1190 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
1191 * |
|
1192 * @param string $message |
|
1193 * @param string $sk |
|
1194 * @return string |
|
1195 * @throws SodiumException |
|
1196 * @throws TypeError |
|
1197 */ |
|
1198 public static function sign_detached($message, $sk) |
|
1199 { |
|
1200 return ParagonIE_Sodium_Core32_Ed25519::sign_detached($message, $sk); |
|
1201 } |
|
1202 |
|
1203 /** |
|
1204 * Attached Ed25519 signature. (Returns a signed message.) |
|
1205 * |
|
1206 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
1207 * |
|
1208 * @param string $message |
|
1209 * @param string $sk |
|
1210 * @return string |
|
1211 * @throws SodiumException |
|
1212 * @throws TypeError |
|
1213 */ |
|
1214 public static function sign($message, $sk) |
|
1215 { |
|
1216 return ParagonIE_Sodium_Core32_Ed25519::sign($message, $sk); |
|
1217 } |
|
1218 |
|
1219 /** |
|
1220 * Opens a signed message. If valid, returns the message. |
|
1221 * |
|
1222 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
1223 * |
|
1224 * @param string $signedMessage |
|
1225 * @param string $pk |
|
1226 * @return string |
|
1227 * @throws SodiumException |
|
1228 * @throws TypeError |
|
1229 */ |
|
1230 public static function sign_open($signedMessage, $pk) |
|
1231 { |
|
1232 return ParagonIE_Sodium_Core32_Ed25519::sign_open($signedMessage, $pk); |
|
1233 } |
|
1234 |
|
1235 /** |
|
1236 * Verify a detached signature of a given message and public key. |
|
1237 * |
|
1238 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
1239 * |
|
1240 * @param string $signature |
|
1241 * @param string $message |
|
1242 * @param string $pk |
|
1243 * @return bool |
|
1244 * @throws SodiumException |
|
1245 * @throws TypeError |
|
1246 */ |
|
1247 public static function sign_verify_detached($signature, $message, $pk) |
|
1248 { |
|
1249 return ParagonIE_Sodium_Core32_Ed25519::verify_detached($signature, $message, $pk); |
|
1250 } |
|
1251 } |