1

I'm trying to "translate" the following function written in C in PHP. The problem is, the results returned by the function written in PHP are different from those returned by the one in C.

I think the problem comes from integer overflows in PHP which is not able to treat variables as unsigned 32-bit integer.

I tried to AND-mask all operation with 0xFFFFFFFF without success. Have you got other solutions that could work?

The C function:

void
decipher(const uint32_t num_rounds, uint32_t v[2], const uint32_t key[4])
{
    uint32_t idx;
    uint32_t v0 = v[0];
    uint32_t v1 = v[1];
    uint32_t delta = 0x9E3779B9;
    uint32_t sum = delta * num_rounds;

    for (idx = 0; idx < num_rounds; ++idx)
    {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
        sum -= delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }

    v[0] = v0;
    v[1] = v1;
}

The PHP function:

function decipher($num_rounds, &$v, $key)
{
    $v0 = $v[0];
    $v1 = $v[1];
    $delta = 0x9E3779B9;
    $sum = ($delta * $num_rounds) & 0xFFFFFFFF;

    for ($idx = 0; $idx < $num_rounds; ++$idx)
    {
        $v1 -= (((($v0 << 4) ^ ($v0 >> 5)) + $v0) ^ ($sum + $key[($sum >> 11) & 3])) & 0xFFFFFFFF;
        $v1 &= 0xFFFFFFFF;
        $sum -= $delta;
        $sum &= 0xFFFFFFFF;
        $v0 -= ((($v1 << 4) ^ ($v1 >> 5) + $v1) ^ ($sum + $key[$sum & 3])) & 0xFFFFFFFF;
        $v0 &= 0xFFFFFFFF;
    }

    $v[0] = $v0;
    $v[1] = $v1;
}

Thank you.

SOLUTION:

I found a solution: the following code uses functions which allows to do shift operations and additions on unsigned 32-bit integers.

function decipher($num_rounds, &$v, $key)
{
    $v0 = $v[0];
    $v1 = $v[1];
    $delta = 0x9E3779B9;
    $sum = ($delta * $num_rounds) & 0xFFFFFFFF;

    for ($idx = 0; $idx < $num_rounds; ++$idx)
    {   
        $v1 = _add($v1, -(_add($v0 << 4 ^ _rshift($v0, 5), $v0) ^ _add($sum, $key[_rshift($sum, 11) & 3])));
        $sum = _add($sum, -$delta);
        $v0 = _add($v0, -(_add($v1 << 4 ^ _rshift($v1, 5), $v1) ^ _add($sum, $key[$sum & 3])));
    }

    $v[0] = $v0;
    $v[1] = $v1;
}

function _rshift($integer, $n)
{
    // convert to 32 bits
    if (0xffffffff < $integer || -0xffffffff > $integer)
    {
        $integer = fmod($integer, 0xffffffff + 1);
    }

    // convert to unsigned integer
    if (0x7fffffff < $integer) {
        $integer -= 0xffffffff + 1.0;
    }
    else if (-0x80000000 > $integer)
    {
        $integer += 0xffffffff + 1.0;
    }

    // do right shift
    if (0 > $integer)
    {
        // remove sign bit before shift
        $integer &= 0x7fffffff;
        // right shift
        $integer >>= $n;
        // set shifted sign bit
        $integer |= 1 << (31 - $n);
    }
    else
    {
        // use normal right shift
        $integer >>= $n;
    }

    return $integer;
}


function _add($i1, $i2)
{
    $result = 0.0;

    foreach (func_get_args() as $value)
    {
        // remove sign if necessary
        if (0.0 > $value)
        {
            $value -= 1.0 + 0xffffffff;
        }

        $result += $value;
    }

    // convert to 32 bits
    if (0xffffffff < $result || -0xffffffff > $result)
    {
        $result = fmod($result, 0xffffffff + 1);
    }

    // convert to signed integer
    if (0x7fffffff < $result)
    {
        $result -= 0xffffffff + 1.0;
    }
    else if (-0x80000000 > $result)
    {
        $result += 0xffffffff + 1.0;
    }

    return $result;
}

Thank you for your answers.

3
  • 4
    post your PHP code so far also Commented Nov 30, 2011 at 10:54
  • 1
    If it's a known cypher, it's possible that you can find it among PHP's Cryptography extensions Commented Nov 30, 2011 at 11:03
  • Also you can run your C function from PHP. Commented Jun 25, 2018 at 6:38

1 Answer 1

2

If you need to do math with very large integers in PHP, your option is basically to use strings and the arbitrary precision library of your choice:

In this case, GMP is your only choice since the other library does not support bitwise operations.

Sign up to request clarification or add additional context in comments.

2 Comments

All integers must be limited to 32-bit. What do you mean by "very large integers"?
"Very large" means "larger that it can fit". For instance, 2147483648. For details about what fits, see the PHP_INT_MAX and PHP_INT_SIZE constants.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.