wp/wp-includes/sodium_compat/src/Compat.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
--- a/wp/wp-includes/sodium_compat/src/Compat.php	Tue Oct 22 16:11:46 2019 +0200
+++ b/wp/wp-includes/sodium_compat/src/Compat.php	Tue Dec 15 13:49:49 2020 +0100
@@ -49,6 +49,10 @@
     const VERSION_STRING = 'polyfill-1.0.8';
 
     // From libsodium
+    const BASE64_VARIANT_ORIGINAL = 1;
+    const BASE64_VARIANT_ORIGINAL_NO_PADDING = 3;
+    const BASE64_VARIANT_URLSAFE = 5;
+    const BASE64_VARIANT_URLSAFE_NO_PADDING = 7;
     const CRYPTO_AEAD_AES256GCM_KEYBYTES = 32;
     const CRYPTO_AEAD_AES256GCM_NSECBYTES = 0;
     const CRYPTO_AEAD_AES256GCM_NPUBBYTES = 12;
@@ -74,10 +78,17 @@
     const CRYPTO_BOX_MACBYTES = 16;
     const CRYPTO_BOX_NONCEBYTES = 24;
     const CRYPTO_BOX_SEEDBYTES = 32;
+    const CRYPTO_KDF_BYTES_MIN = 16;
+    const CRYPTO_KDF_BYTES_MAX = 64;
+    const CRYPTO_KDF_CONTEXTBYTES = 8;
+    const CRYPTO_KDF_KEYBYTES = 32;
     const CRYPTO_KX_BYTES = 32;
+    const CRYPTO_KX_PRIMITIVE = 'x25519blake2b';
     const CRYPTO_KX_SEEDBYTES = 32;
+    const CRYPTO_KX_KEYPAIRBYTES = 64;
     const CRYPTO_KX_PUBLICKEYBYTES = 32;
     const CRYPTO_KX_SECRETKEYBYTES = 32;
+    const CRYPTO_KX_SESSIONKEYBYTES = 32;
     const CRYPTO_GENERICHASH_BYTES = 32;
     const CRYPTO_GENERICHASH_BYTES_MIN = 16;
     const CRYPTO_GENERICHASH_BYTES_MAX = 64;
@@ -85,7 +96,7 @@
     const CRYPTO_GENERICHASH_KEYBYTES_MIN = 16;
     const CRYPTO_GENERICHASH_KEYBYTES_MAX = 64;
     const CRYPTO_PWHASH_SALTBYTES = 16;
-    const CRYPTO_PWHASH_STRPREFIX = '$argon2i$';
+    const CRYPTO_PWHASH_STRPREFIX = '$argon2id$';
     const CRYPTO_PWHASH_ALG_ARGON2I13 = 1;
     const CRYPTO_PWHASH_ALG_ARGON2ID13 = 2;
     const CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE = 33554432;
@@ -107,6 +118,14 @@
     const CRYPTO_SECRETBOX_KEYBYTES = 32;
     const CRYPTO_SECRETBOX_MACBYTES = 16;
     const CRYPTO_SECRETBOX_NONCEBYTES = 24;
+    const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES = 17;
+    const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES = 24;
+    const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES = 32;
+    const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH = 0;
+    const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PULL = 1;
+    const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY = 2;
+    const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL = 3;
+    const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX = 0x3fffffff80;
     const CRYPTO_SIGN_BYTES = 64;
     const CRYPTO_SIGN_SEEDBYTES = 32;
     const CRYPTO_SIGN_PUBLICKEYBYTES = 32;
