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