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
📄Crypto32.php
1<?php
2
3if (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 */
15abstract 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 * Initialize a hashing context for BLAKE2b.
781 *
782 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
783 *
784 * @param string $key
785 * @param int $outputLength
786 * @param string $salt
787 * @param string $personal
788 * @return string
789 * @throws RangeException
790 * @throws SodiumException
791 * @throws TypeError
792 */
793 public static function generichash_init_salt_personal(
794 $key = '',
795 $outputLength = 32,
796 $salt = '',
797 $personal = ''
798 ) {
799 // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
800 ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
801
802 $k = null;
803 if (!empty($key)) {
804 $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
805 if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
806 throw new RangeException('Invalid key size');
807 }
808 }
809 if (!empty($salt)) {
810 $s = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($salt);
811 } else {
812 $s = null;
813 }
814 if (!empty($salt)) {
815 $p = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($personal);
816 } else {
817 $p = null;
818 }
819
820 /** @var SplFixedArray $ctx */
821 $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength, $s, $p);
822
823 return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx);
824 }
825
826 /**
827 * Update a hashing context for BLAKE2b with $message
828 *
829 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
830 *
831 * @param string $ctx
832 * @param string $message
833 * @return string
834 * @throws SodiumException
835 * @throws TypeError
836 */
837 public static function generichash_update($ctx, $message)
838 {
839 // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
840 ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
841
842 /** @var SplFixedArray $context */
843 $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
844
845 /** @var SplFixedArray $in */
846 $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
847
848 ParagonIE_Sodium_Core32_BLAKE2b::update($context, $in, $in->count());
849
850 return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($context);
851 }
852
853 /**
854 * Libsodium's crypto_kx().
855 *
856 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
857 *
858 * @param string $my_sk
859 * @param string $their_pk
860 * @param string $client_pk
861 * @param string $server_pk
862 * @return string
863 * @throws SodiumException
864 * @throws TypeError
865 */
866 public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
867 {
868 return self::generichash(
869 self::scalarmult($my_sk, $their_pk) .
870 $client_pk .
871 $server_pk
872 );
873 }
874
875 /**
876 * ECDH over Curve25519
877 *
878 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
879 *
880 * @param string $sKey
881 * @param string $pKey
882 * @return string
883 *
884 * @throws SodiumException
885 * @throws TypeError
886 */
887 public static function scalarmult($sKey, $pKey)
888 {
889 $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
890 self::scalarmult_throw_if_zero($q);
891 return $q;
892 }
893
894 /**
895 * ECDH over Curve25519, using the basepoint.
896 * Used to get a secret key from a public key.
897 *
898 * @param string $secret
899 * @return string
900 *
901 * @throws SodiumException
902 * @throws TypeError
903 */
904 public static function scalarmult_base($secret)
905 {
906 $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
907 self::scalarmult_throw_if_zero($q);
908 return $q;
909 }
910
911 /**
912 * This throws an Error if a zero public key was passed to the function.
913 *
914 * @param string $q
915 * @return void
916 * @throws SodiumException
917 * @throws TypeError
918 */
919 protected static function scalarmult_throw_if_zero($q)
920 {
921 $d = 0;
922 for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
923 $d |= ParagonIE_Sodium_Core32_Util::chrToInt($q[$i]);
924 }
925
926 /* branch-free variant of === 0 */
927 if (-(1 & (($d - 1) >> 8))) {
928 throw new SodiumException('Zero public key is not allowed');
929 }
930 }
931
932 /**
933 * XSalsa20-Poly1305 authenticated symmetric-key encryption.
934 *
935 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
936 *
937 * @param string $plaintext
938 * @param string $nonce
939 * @param string $key
940 * @return string
941 * @throws SodiumException
942 * @throws TypeError
943 */
944 public static function secretbox($plaintext, $nonce, $key)
945 {
946 /** @var string $subkey */
947 $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
948
949 /** @var string $block0 */
950 $block0 = str_repeat("\x00", 32);
951
952 /** @var int $mlen - Length of the plaintext message */
953 $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
954 $mlen0 = $mlen;
955 if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
956 $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
957 }
958 $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
959
960 /** @var string $block0 */
961 $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor(
962 $block0,
963 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
964 $subkey
965 );
966
967 /** @var string $c */
968 $c = ParagonIE_Sodium_Core32_Util::substr(
969 $block0,
970 self::secretbox_xsalsa20poly1305_ZEROBYTES
971 );
972 if ($mlen > $mlen0) {
973 $c .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
974 ParagonIE_Sodium_Core32_Util::substr(
975 $plaintext,
976 self::secretbox_xsalsa20poly1305_ZEROBYTES
977 ),
978 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
979 1,
980 $subkey
981 );
982 }
983 $state = new ParagonIE_Sodium_Core32_Poly1305_State(
984 ParagonIE_Sodium_Core32_Util::substr(
985 $block0,
986 0,
987 self::onetimeauth_poly1305_KEYBYTES
988 )
989 );
990 try {
991 ParagonIE_Sodium_Compat::memzero($block0);
992 ParagonIE_Sodium_Compat::memzero($subkey);
993 } catch (SodiumException $ex) {
994 $block0 = null;
995 $subkey = null;
996 }
997
998 $state->update($c);
999
1000 /** @var string $c - MAC || ciphertext */
1001 $c = $state->finish() . $c;
1002 unset($state);
1003
1004 return $c;
1005 }
1006
1007 /**
1008 * Decrypt a ciphertext generated via secretbox().
1009 *
1010 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1011 *
1012 * @param string $ciphertext
1013 * @param string $nonce
1014 * @param string $key
1015 * @return string
1016 * @throws SodiumException
1017 * @throws TypeError
1018 */
1019 public static function secretbox_open($ciphertext, $nonce, $key)
1020 {
1021 /** @var string $mac */
1022 $mac = ParagonIE_Sodium_Core32_Util::substr(
1023 $ciphertext,
1024 0,
1025 self::secretbox_xsalsa20poly1305_MACBYTES
1026 );
1027
1028 /** @var string $c */
1029 $c = ParagonIE_Sodium_Core32_Util::substr(
1030 $ciphertext,
1031 self::secretbox_xsalsa20poly1305_MACBYTES
1032 );
1033
1034 /** @var int $clen */
1035 $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
1036
1037 /** @var string $subkey */
1038 $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
1039
1040 /** @var string $block0 */
1041 $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20(
1042 64,
1043 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1044 $subkey
1045 );
1046 $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
1047 $mac,
1048 $c,
1049 ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
1050 );
1051 if (!$verified) {
1052 try {
1053 ParagonIE_Sodium_Compat::memzero($subkey);
1054 } catch (SodiumException $ex) {
1055 $subkey = null;
1056 }
1057 throw new SodiumException('Invalid MAC');
1058 }
1059
1060 /** @var string $m - Decrypted message */
1061 $m = ParagonIE_Sodium_Core32_Util::xorStrings(
1062 ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
1063 ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
1064 );
1065 if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
1066 // We had more than 1 block, so let's continue to decrypt the rest.
1067 $m .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
1068 ParagonIE_Sodium_Core32_Util::substr(
1069 $c,
1070 self::secretbox_xsalsa20poly1305_ZEROBYTES
1071 ),
1072 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1073 1,
1074 (string) $subkey
1075 );
1076 }
1077 return $m;
1078 }
1079
1080 /**
1081 * XChaCha20-Poly1305 authenticated symmetric-key encryption.
1082 *
1083 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1084 *
1085 * @param string $plaintext
1086 * @param string $nonce
1087 * @param string $key
1088 * @return string
1089 * @throws SodiumException
1090 * @throws TypeError
1091 */
1092 public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
1093 {
1094 /** @var string $subkey */
1095 $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
1096 ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
1097 $key
1098 );
1099 $nonceLast = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
1100
1101 /** @var string $block0 */
1102 $block0 = str_repeat("\x00", 32);
1103
1104 /** @var int $mlen - Length of the plaintext message */
1105 $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
1106 $mlen0 = $mlen;
1107 if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
1108 $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
1109 }
1110 $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
1111
1112 /** @var string $block0 */
1113 $block0 = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
1114 $block0,
1115 $nonceLast,
1116 $subkey
1117 );
1118
1119 /** @var string $c */
1120 $c = ParagonIE_Sodium_Core32_Util::substr(
1121 $block0,
1122 self::secretbox_xchacha20poly1305_ZEROBYTES
1123 );
1124 if ($mlen > $mlen0) {
1125 $c .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
1126 ParagonIE_Sodium_Core32_Util::substr(
1127 $plaintext,
1128 self::secretbox_xchacha20poly1305_ZEROBYTES
1129 ),
1130 $nonceLast,
1131 $subkey,
1132 ParagonIE_Sodium_Core32_Util::store64_le(1)
1133 );
1134 }
1135 $state = new ParagonIE_Sodium_Core32_Poly1305_State(
1136 ParagonIE_Sodium_Core32_Util::substr(
1137 $block0,
1138 0,
1139 self::onetimeauth_poly1305_KEYBYTES
1140 )
1141 );
1142 try {
1143 ParagonIE_Sodium_Compat::memzero($block0);
1144 ParagonIE_Sodium_Compat::memzero($subkey);
1145 } catch (SodiumException $ex) {
1146 $block0 = null;
1147 $subkey = null;
1148 }
1149
1150 $state->update($c);
1151
1152 /** @var string $c - MAC || ciphertext */
1153 $c = $state->finish() . $c;
1154 unset($state);
1155
1156 return $c;
1157 }
1158
1159 /**
1160 * Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
1161 *
1162 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1163 *
1164 * @param string $ciphertext
1165 * @param string $nonce
1166 * @param string $key
1167 * @return string
1168 * @throws SodiumException
1169 * @throws TypeError
1170 */
1171 public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
1172 {
1173 /** @var string $mac */
1174 $mac = ParagonIE_Sodium_Core32_Util::substr(
1175 $ciphertext,
1176 0,
1177 self::secretbox_xchacha20poly1305_MACBYTES
1178 );
1179
1180 /** @var string $c */
1181 $c = ParagonIE_Sodium_Core32_Util::substr(
1182 $ciphertext,
1183 self::secretbox_xchacha20poly1305_MACBYTES
1184 );
1185
1186 /** @var int $clen */
1187 $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
1188
1189 /** @var string $subkey */
1190 $subkey = ParagonIE_Sodium_Core32_HChaCha20::hchacha20($nonce, $key);
1191
1192 /** @var string $block0 */
1193 $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
1194 64,
1195 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1196 $subkey
1197 );
1198 $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
1199 $mac,
1200 $c,
1201 ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
1202 );
1203
1204 if (!$verified) {
1205 try {
1206 ParagonIE_Sodium_Compat::memzero($subkey);
1207 } catch (SodiumException $ex) {
1208 $subkey = null;
1209 }
1210 throw new SodiumException('Invalid MAC');
1211 }
1212
1213 /** @var string $m - Decrypted message */
1214 $m = ParagonIE_Sodium_Core32_Util::xorStrings(
1215 ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
1216 ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
1217 );
1218
1219 if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
1220 // We had more than 1 block, so let's continue to decrypt the rest.
1221 $m .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
1222 ParagonIE_Sodium_Core32_Util::substr(
1223 $c,
1224 self::secretbox_xchacha20poly1305_ZEROBYTES
1225 ),
1226 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1227 (string) $subkey,
1228 ParagonIE_Sodium_Core32_Util::store64_le(1)
1229 );
1230 }
1231 return $m;
1232 }
1233
1234 /**
1235 * @param string $key
1236 * @return array<int, string> Returns a state and a header.
1237 * @throws Exception
1238 * @throws SodiumException
1239 */
1240 public static function secretstream_xchacha20poly1305_init_push($key)
1241 {
1242 # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES);
1243 $out = random_bytes(24);
1244
1245 # crypto_core_hchacha20(state->k, out, k, NULL);
1246 $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20($out, $key);
1247 $state = new ParagonIE_Sodium_Core32_SecretStream_State(
1248 $subkey,
1249 ParagonIE_Sodium_Core32_Util::substr($out, 16, 8) . str_repeat("\0", 4)
1250 );
1251
1252 # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
1253 $state->counterReset();
1254
1255 # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES,
1256 # crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1257 # memset(state->_pad, 0, sizeof state->_pad);
1258 return array(
1259 $state->toString(),
1260 $out
1261 );
1262 }
1263
1264 /**
1265 * @param string $key
1266 * @param string $header
1267 * @return string Returns a state.
1268 * @throws Exception
1269 */
1270 public static function secretstream_xchacha20poly1305_init_pull($key, $header)
1271 {
1272 # crypto_core_hchacha20(state->k, in, k, NULL);
1273 $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
1274 ParagonIE_Sodium_Core32_Util::substr($header, 0, 16),
1275 $key
1276 );
1277 $state = new ParagonIE_Sodium_Core32_SecretStream_State(
1278 $subkey,
1279 ParagonIE_Sodium_Core32_Util::substr($header, 16)
1280 );
1281 $state->counterReset();
1282 # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
1283 # crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1284 # memset(state->_pad, 0, sizeof state->_pad);
1285 # return 0;
1286 return $state->toString();
1287 }
1288
1289 /**
1290 * @param string $state
1291 * @param string $msg
1292 * @param string $aad
1293 * @param int $tag
1294 * @return string
1295 * @throws SodiumException
1296 */
1297 public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
1298 {
1299 $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
1300 # crypto_onetimeauth_poly1305_state poly1305_state;
1301 # unsigned char block[64U];
1302 # unsigned char slen[8U];
1303 # unsigned char *c;
1304 # unsigned char *mac;
1305
1306 $msglen = ParagonIE_Sodium_Core32_Util::strlen($msg);
1307 $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad);
1308
1309 if ((($msglen + 63) >> 6) > 0xfffffffe) {
1310 throw new SodiumException(
1311 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
1312 );
1313 }
1314
1315 # if (outlen_p != NULL) {
1316 # *outlen_p = 0U;
1317 # }
1318 # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
1319 # sodium_misuse();
1320 # }
1321
1322 # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
1323 # crypto_onetimeauth_poly1305_init(&poly1305_state, block);
1324 # sodium_memzero(block, sizeof block);
1325 $auth = new ParagonIE_Sodium_Core32_Poly1305_State(
1326 ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
1327 );
1328
1329 # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
1330 $auth->update($aad);
1331
1332 # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
1333 # (0x10 - adlen) & 0xf);
1334 $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
1335
1336 # memset(block, 0, sizeof block);
1337 # block[0] = tag;
1338 # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
1339 # state->nonce, 1U, state->k);
1340 $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
1341 ParagonIE_Sodium_Core32_Util::intToChr($tag) . str_repeat("\0", 63),
1342 $st->getCombinedNonce(),
1343 $st->getKey(),
1344 ParagonIE_Sodium_Core32_Util::store64_le(1)
1345 );
1346
1347 # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
1348 $auth->update($block);
1349
1350 # out[0] = block[0];
1351 $out = $block[0];
1352 # c = out + (sizeof tag);
1353 # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
1354 $cipher = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
1355 $msg,
1356 $st->getCombinedNonce(),
1357 $st->getKey(),
1358 ParagonIE_Sodium_Core32_Util::store64_le(2)
1359 );
1360
1361 # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
1362 $auth->update($cipher);
1363
1364 $out .= $cipher;
1365 unset($cipher);
1366
1367 # crypto_onetimeauth_poly1305_update
1368 # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
1369 $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
1370
1371 # STORE64_LE(slen, (uint64_t) adlen);
1372 $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen);
1373
1374 # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1375 $auth->update($slen);
1376
1377 # STORE64_LE(slen, (sizeof block) + mlen);
1378 $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen);
1379
1380 # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1381 $auth->update($slen);
1382
1383 # mac = c + mlen;
1384 # crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
1385 $mac = $auth->finish();
1386 $out .= $mac;
1387
1388 # sodium_memzero(&poly1305_state, sizeof poly1305_state);
1389 unset($auth);
1390
1391
1392 # XOR_BUF(STATE_INONCE(state), mac,
1393 # crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1394 $st->xorNonce($mac);
1395
1396 # sodium_increment(STATE_COUNTER(state),
1397 # crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
1398 $st->incrementCounter();
1399 // Overwrite by reference:
1400 $state = $st->toString();
1401
1402 /** @var bool $rekey */
1403 $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
1404 # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
1405 # sodium_is_zero(STATE_COUNTER(state),
1406 # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
1407 # crypto_secretstream_xchacha20poly1305_rekey(state);
1408 # }
1409 if ($rekey || $st->needsRekey()) {
1410 // DO REKEY
1411 self::secretstream_xchacha20poly1305_rekey($state);
1412 }
1413 # if (outlen_p != NULL) {
1414 # *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
1415 # }
1416 return $out;
1417 }
1418
1419 /**
1420 * @param string $state
1421 * @param string $cipher
1422 * @param string $aad
1423 * @return bool|array{0: string, 1: int}
1424 * @throws SodiumException
1425 */
1426 public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '')
1427 {
1428 $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
1429
1430 $cipherlen = ParagonIE_Sodium_Core32_Util::strlen($cipher);
1431 # mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
1432 $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
1433 $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad);
1434
1435 # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
1436 # sodium_misuse();
1437 # }
1438 if ((($msglen + 63) >> 6) > 0xfffffffe) {
1439 throw new SodiumException(
1440 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
1441 );
1442 }
1443
1444 # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
1445 # crypto_onetimeauth_poly1305_init(&poly1305_state, block);
1446 # sodium_memzero(block, sizeof block);
1447 $auth = new ParagonIE_Sodium_Core32_Poly1305_State(
1448 ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
1449 );
1450
1451 # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
1452 $auth->update($aad);
1453
1454 # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
1455 # (0x10 - adlen) & 0xf);
1456 $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
1457
1458
1459 # memset(block, 0, sizeof block);
1460 # block[0] = in[0];
1461 # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
1462 # state->nonce, 1U, state->k);
1463 $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
1464 $cipher[0] . str_repeat("\0", 63),
1465 $st->getCombinedNonce(),
1466 $st->getKey(),
1467 ParagonIE_Sodium_Core32_Util::store64_le(1)
1468 );
1469 # tag = block[0];
1470 # block[0] = in[0];
1471 # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
1472 $tag = ParagonIE_Sodium_Core32_Util::chrToInt($block[0]);
1473 $block[0] = $cipher[0];
1474 $auth->update($block);
1475
1476
1477 # c = in + (sizeof tag);
1478 # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
1479 $auth->update(ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen));
1480
1481 # crypto_onetimeauth_poly1305_update
1482 # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
1483 $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
1484
1485 # STORE64_LE(slen, (uint64_t) adlen);
1486 # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1487 $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen);
1488 $auth->update($slen);
1489
1490 # STORE64_LE(slen, (sizeof block) + mlen);
1491 # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1492 $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen);
1493 $auth->update($slen);
1494
1495 # crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
1496 # sodium_memzero(&poly1305_state, sizeof poly1305_state);
1497 $mac = $auth->finish();
1498
1499 # stored_mac = c + mlen;
1500 # if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
1501 # sodium_memzero(mac, sizeof mac);
1502 # return -1;
1503 # }
1504
1505 $stored = ParagonIE_Sodium_Core32_Util::substr($cipher, $msglen + 1, 16);
1506 if (!ParagonIE_Sodium_Core32_Util::hashEquals($mac, $stored)) {
1507 return false;
1508 }
1509
1510 # crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
1511 $out = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
1512 ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen),
1513 $st->getCombinedNonce(),
1514 $st->getKey(),
1515 ParagonIE_Sodium_Core32_Util::store64_le(2)
1516 );
1517
1518 # XOR_BUF(STATE_INONCE(state), mac,
1519 # crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1520 $st->xorNonce($mac);
1521
1522 # sodium_increment(STATE_COUNTER(state),
1523 # crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
1524 $st->incrementCounter();
1525
1526 # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
1527 # sodium_is_zero(STATE_COUNTER(state),
1528 # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
1529 # crypto_secretstream_xchacha20poly1305_rekey(state);
1530 # }
1531
1532 // Overwrite by reference:
1533 $state = $st->toString();
1534
1535 /** @var bool $rekey */
1536 $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
1537 if ($rekey || $st->needsRekey()) {
1538 // DO REKEY
1539 self::secretstream_xchacha20poly1305_rekey($state);
1540 }
1541 return array($out, $tag);
1542 }
1543
1544 /**
1545 * @param string $state
1546 * @return void
1547 * @throws SodiumException
1548 */
1549 public static function secretstream_xchacha20poly1305_rekey(&$state)
1550 {
1551 $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
1552 # unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES +
1553 # crypto_secretstream_xchacha20poly1305_INONCEBYTES];
1554 # size_t i;
1555 # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
1556 # new_key_and_inonce[i] = state->k[i];
1557 # }
1558 $new_key_and_inonce = $st->getKey();
1559
1560 # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
1561 # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] =
1562 # STATE_INONCE(state)[i];
1563 # }
1564 $new_key_and_inonce .= ParagonIE_Sodium_Core32_Util::substR($st->getNonce(), 0, 8);
1565
1566 # crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce,
1567 # sizeof new_key_and_inonce,
1568 # state->nonce, state->k);
1569
1570 $st->rekey(ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
1571 $new_key_and_inonce,
1572 $st->getCombinedNonce(),
1573 $st->getKey(),
1574 ParagonIE_Sodium_Core32_Util::store64_le(0)
1575 ));
1576
1577 # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
1578 # state->k[i] = new_key_and_inonce[i];
1579 # }
1580 # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
1581 # STATE_INONCE(state)[i] =
1582 # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i];
1583 # }
1584 # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
1585 $st->counterReset();
1586
1587 $state = $st->toString();
1588 }
1589
1590 /**
1591 * Detached Ed25519 signature.
1592 *
1593 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1594 *
1595 * @param string $message
1596 * @param string $sk
1597 * @return string
1598 * @throws SodiumException
1599 * @throws TypeError
1600 */
1601 public static function sign_detached($message, $sk)
1602 {
1603 return ParagonIE_Sodium_Core32_Ed25519::sign_detached($message, $sk);
1604 }
1605
1606 /**
1607 * Attached Ed25519 signature. (Returns a signed message.)
1608 *
1609 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1610 *
1611 * @param string $message
1612 * @param string $sk
1613 * @return string
1614 * @throws SodiumException
1615 * @throws TypeError
1616 */
1617 public static function sign($message, $sk)
1618 {
1619 return ParagonIE_Sodium_Core32_Ed25519::sign($message, $sk);
1620 }
1621
1622 /**
1623 * Opens a signed message. If valid, returns the message.
1624 *
1625 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1626 *
1627 * @param string $signedMessage
1628 * @param string $pk
1629 * @return string
1630 * @throws SodiumException
1631 * @throws TypeError
1632 */
1633 public static function sign_open($signedMessage, $pk)
1634 {
1635 return ParagonIE_Sodium_Core32_Ed25519::sign_open($signedMessage, $pk);
1636 }
1637
1638 /**
1639 * Verify a detached signature of a given message and public key.
1640 *
1641 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1642 *
1643 * @param string $signature
1644 * @param string $message
1645 * @param string $pk
1646 * @return bool
1647 * @throws SodiumException
1648 * @throws TypeError
1649 */
1650 public static function sign_verify_detached($signature, $message, $pk)
1651 {
1652 return ParagonIE_Sodium_Core32_Ed25519::verify_detached($signature, $message, $pk);
1653 }
1654}
1655