diff options
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | ext/openssl/openssl.c | 350 | ||||
-rw-r--r-- | ext/openssl/tests/cipher_tests.inc | 111 | ||||
-rw-r--r-- | ext/openssl/tests/openssl_decrypt_ccm.phpt | 38 | ||||
-rw-r--r-- | ext/openssl/tests/openssl_decrypt_error.phpt | 6 | ||||
-rw-r--r-- | ext/openssl/tests/openssl_decrypt_gcm.phpt | 48 | ||||
-rw-r--r-- | ext/openssl/tests/openssl_encrypt_ccm.phpt | 39 | ||||
-rw-r--r-- | ext/openssl/tests/openssl_encrypt_error.phpt | 7 | ||||
-rw-r--r-- | ext/openssl/tests/openssl_encrypt_gcm.phpt | 54 |
9 files changed, 565 insertions, 90 deletions
@@ -23,6 +23,8 @@ PHP NEWS . Fixed bug #72399 (Use-After-Free in MBString (search_re)). (Laruence) - OpenSSL: + . Implemented FR #67304 (Added AEAD support [CCM and GCM modes] to + openssl_encrypt and openssl_decrypt). (Jakub Zelenka) . Implemented error storing to the global queue and cleaning up the OpenSSL error queue (resolves bugs #68276 and #69882). (Jakub Zelenka) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index c27bbae2e3..b462d0e66c 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -391,6 +391,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_encrypt, 0, 0, 3) ZEND_ARG_INFO(0, password) ZEND_ARG_INFO(0, options) ZEND_ARG_INFO(0, iv) + ZEND_ARG_INFO(1, tag) + ZEND_ARG_INFO(0, aad) + ZEND_ARG_INFO(0, tag_length) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_decrypt, 0, 0, 3) @@ -399,6 +402,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_decrypt, 0, 0, 3) ZEND_ARG_INFO(0, password) ZEND_ARG_INFO(0, options) ZEND_ARG_INFO(0, iv) + ZEND_ARG_INFO(0, tag) + ZEND_ARG_INFO(0, aad) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_openssl_cipher_iv_length, 0) @@ -1243,6 +1248,12 @@ PHP_MINIT_FUNCTION(openssl) OpenSSL_add_all_digests(); OpenSSL_add_all_algorithms(); +#if !defined(OPENSSL_NO_AES) && defined(EVP_CIPH_CCM_MODE) && OPENSSL_VERSION_NUMBER < 0x100020000 + EVP_add_cipher(EVP_aes_128_ccm()); + EVP_add_cipher(EVP_aes_192_ccm()); + EVP_add_cipher(EVP_aes_256_ccm()); +#endif + SSL_load_error_strings(); /* register a resource id number with OpenSSL so that we can map SSL -> stream structures in @@ -5577,13 +5588,60 @@ PHP_FUNCTION(openssl_digest) } /* }}} */ -static zend_bool php_openssl_validate_iv(char **piv, size_t *piv_len, size_t iv_required_len) +/* Cipher mode info */ +struct php_openssl_cipher_mode { + zend_bool is_aead; + zend_bool is_single_run_aead; + int aead_get_tag_flag; + int aead_set_tag_flag; + int aead_ivlen_flag; +}; + +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 + case EVP_CIPH_GCM_MODE: + mode->is_aead = 1; + 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 + case EVP_CIPH_CCM_MODE: + mode->is_aead = 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 + default: + memset(mode, 0, sizeof(struct php_openssl_cipher_mode)); + } +} +/* }}} */ + +static int php_openssl_validate_iv(char **piv, size_t *piv_len, size_t iv_required_len, + zend_bool *free_iv, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode) /* {{{ */ { char *iv_new; /* Best case scenario, user behaved */ if (*piv_len == iv_required_len) { - return 0; + return SUCCESS; + } + + if (mode->is_aead) { + if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_ivlen_flag, *piv_len, NULL) != 1) { + php_error_docref(NULL, E_WARNING, + "Setting of IV length for AEAD mode failed, the expected length is %d bytes", + iv_required_len); + return FAILURE; + } + return SUCCESS; } iv_new = ecalloc(1, iv_required_len + 1); @@ -5592,84 +5650,181 @@ static zend_bool php_openssl_validate_iv(char **piv, size_t *piv_len, size_t iv_ /* BC behavior */ *piv_len = iv_required_len; *piv = iv_new; - return 1; + *free_iv = 1; + return SUCCESS; + } if (*piv_len < iv_required_len) { - php_error_docref(NULL, E_WARNING, "IV passed is only %zd bytes long, cipher expects an IV of precisely %zd bytes, padding with \\0", *piv_len, iv_required_len); + php_error_docref(NULL, E_WARNING, + "IV passed is only %zd bytes long, cipher expects an IV of precisely %zd bytes, padding with \\0", + *piv_len, iv_required_len); memcpy(iv_new, *piv, *piv_len); *piv_len = iv_required_len; *piv = iv_new; - return 1; + *free_iv = 1; + return SUCCESS; } - php_error_docref(NULL, E_WARNING, "IV passed is %zd bytes long which is longer than the %zd expected by selected cipher, truncating", *piv_len, iv_required_len); + php_error_docref(NULL, E_WARNING, + "IV passed is %zd bytes long which is longer than the %zd expected by selected cipher, truncating", + *piv_len, iv_required_len); memcpy(iv_new, *piv, iv_required_len); *piv_len = iv_required_len; *piv = iv_new; - return 1; + *free_iv = 1; + return SUCCESS; + +} +/* }}} */ + +static int php_openssl_cipher_init(const EVP_CIPHER *cipher_type, + EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode, + char **ppassword, size_t *ppassword_len, zend_bool *free_password, + char **piv, size_t *piv_len, zend_bool *free_iv, + char *tag, int tag_len, zend_long options, int enc) /* {{{ */ +{ + unsigned char *key; + int key_len, password_len; + size_t max_iv_len; + + /* check and set key */ + password_len = (int) *ppassword_len; + key_len = EVP_CIPHER_key_length(cipher_type); + if (key_len > password_len) { + key = emalloc(key_len); + memset(key, 0, key_len); + memcpy(key, *ppassword, password_len); + *ppassword = (char *) key; + *ppassword_len = key_len; + *free_password = 1; + } else { + key = (unsigned char*)*ppassword; + *free_password = 0; + } + + max_iv_len = EVP_CIPHER_iv_length(cipher_type); + if (enc && *piv_len == 0 && max_iv_len > 0 && !mode->is_aead) { + php_error_docref(NULL, E_WARNING, + "Using an empty Initialization Vector (iv) is potentially insecure and not recommended"); + } + + if (!EVP_CipherInit_ex(cipher_ctx, cipher_type, NULL, NULL, NULL, enc)) { + return FAILURE; + } + 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) { + EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, NULL); + } else 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)) { + php_error_docref(NULL, E_WARNING, "Setting tag for AEAD cipher decryption failed"); + return FAILURE; + } + } + if (password_len > key_len) { + EVP_CIPHER_CTX_set_key_length(cipher_ctx, password_len); + } + if (!EVP_CipherInit_ex(cipher_ctx, NULL, NULL, key, (unsigned char *)*piv, enc)) { + return FAILURE; + } + if (options & OPENSSL_ZERO_PADDING) { + EVP_CIPHER_CTX_set_padding(cipher_ctx, 0); + } + + return SUCCESS; +} +/* }}} */ +static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type, + EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode, + zend_string **poutbuf, int *poutlen, char *data, size_t data_len, + char *aad, size_t aad_len, int enc) /* {{{ */ +{ + int i = 0; + + if (mode->is_single_run_aead && !EVP_EncryptUpdate(cipher_ctx, NULL, &i, NULL, (int)data_len)) { + php_error_docref(NULL, E_WARNING, "Setting of data length failed"); + return FAILURE; + } + + if (mode->is_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, (unsigned char *)aad, (int)aad_len)) { + php_error_docref(NULL, E_WARNING, "Setting of additional application data failed"); + return FAILURE; + } + + *poutbuf = zend_string_alloc((int)data_len + EVP_CIPHER_block_size(cipher_type), 0); + + if ((!enc || data_len > 0) && + !EVP_CipherUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(*poutbuf), + &i, (unsigned char *)data, (int)data_len)) { + /* we don't show warning when we fail but if we ever do, then it should look like this: + if (mode->is_single_run_aead && !enc) { + php_error_docref(NULL, E_WARNING, "Tag verifycation failed"); + } else { + php_error_docref(NULL, E_WARNING, enc ? "Encryption failed" : "Decryption failed"); + } + */ + zend_string_release(*poutbuf); + return FAILURE; + } + + *poutlen = i; + + return SUCCESS; } +/* }}} */ -/* {{{ proto string openssl_encrypt(string data, string method, string password [, long options=0 [, string $iv='']]) +/* {{{ proto string openssl_encrypt(string data, string method, string password [, long options=0 [, string $iv=''[, string &$tag = ''[, string $aad = ''[, long $tag_length = 16]]]]]) Encrypts given data with given method and key, returns raw or base64 encoded string */ PHP_FUNCTION(openssl_encrypt) { - zend_long options = 0; - char *data, *method, *password, *iv = ""; - size_t data_len, method_len, password_len, iv_len = 0, max_iv_len; + zend_long options = 0, tag_len = 16; + char *data, *method, *password, *iv = "", *aad = ""; + size_t data_len, method_len, password_len, iv_len = 0, aad_len = 0; + zval *tag = NULL; const EVP_CIPHER *cipher_type; - EVP_CIPHER_CTX cipher_ctx; - int i=0, outlen, keylen; + EVP_CIPHER_CTX *cipher_ctx; + struct php_openssl_cipher_mode mode; + int i=0, outlen; zend_string *outbuf; - unsigned char *key; - zend_bool free_iv; + zend_bool free_iv = 0, free_password = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|ls", &data, &data_len, &method, &method_len, &password, &password_len, &options, &iv, &iv_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lsz/sl", &data, &data_len, &method, &method_len, + &password, &password_len, &options, &iv, &iv_len, &tag, &aad, &aad_len, &tag_len) == FAILURE) { return; } + + PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data); + PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password); + PHP_OPENSSL_CHECK_SIZE_T_TO_INT(aad_len, aad); + PHP_OPENSSL_CHECK_LONG_TO_INT(tag_len, tag_len); + cipher_type = EVP_get_cipherbyname(method); if (!cipher_type) { php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm"); RETURN_FALSE; } - PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data); - - keylen = EVP_CIPHER_key_length(cipher_type); - if (keylen > password_len) { - key = emalloc(keylen); - memset(key, 0, keylen); - memcpy(key, password, password_len); - } else { - key = (unsigned char*)password; + cipher_ctx = EVP_CIPHER_CTX_new(); + if (!cipher_ctx) { + php_error_docref(NULL, E_WARNING, "Failed to create cipher context"); + RETURN_FALSE; } - max_iv_len = EVP_CIPHER_iv_length(cipher_type); - if (iv_len == 0 && max_iv_len > 0) { - php_error_docref(NULL, E_WARNING, "Using an empty Initialization Vector (iv) is potentially insecure and not recommended"); - } - free_iv = php_openssl_validate_iv(&iv, &iv_len, max_iv_len); + php_openssl_load_cipher_mode(&mode, cipher_type); - outlen = (int)data_len + EVP_CIPHER_block_size(cipher_type); - outbuf = zend_string_alloc(outlen, 0); - EVP_EncryptInit(&cipher_ctx, cipher_type, NULL, NULL); - if (password_len > keylen) { - PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password); - if (!EVP_CIPHER_CTX_set_key_length(&cipher_ctx, (int)password_len)) { - php_openssl_store_errors(); - } - } - EVP_EncryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv); - if (options & OPENSSL_ZERO_PADDING) { - EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0); - } - if (data_len > 0) { - EVP_EncryptUpdate(&cipher_ctx, (unsigned char*)ZSTR_VAL(outbuf), &i, (unsigned char *)data, (int)data_len); - } - outlen = i; - if (EVP_EncryptFinal(&cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + i, &i)) { + if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode, + &password, &password_len, &free_password, + &iv, &iv_len, &free_iv, NULL, tag_len, options, 1) == FAILURE || + php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen, + data, data_len, aad, aad_len, 1) == FAILURE) { + RETVAL_FALSE; + } else if (EVP_EncryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) { outlen += i; if (options & OPENSSL_RAW_DATA) { ZSTR_VAL(outbuf)[outlen] = '\0'; @@ -5682,37 +5837,58 @@ PHP_FUNCTION(openssl_encrypt) zend_string_release(outbuf); RETVAL_STR(base64_str); } + if (mode.is_aead && tag) { + zend_string *tag_str = zend_string_alloc(tag_len, 0); + + if (EVP_CIPHER_CTX_ctrl(cipher_ctx, mode.aead_get_tag_flag, tag_len, ZSTR_VAL(tag_str)) == 1) { + zval_dtor(tag); + ZSTR_VAL(tag_str)[tag_len] = '\0'; + ZSTR_LEN(tag_str) = tag_len; + ZVAL_NEW_STR(tag, tag_str); + } else { + zend_string_release(tag_str); + php_error_docref(NULL, E_WARNING, "Retrieving verification tag failed"); + } + } else if (tag) { + zval_dtor(tag); + ZVAL_NULL(tag); + php_error_docref(NULL, E_WARNING, + "The authenticated tag cannot be provided for cipher that doesn not support AEAD"); + } } else { php_openssl_store_errors(); zend_string_release(outbuf); RETVAL_FALSE; } - if (key != (unsigned char*)password) { - efree(key); + + if (free_password) { + efree(password); } if (free_iv) { efree(iv); } - EVP_CIPHER_CTX_cleanup(&cipher_ctx); + EVP_CIPHER_CTX_cleanup(cipher_ctx); + EVP_CIPHER_CTX_free(cipher_ctx); } /* }}} */ -/* {{{ proto string openssl_decrypt(string data, string method, string password [, long options=0 [, string $iv = '']]) +/* {{{ proto string openssl_decrypt(string data, string method, string password [, long options=0 [, string $iv = ''[, string $tag = ''[, string $aad = '']]]]) Takes raw or base64 encoded string and decrypts it using given method and key */ PHP_FUNCTION(openssl_decrypt) { zend_long options = 0; - char *data, *method, *password, *iv = ""; - size_t data_len, method_len, password_len, iv_len = 0; + char *data, *method, *password, *iv = "", *tag = NULL, *aad = ""; + size_t data_len, method_len, password_len, iv_len = 0, tag_len = 0, aad_len = 0; const EVP_CIPHER *cipher_type; - EVP_CIPHER_CTX cipher_ctx; - int i, outlen, keylen; + EVP_CIPHER_CTX *cipher_ctx; + struct php_openssl_cipher_mode mode; + int outlen, i = 0; zend_string *outbuf; - unsigned char *key; zend_string *base64_str = NULL; - zend_bool free_iv; + zend_bool free_iv = 0, free_password = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|ls", &data, &data_len, &method, &method_len, &password, &password_len, &options, &iv, &iv_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|lsss", &data, &data_len, &method, &method_len, + &password, &password_len, &options, &iv, &iv_len, &tag, &tag_len, &aad, &aad_len) == FAILURE) { return; } @@ -5722,6 +5898,9 @@ PHP_FUNCTION(openssl_decrypt) } PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data); + PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password); + PHP_OPENSSL_CHECK_SIZE_T_TO_INT(aad_len, aad); + PHP_OPENSSL_CHECK_SIZE_T_TO_INT(tag_len, tag); cipher_type = EVP_get_cipherbyname(method); if (!cipher_type) { @@ -5729,44 +5908,33 @@ PHP_FUNCTION(openssl_decrypt) RETURN_FALSE; } + cipher_ctx = EVP_CIPHER_CTX_new(); + if (!cipher_ctx) { + php_error_docref(NULL, E_WARNING, "Failed to create cipher context"); + RETURN_FALSE; + } + + php_openssl_load_cipher_mode(&mode, cipher_type); + if (!(options & OPENSSL_RAW_DATA)) { base64_str = php_base64_decode((unsigned char*)data, (int)data_len); if (!base64_str) { php_error_docref(NULL, E_WARNING, "Failed to base64 decode the input"); + EVP_CIPHER_CTX_free(cipher_ctx); RETURN_FALSE; } data_len = ZSTR_LEN(base64_str); data = ZSTR_VAL(base64_str); } - keylen = EVP_CIPHER_key_length(cipher_type); - if (keylen > password_len) { - key = emalloc(keylen); - memset(key, 0, keylen); - memcpy(key, password, password_len); - } else { - key = (unsigned char*)password; - } - - free_iv = php_openssl_validate_iv(&iv, &iv_len, EVP_CIPHER_iv_length(cipher_type)); - - outlen = (int)data_len + EVP_CIPHER_block_size(cipher_type); - outbuf = zend_string_alloc(outlen, 0); - - EVP_DecryptInit(&cipher_ctx, cipher_type, NULL, NULL); - if (password_len > keylen) { - PHP_OPENSSL_CHECK_SIZE_T_TO_INT(password_len, password); - if (!EVP_CIPHER_CTX_set_key_length(&cipher_ctx, (int)password_len)) { - php_openssl_store_errors(); - } - } - EVP_DecryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv); - if (options & OPENSSL_ZERO_PADDING) { - EVP_CIPHER_CTX_set_padding(&cipher_ctx, 0); - } - EVP_DecryptUpdate(&cipher_ctx, (unsigned char*)ZSTR_VAL(outbuf), &i, (unsigned char *)data, (int)data_len); - outlen = i; - if (EVP_DecryptFinal(&cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + i, &i)) { + if (php_openssl_cipher_init(cipher_type, cipher_ctx, &mode, + &password, &password_len, &free_password, + &iv, &iv_len, &free_iv, tag, tag_len, options, 0) == FAILURE || + php_openssl_cipher_update(cipher_type, cipher_ctx, &mode, &outbuf, &outlen, + data, data_len, aad, aad_len, 0) == FAILURE) { + RETVAL_FALSE; + } else if (mode.is_single_run_aead || + EVP_DecryptFinal(cipher_ctx, (unsigned char *)ZSTR_VAL(outbuf) + outlen, &i)) { outlen += i; ZSTR_VAL(outbuf)[outlen] = '\0'; ZSTR_LEN(outbuf) = outlen; @@ -5776,8 +5944,9 @@ PHP_FUNCTION(openssl_decrypt) zend_string_release(outbuf); RETVAL_FALSE; } - if (key != (unsigned char*)password) { - efree(key); + + if (free_password) { + efree(password); } if (free_iv) { efree(iv); @@ -5785,7 +5954,8 @@ PHP_FUNCTION(openssl_decrypt) if (base64_str) { zend_string_release(base64_str); } - EVP_CIPHER_CTX_cleanup(&cipher_ctx); + EVP_CIPHER_CTX_cleanup(cipher_ctx); + EVP_CIPHER_CTX_free(cipher_ctx); } /* }}} */ diff --git a/ext/openssl/tests/cipher_tests.inc b/ext/openssl/tests/cipher_tests.inc new file mode 100644 index 0000000000..b9e84af8f8 --- /dev/null +++ b/ext/openssl/tests/cipher_tests.inc @@ -0,0 +1,111 @@ +<?php +$php_openssl_cipher_tests = array( + 'aes-256-ccm' => array( + array( + 'key' => '1bde3251d41a8b5ea013c195ae128b21' . + '8b3e0306376357077ef1c1c78548b92e', + 'iv' => '5b8e40746f6b98e00f1d13ff41', + 'aad' => 'c17a32514eb6103f3249e076d4c871dc' . + '97e04b286699e54491dc18f6d734d4c0', + 'tag' => '2024931d73bca480c24a24ece6b6c2bf', + 'pt' => '53bd72a97089e312422bf72e242377b3' . + 'c6ee3e2075389b999c4ef7f28bd2b80a', + 'ct' => '9a5fcccdb4cf04e7293d2775cc76a488' . + 'f042382d949b43b7d6bb2b9864786726', + ), + ), + 'aes-128-gcm' => array( + array( + 'key' => '00000000000000000000000000000000', + 'iv' => '000000000000000000000000', + 'tag' => '58e2fccefa7e3061367f1d57a4e7455a', + 'pt' => '', + 'ct' => '', + ), + array( + 'key' => '00000000000000000000000000000000', + 'iv' => '000000000000000000000000', + 'tag' => 'ab6e47d42cec13bdf53a67b21257bddf', + 'pt' => '00000000000000000000000000000000', + 'ct' => '0388dace60b6a392f328c2b971b2fe78', + ), + array( + 'key' => 'feffe9928665731c6d6a8f9467308308', + 'iv' => 'cafebabefacedbaddecaf888', + 'tag' => '4d5c2af327cd64a62cf35abd2ba6fab4', + 'pt' => 'd9313225f88406e5a55909c5aff5269a' . + '86a7a9531534f7da2e4c303d8a318a72' . + '1c3c0c95956809532fcf0e2449a6b525' . + 'b16aedf5aa0de657ba637b391aafd255', + 'ct' => '42831ec2217774244b7221b784d0d49c' . + 'e3aa212f2c02a4e035c17e2329aca12e' . + '21d514b25466931c7d8f6a5aac84aa05' . + '1ba30b396a0aac973d58e091473f5985', + ), + array( + 'key' => 'feffe9928665731c6d6a8f9467308308', + 'iv' => 'cafebabefacedbaddecaf888', + 'aad' => 'feedfacedeadbeeffeedfacedeadbeefabaddad2', + 'tag' => '5bc94fbc3221a5db94fae95ae7121a47', + 'pt' => 'd9313225f88406e5a55909c5aff5269a' . + '86a7a9531534f7da2e4c303d8a318a72' . + '1c3c0c95956809532fcf0e2449a6b525' . + 'b16aedf5aa0de657ba637b39', + 'ct' => '42831ec2217774244b7221b784d0d49c' . + 'e3aa212f2c02a4e035c17e2329aca12e' . + '21d514b25466931c7d8f6a5aac84aa05' . + '1ba30b396a0aac973d58e091', + ), + array( + 'key' => 'feffe9928665731c6d6a8f9467308308', + 'iv' => 'cafebabefacedbad', + 'aad' => 'feedfacedeadbeeffeedfacedeadbeefabaddad2', + 'tag' => '3612d2e79e3b0785561be14aaca2fccb', + 'pt' => 'd9313225f88406e5a55909c5aff5269a' . + '86a7a9531534f7da2e4c303d8a318a72' . + '1c3c0c95956809532fcf0e2449a6b525' . + 'b16aedf5aa0de657ba637b39', + 'ct' => '61353b4c2806934a777ff51fa22a4755' . + '699b2a714fcdc6f83766e5f97b6c7423' . + '73806900e49f24b22b097544d4896b42' . + '4989b5e1ebac0f07c23f4598' + ), + array( + 'key' => 'feffe9928665731c6d6a8f9467308308', + 'iv' => '9313225df88406e555909c5aff5269aa' . + '6a7a9538534f7da1e4c303d2a318a728' . + 'c3c0c95156809539fcf0e2429a6b5254' . + '16aedbf5a0de6a57a637b39b', + 'aad' => 'feedfacedeadbeeffeedfacedeadbeefabaddad2', + 'tag' => '619cc5aefffe0bfa462af43c1699d050', + 'pt' => 'd9313225f88406e5a55909c5aff5269a' . + '86a7a9531534f7da2e4c303d8a318a72' . + '1c3c0c95956809532fcf0e2449a6b525' . + 'b16aedf5aa0de657ba637b39', + 'ct' => '8ce24998625615b603a033aca13fb894' . + 'be9112a5c3a211a8ba262a3cca7e2ca7' . + '01e4a9a4fba43c90ccdcb281d48c7c6f' . + 'd62875d2aca417034c34aee5', + ), + ) +); + +function openssl_get_cipher_tests($method) +{ + global $php_openssl_cipher_tests; + + $tests = array(); + + foreach ($php_openssl_cipher_tests[$method] as $instance) { + $test = array(); + foreach ($instance as $field_name => $field_value) { + $test[$field_name] = pack("H*", $field_value); + } + if (!isset($test['aad'])) { + $test['aad'] = ""; + } + $tests[] = $test; + } + + return $tests; +} diff --git a/ext/openssl/tests/openssl_decrypt_ccm.phpt b/ext/openssl/tests/openssl_decrypt_ccm.phpt new file mode 100644 index 0000000000..359ce42ed9 --- /dev/null +++ b/ext/openssl/tests/openssl_decrypt_ccm.phpt @@ -0,0 +1,38 @@ +--TEST-- +openssl_decrypt() with CCM cipher algorithm tests +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) + die("skip"); +if (!in_array('aes-256-ccm', openssl_get_cipher_methods())) + die("skip: aes-256-ccm not available"); +?> +--FILE-- +<?php +require_once __DIR__ . "/cipher_tests.inc"; +$method = 'aes-256-ccm'; +$tests = openssl_get_cipher_tests($method); + +foreach ($tests as $idx => $test) { + echo "TEST $idx\n"; + $pt = openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA, + $test['iv'], $test['tag'], $test['aad']); + var_dump($test['pt'] === $pt); +} + +// no IV +var_dump(openssl_decrypt($test['ct'], $method, $test['key'], 0, NULL, $test['tag'], $test['aad'])); +// failed because no AAD +var_dump(openssl_decrypt($test['ct'], $method, $test['key'], 0, $test['iv'], $test['tag'])); +// failed because wrong tag +var_dump(openssl_decrypt($test['ct'], $method, $test['key'], 0, $test['iv'], str_repeat('x', 10), $test['aad'])); + +?> +--EXPECTF-- +TEST 0 +bool(true) + +Warning: openssl_decrypt(): Setting of IV length for AEAD mode failed, the expected length is 12 bytes in %s on line %d +bool(false) +bool(false) +bool(false) diff --git a/ext/openssl/tests/openssl_decrypt_error.phpt b/ext/openssl/tests/openssl_decrypt_error.phpt index 40debbd04f..44c2cd473d 100644 --- a/ext/openssl/tests/openssl_decrypt_error.phpt +++ b/ext/openssl/tests/openssl_decrypt_error.phpt @@ -22,6 +22,9 @@ var_dump(openssl_decrypt($wrong, $wrong, $wrong)); var_dump(openssl_decrypt(array(), $method, $password)); var_dump(openssl_decrypt($encrypted, array(), $password)); var_dump(openssl_decrypt($encrypted, $method, array())); + +// invalid using of an authentication tag +var_dump(openssl_encrypt($data, $method, $password, 0, $iv, $wrong)); ?> --EXPECTF-- @@ -51,3 +54,6 @@ NULL Warning: openssl_decrypt() expects parameter 3 to be string, array given in %s on line %d NULL + +Warning: openssl_encrypt(): The authenticated tag cannot be provided for cipher that doesn not support AEAD in %s on line %d +string(44) "yof6cPPH4mLee6TOc0YQSrh4dvywMqxGUyjp0lV6+aM=" diff --git a/ext/openssl/tests/openssl_decrypt_gcm.phpt b/ext/openssl/tests/openssl_decrypt_gcm.phpt new file mode 100644 index 0000000000..0202e6430b --- /dev/null +++ b/ext/openssl/tests/openssl_decrypt_gcm.phpt @@ -0,0 +1,48 @@ +--TEST-- +openssl_decrypt() with GCM cipher algorithm tests +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) + die("skip"); +if (!in_array('aes-128-gcm', openssl_get_cipher_methods())) + die("skip: aes-128-gcm not available"); +?> +--FILE-- +<?php +require_once __DIR__ . "/cipher_tests.inc"; +$method = 'aes-128-gcm'; +$tests = openssl_get_cipher_tests($method); + +foreach ($tests as $idx => $test) { + echo "TEST $idx\n"; + $pt = openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA, + $test['iv'], $test['tag'], $test['aad']); + var_dump($test['pt'] === $pt); +} + +// no IV +var_dump(openssl_decrypt($test['ct'], $method, $test['key'], 0, NULL, $test['tag'], $test['aad'])); +// failed because no AAD +var_dump(openssl_decrypt($test['ct'], $method, $test['key'], 0, $test['iv'], $test['tag'])); +// failed because wrong tag +var_dump(openssl_decrypt($test['ct'], $method, $test['key'], 0, $test['iv'], str_repeat('x', 16), $test['aad'])); + +?> +--EXPECTF-- +TEST 0 +bool(true) +TEST 1 +bool(true) +TEST 2 +bool(true) +TEST 3 +bool(true) +TEST 4 +bool(true) +TEST 5 +bool(true) + +Warning: openssl_decrypt(): Setting of IV length for AEAD mode failed, the expected length is 12 bytes in %s on line %d +bool(false) +bool(false) +bool(false) diff --git a/ext/openssl/tests/openssl_encrypt_ccm.phpt b/ext/openssl/tests/openssl_encrypt_ccm.phpt new file mode 100644 index 0000000000..1606044997 --- /dev/null +++ b/ext/openssl/tests/openssl_encrypt_ccm.phpt @@ -0,0 +1,39 @@ +--TEST-- +openssl_encrypt() with CCM cipher algorithm tests +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) + die("skip"); +if (!in_array('aes-256-ccm', openssl_get_cipher_methods())) + die("skip: aes-256-ccm not available"); +?> +--FILE-- +<?php +require_once __DIR__ . "/cipher_tests.inc"; +$method = 'aes-256-ccm'; +$tests = openssl_get_cipher_tests($method); + +foreach ($tests as $idx => $test) { + echo "TEST $idx\n"; + $ct = openssl_encrypt($test['pt'], $method, $test['key'], OPENSSL_RAW_DATA, + $test['iv'], $tag, $test['aad'], strlen($test['tag'])); + var_dump($test['ct'] === $ct); + var_dump($test['tag'] === $tag); +} + +// Empty IV error +var_dump(openssl_encrypt('data', $method, 'password', 0, NULL, $tag, '')); + +// Test setting different IV length and unlimeted tag +var_dump(openssl_encrypt('data', $method, 'password', 0, str_repeat('x', 10), $tag, '', 1024)); +var_dump(strlen($tag)); +?> +--EXPECTF-- +TEST 0 +bool(true) +bool(true) + +Warning: openssl_encrypt(): Setting of IV length for AEAD mode failed, the expected length is 12 bytes in %s on line %d +bool(false) +string(8) "p/lvgA==" +int(1024) diff --git a/ext/openssl/tests/openssl_encrypt_error.phpt b/ext/openssl/tests/openssl_encrypt_error.phpt index 7376f48708..791c431211 100644 --- a/ext/openssl/tests/openssl_encrypt_error.phpt +++ b/ext/openssl/tests/openssl_encrypt_error.phpt @@ -7,10 +7,12 @@ openssl_encrypt() error tests $data = "openssl_encrypt() tests"; $method = "AES-128-CBC"; $password = "openssl"; +$iv = str_repeat("\0", openssl_cipher_iv_length($method)); $wrong = "wrong"; $object = new stdclass; $arr = array(1); +// wrong paramters tests var_dump(openssl_encrypt($data, $wrong, $password)); var_dump(openssl_encrypt($object, $method, $password)); var_dump(openssl_encrypt($data, $object, $password)); @@ -18,6 +20,9 @@ var_dump(openssl_encrypt($data, $method, $object)); var_dump(openssl_encrypt($arr, $method, $object)); var_dump(openssl_encrypt($data, $arr, $object)); var_dump(openssl_encrypt($data, $method, $arr)); + +// invalid using of an authentication tag +var_dump(openssl_encrypt($data, $method, $password, 0, $iv, $wrong)); ?> --EXPECTF-- Warning: openssl_encrypt(): Unknown cipher algorithm in %s on line %d @@ -41,3 +46,5 @@ NULL Warning: openssl_encrypt() expects parameter 3 to be string, array given in %s on line %d NULL +Warning: openssl_encrypt(): The authenticated tag cannot be provided for cipher that doesn not support AEAD in %s on line %d +string(44) "iPR4HulskuaP5Z6me5uImk6BqVyJG73+63tkPauVZYk=" diff --git a/ext/openssl/tests/openssl_encrypt_gcm.phpt b/ext/openssl/tests/openssl_encrypt_gcm.phpt new file mode 100644 index 0000000000..6c55404b50 --- /dev/null +++ b/ext/openssl/tests/openssl_encrypt_gcm.phpt @@ -0,0 +1,54 @@ +--TEST-- +openssl_encrypt() with GCM cipher algorithm tests +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) + die("skip"); +if (!in_array('aes-128-gcm', openssl_get_cipher_methods())) + die("skip: aes-128-gcm not available"); +?> +--FILE-- +<?php +require_once __DIR__ . "/cipher_tests.inc"; +$method = 'aes-128-gcm'; +$tests = openssl_get_cipher_tests($method); + +foreach ($tests as $idx => $test) { + echo "TEST $idx\n"; + $ct = openssl_encrypt($test['pt'], $method, $test['key'], OPENSSL_RAW_DATA, + $test['iv'], $tag, $test['aad'], strlen($test['tag'])); + var_dump($test['ct'] === $ct); + var_dump($test['tag'] === $tag); +} + +// Empty IV error +var_dump(openssl_encrypt('data', $method, 'password', 0, NULL, $tag, '')); + +// Failing to retrieve tag (max is 16 bytes) +var_dump(openssl_encrypt('data', $method, 'password', 0, str_repeat('x', 32), $tag, '', 20)); +?> +--EXPECTF-- +TEST 0 +bool(true) +bool(true) +TEST 1 +bool(true) +bool(true) +TEST 2 +bool(true) +bool(true) +TEST 3 +bool(true) +bool(true) +TEST 4 +bool(true) +bool(true) +TEST 5 +bool(true) +bool(true) + +Warning: openssl_encrypt(): Setting of IV length for AEAD mode failed, the expected length is 12 bytes in %s on line %d +bool(false) + +Warning: openssl_encrypt(): Retrieving verification tag failed in %s on line %d +string(8) "S6+N0w==" |