diff --git a/NEWS b/NEWS index 29dd679a21d28..227066f5361ed 100644 --- a/NEWS +++ b/NEWS @@ -191,6 +191,8 @@ PHP NEWS - Sodium: . Add support for AEGIS-128L and AEGIS-256 (jedisct1) . Enable AES-GCM on aarch64 with the ARM crypto extensions (jedisct1) + . Add sodium_crypto_onetimeauth_* functions for one-time Poly1305 + MAC generation and verification (Ayesh Karunaratne) - Standard: . Implement GH-12188 (Indication for the int size in phpinfo()). (timwolla) diff --git a/UPGRADING b/UPGRADING index 09b277339ed3c..a6012ca489022 100644 --- a/UPGRADING +++ b/UPGRADING @@ -435,6 +435,12 @@ PHP 8.4 UPGRADE NOTES that was introduced in libsodium 1.0.19. . sodium_crypto_aead_aes256gcm_*() functions are now enabled on aarch64 CPUs with the ARM cryptographic extensions. + . Added sodium_crypto_onetimeauth(), sodium_crypto_onetimeauth_keygen(), and + sodium_crypto_onetimeauth_verify() functions for one-time authentication + based on Poly1305 Message Authentication Code. These MACs are 16 bytes + long, and are generated using a 32 byte key. Due to its output size, + Poly1305 is recommended for online protocols, exchanging many small + messages, rather than for authenticating very large files. - Standard: . Added the http_get_last_response_headers() and @@ -550,6 +556,8 @@ PHP 8.4 UPGRADE NOTES . SODIUM_CRYPTO_AEAD_AEGIS256_NSECBYTES . SODIUM_CRYPTO_AEAD_AEGIS256_NPUBBYTES . SODIUM_CRYPTO_AEAD_AEGIS256_ABYTES + . SODIUM_CRYPTO_ONETIMEAUTH_BYTES + . SODIUM_CRYPTO_ONETIMEAUTH_KEYBYTES - XML: . Added XML_OPTION_PARSE_HUGE to allow large inputs in xml_parse and diff --git a/ext/sodium/libsodium.c b/ext/sodium/libsodium.c index 6a71d37a34903..19bf606155914 100644 --- a/ext/sodium/libsodium.c +++ b/ext/sodium/libsodium.c @@ -2942,6 +2942,68 @@ PHP_FUNCTION(sodium_crypto_auth_verify) RETURN_TRUE; } +PHP_FUNCTION(sodium_crypto_onetimeauth) +{ + zend_string *mac; + char *key; + char *msg; + size_t msg_len; + size_t key_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", + &msg, &msg_len, + &key, &key_len) == FAILURE) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (key_len != crypto_onetimeauth_KEYBYTES) { + zend_argument_error(sodium_exception_ce, 2, "must be SODIUM_CRYPTO_ONETIMEAUTH_KEYBYTES bytes long"); + RETURN_THROWS(); + } + mac = zend_string_alloc(crypto_onetimeauth_BYTES, 0); + if (crypto_onetimeauth((unsigned char *) ZSTR_VAL(mac), + (const unsigned char *) msg, msg_len, + (const unsigned char *) key) != 0) { + zend_throw_exception(sodium_exception_ce, "internal error", 0); + RETURN_THROWS(); + } + ZSTR_VAL(mac)[crypto_onetimeauth_BYTES] = 0; + + RETURN_STR(mac); +} + +PHP_FUNCTION(sodium_crypto_onetimeauth_verify) +{ + char *mac; + char *key; + char *msg; + size_t mac_len; + size_t msg_len; + size_t key_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", + &mac, &mac_len, + &msg, &msg_len, + &key, &key_len) == FAILURE) { + sodium_remove_param_values_from_backtrace(EG(exception)); + RETURN_THROWS(); + } + if (key_len != crypto_onetimeauth_KEYBYTES) { + zend_argument_error(sodium_exception_ce, 3, "must be SODIUM_CRYPTO_ONETIMEAUTH_KEYBYTES bytes long"); + RETURN_THROWS(); + } + if (mac_len != crypto_onetimeauth_BYTES) { + zend_argument_error(sodium_exception_ce, 1, "must be SODIUM_CRYPTO_ONETIMEAUTH_BYTES bytes long"); + RETURN_THROWS(); + } + if (crypto_onetimeauth_verify((const unsigned char *) mac, + (const unsigned char *) msg, msg_len, + (const unsigned char *) key) != 0) { + RETURN_FALSE; + } + RETURN_TRUE; +} + PHP_FUNCTION(sodium_crypto_sign_ed25519_sk_to_curve25519) { zend_string *ecdhkey; @@ -3103,6 +3165,17 @@ PHP_FUNCTION(sodium_crypto_auth_keygen) RETURN_STRINGL((const char *) key, sizeof key); } +PHP_FUNCTION(sodium_crypto_onetimeauth_keygen) +{ + unsigned char key[crypto_onetimeauth_KEYBYTES]; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + randombytes_buf(key, sizeof key); + RETURN_STRINGL((const char *) key, sizeof key); +} + PHP_FUNCTION(sodium_crypto_generichash_keygen) { unsigned char key[crypto_generichash_KEYBYTES]; diff --git a/ext/sodium/libsodium.stub.php b/ext/sodium/libsodium.stub.php index 4bf6fade9136f..43c032b34c88e 100644 --- a/ext/sodium/libsodium.stub.php +++ b/ext/sodium/libsodium.stub.php @@ -158,6 +158,16 @@ * @cvalue crypto_auth_KEYBYTES */ const SODIUM_CRYPTO_AUTH_KEYBYTES = UNKNOWN; +/** + * @var int + * @cvalue crypto_onetimeauth_BYTES + */ +const SODIUM_CRYPTO_ONETIMEAUTH_BYTES = UNKNOWN; +/** + * @var int + * @cvalue crypto_onetimeauth_KEYBYTES + */ +const SODIUM_CRYPTO_ONETIMEAUTH_KEYBYTES = UNKNOWN; /** * @var int * @cvalue crypto_box_SEALBYTES @@ -595,6 +605,12 @@ function sodium_crypto_auth_keygen(): string {} function sodium_crypto_auth_verify(string $mac, string $message, #[\SensitiveParameter] string $key): bool {} +function sodium_crypto_onetimeauth(string $message, #[\SensitiveParameter] string $key): string {} + +function sodium_crypto_onetimeauth_keygen(): string {} + +function sodium_crypto_onetimeauth_verify(string $mac, string $message, #[\SensitiveParameter] string $key): bool {} + function sodium_crypto_box(#[\SensitiveParameter] string $message, string $nonce, #[\SensitiveParameter] string $key_pair): string {} function sodium_crypto_box_keypair(): string {} diff --git a/ext/sodium/libsodium_arginfo.h b/ext/sodium/libsodium_arginfo.h index ebc5fadae6452..8fe39ec6a55f5 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: 89cbb449ee6146dc8d50ba4bb1e76f83444a2db2 */ + * Stub hash: c4950089e6284f7b834b3dc544c8f4dc4cee64fb */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_aead_aes256gcm_is_available, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -132,6 +132,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_auth_verify, 0, 3, ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() +#define arginfo_sodium_crypto_onetimeauth arginfo_sodium_crypto_auth + +#define arginfo_sodium_crypto_onetimeauth_keygen arginfo_sodium_crypto_aead_chacha20poly1305_keygen + +#define arginfo_sodium_crypto_onetimeauth_verify arginfo_sodium_crypto_auth_verify + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_box, 0, 3, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, nonce, IS_STRING, 0) @@ -597,6 +603,9 @@ ZEND_FUNCTION(sodium_crypto_aead_xchacha20poly1305_ietf_encrypt); ZEND_FUNCTION(sodium_crypto_auth); ZEND_FUNCTION(sodium_crypto_auth_keygen); ZEND_FUNCTION(sodium_crypto_auth_verify); +ZEND_FUNCTION(sodium_crypto_onetimeauth); +ZEND_FUNCTION(sodium_crypto_onetimeauth_keygen); +ZEND_FUNCTION(sodium_crypto_onetimeauth_verify); ZEND_FUNCTION(sodium_crypto_box); ZEND_FUNCTION(sodium_crypto_box_keypair); ZEND_FUNCTION(sodium_crypto_box_seed_keypair); @@ -800,6 +809,9 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(sodium_crypto_auth, arginfo_sodium_crypto_auth) ZEND_FE(sodium_crypto_auth_keygen, arginfo_sodium_crypto_auth_keygen) ZEND_FE(sodium_crypto_auth_verify, arginfo_sodium_crypto_auth_verify) + ZEND_FE(sodium_crypto_onetimeauth, arginfo_sodium_crypto_onetimeauth) + ZEND_FE(sodium_crypto_onetimeauth_keygen, arginfo_sodium_crypto_onetimeauth_keygen) + ZEND_FE(sodium_crypto_onetimeauth_verify, arginfo_sodium_crypto_onetimeauth_verify) ZEND_FE(sodium_crypto_box, arginfo_sodium_crypto_box) ZEND_FE(sodium_crypto_box_keypair, arginfo_sodium_crypto_box_keypair) ZEND_FE(sodium_crypto_box_seed_keypair, arginfo_sodium_crypto_box_seed_keypair) @@ -1026,6 +1038,8 @@ static void register_libsodium_symbols(int module_number) #endif REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AUTH_BYTES", crypto_auth_BYTES, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AUTH_KEYBYTES", crypto_auth_KEYBYTES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_ONETIMEAUTH_BYTES", crypto_onetimeauth_BYTES, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_ONETIMEAUTH_KEYBYTES", crypto_onetimeauth_KEYBYTES, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_BOX_SEALBYTES", crypto_box_SEALBYTES, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_BOX_SECRETKEYBYTES", crypto_box_SECRETKEYBYTES, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_BOX_PUBLICKEYBYTES", crypto_box_PUBLICKEYBYTES, CONST_PERSISTENT); @@ -1231,6 +1245,10 @@ static void register_libsodium_symbols(int module_number) zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_auth_verify", sizeof("sodium_crypto_auth_verify") - 1), 2, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_onetimeauth", sizeof("sodium_crypto_onetimeauth") - 1), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + + zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_onetimeauth_verify", sizeof("sodium_crypto_onetimeauth_verify") - 1), 2, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_box", sizeof("sodium_crypto_box") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "sodium_crypto_box", sizeof("sodium_crypto_box") - 1), 2, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); diff --git a/ext/sodium/tests/crypto_onetimeauth.phpt b/ext/sodium/tests/crypto_onetimeauth.phpt new file mode 100644 index 0000000000000..61d13f5c3b72f --- /dev/null +++ b/ext/sodium/tests/crypto_onetimeauth.phpt @@ -0,0 +1,47 @@ +--TEST-- +Tests for libsodium onetimeauth functionality +--EXTENSIONS-- +sodium +--FILE-- +getMessage(), PHP_EOL; +} + +// Flip the first bit +$badmsg = $msg; +$badmsg[0] = \chr(\ord($badmsg[0]) ^ 0x80); +var_dump(sodium_crypto_onetimeauth_verify($mac, $badmsg, $key)); + +// Let's flip a bit pseudo-randomly +$badmsg = $msg; +$badmsg[$i=mt_rand(0, 999)] = \chr( + \ord($msg[$i]) ^ ( + 1 << mt_rand(0, 7) + ) +); + +var_dump(sodium_crypto_onetimeauth_verify($mac, $badmsg, $key)); + +// Now let's change a bit in the MAC +$badmac = $mac; +$badmac[0] = \chr(\ord($badmac[0]) ^ 0x80); +var_dump(sodium_crypto_onetimeauth_verify($badmac, $msg, $key)); +?> +--EXPECT-- +bool(true) +sodium_crypto_onetimeauth(): Argument #2 ($key) must be SODIUM_CRYPTO_ONETIMEAUTH_KEYBYTES bytes long +bool(false) +bool(false) +bool(false)