Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
28 changes: 28 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
330 changes: 330 additions & 0 deletions ext/sodium/libsodium.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading
Loading