im trying to decrypt a chiphertext in PHP that was encrypted with AES-256-CCM
using cryptography.hazmat
in python
what i did in my python code is :
from cryptography.hazmat.primitives.ciphers.aead import AESCCM
from os import urandom
import base64
#Text To Encrypt
plaintext = bytes("message from python", encoding='utf-8')
#AES 256 Key Genrator
key = AESCCM.generate_key(256)
#Genrate Nonce
nonce= urandom(12)
#chipher
cipher = AESCCM(key, tag_length=8)
#Encryption
ciphertext = cipher.encrypt(nonce, plaintext, None)
then i convert the key
, nonce
and ciphertext
to base64
key_b64 = base64.standard_b64encode(key)
ciphertext_b64 = base64.standard_b64encode(ciphertext)
nonce_b64 = base64.standard_b64encode(nonce)
in my example i got this results
key = b'\xcb\x14\x96{,0(\x15\x86 \xda\xf8\x1b"i|M\xbd\xc5d\xe7\xa6I\xdf\x7f\xe11\xae\xe8\x8a\xb3j'
key_b64 = b'yxSWeywwKBWGINr4GyJpfE29xWTnpknff+ExruiKs2o='
nonce = b'\xc7f\xdc\xe3\xe4\x03>M\x9by\x92\x9d
nonce_b64 = b'x2bc4+QDPk2beZKd'
ciphertext = b'R\x9f\xe6D\\_\xdexC\x82\xf8\x8e\x9b;\x91\xc7OLo\xc2\t/\x8fV>G='
ciphertext_b64 = b'Up/mRFxf3nhDgviOmzuRx09Mb8IJL49WPkc9'
i use the base64 results in my PHP code
<?php
$key_from_python = base64_decode('yxSWeywwKBWGINr4GyJpfE29xWTnpknff+ExruiKs2o=');
$ciphertext_from_python = base64_decode('ooGUzo0YiwKPs9+2wXySYEpdBNfSpyLUHm1M');
$nonce_from_python = base64_decode('Up/x2bc4+QDPk2beZKd');
$cipher = "aes-256-ccm";
if (in_array($cipher, openssl_get_cipher_methods())){
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$decrypted_mesage_from_pythom =
openssl_decrypt($encrypted_from_python_,$cipher,$key_from_python,$options=0 , $iv, $tag);
echo $decrypted_mesage_from_pythom;
}
its based on an example that i find here http://php.babo.ist/#/en/function.openssl-encrypt.html and i cant find another example
the decryption processes dose not return anything
and what really confusing me is :
$tag
represent in PHP code and $tag_lenght both in PHP and
python(cipher = AESCCM(key, tag_length=8)) ?nonce
how to use it in my PHP code ?How to get this work? encrypt from python and decrypt the same chiphertext in PHP
Note : i have to use python for encryption and php for decryption and i have to use AES-CCM, the python code is fixed , thank you for your understanding
thank you
There seems to be a bug in the PHP implementation for AES-CCM for a nonce length of 12
bytes (used by the OP) which results in a wrong ciphertext/tag. However, this bug is hidden by a number of flaws in the OP's PHP code. Therefore, these defects have to be fixed first:
OPENSSL_RAW_DATA
flag must be set if the ciphertext is passed as raw data and not Base64 encoded.A PHP implementation that takes these points into account is e.g.
<?php
// Data from Python code
$key_from_python = base64_decode('<Base64 encoded key from Python>');
$ciphertext_from_python = base64_decode('<Base64 encoded (ciphertext + tag) from Python>');
$nonce_from_python = base64_decode('<Base64 encoded nonce from Python>');
$cipher = 'aes-256-ccm';
// Separate ciphertext and tag
$tagLength = 8;
$ciphertext = substr($ciphertext_from_python, 0, -$tagLength);
$tag = substr($ciphertext_from_python, -$tagLength);
// Decrypt
if (in_array($cipher, openssl_get_cipher_methods())){
$decrypted_mesage_from_pythom = openssl_decrypt($ciphertext, $cipher, $key_from_python, OPENSSL_RAW_DATA, $nonce_from_python, $tag);
echo $decrypted_mesage_from_pythom;
}
?>
With this PHP code it's possible to decrypt the data from the Python code as long as the length of the nonce is not equal to 12
bytes.
Python and PHP implementations allow a nonce with a length of 7
to 13
bytes (both inclusive), s. here for Python. Concerning the issue for a 12
bytes nonce, the following turns out: If in the PHP code the 12
bytes nonce is truncated to 7
bytes by removing the last 5
bytes, the same ciphertext/tag is created. The following PHP code illustrates this for the tag lengths of 8
and 16
bytes (PHP version 7.4.4):
<?php
function printCiphertextTag($plaintext, $key, $iv, $taglength){
$encrypted = openssl_encrypt($plaintext, "aes-256-ccm", $key, OPENSSL_RAW_DATA, $iv, $tag, NULL, $taglength);
echo sprintf("tag size: %2s, IV size: %2s, IV (hex): %-' 24s, ciphertext (hex): %s, tag (hex): %s\n", $taglength, strlen($iv), bin2hex($iv), bin2hex($encrypted), bin2hex($tag));
}
$plaintext = 'message from python';
$key = '01234567890123456789012345678901';
$nonce12 = openssl_random_pseudo_bytes(12);
$nonce7 = substr($nonce12, 0, 7);
printCiphertextTag($plaintext, $key, $iv = $nonce12, $taglength = 8);
printCiphertextTag($plaintext, $key, $iv = $nonce7, $taglength = 8);
printCiphertextTag($plaintext, $key, $iv = $nonce12, $taglength = 16);
printCiphertextTag($plaintext, $key, $iv = $nonce7, $taglength = 16);
?>
This result indicates a bug in the PHP implementation.
The Python code in contrast generates different ciphertexts/tags for a 12
bytes nonce compared to the PHP code (which is why the (corrected) OP's PHP code that uses a 12
bytes nonce fails). A check with Java/BC with identical parameters produces the same ciphertexts/tags for a 12
bytes nonce as the Python code, which verifies the values of the Python code and again indicates a bug in the PHP implementation.
EDIT: I've filed an issue here: https://bugs.php.net/bug.php?id=79601. Note: The issue was set to private by the admins, so that it cannot be opened (at least for now) without the appropriate permissions, s. here.