diff --git a/ext/sodium/libsodium.c b/ext/sodium/libsodium.c index 7cdae32468ee7..e984f968288f2 100644 --- a/ext/sodium/libsodium.c +++ b/ext/sodium/libsodium.c @@ -37,6 +37,19 @@ static zend_class_entry *sodium_exception_ce; # define HAVE_AESGCM 1 #endif +static zend_always_inline zend_string *zend_string_checked_alloc(size_t len, int persistent) +{ + zend_string *zs; + + if (ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(len)) < len) { + zend_error_noreturn(E_ERROR, "Memory allocation too large (%zu bytes)", len); + } + zs = zend_string_alloc(len, persistent); + ZSTR_VAL(zs)[len] = 0; + + return zs; +} + #include "libsodium_arginfo.h" #ifndef crypto_aead_chacha20poly1305_IETF_KEYBYTES @@ -335,6 +348,13 @@ PHP_MINIT_FUNCTION(sodium) crypto_stream_NONCEBYTES, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_STREAM_KEYBYTES", crypto_stream_KEYBYTES, CONST_CS | CONST_PERSISTENT); + +#ifdef crypto_stream_xchacha20_KEYBYTES + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES", + crypto_stream_xchacha20_NONCEBYTES, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES", + crypto_stream_xchacha20_KEYBYTES, CONST_CS | CONST_PERSISTENT); +#endif #ifdef sodium_base64_VARIANT_ORIGINAL REGISTER_LONG_CONSTANT("SODIUM_BASE64_VARIANT_ORIGINAL", sodium_base64_VARIANT_ORIGINAL, CONST_CS | CONST_PERSISTENT); @@ -1465,6 +1485,87 @@ PHP_FUNCTION(sodium_crypto_stream_xor) RETURN_NEW_STR(ciphertext); } +#ifdef crypto_stream_xchacha20_KEYBYTES +PHP_FUNCTION(sodium_crypto_stream_xchacha20) +{ + 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) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (ciphertext_len <= 0 || ciphertext_len >= SIZE_MAX) { + zend_argument_error(sodium_exception_ce, 1, "length must be greater than 0"); + RETURN_THROWS(); + } + if (nonce_len != crypto_stream_xchacha20_NONCEBYTES) { + zend_argument_error(sodium_exception_ce, 2, "nonce must be SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES bytes long"); + RETURN_THROWS(); + } + if (key_len != crypto_stream_xchacha20_KEYBYTES) { + zend_argument_error(sodium_exception_ce, 3, "key must be SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES bytes long"); + RETURN_THROWS(); + } + ciphertext = zend_string_checked_alloc((size_t) ciphertext_len, 0); + if (crypto_stream_xchacha20((unsigned char *) ZSTR_VAL(ciphertext), + (unsigned long long) ciphertext_len, nonce, key) != 0) { + zend_string_free(ciphertext); + zend_throw_exception(sodium_exception_ce, "internal error", 0); + RETURN_THROWS(); + } + ZSTR_VAL(ciphertext)[ciphertext_len] = 0; + + RETURN_NEW_STR(ciphertext); +} + +PHP_FUNCTION(sodium_crypto_stream_xchacha20_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) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (nonce_len != crypto_stream_xchacha20_NONCEBYTES) { + zend_argument_error(sodium_exception_ce, 2, "nonce must be SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES bytes long"); + RETURN_THROWS(); + } + if (key_len != crypto_stream_xchacha20_KEYBYTES) { + zend_argument_error(sodium_exception_ce, 3, "key must be SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES bytes long"); + RETURN_THROWS(); + } + ciphertext_len = msg_len; + ciphertext = zend_string_checked_alloc((size_t) ciphertext_len, 0); + if (crypto_stream_xchacha20_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, "internal error", 0); + RETURN_THROWS(); + } + ZSTR_VAL(ciphertext)[ciphertext_len] = 0; + + RETURN_NEW_STR(ciphertext); +} +#endif + #ifdef crypto_pwhash_SALTBYTES PHP_FUNCTION(sodium_crypto_pwhash) { @@ -2894,6 +2995,18 @@ PHP_FUNCTION(sodium_crypto_stream_keygen) randombytes_buf(key, sizeof key); RETURN_STRINGL((const char *) key, sizeof key); } +#ifdef crypto_stream_xchacha20_KEYBYTES +PHP_FUNCTION(sodium_crypto_stream_xchacha20_keygen) +{ + unsigned char key[crypto_stream_xchacha20_KEYBYTES]; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + randombytes_buf(key, sizeof key); + RETURN_STRINGL((const char *) key, sizeof key); +} +#endif PHP_FUNCTION(sodium_crypto_kdf_derive_from_key) { diff --git a/ext/sodium/libsodium.stub.php b/ext/sodium/libsodium.stub.php index a4029d04d09b7..c6388eca87781 100644 --- a/ext/sodium/libsodium.stub.php +++ b/ext/sodium/libsodium.stub.php @@ -160,6 +160,14 @@ function sodium_crypto_stream_keygen(): string {} function sodium_crypto_stream_xor(string $message, string $nonce, string $key): string {} +#if defined(crypto_stream_xchacha20_KEYBYTES) +function sodium_crypto_stream_xchacha20(int $length, string $nonce, string $key) : string {} + +function sodium_crypto_stream_xchacha20_keygen() : string {} + +function sodium_crypto_stream_xchacha20_xor(string $message, string $nonce, string $key) : string {} +#endif + function sodium_add(string &$string1, string $string2): void {} function sodium_compare(string $string1, string $string2): int {} diff --git a/ext/sodium/libsodium_arginfo.h b/ext/sodium/libsodium_arginfo.h index aad970b37ee53..1f9069f1c51c8 100644 --- a/ext/sodium/libsodium_arginfo.h +++ b/ext/sodium/libsodium_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 457c4c5a0243f815d859bdc9728709b4a8dc84d7 */ + * Stub hash: 55ce0e93db5fac4311ba90693668a92001167573 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_aead_aes256gcm_is_available, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -348,6 +348,27 @@ ZEND_END_ARG_INFO() #define arginfo_sodium_crypto_stream_xor arginfo_sodium_crypto_secretbox +#if defined(crypto_stream_xchacha20_KEYBYTES) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_stream_xchacha20, 0, 3, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, nonce, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) +ZEND_END_ARG_INFO() +#endif + +#if defined(crypto_stream_xchacha20_KEYBYTES) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_stream_xchacha20_keygen, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() +#endif + +#if defined(crypto_stream_xchacha20_KEYBYTES) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_stream_xchacha20_xor, 0, 3, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, nonce, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_add, 0, 2, IS_VOID, 0) ZEND_ARG_TYPE_INFO(1, string1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, string2, IS_STRING, 0) @@ -514,6 +535,15 @@ ZEND_FUNCTION(sodium_crypto_sign_verify_detached); ZEND_FUNCTION(sodium_crypto_stream); ZEND_FUNCTION(sodium_crypto_stream_keygen); ZEND_FUNCTION(sodium_crypto_stream_xor); +#if defined(crypto_stream_xchacha20_KEYBYTES) +ZEND_FUNCTION(sodium_crypto_stream_xchacha20); +#endif +#if defined(crypto_stream_xchacha20_KEYBYTES) +ZEND_FUNCTION(sodium_crypto_stream_xchacha20_keygen); +#endif +#if defined(crypto_stream_xchacha20_KEYBYTES) +ZEND_FUNCTION(sodium_crypto_stream_xchacha20_xor); +#endif ZEND_FUNCTION(sodium_add); ZEND_FUNCTION(sodium_compare); ZEND_FUNCTION(sodium_increment); @@ -643,6 +673,15 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(sodium_crypto_stream, arginfo_sodium_crypto_stream) ZEND_FE(sodium_crypto_stream_keygen, arginfo_sodium_crypto_stream_keygen) ZEND_FE(sodium_crypto_stream_xor, arginfo_sodium_crypto_stream_xor) +#if defined(crypto_stream_xchacha20_KEYBYTES) + ZEND_FE(sodium_crypto_stream_xchacha20, arginfo_sodium_crypto_stream_xchacha20) +#endif +#if defined(crypto_stream_xchacha20_KEYBYTES) + ZEND_FE(sodium_crypto_stream_xchacha20_keygen, arginfo_sodium_crypto_stream_xchacha20_keygen) +#endif +#if defined(crypto_stream_xchacha20_KEYBYTES) + ZEND_FE(sodium_crypto_stream_xchacha20_xor, arginfo_sodium_crypto_stream_xchacha20_xor) +#endif ZEND_FE(sodium_add, arginfo_sodium_add) ZEND_FE(sodium_compare, arginfo_sodium_compare) ZEND_FE(sodium_increment, arginfo_sodium_increment) diff --git a/ext/sodium/php_libsodium.h b/ext/sodium/php_libsodium.h index 1a8d3ef9071f4..d9db3d17e9048 100644 --- a/ext/sodium/php_libsodium.h +++ b/ext/sodium/php_libsodium.h @@ -112,6 +112,9 @@ PHP_FUNCTION(sodium_crypto_sign_verify_detached); PHP_FUNCTION(sodium_crypto_stream); PHP_FUNCTION(sodium_crypto_stream_keygen); PHP_FUNCTION(sodium_crypto_stream_xor); +PHP_FUNCTION(sodium_crypto_stream_xchacha20); +PHP_FUNCTION(sodium_crypto_stream_xchacha20_keygen); +PHP_FUNCTION(sodium_crypto_stream_xchacha20_xor); PHP_FUNCTION(sodium_hex2bin); PHP_FUNCTION(sodium_increment); PHP_FUNCTION(sodium_memcmp); diff --git a/ext/sodium/tests/crypto_stream_xchacha20.phpt b/ext/sodium/tests/crypto_stream_xchacha20.phpt new file mode 100644 index 0000000000000..3800e2a7164a0 --- /dev/null +++ b/ext/sodium/tests/crypto_stream_xchacha20.phpt @@ -0,0 +1,52 @@ +--TEST-- +Check for libsodium stream +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(100) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true)