From 14079688912cf3d1cc13f1dde16d98a07c19b51a Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 18 Aug 2022 13:40:50 +0100 Subject: [PATCH] Implement FR #76935: OpenSSL chacha20-poly1305 AEAD support This adds support for ChaCha20-Poly1305 AEAD algorithm so it is possible to use it in the same way as AES GCM and other AEAD algorithms. This is available in OpenSSL 1.1.0+. --- NEWS | 2 + ext/openssl/openssl.c | 29 ++++++-- ext/openssl/tests/cipher_tests.inc | 66 +++++++++++++++++++ .../openssl_encrypt_chacha20_poly1305.phpt | 48 ++++++++++++++ 4 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 ext/openssl/tests/openssl_encrypt_chacha20_poly1305.phpt diff --git a/NEWS b/NEWS index 54ef77b8296..e69f1ed03e2 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,8 @@ PHP NEWS - OpenSSL: . Fixed bug GH-9310 (SSL local_cert and local_pk do not respect open_basedir). (Jakub Zelenka) + . Implement FR #76935 ("chacha20-poly1305" is an AEAD but does not work like + AEAD). (Jakub Zelenka) - Random: . Fixed bug GH-9415 (Randomizer::getInt(0, 2**32 - 1) with Mt19937 diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index fc72918fb6e..56e37f008c1 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -7145,27 +7145,43 @@ struct php_openssl_cipher_mode { int aead_ivlen_flag; }; -static void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, const EVP_CIPHER *cipher_type) /* {{{ */ +#if PHP_OPENSSL_API_VERSION >= 0x10100 +static inline void php_openssl_set_aead_flags(struct php_openssl_cipher_mode *mode) { + mode->is_aead = true; + 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; +} +#endif + +static void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, const EVP_CIPHER *cipher_type) { int cipher_mode = EVP_CIPHER_mode(cipher_type); memset(mode, 0, sizeof(struct php_openssl_cipher_mode)); switch (cipher_mode) { -#ifdef EVP_CIPH_OCB_MODE +#if PHP_OPENSSL_API_VERSION >= 0x10100 /* Since OpenSSL 1.1, all AEAD ciphers use a common framework. We check for * EVP_CIPH_OCB_MODE, because LibreSSL does not support it. */ case EVP_CIPH_GCM_MODE: +# ifdef EVP_CIPH_OCB_MODE case EVP_CIPH_OCB_MODE: +# endif case EVP_CIPH_CCM_MODE: - mode->is_aead = 1; + php_openssl_set_aead_flags(mode); /* For OCB mode, explicitly set the tag length even when decrypting, * see https://github.com/openssl/openssl/issues/8331. */ mode->set_tag_length_always = cipher_mode == EVP_CIPH_OCB_MODE; mode->set_tag_length_when_encrypting = cipher_mode == EVP_CIPH_CCM_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; +# ifdef NID_chacha20_poly1305 + default: + if (EVP_CIPHER_nid(cipher_type) == NID_chacha20_poly1305) { + php_openssl_set_aead_flags(mode); + } + break; + +# endif #else # ifdef EVP_CIPH_GCM_MODE case EVP_CIPH_GCM_MODE: @@ -7188,7 +7204,6 @@ static void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, c #endif } } -/* }}} */ static int php_openssl_validate_iv(const char **piv, size_t *piv_len, size_t iv_required_len, bool *free_iv, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode) /* {{{ */ diff --git a/ext/openssl/tests/cipher_tests.inc b/ext/openssl/tests/cipher_tests.inc index 1d4988c5a8c..f543a42c9f4 100644 --- a/ext/openssl/tests/cipher_tests.inc +++ b/ext/openssl/tests/cipher_tests.inc @@ -160,6 +160,72 @@ $php_openssl_cipher_tests = array( 'ct' => '1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE884ED93481529C76B6A', ), ), + 'chacha20-poly1305' => array( + array( + 'key' => '808182838485868788898a8b8c8d8e8f' . + '909192939495969798999a9b9c9d9e9f', + 'iv' => '070000004041424344454647', + 'aad' => '50515253c0c1c2c3c4c5c6c7', + 'tag' => '1ae10b594f09e26a7e902ecbd0600691', + 'pt' => '4c616469657320616e642047656e746c' . + '656d656e206f662074686520636c6173' . + '73206f66202739393a20496620492063' . + '6f756c64206f6666657220796f75206f' . + '6e6c79206f6e652074697020666f7220' . + '746865206675747572652c2073756e73' . + '637265656e20776f756c642062652069' . + '742e', + 'ct' => 'd31a8d34648e60db7b86afbc53ef7ec2' . + 'a4aded51296e08fea9e2b5a736ee62d6' . + '3dbea45e8ca9671282fafb69da92728b' . + '1a71de0a9e060b2905d6a5b67ecd3b36' . + '92ddbd7f2d778b8c9803aee328091b58' . + 'fab324e4fad675945585808b4831d7bc' . + '3ff4def08e4b7a9de576d26586cec64b' . + '6116', + ), + array( + 'key' => '1c9240a5eb55d38af333888604f6b5f0' . + '473917c1402b80099dca5cbc207075c0', + 'iv' => '000000000102030405060708', + 'aad' => 'f33388860000000000004e91', + 'tag' => 'eead9d67890cbb22392336fea1851f38', + 'pt' => '496e7465726e65742d44726166747320' . + '61726520647261667420646f63756d65' . + '6e74732076616c696420666f72206120' . + '6d6178696d756d206f6620736978206d' . + '6f6e74687320616e64206d6179206265' . + '20757064617465642c207265706c6163' . + '65642c206f72206f62736f6c65746564' . + '206279206f7468657220646f63756d65' . + '6e747320617420616e792074696d652e' . + '20497420697320696e617070726f7072' . + '6961746520746f2075736520496e7465' . + '726e65742d4472616674732061732072' . + '65666572656e6365206d617465726961' . + '6c206f7220746f206369746520746865' . + '6d206f74686572207468616e20617320' . + '2fe2809c776f726b20696e2070726f67' . + '726573732e2fe2809d', + 'ct' => '64a0861575861af460f062c79be643bd' . + '5e805cfd345cf389f108670ac76c8cb2' . + '4c6cfc18755d43eea09ee94e382d26b0' . + 'bdb7b73c321b0100d4f03b7f355894cf' . + '332f830e710b97ce98c8a84abd0b9481' . + '14ad176e008d33bd60f982b1ff37c855' . + '9797a06ef4f0ef61c186324e2b350638' . + '3606907b6a7c02b0f9f6157b53c867e4' . + 'b9166c767b804d46a59b5216cde7a4e9' . + '9040c5a40433225ee282a1b0a06c523e' . + 'af4534d7f83fa1155b0047718cbc546a' . + '0d072b04b3564eea1b422273f548271a' . + '0bb2316053fa76991955ebd63159434e' . + 'cebb4e466dae5a1073a6727627097a10' . + '49e617d91d361094fa68f0ff77987130' . + '305beaba2eda04df997b714d6c6f2c29' . + 'a6ad5cb4022b02709b', + ), + ), ); function openssl_get_cipher_tests($method) diff --git a/ext/openssl/tests/openssl_encrypt_chacha20_poly1305.phpt b/ext/openssl/tests/openssl_encrypt_chacha20_poly1305.phpt new file mode 100644 index 00000000000..d850f81f930 --- /dev/null +++ b/ext/openssl/tests/openssl_encrypt_chacha20_poly1305.phpt @@ -0,0 +1,48 @@ +--TEST-- +openssl_encrypt() with ChaCha20 and Poly1305 cipher algorithm tests +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + $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, '', $tag, '')); + +// Failing to retrieve tag (max is 16 bytes) +var_dump(openssl_encrypt('data', $method, 'password', 0, str_repeat('x', 12), $tag, '', 20)); + +// Failing when no tag supplied +var_dump(openssl_encrypt('data', $method, 'password', 0, str_repeat('x', 12))); +?> +--EXPECTF-- +TEST 0 +bool(true) +bool(true) +TEST 1 +bool(true) +bool(true) + +Warning: openssl_encrypt(): Setting of IV length for AEAD mode failed in %s on line %d +bool(false) + +Warning: openssl_encrypt(): Retrieving verification tag failed in %s on line %d +bool(false) + +Warning: openssl_encrypt(): A tag should be provided when using AEAD mode in %s on line %d +bool(false)