Commit bdca5a9f498 for php

commit bdca5a9f49840ac65214368907f4282317b268d2
Author: Tim Düsterhus <tim@tideways-gmbh.com>
Date:   Tue Oct 27 17:08:27 2026 +0100

    sodium: Use opaque objects for SHAKE state (#24351)

    * sodium: Use opaque objects for SHAKE state

    * sodium: Make `sodium_crypto_xof_*shake*_update()` void

    * sodium: Fix error message phrasing when requesting invalid lengths from SHAKE

    * NEWS

diff --git a/NEWS b/NEWS
index ce102bda180..099da538e96 100644
--- a/NEWS
+++ b/NEWS
@@ -73,6 +73,10 @@ PHP                                                                        NEWS
   . Fixed infinite loop in sapi_header_op() for headers of 4 GiB or more.
     (Martin Mirchev)

+- Sodium:
+  . Pass sodium_crypto_xof_*() state via objects instead of by-ref strings.
+    (timwolla)
+
 - SPL:
   . Fixed bug GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c).
     (Lazizbek Ergashev)
diff --git a/ext/sodium/libsodium.c b/ext/sodium/libsodium.c
index f54d35506ab..c44d49e283a 100644
--- a/ext/sodium/libsodium.c
+++ b/ext/sodium/libsodium.c
@@ -29,6 +29,156 @@

 static zend_class_entry *sodium_exception_ce;

+#ifdef crypto_xof_shake128_STATEBYTES
+static zend_class_entry *sodium_crypto_xof_shake128_state_ce;
+static zend_class_entry *sodium_crypto_xof_shake256_state_ce;
+static zend_class_entry *sodium_crypto_xof_turboshake128_state_ce;
+static zend_class_entry *sodium_crypto_xof_turboshake256_state_ce;
+
+static zend_object_handlers sodium_crypto_xof_shake128_state_object_handlers;
+static zend_object_handlers sodium_crypto_xof_shake256_state_object_handlers;
+static zend_object_handlers sodium_crypto_xof_turboshake128_state_object_handlers;
+static zend_object_handlers sodium_crypto_xof_turboshake256_state_object_handlers;
+
+typedef struct php_sodium_crypto_xof_shake128_state {
+	crypto_xof_shake128_state state;
+	zend_object std;
+} php_sodium_crypto_xof_shake128_state;
+
+typedef struct php_sodium_crypto_xof_shake256_state {
+	crypto_xof_shake256_state state;
+	zend_object std;
+} php_sodium_crypto_xof_shake256_state;
+
+typedef struct php_sodium_crypto_xof_turboshake128_state {
+	crypto_xof_turboshake128_state state;
+	zend_object std;
+} php_sodium_crypto_xof_turboshake128_state;
+
+typedef struct php_sodium_crypto_xof_turboshake256_state {
+	crypto_xof_turboshake256_state state;
+	zend_object std;
+} php_sodium_crypto_xof_turboshake256_state;
+
+# define php_sodium_crypto_xof_shake128_state_from_obj(object) ZEND_CONTAINER_OF(object, php_sodium_crypto_xof_shake128_state, std)
+# define php_sodium_crypto_xof_shake256_state_from_obj(object) ZEND_CONTAINER_OF(object, php_sodium_crypto_xof_shake256_state, std)
+# define php_sodium_crypto_xof_turboshake128_state_from_obj(object) ZEND_CONTAINER_OF(object, php_sodium_crypto_xof_turboshake128_state, std)
+# define php_sodium_crypto_xof_turboshake256_state_from_obj(object) ZEND_CONTAINER_OF(object, php_sodium_crypto_xof_turboshake256_state, std)
+
+static zend_object *sodium_crypto_xof_shake128_state_new(zend_class_entry *ce)
+{
+	php_sodium_crypto_xof_shake128_state *state = zend_object_alloc(sizeof(*state), ce);
+
+	zend_object_std_init(&state->std, ce);
+	object_properties_init(&state->std, ce);
+
+	return &state->std;
+}
+
+static zend_object *sodium_crypto_xof_shake256_state_new(zend_class_entry *ce)
+{
+	php_sodium_crypto_xof_shake256_state *state = zend_object_alloc(sizeof(*state), ce);
+
+	zend_object_std_init(&state->std, ce);
+	object_properties_init(&state->std, ce);
+
+	return &state->std;
+}
+
+static zend_object *sodium_crypto_xof_turboshake128_state_new(zend_class_entry *ce)
+{
+	php_sodium_crypto_xof_turboshake128_state *state = zend_object_alloc(sizeof(*state), ce);
+
+	zend_object_std_init(&state->std, ce);
+	object_properties_init(&state->std, ce);
+
+	return &state->std;
+}
+
+static zend_object *sodium_crypto_xof_turboshake256_state_new(zend_class_entry *ce)
+{
+	php_sodium_crypto_xof_turboshake256_state *state = zend_object_alloc(sizeof(*state), ce);
+
+	zend_object_std_init(&state->std, ce);
+	object_properties_init(&state->std, ce);
+
+	return &state->std;
+}
+
+static zend_function *sodium_crypto_xof_shake128_state_get_constructor(zend_object *object)
+{
+	zend_throw_error(NULL, "Cannot directly construct %s, use sodium_crypto_xof_shake128_init() instead", ZSTR_VAL(object->ce->name));
+	return NULL;
+}
+
+static zend_function *sodium_crypto_xof_shake256_state_get_constructor(zend_object *object)
+{
+	zend_throw_error(NULL, "Cannot directly construct %s, use sodium_crypto_xof_shake256_init() instead", ZSTR_VAL(object->ce->name));
+	return NULL;
+}
+
+static zend_function *sodium_crypto_xof_turboshake128_state_get_constructor(zend_object *object)
+{
+	zend_throw_error(NULL, "Cannot directly construct %s, use sodium_crypto_xof_turboshake128_init() instead", ZSTR_VAL(object->ce->name));
+	return NULL;
+}
+
+static zend_function *sodium_crypto_xof_turboshake256_state_get_constructor(zend_object *object)
+{
+	zend_throw_error(NULL, "Cannot directly construct %s, use sodium_crypto_xof_turboshake256_init() instead", ZSTR_VAL(object->ce->name));
+	return NULL;
+}
+
+static zend_object *sodium_crypto_xof_shake128_state_clone(zend_object *old_obj)
+{
+	php_sodium_crypto_xof_shake128_state *old_state = php_sodium_crypto_xof_shake128_state_from_obj(old_obj);
+	zend_object *new_obj = sodium_crypto_xof_shake128_state_new(old_obj->ce);
+	php_sodium_crypto_xof_shake128_state *new_state = php_sodium_crypto_xof_shake128_state_from_obj(new_obj);
+
+	zend_objects_clone_members(new_obj, old_obj);
+	memcpy(&new_state->state, &old_state->state, sizeof(new_state->state));
+
+	return new_obj;
+}
+
+static zend_object *sodium_crypto_xof_shake256_state_clone(zend_object *old_obj)
+{
+	php_sodium_crypto_xof_shake256_state *old_state = php_sodium_crypto_xof_shake256_state_from_obj(old_obj);
+	zend_object *new_obj = sodium_crypto_xof_shake256_state_new(old_obj->ce);
+	php_sodium_crypto_xof_shake256_state *new_state = php_sodium_crypto_xof_shake256_state_from_obj(new_obj);
+
+	zend_objects_clone_members(new_obj, old_obj);
+	memcpy(&new_state->state, &old_state->state, sizeof(new_state->state));
+
+	return new_obj;
+}
+
+static zend_object *sodium_crypto_xof_turboshake128_state_clone(zend_object *old_obj)
+{
+	php_sodium_crypto_xof_turboshake128_state *old_state = php_sodium_crypto_xof_turboshake128_state_from_obj(old_obj);
+	zend_object *new_obj = sodium_crypto_xof_turboshake128_state_new(old_obj->ce);
+	php_sodium_crypto_xof_turboshake128_state *new_state = php_sodium_crypto_xof_turboshake128_state_from_obj(new_obj);
+
+	zend_objects_clone_members(new_obj, old_obj);
+	memcpy(&new_state->state, &old_state->state, sizeof(new_state->state));
+
+	return new_obj;
+}
+
+static zend_object *sodium_crypto_xof_turboshake256_state_clone(zend_object *old_obj)
+{
+	php_sodium_crypto_xof_turboshake256_state *old_state = php_sodium_crypto_xof_turboshake256_state_from_obj(old_obj);
+	zend_object *new_obj = sodium_crypto_xof_turboshake256_state_new(old_obj->ce);
+	php_sodium_crypto_xof_turboshake256_state *new_state = php_sodium_crypto_xof_turboshake256_state_from_obj(new_obj);
+
+	zend_objects_clone_members(new_obj, old_obj);
+	memcpy(&new_state->state, &old_state->state, sizeof(new_state->state));
+
+	return new_obj;
+}
+
+#endif
+
 #if (defined(__amd64) || defined(__amd64__) || defined(__x86_64__) || defined(__i386__) || \
 	 defined(_M_AMD64) || defined(_M_IX86) || defined(__aarch64__) || defined(_M_ARM64))
 # define HAVE_AESGCM 1
