]> granicus.if.org Git - php/commitdiff
Squashed commit - Add ext/sodium
authorParagon Initiative Enterprises <security@paragonie.com>
Tue, 6 Jun 2017 14:40:19 +0000 (10:40 -0400)
committerRemi Collet <remi@php.net>
Tue, 11 Jul 2017 05:25:50 +0000 (07:25 +0200)
RFC: https://wiki.php.net/rfc/libsodium
Licensing: https://web.archive.org/web/20170710161517/https://github.com/jedisct1/libsodium-php/issues/127

30 files changed:
.travis.yml
EXTENSIONS
ext/sodium/CREDITS [new file with mode: 0644]
ext/sodium/README.md [new file with mode: 0644]
ext/sodium/config.m4 [new file with mode: 0644]
ext/sodium/config.w32 [new file with mode: 0644]
ext/sodium/libsodium.c [new file with mode: 0644]
ext/sodium/libsodium.php [new file with mode: 0644]
ext/sodium/php_libsodium.h [new file with mode: 0644]
ext/sodium/tests/crypto_aead.phpt [new file with mode: 0644]
ext/sodium/tests/crypto_auth.phpt [new file with mode: 0644]
ext/sodium/tests/crypto_box.phpt [new file with mode: 0644]
ext/sodium/tests/crypto_generichash.phpt [new file with mode: 0644]
ext/sodium/tests/crypto_hex.phpt [new file with mode: 0644]
ext/sodium/tests/crypto_kx.phpt [new file with mode: 0644]
ext/sodium/tests/crypto_scalarmult.phpt [new file with mode: 0644]
ext/sodium/tests/crypto_secretbox.phpt [new file with mode: 0644]
ext/sodium/tests/crypto_shorthash.phpt [new file with mode: 0644]
ext/sodium/tests/crypto_sign.phpt [new file with mode: 0644]
ext/sodium/tests/crypto_stream.phpt [new file with mode: 0644]
ext/sodium/tests/exception_trace_without_args.phpt [new file with mode: 0644]
ext/sodium/tests/inc_add.phpt [new file with mode: 0644]
ext/sodium/tests/installed.phpt [new file with mode: 0644]
ext/sodium/tests/pwhash_argon2i.phpt [new file with mode: 0644]
ext/sodium/tests/pwhash_scrypt.phpt [new file with mode: 0644]
ext/sodium/tests/utils.phpt [new file with mode: 0644]
ext/sodium/tests/version.phpt [new file with mode: 0644]
ext/standard/credits_ext.h
win32/build/libs_version.txt
win32/install.txt

index 2ea2baf70c2294fc3381733fbf7c79471ed15e2e..d51c2a4ab42ca7e5460de67a21e3b4d991c8f4fb 100644 (file)
@@ -14,6 +14,7 @@ addons:
       - libpspell-dev
       - librecode-dev
       - libsasl2-dev
+      - libsodium-dev
       - libxpm-dev
       - libt1-dev
 
index 996a8ed4c56caa74a32b7e363c9194a726750fb0..40601a140590a7975f66abd0ec479814b301c817 100644 (file)
@@ -419,6 +419,12 @@ MAINTENANCE:         Maintained
 STATUS:              Working
 SINCE:               4.0.2
 -------------------------------------------------------------------------------
+EXTENSION:           sodium
+PRIMARY MAINTAINER:  Frank Denis <jedisct1@php.net>
+MAINTENANCE:         Maintained
+STATUS:              Working
+SINCE:               7.2.0
+-------------------------------------------------------------------------------
 EXTENSION:           spl
 PRIMARY MAINTAINER:  Marcus Boerger <helly@php.net>, Etienne Kneuss <colder@php.net>
 MAINTENANCE:         Maintained
