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

https://gitlab.com/VTTE/sitios-vtte · PHP · 1506 lines · 773 code · 167 blank · 566 comment · 32 complexity · c19bd008e102663cbbd2c4f0f3cc1326 MD5 · raw file

  1. <?php
  2. if (class_exists('ParagonIE_Sodium_Crypto32', 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_Crypto32
  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_Core32_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_Core32_Util::strlen($ad);
  73. /** @var string $mac - Message authentication code */
  74. $mac = ParagonIE_Sodium_Core32_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_Core32_Util::substr($message, 0, $clen);
  81. /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
  82. $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
  83. 32,
  84. $nonce,
  85. $key
  86. );
  87. /* Recalculate the Poly1305 authentication tag (MAC): */
  88. $state = new ParagonIE_Sodium_Core32_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_Core32_Util::store64_le($adlen));
  96. $state->update($ciphertext);
  97. $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen));
  98. $computed_mac = $state->finish();
  99. /* Compare the given MAC with the recalculated MAC: */
  100. if (!ParagonIE_Sodium_Core32_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_Core32_ChaCha20::streamXorIc(
  105. $ciphertext,
  106. $nonce,
  107. $key,
  108. ParagonIE_Sodium_Core32_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_Core32_Util::strlen($message);
  132. /** @var int $adlen - Length of the associated data */
  133. $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
  134. /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
  135. $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
  136. 32,
  137. $nonce,
  138. $key
  139. );
  140. $state = new ParagonIE_Sodium_Core32_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_Core32_ChaCha20::streamXorIc(
  148. $message,
  149. $nonce,
  150. $key,
  151. ParagonIE_Sodium_Core32_Util::store64_le(1)
  152. );
  153. $state->update($ad);
  154. $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen));
  155. $state->update($ciphertext);
  156. $state->update(ParagonIE_Sodium_Core32_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_Core32_Util::strlen($ad);
  180. /** @var int $len - Length of message (ciphertext + MAC) */
  181. $len = ParagonIE_Sodium_Core32_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_Core32_ChaCha20::ietfStream(
  186. 32,
  187. $nonce,
  188. $key
  189. );
  190. /** @var string $mac - Message authentication code */
  191. $mac = ParagonIE_Sodium_Core32_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_Core32_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_Core32_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_Core32_Util::store64_le($adlen));
  214. $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen));
  215. $computed_mac = $state->finish();
  216. /* Compare the given MAC with the recalculated MAC: */
  217. if (!ParagonIE_Sodium_Core32_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_Core32_ChaCha20::ietfStreamXorIc(
  222. $ciphertext,
  223. $nonce,
  224. $key,
  225. ParagonIE_Sodium_Core32_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_Core32_Util::strlen($message);
  249. /** @var int $adlen - Length of the associated data */
  250. $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
  251. /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
  252. $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream(
  253. 32,
  254. $nonce,
  255. $key
  256. );
  257. $state = new ParagonIE_Sodium_Core32_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_Core32_ChaCha20::ietfStreamXorIc(
  265. $message,
  266. $nonce,
  267. $key,
  268. ParagonIE_Sodium_Core32_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_Core32_Util::store64_le($adlen));
  275. $state->update(ParagonIE_Sodium_Core32_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_Core32_HChaCha20::hChaCha20(
  298. ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
  299. $key
  300. );
  301. $nonceLast = "\x00\x00\x00\x00" .
  302. ParagonIE_Sodium_Core32_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_Core32_HChaCha20::hChaCha20(
  325. ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
  326. $key
  327. );
  328. $nonceLast = "\x00\x00\x00\x00" .
  329. ParagonIE_Sodium_Core32_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_Core32_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_Core32_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. return self::secretbox(
  384. $plaintext,
  385. $nonce,
  386. self::box_beforenm(
  387. self::box_secretkey($keypair),
  388. self::box_publickey($keypair)
  389. )
  390. );
  391. }
  392. /**
  393. * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair.
  394. *
  395. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  396. *
  397. * @param string $message
  398. * @param string $publicKey
  399. * @return string
  400. * @throws SodiumException
  401. * @throws TypeError
  402. */
  403. public static function box_seal($message, $publicKey)
  404. {
  405. /** @var string $ephemeralKeypair */
  406. $ephemeralKeypair = self::box_keypair();
  407. /** @var string $ephemeralSK */
  408. $ephemeralSK = self::box_secretkey($ephemeralKeypair);
  409. /** @var string $ephemeralPK */
  410. $ephemeralPK = self::box_publickey($ephemeralKeypair);
  411. /** @var string $nonce */
  412. $nonce = self::generichash(
  413. $ephemeralPK . $publicKey,
  414. '',
  415. 24
  416. );
  417. /** @var string $keypair - The combined keypair used in crypto_box() */
  418. $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey);
  419. /** @var string $ciphertext Ciphertext + MAC from crypto_box */
  420. $ciphertext = self::box($message, $nonce, $keypair);
  421. try {
  422. ParagonIE_Sodium_Compat::memzero($ephemeralKeypair);
  423. ParagonIE_Sodium_Compat::memzero($ephemeralSK);
  424. ParagonIE_Sodium_Compat::memzero($nonce);
  425. } catch (SodiumException $ex) {
  426. $ephemeralKeypair = null;
  427. $ephemeralSK = null;
  428. $nonce = null;
  429. }
  430. return $ephemeralPK . $ciphertext;
  431. }
  432. /**
  433. * Opens a message encrypted via box_seal().
  434. *
  435. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  436. *
  437. * @param string $message
  438. * @param string $keypair
  439. * @return string
  440. * @throws SodiumException
  441. * @throws TypeError
  442. */
  443. public static function box_seal_open($message, $keypair)
  444. {
  445. /** @var string $ephemeralPK */
  446. $ephemeralPK = ParagonIE_Sodium_Core32_Util::substr($message, 0, 32);
  447. /** @var string $ciphertext (ciphertext + MAC) */
  448. $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 32);
  449. /** @var string $secretKey */
  450. $secretKey = self::box_secretkey($keypair);
  451. /** @var string $publicKey */
  452. $publicKey = self::box_publickey($keypair);
  453. /** @var string $nonce */
  454. $nonce = self::generichash(
  455. $ephemeralPK . $publicKey,
  456. '',
  457. 24
  458. );
  459. /** @var string $keypair */
  460. $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK);
  461. /** @var string $m */
  462. $m = self::box_open($ciphertext, $nonce, $keypair);
  463. try {
  464. ParagonIE_Sodium_Compat::memzero($secretKey);
  465. ParagonIE_Sodium_Compat::memzero($ephemeralPK);
  466. ParagonIE_Sodium_Compat::memzero($nonce);
  467. } catch (SodiumException $ex) {
  468. $secretKey = null;
  469. $ephemeralPK = null;
  470. $nonce = null;
  471. }
  472. return $m;
  473. }
  474. /**
  475. * Used by crypto_box() to get the crypto_secretbox() key.
  476. *
  477. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  478. *
  479. * @param string $sk
  480. * @param string $pk
  481. * @return string
  482. * @throws SodiumException
  483. * @throws TypeError
  484. */
  485. public static function box_beforenm($sk, $pk)
  486. {
  487. return ParagonIE_Sodium_Core32_HSalsa20::hsalsa20(
  488. str_repeat("\x00", 16),
  489. self::scalarmult($sk, $pk)
  490. );
  491. }
  492. /**
  493. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  494. *
  495. * @return string
  496. * @throws Exception
  497. * @throws SodiumException
  498. * @throws TypeError
  499. */
  500. public static function box_keypair()
  501. {
  502. $sKey = random_bytes(32);
  503. $pKey = self::scalarmult_base($sKey);
  504. return $sKey . $pKey;
  505. }
  506. /**
  507. * @param string $seed
  508. * @return string
  509. * @throws SodiumException
  510. * @throws TypeError
  511. */
  512. public static function box_seed_keypair($seed)
  513. {
  514. $sKey = ParagonIE_Sodium_Core32_Util::substr(
  515. hash('sha512', $seed, true),
  516. 0,
  517. 32
  518. );
  519. $pKey = self::scalarmult_base($sKey);
  520. return $sKey . $pKey;
  521. }
  522. /**
  523. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  524. *
  525. * @param string $sKey
  526. * @param string $pKey
  527. * @return string
  528. * @throws TypeError
  529. */
  530. public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey)
  531. {
  532. return ParagonIE_Sodium_Core32_Util::substr($sKey, 0, 32) .
  533. ParagonIE_Sodium_Core32_Util::substr($pKey, 0, 32);
  534. }
  535. /**
  536. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  537. *
  538. * @param string $keypair
  539. * @return string
  540. * @throws RangeException
  541. * @throws TypeError
  542. */
  543. public static function box_secretkey($keypair)
  544. {
  545. if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== 64) {
  546. throw new RangeException(
  547. 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
  548. );
  549. }
  550. return ParagonIE_Sodium_Core32_Util::substr($keypair, 0, 32);
  551. }
  552. /**
  553. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  554. *
  555. * @param string $keypair
  556. * @return string
  557. * @throws RangeException
  558. * @throws TypeError
  559. */
  560. public static function box_publickey($keypair)
  561. {
  562. if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
  563. throw new RangeException(
  564. 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
  565. );
  566. }
  567. return ParagonIE_Sodium_Core32_Util::substr($keypair, 32, 32);
  568. }
  569. /**
  570. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  571. *
  572. * @param string $sKey
  573. * @return string
  574. * @throws RangeException
  575. * @throws SodiumException
  576. * @throws TypeError
  577. */
  578. public static function box_publickey_from_secretkey($sKey)
  579. {
  580. if (ParagonIE_Sodium_Core32_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) {
  581. throw new RangeException(
  582. 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.'
  583. );
  584. }
  585. return self::scalarmult_base($sKey);
  586. }
  587. /**
  588. * Decrypt a message encrypted with box().
  589. *
  590. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  591. *
  592. * @param string $ciphertext
  593. * @param string $nonce
  594. * @param string $keypair
  595. * @return string
  596. * @throws SodiumException
  597. * @throws TypeError
  598. */
  599. public static function box_open($ciphertext, $nonce, $keypair)
  600. {
  601. return self::secretbox_open(
  602. $ciphertext,
  603. $nonce,
  604. self::box_beforenm(
  605. self::box_secretkey($keypair),
  606. self::box_publickey($keypair)
  607. )
  608. );
  609. }
  610. /**
  611. * Calculate a BLAKE2b hash.
  612. *
  613. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  614. *
  615. * @param string $message
  616. * @param string|null $key
  617. * @param int $outlen
  618. * @return string
  619. * @throws RangeException
  620. * @throws SodiumException
  621. * @throws TypeError
  622. */
  623. public static function generichash($message, $key = '', $outlen = 32)
  624. {
  625. // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
  626. ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
  627. $k = null;
  628. if (!empty($key)) {
  629. /** @var SplFixedArray $k */
  630. $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
  631. if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
  632. throw new RangeException('Invalid key size');
  633. }
  634. }
  635. /** @var SplFixedArray $in */
  636. $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
  637. /** @var SplFixedArray $ctx */
  638. $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outlen);
  639. ParagonIE_Sodium_Core32_BLAKE2b::update($ctx, $in, $in->count());
  640. /** @var SplFixedArray $out */
  641. $out = new SplFixedArray($outlen);
  642. $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($ctx, $out);
  643. /** @var array<int, int> */
  644. $outArray = $out->toArray();
  645. return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray);
  646. }
  647. /**
  648. * Finalize a BLAKE2b hashing context, returning the hash.
  649. *
  650. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  651. *
  652. * @param string $ctx
  653. * @param int $outlen
  654. * @return string
  655. * @throws SodiumException
  656. * @throws TypeError
  657. */
  658. public static function generichash_final($ctx, $outlen = 32)
  659. {
  660. if (!is_string($ctx)) {
  661. throw new TypeError('Context must be a string');
  662. }
  663. $out = new SplFixedArray($outlen);
  664. /** @var SplFixedArray $context */
  665. $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
  666. /** @var SplFixedArray $out */
  667. $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($context, $out);
  668. /** @var array<int, int> */
  669. $outArray = $out->toArray();
  670. return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray);
  671. }
  672. /**
  673. * Initialize a hashing context for BLAKE2b.
  674. *
  675. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  676. *
  677. * @param string $key
  678. * @param int $outputLength
  679. * @return string
  680. * @throws RangeException
  681. * @throws SodiumException
  682. * @throws TypeError
  683. */
  684. public static function generichash_init($key = '', $outputLength = 32)
  685. {
  686. // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
  687. ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
  688. $k = null;
  689. if (!empty($key)) {
  690. $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
  691. if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
  692. throw new RangeException('Invalid key size');
  693. }
  694. }
  695. /** @var SplFixedArray $ctx */
  696. $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength);
  697. return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx);
  698. }
  699. /**
  700. * Initialize a hashing context for BLAKE2b.
  701. *
  702. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  703. *
  704. * @param string $key
  705. * @param int $outputLength
  706. * @param string $salt
  707. * @param string $personal
  708. * @return string
  709. * @throws RangeException
  710. * @throws SodiumException
  711. * @throws TypeError
  712. */
  713. public static function generichash_init_salt_personal(
  714. $key = '',
  715. $outputLength = 32,
  716. $salt = '',
  717. $personal = ''
  718. ) {
  719. // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
  720. ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
  721. $k = null;
  722. if (!empty($key)) {
  723. $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
  724. if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
  725. throw new RangeException('Invalid key size');
  726. }
  727. }
  728. if (!empty($salt)) {
  729. $s = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($salt);
  730. } else {
  731. $s = null;
  732. }
  733. if (!empty($salt)) {
  734. $p = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($personal);
  735. } else {
  736. $p = null;
  737. }
  738. /** @var SplFixedArray $ctx */
  739. $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength, $s, $p);
  740. return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx);
  741. }
  742. /**
  743. * Update a hashing context for BLAKE2b with $message
  744. *
  745. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  746. *
  747. * @param string $ctx
  748. * @param string $message
  749. * @return string
  750. * @throws SodiumException
  751. * @throws TypeError
  752. */
  753. public static function generichash_update($ctx, $message)
  754. {
  755. // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
  756. ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
  757. /** @var SplFixedArray $context */
  758. $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
  759. /** @var SplFixedArray $in */
  760. $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
  761. ParagonIE_Sodium_Core32_BLAKE2b::update($context, $in, $in->count());
  762. return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($context);
  763. }
  764. /**
  765. * Libsodium's crypto_kx().
  766. *
  767. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  768. *
  769. * @param string $my_sk
  770. * @param string $their_pk
  771. * @param string $client_pk
  772. * @param string $server_pk
  773. * @return string
  774. * @throws SodiumException
  775. * @throws TypeError
  776. */
  777. public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
  778. {
  779. return self::generichash(
  780. self::scalarmult($my_sk, $their_pk) .
  781. $client_pk .
  782. $server_pk
  783. );
  784. }
  785. /**
  786. * ECDH over Curve25519
  787. *
  788. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  789. *
  790. * @param string $sKey
  791. * @param string $pKey
  792. * @return string
  793. *
  794. * @throws SodiumException
  795. * @throws TypeError
  796. */
  797. public static function scalarmult($sKey, $pKey)
  798. {
  799. $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
  800. self::scalarmult_throw_if_zero($q);
  801. return $q;
  802. }
  803. /**
  804. * ECDH over Curve25519, using the basepoint.
  805. * Used to get a secret key from a public key.
  806. *
  807. * @param string $secret
  808. * @return string
  809. *
  810. * @throws SodiumException
  811. * @throws TypeError
  812. */
  813. public static function scalarmult_base($secret)
  814. {
  815. $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
  816. self::scalarmult_throw_if_zero($q);
  817. return $q;
  818. }
  819. /**
  820. * This throws an Error if a zero public key was passed to the function.
  821. *
  822. * @param string $q
  823. * @return void
  824. * @throws SodiumException
  825. * @throws TypeError
  826. */
  827. protected static function scalarmult_throw_if_zero($q)
  828. {
  829. $d = 0;
  830. for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
  831. $d |= ParagonIE_Sodium_Core32_Util::chrToInt($q[$i]);
  832. }
  833. /* branch-free variant of === 0 */
  834. if (-(1 & (($d - 1) >> 8))) {
  835. throw new SodiumException('Zero public key is not allowed');
  836. }
  837. }
  838. /**
  839. * XSalsa20-Poly1305 authenticated symmetric-key encryption.
  840. *
  841. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  842. *
  843. * @param string $plaintext
  844. * @param string $nonce
  845. * @param string $key
  846. * @return string
  847. * @throws SodiumException
  848. * @throws TypeError
  849. */
  850. public static function secretbox($plaintext, $nonce, $key)
  851. {
  852. /** @var string $subkey */
  853. $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
  854. /** @var string $block0 */
  855. $block0 = str_repeat("\x00", 32);
  856. /** @var int $mlen - Length of the plaintext message */
  857. $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
  858. $mlen0 = $mlen;
  859. if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
  860. $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
  861. }
  862. $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
  863. /** @var string $block0 */
  864. $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor(
  865. $block0,
  866. ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
  867. $subkey
  868. );
  869. /** @var string $c */
  870. $c = ParagonIE_Sodium_Core32_Util::substr(
  871. $block0,
  872. self::secretbox_xsalsa20poly1305_ZEROBYTES
  873. );
  874. if ($mlen > $mlen0) {
  875. $c .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
  876. ParagonIE_Sodium_Core32_Util::substr(
  877. $plaintext,
  878. self::secretbox_xsalsa20poly1305_ZEROBYTES
  879. ),
  880. ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
  881. 1,
  882. $subkey
  883. );
  884. }
  885. $state = new ParagonIE_Sodium_Core32_Poly1305_State(
  886. ParagonIE_Sodium_Core32_Util::substr(
  887. $block0,
  888. 0,
  889. self::onetimeauth_poly1305_KEYBYTES
  890. )
  891. );
  892. try {
  893. ParagonIE_Sodium_Compat::memzero($block0);
  894. ParagonIE_Sodium_Compat::memzero($subkey);
  895. } catch (SodiumException $ex) {
  896. $block0 = null;
  897. $subkey = null;
  898. }
  899. $state->update($c);
  900. /** @var string $c - MAC || ciphertext */
  901. $c = $state->finish() . $c;
  902. unset($state);
  903. return $c;
  904. }
  905. /**
  906. * Decrypt a ciphertext generated via secretbox().
  907. *
  908. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  909. *
  910. * @param string $ciphertext
  911. * @param string $nonce
  912. * @param string $key
  913. * @return string
  914. * @throws SodiumException
  915. * @throws TypeError
  916. */
  917. public static function secretbox_open($ciphertext, $nonce, $key)
  918. {
  919. /** @var string $mac */
  920. $mac = ParagonIE_Sodium_Core32_Util::substr(
  921. $ciphertext,
  922. 0,
  923. self::secretbox_xsalsa20poly1305_MACBYTES
  924. );
  925. /** @var string $c */
  926. $c = ParagonIE_Sodium_Core32_Util::substr(
  927. $ciphertext,
  928. self::secretbox_xsalsa20poly1305_MACBYTES
  929. );
  930. /** @var int $clen */
  931. $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
  932. /** @var string $subkey */
  933. $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
  934. /** @var string $block0 */
  935. $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20(
  936. 64,
  937. ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
  938. $subkey
  939. );
  940. $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
  941. $mac,
  942. $c,
  943. ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
  944. );
  945. if (!$verified) {
  946. try {
  947. ParagonIE_Sodium_Compat::memzero($subkey);
  948. } catch (SodiumException $ex) {
  949. $subkey = null;
  950. }
  951. throw new SodiumException('Invalid MAC');
  952. }
  953. /** @var string $m - Decrypted message */
  954. $m = ParagonIE_Sodium_Core32_Util::xorStrings(
  955. ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
  956. ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
  957. );
  958. if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
  959. // We had more than 1 block, so let's continue to decrypt the rest.
  960. $m .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
  961. ParagonIE_Sodium_Core32_Util::substr(
  962. $c,
  963. self::secretbox_xsalsa20poly1305_ZEROBYTES
  964. ),
  965. ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
  966. 1,
  967. (string) $subkey
  968. );
  969. }
  970. return $m;
  971. }
  972. /**
  973. * XChaCha20-Poly1305 authenticated symmetric-key encryption.
  974. *
  975. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  976. *
  977. * @param string $plaintext
  978. * @param string $nonce
  979. * @param string $key
  980. * @return string
  981. * @throws SodiumException
  982. * @throws TypeError
  983. */
  984. public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
  985. {
  986. /** @var string $subkey */
  987. $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
  988. ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
  989. $key
  990. );
  991. $nonceLast = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
  992. /** @var string $block0 */
  993. $block0 = str_repeat("\x00", 32);
  994. /** @var int $mlen - Length of the plaintext message */
  995. $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
  996. $mlen0 = $mlen;
  997. if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
  998. $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
  999. }
  1000. $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
  1001. /** @var string $block0 */
  1002. $block0 = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
  1003. $block0,
  1004. $nonceLast,
  1005. $subkey
  1006. );
  1007. /** @var string $c */
  1008. $c = ParagonIE_Sodium_Core32_Util::substr(
  1009. $block0,
  1010. self::secretbox_xchacha20poly1305_ZEROBYTES
  1011. );
  1012. if ($mlen > $mlen0) {
  1013. $c .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
  1014. ParagonIE_Sodium_Core32_Util::substr(
  1015. $plaintext,
  1016. self::secretbox_xchacha20poly1305_ZEROBYTES
  1017. ),
  1018. $nonceLast,
  1019. $subkey,
  1020. ParagonIE_Sodium_Core32_Util::store64_le(1)
  1021. );
  1022. }
  1023. $state = new ParagonIE_Sodium_Core32_Poly1305_State(
  1024. ParagonIE_Sodium_Core32_Util::substr(
  1025. $block0,
  1026. 0,
  1027. self::onetimeauth_poly1305_KEYBYTES
  1028. )
  1029. );
  1030. try {
  1031. ParagonIE_Sodium_Compat::memzero($block0);
  1032. ParagonIE_Sodium_Compat::memzero($subkey);
  1033. } catch (SodiumException $ex) {
  1034. $block0 = null;
  1035. $subkey = null;
  1036. }
  1037. $state->update($c);
  1038. /** @var string $c - MAC || ciphertext */
  1039. $c = $state->finish() . $c;
  1040. unset($state);
  1041. return $c;
  1042. }
  1043. /**
  1044. * Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
  1045. *
  1046. * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  1047. *
  1048. * @param string $ciphertext
  1049. * @param string $nonce
  1050. * @param string $key
  1051. * @return string
  1052. * @throws SodiumException
  1053. * @throws TypeError
  1054. */
  1055. public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
  1056. {
  1057. /** @var string $mac */
  1058. $mac = ParagonIE_Sodium_Core32_Util::substr(
  1059. $ciphertext,
  1060. 0,
  1061. self::secretbox_xchacha20poly1305_MACBYTES
  1062. );
  1063. /** @var string $c */
  1064. $c = ParagonIE_Sodium_Core32_Util::substr(
  1065. $ciphertext,
  1066. self::secretbox_xchacha20poly1305_MACBYTES
  1067. );
  1068. /** @var int $clen */
  1069. $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
  1070. /** @var string $subkey */
  1071. $subkey = ParagonIE_Sodium_Core32_HChaCha20::hchacha20($nonce, $key);
  1072. /** @var string $block0 */
  1073. $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
  1074. 64,
  1075. ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
  1076. $subkey
  1077. );
  1078. $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
  1079. $mac,
  1080. $c,
  1081. ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
  1082. );
  1083. if (!$verified) {
  1084. try {
  1085. ParagonIE_Sodium_Compat::memzero($subkey);
  1086. } catch (SodiumException $ex) {
  1087. $subkey = null;
  1088. }
  1089. throw new SodiumException('Invalid MAC');
  1090. }
  1091. /** @var string $m - Decrypted message */
  1092. $m = ParagonIE_Sodium_Core32_Util::xorStrings(
  1093. ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
  1094. ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
  1095. );
  1096. if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
  1097. // We had more than 1 block, so let's continue to decrypt the rest.
  1098. $m .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
  1099. ParagonIE_Sodium_Core32_Util::substr(
  1100. $c,
  1101. self::secretbox_xchacha20poly1305_ZEROBYTES
  1102. ),
  1103. ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
  1104. (string) $subkey,
  1105. ParagonIE_Sodium_Core32_Util::store64_le(1)
  1106. );
  1107. }
  1108. return $m;
  1109. }
  1110. /**
  1111. * @param string $key
  1112. * @return array<int, string> Returns a state and a header.
  1113. * @throws Exception
  1114. * @throws SodiumException
  1115. */
  1116. public static function secretstream_xchacha20poly1305_init_push($key)
  1117. {
  1118. # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES);
  1119. $out = random_bytes(24);
  1120. # crypto_core_hchacha20(state->k, out, k, NULL);
  1121. $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20($out, $key);
  1122. $state = new ParagonIE_Sodium_Core32_SecretStream_State(
  1123. $subkey,
  1124. ParagonIE_Sodium_Core32_Util::substr($out, 16, 8) . str_repeat("\0", 4)
  1125. );
  1126. # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
  1127. $state->counterReset();
  1128. # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES,
  1129. # crypto_secretstream_xchacha20poly1305_INONCEBYTES);
  1130. # memset(state->_pad, 0, sizeof state->_pad);
  1131. return array(
  1132. $state->toString(),
  1133. $out
  1134. );
  1135. }
  1136. /**
  1137. * @param string $key
  1138. * @param string $header
  1139. * @return string Returns a state.
  1140. * @throws Exception
  1141. */
  1142. public static function secretstream_xchacha20poly1305_init_pull($key, $header)
  1143. {
  1144. # crypto_core_hchacha20(state->k, in, k, NULL);
  1145. $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
  1146. ParagonIE_Sodium_Core32_Util::substr($header, 0, 16),
  1147. $key
  1148. );
  1149. $state = new ParagonIE_Sodium_Core32_SecretStream_State(
  1150. $subkey,
  1151. ParagonIE_Sodium_Core32_Util::substr($header, 16)
  1152. );
  1153. $state->counterReset();
  1154. # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
  1155. # crypto_secretstream_xchacha20poly1305_INONCEBYTES);
  1156. # memset(state->_pad, 0, sizeof state->_pad);
  1157. # return 0;
  1158. return $state->toString();
  1159. }
  1160. /**
  1161. * @param string $state
  1162. * @param string $msg
  1163. * @param string $aad
  1164. * @param int $tag
  1165. * @return string
  1166. * @throws SodiumException
  1167. */
  1168. public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
  1169. {
  1170. $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
  1171. # crypto_onetimeauth_poly1305_state poly1305_state;
  1172. # unsigned char block[64U];
  1173. # unsigned char slen[8U];
  1174. # unsigned char *c;
  1175. # unsigned char *mac;
  1176. $msglen = ParagonIE_Sodium_Core32_Util::strlen($msg);
  1177. $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad);
  1178. if ((($msglen + 63) >> 6) > 0xfffffffe) {
  1179. throw new SodiumException(
  1180. 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
  1181. );
  1182. }
  1183. # if (outlen_p != NULL) {
  1184. # *outlen_p = 0U;
  1185. # }
  1186. # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
  1187. # sodium_misuse();
  1188. # }
  1189. # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
  1190. # crypto_onetimeauth_poly1305_init(&poly1305_state, block);
  1191. # sodium_memzero(block, sizeof block);
  1192. $auth = new ParagonIE_Sodium_Core32_Poly1305_State(
  1193. ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
  1194. );
  1195. # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
  1196. $auth->update($aad);
  1197. # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
  1198. # (0x10 - adlen) & 0xf);
  1199. $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
  1200. # memset(block, 0, sizeof block);
  1201. # block[0] = tag;
  1202. # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
  1203. # state->nonce, 1U, state->k);
  1204. $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
  1205. ParagonIE_Sodium_Core32_Util::intToChr($tag) . str_repeat("\0", 63),
  1206. $st->getCombinedNonce(),
  1207. $st->getKey(),
  1208. ParagonIE_Sodium_Core32_Util::store64_le(1)
  1209. );
  1210. # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
  1211. $auth->update($block);
  1212. # out[0] = block[0];
  1213. $out = $block[0];
  1214. # c = out + (sizeof tag);
  1215. # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
  1216. $cipher = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
  1217. $msg,
  1218. $st->getCombinedNonce(),
  1219. $st->getKey(),
  1220. ParagonIE_Sodium_Core32_Util::store64_le(2)
  1221. );
  1222. # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
  1223. $auth->update($cipher);
  1224. $out .= $cipher;
  1225. unset($cipher);
  1226. # crypto_onetimeauth_poly1305_update
  1227. # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
  1228. $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
  1229. # STORE64_LE(slen, (uint64_t) adlen);
  1230. $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen);
  1231. # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
  1232. $auth->update($slen);
  1233. # STORE64_LE(slen, (sizeof block) + mlen);
  1234. $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen);
  1235. # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
  1236. $auth->update($slen);
  1237. # mac = c + mlen;
  1238. # crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
  1239. $mac = $auth->finish();
  1240. $out .= $mac;
  1241. # sodium_memzero(&poly1305_state, sizeof poly1305_state);
  1242. unset($auth);
  1243. # XOR_BUF(STATE_INONCE(state), mac,
  1244. # crypto_secretstream_xchacha20poly1305_INONCEBYTES);
  1245. $st->xorNonce($mac);
  1246. # sodium_increment(STATE_COUNTER(state),
  1247. # crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
  1248. $st->incrementCounter();
  1249. // Overwrite by reference:
  1250. $state = $st->toString();
  1251. /** @var bool $rekey */
  1252. $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
  1253. # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
  1254. # sodium_is_zero(STATE_COUNTER(state),
  1255. # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
  1256. # crypto_secretstream_xchacha20poly1305_rekey(state);
  1257. # }
  1258. if ($rekey || $st->needsRekey()) {
  1259. // DO REKEY
  1260. self::secretstream_xchacha20poly1305_rekey($state);
  1261. }
  1262. # if (outlen_p != NULL) {
  1263. # *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
  1264. # }
  1265. return $out;
  1266. }
  1267. /**
  1268. * @param string $state
  1269. * @param string $cipher
  1270. * @param string $aad
  1271. * @return bool|array{0: string, 1: int}
  1272. * @throws SodiumException
  1273. */
  1274. public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '')
  1275. {
  1276. $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
  1277. $cipherlen = ParagonIE_Sodium_Core32_Util::strlen($cipher);
  1278. # mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
  1279. $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
  1280. $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad);
  1281. # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
  1282. # sodium_misuse();
  1283. # }
  1284. if ((($msglen + 63) >> 6) > 0xfffffffe) {
  1285. throw new SodiumException(
  1286. 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
  1287. );
  1288. }
  1289. # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
  1290. # crypto_onetimeauth_poly1305_init(&poly1305_state, block);
  1291. # sodium_memzero(block, sizeof block);
  1292. $auth = new ParagonIE_Sodium_Core32_Poly1305_State(
  1293. ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
  1294. );
  1295. # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
  1296. $auth->update($aad);
  1297. # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
  1298. # (0x10 - adlen) & 0xf);
  1299. $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
  1300. # memset(block, 0, sizeof block);
  1301. # block[0] = in[0];
  1302. # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
  1303. # state->nonce, 1U, state->k);
  1304. $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
  1305. $cipher[0] . str_repeat("\0", 63),
  1306. $st->getCombinedNonce(),
  1307. $st->getKey(),
  1308. ParagonIE_Sodium_Core32_Util::store64_le(1)
  1309. );
  1310. # tag = block[0];
  1311. # block[0] = in[0];
  1312. # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
  1313. $tag = ParagonIE_Sodium_Core32_Util::chrToInt($block[0]);
  1314. $block[0] = $cipher[0];
  1315. $auth->update($block);
  1316. # c = in + (sizeof tag);
  1317. # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
  1318. $auth->update(ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen));
  1319. # crypto_onetimeauth_poly1305_update
  1320. # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
  1321. $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
  1322. # STORE64_LE(slen, (uint64_t) adlen);
  1323. # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
  1324. $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen);
  1325. $auth->update($slen);
  1326. # STORE64_LE(slen, (sizeof block) + mlen);
  1327. # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
  1328. $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen);
  1329. $auth->update($slen);
  1330. # crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
  1331. # sodium_memzero(&poly1305_state, sizeof poly1305_state);
  1332. $mac = $auth->finish();
  1333. # stored_mac = c + mlen;
  1334. # if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
  1335. # sodium_memzero(mac, sizeof mac);
  1336. # return -1;
  1337. # }
  1338. $stored = ParagonIE_Sodium_Core32_Util::substr($cipher, $msglen + 1, 16);
  1339. if (!ParagonIE