realdannys realdannys - 6 months ago 15
PHP Question

App works in php 5.3 but not 5.4+

We're running a server app to activate our software, the web app and database itself works fine in php 5.4+ but the activation within the software causes an internal server error if we're running anything higher than php 5.3 (test with 5.4, 5.5 and 5.6) - i'd like to install 5.6 to be future proof and up to date on the server.

I guess the problem lies within the activation php file, can anyone spot any code here which wouldn' tbe compatible with php54+

<?PHP

ini_set('display_errors',1);
error_reporting(E_ALL);

require 'includes/master.inc.php';

$post = trim(file_get_contents('php://input'));
//$post = base64_decode($post);
//$dict = json_decode($post);

$a = new Activation();
$a->app_id = 373; // $dict->app_id;
$a->name = $_REQUEST["nameOrEmail"]; //$dict->email;
$a->serial_number = $_REQUEST["activationCode"]; //$dict->serial;
$a->guid = $_REQUEST["guid"];
$a->dt = dater();
$a->ip = $_REQUEST["ip_overwrite"];
if($a->ip == "") $a->ip = $_SERVER['REMOTE_ADDR'];

if($a->serial_number == null || $a->guid == null) {
header("HTTP/1.0 400 Activation Code or more arguments required");
exit();
}
$a->insert();

$app = new Application($a->app_id);
if(!$app->ok()) {
header("HTTP/1.0 412 Invalid Application");
exit();
}
$o = new Order();
$o->select($a->serial_number, 'serial_number');
if(!$o->ok()) {
header("HTTP/1.0 406 Invalid activation code");
exit();
}

// Because we die before the activation is updated with the found order id,
// this has the added benefit of highlighting the activation as "fraudulent"
// in the activations list. It's not fraudulent obviously, but it does let
// us quickly see if deactivated licenses are still being used.
if($o->deactivated == 1) {
header("HTTP/1.0 409 Activation Code is disabled.");
exit();
}

if($o->license_name != null && $o->license_name != $a->guid) {
header("HTTP/1.0 410 Activation Code has been already claimed.");
exit();
}
$a->order_id = $o->id;
$a->update();

//$o->downloadLicense();


//header("Cache-Control: public");
//header("Cache-Control: no-store, no-cache, must-revalidate");
//header("Cache-Control: post-check=0, pre-check=0", false);
//header("Pragma: no-cache");
//header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
//header("Content-Type: application/x-download"); // Stupid fix for Safari not honoring content-disposition
//header("Content-Length: " . strlen($this->order->license));
//header("Content-Disposition: attachment; filename={$this->application->license_filename}");
//header("Content-Transfer-Encoding: binary");
//echo $this->order->license;
//exit;

/**
* hex2bin
* Converts a hexadecimal string to binary
* @param string Hex string
* @return string Binary string
*/
function hex2bin($hex) {
if (strlen($hex) % 2)
$hex = "0".$hex;
$bin = '';
for ($i = 0; $i < strlen($hex); $i += 2) {
$bin .= chr(hexdec(substr($hex, $i, 2)));
}

return $bin;
}

/**
* dec2hex
* Converts a decimal string to a hexadecimal string
* @param string Decimal string
* @return string Hex string
*/
function dec2hex($number)
{
$hexvalues = array('0','1','2','3','4','5','6','7',
'8','9','A','B','C','D','E','F');
$hexval = '';
while($number != '0')
{
$hexval = $hexvalues[bcmod($number,'16')].$hexval;
$number = bcdiv($number,'16',0);
}
return $hexval;
}

/**
* hex2dec
* Converts a hexadecimal string to decimal string
* @param string Hex string
* @return string Decimal string
*/
function hex2dec($number)
{
$decvalues = array('0' => '0', '1' => '1', '2' => '2',
'3' => '3', '4' => '4', '5' => '5',
'6' => '6', '7' => '7', '8' => '8',
'9' => '9', 'A' => '10', 'B' => '11',
'C' => '12', 'D' => '13', 'E' => '14',
'F' => '15', 'a' => '10', 'b' => '11',
'c' => '12', 'd' => '13', 'e' => '14',
'f' => '15');
$decval = '0';

$number = array_pop(explode("0x", $number, 2));

$number = strrev($number);
for($i = 0; $i < strlen($number); $i++)
{
$decval = bcadd(bcmul(bcpow('16',$i,0),$decvalues[$number{$i}]), $decval);
}
return $decval;
}

