1

I have to decrypt aes-128-gcm encrypted data I get from an external party. Since openssl_decrypt never returned any data, I tried to encrypt the elsewhere decrypted data myself, to see if that works and in fact I receive the same encrypted data I try to decrypt. Therefore I know, all my parameters are correct. So I played around with my PHP code and come to the strange conclusion, that decrypting the data only works for me, after I encrypt the plaintext?!? Does anybody have any idea what's going on here?

thanks, Harry

<?php

$method='aes-128-gcm';

$key = hex2bin('0748BEF58E04D5917ED0B9B558628265');

//echo "iv_length: ". openssl_cipher_iv_length($method)."<br>";
$iv = hex2bin('534D5367700114E600102D29');
$tag = NULL;

$enc = hex2bin('09E89C959CD513057787832142E6796E1F6DE55CBA8E5CEC6E16AA635B3B102DDB22D85841923DDC2EE3052027945DFD00D025A0A5D0EB385E0033DD28037D80B47522B3DB310B01871474686B609D2DA15864785895DF2BE887');
$plain = hex2bin('0F00102D280C07E4081F01103B1000FF8880020C09060006190900FF090D323232313230323031323735360904103B1000090507E4081F0106004C48DF06000000CB06000089CF06000E61E7060000020A060000000009000900');

$decrypted = openssl_decrypt($enc, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
$encrypted = openssl_encrypt($plain, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
$decrypted2 = openssl_decrypt($enc, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);

echo "plain: ".bin2hex($plain)."<br>";
echo "enc: ".bin2hex($encrypted)."<br>";
echo "dec: ".bin2hex($decrypted)."<br>";
echo "dec2: ".bin2hex($decrypted2)."\n";

while ($msg = openssl_error_string())
    echo $msg . "<br>\n";
?>

OUTPUT:

plain: 0f00102d280c07e4081f01103b1000ff8880020c09060006190900ff090d323232313230323031323735360904103b1000090507e4081f0106004c48df06000000cb06000089cf06000e61e7060000020a060000000009000900

enc: 09e89c959cd513057787832142e6796e1f6de55cba8e5cec6e16aa635b3b102ddb22d85841923ddc2ee3052027945dfd00d025a0a5d0eb385e0033dd28037d80b47522b3db310b01871474686b609d2da15864785895df2be887

dec:

dec2: 0f00102d280c07e4081f01103b1000ff8880020c09060006190900ff090d323232313230323031323735360904103b1000090507e4081f0106004c48df06000000cb06000089cf06000e61e7060000020a060000000009000900

harricane
  • 13
  • 4

1 Answers1

3

Welcome to Stackoverflow. You are using the AES algorithm in mode GCM and that means that the ciphertext is secured against modification with an "authentication tag" or short "tag". This tag is generated when encrypting a plaintext with AES-GCM and needs to be available when decrypting the ciphertext.

In your code you provide an empty $tag-variable to the decrypt-function and the decryption fails. When generating a "new" plaintext with openssl_encrypt your $tag-variable gets filled with a tag. Now you are decrypting again and provide this tag to the openssl_decrypt-function and the decryption works like expected.

So you need to get the value of the $tag from the third party to successfully decrypt the ciphertext back to plaintext.

Using this small change in the sourcecode the program provides the $tag:

$decrypted = openssl_decrypt($enc, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
echo "tag: ".bin2hex($tag)."<br>" . PHP_EOL;
$encrypted = openssl_encrypt($plain, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
echo "tag: ".bin2hex($tag)."<br>" . PHP_EOL;
$decrypted2 = openssl_decrypt($enc, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);

result:

tag: <br>
tag: 9268f3568512fc9f15075096c1b47902<br>

Edit with solution

According to this answer of @Maarten Bodewes (https://stackoverflow.com/a/49244840/8166854) there is a chance of decypting "aes gcm" encrypted data without a authentication tag because

AES GCM = AES CTR + AuthTag

Changing your source code as below decrypts the password as expected in the first run, I added manually the hex-data '00000002' to the iv:

plain:  0f00102d280c07e4081f01103b1000ff8880020c09060006190900ff090d323232313230323031323735360904103b1000090507e4081f0106004c48df06000000cb06000089cf06000e61e7060000020a060000000009000900<br>
enc:    09e89c959cd513057787832142e6796e1f6de55cba8e5cec6e16aa635b3b102ddb22d85841923ddc2ee3052027945dfd00d025a0a5d0eb385e0033dd28037d80b47522b3db310b01871474686b609d2da15864785895df2be887<br>
decCtr: 0f00102d280c07e4081f01103b1000ff8880020c09060006190900ff090d323232313230323031323735360904103b1000090507e4081f0106004c48df06000000cb06000089cf06000e61e7060000020a060000000009000900<br>
decGcm: 0f00102d280c07e4081f01103b1000ff8880020c09060006190900ff090d323232313230323031323735360904103b1000090507e4081f0106004c48df06000000cb06000089cf06000e61e7060000020a060000000009000900

code:

<?php
$method='aes-128-gcm';
$key = hex2bin('0748BEF58E04D5917ED0B9B558628265');
$iv = hex2bin('534D5367700114E600102D29');
$tag = NULL;
$enc = hex2bin('09E89C959CD513057787832142E6796E1F6DE55CBA8E5CEC6E16AA635B3B102DDB22D85841923DDC2EE3052027945DFD00D025A0A5D0EB385E0033DD28037D80B47522B3DB310B01871474686B609D2DA15864785895DF2BE887');
$plain = hex2bin('0F00102D280C07E4081F01103B1000FF8880020C09060006190900FF090D323232313230323031323735360904103B1000090507E4081F0106004C48DF06000000CB06000089CF06000E61E7060000020A060000000009000900');

//$decrypted = openssl_decrypt($enc, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
$methodCtr = 'aes-128-ctr';
$ivCtr = hex2bin('534D5367700114E600102D2900000002');
$decryptedCtr = openssl_decrypt($enc, $methodCtr, $key, OPENSSL_RAW_DATA, $ivCtr);
$encrypted = openssl_encrypt($plain, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
echo "tag: ".bin2hex($tag)."<br>" . PHP_EOL;
$decryptedGcm = openssl_decrypt($enc, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);

echo "plain:  ".bin2hex($plain)."<br>" . PHP_EOL;
echo "enc:    ".bin2hex($encrypted)."<br>" . PHP_EOL;
echo "decCtr: ".bin2hex($decryptedCtr)."<br>" . PHP_EOL;
echo "decGcm: ".bin2hex($decryptedGcm)."\n" . PHP_EOL;
while ($msg = openssl_error_string())
    echo $msg . "<br>\n";
?>
Michael Fehr
  • 5,827
  • 2
  • 19
  • 40
  • That explains the behaviour of PHP. But the original encryption is done according to DLMS/COSEM Green Book https://www.dlms.com/files/Green_Book_Edition_9-Excerpt.pdf According to paragraph 9.2.7.2.4 AES-GCM supports authentication only, encryption only and encryption+authentication. Therefore in encrpytion only mode, no tag is needed and defined as a 12 byte filled with 00 -> therefore my empty $tag value, since $tag = hex2bin('000000000000000000000000') made no difference. – harricane Sep 02 '20 at 08:38
  • Great! I already suspected it maybe can be solved with CTR-mode, but wouldn't have found the info for the additional IV-Bytes. – harricane Sep 03 '20 at 10:42