@@ -168,6 +318,40 @@ PHP_MINIT_FUNCTION(sodium)

 	register_libsodium_symbols(module_number);

+#ifdef crypto_xof_shake128_STATEBYTES
+	sodium_crypto_xof_shake128_state_ce = register_class_SodiumCryptoXofShake128State();
+	sodium_crypto_xof_shake128_state_ce->create_object = sodium_crypto_xof_shake128_state_new;
+	sodium_crypto_xof_shake128_state_ce->default_object_handlers = &sodium_crypto_xof_shake128_state_object_handlers;
+	memcpy(&sodium_crypto_xof_shake128_state_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+	sodium_crypto_xof_shake128_state_object_handlers.offset = offsetof(php_sodium_crypto_xof_shake128_state, std);
+	sodium_crypto_xof_shake128_state_object_handlers.clone_obj = sodium_crypto_xof_shake128_state_clone;
+	sodium_crypto_xof_shake128_state_object_handlers.get_constructor = sodium_crypto_xof_shake128_state_get_constructor;
+
+	sodium_crypto_xof_shake256_state_ce = register_class_SodiumCryptoXofShake256State();
+	sodium_crypto_xof_shake256_state_ce->create_object = sodium_crypto_xof_shake256_state_new;
+	sodium_crypto_xof_shake256_state_ce->default_object_handlers = &sodium_crypto_xof_shake256_state_object_handlers;
+	memcpy(&sodium_crypto_xof_shake256_state_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+	sodium_crypto_xof_shake256_state_object_handlers.offset = offsetof(php_sodium_crypto_xof_shake256_state, std);
+	sodium_crypto_xof_shake256_state_object_handlers.clone_obj = sodium_crypto_xof_shake256_state_clone;
+	sodium_crypto_xof_shake256_state_object_handlers.get_constructor = sodium_crypto_xof_shake256_state_get_constructor;
+
+	sodium_crypto_xof_turboshake128_state_ce = register_class_SodiumCryptoXofTurboshake128State();
+	sodium_crypto_xof_turboshake128_state_ce->create_object = sodium_crypto_xof_turboshake128_state_new;
+	sodium_crypto_xof_turboshake128_state_ce->default_object_handlers = &sodium_crypto_xof_turboshake128_state_object_handlers;
+	memcpy(&sodium_crypto_xof_turboshake128_state_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+	sodium_crypto_xof_turboshake128_state_object_handlers.offset = offsetof(php_sodium_crypto_xof_turboshake128_state, std);
+	sodium_crypto_xof_turboshake128_state_object_handlers.clone_obj = sodium_crypto_xof_turboshake128_state_clone;
+	sodium_crypto_xof_turboshake128_state_object_handlers.get_constructor = sodium_crypto_xof_turboshake128_state_get_constructor;
+
+	sodium_crypto_xof_turboshake256_state_ce = register_class_SodiumCryptoXofTurboshake256State();
+	sodium_crypto_xof_turboshake256_state_ce->create_object = sodium_crypto_xof_turboshake256_state_new;
+	sodium_crypto_xof_turboshake256_state_ce->default_object_handlers = &sodium_crypto_xof_turboshake256_state_object_handlers;
+	memcpy(&sodium_crypto_xof_turboshake256_state_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+	sodium_crypto_xof_turboshake256_state_object_handlers.offset = offsetof(php_sodium_crypto_xof_turboshake256_state, std);
+	sodium_crypto_xof_turboshake256_state_object_handlers.clone_obj = sodium_crypto_xof_turboshake256_state_clone;
+	sodium_crypto_xof_turboshake256_state_object_handlers.get_constructor = sodium_crypto_xof_turboshake256_state_get_constructor;
+#endif
+
 	return SUCCESS;
 }

@@ -4269,8 +4453,8 @@ PHP_FUNCTION(sodium_crypto_xof_shake128)
 		sodium_remove_param_values_from_backtrace(EG(exception));
 		RETURN_THROWS();
 	}
-	if (out_len <= 0 || out_len > ZSTR_MAX_LEN) {
-		zend_argument_error(sodium_exception_ce, 1, "must be a positive integer");
+	if (out_len <= 0) {
+		zend_argument_error(sodium_exception_ce, 1, "must be greater than 0");
 		RETURN_THROWS();
 	}
 	out = zend_string_alloc((size_t) out_len, 0);
@@ -4286,8 +4470,6 @@ PHP_FUNCTION(sodium_crypto_xof_shake128)

 PHP_FUNCTION(sodium_crypto_xof_shake128_init)
 {
-	crypto_xof_shake128_state  state_tmp;
-	zend_string              *state;
 	zend_long                 domain = -1;
 	bool                      domain_is_null = 1;

@@ -4296,9 +4478,10 @@ PHP_FUNCTION(sodium_crypto_xof_shake128_init)
 		Z_PARAM_LONG_OR_NULL(domain, domain_is_null)
 	ZEND_PARSE_PARAMETERS_END();

-	memset(&state_tmp, 0, sizeof state_tmp);
+	object_init_ex(return_value, sodium_crypto_xof_shake128_state_ce);
+	php_sodium_crypto_xof_shake128_state *state = php_sodium_crypto_xof_shake128_state_from_obj(Z_OBJ_P(return_value));
 	if (domain_is_null) {
-		if (crypto_xof_shake128_init(&state_tmp) != 0) {
+		if (crypto_xof_shake128_init(&state->state) != 0) {
 			zend_throw_exception(sodium_exception_ce, "internal error", 0);
 			RETURN_THROWS();
 		}
@@ -4307,95 +4490,56 @@ PHP_FUNCTION(sodium_crypto_xof_shake128_init)
 			zend_argument_error(sodium_exception_ce, 1, "must be between 0x01 and 0x7f");
 			RETURN_THROWS();
 		}
-		if (crypto_xof_shake128_init_with_domain(&state_tmp, (unsigned char) domain) != 0) {
+		if (crypto_xof_shake128_init_with_domain(&state->state, (unsigned char) domain) != 0) {
 			zend_throw_exception(sodium_exception_ce, "internal error", 0);
 			RETURN_THROWS();
 		}
 	}
-	state = zend_string_alloc(sizeof state_tmp, 0);
-	memcpy(ZSTR_VAL(state), &state_tmp, sizeof state_tmp);
-	sodium_memzero(&state_tmp, sizeof state_tmp);
-	ZSTR_VAL(state)[sizeof state_tmp] = 0;
-	RETURN_STR(state);
 }

 PHP_FUNCTION(sodium_crypto_xof_shake128_update)
 {
-	crypto_xof_shake128_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) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os",
+							  &state_zv, sodium_crypto_xof_shake128_state_ce, &msg, &msg_len) == FAILURE) {
 		sodium_remove_param_values_from_backtrace(EG(exception));
 		RETURN_THROWS();
 	}
-	ZVAL_DEREF(state_zv);
-	if (Z_TYPE_P(state_zv) != IS_STRING) {
-		zend_argument_error(sodium_exception_ce, 1, "must be a reference to a state");
-		RETURN_THROWS();
-	}
-	sodium_separate_string(state_zv);
-	state = (unsigned char *) Z_STRVAL(*state_zv);
-	state_len = Z_STRLEN(*state_zv);
-	if (state_len != sizeof (crypto_xof_shake128_state)) {
-		zend_argument_error(sodium_exception_ce, 1, "must have a correct state length");
-		RETURN_THROWS();
-	}
-	memcpy(&state_tmp, state, sizeof state_tmp);
-	if (crypto_xof_shake128_update(&state_tmp, msg, (unsigned long long) msg_len) != 0) {
-		sodium_memzero(&state_tmp, sizeof state_tmp);
+
+	php_sodium_crypto_xof_shake128_state *state = php_sodium_crypto_xof_shake128_state_from_obj(Z_OBJ_P(state_zv));
+	if (crypto_xof_shake128_update(&state->state, msg, (unsigned long long) msg_len) != 0) {
 		zend_throw_exception(sodium_exception_ce, "internal error", 0);
 		RETURN_THROWS();
 	}
-	memcpy(state, &state_tmp, sizeof state_tmp);
-	sodium_memzero(&state_tmp, sizeof state_tmp);
-	RETURN_TRUE;
 }

 PHP_FUNCTION(sodium_crypto_xof_shake128_squeeze)
 {
-	crypto_xof_shake128_state  state_tmp;
 	zval                      *state_zv;
-	zend_string               *out;
-	unsigned char             *state;
 	zend_long                  out_len;
-	size_t                     state_len;

-	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl",
-							  &state_zv, &out_len) == FAILURE) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol",
+							  &state_zv, sodium_crypto_xof_shake128_state_ce, &out_len) == FAILURE) {
 		sodium_remove_param_values_from_backtrace(EG(exception));
 		RETURN_THROWS();
 	}
