run:R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
160.83 KB
2026-03-11 16:18:52
R W Run
53.68 KB
2026-03-11 16:18:52
R W Run
53.83 KB
2026-03-11 16:18:52
R W Run
53.53 KB
2026-03-11 16:18:52
R W Run
158 By
2026-03-11 16:18:52
R W Run
error_log
📄Crypto.php
1<?php
2
3if (class_exists('ParagonIE_Sodium_Crypto', 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 */
15abstract class ParagonIE_Sodium_Crypto
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_Core_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_Core_Util::strlen($ad);
85
86 /** @var string $mac - Message authentication code */
87 $mac = ParagonIE_Sodium_Core_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_Core_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_Core_ChaCha20::stream(
98 32,
99 $nonce,
100 $key
101 );
102
103 /* Recalculate the Poly1305 authentication tag (MAC): */
104 $state = new ParagonIE_Sodium_Core_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_Core_Util::store64_le($adlen));
112 $state->update($ciphertext);
113 $state->update(ParagonIE_Sodium_Core_Util::store64_le($clen));
114 $computed_mac = $state->finish();
115
116 /* Compare the given MAC with the recalculated MAC: */
117 if (!ParagonIE_Sodium_Core_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_Core_ChaCha20::streamXorIc(
123 $ciphertext,
124 $nonce,
125 $key,
126 ParagonIE_Sodium_Core_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_Core_Util::strlen($message);
151
152 /** @var int $adlen - Length of the associated data */
153 $adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
154
155 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
156 $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
157 32,
158 $nonce,
159 $key
160 );
161 $state = new ParagonIE_Sodium_Core_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_Core_ChaCha20::streamXorIc(
170 $message,
171 $nonce,
172 $key,
173 ParagonIE_Sodium_Core_Util::store64_le(1)
174 );
175
176 $state->update($ad);
177 $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
178 $state->update($ciphertext);
179 $state->update(ParagonIE_Sodium_Core_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_Core_Util::strlen($ad);
204
205 /** @var int $len - Length of message (ciphertext + MAC) */
206 $len = ParagonIE_Sodium_Core_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_Core_ChaCha20::ietfStream(
213 32,
214 $nonce,
215 $key
216 );
217
218 /** @var string $mac - Message authentication code */
219 $mac = ParagonIE_Sodium_Core_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_Core_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_Core_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_Core_Util::store64_le($adlen));
244 $state->update(ParagonIE_Sodium_Core_Util::store64_le($clen));
245 $computed_mac = $state->finish();
246
247 /* Compare the given MAC with the recalculated MAC: */
248 if (!ParagonIE_Sodium_Core_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_Core_ChaCha20::ietfStreamXorIc(
254 $ciphertext,
255 $nonce,
256 $key,
257 ParagonIE_Sodium_Core_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_Core_Util::strlen($message);
282
283 /** @var int $adlen - Length of the associated data */
284 $adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
285
286 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
287 $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream(
288 32,
289 $nonce,
290 $key
291 );
292 $state = new ParagonIE_Sodium_Core_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_Core_ChaCha20::ietfStreamXorIc(
301 $message,
302 $nonce,
303 $key,
304 ParagonIE_Sodium_Core_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_Core_Util::store64_le($adlen));
312 $state->update(ParagonIE_Sodium_Core_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_Core_HChaCha20::hChaCha20(
336 ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
337 $key
338 );
339 $nonceLast = "\x00\x00\x00\x00" .
340 ParagonIE_Sodium_Core_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_Core_HChaCha20::hChaCha20(
365 ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
366 $key
367 );
368 $nonceLast = "\x00\x00\x00\x00" .
369 ParagonIE_Sodium_Core_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_Core_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_Core_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 $c = self::secretbox(
428 $plaintext,
429 $nonce,
430 self::box_beforenm(
431 self::box_secretkey($keypair),
432 self::box_publickey($keypair)
433 )
434 );
435 return $c;
436 }
437
438 /**
439 * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair.
440 *
441 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
442 *
443 * @param string $message
444 * @param string $publicKey
445 * @return string
446 * @throws SodiumException
447 * @throws TypeError
448 */
449 public static function box_seal($message, $publicKey)
450 {
451 /** @var string $ephemeralKeypair */
452 $ephemeralKeypair = self::box_keypair();
453
454 /** @var string $ephemeralSK */
455 $ephemeralSK = self::box_secretkey($ephemeralKeypair);
456
457 /** @var string $ephemeralPK */
458 $ephemeralPK = self::box_publickey($ephemeralKeypair);
459
460 /** @var string $nonce */
461 $nonce = self::generichash(
462 $ephemeralPK . $publicKey,
463 '',
464 24
465 );
466
467 /** @var string $keypair - The combined keypair used in crypto_box() */
468 $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey);
469
470 /** @var string $ciphertext Ciphertext + MAC from crypto_box */
471 $ciphertext = self::box($message, $nonce, $keypair);
472 try {
473 ParagonIE_Sodium_Compat::memzero($ephemeralKeypair);
474 ParagonIE_Sodium_Compat::memzero($ephemeralSK);
475 ParagonIE_Sodium_Compat::memzero($nonce);
476 } catch (SodiumException $ex) {
477 $ephemeralKeypair = null;
478 $ephemeralSK = null;
479 $nonce = null;
480 }
481 return $ephemeralPK . $ciphertext;
482 }
483
484 /**
485 * Opens a message encrypted via box_seal().
486 *
487 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
488 *
489 * @param string $message
490 * @param string $keypair
491 * @return string
492 * @throws SodiumException
493 * @throws TypeError
494 */
495 public static function box_seal_open($message, $keypair)
496 {
497 /** @var string $ephemeralPK */
498 $ephemeralPK = ParagonIE_Sodium_Core_Util::substr($message, 0, 32);
499
500 /** @var string $ciphertext (ciphertext + MAC) */
501 $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 32);
502
503 /** @var string $secretKey */
504 $secretKey = self::box_secretkey($keypair);
505
506 /** @var string $publicKey */
507 $publicKey = self::box_publickey($keypair);
508
509 /** @var string $nonce */
510 $nonce = self::generichash(
511 $ephemeralPK . $publicKey,
512 '',
513 24
514 );
515
516 /** @var string $keypair */
517 $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK);
518
519 /** @var string $m */
520 $m = self::box_open($ciphertext, $nonce, $keypair);
521 try {
522 ParagonIE_Sodium_Compat::memzero($secretKey);
523 ParagonIE_Sodium_Compat::memzero($ephemeralPK);
524 ParagonIE_Sodium_Compat::memzero($nonce);
525 } catch (SodiumException $ex) {
526 $secretKey = null;
527 $ephemeralPK = null;
528 $nonce = null;
529 }
530 return $m;
531 }
532
533 /**
534 * Used by crypto_box() to get the crypto_secretbox() key.
535 *
536 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
537 *
538 * @param string $sk
539 * @param string $pk
540 * @return string
541 * @throws SodiumException
542 * @throws TypeError
543 */
544 public static function box_beforenm($sk, $pk)
545 {
546 return ParagonIE_Sodium_Core_HSalsa20::hsalsa20(
547 str_repeat("\x00", 16),
548 self::scalarmult($sk, $pk)
549 );
550 }
551
552 /**
553 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
554 *
555 * @return string
556 * @throws Exception
557 * @throws SodiumException
558 * @throws TypeError
559 */
560 public static function box_keypair()
561 {
562 $sKey = random_bytes(32);
563 $pKey = self::scalarmult_base($sKey);
564 return $sKey . $pKey;
565 }
566
567 /**
568 * @param string $seed
569 * @return string
570 * @throws SodiumException
571 * @throws TypeError
572 */
573 public static function box_seed_keypair($seed)
574 {
575 $sKey = ParagonIE_Sodium_Core_Util::substr(
576 hash('sha512', $seed, true),
577 0,
578 32
579 );
580 $pKey = self::scalarmult_base($sKey);
581 return $sKey . $pKey;
582 }
583
584 /**
585 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
586 *
587 * @param string $sKey
588 * @param string $pKey
589 * @return string
590 * @throws TypeError
591 */
592 public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey)
593 {
594 return ParagonIE_Sodium_Core_Util::substr($sKey, 0, 32) .
595 ParagonIE_Sodium_Core_Util::substr($pKey, 0, 32);
596 }
597
598 /**
599 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
600 *
601 * @param string $keypair
602 * @return string
603 * @throws RangeException
604 * @throws TypeError
605 */
606 public static function box_secretkey($keypair)
607 {
608 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== 64) {
609 throw new RangeException(
610 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
611 );
612 }
613 return ParagonIE_Sodium_Core_Util::substr($keypair, 0, 32);
614 }
615
616 /**
617 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
618 *
619 * @param string $keypair
620 * @return string
621 * @throws RangeException
622 * @throws TypeError
623 */
624 public static function box_publickey($keypair)
625 {
626 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
627 throw new RangeException(
628 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
629 );
630 }
631 return ParagonIE_Sodium_Core_Util::substr($keypair, 32, 32);
632 }
633
634 /**
635 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
636 *
637 * @param string $sKey
638 * @return string
639 * @throws RangeException
640 * @throws SodiumException
641 * @throws TypeError
642 */
643 public static function box_publickey_from_secretkey($sKey)
644 {
645 if (ParagonIE_Sodium_Core_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) {
646 throw new RangeException(
647 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.'
648 );
649 }
650 return self::scalarmult_base($sKey);
651 }
652
653 /**
654 * Decrypt a message encrypted with box().
655 *
656 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
657 *
658 * @param string $ciphertext
659 * @param string $nonce
660 * @param string $keypair
661 * @return string
662 * @throws SodiumException
663 * @throws TypeError
664 */
665 public static function box_open($ciphertext, $nonce, $keypair)
666 {
667 return self::secretbox_open(
668 $ciphertext,
669 $nonce,
670 self::box_beforenm(
671 self::box_secretkey($keypair),
672 self::box_publickey($keypair)
673 )
674 );
675 }
676
677 /**
678 * Calculate a BLAKE2b hash.
679 *
680 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
681 *
682 * @param string $message
683 * @param string|null $key
684 * @param int $outlen
685 * @return string
686 * @throws RangeException
687 * @throws SodiumException
688 * @throws TypeError
689 */
690 public static function generichash($message, $key = '', $outlen = 32)
691 {
692 // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
693 ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
694
695 $k = null;
696 if (!empty($key)) {
697 /** @var SplFixedArray $k */
698 $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
699 if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
700 throw new RangeException('Invalid key size');
701 }
702 }
703
704 /** @var SplFixedArray $in */
705 $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message);
706
707 /** @var SplFixedArray $ctx */
708 $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outlen);
709 ParagonIE_Sodium_Core_BLAKE2b::update($ctx, $in, $in->count());
710
711 /** @var SplFixedArray $out */
712 $out = new SplFixedArray($outlen);
713 $out = ParagonIE_Sodium_Core_BLAKE2b::finish($ctx, $out);
714
715 /** @var array<int, int> */
716 $outArray = $out->toArray();
717 return ParagonIE_Sodium_Core_Util::intArrayToString($outArray);
718 }
719
720 /**
721 * Finalize a BLAKE2b hashing context, returning the hash.
722 *
723 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
724 *
725 * @param string $ctx
726 * @param int $outlen
727 * @return string
728 * @throws SodiumException
729 * @throws TypeError
730 */
731 public static function generichash_final($ctx, $outlen = 32)
732 {
733 if (!is_string($ctx)) {
734 throw new TypeError('Context must be a string');
735 }
736 $out = new SplFixedArray($outlen);
737
738 /** @var SplFixedArray $context */
739 $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx);
740
741 /** @var SplFixedArray $out */
742 $out = ParagonIE_Sodium_Core_BLAKE2b::finish($context, $out);
743
744 /** @var array<int, int> */
745 $outArray = $out->toArray();
746 return ParagonIE_Sodium_Core_Util::intArrayToString($outArray);
747 }
748
749 /**
750 * Initialize a hashing context for BLAKE2b.
751 *
752 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
753 *
754 * @param string $key
755 * @param int $outputLength
756 * @return string
757 * @throws RangeException
758 * @throws SodiumException
759 * @throws TypeError
760 */
761 public static function generichash_init($key = '', $outputLength = 32)
762 {
763 // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
764 ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
765
766 $k = null;
767 if (!empty($key)) {
768 $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
769 if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
770 throw new RangeException('Invalid key size');
771 }
772 }
773
774 /** @var SplFixedArray $ctx */
775 $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength);
776
777 return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx);
778 }
779
780 /**
781 * Initialize a hashing context for BLAKE2b.
782 *
783 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
784 *
785 * @param string $key
786 * @param int $outputLength
787 * @param string $salt
788 * @param string $personal
789 * @return string
790 * @throws RangeException
791 * @throws SodiumException
792 * @throws TypeError
793 */
794 public static function generichash_init_salt_personal(
795 $key = '',
796 $outputLength = 32,
797 $salt = '',
798 $personal = ''
799 ) {
800 // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
801 ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
802
803 $k = null;
804 if (!empty($key)) {
805 $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
806 if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
807 throw new RangeException('Invalid key size');
808 }
809 }
810 if (!empty($salt)) {
811 $s = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($salt);
812 } else {
813 $s = null;
814 }
815 if (!empty($salt)) {
816 $p = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($personal);
817 } else {
818 $p = null;
819 }
820
821 /** @var SplFixedArray $ctx */
822 $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength, $s, $p);
823
824 return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx);
825 }
826
827 /**
828 * Update a hashing context for BLAKE2b with $message
829 *
830 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
831 *
832 * @param string $ctx
833 * @param string $message
834 * @return string
835 * @throws SodiumException
836 * @throws TypeError
837 */
838 public static function generichash_update($ctx, $message)
839 {
840 // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
841 ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
842
843 /** @var SplFixedArray $context */
844 $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx);
845
846 /** @var SplFixedArray $in */
847 $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message);
848
849 ParagonIE_Sodium_Core_BLAKE2b::update($context, $in, $in->count());
850
851 return ParagonIE_Sodium_Core_BLAKE2b::contextToString($context);
852 }
853
854 /**
855 * Libsodium's crypto_kx().
856 *
857 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
858 *
859 * @param string $my_sk
860 * @param string $their_pk
861 * @param string $client_pk
862 * @param string $server_pk
863 * @return string
864 * @throws SodiumException
865 * @throws TypeError
866 */
867 public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
868 {
869 return ParagonIE_Sodium_Compat::crypto_generichash(
870 ParagonIE_Sodium_Compat::crypto_scalarmult($my_sk, $their_pk) .
871 $client_pk .
872 $server_pk
873 );
874 }
875
876 /**
877 * ECDH over Curve25519
878 *
879 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
880 *
881 * @param string $sKey
882 * @param string $pKey
883 * @return string
884 *
885 * @throws SodiumException
886 * @throws TypeError
887 */
888 public static function scalarmult($sKey, $pKey)
889 {
890 $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
891 self::scalarmult_throw_if_zero($q);
892 return $q;
893 }
894
895 /**
896 * ECDH over Curve25519, using the basepoint.
897 * Used to get a secret key from a public key.
898 *
899 * @param string $secret
900 * @return string
901 *
902 * @throws SodiumException
903 * @throws TypeError
904 */
905 public static function scalarmult_base($secret)
906 {
907 $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
908 self::scalarmult_throw_if_zero($q);
909 return $q;
910 }
911
912 /**
913 * This throws an Error if a zero public key was passed to the function.
914 *
915 * @param string $q
916 * @return void
917 * @throws SodiumException
918 * @throws TypeError
919 */
920 protected static function scalarmult_throw_if_zero($q)
921 {
922 $d = 0;
923 for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
924 $d |= ParagonIE_Sodium_Core_Util::chrToInt($q[$i]);
925 }
926
927 /* branch-free variant of === 0 */
928 if (-(1 & (($d - 1) >> 8))) {
929 throw new SodiumException('Zero public key is not allowed');
930 }
931 }
932
933 /**
934 * XSalsa20-Poly1305 authenticated symmetric-key encryption.
935 *
936 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
937 *
938 * @param string $plaintext
939 * @param string $nonce
940 * @param string $key
941 * @return string
942 * @throws SodiumException
943 * @throws TypeError
944 */
945 public static function secretbox($plaintext, $nonce, $key)
946 {
947 /** @var string $subkey */
948 $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
949
950 /** @var string $block0 */
951 $block0 = str_repeat("\x00", 32);
952
953 /** @var int $mlen - Length of the plaintext message */
954 $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
955 $mlen0 = $mlen;
956 if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
957 $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
958 }
959 $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
960
961 /** @var string $block0 */
962 $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor(
963 $block0,
964 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
965 $subkey
966 );
967
968 /** @var string $c */
969 $c = ParagonIE_Sodium_Core_Util::substr(
970 $block0,
971 self::secretbox_xsalsa20poly1305_ZEROBYTES
972 );
973 if ($mlen > $mlen0) {
974 $c .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
975 ParagonIE_Sodium_Core_Util::substr(
976 $plaintext,
977 self::secretbox_xsalsa20poly1305_ZEROBYTES
978 ),
979 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
980 1,
981 $subkey
982 );
983 }
984 $state = new ParagonIE_Sodium_Core_Poly1305_State(
985 ParagonIE_Sodium_Core_Util::substr(
986 $block0,
987 0,
988 self::onetimeauth_poly1305_KEYBYTES
989 )
990 );
991 try {
992 ParagonIE_Sodium_Compat::memzero($block0);
993 ParagonIE_Sodium_Compat::memzero($subkey);
994 } catch (SodiumException $ex) {
995 $block0 = null;
996 $subkey = null;
997 }
998
999 $state->update($c);
1000
1001 /** @var string $c - MAC || ciphertext */
1002 $c = $state->finish() . $c;
1003 unset($state);
1004
1005 return $c;
1006 }
1007
1008 /**
1009 * Decrypt a ciphertext generated via secretbox().
1010 *
1011 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1012 *
1013 * @param string $ciphertext
1014 * @param string $nonce
1015 * @param string $key
1016 * @return string
1017 * @throws SodiumException
1018 * @throws TypeError
1019 */
1020 public static function secretbox_open($ciphertext, $nonce, $key)
1021 {
1022 /** @var string $mac */
1023 $mac = ParagonIE_Sodium_Core_Util::substr(
1024 $ciphertext,
1025 0,
1026 self::secretbox_xsalsa20poly1305_MACBYTES
1027 );
1028
1029 /** @var string $c */
1030 $c = ParagonIE_Sodium_Core_Util::substr(
1031 $ciphertext,
1032 self::secretbox_xsalsa20poly1305_MACBYTES
1033 );
1034
1035 /** @var int $clen */
1036 $clen = ParagonIE_Sodium_Core_Util::strlen($c);
1037
1038 /** @var string $subkey */
1039 $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
1040
1041 /** @var string $block0 */
1042 $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20(
1043 64,
1044 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1045 $subkey
1046 );
1047 $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
1048 $mac,
1049 $c,
1050 ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
1051 );
1052 if (!$verified) {
1053 try {
1054 ParagonIE_Sodium_Compat::memzero($subkey);
1055 } catch (SodiumException $ex) {
1056 $subkey = null;
1057 }
1058 throw new SodiumException('Invalid MAC');
1059 }
1060
1061 /** @var string $m - Decrypted message */
1062 $m = ParagonIE_Sodium_Core_Util::xorStrings(
1063 ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
1064 ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
1065 );
1066 if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
1067 // We had more than 1 block, so let's continue to decrypt the rest.
1068 $m .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
1069 ParagonIE_Sodium_Core_Util::substr(
1070 $c,
1071 self::secretbox_xsalsa20poly1305_ZEROBYTES
1072 ),
1073 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1074 1,
1075 (string) $subkey
1076 );
1077 }
1078 return $m;
1079 }
1080
1081 /**
1082 * XChaCha20-Poly1305 authenticated symmetric-key encryption.
1083 *
1084 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1085 *
1086 * @param string $plaintext
1087 * @param string $nonce
1088 * @param string $key
1089 * @return string
1090 * @throws SodiumException
1091 * @throws TypeError
1092 */
1093 public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
1094 {
1095 /** @var string $subkey */
1096 $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
1097 ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
1098 $key
1099 );
1100 $nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
1101
1102 /** @var string $block0 */
1103 $block0 = str_repeat("\x00", 32);
1104
1105 /** @var int $mlen - Length of the plaintext message */
1106 $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
1107 $mlen0 = $mlen;
1108 if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
1109 $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
1110 }
1111 $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
1112
1113 /** @var string $block0 */
1114 $block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
1115 $block0,
1116 $nonceLast,
1117 $subkey
1118 );
1119
1120 /** @var string $c */
1121 $c = ParagonIE_Sodium_Core_Util::substr(
1122 $block0,
1123 self::secretbox_xchacha20poly1305_ZEROBYTES
1124 );
1125 if ($mlen > $mlen0) {
1126 $c .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
1127 ParagonIE_Sodium_Core_Util::substr(
1128 $plaintext,
1129 self::secretbox_xchacha20poly1305_ZEROBYTES
1130 ),
1131 $nonceLast,
1132 $subkey,
1133 ParagonIE_Sodium_Core_Util::store64_le(1)
1134 );
1135 }
1136 $state = new ParagonIE_Sodium_Core_Poly1305_State(
1137 ParagonIE_Sodium_Core_Util::substr(
1138 $block0,
1139 0,
1140 self::onetimeauth_poly1305_KEYBYTES
1141 )
1142 );
1143 try {
1144 ParagonIE_Sodium_Compat::memzero($block0);
1145 ParagonIE_Sodium_Compat::memzero($subkey);
1146 } catch (SodiumException $ex) {
1147 $block0 = null;
1148 $subkey = null;
1149 }
1150
1151 $state->update($c);
1152
1153 /** @var string $c - MAC || ciphertext */
1154 $c = $state->finish() . $c;
1155 unset($state);
1156
1157 return $c;
1158 }
1159
1160 /**
1161 * Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
1162 *
1163 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1164 *
1165 * @param string $ciphertext
1166 * @param string $nonce
1167 * @param string $key
1168 * @return string
1169 * @throws SodiumException
1170 * @throws TypeError
1171 */
1172 public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
1173 {
1174 /** @var string $mac */
1175 $mac = ParagonIE_Sodium_Core_Util::substr(
1176 $ciphertext,
1177 0,
1178 self::secretbox_xchacha20poly1305_MACBYTES
1179 );
1180
1181 /** @var string $c */
1182 $c = ParagonIE_Sodium_Core_Util::substr(
1183 $ciphertext,
1184 self::secretbox_xchacha20poly1305_MACBYTES
1185 );
1186
1187 /** @var int $clen */
1188 $clen = ParagonIE_Sodium_Core_Util::strlen($c);
1189
1190 /** @var string $subkey */
1191 $subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20(
1192 ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
1193 $key
1194 );
1195
1196 /** @var string $block0 */
1197 $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
1198 64,
1199 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1200 $subkey
1201 );
1202 $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
1203 $mac,
1204 $c,
1205 ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
1206 );
1207
1208 if (!$verified) {
1209 try {
1210 ParagonIE_Sodium_Compat::memzero($subkey);
1211 } catch (SodiumException $ex) {
1212 $subkey = null;
1213 }
1214 throw new SodiumException('Invalid MAC');
1215 }
1216
1217 /** @var string $m - Decrypted message */
1218 $m = ParagonIE_Sodium_Core_Util::xorStrings(
1219 ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
1220 ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
1221 );
1222
1223 if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
1224 // We had more than 1 block, so let's continue to decrypt the rest.
1225 $m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
1226 ParagonIE_Sodium_Core_Util::substr(
1227 $c,
1228 self::secretbox_xchacha20poly1305_ZEROBYTES
1229 ),
1230 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1231 (string) $subkey,
1232 ParagonIE_Sodium_Core_Util::store64_le(1)
1233 );
1234 }
1235 return $m;
1236 }
1237
1238 /**
1239 * @param string $key
1240 * @return array<int, string> Returns a state and a header.
1241 * @throws Exception
1242 * @throws SodiumException
1243 */
1244 public static function secretstream_xchacha20poly1305_init_push($key)
1245 {
1246 # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES);
1247 $out = random_bytes(24);
1248
1249 # crypto_core_hchacha20(state->k, out, k, NULL);
1250 $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
1251 ParagonIE_Sodium_Core_Util::substr($out, 0, 16),
1252 $key
1253 );
1254 $state = new ParagonIE_Sodium_Core_SecretStream_State(
1255 $subkey,
1256 ParagonIE_Sodium_Core_Util::substr($out, 16, 8) . str_repeat("\0", 4)
1257 );
1258
1259 # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
1260 $state->counterReset();
1261
1262 # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES,
1263 # crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1264 # memset(state->_pad, 0, sizeof state->_pad);
1265 return array(
1266 $state->toString(),
1267 $out
1268 );
1269 }
1270
1271 /**
1272 * @param string $key
1273 * @param string $header
1274 * @return string Returns a state.
1275 * @throws Exception
1276 */
1277 public static function secretstream_xchacha20poly1305_init_pull($key, $header)
1278 {
1279 # crypto_core_hchacha20(state->k, in, k, NULL);
1280 $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
1281 ParagonIE_Sodium_Core_Util::substr($header, 0, 16),
1282 $key
1283 );
1284 $state = new ParagonIE_Sodium_Core_SecretStream_State(
1285 $subkey,
1286 ParagonIE_Sodium_Core_Util::substr($header, 16)
1287 );
1288 $state->counterReset();
1289 # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
1290 # crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1291 # memset(state->_pad, 0, sizeof state->_pad);
1292 # return 0;
1293 return $state->toString();
1294 }
1295
1296 /**
1297 * @param string $state
1298 * @param string $msg
1299 * @param string $aad
1300 * @param int $tag
1301 * @return string
1302 * @throws SodiumException
1303 */
1304 public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
1305 {
1306 $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
1307 # crypto_onetimeauth_poly1305_state poly1305_state;
1308 # unsigned char block[64U];
1309 # unsigned char slen[8U];
1310 # unsigned char *c;
1311 # unsigned char *mac;
1312
1313 $msglen = ParagonIE_Sodium_Core_Util::strlen($msg);
1314 $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
1315
1316 if ((($msglen + 63) >> 6) > 0xfffffffe) {
1317 throw new SodiumException(
1318 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
1319 );
1320 }
1321
1322 # if (outlen_p != NULL) {
1323 # *outlen_p = 0U;
1324 # }
1325 # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
1326 # sodium_misuse();
1327 # }
1328
1329 # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
1330 # crypto_onetimeauth_poly1305_init(&poly1305_state, block);
1331 # sodium_memzero(block, sizeof block);
1332 $auth = new ParagonIE_Sodium_Core_Poly1305_State(
1333 ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
1334 );
1335
1336 # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
1337 $auth->update($aad);
1338
1339 # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
1340 # (0x10 - adlen) & 0xf);
1341 $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
1342
1343 # memset(block, 0, sizeof block);
1344 # block[0] = tag;
1345 # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
1346 # state->nonce, 1U, state->k);
1347 $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1348 ParagonIE_Sodium_Core_Util::intToChr($tag) . str_repeat("\0", 63),
1349 $st->getCombinedNonce(),
1350 $st->getKey(),
1351 ParagonIE_Sodium_Core_Util::store64_le(1)
1352 );
1353
1354 # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
1355 $auth->update($block);
1356
1357 # out[0] = block[0];
1358 $out = $block[0];
1359 # c = out + (sizeof tag);
1360 # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
1361 $cipher = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1362 $msg,
1363 $st->getCombinedNonce(),
1364 $st->getKey(),
1365 ParagonIE_Sodium_Core_Util::store64_le(2)
1366 );
1367
1368 # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
1369 $auth->update($cipher);
1370
1371 $out .= $cipher;
1372 unset($cipher);
1373
1374 # crypto_onetimeauth_poly1305_update
1375 # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
1376 $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
1377
1378 # STORE64_LE(slen, (uint64_t) adlen);
1379 $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
1380
1381 # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1382 $auth->update($slen);
1383
1384 # STORE64_LE(slen, (sizeof block) + mlen);
1385 $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
1386
1387 # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1388 $auth->update($slen);
1389
1390 # mac = c + mlen;
1391 # crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
1392 $mac = $auth->finish();
1393 $out .= $mac;
1394
1395 # sodium_memzero(&poly1305_state, sizeof poly1305_state);
1396 unset($auth);
1397
1398
1399 # XOR_BUF(STATE_INONCE(state), mac,
1400 # crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1401 $st->xorNonce($mac);
1402
1403 # sodium_increment(STATE_COUNTER(state),
1404 # crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
1405 $st->incrementCounter();
1406 // Overwrite by reference:
1407 $state = $st->toString();
1408
1409 /** @var bool $rekey */
1410 $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
1411 # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
1412 # sodium_is_zero(STATE_COUNTER(state),
1413 # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
1414 # crypto_secretstream_xchacha20poly1305_rekey(state);
1415 # }
1416 if ($rekey || $st->needsRekey()) {
1417 // DO REKEY
1418 self::secretstream_xchacha20poly1305_rekey($state);
1419 }
1420 # if (outlen_p != NULL) {
1421 # *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
1422 # }
1423 return $out;
1424 }
1425
1426 /**
1427 * @param string $state
1428 * @param string $cipher
1429 * @param string $aad
1430 * @return bool|array{0: string, 1: int}
1431 * @throws SodiumException
1432 */
1433 public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '')
1434 {
1435 $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
1436
1437 $cipherlen = ParagonIE_Sodium_Core_Util::strlen($cipher);
1438 # mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
1439 $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
1440 $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
1441
1442 # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
1443 # sodium_misuse();
1444 # }
1445 if ((($msglen + 63) >> 6) > 0xfffffffe) {
1446 throw new SodiumException(
1447 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
1448 );
1449 }
1450
1451 # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
1452 # crypto_onetimeauth_poly1305_init(&poly1305_state, block);
1453 # sodium_memzero(block, sizeof block);
1454 $auth = new ParagonIE_Sodium_Core_Poly1305_State(
1455 ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
1456 );
1457
1458 # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
1459 $auth->update($aad);
1460
1461 # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
1462 # (0x10 - adlen) & 0xf);
1463 $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
1464
1465
1466 # memset(block, 0, sizeof block);
1467 # block[0] = in[0];
1468 # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
1469 # state->nonce, 1U, state->k);
1470 $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1471 $cipher[0] . str_repeat("\0", 63),
1472 $st->getCombinedNonce(),
1473 $st->getKey(),
1474 ParagonIE_Sodium_Core_Util::store64_le(1)
1475 );
1476 # tag = block[0];
1477 # block[0] = in[0];
1478 # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
1479 $tag = ParagonIE_Sodium_Core_Util::chrToInt($block[0]);
1480 $block[0] = $cipher[0];
1481 $auth->update($block);
1482
1483
1484 # c = in + (sizeof tag);
1485 # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
1486 $auth->update(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen));
1487
1488 # crypto_onetimeauth_poly1305_update
1489 # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
1490 $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
1491
1492 # STORE64_LE(slen, (uint64_t) adlen);
1493 # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1494 $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
1495 $auth->update($slen);
1496
1497 # STORE64_LE(slen, (sizeof block) + mlen);
1498 # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1499 $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
1500 $auth->update($slen);
1501
1502 # crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
1503 # sodium_memzero(&poly1305_state, sizeof poly1305_state);
1504 $mac = $auth->finish();
1505
1506 # stored_mac = c + mlen;
1507 # if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
1508 # sodium_memzero(mac, sizeof mac);
1509 # return -1;
1510 # }
1511
1512 $stored = ParagonIE_Sodium_Core_Util::substr($cipher, $msglen + 1, 16);
1513 if (!ParagonIE_Sodium_Core_Util::hashEquals($mac, $stored)) {
1514 return false;
1515 }
1516
1517 # crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
1518 $out = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1519 ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen),
1520 $st->getCombinedNonce(),
1521 $st->getKey(),
1522 ParagonIE_Sodium_Core_Util::store64_le(2)
1523 );
1524
1525 # XOR_BUF(STATE_INONCE(state), mac,
1526 # crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1527 $st->xorNonce($mac);
1528
1529 # sodium_increment(STATE_COUNTER(state),
1530 # crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
1531 $st->incrementCounter();
1532
1533 # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
1534 # sodium_is_zero(STATE_COUNTER(state),
1535 # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
1536 # crypto_secretstream_xchacha20poly1305_rekey(state);
1537 # }
1538
1539 // Overwrite by reference:
1540 $state = $st->toString();
1541
1542 /** @var bool $rekey */
1543 $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
1544 if ($rekey || $st->needsRekey()) {
1545 // DO REKEY
1546 self::secretstream_xchacha20poly1305_rekey($state);
1547 }
1548 return array($out, $tag);
1549 }
1550
1551 /**
1552 * @param string $state
1553 * @return void
1554 * @throws SodiumException
1555 */
1556 public static function secretstream_xchacha20poly1305_rekey(&$state)
1557 {
1558 $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
1559 # unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES +
1560 # crypto_secretstream_xchacha20poly1305_INONCEBYTES];
1561 # size_t i;
1562 # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
1563 # new_key_and_inonce[i] = state->k[i];
1564 # }
1565 $new_key_and_inonce = $st->getKey();
1566
1567 # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
1568 # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] =
1569 # STATE_INONCE(state)[i];
1570 # }
1571 $new_key_and_inonce .= ParagonIE_Sodium_Core_Util::substR($st->getNonce(), 0, 8);
1572
1573 # crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce,
1574 # sizeof new_key_and_inonce,
1575 # state->nonce, state->k);
1576
1577 $st->rekey(ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1578 $new_key_and_inonce,
1579 $st->getCombinedNonce(),
1580 $st->getKey(),
1581 ParagonIE_Sodium_Core_Util::store64_le(0)
1582 ));
1583
1584 # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
1585 # state->k[i] = new_key_and_inonce[i];
1586 # }
1587 # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
1588 # STATE_INONCE(state)[i] =
1589 # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i];
1590 # }
1591 # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
1592 $st->counterReset();
1593
1594 $state = $st->toString();
1595 }
1596
1597 /**
1598 * Detached Ed25519 signature.
1599 *
1600 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1601 *
1602 * @param string $message
1603 * @param string $sk
1604 * @return string
1605 * @throws SodiumException
1606 * @throws TypeError
1607 */
1608 public static function sign_detached($message, $sk)
1609 {
1610 return ParagonIE_Sodium_Core_Ed25519::sign_detached($message, $sk);
1611 }
1612
1613 /**
1614 * Attached Ed25519 signature. (Returns a signed message.)
1615 *
1616 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1617 *
1618 * @param string $message
1619 * @param string $sk
1620 * @return string
1621 * @throws SodiumException
1622 * @throws TypeError
1623 */
1624 public static function sign($message, $sk)
1625 {
1626 return ParagonIE_Sodium_Core_Ed25519::sign($message, $sk);
1627 }
1628
1629 /**
1630 * Opens a signed message. If valid, returns the message.
1631 *
1632 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1633 *
1634 * @param string $signedMessage
1635 * @param string $pk
1636 * @return string
1637 * @throws SodiumException
1638 * @throws TypeError
1639 */
1640 public static function sign_open($signedMessage, $pk)
1641 {
1642 return ParagonIE_Sodium_Core_Ed25519::sign_open($signedMessage, $pk);
1643 }
1644
1645 /**
1646 * Verify a detached signature of a given message and public key.
1647 *
1648 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1649 *
1650 * @param string $signature
1651 * @param string $message
1652 * @param string $pk
1653 * @return bool
1654 * @throws SodiumException
1655 * @throws TypeError
1656 */
1657 public static function sign_verify_detached($signature, $message, $pk)
1658 {
1659 return ParagonIE_Sodium_Core_Ed25519::verify_detached($signature, $message, $pk);
1660 }
1661}
1662