balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

Crypto.php (54810B)


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