-	ZVAL_DEREF(state_zv);
-	if (Z_TYPE_P(state_zv) != IS_STRING) {
-		zend_argument_error(sodium_exception_ce, 1, "must be a reference to a state");
-		RETURN_THROWS();
-	}
-	sodium_separate_string(state_zv);
-	state = (unsigned char *) Z_STRVAL(*state_zv);
-	state_len = Z_STRLEN(*state_zv);
-	if (state_len != sizeof (crypto_xof_shake128_state)) {
-		zend_argument_error(sodium_exception_ce, 1, "must have a correct state length");
-		RETURN_THROWS();
-	}
-	if (out_len <= 0 || out_len > ZSTR_MAX_LEN) {
-		zend_argument_error(sodium_exception_ce, 2, "must be a positive integer");
+
+	if (out_len <= 0) {
+		zend_argument_error(sodium_exception_ce, 2, "must be greater than 0");
 		RETURN_THROWS();
 	}
-	out = zend_string_alloc((size_t) out_len, 0);
-	memcpy(&state_tmp, state, sizeof state_tmp);
-	if (crypto_xof_shake128_squeeze(&state_tmp, (unsigned char *) ZSTR_VAL(out), (size_t) out_len) != 0) {
+
+	php_sodium_crypto_xof_shake128_state *state = php_sodium_crypto_xof_shake128_state_from_obj(Z_OBJ_P(state_zv));
+
+	zend_string *out = zend_string_alloc((size_t) out_len, 0);
+	if (crypto_xof_shake128_squeeze(&state->state, (unsigned char *) ZSTR_VAL(out), (size_t) out_len) != 0) {
 		zend_string_efree(out);
-		sodium_memzero(&state_tmp, sizeof state_tmp);
 		zend_throw_exception(sodium_exception_ce, "internal error", 0);
 		RETURN_THROWS();
 	}
-	memcpy(state, &state_tmp, sizeof state_tmp);
-	sodium_memzero(&state_tmp, sizeof state_tmp);
 	ZSTR_VAL(out)[out_len] = 0;
 	RETURN_NEW_STR(out);
 }
@@ -4412,8 +4556,8 @@ PHP_FUNCTION(sodium_crypto_xof_shake256)
 		sodium_remove_param_values_from_backtrace(EG(exception));
 		RETURN_THROWS();
 	}
