php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #79601 Wrong ciphertext/tag in AES-CCM encryption for a 12 bytes IV
Submitted: 2020-05-15 10:46 UTC Modified: 2020-09-29 20:29 UTC
From: bizxing at web dot de Assigned: bukka (profile)
Status: Closed Package: OpenSSL related
PHP Version: Irrelevant OS: *
Private report: No CVE-ID: 2020-7069
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If this is not your bug, you can add a comment by following this link.
If this is your bug, but you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: bizxing at web dot de
New email:
PHP Version: OS:

 

 [2020-05-15 10:46 UTC] bizxing at web dot de
Description:
------------
openssl_encrypt generates for AES-CCM for a 12 bytes IV a wrong ciphertext and a wrong tag. Ciphertext and tag are identical to the respective values for a 7 bytes IV, which results from the 12 bytes IV when the last 5 bytes are removed.


Test script:
---------------
The following test vector for a 12 bytes IV is from NIST Special Publication 800-38C (https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38c.pdf), Example 3, page 18:

Test vector (all data hex): Tag length: 8 bytes
Key:  					40414243 44454647 48494a4b 4c4d4e4f
Nonce/IV:  				10111213 14151617 18191a1b
AAD:  					00010203 04050607 08090a0b 0c0d0e0f 10111213
Plaintext: 				20212223 24252627 28292a2b 2c2d2e2f 30313233 34353637
Ciphertext || Tag (last 8 bytes): 	e3b201a9 f5b71a7a 9b1ceaec cd97e70b 6176aad9 a4428aa5 484392fb c1b09951

The following script calculates ciphertext and tag for this test vector. Additionally, ciphertext and tag are determined for the 7 bytes IV that results from the 12 bytes IV when the last 5 bytes are removed: 

<?php
function printTag($plaintext, $key, $iv, $aad, $taglength){
    $encrypted = openssl_encrypt($plaintext, 'aes-128-ccm', $key, OPENSSL_RAW_DATA, $iv, $tag, $aad, $taglength);
    print('ciphertext: ' . bin2hex($encrypted) . ' - tag: ' .  bin2hex($tag) . "\n");
}

$key = hex2bin('404142434445464748494a4b4c4d4e4f');
$aad = hex2bin('000102030405060708090a0b0c0d0e0f10111213');
$plaintext = hex2bin('202122232425262728292a2b2c2d2e2f3031323334353637');
$tagLength = 8;
                                                               
$iv = hex2bin('101112131415161718191a1b'); // 12 bytes
printTag($plaintext, $key, $iv, $aad, $tagLength); // ciphertext: 7162015bc051951e5918aeaf3c11f3d4ac363f8d5b6af3d3 - tag: af9831fb22f8931f

$iv = hex2bin('10111213141516'); // 7 bytes
printTag($plaintext, $key, $iv, $aad, $tagLength); // ciphertext: 7162015bc051951e5918aeaf3c11f3d4ac363f8d5b6af3d3 - tag: af9831fb22f8931f
?> 


Expected result:
----------------
For the input data of the test vector, openssl_encrypt calculates that ciphertext and that tag which are specified in the test vector.


Actual result:
--------------
For the input data of the test vector, openssl_encrypt calculates a ciphertext and a tag that are different from those specified in the test vector. Ciphertext and tag are identical to the respective values for a 7 bytes IV, which results from the 12 bytes IV of the test vector when the last 5 bytes are removed.


Patches

openssl_aes_ccm_iv_fix (last revision 2020-09-20 18:16 UTC by bukka@php.net)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-05-15 12:47 UTC] cmb@php.net
-Type: Bug +Type: Security -Private report: No +Private report: Yes
 [2020-05-15 12:47 UTC] cmb@php.net
Tentatively marking as sec issue.
 [2020-05-15 14:21 UTC] cmb@php.net
-Assigned To: +Assigned To: bukka
 [2020-05-15 14:21 UTC] cmb@php.net
