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+.
This commit is contained in:
Jakub Zelenka 2022-08-18 13:40:50 +01:00
parent 505e8d2a04
commit 1407968891
No known key found for this signature in database
GPG key ID: 1C0779DC5C0A9DE4
4 changed files with 138 additions and 7 deletions

2
NEWS
View file

@ -17,6 +17,8 @@ PHP NEWS
- OpenSSL: - OpenSSL:
. Fixed bug GH-9310 (SSL local_cert and local_pk do not respect . Fixed bug GH-9310 (SSL local_cert and local_pk do not respect
open_basedir). (Jakub Zelenka) open_basedir). (Jakub Zelenka)
. Implement FR #76935 ("chacha20-poly1305" is an AEAD but does not work like
AEAD). (Jakub Zelenka)
- Random: - Random:
. Fixed bug GH-9415 (Randomizer::getInt(0, 2**32 - 1) with Mt19937 . Fixed bug GH-9415 (Randomizer::getInt(0, 2**32 - 1) with Mt19937

View file

@ -7145,27 +7145,43 @@ struct php_openssl_cipher_mode {
int aead_ivlen_flag; 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); int cipher_mode = EVP_CIPHER_mode(cipher_type);
memset(mode, 0, sizeof(struct php_openssl_cipher_mode)); memset(mode, 0, sizeof(struct php_openssl_cipher_mode));
switch (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 /* Since OpenSSL 1.1, all AEAD ciphers use a common framework. We check for
* EVP_CIPH_OCB_MODE, because LibreSSL does not support it. */ * EVP_CIPH_OCB_MODE, because LibreSSL does not support it. */
case EVP_CIPH_GCM_MODE: case EVP_CIPH_GCM_MODE:
# ifdef EVP_CIPH_OCB_MODE
case EVP_CIPH_OCB_MODE: case EVP_CIPH_OCB_MODE:
# endif
case EVP_CIPH_CCM_MODE: 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, /* For OCB mode, explicitly set the tag length even when decrypting,
* see https://github.com/openssl/openssl/issues/8331. */ * see https://github.com/openssl/openssl/issues/8331. */
mode->set_tag_length_always = cipher_mode == EVP_CIPH_OCB_MODE; mode->set_tag_length_always = cipher_mode == EVP_CIPH_OCB_MODE;
mode->set_tag_length_when_encrypting = cipher_mode == EVP_CIPH_CCM_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->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; break;
# ifdef NID_chacha20_poly1305
default:
if (EVP_CIPHER_nid(cipher_type) == NID_chacha20_poly1305) {
php_openssl_set_aead_flags(mode);
}
break;
# endif
#else #else
# ifdef EVP_CIPH_GCM_MODE # ifdef EVP_CIPH_GCM_MODE
case 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 #endif
} }
} }
/* }}} */
static int php_openssl_validate_iv(const char **piv, size_t *piv_len, size_t iv_required_len, 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) /* {{{ */ bool *free_iv, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode) /* {{{ */

View file

@ -160,6 +160,72 @@ $php_openssl_cipher_tests = array(
'ct' => '1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE884ED93481529C76B6A', '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) function openssl_get_cipher_tests($method)

View file

@ -0,0 +1,48 @@
--TEST--
openssl_encrypt() with ChaCha20 and Poly1305 cipher algorithm tests
--EXTENSIONS--
openssl
--SKIPIF--
<?php
if (!in_array('chacha20-poly1305', openssl_get_cipher_methods()))
die("skip: chacha20-poly1305 not available");
?>
--FILE--
<?php
require_once __DIR__ . "/cipher_tests.inc";
$method = 'chacha20-poly1305';
$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, '', $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)