wp/wp-includes/sodium_compat/src/Crypto32.php
changeset 9 177826044cd9
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
       
     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      * Update a hashing context for BLAKE2b with $message
       
   781      *
       
   782      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
       
   783      *
       
   784      * @param string $ctx
       
   785      * @param string $message
       
   786      * @return string
       
   787      * @throws SodiumException
       
   788      * @throws TypeError
       
   789      */
       
   790     public static function generichash_update($ctx, $message)
       
   791     {
       
   792         // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
       
   793         ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
       
   794 
       
   795         /** @var SplFixedArray $context */
       
   796         $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
       
   797 
       
   798         /** @var SplFixedArray $in */
       
   799         $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
       
   800 
       
   801         ParagonIE_Sodium_Core32_BLAKE2b::update($context, $in, $in->count());
       
   802 
       
   803         return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($context);
       
   804     }
       
   805 
       
   806     /**
       
   807      * Libsodium's crypto_kx().
       
   808      *
       
   809      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
       
   810      *
       
   811      * @param string $my_sk
       
   812      * @param string $their_pk
       
   813      * @param string $client_pk
       
   814      * @param string $server_pk
       
   815      * @return string
       
   816      * @throws SodiumException
       
   817      * @throws TypeError
       
   818      */
       
   819     public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
       
   820     {
       
   821         return self::generichash(
       
   822             self::scalarmult($my_sk, $their_pk) .
       
   823             $client_pk .
       
   824             $server_pk
       
   825         );
       
   826     }
       
   827 
       
   828     /**
       
   829      * ECDH over Curve25519
       
   830      *
       
   831      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
       
   832      *
       
   833      * @param string $sKey
       
   834      * @param string $pKey
       
   835      * @return string
       
   836      *
       
   837      * @throws SodiumException
       
   838      * @throws TypeError
       
   839      */
       
   840     public static function scalarmult($sKey, $pKey)
       
   841     {
       
   842         $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
       
   843         self::scalarmult_throw_if_zero($q);
       
   844         return $q;
       
   845     }
       
   846 
       
   847     /**
       
   848      * ECDH over Curve25519, using the basepoint.
       
   849      * Used to get a secret key from a public key.
       
   850      *
       
   851      * @param string $secret
       
   852      * @return string
       
   853      *
       
   854      * @throws SodiumException
       
   855      * @throws TypeError
       
   856      */
       
   857     public static function scalarmult_base($secret)
       
   858     {
       
   859         $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
       
   860         self::scalarmult_throw_if_zero($q);
       
   861         return $q;
       
   862     }
       
   863 
       
   864     /**
       
   865      * This throws an Error if a zero public key was passed to the function.
       
   866      *
       
   867      * @param string $q
       
   868      * @return void
       
   869      * @throws SodiumException
       
   870      * @throws TypeError
       
   871      */
       
   872     protected static function scalarmult_throw_if_zero($q)
       
   873     {
       
   874         $d = 0;
       
   875         for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
       
   876             $d |= ParagonIE_Sodium_Core32_Util::chrToInt($q[$i]);
       
   877         }
       
   878 
       
   879         /* branch-free variant of === 0 */
       
   880         if (-(1 & (($d - 1) >> 8))) {
       
   881             throw new SodiumException('Zero public key is not allowed');
       
   882         }
       
   883     }
       
   884 
       
   885     /**
       
   886      * XSalsa20-Poly1305 authenticated symmetric-key encryption.
       
   887      *
       
   888      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
       
   889      *
       
   890      * @param string $plaintext
       
   891      * @param string $nonce
       
   892      * @param string $key
       
   893      * @return string
       
   894      * @throws SodiumException
       
   895      * @throws TypeError
       
   896      */
       
   897     public static function secretbox($plaintext, $nonce, $key)
       
   898     {
       
   899         /** @var string $subkey */
       
   900         $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
       
   901 
       
   902         /** @var string $block0 */
       
   903         $block0 = str_repeat("\x00", 32);
       
   904 
       
   905         /** @var int $mlen - Length of the plaintext message */
       
   906         $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
       
   907         $mlen0 = $mlen;
       
   908         if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
       
   909             $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
       
   910         }
       
   911         $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
       
   912 
       
   913         /** @var string $block0 */
       
   914         $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor(
       
   915             $block0,
       
   916             ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
       
   917             $subkey
       
   918         );
       
   919 
       
   920         /** @var string $c */
       
   921         $c = ParagonIE_Sodium_Core32_Util::substr(
       
   922             $block0,
       
   923             self::secretbox_xsalsa20poly1305_ZEROBYTES
       
   924         );
       
   925         if ($mlen > $mlen0) {
       
   926             $c .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
       
   927                 ParagonIE_Sodium_Core32_Util::substr(
       
   928                     $plaintext,
       
   929                     self::secretbox_xsalsa20poly1305_ZEROBYTES
       
   930                 ),
       
   931                 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
       
   932                 1,
       
   933                 $subkey
       
   934             );
       
   935         }
       
   936         $state = new ParagonIE_Sodium_Core32_Poly1305_State(
       
   937             ParagonIE_Sodium_Core32_Util::substr(
       
   938                 $block0,
       
   939                 0,
       
   940                 self::onetimeauth_poly1305_KEYBYTES
       
   941             )
       
   942         );
       
   943         try {
       
   944             ParagonIE_Sodium_Compat::memzero($block0);
       
   945             ParagonIE_Sodium_Compat::memzero($subkey);
       
   946         } catch (SodiumException $ex) {
       
   947             $block0 = null;
       
   948             $subkey = null;
       
   949         }
       
   950 
       
   951         $state->update($c);
       
   952 
       
   953         /** @var string $c - MAC || ciphertext */
       
   954         $c = $state->finish() . $c;
       
   955         unset($state);
       
   956 
       
   957         return $c;
       
   958     }
       
   959 
       
   960     /**
       
   961      * Decrypt a ciphertext generated via secretbox().
       
   962      *
       
   963      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
       
   964      *
       
   965      * @param string $ciphertext
       
   966      * @param string $nonce
       
   967      * @param string $key
       
   968      * @return string
       
   969      * @throws SodiumException
       
   970      * @throws TypeError
       
   971      */
       
   972     public static function secretbox_open($ciphertext, $nonce, $key)
       
   973     {
       
   974         /** @var string $mac */
       
   975         $mac = ParagonIE_Sodium_Core32_Util::substr(
       
   976             $ciphertext,
       
   977             0,
       
   978             self::secretbox_xsalsa20poly1305_MACBYTES
       
   979         );
       
   980 
       
   981         /** @var string $c */
       
   982         $c = ParagonIE_Sodium_Core32_Util::substr(
       
   983             $ciphertext,
       
   984             self::secretbox_xsalsa20poly1305_MACBYTES
       
   985         );
       
   986 
       
   987         /** @var int $clen */
       
   988         $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
       
   989 
       
   990         /** @var string $subkey */
       
   991         $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
       
   992 
       
   993         /** @var string $block0 */
       
   994         $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20(
       
   995             64,
       
   996             ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
       
   997             $subkey
       
   998         );
       
   999         $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
       
  1000             $mac,
       
  1001             $c,
       
  1002             ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
       
  1003         );
       
  1004         if (!$verified) {
       
  1005             try {
       
  1006                 ParagonIE_Sodium_Compat::memzero($subkey);
       
  1007             } catch (SodiumException $ex) {
       
  1008                 $subkey = null;
       
  1009             }
       
  1010             throw new SodiumException('Invalid MAC');
       
  1011         }
       
  1012 
       
  1013         /** @var string $m - Decrypted message */
       
  1014         $m = ParagonIE_Sodium_Core32_Util::xorStrings(
       
  1015             ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
       
  1016             ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
       
  1017         );
       
  1018         if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
       
  1019             // We had more than 1 block, so let's continue to decrypt the rest.
       
  1020             $m .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
       
  1021                 ParagonIE_Sodium_Core32_Util::substr(
       
  1022                     $c,
       
  1023                     self::secretbox_xsalsa20poly1305_ZEROBYTES
       
  1024                 ),
       
  1025                 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
       
  1026                 1,
       
  1027                 (string) $subkey
       
  1028             );
       
  1029         }
       
  1030         return $m;
       
  1031     }
       
  1032 
       
  1033     /**
       
  1034      * XChaCha20-Poly1305 authenticated symmetric-key encryption.
       
  1035      *
       
  1036      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
       
  1037      *
       
  1038      * @param string $plaintext
       
  1039      * @param string $nonce
       
  1040      * @param string $key
       
  1041      * @return string
       
  1042      * @throws SodiumException
       
  1043      * @throws TypeError
       
  1044      */
       
  1045     public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
       
  1046     {
       
  1047         /** @var string $subkey */
       
  1048         $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
       
  1049             ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
       
  1050             $key
       
  1051         );
       
  1052         $nonceLast = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
       
  1053 
       
  1054         /** @var string $block0 */
       
  1055         $block0 = str_repeat("\x00", 32);
       
  1056 
       
  1057         /** @var int $mlen - Length of the plaintext message */
       
  1058         $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
       
  1059         $mlen0 = $mlen;
       
  1060         if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
       
  1061             $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
       
  1062         }
       
  1063         $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
       
  1064 
       
  1065         /** @var string $block0 */
       
  1066         $block0 = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
       
  1067             $block0,
       
  1068             $nonceLast,
       
  1069             $subkey
       
  1070         );
       
  1071 
       
  1072         /** @var string $c */
       
  1073         $c = ParagonIE_Sodium_Core32_Util::substr(
       
  1074             $block0,
       
  1075             self::secretbox_xchacha20poly1305_ZEROBYTES
       
  1076         );
       
  1077         if ($mlen > $mlen0) {
       
  1078             $c .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
       
  1079                 ParagonIE_Sodium_Core32_Util::substr(
       
  1080                     $plaintext,
       
  1081                     self::secretbox_xchacha20poly1305_ZEROBYTES
       
  1082                 ),
       
  1083                 $nonceLast,
       
  1084                 $subkey,
       
  1085                 ParagonIE_Sodium_Core32_Util::store64_le(1)
       
  1086             );
       
  1087         }
       
  1088         $state = new ParagonIE_Sodium_Core32_Poly1305_State(
       
  1089             ParagonIE_Sodium_Core32_Util::substr(
       
  1090                 $block0,
       
  1091                 0,
       
  1092                 self::onetimeauth_poly1305_KEYBYTES
       
  1093             )
       
  1094         );
       
  1095         try {
       
  1096             ParagonIE_Sodium_Compat::memzero($block0);
       
  1097             ParagonIE_Sodium_Compat::memzero($subkey);
       
  1098         } catch (SodiumException $ex) {
       
  1099             $block0 = null;
       
  1100             $subkey = null;
       
  1101         }
       
  1102 
       
  1103         $state->update($c);
       
  1104 
       
  1105         /** @var string $c - MAC || ciphertext */
       
  1106         $c = $state->finish() . $c;
       
  1107         unset($state);
       
  1108 
       
  1109         return $c;
       
  1110     }
       
  1111 
       
  1112     /**
       
  1113      * Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
       
  1114      *
       
  1115      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
       
  1116      *
       
  1117      * @param string $ciphertext
       
  1118      * @param string $nonce
       
  1119      * @param string $key
       
  1120      * @return string
       
  1121      * @throws SodiumException
       
  1122      * @throws TypeError
       
  1123      */
       
  1124     public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
       
  1125     {
       
  1126         /** @var string $mac */
       
  1127         $mac = ParagonIE_Sodium_Core32_Util::substr(
       
  1128             $ciphertext,
       
  1129             0,
       
  1130             self::secretbox_xchacha20poly1305_MACBYTES
       
  1131         );
       
  1132 
       
  1133         /** @var string $c */
       
  1134         $c = ParagonIE_Sodium_Core32_Util::substr(
       
  1135             $ciphertext,
       
  1136             self::secretbox_xchacha20poly1305_MACBYTES
       
  1137         );
       
  1138 
       
  1139         /** @var int $clen */
       
  1140         $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
       
  1141 
       
  1142         /** @var string $subkey */
       
  1143         $subkey = ParagonIE_Sodium_Core32_HChaCha20::hchacha20($nonce, $key);
       
  1144 
       
  1145         /** @var string $block0 */
       
  1146         $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
       
  1147             64,
       
  1148             ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
       
  1149             $subkey
       
  1150         );
       
  1151         $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
       
  1152             $mac,
       
  1153             $c,
       
  1154             ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
       
  1155         );
       
  1156 
       
  1157         if (!$verified) {
       
  1158             try {
       
  1159                 ParagonIE_Sodium_Compat::memzero($subkey);
       
  1160             } catch (SodiumException $ex) {
       
  1161                 $subkey = null;
       
  1162             }
       
  1163             throw new SodiumException('Invalid MAC');
       
  1164         }
       
  1165 
       
  1166         /** @var string $m - Decrypted message */
       
  1167         $m = ParagonIE_Sodium_Core32_Util::xorStrings(
       
  1168             ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
       
  1169             ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
       
  1170         );
       
  1171 
       
  1172         if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
       
  1173             // We had more than 1 block, so let's continue to decrypt the rest.
       
  1174             $m .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
       
  1175                 ParagonIE_Sodium_Core32_Util::substr(
       
  1176                     $c,
       
  1177                     self::secretbox_xchacha20poly1305_ZEROBYTES
       
  1178                 ),
       
  1179                 ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
       
  1180                 (string) $subkey,
       
  1181                 ParagonIE_Sodium_Core32_Util::store64_le(1)
       
  1182             );
       
  1183         }
       
  1184         return $m;
       
  1185     }
       
  1186 
       
  1187     /**
       
  1188      * Detached Ed25519 signature.
       
  1189      *
       
  1190      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
       
  1191      *
       
  1192      * @param string $message
       
  1193      * @param string $sk
       
  1194      * @return string
       
  1195      * @throws SodiumException
       
  1196      * @throws TypeError
       
  1197      */
       
  1198     public static function sign_detached($message, $sk)
       
  1199     {
       
  1200         return ParagonIE_Sodium_Core32_Ed25519::sign_detached($message, $sk);
       
  1201     }
       
  1202 
       
  1203     /**
       
  1204      * Attached Ed25519 signature. (Returns a signed message.)
       
  1205      *
       
  1206      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
       
  1207      *
       
  1208      * @param string $message
       
  1209      * @param string $sk
       
  1210      * @return string
       
  1211      * @throws SodiumException
       
  1212      * @throws TypeError
       
  1213      */
       
  1214     public static function sign($message, $sk)
       
  1215     {
       
  1216         return ParagonIE_Sodium_Core32_Ed25519::sign($message, $sk);
       
  1217     }
       
  1218 
       
  1219     /**
       
  1220      * Opens a signed message. If valid, returns the message.
       
  1221      *
       
  1222      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
       
  1223      *
       
  1224      * @param string $signedMessage
       
  1225      * @param string $pk
       
  1226      * @return string
       
  1227      * @throws SodiumException
       
  1228      * @throws TypeError
       
  1229      */
       
  1230     public static function sign_open($signedMessage, $pk)
       
  1231     {
       
  1232         return ParagonIE_Sodium_Core32_Ed25519::sign_open($signedMessage, $pk);
       
  1233     }
       
  1234 
       
  1235     /**
       
  1236      * Verify a detached signature of a given message and public key.
       
  1237      *
       
  1238      * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
       
  1239      *
       
  1240      * @param string $signature
       
  1241      * @param string $message
       
  1242      * @param string $pk
       
  1243      * @return bool
       
  1244      * @throws SodiumException
       
  1245      * @throws TypeError
       
  1246      */
       
  1247     public static function sign_verify_detached($signature, $message, $pk)
       
  1248     {
       
  1249         return ParagonIE_Sodium_Core32_Ed25519::verify_detached($signature, $message, $pk);
       
  1250     }
       
  1251 }