It seems that our assumption that the user behaved[1] is invalid.
`iv_required_len` is retrieved via `EVP_CIPHER_iv_length()`, which
returns 12 for aes-128-ccm, while its default is actually 7.
Since we're bailing out for a given length of 12, the actually
desired length is not set, and therefore `openssl_encrypt()`
behaves as if an IV of length 7 would have been passed.

Or is our assumption correct, and the values would have to match
in OpenSSL?

Jakub, what do you think?

[1] <https://github.com/php/php-src/blob/php-7.3.18/ext/openssl/openssl.c#L6444-L6447>
 [2020-05-15 14:23 UTC] cmb@php.net
-Operating System: Windows 10 +Operating System: *
 [2020-05-22 10:56 UTC] bizxing at web dot de
Some additional information: 

The PHP bug has been revealed in the context of this issue [1] on StackOverflow: openssl_encrypt returns a different ciphertext/tag for AES CCM on a 12 bytes IV than Python with the Cryptography library. A check with Java/BouncyCastle confirms the Python result. 

The same applies to the NIST test vector posted here: Python and Java fulfill the test vector, openssl_encrypt does not. 

OpenSSL itself also satisfies the test vector (verified using this code [2] adapted for the test case).

[1]: https://stackoverflow.com/a/61797909
[2]: https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_CCM_mode
 [2020-06-14 16:58 UTC] bukka@php.net
Ah I see yeah that condition is wrong for CCM. I will fix this.

Is this really a security issue? If think so, can you please detail how the security is impacted by using a shorter IV in CCM mode?
 [2020-06-15 04:47 UTC] stas@php.net
I am not sure it can cause security problem like revealing credentials etc. but I think producing broken encryption is security-relevant...
 [2020-06-18 11:44 UTC] bizxing at web dot de
IMO the following example illustrates a scenario where the bug severely affects the security of CCM:

For CCM the IV consists of a nonce (e.g. 12 bytes) and a counter (e.g. 3 bytes) [RFC3610, 2.3. Encryption, L = 3][1]. The counter is incremented with each block and ensures that different IVs are used within the same message. As nonce a random nonce for each message is generated or alternatively a random nonce that is incremented for each further message [RFC3610, 5. Nonce Sugesstions][2]. This reliably ensures that different IVs are also used across different messages.

A 12 bytes nonce that is incremented is converted by the bug into a 7 bytes nonce in which the last 5 bytes are truncated so that the nonce is identical for the first 0x100^5 = 2^40 (different) messages (most-significant-byte first order assumed). I.e. for all these messages, the i-th block of each message would be encrypted with the same key/IV(i) pair.

The crucial condition for CCM is that no key/IV pair may be used more than once, as this would destroy the security of CCM, [CCM mode][3], [RFC4309, 2. AES CCM Mode, last section][4].  

In case of a random nonce for each message (instead of an incremented one) the issue would not occur in this form. This means that the bug may have no or serious security implications depending on the implementation details.

[1]: https://tools.ietf.org/html/rfc3610#section-2.3
[2]: https://tools.ietf.org/html/rfc3610#section-5
[3]: https://en.wikipedia.org/wiki/CCM_mode
[4]: https://tools.ietf.org/html/rfc4309#section-2
 [2020-06-21 16:03 UTC] bukka@php.net
Ok I think we should treat it as a security issue then.