-	if (out_len <= 0 || out_len > ZSTR_MAX_LEN) {
-		zend_argument_error(sodium_exception_ce, 1, "must be a positive integer");
+	if (out_len <= 0) {
+		zend_argument_error(sodium_exception_ce, 1, "must be greater than 0");
 		RETURN_THROWS();
 	}
 	out = zend_string_alloc((size_t) out_len, 0);
@@ -4429,8 +4573,6 @@ PHP_FUNCTION(sodium_crypto_xof_shake256)

 PHP_FUNCTION(sodium_crypto_xof_shake256_init)
 {
-	crypto_xof_shake256_state  state_tmp;
-	zend_string              *state;
 	zend_long                 domain = -1;
 	bool                      domain_is_null = 1;

@@ -4439,9 +4581,10 @@ PHP_FUNCTION(sodium_crypto_xof_shake256_init)
 		Z_PARAM_LONG_OR_NULL(domain, domain_is_null)
 	ZEND_PARSE_PARAMETERS_END();

-	memset(&state_tmp, 0, sizeof state_tmp);
+	object_init_ex(return_value, sodium_crypto_xof_shake256_state_ce);
+	php_sodium_crypto_xof_shake256_state *state = php_sodium_crypto_xof_shake256_state_from_obj(Z_OBJ_P(return_value));
 	if (domain_is_null) {
-		if (crypto_xof_shake256_init(&state_tmp) != 0) {
+		if (crypto_xof_shake256_init(&state->state) != 0) {
 			zend_throw_exception(sodium_exception_ce, "internal error", 0);
 			RETURN_THROWS();
 		}
@@ -4450,95 +4593,55 @@ PHP_FUNCTION(sodium_crypto_xof_shake256_init)
 			zend_argument_error(sodium_exception_ce, 1, "must be between 0x01 and 0x7f");
 			RETURN_THROWS();
 		}
-		if (crypto_xof_shake256_init_with_domain(&state_tmp, (unsigned char) domain) != 0) {
+		if (crypto_xof_shake256_init_with_domain(&state->state, (unsigned char) domain) != 0) {
 			zend_throw_exception(sodium_exception_ce, "internal error", 0);
 			RETURN_THROWS();
 		}
 	}
-	state = zend_string_alloc(sizeof state_tmp, 0);
-	memcpy(ZSTR_VAL(state), &state_tmp, sizeof state_tmp);
-	sodium_memzero(&state_tmp, sizeof state_tmp);
-	ZSTR_VAL(state)[sizeof state_tmp] = 0;
-	RETURN_STR(state);
 }

 PHP_FUNCTION(sodium_crypto_xof_shake256_update)
 {
-	crypto_xof_shake256_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) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os",
+							  &state_zv, sodium_crypto_xof_shake256_state_ce, &msg, &msg_len) == FAILURE) {
 		sodium_remove_param_values_from_backtrace(EG(exception));
 		RETURN_THROWS();
 	}
-	ZVAL_DEREF(state_zv);
-	if (Z_TYPE_P(state_zv) != IS_STRING) {
-		zend_argument_error(sodium_exception_ce, 1, "must be a reference to a state");
-		RETURN_THROWS();
-	}
-	sodium_separate_string(state_zv);
-	state = (unsigned char *) Z_STRVAL(*state_zv);
-	state_len = Z_STRLEN(*state_zv);
-	if (state_len != sizeof (crypto_xof_shake256_state)) {
-		zend_argument_error(sodium_exception_ce, 1, "must have a correct state length");
-		RETURN_THROWS();
-	}
-	memcpy(&state_tmp, state, sizeof state_tmp);
-	if (crypto_xof_shake256_update(&state_tmp, msg, (unsigned long long) msg_len) != 0) {
-		sodium_memzero(&state_tmp, sizeof state_tmp);
+
+	php_sodium_crypto_xof_shake256_state *state = php_sodium_crypto_xof_shake256_state_from_obj(Z_OBJ_P(state_zv));
+	if (crypto_xof_shake256_update(&state->state, msg, (unsigned long long) msg_len) != 0) {
 		zend_throw_exception(sodium_exception_ce, "internal error", 0);
 		RETURN_THROWS();
 	}
-	memcpy(state, &state_tmp, sizeof state_tmp);
-	sodium_memzero(&state_tmp, sizeof state_tmp);
-	RETURN_TRUE;
 }

 PHP_FUNCTION(sodium_crypto_xof_shake256_squeeze)
 {
-	crypto_xof_shake256_state  state_tmp;
 	zval                      *state_zv;
-	zend_string               *out;
-	unsigned char             *state;
 	zend_long                  out_len;
-	size_t                     state_len;

-	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl",
-							  &state_zv, &out_len) == FAILURE) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol",
+							  &state_zv, sodium_crypto_xof_shake256_state_ce, &out_len) == FAILURE) {
 		sodium_remove_param_values_from_backtrace(EG(exception));
 		RETURN_THROWS();
 	}
