Skip to content

Implement FR #76935: OpenSSL chacha20-poly1305 AEAD support #9366

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions ext/openssl/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -7170,27 +7170,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:
Expand All @@ -7213,7 +7229,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) /* {{{ */
Expand Down
66 changes: 66 additions & 0 deletions ext/openssl/tests/cipher_tests.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
48 changes: 48 additions & 0 deletions ext/openssl/tests/openssl_encrypt_chacha20_poly1305.phpt
Original file line number Diff line number Diff line change
@@ -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)