/htdocs/wp-includes/sodium_compat/src/Crypto.php

https://gitlab.com/VTTE/sitios-vtte · PHP · 1513 lines · 778 code · 168 blank · 567 comment · 32 complexity · 2388759db9a25d074eba5b36a953ee7b MD5 · raw file

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