Skip to content

Commit 7727a02

Browse files
committed
Merge branch 'PHP-7.4' into PHP-8.0
* PHP-7.4: Fix bug #79983: Add support for OCB mode
2 parents 8bc5e23 + 750a74e commit 7727a02

File tree

4 files changed

+199
-7
lines changed

4 files changed

+199
-7
lines changed

ext/openssl/openssl.c

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6888,6 +6888,7 @@ PHP_FUNCTION(openssl_digest)
68886888
/* Cipher mode info */
68896889
struct php_openssl_cipher_mode {
68906890
zend_bool is_aead;
6891+
zend_bool should_set_tag_length;
68916892
zend_bool is_single_run_aead;
68926893
int aead_get_tag_flag;
68936894
int aead_set_tag_flag;
@@ -6896,24 +6897,41 @@ struct php_openssl_cipher_mode {
68966897

68976898
static void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, const EVP_CIPHER *cipher_type) /* {{{ */
68986899
{
6899-
switch (EVP_CIPHER_mode(cipher_type)) {
6900-
#ifdef EVP_CIPH_GCM_MODE
6900+
int cipher_mode = EVP_CIPHER_mode(cipher_type);
6901+
switch (cipher_mode) {
6902+
#if PHP_OPENSSL_API_VERSION >= 0x10100
6903+
case EVP_CIPH_GCM_MODE:
6904+
case EVP_CIPH_OCB_MODE:
6905+
case EVP_CIPH_CCM_MODE:
6906+
mode->is_aead = 1;
6907+
mode->should_set_tag_length =
6908+
cipher_mode == EVP_CIPH_CCM_MODE || cipher_mode == EVP_CIPH_OCB_MODE;
6909+
mode->is_single_run_aead = cipher_mode == EVP_CIPH_CCM_MODE;
6910+
mode->aead_get_tag_flag = EVP_CTRL_AEAD_GET_TAG;
6911+
mode->aead_set_tag_flag = EVP_CTRL_AEAD_SET_TAG;
6912+
mode->aead_ivlen_flag = EVP_CTRL_AEAD_SET_IVLEN;
6913+
break;
6914+
#else
6915+
# ifdef EVP_CIPH_GCM_MODE
69016916
case EVP_CIPH_GCM_MODE:
69026917
mode->is_aead = 1;
6918+
mode->should_set_tag_length = 0;
69036919
mode->is_single_run_aead = 0;
69046920
mode->aead_get_tag_flag = EVP_CTRL_GCM_GET_TAG;
69056921
mode->aead_set_tag_flag = EVP_CTRL_GCM_SET_TAG;
69066922
mode->aead_ivlen_flag = EVP_CTRL_GCM_SET_IVLEN;
69076923
break;
6908-
#endif
6909-
#ifdef EVP_CIPH_CCM_MODE
6924+
# endif
6925+
# ifdef EVP_CIPH_CCM_MODE
69106926
case EVP_CIPH_CCM_MODE:
69116927
mode->is_aead = 1;
6928+
mode->should_set_tag_length = 1;
69126929
mode->is_single_run_aead = 1;
69136930
mode->aead_get_tag_flag = EVP_CTRL_CCM_GET_TAG;
69146931
mode->aead_set_tag_flag = EVP_CTRL_CCM_SET_TAG;
69156932
mode->aead_ivlen_flag = EVP_CTRL_CCM_SET_IVLEN;
69166933
break;
6934+
# endif
69176935
#endif
69186936
default:
69196937
memset(mode, 0, sizeof(struct php_openssl_cipher_mode));
@@ -6998,12 +7016,15 @@ static int php_openssl_cipher_init(const EVP_CIPHER *cipher_type,
69987016
if (php_openssl_validate_iv(piv, piv_len, max_iv_len, free_iv, cipher_ctx, mode) == FAILURE) {
69997017
return FAILURE;
70007018
}
7001-
if (mode->is_single_run_aead && enc) {
7019+
if (mode->should_set_tag_length) {
7020+
/* Explicitly set the tag length even when decrypting,
7021+
* see https://github.com/openssl/openssl/issues/8331. */
70027022
if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, NULL)) {
70037023
php_error_docref(NULL, E_WARNING, "Setting tag length for AEAD cipher failed");
70047024
return FAILURE;
70057025
}
7006-
} else if (!enc && tag && tag_len > 0) {
7026+
}
7027+
if (!enc && tag && tag_len > 0) {
70077028
if (!mode->is_aead) {
70087029
php_error_docref(NULL, E_WARNING, "The tag cannot be used because the cipher algorithm does not support AEAD");
70097030
} else if (!EVP_CIPHER_CTX_ctrl(cipher_ctx, mode->aead_set_tag_flag, tag_len, (unsigned char *) tag)) {

ext/openssl/tests/cipher_tests.inc

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,58 @@ $php_openssl_cipher_tests = array(
108108
'01e4a9a4fba43c90ccdcb281d48c7c6f' .
109109
'd62875d2aca417034c34aee5',
110110
),
111-
)
111+
),
112+
// First few test vectors from RFC 7253.
113+
'aes-128-ocb' => array(
114+
array(
115+
'key' => '000102030405060708090A0B0C0D0E0F',
116+
'iv' => 'BBAA99887766554433221100',
117+
'aad' => '',
118+
'pt' => '',
119+
'tag' => '785407BFFFC8AD9EDCC5520AC9111EE6',
120+
'ct' => '',
121+
),
122+
array(
123+
'key' => '000102030405060708090A0B0C0D0E0F',
124+
'iv' => 'BBAA99887766554433221101',
125+
'aad' => '0001020304050607',
126+
'pt' => '0001020304050607',
127+
'tag' => '5725BDA0D3B4EB3A257C9AF1F8F03009',
128+
'ct' => '6820B3657B6F615A',
129+
),
130+
array(
131+
'key' => '000102030405060708090A0B0C0D0E0F',
132+
'iv' => 'BBAA99887766554433221102',
133+
'aad' => '0001020304050607',
134+
'pt' => '',
135+
'tag' => '81017F8203F081277152FADE694A0A00',
136+
'ct' => '',
137+
),
138+
array(
139+
'key' => '000102030405060708090A0B0C0D0E0F',
140+
'iv' => 'BBAA99887766554433221103',
141+
'aad' => '',
142+
'pt' => '0001020304050607',
143+
'tag' => '14054CD1F35D82760B2CD00D2F99BFA9',
144+
'ct' => '45DD69F8F5AAE724',
145+
),
146+
array(
147+
'key' => '000102030405060708090A0B0C0D0E0F',
148+
'iv' => 'BBAA99887766554433221104',
149+
'aad' => '000102030405060708090A0B0C0D0E0F',
150+
'pt' => '000102030405060708090A0B0C0D0E0F',
151+
'tag' => '3AD7A4FF3835B8C5701C1CCEC8FC3358',
152+
'ct' => '571D535B60B277188BE5147170A9A22C',
153+
),
154+
array(
155+
'key' => '0F0E0D0C0B0A09080706050403020100',
156+
'iv' => 'BBAA9988776655443322110D',
157+
'aad' => '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627',
158+
'pt' => '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627',
159+
'tag' => 'D0C515F4D1CDD4FDAC4F02AA',
160+
'ct' => '1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE884ED93481529C76B6A',
161+
),
162+
),
112163
);
113164

114165
function openssl_get_cipher_tests($method)
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
--TEST--
2+
openssl_decrypt() with OCB cipher algorithm tests
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded("openssl"))
6+
die("skip");
7+
if (!in_array('aes-128-ocb', openssl_get_cipher_methods()))
8+
die("skip: aes-128-ocb not available");
9+
?>
10+
--FILE--
11+
<?php
12+
require_once __DIR__ . "/cipher_tests.inc";
13+
$method = 'aes-128-ocb';
14+
$tests = openssl_get_cipher_tests($method);
15+
16+
foreach ($tests as $idx => $test) {
17+
echo "TEST $idx\n";
18+
$pt = openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
19+
$test['iv'], $test['tag'], $test['aad']);
20+
var_dump($test['pt'] === $pt);
21+
}
22+
23+
// no IV
24+
var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
25+
NULL, $test['tag'], $test['aad']));
26+
27+
// IV too long
28+
var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
29+
str_repeat('x', 32), $test['tag'], $test['aad']));
30+
31+
// failed because no AAD
32+
var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
33+
$test['iv'], $test['tag']));
34+
35+
// failed because wrong tag
36+
var_dump(openssl_decrypt($test['ct'], $method, $test['key'], OPENSSL_RAW_DATA,
37+
$test['iv'], str_repeat('x', 16), $test['aad']));
38+
39+
?>
40+
--EXPECTF--
41+
TEST 0
42+
bool(true)
43+
TEST 1
44+
bool(true)
45+
TEST 2
46+
bool(true)
47+
TEST 3
48+
bool(true)
49+
TEST 4
50+
bool(true)
51+
TEST 5
52+
bool(true)
53+
54+
Warning: openssl_decrypt(): Setting of IV length for AEAD mode failed in %s on line %d
55+
bool(false)
56+
57+
Warning: openssl_decrypt(): Setting of IV length for AEAD mode failed in %s on line %d
58+
bool(false)
59+
bool(false)
60+
bool(false)
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
--TEST--
2+
openssl_encrypt() with OCB cipher algorithm tests
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded("openssl"))
6+
die("skip");
7+
if (!in_array('aes-128-ocb', openssl_get_cipher_methods()))
8+
die("skip: aes-128-ocb not available");
9+
?>
10+
--FILE--
11+
<?php
12+
require_once __DIR__ . "/cipher_tests.inc";
13+
$method = 'aes-128-ocb';
14+
$tests = openssl_get_cipher_tests($method);
15+
16+
foreach ($tests as $idx => $test) {
17+
echo "TEST $idx\n";
18+
$ct = openssl_encrypt($test['pt'], $method, $test['key'], OPENSSL_RAW_DATA,
19+
$test['iv'], $tag, $test['aad'], strlen($test['tag']));
20+
var_dump($test['ct'] === $ct);
21+
var_dump($test['tag'] === $tag);
22+
}
23+
24+
// Empty IV error
25+
var_dump(openssl_encrypt('data', $method, 'password', 0, NULL, $tag, ''));
26+
27+
// Failing to retrieve tag (must be at most 16 bytes)
28+
var_dump(openssl_encrypt('data', $method, 'password', 0, str_repeat('x', 12), $tag, '', 20));
29+
30+
// Failing when no tag supplied
31+
var_dump(openssl_encrypt('data', $method, 'password', 0, str_repeat('x', 12)));
32+
?>
33+
--EXPECTF--
34+
TEST 0
35+
bool(true)
36+
bool(true)
37+
TEST 1
38+
bool(true)
39+
bool(true)
40+
TEST 2
41+
bool(true)
42+
bool(true)
43+
TEST 3
44+
bool(true)
45+
bool(true)
46+
TEST 4
47+
bool(true)
48+
bool(true)
49+
TEST 5
50+
bool(true)
51+
bool(true)
52+
53+
Warning: openssl_encrypt(): Setting of IV length for AEAD mode failed in %s on line %d
54+
bool(false)
55+
56+
Warning: openssl_encrypt(): Setting tag length for AEAD cipher failed in %s on line %d
57+
bool(false)
58+
59+
Warning: openssl_encrypt(): A tag should be provided when using AEAD mode in %s on line %d
60+
bool(false)

0 commit comments

Comments
 (0)