-	ZVAL_DEREF(state_zv);
-	if (Z_TYPE_P(state_zv) != IS_STRING) {
-		zend_argument_error(sodium_exception_ce, 1, "must be a reference to a state");
-		RETURN_THROWS();
-	}
-	sodium_separate_string(state_zv);
-	state = (unsigned char *) Z_STRVAL(*state_zv);
-	state_len = Z_STRLEN(*state_zv);
-	if (state_len != sizeof (crypto_xof_shake256_state)) {
-		zend_argument_error(sodium_exception_ce, 1, "must have a correct state length");
-		RETURN_THROWS();
-	}
-	if (out_len <= 0 || out_len > ZSTR_MAX_LEN) {
-		zend_argument_error(sodium_exception_ce, 2, "must be a positive integer");
+	if (out_len <= 0) {
+		zend_argument_error(sodium_exception_ce, 2, "must be greater than 0");
 		RETURN_THROWS();
 	}
-	out = zend_string_alloc((size_t) out_len, 0);
-	memcpy(&state_tmp, state, sizeof state_tmp);
-	if (crypto_xof_shake256_squeeze(&state_tmp, (unsigned char *) ZSTR_VAL(out), (size_t) out_len) != 0) {
+
+	php_sodium_crypto_xof_shake256_state *state = php_sodium_crypto_xof_shake256_state_from_obj(Z_OBJ_P(state_zv));
+
+	zend_string *out = zend_string_alloc((size_t) out_len, 0);
+	if (crypto_xof_shake256_squeeze(&state->state, (unsigned char *) ZSTR_VAL(out), (size_t) out_len) != 0) {
 		zend_string_efree(out);
-		sodium_memzero(&state_tmp, sizeof state_tmp);
 		zend_throw_exception(sodium_exception_ce, "internal error", 0);
 		RETURN_THROWS();
 	}
-	memcpy(state, &state_tmp, sizeof state_tmp);
-	sodium_memzero(&state_tmp, sizeof state_tmp);
 	ZSTR_VAL(out)[out_len] = 0;
 	RETURN_NEW_STR(out);
 }
@@ -4555,8 +4658,8 @@ PHP_FUNCTION(sodium_crypto_xof_turboshake128)
 		sodium_remove_param_values_from_backtrace(EG(exception));
 		RETURN_THROWS();
 	}
-	if (out_len <= 0 || out_len > ZSTR_MAX_LEN) {
-		zend_argument_error(sodium_exception_ce, 1, "must be a positive integer");
+	if (out_len <= 0) {
+		zend_argument_error(sodium_exception_ce, 1, "must be greater than 0");
 		RETURN_THROWS();
 	}
 	out = zend_string_alloc((size_t) out_len, 0);
@@ -4572,8 +4675,6 @@ PHP_FUNCTION(sodium_crypto_xof_turboshake128)

 PHP_FUNCTION(sodium_crypto_xof_turboshake128_init)
 {
-	crypto_xof_turboshake128_state  state_tmp;
-	zend_string                    *state;
 	zend_long                       domain = -1;
 	bool                            domain_is_null = 1;

@@ -4582,9 +4683,10 @@ PHP_FUNCTION(sodium_crypto_xof_turboshake128_init)
 		Z_PARAM_LONG_OR_NULL(domain, domain_is_null)
 	ZEND_PARSE_PARAMETERS_END();

-	memset(&state_tmp, 0, sizeof state_tmp);
+	object_init_ex(return_value, sodium_crypto_xof_turboshake128_state_ce);
+	php_sodium_crypto_xof_turboshake128_state *state = php_sodium_crypto_xof_turboshake128_state_from_obj(Z_OBJ_P(return_value));
 	if (domain_is_null) {
-		if (crypto_xof_turboshake128_init(&state_tmp) != 0) {
+		if (crypto_xof_turboshake128_init(&state->state) != 0) {
 			zend_throw_exception(sodium_exception_ce, "internal error", 0);
 			RETURN_THROWS();
 		}
@@ -4593,95 +4695,56 @@ PHP_FUNCTION(sodium_crypto_xof_turboshake128_init)
 			zend_argument_error(sodium_exception_ce, 1, "must be between 0x01 and 0x7f");
 			RETURN_THROWS();
 		}
-		if (crypto_xof_turboshake128_init_with_domain(&state_tmp, (unsigned char) domain) != 0) {
+		if (crypto_xof_turboshake128_init_with_domain(&state->state, (unsigned char) domain) != 0) {
 			zend_throw_exception(sodium_exception_ce, "internal error", 0);
 			RETURN_THROWS();
 		}
 	}
-	state = zend_string_alloc(sizeof state_tmp, 0);
-	memcpy(ZSTR_VAL(state), &state_tmp, sizeof state_tmp);
-	sodium_memzero(&state_tmp, sizeof state_tmp);
-	ZSTR_VAL(state)[sizeof state_tmp] = 0;
-	RETURN_STR(state);
 }

 PHP_FUNCTION(sodium_crypto_xof_turboshake128_update)
 {
-	crypto_xof_turboshake128_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) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os",
+							  &state_zv, sodium_crypto_xof_turboshake128_state_ce, &msg, &msg_len) == FAILURE) {
 		sodium_remove_param_values_from_backtrace(EG(exception));
 		RETURN_THROWS();
 	}
-	ZVAL_DEREF(state_zv);
-	if (Z_TYPE_P(state_zv) != IS_STRING) {
-		zend_argument_error(sodium_exception_ce, 1, "must be a reference to a state");
-		RETURN_THROWS();
-	}
-	sodium_separate_string(state_zv);
-	state = (unsigned char *) Z_STRVAL(*state_zv);
-	state_len = Z_STRLEN(*state_zv);
-	if (state_len != sizeof (crypto_xof_turboshake128_state)) {
-		zend_argument_error(sodium_exception_ce, 1, "must have a correct state length");
-		RETURN_THROWS();
-	}
-	memcpy(&state_tmp, state, sizeof state_tmp);
-	if (crypto_xof_turboshake128_update(&state_tmp, msg, (unsigned long long) msg_len) != 0) {
-		sodium_memzero(&state_tmp, sizeof state_tmp);
+
+	php_sodium_crypto_xof_turboshake128_state *state = php_sodium_crypto_xof_turboshake128_state_from_obj(Z_OBJ_P(state_zv));
+	if (crypto_xof_turboshake128_update(&state->state, msg, (unsigned long long) msg_len) != 0) {
 		zend_throw_exception(sodium_exception_ce, "internal error", 0);
 		RETURN_THROWS();
 	}
-	memcpy(state, &state_tmp, sizeof state_tmp);
-	sodium_memzero(&state_tmp, sizeof state_tmp);
-	RETURN_TRUE;
 }

 PHP_FUNCTION(sodium_crypto_xof_turboshake128_squeeze)
 {
-	crypto_xof_turboshake128_state  state_tmp;
 	zval                           *state_zv;
-	zend_string                    *out;
-	unsigned char                  *state;
 	zend_long                       out_len;
-	size_t                          state_len;

-	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl",
-							  &state_zv, &out_len) == FAILURE) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol",
+							  &state_zv, sodium_crypto_xof_turboshake128_state_ce, &out_len) == FAILURE) {
 		sodium_remove_param_values_from_backtrace(EG(exception));
 		RETURN_THROWS();
 	}