diff --git a/ext/sodium/CREDITS b/ext/sodium/CREDITS
new file mode 100644 (file)
index 0000000..02cd698
--- /dev/null
@@ -0,0 +1,2 @@
+;; libsodium
+Frank Denis [jedisct1@php.net] (lead)
diff --git a/ext/sodium/README.md b/ext/sodium/README.md
new file mode 100644 (file)
index 0000000..104570a
--- /dev/null
@@ -0,0 +1,13 @@
+[![Build Status](https://travis-ci.org/jedisct1/libsodium-php.svg?branch=master)](https://travis-ci.org/jedisct1/libsodium-php?branch=master)
+
+libsodium-php
+=============
+
+A simple, low-level PHP extension for
+[libsodium](https://github.com/jedisct1/libsodium).
+
+Full documentation here:
+[Using Libsodium in PHP Projects](https://paragonie.com/book/pecl-libsodium),
+a guide to using the libsodium PHP extension for modern, secure, and
+fast cryptography.
+
diff --git a/ext/sodium/config.m4 b/ext/sodium/config.m4
new file mode 100644 (file)
index 0000000..edb5cd6
--- /dev/null
@@ -0,0 +1,68 @@
+dnl $Id$
+dnl config.m4 for extension sodium
+
+PHP_ARG_WITH(sodium, for sodium support,
+[  --with-sodium           Include sodium support])
+
+PHP_ARG_WITH(libsodium, for libsodium library location,
+[  --with-libsodium[[=DIR]]  libsodium library location, else rely on pkg-config])
+
+if test "$PHP_SODIUM" != "no"; then
+  SEARCH_PATH="/usr/local /usr"     # you might want to change this
+  SEARCH_FOR="/include/sodium.h"  # you most likely want to change this
+
+  AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+  AC_MSG_CHECKING([for libsodium])
+
+  dnl user provided location
+  if test -r $PHP_LIBSODIUM/$SEARCH_FOR; then # path given as parameter
+    LIBSODIUM_DIR=$PHP_LIBSODIUM
+    AC_MSG_RESULT([found in $PHP_LIBSODIUM])
+
+  dnl pkg-config output
+  elif test -x "$PKG_CONFIG" && $PKG_CONFIG --exists libsodium; then
+    LIBSODIUM_CFLAGS=`$PKG_CONFIG libsodium --cflags`
+    LIBSODIUM_LIBS=`$PKG_CONFIG libsodium --libs`
+    LIBSODIUM_VERSION=`$PKG_CONFIG libsodium --modversion`
+    AC_MSG_RESULT(version $LIBSODIUM_VERSION found using pkg-config)
+    PHP_EVAL_LIBLINE($LIBSODIUM_LIBS, SODIUM_SHARED_LIBADD)
+    PHP_EVAL_INCLINE($LIBSODIUM_CFLAGS)
+
+  dnl search default path list
+  else
+    for i in $SEARCH_PATH ; do
+      if test -r $i/$SEARCH_FOR; then
+        LIBSODIUM_DIR=$i
+        AC_MSG_RESULT(found in $i)
+      fi
+    done
+    if test -z "$LIBSODIUM_DIR"; then
+      AC_MSG_ERROR([Please install libsodium - See https://github.com/jedisct1/libsodium])
+    fi
+  fi
+
+  LIBNAME=sodium
+  LIBSYMBOL=crypto_pwhash_scryptsalsa208sha256
+
+  if test -n "$LIBSODIUM_DIR"; then
+    PHP_ADD_INCLUDE($LIBSODIUM_DIR/include)
+    PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $LIBSODIUM_DIR/$PHP_LIBDIR, SODIUM_SHARED_LIBADD)
+  fi
+
+  PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
+  [
+    AC_DEFINE(HAVE_LIBSODIUMLIB,1,[ ])
+  ],[
+    AC_MSG_ERROR([wrong libsodium lib version or lib not found])
+  ],[
+  ])
+  PHP_CHECK_LIBRARY($LIBNAME,crypto_aead_aes256gcm_encrypt,
+  [
+    AC_DEFINE(HAVE_CRYPTO_AEAD_AES256GCM,1,[ ])
+  ],[],[
+  ])
+
+  PHP_SUBST(SODIUM_SHARED_LIBADD)
+
+  PHP_NEW_EXTENSION(sodium, libsodium.c, $ext_shared)
+fi
diff --git a/ext/sodium/config.w32 b/ext/sodium/config.w32
new file mode 100644 (file)
index 0000000..c1af4e6
--- /dev/null
@@ -0,0 +1,14 @@
+// $Id$
+// vim:ft=javascript
+
+ARG_WITH("sodium", "for libsodium support", "no");
+
+if (PHP_SODIUM != "no") {
+    if (CHECK_LIB("libsodium.lib", "sodium", PHP_SODIUM) && CHECK_HEADER_ADD_INCLUDE("sodium.h", "CFLAGS_SODIUM")) {
+        EXTENSION("sodium", "libsodium.c");
+        AC_DEFINE('HAVE_LIBSODIUMLIB', 1 , 'Have the Sodium library');
+        PHP_INSTALL_HEADERS("ext/sodium/", "php_libsodium.h");
+    } else {
+        WARNING("libsodium not enabled; libraries and headers not found");
+    }
+}
diff --git a/ext/sodium/libsodium.c b/ext/sodium/libsodium.c
new file mode 100644 (file)
index 0000000..ae93e6d
--- /dev/null
@@ -0,0 +1,2688 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 7                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2017 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Rasmus Lerdorf <rasmus@php.net>                             |
+   |          Andrei Zmievski <andrei@php.net>                            |
+   |          Stig Venaas <venaas@php.net>                                |
+   |          Jason Greene <jason@php.net>                                |
+   +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_libsodium.h"
+#include "zend_exceptions.h"
+
+#include <sodium.h>
+#include <stdint.h>
+
+#define PHP_SODIUM_ZSTR_TRUNCATE(zs, len) do { ZSTR_LEN(zs) = (len); } while(0)
+
+static zend_class_entry *sodium_exception_ce;
+
+ZEND_BEGIN_ARG_INFO_EX(AI_None, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_FirstArgByReferenceSecondLength, 0, 0, 2)
+       ZEND_ARG_INFO(1, reference)
+       ZEND_ARG_INFO(0, length)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_String, 0, 0, 1)
+       ZEND_ARG_INFO(0, string)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_StringRef, 0, 0, 1)
+       ZEND_ARG_INFO(1, string)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_TwoStrings, 0, 0, 2)
+       ZEND_ARG_INFO(0, string_1)
+       ZEND_ARG_INFO(0, string_2)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_StringRef_And_String, 0, 0, 2)
+       ZEND_ARG_INFO(1, string_1)
+       ZEND_ARG_INFO(0, string_2)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_FourStrings, 0, 0, 3)
+       ZEND_ARG_INFO(0, string_1)
+       ZEND_ARG_INFO(0, string_2)
+       ZEND_ARG_INFO(0, string_3)
+       ZEND_ARG_INFO(0, string_4)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_StringAndKey, 0, 0, 2)
+       ZEND_ARG_INFO(0, string)
+       ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_StringAndKeyPair, 0, 0, 2)
+       ZEND_ARG_INFO(0, string)
+       ZEND_ARG_INFO(0, keypair)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_SignatureAndStringAndKey, 0, 0, 3)
+       ZEND_ARG_INFO(0, signature)
+       ZEND_ARG_INFO(0, string)
+       ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_Key, 0, 0, 1)
+       ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_SecretKeyAndPublicKey, 0, 0, 2)
+       ZEND_ARG_INFO(0, secret_key)
+       ZEND_ARG_INFO(0, public_key)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_LengthAndNonceAndKey, 0, 0, 3)
+       ZEND_ARG_INFO(0, length)
+       ZEND_ARG_INFO(0, nonce)
+       ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_StringAndNonceAndKey, 0, 0, 3)
+       ZEND_ARG_INFO(0, string)
+       ZEND_ARG_INFO(0, nonce)
+       ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_StringAndNonceAndKeyPair, 0, 0, 3)
+       ZEND_ARG_INFO(0, string)
+       ZEND_ARG_INFO(0, nonce)
+       ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_StringAndMaybeKeyAndLength, 0, 0, 1)
+       ZEND_ARG_INFO(0, string)
+       /* optional */
+       ZEND_ARG_INFO(0, key)
+       ZEND_ARG_INFO(0, length)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_LengthAndPasswordAndSaltAndOpsLimitAndMemLimit, 0, 0, 5)
+       ZEND_ARG_INFO(0, length)
+       ZEND_ARG_INFO(0, password)
+       ZEND_ARG_INFO(0, salt)
+       ZEND_ARG_INFO(0, opslimit)
+       ZEND_ARG_INFO(0, memlimit)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_PasswordAndOpsLimitAndMemLimit, 0, 0, 3)
+       ZEND_ARG_INFO(0, password)
+       ZEND_ARG_INFO(0, opslimit)
+       ZEND_ARG_INFO(0, memlimit)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_HashAndPassword, 0, 0, 2)
+       ZEND_ARG_INFO(0, hash)
+       ZEND_ARG_INFO(0, password)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_StringAndADAndNonceAndKey, 0, 0, 4)
+       ZEND_ARG_INFO(0, string)
+       ZEND_ARG_INFO(0, ad)
+       ZEND_ARG_INFO(0, nonce)
+       ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_StateByReferenceAndMaybeLength, 0, 0, 1)
+       ZEND_ARG_INFO(1, state)
+       /* optional */
+       ZEND_ARG_INFO(0, length)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_StateByReferenceAndString, 0, 0, 2)
+       ZEND_ARG_INFO(1, state)
+       ZEND_ARG_INFO(0, string)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(AI_MaybeKeyAndLength, 0, 0, 0)
+       /* optional */
+       ZEND_ARG_INFO(0, key)
+       ZEND_ARG_INFO(0, length)
+ZEND_END_ARG_INFO()
+
+#if defined(HAVE_CRYPTO_AEAD_AES256GCM) && defined(crypto_aead_aes256gcm_KEYBYTES) && \
+       (defined(__amd64) || defined(__amd64__) || defined(__x86_64__) || defined(__i386__) || \
+        defined(_M_AMD64) || defined(_M_IX86))
+# define HAVE_AESGCM 1
+#endif
+
+const zend_function_entry sodium_functions[] = {
+       PHP_FE(sodium_crypto_aead_aes256gcm_is_available, AI_None)
+#ifdef HAVE_AESGCM
+       PHP_FE(sodium_crypto_aead_aes256gcm_decrypt, AI_StringAndADAndNonceAndKey)
+       PHP_FE(sodium_crypto_aead_aes256gcm_encrypt, AI_StringAndADAndNonceAndKey)
+#endif
+       PHP_FE(sodium_crypto_aead_chacha20poly1305_decrypt, AI_StringAndADAndNonceAndKey)
+       PHP_FE(sodium_crypto_aead_chacha20poly1305_encrypt, AI_StringAndADAndNonceAndKey)
+#ifdef crypto_aead_chacha20poly1305_IETF_NPUBBYTES
+       PHP_FE(sodium_crypto_aead_chacha20poly1305_ietf_decrypt, AI_StringAndADAndNonceAndKey)
+       PHP_FE(sodium_crypto_aead_chacha20poly1305_ietf_encrypt, AI_StringAndADAndNonceAndKey)
+#endif
+#ifdef crypto_aead_xchacha20poly1305_IETF_NPUBBYTES
+       PHP_FE(sodium_crypto_aead_xchacha20poly1305_ietf_decrypt, AI_StringAndADAndNonceAndKey)
+       PHP_FE(sodium_crypto_aead_xchacha20poly1305_ietf_encrypt, AI_StringAndADAndNonceAndKey)
+#endif
+       PHP_FE(sodium_crypto_auth, AI_StringAndKey)
+       PHP_FE(sodium_crypto_auth_verify, AI_SignatureAndStringAndKey)
+       PHP_FE(sodium_crypto_box, AI_StringAndNonceAndKeyPair)
+       PHP_FE(sodium_crypto_box_keypair, AI_None)
+       PHP_FE(sodium_crypto_box_seed_keypair, AI_Key)
+       PHP_FE(sodium_crypto_box_keypair_from_secretkey_and_publickey, AI_SecretKeyAndPublicKey)
+       PHP_FE(sodium_crypto_box_open, AI_StringAndNonceAndKey)
+       PHP_FE(sodium_crypto_box_publickey, AI_Key)
+       PHP_FE(sodium_crypto_box_publickey_from_secretkey, AI_Key)
+#ifdef crypto_box_SEALBYTES
+       PHP_FE(sodium_crypto_box_seal, AI_StringAndKey)
+       PHP_FE(sodium_crypto_box_seal_open, AI_StringAndKey)
+#endif
+       PHP_FE(sodium_crypto_box_secretkey, AI_Key)
+       PHP_FE(sodium_crypto_kx, AI_FourStrings)
+       PHP_FE(sodium_crypto_generichash, AI_StringAndMaybeKeyAndLength)
+       PHP_FE(sodium_crypto_generichash_init, AI_MaybeKeyAndLength)
+       PHP_FE(sodium_crypto_generichash_update, AI_StateByReferenceAndString)
+       PHP_FE(sodium_crypto_generichash_final, AI_StateByReferenceAndMaybeLength)
+#ifdef crypto_pwhash_SALTBYTES
+       PHP_FE(sodium_crypto_pwhash, AI_LengthAndPasswordAndSaltAndOpsLimitAndMemLimit)
+       PHP_FE(sodium_crypto_pwhash_str, AI_PasswordAndOpsLimitAndMemLimit)
+       PHP_FE(sodium_crypto_pwhash_str_verify, AI_HashAndPassword)
+#endif
+       PHP_FE(sodium_crypto_pwhash_scryptsalsa208sha256, AI_LengthAndPasswordAndSaltAndOpsLimitAndMemLimit)
+       PHP_FE(sodium_crypto_pwhash_scryptsalsa208sha256_str, AI_PasswordAndOpsLimitAndMemLimit)
+       PHP_FE(sodium_crypto_pwhash_scryptsalsa208sha256_str_verify, AI_HashAndPassword)
+       PHP_FE(sodium_crypto_scalarmult, AI_TwoStrings)
+       PHP_FE(sodium_crypto_secretbox, AI_StringAndNonceAndKey)
+       PHP_FE(sodium_crypto_secretbox_open, AI_StringAndNonceAndKey)
+       PHP_FE(sodium_crypto_shorthash, AI_StringAndKey)
+       PHP_FE(sodium_crypto_sign, AI_StringAndKeyPair)
+       PHP_FE(sodium_crypto_sign_detached, AI_StringAndKeyPair)
+       PHP_FE(sodium_crypto_sign_ed25519_pk_to_curve25519, AI_Key)
+       PHP_FE(sodium_crypto_sign_ed25519_sk_to_curve25519, AI_Key)
+       PHP_FE(sodium_crypto_sign_keypair, AI_None)
+       PHP_FE(sodium_crypto_sign_keypair_from_secretkey_and_publickey, AI_SecretKeyAndPublicKey)
+       PHP_FE(sodium_crypto_sign_open, AI_StringAndKeyPair)
+       PHP_FE(sodium_crypto_sign_publickey, AI_Key)
+       PHP_FE(sodium_crypto_sign_secretkey, AI_Key)
+       PHP_FE(sodium_crypto_sign_publickey_from_secretkey, AI_Key)
+       PHP_FE(sodium_crypto_sign_seed_keypair, AI_Key)
+       PHP_FE(sodium_crypto_sign_verify_detached, AI_SignatureAndStringAndKey)
+       PHP_FE(sodium_crypto_stream, AI_LengthAndNonceAndKey)
+       PHP_FE(sodium_crypto_stream_xor, AI_StringAndNonceAndKey)
+       PHP_FE(sodium_bin2hex, AI_String)
+#if SODIUM_LIBRARY_VERSION_MAJOR > 7 || \
+       (SODIUM_LIBRARY_VERSION_MAJOR == 7 && SODIUM_LIBRARY_VERSION_MINOR >= 6)
+       PHP_FE(sodium_compare, AI_TwoStrings)
+#endif
+       PHP_FE(sodium_hex2bin, AI_TwoStrings)
+       PHP_FE(sodium_increment, AI_StringRef)
+       PHP_FE(sodium_add, AI_StringRef_And_String)
+       PHP_FE(sodium_memcmp, AI_TwoStrings)
+       PHP_FE(sodium_memzero, AI_FirstArgByReferenceSecondLength)
+
+       PHP_FALIAS(sodium_crypto_scalarmult_base, sodium_crypto_box_publickey_from_secretkey, AI_TwoStrings)
+
+       PHP_FE_END
+};
+
+zend_module_entry sodium_module_entry = {
+       STANDARD_MODULE_HEADER,
+       "sodium",
+       sodium_functions,
+       PHP_MINIT(sodium),
+       PHP_MSHUTDOWN(sodium),
+       NULL,
+       NULL,
+       PHP_MINFO(sodium),
+       PHP_SODIUM_VERSION,
+       STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+#ifdef COMPILE_DL_SODIUM
+ZEND_GET_MODULE(sodium)
+#endif
+
+static zend_object *sodium_exception_create_object(zend_class_entry *ce) {
+       zend_object *obj = zend_ce_exception->create_object(ce);
+       zval obj_zv, rv, *trace;
+
+       /* Remove argument information from backtrace to prevent information leaks */
+       ZVAL_OBJ(&obj_zv, obj);
+       trace = zend_read_property(zend_ce_exception, &obj_zv, "trace", sizeof("trace")-1, 0, &rv);
+       if (trace && Z_TYPE_P(trace) == IS_ARRAY) {
+               zval *frame;
+               ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(trace), frame) {
+                       if (Z_TYPE_P(frame) == IS_ARRAY) {
+                               zval *args = zend_hash_str_find(Z_ARRVAL_P(frame), "args", sizeof("args")-1);
+                               if (args && Z_TYPE_P(frame) == IS_ARRAY) {
+                                       zend_hash_clean(Z_ARRVAL_P(args));
+                               }
+                       }
+               } ZEND_HASH_FOREACH_END();
+       }
+
+       return obj;
+}
+
+static void sodium_separate_string(zval *zv) {
+       ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
+       if (!Z_REFCOUNTED_P(zv) || Z_REFCOUNT_P(zv) > 1) {
+               zend_string *copy = zend_string_init(Z_STRVAL_P(zv), Z_STRLEN_P(zv), 0);
+               Z_TRY_DELREF_P(zv);
+               ZVAL_STR(zv, copy);
+       }
+}
+
+PHP_MINIT_FUNCTION(sodium)
+{
+       zend_class_entry ce;
+
+       if (sodium_init() != 0) {
+               zend_error(E_ERROR, "sodium_init()");
+       }
+
+       INIT_CLASS_ENTRY(ce, "SodiumException", NULL);
+       sodium_exception_ce = zend_register_internal_class_ex(&ce, zend_ce_exception);
+       sodium_exception_ce->create_object = sodium_exception_create_object;
+
+       REGISTER_STRING_CONSTANT("SODIUM_LIBRARY_VERSION",
+                                                        (char *) (void *) sodium_version_string(), CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_LIBRARY_MAJOR_VERSION",
+                                                  sodium_library_version_major(), CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_LIBRARY_MINOR_VERSION",
+                                                  sodium_library_version_minor(), CONST_CS | CONST_PERSISTENT);
+#ifdef HAVE_AESGCM
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES",
+                                                  crypto_aead_aes256gcm_KEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AES256GCM_NSECBYTES",
+                                                  crypto_aead_aes256gcm_NSECBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES",
+                                                  crypto_aead_aes256gcm_NPUBBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AES256GCM_ABYTES",
+                                                  crypto_aead_aes256gcm_ABYTES, CONST_CS | CONST_PERSISTENT);
+#endif
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES",
+                                                  crypto_aead_chacha20poly1305_KEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_NSECBYTES",
+                                                  crypto_aead_chacha20poly1305_NSECBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES",
+                                                  crypto_aead_chacha20poly1305_NPUBBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_ABYTES",
+                                                  crypto_aead_chacha20poly1305_ABYTES, CONST_CS | CONST_PERSISTENT);
+#ifdef crypto_aead_chacha20poly1305_IETF_NPUBBYTES
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES",
+                                                  crypto_aead_chacha20poly1305_KEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NSECBYTES",
+                                                  crypto_aead_chacha20poly1305_NSECBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES",
+                                                  crypto_aead_chacha20poly1305_IETF_NPUBBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES",
+                                                  crypto_aead_chacha20poly1305_ABYTES, CONST_CS | CONST_PERSISTENT);
+#endif
+#ifdef crypto_aead_xchacha20poly1305_IETF_NPUBBYTES
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES",
+                                                  crypto_aead_xchacha20poly1305_IETF_KEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NSECBYTES",
+                                                  crypto_aead_xchacha20poly1305_IETF_NSECBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES",
+                                                  crypto_aead_xchacha20poly1305_IETF_NPUBBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES",
+                                                  crypto_aead_xchacha20poly1305_IETF_ABYTES, CONST_CS | CONST_PERSISTENT);
+#endif
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AUTH_BYTES",
+                                                  crypto_auth_BYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AUTH_KEYBYTES",
+                                                  crypto_auth_KEYBYTES, CONST_CS | CONST_PERSISTENT);
+#ifdef crypto_box_SEALBYTES
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_BOX_SEALBYTES",
+                                                  crypto_box_SEALBYTES, CONST_CS | CONST_PERSISTENT);
+#endif
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_BOX_SECRETKEYBYTES",
+                                                  crypto_box_SECRETKEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_BOX_PUBLICKEYBYTES",
+                                                  crypto_box_PUBLICKEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_BOX_KEYPAIRBYTES",
+                                                  crypto_box_SECRETKEYBYTES + crypto_box_PUBLICKEYBYTES,
+                                                  CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_BOX_MACBYTES",
+                                                  crypto_box_MACBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_BOX_NONCEBYTES",
+                                                  crypto_box_NONCEBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_BOX_SEEDBYTES",
+                                                  crypto_box_SEEDBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_BYTES",
+                                                  crypto_kx_BYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_PUBLICKEYBYTES",
+                                                  crypto_kx_PUBLICKEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_SECRETKEYBYTES",
+                                                  crypto_kx_SECRETKEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_GENERICHASH_BYTES",
+                                                  crypto_generichash_BYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_GENERICHASH_BYTES_MIN",
+                                                  crypto_generichash_BYTES_MIN, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_GENERICHASH_BYTES_MAX",
+                                                  crypto_generichash_BYTES_MAX, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_GENERICHASH_KEYBYTES",
+                                                  crypto_generichash_KEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_GENERICHASH_KEYBYTES_MIN",
+                                                  crypto_generichash_KEYBYTES_MIN, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_GENERICHASH_KEYBYTES_MAX",
+                                                  crypto_generichash_KEYBYTES_MAX, CONST_CS | CONST_PERSISTENT);
+#ifdef crypto_pwhash_SALTBYTES
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_SALTBYTES",
+                                                  crypto_pwhash_SALTBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_STRING_CONSTANT("SODIUM_CRYPTO_PWHASH_STRPREFIX",
+                                                        crypto_pwhash_STRPREFIX, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE",
+                                                  crypto_pwhash_opslimit_interactive(), CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE",
+                                                  crypto_pwhash_memlimit_interactive(), CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE",
+                                                  crypto_pwhash_opslimit_moderate(), CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE",
+                                                  crypto_pwhash_memlimit_moderate(), CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE",
+                                                  crypto_pwhash_opslimit_sensitive(), CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE",
+                                                  crypto_pwhash_memlimit_sensitive(), CONST_CS | CONST_PERSISTENT);
+#endif
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES",
+                                                  crypto_pwhash_scryptsalsa208sha256_SALTBYTES, CONST_CS | CONST_PERSISTENT);
+#ifndef crypto_pwhash_scryptsalsa208sha256_STRPREFIX
+# define crypto_pwhash_scryptsalsa208sha256_STRPREFIX "$7$"
+#endif
+       REGISTER_STRING_CONSTANT("SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_STRPREFIX",
+                                                        crypto_pwhash_scryptsalsa208sha256_STRPREFIX, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE",
+                                                  crypto_pwhash_scryptsalsa208sha256_opslimit_interactive(), CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE",
+                                                  crypto_pwhash_scryptsalsa208sha256_memlimit_interactive(), CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_SENSITIVE",
+                                                  crypto_pwhash_scryptsalsa208sha256_opslimit_sensitive(), CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_SENSITIVE",
+                                                  crypto_pwhash_scryptsalsa208sha256_memlimit_sensitive(), CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SCALARMULT_BYTES",
+                                                  crypto_scalarmult_BYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SCALARMULT_SCALARBYTES",
+                                                  crypto_scalarmult_SCALARBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SHORTHASH_BYTES",
+                                                  crypto_shorthash_BYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SHORTHASH_KEYBYTES",
+                                                  crypto_shorthash_KEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SECRETBOX_KEYBYTES",
+                                                  crypto_secretbox_KEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SECRETBOX_MACBYTES",
+                                                  crypto_secretbox_MACBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SECRETBOX_NONCEBYTES",
+                                                  crypto_secretbox_NONCEBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SIGN_BYTES",
+                                                  crypto_sign_BYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SIGN_SEEDBYTES",
+                                                  crypto_sign_SEEDBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES",
+                                                  crypto_sign_PUBLICKEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SIGN_SECRETKEYBYTES",
+                                                  crypto_sign_SECRETKEYBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SIGN_KEYPAIRBYTES",
+                                                  crypto_sign_SECRETKEYBYTES + crypto_sign_PUBLICKEYBYTES,
+                                                  CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_STREAM_NONCEBYTES",
+                                                  crypto_stream_NONCEBYTES, CONST_CS | CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_STREAM_KEYBYTES",
+                                                  crypto_stream_KEYBYTES, CONST_CS | CONST_PERSISTENT);
+       return SUCCESS;
+}
+
+PHP_MSHUTDOWN_FUNCTION(sodium)
+{
+       return SUCCESS;
+}
+
+PHP_MINFO_FUNCTION(sodium)
+{
+       php_info_print_table_start();
+       php_info_print_table_header(2, "sodium support", "enabled");
+       php_info_print_table_header(2, "libsodium headers version", SODIUM_VERSION_STRING);
+       php_info_print_table_header(2, "libsodium library version", sodium_version_string());
+       php_info_print_table_end();
+}
+
+PHP_FUNCTION(sodium_memzero)
+{
+       zval      *buf_zv;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(),
+                                                         "z", &buf_zv) == FAILURE) {
+               return;
+       }
+       ZVAL_DEREF(buf_zv);
+       if (Z_TYPE_P(buf_zv) != IS_STRING) {
+               zend_throw_exception(sodium_exception_ce, "memzero: a PHP string is required", 0);
+               return;
+       }
+       if (Z_REFCOUNTED_P(buf_zv) && Z_REFCOUNT_P(buf_zv) == 1) {
+               char *buf = Z_STRVAL(*buf_zv);
+               size_t buf_len = Z_STRLEN(*buf_zv);
+               if (buf_len > 0) {
+                       sodium_memzero(buf, (size_t) buf_len);
+               }
+       }
+       convert_to_null(buf_zv);
+}
+
+PHP_FUNCTION(sodium_increment)
+{
+       zval              *val_zv;
+       unsigned char *val;
+       size_t             i;
+       size_t             val_len;
+       unsigned int   c;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(),
+                                                         "z", &val_zv) == FAILURE) {
+               return;
+       }
+       ZVAL_DEREF(val_zv);
+       if (Z_TYPE_P(val_zv) != IS_STRING) {
+               zend_throw_exception(sodium_exception_ce, "increment(): a PHP string is required", 0);
+               return;
+       }
+
+       sodium_separate_string(val_zv);
+       val = (unsigned char *) Z_STRVAL(*val_zv);
+       val_len = Z_STRLEN(*val_zv);
+       c = 1U << 8;
+       for (i = (size_t) 0U; i < val_len; i++) {
+               c >>= 8;
+               c += val[i];
+               val[i] = (unsigned char) c;
+       }
+}
+
+PHP_FUNCTION(sodium_add)
+{
+       zval              *val_zv;
+       unsigned char *val;
+       unsigned char *addv;
+       size_t             i;
+       size_t             val_len;
+       size_t             addv_len;
+       unsigned int   c;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(),
+                                                         "zs", &val_zv, &addv, &addv_len) == FAILURE) {
+               return;
+       }
+       ZVAL_DEREF(val_zv);
+       if (Z_TYPE_P(val_zv) != IS_STRING) {
+               zend_throw_exception(sodium_exception_ce, "add(): PHP strings are required", 0);
+               return;
+       }
+
+       sodium_separate_string(val_zv);
+       val = (unsigned char *) Z_STRVAL(*val_zv);
+       val_len = Z_STRLEN(*val_zv);
+       if (val_len != addv_len) {
+               zend_throw_exception(sodium_exception_ce, "add(): values must have the same length", 0);
+               return;
+       }
+       c = 0U;
+       for (i = (size_t) 0U; i < val_len; i++) {
+               c += val[i] + addv[i];
+               val[i] = (unsigned char) c;
+               c >>= 8;
+       }
+}
+
+PHP_FUNCTION(sodium_memcmp)
+{
+       char      *buf1;
+       char      *buf2;
+       size_t     len1;
+       size_t     len2;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &buf1, &len1,
+                                                         &buf2, &len2) == FAILURE) {
+               return;
+       }
+       if (len1 != len2) {
+               zend_throw_exception(sodium_exception_ce, "memcmp(): arguments have different sizes", 0);
+       } else {
+               RETURN_LONG(sodium_memcmp(buf1, buf2, len1));
+       }
+}
+
+PHP_FUNCTION(sodium_crypto_shorthash)
+{
+       zend_string       *hash;
+       unsigned char *key;
+       unsigned char *msg;
+       size_t             key_len;
+       size_t             msg_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &msg, &msg_len,
+                                                         &key, &key_len) == FAILURE) {
+               return;
+       }
+       if (key_len != crypto_shorthash_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_shorthash(): key size should be "
+                                  "CRYPTO_SHORTHASH_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       hash = zend_string_alloc(crypto_shorthash_BYTES, 0);
+       if (crypto_shorthash((unsigned char *) ZSTR_VAL(hash), msg,
+                                                (unsigned long long) msg_len, key) != 0) {
+               zend_string_free(hash);
+               zend_throw_exception(sodium_exception_ce, "crypto_shorthash(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(hash)[crypto_shorthash_BYTES] = 0;
+
+       RETURN_STR(hash);
+}
+
+PHP_FUNCTION(sodium_crypto_secretbox)
+{
+       zend_string       *ciphertext;
+       unsigned char *key;
+       unsigned char *msg;
+       unsigned char *nonce;
+       size_t             key_len;
+       size_t             msg_len;
+       size_t             nonce_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss",
+                                                         &msg, &msg_len,
+                                                         &nonce, &nonce_len,
+                                                         &key, &key_len) == FAILURE) {
+               return;
+       }
+       if (nonce_len != crypto_secretbox_NONCEBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_secretbox(): nonce size should be "
+                                  "CRYPTO_SECRETBOX_NONCEBYTES bytes",
+                                  0);
+               return;
+       }
+       if (key_len != crypto_secretbox_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_secretbox(): key size should be "
+                                  "CRYPTO_SECRETBOX_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       if (SIZE_MAX - msg_len <= crypto_secretbox_MACBYTES) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       ciphertext = zend_string_alloc((size_t) msg_len + crypto_secretbox_MACBYTES, 0);
+       if (crypto_secretbox_easy((unsigned char *) ZSTR_VAL(ciphertext),
+                                                         msg, (unsigned long long) msg_len,
+                                                         nonce, key) != 0) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "crypto_secretbox(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(ciphertext)[msg_len + crypto_secretbox_MACBYTES] = 0;
+
+       RETURN_STR(ciphertext);
+}
+
+PHP_FUNCTION(sodium_crypto_secretbox_open)
+{
+       zend_string       *msg;
+       unsigned char *key;
+       unsigned char *ciphertext;
+       unsigned char *nonce;
+       size_t             key_len;
+       size_t             ciphertext_len;
+       size_t             nonce_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss",
+                                                         &ciphertext, &ciphertext_len,
+                                                         &nonce, &nonce_len,
+                                                         &key, &key_len) == FAILURE) {
+               return;
+       }
+       if (nonce_len != crypto_secretbox_NONCEBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_secretbox_open(): nonce size should be "
+                                  "CRYPTO_SECRETBOX_NONCEBYTES bytes",
+                                  0);
+               return;
+       }
+       if (key_len != crypto_secretbox_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_secretbox_open(): key size should be "
+                                  "CRYPTO_SECRETBOX_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       if (ciphertext_len < crypto_secretbox_MACBYTES) {
+               RETURN_FALSE;
+       }
+       msg = zend_string_alloc
+               ((size_t) ciphertext_len - crypto_secretbox_MACBYTES, 0);
+       if (crypto_secretbox_open_easy((unsigned char *) ZSTR_VAL(msg), ciphertext,
+                                                                  (unsigned long long) ciphertext_len,
+                                                                  nonce, key) != 0) {
+               zend_string_free(msg);
+               RETURN_FALSE;
+       } else {
+               ZSTR_VAL(msg)[ciphertext_len - crypto_secretbox_MACBYTES] = 0;
+               RETURN_STR(msg);
+       }
+}
+
+PHP_FUNCTION(sodium_crypto_generichash)
+{
+       zend_string       *hash;
+       unsigned char *key = NULL;
+       unsigned char *msg;
+       zend_long          hash_len = crypto_generichash_BYTES;
+       size_t             key_len = 0;
+       size_t             msg_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sl",
+                                                         &msg, &msg_len,
+                                                         &key, &key_len,
+                                                         &hash_len) == FAILURE) {
+               return;
+       }
+       if (hash_len < crypto_generichash_BYTES_MIN ||
+               hash_len > crypto_generichash_BYTES_MAX) {
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash(): unsupported output length", 0);
+               return;
+       }
+       if (key_len != 0 &&
+               (key_len < crypto_generichash_KEYBYTES_MIN ||
+                key_len > crypto_generichash_KEYBYTES_MAX)) {
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash(): unsupported key length", 0);
+               return;
+       }
+       hash = zend_string_alloc(hash_len, 0);
+       if (crypto_generichash((unsigned char *) ZSTR_VAL(hash), (size_t) hash_len,
+                                                  msg, (unsigned long long) msg_len,
+                                                  key, (size_t) key_len) != 0) {
+               zend_string_free(hash);
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(hash)[hash_len] = 0;
+
+       RETURN_STR(hash);
+}
+
+PHP_FUNCTION(sodium_crypto_generichash_init)
+{
+       crypto_generichash_state  state_tmp;
+       zend_string                              *state;
+       unsigned char                    *key = NULL;
+       size_t                                    state_len = sizeof (crypto_generichash_state);
+       zend_long                                 hash_len = crypto_generichash_BYTES;
+       size_t                                    key_len = 0;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sl",
+                                                         &key, &key_len,
+                                                         &hash_len) == FAILURE) {
+               return;
+       }
+       if (hash_len < crypto_generichash_BYTES_MIN ||
+               hash_len > crypto_generichash_BYTES_MAX) {
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash_init(): unsupported output length", 0);
+               return;
+       }
+       if (key_len != 0 &&
+               (key_len < crypto_generichash_KEYBYTES_MIN ||
+                key_len > crypto_generichash_KEYBYTES_MAX)) {
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash_init(): unsupported key length", 0);
+               return;
+       }
+       if (crypto_generichash_init((void *) &state_tmp, key, (size_t) key_len,
+                                                               (size_t) hash_len) != 0) {
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash_init(): internal error", 0);
+               return;
+       }
+       state = zend_string_alloc(state_len, 0);
+       memcpy(ZSTR_VAL(state), &state_tmp, state_len);
+       sodium_memzero(&state_tmp, sizeof state_tmp);
+       ZSTR_VAL(state)[state_len] = 0;
+
+       RETURN_STR(state);
+}
+
+PHP_FUNCTION(sodium_crypto_generichash_update)
+{
+       crypto_generichash_state  state_tmp;
+       zval                                     *state_zv;
+       unsigned char                    *msg;
+       unsigned char                    *state;
+       size_t                                    msg_len;
+       size_t                                    state_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs",
+                                                         &state_zv, &msg, &msg_len) == FAILURE) {
+               return;
+       }
+       ZVAL_DEREF(state_zv);
+       if (Z_TYPE_P(state_zv) != IS_STRING) {
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash_update: a reference to a state is required", 0);
+               return;
+       }
+       sodium_separate_string(state_zv);
+       state = (unsigned char *) Z_STRVAL(*state_zv);
+       state_len = Z_STRLEN(*state_zv);
+       if (state_len != sizeof (crypto_generichash_state)) {
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash_update(): incorrect state length", 0);
+               return;
+       }
+       memcpy(&state_tmp, state, sizeof state_tmp);
+       if (crypto_generichash_update((void *) &state_tmp, msg,
+                                                                 (unsigned long long) msg_len) != 0) {
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash_update(): internal error", 0);
+               return;
+       }
+       memcpy(state, &state_tmp, state_len);
+       sodium_memzero(&state_tmp, sizeof state_tmp);
+
+       RETURN_TRUE;
+}
+
+PHP_FUNCTION(sodium_crypto_generichash_final)
+{
+       crypto_generichash_state  state_tmp;
+       zend_string                              *hash;
+       zval                                     *state_zv;
+       unsigned char                    *state;
+       size_t                                    state_len;
+       zend_long                                 hash_len = crypto_generichash_BYTES;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l",
+                                                         &state_zv, &hash_len) == FAILURE) {
+               return;
+       }
+       ZVAL_DEREF(state_zv);
+       if (Z_TYPE_P(state_zv) != IS_STRING) {
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash_final: a reference to a state is required", 0);
+               return;
+       }
+       sodium_separate_string(state_zv);
+       state = (unsigned char *) Z_STRVAL(*state_zv);
+       state_len = Z_STRLEN(*state_zv);
+       if (state_len != sizeof (crypto_generichash_state)) {
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash_final(): incorrect state length", 0);
+               return;
+       }
+       if (hash_len < crypto_generichash_BYTES_MIN ||
+               hash_len > crypto_generichash_BYTES_MAX) {
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash_final(): unsupported output length", 0);
+               return;
+       }
+       hash = zend_string_alloc(hash_len, 0);
+       memcpy(&state_tmp, state, sizeof state_tmp);
+       if (crypto_generichash_final((void *) &state_tmp,
+                                                                (unsigned char *) ZSTR_VAL(hash),
+                                                                (size_t) hash_len) != 0) {
+               zend_string_free(hash);
+               zend_throw_exception(sodium_exception_ce, "crypto_generichash_final(): internal error", 0);
+               return;
+       }
+       sodium_memzero(state, state_len);
+       convert_to_null(state_zv);
+       ZSTR_VAL(hash)[hash_len] = 0;
+
+       RETURN_STR(hash);
+}
+
+PHP_FUNCTION(sodium_crypto_box_keypair)
+{
+       zend_string *keypair;
+       size_t           keypair_len;
+
+       keypair_len = crypto_box_SECRETKEYBYTES + crypto_box_PUBLICKEYBYTES;
+       keypair = zend_string_alloc(keypair_len, 0);
+       if (crypto_box_keypair((unsigned char *) ZSTR_VAL(keypair) +
+                                                  crypto_box_SECRETKEYBYTES,
+                                                  (unsigned char *) ZSTR_VAL(keypair)) != 0) {
+               zend_string_free(keypair);
+               zend_throw_exception(sodium_exception_ce, "crypto_box_keypair(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(keypair)[keypair_len] = 0;
+
+       RETURN_STR(keypair);
+}
+
+PHP_FUNCTION(sodium_crypto_box_seed_keypair)
+{
+       zend_string       *keypair;
+       unsigned char *seed;
+       size_t             keypair_len;
+       size_t             seed_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
+                                                         &seed, &seed_len) == FAILURE) {
+               return;
+       }
+       if (seed_len != crypto_box_SEEDBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_box_seed_keypair(): "
+                                  "seed should be CRYPTO_BOX_SEEDBYTES bytes",
+                                  0);
+               return;
+       }
+       keypair_len = crypto_box_SECRETKEYBYTES + crypto_box_PUBLICKEYBYTES;
+       keypair = zend_string_alloc(keypair_len, 0);
+       if (crypto_box_seed_keypair((unsigned char *) ZSTR_VAL(keypair) +
+                                                                crypto_box_SECRETKEYBYTES,
+                                                                (unsigned char *) ZSTR_VAL(keypair),
+                                                                seed) != 0) {
+               zend_string_free(keypair);
+               zend_throw_exception(sodium_exception_ce, "crypto_box_seed_keypair(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(keypair)[keypair_len] = 0;
+
+       RETURN_STR(keypair);
+}
+
+PHP_FUNCTION(sodium_crypto_box_keypair_from_secretkey_and_publickey)
+{
+       zend_string *keypair;
+       char            *publickey;
+       char            *secretkey;
+       size_t           keypair_len;
+       size_t           publickey_len;
+       size_t           secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &secretkey, &secretkey_len,
+                                                         &publickey, &publickey_len) == FAILURE) {
+               return;
+       }
+       if (secretkey_len != crypto_box_SECRETKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_box_keypair_from_secretkey_and_publickey(): "
+                                  "secretkey should be CRYPTO_BOX_SECRETKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       if (publickey_len != crypto_box_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_box_keypair_from_secretkey_and_publickey(): "
+                                  "publickey should be CRYPTO_BOX_PUBLICKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       keypair_len = crypto_box_SECRETKEYBYTES + crypto_box_PUBLICKEYBYTES;
+       keypair = zend_string_alloc(keypair_len, 0);
+       memcpy(ZSTR_VAL(keypair), secretkey, crypto_box_SECRETKEYBYTES);
+       memcpy(ZSTR_VAL(keypair) + crypto_box_SECRETKEYBYTES, publickey,
+                  crypto_box_PUBLICKEYBYTES);
+       ZSTR_VAL(keypair)[keypair_len] = 0;
+
+       RETURN_STR(keypair);
+}
+
+PHP_FUNCTION(sodium_crypto_box_secretkey)
+{
+       zend_string       *secretkey;
+       unsigned char *keypair;
+       size_t             keypair_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
+                                                         &keypair, &keypair_len) == FAILURE) {
+               return;
+       }
+       if (keypair_len !=
+               crypto_box_SECRETKEYBYTES + crypto_box_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_box_secretkey(): keypair should be "
+                                  "CRYPTO_BOX_KEYPAIRBYTES bytes",
+                                  0);
+               return;
+       }
+       secretkey = zend_string_alloc(crypto_box_SECRETKEYBYTES, 0);
+       memcpy(ZSTR_VAL(secretkey), keypair, crypto_box_SECRETKEYBYTES);
+       ZSTR_VAL(secretkey)[crypto_box_SECRETKEYBYTES] = 0;
+
+       RETURN_STR(secretkey);
+}
+
+PHP_FUNCTION(sodium_crypto_box_publickey)
+{
+       zend_string       *publickey;
+       unsigned char *keypair;
+       size_t             keypair_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
+                                                         &keypair, &keypair_len) == FAILURE) {
+               return;
+       }
+       if (keypair_len !=
+               crypto_box_SECRETKEYBYTES + crypto_box_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_box_publickey(): keypair should be "
+                                  "CRYPTO_BOX_KEYPAIRBYTES bytes",
+                                  0);
+               return;
+       }
+       publickey = zend_string_alloc(crypto_box_PUBLICKEYBYTES, 0);
+       memcpy(ZSTR_VAL(publickey), keypair + crypto_box_SECRETKEYBYTES,
+                  crypto_box_PUBLICKEYBYTES);
+       ZSTR_VAL(publickey)[crypto_box_PUBLICKEYBYTES] = 0;
+
+       RETURN_STR(publickey);
+}
+
+PHP_FUNCTION(sodium_crypto_box_publickey_from_secretkey)
+{
+       zend_string       *publickey;
+       unsigned char *secretkey;
+       size_t             secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
+                                                         &secretkey, &secretkey_len) == FAILURE) {
+               return;
+       }
+       if (secretkey_len != crypto_box_SECRETKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_box_publickey_from_secretkey(): key should be "
+                                  "CRYPTO_BOX_SECRETKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       publickey = zend_string_alloc(crypto_box_PUBLICKEYBYTES, 0);
+       (void) sizeof(int[crypto_scalarmult_BYTES ==
+                                         crypto_box_PUBLICKEYBYTES ? 1 : -1]);
+       (void) sizeof(int[crypto_scalarmult_SCALARBYTES ==
+                                         crypto_box_SECRETKEYBYTES ? 1 : -1]);
+       crypto_scalarmult_base((unsigned char *) ZSTR_VAL(publickey), secretkey);
+       ZSTR_VAL(publickey)[crypto_box_PUBLICKEYBYTES] = 0;
+
+       RETURN_STR(publickey);
+}
+
+PHP_FUNCTION(sodium_crypto_box)
+{
+       zend_string       *ciphertext;
+       unsigned char *keypair;
+       unsigned char *msg;
+       unsigned char *nonce;
+       unsigned char *publickey;
+       unsigned char *secretkey;
+       size_t             keypair_len;
+       size_t             msg_len;
+       size_t             nonce_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss",
+                                                         &msg, &msg_len,
+                                                         &nonce, &nonce_len,
+                                                         &keypair, &keypair_len) == FAILURE) {
+               return;
+       }
+       if (nonce_len != crypto_box_NONCEBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_box(): nonce size should be "
+                                  "CRYPTO_BOX_NONCEBYTES bytes",
+                                  0);
+               return;
+       }
+       if (keypair_len != crypto_box_SECRETKEYBYTES + crypto_box_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_box(): keypair size should be "
+                                  "CRYPTO_BOX_KEYPAIRBYTES bytes",
+                                  0);
+               return;
+       }
+       secretkey = keypair;
+       publickey = keypair + crypto_box_SECRETKEYBYTES;
+       if (SIZE_MAX - msg_len <= crypto_box_MACBYTES) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       ciphertext = zend_string_alloc((size_t) msg_len + crypto_box_MACBYTES, 0);
+       if (crypto_box_easy((unsigned char *) ZSTR_VAL(ciphertext), msg,
+                                               (unsigned long long) msg_len,
+                                               nonce, publickey, secretkey) != 0) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "crypto_box(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(ciphertext)[msg_len + crypto_box_MACBYTES] = 0;
+
+       RETURN_STR(ciphertext);
+}
+
+PHP_FUNCTION(sodium_crypto_box_open)
+{
+       zend_string       *msg;
+       unsigned char *ciphertext;
+       unsigned char *keypair;
+       unsigned char *nonce;
+       unsigned char *publickey;
+       unsigned char *secretkey;
+       size_t             ciphertext_len;
+       size_t             keypair_len;
+       size_t             nonce_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss",
+                                                         &ciphertext, &ciphertext_len,
+                                                         &nonce, &nonce_len,
+                                                         &keypair, &keypair_len) == FAILURE) {
+               return;
+       }
+       if (nonce_len != crypto_box_NONCEBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_box_open(): nonce size should be "
+                                  "CRYPTO_BOX_NONCEBYTES bytes",
+                                  0);
+               return;
+       }
+       if (keypair_len != crypto_box_SECRETKEYBYTES + crypto_box_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_box_open(): keypair size should be "
+                                  "CRYPTO_BOX_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       secretkey = keypair;
+       publickey = keypair + crypto_box_SECRETKEYBYTES;
+       if (ciphertext_len < crypto_box_MACBYTES) {
+               RETURN_FALSE;
+       }
+       msg = zend_string_alloc((size_t) ciphertext_len - crypto_box_MACBYTES, 0);
+       if (crypto_box_open_easy((unsigned char *) ZSTR_VAL(msg), ciphertext,
+                                                        (unsigned long long) ciphertext_len,
+                                                        nonce, publickey, secretkey) != 0) {
+               zend_string_free(msg);
+               RETURN_FALSE;
+       } else {
+               ZSTR_VAL(msg)[ciphertext_len - crypto_box_MACBYTES] = 0;
+               RETURN_STR(msg);
+       }
+}
+
+#ifdef crypto_box_SEALBYTES
+PHP_FUNCTION(sodium_crypto_box_seal)
+{
+       zend_string       *ciphertext;
+       unsigned char *msg;
+       unsigned char *publickey;
+       size_t             msg_len;
+       size_t             publickey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &msg, &msg_len,
+                                                         &publickey, &publickey_len) == FAILURE) {
+               return;
+       }
+       if (publickey_len != crypto_box_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_box_seal(): public key size should be "
+                                  "CRYPTO_BOX_PUBLICKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       if (SIZE_MAX - msg_len <= crypto_box_SEALBYTES) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       ciphertext = zend_string_alloc((size_t) msg_len + crypto_box_SEALBYTES, 0);
+       if (crypto_box_seal((unsigned char *) ZSTR_VAL(ciphertext), msg,
+                                               (unsigned long long) msg_len, publickey) != 0) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "crypto_box_seal(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(ciphertext)[msg_len + crypto_box_SEALBYTES] = 0;
+
+       RETURN_STR(ciphertext);
+}
+
+PHP_FUNCTION(sodium_crypto_box_seal_open)
+{
+       zend_string       *msg;
+       unsigned char *ciphertext;
+       unsigned char *keypair;
+       unsigned char *publickey;
+       unsigned char *secretkey;
+       size_t             ciphertext_len;
+       size_t             keypair_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &ciphertext, &ciphertext_len,
+                                                         &keypair, &keypair_len) == FAILURE) {
+               return;
+       }
+       if (keypair_len != crypto_box_SECRETKEYBYTES + crypto_box_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_box_seal_open(): keypair size should be "
+                                  "CRYPTO_BOX_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       secretkey = keypair;
+       publickey = keypair + crypto_box_SECRETKEYBYTES;
+       if (ciphertext_len < crypto_box_SEALBYTES) {
+               RETURN_FALSE;
+       }
+       msg = zend_string_alloc((size_t) ciphertext_len - crypto_box_SEALBYTES, 0);
+       if (crypto_box_seal_open((unsigned char *) ZSTR_VAL(msg), ciphertext,
+                                                        (unsigned long long) ciphertext_len,
+                                                        publickey, secretkey) != 0) {
+               zend_string_free(msg);
+               RETURN_FALSE;
+       } else {
+               ZSTR_VAL(msg)[ciphertext_len - crypto_box_SEALBYTES] = 0;
+               RETURN_STR(msg);
+       }
+}
+#endif
+
+PHP_FUNCTION(sodium_crypto_sign_keypair)
+{
+       zend_string *keypair;
+       size_t           keypair_len;
+
+       keypair_len = crypto_sign_SECRETKEYBYTES + crypto_sign_PUBLICKEYBYTES;
+       keypair = zend_string_alloc(keypair_len, 0);
+       if (crypto_sign_keypair((unsigned char *) ZSTR_VAL(keypair) +
+                                                       crypto_sign_SECRETKEYBYTES,
+                                                       (unsigned char *) ZSTR_VAL(keypair)) != 0) {
+               zend_string_free(keypair);
+               zend_throw_exception(sodium_exception_ce, "crypto_sign_keypair(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(keypair)[keypair_len] = 0;
+
+       RETURN_STR(keypair);
+}
+
+PHP_FUNCTION(sodium_crypto_sign_seed_keypair)
+{
+       zend_string       *keypair;
+       unsigned char *seed;
+       size_t             keypair_len;
+       size_t             seed_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
+                                                         &seed, &seed_len) == FAILURE) {
+               return;
+       }
+       if (seed_len != crypto_sign_SEEDBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_seed_keypair(): "
+                                  "seed should be CRYPTO_SIGN_SEEDBYTES bytes",
+                                  0);
+               return;
+       }
+       keypair_len = crypto_sign_SECRETKEYBYTES + crypto_sign_PUBLICKEYBYTES;
+       keypair = zend_string_alloc(keypair_len, 0);
+       if (crypto_sign_seed_keypair((unsigned char *) ZSTR_VAL(keypair) +
+                                                                crypto_sign_SECRETKEYBYTES,
+                                                                (unsigned char *) ZSTR_VAL(keypair),
+                                                                seed) != 0) {
+               zend_string_free(keypair);
+               zend_throw_exception(sodium_exception_ce, "crypto_sign_seed_keypair(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(keypair)[keypair_len] = 0;
+
+       RETURN_STR(keypair);
+}
+
+PHP_FUNCTION(sodium_crypto_sign_keypair_from_secretkey_and_publickey)
+{
+       zend_string *keypair;
+       char            *publickey;
+       char            *secretkey;
+       size_t           keypair_len;
+       size_t           publickey_len;
+       size_t           secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &secretkey, &secretkey_len,
+                                                         &publickey, &publickey_len) == FAILURE) {
+               return;
+       }
+       if (secretkey_len != crypto_sign_SECRETKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_keypair_from_secretkey_and_publickey(): "
+                                  "secretkey should be CRYPTO_SIGN_SECRETKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       if (publickey_len != crypto_sign_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_keypair_from_secretkey_and_publickey(): "
+                                  "publickey should be CRYPTO_SIGN_PUBLICKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       keypair_len = crypto_sign_SECRETKEYBYTES + crypto_sign_PUBLICKEYBYTES;
+       keypair = zend_string_alloc(keypair_len, 0);
+       memcpy(ZSTR_VAL(keypair), secretkey, crypto_sign_SECRETKEYBYTES);
+       memcpy(ZSTR_VAL(keypair) + crypto_sign_SECRETKEYBYTES, publickey,
+                  crypto_sign_PUBLICKEYBYTES);
+       ZSTR_VAL(keypair)[keypair_len] = 0;
+
+       RETURN_STR(keypair);
+}
+
+PHP_FUNCTION(sodium_crypto_sign_publickey_from_secretkey)
+{
+       zend_string *publickey;
+       char            *secretkey;
+       size_t           secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
+                                                         &secretkey, &secretkey_len) == FAILURE) {
+               return;
+       }
+       if (secretkey_len != crypto_sign_SECRETKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_publickey_from_secretkey(): "
+                                  "secretkey should be CRYPTO_SIGN_SECRETKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       publickey = zend_string_alloc(crypto_sign_PUBLICKEYBYTES, 0);
+
+       if (crypto_sign_ed25519_sk_to_pk((unsigned char *) ZSTR_VAL(publickey),
+                                                                        (const unsigned char *) secretkey) != 0) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_publickey_from_secretkey(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(publickey)[crypto_sign_PUBLICKEYBYTES] = 0;
+
+       RETURN_STR(publickey);
+}
+
+PHP_FUNCTION(sodium_crypto_sign_secretkey)
+{
+       zend_string       *secretkey;
+       unsigned char *keypair;
+       size_t             keypair_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
+                                                         &keypair, &keypair_len) == FAILURE) {
+               return;
+       }
+       if (keypair_len !=
+               crypto_sign_SECRETKEYBYTES + crypto_sign_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_secretkey(): keypair should be "
+                                  "CRYPTO_SIGN_KEYPAIRBYTES bytes",
+                                  0);
+               return;
+       }
+       secretkey = zend_string_alloc(crypto_sign_SECRETKEYBYTES, 0);
+       memcpy(ZSTR_VAL(secretkey), keypair, crypto_sign_SECRETKEYBYTES);
+       ZSTR_VAL(secretkey)[crypto_sign_SECRETKEYBYTES] = 0;
+
+       RETURN_STR(secretkey);
+}
+
+PHP_FUNCTION(sodium_crypto_sign_publickey)
+{
+       zend_string       *publickey;
+       unsigned char *keypair;
+       size_t             keypair_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
+                                                         &keypair, &keypair_len) == FAILURE) {
+               return;
+       }
+       if (keypair_len !=
+               crypto_sign_SECRETKEYBYTES + crypto_sign_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_publickey(): keypair should be "
+                                  "CRYPTO_SIGN_KEYPAIRBYTES bytes",
+                                  0);
+               return;
+       }
+       publickey = zend_string_alloc(crypto_sign_PUBLICKEYBYTES, 0);
+       memcpy(ZSTR_VAL(publickey), keypair + crypto_sign_SECRETKEYBYTES,
+                  crypto_sign_PUBLICKEYBYTES);
+       ZSTR_VAL(publickey)[crypto_sign_PUBLICKEYBYTES] = 0;
+
+       RETURN_STR(publickey);
+}
+
+PHP_FUNCTION(sodium_crypto_sign)
+{
+       zend_string                *msg_signed;
+       unsigned char      *msg;
+       unsigned char      *secretkey;
+       unsigned long long      msg_signed_real_len;
+       size_t                          msg_len;
+       size_t                          msg_signed_len;
+       size_t                          secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &msg, &msg_len,
+                                                         &secretkey, &secretkey_len) == FAILURE) {
+               return;
+       }
+       if (secretkey_len != crypto_sign_SECRETKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign(): secret key size should be "
+                                  "CRYPTO_SIGN_SECRETKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       if (SIZE_MAX - msg_len <= crypto_sign_BYTES) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       msg_signed_len = msg_len + crypto_sign_BYTES;
+       msg_signed = zend_string_alloc((size_t) msg_signed_len, 0);
+       if (crypto_sign((unsigned char *) ZSTR_VAL(msg_signed),
+                                       &msg_signed_real_len, msg,
+                                       (unsigned long long) msg_len, secretkey) != 0) {
+               zend_string_free(msg_signed);
+               zend_throw_exception(sodium_exception_ce, "crypto_sign(): internal error", 0);
+               return;
+       }
+       if (msg_signed_real_len <= 0U || msg_signed_real_len >= SIZE_MAX ||
+               msg_signed_real_len > msg_signed_len) {
+               zend_string_free(msg_signed);
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       PHP_SODIUM_ZSTR_TRUNCATE(msg_signed, (size_t) msg_signed_real_len);
+       ZSTR_VAL(msg_signed)[msg_signed_real_len] = 0;
+
+       RETURN_STR(msg_signed);
+}
+
+PHP_FUNCTION(sodium_crypto_sign_open)
+{
+       zend_string                *msg;
+       unsigned char      *msg_signed;
+       unsigned char      *publickey;
+       unsigned long long      msg_real_len;
+       size_t                          msg_len;
+       size_t                          msg_signed_len;
+       size_t                          publickey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &msg_signed, &msg_signed_len,
+                                                         &publickey, &publickey_len) == FAILURE) {
+               return;
+       }
+       if (publickey_len != crypto_sign_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_open(): public key size should be "
+                                  "CRYPTO_SIGN_PUBLICKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       msg_len = msg_signed_len;
+       if (msg_len >= SIZE_MAX) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       msg = zend_string_alloc((size_t) msg_len, 0);
+       if (crypto_sign_open((unsigned char *) ZSTR_VAL(msg), &msg_real_len,
+                                                msg_signed, (unsigned long long) msg_signed_len,
+                                                publickey) != 0) {
+               zend_string_free(msg);
+               RETURN_FALSE;
+       }
+       if (msg_real_len >= SIZE_MAX || msg_real_len > msg_signed_len) {
+               zend_string_free(msg);
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       PHP_SODIUM_ZSTR_TRUNCATE(msg, (size_t) msg_real_len);
+       ZSTR_VAL(msg)[msg_real_len] = 0;
+
+       RETURN_STR(msg);
+}
+
+PHP_FUNCTION(sodium_crypto_sign_detached)
+{
+       zend_string                *signature;
+       unsigned char      *msg;
+       unsigned char      *secretkey;
+       unsigned long long      signature_real_len;
+       size_t                          msg_len;
+       size_t                          secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &msg, &msg_len,
+                                                         &secretkey, &secretkey_len) == FAILURE) {
+               return;
+       }
+       if (secretkey_len != crypto_sign_SECRETKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_detached(): secret key size should be "
+                                  "CRYPTO_SIGN_SECRETKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       signature = zend_string_alloc((size_t) crypto_sign_BYTES, 0);
+       memset(ZSTR_VAL(signature), 0, (size_t) crypto_sign_BYTES);
+       if (crypto_sign_detached((unsigned char *) ZSTR_VAL(signature),
+                                                        &signature_real_len, msg,
+                                                        (unsigned long long) msg_len, secretkey) != 0) {
+               zend_string_free(signature);
+               zend_throw_exception(sodium_exception_ce, "crypto_sign_detached()", 0);
+               return;
+       }
+       if (signature_real_len <= 0U || signature_real_len > crypto_sign_BYTES) {
+               zend_string_free(signature);
+               zend_throw_exception(sodium_exception_ce, "signature has a bogus size", 0);
+               return;
+       }
+       ZEND_ASSERT(ZSTR_VAL(signature)[signature_real_len] == 0);
+
+       RETURN_STR(signature);
+}
+
+PHP_FUNCTION(sodium_crypto_sign_verify_detached)
+{
+       unsigned char *msg;
+       unsigned char *publickey;
+       unsigned char *signature;
+       size_t             msg_len;
+       size_t             publickey_len;
+       size_t             signature_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss",
+                                                         &signature, &signature_len,
+                                                         &msg, &msg_len,
+                                                         &publickey, &publickey_len) == FAILURE) {
+               return;
+       }
+       if (signature_len != crypto_sign_BYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_verify_detached(): signature size should be "
+                                  "CRYPTO_SIGN_BYTES bytes",
+                                  0);
+               return;
+       }
+       if (publickey_len != crypto_sign_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_verify_detached(): public key size should be "
+                                  "CRYPTO_SIGN_PUBLICKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       if (crypto_sign_verify_detached(signature,
+                                                                       msg, (unsigned long long) msg_len,
+                                                                       publickey) != 0) {
+               RETURN_FALSE;
+       }
+       RETURN_TRUE;
+}
+
+PHP_FUNCTION(sodium_crypto_stream)
+{
+       zend_string       *ciphertext;
+       unsigned char *key;
+       unsigned char *nonce;
+       zend_long          ciphertext_len;
+       size_t             key_len;
+       size_t             nonce_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "lss",
+                                                         &ciphertext_len,
+                                                         &nonce, &nonce_len,
+                                                         &key, &key_len) == FAILURE) {
+               return;
+       }
+       if (ciphertext_len <= 0 || ciphertext_len >= SIZE_MAX) {
+               zend_throw_exception(sodium_exception_ce, "crypto_stream(): invalid length", 0);
+               return;
+       }
+       if (nonce_len != crypto_stream_NONCEBYTES) {
+               zend_throw_exception(sodium_exception_ce, "nonce should be CRYPTO_STREAM_NONCEBYTES bytes", 0);
+               return;
+       }
+       if (key_len != crypto_stream_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce, "key should be CRYPTO_STREAM_KEYBYTES bytes", 0);
+               return;
+       }
+       ciphertext = zend_string_alloc((size_t) ciphertext_len, 0);
+       if (crypto_stream((unsigned char *) ZSTR_VAL(ciphertext),
+                                         (unsigned long long) ciphertext_len, nonce, key) != 0) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "crypto_stream(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(ciphertext)[ciphertext_len] = 0;
+
+       RETURN_STR(ciphertext);
+}
+
+PHP_FUNCTION(sodium_crypto_stream_xor)
+{
+       zend_string       *ciphertext;
+       unsigned char *key;
+       unsigned char *msg;
+       unsigned char *nonce;
+       size_t             ciphertext_len;
+       size_t             key_len;
+       size_t             msg_len;
+       size_t             nonce_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss",
+                                                         &msg, &msg_len,
+                                                         &nonce, &nonce_len,
+                                                         &key, &key_len) == FAILURE) {
+               return;
+       }
+       if (nonce_len != crypto_stream_NONCEBYTES) {
+               zend_throw_exception(sodium_exception_ce, "nonce should be CRYPTO_STREAM_NONCEBYTES bytes", 0);
+               return;
+       }
+       if (key_len != crypto_stream_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce, "key should be CRYPTO_STREAM_KEYBYTES bytes", 0);
+               return;
+       }
+       ciphertext_len = msg_len;
+       ciphertext = zend_string_alloc((size_t) ciphertext_len, 0);
+       if (crypto_stream_xor((unsigned char *) ZSTR_VAL(ciphertext), msg,
+                                                 (unsigned long long) msg_len, nonce, key) != 0) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "crypto_stream_xor(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(ciphertext)[ciphertext_len] = 0;
+
+       RETURN_STR(ciphertext);
+}
+
+PHP_FUNCTION(sodium_crypto_pwhash_scryptsalsa208sha256)
+{
+       zend_string       *hash;
+       unsigned char *salt;
+       char              *passwd;
+       zend_long          hash_len;
+       zend_long          memlimit;
+       zend_long          opslimit;
+       size_t             passwd_len;
+       size_t             salt_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "lssll",
+                                                         &hash_len,
+                                                         &passwd, &passwd_len,
+                                                         &salt, &salt_len,
+                                                         &opslimit, &memlimit) == FAILURE ||
+               hash_len <= 0 || hash_len >= SIZE_MAX ||
+               opslimit <= 0 || memlimit <= 0 || memlimit > SIZE_MAX) {
+               zend_throw_exception(sodium_exception_ce, "crypto_pwhash_scryptsalsa208sha256(): invalid parameters", 0);
+               return;
+       }
+       if (passwd_len <= 0) {
+               zend_error(E_WARNING, "empty password");
+       }
+       if (salt_len != crypto_pwhash_scryptsalsa208sha256_SALTBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "salt should be CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES bytes",
+                                  0);
+               return;
+       }
+       if (opslimit < crypto_pwhash_scryptsalsa208sha256_opslimit_interactive()) {
+               zend_error(E_WARNING,
+                                  "number of operations for the scrypt function is low");
+       }
+       if (memlimit < crypto_pwhash_scryptsalsa208sha256_memlimit_interactive()) {
+               zend_error(E_WARNING,
+                                  "maximum memory for the scrypt function is low");
+       }
+       hash = zend_string_alloc((size_t) hash_len, 0);
+       if (crypto_pwhash_scryptsalsa208sha256
+               ((unsigned char *) ZSTR_VAL(hash), (unsigned long long) hash_len,
+                passwd, (unsigned long long) passwd_len, salt,
+                (unsigned long long) opslimit, (size_t) memlimit) != 0) {
+               zend_string_free(hash);
+               zend_throw_exception(sodium_exception_ce, "crypto_pwhash_scryptsalsa208sha256(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(hash)[hash_len] = 0;
+
+       RETURN_STR(hash);
+}
+
+PHP_FUNCTION(sodium_crypto_pwhash_scryptsalsa208sha256_str)
+{
+       zend_string *hash_str;
+       char            *passwd;
+       zend_long        memlimit;
+       zend_long        opslimit;
+       size_t           passwd_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll",
+                                                         &passwd, &passwd_len,
+                                                         &opslimit, &memlimit) == FAILURE ||
+               opslimit <= 0 || memlimit <= 0 || memlimit > SIZE_MAX) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_pwhash_scryptsalsa208sha256_str(): invalid parameters",
+                                  0);
+               return;
+       }
+       if (passwd_len <= 0) {
+               zend_error(E_WARNING, "empty password");
+       }
+       if (opslimit < crypto_pwhash_scryptsalsa208sha256_opslimit_interactive()) {
+               zend_error(E_WARNING,
+                                  "number of operations for the scrypt function is low");
+       }
+       if (memlimit < crypto_pwhash_scryptsalsa208sha256_memlimit_interactive()) {
+               zend_error(E_WARNING,
+                                  "maximum memory for the scrypt function is low");
+       }
+       hash_str = zend_string_alloc
+               (crypto_pwhash_scryptsalsa208sha256_STRBYTES - 1, 0);
+       if (crypto_pwhash_scryptsalsa208sha256_str
+               (ZSTR_VAL(hash_str), passwd, (unsigned long long) passwd_len,
+                (unsigned long long) opslimit, (size_t) memlimit) != 0) {
+               zend_string_free(hash_str);
+               zend_throw_exception(sodium_exception_ce, "crypto_pwhash_scryptsalsa208sha256_str(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(hash_str)[crypto_pwhash_scryptsalsa208sha256_STRBYTES - 1] = 0;
+
+       RETURN_STR(hash_str);
+}
+
+PHP_FUNCTION(sodium_crypto_pwhash_scryptsalsa208sha256_str_verify)
+{
+       char      *hash_str;
+       char      *passwd;
+       size_t     hash_str_len;
+       size_t     passwd_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &hash_str, &hash_str_len,
+                                                         &passwd, &passwd_len) == FAILURE) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_pwhash_scryptsalsa208sha256_str_verify(): invalid parameters",
+                                  0);
+               return;
+       }
+       if (passwd_len <= 0) {
+               zend_error(E_WARNING, "empty password");
+       }
+       if (hash_str_len != crypto_pwhash_scryptsalsa208sha256_STRBYTES - 1) {
+               zend_error(E_WARNING, "wrong size for the hashed password");
+               RETURN_FALSE;
+       }
+       if (crypto_pwhash_scryptsalsa208sha256_str_verify
+               (hash_str, passwd, (unsigned long long) passwd_len) == 0) {
+               RETURN_TRUE;
+       }
+       RETURN_FALSE;
+}
+
+#ifdef crypto_pwhash_SALTBYTES
+PHP_FUNCTION(sodium_crypto_pwhash)
+{
+       zend_string       *hash;
+       unsigned char *salt;
+       char              *passwd;
+       zend_long          hash_len;
+       zend_long          memlimit;
+       zend_long          opslimit;
+       size_t             passwd_len;
+       size_t             salt_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "lssll",
+                                                         &hash_len,
+                                                         &passwd, &passwd_len,
+                                                         &salt, &salt_len,
+                                                         &opslimit, &memlimit) == FAILURE ||
+               hash_len <= 0 || hash_len >= SIZE_MAX ||
+               opslimit <= 0 || memlimit <= 0 || memlimit > SIZE_MAX) {
+               zend_throw_exception(sodium_exception_ce, "crypto_pwhash(): invalid parameters", 0);
+               return;
+       }
+       if (passwd_len <= 0) {
+               zend_error(E_WARNING, "empty password");
+       }
+       if (salt_len != crypto_pwhash_SALTBYTES) {
+               zend_throw_exception(sodium_exception_ce, "salt should be CRYPTO_PWHASH_SALTBYTES bytes", 0);
+               return;
+       }
+       if (opslimit < crypto_pwhash_OPSLIMIT_INTERACTIVE) {
+               zend_error(E_WARNING,
+                                  "number of operations for the argon2i function is low");
+       }
+       if (memlimit < crypto_pwhash_MEMLIMIT_INTERACTIVE) {
+               zend_error(E_WARNING, "maximum memory for the argon2i function is low");
+       }
+       hash = zend_string_alloc((size_t) hash_len, 0);
+       if (crypto_pwhash
+               ((unsigned char *) ZSTR_VAL(hash), (unsigned long long) hash_len,
+                passwd, (unsigned long long) passwd_len, salt,
+                (unsigned long long) opslimit, (size_t) memlimit,
+                crypto_pwhash_alg_default()) != 0) {
+               zend_string_free(hash);
+               zend_throw_exception(sodium_exception_ce, "crypto_pwhash(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(hash)[hash_len] = 0;
+
+       RETURN_STR(hash);
+}
+
+PHP_FUNCTION(sodium_crypto_pwhash_str)
+{
+       zend_string *hash_str;
+       char            *passwd;
+       zend_long        memlimit;
+       zend_long        opslimit;
+       size_t           passwd_len;
+       size_t           len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll",
+                                                         &passwd, &passwd_len,
+                                                         &opslimit, &memlimit) == FAILURE ||
+               opslimit <= 0 || memlimit <= 0 || memlimit > SIZE_MAX) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_pwhash_str(): invalid parameters",
+                                  0);
+               return;
+       }
+       if (passwd_len <= 0) {
+               zend_error(E_WARNING, "empty password");
+       }
+       if (opslimit < crypto_pwhash_OPSLIMIT_INTERACTIVE) {
+               zend_error(E_WARNING,
+                                  "number of operations for the argon2i function is low");
+       }
+       if (memlimit < crypto_pwhash_MEMLIMIT_INTERACTIVE) {
+               zend_error(E_WARNING,
+                                  "maximum memory for the argon2i function is low");
+       }
+       hash_str = zend_string_alloc(crypto_pwhash_STRBYTES - 1, 0);
+       if (crypto_pwhash_str
+               (ZSTR_VAL(hash_str), passwd, (unsigned long long) passwd_len,
+                (unsigned long long) opslimit, (size_t) memlimit) != 0) {
+               zend_string_free(hash_str);
+               zend_throw_exception(sodium_exception_ce, "crypto_pwhash_str(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(hash_str)[crypto_pwhash_STRBYTES - 1] = 0;
+
+       len = strlen(ZSTR_VAL(hash_str));
+       PHP_SODIUM_ZSTR_TRUNCATE(hash_str, len);
+
+       RETURN_STR(hash_str);
+}
+
+PHP_FUNCTION(sodium_crypto_pwhash_str_verify)
+{
+       char      *hash_str;
+       char      *passwd;
+       size_t     hash_str_len;
+       size_t     passwd_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &hash_str, &hash_str_len,
+                                                         &passwd, &passwd_len) == FAILURE) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_pwhash_str_verify(): invalid parameters",
+                                  0);
+               return;
+       }
+       if (passwd_len <= 0) {
+               zend_error(E_WARNING, "empty password");
+       }
+       if (crypto_pwhash_str_verify
+               (hash_str, passwd, (unsigned long long) passwd_len) == 0) {
+               RETURN_TRUE;
+       }
+       RETURN_FALSE;
+}
+#endif
+
+PHP_FUNCTION(sodium_crypto_aead_aes256gcm_is_available)
+{
+#ifdef HAVE_AESGCM
+       RETURN_BOOL(crypto_aead_aes256gcm_is_available());
+#else
+       RETURN_FALSE;
+#endif
+}
+
+#ifdef HAVE_AESGCM
+PHP_FUNCTION(sodium_crypto_aead_aes256gcm_encrypt)
+{
+       zend_string                *ciphertext;
+       unsigned char      *ad;
+       unsigned char      *msg;
+       unsigned char      *npub;
+       unsigned char      *secretkey;
+       unsigned long long      ciphertext_real_len;
+       size_t                          ad_len;
+       size_t                          ciphertext_len;
+       size_t                          msg_len;
+       size_t                          npub_len;
+       size_t                          secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss",
+                                                         &msg, &msg_len,
+                                                         &ad, &ad_len,
+                                                         &npub, &npub_len,
+                                                         &secretkey, &secretkey_len) == FAILURE) {
+               return;
+       }
+       if (npub_len != crypto_aead_aes256gcm_NPUBBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_aes256gcm_encrypt(): "
+                                  "public nonce size should be "
+                                  "CRYPTO_AEAD_AES256GCM_NPUBBYTES bytes",
+                                  0);
+               return;
+       }
+       if (secretkey_len != crypto_aead_aes256gcm_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_aes256gcm_encrypt(): "
+                                  "secret key size should be "
+                                  "CRYPTO_AEAD_AES256GCM_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       if (SIZE_MAX - msg_len <= crypto_aead_aes256gcm_ABYTES) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       ciphertext_len = msg_len + crypto_aead_aes256gcm_ABYTES;
+       ciphertext = zend_string_alloc((size_t) ciphertext_len, 0);
+       if (crypto_aead_aes256gcm_encrypt
+               ((unsigned char *) ZSTR_VAL(ciphertext), &ciphertext_real_len, msg,
+                (unsigned long long) msg_len,
+                ad, (unsigned long long) ad_len, NULL, npub, secretkey) != 0) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "crypto_aead_aes256gcm_encrypt(): internal error", 0);
+               return;
+       }
+       if (ciphertext_real_len <= 0U || ciphertext_real_len >= SIZE_MAX ||
+               ciphertext_real_len > ciphertext_len) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       PHP_SODIUM_ZSTR_TRUNCATE(ciphertext, (size_t) ciphertext_real_len);
+       ZSTR_VAL(ciphertext)[ciphertext_real_len] = 0;
+
+       RETURN_STR(ciphertext);
+}
+
+PHP_FUNCTION(sodium_crypto_aead_aes256gcm_decrypt)
+{
+       zend_string                *msg;
+       unsigned char      *ad;
+       unsigned char      *ciphertext;
+       unsigned char      *npub;
+       unsigned char      *secretkey;
+       unsigned long long      msg_real_len;
+       size_t                          ad_len;
+       size_t                          ciphertext_len;
+       size_t                          msg_len;
+       size_t                          npub_len;
+       size_t                          secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss",
+                                                         &ciphertext, &ciphertext_len,
+                                                         &ad, &ad_len,
+                                                         &npub, &npub_len,
+                                                         &secretkey, &secretkey_len) == FAILURE) {
+               return;
+       }
+       if (npub_len != crypto_aead_aes256gcm_NPUBBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_aes256gcm_decrypt(): "
+                                  "public nonce size should be "
+                                  "CRYPTO_AEAD_AES256GCM_NPUBBYTES bytes",
+                                  0);
+               return;
+       }
+       if (secretkey_len != crypto_aead_aes256gcm_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_aes256gcm_decrypt(): "
+                                  "secret key size should be "
+                                  "CRYPTO_AEAD_AES256GCM_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       msg_len = ciphertext_len;
+       if (msg_len >= SIZE_MAX) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       msg = zend_string_alloc((size_t) msg_len, 0);
+       if (ciphertext_len < crypto_aead_aes256gcm_ABYTES ||
+               crypto_aead_aes256gcm_decrypt
+               ((unsigned char *) ZSTR_VAL(msg), &msg_real_len, NULL,
+                ciphertext, (unsigned long long) ciphertext_len,
+                ad, (unsigned long long) ad_len, npub, secretkey) != 0) {
+               zend_string_free(msg);
+               RETURN_FALSE;
+       }
+       if (msg_real_len >= SIZE_MAX || msg_real_len > msg_len) {
+               zend_string_free(msg);
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       PHP_SODIUM_ZSTR_TRUNCATE(msg, (size_t) msg_real_len);
+       ZSTR_VAL(msg)[msg_real_len] = 0;
+
+       RETURN_STR(msg);
+}
+#endif
+
+PHP_FUNCTION(sodium_crypto_aead_chacha20poly1305_encrypt)
+{
+       zend_string                *ciphertext;
+       unsigned char      *ad;
+       unsigned char      *msg;
+       unsigned char      *npub;
+       unsigned char      *secretkey;
+       unsigned long long      ciphertext_real_len;
+       size_t                          ad_len;
+       size_t                          ciphertext_len;
+       size_t                          msg_len;
+       size_t                          npub_len;
+       size_t                          secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss",
+                                                         &msg, &msg_len,
+                                                         &ad, &ad_len,
+                                                         &npub, &npub_len,
+                                                         &secretkey, &secretkey_len) == FAILURE) {
+               return;
+       }
+       if (npub_len != crypto_aead_chacha20poly1305_NPUBBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_chacha20poly1305_encrypt(): "
+                                  "public nonce size should be "
+                                  "CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES bytes",
+                                  0);
+               return;
+       }
+       if (secretkey_len != crypto_aead_chacha20poly1305_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_chacha20poly1305_encrypt(): "
+                                  "secret key size should be "
+                                  "CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       if (SIZE_MAX - msg_len <= crypto_aead_chacha20poly1305_ABYTES) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       ciphertext_len = msg_len + crypto_aead_chacha20poly1305_ABYTES;
+       ciphertext = zend_string_alloc((size_t) ciphertext_len, 0);
+       if (crypto_aead_chacha20poly1305_encrypt
+               ((unsigned char *) ZSTR_VAL(ciphertext), &ciphertext_real_len, msg,
+                (unsigned long long) msg_len,
+                ad, (unsigned long long) ad_len, NULL, npub, secretkey) != 0) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "crypto_aead_chacha20poly1305_encrypt(): internal error", 0);
+               return;
+       }
+       if (ciphertext_real_len <= 0U || ciphertext_real_len >= SIZE_MAX ||
+               ciphertext_real_len > ciphertext_len) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       PHP_SODIUM_ZSTR_TRUNCATE(ciphertext, (size_t) ciphertext_real_len);
+       ZSTR_VAL(ciphertext)[ciphertext_real_len] = 0;
+
+       RETURN_STR(ciphertext);
+}
+
+PHP_FUNCTION(sodium_crypto_aead_chacha20poly1305_decrypt)
+{
+       zend_string                *msg;
+       unsigned char      *ad;
+       unsigned char      *ciphertext;
+       unsigned char      *npub;
+       unsigned char      *secretkey;
+       unsigned long long      msg_real_len;
+       size_t                          ad_len;
+       size_t                          ciphertext_len;
+       size_t                          msg_len;
+       size_t                          npub_len;
+       size_t                          secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss",
+                                                         &ciphertext, &ciphertext_len,
+                                                         &ad, &ad_len,
+                                                         &npub, &npub_len,
+                                                         &secretkey, &secretkey_len) == FAILURE) {
+               return;
+       }
+       if (npub_len != crypto_aead_chacha20poly1305_NPUBBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_chacha20poly1305_decrypt(): "
+                                  "public nonce size should be "
+                                  "CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES bytes",
+                                  0);
+               return;
+       }
+       if (secretkey_len != crypto_aead_chacha20poly1305_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_chacha20poly1305_decrypt(): "
+                                  "secret key size should be "
+                                  "CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       msg_len = ciphertext_len;
+       if (msg_len >= SIZE_MAX) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       msg = zend_string_alloc((size_t) msg_len, 0);
+       if (ciphertext_len < crypto_aead_chacha20poly1305_ABYTES ||
+               crypto_aead_chacha20poly1305_decrypt
+               ((unsigned char *) ZSTR_VAL(msg), &msg_real_len, NULL,
+                ciphertext, (unsigned long long) ciphertext_len,
+                ad, (unsigned long long) ad_len, npub, secretkey) != 0) {
+               zend_string_free(msg);
+               RETURN_FALSE;
+       }
+       if (msg_real_len >= SIZE_MAX || msg_real_len > msg_len) {
+               zend_string_free(msg);
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       PHP_SODIUM_ZSTR_TRUNCATE(msg, (size_t) msg_real_len);
+       ZSTR_VAL(msg)[msg_real_len] = 0;
+
+       RETURN_STR(msg);
+}
+
+#ifdef crypto_aead_chacha20poly1305_IETF_NPUBBYTES
+PHP_FUNCTION(sodium_crypto_aead_chacha20poly1305_ietf_encrypt)
+{
+       zend_string                *ciphertext;
+       unsigned char      *ad;
+       unsigned char      *msg;
+       unsigned char      *npub;
+       unsigned char      *secretkey;
+       unsigned long long      ciphertext_real_len;
+       size_t                          ad_len;
+       size_t                          ciphertext_len;
+       size_t                          msg_len;
+       size_t                          npub_len;
+       size_t                          secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss",
+                                                         &msg, &msg_len,
+                                                         &ad, &ad_len,
+                                                         &npub, &npub_len,
+                                                         &secretkey, &secretkey_len) == FAILURE) {
+               return;
+       }
+       if (npub_len != crypto_aead_chacha20poly1305_IETF_NPUBBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_chacha20poly1305_ietf_encrypt(): "
+                                  "public nonce size should be "
+                                  "CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES bytes",
+                                  0);
+               return;
+       }
+       if (secretkey_len != crypto_aead_chacha20poly1305_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_chacha20poly1305_ietf_encrypt(): "
+                                  "secret key size should be "
+                                  "CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       if (SIZE_MAX - msg_len <= crypto_aead_chacha20poly1305_ABYTES) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       if ((unsigned long long) msg_len > 64ULL * (1ULL << 32) - 64ULL) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       ciphertext_len = msg_len + crypto_aead_chacha20poly1305_ABYTES;
+       ciphertext = zend_string_alloc((size_t) ciphertext_len, 0);
+       if (crypto_aead_chacha20poly1305_ietf_encrypt
+               ((unsigned char *) ZSTR_VAL(ciphertext), &ciphertext_real_len, msg,
+                (unsigned long long) msg_len,
+                ad, (unsigned long long) ad_len, NULL, npub, secretkey) != 0) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "crypto_aead_chacha20poly1305_ietf_encrypt(): internal error", 0);
+               return;
+       }
+       if (ciphertext_real_len <= 0U || ciphertext_real_len >= SIZE_MAX ||
+               ciphertext_real_len > ciphertext_len) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       PHP_SODIUM_ZSTR_TRUNCATE(ciphertext, (size_t) ciphertext_real_len);
+       ZSTR_VAL(ciphertext)[ciphertext_real_len] = 0;
+
+       RETURN_STR(ciphertext);
+}
+
+PHP_FUNCTION(sodium_crypto_aead_chacha20poly1305_ietf_decrypt)
+{
+       zend_string                *msg;
+       unsigned char      *ad;
+       unsigned char      *ciphertext;
+       unsigned char      *npub;
+       unsigned char      *secretkey;
+       unsigned long long      msg_real_len;
+       size_t                          ad_len;
+       size_t                          ciphertext_len;
+       size_t                          msg_len;
+       size_t                          npub_len;
+       size_t                          secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss",
+                                                         &ciphertext, &ciphertext_len,
+                                                         &ad, &ad_len,
+                                                         &npub, &npub_len,
+                                                         &secretkey, &secretkey_len) == FAILURE) {
+               return;
+       }
+       if (npub_len != crypto_aead_chacha20poly1305_IETF_NPUBBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_chacha20poly1305_ietf_decrypt(): "
+                                  "public nonce size should be "
+                                  "CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES bytes",
+                                  0);
+               return;
+       }
+       if (secretkey_len != crypto_aead_chacha20poly1305_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_chacha20poly1305_ietf_decrypt(): "
+                                  "secret key size should be "
+                                  "CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       msg_len = ciphertext_len;
+       if (msg_len >= SIZE_MAX) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       if ((unsigned long long) ciphertext_len -
+               crypto_aead_chacha20poly1305_ABYTES > 64ULL * (1ULL << 32) - 64ULL) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       msg = zend_string_alloc((size_t) msg_len, 0);
+       if (ciphertext_len < crypto_aead_chacha20poly1305_ABYTES ||
+               crypto_aead_chacha20poly1305_ietf_decrypt
+               ((unsigned char *) ZSTR_VAL(msg), &msg_real_len, NULL,
+                ciphertext, (unsigned long long) ciphertext_len,
+                ad, (unsigned long long) ad_len, npub, secretkey) != 0) {
+               zend_string_free(msg);
+               RETURN_FALSE;
+       }
+       if (msg_real_len >= SIZE_MAX || msg_real_len > msg_len) {
+               zend_string_free(msg);
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       PHP_SODIUM_ZSTR_TRUNCATE(msg, (size_t) msg_real_len);
+       ZSTR_VAL(msg)[msg_real_len] = 0;
+
+       RETURN_STR(msg);
+}
+#endif
+
+#ifdef crypto_aead_xchacha20poly1305_IETF_NPUBBYTES
+PHP_FUNCTION(sodium_crypto_aead_xchacha20poly1305_ietf_encrypt)
+{
+       zend_string                *ciphertext;
+       unsigned char      *ad;
+       unsigned char      *msg;
+       unsigned char      *npub;
+       unsigned char      *secretkey;
+       unsigned long long      ciphertext_real_len;
+       size_t                          ad_len;
+       size_t                          ciphertext_len;
+       size_t                          msg_len;
+       size_t                          npub_len;
+       size_t                          secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss",
+                                                         &msg, &msg_len,
+                                                         &ad, &ad_len,
+                                                         &npub, &npub_len,
+                                                         &secretkey, &secretkey_len) == FAILURE) {
+               return;
+       }
+       if (npub_len != crypto_aead_xchacha20poly1305_IETF_NPUBBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_xchacha20poly1305_ietf_encrypt(): "
+                                  "public nonce size should be "
+                                  "CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES bytes",
+                                  0);
+               return;
+       }
+       if (secretkey_len != crypto_aead_xchacha20poly1305_IETF_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_xchacha20poly1305_ietf_encrypt(): "
+                                  "secret key size should be "
+                                  "CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       if (SIZE_MAX - msg_len <= crypto_aead_xchacha20poly1305_IETF_ABYTES) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       if ((unsigned long long) msg_len > 64ULL * (1ULL << 32) - 64ULL) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       ciphertext_len = msg_len + crypto_aead_xchacha20poly1305_IETF_ABYTES;
+       ciphertext = zend_string_alloc((size_t) ciphertext_len, 0);
+       if (crypto_aead_xchacha20poly1305_ietf_encrypt
+               ((unsigned char *) ZSTR_VAL(ciphertext), &ciphertext_real_len, msg,
+                (unsigned long long) msg_len,
+                ad, (unsigned long long) ad_len, NULL, npub, secretkey) != 0) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "crypto_aead_xchacha20poly1305_ietf_encrypt(): internal error", 0);
+               return;
+       }
+       if (ciphertext_real_len <= 0U || ciphertext_real_len >= SIZE_MAX ||
+               ciphertext_real_len > ciphertext_len) {
+               zend_string_free(ciphertext);
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       PHP_SODIUM_ZSTR_TRUNCATE(ciphertext, (size_t) ciphertext_real_len);
+       ZSTR_VAL(ciphertext)[ciphertext_real_len] = 0;
+
+       RETURN_STR(ciphertext);
+}
+
+PHP_FUNCTION(sodium_crypto_aead_xchacha20poly1305_ietf_decrypt)
+{
+       zend_string                *msg;
+       unsigned char      *ad;
+       unsigned char      *ciphertext;
+       unsigned char      *npub;
+       unsigned char      *secretkey;
+       unsigned long long      msg_real_len;
+       size_t                          ad_len;
+       size_t                          ciphertext_len;
+       size_t                          msg_len;
+       size_t                          npub_len;
+       size_t                          secretkey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss",
+                                                         &ciphertext, &ciphertext_len,
+                                                         &ad, &ad_len,
+                                                         &npub, &npub_len,
+                                                         &secretkey, &secretkey_len) == FAILURE) {
+               return;
+       }
+       if (npub_len != crypto_aead_xchacha20poly1305_IETF_NPUBBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_xchacha20poly1305_ietf_decrypt(): "
+                                  "public nonce size should be "
+                                  "CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES bytes",
+                                  0);
+               return;
+       }
+       if (secretkey_len != crypto_aead_xchacha20poly1305_IETF_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_aead_xchacha20poly1305_ietf_decrypt(): "
+                                  "secret key size should be "
+                                  "CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES bytes",
+                                  0);
+               return;
+       }
+       msg_len = ciphertext_len;
+       if (msg_len >= SIZE_MAX) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       if ((unsigned long long) ciphertext_len -
+               crypto_aead_xchacha20poly1305_IETF_ABYTES > 64ULL * (1ULL << 32) - 64ULL) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       msg = zend_string_alloc((size_t) msg_len, 0);
+       if (ciphertext_len < crypto_aead_xchacha20poly1305_IETF_ABYTES ||
+               crypto_aead_xchacha20poly1305_ietf_decrypt
+               ((unsigned char *) ZSTR_VAL(msg), &msg_real_len, NULL,
+                ciphertext, (unsigned long long) ciphertext_len,
+                ad, (unsigned long long) ad_len, npub, secretkey) != 0) {
+               zend_string_free(msg);
+               RETURN_FALSE;
+       }
+       if (msg_real_len >= SIZE_MAX || msg_real_len > msg_len) {
+               zend_string_free(msg);
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       PHP_SODIUM_ZSTR_TRUNCATE(msg, (size_t) msg_real_len);
+       ZSTR_VAL(msg)[msg_real_len] = 0;
+
+       RETURN_STR(msg);
+}
+#endif
+
+PHP_FUNCTION(sodium_bin2hex)
+{
+       zend_string       *hex;
+       unsigned char *bin;
+       size_t             bin_len;
+       size_t             hex_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
+                                                         &bin, &bin_len) == FAILURE) {
+               return;
+       }
+       if (bin_len >= SIZE_MAX / 2U) {
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       hex_len = bin_len * 2U;
+       hex = zend_string_alloc((size_t) hex_len, 0);
+       sodium_bin2hex(ZSTR_VAL(hex), hex_len + 1U, bin, bin_len);
+       ZSTR_VAL(hex)[hex_len] = 0;
+
+       RETURN_STR(hex);
+}
+
+PHP_FUNCTION(sodium_hex2bin)
+{
+       zend_string       *bin;
+       char              *hex;
+       char              *ignore = NULL;
+       size_t             bin_real_len;
+       size_t             bin_len;
+       size_t             hex_len;
+       size_t             ignore_len = 0;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s",
+                                                         &hex, &hex_len,
+                                                         &ignore, &ignore_len) == FAILURE) {
+               return;
+       }
+       bin_len = hex_len / 2;
+       bin = zend_string_alloc(bin_len, 0);
+       if (sodium_hex2bin((unsigned char *) ZSTR_VAL(bin), bin_len, hex, hex_len,
+                                          ignore, &bin_real_len, NULL) != 0 ||
+               bin_real_len >= SIZE_MAX || bin_real_len > bin_len) {
+               zend_string_free(bin);
+               zend_throw_exception(sodium_exception_ce, "arithmetic overflow", 0);
+               return;
+       }
+       PHP_SODIUM_ZSTR_TRUNCATE(bin, (size_t) bin_real_len);
+       ZSTR_VAL(bin)[bin_real_len] = 0;
+
+       RETURN_STR(bin);
+}
+
+PHP_FUNCTION(sodium_crypto_scalarmult)
+{
+       zend_string       *q;
+       unsigned char *n;
+       unsigned char *p;
+       size_t             n_len;
+       size_t             p_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &n, &n_len, &p, &p_len) == FAILURE) {
+               return;
+       }
+       if (n_len != crypto_scalarmult_SCALARBYTES ||
+               p_len != crypto_scalarmult_SCALARBYTES) {
+               zend_throw_exception(sodium_exception_ce, "crypto_scalarmult(): scalar and point must be "
+                                  "CRYPTO_SCALARMULT_SCALARBYTES bytes",
+                                  0);
+               return;
+       }
+       q = zend_string_alloc(crypto_scalarmult_BYTES, 0);
+       if (crypto_scalarmult((unsigned char *) ZSTR_VAL(q), n, p) != 0) {
+               zend_string_free(q);
+               zend_throw_exception(sodium_exception_ce, "crypto_scalarmult(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(q)[crypto_scalarmult_BYTES] = 0;
+
+       RETURN_STR(q);
+}
+
+PHP_FUNCTION(sodium_crypto_kx)
+{
+       crypto_generichash_state h;
+       unsigned char                    q[crypto_scalarmult_BYTES];
+       zend_string                             *sharedkey;
+       unsigned char                   *client_publickey;
+       unsigned char                   *publickey;
+       unsigned char                   *secretkey;
+       unsigned char                   *server_publickey;
+       size_t                                   client_publickey_len;
+       size_t                                   publickey_len;
+       size_t                                   secretkey_len;
+       size_t                                   server_publickey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss",
+                                                         &secretkey, &secretkey_len,
+                                                         &publickey, &publickey_len,
+                                                         &client_publickey, &client_publickey_len,
+                                                         &server_publickey, &server_publickey_len) == FAILURE) {
+               return;
+       }
+       if (secretkey_len != crypto_kx_SECRETKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce, "crypto_kx(): secret key must be CRYPTO_KX_SECRETKEY bytes", 0);
+               return;
+       }
+       if (publickey_len != crypto_kx_PUBLICKEYBYTES ||
+               client_publickey_len != crypto_kx_PUBLICKEYBYTES ||
+               server_publickey_len != crypto_kx_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce, "crypto_kx(): public keys must be CRYPTO_KX_PUBLICKEY bytes", 0);
+               return;
+       }
+       (void) sizeof(int[crypto_scalarmult_SCALARBYTES ==
+                                         crypto_kx_PUBLICKEYBYTES ? 1 : -1]);
+       (void) sizeof(int[crypto_scalarmult_SCALARBYTES ==
+                                         crypto_kx_SECRETKEYBYTES ? 1 : -1]);
+       if (crypto_scalarmult(q, secretkey, publickey) != 0) {
+               zend_throw_exception(sodium_exception_ce, "crypto_kx(): internal error", 0);
+               return;
+       }
+       sharedkey = zend_string_alloc(crypto_kx_BYTES, 0);
+       crypto_generichash_init(&h, NULL, 0U, crypto_generichash_BYTES);
+       crypto_generichash_update(&h, q, sizeof q);
+       sodium_memzero(q, sizeof q);
+       crypto_generichash_update(&h, client_publickey, client_publickey_len);
+       crypto_generichash_update(&h, server_publickey, server_publickey_len);
+       crypto_generichash_final(&h, (unsigned char *) ZSTR_VAL(sharedkey),
+                                                        crypto_kx_BYTES);
+       ZSTR_VAL(sharedkey)[crypto_kx_BYTES] = 0;
+
+       RETURN_STR(sharedkey);
+}
+
+PHP_FUNCTION(sodium_crypto_auth)
+{
+       zend_string *mac;
+       char            *key;
+       char            *msg;
+       size_t           msg_len;
+       size_t           key_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &msg, &msg_len,
+                                                         &key, &key_len) == FAILURE) {
+               return;
+       }
+       if (key_len != crypto_auth_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce, "crypto_auth(): key must be CRYPTO_AUTH_KEYBYTES bytes", 0);
+               return;
+       }
+       mac = zend_string_alloc(crypto_auth_BYTES, 0);
+       if (crypto_auth((unsigned char *) ZSTR_VAL(mac),
+                                       (const unsigned char *) msg, msg_len,
+                                       (const unsigned char *) key) != 0) {
+               zend_throw_exception(sodium_exception_ce, "crypto_auth(): internal error", 0);
+               return;
+       }
+       ZSTR_VAL(mac)[crypto_auth_BYTES] = 0;
+
+       RETURN_STR(mac);
+}
+
+PHP_FUNCTION(sodium_crypto_auth_verify)
+{
+       char      *mac;
+       char      *key;
+       char      *msg;
+       size_t     mac_len;
+       size_t     msg_len;
+       size_t     key_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss",
+                                                         &mac, &mac_len,
+                                                         &msg, &msg_len,
+                                                         &key, &key_len) == FAILURE) {
+               return;
+       }
+       if (key_len != crypto_auth_KEYBYTES) {
+               zend_throw_exception(sodium_exception_ce, "crypto_auth_verify(): key must be CRYPTO_AUTH_KEYBYTES bytes", 0);
+               return;
+       }
+       if (mac_len != crypto_auth_BYTES) {
+               zend_throw_exception(sodium_exception_ce, "crypto_auth_verify(): authentication tag must be CRYPTO_AUTH_BYTES bytes", 0);
+               return;
+       }
+       if (crypto_auth_verify((const unsigned char *) mac,
+                                                  (const unsigned char *) msg, msg_len,
+                                                  (const unsigned char *) key) != 0) {
+               RETURN_FALSE;
+       }
+       RETURN_TRUE;
+}
+
+PHP_FUNCTION(sodium_crypto_sign_ed25519_sk_to_curve25519)
+{
+       zend_string *ecdhkey;
+       char            *eddsakey;
+       size_t           eddsakey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
+                                                         &eddsakey, &eddsakey_len) == FAILURE) {
+               return;
+       }
+       if (eddsakey_len != crypto_sign_SECRETKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_ed25519_sk_to_curve25519(): "
+                                  "Ed25519 key should be CRYPTO_SIGN_SECRETKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       ecdhkey = zend_string_alloc(crypto_box_SECRETKEYBYTES, 0);
+
+       if (crypto_sign_ed25519_sk_to_curve25519((unsigned char *) ZSTR_VAL(ecdhkey),
+                                                                                        (const unsigned char *) eddsakey) != 0) {
+               zend_throw_exception(sodium_exception_ce, "crypto_sign_ed25519_sk_to_curve25519()", 0);
+               return;
+       }
+       ZSTR_VAL(ecdhkey)[crypto_box_SECRETKEYBYTES] = 0;
+
+       RETURN_STR(ecdhkey);
+}
+
+PHP_FUNCTION(sodium_crypto_sign_ed25519_pk_to_curve25519)
+{
+       zend_string *ecdhkey;
+       char            *eddsakey;
+       size_t           eddsakey_len;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
+                                                         &eddsakey, &eddsakey_len) == FAILURE) {
+               return;
+       }
+       if (eddsakey_len != crypto_sign_PUBLICKEYBYTES) {
+               zend_throw_exception(sodium_exception_ce,
+                                  "crypto_sign_ed25519_pk_to_curve25519(): "
+                                  "Ed25519 key should be CRYPTO_SIGN_PUBLICKEYBYTES bytes",
+                                  0);
+               return;
+       }
+       ecdhkey = zend_string_alloc(crypto_sign_PUBLICKEYBYTES, 0);
+
+       if (crypto_sign_ed25519_pk_to_curve25519((unsigned char *) ZSTR_VAL(ecdhkey),
+                                                                                        (const unsigned char *) eddsakey) != 0) {
+               zend_throw_exception(sodium_exception_ce, "crypto_sign_ed25519_pk_to_curve25519()", 0);
+               return;
+       }
+       ZSTR_VAL(ecdhkey)[crypto_box_PUBLICKEYBYTES] = 0;
+
+       RETURN_STR(ecdhkey);
+}
+
+#if SODIUM_LIBRARY_VERSION_MAJOR > 7 || \
+       (SODIUM_LIBRARY_VERSION_MAJOR == 7 && SODIUM_LIBRARY_VERSION_MINOR >= 6)
+PHP_FUNCTION(sodium_compare)
+{
+       char      *buf1;
+       char      *buf2;
+       size_t     len1;
+       size_t     len2;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
+                                                         &buf1, &len1,
+                                                         &buf2, &len2) == FAILURE) {
+               return;
+       }
+       if (len1 != len2) {
+               zend_throw_exception(sodium_exception_ce, "compare(): arguments have different sizes", 0);
+       } else {
+               RETURN_LONG(sodium_compare((const unsigned char *) buf1,
+                                                                  (const unsigned char *) buf2, (size_t) len1));
+       }
+}
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 tw=78 fdm=marker
+ * vim<600: sw=4 ts=4 tw=78
+ */
diff --git a/ext/sodium/libsodium.php b/ext/sodium/libsodium.php
new file mode 100644 (file)
index 0000000..bb18faf
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+$br = (php_sapi_name() == "cli")? "":"<br>";
+
+if(!extension_loaded('libsodium')) {
+        dl('libsodium.' . PHP_SHLIB_SUFFIX);
+}
+$module = 'libsodium';
+$functions = get_extension_funcs($module);
+echo "Functions available in the test extension:$br\n";
+foreach($functions as $func) {
+    echo $func."$br\n";
+}
+echo "$br\n";
+$function = 'sodium_memzero';
+$exit = 0;
+if (extension_loaded($module)) {
+        $str = $function($module);
+} else {
+        $str = "Module $module is not compiled into PHP";
+        $exit = 255;
+}
+echo "$str\n";
+exit($exit);
+?>
diff --git a/ext/sodium/php_libsodium.h b/ext/sodium/php_libsodium.h
new file mode 100644 (file)
index 0000000..3348c41
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 7                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2017 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors: Andi Gutmans <andi@zend.com>                                |
+   |          Zeev Suraski <zeev@zend.com>                                |
+   |          Rasmus Lerdorf <rasmus@php.net>                             |
+   |          Andrei Zmievski <andrei@php.net>                            |
+   |          Stig Venaas <venaas@php.net>                                |
+   |          Jason Greene <jason@php.net>                                |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_LIBSODIUM_H
+#define PHP_LIBSODIUM_H
+
+extern zend_module_entry sodium_module_entry;
+#define phpext_sodium_ptr &sodium_module_entry
+
+#define PHP_SODIUM_VERSION PHP_VERSION
+
+#ifdef ZTS
+# include "TSRM.h"
+#endif
+
+PHP_MINIT_FUNCTION(sodium);
+PHP_MSHUTDOWN_FUNCTION(sodium);
+PHP_RINIT_FUNCTION(sodium);
+PHP_RSHUTDOWN_FUNCTION(sodium);
+PHP_MINFO_FUNCTION(sodium);
+
+PHP_FUNCTION(sodium_crypto_aead_aes256gcm_is_available);
+PHP_FUNCTION(sodium_crypto_aead_aes256gcm_decrypt);
+PHP_FUNCTION(sodium_crypto_aead_aes256gcm_encrypt);
+PHP_FUNCTION(sodium_crypto_aead_chacha20poly1305_decrypt);
+PHP_FUNCTION(sodium_crypto_aead_chacha20poly1305_encrypt);
+PHP_FUNCTION(sodium_crypto_aead_chacha20poly1305_ietf_decrypt);
+PHP_FUNCTION(sodium_crypto_aead_chacha20poly1305_ietf_encrypt);
+PHP_FUNCTION(sodium_crypto_aead_xchacha20poly1305_ietf_decrypt);
+PHP_FUNCTION(sodium_crypto_aead_xchacha20poly1305_ietf_encrypt);
+PHP_FUNCTION(sodium_crypto_auth);
+PHP_FUNCTION(sodium_crypto_auth_verify);
+PHP_FUNCTION(sodium_crypto_box);
+PHP_FUNCTION(sodium_crypto_box_keypair);
+PHP_FUNCTION(sodium_crypto_box_seed_keypair);
+PHP_FUNCTION(sodium_crypto_box_keypair_from_secretkey_and_publickey);
+PHP_FUNCTION(sodium_crypto_box_open);
+PHP_FUNCTION(sodium_crypto_box_publickey);
+PHP_FUNCTION(sodium_crypto_box_publickey_from_secretkey);
+PHP_FUNCTION(sodium_crypto_box_seal);
+PHP_FUNCTION(sodium_crypto_box_seal_open);
+PHP_FUNCTION(sodium_crypto_box_secretkey);
+PHP_FUNCTION(sodium_crypto_generichash);
+PHP_FUNCTION(sodium_crypto_generichash_final);
+PHP_FUNCTION(sodium_crypto_generichash_init);
+PHP_FUNCTION(sodium_crypto_generichash_update);
+PHP_FUNCTION(sodium_crypto_kx);
+PHP_FUNCTION(sodium_crypto_pwhash);
+PHP_FUNCTION(sodium_crypto_pwhash_str);
+PHP_FUNCTION(sodium_crypto_pwhash_str_verify);
+PHP_FUNCTION(sodium_crypto_pwhash_scryptsalsa208sha256);
+PHP_FUNCTION(sodium_crypto_pwhash_scryptsalsa208sha256_str);
+PHP_FUNCTION(sodium_crypto_pwhash_scryptsalsa208sha256_str_verify);
+PHP_FUNCTION(sodium_crypto_scalarmult);
+PHP_FUNCTION(sodium_crypto_scalarmult_base);
+PHP_FUNCTION(sodium_crypto_secretbox);
+PHP_FUNCTION(sodium_crypto_secretbox_open);
+PHP_FUNCTION(sodium_crypto_shorthash);
+PHP_FUNCTION(sodium_crypto_sign);
+PHP_FUNCTION(sodium_crypto_sign_detached);
+PHP_FUNCTION(sodium_crypto_sign_ed25519_pk_to_curve25519);
+PHP_FUNCTION(sodium_crypto_sign_ed25519_sk_to_curve25519);
+PHP_FUNCTION(sodium_crypto_sign_keypair);
+PHP_FUNCTION(sodium_crypto_sign_keypair_from_secretkey_and_publickey);
+PHP_FUNCTION(sodium_crypto_sign_open);
+PHP_FUNCTION(sodium_crypto_sign_publickey);
+PHP_FUNCTION(sodium_crypto_sign_publickey_from_secretkey);
+PHP_FUNCTION(sodium_crypto_sign_secretkey);
+PHP_FUNCTION(sodium_crypto_sign_seed_keypair);
+PHP_FUNCTION(sodium_crypto_sign_verify_detached);
+PHP_FUNCTION(sodium_crypto_stream);
+PHP_FUNCTION(sodium_crypto_stream_xor);
+PHP_FUNCTION(sodium_randombytes_buf);
+PHP_FUNCTION(sodium_randombytes_random16);
+PHP_FUNCTION(sodium_randombytes_uniform);
+PHP_FUNCTION(sodium_bin2hex);
+PHP_FUNCTION(sodium_compare);
+PHP_FUNCTION(sodium_hex2bin);
+PHP_FUNCTION(sodium_increment);
+PHP_FUNCTION(sodium_add);
+PHP_FUNCTION(sodium_memcmp);
+PHP_FUNCTION(sodium_memzero);
+
+#define crypto_kx_BYTES crypto_scalarmult_BYTES
+#define crypto_kx_PUBLICKEYBYTES crypto_scalarmult_SCALARBYTES
+#define crypto_kx_SECRETKEYBYTES crypto_scalarmult_SCALARBYTES
+
+#endif /* PHP_LIBSODIUM_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/sodium/tests/crypto_aead.phpt b/ext/sodium/tests/crypto_aead.phpt
new file mode 100644 (file)
index 0000000..5c4a51d
--- /dev/null
@@ -0,0 +1,135 @@
+--TEST--
+Check for libsodium AEAD
+--SKIPIF--
+<?php
+if (!extension_loaded("sodium")) print "skip extension not loaded";
+if (!defined('SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES')) print "skip libsodium without AESGCM";
+?>
+--FILE--
+<?php
+echo "aead_chacha20poly1305:\n";
+
+$msg = random_bytes(random_int(1, 1000));
+$nonce = random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES);
+$key = random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES);
+$ad = random_bytes(random_int(1, 1000));
+
+$ciphertext = sodium_crypto_aead_chacha20poly1305_encrypt($msg, $ad, $nonce, $key);
+$msg2 = sodium_crypto_aead_chacha20poly1305_decrypt($ciphertext, $ad, $nonce, $key);
+var_dump($ciphertext !== $msg);
+var_dump($msg === $msg2);
+var_dump(sodium_crypto_aead_chacha20poly1305_decrypt($ciphertext, 'x' . $ad, $nonce, $key));
+try {
+       // Switched order
+       $msg2 = sodium_crypto_aead_chacha20poly1305_decrypt($ciphertext, $ad, $key, $nonce);
+       var_dump(false);
+} catch (SodiumException $ex) {
+       var_dump(true);
+}
+
+echo "aead_chacha20poly1305_ietf:\n";
+
+if (SODIUM_LIBRARY_MAJOR_VERSION > 7 ||
+       (SODIUM_LIBRARY_MAJOR_VERSION == 7 &&
+        SODIUM_LIBRARY_MINOR_VERSION >= 6)) {
+       $msg = random_bytes(random_int(1, 1000));
+       $nonce = random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES);
+       $key = random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES);
+       $ad = random_bytes(random_int(1, 1000));
+
+       $ciphertext = sodium_crypto_aead_chacha20poly1305_ietf_encrypt($msg, $ad, $nonce, $key);
+       $msg2 = sodium_crypto_aead_chacha20poly1305_ietf_decrypt($ciphertext, $ad, $nonce, $key);
+       var_dump($ciphertext !== $msg);
+       var_dump($msg === $msg2);
+       var_dump(sodium_crypto_aead_chacha20poly1305_ietf_decrypt($ciphertext, 'x' . $ad, $nonce, $key));
+       try {
+               // Switched order
+               $msg2 = sodium_crypto_aead_chacha20poly1305_ietf_decrypt($ciphertext, $ad, $key, $nonce);
+               var_dump(false);
+       } catch (SodiumException $ex) {
+               var_dump(true);
+       }
+} else {
+       var_dump(true);
+       var_dump(true);
+       var_dump(false);
+       var_dump(true);
+}
+
+echo "aead_xchacha20poly1305_ietf:\n";
+
+if (SODIUM_LIBRARY_MAJOR_VERSION > 9 ||
+       (SODIUM_LIBRARY_MAJOR_VERSION == 9 &&
+        SODIUM_LIBRARY_MINOR_VERSION >= 4)) {
+       $msg = random_bytes(random_int(1, 1000));
+       $nonce = random_bytes(SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);
+       $key = random_bytes(SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES);
+       $ad = random_bytes(random_int(1, 1000));
+
+       $ciphertext = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt($msg, $ad, $nonce, $key);
+       $msg2 = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($ciphertext, $ad, $nonce, $key);
+       var_dump($ciphertext !== $msg);
+       var_dump($msg === $msg2);
+       var_dump(sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($ciphertext, 'x' . $ad, $nonce, $key));
+       try {
+               // Switched order
+               $msg2 = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($ciphertext, $ad, $key, $nonce);
+               var_dump(false);
+       } catch (SodiumException $ex) {
+               var_dump(true);
+       }
+} else {
+       var_dump(true);
+       var_dump(true);
+       var_dump(false);
+       var_dump(true);
+}
+
+echo "aead_aes256gcm:\n";
+
+$msg = random_bytes(random_int(1, 1000));
+$nonce = random_bytes(SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES);
+$key = random_bytes(SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES);
+$ad = random_bytes(random_int(1, 1000));
+
+if (sodium_crypto_aead_aes256gcm_is_available()) {
+       $ciphertext = sodium_crypto_aead_aes256gcm_encrypt($msg, $ad, $nonce, $key);
+       $msg2 = sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $ad, $nonce, $key);
+       var_dump($ciphertext !== $msg);
+       var_dump($msg === $msg2);
+       var_dump(sodium_crypto_aead_aes256gcm_decrypt($ciphertext, 'x' . $ad, $nonce, $key));
+       try {
+               // Switched order
+               $msg2 = sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $ad, $key, $nonce);
+               var_dump(false);
+       } catch (SodiumException $ex) {
+               var_dump(true);
+       }
+} else {
+       var_dump(true);
+       var_dump(true);
+       var_dump(false);
+       var_dump(true);
+}
+?>
+--EXPECT--
+aead_chacha20poly1305:
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+aead_chacha20poly1305_ietf:
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+aead_xchacha20poly1305_ietf:
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+aead_aes256gcm:
+bool(true)
+bool(true)
+bool(false)
+bool(true)
diff --git a/ext/sodium/tests/crypto_auth.phpt b/ext/sodium/tests/crypto_auth.phpt
new file mode 100644 (file)
index 0000000..25ddff5
--- /dev/null
@@ -0,0 +1,47 @@
+--TEST--
+Check for libsodium auth
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+$msg = random_bytes(1000);
+$key = random_bytes(SODIUM_CRYPTO_AUTH_KEYBYTES);
+$mac = sodium_crypto_auth($msg, $key);
+
+// This should validate
+var_dump(sodium_crypto_auth_verify($mac, $msg, $key));
+
+$bad_key = random_bytes(SODIUM_CRYPTO_AUTH_KEYBYTES - 1);
+try {
+       $mac = sodium_crypto_auth($msg, $bad_key);
+       echo 'Fail!', PHP_EOL;
+} catch (SodiumException $ex) {
+  echo $ex->getMessage(), PHP_EOL;
+}
+
+// Flip the first bit
+$badmsg = $msg;
+$badmsg[0] = \chr(\ord($badmsg[0]) ^ 0x80);
+var_dump(sodium_crypto_auth_verify($mac, $badmsg, $key));
+
+// Let's flip a bit pseudo-randomly
+$badmsg = $msg;
+$badmsg[$i=mt_rand(0, 999)] = \chr(
+       \ord($msg[$i]) ^ (
+               1 << mt_rand(0, 7)
+       )
+);
+
+var_dump(sodium_crypto_auth_verify($mac, $badmsg, $key));
+
+// Now let's change a bit in the MAC
+$badmac = $mac;
+$badmac[0] = \chr(\ord($badmac[0]) ^ 0x80);
+var_dump(sodium_crypto_auth_verify($badmac, $msg, $key));
+?>
+--EXPECT--
+bool(true)
+crypto_auth(): key must be CRYPTO_AUTH_KEYBYTES bytes
+bool(false)
+bool(false)
+bool(false)
diff --git a/ext/sodium/tests/crypto_box.phpt b/ext/sodium/tests/crypto_box.phpt
new file mode 100644 (file)
index 0000000..ef35714
--- /dev/null
@@ -0,0 +1,150 @@
+--TEST--
+Check for libsodium box
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+$keypair = sodium_crypto_box_keypair();
+var_dump(strlen($keypair) === SODIUM_CRYPTO_BOX_KEYPAIRBYTES);
+$sk = sodium_crypto_box_secretkey($keypair);
+var_dump(strlen($sk) === SODIUM_CRYPTO_BOX_SECRETKEYBYTES);
+$pk = sodium_crypto_box_publickey($keypair);
+var_dump(strlen($pk) === SODIUM_CRYPTO_BOX_PUBLICKEYBYTES);
+var_dump($pk !== $sk);
+$pk2 = sodium_crypto_box_publickey_from_secretkey($sk);
+var_dump($pk === $pk2);
+$pk2 = sodium_crypto_scalarmult_base($sk);
+var_dump($pk === $pk2);
+$keypair2 = sodium_crypto_box_keypair_from_secretkey_and_publickey($sk, $pk);
+var_dump($keypair === $keypair2);
+
+$seed_x = str_repeat('x', SODIUM_CRYPTO_BOX_SEEDBYTES);
+$seed_y = str_repeat('y', SODIUM_CRYPTO_BOX_SEEDBYTES);
+$alice_box_kp = sodium_crypto_box_seed_keypair($seed_x);
+$bob_box_kp = sodium_crypto_box_seed_keypair($seed_y);
+$message_nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES);
+
+$alice_box_secretkey = sodium_crypto_box_secretkey($alice_box_kp);
+$bob_box_publickey = sodium_crypto_box_publickey($bob_box_kp);
+
+$alice_to_bob_kp = sodium_crypto_box_keypair_from_secretkey_and_publickey(
+       $alice_box_secretkey,
+       $bob_box_publickey
+);
+
+$msg = "Here is another message, to be signed using Alice's secret key, and " .
+  "to be encrypted using Bob's public key. The keys will always be the same " .
+  "since they are derived from a fixed seeds";
+
+$ciphertext = sodium_crypto_box(
+       $msg,
+       $message_nonce,
+       $alice_to_bob_kp
+);
+
+try {
+  $ciphertext = sodium_crypto_box(
+         $msg,
+         $message_nonce,
+         substr($alice_to_bob_kp, 1)
+  );
+} catch (SodiumException $ex) {
+       echo $ex->getMessage(), PHP_EOL;
+}
+
+sodium_memzero($alice_box_kp);
+sodium_memzero($bob_box_kp);
+
+$alice_box_kp = sodium_crypto_box_seed_keypair($seed_x);
+$bob_box_kp = sodium_crypto_box_seed_keypair($seed_y);
+
+$alice_box_publickey = sodium_crypto_box_publickey($alice_box_kp);
+$bob_box_secretkey = sodium_crypto_box_secretkey($bob_box_kp);
+
+$bob_to_alice_kp = sodium_crypto_box_keypair_from_secretkey_and_publickey(
+       $bob_box_secretkey,
+       $alice_box_publickey
+);
+
+$plaintext = sodium_crypto_box_open(
+       $ciphertext,
+       $message_nonce,
+       $bob_to_alice_kp
+);
+
+var_dump($msg === $plaintext);
+
+$alice_kp = sodium_crypto_box_keypair();
+$alice_secretkey = sodium_crypto_box_secretkey($alice_kp);
+$alice_publickey = sodium_crypto_box_publickey($alice_kp);
+
+$bob_kp = sodium_crypto_box_keypair();
+$bob_secretkey = sodium_crypto_box_secretkey($bob_kp);
+$bob_publickey = sodium_crypto_box_publickey($bob_kp);
+
+$alice_to_bob_kp = sodium_crypto_box_keypair_from_secretkey_and_publickey
+  ($alice_secretkey, $bob_publickey);
+
+$bob_to_alice_kp = sodium_crypto_box_keypair_from_secretkey_and_publickey
+  ($bob_secretkey, $alice_publickey);
+
+$alice_to_bob_message_nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES);
+
+$alice_to_bob_ciphertext = sodium_crypto_box('Hi, this is Alice',
+                                                                                         $alice_to_bob_message_nonce,
+                                                                                         $alice_to_bob_kp);
+
+$alice_message_decrypted_by_bob = sodium_crypto_box_open($alice_to_bob_ciphertext,
+                                                                                                                 $alice_to_bob_message_nonce,
+                                                                                                                 $bob_to_alice_kp);
+
+$bob_to_alice_message_nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES);
+
+$bob_to_alice_ciphertext = sodium_crypto_box('Hi Alice! This is Bob',
+                                                                                         $bob_to_alice_message_nonce,
+                                                                                         $bob_to_alice_kp);
+
+$bob_message_decrypted_by_alice = sodium_crypto_box_open($bob_to_alice_ciphertext,
+                                                                                                                 $bob_to_alice_message_nonce,
+                                                                                                                 $alice_to_bob_kp);
+
+var_dump($alice_message_decrypted_by_bob);
+var_dump($bob_message_decrypted_by_alice);
+
+if (SODIUM_LIBRARY_MAJOR_VERSION > 7 ||
+       (SODIUM_LIBRARY_MAJOR_VERSION == 7 &&
+        SODIUM_LIBRARY_MINOR_VERSION >= 5)) {
+       $anonymous_message_to_alice = sodium_crypto_box_seal('Anonymous message',
+                                                                                                                 $alice_publickey);
+
+       $decrypted_message = sodium_crypto_box_seal_open($anonymous_message_to_alice,
+                                                                                                         $alice_kp);
+} else {
+       $decrypted_message = 'Anonymous message';
+}
+var_dump($decrypted_message);
+
+$msg = sodium_hex2bin(
+       '7375f4094f1151640bd853cb13dbc1a0ee9e13b0287a89d34fa2f6732be9de13f88457553d'.
+       '768347116522d6d32c9cb353ef07aa7c83bd129b2bb5db35b28334c935b24f2639405a0604'
+);
+$kp = sodium_hex2bin(
+       '36a6c2b96a650d80bf7e025e0f58f3d636339575defb370801a54213bd54582d'.
+       '5aecbcf7866e7a4d58a6c1317e2b955f54ecbe2fcbbf7d262c10636ed524480c'
+);
+var_dump(sodium_crypto_box_seal_open($msg, $kp));
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+crypto_box(): keypair size should be CRYPTO_BOX_KEYPAIRBYTES bytes
+bool(true)
+string(17) "Hi, this is Alice"
+string(21) "Hi Alice! This is Bob"
+string(17) "Anonymous message"
+string(26) "This is for your eyes only"
diff --git a/ext/sodium/tests/crypto_generichash.phpt b/ext/sodium/tests/crypto_generichash.phpt
new file mode 100644 (file)
index 0000000..364c5dc
--- /dev/null
@@ -0,0 +1,61 @@
+--TEST--
+Check for libsodium generichash
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+$q = sodium_crypto_generichash('msg');
+var_dump(bin2hex($q));
+$q = sodium_crypto_generichash('msg', '0123456789abcdef');
+var_dump(bin2hex($q));
+$q = sodium_crypto_generichash('msg', '0123456789abcdef', 64);
+var_dump(bin2hex($q));
+$q = sodium_crypto_generichash('msg', '0123456789abcdef0123456789abcdef', 64);
+var_dump(bin2hex($q));
+$state = sodium_crypto_generichash_init();
+$q = sodium_crypto_generichash_final($state);
+var_dump(bin2hex($q));
+$state = sodium_crypto_generichash_init();
+sodium_crypto_generichash_update($state, 'm');
+sodium_crypto_generichash_update($state, 'sg');
+$q = sodium_crypto_generichash_final($state);
+var_dump(bin2hex($q));
+$state = sodium_crypto_generichash_init('0123456789abcdef');
+sodium_crypto_generichash_update($state, 'm');
+sodium_crypto_generichash_update($state, 'sg');
+$q = sodium_crypto_generichash_final($state);
+var_dump(bin2hex($q));
+$state = sodium_crypto_generichash_init('0123456789abcdef', 64);
+sodium_crypto_generichash_update($state, 'm');
+sodium_crypto_generichash_update($state, 'sg');
+$state2 = '' . $state;
+$q = sodium_crypto_generichash_final($state, 64);
+var_dump(bin2hex($q));
+sodium_crypto_generichash_update($state2, '2');
+$q = sodium_crypto_generichash_final($state2, 64);
+$exp = bin2hex($q);
+var_dump($exp);
+$act = bin2hex(
+       sodium_crypto_generichash('msg2', '0123456789abcdef', 64)
+);
+var_dump($act);
+var_dump($exp === $act);
+try {
+       $hash = sodium_crypto_generichash('test', '', 128);
+} catch (SodiumException $ex) {
+       var_dump(true);
+}
+?>
+--EXPECT--
+string(64) "96a7ed8861db0abc006f473f9e64687875f3d9df8e723adae9f53a02b2aec378"
+string(64) "ba03e32a94ece425a77b350f029e0a3d37e6383158aa7cefa2b1b9470a7fcb7a"
+string(128) "8ccd640462e7380010c5722d7f3c2354781d1360430197ff233509c27353fd2597c8d689bfe769467056a0655b3faba6af4e4ade248558f7c53538c4d5b94806"
+string(128) "30f0e5f1e3beb7e0340976ac05a94043cce082d870e28e03c906e8fe9a88786271c6ba141eee2885e7444a870fac498cc78a13b0c53aefaec01bf38ebfe73b3f"
+string(64) "0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8"
+string(64) "96a7ed8861db0abc006f473f9e64687875f3d9df8e723adae9f53a02b2aec378"
+string(64) "ba03e32a94ece425a77b350f029e0a3d37e6383158aa7cefa2b1b9470a7fcb7a"
+string(128) "8ccd640462e7380010c5722d7f3c2354781d1360430197ff233509c27353fd2597c8d689bfe769467056a0655b3faba6af4e4ade248558f7c53538c4d5b94806"
+string(128) "9ef702f51114c9dc2cc7521746e8beebe0a3ca9bb29ec729e16682ca982e7f69ff70235a46659a9a6c28f92fbd990288301b9a6b5517f1f2ba6518074af19a5a"
+string(128) "9ef702f51114c9dc2cc7521746e8beebe0a3ca9bb29ec729e16682ca982e7f69ff70235a46659a9a6c28f92fbd990288301b9a6b5517f1f2ba6518074af19a5a"
+bool(true)
+bool(true)
diff --git a/ext/sodium/tests/crypto_hex.phpt b/ext/sodium/tests/crypto_hex.phpt
new file mode 100644 (file)
index 0000000..0f872ee
--- /dev/null
@@ -0,0 +1,21 @@
+--TEST--
+Check for libsodium bin2hex
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+$bin = random_bytes(random_int(1, 1000));
+$hex = sodium_bin2hex($bin);
+$phphex = bin2hex($bin);
+var_dump(strcasecmp($hex, $phphex));
+
+$bin2 = sodium_hex2bin($hex);
+var_dump($bin2 === $bin);
+
+$bin2 = sodium_hex2bin('[' . $hex .']', '[]');
+var_dump($bin2 === $bin);
+?>
+--EXPECT--
+int(0)
+bool(true)
+bool(true)
diff --git a/ext/sodium/tests/crypto_kx.phpt b/ext/sodium/tests/crypto_kx.phpt
new file mode 100644 (file)
index 0000000..ef8c5f7
--- /dev/null
@@ -0,0 +1,37 @@
+--TEST--
+Check for libsodium-based key exchange
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+$client_secretkey = sodium_hex2bin("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
+$client_publickey = sodium_crypto_box_publickey_from_secretkey($client_secretkey);
+
+$server_secretkey = sodium_hex2bin("948f00e90a246fb5909f8648c2ac6f21515771235523266439e0d775ba0c3671");
+$server_publickey = sodium_crypto_box_publickey_from_secretkey($server_secretkey);
+
+$shared_key_computed_by_client =
+  sodium_crypto_kx($client_secretkey, $server_publickey,
+                                       $client_publickey, $server_publickey);
+
+$shared_key_computed_by_server =
+  sodium_crypto_kx($server_secretkey, $client_publickey,
+                                       $client_publickey, $server_publickey);
+
+var_dump(sodium_bin2hex($shared_key_computed_by_client));
+var_dump(sodium_bin2hex($shared_key_computed_by_server));
+try {
+       sodium_crypto_kx(
+               substr($client_secretkey, 1),
+               $server_publickey,
+               $client_publickey,
+               $server_publickey
+       );
+} catch (SodiumException $ex) {
+       var_dump(true);
+}
+?>
+--EXPECT--
+string(64) "509a1580c2ee30c565317e29e0fea0b1c232e0ef3a7871d91dc64814b19a3bd2"
+string(64) "509a1580c2ee30c565317e29e0fea0b1c232e0ef3a7871d91dc64814b19a3bd2"
+bool(true)
diff --git a/ext/sodium/tests/crypto_scalarmult.phpt b/ext/sodium/tests/crypto_scalarmult.phpt
new file mode 100644 (file)
index 0000000..22496cd
--- /dev/null
@@ -0,0 +1,20 @@
+--TEST--
+Check for libsodium scalarmult
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+$n = sodium_hex2bin("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb");
+$p = sodium_hex2bin("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
+$q = sodium_crypto_scalarmult($n, $p);
+
+var_dump(sodium_bin2hex($q));
+try {
+       sodium_crypto_scalarmult(substr($n, 1), $p);
+} catch (SodiumException $ex) {
+       var_dump(true);
+}
+?>
+--EXPECT--
+string(64) "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"
+bool(true)
diff --git a/ext/sodium/tests/crypto_secretbox.phpt b/ext/sodium/tests/crypto_secretbox.phpt
new file mode 100644 (file)
index 0000000..f4bf53e
--- /dev/null
@@ -0,0 +1,26 @@
+--TEST--
+Check for libsodium secretbox
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
+$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
+
+$a = sodium_crypto_secretbox('test', $nonce, $key);
+$x = sodium_crypto_secretbox_open($a, $nonce, $key);
+var_dump(bin2hex($x));
+$y = sodium_crypto_secretbox_open("\0" . $a, $nonce, $key);
+var_dump($y);
+
+try {
+       sodium_crypto_secretbox('test', substr($nonce, 1), $key);
+} catch (SodiumException $ex) {
+       var_dump(true);
+}
+
+?>
+--EXPECT--
+string(8) "74657374"
+bool(false)
+bool(true)
diff --git a/ext/sodium/tests/crypto_shorthash.phpt b/ext/sodium/tests/crypto_shorthash.phpt
new file mode 100644 (file)
index 0000000..3398def
--- /dev/null
@@ -0,0 +1,28 @@
+--TEST--
+Check for libsodium shorthash
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+$m1 = 'message';
+$k1 = '0123456789ABCDEF';
+$h1 = sodium_crypto_shorthash($m1, $k1);
+echo bin2hex($h1) . "\n";
+$k2 = '0123456789abcdef';
+$h2 = sodium_crypto_shorthash($m1, $k2);
+echo bin2hex($h2) . "\n";
+$m2 = 'msg';
+$h3 = sodium_crypto_shorthash($m2, $k2);
+echo bin2hex($h3) . "\n";
+
+try {
+       sodium_crypto_shorthash($m1, $k1 . $k2);
+} catch (SodiumException $ex) {
+       var_dump(true);
+}
+?>
+--EXPECT--
+e0ad6fdbf8b9a191
+c667b37af201a2d9
+d27fa3fc70b45b72
+bool(true)
diff --git a/ext/sodium/tests/crypto_sign.phpt b/ext/sodium/tests/crypto_sign.phpt
new file mode 100644 (file)
index 0000000..55fe892
--- /dev/null
@@ -0,0 +1,81 @@
+--TEST--
+Check for libsodium ed25519 signatures
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+$keypair = sodium_crypto_sign_keypair();
+var_dump(strlen($keypair) === SODIUM_CRYPTO_SIGN_KEYPAIRBYTES);
+$sk = sodium_crypto_sign_secretkey($keypair);
+var_dump(strlen($sk) === SODIUM_CRYPTO_SIGN_SECRETKEYBYTES);
+$pk = sodium_crypto_sign_publickey($keypair);
+var_dump(strlen($pk) === SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES);
+var_dump($pk !== $sk);
+$keypair2 = sodium_crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk);
+var_dump($keypair === $keypair2);
+
+$alice_kp = sodium_crypto_sign_keypair();
+$alice_secretkey = sodium_crypto_sign_secretkey($alice_kp);
+$alice_publickey = sodium_crypto_sign_publickey($alice_kp);
+
+$msg = "Here is the message, to be signed using Alice's secret key, and " .
+  "to be verified using Alice's public key";
+
+$msg_signed = sodium_crypto_sign($msg, $alice_secretkey);
+var_dump(strlen($msg_signed) - strlen($msg) === SODIUM_CRYPTO_SIGN_BYTES);
+
+$msg_orig = sodium_crypto_sign_open($msg_signed, $alice_publickey);
+var_dump($msg_orig === $msg);
+
+$seed = str_repeat('x', SODIUM_CRYPTO_SIGN_SEEDBYTES);
+$alice_kp = sodium_crypto_sign_seed_keypair($seed);
+
+$alice_secretkey = sodium_crypto_sign_secretkey($alice_kp);
+$alice_publickey = sodium_crypto_sign_publickey($alice_kp);
+
+$msg = "Here is another message, to be signed using Alice's secret key, and " .
+  "to be verified using Alice's public key, which will be always the same " .
+  "since they are derived from a fixed seed";
+
+$msg_signed = sodium_crypto_sign($msg, $alice_secretkey);
+var_dump(strlen($msg_signed) - strlen($msg) === SODIUM_CRYPTO_SIGN_BYTES);
+
+$msg_orig = sodium_crypto_sign_open($msg_signed, $alice_publickey);
+var_dump($msg_orig === $msg);
+
+$signature = sodium_crypto_sign_detached($msg, $alice_secretkey);
+var_dump(strlen($signature) === SODIUM_CRYPTO_SIGN_BYTES);
+var_dump(sodium_crypto_sign_verify_detached($signature,
+                                                                                        $msg, $alice_publickey));
+var_dump(sodium_crypto_sign_verify_detached($signature,
+                                                                                        $msg . "\0", $alice_publickey));
+
+$calc_pubkey = sodium_crypto_sign_publickey_from_secretkey($alice_secretkey);
+var_dump(sodium_memcmp($calc_pubkey, $alice_publickey) === 0);
+
+$ed25519key = sodium_hex2bin("55b62f664bf1c359f58a6b91b89556f97284273510573055b9237d17f5a20564607f0626f49e63c2c8f814ed6d955bf8b005b33fd5fd56eaca93073d8eb99165");
+$curve25519key = sodium_crypto_sign_ed25519_sk_to_curve25519($ed25519key);
+var_dump($curve25519key === sodium_hex2bin("381b2be5e3d38820deb1243fb58b4be654da30dd3ccde492cb88f937eb489363"));
+
+try {
+       sodium_crypto_sign($msg, substr($alice_secretkey, 1));
+} catch (SodiumException $ex) {
+       var_dump(true);
+}
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+bool(true)
+bool(true)
diff --git a/ext/sodium/tests/crypto_stream.phpt b/ext/sodium/tests/crypto_stream.phpt
new file mode 100644 (file)
index 0000000..7439e28
--- /dev/null
@@ -0,0 +1,52 @@
+--TEST--
+Check for libsodium stream
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+$nonce = random_bytes(SODIUM_CRYPTO_STREAM_NONCEBYTES);
+$key = random_bytes(SODIUM_CRYPTO_STREAM_KEYBYTES);
+
+$len = 100;
+$stream = sodium_crypto_stream($len, $nonce, $key);
+var_dump(strlen($stream));
+
+$stream2 = sodium_crypto_stream($len, $nonce, $key);
+
+$nonce = random_bytes(SODIUM_CRYPTO_STREAM_NONCEBYTES);
+$stream3 = sodium_crypto_stream($len, $nonce, $key);
+
+$key = random_bytes(SODIUM_CRYPTO_STREAM_KEYBYTES);
+$stream4 = sodium_crypto_stream($len, $nonce, $key);
+
+var_dump($stream === $stream2);
+var_dump($stream !== $stream3);
+var_dump($stream !== $stream4);
+var_dump($stream2 !== $stream3);
+var_dump($stream2 !== $stream4);
+var_dump($stream3 !== $stream4);
+
+$stream5 = sodium_crypto_stream_xor($stream, $nonce, $key);
+var_dump($stream5 !== $stream);
+$stream6 = sodium_crypto_stream_xor($stream5, $nonce, $key);
+
+var_dump($stream6 === $stream);
+
+try {
+       sodium_crypto_stream($len, substr($nonce, 1), $key);
+} catch (SodiumException $ex) {
+       var_dump(true);
+}
+
+?>
+--EXPECT--
+int(100)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
diff --git a/ext/sodium/tests/exception_trace_without_args.phpt b/ext/sodium/tests/exception_trace_without_args.phpt
new file mode 100644 (file)
index 0000000..c821df0
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+SodiumException backtraces do not contain function arguments
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+
+function do_memzero($x) {
+       sodium_memzero($x);
+}
+
+$x = 42;
+do_memzero($x);
+
+?>
+--EXPECTF--
+Fatal error: Uncaught SodiumException: memzero: a PHP string is required in %s:%d
+Stack trace:
+#0 %s(%d): sodium_memzero()
+#1 %s(%d): do_memzero()
+#2 {main}
+  thrown in %s on line %d
diff --git a/ext/sodium/tests/inc_add.phpt b/ext/sodium/tests/inc_add.phpt
new file mode 100644 (file)
index 0000000..2ab9b92
--- /dev/null
@@ -0,0 +1,55 @@
+--TEST--
+increment and add edge cases
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+
+$notStr = 123;
+try {
+       sodium_increment($notStr);
+} catch (SodiumException $e) {
+       echo $e->getMessage(), "\n";
+}
+
+$str = "abc";
+$str2 = $str;
+sodium_increment($str);
+var_dump($str, $str2);
+
+$str = "ab" . ($x = "c");
+$str2 = $str;
+sodium_increment($str);
+var_dump($str, $str2);
+
+$addStr = "\2\0\0";
+
+$notStr = 123;
+try {
+       sodium_add($notStr, $addStr);
+} catch (SodiumException $e) {
+       echo $e->getMessage(), "\n";
+}
+
+$str = "abc";
+$str2 = $str;
+sodium_add($str, $addStr);
+var_dump($str, $str2);
+
+$str = "ab" . ($x = "c");
+$str2 = $str;
+sodium_add($str, $addStr);
+var_dump($str, $str2);
+
+?>
+--EXPECT--
+increment(): a PHP string is required
+string(3) "bbc"
+string(3) "abc"
+string(3) "bbc"
+string(3) "abc"
+add(): PHP strings are required
+string(3) "cbc"
+string(3) "abc"
+string(3) "cbc"
+string(3) "abc"
diff --git a/ext/sodium/tests/installed.phpt b/ext/sodium/tests/installed.phpt
new file mode 100644 (file)
index 0000000..43ad4fd
--- /dev/null
@@ -0,0 +1,21 @@
+--TEST--
+Check for libsodium presence
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+echo "libsodium extension is available";
+/*
+               you can add regression tests for your extension here
+
+  the output of your test code has to be equal to the
+  text in the --EXPECT-- section below for the tests
+  to pass, differences between the output and the
+  expected text are interpreted as failure
+
+               see php5/README.TESTING for further information on
+  writing regression tests
+*/
+?>
+--EXPECT--
+libsodium extension is available
diff --git a/ext/sodium/tests/pwhash_argon2i.phpt b/ext/sodium/tests/pwhash_argon2i.phpt
new file mode 100644 (file)
index 0000000..eb36223
--- /dev/null
@@ -0,0 +1,44 @@
+--TEST--
+Check for libsodium utils
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip";
+if (!defined('SODIUM_CRYPTO_PWHASH_SALTBYTES')) print "skip libsodium without argon2i"; ?>
+--FILE--
+<?php
+$passwd = 'password';
+
+$hash = sodium_crypto_pwhash_str
+  ($passwd, SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
+                       SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE);
+var_dump(substr($hash, 0, 9) ===
+                SODIUM_CRYPTO_PWHASH_STRPREFIX);
+
+$testHash = '$argon2i$v=19$m=4096,t=3,p=1$MzE4ODFiZWFlMjAzOWUAAA$FWUV6tsyJ32qThiLi1cCsLIbf3dIOG/RwXcTzt536KY';
+$c = sodium_crypto_pwhash_str_verify($testHash, $passwd);
+var_dump($c);
+
+$testHash = '$argon2i$v=19$m=4096,t=2,p=1$c29tZXNhbHQAAAAAAAAAAA$JTBozgKQiCn5yKAm3Hz0vUSX/XgfqhZloNCxDWmeDr0';
+$c = sodium_crypto_pwhash_str_verify($testHash, $passwd);
+var_dump($c);
+
+$c = sodium_crypto_pwhash_str_verify($hash, $passwd);
+var_dump($c);
+
+$c = sodium_crypto_pwhash_str_verify($hash, 'passwd');
+var_dump($c);
+
+$salt = random_bytes(SODIUM_CRYPTO_PWHASH_SALTBYTES);
+$out_len = 100;
+$key = sodium_crypto_pwhash
+  ($out_len, $passwd, $salt,
+   SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
+   SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE);
+var_dump(strlen($key) === $out_len);
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
diff --git a/ext/sodium/tests/pwhash_scrypt.phpt b/ext/sodium/tests/pwhash_scrypt.phpt
new file mode 100644 (file)
index 0000000..e5a2049
--- /dev/null
@@ -0,0 +1,33 @@
+--TEST--
+Check for libsodium utils
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+$passwd = 'test';
+
+$hash = sodium_crypto_pwhash_scryptsalsa208sha256_str
+  ($passwd, SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE,
+                       SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE);
+var_dump(substr($hash, 0, 3) ===
+                SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_STRPREFIX);
+
+$c = sodium_crypto_pwhash_scryptsalsa208sha256_str_verify($hash, $passwd);
+var_dump($c);
+
+$c = sodium_crypto_pwhash_scryptsalsa208sha256_str_verify($hash, 'passwd');
+var_dump($c);
+
+$salt = random_bytes(SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES);
+$out_len = 100;
+$key = sodium_crypto_pwhash_scryptsalsa208sha256
+  ($out_len, $passwd, $salt,
+   SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE,
+   SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE);
+var_dump(strlen($key) === $out_len);
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(false)
+bool(true)
diff --git a/ext/sodium/tests/utils.phpt b/ext/sodium/tests/utils.phpt
new file mode 100644 (file)
index 0000000..c413e01
--- /dev/null
@@ -0,0 +1,62 @@
+--TEST--
+Check for libsodium utils
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+$a = 'test';
+sodium_memzero($a);
+if ($a !== 'test') {
+  echo strlen($a);
+} else {
+  echo $a;
+}
+echo "\n";
+$b = 'string';
+$c = 'string';
+var_dump(!sodium_memcmp($b, $c));
+var_dump(!sodium_memcmp($b, 'String'));
+$v = "\xFF\xFF\x80\x01\x02\x03\x04\x05\x06\x07";
+$v .= "\x08";
+sodium_increment($v);
+var_dump(bin2hex($v));
+
+$w = "\x01\x02\x03\x04\x05\x06\x07\x08\xFA\xFB";
+$w .= "\xFC";
+sodium_add($v, $w);
+var_dump(bin2hex($v));
+
+if (SODIUM_LIBRARY_MAJOR_VERSION > 7 ||
+       (SODIUM_LIBRARY_MAJOR_VERSION == 7 &&
+        SODIUM_LIBRARY_MINOR_VERSION >= 6)) {
+       $v_1 = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
+       $v_2 = ""."\x02\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
+       $v_1 .= '';
+       var_dump(sodium_compare($v_1, $v_2));
+       sodium_increment($v_1);
+       var_dump(sodium_compare($v_1, $v_2));
+       sodium_increment($v_1);
+       var_dump(sodium_compare($v_1, $v_2));
+} else {
+       // Dummy test results for libsodium < 1.0.4
+       var_dump(-1, 0, 1);
+}
+
+$str = 'stdClass';
+sodium_memzero($str);
+$obj = (object)array('foo' => 'bar');
+var_dump($obj);
+?>
+--EXPECT--
+0
+bool(true)
+bool(false)
+string(22) "0000810102030405060708"
+string(22) "0102840507090b0d000305"
+int(-1)
+int(0)
+int(1)
+object(stdClass)#1 (1) {
+  ["foo"]=>
+  string(3) "bar"
+}
diff --git a/ext/sodium/tests/version.phpt b/ext/sodium/tests/version.phpt
new file mode 100644 (file)
index 0000000..f13345e
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+Check for libsodium version
+--SKIPIF--
+<?php if (!extension_loaded("sodium")) print "skip"; ?>
+--FILE--
+<?php
+echo strlen(SODIUM_LIBRARY_VERSION) >= 5;
+echo "\n";
+echo SODIUM_LIBRARY_MAJOR_VERSION >= 4;
+echo "\n";
+echo SODIUM_LIBRARY_MINOR_VERSION >= 0;
+?>
+--EXPECT--
+1
+1
+1
+
index 83b74977e819f57b1b6c223cafd0456e30cc0ca4..94a3d6fe7e29a8d7fba4998b0bcadaa062c1a220 100644 (file)
@@ -64,6 +64,7 @@ CREDIT_LINE("SimpleXML", "Sterling Hughes, Marcus Boerger, Rob Richards");
 CREDIT_LINE("SNMP", "Rasmus Lerdorf, Harrie Hazewinkel, Mike Jackson, Steven Lawrance, Johann Hanne, Boris Lytochkin");
 CREDIT_LINE("SOAP", "Brad Lafountain, Shane Caraveo, Dmitry Stogov");
 CREDIT_LINE("Sockets", "Chris Vandomelen, Sterling Hughes, Daniel Beulshausen, Jason Greene");
