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