2

I have an encrypted NSData variable that has been Base64 Encoded in Objective-C, and is being passed via POST to a PHP script. I am attempting to decrypt this string, but keep receiving a HMAC mismatch error. Here is my PHP code:

// back to binary
$bin_data = mb_convert_encoding($username_encrypted, "UTF-8", "BASE64");
// extract salt
$salt = substr($bin_data, 2, 8);
// extract HMAC salt
$hmac_salt = substr($bin_data, 10, 8);
// extract IV
$iv = substr($bin_data, 18, 16);
// extract data
$data = substr($bin_data, 34, strlen($bin_data) - 34 - 32);
// extract HMAC
$hmac = substr($bin_data, strlen($bin_data) - 32);

// make HMAC key
$hmac_key = pbkdf2('SHA1', $password, $hmac_salt, 10000, 32, true);
// make HMAC hash
$hmac_hash = hash_hmac('sha256', $data, $hmac_key, true);
// check if HMAC hash matches HMAC
if($hmac_hash != $hmac)
    exit("Error HMAC mismatch");

// make data key
$key = pbkdf2('SHA1', $password, $salt, 10000, 32, true);
// decrypt
$ret = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
echo trim(preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\xFF]/u', '', $ret));

function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        die('PBKDF2 ERROR: Invalid hash algorithm.');
    if($count <= 0 || $key_length <= 0)
        die('PBKDF2 ERROR: Invalid parameters.');

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}

And this is my iOS code:

NSData *plain_data = [plain_string dataUsingEncoding:NSUTF8StringEncoding];
NSError *error_encryption = nil;
NSData *encrypted_data = [RNOpenSSLEncryptor encryptData:plain_data
                                            withSettings:kRNCryptorAES256Settings
                                                password:key
                                                   error:&error_encryption];

NSString *test_variable = [encrypted_data base64Encoding];

Any idea what is wrong?

Wes Cossick
  • 2,923
  • 2
  • 20
  • 35

1 Answers1

1

The issue was fixed by following this post: Base64 issue in NSMutableURLRequest POST message?

Essentially, the +'s were being converted to spaces by PHP. Also, I used the PHP decrypt2() function found in his example, rather than mine. (I've included the code below for reference.)

function decrypt2($b64_data,$password)
{
            // back to binary
            //$bin_data = mb_convert_encoding($b64_data, "UTF-8", "BASE64");
            $bin_data = base64_decode($b64_data);
            // extract salt
            $salt = substr($bin_data, 2, 8);
            // extract HMAC salt
            $hmac_salt = substr($bin_data, 10, 8);
            // extract IV
            $iv = substr($bin_data, 18, 16);
            // extract data
            $data = substr($bin_data, 34, strlen($bin_data) - 34 - 32);
            $dataWithoutHMAC = chr(2).chr(1).$salt.$hmac_salt.$iv.$data;
            // extract HMAC
            $hmac = substr($bin_data, strlen($bin_data) - 32);
            // make HMAC key
            $hmac_key = pbkdf2('SHA1', $password, $hmac_salt, 10000, 32, true);
            // make HMAC hash
            $hmac_hash = hash_hmac('sha256', $dataWithoutHMAC , $hmac_key, true);
            // check if HMAC hash matches HMAC
            if($hmac_hash != $hmac) {
                echo "HMAC mismatch".$nl.$nl.$nl;
               // return false;
            }
            // make data key
            $key = pbkdf2('SHA1', $password, $salt, 10000, 32, true);
            // decrypt
            $ret = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);      
            return $ret;
}
$passkey = "123456";
Community
  • 1
  • 1
Wes Cossick
  • 2,923
  • 2
  • 20
  • 35
  • Are you sure you solved the HMAC mismatch problem? In `decrypt2()`, the `return false;` part is commented out. So if it didn't match, it would print "HMAC mismatch" along with some E_NOTICE errors because of the missing `$nl` variable, followed by decrypting the string anyway. – curtisdf May 08 '13 at 23:50
  • I've been digging into my own HMAC mismatch issue, and I discovered that RNCryptor data encrypted with file version 1 computes the $dataWithoutHMAC with *only* the ciphertext, not the headers or the IV. For version 1, the second line under `extract data` should say `$dataWithoutHMAC = $data;`. – curtisdf May 09 '13 at 00:45
  • FYI (since the above is my code), $nl is simply "
    ", since I was testing with this in my browser. return false was replaced with echo "HMAC mismatch" to make debugging easier. If the HMAC mismatch is being hit, there is an issue elsewhere, not in this line.
    – Tiago Feb 10 '14 at 10:18