-	ZVAL_DEREF(state_zv);
-	if (Z_TYPE_P(state_zv) != IS_STRING) {
-		zend_argument_error(sodium_exception_ce, 1, "must be a reference to a state");
-		RETURN_THROWS();
-	}
-	sodium_separate_string(state_zv);
-	state = (unsigned char *) Z_STRVAL(*state_zv);
-	state_len = Z_STRLEN(*state_zv);
-	if (state_len != sizeof (crypto_xof_turboshake128_state)) {
-		zend_argument_error(sodium_exception_ce, 1, "must have a correct state length");
-		RETURN_THROWS();
-	}
-	if (out_len <= 0 || out_len > ZSTR_MAX_LEN) {
-		zend_argument_error(sodium_exception_ce, 2, "must be a positive integer");
+
+	if (out_len <= 0) {
+		zend_argument_error(sodium_exception_ce, 2, "must be greater than 0");
 		RETURN_THROWS();
 	}
-	out = zend_string_alloc((size_t) out_len, 0);
-	memcpy(&state_tmp, state, sizeof state_tmp);
-	if (crypto_xof_turboshake128_squeeze(&state_tmp, (unsigned char *) ZSTR_VAL(out), (size_t) out_len) != 0) {
+
+	php_sodium_crypto_xof_turboshake128_state *state = php_sodium_crypto_xof_turboshake128_state_from_obj(Z_OBJ_P(state_zv));
+
+	zend_string *out = zend_string_alloc((size_t) out_len, 0);
+	if (crypto_xof_turboshake128_squeeze(&state->state, (unsigned char *) ZSTR_VAL(out), (size_t) out_len) != 0) {
 		zend_string_efree(out);
-		sodium_memzero(&state_tmp, sizeof state_tmp);
 		zend_throw_exception(sodium_exception_ce, "internal error", 0);
 		RETURN_THROWS();
 	}
-	memcpy(state, &state_tmp, sizeof state_tmp);
-	sodium_memzero(&state_tmp, sizeof state_tmp);
 	ZSTR_VAL(out)[out_len] = 0;
 	RETURN_NEW_STR(out);
 }
@@ -4698,8 +4761,8 @@ PHP_FUNCTION(sodium_crypto_xof_turboshake256)
 		sodium_remove_param_values_from_backtrace(EG(exception));
 		RETURN_THROWS();
 	}
-	if (out_len <= 0 || out_len > ZSTR_MAX_LEN) {
-		zend_argument_error(sodium_exception_ce, 1, "must be a positive integer");
+	if (out_len <= 0) {
+		zend_argument_error(sodium_exception_ce, 1, "must be greater than 0");
 		RETURN_THROWS();
 	}
 	out = zend_string_alloc((size_t) out_len, 0);
@@ -4715,8 +4778,6 @@ PHP_FUNCTION(sodium_crypto_xof_turboshake256)

 PHP_FUNCTION(sodium_crypto_xof_turboshake256_init)
 {
-	crypto_xof_turboshake256_state  state_tmp;
-	zend_string                    *state;
 	zend_long                       domain = -1;
 	bool                            domain_is_null = 1;

@@ -4725,9 +4786,10 @@ PHP_FUNCTION(sodium_crypto_xof_turboshake256_init)
 		Z_PARAM_LONG_OR_NULL(domain, domain_is_null)
 	ZEND_PARSE_PARAMETERS_END();

-	memset(&state_tmp, 0, sizeof state_tmp);
+	object_init_ex(return_value, sodium_crypto_xof_turboshake256_state_ce);
+	php_sodium_crypto_xof_turboshake256_state *state = php_sodium_crypto_xof_turboshake256_state_from_obj(Z_OBJ_P(return_value));
 	if (domain_is_null) {
-		if (crypto_xof_turboshake256_init(&state_tmp) != 0) {
+		if (crypto_xof_turboshake256_init(&state->state) != 0) {
 			zend_throw_exception(sodium_exception_ce, "internal error", 0);
 			RETURN_THROWS();
 		}
@@ -4736,95 +4798,56 @@ PHP_FUNCTION(sodium_crypto_xof_turboshake256_init)
 			zend_argument_error(sodium_exception_ce, 1, "must be between 0x01 and 0x7f");
 			RETURN_THROWS();
 		}
-		if (crypto_xof_turboshake256_init_with_domain(&state_tmp, (unsigned char) domain) != 0) {
+		if (crypto_xof_turboshake256_init_with_domain(&state->state, (unsigned char) domain) != 0) {
 			zend_throw_exception(sodium_exception_ce, "internal error", 0);
 			RETURN_THROWS();
 		}
 	}
-	state = zend_string_alloc(sizeof state_tmp, 0);
-	memcpy(ZSTR_VAL(state), &state_tmp, sizeof state_tmp);
-	sodium_memzero(&state_tmp, sizeof state_tmp);
-	ZSTR_VAL(state)[sizeof state_tmp] = 0;
-	RETURN_STR(state);
 }

 PHP_FUNCTION(sodium_crypto_xof_turboshake256_update)
 {
-	crypto_xof_turboshake256_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) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os",
+							  &state_zv, sodium_crypto_xof_turboshake256_state_ce, &msg, &msg_len) == FAILURE) {
 		sodium_remove_param_values_from_backtrace(EG(exception));
 		RETURN_THROWS();
 	}
