wp/wp-includes/sodium_compat/src/Compat.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    47     const LIBRARY_VERSION_MAJOR = 9;
    47     const LIBRARY_VERSION_MAJOR = 9;
    48     const LIBRARY_VERSION_MINOR = 1;
    48     const LIBRARY_VERSION_MINOR = 1;
    49     const VERSION_STRING = 'polyfill-1.0.8';
    49     const VERSION_STRING = 'polyfill-1.0.8';
    50 
    50 
    51     // From libsodium
    51     // From libsodium
       
    52     const BASE64_VARIANT_ORIGINAL = 1;
       
    53     const BASE64_VARIANT_ORIGINAL_NO_PADDING = 3;
       
    54     const BASE64_VARIANT_URLSAFE = 5;
       
    55     const BASE64_VARIANT_URLSAFE_NO_PADDING = 7;
    52     const CRYPTO_AEAD_AES256GCM_KEYBYTES = 32;
    56     const CRYPTO_AEAD_AES256GCM_KEYBYTES = 32;
    53     const CRYPTO_AEAD_AES256GCM_NSECBYTES = 0;
    57     const CRYPTO_AEAD_AES256GCM_NSECBYTES = 0;
    54     const CRYPTO_AEAD_AES256GCM_NPUBBYTES = 12;
    58     const CRYPTO_AEAD_AES256GCM_NPUBBYTES = 12;
    55     const CRYPTO_AEAD_AES256GCM_ABYTES = 16;
    59     const CRYPTO_AEAD_AES256GCM_ABYTES = 16;
    56     const CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES = 32;
    60     const CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES = 32;
    72     const CRYPTO_BOX_PUBLICKEYBYTES = 32;
    76     const CRYPTO_BOX_PUBLICKEYBYTES = 32;
    73     const CRYPTO_BOX_KEYPAIRBYTES = 64;
    77     const CRYPTO_BOX_KEYPAIRBYTES = 64;
    74     const CRYPTO_BOX_MACBYTES = 16;
    78     const CRYPTO_BOX_MACBYTES = 16;
    75     const CRYPTO_BOX_NONCEBYTES = 24;
    79     const CRYPTO_BOX_NONCEBYTES = 24;
    76     const CRYPTO_BOX_SEEDBYTES = 32;
    80     const CRYPTO_BOX_SEEDBYTES = 32;
       
    81     const CRYPTO_KDF_BYTES_MIN = 16;
       
    82     const CRYPTO_KDF_BYTES_MAX = 64;
       
    83     const CRYPTO_KDF_CONTEXTBYTES = 8;
       
    84     const CRYPTO_KDF_KEYBYTES = 32;
    77     const CRYPTO_KX_BYTES = 32;
    85     const CRYPTO_KX_BYTES = 32;
       
    86     const CRYPTO_KX_PRIMITIVE = 'x25519blake2b';
    78     const CRYPTO_KX_SEEDBYTES = 32;
    87     const CRYPTO_KX_SEEDBYTES = 32;
       
    88     const CRYPTO_KX_KEYPAIRBYTES = 64;
    79     const CRYPTO_KX_PUBLICKEYBYTES = 32;
    89     const CRYPTO_KX_PUBLICKEYBYTES = 32;
    80     const CRYPTO_KX_SECRETKEYBYTES = 32;
    90     const CRYPTO_KX_SECRETKEYBYTES = 32;
       
    91     const CRYPTO_KX_SESSIONKEYBYTES = 32;
    81     const CRYPTO_GENERICHASH_BYTES = 32;
    92     const CRYPTO_GENERICHASH_BYTES = 32;
    82     const CRYPTO_GENERICHASH_BYTES_MIN = 16;
    93     const CRYPTO_GENERICHASH_BYTES_MIN = 16;
    83     const CRYPTO_GENERICHASH_BYTES_MAX = 64;
    94     const CRYPTO_GENERICHASH_BYTES_MAX = 64;
    84     const CRYPTO_GENERICHASH_KEYBYTES = 32;
    95     const CRYPTO_GENERICHASH_KEYBYTES = 32;
    85     const CRYPTO_GENERICHASH_KEYBYTES_MIN = 16;
    96     const CRYPTO_GENERICHASH_KEYBYTES_MIN = 16;
    86     const CRYPTO_GENERICHASH_KEYBYTES_MAX = 64;
    97     const CRYPTO_GENERICHASH_KEYBYTES_MAX = 64;
    87     const CRYPTO_PWHASH_SALTBYTES = 16;
    98     const CRYPTO_PWHASH_SALTBYTES = 16;
    88     const CRYPTO_PWHASH_STRPREFIX = '$argon2i$';
    99     const CRYPTO_PWHASH_STRPREFIX = '$argon2id$';
    89     const CRYPTO_PWHASH_ALG_ARGON2I13 = 1;
   100     const CRYPTO_PWHASH_ALG_ARGON2I13 = 1;
    90     const CRYPTO_PWHASH_ALG_ARGON2ID13 = 2;
   101     const CRYPTO_PWHASH_ALG_ARGON2ID13 = 2;
    91     const CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE = 33554432;
   102     const CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE = 33554432;
    92     const CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE = 4;
   103     const CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE = 4;
    93     const CRYPTO_PWHASH_MEMLIMIT_MODERATE = 134217728;
   104     const CRYPTO_PWHASH_MEMLIMIT_MODERATE = 134217728;
   105     const CRYPTO_SHORTHASH_BYTES = 8;
   116     const CRYPTO_SHORTHASH_BYTES = 8;
   106     const CRYPTO_SHORTHASH_KEYBYTES = 16;
   117     const CRYPTO_SHORTHASH_KEYBYTES = 16;
   107     const CRYPTO_SECRETBOX_KEYBYTES = 32;
   118     const CRYPTO_SECRETBOX_KEYBYTES = 32;
   108     const CRYPTO_SECRETBOX_MACBYTES = 16;
   119     const CRYPTO_SECRETBOX_MACBYTES = 16;
   109     const CRYPTO_SECRETBOX_NONCEBYTES = 24;
   120     const CRYPTO_SECRETBOX_NONCEBYTES = 24;
       
   121     const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES = 17;
       
   122     const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES = 24;
       
   123     const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES = 32;
       
   124     const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH = 0;
       
   125     const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PULL = 1;
       
   126     const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY = 2;
       
   127     const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL = 3;
       
   128     const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX = 0x3fffffff80;
   110     const CRYPTO_SIGN_BYTES = 64;
   129     const CRYPTO_SIGN_BYTES = 64;
   111     const CRYPTO_SIGN_SEEDBYTES = 32;
   130     const CRYPTO_SIGN_SEEDBYTES = 32;
   112     const CRYPTO_SIGN_PUBLICKEYBYTES = 32;
   131     const CRYPTO_SIGN_PUBLICKEYBYTES = 32;
   113     const CRYPTO_SIGN_SECRETKEYBYTES = 64;
   132     const CRYPTO_SIGN_SECRETKEYBYTES = 64;
   114     const CRYPTO_SIGN_KEYPAIRBYTES = 96;
   133     const CRYPTO_SIGN_KEYPAIRBYTES = 96;
   115     const CRYPTO_STREAM_KEYBYTES = 32;
   134     const CRYPTO_STREAM_KEYBYTES = 32;
   116     const CRYPTO_STREAM_NONCEBYTES = 24;
   135     const CRYPTO_STREAM_NONCEBYTES = 24;
       
   136 
       
   137     /**
       
   138      * Add two numbers (little-endian unsigned), storing the value in the first
       
   139      * parameter.
       
   140      *
       
   141      * This mutates $val.
       
   142      *
       
   143      * @param string $val
       
   144      * @param string $addv
       
   145      * @return void
       
   146      * @throws SodiumException
       
   147      */
       
   148     public static function add(&$val, $addv)
       
   149     {
       
   150         $val_len = ParagonIE_Sodium_Core_Util::strlen($val);
       
   151         $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv);
       
   152         if ($val_len !== $addv_len) {
       
   153             throw new SodiumException('values must have the same length');
       
   154         }
       
   155         $A = ParagonIE_Sodium_Core_Util::stringToIntArray($val);
       
   156         $B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv);
       
   157 
       
   158         $c = 0;
       
   159         for ($i = 0; $i < $val_len; $i++) {
       
   160             $c += ($A[$i] + $B[$i]);
       
   161             $A[$i] = ($c & 0xff);
       
   162             $c >>= 8;
       
   163         }
       
   164         $val = ParagonIE_Sodium_Core_Util::intArrayToString($A);
       
   165     }
       
   166 
       
   167     /**
       
   168      * @param string $encoded
       
   169      * @param int $variant
       
   170      * @param string $ignore
       
   171      * @return string
       
   172      * @throws SodiumException
       
   173      */
       
   174     public static function base642bin($encoded, $variant, $ignore = '')
       
   175     {
       
   176         /* Type checks: */
       
   177         ParagonIE_Sodium_Core_Util::declareScalarType($encoded, 'string', 1);
       
   178 
       
   179         /** @var string $encoded */
       
   180         $encoded = (string) $encoded;
       
   181         if (ParagonIE_Sodium_Core_Util::strlen($encoded) === 0) {
       
   182             return '';
       
   183         }
       
   184 
       
   185         // Just strip before decoding
       
   186         if (!empty($ignore)) {
       
   187             $encoded = str_replace($ignore, '', $encoded);
       
   188         }
       
   189 
       
   190         try {
       
   191             switch ($variant) {
       
   192                 case self::BASE64_VARIANT_ORIGINAL:
       
   193                     return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, true);
       
   194                 case self::BASE64_VARIANT_ORIGINAL_NO_PADDING:
       
   195                     return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, false);
       
   196                 case self::BASE64_VARIANT_URLSAFE:
       
   197                     return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, true);
       
   198                 case self::BASE64_VARIANT_URLSAFE_NO_PADDING:
       
   199                     return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, false);
       
   200                 default:
       
   201                     throw new SodiumException('invalid base64 variant identifier');
       
   202             }
       
   203         } catch (Exception $ex) {
       
   204             if ($ex instanceof SodiumException) {
       
   205                 throw $ex;
       
   206             }
       
   207             throw new SodiumException('invalid base64 string');
       
   208         }
       
   209     }
       
   210 
       
   211     /**
       
   212      * @param string $decoded
       
   213      * @param int $variant
       
   214      * @return string
       
   215      * @throws SodiumException
       
   216      */
       
   217     public static function bin2base64($decoded, $variant)
       
   218     {
       
   219         /* Type checks: */
       
   220         ParagonIE_Sodium_Core_Util::declareScalarType($decoded, 'string', 1);
       
   221         /** @var string $decoded */
       
   222         $decoded = (string) $decoded;
       
   223         if (ParagonIE_Sodium_Core_Util::strlen($decoded) === 0) {
       
   224             return '';
       
   225         }
       
   226 
       
   227         switch ($variant) {
       
   228             case self::BASE64_VARIANT_ORIGINAL:
       
   229                 return ParagonIE_Sodium_Core_Base64_Original::encode($decoded);
       
   230             case self::BASE64_VARIANT_ORIGINAL_NO_PADDING:
       
   231                 return ParagonIE_Sodium_Core_Base64_Original::encodeUnpadded($decoded);
       
   232             case self::BASE64_VARIANT_URLSAFE:
       
   233                 return ParagonIE_Sodium_Core_Base64_UrlSafe::encode($decoded);
       
   234             case self::BASE64_VARIANT_URLSAFE_NO_PADDING:
       
   235                 return ParagonIE_Sodium_Core_Base64_UrlSafe::encodeUnpadded($decoded);
       
   236             default:
       
   237                 throw new SodiumException('invalid base64 variant identifier');
       
   238         }
       
   239     }
   117 
   240 
   118     /**
   241     /**
   119      * Cache-timing-safe implementation of bin2hex().
   242      * Cache-timing-safe implementation of bin2hex().
   120      *
   243      *
   121      * @param string $string A string (probably raw binary)
   244      * @param string $string A string (probably raw binary)
  1308      * @return string     Final BLAKE2b hash.
  1431      * @return string     Final BLAKE2b hash.
  1309      * @throws SodiumException
  1432      * @throws SodiumException
  1310      * @throws TypeError
  1433      * @throws TypeError
  1311      * @psalm-suppress MixedArgument
  1434      * @psalm-suppress MixedArgument
  1312      * @psalm-suppress ReferenceConstraintViolation
  1435      * @psalm-suppress ReferenceConstraintViolation
       
  1436      * @psalm-suppress ConflictingReferenceConstraint
  1313      */
  1437      */
  1314     public static function crypto_generichash_final(&$ctx, $length = self::CRYPTO_GENERICHASH_BYTES)
  1438     public static function crypto_generichash_final(&$ctx, $length = self::CRYPTO_GENERICHASH_BYTES)
  1315     {
  1439     {
  1316         /* Type checks: */
  1440         /* Type checks: */
  1317         ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1);
  1441         ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1);
  1321             return sodium_crypto_generichash_final($ctx, $length);
  1445             return sodium_crypto_generichash_final($ctx, $length);
  1322         }
  1446         }
  1323         if (self::use_fallback('crypto_generichash_final')) {
  1447         if (self::use_fallback('crypto_generichash_final')) {
  1324             $func = '\\Sodium\\crypto_generichash_final';
  1448             $func = '\\Sodium\\crypto_generichash_final';
  1325             return (string) $func($ctx, $length);
  1449             return (string) $func($ctx, $length);
       
  1450         }
       
  1451         if ($length < 1) {
       
  1452             try {
       
  1453                 self::memzero($ctx);
       
  1454             } catch (SodiumException $ex) {
       
  1455                 unset($ctx);
       
  1456             }
       
  1457             return '';
  1326         }
  1458         }
  1327         if (PHP_INT_SIZE === 4) {
  1459         if (PHP_INT_SIZE === 4) {
  1328             $result = ParagonIE_Sodium_Crypto32::generichash_final($ctx, $length);
  1460             $result = ParagonIE_Sodium_Crypto32::generichash_final($ctx, $length);
  1329         } else {
  1461         } else {
  1330             $result = ParagonIE_Sodium_Crypto::generichash_final($ctx, $length);
  1462             $result = ParagonIE_Sodium_Crypto::generichash_final($ctx, $length);
  1378         }
  1510         }
  1379         return ParagonIE_Sodium_Crypto::generichash_init($key, $length);
  1511         return ParagonIE_Sodium_Crypto::generichash_init($key, $length);
  1380     }
  1512     }
  1381 
  1513 
  1382     /**
  1514     /**
       
  1515      * Initialize a BLAKE2b hashing context, for use in a streaming interface.
       
  1516      *
       
  1517      * @param string|null $key If specified must be a string between 16 and 64 bytes
       
  1518      * @param int $length      The size of the desired hash output
       
  1519      * @param string $salt     Salt (up to 16 bytes)
       
  1520      * @param string $personal Personalization string (up to 16 bytes)
       
  1521      * @return string          A BLAKE2 hashing context, encoded as a string
       
  1522      *                         (To be 100% compatible with ext/libsodium)
       
  1523      * @throws SodiumException
       
  1524      * @throws TypeError
       
  1525      * @psalm-suppress MixedArgument
       
  1526      */
       
  1527     public static function crypto_generichash_init_salt_personal(
       
  1528         $key = '',
       
  1529         $length = self::CRYPTO_GENERICHASH_BYTES,
       
  1530         $salt = '',
       
  1531         $personal = ''
       
  1532     ) {
       
  1533         /* Type checks: */
       
  1534         if (is_null($key)) {
       
  1535             $key = '';
       
  1536         }
       
  1537         ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1);
       
  1538         ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
       
  1539         ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3);
       
  1540         ParagonIE_Sodium_Core_Util::declareScalarType($personal, 'string', 4);
       
  1541         $salt = str_pad($salt, 16, "\0", STR_PAD_RIGHT);
       
  1542         $personal = str_pad($personal, 16, "\0", STR_PAD_RIGHT);
       
  1543 
       
  1544         /* Input validation: */
       
  1545         if (!empty($key)) {
       
  1546             /*
       
  1547             if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
       
  1548                 throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
       
  1549             }
       
  1550             */
       
  1551             if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
       
  1552                 throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
       
  1553             }
       
  1554         }
       
  1555         if (PHP_INT_SIZE === 4) {
       
  1556             return ParagonIE_Sodium_Crypto32::generichash_init_salt_personal($key, $length, $salt, $personal);
       
  1557         }
       
  1558         return ParagonIE_Sodium_Crypto::generichash_init_salt_personal($key, $length, $salt, $personal);
       
  1559     }
       
  1560 
       
  1561     /**
  1383      * Update a BLAKE2b hashing context with additional data.
  1562      * Update a BLAKE2b hashing context with additional data.
  1384      *
  1563      *
  1385      * @param string $ctx    BLAKE2 hashing context. Generated by crypto_generichash_init().
  1564      * @param string $ctx    BLAKE2 hashing context. Generated by crypto_generichash_init().
  1386      *                       $ctx is passed by reference and gets updated in-place.
  1565      *                       $ctx is passed by reference and gets updated in-place.
  1387      * @param-out string $ctx
  1566      * @param-out string $ctx
  1420      * @throws Error
  1599      * @throws Error
  1421      */
  1600      */
  1422     public static function crypto_generichash_keygen()
  1601     public static function crypto_generichash_keygen()
  1423     {
  1602     {
  1424         return random_bytes(self::CRYPTO_GENERICHASH_KEYBYTES);
  1603         return random_bytes(self::CRYPTO_GENERICHASH_KEYBYTES);
       
  1604     }
       
  1605 
       
  1606     /**
       
  1607      * @param int $subkey_len
       
  1608      * @param int $subkey_id
       
  1609      * @param string $context
       
  1610      * @param string $key
       
  1611      * @return string
       
  1612      * @throws SodiumException
       
  1613      */
       
  1614     public static function crypto_kdf_derive_from_key(
       
  1615         $subkey_len,
       
  1616         $subkey_id,
       
  1617         $context,
       
  1618         $key
       
  1619     ) {
       
  1620         ParagonIE_Sodium_Core_Util::declareScalarType($subkey_len, 'int', 1);
       
  1621         ParagonIE_Sodium_Core_Util::declareScalarType($subkey_id, 'int', 2);
       
  1622         ParagonIE_Sodium_Core_Util::declareScalarType($context, 'string', 3);
       
  1623         ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
       
  1624         $subkey_id = (int) $subkey_id;
       
  1625         $subkey_len = (int) $subkey_len;
       
  1626         $context = (string) $context;
       
  1627         $key = (string) $key;
       
  1628 
       
  1629         if ($subkey_len < self::CRYPTO_KDF_BYTES_MIN) {
       
  1630             throw new SodiumException('subkey cannot be smaller than SODIUM_CRYPTO_KDF_BYTES_MIN');
       
  1631         }
       
  1632         if ($subkey_len > self::CRYPTO_KDF_BYTES_MAX) {
       
  1633             throw new SodiumException('subkey cannot be larger than SODIUM_CRYPTO_KDF_BYTES_MAX');
       
  1634         }
       
  1635         if ($subkey_id < 0) {
       
  1636             throw new SodiumException('subkey_id cannot be negative');
       
  1637         }
       
  1638         if (ParagonIE_Sodium_Core_Util::strlen($context) !== self::CRYPTO_KDF_CONTEXTBYTES) {
       
  1639             throw new SodiumException('context should be SODIUM_CRYPTO_KDF_CONTEXTBYTES bytes');
       
  1640         }
       
  1641         if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_KDF_KEYBYTES) {
       
  1642             throw new SodiumException('key should be SODIUM_CRYPTO_KDF_KEYBYTES bytes');
       
  1643         }
       
  1644 
       
  1645         $salt = ParagonIE_Sodium_Core_Util::store64_le($subkey_id);
       
  1646         $state = self::crypto_generichash_init_salt_personal(
       
  1647             $key,
       
  1648             $subkey_len,
       
  1649             $salt,
       
  1650             $context
       
  1651         );
       
  1652         return self::crypto_generichash_final($state, $subkey_len);
       
  1653     }
       
  1654 
       
  1655     /**
       
  1656      * @return string
       
  1657      * @throws Exception
       
  1658      * @throws Error
       
  1659      */
       
  1660     public static function crypto_kdf_keygen()
       
  1661     {
       
  1662         return random_bytes(self::CRYPTO_KDF_KEYBYTES);
  1425     }
  1663     }
  1426 
  1664 
  1427     /**
  1665     /**
  1428      * Perform a key exchange, between a designated client and a server.
  1666      * Perform a key exchange, between a designated client and a server.
  1429      *
  1667      *
  1509             $server_public
  1747             $server_public
  1510         );
  1748         );
  1511     }
  1749     }
  1512 
  1750 
  1513     /**
  1751     /**
       
  1752      * @param string $seed
       
  1753      * @return string
       
  1754      * @throws SodiumException
       
  1755      */
       
  1756     public static function crypto_kx_seed_keypair($seed)
       
  1757     {
       
  1758         ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
       
  1759 
       
  1760         $seed = (string) $seed;
       
  1761 
       
  1762         if (ParagonIE_Sodium_Core_Util::strlen($seed) !== self::CRYPTO_KX_SEEDBYTES) {
       
  1763             throw new SodiumException('seed must be SODIUM_CRYPTO_KX_SEEDBYTES bytes');
       
  1764         }
       
  1765 
       
  1766         $sk = self::crypto_generichash($seed, '', self::CRYPTO_KX_SECRETKEYBYTES);
       
  1767         $pk = self::crypto_scalarmult_base($sk);
       
  1768         return $sk . $pk;
       
  1769     }
       
  1770 
       
  1771     /**
       
  1772      * @return string
       
  1773      * @throws Exception
       
  1774      */
       
  1775     public static function crypto_kx_keypair()
       
  1776     {
       
  1777         $sk = self::randombytes_buf(self::CRYPTO_KX_SECRETKEYBYTES);
       
  1778         $pk = self::crypto_scalarmult_base($sk);
       
  1779         return $sk . $pk;
       
  1780     }
       
  1781 
       
  1782     /**
       
  1783      * @param string $keypair
       
  1784      * @param string $serverPublicKey
       
  1785      * @return array{0: string, 1: string}
       
  1786      * @throws SodiumException
       
  1787      */
       
  1788     public static function crypto_kx_client_session_keys($keypair, $serverPublicKey)
       
  1789     {
       
  1790         ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
       
  1791         ParagonIE_Sodium_Core_Util::declareScalarType($serverPublicKey, 'string', 2);
       
  1792 
       
  1793         $keypair = (string) $keypair;
       
  1794         $serverPublicKey = (string) $serverPublicKey;
       
  1795 
       
  1796         if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) {
       
  1797             throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes');
       
  1798         }
       
  1799         if (ParagonIE_Sodium_Core_Util::strlen($serverPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) {
       
  1800             throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes');
       
  1801         }
       
  1802 
       
  1803         $sk = self::crypto_kx_secretkey($keypair);
       
  1804         $pk = self::crypto_kx_publickey($keypair);
       
  1805         $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
       
  1806         self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $serverPublicKey));
       
  1807         self::crypto_generichash_update($h, $pk);
       
  1808         self::crypto_generichash_update($h, $serverPublicKey);
       
  1809         $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
       
  1810         return array(
       
  1811             ParagonIE_Sodium_Core_Util::substr(
       
  1812                 $sessionKeys,
       
  1813                 0,
       
  1814                 self::CRYPTO_KX_SESSIONKEYBYTES
       
  1815             ),
       
  1816             ParagonIE_Sodium_Core_Util::substr(
       
  1817                 $sessionKeys,
       
  1818                 self::CRYPTO_KX_SESSIONKEYBYTES,
       
  1819                 self::CRYPTO_KX_SESSIONKEYBYTES
       
  1820             )
       
  1821         );
       
  1822     }
       
  1823 
       
  1824     /**
       
  1825      * @param string $keypair
       
  1826      * @param string $clientPublicKey
       
  1827      * @return array{0: string, 1: string}
       
  1828      * @throws SodiumException
       
  1829      */
       
  1830     public static function crypto_kx_server_session_keys($keypair, $clientPublicKey)
       
  1831     {
       
  1832         ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
       
  1833         ParagonIE_Sodium_Core_Util::declareScalarType($clientPublicKey, 'string', 2);
       
  1834 
       
  1835         $keypair = (string) $keypair;
       
  1836         $clientPublicKey = (string) $clientPublicKey;
       
  1837 
       
  1838         if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) {
       
  1839             throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes');
       
  1840         }
       
  1841         if (ParagonIE_Sodium_Core_Util::strlen($clientPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) {
       
  1842             throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes');
       
  1843         }
       
  1844 
       
  1845         $sk = self::crypto_kx_secretkey($keypair);
       
  1846         $pk = self::crypto_kx_publickey($keypair);
       
  1847         $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
       
  1848         self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $clientPublicKey));
       
  1849         self::crypto_generichash_update($h, $clientPublicKey);
       
  1850         self::crypto_generichash_update($h, $pk);
       
  1851         $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
       
  1852         return array(
       
  1853             ParagonIE_Sodium_Core_Util::substr(
       
  1854                 $sessionKeys,
       
  1855                 self::CRYPTO_KX_SESSIONKEYBYTES,
       
  1856                 self::CRYPTO_KX_SESSIONKEYBYTES
       
  1857             ),
       
  1858             ParagonIE_Sodium_Core_Util::substr(
       
  1859                 $sessionKeys,
       
  1860                 0,
       
  1861                 self::CRYPTO_KX_SESSIONKEYBYTES
       
  1862             )
       
  1863         );
       
  1864     }
       
  1865 
       
  1866     /**
       
  1867      * @param string $kp
       
  1868      * @return string
       
  1869      * @throws SodiumException
       
  1870      */
       
  1871     public static function crypto_kx_secretkey($kp)
       
  1872     {
       
  1873         return ParagonIE_Sodium_Core_Util::substr(
       
  1874             $kp,
       
  1875             0,
       
  1876             self::CRYPTO_KX_SECRETKEYBYTES
       
  1877         );
       
  1878     }
       
  1879 
       
  1880     /**
       
  1881      * @param string $kp
       
  1882      * @return string
       
  1883      * @throws SodiumException
       
  1884      */
       
  1885     public static function crypto_kx_publickey($kp)
       
  1886     {
       
  1887         return ParagonIE_Sodium_Core_Util::substr(
       
  1888             $kp,
       
  1889             self::CRYPTO_KX_SECRETKEYBYTES,
       
  1890             self::CRYPTO_KX_PUBLICKEYBYTES
       
  1891         );
       
  1892     }
       
  1893 
       
  1894     /**
  1514      * @param int $outlen
  1895      * @param int $outlen
  1515      * @param string $passwd
  1896      * @param string $passwd
  1516      * @param string $salt
  1897      * @param string $salt
  1517      * @param int $opslimit
  1898      * @param int $opslimit
  1518      * @param int $memlimit
  1899      * @param int $memlimit
  1588         }
  1969         }
  1589         // This is the best we can do.
  1970         // This is the best we can do.
  1590         throw new SodiumException(
  1971         throw new SodiumException(
  1591             'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
  1972             'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
  1592         );
  1973         );
       
  1974     }
       
  1975 
       
  1976     /**
       
  1977      * Do we need to rehash this password?
       
  1978      *
       
  1979      * @param string $hash
       
  1980      * @param int $opslimit
       
  1981      * @param int $memlimit
       
  1982      * @return bool
       
  1983      * @throws SodiumException
       
  1984      */
       
  1985     public static function crypto_pwhash_str_needs_rehash($hash, $opslimit, $memlimit)
       
  1986     {
       
  1987         ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 1);
       
  1988         ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
       
  1989         ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
       
  1990 
       
  1991         // Just grab the first 4 pieces.
       
  1992         $pieces = explode('$', (string) $hash);
       
  1993         $prefix = implode('$', array_slice($pieces, 0, 4));
       
  1994 
       
  1995         // Rebuild the expected header.
       
  1996         /** @var int $ops */
       
  1997         $ops = (int) $opslimit;
       
  1998         /** @var int $mem */
       
  1999         $mem = (int) $memlimit >> 10;
       
  2000         $encoded = self::CRYPTO_PWHASH_STRPREFIX . 'v=19$m=' . $mem . ',t=' . $ops . ',p=1';
       
  2001 
       
  2002         // Do they match? If so, we don't need to rehash, so return false.
       
  2003         return !ParagonIE_Sodium_Core_Util::hashEquals($encoded, $prefix);
  1593     }
  2004     }
  1594 
  2005 
  1595     /**
  2006     /**
  1596      * @param string $passwd
  2007      * @param string $passwd
  1597      * @param string $hash
  2008      * @param string $hash
  1986         }
  2397         }
  1987         return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key);
  2398         return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key);
  1988     }
  2399     }
  1989 
  2400 
  1990     /**
  2401     /**
       
  2402      * @param string $key
       
  2403      * @return array<int, string> Returns a state and a header.
       
  2404      * @throws Exception
       
  2405      * @throws SodiumException
       
  2406      */
       
  2407     public static function crypto_secretstream_xchacha20poly1305_init_push($key)
       
  2408     {
       
  2409         if (PHP_INT_SIZE === 4) {
       
  2410             return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_push($key);
       
  2411         }
       
  2412         return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_push($key);
       
  2413     }
       
  2414 
       
  2415     /**
       
  2416      * @param string $header
       
  2417      * @param string $key
       
  2418      * @return string Returns a state.
       
  2419      * @throws Exception
       
  2420      */
       
  2421     public static function crypto_secretstream_xchacha20poly1305_init_pull($header, $key)
       
  2422     {
       
  2423         if (ParagonIE_Sodium_Core_Util::strlen($header) < self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES) {
       
  2424             throw new SodiumException(
       
  2425                 'header size should be SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES bytes'
       
  2426             );
       
  2427         }
       
  2428         if (PHP_INT_SIZE === 4) {
       
  2429             return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_pull($key, $header);
       
  2430         }
       
  2431         return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_pull($key, $header);
       
  2432     }
       
  2433 
       
  2434     /**
       
  2435      * @param string $state
       
  2436      * @param string $msg
       
  2437      * @param string $aad
       
  2438      * @param int $tag
       
  2439      * @return string
       
  2440      * @throws SodiumException
       
  2441      */
       
  2442     public static function crypto_secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
       
  2443     {
       
  2444         if (PHP_INT_SIZE === 4) {
       
  2445             return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_push(
       
  2446                 $state,
       
  2447                 $msg,
       
  2448                 $aad,
       
  2449                 $tag
       
  2450             );
       
  2451         }
       
  2452         return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_push(
       
  2453             $state,
       
  2454             $msg,
       
  2455             $aad,
       
  2456             $tag
       
  2457         );
       
  2458     }
       
  2459 
       
  2460     /**
       
  2461      * @param string $state
       
  2462      * @param string $msg
       
  2463      * @param string $aad
       
  2464      * @return bool|array{0: string, 1: int}
       
  2465      * @throws SodiumException
       
  2466      */
       
  2467     public static function crypto_secretstream_xchacha20poly1305_pull(&$state, $msg, $aad = '')
       
  2468     {
       
  2469         if (PHP_INT_SIZE === 4) {
       
  2470             return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_pull(
       
  2471                 $state,
       
  2472                 $msg,
       
  2473                 $aad
       
  2474             );
       
  2475         }
       
  2476         return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_pull(
       
  2477             $state,
       
  2478             $msg,
       
  2479             $aad
       
  2480         );
       
  2481     }
       
  2482 
       
  2483     /**
       
  2484      * @return string
       
  2485      * @throws Exception
       
  2486      */
       
  2487     public static function crypto_secretstream_xchacha20poly1305_keygen()
       
  2488     {
       
  2489         return random_bytes(self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES);
       
  2490     }
       
  2491 
       
  2492     /**
       
  2493      * @param string $state
       
  2494      * @return void
       
  2495      * @throws SodiumException
       
  2496      */
       
  2497     public static function crypto_secretstream_xchacha20poly1305_rekey(&$state)
       
  2498     {
       
  2499         if (PHP_INT_SIZE === 4) {
       
  2500             ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_rekey($state);
       
  2501         } else {
       
  2502             ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_rekey($state);
       
  2503         }
       
  2504     }
       
  2505 
       
  2506     /**
  1991      * Calculates a SipHash-2-4 hash of a message for a given key.
  2507      * Calculates a SipHash-2-4 hash of a message for a given key.
  1992      *
  2508      *
  1993      * @param string $message Input message
  2509      * @param string $message Input message
  1994      * @param string $key SipHash-2-4 key
  2510      * @param string $key SipHash-2-4 key
  1995      * @return string         Hash
  2511      * @return string         Hash
  2132         }
  2648         }
  2133         if (PHP_INT_SIZE === 4) {
  2649         if (PHP_INT_SIZE === 4) {
  2134             return ParagonIE_Sodium_Core32_Ed25519::keypair();
  2650             return ParagonIE_Sodium_Core32_Ed25519::keypair();
  2135         }
  2651         }
  2136         return ParagonIE_Sodium_Core_Ed25519::keypair();
  2652         return ParagonIE_Sodium_Core_Ed25519::keypair();
       
  2653     }
       
  2654 
       
  2655     /**
       
  2656      * @param string $sk
       
  2657      * @param string $pk
       
  2658      * @return string
       
  2659      * @throws SodiumException
       
  2660      */
       
  2661     public static function crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk)
       
  2662     {
       
  2663         ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1);
       
  2664         ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1);
       
  2665         $sk = (string) $sk;
       
  2666         $pk = (string) $pk;
       
  2667 
       
  2668         if (ParagonIE_Sodium_Core_Util::strlen($sk) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
       
  2669             throw new SodiumException('secretkey should be SODIUM_CRYPTO_SIGN_SECRETKEYBYTES bytes');
       
  2670         }
       
  2671         if (ParagonIE_Sodium_Core_Util::strlen($pk) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) {
       
  2672             throw new SodiumException('publickey should be SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES bytes');
       
  2673         }
       
  2674 
       
  2675         if (self::useNewSodiumAPI()) {
       
  2676             return sodium_crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk);
       
  2677         }
       
  2678         return $sk . $pk;
  2137     }
  2679     }
  2138 
  2680 
  2139     /**
  2681     /**
  2140      * Generate an Ed25519 keypair from a seed.
  2682      * Generate an Ed25519 keypair from a seed.
  2141      *
  2683      *
  2622     {
  3164     {
  2623         /* Type checks: */
  3165         /* Type checks: */
  2624         ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1);
  3166         ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1);
  2625         ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2);
  3167         ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2);
  2626 
  3168 
       
  3169         if (self::useNewSodiumAPI()) {
       
  3170             return sodium_memcmp($left, $right);
       
  3171         }
  2627         if (self::use_fallback('memcmp')) {
  3172         if (self::use_fallback('memcmp')) {
  2628             return (int) call_user_func('\\Sodium\\memcmp', $left, $right);
  3173             return (int) call_user_func('\\Sodium\\memcmp', $left, $right);
  2629         }
  3174         }
  2630         /** @var string $left */
  3175         /** @var string $left */
  2631         /** @var string $right */
  3176         /** @var string $right */
  2664         // This is the best we can do.
  3209         // This is the best we can do.
  2665         throw new SodiumException(
  3210         throw new SodiumException(
  2666             'This is not implemented in sodium_compat, as it is not possible to securely wipe memory from PHP. ' .
  3211             'This is not implemented in sodium_compat, as it is not possible to securely wipe memory from PHP. ' .
  2667             'To fix this error, make sure libsodium is installed and the PHP extension is enabled.'
  3212             'To fix this error, make sure libsodium is installed and the PHP extension is enabled.'
  2668         );
  3213         );
       
  3214     }
       
  3215 
       
  3216     /**
       
  3217      * @param string $unpadded
       
  3218      * @param int $blockSize
       
  3219      * @param bool $dontFallback
       
  3220      * @return string
       
  3221      * @throws SodiumException
       
  3222      */
       
  3223     public static function pad($unpadded, $blockSize, $dontFallback = false)
       
  3224     {
       
  3225         /* Type checks: */
       
  3226         ParagonIE_Sodium_Core_Util::declareScalarType($unpadded, 'string', 1);
       
  3227         ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
       
  3228 
       
  3229         $unpadded = (string) $unpadded;
       
  3230         $blockSize = (int) $blockSize;
       
  3231 
       
  3232         if (self::useNewSodiumAPI() && !$dontFallback) {
       
  3233             return (string) sodium_pad($unpadded, $blockSize);
       
  3234         }
       
  3235 
       
  3236         if ($blockSize <= 0) {
       
  3237             throw new SodiumException(
       
  3238                 'block size cannot be less than 1'
       
  3239             );
       
  3240         }
       
  3241         $unpadded_len = ParagonIE_Sodium_Core_Util::strlen($unpadded);
       
  3242         $xpadlen = ($blockSize - 1);
       
  3243         if (($blockSize & ($blockSize - 1)) === 0) {
       
  3244             $xpadlen -= $unpadded_len & ($blockSize - 1);
       
  3245         } else {
       
  3246             $xpadlen -= $unpadded_len % $blockSize;
       
  3247         }
       
  3248 
       
  3249         $xpadded_len = $unpadded_len + $xpadlen;
       
  3250         $padded = str_repeat("\0", $xpadded_len - 1);
       
  3251         if ($unpadded_len > 0) {
       
  3252             $st = 1;
       
  3253             $i = 0;
       
  3254             $k = $unpadded_len;
       
  3255             for ($j = 0; $j <= $xpadded_len; ++$j) {
       
  3256                 $i = (int) $i;
       
  3257                 $k = (int) $k;
       
  3258                 $st = (int) $st;
       
  3259                 if ($j >= $unpadded_len) {
       
  3260                     $padded[$j] = "\0";
       
  3261                 } else {
       
  3262                     $padded[$j] = $unpadded[$j];
       
  3263                 }
       
  3264                 /** @var int $k */
       
  3265                 $k -= $st;
       
  3266                 $st = (int) (~(
       
  3267                             (
       
  3268                                 (
       
  3269                                     ($k >> 48)
       
  3270                                         |
       
  3271                                     ($k >> 32)
       
  3272                                         |
       
  3273                                     ($k >> 16)
       
  3274                                         |
       
  3275                                     $k
       
  3276                                 ) - 1
       
  3277                             ) >> 16
       
  3278                         )
       
  3279                     ) & 1;
       
  3280                 $i += $st;
       
  3281             }
       
  3282         }
       
  3283 
       
  3284         $mask = 0;
       
  3285         $tail = $xpadded_len;
       
  3286         for ($i = 0; $i < $blockSize; ++$i) {
       
  3287             # barrier_mask = (unsigned char)
       
  3288             #     (((i ^ xpadlen) - 1U) >> ((sizeof(size_t) - 1U) * CHAR_BIT));
       
  3289             $barrier_mask = (($i ^ $xpadlen) -1) >> ((PHP_INT_SIZE << 3) - 1);
       
  3290             # tail[-i] = (tail[-i] & mask) | (0x80 & barrier_mask);
       
  3291             $padded[$tail - $i] = ParagonIE_Sodium_Core_Util::intToChr(
       
  3292                 (ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]) & $mask)
       
  3293                     |
       
  3294                 (0x80 & $barrier_mask)
       
  3295             );
       
  3296             # mask |= barrier_mask;
       
  3297             $mask |= $barrier_mask;
       
  3298         }
       
  3299         return $padded;
       
  3300     }
       
  3301 
       
  3302     /**
       
  3303      * @param string $padded
       
  3304      * @param int $blockSize
       
  3305      * @param bool $dontFallback
       
  3306      * @return string
       
  3307      * @throws SodiumException
       
  3308      */
       
  3309     public static function unpad($padded, $blockSize, $dontFallback = false)
       
  3310     {
       
  3311         /* Type checks: */
       
  3312         ParagonIE_Sodium_Core_Util::declareScalarType($padded, 'string', 1);
       
  3313         ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
       
  3314 
       
  3315         $padded = (string) $padded;
       
  3316         $blockSize = (int) $blockSize;
       
  3317 
       
  3318         if (self::useNewSodiumAPI() && !$dontFallback) {
       
  3319             return (string) sodium_unpad($padded, $blockSize);
       
  3320         }
       
  3321         if ($blockSize <= 0) {
       
  3322             throw new SodiumException('block size cannot be less than 1');
       
  3323         }
       
  3324         $padded_len = ParagonIE_Sodium_Core_Util::strlen($padded);
       
  3325         if ($padded_len < $blockSize) {
       
  3326             throw new SodiumException('invalid padding');
       
  3327         }
       
  3328 
       
  3329         # tail = &padded[padded_len - 1U];
       
  3330         $tail = $padded_len - 1;
       
  3331 
       
  3332         $acc = 0;
       
  3333         $valid = 0;
       
  3334         $pad_len = 0;
       
  3335 
       
  3336         $found = 0;
       
  3337         for ($i = 0; $i < $blockSize; ++$i) {
       
  3338             # c = tail[-i];
       
  3339             $c = ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]);
       
  3340 
       
  3341             # is_barrier =
       
  3342             #     (( (acc - 1U) & (pad_len - 1U) & ((c ^ 0x80) - 1U) ) >> 8) & 1U;
       
  3343             $is_barrier = (
       
  3344                 (
       
  3345                     ($acc - 1) & ($pad_len - 1) & (($c ^ 80) - 1)
       
  3346                 ) >> 7
       
  3347             ) & 1;
       
  3348             $is_barrier &= ~$found;
       
  3349             $found |= $is_barrier;
       
  3350 
       
  3351             # acc |= c;
       
  3352             $acc |= $c;
       
  3353 
       
  3354             # pad_len |= i & (1U + ~is_barrier);
       
  3355             $pad_len |= $i & (1 + ~$is_barrier);
       
  3356 
       
  3357             # valid |= (unsigned char) is_barrier;
       
  3358             $valid |= ($is_barrier & 0xff);
       
  3359         }
       
  3360         # unpadded_len = padded_len - 1U - pad_len;
       
  3361         $unpadded_len = $padded_len - 1 - $pad_len;
       
  3362         if ($valid !== 1) {
       
  3363             throw new SodiumException('invalid padding');
       
  3364         }
       
  3365         return ParagonIE_Sodium_Core_Util::substr($padded, 0, $unpadded_len);
  2669     }
  3366     }
  2670 
  3367 
  2671     /**
  3368     /**
  2672      * Will sodium_compat run fast on the current hardware and PHP configuration?
  3369      * Will sodium_compat run fast on the current hardware and PHP configuration?
  2673      *
  3370      *