[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 <?php 2 3 if (class_exists('ParagonIE_Sodium_Crypto', false)) { 4 return; 5 } 6 7 /** 8 * Class ParagonIE_Sodium_Crypto 9 * 10 * ATTENTION! 11 * 12 * If you are using this library, you should be using 13 * ParagonIE_Sodium_Compat in your code, not this class. 14 */ 15 abstract class ParagonIE_Sodium_Crypto 16 { 17 const aead_chacha20poly1305_KEYBYTES = 32; 18 const aead_chacha20poly1305_NSECBYTES = 0; 19 const aead_chacha20poly1305_NPUBBYTES = 8; 20 const aead_chacha20poly1305_ABYTES = 16; 21 22 const aead_chacha20poly1305_IETF_KEYBYTES = 32; 23 const aead_chacha20poly1305_IETF_NSECBYTES = 0; 24 const aead_chacha20poly1305_IETF_NPUBBYTES = 12; 25 const aead_chacha20poly1305_IETF_ABYTES = 16; 26 27 const aead_xchacha20poly1305_IETF_KEYBYTES = 32; 28 const aead_xchacha20poly1305_IETF_NSECBYTES = 0; 29 const aead_xchacha20poly1305_IETF_NPUBBYTES = 24; 30 const aead_xchacha20poly1305_IETF_ABYTES = 16; 31 32 const box_curve25519xsalsa20poly1305_SEEDBYTES = 32; 33 const box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32; 34 const box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32; 35 const box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32; 36 const box_curve25519xsalsa20poly1305_NONCEBYTES = 24; 37 const box_curve25519xsalsa20poly1305_MACBYTES = 16; 38 const box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16; 39 const box_curve25519xsalsa20poly1305_ZEROBYTES = 32; 40 41 const onetimeauth_poly1305_BYTES = 16; 42 const onetimeauth_poly1305_KEYBYTES = 32; 43 44 const secretbox_xsalsa20poly1305_KEYBYTES = 32; 45 const secretbox_xsalsa20poly1305_NONCEBYTES = 24; 46 const secretbox_xsalsa20poly1305_MACBYTES = 16; 47 const secretbox_xsalsa20poly1305_BOXZEROBYTES = 16; 48 const secretbox_xsalsa20poly1305_ZEROBYTES = 32; 49 50 const secretbox_xchacha20poly1305_KEYBYTES = 32; 51 const secretbox_xchacha20poly1305_NONCEBYTES = 24; 52 const secretbox_xchacha20poly1305_MACBYTES = 16; 53 const secretbox_xchacha20poly1305_BOXZEROBYTES = 16; 54 const secretbox_xchacha20poly1305_ZEROBYTES = 32; 55 56 const stream_salsa20_KEYBYTES = 32; 57 58 /** 59 * AEAD Decryption with ChaCha20-Poly1305 60 * 61 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 62 * 63 * @param string $message 64 * @param string $ad 65 * @param string $nonce 66 * @param string $key 67 * @return string 68 * @throws SodiumException 69 * @throws TypeError 70 */ 71 public static function aead_chacha20poly1305_decrypt( 72 $message = '', 73 $ad = '', 74 $nonce = '', 75 $key = '' 76 ) { 77 /** @var int $len - Length of message (ciphertext + MAC) */ 78 $len = ParagonIE_Sodium_Core_Util::strlen($message); 79 80 /** @var int $clen - Length of ciphertext */ 81 $clen = $len - self::aead_chacha20poly1305_ABYTES; 82 83 /** @var int $adlen - Length of associated data */ 84 $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); 85 86 /** @var string $mac - Message authentication code */ 87 $mac = ParagonIE_Sodium_Core_Util::substr( 88 $message, 89 $clen, 90 self::aead_chacha20poly1305_ABYTES 91 ); 92 93 /** @var string $ciphertext - The encrypted message (sans MAC) */ 94 $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 0, $clen); 95 96 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ 97 $block0 = ParagonIE_Sodium_Core_ChaCha20::stream( 98 32, 99 $nonce, 100 $key 101 ); 102 103 /* Recalculate the Poly1305 authentication tag (MAC): */ 104 $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); 105 try { 106 ParagonIE_Sodium_Compat::memzero($block0); 107 } catch (SodiumException $ex) { 108 $block0 = null; 109 } 110 $state->update($ad); 111 $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); 112 $state->update($ciphertext); 113 $state->update(ParagonIE_Sodium_Core_Util::store64_le($clen)); 114 $computed_mac = $state->finish(); 115 116 /* Compare the given MAC with the recalculated MAC: */ 117 if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) { 118 throw new SodiumException('Invalid MAC'); 119 } 120 121 // Here, we know that the MAC is valid, so we decrypt and return the plaintext 122 return ParagonIE_Sodium_Core_ChaCha20::streamXorIc( 123 $ciphertext, 124 $nonce, 125 $key, 126 ParagonIE_Sodium_Core_Util::store64_le(1) 127 ); 128 } 129 130 /** 131 * AEAD Encryption with ChaCha20-Poly1305 132 * 133 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 134 * 135 * @param string $message 136 * @param string $ad 137 * @param string $nonce 138 * @param string $key 139 * @return string 140 * @throws SodiumException 141 * @throws TypeError 142 */ 143 public static function aead_chacha20poly1305_encrypt( 144 $message = '', 145 $ad = '', 146 $nonce = '', 147 $key = '' 148 ) { 149 /** @var int $len - Length of the plaintext message */ 150 $len = ParagonIE_Sodium_Core_Util::strlen($message); 151 152 /** @var int $adlen - Length of the associated data */ 153 $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); 154 155 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ 156 $block0 = ParagonIE_Sodium_Core_ChaCha20::stream( 157 32, 158 $nonce, 159 $key 160 ); 161 $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); 162 try { 163 ParagonIE_Sodium_Compat::memzero($block0); 164 } catch (SodiumException $ex) { 165 $block0 = null; 166 } 167 168 /** @var string $ciphertext - Raw encrypted data */ 169 $ciphertext = ParagonIE_Sodium_Core_ChaCha20::streamXorIc( 170 $message, 171 $nonce, 172 $key, 173 ParagonIE_Sodium_Core_Util::store64_le(1) 174 ); 175 176 $state->update($ad); 177 $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); 178 $state->update($ciphertext); 179 $state->update(ParagonIE_Sodium_Core_Util::store64_le($len)); 180 return $ciphertext . $state->finish(); 181 } 182 183 /** 184 * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) 185 * 186 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 187 * 188 * @param string $message 189 * @param string $ad 190 * @param string $nonce 191 * @param string $key 192 * @return string 193 * @throws SodiumException 194 * @throws TypeError 195 */ 196 public static function aead_chacha20poly1305_ietf_decrypt( 197 $message = '', 198 $ad = '', 199 $nonce = '', 200 $key = '' 201 ) { 202 /** @var int $adlen - Length of associated data */ 203 $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); 204 205 /** @var int $len - Length of message (ciphertext + MAC) */ 206 $len = ParagonIE_Sodium_Core_Util::strlen($message); 207 208 /** @var int $clen - Length of ciphertext */ 209 $clen = $len - self::aead_chacha20poly1305_IETF_ABYTES; 210 211 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ 212 $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream( 213 32, 214 $nonce, 215 $key 216 ); 217 218 /** @var string $mac - Message authentication code */ 219 $mac = ParagonIE_Sodium_Core_Util::substr( 220 $message, 221 $len - self::aead_chacha20poly1305_IETF_ABYTES, 222 self::aead_chacha20poly1305_IETF_ABYTES 223 ); 224 225 /** @var string $ciphertext - The encrypted message (sans MAC) */ 226 $ciphertext = ParagonIE_Sodium_Core_Util::substr( 227 $message, 228 0, 229 $len - self::aead_chacha20poly1305_IETF_ABYTES 230 ); 231 232 /* Recalculate the Poly1305 authentication tag (MAC): */ 233 $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); 234 try { 235 ParagonIE_Sodium_Compat::memzero($block0); 236 } catch (SodiumException $ex) { 237 $block0 = null; 238 } 239 $state->update($ad); 240 $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); 241 $state->update($ciphertext); 242 $state->update(str_repeat("\x00", (0x10 - $clen) & 0xf)); 243 $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); 244 $state->update(ParagonIE_Sodium_Core_Util::store64_le($clen)); 245 $computed_mac = $state->finish(); 246 247 /* Compare the given MAC with the recalculated MAC: */ 248 if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) { 249 throw new SodiumException('Invalid MAC'); 250 } 251 252 // Here, we know that the MAC is valid, so we decrypt and return the plaintext 253 return ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( 254 $ciphertext, 255 $nonce, 256 $key, 257 ParagonIE_Sodium_Core_Util::store64_le(1) 258 ); 259 } 260 261 /** 262 * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) 263 * 264 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 265 * 266 * @param string $message 267 * @param string $ad 268 * @param string $nonce 269 * @param string $key 270 * @return string 271 * @throws SodiumException 272 * @throws TypeError 273 */ 274 public static function aead_chacha20poly1305_ietf_encrypt( 275 $message = '', 276 $ad = '', 277 $nonce = '', 278 $key = '' 279 ) { 280 /** @var int $len - Length of the plaintext message */ 281 $len = ParagonIE_Sodium_Core_Util::strlen($message); 282 283 /** @var int $adlen - Length of the associated data */ 284 $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); 285 286 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ 287 $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream( 288 32, 289 $nonce, 290 $key 291 ); 292 $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); 293 try { 294 ParagonIE_Sodium_Compat::memzero($block0); 295 } catch (SodiumException $ex) { 296 $block0 = null; 297 } 298 299 /** @var string $ciphertext - Raw encrypted data */ 300 $ciphertext = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( 301 $message, 302 $nonce, 303 $key, 304 ParagonIE_Sodium_Core_Util::store64_le(1) 305 ); 306 307 $state->update($ad); 308 $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); 309 $state->update($ciphertext); 310 $state->update(str_repeat("\x00", ((0x10 - $len) & 0xf))); 311 $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); 312 $state->update(ParagonIE_Sodium_Core_Util::store64_le($len)); 313 return $ciphertext . $state->finish(); 314 } 315 316 /** 317 * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) 318 * 319 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 320 * 321 * @param string $message 322 * @param string $ad 323 * @param string $nonce 324 * @param string $key 325 * @return string 326 * @throws SodiumException 327 * @throws TypeError 328 */ 329 public static function aead_xchacha20poly1305_ietf_decrypt( 330 $message = '', 331 $ad = '', 332 $nonce = '', 333 $key = '' 334 ) { 335 $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20( 336 ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), 337 $key 338 ); 339 $nonceLast = "\x00\x00\x00\x00" . 340 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); 341 342 return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey); 343 } 344 345 /** 346 * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) 347 * 348 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 349 * 350 * @param string $message 351 * @param string $ad 352 * @param string $nonce 353 * @param string $key 354 * @return string 355 * @throws SodiumException 356 * @throws TypeError 357 */ 358 public static function aead_xchacha20poly1305_ietf_encrypt( 359 $message = '', 360 $ad = '', 361 $nonce = '', 362 $key = '' 363 ) { 364 $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20( 365 ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), 366 $key 367 ); 368 $nonceLast = "\x00\x00\x00\x00" . 369 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); 370 371 return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey); 372 } 373 374 /** 375 * HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512) 376 * 377 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 378 * 379 * @param string $message 380 * @param string $key 381 * @return string 382 * @throws TypeError 383 */ 384 public static function auth($message, $key) 385 { 386 return ParagonIE_Sodium_Core_Util::substr( 387 hash_hmac('sha512', $message, $key, true), 388 0, 389 32 390 ); 391 } 392 393 /** 394 * HMAC-SHA-512-256 validation. Constant-time via hash_equals(). 395 * 396 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 397 * 398 * @param string $mac 399 * @param string $message 400 * @param string $key 401 * @return bool 402 * @throws SodiumException 403 * @throws TypeError 404 */ 405 public static function auth_verify($mac, $message, $key) 406 { 407 return ParagonIE_Sodium_Core_Util::hashEquals( 408 $mac, 409 self::auth($message, $key) 410 ); 411 } 412 413 /** 414 * X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption 415 * 416 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 417 * 418 * @param string $plaintext 419 * @param string $nonce 420 * @param string $keypair 421 * @return string 422 * @throws SodiumException 423 * @throws TypeError 424 */ 425 public static function box($plaintext, $nonce, $keypair) 426 { 427 $c = self::secretbox( 428 $plaintext, 429 $nonce, 430 self::box_beforenm( 431 self::box_secretkey($keypair), 432 self::box_publickey($keypair) 433 ) 434 ); 435 return $c; 436 } 437 438 /** 439 * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair. 440 * 441 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 442 * 443 * @param string $message 444 * @param string $publicKey 445 * @return string 446 * @throws SodiumException 447 * @throws TypeError 448 */ 449 public static function box_seal($message, $publicKey) 450 { 451 /** @var string $ephemeralKeypair */ 452 $ephemeralKeypair = self::box_keypair(); 453 454 /** @var string $ephemeralSK */ 455 $ephemeralSK = self::box_secretkey($ephemeralKeypair); 456 457 /** @var string $ephemeralPK */ 458 $ephemeralPK = self::box_publickey($ephemeralKeypair); 459 460 /** @var string $nonce */ 461 $nonce = self::generichash( 462 $ephemeralPK . $publicKey, 463 '', 464 24 465 ); 466 467 /** @var string $keypair - The combined keypair used in crypto_box() */ 468 $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey); 469 470 /** @var string $ciphertext Ciphertext + MAC from crypto_box */ 471 $ciphertext = self::box($message, $nonce, $keypair); 472 try { 473 ParagonIE_Sodium_Compat::memzero($ephemeralKeypair); 474 ParagonIE_Sodium_Compat::memzero($ephemeralSK); 475 ParagonIE_Sodium_Compat::memzero($nonce); 476 } catch (SodiumException $ex) { 477 $ephemeralKeypair = null; 478 $ephemeralSK = null; 479 $nonce = null; 480 } 481 return $ephemeralPK . $ciphertext; 482 } 483 484 /** 485 * Opens a message encrypted via box_seal(). 486 * 487 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 488 * 489 * @param string $message 490 * @param string $keypair 491 * @return string 492 * @throws SodiumException 493 * @throws TypeError 494 */ 495 public static function box_seal_open($message, $keypair) 496 { 497 /** @var string $ephemeralPK */ 498 $ephemeralPK = ParagonIE_Sodium_Core_Util::substr($message, 0, 32); 499 500 /** @var string $ciphertext (ciphertext + MAC) */ 501 $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 32); 502 503 /** @var string $secretKey */ 504 $secretKey = self::box_secretkey($keypair); 505 506 /** @var string $publicKey */ 507 $publicKey = self::box_publickey($keypair); 508 509 /** @var string $nonce */ 510 $nonce = self::generichash( 511 $ephemeralPK . $publicKey, 512 '', 513 24 514 ); 515 516 /** @var string $keypair */ 517 $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK); 518 519 /** @var string $m */ 520 $m = self::box_open($ciphertext, $nonce, $keypair); 521 try { 522 ParagonIE_Sodium_Compat::memzero($secretKey); 523 ParagonIE_Sodium_Compat::memzero($ephemeralPK); 524 ParagonIE_Sodium_Compat::memzero($nonce); 525 } catch (SodiumException $ex) { 526 $secretKey = null; 527 $ephemeralPK = null; 528 $nonce = null; 529 } 530 return $m; 531 } 532 533 /** 534 * Used by crypto_box() to get the crypto_secretbox() key. 535 * 536 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 537 * 538 * @param string $sk 539 * @param string $pk 540 * @return string 541 * @throws SodiumException 542 * @throws TypeError 543 */ 544 public static function box_beforenm($sk, $pk) 545 { 546 return ParagonIE_Sodium_Core_HSalsa20::hsalsa20( 547 str_repeat("\x00", 16), 548 self::scalarmult($sk, $pk) 549 ); 550 } 551 552 /** 553 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 554 * 555 * @return string 556 * @throws Exception 557 * @throws SodiumException 558 * @throws TypeError 559 */ 560 public static function box_keypair() 561 { 562 $sKey = random_bytes(32); 563 $pKey = self::scalarmult_base($sKey); 564 return $sKey . $pKey; 565 } 566 567 /** 568 * @param string $seed 569 * @return string 570 * @throws SodiumException 571 * @throws TypeError 572 */ 573 public static function box_seed_keypair($seed) 574 { 575 $sKey = ParagonIE_Sodium_Core_Util::substr( 576 hash('sha512', $seed, true), 577 0, 578 32 579 ); 580 $pKey = self::scalarmult_base($sKey); 581 return $sKey . $pKey; 582 } 583 584 /** 585 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 586 * 587 * @param string $sKey 588 * @param string $pKey 589 * @return string 590 * @throws TypeError 591 */ 592 public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey) 593 { 594 return ParagonIE_Sodium_Core_Util::substr($sKey, 0, 32) . 595 ParagonIE_Sodium_Core_Util::substr($pKey, 0, 32); 596 } 597 598 /** 599 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 600 * 601 * @param string $keypair 602 * @return string 603 * @throws RangeException 604 * @throws TypeError 605 */ 606 public static function box_secretkey($keypair) 607 { 608 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== 64) { 609 throw new RangeException( 610 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.' 611 ); 612 } 613 return ParagonIE_Sodium_Core_Util::substr($keypair, 0, 32); 614 } 615 616 /** 617 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 618 * 619 * @param string $keypair 620 * @return string 621 * @throws RangeException 622 * @throws TypeError 623 */ 624 public static function box_publickey($keypair) 625 { 626 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { 627 throw new RangeException( 628 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.' 629 ); 630 } 631 return ParagonIE_Sodium_Core_Util::substr($keypair, 32, 32); 632 } 633 634 /** 635 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 636 * 637 * @param string $sKey 638 * @return string 639 * @throws RangeException 640 * @throws SodiumException 641 * @throws TypeError 642 */ 643 public static function box_publickey_from_secretkey($sKey) 644 { 645 if (ParagonIE_Sodium_Core_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) { 646 throw new RangeException( 647 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.' 648 ); 649 } 650 return self::scalarmult_base($sKey); 651 } 652 653 /** 654 * Decrypt a message encrypted with box(). 655 * 656 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 657 * 658 * @param string $ciphertext 659 * @param string $nonce 660 * @param string $keypair 661 * @return string 662 * @throws SodiumException 663 * @throws TypeError 664 */ 665 public static function box_open($ciphertext, $nonce, $keypair) 666 { 667 return self::secretbox_open( 668 $ciphertext, 669 $nonce, 670 self::box_beforenm( 671 self::box_secretkey($keypair), 672 self::box_publickey($keypair) 673 ) 674 ); 675 } 676 677 /** 678 * Calculate a BLAKE2b hash. 679 * 680 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 681 * 682 * @param string $message 683 * @param string|null $key 684 * @param int $outlen 685 * @return string 686 * @throws RangeException 687 * @throws SodiumException 688 * @throws TypeError 689 */ 690 public static function generichash($message, $key = '', $outlen = 32) 691 { 692 // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized 693 ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); 694 695 $k = null; 696 if (!empty($key)) { 697 /** @var SplFixedArray $k */ 698 $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key); 699 if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) { 700 throw new RangeException('Invalid key size'); 701 } 702 } 703 704 /** @var SplFixedArray $in */ 705 $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message); 706 707 /** @var SplFixedArray $ctx */ 708 $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outlen); 709 ParagonIE_Sodium_Core_BLAKE2b::update($ctx, $in, $in->count()); 710 711 /** @var SplFixedArray $out */ 712 $out = new SplFixedArray($outlen); 713 $out = ParagonIE_Sodium_Core_BLAKE2b::finish($ctx, $out); 714 715 /** @var array<int, int> */ 716 $outArray = $out->toArray(); 717 return ParagonIE_Sodium_Core_Util::intArrayToString($outArray); 718 } 719 720 /** 721 * Finalize a BLAKE2b hashing context, returning the hash. 722 * 723 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 724 * 725 * @param string $ctx 726 * @param int $outlen 727 * @return string 728 * @throws SodiumException 729 * @throws TypeError 730 */ 731 public static function generichash_final($ctx, $outlen = 32) 732 { 733 if (!is_string($ctx)) { 734 throw new TypeError('Context must be a string'); 735 } 736 $out = new SplFixedArray($outlen); 737 738 /** @var SplFixedArray $context */ 739 $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx); 740 741 /** @var SplFixedArray $out */ 742 $out = ParagonIE_Sodium_Core_BLAKE2b::finish($context, $out); 743 744 /** @var array<int, int> */ 745 $outArray = $out->toArray(); 746 return ParagonIE_Sodium_Core_Util::intArrayToString($outArray); 747 } 748 749 /** 750 * Initialize a hashing context for BLAKE2b. 751 * 752 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 753 * 754 * @param string $key 755 * @param int $outputLength 756 * @return string 757 * @throws RangeException 758 * @throws SodiumException 759 * @throws TypeError 760 */ 761 public static function generichash_init($key = '', $outputLength = 32) 762 { 763 // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized 764 ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); 765 766 $k = null; 767 if (!empty($key)) { 768 $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key); 769 if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) { 770 throw new RangeException('Invalid key size'); 771 } 772 } 773 774 /** @var SplFixedArray $ctx */ 775 $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength); 776 777 return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx); 778 } 779 780 /** 781 * Initialize a hashing context for BLAKE2b. 782 * 783 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 784 * 785 * @param string $key 786 * @param int $outputLength 787 * @param string $salt 788 * @param string $personal 789 * @return string 790 * @throws RangeException 791 * @throws SodiumException 792 * @throws TypeError 793 */ 794 public static function generichash_init_salt_personal( 795 $key = '', 796 $outputLength = 32, 797 $salt = '', 798 $personal = '' 799 ) { 800 // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized 801 ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); 802 803 $k = null; 804 if (!empty($key)) { 805 $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key); 806 if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) { 807 throw new RangeException('Invalid key size'); 808 } 809 } 810 if (!empty($salt)) { 811 $s = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($salt); 812 } else { 813 $s = null; 814 } 815 if (!empty($salt)) { 816 $p = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($personal); 817 } else { 818 $p = null; 819 } 820 821 /** @var SplFixedArray $ctx */ 822 $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength, $s, $p); 823 824 return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx); 825 } 826 827 /** 828 * Update a hashing context for BLAKE2b with $message 829 * 830 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 831 * 832 * @param string $ctx 833 * @param string $message 834 * @return string 835 * @throws SodiumException 836 * @throws TypeError 837 */ 838 public static function generichash_update($ctx, $message) 839 { 840 // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized 841 ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); 842 843 /** @var SplFixedArray $context */ 844 $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx); 845 846 /** @var SplFixedArray $in */ 847 $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message); 848 849 ParagonIE_Sodium_Core_BLAKE2b::update($context, $in, $in->count()); 850 851 return ParagonIE_Sodium_Core_BLAKE2b::contextToString($context); 852 } 853 854 /** 855 * Libsodium's crypto_kx(). 856 * 857 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 858 * 859 * @param string $my_sk 860 * @param string $their_pk 861 * @param string $client_pk 862 * @param string $server_pk 863 * @return string 864 * @throws SodiumException 865 * @throws TypeError 866 */ 867 public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk) 868 { 869 return ParagonIE_Sodium_Compat::crypto_generichash( 870 ParagonIE_Sodium_Compat::crypto_scalarmult($my_sk, $their_pk) . 871 $client_pk . 872 $server_pk 873 ); 874 } 875 876 /** 877 * ECDH over Curve25519 878 * 879 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 880 * 881 * @param string $sKey 882 * @param string $pKey 883 * @return string 884 * 885 * @throws SodiumException 886 * @throws TypeError 887 */ 888 public static function scalarmult($sKey, $pKey) 889 { 890 $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey); 891 self::scalarmult_throw_if_zero($q); 892 return $q; 893 } 894 895 /** 896 * ECDH over Curve25519, using the basepoint. 897 * Used to get a secret key from a public key. 898 * 899 * @param string $secret 900 * @return string 901 * 902 * @throws SodiumException 903 * @throws TypeError 904 */ 905 public static function scalarmult_base($secret) 906 { 907 $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10_base($secret); 908 self::scalarmult_throw_if_zero($q); 909 return $q; 910 } 911 912 /** 913 * This throws an Error if a zero public key was passed to the function. 914 * 915 * @param string $q 916 * @return void 917 * @throws SodiumException 918 * @throws TypeError 919 */ 920 protected static function scalarmult_throw_if_zero($q) 921 { 922 $d = 0; 923 for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) { 924 $d |= ParagonIE_Sodium_Core_Util::chrToInt($q[$i]); 925 } 926 927 /* branch-free variant of === 0 */ 928 if (-(1 & (($d - 1) >> 8))) { 929 throw new SodiumException('Zero public key is not allowed'); 930 } 931 } 932 933 /** 934 * XSalsa20-Poly1305 authenticated symmetric-key encryption. 935 * 936 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 937 * 938 * @param string $plaintext 939 * @param string $nonce 940 * @param string $key 941 * @return string 942 * @throws SodiumException 943 * @throws TypeError 944 */ 945 public static function secretbox($plaintext, $nonce, $key) 946 { 947 /** @var string $subkey */ 948 $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); 949 950 /** @var string $block0 */ 951 $block0 = str_repeat("\x00", 32); 952 953 /** @var int $mlen - Length of the plaintext message */ 954 $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext); 955 $mlen0 = $mlen; 956 if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) { 957 $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES; 958 } 959 $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0); 960 961 /** @var string $block0 */ 962 $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor( 963 $block0, 964 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), 965 $subkey 966 ); 967 968 /** @var string $c */ 969 $c = ParagonIE_Sodium_Core_Util::substr( 970 $block0, 971 self::secretbox_xsalsa20poly1305_ZEROBYTES 972 ); 973 if ($mlen > $mlen0) { 974 $c .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( 975 ParagonIE_Sodium_Core_Util::substr( 976 $plaintext, 977 self::secretbox_xsalsa20poly1305_ZEROBYTES 978 ), 979 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), 980 1, 981 $subkey 982 ); 983 } 984 $state = new ParagonIE_Sodium_Core_Poly1305_State( 985 ParagonIE_Sodium_Core_Util::substr( 986 $block0, 987 0, 988 self::onetimeauth_poly1305_KEYBYTES 989 ) 990 ); 991 try { 992 ParagonIE_Sodium_Compat::memzero($block0); 993 ParagonIE_Sodium_Compat::memzero($subkey); 994 } catch (SodiumException $ex) { 995 $block0 = null; 996 $subkey = null; 997 } 998 999 $state->update($c); 1000 1001 /** @var string $c - MAC || ciphertext */ 1002 $c = $state->finish() . $c; 1003 unset($state); 1004 1005 return $c; 1006 } 1007 1008 /** 1009 * Decrypt a ciphertext generated via secretbox(). 1010 * 1011 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 1012 * 1013 * @param string $ciphertext 1014 * @param string $nonce 1015 * @param string $key 1016 * @return string 1017 * @throws SodiumException 1018 * @throws TypeError 1019 */ 1020 public static function secretbox_open($ciphertext, $nonce, $key) 1021 { 1022 /** @var string $mac */ 1023 $mac = ParagonIE_Sodium_Core_Util::substr( 1024 $ciphertext, 1025 0, 1026 self::secretbox_xsalsa20poly1305_MACBYTES 1027 ); 1028 1029 /** @var string $c */ 1030 $c = ParagonIE_Sodium_Core_Util::substr( 1031 $ciphertext, 1032 self::secretbox_xsalsa20poly1305_MACBYTES 1033 ); 1034 1035 /** @var int $clen */ 1036 $clen = ParagonIE_Sodium_Core_Util::strlen($c); 1037 1038 /** @var string $subkey */ 1039 $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); 1040 1041 /** @var string $block0 */ 1042 $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20( 1043 64, 1044 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), 1045 $subkey 1046 ); 1047 $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify( 1048 $mac, 1049 $c, 1050 ParagonIE_Sodium_Core_Util::substr($block0, 0, 32) 1051 ); 1052 if (!$verified) { 1053 try { 1054 ParagonIE_Sodium_Compat::memzero($subkey); 1055 } catch (SodiumException $ex) { 1056 $subkey = null; 1057 } 1058 throw new SodiumException('Invalid MAC'); 1059 } 1060 1061 /** @var string $m - Decrypted message */ 1062 $m = ParagonIE_Sodium_Core_Util::xorStrings( 1063 ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES), 1064 ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES) 1065 ); 1066 if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) { 1067 // We had more than 1 block, so let's continue to decrypt the rest. 1068 $m .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( 1069 ParagonIE_Sodium_Core_Util::substr( 1070 $c, 1071 self::secretbox_xsalsa20poly1305_ZEROBYTES 1072 ), 1073 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), 1074 1, 1075 (string) $subkey 1076 ); 1077 } 1078 return $m; 1079 } 1080 1081 /** 1082 * XChaCha20-Poly1305 authenticated symmetric-key encryption. 1083 * 1084 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 1085 * 1086 * @param string $plaintext 1087 * @param string $nonce 1088 * @param string $key 1089 * @return string 1090 * @throws SodiumException 1091 * @throws TypeError 1092 */ 1093 public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key) 1094 { 1095 /** @var string $subkey */ 1096 $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20( 1097 ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), 1098 $key 1099 ); 1100 $nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); 1101 1102 /** @var string $block0 */ 1103 $block0 = str_repeat("\x00", 32); 1104 1105 /** @var int $mlen - Length of the plaintext message */ 1106 $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext); 1107 $mlen0 = $mlen; 1108 if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) { 1109 $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES; 1110 } 1111 $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0); 1112 1113 /** @var string $block0 */ 1114 $block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc( 1115 $block0, 1116 $nonceLast, 1117 $subkey 1118 ); 1119 1120 /** @var string $c */ 1121 $c = ParagonIE_Sodium_Core_Util::substr( 1122 $block0, 1123 self::secretbox_xchacha20poly1305_ZEROBYTES 1124 ); 1125 if ($mlen > $mlen0) { 1126 $c .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc( 1127 ParagonIE_Sodium_Core_Util::substr( 1128 $plaintext, 1129 self::secretbox_xchacha20poly1305_ZEROBYTES 1130 ), 1131 $nonceLast, 1132 $subkey, 1133 ParagonIE_Sodium_Core_Util::store64_le(1) 1134 ); 1135 } 1136 $state = new ParagonIE_Sodium_Core_Poly1305_State( 1137 ParagonIE_Sodium_Core_Util::substr( 1138 $block0, 1139 0, 1140 self::onetimeauth_poly1305_KEYBYTES 1141 ) 1142 ); 1143 try { 1144 ParagonIE_Sodium_Compat::memzero($block0); 1145 ParagonIE_Sodium_Compat::memzero($subkey); 1146 } catch (SodiumException $ex) { 1147 $block0 = null; 1148 $subkey = null; 1149 } 1150 1151 $state->update($c); 1152 1153 /** @var string $c - MAC || ciphertext */ 1154 $c = $state->finish() . $c; 1155 unset($state); 1156 1157 return $c; 1158 } 1159 1160 /** 1161 * Decrypt a ciphertext generated via secretbox_xchacha20poly1305(). 1162 * 1163 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 1164 * 1165 * @param string $ciphertext 1166 * @param string $nonce 1167 * @param string $key 1168 * @return string 1169 * @throws SodiumException 1170 * @throws TypeError 1171 */ 1172 public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key) 1173 { 1174 /** @var string $mac */ 1175 $mac = ParagonIE_Sodium_Core_Util::substr( 1176 $ciphertext, 1177 0, 1178 self::secretbox_xchacha20poly1305_MACBYTES 1179 ); 1180 1181 /** @var string $c */ 1182 $c = ParagonIE_Sodium_Core_Util::substr( 1183 $ciphertext, 1184 self::secretbox_xchacha20poly1305_MACBYTES 1185 ); 1186 1187 /** @var int $clen */ 1188 $clen = ParagonIE_Sodium_Core_Util::strlen($c); 1189 1190 /** @var string $subkey */ 1191 $subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20($nonce, $key); 1192 1193 /** @var string $block0 */ 1194 $block0 = ParagonIE_Sodium_Core_ChaCha20::stream( 1195 64, 1196 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), 1197 $subkey 1198 ); 1199 $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify( 1200 $mac, 1201 $c, 1202 ParagonIE_Sodium_Core_Util::substr($block0, 0, 32) 1203 ); 1204 1205 if (!$verified) { 1206 try { 1207 ParagonIE_Sodium_Compat::memzero($subkey); 1208 } catch (SodiumException $ex) { 1209 $subkey = null; 1210 } 1211 throw new SodiumException('Invalid MAC'); 1212 } 1213 1214 /** @var string $m - Decrypted message */ 1215 $m = ParagonIE_Sodium_Core_Util::xorStrings( 1216 ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES), 1217 ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES) 1218 ); 1219 1220 if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) { 1221 // We had more than 1 block, so let's continue to decrypt the rest. 1222 $m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc( 1223 ParagonIE_Sodium_Core_Util::substr( 1224 $c, 1225 self::secretbox_xchacha20poly1305_ZEROBYTES 1226 ), 1227 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), 1228 (string) $subkey, 1229 ParagonIE_Sodium_Core_Util::store64_le(1) 1230 ); 1231 } 1232 return $m; 1233 } 1234 1235 /** 1236 * @param string $key 1237 * @return array<int, string> Returns a state and a header. 1238 * @throws Exception 1239 * @throws SodiumException 1240 */ 1241 public static function secretstream_xchacha20poly1305_init_push($key) 1242 { 1243 # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES); 1244 $out = random_bytes(24); 1245 1246 # crypto_core_hchacha20(state->k, out, k, NULL); 1247 $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20($out, $key); 1248 $state = new ParagonIE_Sodium_Core_SecretStream_State( 1249 $subkey, 1250 ParagonIE_Sodium_Core_Util::substr($out, 16, 8) . str_repeat("\0", 4) 1251 ); 1252 1253 # _crypto_secretstream_xchacha20poly1305_counter_reset(state); 1254 $state->counterReset(); 1255 1256 # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES, 1257 # crypto_secretstream_xchacha20poly1305_INONCEBYTES); 1258 # memset(state->_pad, 0, sizeof state->_pad); 1259 return array( 1260 $state->toString(), 1261 $out 1262 ); 1263 } 1264 1265 /** 1266 * @param string $key 1267 * @param string $header 1268 * @return string Returns a state. 1269 * @throws Exception 1270 */ 1271 public static function secretstream_xchacha20poly1305_init_pull($key, $header) 1272 { 1273 # crypto_core_hchacha20(state->k, in, k, NULL); 1274 $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20( 1275 ParagonIE_Sodium_Core_Util::substr($header, 0, 16), 1276 $key 1277 ); 1278 $state = new ParagonIE_Sodium_Core_SecretStream_State( 1279 $subkey, 1280 ParagonIE_Sodium_Core_Util::substr($header, 16) 1281 ); 1282 $state->counterReset(); 1283 # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES, 1284 # crypto_secretstream_xchacha20poly1305_INONCEBYTES); 1285 # memset(state->_pad, 0, sizeof state->_pad); 1286 # return 0; 1287 return $state->toString(); 1288 } 1289 1290 /** 1291 * @param string $state 1292 * @param string $msg 1293 * @param string $aad 1294 * @param int $tag 1295 * @return string 1296 * @throws SodiumException 1297 */ 1298 public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0) 1299 { 1300 $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state); 1301 # crypto_onetimeauth_poly1305_state poly1305_state; 1302 # unsigned char block[64U]; 1303 # unsigned char slen[8U]; 1304 # unsigned char *c; 1305 # unsigned char *mac; 1306 1307 $msglen = ParagonIE_Sodium_Core_Util::strlen($msg); 1308 $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad); 1309 1310 if ((($msglen + 63) >> 6) > 0xfffffffe) { 1311 throw new SodiumException( 1312 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes' 1313 ); 1314 } 1315 1316 # if (outlen_p != NULL) { 1317 # *outlen_p = 0U; 1318 # } 1319 # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) { 1320 # sodium_misuse(); 1321 # } 1322 1323 # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k); 1324 # crypto_onetimeauth_poly1305_init(&poly1305_state, block); 1325 # sodium_memzero(block, sizeof block); 1326 $auth = new ParagonIE_Sodium_Core_Poly1305_State( 1327 ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()) 1328 ); 1329 1330 # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen); 1331 $auth->update($aad); 1332 1333 # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0, 1334 # (0x10 - adlen) & 0xf); 1335 $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf))); 1336 1337 # memset(block, 0, sizeof block); 1338 # block[0] = tag; 1339 # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, 1340 # state->nonce, 1U, state->k); 1341 $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( 1342 ParagonIE_Sodium_Core_Util::intToChr($tag) . str_repeat("\0", 63), 1343 $st->getCombinedNonce(), 1344 $st->getKey(), 1345 ParagonIE_Sodium_Core_Util::store64_le(1) 1346 ); 1347 1348 # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block); 1349 $auth->update($block); 1350 1351 # out[0] = block[0]; 1352 $out = $block[0]; 1353 # c = out + (sizeof tag); 1354 # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k); 1355 $cipher = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( 1356 $msg, 1357 $st->getCombinedNonce(), 1358 $st->getKey(), 1359 ParagonIE_Sodium_Core_Util::store64_le(2) 1360 ); 1361 1362 # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen); 1363 $auth->update($cipher); 1364 1365 $out .= $cipher; 1366 unset($cipher); 1367 1368 # crypto_onetimeauth_poly1305_update 1369 # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf); 1370 $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf))); 1371 1372 # STORE64_LE(slen, (uint64_t) adlen); 1373 $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen); 1374 1375 # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); 1376 $auth->update($slen); 1377 1378 # STORE64_LE(slen, (sizeof block) + mlen); 1379 $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen); 1380 1381 # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); 1382 $auth->update($slen); 1383 1384 # mac = c + mlen; 1385 # crypto_onetimeauth_poly1305_final(&poly1305_state, mac); 1386 $mac = $auth->finish(); 1387 $out .= $mac; 1388 1389 # sodium_memzero(&poly1305_state, sizeof poly1305_state); 1390 unset($auth); 1391 1392 1393 # XOR_BUF(STATE_INONCE(state), mac, 1394 # crypto_secretstream_xchacha20poly1305_INONCEBYTES); 1395 $st->xorNonce($mac); 1396 1397 # sodium_increment(STATE_COUNTER(state), 1398 # crypto_secretstream_xchacha20poly1305_COUNTERBYTES); 1399 $st->incrementCounter(); 1400 // Overwrite by reference: 1401 $state = $st->toString(); 1402 1403 /** @var bool $rekey */ 1404 $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0; 1405 # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 || 1406 # sodium_is_zero(STATE_COUNTER(state), 1407 # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) { 1408 # crypto_secretstream_xchacha20poly1305_rekey(state); 1409 # } 1410 if ($rekey || $st->needsRekey()) { 1411 // DO REKEY 1412 self::secretstream_xchacha20poly1305_rekey($state); 1413 } 1414 # if (outlen_p != NULL) { 1415 # *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen; 1416 # } 1417 return $out; 1418 } 1419 1420 /** 1421 * @param string $state 1422 * @param string $cipher 1423 * @param string $aad 1424 * @return bool|array{0: string, 1: int} 1425 * @throws SodiumException 1426 */ 1427 public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '') 1428 { 1429 $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state); 1430 1431 $cipherlen = ParagonIE_Sodium_Core_Util::strlen($cipher); 1432 # mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES; 1433 $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES; 1434 $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad); 1435 1436 # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) { 1437 # sodium_misuse(); 1438 # } 1439 if ((($msglen + 63) >> 6) > 0xfffffffe) { 1440 throw new SodiumException( 1441 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes' 1442 ); 1443 } 1444 1445 # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k); 1446 # crypto_onetimeauth_poly1305_init(&poly1305_state, block); 1447 # sodium_memzero(block, sizeof block); 1448 $auth = new ParagonIE_Sodium_Core_Poly1305_State( 1449 ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()) 1450 ); 1451 1452 # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen); 1453 $auth->update($aad); 1454 1455 # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0, 1456 # (0x10 - adlen) & 0xf); 1457 $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf))); 1458 1459 1460 # memset(block, 0, sizeof block); 1461 # block[0] = in[0]; 1462 # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, 1463 # state->nonce, 1U, state->k); 1464 $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( 1465 $cipher[0] . str_repeat("\0", 63), 1466 $st->getCombinedNonce(), 1467 $st->getKey(), 1468 ParagonIE_Sodium_Core_Util::store64_le(1) 1469 ); 1470 # tag = block[0]; 1471 # block[0] = in[0]; 1472 # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block); 1473 $tag = ParagonIE_Sodium_Core_Util::chrToInt($block[0]); 1474 $block[0] = $cipher[0]; 1475 $auth->update($block); 1476 1477 1478 # c = in + (sizeof tag); 1479 # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen); 1480 $auth->update(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen)); 1481 1482 # crypto_onetimeauth_poly1305_update 1483 # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf); 1484 $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf))); 1485 1486 # STORE64_LE(slen, (uint64_t) adlen); 1487 # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); 1488 $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen); 1489 $auth->update($slen); 1490 1491 # STORE64_LE(slen, (sizeof block) + mlen); 1492 # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); 1493 $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen); 1494 $auth->update($slen); 1495 1496 # crypto_onetimeauth_poly1305_final(&poly1305_state, mac); 1497 # sodium_memzero(&poly1305_state, sizeof poly1305_state); 1498 $mac = $auth->finish(); 1499 1500 # stored_mac = c + mlen; 1501 # if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) { 1502 # sodium_memzero(mac, sizeof mac); 1503 # return -1; 1504 # } 1505 1506 $stored = ParagonIE_Sodium_Core_Util::substr($cipher, $msglen + 1, 16); 1507 if (!ParagonIE_Sodium_Core_Util::hashEquals($mac, $stored)) { 1508 return false; 1509 } 1510 1511 # crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k); 1512 $out = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( 1513 ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen), 1514 $st->getCombinedNonce(), 1515 $st->getKey(), 1516 ParagonIE_Sodium_Core_Util::store64_le(2) 1517 ); 1518 1519 # XOR_BUF(STATE_INONCE(state), mac, 1520 # crypto_secretstream_xchacha20poly1305_INONCEBYTES); 1521 $st->xorNonce($mac); 1522 1523 # sodium_increment(STATE_COUNTER(state), 1524 # crypto_secretstream_xchacha20poly1305_COUNTERBYTES); 1525 $st->incrementCounter(); 1526 1527 # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 || 1528 # sodium_is_zero(STATE_COUNTER(state), 1529 # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) { 1530 # crypto_secretstream_xchacha20poly1305_rekey(state); 1531 # } 1532 1533 // Overwrite by reference: 1534 $state = $st->toString(); 1535 1536 /** @var bool $rekey */ 1537 $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0; 1538 if ($rekey || $st->needsRekey()) { 1539 // DO REKEY 1540 self::secretstream_xchacha20poly1305_rekey($state); 1541 } 1542 return array($out, $tag); 1543 } 1544 1545 /** 1546 * @param string $state 1547 * @return void 1548 * @throws SodiumException 1549 */ 1550 public static function secretstream_xchacha20poly1305_rekey(&$state) 1551 { 1552 $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state); 1553 # unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + 1554 # crypto_secretstream_xchacha20poly1305_INONCEBYTES]; 1555 # size_t i; 1556 # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) { 1557 # new_key_and_inonce[i] = state->k[i]; 1558 # } 1559 $new_key_and_inonce = $st->getKey(); 1560 1561 # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) { 1562 # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] = 1563 # STATE_INONCE(state)[i]; 1564 # } 1565 $new_key_and_inonce .= ParagonIE_Sodium_Core_Util::substR($st->getNonce(), 0, 8); 1566 1567 # crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce, 1568 # sizeof new_key_and_inonce, 1569 # state->nonce, state->k); 1570 1571 $st->rekey(ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( 1572 $new_key_and_inonce, 1573 $st->getCombinedNonce(), 1574 $st->getKey(), 1575 ParagonIE_Sodium_Core_Util::store64_le(0) 1576 )); 1577 1578 # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) { 1579 # state->k[i] = new_key_and_inonce[i]; 1580 # } 1581 # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) { 1582 # STATE_INONCE(state)[i] = 1583 # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i]; 1584 # } 1585 # _crypto_secretstream_xchacha20poly1305_counter_reset(state); 1586 $st->counterReset(); 1587 1588 $state = $st->toString(); 1589 } 1590 1591 /** 1592 * Detached Ed25519 signature. 1593 * 1594 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 1595 * 1596 * @param string $message 1597 * @param string $sk 1598 * @return string 1599 * @throws SodiumException 1600 * @throws TypeError 1601 */ 1602 public static function sign_detached($message, $sk) 1603 { 1604 return ParagonIE_Sodium_Core_Ed25519::sign_detached($message, $sk); 1605 } 1606 1607 /** 1608 * Attached Ed25519 signature. (Returns a signed message.) 1609 * 1610 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 1611 * 1612 * @param string $message 1613 * @param string $sk 1614 * @return string 1615 * @throws SodiumException 1616 * @throws TypeError 1617 */ 1618 public static function sign($message, $sk) 1619 { 1620 return ParagonIE_Sodium_Core_Ed25519::sign($message, $sk); 1621 } 1622 1623 /** 1624 * Opens a signed message. If valid, returns the message. 1625 * 1626 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 1627 * 1628 * @param string $signedMessage 1629 * @param string $pk 1630 * @return string 1631 * @throws SodiumException 1632 * @throws TypeError 1633 */ 1634 public static function sign_open($signedMessage, $pk) 1635 { 1636 return ParagonIE_Sodium_Core_Ed25519::sign_open($signedMessage, $pk); 1637 } 1638 1639 /** 1640 * Verify a detached signature of a given message and public key. 1641 * 1642 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. 1643 * 1644 * @param string $signature 1645 * @param string $message 1646 * @param string $pk 1647 * @return bool 1648 * @throws SodiumException 1649 * @throws TypeError 1650 */ 1651 public static function sign_verify_detached($signature, $message, $pk) 1652 { 1653 return ParagonIE_Sodium_Core_Ed25519::verify_detached($signature, $message, $pk); 1654 } 1655 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Nov 21 01:00:03 2024 | Cross-referenced by PHPXref 0.7.1 |