-	ZVAL_DEREF(state_zv);
-	if (Z_TYPE_P(state_zv) != IS_STRING) {
-		zend_argument_error(sodium_exception_ce, 1, "must be a reference to a state");
-		RETURN_THROWS();
-	}
-	sodium_separate_string(state_zv);
-	state = (unsigned char *) Z_STRVAL(*state_zv);
-	state_len = Z_STRLEN(*state_zv);
-	if (state_len != sizeof (crypto_xof_turboshake256_state)) {
-		zend_argument_error(sodium_exception_ce, 1, "must have a correct state length");
-		RETURN_THROWS();
-	}
-	memcpy(&state_tmp, state, sizeof state_tmp);
-	if (crypto_xof_turboshake256_update(&state_tmp, msg, (unsigned long long) msg_len) != 0) {
-		sodium_memzero(&state_tmp, sizeof state_tmp);
+
+	php_sodium_crypto_xof_turboshake256_state *state = php_sodium_crypto_xof_turboshake256_state_from_obj(Z_OBJ_P(state_zv));
+	if (crypto_xof_turboshake256_update(&state->state, msg, (unsigned long long) msg_len) != 0) {
 		zend_throw_exception(sodium_exception_ce, "internal error", 0);
 		RETURN_THROWS();
 	}
-	memcpy(state, &state_tmp, sizeof state_tmp);
-	sodium_memzero(&state_tmp, sizeof state_tmp);
-	RETURN_TRUE;
 }

 PHP_FUNCTION(sodium_crypto_xof_turboshake256_squeeze)
 {
-	crypto_xof_turboshake256_state  state_tmp;
 	zval                           *state_zv;
-	zend_string                    *out;
-	unsigned char                  *state;
 	zend_long                       out_len;
-	size_t                          state_len;

-	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl",
-							  &state_zv, &out_len) == FAILURE) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol",
+							  &state_zv, sodium_crypto_xof_turboshake256_state_ce, &out_len) == FAILURE) {
 		sodium_remove_param_values_from_backtrace(EG(exception));
 		RETURN_THROWS();
 	}
-	ZVAL_DEREF(state_zv);
-	if (Z_TYPE_P(state_zv) != IS_STRING) {
-		zend_argument_error(sodium_exception_ce, 1, "must be a reference to a state");
-		RETURN_THROWS();
-	}
-	sodium_separate_string(state_zv);
-	state = (unsigned char *) Z_STRVAL(*state_zv);
-	state_len = Z_STRLEN(*state_zv);
-	if (state_len != sizeof (crypto_xof_turboshake256_state)) {
-		zend_argument_error(sodium_exception_ce, 1, "must have a correct state length");
-		RETURN_THROWS();
-	}
-	if (out_len <= 0 || out_len > ZSTR_MAX_LEN) {
-		zend_argument_error(sodium_exception_ce, 2, "must be a positive integer");
+
+	if (out_len <= 0) {
+		zend_argument_error(sodium_exception_ce, 2, "must be greater than 0");
 		RETURN_THROWS();
 	}
-	out = zend_string_alloc((size_t) out_len, 0);
-	memcpy(&state_tmp, state, sizeof state_tmp);
-	if (crypto_xof_turboshake256_squeeze(&state_tmp, (unsigned char *) ZSTR_VAL(out), (size_t) out_len) != 0) {
+
+	php_sodium_crypto_xof_turboshake256_state *state = php_sodium_crypto_xof_turboshake256_state_from_obj(Z_OBJ_P(state_zv));
+
+	zend_string *out = zend_string_alloc((size_t) out_len, 0);
+	if (crypto_xof_turboshake256_squeeze(&state->state, (unsigned char *) ZSTR_VAL(out), (size_t) out_len) != 0) {
 		zend_string_efree(out);
-		sodium_memzero(&state_tmp, sizeof state_tmp);
 		zend_throw_exception(sodium_exception_ce, "internal error", 0);
 		RETURN_THROWS();
 	}
-	memcpy(state, &state_tmp, sizeof state_tmp);
-	sodium_memzero(&state_tmp, sizeof state_tmp);
 	ZSTR_VAL(out)[out_len] = 0;
 	RETURN_NEW_STR(out);
 }
diff --git a/ext/sodium/libsodium.stub.php b/ext/sodium/libsodium.stub.php
index f1fefefe65e..a9b6d345c66 100644
--- a/ext/sodium/libsodium.stub.php
+++ b/ext/sodium/libsodium.stub.php
@@ -936,35 +936,67 @@ function sodium_ip2bin(string $ip): string {}

 function sodium_crypto_xof_shake128(int $length, string $message): string {}

-function sodium_crypto_xof_shake128_init(?int $domain = null): string {}
+function sodium_crypto_xof_shake128_init(?int $domain = null): SodiumCryptoXofShake128State {}

-function sodium_crypto_xof_shake128_update(string &$state, string $message): true {}
+function sodium_crypto_xof_shake128_update(SodiumCryptoXofShake128State $state, string $message): void {}

-function sodium_crypto_xof_shake128_squeeze(string &$state, int $length): string {}
+function sodium_crypto_xof_shake128_squeeze(SodiumCryptoXofShake128State $state, int $length): string {}
+
+/**
+ * @strict-properties
+ * @not-serializable
+ */
+final class SodiumCryptoXofShake128State {
+
+}

 function sodium_crypto_xof_shake256(int $length, string $message): string {}

-function sodium_crypto_xof_shake256_init(?int $domain = null): string {}
+function sodium_crypto_xof_shake256_init(?int $domain = null): SodiumCryptoXofShake256State {}

-function sodium_crypto_xof_shake256_update(string &$state, string $message): true {}
+function sodium_crypto_xof_shake256_update(SodiumCryptoXofShake256State $state, string $message): void {}
+
+function sodium_crypto_xof_shake256_squeeze(SodiumCryptoXofShake256State $state, int $length): string {}
+
+/**
+ * @strict-properties
+ * @not-serializable
+ */
+final class SodiumCryptoXofShake256State {

-function sodium_crypto_xof_shake256_squeeze(string &$state, int $length): string {}
+}

 function sodium_crypto_xof_turboshake128(int $length, string $message): string {}

-function sodium_crypto_xof_turboshake128_init(?int $domain = null): string {}
+function sodium_crypto_xof_turboshake128_init(?int $domain = null): SodiumCryptoXofTurboshake128State {}

-function sodium_crypto_xof_turboshake128_update(string &$state, string $message): true {}
+function sodium_crypto_xof_turboshake128_update(SodiumCryptoXofTurboshake128State $state, string $message): void {}

-function sodium_crypto_xof_turboshake128_squeeze(string &$state, int $length): string {}
+function sodium_crypto_xof_turboshake128_squeeze(SodiumCryptoXofTurboshake128State $state, int $length): string {}
+
+/**
+ * @strict-properties
+ * @not-serializable
+ */
+final class SodiumCryptoXofTurboshake128State {
+
+}

 function sodium_crypto_xof_turboshake256(int $length, string $message): string {}

-function sodium_crypto_xof_turboshake256_init(?int $domain = null): string {}
+function sodium_crypto_xof_turboshake256_init(?int $domain = null): SodiumCryptoXofTurboshake256State {}

-function sodium_crypto_xof_turboshake256_update(string &$state, string $message): true {}
+function sodium_crypto_xof_turboshake256_update(SodiumCryptoXofTurboshake256State $state, string $message): void {}
+
+function sodium_crypto_xof_turboshake256_squeeze(SodiumCryptoXofTurboshake256State $state, int $length): string {}
+
+/**
+ * @strict-properties
+ * @not-serializable
+ */
+final class SodiumCryptoXofTurboshake256State {

-function sodium_crypto_xof_turboshake256_squeeze(string &$state, int $length): string {}
+}
 #endif

 #ifdef crypto_kem_PUBLICKEYBYTES
diff --git a/ext/sodium/libsodium_arginfo.h b/ext/sodium/libsodium_arginfo.h
index d13759fd75f..ac6f90ef603 100644
Binary files a/ext/sodium/libsodium_arginfo.h and b/ext/sodium/libsodium_arginfo.h differ
diff --git a/ext/sodium/tests/crypto_xof_shake128.phpt b/ext/sodium/tests/crypto_xof_shake128.phpt
index cb075b7ec28..c25e4328df8 100644
--- a/ext/sodium/tests/crypto_xof_shake128.phpt
+++ b/ext/sodium/tests/crypto_xof_shake128.phpt
@@ -78,13 +78,6 @@
     echo $e->getMessage() . "\n";
 }