@@ -116,6 +135,110 @@
     const CRYPTO_STREAM_NONCEBYTES = 24;
 
     /**
+     * Add two numbers (little-endian unsigned), storing the value in the first
+     * parameter.
+     *
+     * This mutates $val.
+     *
+     * @param string $val
+     * @param string $addv
+     * @return void
+     * @throws SodiumException
+     */
+    public static function add(&$val, $addv)
+    {
+        $val_len = ParagonIE_Sodium_Core_Util::strlen($val);
+        $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv);
+        if ($val_len !== $addv_len) {
+            throw new SodiumException('values must have the same length');
+        }
+        $A = ParagonIE_Sodium_Core_Util::stringToIntArray($val);
+        $B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv);
+
+        $c = 0;
+        for ($i = 0; $i < $val_len; $i++) {
+            $c += ($A[$i] + $B[$i]);
+            $A[$i] = ($c & 0xff);
+            $c >>= 8;
+        }
+        $val = ParagonIE_Sodium_Core_Util::intArrayToString($A);
+    }
+
+    /**
+     * @param string $encoded
+     * @param int $variant
+     * @param string $ignore
+     * @return string
+     * @throws SodiumException
+     */
+    public static function base642bin($encoded, $variant, $ignore = '')
+    {
+        /* Type checks: */
+        ParagonIE_Sodium_Core_Util::declareScalarType($encoded, 'string', 1);
+
+        /** @var string $encoded */
+        $encoded = (string) $encoded;
+        if (ParagonIE_Sodium_Core_Util::strlen($encoded) === 0) {
+            return '';
+        }
+
+        // Just strip before decoding
+        if (!empty($ignore)) {
+            $encoded = str_replace($ignore, '', $encoded);
+        }
+
+        try {
+            switch ($variant) {
+                case self::BASE64_VARIANT_ORIGINAL:
+                    return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, true);
+                case self::BASE64_VARIANT_ORIGINAL_NO_PADDING:
+                    return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, false);
+                case self::BASE64_VARIANT_URLSAFE:
+                    return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, true);
+                case self::BASE64_VARIANT_URLSAFE_NO_PADDING:
+                    return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, false);
+                default:
+                    throw new SodiumException('invalid base64 variant identifier');
+            }
+        } catch (Exception $ex) {
+            if ($ex instanceof SodiumException) {
+                throw $ex;
+            }
+            throw new SodiumException('invalid base64 string');
+        }
+    }
+
+    /**
+     * @param string $decoded
+     * @param int $variant
+     * @return string
+     * @throws SodiumException
+     */
+    public static function bin2base64($decoded, $variant)
+    {
+        /* Type checks: */
+        ParagonIE_Sodium_Core_Util::declareScalarType($decoded, 'string', 1);
+        /** @var string $decoded */
+        $decoded = (string) $decoded;
+        if (ParagonIE_Sodium_Core_Util::strlen($decoded) === 0) {
+            return '';
+        }
+
+        switch ($variant) {
+            case self::BASE64_VARIANT_ORIGINAL:
+                return ParagonIE_Sodium_Core_Base64_Original::encode($decoded);
+            case self::BASE64_VARIANT_ORIGINAL_NO_PADDING:
+                return ParagonIE_Sodium_Core_Base64_Original::encodeUnpadded($decoded);
+            case self::BASE64_VARIANT_URLSAFE:
+                return ParagonIE_Sodium_Core_Base64_UrlSafe::encode($decoded);
+            case self::BASE64_VARIANT_URLSAFE_NO_PADDING:
+                return ParagonIE_Sodium_Core_Base64_UrlSafe::encodeUnpadded($decoded);
+            default:
+                throw new SodiumException('invalid base64 variant identifier');
+        }
+    }
+
+    /**
      * Cache-timing-safe implementation of bin2hex().
      *
      * @param string $string A string (probably raw binary)
@@ -1310,6 +1433,7 @@
      * @throws TypeError
      * @psalm-suppress MixedArgument
      * @psalm-suppress ReferenceConstraintViolation
+     * @psalm-suppress ConflictingReferenceConstraint
      */
     public static function crypto_generichash_final(&$ctx, $length = self::CRYPTO_GENERICHASH_BYTES)
     {
@@ -1324,6 +1448,14 @@
             $func = '\\Sodium\\crypto_generichash_final';
             return (string) $func($ctx, $length);
         }
