diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2020-10-14 13:03:03 +0200 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-10-19 11:09:00 +0200 |
commit | 750a74ed9c8061681dba26ffc779c81b391b7718 (patch) | |
tree | 396b24842536ed48be7175ae63755c2fd6f3fbc0 /ext/openssl/openssl.c | |
parent | a4c1a43a5b855c9331c1a08074056df3309660ab (diff) | |
download | php-git-750a74ed9c8061681dba26ffc779c81b391b7718.tar.gz |
Fix bug #79983: Add support for OCB mode
OCB mode ciphers were already exposed to openssl_encrypt/decrypt,
but misbehaved, because they were not treated as AEAD ciphers.
From that perspective, OCB should be treated the same way as GCM.
In OpenSSL 1.1 the necessary controls were unified under
EVP_CTRL_AEAD_* (and OCB is only supported since OpenSSL 1.1).
Closes GH-6337.
Diffstat (limited to 'ext/openssl/openssl.c')
-rw-r--r-- | ext/openssl/openssl.c | 33 |
1 files changed, 27 insertions, 6 deletions
diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 8489d9bfdd..b18e5f8d99 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -6483,6 +6483,7 @@ PHP_FUNCTION(openssl_digest) /* Cipher mode info */ struct php_openssl_cipher_mode { zend_bool is_aead; + zend_bool should_set_tag_length; zend_bool is_single_run_aead; int aead_get_tag_flag; int aead_set_tag_flag; @@ -6491,24 +6492,41 @@ struct php_openssl_cipher_mode { static void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, const EVP_CIPHER *cipher_type) /* {{{ */ { - switch (EVP_CIPHER_mode(cipher_type)) { -#ifdef EVP_CIPH_GCM_MODE + int cipher_mode = EVP_CIPHER_mode(cipher_type); + switch (cipher_mode) { +#if PHP_OPENSSL_API_VERSION >= 0x10100 + case EVP_CIPH_GCM_MODE: + case EVP_CIPH_OCB_MODE: + case EVP_CIPH_CCM_MODE: + mode->is_aead = 1; + mode->should_set_tag_length = + cipher_mode == EVP_CIPH_CCM_MODE || cipher_mode == EVP_CIPH_OCB_MODE; + mode->is_single_run_aead = cipher_mode == EVP_CIPH_CCM_MODE; + mode->aead_get_tag_flag = EVP_CTRL_AEAD_GET_TAG; + mode->aead_set_tag_flag = EVP_CTRL_AEAD_SET_TAG; + mode->aead_ivlen_flag = EVP_CTRL_AEAD_SET_IVLEN; + break; +#else +# ifdef EVP_CIPH_GCM_MODE case EVP_CIPH_GCM_MODE: mode->is_aead = 1; + mode->should_set_tag_length = 0; mode->is_single_run_aead = 0; mode->aead_get_tag_flag = EVP_CTRL_GCM_GET_TAG; mode->aead_set_tag_flag = EVP_CTRL_GCM_SET_TAG; mode->aead_ivlen_flag = EVP_CTRL_GCM_SET_IVLEN; break; -#endif -#ifdef EVP_CIPH_CCM_MODE +# endif +# ifdef EVP_CIPH_CCM_MODE case EVP_CIPH_CCM_MODE: mode->is_aead = 1; + mode->should_set_tag_length = 1; mode->is_single_run_aead = 1; mode->aead_get_tag_flag = EVP_CTRL_CCM_GET_TAG; mode->aead_set_tag_flag = EVP_CTRL_CCM_SET_TAG; mode->aead_ivlen_flag = EVP_CTRL_CCM_SET_IVLEN; break; +# endif #endif default: memset(mode, 0, sizeof(struct php_openssl_cipher_mode)); @@ -6593,12 +6611,15 @@ static int php_openssl_cipher_init(const EVP_CIPHER *cipher_type, if (php_openssl_validate_iv(piv, piv_len, max_iv_len, free_iv, cipher_ctx, mode) == FAILURE) { return FAILURE; } - if (mode->is_single_run_aead && enc) { + if (mode->should_set_tag_length) { + /* Explicitly set the tag length even when decrypting, + * see https://github.com/openssl/openssl/issues/8331. */ if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, NULL)) { php_error_docref(NULL, E_WARNING, "Setting tag length for AEAD cipher failed"); return FAILURE; } - } else if (!enc && tag && tag_len > 0) { + } + if (!enc && tag && tag_len > 0) { if (!mode->is_aead) { php_error_docref(NULL, E_WARNING, "The tag cannot be used because the cipher method does not support AEAD"); } else if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, (unsigned char *) tag)) { |