2
votes

I am trying to develop a php program that encrypts some data which can later be decrypted. However, I am having trouble decrypting the data with PHP using AES-256-CBC algorithm by using the openssl_decrypt() method in PHP. The encryption works but when I try to decrypt it gives me an error:-
hash_equals(): Expected known_string to be a string, bool given on line 44.

My code below:

<?php

class Encryption{
    
    protected $data, $encryption_type, $key, $iv;

    public function __construct(){
        $mydata = "Is this safe";
        $encryption = $this->secured_encryption($mydata);

        // Decrypting data
        $data_to_decrypt = $encryption;
        $this->decrypt_data($data_to_decrypt);
    }

    public function secured_encryption($data){
        $this->data = $data;
        $this->encryption_type = "AES-256-CBC"; // cipher algorithm
        $this->key = ['6d2d823df2e08c0d3cdf70f148998d49', 'fdb3a18c7b0a322fdb3a18c7b0a320d3'];
        $this->iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->encryption_type));
        $encrypted_data = openssl_encrypt($this->data, $this->encryption_type, $this->key[0], OPENSSL_RAW_DATA, $this->iv);
        $final_encryption = hash_hmac('SHA3-512', $encrypted_data, $this->key[1], true);
        $output = base64_encode($this->iv.$final_encryption.$encrypted_data);

        if($output):
            print("Encrypted data: {$output}<br/>");
        else:
            print("Error in encrypting data");
        endif;
    }

    public function decrypt_data($data){
        $this->data = base64_decode($data);
        $this->encryption_type = "AES-256-CBC"; // cipher algorithm
        $this->key = ['6d2d823df2e08c0d3cdf70f148998d49', 'fdb3a18c7b0a322fdb3a18c7b0a320d3'];
        $ivlen = openssl_cipher_iv_length($this->encryption_type);
        $this->iv = substr($this->data, 0, $ivlen);
        $hmac = substr($this->data, $ivlen, $sha2len = 32);
        $decrypt_data = substr($this->data, $ivlen, $sha2len);
        $final_decryption = openssl_decrypt($decrypt_data, $this->encryption_type, $this->key[0], OPENSSL_RAW_DATA, $this->iv);
        $calcmac = hash_hmac('SHA3-512', $decrypt_data, $this->key[1], true);

        if(hash_equals($hmac, $calcmac)):
            print($final_decryption);
        else:
            print("Error in decrypting data");
        endif;
    }
}

$encryption = Encryption();

?> 

Anybody to help me

1
Welcome to Stackoverflow. Your secured_encryption gives an output of 3 elements ($this->iv.$final_encryption.$encrypted_data) but on decrypt_data you don't split the data and feed the complete (Base64-decoded) data to openssl_decrypt and hash_hmac so you HAVE to get an error when decrypting. - Michael Fehr
Don't invent your own crypto scheme. There are well-established modes for authenticated encryption, such as GCM. - Peter
@Peter - I would not say that this is their encryption scheme, see encrypt-then-MAC. But I agree, that nowadays modes like GCM are more convenient (and modern), as long as they are supported in the respective environment. - Topaco

1 Answers

3
votes

There are a few minor issues in the code:

  • There is a return statement missing in the secured_encryption method. This is the trigger for the error message (hash_equals(): Expected known_string to be a string, bool given), since $hmac does not contain a string but is false. So at the end of secured_encryption you have to add:

    return $output;
    

    With this, encryption works, but decryption does not work yet.

  • When separating the HMac in decrypt_data the wrong length is used. SHA3-512 results in a 512 bit, i.e. 64 byte hash, i.e.:

    $hmac = substr($this->data, $ivlen, $sha2len = 32);
    

    must be replaced by:

    $hmac = substr($this->data, $ivlen, $sha2len = 64);   
    
  • When separating the data, start at the position $ivlen + $sha2len and consider everything to the end, i.e.:

    $decrypt_data = substr($this->data, $ivlen, $sha2len); 
    

    must be replaced by:

    $decrypt_data = substr($this->data, $ivlen + $sha2len);
    

With these changes everything works and an output looks e.g. like this:

Encrypted data: uTjcPmLK8jTktJ0oy5AqB40d5YFuAbgXMHfNEM6+JOH+DtyYAhckcv3mkLhdOvUqPlriKqYjHO6cpJI3ZdoTSS4lqDr0eH0MMiUpJMSXcan81irvobcdIV+rvaMPPRj7
Is this safe 

By the way, besides the ciphertext also the IV should be authenticated, see here. There are also authenticatable operation modes that provide confidentiality as well as authentication, e.g. GCM (e.g. aes-256-gcm), s. Peter's comment. You can check with openssl_get_cipher_methods() which algorithms/modes are available in your environment.