/**
* powmod
* Raise a number to a power mod n
* This could probably be made faster with some Montgomery trickery, but it's just fallback for now
* @param string Decimal string to be raised
* @param string Decimal string of the power to raise to
* @param string Decimal string the modulus
* @return string Decimal string
*/
function powmod($num, $pow, $mod)
{
if (function_exists('bcpowmod')) {
// bcpowmod is only available under PHP5
return bcpowmod($num, $pow, $mod);
}

// emulate bcpowmod
$result = '1';
do {
if (!bccomp(bcmod($pow, '2'), '1')) {
$result = bcmod(bcmul($result, $num), $mod);
}
$num = bcmod(bcpow($num, '2'), $mod);
$pow = bcdiv($pow, '2');
} while (bccomp($pow, '0'));
return $result;
}

/**
* getSignature
* Get the base64 signature of a dictionary
* @param array Associative array (i.e. dictionary) of key-value pairs
* @param string Hexadecimal string of public key
* @param string Hexadecimal string the private key
* @return string Base64 encoded signature
*/
function getSignature($dict, $key, $privKey)
{
// Sort keys alphabetically
uksort($dict, "strcasecmp");

// Concatenate all values
$total = '';
foreach ($dict as $value)
$total .= $value;

// Get the hash
$hash = sha1(utf8_encode($total));

// OpenSSL-compatible PKCS1 Padding
// 128 bytes - 20 bytes hash - 3 bytes extra padding = 105 bytes '0xff'
$paddedHash = '0001';
for ($i = 0; $i < 105; $i++)
{
$paddedHash .= 'ff';
}
$paddedHash .= '00'.$hash;

$decryptedSig = hex2dec($paddedHash);

// Encrypt into a signature
$sig = powmod($decryptedSig, hex2dec($privKey), hex2dec($key));
$sig = base64_encode(hex2bin(dec2hex($sig)));
return $sig;
}

$activationResponse = getSignature(array("activationCode"=>$a->serial_number, "guid"=>$a- >guid), "BE6749000DA18D2D21F1D09455DB1E6C354E0B", "7EEF860009165E1E164517CBDDF6D66A6F5D176C03883EC3E7BB643D84B0E17A41E68A57AE09670A943042F50AE9E41633E E64A88639DA4691C3A11778839AAEB");
echo $activationResponse;

$o->license_name = $a->guid;
$o->license = $activationResponse;
$o->update();

exit();


Here is the hex error from log

2014/12/02 13:05:22 [error] 22848#0: *280 FastCGI sent in stderr: "PHP message: PHP Fatal error: Cannot redeclare hex2bin() in /var/www/html/aurora/aurora-inapp.php on line 85" while reading response header from upstream, client: 141.101.00.22, server: test.com, request: "POST /aurora/aurora-inapp.php HTTP/1.1", upstream: "fastcgi://unix:/var/run/php-fpm.sock:", host: "test.com"

Answer

Your code is failing as since PHP 5.4, PHP has had its own hex2bin function. To resolve this you need to remove the hex2bin function that you are using.

You might want to do a simple version check in an if statement before the function is declared using phpversion():

if (!function_exists('hex2bin')) 
{
    function hex2bin($hex) {
    if (strlen($hex) % 2)
        $hex = "0".$hex;
    $bin = '';
    for ($i = 0; $i < strlen($hex); $i += 2) { 
        $bin .= chr(hexdec(substr($hex, $i, 2))); 
    }

       return $bin; 
    } 
}

From Github:

Since PHP 5.4 PHP provides its own hex2bin function, which causes the hex2bin declaration in ykksm-util.php to fail, causing the error below:

PHP Fatal error: Cannot redeclare hex2bin()

Cheers to Ryan P for useful comment:

This code won't work - it calls phpinfo() with an invalid argument, and calling phpversion("tidy") will return the version of the tidy extension (2.0). A better way is to just check for existence of the function - i.e. if (!function_exists('hex2bin')) {