allan.simon
allan.simon

Reputation: 4306

Decrypt file ciphered in bash with AES-256-CBC and PBKDF2 using php

I have a string encrypted using this command :

echo 'hello world' |  openssl enc -pass pass:MYPASSWORD -aes-256-cbc -md sha3-256 -pbkdf2 -iter 10000 -out crypt

that is stored in a crypt file

I would like to know how to decrypt this file in php

I understand I need to use a combination of these two functions

However I fail to understand

how do i provide these 3 parameters ?

Upvotes: 0

Views: 1121

Answers (1)

allan.simon
allan.simon

Reputation: 4306

Ok as the information were scatered all accross the internet I gather all it here, hoping it will save somebody times in the future

if you just want the code, jump to the end

First: The salt is stored in the output file by openssl

it took me quite some time to understand this fact. As this question state, the format is not based on any standard.

so you need to extract these values from the file, as of openssl 1.1.1 the format is the following

(you can find the exact implementation here: https://github.com/openssl/openssl/blob/f7d2427ac3404ce1ed555bf61885eeb0432b5789/apps/enc.c )

  1. 8 bytes containing Salted__
  2. 8 bytes containing the salt itself

Second: The IV and KEY are derivated from PASSWORD + SALT using openssl_pbkdf2

The IV is not a random one, uncorrelated from anything. The IV is derivated from the password

openssl_pbkdf2 returns 1 string, but this is BOTH the IV and the KEY concatenated (it's not explained in the php documentation) but you can see it here in the c code

The code

Once you get this it's actually straitghforward

  1. read the file
  2. extract the salt
  3. give the password + salt to openssl_pbkdf2 which gives you key + iv
  4. split the key and iv in dedicated variable
  5. feed key and iv to openssl_decrypt along with the ciphered text (stripped from the salt header)
  6. profit

Here's the code

<?php
$content = file_get_contents('crypt');
$salt = mb_substr($content, 8, 8, '8bit');
$derivatedKey = openssl_pbkdf2(
    'MYPASSOWRD',
    $salt,
    // key_length is 48 bytes because
    // the key itself is 32 bytes (256 bits, because aes 256)
    // and the IV is 16 bytes (returned by openssl_cipher_iv_length)
    // so 32+16 -> 48
    key_length: 48,
    // 10000 is a of 2021 the amount recommended by the NIST
    // see https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63b.pdf section 5.1.1.2
    // quote:
    // For PBKDF2, the cost factor is an iteration count: the more times the PBKDF2 function is
    // iterated, the longer it takes to compute the password hash. Therefore, the iteration count
    // SHOULD be as large as verification server performance will allow, typically at least 10,000
    // iterations.
    iterations: 10000,
    // for the same reason we use sha3-256
    // quote:
    // A memory-hard function SHOULD be used because it increases the cost of an attack.
    // The key derivation function SHALL use an approved one-way function such as
    // Keyed Hash Message Authentication Code (HMAC) [FIPS 198-1],
    // any approved hash function in SP 800-107, Secure Hash Algorithm 3 (SHA-3)
    digest_algo: 'sha3-256'
);
// the key itself is 32 bytes (i.e 256 bits, because aes *256*)
$key = mb_substr($derivatedKey, 0, 32, '8bit');
$iv = mb_substr($derivatedKey, 32, openssl_cipher_iv_length('aes-256-cbc'), '8bit');
// 16 is the 8 bytes of `Salted__`  and 8 bytes of salt itself
$cypherText = mb_substr($content, 16, encoding: '8bit');
echo openssl_decrypt($cypherText, 'aes-256-cbc', $key, iv: $iv);

Upvotes: 2

Related Questions