+        if ($length < 1) {
+            try {
+                self::memzero($ctx);
+            } catch (SodiumException $ex) {
+                unset($ctx);
+            }
+            return '';
+        }
         if (PHP_INT_SIZE === 4) {
             $result = ParagonIE_Sodium_Crypto32::generichash_final($ctx, $length);
         } else {
@@ -1380,6 +1512,53 @@
     }
 
     /**
+     * Initialize a BLAKE2b hashing context, for use in a streaming interface.
+     *
+     * @param string|null $key If specified must be a string between 16 and 64 bytes
+     * @param int $length      The size of the desired hash output
+     * @param string $salt     Salt (up to 16 bytes)
+     * @param string $personal Personalization string (up to 16 bytes)
+     * @return string          A BLAKE2 hashing context, encoded as a string
+     *                         (To be 100% compatible with ext/libsodium)
+     * @throws SodiumException
+     * @throws TypeError
+     * @psalm-suppress MixedArgument
+     */
+    public static function crypto_generichash_init_salt_personal(
+        $key = '',
+        $length = self::CRYPTO_GENERICHASH_BYTES,
+        $salt = '',
+        $personal = ''
+    ) {
+        /* Type checks: */
+        if (is_null($key)) {
+            $key = '';
+        }
+        ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1);
+        ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
+        ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3);
+        ParagonIE_Sodium_Core_Util::declareScalarType($personal, 'string', 4);
+        $salt = str_pad($salt, 16, "\0", STR_PAD_RIGHT);
+        $personal = str_pad($personal, 16, "\0", STR_PAD_RIGHT);
+
+        /* Input validation: */
+        if (!empty($key)) {
+            /*
+            if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
+                throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
+            }
+            */
+            if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
+                throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
+            }
+        }
+        if (PHP_INT_SIZE === 4) {
+            return ParagonIE_Sodium_Crypto32::generichash_init_salt_personal($key, $length, $salt, $personal);
+        }
+        return ParagonIE_Sodium_Crypto::generichash_init_salt_personal($key, $length, $salt, $personal);
+    }
+
+    /**
      * Update a BLAKE2b hashing context with additional data.
      *
      * @param string $ctx    BLAKE2 hashing context. Generated by crypto_generichash_init().
@@ -1425,6 +1604,65 @@
     }
 
     /**
+     * @param int $subkey_len
+     * @param int $subkey_id
+     * @param string $context
+     * @param string $key
+     * @return string
+     * @throws SodiumException
+     */
+    public static function crypto_kdf_derive_from_key(
+        $subkey_len,
+        $subkey_id,
+        $context,
+        $key
+    ) {
+        ParagonIE_Sodium_Core_Util::declareScalarType($subkey_len, 'int', 1);
+        ParagonIE_Sodium_Core_Util::declareScalarType($subkey_id, 'int', 2);
+        ParagonIE_Sodium_Core_Util::declareScalarType($context, 'string', 3);
+        ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
+        $subkey_id = (int) $subkey_id;
+        $subkey_len = (int) $subkey_len;
+        $context = (string) $context;
+        $key = (string) $key;
+
+        if ($subkey_len < self::CRYPTO_KDF_BYTES_MIN) {
+            throw new SodiumException('subkey cannot be smaller than SODIUM_CRYPTO_KDF_BYTES_MIN');
+        }
+        if ($subkey_len > self::CRYPTO_KDF_BYTES_MAX) {
+            throw new SodiumException('subkey cannot be larger than SODIUM_CRYPTO_KDF_BYTES_MAX');
+        }
+        if ($subkey_id < 0) {
+            throw new SodiumException('subkey_id cannot be negative');
+        }
+        if (ParagonIE_Sodium_Core_Util::strlen($context) !== self::CRYPTO_KDF_CONTEXTBYTES) {
+            throw new SodiumException('context should be SODIUM_CRYPTO_KDF_CONTEXTBYTES bytes');
+        }
+        if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_KDF_KEYBYTES) {
+            throw new SodiumException('key should be SODIUM_CRYPTO_KDF_KEYBYTES bytes');
+        }
+
+        $salt = ParagonIE_Sodium_Core_Util::store64_le($subkey_id);
+        $state = self::crypto_generichash_init_salt_personal(
+            $key,
+            $subkey_len,
+            $salt,
+            $context
+        );
+        return self::crypto_generichash_final($state, $subkey_len);
+    }
+
+    /**
+     * @return string
+     * @throws Exception
+     * @throws Error
+     */
+    public static function crypto_kdf_keygen()
+    {
+        return random_bytes(self::CRYPTO_KDF_KEYBYTES);
+    }
+
+    /**
      * Perform a key exchange, between a designated client and a server.
      *
      * Typically, you would designate one machine to be the client and the
@@ -1511,6 +1749,149 @@
     }
 
     /**
+     * @param string $seed
+     * @return string
+     * @throws SodiumException
+     */
+    public static function crypto_kx_seed_keypair($seed)
+    {
+        ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
+
+        $seed = (string) $seed;
+
+        if (ParagonIE_Sodium_Core_Util::strlen($seed) !== self::CRYPTO_KX_SEEDBYTES) {
+            throw new SodiumException('seed must be SODIUM_CRYPTO_KX_SEEDBYTES bytes');
+        }
+
+        $sk = self::crypto_generichash($seed, '', self::CRYPTO_KX_SECRETKEYBYTES);
+        $pk = self::crypto_scalarmult_base($sk);
+        return $sk . $pk;
+    }
+
+    /**
+     * @return string
+     * @throws Exception
+     */
+    public static function crypto_kx_keypair()
+    {
+        $sk = self::randombytes_buf(self::CRYPTO_KX_SECRETKEYBYTES);
+        $pk = self::crypto_scalarmult_base($sk);
+        return $sk . $pk;
+    }
+
+    /**
+     * @param string $keypair
+     * @param string $serverPublicKey
+     * @return array{0: string, 1: string}
+     * @throws SodiumException
+     */
+    public static function crypto_kx_client_session_keys($keypair, $serverPublicKey)
+    {
+        ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
+        ParagonIE_Sodium_Core_Util::declareScalarType($serverPublicKey, 'string', 2);
+
+        $keypair = (string) $keypair;
+        $serverPublicKey = (string) $serverPublicKey;
+
+        if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) {
+            throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes');
+        }
+        if (ParagonIE_Sodium_Core_Util::strlen($serverPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) {
+            throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes');
+        }
+
+        $sk = self::crypto_kx_secretkey($keypair);
+        $pk = self::crypto_kx_publickey($keypair);
+        $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
+        self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $serverPublicKey));
+        self::crypto_generichash_update($h, $pk);
+        self::crypto_generichash_update($h, $serverPublicKey);
+        $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
+        return array(
+            ParagonIE_Sodium_Core_Util::substr(
+                $sessionKeys,
+                0,
+                self::CRYPTO_KX_SESSIONKEYBYTES
+            ),
+            ParagonIE_Sodium_Core_Util::substr(
+                $sessionKeys,
+                self::CRYPTO_KX_SESSIONKEYBYTES,
+                self::CRYPTO_KX_SESSIONKEYBYTES
+            )
+        );
+    }
+
+    /**
+     * @param string $keypair
+     * @param string $clientPublicKey
+     * @return array{0: string, 1: string}
+     * @throws SodiumException
+     */
+    public static function crypto_kx_server_session_keys($keypair, $clientPublicKey)
+    {
+        ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
+        ParagonIE_Sodium_Core_Util::declareScalarType($clientPublicKey, 'string', 2);
+
+        $keypair = (string) $keypair;
+        $clientPublicKey = (string) $clientPublicKey;
+
+        if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) {
+            throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes');
+        }
+        if (ParagonIE_Sodium_Core_Util::strlen($clientPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) {
+            throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes');
+        }
+
+        $sk = self::crypto_kx_secretkey($keypair);
+        $pk = self::crypto_kx_publickey($keypair);
+        $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
+        self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $clientPublicKey));
+        self::crypto_generichash_update($h, $clientPublicKey);
+        self::crypto_generichash_update($h, $pk);
+        $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
+        return array(
+            ParagonIE_Sodium_Core_Util::substr(
+                $sessionKeys,
+                self::CRYPTO_KX_SESSIONKEYBYTES,
+                self::CRYPTO_KX_SESSIONKEYBYTES
+            ),
+            ParagonIE_Sodium_Core_Util::substr(
+                $sessionKeys,
+                0,
+                self::CRYPTO_KX_SESSIONKEYBYTES
+            )
+        );
+    }
+
+    /**
+     * @param string $kp
+     * @return string
+     * @throws SodiumException
+     */
+    public static function crypto_kx_secretkey($kp)
+    {
+        return ParagonIE_Sodium_Core_Util::substr(
+            $kp,
+            0,
+            self::CRYPTO_KX_SECRETKEYBYTES
+        );
+    }
+
+    /**
+     * @param string $kp
+     * @return string
+     * @throws SodiumException
+     */
+    public static function crypto_kx_publickey($kp)
+    {
+        return ParagonIE_Sodium_Core_Util::substr(
+            $kp,
+            self::CRYPTO_KX_SECRETKEYBYTES,
+            self::CRYPTO_KX_PUBLICKEYBYTES
+        );
+    }
+
+    /**
      * @param int $outlen
      * @param string $passwd
      * @param string $salt
@@ -1593,6 +1974,36 @@
     }
 
     /**
+     * Do we need to rehash this password?
+     *
+     * @param string $hash
+     * @param int $opslimit
+     * @param int $memlimit
+     * @return bool
+     * @throws SodiumException
+     */
+    public static function crypto_pwhash_str_needs_rehash($hash, $opslimit, $memlimit)
+    {
+        ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 1);
+        ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
+        ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
+
+        // Just grab the first 4 pieces.
+        $pieces = explode('$', (string) $hash);
+        $prefix = implode('$', array_slice($pieces, 0, 4));
+
+        // Rebuild the expected header.
+        /** @var int $ops */
+        $ops = (int) $opslimit;
+        /** @var int $mem */
+        $mem = (int) $memlimit >> 10;
+        $encoded = self::CRYPTO_PWHASH_STRPREFIX . 'v=19$m=' . $mem . ',t=' . $ops . ',p=1';
+
+        // Do they match? If so, we don't need to rehash, so return false.
+        return !ParagonIE_Sodium_Core_Util::hashEquals($encoded, $prefix);
+    }
+
+    /**
      * @param string $passwd
      * @param string $hash
      * @return bool
@@ -1988,6 +2399,111 @@
     }
 
     /**
+     * @param string $key
+     * @return array<int, string> Returns a state and a header.
+     * @throws Exception
+     * @throws SodiumException
+     */
+    public static function crypto_secretstream_xchacha20poly1305_init_push($key)
+    {
+        if (PHP_INT_SIZE === 4) {
+            return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_push($key);
+        }
+        return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_push($key);
+    }
+
+    /**
+     * @param string $header
+     * @param string $key
+     * @return string Returns a state.
+     * @throws Exception
+     */
+    public static function crypto_secretstream_xchacha20poly1305_init_pull($header, $key)
+    {
+        if (ParagonIE_Sodium_Core_Util::strlen($header) < self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES) {
+            throw new SodiumException(
+                'header size should be SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES bytes'
+            );
+        }
+        if (PHP_INT_SIZE === 4) {
+            return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_pull($key, $header);
+        }
+        return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_pull($key, $header);
+    }
+
+    /**
+     * @param string $state
+     * @param string $msg
+     * @param string $aad
+     * @param int $tag
+     * @return string
+     * @throws SodiumException
+     */
+    public static function crypto_secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
+    {
+        if (PHP_INT_SIZE === 4) {
+            return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_push(
+                $state,
+                $msg,
+                $aad,
+                $tag
+            );
+        }
+        return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_push(
+            $state,
+            $msg,
+            $aad,
+            $tag
+        );
+    }
+
+    /**
+     * @param string $state
+     * @param string $msg
+     * @param string $aad
+     * @return bool|array{0: string, 1: int}
+     * @throws SodiumException
+     */
+    public static function crypto_secretstream_xchacha20poly1305_pull(&$state, $msg, $aad = '')
+    {
+        if (PHP_INT_SIZE === 4) {
+            return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_pull(
+                $state,
+                $msg,
+                $aad
+            );
+        }
+        return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_pull(
+            $state,
+            $msg,
+            $aad
+        );
+    }
+
+    /**
+     * @return string
+     * @throws Exception
+     */
+    public static function crypto_secretstream_xchacha20poly1305_keygen()
+    {
+        return random_bytes(self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES);
+    }
+
+    /**
+     * @param string $state
+     * @return void
+     * @throws SodiumException
+     */
+    public static function crypto_secretstream_xchacha20poly1305_rekey(&$state)
+    {
+        if (PHP_INT_SIZE === 4) {
+            ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_rekey($state);
+        } else {
+            ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_rekey($state);
+        }
+    }
+
+    /**
      * Calculates a SipHash-2-4 hash of a message for a given key.
      *
      * @param string $message Input message
@@ -2137,6 +2653,32 @@
     }
 
     /**
+     * @param string $sk
+     * @param string $pk
+     * @return string
+     * @throws SodiumException
+     */
+    public static function crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk)
+    {
+        ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1);
+        ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1);
+        $sk = (string) $sk;
+        $pk = (string) $pk;
+
+        if (ParagonIE_Sodium_Core_Util::strlen($sk) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
+            throw new SodiumException('secretkey should be SODIUM_CRYPTO_SIGN_SECRETKEYBYTES bytes');
+        }
+        if (ParagonIE_Sodium_Core_Util::strlen($pk) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) {
+            throw new SodiumException('publickey should be SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES bytes');
+        }
+
+        if (self::useNewSodiumAPI()) {
+            return sodium_crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk);
+        }
+        return $sk . $pk;
+    }
+
+    /**
      * Generate an Ed25519 keypair from a seed.
      *
      * @param string $seed Input seed
@@ -2624,6 +3166,9 @@
         ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2);
 
+        if (self::useNewSodiumAPI()) {
+            return sodium_memcmp($left, $right);
+        }
         if (self::use_fallback('memcmp')) {
             return (int) call_user_func('\\Sodium\\memcmp', $left, $right);
         }
@@ -2669,6 +3214,158 @@
     }
 
     /**
+     * @param string $unpadded
+     * @param int $blockSize
+     * @param bool $dontFallback
+     * @return string
+     * @throws SodiumException
+     */
+    public static function pad($unpadded, $blockSize, $dontFallback = false)
+    {
+        /* Type checks: */
+        ParagonIE_Sodium_Core_Util::declareScalarType($unpadded, 'string', 1);
+        ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
+
+        $unpadded = (string) $unpadded;
+        $blockSize = (int) $blockSize;
+
+        if (self::useNewSodiumAPI() && !$dontFallback) {
+            return (string) sodium_pad($unpadded, $blockSize);
+        }
+
+        if ($blockSize <= 0) {
+            throw new SodiumException(
+                'block size cannot be less than 1'
+            );
+        }
+        $unpadded_len = ParagonIE_Sodium_Core_Util::strlen($unpadded);
+        $xpadlen = ($blockSize - 1);
+        if (($blockSize & ($blockSize - 1)) === 0) {
+            $xpadlen -= $unpadded_len & ($blockSize - 1);
+        } else {
+            $xpadlen -= $unpadded_len % $blockSize;
+        }
+
+        $xpadded_len = $unpadded_len + $xpadlen;
+        $padded = str_repeat("\0", $xpadded_len - 1);
+        if ($unpadded_len > 0) {
+            $st = 1;
+            $i = 0;
+            $k = $unpadded_len;
+            for ($j = 0; $j <= $xpadded_len; ++$j) {
+                $i = (int) $i;
+                $k = (int) $k;
+                $st = (int) $st;
+                if ($j >= $unpadded_len) {
+                    $padded[$j] = "\0";
+                } else {
+                    $padded[$j] = $unpadded[$j];
+                }
+                /** @var int $k */
+                $k -= $st;
+                $st = (int) (~(
+                            (
+                                (
+                                    ($k >> 48)
+                                        |
+                                    ($k >> 32)
+                                        |
+                                    ($k >> 16)
+                                        |
+                                    $k
+                                ) - 1
+                            ) >> 16
+                        )
+                    ) & 1;
+                $i += $st;
+            }
+        }
+
+        $mask = 0;
+        $tail = $xpadded_len;
+        for ($i = 0; $i < $blockSize; ++$i) {
+            # barrier_mask = (unsigned char)
+            #     (((i ^ xpadlen) - 1U) >> ((sizeof(size_t) - 1U) * CHAR_BIT));
+            $barrier_mask = (($i ^ $xpadlen) -1) >> ((PHP_INT_SIZE << 3) - 1);
+            # tail[-i] = (tail[-i] & mask) | (0x80 & barrier_mask);
+            $padded[$tail - $i] = ParagonIE_Sodium_Core_Util::intToChr(
+                (ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]) & $mask)
+                    |
+                (0x80 & $barrier_mask)
+            );
+            # mask |= barrier_mask;
+            $mask |= $barrier_mask;
+        }
+        return $padded;
+    }
+
+    /**
+     * @param string $padded
+     * @param int $blockSize
+     * @param bool $dontFallback
+     * @return string
+     * @throws SodiumException
+     */
+    public static function unpad($padded, $blockSize, $dontFallback = false)
+    {
+        /* Type checks: */
+        ParagonIE_Sodium_Core_Util::declareScalarType($padded, 'string', 1);
+        ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
+
+        $padded = (string) $padded;
+        $blockSize = (int) $blockSize;
+
+        if (self::useNewSodiumAPI() && !$dontFallback) {
+            return (string) sodium_unpad($padded, $blockSize);
+        }
+        if ($blockSize <= 0) {
+            throw new SodiumException('block size cannot be less than 1');
+        }
+        $padded_len = ParagonIE_Sodium_Core_Util::strlen($padded);
+        if ($padded_len < $blockSize) {
+            throw new SodiumException('invalid padding');
+        }
+
+        # tail = &padded[padded_len - 1U];
+        $tail = $padded_len - 1;
+
+        $acc = 0;
+        $valid = 0;
+        $pad_len = 0;
+
+        $found = 0;
+        for ($i = 0; $i < $blockSize; ++$i) {
+            # c = tail[-i];
+            $c = ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]);
+
+            # is_barrier =
+            #     (( (acc - 1U) & (pad_len - 1U) & ((c ^ 0x80) - 1U) ) >> 8) & 1U;
+            $is_barrier = (
+                (
+                    ($acc - 1) & ($pad_len - 1) & (($c ^ 80) - 1)
+                ) >> 7
+            ) & 1;
+            $is_barrier &= ~$found;
+            $found |= $is_barrier;
+
+            # acc |= c;
+            $acc |= $c;
+
+            # pad_len |= i & (1U + ~is_barrier);
+            $pad_len |= $i & (1 + ~$is_barrier);
+
+            # valid |= (unsigned char) is_barrier;
+            $valid |= ($is_barrier & 0xff);
+        }
+        # unpadded_len = padded_len - 1U - pad_len;
+        $unpadded_len = $padded_len - 1 - $pad_len;
+        if ($valid !== 1) {
+            throw new SodiumException('invalid padding');
+        }
+        return ParagonIE_Sodium_Core_Util::substr($padded, 0, $unpadded_len);
+    }
+
+    /**
      * Will sodium_compat run fast on the current hardware and PHP configuration?
      *
      * @return bool