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