-/* Error: bad state */
-try {
-    $bad = "not_a_state";
-    sodium_crypto_xof_shake128_update($bad, "test");
-} catch (SodiumException $e) {
-    echo $e->getMessage() . "\n";
-}
 ?>
 --EXPECT--
 string(64) "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26"
@@ -97,8 +90,7 @@
 string(64) "5881092dd818bf5cf8a3ddb793fbcba74097d5c526a6d35f97b83351940f2cc8"
 string(64) "f3a3a89c329e644a7d2351744d9a28c953698b64102e912085ce1f6d79fa311e"
 bool(true)
-sodium_crypto_xof_shake128(): Argument #1 ($length) must be a positive integer
-sodium_crypto_xof_shake128(): Argument #1 ($length) must be a positive integer
+sodium_crypto_xof_shake128(): Argument #1 ($length) must be greater than 0
+sodium_crypto_xof_shake128(): Argument #1 ($length) must be greater than 0
 sodium_crypto_xof_shake128_init(): Argument #1 ($domain) must be between 0x01 and 0x7f
 sodium_crypto_xof_shake128_init(): Argument #1 ($domain) must be between 0x01 and 0x7f
-sodium_crypto_xof_shake128_update(): Argument #1 ($state) must have a correct state length
diff --git a/ext/sodium/tests/crypto_xof_shake256.phpt b/ext/sodium/tests/crypto_xof_shake256.phpt
index 3e691671332..96a8b22c8e2 100644
--- a/ext/sodium/tests/crypto_xof_shake256.phpt
+++ b/ext/sodium/tests/crypto_xof_shake256.phpt
@@ -49,14 +49,6 @@
     echo $e->getMessage() . "\n";
 }

-/* Error: bad state */
-try {
-    $bad = "not_a_state";
-    sodium_crypto_xof_shake256_update($bad, "test");
-} catch (SodiumException $e) {
-    echo $e->getMessage() . "\n";
-}
-
 /* Error: squeeze bad length */
 try {
     $state = sodium_crypto_xof_shake256_init();
@@ -75,6 +67,5 @@
 string(64) "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739"
 string(64) "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739"
 string(64) "1259d63c872d50fee4500685419f489966971c0a77e2058fab0e48dfb12d24bc"
-sodium_crypto_xof_shake256(): Argument #1 ($length) must be a positive integer
-sodium_crypto_xof_shake256_update(): Argument #1 ($state) must have a correct state length
-sodium_crypto_xof_shake256_squeeze(): Argument #2 ($length) must be a positive integer
+sodium_crypto_xof_shake256(): Argument #1 ($length) must be greater than 0
+sodium_crypto_xof_shake256_squeeze(): Argument #2 ($length) must be greater than 0
diff --git a/ext/sodium/tests/crypto_xof_turboshake.phpt b/ext/sodium/tests/crypto_xof_turboshake.phpt
index 1f404140cc0..e65f7c30356 100644
--- a/ext/sodium/tests/crypto_xof_turboshake.phpt
+++ b/ext/sodium/tests/crypto_xof_turboshake.phpt
@@ -72,14 +72,6 @@
     echo $e->getMessage() . "\n";
 }

-/* TurboSHAKE256 error: bad state */
-try {
-    $bad = "not_a_state";
-    sodium_crypto_xof_turboshake256_update($bad, "test");
-} catch (SodiumException $e) {
-    echo $e->getMessage() . "\n";
-}
-
 /* TurboSHAKE128 error: domain out of range */
 try {
     sodium_crypto_xof_turboshake128_init(0);
@@ -106,7 +98,6 @@
 int(16)
 int(64)
 bool(true)
-sodium_crypto_xof_turboshake128(): Argument #1 ($length) must be a positive integer
-sodium_crypto_xof_turboshake256_update(): Argument #1 ($state) must have a correct state length
+sodium_crypto_xof_turboshake128(): Argument #1 ($length) must be greater than 0
 sodium_crypto_xof_turboshake128_init(): Argument #1 ($domain) must be between 0x01 and 0x7f
 sodium_crypto_xof_turboshake256_init(): Argument #1 ($domain) must be between 0x01 and 0x7f