Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ to php translation, decryption function

So, I'm trying to translate a piece of C++ code to php. The C++ is from a external source, and my knowledge of both C++ and decryption is lacking, to say the least.

The source C++ is:

void parser_t::decrypt(buffer_t &replay_data, const unsigned char *key_data) {  
    /*\ 
    |*| Performs an in place decryption of the replay using the given key.
    |*| The decryption is a (broken) variant of CBC decryption and is performed as follows:
    |*| -# Set the variable previous_block (with size of 16 bytes) to 0
    |*| -# Decrypt a block with the given key
    |*| -# XOR the block with the previous (decrypted) block
    |*| -# Go back to step 2, until there are no more blocks.
    \*/
    BF_KEY key = {{0}};
    BF_set_key(&key, 16, key_data);
    
    const int block_size = 8;
    
    size_t padding_size = (block_size - (replay_data.size() % block_size));
    if (padding_size != 0) {
        size_t required_size = replay_data.size() + padding_size;
        replay_data.resize(required_size, 0);
    }
    
    unsigned char previous[block_size] = {0};
    for (auto it = replay_data.begin(); it != replay_data.end(); it += block_size) {
        unsigned char decrypted[block_size] = { 0 };
        BF_ecb_encrypt(reinterpret_cast<unsigned char*>(&(*it)), decrypted, &key, BF_DECRYPT);
        std::transform(previous, previous + block_size, decrypted, decrypted, std::bit_xor<unsigned char>());
        std::copy_n(decrypted, block_size, previous);
        std::copy_n(decrypted, block_size, reinterpret_cast<unsigned char*>(&(*it)));
    }
    
    if (padding_size != 0) {
        size_t original_size = replay_data.size() - padding_size;
        replay_data.resize(original_size, 0);
    }
}

What I have got so far:

function decrypt($data){ // $data is a encrypted string
    $key = array(0xDE, <.....>, 0xEF); // (16 entries in the array)
    
    //BF_KEY key = {{0}};             // ?
    //BF_set_key(&key, 16, key_data); // ?
    
    $block_size = 8;

    $padding_size = ($block_size - (strlen($data) % $block_size));
    if ($padding_size != 0) {
        $required_size = strlen($data) + $padding_size;
        //replay_data.resize(required_size, 0);
        // Seems unnecessary in php? string lengths are pretty dynamic.
    }

    $keyString = '';
    for($i = 0; $i < count($key); $i++){
        $keyString .= chr($key[$i]);
    }
    $output = '';

    for ($i = 0; $i < stlen($data); $i += $block_size) {
        $char = array(0, 0, 0, 0, 0, 0, 0, 0);                     // ?
        $decrypted_piece = mcrypt_decrypt(MCRYPT_BLOWFISH, $keyString, $data, "cbc"); // ??
        // And this is where I completely get lost.
        $output = transform($in, $start, $end, $in2);
    }
}

function transform($in, $start, $end, $in2){
    $out = ''; // Yea, that won't work...
    for($x = $start; $x < $end; $x++){
        $out[$x] = $in[$x] ^ $in2[$x];
    }
    return $output
}

I realize I'm basically asking you guys to do something for me, but I'm really stuck at the contents of that for (auto it....

Hints / explanations that'd really help me along would be:

  • What does BF_ecb_encrypt do in this case? (In pseudocode or even php?) (slaps self on fingers. "don't ask for finished products")
  • Am I on the right track with the translation of the transform?
  • What is {{0}}, BF_set_key(&key, 16, key_data);?
  • What is reinterpret_cast<unsigned char*>(&(*it))?

I did get a look at these documentation pages, but to no avail:

  • php mcrypt-decrypt
  • BF_ecb_encrypt
  • C++ transform

The full source is available on github.
This specific code comes from src/parser.cpp

like image 530
Cerbrus Avatar asked Apr 25 '26 07:04

Cerbrus


1 Answers

The "broken variant of CBC decryption" that the original code is doing can, equivalently, be described as ECB decryption followed by (cumulatively) XORing each plaintext block with the previous one.

For the XOR, you don't need anything fancy: the PHP bitwise XOR operator can operate on strings.

Thus, a simple PHP version of your C++ code could look something like this (warning: untested code):

function decrypt( $ciphertext, $key ) {
    $plaintext = mcrypt_decrypt( MCRYPT_BLOWFISH, $key, $ciphertext, "ecb" );

    $block_size = 8;  // Blowfish block size = 64 bits = 8 bytes
    $blocks = str_split( $plaintext, $block_size );

    $previous = str_repeat( "\0", $block_size );
    foreach ( $blocks as &$block ) {
        $block ^= $previous;
        $previous = $block;
    }

    return implode( $blocks );
}

Note that I haven't implemented any padding for truncated last blocks; there's something very screwy about the padding handling in the original code, and I don't see how it could possibly correctly decrypt messages whose length is not divisible by 8 bytes. (Does it, actually?) Rather than try to guess what the heck is going on and how to translate it to PHP, I just chose to ignore all that stuff and assume that the message length is divisible by the block size.

like image 153
Ilmari Karonen Avatar answered Apr 27 '26 21:04

Ilmari Karonen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!