angelovcom.net

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

Crypto32.php (55119B)


      1 <?php
      2 
      3 if (class_exists('ParagonIE_Sodium_Crypto32', 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_Crypto32
     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_Core32_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_Core32_Util::strlen($ad);
     85 
     86         /** @var string $mac - Message authentication code */
     87         $mac = ParagonIE_Sodium_Core32_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_Core32_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_Core32_ChaCha20::stream(
     98             32,
     99             $nonce,
    100             $key
    101         );
    102 
    103         /* Recalculate the Poly1305 authentication tag (MAC): */
    104         $state = new ParagonIE_Sodium_Core32_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_Core32_Util::store64_le($adlen));
    112         $state->update($ciphertext);
    113         $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen));
    114         $computed_mac = $state->finish();
    115 
    116         /* Compare the given MAC with the recalculated MAC: */
    117         if (!ParagonIE_Sodium_Core32_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_Core32_ChaCha20::streamXorIc(
    123             $ciphertext,
    124             $nonce,
    125             $key,
    126             ParagonIE_Sodium_Core32_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_Core32_Util::strlen($message);
    151 
    152         /** @var int $adlen - Length of the associated data */
    153         $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
    154 
    155         /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
    156         $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
    157             32,
    158             $nonce,
    159             $key
    160         );
    161         $state = new ParagonIE_Sodium_Core32_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_Core32_ChaCha20::streamXorIc(
    170             $message,
    171             $nonce,
    172             $key,
    173             ParagonIE_Sodium_Core32_Util::store64_le(1)
    174         );
    175 
    176         $state->update($ad);
    177         $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen));
    178         $state->update($ciphertext);
    179         $state->update(ParagonIE_Sodium_Core32_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_Core32_Util::strlen($ad);
    204 
    205         /** @var int $len - Length of message (ciphertext + MAC) */
    206         $len = ParagonIE_Sodium_Core32_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_Core32_ChaCha20::ietfStream(
    213             32,
    214             $nonce,
    215             $key
    216         );
    217 
    218         /** @var string $mac - Message authentication code */
    219         $mac = ParagonIE_Sodium_Core32_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_Core32_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_Core32_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_Core32_Util::store64_le($adlen));
    244         $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen));
    245         $computed_mac = $state->finish();
    246 
    247         /* Compare the given MAC with the recalculated MAC: */
    248         if (!ParagonIE_Sodium_Core32_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_Core32_ChaCha20::ietfStreamXorIc(
    254             $ciphertext,
    255             $nonce,
    256             $key,
    257             ParagonIE_Sodium_Core32_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_Core32_Util::strlen($message);
    282 
    283         /** @var int $adlen - Length of the associated data */
    284         $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
    285 
    286         /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
    287         $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream(
    288             32,
    289             $nonce,
    290             $key
    291         );
    292         $state = new ParagonIE_Sodium_Core32_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_Core32_ChaCha20::ietfStreamXorIc(
    301             $message,
    302             $nonce,
    303             $key,
    304             ParagonIE_Sodium_Core32_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_Core32_Util::store64_le($adlen));
    312         $state->update(ParagonIE_Sodium_Core32_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_Core32_HChaCha20::hChaCha20(
    336             ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
    337             $key
    338         );
    339         $nonceLast = "\x00\x00\x00\x00" .
    340             ParagonIE_Sodium_Core32_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_Core32_HChaCha20::hChaCha20(
    365             ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
    366             $key
    367         );
    368         $nonceLast = "\x00\x00\x00\x00" .
    369             ParagonIE_Sodium_Core32_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_Core32_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_Core32_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         return self::secretbox(
    428             $plaintext,
    429             $nonce,
    430             self::box_beforenm(
    431                 self::box_secretkey($keypair),
    432                 self::box_publickey($keypair)
    433             )
    434         );
    435     }
    436 
    437     /**
    438      * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair.
    439      *
    440      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    441      *
    442      * @param string $message
    443      * @param string $publicKey
    444      * @return string
    445      * @throws SodiumException
    446      * @throws TypeError
    447      */
    448     public static function box_seal($message, $publicKey)
    449     {
    450         /** @var string $ephemeralKeypair */
    451         $ephemeralKeypair = self::box_keypair();
    452 
    453         /** @var string $ephemeralSK */
    454         $ephemeralSK = self::box_secretkey($ephemeralKeypair);
    455 
    456         /** @var string $ephemeralPK */
    457         $ephemeralPK = self::box_publickey($ephemeralKeypair);
    458 
    459         /** @var string $nonce */
    460         $nonce = self::generichash(
    461             $ephemeralPK . $publicKey,
    462             '',
    463             24
    464         );
    465 
    466         /** @var string $keypair - The combined keypair used in crypto_box() */
    467         $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey);
    468 
    469         /** @var string $ciphertext Ciphertext + MAC from crypto_box */
    470         $ciphertext = self::box($message, $nonce, $keypair);
    471         try {
    472             ParagonIE_Sodium_Compat::memzero($ephemeralKeypair);
    473             ParagonIE_Sodium_Compat::memzero($ephemeralSK);
    474             ParagonIE_Sodium_Compat::memzero($nonce);
    475         } catch (SodiumException $ex) {
    476             $ephemeralKeypair = null;
    477             $ephemeralSK = null;
    478             $nonce = null;
    479         }
    480         return $ephemeralPK . $ciphertext;
    481     }
    482 
    483     /**
    484      * Opens a message encrypted via box_seal().
    485      *
    486      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    487      *
    488      * @param string $message
    489      * @param string $keypair
    490      * @return string
    491      * @throws SodiumException
    492      * @throws TypeError
    493      */
    494     public static function box_seal_open($message, $keypair)
    495     {
    496         /** @var string $ephemeralPK */
    497         $ephemeralPK = ParagonIE_Sodium_Core32_Util::substr($message, 0, 32);
    498 
    499         /** @var string $ciphertext (ciphertext + MAC) */
    500         $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 32);
    501 
    502         /** @var string $secretKey */
    503         $secretKey = self::box_secretkey($keypair);
    504 
    505         /** @var string $publicKey */
    506         $publicKey = self::box_publickey($keypair);
    507 
    508         /** @var string $nonce */
    509         $nonce = self::generichash(
    510             $ephemeralPK . $publicKey,
    511             '',
    512             24
    513         );
    514 
    515         /** @var string $keypair */
    516         $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK);
    517 
    518         /** @var string $m */
    519         $m = self::box_open($ciphertext, $nonce, $keypair);
    520         try {
    521             ParagonIE_Sodium_Compat::memzero($secretKey);
    522             ParagonIE_Sodium_Compat::memzero($ephemeralPK);
    523             ParagonIE_Sodium_Compat::memzero($nonce);
    524         } catch (SodiumException $ex) {
    525             $secretKey = null;
    526             $ephemeralPK = null;
    527             $nonce = null;
    528         }
    529         return $m;
    530     }
    531 
    532     /**
    533      * Used by crypto_box() to get the crypto_secretbox() key.
    534      *
    535      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    536      *
    537      * @param string $sk
    538      * @param string $pk
    539      * @return string
    540      * @throws SodiumException
    541      * @throws TypeError
    542      */
    543     public static function box_beforenm($sk, $pk)
    544     {
    545         return ParagonIE_Sodium_Core32_HSalsa20::hsalsa20(
    546             str_repeat("\x00", 16),
    547             self::scalarmult($sk, $pk)
    548         );
    549     }
    550 
    551     /**
    552      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    553      *
    554      * @return string
    555      * @throws Exception
    556      * @throws SodiumException
    557      * @throws TypeError
    558      */
    559     public static function box_keypair()
    560     {
    561         $sKey = random_bytes(32);
    562         $pKey = self::scalarmult_base($sKey);
    563         return $sKey . $pKey;
    564     }
    565 
    566     /**
    567      * @param string $seed
    568      * @return string
    569      * @throws SodiumException
    570      * @throws TypeError
    571      */
    572     public static function box_seed_keypair($seed)
    573     {
    574         $sKey = ParagonIE_Sodium_Core32_Util::substr(
    575             hash('sha512', $seed, true),
    576             0,
    577             32
    578         );
    579         $pKey = self::scalarmult_base($sKey);
    580         return $sKey . $pKey;
    581     }
    582 
    583     /**
    584      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    585      *
    586      * @param string $sKey
    587      * @param string $pKey
    588      * @return string
    589      * @throws TypeError
    590      */
    591     public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey)
    592     {
    593         return ParagonIE_Sodium_Core32_Util::substr($sKey, 0, 32) .
    594             ParagonIE_Sodium_Core32_Util::substr($pKey, 0, 32);
    595     }
    596 
    597     /**
    598      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    599      *
    600      * @param string $keypair
    601      * @return string
    602      * @throws RangeException
    603      * @throws TypeError
    604      */
    605     public static function box_secretkey($keypair)
    606     {
    607         if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== 64) {
    608             throw new RangeException(
    609                 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
    610             );
    611         }
    612         return ParagonIE_Sodium_Core32_Util::substr($keypair, 0, 32);
    613     }
    614 
    615     /**
    616      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    617      *
    618      * @param string $keypair
    619      * @return string
    620      * @throws RangeException
    621      * @throws TypeError
    622      */
    623     public static function box_publickey($keypair)
    624     {
    625         if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
    626             throw new RangeException(
    627                 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
    628             );
    629         }
    630         return ParagonIE_Sodium_Core32_Util::substr($keypair, 32, 32);
    631     }
    632 
    633     /**
    634      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    635      *
    636      * @param string $sKey
    637      * @return string
    638      * @throws RangeException
    639      * @throws SodiumException
    640      * @throws TypeError
    641      */
    642     public static function box_publickey_from_secretkey($sKey)
    643     {
    644         if (ParagonIE_Sodium_Core32_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) {
    645             throw new RangeException(
    646                 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.'
    647             );
    648         }
    649         return self::scalarmult_base($sKey);
    650     }
    651 
    652     /**
    653      * Decrypt a message encrypted with box().
    654      *
    655      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    656      *
    657      * @param string $ciphertext
    658      * @param string $nonce
    659      * @param string $keypair
    660      * @return string
    661      * @throws SodiumException
    662      * @throws TypeError
    663      */
    664     public static function box_open($ciphertext, $nonce, $keypair)
    665     {
    666         return self::secretbox_open(
    667             $ciphertext,
    668             $nonce,
    669             self::box_beforenm(
    670                 self::box_secretkey($keypair),
    671                 self::box_publickey($keypair)
    672             )
    673         );
    674     }
    675 
    676     /**
    677      * Calculate a BLAKE2b hash.
    678      *
    679      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    680      *
    681      * @param string $message
    682      * @param string|null $key
    683      * @param int $outlen
    684      * @return string
    685      * @throws RangeException
    686      * @throws SodiumException
    687      * @throws TypeError
    688      */
    689     public static function generichash($message, $key = '', $outlen = 32)
    690     {
    691         // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
    692         ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
    693 
    694         $k = null;
    695         if (!empty($key)) {
    696             /** @var SplFixedArray $k */
    697             $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
    698             if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
    699                 throw new RangeException('Invalid key size');
    700             }
    701         }
    702 
    703         /** @var SplFixedArray $in */
    704         $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
    705 
    706         /** @var SplFixedArray $ctx */
    707         $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outlen);
    708         ParagonIE_Sodium_Core32_BLAKE2b::update($ctx, $in, $in->count());
    709 
    710         /** @var SplFixedArray $out */
    711         $out = new SplFixedArray($outlen);
    712         $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($ctx, $out);
    713 
    714         /** @var array<int, int> */
    715         $outArray = $out->toArray();
    716         return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray);
    717     }
    718 
    719     /**
    720      * Finalize a BLAKE2b hashing context, returning the hash.
    721      *
    722      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    723      *
    724      * @param string $ctx
    725      * @param int $outlen
    726      * @return string
    727      * @throws SodiumException
    728      * @throws TypeError
    729      */
    730     public static function generichash_final($ctx, $outlen = 32)
    731     {
    732         if (!is_string($ctx)) {
    733             throw new TypeError('Context must be a string');
    734         }
    735         $out = new SplFixedArray($outlen);
    736 
    737         /** @var SplFixedArray $context */
    738         $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
    739 
    740         /** @var SplFixedArray $out */
    741         $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($context, $out);
    742 
    743         /** @var array<int, int> */
    744         $outArray = $out->toArray();
    745         return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray);
    746     }
    747 
    748     /**
    749      * Initialize a hashing context for BLAKE2b.
    750      *
    751      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    752      *
    753      * @param string $key
    754      * @param int $outputLength
    755      * @return string
    756      * @throws RangeException
    757      * @throws SodiumException
    758      * @throws TypeError
    759      */
    760     public static function generichash_init($key = '', $outputLength = 32)
    761     {
    762         // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
    763         ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
    764 
    765         $k = null;
    766         if (!empty($key)) {
    767             $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
    768             if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
    769                 throw new RangeException('Invalid key size');
    770             }
    771         }
    772 
    773         /** @var SplFixedArray $ctx */
    774         $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength);
    775 
    776         return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx);
    777     }
    778 
    779     /**
    780      * Initialize a hashing context for BLAKE2b.
    781      *
    782      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    783      *
    784      * @param string $key
    785      * @param int $outputLength
    786      * @param string $salt
    787      * @param string $personal
    788      * @return string
    789      * @throws RangeException
    790      * @throws SodiumException
    791      * @throws TypeError
    792      */
    793     public static function generichash_init_salt_personal(
    794         $key = '',
    795         $outputLength = 32,
    796         $salt = '',
    797         $personal = ''
    798     ) {
    799         // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
    800         ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
    801 
    802         $k = null;
    803         if (!empty($key)) {
    804             $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
    805             if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
    806                 throw new RangeException('Invalid key size');
    807             }
    808         }
    809         if (!empty($salt)) {
    810             $s = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($salt);
    811         } else {
    812             $s = null;
    813         }
    814         if (!empty($salt)) {
    815             $p = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($personal);
    816         } else {
    817             $p = null;
    818         }
    819 
    820         /** @var SplFixedArray $ctx */
    821         $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength, $s, $p);
    822 
    823         return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx);
    824     }
    825 
    826     /**
    827      * Update a hashing context for BLAKE2b with $message
    828      *
    829      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    830      *
    831      * @param string $ctx
    832      * @param string $message
    833      * @return string
    834      * @throws SodiumException
    835      * @throws TypeError
    836      */
    837     public static function generichash_update($ctx, $message)
    838     {
    839         // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
    840         ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
    841 
    842         /** @var SplFixedArray $context */
    843         $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
    844 
    845         /** @var SplFixedArray $in */
    846         $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
    847 
    848         ParagonIE_Sodium_Core32_BLAKE2b::update($context, $in, $in->count());
    849 
    850         return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($context);
    851     }
    852 
    853     /**
    854      * Libsodium's crypto_kx().
    855      *
    856      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    857      *
    858      * @param string $my_sk
    859      * @param string $their_pk
    860      * @param string $client_pk
    861      * @param string $server_pk
    862      * @return string
    863      * @throws SodiumException
    864      * @throws TypeError
    865      */
    866     public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
    867     {
    868         return self::generichash(
    869             self::scalarmult($my_sk, $their_pk) .
    870             $client_pk .
    871             $server_pk
    872         );
    873     }
    874 
    875     /**
    876      * ECDH over Curve25519
    877      *
    878      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    879      *
    880      * @param string $sKey
    881      * @param string $pKey
    882      * @return string
    883      *
    884      * @throws SodiumException
    885      * @throws TypeError
    886      */
    887     public static function scalarmult($sKey, $pKey)
    888     {
    889         $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
    890         self::scalarmult_throw_if_zero($q);
    891         return $q;
    892     }
    893 
    894     /**
    895      * ECDH over Curve25519, using the basepoint.
    896      * Used to get a secret key from a public key.
    897      *
    898      * @param string $secret
    899      * @return string
    900      *
    901      * @throws SodiumException
    902      * @throws TypeError
    903      */
    904     public static function scalarmult_base($secret)
    905     {
    906         $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
    907         self::scalarmult_throw_if_zero($q);
    908         return $q;
    909     }
    910 
    911     /**
    912      * This throws an Error if a zero public key was passed to the function.
    913      *
    914      * @param string $q
    915      * @return void
    916      * @throws SodiumException
    917      * @throws TypeError
    918      */
    919     protected static function scalarmult_throw_if_zero($q)
    920     {
    921         $d = 0;
    922         for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
    923             $d |= ParagonIE_Sodium_Core32_Util::chrToInt($q[$i]);
    924         }
    925 
    926         /* branch-free variant of === 0 */
    927         if (-(1 & (($d - 1) >> 8))) {
    928             throw new SodiumException('Zero public key is not allowed');
    929         }
    930     }
    931 
    932     /**
    933      * XSalsa20-Poly1305 authenticated symmetric-key encryption.
    934      *
    935      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
    936      *
    937      * @param string $plaintext
    938      * @param string $nonce
    939      * @param string $key
    940      * @return string
    941      * @throws SodiumException
    942      * @throws TypeError
    943      */
    944     public static function secretbox($plaintext, $nonce, $key)
    945     {
    946         /** @var string $subkey */
    947         $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
    948 
    949         /** @var string $block0 */
    950         $block0 = str_repeat("\x00", 32);
    951 
    952         /** @var int $mlen - Length of the plaintext message */
    953         $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
    954         $mlen0 = $mlen;
    955         if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
    956             $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
    957         }
    958         $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
    959 
    960         /** @var string $block0 */
    961         $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor(
    962             $block0,
    963             ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
    964             $subkey
    965         );
    966 
    967         /** @var string $c */
    968         $c = ParagonIE_Sodium_Core32_Util::substr(
    969             $block0,
    970             self::secretbox_xsalsa20poly1305_ZEROBYTES
    971         );
    972         if ($mlen > $mlen0) {
    973             $c .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
    974                 ParagonIE_Sodium_Core32_Util::substr(
    975                     $plaintext,
    976                     self::secretbox_xsalsa20poly1305_ZEROBYTES
    977                 ),
    978                 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
    979                 1,
    980                 $subkey
    981             );
    982         }
    983         $state = new ParagonIE_Sodium_Core32_Poly1305_State(
    984             ParagonIE_Sodium_Core32_Util::substr(
    985                 $block0,
    986                 0,
    987                 self::onetimeauth_poly1305_KEYBYTES
    988             )
    989         );
    990         try {
    991             ParagonIE_Sodium_Compat::memzero($block0);
    992             ParagonIE_Sodium_Compat::memzero($subkey);
    993         } catch (SodiumException $ex) {
    994             $block0 = null;
    995             $subkey = null;
    996         }
    997 
    998         $state->update($c);
    999 
   1000         /** @var string $c - MAC || ciphertext */
   1001         $c = $state->finish() . $c;
   1002         unset($state);
   1003 
   1004         return $c;
   1005     }
   1006 
   1007     /**
   1008      * Decrypt a ciphertext generated via secretbox().
   1009      *
   1010      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   1011      *
   1012      * @param string $ciphertext
   1013      * @param string $nonce
   1014      * @param string $key
   1015      * @return string
   1016      * @throws SodiumException
   1017      * @throws TypeError
   1018      */
   1019     public static function secretbox_open($ciphertext, $nonce, $key)
   1020     {
   1021         /** @var string $mac */
   1022         $mac = ParagonIE_Sodium_Core32_Util::substr(
   1023             $ciphertext,
   1024             0,
   1025             self::secretbox_xsalsa20poly1305_MACBYTES
   1026         );
   1027 
   1028         /** @var string $c */
   1029         $c = ParagonIE_Sodium_Core32_Util::substr(
   1030             $ciphertext,
   1031             self::secretbox_xsalsa20poly1305_MACBYTES
   1032         );
   1033 
   1034         /** @var int $clen */
   1035         $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
   1036 
   1037         /** @var string $subkey */
   1038         $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
   1039 
   1040         /** @var string $block0 */
   1041         $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20(
   1042             64,
   1043             ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
   1044             $subkey
   1045         );
   1046         $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
   1047             $mac,
   1048             $c,
   1049             ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
   1050         );
   1051         if (!$verified) {
   1052             try {
   1053                 ParagonIE_Sodium_Compat::memzero($subkey);
   1054             } catch (SodiumException $ex) {
   1055                 $subkey = null;
   1056             }
   1057             throw new SodiumException('Invalid MAC');
   1058         }
   1059 
   1060         /** @var string $m - Decrypted message */
   1061         $m = ParagonIE_Sodium_Core32_Util::xorStrings(
   1062             ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
   1063             ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
   1064         );
   1065         if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
   1066             // We had more than 1 block, so let's continue to decrypt the rest.
   1067             $m .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
   1068                 ParagonIE_Sodium_Core32_Util::substr(
   1069                     $c,
   1070                     self::secretbox_xsalsa20poly1305_ZEROBYTES
   1071                 ),
   1072                 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
   1073                 1,
   1074                 (string) $subkey
   1075             );
   1076         }
   1077         return $m;
   1078     }
   1079 
   1080     /**
   1081      * XChaCha20-Poly1305 authenticated symmetric-key encryption.
   1082      *
   1083      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   1084      *
   1085      * @param string $plaintext
   1086      * @param string $nonce
   1087      * @param string $key
   1088      * @return string
   1089      * @throws SodiumException
   1090      * @throws TypeError
   1091      */
   1092     public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
   1093     {
   1094         /** @var string $subkey */
   1095         $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
   1096             ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
   1097             $key
   1098         );
   1099         $nonceLast = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
   1100 
   1101         /** @var string $block0 */
   1102         $block0 = str_repeat("\x00", 32);
   1103 
   1104         /** @var int $mlen - Length of the plaintext message */
   1105         $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
   1106         $mlen0 = $mlen;
   1107         if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
   1108             $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
   1109         }
   1110         $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
   1111 
   1112         /** @var string $block0 */
   1113         $block0 = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
   1114             $block0,
   1115             $nonceLast,
   1116             $subkey
   1117         );
   1118 
   1119         /** @var string $c */
   1120         $c = ParagonIE_Sodium_Core32_Util::substr(
   1121             $block0,
   1122             self::secretbox_xchacha20poly1305_ZEROBYTES
   1123         );
   1124         if ($mlen > $mlen0) {
   1125             $c .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
   1126                 ParagonIE_Sodium_Core32_Util::substr(
   1127                     $plaintext,
   1128                     self::secretbox_xchacha20poly1305_ZEROBYTES
   1129                 ),
   1130                 $nonceLast,
   1131                 $subkey,
   1132                 ParagonIE_Sodium_Core32_Util::store64_le(1)
   1133             );
   1134         }
   1135         $state = new ParagonIE_Sodium_Core32_Poly1305_State(
   1136             ParagonIE_Sodium_Core32_Util::substr(
   1137                 $block0,
   1138                 0,
   1139                 self::onetimeauth_poly1305_KEYBYTES
   1140             )
   1141         );
   1142         try {
   1143             ParagonIE_Sodium_Compat::memzero($block0);
   1144             ParagonIE_Sodium_Compat::memzero($subkey);
   1145         } catch (SodiumException $ex) {
   1146             $block0 = null;
   1147             $subkey = null;
   1148         }
   1149 
   1150         $state->update($c);
   1151 
   1152         /** @var string $c - MAC || ciphertext */
   1153         $c = $state->finish() . $c;
   1154         unset($state);
   1155 
   1156         return $c;
   1157     }
   1158 
   1159     /**
   1160      * Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
   1161      *
   1162      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   1163      *
   1164      * @param string $ciphertext
   1165      * @param string $nonce
   1166      * @param string $key
   1167      * @return string
   1168      * @throws SodiumException
   1169      * @throws TypeError
   1170      */
   1171     public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
   1172     {
   1173         /** @var string $mac */
   1174         $mac = ParagonIE_Sodium_Core32_Util::substr(
   1175             $ciphertext,
   1176             0,
   1177             self::secretbox_xchacha20poly1305_MACBYTES
   1178         );
   1179 
   1180         /** @var string $c */
   1181         $c = ParagonIE_Sodium_Core32_Util::substr(
   1182             $ciphertext,
   1183             self::secretbox_xchacha20poly1305_MACBYTES
   1184         );
   1185 
   1186         /** @var int $clen */
   1187         $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
   1188 
   1189         /** @var string $subkey */
   1190         $subkey = ParagonIE_Sodium_Core32_HChaCha20::hchacha20($nonce, $key);
   1191 
   1192         /** @var string $block0 */
   1193         $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
   1194             64,
   1195             ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
   1196             $subkey
   1197         );
   1198         $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
   1199             $mac,
   1200             $c,
   1201             ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
   1202         );
   1203 
   1204         if (!$verified) {
   1205             try {
   1206                 ParagonIE_Sodium_Compat::memzero($subkey);
   1207             } catch (SodiumException $ex) {
   1208                 $subkey = null;
   1209             }
   1210             throw new SodiumException('Invalid MAC');
   1211         }
   1212 
   1213         /** @var string $m - Decrypted message */
   1214         $m = ParagonIE_Sodium_Core32_Util::xorStrings(
   1215             ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
   1216             ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
   1217         );
   1218 
   1219         if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
   1220             // We had more than 1 block, so let's continue to decrypt the rest.
   1221             $m .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
   1222                 ParagonIE_Sodium_Core32_Util::substr(
   1223                     $c,
   1224                     self::secretbox_xchacha20poly1305_ZEROBYTES
   1225                 ),
   1226                 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
   1227                 (string) $subkey,
   1228                 ParagonIE_Sodium_Core32_Util::store64_le(1)
   1229             );
   1230         }
   1231         return $m;
   1232     }
   1233 
   1234     /**
   1235      * @param string $key
   1236      * @return array<int, string> Returns a state and a header.
   1237      * @throws Exception
   1238      * @throws SodiumException
   1239      */
   1240     public static function secretstream_xchacha20poly1305_init_push($key)
   1241     {
   1242         # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES);
   1243         $out = random_bytes(24);
   1244 
   1245         # crypto_core_hchacha20(state->k, out, k, NULL);
   1246         $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20($out, $key);
   1247         $state = new ParagonIE_Sodium_Core32_SecretStream_State(
   1248             $subkey,
   1249             ParagonIE_Sodium_Core32_Util::substr($out, 16, 8) . str_repeat("\0", 4)
   1250         );
   1251 
   1252         # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
   1253         $state->counterReset();
   1254 
   1255         # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES,
   1256         #        crypto_secretstream_xchacha20poly1305_INONCEBYTES);
   1257         # memset(state->_pad, 0, sizeof state->_pad);
   1258         return array(
   1259             $state->toString(),
   1260             $out
   1261         );
   1262     }
   1263 
   1264     /**
   1265      * @param string $key
   1266      * @param string $header
   1267      * @return string Returns a state.
   1268      * @throws Exception
   1269      */
   1270     public static function secretstream_xchacha20poly1305_init_pull($key, $header)
   1271     {
   1272         # crypto_core_hchacha20(state->k, in, k, NULL);
   1273         $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
   1274             ParagonIE_Sodium_Core32_Util::substr($header, 0, 16),
   1275             $key
   1276         );
   1277         $state = new ParagonIE_Sodium_Core32_SecretStream_State(
   1278             $subkey,
   1279             ParagonIE_Sodium_Core32_Util::substr($header, 16)
   1280         );
   1281         $state->counterReset();
   1282         # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
   1283         #     crypto_secretstream_xchacha20poly1305_INONCEBYTES);
   1284         # memset(state->_pad, 0, sizeof state->_pad);
   1285         # return 0;
   1286         return $state->toString();
   1287     }
   1288 
   1289     /**
   1290      * @param string $state
   1291      * @param string $msg
   1292      * @param string $aad
   1293      * @param int $tag
   1294      * @return string
   1295      * @throws SodiumException
   1296      */
   1297     public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
   1298     {
   1299         $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
   1300         # crypto_onetimeauth_poly1305_state poly1305_state;
   1301         # unsigned char                     block[64U];
   1302         # unsigned char                     slen[8U];
   1303         # unsigned char                    *c;
   1304         # unsigned char                    *mac;
   1305 
   1306         $msglen = ParagonIE_Sodium_Core32_Util::strlen($msg);
   1307         $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad);
   1308 
   1309         if ((($msglen + 63) >> 6) > 0xfffffffe) {
   1310             throw new SodiumException(
   1311                 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
   1312             );
   1313         }
   1314 
   1315         # if (outlen_p != NULL) {
   1316         #     *outlen_p = 0U;
   1317         # }
   1318         # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
   1319         #     sodium_misuse();
   1320         # }
   1321 
   1322         # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
   1323         # crypto_onetimeauth_poly1305_init(&poly1305_state, block);
   1324         # sodium_memzero(block, sizeof block);
   1325         $auth = new ParagonIE_Sodium_Core32_Poly1305_State(
   1326             ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
   1327         );
   1328 
   1329         # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
   1330         $auth->update($aad);
   1331 
   1332         # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
   1333         #     (0x10 - adlen) & 0xf);
   1334         $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
   1335 
   1336         # memset(block, 0, sizeof block);
   1337         # block[0] = tag;
   1338         # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
   1339         #                                    state->nonce, 1U, state->k);
   1340         $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
   1341             ParagonIE_Sodium_Core32_Util::intToChr($tag) . str_repeat("\0", 63),
   1342             $st->getCombinedNonce(),
   1343             $st->getKey(),
   1344             ParagonIE_Sodium_Core32_Util::store64_le(1)
   1345         );
   1346 
   1347         # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
   1348         $auth->update($block);
   1349 
   1350         # out[0] = block[0];
   1351         $out = $block[0];
   1352         # c = out + (sizeof tag);
   1353         # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
   1354         $cipher = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
   1355             $msg,
   1356             $st->getCombinedNonce(),
   1357             $st->getKey(),
   1358             ParagonIE_Sodium_Core32_Util::store64_le(2)
   1359         );
   1360 
   1361         # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
   1362         $auth->update($cipher);
   1363 
   1364         $out .= $cipher;
   1365         unset($cipher);
   1366 
   1367         # crypto_onetimeauth_poly1305_update
   1368         # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
   1369         $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
   1370 
   1371         # STORE64_LE(slen, (uint64_t) adlen);
   1372         $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen);
   1373 
   1374         # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
   1375         $auth->update($slen);
   1376 
   1377         # STORE64_LE(slen, (sizeof block) + mlen);
   1378         $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen);
   1379 
   1380         # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
   1381         $auth->update($slen);
   1382 
   1383         # mac = c + mlen;
   1384         # crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
   1385         $mac = $auth->finish();
   1386         $out .= $mac;
   1387 
   1388         # sodium_memzero(&poly1305_state, sizeof poly1305_state);
   1389         unset($auth);
   1390 
   1391 
   1392         # XOR_BUF(STATE_INONCE(state), mac,
   1393         #     crypto_secretstream_xchacha20poly1305_INONCEBYTES);
   1394         $st->xorNonce($mac);
   1395 
   1396         # sodium_increment(STATE_COUNTER(state),
   1397         #     crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
   1398         $st->incrementCounter();
   1399         // Overwrite by reference:
   1400         $state = $st->toString();
   1401 
   1402         /** @var bool $rekey */
   1403         $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
   1404         # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
   1405         #     sodium_is_zero(STATE_COUNTER(state),
   1406         #         crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
   1407         #     crypto_secretstream_xchacha20poly1305_rekey(state);
   1408         # }
   1409         if ($rekey || $st->needsRekey()) {
   1410             // DO REKEY
   1411             self::secretstream_xchacha20poly1305_rekey($state);
   1412         }
   1413         # if (outlen_p != NULL) {
   1414         #     *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
   1415         # }
   1416         return $out;
   1417     }
   1418 
   1419     /**
   1420      * @param string $state
   1421      * @param string $cipher
   1422      * @param string $aad
   1423      * @return bool|array{0: string, 1: int}
   1424      * @throws SodiumException
   1425      */
   1426     public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '')
   1427     {
   1428         $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
   1429 
   1430         $cipherlen = ParagonIE_Sodium_Core32_Util::strlen($cipher);
   1431         #     mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
   1432         $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
   1433         $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad);
   1434 
   1435         #     if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
   1436         #         sodium_misuse();
   1437         #     }
   1438         if ((($msglen + 63) >> 6) > 0xfffffffe) {
   1439             throw new SodiumException(
   1440                 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
   1441             );
   1442         }
   1443 
   1444         #     crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
   1445         #     crypto_onetimeauth_poly1305_init(&poly1305_state, block);
   1446         #     sodium_memzero(block, sizeof block);
   1447         $auth = new ParagonIE_Sodium_Core32_Poly1305_State(
   1448             ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
   1449         );
   1450 
   1451         #     crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
   1452         $auth->update($aad);
   1453 
   1454         #     crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
   1455         #         (0x10 - adlen) & 0xf);
   1456         $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
   1457 
   1458 
   1459         #     memset(block, 0, sizeof block);
   1460         #     block[0] = in[0];
   1461         #     crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
   1462         #                                        state->nonce, 1U, state->k);
   1463         $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
   1464             $cipher[0] . str_repeat("\0", 63),
   1465             $st->getCombinedNonce(),
   1466             $st->getKey(),
   1467             ParagonIE_Sodium_Core32_Util::store64_le(1)
   1468         );
   1469         #     tag = block[0];
   1470         #     block[0] = in[0];
   1471         #     crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
   1472         $tag = ParagonIE_Sodium_Core32_Util::chrToInt($block[0]);
   1473         $block[0] = $cipher[0];
   1474         $auth->update($block);
   1475 
   1476 
   1477         #     c = in + (sizeof tag);
   1478         #     crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
   1479         $auth->update(ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen));
   1480 
   1481         #     crypto_onetimeauth_poly1305_update
   1482         #     (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
   1483         $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
   1484 
   1485         #     STORE64_LE(slen, (uint64_t) adlen);
   1486         #     crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
   1487         $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen);
   1488         $auth->update($slen);
   1489 
   1490         #     STORE64_LE(slen, (sizeof block) + mlen);
   1491         #     crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
   1492         $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen);
   1493         $auth->update($slen);
   1494 
   1495         #     crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
   1496         #     sodium_memzero(&poly1305_state, sizeof poly1305_state);
   1497         $mac = $auth->finish();
   1498 
   1499         #     stored_mac = c + mlen;
   1500         #     if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
   1501         #     sodium_memzero(mac, sizeof mac);
   1502         #         return -1;
   1503         #     }
   1504 
   1505         $stored = ParagonIE_Sodium_Core32_Util::substr($cipher, $msglen + 1, 16);
   1506         if (!ParagonIE_Sodium_Core32_Util::hashEquals($mac, $stored)) {
   1507             return false;
   1508         }
   1509 
   1510         #     crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
   1511         $out = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
   1512             ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen),
   1513             $st->getCombinedNonce(),
   1514             $st->getKey(),
   1515             ParagonIE_Sodium_Core32_Util::store64_le(2)
   1516         );
   1517 
   1518         #     XOR_BUF(STATE_INONCE(state), mac,
   1519         #         crypto_secretstream_xchacha20poly1305_INONCEBYTES);
   1520         $st->xorNonce($mac);
   1521 
   1522         #     sodium_increment(STATE_COUNTER(state),
   1523         #         crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
   1524         $st->incrementCounter();
   1525 
   1526         #     if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
   1527         #         sodium_is_zero(STATE_COUNTER(state),
   1528         #             crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
   1529         #         crypto_secretstream_xchacha20poly1305_rekey(state);
   1530         #     }
   1531 
   1532         // Overwrite by reference:
   1533         $state = $st->toString();
   1534 
   1535         /** @var bool $rekey */
   1536         $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
   1537         if ($rekey || $st->needsRekey()) {
   1538             // DO REKEY
   1539             self::secretstream_xchacha20poly1305_rekey($state);
   1540         }
   1541         return array($out, $tag);
   1542     }
   1543 
   1544     /**
   1545      * @param string $state
   1546      * @return void
   1547      * @throws SodiumException
   1548      */
   1549     public static function secretstream_xchacha20poly1305_rekey(&$state)
   1550     {
   1551         $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
   1552         # unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES +
   1553         # crypto_secretstream_xchacha20poly1305_INONCEBYTES];
   1554         # size_t        i;
   1555         # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
   1556         #     new_key_and_inonce[i] = state->k[i];
   1557         # }
   1558         $new_key_and_inonce = $st->getKey();
   1559 
   1560         # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
   1561         #     new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] =
   1562         #         STATE_INONCE(state)[i];
   1563         # }
   1564         $new_key_and_inonce .= ParagonIE_Sodium_Core32_Util::substR($st->getNonce(), 0, 8);
   1565 
   1566         # crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce,
   1567         #                                 sizeof new_key_and_inonce,
   1568         #                                 state->nonce, state->k);
   1569 
   1570         $st->rekey(ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
   1571             $new_key_and_inonce,
   1572             $st->getCombinedNonce(),
   1573             $st->getKey(),
   1574             ParagonIE_Sodium_Core32_Util::store64_le(0)
   1575         ));
   1576 
   1577         # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
   1578         #     state->k[i] = new_key_and_inonce[i];
   1579         # }
   1580         # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
   1581         #     STATE_INONCE(state)[i] =
   1582         #          new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i];
   1583         # }
   1584         # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
   1585         $st->counterReset();
   1586 
   1587         $state = $st->toString();
   1588     }
   1589 
   1590     /**
   1591      * Detached Ed25519 signature.
   1592      *
   1593      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   1594      *
   1595      * @param string $message
   1596      * @param string $sk
   1597      * @return string
   1598      * @throws SodiumException
   1599      * @throws TypeError
   1600      */
   1601     public static function sign_detached($message, $sk)
   1602     {
   1603         return ParagonIE_Sodium_Core32_Ed25519::sign_detached($message, $sk);
   1604     }
   1605 
   1606     /**
   1607      * Attached Ed25519 signature. (Returns a signed message.)
   1608      *
   1609      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   1610      *
   1611      * @param string $message
   1612      * @param string $sk
   1613      * @return string
   1614      * @throws SodiumException
   1615      * @throws TypeError
   1616      */
   1617     public static function sign($message, $sk)
   1618     {
   1619         return ParagonIE_Sodium_Core32_Ed25519::sign($message, $sk);
   1620     }
   1621 
   1622     /**
   1623      * Opens a signed message. If valid, returns the message.
   1624      *
   1625      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   1626      *
   1627      * @param string $signedMessage
   1628      * @param string $pk
   1629      * @return string
   1630      * @throws SodiumException
   1631      * @throws TypeError
   1632      */
   1633     public static function sign_open($signedMessage, $pk)
   1634     {
   1635         return ParagonIE_Sodium_Core32_Ed25519::sign_open($signedMessage, $pk);
   1636     }
   1637 
   1638     /**
   1639      * Verify a detached signature of a given message and public key.
   1640      *
   1641      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   1642      *
   1643      * @param string $signature
   1644      * @param string $message
   1645      * @param string $pk
   1646      * @return bool
   1647      * @throws SodiumException
   1648      * @throws TypeError
   1649      */
   1650     public static function sign_verify_detached($signature, $message, $pk)
   1651     {
   1652         return ParagonIE_Sodium_Core32_Ed25519::verify_detached($signature, $message, $pk);
   1653     }
   1654 }