Just for the interest how can this have a serious security implications (not trying to say that it doesn't impact on security - just wondering why serious). I might be not understanding it correctly but with nonce being 7 bytes (it means L = 8), the counter should be 8 bytes so one can use larger messages for the price of having shorter nonce. With nonce 12 bytes, the pre computation attack will be more difficult so it's probably better for shorter messages but shouldn't the difficulty for 7 bytes nonce be still high enough when it's default?
 [2020-06-21 18:12 UTC] bizxing at web dot de
The problem is not whether the 7 bytes nonce is in principle less secure than the 12 bytes nonce. CCM allows a nounce size of 7 to 13 bytes with regard to different requirements and the security should be comparable.

I don't see the problem with a new implementation either, because if the bug is known to the developers, the implementation can be designed so that the bug does not become a serious security risk. 

I see the problem with a part of already existing applications that were designed without knowledge of this bug. E.g. in the case of the scenario described in my last comment, the developers assume that a 12 bytes nonce is used, which is incremented for each message (at the end), and do not suspect that the nounce is truncated due to the bug, so that the same nounce and thus the same IV sequence is applied for practically all messages. For a mode related to the CTR mode this is the worst case, e.g.: "The reuse of an AES-CCM or AES-GCM nonce/key combination destroys the security guarantees" [1] or "For ... CTR, reusing an IV completely destroys security" [2]. Such a scenario most likely meets the criteria for a serious security problem. 

But of course, there will also be already existing applications that are designed in such a way that the bug has no impact.

[1]: https://tools.ietf.org/html/rfc5084#section-2 
[2]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Initialization_vector_(IV)
 [2020-09-20 18:16 UTC] bukka@php.net
The following patch has been added/updated:

Patch Name: openssl_aes_ccm_iv_fix
Revision:   1600625816
URL:        https://bugs.php.net/patch-display.php?bug=79601&patch=openssl_aes_ccm_iv_fix&revision=1600625816
 [2020-09-20 18:21 UTC] bukka@php.net
Ok, the patch that applies fine to PHP 7.3 is attached. Will need to check if it applies to later version as there has been some code changes.

As this has been already exposed and the security issue can be treated in PHP code if the user has got a knowledge about the issue, I'm not really sure if we need to backport it to 7.2 and do a security release.

If everyone is fine with that, I can just push it to 7.3+.
 [2020-09-20 23:52 UTC] stas@php.net
I think unless it requires an extraordinary effort it should be backported. The patch doesn't seem to be too complicated...
 [2020-09-21 00:23 UTC] stas@php.net
In fact, 7.2 code looks the same as 7.3, and I just checked the patch seems to fix the issue for 7.2 too.
 [2020-09-21 00:31 UTC] stas@php.net
-CVE-ID: +CVE-ID: 2020-7069
 [2020-09-29 06:13 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=0216630ea2815a5789a24279a1211ac398d4de79
Log: Fix bug #79601 (Wrong ciphertext/tag in AES-CCM encryption for a 12 bytes IV)
 [2020-09-29 06:13 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2020-09-29 06:13 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=2f5cb702ffc31c8d88ff95a226723aeac0dec8be
Log: Fix bug #79601 (Wrong ciphertext/tag in AES-CCM encryption for a 12 bytes IV)
 [2020-09-29 06:52 UTC] remi@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=0216630ea2815a5789a24279a1211ac398d4de79
Log: Fix bug #79601 (Wrong ciphertext/tag in AES-CCM encryption for a 12 bytes IV)
 [2020-09-29 06:52 UTC] remi@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=2f5cb702ffc31c8d88ff95a226723aeac0dec8be
Log: Fix bug #79601 (Wrong ciphertext/tag in AES-CCM encryption for a 12 bytes IV)
 [2020-09-29 07:50 UTC] cmb@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=de777c8fd22807c2121775e0f936336dc34a01a4
Log: Fix bug #79601 (Wrong ciphertext/tag in AES-CCM encryption for a 12 bytes IV)
 [2020-09-29 10:21 UTC] bukka@php.net
@stas why did you change the author of the commit? I think you applied it incorrectly. You should be really using `git am`. Please can you re-apply it?
 [2020-09-29 10:24 UTC] bukka@php.net
I think you will need to revert and then apply it again. I spent a bit of time on this so I would really appriciate credit for this work. Thanks.
 [2020-09-29 10:42 UTC] bukka@php.net
Or maybe better could someone give me access so I can push to security branches so I don't bother Stas and others with things like this. I think it's not really productive and don't really see reason why we are doing this kind of blocking. At least the commiters that are aware of the process should be given access to commit to those branches as well because it seems like a waste of our time to provide patches and let others handle conflicts and other bits.
 [2020-09-29 17:01 UTC] stas@php.net
Jakub, is only the author wrong or something else too?
 [2020-09-29 20:29 UTC] bukka@php.net
As discussed in the commit on GitHub - it's fine to leave it as it is.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Mar 29 14:01:28 2024 UTC