+CREDIT_LINE("Sodium", "Frank Denis");
 CREDIT_LINE("SPL", "Marcus Boerger, Etienne Kneuss");
 CREDIT_LINE("SQLite 3.x driver for PDO", "Wez Furlong");
 CREDIT_LINE("SQLite3", "Scott MacVicar, Ilia Alshanetsky, Brad Dewar");
index 46851faf73804dd797c1b3ec406d76f388640378..a0e77995a289ca8b1d2273f0ea1c13c689a127ed 100644 (file)
@@ -8,6 +8,7 @@ libiconv-1.15
 libmpir-3.0.0
 libpng-1.6.29
 libpq-9.6.2
+libsodium-1.0.12
 libssh2-1.8.0
 libtidy-5.4.0
 libxslt-1.1.29
index 447d9101dce62243894645193fe10f44c56f1025..0b989e8fe975b17d88acd4fb9450ce00bea740c6 100644 (file)
@@ -1028,6 +1028,7 @@ extension=php_bz2.dll
    php_snmp.dll SNMP get and walk functions NT only!
    php_soap.dll SOAP functions PHP >= 5.0.0
    php_sockets.dll Socket functions None
+   php_sodium.dll Sodium cryptography library PHP >= 7.2.0
    php_sybase_ct.dll Sybase functions Requires: Sybase client libraries
    php_tidy.dll Tidy functions PHP >= 5.0.0
    php_tokenizer.dll Tokenizer functions Built in since PHP 4.3.0