diff --git a/NEWS b/NEWS index 2e7adae0d951..eeddc9b08ffb 100644 --- a/NEWS +++ b/NEWS @@ -81,6 +81,8 @@ PHP NEWS session.use_strict_mode applies to the built-in handler. (Girgias) - Sodium: + . Added support for the libsodium 1.0.22 KEM APIs (X-Wing and ML-KEM768). + (Zachary DuBois) . Fixed incorrect parameter name in sodium_add(), sodium_memcmp(), and sodium_compare() length-mismatch error messages. (lacatoire) diff --git a/UPGRADING b/UPGRADING index b55cb53a2bad..3c33602250fe 100644 --- a/UPGRADING +++ b/UPGRADING @@ -660,6 +660,20 @@ PHP 8.6 UPGRADE NOTES . ReflectionAttribute::getNamespaceName() . ReflectionAttribute::getShortName() +- Sodium: + . sodium_crypto_kem_keypair(), sodium_crypto_kem_seed_keypair(), + sodium_crypto_kem_secretkey(), sodium_crypto_kem_publickey(), + sodium_crypto_kem_enc() and sodium_crypto_kem_dec() expose the X-Wing + KEM (hybrid ML-KEM768+X25519, libsodium's recommended KEM). + Available when PHP is built against libsodium >= 1.0.22. + . sodium_crypto_kem_mlkem768_keypair(), + sodium_crypto_kem_mlkem768_seed_keypair(), + sodium_crypto_kem_mlkem768_secretkey(), + sodium_crypto_kem_mlkem768_publickey(), + sodium_crypto_kem_mlkem768_enc() and sodium_crypto_kem_mlkem768_dec() + expose the ML-KEM768 (FIPS 203) KEM. + Available when PHP is built against libsodium >= 1.0.22. + - Standard: . clamp() returns the given value if in range, else return the nearest bound. @@ -786,6 +800,20 @@ PHP 8.6 UPGRADE NOTES . EAI_INTR. . EAI_IDN_ENCODE. +- Sodium: + . SODIUM_CRYPTO_KEM_PUBLICKEYBYTES (libsodium >= 1.0.22). + . SODIUM_CRYPTO_KEM_SECRETKEYBYTES (libsodium >= 1.0.22). + . SODIUM_CRYPTO_KEM_CIPHERTEXTBYTES (libsodium >= 1.0.22). + . SODIUM_CRYPTO_KEM_SHAREDSECRETBYTES (libsodium >= 1.0.22). + . SODIUM_CRYPTO_KEM_SEEDBYTES (libsodium >= 1.0.22). + . SODIUM_CRYPTO_KEM_KEYPAIRBYTES (libsodium >= 1.0.22). + . SODIUM_CRYPTO_KEM_MLKEM768_PUBLICKEYBYTES (libsodium >= 1.0.22). + . SODIUM_CRYPTO_KEM_MLKEM768_SECRETKEYBYTES (libsodium >= 1.0.22). + . SODIUM_CRYPTO_KEM_MLKEM768_CIPHERTEXTBYTES (libsodium >= 1.0.22). + . SODIUM_CRYPTO_KEM_MLKEM768_SHAREDSECRETBYTES (libsodium >= 1.0.22). + . SODIUM_CRYPTO_KEM_MLKEM768_SEEDBYTES (libsodium >= 1.0.22). + . SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES (libsodium >= 1.0.22). + - Standard . ARRAY_FILTER_USE_VALUE. . STREAM_CRYPTO_STATUS_NONE. diff --git a/ext/sodium/libsodium.c b/ext/sodium/libsodium.c index 7b8f41f2f4bb..4eb0cdccb30a 100644 --- a/ext/sodium/libsodium.c +++ b/ext/sodium/libsodium.c @@ -4830,3 +4830,333 @@ PHP_FUNCTION(sodium_crypto_xof_turboshake256_squeeze) RETURN_NEW_STR(out); } #endif + +#ifdef crypto_kem_PUBLICKEYBYTES +PHP_FUNCTION(sodium_crypto_kem_keypair) +{ + zend_string *keypair; + size_t keypair_len; + + ZEND_PARSE_PARAMETERS_NONE(); + + keypair_len = crypto_kem_SECRETKEYBYTES + crypto_kem_PUBLICKEYBYTES; + keypair = zend_string_alloc(keypair_len, 0); + if (crypto_kem_keypair((unsigned char *) ZSTR_VAL(keypair) + + crypto_kem_SECRETKEYBYTES, + (unsigned char *) ZSTR_VAL(keypair)) != 0) { + sodium_memzero(ZSTR_VAL(keypair), keypair_len); + zend_string_efree(keypair); + zend_throw_exception(sodium_exception_ce, "internal error", 0); + RETURN_THROWS(); + } + ZSTR_VAL(keypair)[keypair_len] = 0; + + RETURN_NEW_STR(keypair); +} + +PHP_FUNCTION(sodium_crypto_kem_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) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (seed_len != crypto_kem_SEEDBYTES) { + zend_argument_error(sodium_exception_ce, 1, "must be SODIUM_CRYPTO_KEM_SEEDBYTES bytes long"); + RETURN_THROWS(); + } + keypair_len = crypto_kem_SECRETKEYBYTES + crypto_kem_PUBLICKEYBYTES; + keypair = zend_string_alloc(keypair_len, 0); + if (crypto_kem_seed_keypair((unsigned char *) ZSTR_VAL(keypair) + + crypto_kem_SECRETKEYBYTES, + (unsigned char *) ZSTR_VAL(keypair), + seed) != 0) { + sodium_memzero(ZSTR_VAL(keypair), keypair_len); + zend_string_efree(keypair); + zend_throw_exception(sodium_exception_ce, "internal error", 0); + RETURN_THROWS(); + } + ZSTR_VAL(keypair)[keypair_len] = 0; + + RETURN_NEW_STR(keypair); +} + +PHP_FUNCTION(sodium_crypto_kem_secretkey) +{ + zend_string *secretkey; + unsigned char *keypair; + size_t keypair_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", + &keypair, &keypair_len) == FAILURE) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (keypair_len != + crypto_kem_SECRETKEYBYTES + crypto_kem_PUBLICKEYBYTES) { + zend_argument_error(sodium_exception_ce, 1, "must be SODIUM_CRYPTO_KEM_KEYPAIRBYTES bytes long"); + RETURN_THROWS(); + } + secretkey = zend_string_alloc(crypto_kem_SECRETKEYBYTES, 0); + memcpy(ZSTR_VAL(secretkey), keypair, crypto_kem_SECRETKEYBYTES); + ZSTR_VAL(secretkey)[crypto_kem_SECRETKEYBYTES] = 0; + + RETURN_STR(secretkey); +} + +PHP_FUNCTION(sodium_crypto_kem_publickey) +{ + zend_string *publickey; + unsigned char *keypair; + size_t keypair_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", + &keypair, &keypair_len) == FAILURE) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (keypair_len != + crypto_kem_SECRETKEYBYTES + crypto_kem_PUBLICKEYBYTES) { + zend_argument_error(sodium_exception_ce, 1, "must be SODIUM_CRYPTO_KEM_KEYPAIRBYTES bytes long"); + RETURN_THROWS(); + } + publickey = zend_string_alloc(crypto_kem_PUBLICKEYBYTES, 0); + memcpy(ZSTR_VAL(publickey), keypair + crypto_kem_SECRETKEYBYTES, + crypto_kem_PUBLICKEYBYTES); + ZSTR_VAL(publickey)[crypto_kem_PUBLICKEYBYTES] = 0; + + RETURN_STR(publickey); +} + +PHP_FUNCTION(sodium_crypto_kem_enc) +{ + unsigned char ciphertext[crypto_kem_CIPHERTEXTBYTES]; + unsigned char shared_secret[crypto_kem_SHAREDSECRETBYTES]; + unsigned char *publickey; + size_t publickey_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", + &publickey, &publickey_len) == FAILURE) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (publickey_len != crypto_kem_PUBLICKEYBYTES) { + zend_argument_error(sodium_exception_ce, 1, "must be SODIUM_CRYPTO_KEM_PUBLICKEYBYTES bytes long"); + RETURN_THROWS(); + } + if (crypto_kem_enc(ciphertext, shared_secret, publickey) != 0) { + sodium_memzero(shared_secret, sizeof shared_secret); + zend_throw_exception(sodium_exception_ce, "internal error", 0); + RETURN_THROWS(); + } + array_init(return_value); + add_next_index_stringl(return_value, (const char *) ciphertext, sizeof ciphertext); + add_next_index_stringl(return_value, (const char *) shared_secret, sizeof shared_secret); + sodium_memzero(shared_secret, sizeof shared_secret); +} + +PHP_FUNCTION(sodium_crypto_kem_dec) +{ + zend_string *shared_secret; + unsigned char *ciphertext; + unsigned char *secretkey; + size_t ciphertext_len; + size_t secretkey_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", + &ciphertext, &ciphertext_len, + &secretkey, &secretkey_len) == FAILURE) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (ciphertext_len != crypto_kem_CIPHERTEXTBYTES) { + zend_argument_error(sodium_exception_ce, 1, "must be SODIUM_CRYPTO_KEM_CIPHERTEXTBYTES bytes long"); + RETURN_THROWS(); + } + if (secretkey_len != crypto_kem_SECRETKEYBYTES) { + zend_argument_error(sodium_exception_ce, 2, "must be SODIUM_CRYPTO_KEM_SECRETKEYBYTES bytes long"); + RETURN_THROWS(); + } + shared_secret = zend_string_alloc(crypto_kem_SHAREDSECRETBYTES, 0); + if (crypto_kem_dec((unsigned char *) ZSTR_VAL(shared_secret), + ciphertext, secretkey) != 0) { + sodium_memzero(ZSTR_VAL(shared_secret), crypto_kem_SHAREDSECRETBYTES); + zend_string_efree(shared_secret); + zend_throw_exception(sodium_exception_ce, "internal error", 0); + RETURN_THROWS(); + } + ZSTR_VAL(shared_secret)[crypto_kem_SHAREDSECRETBYTES] = 0; + + RETURN_NEW_STR(shared_secret); +} +#endif + +#ifdef crypto_kem_mlkem768_PUBLICKEYBYTES +PHP_FUNCTION(sodium_crypto_kem_mlkem768_keypair) +{ + zend_string *keypair; + size_t keypair_len; + + ZEND_PARSE_PARAMETERS_NONE(); + + keypair_len = crypto_kem_mlkem768_SECRETKEYBYTES + crypto_kem_mlkem768_PUBLICKEYBYTES; + keypair = zend_string_alloc(keypair_len, 0); + if (crypto_kem_mlkem768_keypair((unsigned char *) ZSTR_VAL(keypair) + + crypto_kem_mlkem768_SECRETKEYBYTES, + (unsigned char *) ZSTR_VAL(keypair)) != 0) { + sodium_memzero(ZSTR_VAL(keypair), keypair_len); + zend_string_efree(keypair); + zend_throw_exception(sodium_exception_ce, "internal error", 0); + RETURN_THROWS(); + } + ZSTR_VAL(keypair)[keypair_len] = 0; + + RETURN_NEW_STR(keypair); +} + +PHP_FUNCTION(sodium_crypto_kem_mlkem768_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) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (seed_len != crypto_kem_mlkem768_SEEDBYTES) { + zend_argument_error(sodium_exception_ce, 1, "must be SODIUM_CRYPTO_KEM_MLKEM768_SEEDBYTES bytes long"); + RETURN_THROWS(); + } + keypair_len = crypto_kem_mlkem768_SECRETKEYBYTES + crypto_kem_mlkem768_PUBLICKEYBYTES; + keypair = zend_string_alloc(keypair_len, 0); + if (crypto_kem_mlkem768_seed_keypair((unsigned char *) ZSTR_VAL(keypair) + + crypto_kem_mlkem768_SECRETKEYBYTES, + (unsigned char *) ZSTR_VAL(keypair), + seed) != 0) { + sodium_memzero(ZSTR_VAL(keypair), keypair_len); + zend_string_efree(keypair); + zend_throw_exception(sodium_exception_ce, "internal error", 0); + RETURN_THROWS(); + } + ZSTR_VAL(keypair)[keypair_len] = 0; + + RETURN_NEW_STR(keypair); +} + +PHP_FUNCTION(sodium_crypto_kem_mlkem768_secretkey) +{ + zend_string *secretkey; + unsigned char *keypair; + size_t keypair_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", + &keypair, &keypair_len) == FAILURE) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (keypair_len != + crypto_kem_mlkem768_SECRETKEYBYTES + crypto_kem_mlkem768_PUBLICKEYBYTES) { + zend_argument_error(sodium_exception_ce, 1, "must be SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES bytes long"); + RETURN_THROWS(); + } + secretkey = zend_string_alloc(crypto_kem_mlkem768_SECRETKEYBYTES, 0); + memcpy(ZSTR_VAL(secretkey), keypair, crypto_kem_mlkem768_SECRETKEYBYTES); + ZSTR_VAL(secretkey)[crypto_kem_mlkem768_SECRETKEYBYTES] = 0; + + RETURN_STR(secretkey); +} + +PHP_FUNCTION(sodium_crypto_kem_mlkem768_publickey) +{ + zend_string *publickey; + unsigned char *keypair; + size_t keypair_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", + &keypair, &keypair_len) == FAILURE) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (keypair_len != + crypto_kem_mlkem768_SECRETKEYBYTES + crypto_kem_mlkem768_PUBLICKEYBYTES) { + zend_argument_error(sodium_exception_ce, 1, "must be SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES bytes long"); + RETURN_THROWS(); + } + publickey = zend_string_alloc(crypto_kem_mlkem768_PUBLICKEYBYTES, 0); + memcpy(ZSTR_VAL(publickey), keypair + crypto_kem_mlkem768_SECRETKEYBYTES, + crypto_kem_mlkem768_PUBLICKEYBYTES); + ZSTR_VAL(publickey)[crypto_kem_mlkem768_PUBLICKEYBYTES] = 0; + + RETURN_STR(publickey); +} + +PHP_FUNCTION(sodium_crypto_kem_mlkem768_enc) +{ + unsigned char ciphertext[crypto_kem_mlkem768_CIPHERTEXTBYTES]; + unsigned char shared_secret[crypto_kem_mlkem768_SHAREDSECRETBYTES]; + unsigned char *publickey; + size_t publickey_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", + &publickey, &publickey_len) == FAILURE) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (publickey_len != crypto_kem_mlkem768_PUBLICKEYBYTES) { + zend_argument_error(sodium_exception_ce, 1, "must be SODIUM_CRYPTO_KEM_MLKEM768_PUBLICKEYBYTES bytes long"); + RETURN_THROWS(); + } + if (crypto_kem_mlkem768_enc(ciphertext, shared_secret, publickey) != 0) { + sodium_memzero(shared_secret, sizeof shared_secret); + zend_throw_exception(sodium_exception_ce, "internal error", 0); + RETURN_THROWS(); + } + array_init(return_value); + add_next_index_stringl(return_value, (const char *) ciphertext, sizeof ciphertext); + add_next_index_stringl(return_value, (const char *) shared_secret, sizeof shared_secret); + sodium_memzero(shared_secret, sizeof shared_secret); +} + +PHP_FUNCTION(sodium_crypto_kem_mlkem768_dec) +{ + zend_string *shared_secret; + unsigned char *ciphertext; + unsigned char *secretkey; + size_t ciphertext_len; + size_t secretkey_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", + &ciphertext, &ciphertext_len, + &secretkey, &secretkey_len) == FAILURE) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (ciphertext_len != crypto_kem_mlkem768_CIPHERTEXTBYTES) { + zend_argument_error(sodium_exception_ce, 1, "must be SODIUM_CRYPTO_KEM_MLKEM768_CIPHERTEXTBYTES bytes long"); + RETURN_THROWS(); + } + if (secretkey_len != crypto_kem_mlkem768_SECRETKEYBYTES) { + zend_argument_error(sodium_exception_ce, 2, "must be SODIUM_CRYPTO_KEM_MLKEM768_SECRETKEYBYTES bytes long"); + RETURN_THROWS(); + } + shared_secret = zend_string_alloc(crypto_kem_mlkem768_SHAREDSECRETBYTES, 0); + if (crypto_kem_mlkem768_dec((unsigned char *) ZSTR_VAL(shared_secret), + ciphertext, secretkey) != 0) { + sodium_memzero(ZSTR_VAL(shared_secret), crypto_kem_mlkem768_SHAREDSECRETBYTES); + zend_string_efree(shared_secret); + zend_throw_exception(sodium_exception_ce, "internal error", 0); + RETURN_THROWS(); + } + ZSTR_VAL(shared_secret)[crypto_kem_mlkem768_SHAREDSECRETBYTES] = 0; + + RETURN_NEW_STR(shared_secret); +} +#endif diff --git a/ext/sodium/libsodium.stub.php b/ext/sodium/libsodium.stub.php index 9fda5f4ddd73..80d8de2c0b29 100644 --- a/ext/sodium/libsodium.stub.php +++ b/ext/sodium/libsodium.stub.php @@ -967,4 +967,94 @@ function sodium_crypto_xof_turboshake256_update(string &$state, string $message) function sodium_crypto_xof_turboshake256_squeeze(string &$state, int $length): string {} #endif +#ifdef crypto_kem_PUBLICKEYBYTES +/** + * @var int + * @cvalue crypto_kem_PUBLICKEYBYTES + */ +const SODIUM_CRYPTO_KEM_PUBLICKEYBYTES = UNKNOWN; +/** + * @var int + * @cvalue crypto_kem_SECRETKEYBYTES + */ +const SODIUM_CRYPTO_KEM_SECRETKEYBYTES = UNKNOWN; +/** + * @var int + * @cvalue crypto_kem_CIPHERTEXTBYTES + */ +const SODIUM_CRYPTO_KEM_CIPHERTEXTBYTES = UNKNOWN; +/** + * @var int + * @cvalue crypto_kem_SHAREDSECRETBYTES + */ +const SODIUM_CRYPTO_KEM_SHAREDSECRETBYTES = UNKNOWN; +/** + * @var int + * @cvalue crypto_kem_SEEDBYTES + */ +const SODIUM_CRYPTO_KEM_SEEDBYTES = UNKNOWN; +/** + * @var int + * @cvalue SODIUM_CRYPTO_KEM_KEYPAIRBYTES() + */ +const SODIUM_CRYPTO_KEM_KEYPAIRBYTES = UNKNOWN; + +function sodium_crypto_kem_keypair(): string {} + +function sodium_crypto_kem_seed_keypair(#[\SensitiveParameter] string $seed): string {} + +function sodium_crypto_kem_secretkey(#[\SensitiveParameter] string $key_pair): string {} + +function sodium_crypto_kem_publickey(#[\SensitiveParameter] string $key_pair): string {} + +function sodium_crypto_kem_enc(string $public_key): array {} + +function sodium_crypto_kem_dec(string $ciphertext, #[\SensitiveParameter] string $secret_key): string {} +#endif + +#ifdef crypto_kem_mlkem768_PUBLICKEYBYTES +/** + * @var int + * @cvalue crypto_kem_mlkem768_PUBLICKEYBYTES + */ +const SODIUM_CRYPTO_KEM_MLKEM768_PUBLICKEYBYTES = UNKNOWN; +/** + * @var int + * @cvalue crypto_kem_mlkem768_SECRETKEYBYTES + */ +const SODIUM_CRYPTO_KEM_MLKEM768_SECRETKEYBYTES = UNKNOWN; +/** + * @var int + * @cvalue crypto_kem_mlkem768_CIPHERTEXTBYTES + */ +const SODIUM_CRYPTO_KEM_MLKEM768_CIPHERTEXTBYTES = UNKNOWN; +/** + * @var int + * @cvalue crypto_kem_mlkem768_SHAREDSECRETBYTES + */ +const SODIUM_CRYPTO_KEM_MLKEM768_SHAREDSECRETBYTES = UNKNOWN; +/** + * @var int + * @cvalue crypto_kem_mlkem768_SEEDBYTES + */ +const SODIUM_CRYPTO_KEM_MLKEM768_SEEDBYTES = UNKNOWN; +/** + * @var int + * @cvalue SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES() + */ +const SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES = UNKNOWN; + +function sodium_crypto_kem_mlkem768_keypair(): string {} + +function sodium_crypto_kem_mlkem768_seed_keypair(#[\SensitiveParameter] string $seed): string {} + +function sodium_crypto_kem_mlkem768_secretkey(#[\SensitiveParameter] string $key_pair): string {} + +function sodium_crypto_kem_mlkem768_publickey(#[\SensitiveParameter] string $key_pair): string {} + +function sodium_crypto_kem_mlkem768_enc(string $public_key): array {} + +function sodium_crypto_kem_mlkem768_dec(string $ciphertext, #[\SensitiveParameter] string $secret_key): string {} +#endif + class SodiumException extends Exception {} diff --git a/ext/sodium/libsodium_arginfo.h b/ext/sodium/libsodium_arginfo.h index 1b291e9a2330..548cd132c555 100644 --- a/ext/sodium/libsodium_arginfo.h +++ b/ext/sodium/libsodium_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit libsodium.stub.php instead. - * Stub hash: 7b337a297ae333dd8d0c232979b770332c9e7eb6 */ + * Stub hash: 82dc3f80ea85b0c71ed9db9b111097f3eb49d71d */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_aead_aes256gcm_is_available, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -572,6 +572,54 @@ ZEND_END_ARG_INFO() #define arginfo_sodium_crypto_xof_turboshake256_squeeze arginfo_sodium_crypto_xof_shake128_squeeze #endif +#if defined(crypto_kem_PUBLICKEYBYTES) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_kem_keypair, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_kem_seed_keypair, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, seed, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_kem_secretkey, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, key_pair, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_sodium_crypto_kem_publickey arginfo_sodium_crypto_kem_secretkey + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_kem_enc, 0, 1, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, public_key, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_kem_dec, 0, 2, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, ciphertext, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, secret_key, IS_STRING, 0) +ZEND_END_ARG_INFO() +#endif + +#if defined(crypto_kem_mlkem768_PUBLICKEYBYTES) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_kem_mlkem768_keypair, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_kem_mlkem768_seed_keypair, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, seed, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_kem_mlkem768_secretkey, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, key_pair, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_sodium_crypto_kem_mlkem768_publickey arginfo_sodium_crypto_kem_mlkem768_secretkey + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_kem_mlkem768_enc, 0, 1, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, public_key, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_kem_mlkem768_dec, 0, 2, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, ciphertext, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, secret_key, IS_STRING, 0) +ZEND_END_ARG_INFO() +#endif + ZEND_FUNCTION(sodium_crypto_aead_aes256gcm_is_available); #if defined(HAVE_AESGCM) ZEND_FUNCTION(sodium_crypto_aead_aes256gcm_decrypt); @@ -739,6 +787,22 @@ ZEND_FUNCTION(sodium_crypto_xof_turboshake256_init); ZEND_FUNCTION(sodium_crypto_xof_turboshake256_update); ZEND_FUNCTION(sodium_crypto_xof_turboshake256_squeeze); #endif +#if defined(crypto_kem_PUBLICKEYBYTES) +ZEND_FUNCTION(sodium_crypto_kem_keypair); +ZEND_FUNCTION(sodium_crypto_kem_seed_keypair); +ZEND_FUNCTION(sodium_crypto_kem_secretkey); +ZEND_FUNCTION(sodium_crypto_kem_publickey); +ZEND_FUNCTION(sodium_crypto_kem_enc); +ZEND_FUNCTION(sodium_crypto_kem_dec); +#endif +#if defined(crypto_kem_mlkem768_PUBLICKEYBYTES) +ZEND_FUNCTION(sodium_crypto_kem_mlkem768_keypair); +ZEND_FUNCTION(sodium_crypto_kem_mlkem768_seed_keypair); +ZEND_FUNCTION(sodium_crypto_kem_mlkem768_secretkey); +ZEND_FUNCTION(sodium_crypto_kem_mlkem768_publickey); +ZEND_FUNCTION(sodium_crypto_kem_mlkem768_enc); +ZEND_FUNCTION(sodium_crypto_kem_mlkem768_dec); +#endif static const zend_function_entry ext_functions[] = { ZEND_FE(sodium_crypto_aead_aes256gcm_is_available, arginfo_sodium_crypto_aead_aes256gcm_is_available) @@ -908,6 +972,22 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(sodium_crypto_xof_turboshake256_init, arginfo_sodium_crypto_xof_turboshake256_init) ZEND_FE(sodium_crypto_xof_turboshake256_update, arginfo_sodium_crypto_xof_turboshake256_update) ZEND_FE(sodium_crypto_xof_turboshake256_squeeze, arginfo_sodium_crypto_xof_turboshake256_squeeze) +#endif +#if defined(crypto_kem_PUBLICKEYBYTES) + ZEND_FE(sodium_crypto_kem_keypair, arginfo_sodium_crypto_kem_keypair) + ZEND_FE(sodium_crypto_kem_seed_keypair, arginfo_sodium_crypto_kem_seed_keypair) + ZEND_FE(sodium_crypto_kem_secretkey, arginfo_sodium_crypto_kem_secretkey) + ZEND_FE(sodium_crypto_kem_publickey, arginfo_sodium_crypto_kem_publickey) + ZEND_FE(sodium_crypto_kem_enc, arginfo_sodium_crypto_kem_enc) + ZEND_FE(sodium_crypto_kem_dec, arginfo_sodium_crypto_kem_dec) +#endif +#if defined(crypto_kem_mlkem768_PUBLICKEYBYTES) + ZEND_FE(sodium_crypto_kem_mlkem768_keypair, arginfo_sodium_crypto_kem_mlkem768_keypair) + ZEND_FE(sodium_crypto_kem_mlkem768_seed_keypair, arginfo_sodium_crypto_kem_mlkem768_seed_keypair) + ZEND_FE(sodium_crypto_kem_mlkem768_secretkey, arginfo_sodium_crypto_kem_mlkem768_secretkey) + ZEND_FE(sodium_crypto_kem_mlkem768_publickey, arginfo_sodium_crypto_kem_mlkem768_publickey) + ZEND_FE(sodium_crypto_kem_mlkem768_enc, arginfo_sodium_crypto_kem_mlkem768_enc) + ZEND_FE(sodium_crypto_kem_mlkem768_dec, arginfo_sodium_crypto_kem_mlkem768_dec) #endif ZEND_FE_END }; @@ -1064,6 +1144,22 @@ static void register_libsodium_symbols(int module_number) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_XOF_TURBOSHAKE256_BLOCKBYTES", crypto_xof_turboshake256_BLOCKBYTES, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_XOF_TURBOSHAKE256_STATEBYTES", crypto_xof_turboshake256_STATEBYTES, CONST_PERSISTENT); #endif +#if defined(crypto_kem_PUBLICKEYBYTES) + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KEM_PUBLICKEYBYTES", crypto_kem_PUBLICKEYBYTES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KEM_SECRETKEYBYTES", crypto_kem_SECRETKEYBYTES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KEM_CIPHERTEXTBYTES", crypto_kem_CIPHERTEXTBYTES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KEM_SHAREDSECRETBYTES", crypto_kem_SHAREDSECRETBYTES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KEM_SEEDBYTES", crypto_kem_SEEDBYTES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KEM_KEYPAIRBYTES", SODIUM_CRYPTO_KEM_KEYPAIRBYTES(), CONST_PERSISTENT); +#endif +#if defined(crypto_kem_mlkem768_PUBLICKEYBYTES) + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KEM_MLKEM768_PUBLICKEYBYTES", crypto_kem_mlkem768_PUBLICKEYBYTES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KEM_MLKEM768_SECRETKEYBYTES", crypto_kem_mlkem768_SECRETKEYBYTES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KEM_MLKEM768_CIPHERTEXTBYTES", crypto_kem_mlkem768_CIPHERTEXTBYTES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KEM_MLKEM768_SHAREDSECRETBYTES", crypto_kem_mlkem768_SHAREDSECRETBYTES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KEM_MLKEM768_SEEDBYTES", crypto_kem_mlkem768_SEEDBYTES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES", SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES(), CONST_PERSISTENT); +#endif #if defined(HAVE_AESGCM) @@ -1259,6 +1355,26 @@ static void register_libsodium_symbols(int module_number) zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_ipcrypt_pfx_decrypt", sizeof("sodium_crypto_ipcrypt_pfx_decrypt") - 1), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); #endif +#if defined(crypto_kem_PUBLICKEYBYTES) + + zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_kem_seed_keypair", sizeof("sodium_crypto_kem_seed_keypair") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + + zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_kem_secretkey", sizeof("sodium_crypto_kem_secretkey") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + + zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_kem_publickey", sizeof("sodium_crypto_kem_publickey") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + + zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_kem_dec", sizeof("sodium_crypto_kem_dec") - 1), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); +#endif +#if defined(crypto_kem_mlkem768_PUBLICKEYBYTES) + + zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_kem_mlkem768_seed_keypair", sizeof("sodium_crypto_kem_mlkem768_seed_keypair") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + + zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_kem_mlkem768_secretkey", sizeof("sodium_crypto_kem_mlkem768_secretkey") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + + zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_kem_mlkem768_publickey", sizeof("sodium_crypto_kem_mlkem768_publickey") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + + zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_kem_mlkem768_dec", sizeof("sodium_crypto_kem_mlkem768_dec") - 1), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); +#endif } static zend_class_entry *register_class_SodiumException(zend_class_entry *class_entry_Exception) diff --git a/ext/sodium/php_libsodium.h b/ext/sodium/php_libsodium.h index 8132472d684f..725e3cdc76c0 100644 --- a/ext/sodium/php_libsodium.h +++ b/ext/sodium/php_libsodium.h @@ -34,6 +34,10 @@ extern zend_module_entry sodium_module_entry; #define SODIUM_CRYPTO_SIGN_KEYPAIRBYTES() crypto_sign_SECRETKEYBYTES + crypto_sign_PUBLICKEYBYTES +#define SODIUM_CRYPTO_KEM_KEYPAIRBYTES() crypto_kem_SECRETKEYBYTES + crypto_kem_PUBLICKEYBYTES + +#define SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES() crypto_kem_mlkem768_SECRETKEYBYTES + crypto_kem_mlkem768_PUBLICKEYBYTES + #if SODIUM_LIBRARY_VERSION_MAJOR > 9 || (SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 6) /** diff --git a/ext/sodium/tests/crypto_kem.phpt b/ext/sodium/tests/crypto_kem.phpt new file mode 100644 index 000000000000..420338956fad --- /dev/null +++ b/ext/sodium/tests/crypto_kem.phpt @@ -0,0 +1,119 @@ +--TEST-- +Check for libsodium crypto_kem (X-Wing) +--EXTENSIONS-- +sodium +--SKIPIF-- += 1.0.22)"; +?> +--FILE-- + extractors -> enc -> dec */ +$keypair = sodium_crypto_kem_keypair(); +var_dump(strlen($keypair) === SODIUM_CRYPTO_KEM_KEYPAIRBYTES); +$secret_key = sodium_crypto_kem_secretkey($keypair); +$public_key = sodium_crypto_kem_publickey($keypair); +var_dump(strlen($secret_key) === SODIUM_CRYPTO_KEM_SECRETKEYBYTES); +var_dump(strlen($public_key) === SODIUM_CRYPTO_KEM_PUBLICKEYBYTES); + +[$ciphertext, $shared_secret] = sodium_crypto_kem_enc($public_key); +var_dump(strlen($ciphertext) === SODIUM_CRYPTO_KEM_CIPHERTEXTBYTES); +var_dump(strlen($shared_secret) === SODIUM_CRYPTO_KEM_SHAREDSECRETBYTES); +var_dump(sodium_crypto_kem_dec($ciphertext, $secret_key) === $shared_secret); + +/* Encapsulation is randomized: same public key, fresh ciphertext and secret */ +[$ciphertext2, $shared_secret2] = sodium_crypto_kem_enc($public_key); +var_dump($ciphertext2 !== $ciphertext); +var_dump($shared_secret2 !== $shared_secret); +var_dump(sodium_crypto_kem_dec($ciphertext2, $secret_key) === $shared_secret2); + +/* Key generation is randomized */ +var_dump(sodium_crypto_kem_keypair() !== $keypair); + +/* seed_keypair is deterministic and round-trips */ +$seed = random_bytes(SODIUM_CRYPTO_KEM_SEEDBYTES); +$seeded_keypair = sodium_crypto_kem_seed_keypair($seed); +var_dump(strlen($seeded_keypair) === SODIUM_CRYPTO_KEM_KEYPAIRBYTES); +var_dump(sodium_crypto_kem_seed_keypair($seed) === $seeded_keypair); +[$seeded_ciphertext, $seeded_shared_secret] = + sodium_crypto_kem_enc(sodium_crypto_kem_publickey($seeded_keypair)); +var_dump(sodium_crypto_kem_dec($seeded_ciphertext, + sodium_crypto_kem_secretkey($seeded_keypair)) === $seeded_shared_secret); + +/* Implicit rejection: a tampered ciphertext still decapsulates to a + * shared secret of the right length, but not the original one */ +$tampered_ciphertext = $ciphertext; +$tampered_ciphertext[0] = $tampered_ciphertext[0] === "\x00" ? "\x01" : "\x00"; +$tampered_shared_secret = sodium_crypto_kem_dec($tampered_ciphertext, $secret_key); +var_dump(strlen($tampered_shared_secret) === SODIUM_CRYPTO_KEM_SHAREDSECRETBYTES); +var_dump($tampered_shared_secret !== $shared_secret); + +/* Error: truncated public key */ +try { + sodium_crypto_kem_enc(substr($public_key, 0, -1)); +} catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +/* Error: truncated ciphertext */ +try { + sodium_crypto_kem_dec(substr($ciphertext, 0, -1), $secret_key); +} catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +/* Error: truncated secret key */ +try { + sodium_crypto_kem_dec($ciphertext, substr($secret_key, 0, -1)); +} catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +/* Error: truncated keypair */ +try { + sodium_crypto_kem_secretkey(substr($keypair, 0, -1)); +} catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +/* Error: overlong keypair */ +try { + sodium_crypto_kem_publickey($keypair . 'x'); +} catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +/* Error: wrong-length seed */ +try { + sodium_crypto_kem_seed_keypair(random_bytes(SODIUM_CRYPTO_KEM_SEEDBYTES - 1)); +} catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +?> +--EXPECT-- +crypto_kem (X-Wing): +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +SodiumException: sodium_crypto_kem_enc(): Argument #1 ($public_key) must be SODIUM_CRYPTO_KEM_PUBLICKEYBYTES bytes long +SodiumException: sodium_crypto_kem_dec(): Argument #1 ($ciphertext) must be SODIUM_CRYPTO_KEM_CIPHERTEXTBYTES bytes long +SodiumException: sodium_crypto_kem_dec(): Argument #2 ($secret_key) must be SODIUM_CRYPTO_KEM_SECRETKEYBYTES bytes long +SodiumException: sodium_crypto_kem_secretkey(): Argument #1 ($key_pair) must be SODIUM_CRYPTO_KEM_KEYPAIRBYTES bytes long +SodiumException: sodium_crypto_kem_publickey(): Argument #1 ($key_pair) must be SODIUM_CRYPTO_KEM_KEYPAIRBYTES bytes long +SodiumException: sodium_crypto_kem_seed_keypair(): Argument #1 ($seed) must be SODIUM_CRYPTO_KEM_SEEDBYTES bytes long diff --git a/ext/sodium/tests/crypto_kem_mlkem768.phpt b/ext/sodium/tests/crypto_kem_mlkem768.phpt new file mode 100644 index 000000000000..e97088d96dd4 --- /dev/null +++ b/ext/sodium/tests/crypto_kem_mlkem768.phpt @@ -0,0 +1,130 @@ +--TEST-- +Check for libsodium crypto_kem_mlkem768 +--EXTENSIONS-- +sodium +--SKIPIF-- += 1.0.22)"; +?> +--FILE-- + extractors -> enc -> dec */ +$keypair = sodium_crypto_kem_mlkem768_keypair(); +var_dump(strlen($keypair) === SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES); +$secret_key = sodium_crypto_kem_mlkem768_secretkey($keypair); +$public_key = sodium_crypto_kem_mlkem768_publickey($keypair); +var_dump(strlen($secret_key) === SODIUM_CRYPTO_KEM_MLKEM768_SECRETKEYBYTES); +var_dump(strlen($public_key) === SODIUM_CRYPTO_KEM_MLKEM768_PUBLICKEYBYTES); + +[$ciphertext, $shared_secret] = sodium_crypto_kem_mlkem768_enc($public_key); +var_dump(strlen($ciphertext) === SODIUM_CRYPTO_KEM_MLKEM768_CIPHERTEXTBYTES); +var_dump(strlen($shared_secret) === SODIUM_CRYPTO_KEM_MLKEM768_SHAREDSECRETBYTES); +var_dump(sodium_crypto_kem_mlkem768_dec($ciphertext, $secret_key) === $shared_secret); + +/* Encapsulation is randomized: same public key, fresh ciphertext and secret */ +[$ciphertext2, $shared_secret2] = sodium_crypto_kem_mlkem768_enc($public_key); +var_dump($ciphertext2 !== $ciphertext); +var_dump($shared_secret2 !== $shared_secret); +var_dump(sodium_crypto_kem_mlkem768_dec($ciphertext2, $secret_key) === $shared_secret2); + +/* Key generation is randomized */ +var_dump(sodium_crypto_kem_mlkem768_keypair() !== $keypair); + +/* seed_keypair is deterministic and round-trips */ +$seed = random_bytes(SODIUM_CRYPTO_KEM_MLKEM768_SEEDBYTES); +$seeded_keypair = sodium_crypto_kem_mlkem768_seed_keypair($seed); +var_dump(strlen($seeded_keypair) === SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES); +var_dump(sodium_crypto_kem_mlkem768_seed_keypair($seed) === $seeded_keypair); +[$seeded_ciphertext, $seeded_shared_secret] = + sodium_crypto_kem_mlkem768_enc(sodium_crypto_kem_mlkem768_publickey($seeded_keypair)); +var_dump(sodium_crypto_kem_mlkem768_dec($seeded_ciphertext, + sodium_crypto_kem_mlkem768_secretkey($seeded_keypair)) === $seeded_shared_secret); + +/* Implicit rejection: a tampered ciphertext still decapsulates to a + * shared secret of the right length, but not the original one */ +$tampered_ciphertext = $ciphertext; +$tampered_ciphertext[0] = $tampered_ciphertext[0] === "\x00" ? "\x01" : "\x00"; +$tampered_shared_secret = sodium_crypto_kem_mlkem768_dec($tampered_ciphertext, $secret_key); +var_dump(strlen($tampered_shared_secret) === SODIUM_CRYPTO_KEM_MLKEM768_SHAREDSECRETBYTES); +var_dump($tampered_shared_secret !== $shared_secret); + +/* Error: truncated public key */ +try { + sodium_crypto_kem_mlkem768_enc(substr($public_key, 0, -1)); +} catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +/* Error: an ML-KEM768 public key is not a valid X-Wing public key */ +if (defined('SODIUM_CRYPTO_KEM_PUBLICKEYBYTES')) { + try { + sodium_crypto_kem_enc($public_key); + } catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; + } +} else { + echo "SodiumException: sodium_crypto_kem_enc(): Argument #1 (\$public_key) must be SODIUM_CRYPTO_KEM_PUBLICKEYBYTES bytes long\n"; +} +/* Error: truncated ciphertext */ +try { + sodium_crypto_kem_mlkem768_dec(substr($ciphertext, 0, -1), $secret_key); +} catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +/* Error: truncated secret key */ +try { + sodium_crypto_kem_mlkem768_dec($ciphertext, substr($secret_key, 0, -1)); +} catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +/* Error: truncated keypair */ +try { + sodium_crypto_kem_mlkem768_secretkey(substr($keypair, 0, -1)); +} catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +/* Error: overlong keypair */ +try { + sodium_crypto_kem_mlkem768_publickey($keypair . 'x'); +} catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +/* Error: wrong-length seed */ +try { + sodium_crypto_kem_mlkem768_seed_keypair(random_bytes(SODIUM_CRYPTO_KEM_MLKEM768_SEEDBYTES - 1)); +} catch (SodiumException $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +?> +--EXPECT-- +crypto_kem_mlkem768: +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +SodiumException: sodium_crypto_kem_mlkem768_enc(): Argument #1 ($public_key) must be SODIUM_CRYPTO_KEM_MLKEM768_PUBLICKEYBYTES bytes long +SodiumException: sodium_crypto_kem_enc(): Argument #1 ($public_key) must be SODIUM_CRYPTO_KEM_PUBLICKEYBYTES bytes long +SodiumException: sodium_crypto_kem_mlkem768_dec(): Argument #1 ($ciphertext) must be SODIUM_CRYPTO_KEM_MLKEM768_CIPHERTEXTBYTES bytes long +SodiumException: sodium_crypto_kem_mlkem768_dec(): Argument #2 ($secret_key) must be SODIUM_CRYPTO_KEM_MLKEM768_SECRETKEYBYTES bytes long +SodiumException: sodium_crypto_kem_mlkem768_secretkey(): Argument #1 ($key_pair) must be SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES bytes long +SodiumException: sodium_crypto_kem_mlkem768_publickey(): Argument #1 ($key_pair) must be SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES bytes long +SodiumException: sodium_crypto_kem_mlkem768_seed_keypair(): Argument #1 ($seed) must be SODIUM_CRYPTO_KEM_MLKEM768_SEEDBYTES bytes long