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