Skip to content

Add support for OCB mode in OpenSSL #6337

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 3 commits 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
33 changes: 27 additions & 6 deletions ext/openssl/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
Expand Down Expand Up @@ -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)) {
Expand Down
53 changes: 52 additions & 1 deletion ext/openssl/tests/cipher_tests.inc
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,58 @@ $php_openssl_cipher_tests = array(
'01e4a9a4fba43c90ccdcb281d48c7c6f' .
'd62875d2aca417034c34aee5',
),
)
),
// First few test vectors from RFC 7253.
'aes-128-ocb' => array(
array(
'key' => '000102030405060708090A0B0C0D0E0F',
'iv' => 'BBAA99887766554433221100',
'aad' => '',
'pt' => '',
'tag' => '785407BFFFC8AD9EDCC5520AC9111EE6',
'ct' => '',
),
array(
'key' => '000102030405060708090A0B0C0D0E0F',
'iv' => 'BBAA99887766554433221101',
'aad' => '0001020304050607',
'pt' => '0001020304050607',
'tag' => '5725BDA0D3B4EB3A257C9AF1F8F03009',
'ct' => '6820B3657B6F615A',
),
array(
'key' => '000102030405060708090A0B0C0D0E0F',
'iv' => 'BBAA99887766554433221102',
'aad' => '0001020304050607',
'pt' => '',
'tag' => '81017F8203F081277152FADE694A0A00',
'ct' => '',
),
array(
'key' => '000102030405060708090A0B0C0D0E0F',
'iv' => 'BBAA99887766554433221103',
'aad' => '',
'pt' => '0001020304050607',
'tag' => '14054CD1F35D82760B2CD00D2F99BFA9',
'ct' => '45DD69F8F5AAE724',
),
array(
'key' => '000102030405060708090A0B0C0D0E0F',
'iv' => 'BBAA99887766554433221104',
'aad' => '000102030405060708090A0B0C0D0E0F',
'pt' => '000102030405060708090A0B0C0D0E0F',
'tag' => '3AD7A4FF3835B8C5701C1CCEC8FC3358',
'ct' => '571D535B60B277188BE5147170A9A22C',
),
array(
'key' => '0F0E0D0C0B0A09080706050403020100',
'iv' => 'BBAA9988776655443322110D',
'aad' => '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627',
'pt' => '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627',
'tag' => 'D0C515F4D1CDD4FDAC4F02AA',
'ct' => '1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE884ED93481529C76B6A',
),
),
);

function openssl_get_cipher_tests($method)
Expand Down
60 changes: 60 additions & 0 deletions ext/openssl/tests/openssl_decrypt_ocb.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
--TEST--
openssl_decrypt() with OCB cipher algorithm tests
--SKIPIF--
<?php
if (!extension_loaded("openssl"))
die("skip");
if (!in_array('aes-128-ocb', openssl_get_cipher_methods()))
die("skip: aes-128-ocb not available");
?>
--FILE--
<?php
require_once __DIR__ . "/cipher_tests.inc";
$method = 'aes-128-ocb';
$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'], OPENSSL_RAW_DATA,
NULL, $test['tag'], $test['aad']));

// IV too long
var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
str_repeat('x', 32), $test['tag'], $test['aad']));

// failed because no AAD
var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
$test['iv'], $test['tag']));

// failed because wrong tag
var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
$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 in %s on line %d
bool(false)

Warning: openssl_decrypt(): Setting of IV length for AEAD mode failed in %s on line %d
bool(false)
bool(false)
bool(false)
60 changes: 60 additions & 0 deletions ext/openssl/tests/openssl_encrypt_ocb.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
--TEST--
openssl_encrypt() with OCB cipher algorithm tests
--SKIPIF--
<?php
if (!extension_loaded("openssl"))
die("skip");
if (!in_array('aes-128-ocb', openssl_get_cipher_methods()))
die("skip: aes-128-ocb not available");
?>
--FILE--
<?php
require_once __DIR__ . "/cipher_tests.inc";
$method = 'aes-128-ocb';
$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 (must be at most 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)
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 in %s on line %d
bool(false)

Warning: openssl_encrypt(): Setting tag length for AEAD cipher 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)