Translate embedded C function to PHP function

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.

php
c
function
unsigned-integer
asked on Stack Overflow Nov 30, 2011 by johsey • edited Jun 25, 2018 by Lundin

1 Answer

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.

answered on Stack Overflow Nov 30, 2011 by Álvaro González

User contributions licensed under CC BY-SA 3.0