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
📄Compat.php
1<?php
2
3/**
4 * Libsodium compatibility layer
5 *
6 * This is the only class you should be interfacing with, as a user of
7 * sodium_compat.
8 *
9 * If the PHP extension for libsodium is installed, it will always use that
10 * instead of our implementations. You get better performance and stronger
11 * guarantees against side-channels that way.
12 *
13 * However, if your users don't have the PHP extension installed, we offer a
14 * compatible interface here. It will give you the correct results as if the
15 * PHP extension was installed. It won't be as fast, of course.
16 *
17 * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION *
18 * *
19 * Until audited, this is probably not safe to use! DANGER WILL ROBINSON *
20 * *
21 * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION *
22 */
23
24if (class_exists('ParagonIE_Sodium_Compat', false)) {
25 return;
26}
27
28/**
29 * @api
30 */
31class ParagonIE_Sodium_Compat
32{
33 /**
34 * This parameter prevents the use of the PECL extension.
35 * It should only be used for unit testing.
36 *
37 * @var bool
38 */
39 public static $disableFallbackForUnitTests = false;
40
41 /**
42 * Use fast multiplication rather than our constant-time multiplication
43 * implementation. Can be enabled at runtime. Only enable this if you
44 * are absolutely certain that there is no timing leak on your platform.
45 *
46 * @var bool
47 */
48 public static $fastMult = false;
49
50 const LIBRARY_MAJOR_VERSION = 9;
51 const LIBRARY_MINOR_VERSION = 1;
52 const LIBRARY_VERSION_MAJOR = 9;
53 const LIBRARY_VERSION_MINOR = 1;
54 const VERSION_STRING = 'polyfill-1.0.8';
55
56 // From libsodium
57 const BASE64_VARIANT_ORIGINAL = 1;
58 const BASE64_VARIANT_ORIGINAL_NO_PADDING = 3;
59 const BASE64_VARIANT_URLSAFE = 5;
60 const BASE64_VARIANT_URLSAFE_NO_PADDING = 7;
61 const CRYPTO_AEAD_AES256GCM_KEYBYTES = 32;
62 const CRYPTO_AEAD_AES256GCM_NSECBYTES = 0;
63 const CRYPTO_AEAD_AES256GCM_NPUBBYTES = 12;
64 const CRYPTO_AEAD_AES256GCM_ABYTES = 16;
65 const CRYPTO_AEAD_AEGIS128L_KEYBYTES = 16;
66 const CRYPTO_AEAD_AEGIS128L_NSECBYTES = 0;
67 const CRYPTO_AEAD_AEGIS128L_NPUBBYTES = 16;
68 const CRYPTO_AEAD_AEGIS128L_ABYTES = 32;
69 const CRYPTO_AEAD_AEGIS256_KEYBYTES = 32;
70 const CRYPTO_AEAD_AEGIS256_NSECBYTES = 0;
71 const CRYPTO_AEAD_AEGIS256_NPUBBYTES = 32;
72 const CRYPTO_AEAD_AEGIS256_ABYTES = 32;
73 const CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES = 32;
74 const CRYPTO_AEAD_CHACHA20POLY1305_NSECBYTES = 0;
75 const CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES = 8;
76 const CRYPTO_AEAD_CHACHA20POLY1305_ABYTES = 16;
77 const CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES = 32;
78 const CRYPTO_AEAD_CHACHA20POLY1305_IETF_NSECBYTES = 0;
79 const CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES = 12;
80 const CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES = 16;
81 const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES = 32;
82 const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NSECBYTES = 0;
83 const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES = 24;
84 const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES = 16;
85 const CRYPTO_AUTH_BYTES = 32;
86 const CRYPTO_AUTH_KEYBYTES = 32;
87 const CRYPTO_BOX_SEALBYTES = 16;
88 const CRYPTO_BOX_SECRETKEYBYTES = 32;
89 const CRYPTO_BOX_PUBLICKEYBYTES = 32;
90 const CRYPTO_BOX_KEYPAIRBYTES = 64;
91 const CRYPTO_BOX_MACBYTES = 16;
92 const CRYPTO_BOX_NONCEBYTES = 24;
93 const CRYPTO_BOX_SEEDBYTES = 32;
94 const CRYPTO_CORE_RISTRETTO255_BYTES = 32;
95 const CRYPTO_CORE_RISTRETTO255_SCALARBYTES = 32;
96 const CRYPTO_CORE_RISTRETTO255_HASHBYTES = 64;
97 const CRYPTO_CORE_RISTRETTO255_NONREDUCEDSCALARBYTES = 64;
98 const CRYPTO_KDF_BYTES_MIN = 16;
99 const CRYPTO_KDF_BYTES_MAX = 64;
100 const CRYPTO_KDF_CONTEXTBYTES = 8;
101 const CRYPTO_KDF_KEYBYTES = 32;
102 const CRYPTO_KX_BYTES = 32;
103 const CRYPTO_KX_PRIMITIVE = 'x25519blake2b';
104 const CRYPTO_KX_SEEDBYTES = 32;
105 const CRYPTO_KX_KEYPAIRBYTES = 64;
106 const CRYPTO_KX_PUBLICKEYBYTES = 32;
107 const CRYPTO_KX_SECRETKEYBYTES = 32;
108 const CRYPTO_KX_SESSIONKEYBYTES = 32;
109 const CRYPTO_GENERICHASH_BYTES = 32;
110 const CRYPTO_GENERICHASH_BYTES_MIN = 16;
111 const CRYPTO_GENERICHASH_BYTES_MAX = 64;
112 const CRYPTO_GENERICHASH_KEYBYTES = 32;
113 const CRYPTO_GENERICHASH_KEYBYTES_MIN = 16;
114 const CRYPTO_GENERICHASH_KEYBYTES_MAX = 64;
115 const CRYPTO_PWHASH_SALTBYTES = 16;
116 const CRYPTO_PWHASH_STRPREFIX = '$argon2id$';
117 const CRYPTO_PWHASH_ALG_ARGON2I13 = 1;
118 const CRYPTO_PWHASH_ALG_ARGON2ID13 = 2;
119 const CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE = 33554432;
120 const CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE = 4;
121 const CRYPTO_PWHASH_MEMLIMIT_MODERATE = 134217728;
122 const CRYPTO_PWHASH_OPSLIMIT_MODERATE = 6;
123 const CRYPTO_PWHASH_MEMLIMIT_SENSITIVE = 536870912;
124 const CRYPTO_PWHASH_OPSLIMIT_SENSITIVE = 8;
125 const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES = 32;
126 const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_STRPREFIX = '$7$';
127 const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE = 534288;
128 const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE = 16777216;
129 const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_SENSITIVE = 33554432;
130 const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_SENSITIVE = 1073741824;
131 const CRYPTO_SCALARMULT_BYTES = 32;
132 const CRYPTO_SCALARMULT_SCALARBYTES = 32;
133 const CRYPTO_SCALARMULT_RISTRETTO255_BYTES = 32;
134 const CRYPTO_SCALARMULT_RISTRETTO255_SCALARBYTES = 32;
135 const CRYPTO_SHORTHASH_BYTES = 8;
136 const CRYPTO_SHORTHASH_KEYBYTES = 16;
137 const CRYPTO_SECRETBOX_KEYBYTES = 32;
138 const CRYPTO_SECRETBOX_MACBYTES = 16;
139 const CRYPTO_SECRETBOX_NONCEBYTES = 24;
140 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES = 17;
141 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES = 24;
142 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES = 32;
143 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH = 0;
144 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PULL = 1;
145 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY = 2;
146 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL = 3;
147 const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX = 0x3fffffff80;
148 const CRYPTO_SIGN_BYTES = 64;
149 const CRYPTO_SIGN_SEEDBYTES = 32;
150 const CRYPTO_SIGN_PUBLICKEYBYTES = 32;
151 const CRYPTO_SIGN_SECRETKEYBYTES = 64;
152 const CRYPTO_SIGN_KEYPAIRBYTES = 96;
153 const CRYPTO_STREAM_KEYBYTES = 32;
154 const CRYPTO_STREAM_NONCEBYTES = 24;
155 const CRYPTO_STREAM_XCHACHA20_KEYBYTES = 32;
156 const CRYPTO_STREAM_XCHACHA20_NONCEBYTES = 24;
157
158 /**
159 * Add two numbers (little-endian unsigned), storing the value in the first
160 * parameter.
161 *
162 * This mutates $val.
163 *
164 * @param string $val
165 * @param string $addv
166 * @return void
167 * @throws SodiumException
168 */
169 public static function add(
170 #[\SensitiveParameter]
171 &$val,
172 #[\SensitiveParameter]
173 $addv
174 ) {
175 $val_len = ParagonIE_Sodium_Core_Util::strlen($val);
176 $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv);
177 if ($val_len !== $addv_len) {
178 throw new SodiumException('values must have the same length');
179 }
180 $A = ParagonIE_Sodium_Core_Util::stringToIntArray($val);
181 $B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv);
182
183 $c = 0;
184 for ($i = 0; $i < $val_len; $i++) {
185 $c += ($A[$i] + $B[$i]);
186 $A[$i] = ($c & 0xff);
187 $c >>= 8;
188 }
189 $val = ParagonIE_Sodium_Core_Util::intArrayToString($A);
190 }
191
192 /**
193 * @param string $encoded
194 * @param int $variant
195 * @param string $ignore
196 * @return string
197 * @throws SodiumException
198 */
199 public static function base642bin(
200 #[\SensitiveParameter]
201 $encoded,
202 $variant,
203 $ignore = ''
204 ) {
205 /* Type checks: */
206 ParagonIE_Sodium_Core_Util::declareScalarType($encoded, 'string', 1);
207
208 /** @var string $encoded */
209 $encoded = (string) $encoded;
210
211 // Just strip before decoding
212 if (!empty($ignore)) {
213 $encoded = str_replace($ignore, '', $encoded);
214 }
215
216 try {
217 switch ($variant) {
218 case self::BASE64_VARIANT_ORIGINAL:
219 return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, true);
220 case self::BASE64_VARIANT_ORIGINAL_NO_PADDING:
221 return ParagonIE_Sodium_Core_Base64_Original::decodeNoPadding($encoded);
222 case self::BASE64_VARIANT_URLSAFE:
223 return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, true);
224 case self::BASE64_VARIANT_URLSAFE_NO_PADDING:
225 return ParagonIE_Sodium_Core_Base64_UrlSafe::decodeNoPadding($encoded);
226 default:
227 throw new SodiumException('invalid base64 variant identifier');
228 }
229 } catch (Exception $ex) {
230 if ($ex instanceof SodiumException) {
231 throw $ex;
232 }
233 throw new SodiumException('invalid base64 string', 0, $ex);
234 }
235 }
236
237 /**
238 * @param string $decoded
239 * @param int $variant
240 * @return string
241 * @throws SodiumException
242 */
243 public static function bin2base64(
244 #[\SensitiveParameter]
245 $decoded,
246 $variant
247 ) {
248 /* Type checks: */
249 ParagonIE_Sodium_Core_Util::declareScalarType($decoded, 'string', 1);
250 /** @var string $decoded */
251 $decoded = (string) $decoded;
252 if (ParagonIE_Sodium_Core_Util::strlen($decoded) === 0) {
253 return '';
254 }
255
256 switch ($variant) {
257 case self::BASE64_VARIANT_ORIGINAL:
258 return ParagonIE_Sodium_Core_Base64_Original::encode($decoded);
259 case self::BASE64_VARIANT_ORIGINAL_NO_PADDING:
260 return ParagonIE_Sodium_Core_Base64_Original::encodeUnpadded($decoded);
261 case self::BASE64_VARIANT_URLSAFE:
262 return ParagonIE_Sodium_Core_Base64_UrlSafe::encode($decoded);
263 case self::BASE64_VARIANT_URLSAFE_NO_PADDING:
264 return ParagonIE_Sodium_Core_Base64_UrlSafe::encodeUnpadded($decoded);
265 default:
266 throw new SodiumException('invalid base64 variant identifier');
267 }
268 }
269
270 /**
271 * Cache-timing-safe implementation of bin2hex().
272 *
273 * @param string $string A string (probably raw binary)
274 * @return string A hexadecimal-encoded string
275 * @throws SodiumException
276 * @throws TypeError
277 * @psalm-suppress MixedArgument
278 */
279 public static function bin2hex(
280 #[\SensitiveParameter]
281 $string
282 ) {
283 /* Type checks: */
284 ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1);
285
286 if (self::useNewSodiumAPI()) {
287 return (string) sodium_bin2hex($string);
288 }
289 if (self::use_fallback('bin2hex')) {
290 return (string) call_user_func('\\Sodium\\bin2hex', $string);
291 }
292 return ParagonIE_Sodium_Core_Util::bin2hex($string);
293 }
294
295 /**
296 * Compare two strings, in constant-time.
297 * Compared to memcmp(), compare() is more useful for sorting.
298 *
299 * @param string $left The left operand; must be a string
300 * @param string $right The right operand; must be a string
301 * @return int If < 0 if the left operand is less than the right
302 * If = 0 if both strings are equal
303 * If > 0 if the right operand is less than the left
304 * @throws SodiumException
305 * @throws TypeError
306 * @psalm-suppress MixedArgument
307 */
308 public static function compare(
309 #[\SensitiveParameter]
310 $left,
311 #[\SensitiveParameter]
312 $right
313 ) {
314 /* Type checks: */
315 ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1);
316 ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2);
317
318 if (self::useNewSodiumAPI()) {
319 return (int) sodium_compare($left, $right);
320 }
321 if (self::use_fallback('compare')) {
322 return (int) call_user_func('\\Sodium\\compare', $left, $right);
323 }
324 return ParagonIE_Sodium_Core_Util::compare($left, $right);
325 }
326
327 /**
328 * Authenticated Encryption with Associated Data: Decryption
329 *
330 * Algorithm:
331 * AEGIS-128L
332 *
333 * @param string $ciphertext Encrypted message (with MAC appended)
334 * @param string $assocData Authenticated Associated Data (unencrypted)
335 * @param string $nonce Number to be used only Once; must be 32 bytes
336 * @param string $key Encryption key
337 *
338 * @return string The original plaintext message
339 * @throws SodiumException
340 * @throws TypeError
341 * @psalm-suppress MixedArgument
342 * @psalm-suppress MixedInferredReturnType
343 * @psalm-suppress MixedReturnStatement
344 */
345 public static function crypto_aead_aegis128l_decrypt(
346 $ciphertext = '',
347 $assocData = '',
348 $nonce = '',
349 #[\SensitiveParameter]
350 $key = ''
351 ) {
352 ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
353 ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
354 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
355 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
356
357 /* Input validation: */
358 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AEGIS128L_NPUBBYTES) {
359 throw new SodiumException('Nonce must be CRYPTO_AEAD_AEGIS128L_NPUBBYTES long');
360 }
361 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AEGIS128L_KEYBYTES) {
362 throw new SodiumException('Key must be CRYPTO_AEAD_AEGIS128L_KEYBYTES long');
363 }
364 $ct_length = ParagonIE_Sodium_Core_Util::strlen($ciphertext);
365 if ($ct_length < self::CRYPTO_AEAD_AEGIS128L_ABYTES) {
366 throw new SodiumException('Message must be at least CRYPTO_AEAD_AEGIS128L_ABYTES long');
367 }
368
369 $ct = ParagonIE_Sodium_Core_Util::substr(
370 $ciphertext,
371 0,
372 $ct_length - self::CRYPTO_AEAD_AEGIS128L_ABYTES
373 );
374 $tag = ParagonIE_Sodium_Core_Util::substr(
375 $ciphertext,
376 $ct_length - self::CRYPTO_AEAD_AEGIS128L_ABYTES,
377 self::CRYPTO_AEAD_AEGIS128L_ABYTES
378 );
379 return ParagonIE_Sodium_Core_AEGIS128L::decrypt($ct, $tag, $assocData, $key, $nonce);
380 }
381
382 /**
383 * Authenticated Encryption with Associated Data: Encryption
384 *
385 * Algorithm:
386 * AEGIS-128L
387 *
388 * @param string $plaintext Message to be encrypted
389 * @param string $assocData Authenticated Associated Data (unencrypted)
390 * @param string $nonce Number to be used only Once; must be 32 bytes
391 * @param string $key Encryption key
392 *
393 * @return string Ciphertext with 32-byte authentication tag appended
394 * @throws SodiumException
395 * @throws TypeError
396 * @psalm-suppress MixedArgument
397 */
398 public static function crypto_aead_aegis128l_encrypt(
399 #[\SensitiveParameter]
400 $plaintext = '',
401 $assocData = '',
402 $nonce = '',
403 #[\SensitiveParameter]
404 $key = ''
405 ) {
406 ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
407 ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
408 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
409 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
410
411 /* Input validation: */
412 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AEGIS128L_NPUBBYTES) {
413 throw new SodiumException('Nonce must be CRYPTO_AEAD_AEGIS128L_NPUBBYTES long');
414 }
415 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AEGIS128L_KEYBYTES) {
416 throw new SodiumException('Key must be CRYPTO_AEAD_AEGIS128L_KEYBYTES long');
417 }
418
419 list($ct, $tag) = ParagonIE_Sodium_Core_AEGIS128L::encrypt($plaintext, $assocData, $key, $nonce);
420 return $ct . $tag;
421 }
422
423 /**
424 * Return a secure random key for use with the AEGIS-128L
425 * symmetric AEAD interface.
426 *
427 * @return string
428 * @throws Exception
429 * @throws Error
430 */
431 public static function crypto_aead_aegis128l_keygen()
432 {
433 return random_bytes(self::CRYPTO_AEAD_AEGIS128L_KEYBYTES);
434 }
435
436 /**
437 * Authenticated Encryption with Associated Data: Decryption
438 *
439 * Algorithm:
440 * AEGIS-256
441 *
442 * @param string $ciphertext Encrypted message (with MAC appended)
443 * @param string $assocData Authenticated Associated Data (unencrypted)
444 * @param string $nonce Number to be used only Once; must be 32 bytes
445 * @param string $key Encryption key
446 *
447 * @return string The original plaintext message
448 * @throws SodiumException
449 * @throws TypeError
450 * @psalm-suppress MixedArgument
451 * @psalm-suppress MixedInferredReturnType
452 * @psalm-suppress MixedReturnStatement
453 */
454 public static function crypto_aead_aegis256_decrypt(
455 $ciphertext = '',
456 $assocData = '',
457 $nonce = '',
458 #[\SensitiveParameter]
459 $key = ''
460 ) {
461 ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
462 ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
463 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
464 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
465
466 /* Input validation: */
467 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AEGIS256_NPUBBYTES) {
468 throw new SodiumException('Nonce must be CRYPTO_AEAD_AEGIS256_NPUBBYTES long');
469 }
470 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AEGIS256_KEYBYTES) {
471 throw new SodiumException('Key must be CRYPTO_AEAD_AEGIS256_KEYBYTES long');
472 }
473 $ct_length = ParagonIE_Sodium_Core_Util::strlen($ciphertext);
474 if ($ct_length < self::CRYPTO_AEAD_AEGIS256_ABYTES) {
475 throw new SodiumException('Message must be at least CRYPTO_AEAD_AEGIS256_ABYTES long');
476 }
477
478 $ct = ParagonIE_Sodium_Core_Util::substr(
479 $ciphertext,
480 0,
481 $ct_length - self::CRYPTO_AEAD_AEGIS256_ABYTES
482 );
483 $tag = ParagonIE_Sodium_Core_Util::substr(
484 $ciphertext,
485 $ct_length - self::CRYPTO_AEAD_AEGIS256_ABYTES,
486 self::CRYPTO_AEAD_AEGIS256_ABYTES
487 );
488 return ParagonIE_Sodium_Core_AEGIS256::decrypt($ct, $tag, $assocData, $key, $nonce);
489 }
490
491 /**
492 * Authenticated Encryption with Associated Data: Encryption
493 *
494 * Algorithm:
495 * AEGIS-256
496 *
497 * @param string $plaintext Message to be encrypted
498 * @param string $assocData Authenticated Associated Data (unencrypted)
499 * @param string $nonce Number to be used only Once; must be 32 bytes
500 * @param string $key Encryption key
501 *
502 * @return string Ciphertext with 32-byte authentication tag appended
503 * @throws SodiumException
504 * @throws TypeError
505 * @psalm-suppress MixedArgument
506 */
507 public static function crypto_aead_aegis256_encrypt(
508 #[\SensitiveParameter]
509 $plaintext = '',
510 $assocData = '',
511 $nonce = '',
512 #[\SensitiveParameter]
513 $key = ''
514 ) {
515 ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
516 ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
517 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
518 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
519
520 /* Input validation: */
521 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AEGIS256_NPUBBYTES) {
522 throw new SodiumException('Nonce must be CRYPTO_AEAD_AEGIS256_NPUBBYTES long');
523 }
524 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AEGIS256_KEYBYTES) {
525 throw new SodiumException('Key must be CRYPTO_AEAD_AEGIS256_KEYBYTES long');
526 }
527
528 list($ct, $tag) = ParagonIE_Sodium_Core_AEGIS256::encrypt($plaintext, $assocData, $key, $nonce);
529 return $ct . $tag;
530 }
531
532 /**
533 * Return a secure random key for use with the AEGIS-256
534 * symmetric AEAD interface.
535 *
536 * @return string
537 * @throws Exception
538 * @throws Error
539 */
540 public static function crypto_aead_aegis256_keygen()
541 {
542 return random_bytes(self::CRYPTO_AEAD_AEGIS256_KEYBYTES);
543 }
544
545 /**
546 * Is AES-256-GCM even available to use?
547 *
548 * @return bool
549 * @psalm-suppress UndefinedFunction
550 * @psalm-suppress MixedInferredReturnType
551 * @psalm-suppress MixedReturnStatement
552 */
553 public static function crypto_aead_aes256gcm_is_available()
554 {
555 if (self::useNewSodiumAPI()) {
556 return sodium_crypto_aead_aes256gcm_is_available();
557 }
558 if (self::use_fallback('crypto_aead_aes256gcm_is_available')) {
559 return call_user_func('\\Sodium\\crypto_aead_aes256gcm_is_available');
560 }
561 if (PHP_VERSION_ID < 70100) {
562 // OpenSSL doesn't support AEAD before 7.1.0
563 return false;
564 }
565 if (!extension_loaded('openssl')) {
566 return false;
567 }
568 if (!is_callable('openssl_encrypt') || !is_callable('openssl_decrypt')) {
569 // OpenSSL isn't installed
570 return false;
571 }
572 return (bool) in_array('aes-256-gcm', openssl_get_cipher_methods());
573 }
574
575 /**
576 * Authenticated Encryption with Associated Data: Decryption
577 *
578 * Algorithm:
579 * AES-256-GCM
580 *
581 * This mode uses a 64-bit random nonce with a 64-bit counter.
582 * IETF mode uses a 96-bit random nonce with a 32-bit counter.
583 *
584 * @param string $ciphertext Encrypted message (with Poly1305 MAC appended)
585 * @param string $assocData Authenticated Associated Data (unencrypted)
586 * @param string $nonce Number to be used only Once; must be 8 bytes
587 * @param string $key Encryption key
588 *
589 * @return string|bool The original plaintext message
590 * @throws SodiumException
591 * @throws TypeError
592 * @psalm-suppress MixedArgument
593 * @psalm-suppress MixedInferredReturnType
594 * @psalm-suppress MixedReturnStatement
595 */
596 public static function crypto_aead_aes256gcm_decrypt(
597 $ciphertext = '',
598 $assocData = '',
599 $nonce = '',
600 #[\SensitiveParameter]
601 $key = ''
602 ) {
603 if (!self::crypto_aead_aes256gcm_is_available()) {
604 throw new SodiumException('AES-256-GCM is not available');
605 }
606 ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
607 ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
608 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
609 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
610
611 /* Input validation: */
612 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AES256GCM_NPUBBYTES) {
613 throw new SodiumException('Nonce must be CRYPTO_AEAD_AES256GCM_NPUBBYTES long');
614 }
615 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AES256GCM_KEYBYTES) {
616 throw new SodiumException('Key must be CRYPTO_AEAD_AES256GCM_KEYBYTES long');
617 }
618 if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_AES256GCM_ABYTES) {
619 throw new SodiumException('Message must be at least CRYPTO_AEAD_AES256GCM_ABYTES long');
620 }
621 if (!extension_loaded('openssl')) {
622 throw new SodiumException('The OpenSSL extension is not installed');
623 }
624 if (!is_callable('openssl_decrypt')) {
625 throw new SodiumException('The OpenSSL extension is not installed, or openssl_decrypt() is not available');
626 }
627
628 /** @var string $ctext */
629 $ctext = ParagonIE_Sodium_Core_Util::substr($ciphertext, 0, -self::CRYPTO_AEAD_AES256GCM_ABYTES);
630 /** @var string $authTag */
631 $authTag = ParagonIE_Sodium_Core_Util::substr($ciphertext, -self::CRYPTO_AEAD_AES256GCM_ABYTES, 16);
632 return openssl_decrypt(
633 $ctext,
634 'aes-256-gcm',
635 $key,
636 OPENSSL_RAW_DATA,
637 $nonce,
638 $authTag,
639 $assocData
640 );
641 }
642
643 /**
644 * Authenticated Encryption with Associated Data: Encryption
645 *
646 * Algorithm:
647 * AES-256-GCM
648 *
649 * @param string $plaintext Message to be encrypted
650 * @param string $assocData Authenticated Associated Data (unencrypted)
651 * @param string $nonce Number to be used only Once; must be 8 bytes
652 * @param string $key Encryption key
653 *
654 * @return string Ciphertext with a 16-byte GCM message
655 * authentication code appended
656 * @throws SodiumException
657 * @throws TypeError
658 * @psalm-suppress MixedArgument
659 */
660 public static function crypto_aead_aes256gcm_encrypt(
661 #[\SensitiveParameter]
662 $plaintext = '',
663 $assocData = '',
664 $nonce = '',
665 #[\SensitiveParameter]
666 $key = ''
667 ) {
668 if (!self::crypto_aead_aes256gcm_is_available()) {
669 throw new SodiumException('AES-256-GCM is not available');
670 }
671 ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
672 ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
673 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
674 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
675
676 /* Input validation: */
677 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AES256GCM_NPUBBYTES) {
678 throw new SodiumException('Nonce must be CRYPTO_AEAD_AES256GCM_NPUBBYTES long');
679 }
680 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AES256GCM_KEYBYTES) {
681 throw new SodiumException('Key must be CRYPTO_AEAD_AES256GCM_KEYBYTES long');
682 }
683
684 if (!extension_loaded('openssl')) {
685 throw new SodiumException('The OpenSSL extension is not installed');
686 }
687 if (!is_callable('openssl_encrypt')) {
688 throw new SodiumException('The OpenSSL extension is not installed, or openssl_encrypt() is not available');
689 }
690
691 $authTag = '';
692 $ciphertext = openssl_encrypt(
693 $plaintext,
694 'aes-256-gcm',
695 $key,
696 OPENSSL_RAW_DATA,
697 $nonce,
698 $authTag,
699 $assocData
700 );
701 return $ciphertext . $authTag;
702 }
703
704 /**
705 * Return a secure random key for use with the AES-256-GCM
706 * symmetric AEAD interface.
707 *
708 * @return string
709 * @throws Exception
710 * @throws Error
711 */
712 public static function crypto_aead_aes256gcm_keygen()
713 {
714 return random_bytes(self::CRYPTO_AEAD_AES256GCM_KEYBYTES);
715 }
716
717 /**
718 * Authenticated Encryption with Associated Data: Decryption
719 *
720 * Algorithm:
721 * ChaCha20-Poly1305
722 *
723 * This mode uses a 64-bit random nonce with a 64-bit counter.
724 * IETF mode uses a 96-bit random nonce with a 32-bit counter.
725 *
726 * @param string $ciphertext Encrypted message (with Poly1305 MAC appended)
727 * @param string $assocData Authenticated Associated Data (unencrypted)
728 * @param string $nonce Number to be used only Once; must be 8 bytes
729 * @param string $key Encryption key
730 *
731 * @return string The original plaintext message
732 * @throws SodiumException
733 * @throws TypeError
734 * @psalm-suppress MixedArgument
735 * @psalm-suppress MixedInferredReturnType
736 * @psalm-suppress MixedReturnStatement
737 */
738 public static function crypto_aead_chacha20poly1305_decrypt(
739 $ciphertext = '',
740 $assocData = '',
741 $nonce = '',
742 #[\SensitiveParameter]
743 $key = ''
744 ) {
745 /* Type checks: */
746 ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
747 ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
748 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
749 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
750
751 /* Input validation: */
752 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES) {
753 throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES long');
754 }
755 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) {
756 throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long');
757 }
758 if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_CHACHA20POLY1305_ABYTES) {
759 throw new SodiumException('Message must be at least CRYPTO_AEAD_CHACHA20POLY1305_ABYTES long');
760 }
761
762 if (self::useNewSodiumAPI()) {
763 /**
764 * @psalm-suppress InvalidReturnStatement
765 * @psalm-suppress FalsableReturnStatement
766 */
767 return sodium_crypto_aead_chacha20poly1305_decrypt(
768 $ciphertext,
769 $assocData,
770 $nonce,
771 $key
772 );
773 }
774 if (self::use_fallback('crypto_aead_chacha20poly1305_decrypt')) {
775 return call_user_func(
776 '\\Sodium\\crypto_aead_chacha20poly1305_decrypt',
777 $ciphertext,
778 $assocData,
779 $nonce,
780 $key
781 );
782 }
783 if (PHP_INT_SIZE === 4) {
784 return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_decrypt(
785 $ciphertext,
786 $assocData,
787 $nonce,
788 $key
789 );
790 }
791 return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_decrypt(
792 $ciphertext,
793 $assocData,
794 $nonce,
795 $key
796 );
797 }
798
799 /**
800 * Authenticated Encryption with Associated Data
801 *
802 * Algorithm:
803 * ChaCha20-Poly1305
804 *
805 * This mode uses a 64-bit random nonce with a 64-bit counter.
806 * IETF mode uses a 96-bit random nonce with a 32-bit counter.
807 *
808 * @param string $plaintext Message to be encrypted
809 * @param string $assocData Authenticated Associated Data (unencrypted)
810 * @param string $nonce Number to be used only Once; must be 8 bytes
811 * @param string $key Encryption key
812 *
813 * @return string Ciphertext with a 16-byte Poly1305 message
814 * authentication code appended
815 * @throws SodiumException
816 * @throws TypeError
817 * @psalm-suppress MixedArgument
818 */
819 public static function crypto_aead_chacha20poly1305_encrypt(
820 #[\SensitiveParameter]
821 $plaintext = '',
822 $assocData = '',
823 $nonce = '',
824 #[\SensitiveParameter]
825 $key = ''
826 ) {
827 /* Type checks: */
828 ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
829 ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
830 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
831 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
832
833 /* Input validation: */
834 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES) {
835 throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES long');
836 }
837 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) {
838 throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES long');
839 }
840
841 if (self::useNewSodiumAPI()) {
842 return (string) sodium_crypto_aead_chacha20poly1305_encrypt(
843 $plaintext,
844 $assocData,
845 $nonce,
846 $key
847 );
848 }
849 if (self::use_fallback('crypto_aead_chacha20poly1305_encrypt')) {
850 return (string) call_user_func(
851 '\\Sodium\\crypto_aead_chacha20poly1305_encrypt',
852 $plaintext,
853 $assocData,
854 $nonce,
855 $key
856 );
857 }
858 if (PHP_INT_SIZE === 4) {
859 return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_encrypt(
860 $plaintext,
861 $assocData,
862 $nonce,
863 $key
864 );
865 }
866 return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_encrypt(
867 $plaintext,
868 $assocData,
869 $nonce,
870 $key
871 );
872 }
873
874 /**
875 * Authenticated Encryption with Associated Data: Decryption
876 *
877 * Algorithm:
878 * ChaCha20-Poly1305
879 *
880 * IETF mode uses a 96-bit random nonce with a 32-bit counter.
881 * Regular mode uses a 64-bit random nonce with a 64-bit counter.
882 *
883 * @param string $ciphertext Encrypted message (with Poly1305 MAC appended)
884 * @param string $assocData Authenticated Associated Data (unencrypted)
885 * @param string $nonce Number to be used only Once; must be 12 bytes
886 * @param string $key Encryption key
887 *
888 * @return string The original plaintext message
889 * @throws SodiumException
890 * @throws TypeError
891 * @psalm-suppress MixedArgument
892 * @psalm-suppress MixedInferredReturnType
893 * @psalm-suppress MixedReturnStatement
894 */
895 public static function crypto_aead_chacha20poly1305_ietf_decrypt(
896 $ciphertext = '',
897 $assocData = '',
898 $nonce = '',
899 #[\SensitiveParameter]
900 $key = ''
901 ) {
902 /* Type checks: */
903 ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
904 ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
905 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
906 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
907
908 /* Input validation: */
909 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES) {
910 throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES long');
911 }
912 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) {
913 throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES long');
914 }
915 if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_CHACHA20POLY1305_ABYTES) {
916 throw new SodiumException('Message must be at least CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES long');
917 }
918
919 if (self::useNewSodiumAPI()) {
920 /**
921 * @psalm-suppress InvalidReturnStatement
922 * @psalm-suppress FalsableReturnStatement
923 */
924 return sodium_crypto_aead_chacha20poly1305_ietf_decrypt(
925 $ciphertext,
926 $assocData,
927 $nonce,
928 $key
929 );
930 }
931 if (self::use_fallback('crypto_aead_chacha20poly1305_ietf_decrypt')) {
932 return call_user_func(
933 '\\Sodium\\crypto_aead_chacha20poly1305_ietf_decrypt',
934 $ciphertext,
935 $assocData,
936 $nonce,
937 $key
938 );
939 }
940 if (PHP_INT_SIZE === 4) {
941 return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_ietf_decrypt(
942 $ciphertext,
943 $assocData,
944 $nonce,
945 $key
946 );
947 }
948 return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_decrypt(
949 $ciphertext,
950 $assocData,
951 $nonce,
952 $key
953 );
954 }
955
956 /**
957 * Return a secure random key for use with the ChaCha20-Poly1305
958 * symmetric AEAD interface.
959 *
960 * @return string
961 * @throws Exception
962 * @throws Error
963 */
964 public static function crypto_aead_chacha20poly1305_keygen()
965 {
966 return random_bytes(self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES);
967 }
968
969 /**
970 * Authenticated Encryption with Associated Data
971 *
972 * Algorithm:
973 * ChaCha20-Poly1305
974 *
975 * IETF mode uses a 96-bit random nonce with a 32-bit counter.
976 * Regular mode uses a 64-bit random nonce with a 64-bit counter.
977 *
978 * @param string $plaintext Message to be encrypted
979 * @param string $assocData Authenticated Associated Data (unencrypted)
980 * @param string $nonce Number to be used only Once; must be 8 bytes
981 * @param string $key Encryption key
982 *
983 * @return string Ciphertext with a 16-byte Poly1305 message
984 * authentication code appended
985 * @throws SodiumException
986 * @throws TypeError
987 * @psalm-suppress MixedArgument
988 */
989 public static function crypto_aead_chacha20poly1305_ietf_encrypt(
990 #[\SensitiveParameter]
991 $plaintext = '',
992 $assocData = '',
993 $nonce = '',
994 #[\SensitiveParameter]
995 $key = ''
996 ) {
997 /* Type checks: */
998 ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
999 if (!is_null($assocData)) {
1000 ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
1001 }
1002 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
1003 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
1004
1005 /* Input validation: */
1006 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES) {
1007 throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES long');
1008 }
1009 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) {
1010 throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long');
1011 }
1012
1013 if (self::useNewSodiumAPI()) {
1014 return (string) sodium_crypto_aead_chacha20poly1305_ietf_encrypt(
1015 $plaintext,
1016 $assocData,
1017 $nonce,
1018 $key
1019 );
1020 }
1021 if (self::use_fallback('crypto_aead_chacha20poly1305_ietf_encrypt')) {
1022 return (string) call_user_func(
1023 '\\Sodium\\crypto_aead_chacha20poly1305_ietf_encrypt',
1024 $plaintext,
1025 $assocData,
1026 $nonce,
1027 $key
1028 );
1029 }
1030 if (PHP_INT_SIZE === 4) {
1031 return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_ietf_encrypt(
1032 $plaintext,
1033 $assocData,
1034 $nonce,
1035 $key
1036 );
1037 }
1038 return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_encrypt(
1039 $plaintext,
1040 $assocData,
1041 $nonce,
1042 $key
1043 );
1044 }
1045
1046 /**
1047 * Return a secure random key for use with the ChaCha20-Poly1305
1048 * symmetric AEAD interface. (IETF version)
1049 *
1050 * @return string
1051 * @throws Exception
1052 * @throws Error
1053 */
1054 public static function crypto_aead_chacha20poly1305_ietf_keygen()
1055 {
1056 return random_bytes(self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES);
1057 }
1058
1059 /**
1060 * Authenticated Encryption with Associated Data: Decryption
1061 *
1062 * Algorithm:
1063 * XChaCha20-Poly1305
1064 *
1065 * This mode uses a 64-bit random nonce with a 64-bit counter.
1066 * IETF mode uses a 96-bit random nonce with a 32-bit counter.
1067 *
1068 * @param string $ciphertext Encrypted message (with Poly1305 MAC appended)
1069 * @param string $assocData Authenticated Associated Data (unencrypted)
1070 * @param string $nonce Number to be used only Once; must be 8 bytes
1071 * @param string $key Encryption key
1072 * @param bool $dontFallback Don't fallback to ext/sodium
1073 *
1074 * @return string|bool The original plaintext message
1075 * @throws SodiumException
1076 * @throws TypeError
1077 * @psalm-suppress MixedArgument
1078 */
1079 public static function crypto_aead_xchacha20poly1305_ietf_decrypt(
1080 $ciphertext = '',
1081 $assocData = '',
1082 $nonce = '',
1083 #[\SensitiveParameter]
1084 $key = '',
1085 $dontFallback = false
1086 ) {
1087 /* Type checks: */
1088 ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
1089 if (!is_null($assocData)) {
1090 ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
1091 } else {
1092 $assocData = '';
1093 }
1094 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
1095 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
1096
1097 /* Input validation: */
1098 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES) {
1099 throw new SodiumException('Nonce must be CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES long');
1100 }
1101 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) {
1102 throw new SodiumException('Key must be CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES long');
1103 }
1104 if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES) {
1105 throw new SodiumException('Message must be at least CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES long');
1106 }
1107 if (self::useNewSodiumAPI() && !$dontFallback) {
1108 if (is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) {
1109 return sodium_crypto_aead_xchacha20poly1305_ietf_decrypt(
1110 $ciphertext,
1111 $assocData,
1112 $nonce,
1113 $key
1114 );
1115 }
1116 }
1117
1118 if (PHP_INT_SIZE === 4) {
1119 return ParagonIE_Sodium_Crypto32::aead_xchacha20poly1305_ietf_decrypt(
1120 $ciphertext,
1121 $assocData,
1122 $nonce,
1123 $key
1124 );
1125 }
1126 return ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_decrypt(
1127 $ciphertext,
1128 $assocData,
1129 $nonce,
1130 $key
1131 );
1132 }
1133
1134 /**
1135 * Authenticated Encryption with Associated Data
1136 *
1137 * Algorithm:
1138 * XChaCha20-Poly1305
1139 *
1140 * This mode uses a 64-bit random nonce with a 64-bit counter.
1141 * IETF mode uses a 96-bit random nonce with a 32-bit counter.
1142 *
1143 * @param string $plaintext Message to be encrypted
1144 * @param string $assocData Authenticated Associated Data (unencrypted)
1145 * @param string $nonce Number to be used only Once; must be 8 bytes
1146 * @param string $key Encryption key
1147 * @param bool $dontFallback Don't fallback to ext/sodium
1148 *
1149 * @return string Ciphertext with a 16-byte Poly1305 message
1150 * authentication code appended
1151 * @throws SodiumException
1152 * @throws TypeError
1153 * @psalm-suppress MixedArgument
1154 */
1155 public static function crypto_aead_xchacha20poly1305_ietf_encrypt(
1156 #[\SensitiveParameter]
1157 $plaintext = '',
1158 $assocData = '',
1159 $nonce = '',
1160 #[\SensitiveParameter]
1161 $key = '',
1162 $dontFallback = false
1163 ) {
1164 /* Type checks: */
1165 ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
1166 if (!is_null($assocData)) {
1167 ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
1168 } else {
1169 $assocData = '';
1170 }
1171 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
1172 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
1173
1174 /* Input validation: */
1175 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES) {
1176 throw new SodiumException('Nonce must be CRYPTO_AEAD_XCHACHA20POLY1305_NPUBBYTES long');
1177 }
1178 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) {
1179 throw new SodiumException('Key must be CRYPTO_AEAD_XCHACHA20POLY1305_KEYBYTES long');
1180 }
1181 if (self::useNewSodiumAPI() && !$dontFallback) {
1182 if (is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) {
1183 return sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(
1184 $plaintext,
1185 $assocData,
1186 $nonce,
1187 $key
1188 );
1189 }
1190 }
1191
1192 if (PHP_INT_SIZE === 4) {
1193 return ParagonIE_Sodium_Crypto32::aead_xchacha20poly1305_ietf_encrypt(
1194 $plaintext,
1195 $assocData,
1196 $nonce,
1197 $key
1198 );
1199 }
1200 return ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_encrypt(
1201 $plaintext,
1202 $assocData,
1203 $nonce,
1204 $key
1205 );
1206 }
1207
1208 /**
1209 * Return a secure random key for use with the XChaCha20-Poly1305
1210 * symmetric AEAD interface.
1211 *
1212 * @return string
1213 * @throws Exception
1214 * @throws Error
1215 */
1216 public static function crypto_aead_xchacha20poly1305_ietf_keygen()
1217 {
1218 return random_bytes(self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES);
1219 }
1220
1221 /**
1222 * Authenticate a message. Uses symmetric-key cryptography.
1223 *
1224 * Algorithm:
1225 * HMAC-SHA512-256. Which is HMAC-SHA-512 truncated to 256 bits.
1226 * Not to be confused with HMAC-SHA-512/256 which would use the
1227 * SHA-512/256 hash function (uses different initial parameters
1228 * but still truncates to 256 bits to sidestep length-extension
1229 * attacks).
1230 *
1231 * @param string $message Message to be authenticated
1232 * @param string $key Symmetric authentication key
1233 * @return string Message authentication code
1234 * @throws SodiumException
1235 * @throws TypeError
1236 * @psalm-suppress MixedArgument
1237 */
1238 public static function crypto_auth(
1239 $message,
1240 #[\SensitiveParameter]
1241 $key
1242 ) {
1243 /* Type checks: */
1244 ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
1245 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2);
1246
1247 /* Input validation: */
1248 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AUTH_KEYBYTES) {
1249 throw new SodiumException('Argument 2 must be CRYPTO_AUTH_KEYBYTES long.');
1250 }
1251
1252 if (self::useNewSodiumAPI()) {
1253 return (string) sodium_crypto_auth($message, $key);
1254 }
1255 if (self::use_fallback('crypto_auth')) {
1256 return (string) call_user_func('\\Sodium\\crypto_auth', $message, $key);
1257 }
1258 if (PHP_INT_SIZE === 4) {
1259 return ParagonIE_Sodium_Crypto32::auth($message, $key);
1260 }
1261 return ParagonIE_Sodium_Crypto::auth($message, $key);
1262 }
1263
1264 /**
1265 * @return string
1266 * @throws Exception
1267 * @throws Error
1268 */
1269 public static function crypto_auth_keygen()
1270 {
1271 return random_bytes(self::CRYPTO_AUTH_KEYBYTES);
1272 }
1273
1274 /**
1275 * Verify the MAC of a message previously authenticated with crypto_auth.
1276 *
1277 * @param string $mac Message authentication code
1278 * @param string $message Message whose authenticity you are attempting to
1279 * verify (with a given MAC and key)
1280 * @param string $key Symmetric authentication key
1281 * @return bool TRUE if authenticated, FALSE otherwise
1282 * @throws SodiumException
1283 * @throws TypeError
1284 * @psalm-suppress MixedArgument
1285 */
1286 public static function crypto_auth_verify(
1287 $mac,
1288 $message,
1289 #[\SensitiveParameter]
1290 $key
1291 ) {
1292 /* Type checks: */
1293 ParagonIE_Sodium_Core_Util::declareScalarType($mac, 'string', 1);
1294 ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2);
1295 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
1296
1297 /* Input validation: */
1298 if (ParagonIE_Sodium_Core_Util::strlen($mac) !== self::CRYPTO_AUTH_BYTES) {
1299 throw new SodiumException('Argument 1 must be CRYPTO_AUTH_BYTES long.');
1300 }
1301 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AUTH_KEYBYTES) {
1302 throw new SodiumException('Argument 3 must be CRYPTO_AUTH_KEYBYTES long.');
1303 }
1304
1305 if (self::useNewSodiumAPI()) {
1306 return (bool) sodium_crypto_auth_verify($mac, $message, $key);
1307 }
1308 if (self::use_fallback('crypto_auth_verify')) {
1309 return (bool) call_user_func('\\Sodium\\crypto_auth_verify', $mac, $message, $key);
1310 }
1311 if (PHP_INT_SIZE === 4) {
1312 return ParagonIE_Sodium_Crypto32::auth_verify($mac, $message, $key);
1313 }
1314 return ParagonIE_Sodium_Crypto::auth_verify($mac, $message, $key);
1315 }
1316
1317 /**
1318 * Authenticated asymmetric-key encryption. Both the sender and recipient
1319 * may decrypt messages.
1320 *
1321 * Algorithm: X25519-XSalsa20-Poly1305.
1322 * X25519: Elliptic-Curve Diffie Hellman over Curve25519.
1323 * XSalsa20: Extended-nonce variant of salsa20.
1324 * Poyl1305: Polynomial MAC for one-time message authentication.
1325 *
1326 * @param string $plaintext The message to be encrypted
1327 * @param string $nonce A Number to only be used Once; must be 24 bytes
1328 * @param string $keypair Your secret key and your recipient's public key
1329 * @return string Ciphertext with 16-byte Poly1305 MAC
1330 * @throws SodiumException
1331 * @throws TypeError
1332 * @psalm-suppress MixedArgument
1333 */
1334 public static function crypto_box(
1335 $plaintext,
1336 $nonce,
1337 #[\SensitiveParameter]
1338 $keypair
1339 ) {
1340 /* Type checks: */
1341 ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
1342 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
1343 ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 3);
1344
1345 /* Input validation: */
1346 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_BOX_NONCEBYTES) {
1347 throw new SodiumException('Argument 2 must be CRYPTO_BOX_NONCEBYTES long.');
1348 }
1349 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
1350 throw new SodiumException('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES long.');
1351 }
1352
1353 if (self::useNewSodiumAPI()) {
1354 return (string) sodium_crypto_box($plaintext, $nonce, $keypair);
1355 }
1356 if (self::use_fallback('crypto_box')) {
1357 return (string) call_user_func('\\Sodium\\crypto_box', $plaintext, $nonce, $keypair);
1358 }
1359 if (PHP_INT_SIZE === 4) {
1360 return ParagonIE_Sodium_Crypto32::box($plaintext, $nonce, $keypair);
1361 }
1362 return ParagonIE_Sodium_Crypto::box($plaintext, $nonce, $keypair);
1363 }
1364
1365 /**
1366 * Anonymous public-key encryption. Only the recipient may decrypt messages.
1367 *
1368 * Algorithm: X25519-XSalsa20-Poly1305, as with crypto_box.
1369 * The sender's X25519 keypair is ephemeral.
1370 * Nonce is generated from the BLAKE2b hash of both public keys.
1371 *
1372 * This provides ciphertext integrity.
1373 *
1374 * @param string $plaintext Message to be sealed
1375 * @param string $publicKey Your recipient's public key
1376 * @return string Sealed message that only your recipient can
1377 * decrypt
1378 * @throws SodiumException
1379 * @throws TypeError
1380 * @psalm-suppress MixedArgument
1381 */
1382 public static function crypto_box_seal(
1383 #[\SensitiveParameter]
1384 $plaintext,
1385 $publicKey
1386 ) {
1387 /* Type checks: */
1388 ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
1389 ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
1390
1391 /* Input validation: */
1392 if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
1393 throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
1394 }
1395
1396 if (self::useNewSodiumAPI()) {
1397 return (string) sodium_crypto_box_seal($plaintext, $publicKey);
1398 }
1399 if (self::use_fallback('crypto_box_seal')) {
1400 return (string) call_user_func('\\Sodium\\crypto_box_seal', $plaintext, $publicKey);
1401 }
1402 if (PHP_INT_SIZE === 4) {
1403 return ParagonIE_Sodium_Crypto32::box_seal($plaintext, $publicKey);
1404 }
1405 return ParagonIE_Sodium_Crypto::box_seal($plaintext, $publicKey);
1406 }
1407
1408 /**
1409 * Opens a message encrypted with crypto_box_seal(). Requires
1410 * the recipient's keypair (sk || pk) to decrypt successfully.
1411 *
1412 * This validates ciphertext integrity.
1413 *
1414 * @param string $ciphertext Sealed message to be opened
1415 * @param string $keypair Your crypto_box keypair
1416 * @return string The original plaintext message
1417 * @throws SodiumException
1418 * @throws TypeError
1419 * @psalm-suppress MixedArgument
1420 * @psalm-suppress MixedInferredReturnType
1421 * @psalm-suppress MixedReturnStatement
1422 */
1423 public static function crypto_box_seal_open(
1424 $ciphertext,
1425 #[\SensitiveParameter]
1426 $keypair
1427 ) {
1428 /* Type checks: */
1429 ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
1430 ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 2);
1431
1432 /* Input validation: */
1433 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
1434 throw new SodiumException('Argument 2 must be CRYPTO_BOX_KEYPAIRBYTES long.');
1435 }
1436
1437 if (self::useNewSodiumAPI()) {
1438 /**
1439 * @psalm-suppress InvalidReturnStatement
1440 * @psalm-suppress FalsableReturnStatement
1441 */
1442 return sodium_crypto_box_seal_open($ciphertext, $keypair);
1443 }
1444 if (self::use_fallback('crypto_box_seal_open')) {
1445 return call_user_func('\\Sodium\\crypto_box_seal_open', $ciphertext, $keypair);
1446 }
1447 if (PHP_INT_SIZE === 4) {
1448 return ParagonIE_Sodium_Crypto32::box_seal_open($ciphertext, $keypair);
1449 }
1450 return ParagonIE_Sodium_Crypto::box_seal_open($ciphertext, $keypair);
1451 }
1452
1453 /**
1454 * Generate a new random X25519 keypair.
1455 *
1456 * @return string A 64-byte string; the first 32 are your secret key, while
1457 * the last 32 are your public key. crypto_box_secretkey()
1458 * and crypto_box_publickey() exist to separate them so you
1459 * don't accidentally get them mixed up!
1460 * @throws SodiumException
1461 * @throws TypeError
1462 * @psalm-suppress MixedArgument
1463 */
1464 public static function crypto_box_keypair()
1465 {
1466 if (self::useNewSodiumAPI()) {
1467 return (string) sodium_crypto_box_keypair();
1468 }
1469 if (self::use_fallback('crypto_box_keypair')) {
1470 return (string) call_user_func('\\Sodium\\crypto_box_keypair');
1471 }
1472 if (PHP_INT_SIZE === 4) {
1473 return ParagonIE_Sodium_Crypto32::box_keypair();
1474 }
1475 return ParagonIE_Sodium_Crypto::box_keypair();
1476 }
1477
1478 /**
1479 * Combine two keys into a keypair for use in library methods that expect
1480 * a keypair. This doesn't necessarily have to be the same person's keys.
1481 *
1482 * @param string $secretKey Secret key
1483 * @param string $publicKey Public key
1484 * @return string Keypair
1485 * @throws SodiumException
1486 * @throws TypeError
1487 * @psalm-suppress MixedArgument
1488 */
1489 public static function crypto_box_keypair_from_secretkey_and_publickey(
1490 #[\SensitiveParameter]
1491 $secretKey,
1492 $publicKey
1493 ) {
1494 /* Type checks: */
1495 ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
1496 ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
1497
1498 /* Input validation: */
1499 if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
1500 throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
1501 }
1502 if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
1503 throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
1504 }
1505
1506 if (self::useNewSodiumAPI()) {
1507 return (string) sodium_crypto_box_keypair_from_secretkey_and_publickey($secretKey, $publicKey);
1508 }
1509 if (self::use_fallback('crypto_box_keypair_from_secretkey_and_publickey')) {
1510 return (string) call_user_func('\\Sodium\\crypto_box_keypair_from_secretkey_and_publickey', $secretKey, $publicKey);
1511 }
1512 if (PHP_INT_SIZE === 4) {
1513 return ParagonIE_Sodium_Crypto32::box_keypair_from_secretkey_and_publickey($secretKey, $publicKey);
1514 }
1515 return ParagonIE_Sodium_Crypto::box_keypair_from_secretkey_and_publickey($secretKey, $publicKey);
1516 }
1517
1518 /**
1519 * Decrypt a message previously encrypted with crypto_box().
1520 *
1521 * @param string $ciphertext Encrypted message
1522 * @param string $nonce Number to only be used Once; must be 24 bytes
1523 * @param string $keypair Your secret key and the sender's public key
1524 * @return string The original plaintext message
1525 * @throws SodiumException
1526 * @throws TypeError
1527 * @psalm-suppress MixedArgument
1528 * @psalm-suppress MixedInferredReturnType
1529 * @psalm-suppress MixedReturnStatement
1530 */
1531 public static function crypto_box_open(
1532 $ciphertext,
1533 $nonce,
1534 #[\SensitiveParameter]
1535 $keypair
1536 ) {
1537 /* Type checks: */
1538 ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
1539 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
1540 ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 3);
1541
1542 /* Input validation: */
1543 if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_BOX_MACBYTES) {
1544 throw new SodiumException('Argument 1 must be at least CRYPTO_BOX_MACBYTES long.');
1545 }
1546 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_BOX_NONCEBYTES) {
1547 throw new SodiumException('Argument 2 must be CRYPTO_BOX_NONCEBYTES long.');
1548 }
1549 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
1550 throw new SodiumException('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES long.');
1551 }
1552
1553 if (self::useNewSodiumAPI()) {
1554 /**
1555 * @psalm-suppress InvalidReturnStatement
1556 * @psalm-suppress FalsableReturnStatement
1557 */
1558 return sodium_crypto_box_open($ciphertext, $nonce, $keypair);
1559 }
1560 if (self::use_fallback('crypto_box_open')) {
1561 return call_user_func('\\Sodium\\crypto_box_open', $ciphertext, $nonce, $keypair);
1562 }
1563 if (PHP_INT_SIZE === 4) {
1564 return ParagonIE_Sodium_Crypto32::box_open($ciphertext, $nonce, $keypair);
1565 }
1566 return ParagonIE_Sodium_Crypto::box_open($ciphertext, $nonce, $keypair);
1567 }
1568
1569 /**
1570 * Extract the public key from a crypto_box keypair.
1571 *
1572 * @param string $keypair Keypair containing secret and public key
1573 * @return string Your crypto_box public key
1574 * @throws SodiumException
1575 * @throws TypeError
1576 * @psalm-suppress MixedArgument
1577 */
1578 public static function crypto_box_publickey(
1579 #[\SensitiveParameter]
1580 $keypair
1581 ) {
1582 /* Type checks: */
1583 ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
1584
1585 /* Input validation: */
1586 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
1587 throw new SodiumException('Argument 1 must be CRYPTO_BOX_KEYPAIRBYTES long.');
1588 }
1589
1590 if (self::useNewSodiumAPI()) {
1591 return (string) sodium_crypto_box_publickey($keypair);
1592 }
1593 if (self::use_fallback('crypto_box_publickey')) {
1594 return (string) call_user_func('\\Sodium\\crypto_box_publickey', $keypair);
1595 }
1596 if (PHP_INT_SIZE === 4) {
1597 return ParagonIE_Sodium_Crypto32::box_publickey($keypair);
1598 }
1599 return ParagonIE_Sodium_Crypto::box_publickey($keypair);
1600 }
1601
1602 /**
1603 * Calculate the X25519 public key from a given X25519 secret key.
1604 *
1605 * @param string $secretKey Any X25519 secret key
1606 * @return string The corresponding X25519 public key
1607 * @throws SodiumException
1608 * @throws TypeError
1609 * @psalm-suppress MixedArgument
1610 */
1611 public static function crypto_box_publickey_from_secretkey(
1612 #[\SensitiveParameter]
1613 $secretKey
1614 ) {
1615 /* Type checks: */
1616 ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
1617
1618 /* Input validation: */
1619 if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
1620 throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
1621 }
1622
1623 if (self::useNewSodiumAPI()) {
1624 return (string) sodium_crypto_box_publickey_from_secretkey($secretKey);
1625 }
1626 if (self::use_fallback('crypto_box_publickey_from_secretkey')) {
1627 return (string) call_user_func('\\Sodium\\crypto_box_publickey_from_secretkey', $secretKey);
1628 }
1629 if (PHP_INT_SIZE === 4) {
1630 return ParagonIE_Sodium_Crypto32::box_publickey_from_secretkey($secretKey);
1631 }
1632 return ParagonIE_Sodium_Crypto::box_publickey_from_secretkey($secretKey);
1633 }
1634
1635 /**
1636 * Extract the secret key from a crypto_box keypair.
1637 *
1638 * @param string $keypair
1639 * @return string Your crypto_box secret key
1640 * @throws SodiumException
1641 * @throws TypeError
1642 * @psalm-suppress MixedArgument
1643 */
1644 public static function crypto_box_secretkey(
1645 #[\SensitiveParameter]
1646 $keypair
1647 ) {
1648 /* Type checks: */
1649 ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
1650
1651 /* Input validation: */
1652 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
1653 throw new SodiumException('Argument 1 must be CRYPTO_BOX_KEYPAIRBYTES long.');
1654 }
1655
1656 if (self::useNewSodiumAPI()) {
1657 return (string) sodium_crypto_box_secretkey($keypair);
1658 }
1659 if (self::use_fallback('crypto_box_secretkey')) {
1660 return (string) call_user_func('\\Sodium\\crypto_box_secretkey', $keypair);
1661 }
1662 if (PHP_INT_SIZE === 4) {
1663 return ParagonIE_Sodium_Crypto32::box_secretkey($keypair);
1664 }
1665 return ParagonIE_Sodium_Crypto::box_secretkey($keypair);
1666 }
1667
1668 /**
1669 * Generate an X25519 keypair from a seed.
1670 *
1671 * @param string $seed
1672 * @return string
1673 * @throws SodiumException
1674 * @throws TypeError
1675 * @psalm-suppress MixedArgument
1676 * @psalm-suppress UndefinedFunction
1677 */
1678 public static function crypto_box_seed_keypair(
1679 #[\SensitiveParameter]
1680 $seed
1681 ) {
1682 /* Type checks: */
1683 ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
1684
1685 if (self::useNewSodiumAPI()) {
1686 return (string) sodium_crypto_box_seed_keypair($seed);
1687 }
1688 if (self::use_fallback('crypto_box_seed_keypair')) {
1689 return (string) call_user_func('\\Sodium\\crypto_box_seed_keypair', $seed);
1690 }
1691 if (PHP_INT_SIZE === 4) {
1692 return ParagonIE_Sodium_Crypto32::box_seed_keypair($seed);
1693 }
1694 return ParagonIE_Sodium_Crypto::box_seed_keypair($seed);
1695 }
1696
1697 /**
1698 * Calculates a BLAKE2b hash, with an optional key.
1699 *
1700 * @param string $message The message to be hashed
1701 * @param string|null $key If specified, must be a string between 16
1702 * and 64 bytes long
1703 * @param int $length Output length in bytes; must be between 16
1704 * and 64 (default = 32)
1705 * @return string Raw binary
1706 * @throws SodiumException
1707 * @throws TypeError
1708 * @psalm-suppress MixedArgument
1709 */
1710 public static function crypto_generichash(
1711 $message,
1712 #[\SensitiveParameter]
1713 $key = '',
1714 $length = self::CRYPTO_GENERICHASH_BYTES
1715 ) {
1716 /* Type checks: */
1717 ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
1718 if (is_null($key)) {
1719 $key = '';
1720 }
1721 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2);
1722 ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 3);
1723
1724 /* Input validation: */
1725 if (!empty($key)) {
1726 if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
1727 throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
1728 }
1729 if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
1730 throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
1731 }
1732 }
1733
1734 if (self::useNewSodiumAPI()) {
1735 return (string) sodium_crypto_generichash($message, $key, $length);
1736 }
1737 if (self::use_fallback('crypto_generichash')) {
1738 return (string) call_user_func('\\Sodium\\crypto_generichash', $message, $key, $length);
1739 }
1740 if (PHP_INT_SIZE === 4) {
1741 return ParagonIE_Sodium_Crypto32::generichash($message, $key, $length);
1742 }
1743 return ParagonIE_Sodium_Crypto::generichash($message, $key, $length);
1744 }
1745
1746 /**
1747 * Get the final BLAKE2b hash output for a given context.
1748 *
1749 * @param string $ctx BLAKE2 hashing context. Generated by crypto_generichash_init().
1750 * @param int $length Hash output size.
1751 * @return string Final BLAKE2b hash.
1752 * @throws SodiumException
1753 * @throws TypeError
1754 * @psalm-suppress MixedArgument
1755 * @psalm-suppress ReferenceConstraintViolation
1756 * @psalm-suppress ConflictingReferenceConstraint
1757 */
1758 public static function crypto_generichash_final(
1759 #[\SensitiveParameter]
1760 &$ctx,
1761 $length = self::CRYPTO_GENERICHASH_BYTES
1762 ) {
1763 /* Type checks: */
1764 ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1);
1765 ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
1766
1767 if (self::useNewSodiumAPI()) {
1768 return sodium_crypto_generichash_final($ctx, $length);
1769 }
1770 if (self::use_fallback('crypto_generichash_final')) {
1771 $func = '\\Sodium\\crypto_generichash_final';
1772 return (string) $func($ctx, $length);
1773 }
1774 if ($length < 1) {
1775 try {
1776 self::memzero($ctx);
1777 } catch (SodiumException $ex) {
1778 unset($ctx);
1779 }
1780 return '';
1781 }
1782 if (PHP_INT_SIZE === 4) {
1783 $result = ParagonIE_Sodium_Crypto32::generichash_final($ctx, $length);
1784 } else {
1785 $result = ParagonIE_Sodium_Crypto::generichash_final($ctx, $length);
1786 }
1787 try {
1788 self::memzero($ctx);
1789 } catch (SodiumException $ex) {
1790 unset($ctx);
1791 }
1792 return $result;
1793 }
1794
1795 /**
1796 * Initialize a BLAKE2b hashing context, for use in a streaming interface.
1797 *
1798 * @param string|null $key If specified must be a string between 16 and 64 bytes
1799 * @param int $length The size of the desired hash output
1800 * @return string A BLAKE2 hashing context, encoded as a string
1801 * (To be 100% compatible with ext/libsodium)
1802 * @throws SodiumException
1803 * @throws TypeError
1804 * @psalm-suppress MixedArgument
1805 */
1806 public static function crypto_generichash_init(
1807 #[\SensitiveParameter]
1808 $key = '',
1809 $length = self::CRYPTO_GENERICHASH_BYTES
1810 ) {
1811 /* Type checks: */
1812 if (is_null($key)) {
1813 $key = '';
1814 }
1815 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1);
1816 ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
1817
1818 /* Input validation: */
1819 if (!empty($key)) {
1820 if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
1821 throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
1822 }
1823 if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
1824 throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
1825 }
1826 }
1827
1828 if (self::useNewSodiumAPI()) {
1829 return sodium_crypto_generichash_init($key, $length);
1830 }
1831 if (self::use_fallback('crypto_generichash_init')) {
1832 return (string) call_user_func('\\Sodium\\crypto_generichash_init', $key, $length);
1833 }
1834 if (PHP_INT_SIZE === 4) {
1835 return ParagonIE_Sodium_Crypto32::generichash_init($key, $length);
1836 }
1837 return ParagonIE_Sodium_Crypto::generichash_init($key, $length);
1838 }
1839
1840 /**
1841 * Initialize a BLAKE2b hashing context, for use in a streaming interface.
1842 *
1843 * @param string|null $key If specified must be a string between 16 and 64 bytes
1844 * @param int $length The size of the desired hash output
1845 * @param string $salt Salt (up to 16 bytes)
1846 * @param string $personal Personalization string (up to 16 bytes)
1847 * @return string A BLAKE2 hashing context, encoded as a string
1848 * (To be 100% compatible with ext/libsodium)
1849 * @throws SodiumException
1850 * @throws TypeError
1851 * @psalm-suppress MixedArgument
1852 */
1853 public static function crypto_generichash_init_salt_personal(
1854 #[\SensitiveParameter]
1855 $key = '',
1856 $length = self::CRYPTO_GENERICHASH_BYTES,
1857 $salt = '',
1858 $personal = ''
1859 ) {
1860 /* Type checks: */
1861 if (is_null($key)) {
1862 $key = '';
1863 }
1864 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1);
1865 ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
1866 ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3);
1867 ParagonIE_Sodium_Core_Util::declareScalarType($personal, 'string', 4);
1868 $salt = str_pad($salt, 16, "\0", STR_PAD_RIGHT);
1869 $personal = str_pad($personal, 16, "\0", STR_PAD_RIGHT);
1870
1871 /* Input validation: */
1872 if (!empty($key)) {
1873 /*
1874 if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
1875 throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
1876 }
1877 */
1878 if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
1879 throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
1880 }
1881 }
1882 if (PHP_INT_SIZE === 4) {
1883 return ParagonIE_Sodium_Crypto32::generichash_init_salt_personal($key, $length, $salt, $personal);
1884 }
1885 return ParagonIE_Sodium_Crypto::generichash_init_salt_personal($key, $length, $salt, $personal);
1886 }
1887
1888 /**
1889 * Update a BLAKE2b hashing context with additional data.
1890 *
1891 * @param string $ctx BLAKE2 hashing context. Generated by crypto_generichash_init().
1892 * $ctx is passed by reference and gets updated in-place.
1893 * @param-out string $ctx
1894 * @param string $message The message to append to the existing hash state.
1895 * @return void
1896 * @throws SodiumException
1897 * @throws TypeError
1898 * @psalm-suppress MixedArgument
1899 * @psalm-suppress ReferenceConstraintViolation
1900 */
1901 public static function crypto_generichash_update(
1902 #[\SensitiveParameter]
1903 &$ctx,
1904 $message
1905 ) {
1906 /* Type checks: */
1907 ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1);
1908 ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2);
1909
1910 if (self::useNewSodiumAPI()) {
1911 sodium_crypto_generichash_update($ctx, $message);
1912 return;
1913 }
1914 if (self::use_fallback('crypto_generichash_update')) {
1915 $func = '\\Sodium\\crypto_generichash_update';
1916 $func($ctx, $message);
1917 return;
1918 }
1919 if (PHP_INT_SIZE === 4) {
1920 $ctx = ParagonIE_Sodium_Crypto32::generichash_update($ctx, $message);
1921 } else {
1922 $ctx = ParagonIE_Sodium_Crypto::generichash_update($ctx, $message);
1923 }
1924 }
1925
1926 /**
1927 * @return string
1928 * @throws Exception
1929 * @throws Error
1930 */
1931 public static function crypto_generichash_keygen()
1932 {
1933 return random_bytes(self::CRYPTO_GENERICHASH_KEYBYTES);
1934 }
1935
1936 /**
1937 * @param int $subkey_len
1938 * @param int $subkey_id
1939 * @param string $context
1940 * @param string $key
1941 * @return string
1942 * @throws SodiumException
1943 */
1944 public static function crypto_kdf_derive_from_key(
1945 $subkey_len,
1946 $subkey_id,
1947 $context,
1948 #[\SensitiveParameter]
1949 $key
1950 ) {
1951 ParagonIE_Sodium_Core_Util::declareScalarType($subkey_len, 'int', 1);
1952 ParagonIE_Sodium_Core_Util::declareScalarType($subkey_id, 'int', 2);
1953 ParagonIE_Sodium_Core_Util::declareScalarType($context, 'string', 3);
1954 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
1955 $subkey_id = (int) $subkey_id;
1956 $subkey_len = (int) $subkey_len;
1957 $context = (string) $context;
1958 $key = (string) $key;
1959
1960 if ($subkey_len < self::CRYPTO_KDF_BYTES_MIN) {
1961 throw new SodiumException('subkey cannot be smaller than SODIUM_CRYPTO_KDF_BYTES_MIN');
1962 }
1963 if ($subkey_len > self::CRYPTO_KDF_BYTES_MAX) {
1964 throw new SodiumException('subkey cannot be larger than SODIUM_CRYPTO_KDF_BYTES_MAX');
1965 }
1966 if ($subkey_id < 0) {
1967 throw new SodiumException('subkey_id cannot be negative');
1968 }
1969 if (ParagonIE_Sodium_Core_Util::strlen($context) !== self::CRYPTO_KDF_CONTEXTBYTES) {
1970 throw new SodiumException('context should be SODIUM_CRYPTO_KDF_CONTEXTBYTES bytes');
1971 }
1972 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_KDF_KEYBYTES) {
1973 throw new SodiumException('key should be SODIUM_CRYPTO_KDF_KEYBYTES bytes');
1974 }
1975
1976 $salt = ParagonIE_Sodium_Core_Util::store64_le($subkey_id);
1977 $state = self::crypto_generichash_init_salt_personal(
1978 $key,
1979 $subkey_len,
1980 $salt,
1981 $context
1982 );
1983 return self::crypto_generichash_final($state, $subkey_len);
1984 }
1985
1986 /**
1987 * @return string
1988 * @throws Exception
1989 * @throws Error
1990 */
1991 public static function crypto_kdf_keygen()
1992 {
1993 return random_bytes(self::CRYPTO_KDF_KEYBYTES);
1994 }
1995
1996 /**
1997 * Perform a key exchange, between a designated client and a server.
1998 *
1999 * Typically, you would designate one machine to be the client and the
2000 * other to be the server. The first two keys are what you'd expect for
2001 * scalarmult() below, but the latter two public keys don't swap places.
2002 *
2003 * | ALICE | BOB |
2004 * | Client | Server |
2005 * |--------------------------------|-------------------------------------|
2006 * | shared = crypto_kx( | shared = crypto_kx( |
2007 * | alice_sk, | bob_sk, | <- contextual
2008 * | bob_pk, | alice_pk, | <- contextual
2009 * | alice_pk, | alice_pk, | <----- static
2010 * | bob_pk | bob_pk | <----- static
2011 * | ) | ) |
2012 *
2013 * They are used along with the scalarmult product to generate a 256-bit
2014 * BLAKE2b hash unique to the client and server keys.
2015 *
2016 * @param string $my_secret
2017 * @param string $their_public
2018 * @param string $client_public
2019 * @param string $server_public
2020 * @param bool $dontFallback
2021 * @return string
2022 * @throws SodiumException
2023 * @throws TypeError
2024 * @psalm-suppress MixedArgument
2025 */
2026 public static function crypto_kx(
2027 #[\SensitiveParameter]
2028 $my_secret,
2029 $their_public,
2030 $client_public,
2031 $server_public,
2032 $dontFallback = false
2033 ) {
2034 /* Type checks: */
2035 ParagonIE_Sodium_Core_Util::declareScalarType($my_secret, 'string', 1);
2036 ParagonIE_Sodium_Core_Util::declareScalarType($their_public, 'string', 2);
2037 ParagonIE_Sodium_Core_Util::declareScalarType($client_public, 'string', 3);
2038 ParagonIE_Sodium_Core_Util::declareScalarType($server_public, 'string', 4);
2039
2040 /* Input validation: */
2041 if (ParagonIE_Sodium_Core_Util::strlen($my_secret) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
2042 throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
2043 }
2044 if (ParagonIE_Sodium_Core_Util::strlen($their_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
2045 throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
2046 }
2047 if (ParagonIE_Sodium_Core_Util::strlen($client_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
2048 throw new SodiumException('Argument 3 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
2049 }
2050 if (ParagonIE_Sodium_Core_Util::strlen($server_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
2051 throw new SodiumException('Argument 4 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
2052 }
2053
2054 if (self::useNewSodiumAPI() && !$dontFallback) {
2055 if (is_callable('sodium_crypto_kx')) {
2056 return (string) sodium_crypto_kx(
2057 $my_secret,
2058 $their_public,
2059 $client_public,
2060 $server_public
2061 );
2062 }
2063 }
2064 if (self::use_fallback('crypto_kx')) {
2065 return (string) call_user_func(
2066 '\\Sodium\\crypto_kx',
2067 $my_secret,
2068 $their_public,
2069 $client_public,
2070 $server_public
2071 );
2072 }
2073 if (PHP_INT_SIZE === 4) {
2074 return ParagonIE_Sodium_Crypto32::keyExchange(
2075 $my_secret,
2076 $their_public,
2077 $client_public,
2078 $server_public
2079 );
2080 }
2081 return ParagonIE_Sodium_Crypto::keyExchange(
2082 $my_secret,
2083 $their_public,
2084 $client_public,
2085 $server_public
2086 );
2087 }
2088
2089 /**
2090 * @param string $seed
2091 * @return string
2092 * @throws SodiumException
2093 */
2094 public static function crypto_kx_seed_keypair(
2095 #[\SensitiveParameter]
2096 $seed
2097 ) {
2098 ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
2099
2100 $seed = (string) $seed;
2101
2102 if (ParagonIE_Sodium_Core_Util::strlen($seed) !== self::CRYPTO_KX_SEEDBYTES) {
2103 throw new SodiumException('seed must be SODIUM_CRYPTO_KX_SEEDBYTES bytes');
2104 }
2105
2106 $sk = self::crypto_generichash($seed, '', self::CRYPTO_KX_SECRETKEYBYTES);
2107 $pk = self::crypto_scalarmult_base($sk);
2108 return $sk . $pk;
2109 }
2110
2111 /**
2112 * @return string
2113 * @throws Exception
2114 */
2115 public static function crypto_kx_keypair()
2116 {
2117 $sk = self::randombytes_buf(self::CRYPTO_KX_SECRETKEYBYTES);
2118 $pk = self::crypto_scalarmult_base($sk);
2119 return $sk . $pk;
2120 }
2121
2122 /**
2123 * @param string $keypair
2124 * @param string $serverPublicKey
2125 * @return array{0: string, 1: string}
2126 * @throws SodiumException
2127 */
2128 public static function crypto_kx_client_session_keys(
2129 #[\SensitiveParameter]
2130 $keypair,
2131 $serverPublicKey
2132 ) {
2133 ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
2134 ParagonIE_Sodium_Core_Util::declareScalarType($serverPublicKey, 'string', 2);
2135
2136 $keypair = (string) $keypair;
2137 $serverPublicKey = (string) $serverPublicKey;
2138
2139 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) {
2140 throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes');
2141 }
2142 if (ParagonIE_Sodium_Core_Util::strlen($serverPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) {
2143 throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes');
2144 }
2145
2146 $sk = self::crypto_kx_secretkey($keypair);
2147 $pk = self::crypto_kx_publickey($keypair);
2148 $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
2149 self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $serverPublicKey));
2150 self::crypto_generichash_update($h, $pk);
2151 self::crypto_generichash_update($h, $serverPublicKey);
2152 $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
2153 return array(
2154 ParagonIE_Sodium_Core_Util::substr(
2155 $sessionKeys,
2156 0,
2157 self::CRYPTO_KX_SESSIONKEYBYTES
2158 ),
2159 ParagonIE_Sodium_Core_Util::substr(
2160 $sessionKeys,
2161 self::CRYPTO_KX_SESSIONKEYBYTES,
2162 self::CRYPTO_KX_SESSIONKEYBYTES
2163 )
2164 );
2165 }
2166
2167 /**
2168 * @param string $keypair
2169 * @param string $clientPublicKey
2170 * @return array{0: string, 1: string}
2171 * @throws SodiumException
2172 */
2173 public static function crypto_kx_server_session_keys(
2174 #[\SensitiveParameter]
2175 $keypair,
2176 $clientPublicKey
2177 ) {
2178 ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
2179 ParagonIE_Sodium_Core_Util::declareScalarType($clientPublicKey, 'string', 2);
2180
2181 $keypair = (string) $keypair;
2182 $clientPublicKey = (string) $clientPublicKey;
2183
2184 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) {
2185 throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes');
2186 }
2187 if (ParagonIE_Sodium_Core_Util::strlen($clientPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) {
2188 throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes');
2189 }
2190
2191 $sk = self::crypto_kx_secretkey($keypair);
2192 $pk = self::crypto_kx_publickey($keypair);
2193 $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
2194 self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $clientPublicKey));
2195 self::crypto_generichash_update($h, $clientPublicKey);
2196 self::crypto_generichash_update($h, $pk);
2197 $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
2198 return array(
2199 ParagonIE_Sodium_Core_Util::substr(
2200 $sessionKeys,
2201 self::CRYPTO_KX_SESSIONKEYBYTES,
2202 self::CRYPTO_KX_SESSIONKEYBYTES
2203 ),
2204 ParagonIE_Sodium_Core_Util::substr(
2205 $sessionKeys,
2206 0,
2207 self::CRYPTO_KX_SESSIONKEYBYTES
2208 )
2209 );
2210 }
2211
2212 /**
2213 * @param string $kp
2214 * @return string
2215 * @throws SodiumException
2216 */
2217 public static function crypto_kx_secretkey(
2218 #[\SensitiveParameter]
2219 $kp
2220 ) {
2221 return ParagonIE_Sodium_Core_Util::substr(
2222 $kp,
2223 0,
2224 self::CRYPTO_KX_SECRETKEYBYTES
2225 );
2226 }
2227
2228 /**
2229 * @param string $kp
2230 * @return string
2231 * @throws SodiumException
2232 */
2233 public static function crypto_kx_publickey($kp)
2234 {
2235 return ParagonIE_Sodium_Core_Util::substr(
2236 $kp,
2237 self::CRYPTO_KX_SECRETKEYBYTES,
2238 self::CRYPTO_KX_PUBLICKEYBYTES
2239 );
2240 }
2241
2242 /**
2243 * @param int $outlen
2244 * @param string $passwd
2245 * @param string $salt
2246 * @param int $opslimit
2247 * @param int $memlimit
2248 * @param int|null $alg
2249 * @return string
2250 * @throws SodiumException
2251 * @throws TypeError
2252 * @psalm-suppress MixedArgument
2253 */
2254 public static function crypto_pwhash(
2255 $outlen,
2256 #[\SensitiveParameter]
2257 $passwd,
2258 $salt,
2259 $opslimit,
2260 $memlimit,
2261 $alg = null
2262 ) {
2263 ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1);
2264 ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2);
2265 ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3);
2266 ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 4);
2267 ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 5);
2268
2269 if (self::useNewSodiumAPI()) {
2270 if (!is_null($alg)) {
2271 ParagonIE_Sodium_Core_Util::declareScalarType($alg, 'int', 6);
2272 return sodium_crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit, $alg);
2273 }
2274 return sodium_crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit);
2275 }
2276 if (self::use_fallback('crypto_pwhash')) {
2277 return (string) call_user_func('\\Sodium\\crypto_pwhash', $outlen, $passwd, $salt, $opslimit, $memlimit);
2278 }
2279 // This is the best we can do.
2280 throw new SodiumException(
2281 'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
2282 );
2283 }
2284
2285 /**
2286 * !Exclusive to sodium_compat!
2287 *
2288 * This returns TRUE if the native crypto_pwhash API is available by libsodium.
2289 * This returns FALSE if only sodium_compat is available.
2290 *
2291 * @return bool
2292 */
2293 public static function crypto_pwhash_is_available()
2294 {
2295 if (self::useNewSodiumAPI()) {
2296 return true;
2297 }
2298 if (self::use_fallback('crypto_pwhash')) {
2299 return true;
2300 }
2301 return false;
2302 }
2303
2304 /**
2305 * @param string $passwd
2306 * @param int $opslimit
2307 * @param int $memlimit
2308 * @return string
2309 * @throws SodiumException
2310 * @throws TypeError
2311 * @psalm-suppress MixedArgument
2312 */
2313 public static function crypto_pwhash_str(
2314 #[\SensitiveParameter]
2315 $passwd,
2316 $opslimit,
2317 $memlimit
2318 ) {
2319 ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
2320 ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
2321 ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
2322
2323 if (self::useNewSodiumAPI()) {
2324 return sodium_crypto_pwhash_str($passwd, $opslimit, $memlimit);
2325 }
2326 if (self::use_fallback('crypto_pwhash_str')) {
2327 return (string) call_user_func('\\Sodium\\crypto_pwhash_str', $passwd, $opslimit, $memlimit);
2328 }
2329 // This is the best we can do.
2330 throw new SodiumException(
2331 'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
2332 );
2333 }
2334
2335 /**
2336 * Do we need to rehash this password?
2337 *
2338 * @param string $hash
2339 * @param int $opslimit
2340 * @param int $memlimit
2341 * @return bool
2342 * @throws SodiumException
2343 */
2344 public static function crypto_pwhash_str_needs_rehash(
2345 #[\SensitiveParameter]
2346 $hash,
2347 $opslimit,
2348 $memlimit
2349 ) {
2350 ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 1);
2351 ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
2352 ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
2353
2354 // Just grab the first 4 pieces.
2355 $pieces = explode('$', (string) $hash);
2356 $prefix = implode('$', array_slice($pieces, 0, 4));
2357
2358 // Rebuild the expected header.
2359 /** @var int $ops */
2360 $ops = (int) $opslimit;
2361 /** @var int $mem */
2362 $mem = (int) $memlimit >> 10;
2363 $encoded = self::CRYPTO_PWHASH_STRPREFIX . 'v=19$m=' . $mem . ',t=' . $ops . ',p=1';
2364
2365 // Do they match? If so, we don't need to rehash, so return false.
2366 return !ParagonIE_Sodium_Core_Util::hashEquals($encoded, $prefix);
2367 }
2368
2369 /**
2370 * @param string $passwd
2371 * @param string $hash
2372 * @return bool
2373 * @throws SodiumException
2374 * @throws TypeError
2375 * @psalm-suppress MixedArgument
2376 */
2377 public static function crypto_pwhash_str_verify(
2378 #[\SensitiveParameter]
2379 $passwd,
2380 #[\SensitiveParameter]
2381 $hash
2382 ) {
2383 ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
2384 ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 2);
2385
2386 if (self::useNewSodiumAPI()) {
2387 return (bool) sodium_crypto_pwhash_str_verify($passwd, $hash);
2388 }
2389 if (self::use_fallback('crypto_pwhash_str_verify')) {
2390 return (bool) call_user_func('\\Sodium\\crypto_pwhash_str_verify', $passwd, $hash);
2391 }
2392 // This is the best we can do.
2393 throw new SodiumException(
2394 'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
2395 );
2396 }
2397
2398 /**
2399 * @param int $outlen
2400 * @param string $passwd
2401 * @param string $salt
2402 * @param int $opslimit
2403 * @param int $memlimit
2404 * @return string
2405 * @throws SodiumException
2406 * @throws TypeError
2407 */
2408 public static function crypto_pwhash_scryptsalsa208sha256(
2409 $outlen,
2410 #[\SensitiveParameter]
2411 $passwd,
2412 $salt,
2413 $opslimit,
2414 $memlimit
2415 ) {
2416 ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1);
2417 ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2);
2418 ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3);
2419 ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 4);
2420 ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 5);
2421
2422 if (self::useNewSodiumAPI()) {
2423 return (string) sodium_crypto_pwhash_scryptsalsa208sha256(
2424 (int) $outlen,
2425 (string) $passwd,
2426 (string) $salt,
2427 (int) $opslimit,
2428 (int) $memlimit
2429 );
2430 }
2431 if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256')) {
2432 return (string) call_user_func(
2433 '\\Sodium\\crypto_pwhash_scryptsalsa208sha256',
2434 (int) $outlen,
2435 (string) $passwd,
2436 (string) $salt,
2437 (int) $opslimit,
2438 (int) $memlimit
2439 );
2440 }
2441 // This is the best we can do.
2442 throw new SodiumException(
2443 'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP'
2444 );
2445 }
2446
2447 /**
2448 * !Exclusive to sodium_compat!
2449 *
2450 * This returns TRUE if the native crypto_pwhash API is available by libsodium.
2451 * This returns FALSE if only sodium_compat is available.
2452 *
2453 * @return bool
2454 */
2455 public static function crypto_pwhash_scryptsalsa208sha256_is_available()
2456 {
2457 if (self::useNewSodiumAPI()) {
2458 return true;
2459 }
2460 if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256')) {
2461 return true;
2462 }
2463 return false;
2464 }
2465
2466 /**
2467 * @param string $passwd
2468 * @param int $opslimit
2469 * @param int $memlimit
2470 * @return string
2471 * @throws SodiumException
2472 * @throws TypeError
2473 */
2474 public static function crypto_pwhash_scryptsalsa208sha256_str(
2475 #[\SensitiveParameter]
2476 $passwd,
2477 $opslimit,
2478 $memlimit
2479 ) {
2480 ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
2481 ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
2482 ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
2483
2484 if (self::useNewSodiumAPI()) {
2485 return (string) sodium_crypto_pwhash_scryptsalsa208sha256_str(
2486 (string) $passwd,
2487 (int) $opslimit,
2488 (int) $memlimit
2489 );
2490 }
2491 if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256_str')) {
2492 return (string) call_user_func(
2493 '\\Sodium\\crypto_pwhash_scryptsalsa208sha256_str',
2494 (string) $passwd,
2495 (int) $opslimit,
2496 (int) $memlimit
2497 );
2498 }
2499 // This is the best we can do.
2500 throw new SodiumException(
2501 'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP'
2502 );
2503 }
2504
2505 /**
2506 * @param string $passwd
2507 * @param string $hash
2508 * @return bool
2509 * @throws SodiumException
2510 * @throws TypeError
2511 */
2512 public static function crypto_pwhash_scryptsalsa208sha256_str_verify(
2513 #[\SensitiveParameter]
2514 $passwd,
2515 #[\SensitiveParameter]
2516 $hash
2517 ) {
2518 ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
2519 ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 2);
2520
2521 if (self::useNewSodiumAPI()) {
2522 return (bool) sodium_crypto_pwhash_scryptsalsa208sha256_str_verify(
2523 (string) $passwd,
2524 (string) $hash
2525 );
2526 }
2527 if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256_str_verify')) {
2528 return (bool) call_user_func(
2529 '\\Sodium\\crypto_pwhash_scryptsalsa208sha256_str_verify',
2530 (string) $passwd,
2531 (string) $hash
2532 );
2533 }
2534 // This is the best we can do.
2535 throw new SodiumException(
2536 'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP'
2537 );
2538 }
2539
2540 /**
2541 * Calculate the shared secret between your secret key and your
2542 * recipient's public key.
2543 *
2544 * Algorithm: X25519 (ECDH over Curve25519)
2545 *
2546 * @param string $secretKey
2547 * @param string $publicKey
2548 * @return string
2549 * @throws SodiumException
2550 * @throws TypeError
2551 * @psalm-suppress MixedArgument
2552 */
2553 public static function crypto_scalarmult(
2554 #[\SensitiveParameter]
2555 $secretKey,
2556 $publicKey
2557 ) {
2558 /* Type checks: */
2559 ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
2560 ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
2561
2562 /* Input validation: */
2563 if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
2564 throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
2565 }
2566 if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
2567 throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
2568 }
2569
2570 if (self::useNewSodiumAPI()) {
2571 return sodium_crypto_scalarmult($secretKey, $publicKey);
2572 }
2573 if (self::use_fallback('crypto_scalarmult')) {
2574 return (string) call_user_func('\\Sodium\\crypto_scalarmult', $secretKey, $publicKey);
2575 }
2576
2577 /* Output validation: Forbid all-zero keys */
2578 if (ParagonIE_Sodium_Core_Util::hashEquals($secretKey, str_repeat("\0", self::CRYPTO_BOX_SECRETKEYBYTES))) {
2579 throw new SodiumException('Zero secret key is not allowed');
2580 }
2581 if (ParagonIE_Sodium_Core_Util::hashEquals($publicKey, str_repeat("\0", self::CRYPTO_BOX_PUBLICKEYBYTES))) {
2582 throw new SodiumException('Zero public key is not allowed');
2583 }
2584 if (PHP_INT_SIZE === 4) {
2585 return ParagonIE_Sodium_Crypto32::scalarmult($secretKey, $publicKey);
2586 }
2587 return ParagonIE_Sodium_Crypto::scalarmult($secretKey, $publicKey);
2588 }
2589
2590 /**
2591 * Calculate an X25519 public key from an X25519 secret key.
2592 *
2593 * @param string $secretKey
2594 * @return string
2595 * @throws SodiumException
2596 * @throws TypeError
2597 * @psalm-suppress TooFewArguments
2598 * @psalm-suppress MixedArgument
2599 */
2600 public static function crypto_scalarmult_base(
2601 #[\SensitiveParameter]
2602 $secretKey
2603 ) {
2604 /* Type checks: */
2605 ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
2606
2607 /* Input validation: */
2608 if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
2609 throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
2610 }
2611
2612 if (self::useNewSodiumAPI()) {
2613 return sodium_crypto_scalarmult_base($secretKey);
2614 }
2615 if (self::use_fallback('crypto_scalarmult_base')) {
2616 return (string) call_user_func('\\Sodium\\crypto_scalarmult_base', $secretKey);
2617 }
2618 if (ParagonIE_Sodium_Core_Util::hashEquals($secretKey, str_repeat("\0", self::CRYPTO_BOX_SECRETKEYBYTES))) {
2619 throw new SodiumException('Zero secret key is not allowed');
2620 }
2621 if (PHP_INT_SIZE === 4) {
2622 return ParagonIE_Sodium_Crypto32::scalarmult_base($secretKey);
2623 }
2624 return ParagonIE_Sodium_Crypto::scalarmult_base($secretKey);
2625 }
2626
2627 /**
2628 * Authenticated symmetric-key encryption.
2629 *
2630 * Algorithm: XSalsa20-Poly1305
2631 *
2632 * @param string $plaintext The message you're encrypting
2633 * @param string $nonce A Number to be used Once; must be 24 bytes
2634 * @param string $key Symmetric encryption key
2635 * @return string Ciphertext with Poly1305 MAC
2636 * @throws SodiumException
2637 * @throws TypeError
2638 * @psalm-suppress MixedArgument
2639 */
2640 public static function crypto_secretbox(
2641 #[\SensitiveParameter]
2642 $plaintext,
2643 $nonce,
2644 #[\SensitiveParameter]
2645 $key
2646 ) {
2647 /* Type checks: */
2648 ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
2649 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
2650 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
2651
2652 /* Input validation: */
2653 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) {
2654 throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
2655 }
2656 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) {
2657 throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
2658 }
2659
2660 if (self::useNewSodiumAPI()) {
2661 return sodium_crypto_secretbox($plaintext, $nonce, $key);
2662 }
2663 if (self::use_fallback('crypto_secretbox')) {
2664 return (string) call_user_func('\\Sodium\\crypto_secretbox', $plaintext, $nonce, $key);
2665 }
2666 if (PHP_INT_SIZE === 4) {
2667 return ParagonIE_Sodium_Crypto32::secretbox($plaintext, $nonce, $key);
2668 }
2669 return ParagonIE_Sodium_Crypto::secretbox($plaintext, $nonce, $key);
2670 }
2671
2672 /**
2673 * Decrypts a message previously encrypted with crypto_secretbox().
2674 *
2675 * @param string $ciphertext Ciphertext with Poly1305 MAC
2676 * @param string $nonce A Number to be used Once; must be 24 bytes
2677 * @param string $key Symmetric encryption key
2678 * @return string Original plaintext message
2679 * @throws SodiumException
2680 * @throws TypeError
2681 * @psalm-suppress MixedArgument
2682 * @psalm-suppress MixedInferredReturnType
2683 * @psalm-suppress MixedReturnStatement
2684 */
2685 public static function crypto_secretbox_open(
2686 $ciphertext,
2687 $nonce,
2688 #[\SensitiveParameter]
2689 $key
2690 ) {
2691 /* Type checks: */
2692 ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
2693 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
2694 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
2695
2696 /* Input validation: */
2697 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) {
2698 throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
2699 }
2700 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) {
2701 throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
2702 }
2703
2704 if (self::useNewSodiumAPI()) {
2705 /**
2706 * @psalm-suppress InvalidReturnStatement
2707 * @psalm-suppress FalsableReturnStatement
2708 */
2709 return sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
2710 }
2711 if (self::use_fallback('crypto_secretbox_open')) {
2712 return call_user_func('\\Sodium\\crypto_secretbox_open', $ciphertext, $nonce, $key);
2713 }
2714 if (PHP_INT_SIZE === 4) {
2715 return ParagonIE_Sodium_Crypto32::secretbox_open($ciphertext, $nonce, $key);
2716 }
2717 return ParagonIE_Sodium_Crypto::secretbox_open($ciphertext, $nonce, $key);
2718 }
2719
2720 /**
2721 * Return a secure random key for use with crypto_secretbox
2722 *
2723 * @return string
2724 * @throws Exception
2725 * @throws Error
2726 */
2727 public static function crypto_secretbox_keygen()
2728 {
2729 return random_bytes(self::CRYPTO_SECRETBOX_KEYBYTES);
2730 }
2731
2732 /**
2733 * Authenticated symmetric-key encryption.
2734 *
2735 * Algorithm: XChaCha20-Poly1305
2736 *
2737 * @param string $plaintext The message you're encrypting
2738 * @param string $nonce A Number to be used Once; must be 24 bytes
2739 * @param string $key Symmetric encryption key
2740 * @return string Ciphertext with Poly1305 MAC
2741 * @throws SodiumException
2742 * @throws TypeError
2743 * @psalm-suppress MixedArgument
2744 */
2745 public static function crypto_secretbox_xchacha20poly1305($plaintext, $nonce, $key)
2746 {
2747 /* Type checks: */
2748 ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
2749 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
2750 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
2751
2752 /* Input validation: */
2753 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) {
2754 throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
2755 }
2756 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) {
2757 throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
2758 }
2759 if (PHP_INT_SIZE === 4) {
2760 return ParagonIE_Sodium_Crypto32::secretbox_xchacha20poly1305($plaintext, $nonce, $key);
2761 }
2762 return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305($plaintext, $nonce, $key);
2763 }
2764 /**
2765 * Decrypts a message previously encrypted with crypto_secretbox_xchacha20poly1305().
2766 *
2767 * @param string $ciphertext Ciphertext with Poly1305 MAC
2768 * @param string $nonce A Number to be used Once; must be 24 bytes
2769 * @param string $key Symmetric encryption key
2770 * @return string Original plaintext message
2771 * @throws SodiumException
2772 * @throws TypeError
2773 * @psalm-suppress MixedArgument
2774 */
2775 public static function crypto_secretbox_xchacha20poly1305_open(
2776 $ciphertext,
2777 $nonce,
2778 #[\SensitiveParameter]
2779 $key
2780 ) {
2781 /* Type checks: */
2782 ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
2783 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
2784 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
2785
2786 /* Input validation: */
2787 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) {
2788 throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
2789 }
2790 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) {
2791 throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
2792 }
2793 if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_SECRETBOX_MACBYTES) {
2794 throw new SodiumException("Ciphertext must be at least CRYPTO_SECRETBOX_MACBYTES long");
2795 }
2796
2797 if (PHP_INT_SIZE === 4) {
2798 return ParagonIE_Sodium_Crypto32::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key);
2799 }
2800 return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key);
2801 }
2802
2803 /**
2804 * @param string $key
2805 * @return array<int, string> Returns a state and a header.
2806 * @throws Exception
2807 * @throws SodiumException
2808 */
2809 public static function crypto_secretstream_xchacha20poly1305_init_push(
2810 #[\SensitiveParameter]
2811 $key
2812 ) {
2813 if (PHP_INT_SIZE === 4) {
2814 return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_push($key);
2815 }
2816 return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_push($key);
2817 }
2818
2819 /**
2820 * @param string $header
2821 * @param string $key
2822 * @return string Returns a state.
2823 * @throws Exception
2824 */
2825 public static function crypto_secretstream_xchacha20poly1305_init_pull(
2826 $header,
2827 #[\SensitiveParameter]
2828 $key
2829 ) {
2830 if (ParagonIE_Sodium_Core_Util::strlen($header) < self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES) {
2831 throw new SodiumException(
2832 'header size should be SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES bytes'
2833 );
2834 }
2835 if (PHP_INT_SIZE === 4) {
2836 return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_pull($key, $header);
2837 }
2838 return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_pull($key, $header);
2839 }
2840
2841 /**
2842 * @param string $state
2843 * @param string $msg
2844 * @param string $aad
2845 * @param int $tag
2846 * @return string
2847 * @throws SodiumException
2848 */
2849 public static function crypto_secretstream_xchacha20poly1305_push(
2850 #[\SensitiveParameter]
2851 &$state,
2852 #[\SensitiveParameter]
2853 $msg,
2854 $aad = '',
2855 $tag = 0
2856 ) {
2857 if (PHP_INT_SIZE === 4) {
2858 return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_push(
2859 $state,
2860 $msg,
2861 $aad,
2862 $tag
2863 );
2864 }
2865 return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_push(
2866 $state,
2867 $msg,
2868 $aad,
2869 $tag
2870 );
2871 }
2872
2873 /**
2874 * @param string $state
2875 * @param string $msg
2876 * @param string $aad
2877 * @return bool|array{0: string, 1: int}
2878 * @throws SodiumException
2879 */
2880 public static function crypto_secretstream_xchacha20poly1305_pull(
2881 #[\SensitiveParameter]
2882 &$state,
2883 $msg,
2884 $aad = ''
2885 ) {
2886 if (PHP_INT_SIZE === 4) {
2887 return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_pull(
2888 $state,
2889 $msg,
2890 $aad
2891 );
2892 }
2893 return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_pull(
2894 $state,
2895 $msg,
2896 $aad
2897 );
2898 }
2899
2900 /**
2901 * @return string
2902 * @throws Exception
2903 */
2904 public static function crypto_secretstream_xchacha20poly1305_keygen()
2905 {
2906 return random_bytes(self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES);
2907 }
2908
2909 /**
2910 * @param string $state
2911 * @return void
2912 * @throws SodiumException
2913 */
2914 public static function crypto_secretstream_xchacha20poly1305_rekey(
2915 #[\SensitiveParameter]
2916 &$state
2917 ) {
2918 if (PHP_INT_SIZE === 4) {
2919 ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_rekey($state);
2920 } else {
2921 ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_rekey($state);
2922 }
2923 }
2924
2925 /**
2926 * Calculates a SipHash-2-4 hash of a message for a given key.
2927 *
2928 * @param string $message Input message
2929 * @param string $key SipHash-2-4 key
2930 * @return string Hash
2931 * @throws SodiumException
2932 * @throws TypeError
2933 * @psalm-suppress MixedArgument
2934 * @psalm-suppress MixedInferredReturnType
2935 * @psalm-suppress MixedReturnStatement
2936 */
2937 public static function crypto_shorthash(
2938 $message,
2939 #[\SensitiveParameter]
2940 $key
2941 ) {
2942 /* Type checks: */
2943 ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
2944 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2);
2945
2946 /* Input validation: */
2947 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SHORTHASH_KEYBYTES) {
2948 throw new SodiumException('Argument 2 must be CRYPTO_SHORTHASH_KEYBYTES long.');
2949 }
2950
2951 if (self::useNewSodiumAPI()) {
2952 return sodium_crypto_shorthash($message, $key);
2953 }
2954 if (self::use_fallback('crypto_shorthash')) {
2955 return (string) call_user_func('\\Sodium\\crypto_shorthash', $message, $key);
2956 }
2957 if (PHP_INT_SIZE === 4) {
2958 return ParagonIE_Sodium_Core32_SipHash::sipHash24($message, $key);
2959 }
2960 return ParagonIE_Sodium_Core_SipHash::sipHash24($message, $key);
2961 }
2962
2963 /**
2964 * Return a secure random key for use with crypto_shorthash
2965 *
2966 * @return string
2967 * @throws Exception
2968 * @throws Error
2969 */
2970 public static function crypto_shorthash_keygen()
2971 {
2972 return random_bytes(self::CRYPTO_SHORTHASH_KEYBYTES);
2973 }
2974
2975 /**
2976 * Returns a signed message. You probably want crypto_sign_detached()
2977 * instead, which only returns the signature.
2978 *
2979 * Algorithm: Ed25519 (EdDSA over Curve25519)
2980 *
2981 * @param string $message Message to be signed.
2982 * @param string $secretKey Secret signing key.
2983 * @return string Signed message (signature is prefixed).
2984 * @throws SodiumException
2985 * @throws TypeError
2986 * @psalm-suppress MixedArgument
2987 * @psalm-suppress MixedInferredReturnType
2988 * @psalm-suppress MixedReturnStatement
2989 */
2990 public static function crypto_sign(
2991 $message,
2992 #[\SensitiveParameter]
2993 $secretKey
2994 ) {
2995 /* Type checks: */
2996 ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
2997 ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 2);
2998
2999 /* Input validation: */
3000 if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
3001 throw new SodiumException('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES long.');
3002 }
3003
3004 if (self::useNewSodiumAPI()) {
3005 return sodium_crypto_sign($message, $secretKey);
3006 }
3007 if (self::use_fallback('crypto_sign')) {
3008 return (string) call_user_func('\\Sodium\\crypto_sign', $message, $secretKey);
3009 }
3010 if (PHP_INT_SIZE === 4) {
3011 return ParagonIE_Sodium_Crypto32::sign($message, $secretKey);
3012 }
3013 return ParagonIE_Sodium_Crypto::sign($message, $secretKey);
3014 }
3015
3016 /**
3017 * Validates a signed message then returns the message.
3018 *
3019 * @param string $signedMessage A signed message
3020 * @param string $publicKey A public key
3021 * @return string The original message (if the signature is
3022 * valid for this public key)
3023 * @throws SodiumException
3024 * @throws TypeError
3025 * @psalm-suppress MixedArgument
3026 * @psalm-suppress MixedInferredReturnType
3027 * @psalm-suppress MixedReturnStatement
3028 */
3029 public static function crypto_sign_open(
3030 $signedMessage,
3031 $publicKey
3032 ) {
3033 /* Type checks: */
3034 ParagonIE_Sodium_Core_Util::declareScalarType($signedMessage, 'string', 1);
3035 ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
3036
3037 /* Input validation: */
3038 if (ParagonIE_Sodium_Core_Util::strlen($signedMessage) < self::CRYPTO_SIGN_BYTES) {
3039 throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_BYTES long.');
3040 }
3041 if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) {
3042 throw new SodiumException('Argument 2 must be CRYPTO_SIGN_PUBLICKEYBYTES long.');
3043 }
3044
3045 if (self::useNewSodiumAPI()) {
3046 /**
3047 * @psalm-suppress InvalidReturnStatement
3048 * @psalm-suppress FalsableReturnStatement
3049 */
3050 return sodium_crypto_sign_open($signedMessage, $publicKey);
3051 }
3052 if (self::use_fallback('crypto_sign_open')) {
3053 return call_user_func('\\Sodium\\crypto_sign_open', $signedMessage, $publicKey);
3054 }
3055 if (PHP_INT_SIZE === 4) {
3056 return ParagonIE_Sodium_Crypto32::sign_open($signedMessage, $publicKey);
3057 }
3058 return ParagonIE_Sodium_Crypto::sign_open($signedMessage, $publicKey);
3059 }
3060
3061 /**
3062 * Generate a new random Ed25519 keypair.
3063 *
3064 * @return string
3065 * @throws SodiumException
3066 * @throws TypeError
3067 */
3068 public static function crypto_sign_keypair()
3069 {
3070 if (self::useNewSodiumAPI()) {
3071 return sodium_crypto_sign_keypair();
3072 }
3073 if (self::use_fallback('crypto_sign_keypair')) {
3074 return (string) call_user_func('\\Sodium\\crypto_sign_keypair');
3075 }
3076 if (PHP_INT_SIZE === 4) {
3077 return ParagonIE_Sodium_Core32_Ed25519::keypair();
3078 }
3079 return ParagonIE_Sodium_Core_Ed25519::keypair();
3080 }
3081
3082 /**
3083 * @param string $sk
3084 * @param string $pk
3085 * @return string
3086 * @throws SodiumException
3087 */
3088 public static function crypto_sign_keypair_from_secretkey_and_publickey(
3089 #[\SensitiveParameter]
3090 $sk,
3091 $pk
3092 ) {
3093 ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1);
3094 ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1);
3095 $sk = (string) $sk;
3096 $pk = (string) $pk;
3097
3098 if (ParagonIE_Sodium_Core_Util::strlen($sk) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
3099 throw new SodiumException('secretkey should be SODIUM_CRYPTO_SIGN_SECRETKEYBYTES bytes');
3100 }
3101 if (ParagonIE_Sodium_Core_Util::strlen($pk) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) {
3102 throw new SodiumException('publickey should be SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES bytes');
3103 }
3104
3105 if (self::useNewSodiumAPI()) {
3106 return sodium_crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk);
3107 }
3108 return $sk . $pk;
3109 }
3110
3111 /**
3112 * Generate an Ed25519 keypair from a seed.
3113 *
3114 * @param string $seed Input seed
3115 * @return string Keypair
3116 * @throws SodiumException
3117 * @throws TypeError
3118 * @psalm-suppress MixedArgument
3119 */
3120 public static function crypto_sign_seed_keypair(
3121 #[\SensitiveParameter]
3122 $seed
3123 ) {
3124 ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
3125
3126 if (self::useNewSodiumAPI()) {
3127 return sodium_crypto_sign_seed_keypair($seed);
3128 }
3129 if (self::use_fallback('crypto_sign_keypair')) {
3130 return (string) call_user_func('\\Sodium\\crypto_sign_seed_keypair', $seed);
3131 }
3132 $publicKey = '';
3133 $secretKey = '';
3134 if (PHP_INT_SIZE === 4) {
3135 ParagonIE_Sodium_Core32_Ed25519::seed_keypair($publicKey, $secretKey, $seed);
3136 } else {
3137 ParagonIE_Sodium_Core_Ed25519::seed_keypair($publicKey, $secretKey, $seed);
3138 }
3139 return $secretKey . $publicKey;
3140 }
3141
3142 /**
3143 * Extract an Ed25519 public key from an Ed25519 keypair.
3144 *
3145 * @param string $keypair Keypair
3146 * @return string Public key
3147 * @throws SodiumException
3148 * @throws TypeError
3149 * @psalm-suppress MixedArgument
3150 */
3151 public static function crypto_sign_publickey(
3152 #[\SensitiveParameter]
3153 $keypair
3154 ) {
3155 /* Type checks: */
3156 ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
3157
3158 /* Input validation: */
3159 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_SIGN_KEYPAIRBYTES) {
3160 throw new SodiumException('Argument 1 must be CRYPTO_SIGN_KEYPAIRBYTES long.');
3161 }
3162
3163 if (self::useNewSodiumAPI()) {
3164 return sodium_crypto_sign_publickey($keypair);
3165 }
3166 if (self::use_fallback('crypto_sign_publickey')) {
3167 return (string) call_user_func('\\Sodium\\crypto_sign_publickey', $keypair);
3168 }
3169 if (PHP_INT_SIZE === 4) {
3170 return ParagonIE_Sodium_Core32_Ed25519::publickey($keypair);
3171 }
3172 return ParagonIE_Sodium_Core_Ed25519::publickey($keypair);
3173 }
3174
3175 /**
3176 * Calculate an Ed25519 public key from an Ed25519 secret key.
3177 *
3178 * @param string $secretKey Your Ed25519 secret key
3179 * @return string The corresponding Ed25519 public key
3180 * @throws SodiumException
3181 * @throws TypeError
3182 * @psalm-suppress MixedArgument
3183 */
3184 public static function crypto_sign_publickey_from_secretkey(
3185 #[\SensitiveParameter]
3186 $secretKey
3187 ) {
3188 /* Type checks: */
3189 ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
3190
3191 /* Input validation: */
3192 if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
3193 throw new SodiumException('Argument 1 must be CRYPTO_SIGN_SECRETKEYBYTES long.');
3194 }
3195
3196 if (self::useNewSodiumAPI()) {
3197 return sodium_crypto_sign_publickey_from_secretkey($secretKey);
3198 }
3199 if (self::use_fallback('crypto_sign_publickey_from_secretkey')) {
3200 return (string) call_user_func('\\Sodium\\crypto_sign_publickey_from_secretkey', $secretKey);
3201 }
3202 if (PHP_INT_SIZE === 4) {
3203 return ParagonIE_Sodium_Core32_Ed25519::publickey_from_secretkey($secretKey);
3204 }
3205 return ParagonIE_Sodium_Core_Ed25519::publickey_from_secretkey($secretKey);
3206 }
3207
3208 /**
3209 * Extract an Ed25519 secret key from an Ed25519 keypair.
3210 *
3211 * @param string $keypair Keypair
3212 * @return string Secret key
3213 * @throws SodiumException
3214 * @throws TypeError
3215 * @psalm-suppress MixedArgument
3216 */
3217 public static function crypto_sign_secretkey(
3218 #[\SensitiveParameter]
3219 $keypair
3220 ) {
3221 /* Type checks: */
3222 ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
3223
3224 /* Input validation: */
3225 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_SIGN_KEYPAIRBYTES) {
3226 throw new SodiumException('Argument 1 must be CRYPTO_SIGN_KEYPAIRBYTES long.');
3227 }
3228
3229 if (self::useNewSodiumAPI()) {
3230 return sodium_crypto_sign_secretkey($keypair);
3231 }
3232 if (self::use_fallback('crypto_sign_secretkey')) {
3233 return (string) call_user_func('\\Sodium\\crypto_sign_secretkey', $keypair);
3234 }
3235 if (PHP_INT_SIZE === 4) {
3236 return ParagonIE_Sodium_Core32_Ed25519::secretkey($keypair);
3237 }
3238 return ParagonIE_Sodium_Core_Ed25519::secretkey($keypair);
3239 }
3240
3241 /**
3242 * Calculate the Ed25519 signature of a message and return ONLY the signature.
3243 *
3244 * Algorithm: Ed25519 (EdDSA over Curve25519)
3245 *
3246 * @param string $message Message to be signed
3247 * @param string $secretKey Secret signing key
3248 * @return string Digital signature
3249 * @throws SodiumException
3250 * @throws TypeError
3251 * @psalm-suppress MixedArgument
3252 */
3253 public static function crypto_sign_detached(
3254 $message,
3255 #[\SensitiveParameter]
3256 $secretKey
3257 ) {
3258 /* Type checks: */
3259 ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
3260 ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 2);
3261
3262 /* Input validation: */
3263 if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
3264 throw new SodiumException('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES long.');
3265 }
3266
3267 if (self::useNewSodiumAPI()) {
3268 return sodium_crypto_sign_detached($message, $secretKey);
3269 }
3270 if (self::use_fallback('crypto_sign_detached')) {
3271 return (string) call_user_func('\\Sodium\\crypto_sign_detached', $message, $secretKey);
3272 }
3273 if (PHP_INT_SIZE === 4) {
3274 return ParagonIE_Sodium_Crypto32::sign_detached($message, $secretKey);
3275 }
3276 return ParagonIE_Sodium_Crypto::sign_detached($message, $secretKey);
3277 }
3278
3279 /**
3280 * Verify the Ed25519 signature of a message.
3281 *
3282 * @param string $signature Digital sginature
3283 * @param string $message Message to be verified
3284 * @param string $publicKey Public key
3285 * @return bool TRUE if this signature is good for this public key;
3286 * FALSE otherwise
3287 * @throws SodiumException
3288 * @throws TypeError
3289 * @psalm-suppress MixedArgument
3290 */
3291 public static function crypto_sign_verify_detached($signature, $message, $publicKey)
3292 {
3293 /* Type checks: */
3294 ParagonIE_Sodium_Core_Util::declareScalarType($signature, 'string', 1);
3295 ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2);
3296 ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 3);
3297
3298 /* Input validation: */
3299 if (ParagonIE_Sodium_Core_Util::strlen($signature) !== self::CRYPTO_SIGN_BYTES) {
3300 throw new SodiumException('Argument 1 must be CRYPTO_SIGN_BYTES long.');
3301 }
3302 if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) {
3303 throw new SodiumException('Argument 3 must be CRYPTO_SIGN_PUBLICKEYBYTES long.');
3304 }
3305
3306 if (self::useNewSodiumAPI()) {
3307 return sodium_crypto_sign_verify_detached($signature, $message, $publicKey);
3308 }
3309 if (self::use_fallback('crypto_sign_verify_detached')) {
3310 return (bool) call_user_func(
3311 '\\Sodium\\crypto_sign_verify_detached',
3312 $signature,
3313 $message,
3314 $publicKey
3315 );
3316 }
3317 if (PHP_INT_SIZE === 4) {
3318 return ParagonIE_Sodium_Crypto32::sign_verify_detached($signature, $message, $publicKey);
3319 }
3320 return ParagonIE_Sodium_Crypto::sign_verify_detached($signature, $message, $publicKey);
3321 }
3322
3323 /**
3324 * Convert an Ed25519 public key to a Curve25519 public key
3325 *
3326 * @param string $pk
3327 * @return string
3328 * @throws SodiumException
3329 * @throws TypeError
3330 * @psalm-suppress MixedArgument
3331 */
3332 public static function crypto_sign_ed25519_pk_to_curve25519($pk)
3333 {
3334 /* Type checks: */
3335 ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1);
3336
3337 /* Input validation: */
3338 if (ParagonIE_Sodium_Core_Util::strlen($pk) < self::CRYPTO_SIGN_PUBLICKEYBYTES) {
3339 throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_PUBLICKEYBYTES long.');
3340 }
3341 if (self::useNewSodiumAPI()) {
3342 if (is_callable('crypto_sign_ed25519_pk_to_curve25519')) {
3343 return (string) sodium_crypto_sign_ed25519_pk_to_curve25519($pk);
3344 }
3345 }
3346 if (self::use_fallback('crypto_sign_ed25519_pk_to_curve25519')) {
3347 return (string) call_user_func('\\Sodium\\crypto_sign_ed25519_pk_to_curve25519', $pk);
3348 }
3349 if (PHP_INT_SIZE === 4) {
3350 return ParagonIE_Sodium_Core32_Ed25519::pk_to_curve25519($pk);
3351 }
3352 return ParagonIE_Sodium_Core_Ed25519::pk_to_curve25519($pk);
3353 }
3354
3355 /**
3356 * Convert an Ed25519 secret key to a Curve25519 secret key
3357 *
3358 * @param string $sk
3359 * @return string
3360 * @throws SodiumException
3361 * @throws TypeError
3362 * @psalm-suppress MixedArgument
3363 */
3364 public static function crypto_sign_ed25519_sk_to_curve25519(
3365 #[\SensitiveParameter]
3366 $sk
3367 ) {
3368 /* Type checks: */
3369 ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1);
3370
3371 /* Input validation: */
3372 if (ParagonIE_Sodium_Core_Util::strlen($sk) < self::CRYPTO_SIGN_SEEDBYTES) {
3373 throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_SEEDBYTES long.');
3374 }
3375 if (self::useNewSodiumAPI()) {
3376 if (is_callable('crypto_sign_ed25519_sk_to_curve25519')) {
3377 return sodium_crypto_sign_ed25519_sk_to_curve25519($sk);
3378 }
3379 }
3380 if (self::use_fallback('crypto_sign_ed25519_sk_to_curve25519')) {
3381 return (string) call_user_func('\\Sodium\\crypto_sign_ed25519_sk_to_curve25519', $sk);
3382 }
3383
3384 $h = hash('sha512', ParagonIE_Sodium_Core_Util::substr($sk, 0, 32), true);
3385 $h[0] = ParagonIE_Sodium_Core_Util::intToChr(
3386 ParagonIE_Sodium_Core_Util::chrToInt($h[0]) & 248
3387 );
3388 $h[31] = ParagonIE_Sodium_Core_Util::intToChr(
3389 (ParagonIE_Sodium_Core_Util::chrToInt($h[31]) & 127) | 64
3390 );
3391 return ParagonIE_Sodium_Core_Util::substr($h, 0, 32);
3392 }
3393
3394 /**
3395 * Expand a key and nonce into a keystream of pseudorandom bytes.
3396 *
3397 * @param int $len Number of bytes desired
3398 * @param string $nonce Number to be used Once; must be 24 bytes
3399 * @param string $key XSalsa20 key
3400 * @return string Pseudorandom stream that can be XORed with messages
3401 * to provide encryption (but not authentication; see
3402 * Poly1305 or crypto_auth() for that, which is not
3403 * optional for security)
3404 * @throws SodiumException
3405 * @throws TypeError
3406 * @psalm-suppress MixedArgument
3407 */
3408 public static function crypto_stream(
3409 $len,
3410 $nonce,
3411 #[\SensitiveParameter]
3412 $key
3413 ) {
3414 /* Type checks: */
3415 ParagonIE_Sodium_Core_Util::declareScalarType($len, 'int', 1);
3416 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
3417 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
3418
3419 /* Input validation: */
3420 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_NONCEBYTES) {
3421 throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
3422 }
3423 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_KEYBYTES) {
3424 throw new SodiumException('Argument 3 must be CRYPTO_STREAM_KEYBYTES long.');
3425 }
3426
3427 if (self::useNewSodiumAPI()) {
3428 return sodium_crypto_stream($len, $nonce, $key);
3429 }
3430 if (self::use_fallback('crypto_stream')) {
3431 return (string) call_user_func('\\Sodium\\crypto_stream', $len, $nonce, $key);
3432 }
3433 if (PHP_INT_SIZE === 4) {
3434 return ParagonIE_Sodium_Core32_XSalsa20::xsalsa20($len, $nonce, $key);
3435 }
3436 return ParagonIE_Sodium_Core_XSalsa20::xsalsa20($len, $nonce, $key);
3437 }
3438
3439 /**
3440 * DANGER! UNAUTHENTICATED ENCRYPTION!
3441 *
3442 * Unless you are following expert advice, do not use this feature.
3443 *
3444 * Algorithm: XSalsa20
3445 *
3446 * This DOES NOT provide ciphertext integrity.
3447 *
3448 * @param string $message Plaintext message
3449 * @param string $nonce Number to be used Once; must be 24 bytes
3450 * @param string $key Encryption key
3451 * @return string Encrypted text which is vulnerable to chosen-
3452 * ciphertext attacks unless you implement some
3453 * other mitigation to the ciphertext (i.e.
3454 * Encrypt then MAC)
3455 * @throws SodiumException
3456 * @throws TypeError
3457 * @psalm-suppress MixedArgument
3458 */
3459 public static function crypto_stream_xor(
3460 #[\SensitiveParameter]
3461 $message,
3462 $nonce,
3463 #[\SensitiveParameter]
3464 $key
3465 ) {
3466 /* Type checks: */
3467 ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
3468 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
3469 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
3470
3471 /* Input validation: */
3472 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_NONCEBYTES) {
3473 throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
3474 }
3475 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_KEYBYTES) {
3476 throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
3477 }
3478
3479 if (self::useNewSodiumAPI()) {
3480 return sodium_crypto_stream_xor($message, $nonce, $key);
3481 }
3482 if (self::use_fallback('crypto_stream_xor')) {
3483 return (string) call_user_func('\\Sodium\\crypto_stream_xor', $message, $nonce, $key);
3484 }
3485 if (PHP_INT_SIZE === 4) {
3486 return ParagonIE_Sodium_Core32_XSalsa20::xsalsa20_xor($message, $nonce, $key);
3487 }
3488 return ParagonIE_Sodium_Core_XSalsa20::xsalsa20_xor($message, $nonce, $key);
3489 }
3490
3491 /**
3492 * Return a secure random key for use with crypto_stream
3493 *
3494 * @return string
3495 * @throws Exception
3496 * @throws Error
3497 */
3498 public static function crypto_stream_keygen()
3499 {
3500 return random_bytes(self::CRYPTO_STREAM_KEYBYTES);
3501 }
3502
3503
3504 /**
3505 * Expand a key and nonce into a keystream of pseudorandom bytes.
3506 *
3507 * @param int $len Number of bytes desired
3508 * @param string $nonce Number to be used Once; must be 24 bytes
3509 * @param string $key XChaCha20 key
3510 * @param bool $dontFallback
3511 * @return string Pseudorandom stream that can be XORed with messages
3512 * to provide encryption (but not authentication; see
3513 * Poly1305 or crypto_auth() for that, which is not
3514 * optional for security)
3515 * @throws SodiumException
3516 * @throws TypeError
3517 * @psalm-suppress MixedArgument
3518 */
3519 public static function crypto_stream_xchacha20(
3520 $len,
3521 $nonce,
3522 #[\SensitiveParameter]
3523 $key,
3524 $dontFallback = false
3525 ) {
3526 /* Type checks: */
3527 ParagonIE_Sodium_Core_Util::declareScalarType($len, 'int', 1);
3528 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
3529 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
3530
3531 /* Input validation: */
3532 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) {
3533 throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.');
3534 }
3535 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) {
3536 throw new SodiumException('Argument 3 must be CRYPTO_STREAM_XCHACHA20_KEYBYTES long.');
3537 }
3538
3539 if (self::useNewSodiumAPI() && !$dontFallback) {
3540 return sodium_crypto_stream_xchacha20($len, $nonce, $key);
3541 }
3542 if (PHP_INT_SIZE === 4) {
3543 return ParagonIE_Sodium_Core32_XChaCha20::stream($len, $nonce, $key);
3544 }
3545 return ParagonIE_Sodium_Core_XChaCha20::stream($len, $nonce, $key);
3546 }
3547
3548 /**
3549 * DANGER! UNAUTHENTICATED ENCRYPTION!
3550 *
3551 * Unless you are following expert advice, do not use this feature.
3552 *
3553 * Algorithm: XChaCha20
3554 *
3555 * This DOES NOT provide ciphertext integrity.
3556 *
3557 * @param string $message Plaintext message
3558 * @param string $nonce Number to be used Once; must be 24 bytes
3559 * @param string $key Encryption key
3560 * @return string Encrypted text which is vulnerable to chosen-
3561 * ciphertext attacks unless you implement some
3562 * other mitigation to the ciphertext (i.e.
3563 * Encrypt then MAC)
3564 * @param bool $dontFallback
3565 * @throws SodiumException
3566 * @throws TypeError
3567 * @psalm-suppress MixedArgument
3568 */
3569 public static function crypto_stream_xchacha20_xor(
3570 #[\SensitiveParameter]
3571 $message,
3572 $nonce,
3573 #[\SensitiveParameter]
3574 $key,
3575 $dontFallback = false
3576 ) {
3577 /* Type checks: */
3578 ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
3579 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
3580 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
3581
3582 /* Input validation: */
3583 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) {
3584 throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.');
3585 }
3586 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) {
3587 throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_XCHACHA20_KEYBYTES long.');
3588 }
3589
3590 if (self::useNewSodiumAPI() && !$dontFallback) {
3591 return sodium_crypto_stream_xchacha20_xor($message, $nonce, $key);
3592 }
3593 if (PHP_INT_SIZE === 4) {
3594 return ParagonIE_Sodium_Core32_XChaCha20::streamXorIc($message, $nonce, $key);
3595 }
3596 return ParagonIE_Sodium_Core_XChaCha20::streamXorIc($message, $nonce, $key);
3597 }
3598
3599 /**
3600 * DANGER! UNAUTHENTICATED ENCRYPTION!
3601 *
3602 * Unless you are following expert advice, do not use this feature.
3603 *
3604 * Algorithm: XChaCha20
3605 *
3606 * This DOES NOT provide ciphertext integrity.
3607 *
3608 * @param string $message Plaintext message
3609 * @param string $nonce Number to be used Once; must be 24 bytes
3610 * @param int $counter
3611 * @param string $key Encryption key
3612 * @return string Encrypted text which is vulnerable to chosen-
3613 * ciphertext attacks unless you implement some
3614 * other mitigation to the ciphertext (i.e.
3615 * Encrypt then MAC)
3616 * @param bool $dontFallback
3617 * @throws SodiumException
3618 * @throws TypeError
3619 * @psalm-suppress MixedArgument
3620 */
3621 public static function crypto_stream_xchacha20_xor_ic(
3622 #[\SensitiveParameter]
3623 $message,
3624 $nonce,
3625 $counter,
3626 #[\SensitiveParameter]
3627 $key,
3628 $dontFallback = false
3629 ) {
3630 /* Type checks: */
3631 ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
3632 ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
3633 ParagonIE_Sodium_Core_Util::declareScalarType($counter, 'int', 3);
3634 ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
3635
3636 /* Input validation: */
3637 if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) {
3638 throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.');
3639 }
3640 if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) {
3641 throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_XCHACHA20_KEYBYTES long.');
3642 }
3643
3644 if (is_callable('sodium_crypto_stream_xchacha20_xor_ic') && !$dontFallback) {
3645 return sodium_crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key);
3646 }
3647
3648 $ic = ParagonIE_Sodium_Core_Util::store64_le($counter);
3649 if (PHP_INT_SIZE === 4) {
3650 return ParagonIE_Sodium_Core32_XChaCha20::streamXorIc($message, $nonce, $key, $ic);
3651 }
3652 return ParagonIE_Sodium_Core_XChaCha20::streamXorIc($message, $nonce, $key, $ic);
3653 }
3654
3655 /**
3656 * Return a secure random key for use with crypto_stream_xchacha20
3657 *
3658 * @return string
3659 * @throws Exception
3660 * @throws Error
3661 */
3662 public static function crypto_stream_xchacha20_keygen()
3663 {
3664 return random_bytes(self::CRYPTO_STREAM_XCHACHA20_KEYBYTES);
3665 }
3666
3667 /**
3668 * Cache-timing-safe implementation of hex2bin().
3669 *
3670 * @param string $string Hexadecimal string
3671 * @param string $ignore List of characters to ignore; useful for whitespace
3672 * @return string Raw binary string
3673 * @throws SodiumException
3674 * @throws TypeError
3675 * @psalm-suppress TooFewArguments
3676 * @psalm-suppress MixedArgument
3677 */
3678 public static function hex2bin(
3679 #[\SensitiveParameter]
3680 $string,
3681 $ignore = ''
3682 ) {
3683 /* Type checks: */
3684 ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1);
3685 ParagonIE_Sodium_Core_Util::declareScalarType($ignore, 'string', 2);
3686
3687 if (self::useNewSodiumAPI()) {
3688 if (is_callable('sodium_hex2bin')) {
3689 return (string) sodium_hex2bin($string, $ignore);
3690 }
3691 }
3692 if (self::use_fallback('hex2bin')) {
3693 return (string) call_user_func('\\Sodium\\hex2bin', $string, $ignore);
3694 }
3695 return ParagonIE_Sodium_Core_Util::hex2bin($string, $ignore);
3696 }
3697
3698 /**
3699 * Increase a string (little endian)
3700 *
3701 * @param string $var
3702 *
3703 * @return void
3704 * @throws SodiumException
3705 * @throws TypeError
3706 * @psalm-suppress MixedArgument
3707 */
3708 public static function increment(
3709 #[\SensitiveParameter]
3710 &$var
3711 ) {
3712 /* Type checks: */
3713 ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1);
3714
3715 if (self::useNewSodiumAPI()) {
3716 sodium_increment($var);
3717 return;
3718 }
3719 if (self::use_fallback('increment')) {
3720 $func = '\\Sodium\\increment';
3721 $func($var);
3722 return;
3723 }
3724
3725 $len = ParagonIE_Sodium_Core_Util::strlen($var);
3726 if ($len < 1) {
3727 throw new SodiumException('Argument 1 cannot be empty');
3728 }
3729 $c = 1;
3730 $copy = '';
3731 for ($i = 0; $i < $len; ++$i) {
3732 $c += ParagonIE_Sodium_Core_Util::chrToInt(
3733 ParagonIE_Sodium_Core_Util::substr($var, $i, 1)
3734 );
3735 $copy .= ParagonIE_Sodium_Core_Util::intToChr($c);
3736 $c >>= 8;
3737 }
3738 $var = $copy;
3739 }
3740
3741 /**
3742 * @param string $str
3743 * @return bool
3744 *
3745 * @throws SodiumException
3746 */
3747 public static function is_zero(
3748 #[\SensitiveParameter]
3749 $str
3750 ) {
3751 $d = 0;
3752 for ($i = 0; $i < 32; ++$i) {
3753 $d |= ParagonIE_Sodium_Core_Util::chrToInt($str[$i]);
3754 }
3755 return ((($d - 1) >> 31) & 1) === 1;
3756 }
3757
3758 /**
3759 * The equivalent to the libsodium minor version we aim to be compatible
3760 * with (sans pwhash and memzero).
3761 *
3762 * @return int
3763 */
3764 public static function library_version_major()
3765 {
3766 if (self::useNewSodiumAPI() && defined('SODIUM_LIBRARY_MAJOR_VERSION')) {
3767 return SODIUM_LIBRARY_MAJOR_VERSION;
3768 }
3769 if (self::use_fallback('library_version_major')) {
3770 /** @psalm-suppress UndefinedFunction */
3771 return (int) call_user_func('\\Sodium\\library_version_major');
3772 }
3773 return self::LIBRARY_VERSION_MAJOR;
3774 }
3775
3776 /**
3777 * The equivalent to the libsodium minor version we aim to be compatible
3778 * with (sans pwhash and memzero).
3779 *
3780 * @return int
3781 */
3782 public static function library_version_minor()
3783 {
3784 if (self::useNewSodiumAPI() && defined('SODIUM_LIBRARY_MINOR_VERSION')) {
3785 return SODIUM_LIBRARY_MINOR_VERSION;
3786 }
3787 if (self::use_fallback('library_version_minor')) {
3788 /** @psalm-suppress UndefinedFunction */
3789 return (int) call_user_func('\\Sodium\\library_version_minor');
3790 }
3791 return self::LIBRARY_VERSION_MINOR;
3792 }
3793
3794 /**
3795 * Compare two strings.
3796 *
3797 * @param string $left
3798 * @param string $right
3799 * @return int
3800 * @throws SodiumException
3801 * @throws TypeError
3802 * @psalm-suppress MixedArgument
3803 */
3804 public static function memcmp(
3805 #[\SensitiveParameter]
3806 $left,
3807 #[\SensitiveParameter]
3808 $right
3809 ) {
3810 /* Type checks: */
3811 ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1);
3812 ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2);
3813
3814 if (self::useNewSodiumAPI()) {
3815 return sodium_memcmp($left, $right);
3816 }
3817 if (self::use_fallback('memcmp')) {
3818 return (int) call_user_func('\\Sodium\\memcmp', $left, $right);
3819 }
3820 /** @var string $left */
3821 /** @var string $right */
3822 return ParagonIE_Sodium_Core_Util::memcmp($left, $right);
3823 }
3824
3825 /**
3826 * It's actually not possible to zero memory buffers in PHP. You need the
3827 * native library for that.
3828 *
3829 * @param string|null $var
3830 * @param-out string|null $var
3831 *
3832 * @return void
3833 * @throws SodiumException (Unless libsodium is installed)
3834 * @throws TypeError
3835 * @psalm-suppress TooFewArguments
3836 */
3837 public static function memzero(
3838 #[\SensitiveParameter]
3839 &$var
3840 ) {
3841 /* Type checks: */
3842 ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1);
3843
3844 if (self::useNewSodiumAPI()) {
3845 /** @psalm-suppress MixedArgument */
3846 sodium_memzero($var);
3847 return;
3848 }
3849 if (self::use_fallback('memzero')) {
3850 $func = '\\Sodium\\memzero';
3851 $func($var);
3852 if ($var === null) {
3853 return;
3854 }
3855 }
3856 // This is the best we can do.
3857 throw new SodiumException(
3858 'This is not implemented in sodium_compat, as it is not possible to securely wipe memory from PHP. ' .
3859 'To fix this error, make sure libsodium is installed and the PHP extension is enabled.'
3860 );
3861 }
3862
3863 /**
3864 * @param string $unpadded
3865 * @param int $blockSize
3866 * @param bool $dontFallback
3867 * @return string
3868 * @throws SodiumException
3869 */
3870 public static function pad(
3871 #[\SensitiveParameter]
3872 $unpadded,
3873 $blockSize,
3874 $dontFallback = false
3875 ) {
3876 /* Type checks: */
3877 ParagonIE_Sodium_Core_Util::declareScalarType($unpadded, 'string', 1);
3878 ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
3879
3880 $unpadded = (string) $unpadded;
3881 $blockSize = (int) $blockSize;
3882
3883 if (self::useNewSodiumAPI() && !$dontFallback) {
3884 return (string) sodium_pad($unpadded, $blockSize);
3885 }
3886
3887 if ($blockSize <= 0) {
3888 throw new SodiumException(
3889 'block size cannot be less than 1'
3890 );
3891 }
3892 $unpadded_len = ParagonIE_Sodium_Core_Util::strlen($unpadded);
3893 $xpadlen = ($blockSize - 1);
3894 if (($blockSize & ($blockSize - 1)) === 0) {
3895 $xpadlen -= $unpadded_len & ($blockSize - 1);
3896 } else {
3897 $xpadlen -= $unpadded_len % $blockSize;
3898 }
3899
3900 $xpadded_len = $unpadded_len + $xpadlen;
3901 $padded = str_repeat("\0", $xpadded_len - 1);
3902 if ($unpadded_len > 0) {
3903 $st = 1;
3904 $i = 0;
3905 $k = $unpadded_len;
3906 for ($j = 0; $j <= $xpadded_len; ++$j) {
3907 $i = (int) $i;
3908 $k = (int) $k;
3909 $st = (int) $st;
3910 if ($j >= $unpadded_len) {
3911 $padded[$j] = "\0";
3912 } else {
3913 $padded[$j] = $unpadded[$j];
3914 }
3915 /** @var int $k */
3916 $k -= $st;
3917 $st = (int) (~(
3918 (
3919 (
3920 ($k >> 48)
3921 |
3922 ($k >> 32)
3923 |
3924 ($k >> 16)
3925 |
3926 $k
3927 ) - 1
3928 ) >> 16
3929 )
3930 ) & 1;
3931 $i += $st;
3932 }
3933 }
3934
3935 $mask = 0;
3936 $tail = $xpadded_len;
3937 for ($i = 0; $i < $blockSize; ++$i) {
3938 # barrier_mask = (unsigned char)
3939 # (((i ^ xpadlen) - 1U) >> ((sizeof(size_t) - 1U) * CHAR_BIT));
3940 $barrier_mask = (($i ^ $xpadlen) -1) >> ((PHP_INT_SIZE << 3) - 1);
3941 # tail[-i] = (tail[-i] & mask) | (0x80 & barrier_mask);
3942 $padded[$tail - $i] = ParagonIE_Sodium_Core_Util::intToChr(
3943 (ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]) & $mask)
3944 |
3945 (0x80 & $barrier_mask)
3946 );
3947 # mask |= barrier_mask;
3948 $mask |= $barrier_mask;
3949 }
3950 return $padded;
3951 }
3952
3953 /**
3954 * @param string $padded
3955 * @param int $blockSize
3956 * @param bool $dontFallback
3957 * @return string
3958 * @throws SodiumException
3959 */
3960 public static function unpad(
3961 #[\SensitiveParameter]
3962 $padded,
3963 $blockSize,
3964 $dontFallback = false
3965 ) {
3966 /* Type checks: */
3967 ParagonIE_Sodium_Core_Util::declareScalarType($padded, 'string', 1);
3968 ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
3969
3970 $padded = (string) $padded;
3971 $blockSize = (int) $blockSize;
3972
3973 if (self::useNewSodiumAPI() && !$dontFallback) {
3974 return (string) sodium_unpad($padded, $blockSize);
3975 }
3976 if ($blockSize <= 0) {
3977 throw new SodiumException('block size cannot be less than 1');
3978 }
3979 $padded_len = ParagonIE_Sodium_Core_Util::strlen($padded);
3980 if ($padded_len < $blockSize) {
3981 throw new SodiumException('invalid padding');
3982 }
3983
3984 # tail = &padded[padded_len - 1U];
3985 $tail = $padded_len - 1;
3986
3987 $acc = 0;
3988 $valid = 0;
3989 $pad_len = 0;
3990
3991 $found = 0;
3992 for ($i = 0; $i < $blockSize; ++$i) {
3993 # c = tail[-i];
3994 $c = ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]);
3995
3996 # is_barrier =
3997 # (( (acc - 1U) & (pad_len - 1U) & ((c ^ 0x80) - 1U) ) >> 8) & 1U;
3998 $is_barrier = (
3999 (
4000 ($acc - 1) & ($pad_len - 1) & (($c ^ 80) - 1)
4001 ) >> 7
4002 ) & 1;
4003 $is_barrier &= ~$found;
4004 $found |= $is_barrier;
4005
4006 # acc |= c;
4007 $acc |= $c;
4008
4009 # pad_len |= i & (1U + ~is_barrier);
4010 $pad_len |= $i & (1 + ~$is_barrier);
4011
4012 # valid |= (unsigned char) is_barrier;
4013 $valid |= ($is_barrier & 0xff);
4014 }
4015 # unpadded_len = padded_len - 1U - pad_len;
4016 $unpadded_len = $padded_len - 1 - $pad_len;
4017 if ($valid !== 1) {
4018 throw new SodiumException('invalid padding');
4019 }
4020 return ParagonIE_Sodium_Core_Util::substr($padded, 0, $unpadded_len);
4021 }
4022
4023 /**
4024 * Will sodium_compat run fast on the current hardware and PHP configuration?
4025 *
4026 * @return bool
4027 */
4028 public static function polyfill_is_fast()
4029 {
4030 if (extension_loaded('sodium')) {
4031 return true;
4032 }
4033 if (extension_loaded('libsodium')) {
4034 return true;
4035 }
4036 return PHP_INT_SIZE === 8;
4037 }
4038
4039 /**
4040 * Generate a string of bytes from the kernel's CSPRNG.
4041 * Proudly uses /dev/urandom (if getrandom(2) is not available).
4042 *
4043 * @param int $numBytes
4044 * @return string
4045 * @throws Exception
4046 * @throws TypeError
4047 */
4048 public static function randombytes_buf($numBytes)
4049 {
4050 /* Type checks: */
4051 if (!is_int($numBytes)) {
4052 if (is_numeric($numBytes)) {
4053 $numBytes = (int) $numBytes;
4054 } else {
4055 throw new TypeError(
4056 'Argument 1 must be an integer, ' . gettype($numBytes) . ' given.'
4057 );
4058 }
4059 }
4060 /** @var positive-int $numBytes */
4061 if (self::use_fallback('randombytes_buf')) {
4062 return (string) call_user_func('\\Sodium\\randombytes_buf', $numBytes);
4063 }
4064 if ($numBytes < 0) {
4065 throw new SodiumException("Number of bytes must be a positive integer");
4066 }
4067 return random_bytes($numBytes);
4068 }
4069
4070 /**
4071 * Generate an integer between 0 and $range (non-inclusive).
4072 *
4073 * @param int $range
4074 * @return int
4075 * @throws Exception
4076 * @throws Error
4077 * @throws TypeError
4078 */
4079 public static function randombytes_uniform($range)
4080 {
4081 /* Type checks: */
4082 if (!is_int($range)) {
4083 if (is_numeric($range)) {
4084 $range = (int) $range;
4085 } else {
4086 throw new TypeError(
4087 'Argument 1 must be an integer, ' . gettype($range) . ' given.'
4088 );
4089 }
4090 }
4091 if (self::use_fallback('randombytes_uniform')) {
4092 return (int) call_user_func('\\Sodium\\randombytes_uniform', $range);
4093 }
4094 return random_int(0, $range - 1);
4095 }
4096
4097 /**
4098 * Generate a random 16-bit integer.
4099 *
4100 * @return int
4101 * @throws Exception
4102 * @throws Error
4103 * @throws TypeError
4104 */
4105 public static function randombytes_random16()
4106 {
4107 if (self::use_fallback('randombytes_random16')) {
4108 return (int) call_user_func('\\Sodium\\randombytes_random16');
4109 }
4110 return random_int(0, 65535);
4111 }
4112
4113 /**
4114 * @param string $p
4115 * @param bool $dontFallback
4116 * @return bool
4117 * @throws SodiumException
4118 */
4119 public static function ristretto255_is_valid_point(
4120 #[\SensitiveParameter]
4121 $p,
4122 $dontFallback = false
4123 ) {
4124 if (self::useNewSodiumAPI() && !$dontFallback) {
4125 return sodium_crypto_core_ristretto255_is_valid_point($p);
4126 }
4127 try {
4128 $r = ParagonIE_Sodium_Core_Ristretto255::ristretto255_frombytes($p);
4129 return $r['res'] === 0 &&
4130 ParagonIE_Sodium_Core_Ristretto255::ristretto255_point_is_canonical($p) === 1;
4131 } catch (SodiumException $ex) {
4132 if ($ex->getMessage() === 'S is not canonical') {
4133 return false;
4134 }
4135 throw $ex;
4136 }
4137 }
4138
4139 /**
4140 * @param string $p
4141 * @param string $q
4142 * @param bool $dontFallback
4143 * @return string
4144 * @throws SodiumException
4145 */
4146 public static function ristretto255_add(
4147 #[\SensitiveParameter]
4148 $p,
4149 #[\SensitiveParameter]
4150 $q,
4151 $dontFallback = false
4152 ) {
4153 if (self::useNewSodiumAPI() && !$dontFallback) {
4154 return sodium_crypto_core_ristretto255_add($p, $q);
4155 }
4156 return ParagonIE_Sodium_Core_Ristretto255::ristretto255_add($p, $q);
4157 }
4158
4159 /**
4160 * @param string $p
4161 * @param string $q
4162 * @param bool $dontFallback
4163 * @return string
4164 * @throws SodiumException
4165 */
4166 public static function ristretto255_sub(
4167 #[\SensitiveParameter]
4168 $p,
4169 #[\SensitiveParameter]
4170 $q,
4171 $dontFallback = false
4172 ) {
4173 if (self::useNewSodiumAPI() && !$dontFallback) {
4174 return sodium_crypto_core_ristretto255_sub($p, $q);
4175 }
4176 return ParagonIE_Sodium_Core_Ristretto255::ristretto255_sub($p, $q);
4177 }
4178
4179 /**
4180 * @param string $r
4181 * @param bool $dontFallback
4182 * @return string
4183 *
4184 * @throws SodiumException
4185 */
4186 public static function ristretto255_from_hash(
4187 #[\SensitiveParameter]
4188 $r,
4189 $dontFallback = false
4190 ) {
4191 if (self::useNewSodiumAPI() && !$dontFallback) {
4192 return sodium_crypto_core_ristretto255_from_hash($r);
4193 }
4194 return ParagonIE_Sodium_Core_Ristretto255::ristretto255_from_hash($r);
4195 }
4196
4197 /**
4198 * @param bool $dontFallback
4199 * @return string
4200 *
4201 * @throws SodiumException
4202 */
4203 public static function ristretto255_random($dontFallback = false)
4204 {
4205 if (self::useNewSodiumAPI() && !$dontFallback) {
4206 return sodium_crypto_core_ristretto255_random();
4207 }
4208 return ParagonIE_Sodium_Core_Ristretto255::ristretto255_random();
4209 }
4210
4211 /**
4212 * @param bool $dontFallback
4213 * @return string
4214 *
4215 * @throws SodiumException
4216 */
4217 public static function ristretto255_scalar_random($dontFallback = false)
4218 {
4219 if (self::useNewSodiumAPI() && !$dontFallback) {
4220 return sodium_crypto_core_ristretto255_scalar_random();
4221 }
4222 return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_random();
4223 }
4224
4225 /**
4226 * @param string $s
4227 * @param bool $dontFallback
4228 * @return string
4229 * @throws SodiumException
4230 */
4231 public static function ristretto255_scalar_invert(
4232 #[\SensitiveParameter]
4233 $s,
4234 $dontFallback = false
4235 ) {
4236 if (self::useNewSodiumAPI() && !$dontFallback) {
4237 return sodium_crypto_core_ristretto255_scalar_invert($s);
4238 }
4239 return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_invert($s);
4240 }
4241 /**
4242 * @param string $s
4243 * @param bool $dontFallback
4244 * @return string
4245 * @throws SodiumException
4246 */
4247 public static function ristretto255_scalar_negate(
4248 #[\SensitiveParameter]
4249 $s,
4250 $dontFallback = false
4251 ) {
4252 if (self::useNewSodiumAPI() && !$dontFallback) {
4253 return sodium_crypto_core_ristretto255_scalar_negate($s);
4254 }
4255 return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_negate($s);
4256 }
4257
4258 /**
4259 * @param string $s
4260 * @param bool $dontFallback
4261 * @return string
4262 * @throws SodiumException
4263 */
4264 public static function ristretto255_scalar_complement(
4265 #[\SensitiveParameter]
4266 $s,
4267 $dontFallback = false
4268 ) {
4269 if (self::useNewSodiumAPI() && !$dontFallback) {
4270 return sodium_crypto_core_ristretto255_scalar_complement($s);
4271 }
4272 return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_complement($s);
4273 }
4274
4275 /**
4276 * @param string $x
4277 * @param string $y
4278 * @param bool $dontFallback
4279 * @return string
4280 * @throws SodiumException
4281 */
4282 public static function ristretto255_scalar_add(
4283 #[\SensitiveParameter]
4284 $x,
4285 #[\SensitiveParameter]
4286 $y,
4287 $dontFallback = false
4288 ) {
4289 if (self::useNewSodiumAPI() && !$dontFallback) {
4290 return sodium_crypto_core_ristretto255_scalar_add($x, $y);
4291 }
4292 return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_add($x, $y);
4293 }
4294
4295 /**
4296 * @param string $x
4297 * @param string $y
4298 * @param bool $dontFallback
4299 * @return string
4300 * @throws SodiumException
4301 */
4302 public static function ristretto255_scalar_sub(
4303 #[\SensitiveParameter]
4304 $x,
4305 #[\SensitiveParameter]
4306 $y,
4307 $dontFallback = false
4308 ) {
4309 if (self::useNewSodiumAPI() && !$dontFallback) {
4310 return sodium_crypto_core_ristretto255_scalar_sub($x, $y);
4311 }
4312 return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_sub($x, $y);
4313 }
4314
4315 /**
4316 * @param string $x
4317 * @param string $y
4318 * @param bool $dontFallback
4319 * @return string
4320 * @throws SodiumException
4321 */
4322 public static function ristretto255_scalar_mul(
4323 #[\SensitiveParameter]
4324 $x,
4325 #[\SensitiveParameter]
4326 $y,
4327 $dontFallback = false
4328 ) {
4329 if (self::useNewSodiumAPI() && !$dontFallback) {
4330 return sodium_crypto_core_ristretto255_scalar_mul($x, $y);
4331 }
4332 return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_mul($x, $y);
4333 }
4334
4335 /**
4336 * @param string $n
4337 * @param string $p
4338 * @param bool $dontFallback
4339 * @return string
4340 * @throws SodiumException
4341 */
4342 public static function scalarmult_ristretto255(
4343 #[\SensitiveParameter]
4344 $n,
4345 #[\SensitiveParameter]
4346 $p,
4347 $dontFallback = false
4348 ) {
4349 if (self::useNewSodiumAPI() && !$dontFallback) {
4350 return sodium_crypto_scalarmult_ristretto255($n, $p);
4351 }
4352 return ParagonIE_Sodium_Core_Ristretto255::scalarmult_ristretto255($n, $p);
4353 }
4354
4355 /**
4356 * @param string $n
4357 * @param string $p
4358 * @param bool $dontFallback
4359 * @return string
4360 * @throws SodiumException
4361 */
4362 public static function scalarmult_ristretto255_base(
4363 #[\SensitiveParameter]
4364 $n,
4365 $dontFallback = false
4366 ) {
4367 if (self::useNewSodiumAPI() && !$dontFallback) {
4368 return sodium_crypto_scalarmult_ristretto255_base($n);
4369 }
4370 return ParagonIE_Sodium_Core_Ristretto255::scalarmult_ristretto255_base($n);
4371 }
4372
4373 /**
4374 * @param string $s
4375 * @param bool $dontFallback
4376 * @return string
4377 * @throws SodiumException
4378 */
4379 public static function ristretto255_scalar_reduce(
4380 #[\SensitiveParameter]
4381 $s,
4382 $dontFallback = false
4383 ) {
4384 if (self::useNewSodiumAPI() && !$dontFallback) {
4385 return sodium_crypto_core_ristretto255_scalar_reduce($s);
4386 }
4387 return ParagonIE_Sodium_Core_Ristretto255::sc_reduce($s);
4388 }
4389
4390 /**
4391 * Runtime testing method for 32-bit platforms.
4392 *
4393 * Usage: If runtime_speed_test() returns FALSE, then our 32-bit
4394 * implementation is to slow to use safely without risking timeouts.
4395 * If this happens, install sodium from PECL to get acceptable
4396 * performance.
4397 *
4398 * @param int $iterations Number of multiplications to attempt
4399 * @param int $maxTimeout Milliseconds
4400 * @return bool TRUE if we're fast enough, FALSE is not
4401 * @throws SodiumException
4402 */
4403 public static function runtime_speed_test($iterations, $maxTimeout)
4404 {
4405 if (self::polyfill_is_fast()) {
4406 return true;
4407 }
4408 /** @var float $end */
4409 $end = 0.0;
4410 /** @var float $start */
4411 $start = microtime(true);
4412 /** @var ParagonIE_Sodium_Core32_Int64 $a */
4413 $a = ParagonIE_Sodium_Core32_Int64::fromInt(random_int(3, 1 << 16));
4414 for ($i = 0; $i < $iterations; ++$i) {
4415 /** @var ParagonIE_Sodium_Core32_Int64 $b */
4416 $b = ParagonIE_Sodium_Core32_Int64::fromInt(random_int(3, 1 << 16));
4417 $a->mulInt64($b);
4418 }
4419 /** @var float $end */
4420 $end = microtime(true);
4421 /** @var int $diff */
4422 $diff = (int) ceil(($end - $start) * 1000);
4423 return $diff < $maxTimeout;
4424 }
4425
4426 /**
4427 * Add two numbers (little-endian unsigned), storing the value in the first
4428 * parameter.
4429 *
4430 * This mutates $val.
4431 *
4432 * @param string $val
4433 * @param string $addv
4434 * @return void
4435 * @throws SodiumException
4436 */
4437 public static function sub(
4438 #[\SensitiveParameter]
4439 &$val,
4440 #[\SensitiveParameter]
4441 $addv
4442 ) {
4443 $val_len = ParagonIE_Sodium_Core_Util::strlen($val);
4444 $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv);
4445 if ($val_len !== $addv_len) {
4446 throw new SodiumException('values must have the same length');
4447 }
4448 $A = ParagonIE_Sodium_Core_Util::stringToIntArray($val);
4449 $B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv);
4450
4451 $c = 0;
4452 for ($i = 0; $i < $val_len; $i++) {
4453 $c = ($A[$i] - $B[$i] - $c);
4454 $A[$i] = ($c & 0xff);
4455 $c = ($c >> 8) & 1;
4456 }
4457 $val = ParagonIE_Sodium_Core_Util::intArrayToString($A);
4458 }
4459
4460 /**
4461 * This emulates libsodium's version_string() function, except ours is
4462 * prefixed with 'polyfill-'.
4463 *
4464 * @return string
4465 * @psalm-suppress MixedInferredReturnType
4466 * @psalm-suppress UndefinedFunction
4467 */
4468 public static function version_string()
4469 {
4470 if (self::useNewSodiumAPI()) {
4471 return (string) sodium_version_string();
4472 }
4473 if (self::use_fallback('version_string')) {
4474 return (string) call_user_func('\\Sodium\\version_string');
4475 }
4476 return (string) self::VERSION_STRING;
4477 }
4478
4479 /**
4480 * Should we use the libsodium core function instead?
4481 * This is always a good idea, if it's available. (Unless we're in the
4482 * middle of running our unit test suite.)
4483 *
4484 * If ext/libsodium is available, use it. Return TRUE.
4485 * Otherwise, we have to use the code provided herein. Return FALSE.
4486 *
4487 * @param string $sodium_func_name
4488 *
4489 * @return bool
4490 */
4491 protected static function use_fallback($sodium_func_name = '')
4492 {
4493 static $res = null;
4494 if ($res === null) {
4495 $res = extension_loaded('libsodium') && PHP_VERSION_ID >= 50300;
4496 }
4497 if ($res === false) {
4498 // No libsodium installed
4499 return false;
4500 }
4501 if (self::$disableFallbackForUnitTests) {
4502 // Don't fallback. Use the PHP implementation.
4503 return false;
4504 }
4505 if (!empty($sodium_func_name)) {
4506 return is_callable('\\Sodium\\' . $sodium_func_name);
4507 }
4508 return true;
4509 }
4510
4511 /**
4512 * Libsodium as implemented in PHP 7.2
4513 * and/or ext/sodium (via PECL)
4514 *
4515 * @ref https://wiki.php.net/rfc/libsodium
4516 * @return bool
4517 */
4518 protected static function useNewSodiumAPI()
4519 {
4520 static $res = null;
4521 if ($res === null) {
4522 $res = PHP_VERSION_ID >= 70000 && extension_loaded('sodium');
4523 }
4524 if (self::$disableFallbackForUnitTests) {
4525 // Don't fallback. Use the PHP implementation.
4526 return false;
4527 }
4528 return (bool) $res;
4529 }
4530}
4531
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1