Hashing Axapta 3 compatible passwords from PHP

August 21, 2009
Tags: ,

As an interlude from the CoughPHP, I present the following:

The downside of working with legacy systems is that sometimes you find it necessary to work with existing data in ways that are completely arbitrary. The worst of all of these is password hashing.

It is often the case that you have to maintain compatibility with legacy passwords, and while you can replace the old algorithms going forward with a sensible schema, you still have to be able to validate against passwords stored in the old way.

So I present to you a way to hash passwords compatible with Axapta 3:

#!/usr/bin/php
<?php
echo "PHP_INT_SIZE: " . PHP_INT_SIZE . "\n";
 
function str2int($string)
{
	$string = strtolower($string);
	$tmpCode = "0x015A4E35";
 
	for ($i = 0; $i < strlen($string); $i++)
	{
		$tmpChar = gmp_and(gmp_init(ord($string[$i])), "0xff");
		$tmpCode = gmp_and($tmpCode, "0xFFFFFFFF");
		$tmpCode = gmp_add(gmp_and(gmp_mul($tmpCode, "8192"), "0x0FFFFE000"), gmp_and(gmp_div_q($tmpCode, "524288"), "0x000001FFF"));
		$tmpCode = gmp_xor($tmpCode, (gmp_and(gmp_mul($tmpChar, "32"), "0x01FE0")));
		$tmpCode = gmp_add(gmp_xor($tmpCode, $tmpChar), "3");
 
	}
	return gmp_and($tmpCode, "0xFFFFFFFF");
}
 
function int2code($intCode)
{
 
	$tmpCode = gmp_and($intCode,  "0xFFFFFFFF");
	$i = 0;
	$code = '';
	while ($i < 7)
	{
		$tmpByte = gmp_intval(gmp_mod($tmpCode, "0x24"));
		if ($tmpByte < 10)
		{
			$code = chr(ord('0') + (int)$tmpByte) . $code;
		}
		else
		{
			$code = chr(ord('A') + (int)$tmpByte - 10) . $code;
 
		}
		$tmpCode = gmp_div_q(gmp_mul($tmpCode, "0x38E38E39"), 8);
		$tmpCode = gmp_div_q(gmp_and($tmpCode, "0xFFFFFFFF00000000"), "4294967296");
		$i++;
	}
 
	return $code;
}
 
 
function cryptUserPass($password, $userName)
{
	$tmpConst = "0xA59C29F1";
 
	$tmpCode = gmp_xor(str2Int($userName . $password), $tmpConst);
	$code = int2Code($tmpCode);
 
	$tmpConst = gmp_and(gmp_add("0x928379A1", $tmpCode), "0xFFFFFFFF");
 
	$tmpCode = gmp_xor(str2Int($password), $tmpConst);
	$code = $code . Int2Code($tmpCode);
 
	return $code;
}
 
print_r(cryptUserPass('hello', 'jw02'));
echo "\n" . '0OYMDGB17QNP49' . "\n";

Or if you have a 64-bit PHP binary:

#!/usr/bin/php
<?php
echo "PHP_INT_SIZE: " . PHP_INT_SIZE . "\n";
 
function str2int($string)
{
	$string = strtolower($string);
	$tmpCode = 0x015A4E35;
 
	for ($i = 0; $i < strlen($string); $i++)
	{
		$tmpChar = ord($string[$i]) & 0xff;
		$tmpCode = $tmpCode & 0xFFFFFFFF;
		$tmpCode = (($tmpCode << 13) & 0x0FFFFE000) + (($tmpCode >> 19) & 0x000001FFF);
		$tmpCode = $tmpCode ^ (($tmpChar << 5) & 0x01FE0);
		$tmpCode = ($tmpCode ^ $tmpChar) + 3;
 
	}
	return $tmpCode & 0xFFFFFFFF;
}
 
function int2code($intCode)
{
 
	$tmpCode = $intCode & 0xFFFFFFFF;
	$i = 0;
	$code = '';
	while ($i < 7)
	{
		$tmpByte = $tmpCode % 0x24;
		if ($tmpByte < 10)
		{
			$code = chr(ord('0') + (int)$tmpByte) . $code;
		}
		else
		{
			$code = chr(ord('A') + (int)$tmpByte - 10) . $code;
 
		}
		$tmpCode = ($tmpCode * 0x38E38E39) >> 3;
		$tmpCode = ($tmpCode & 0xFFFFFFFF00000000) >> 32;
		$i++;
	}
 
	return $code;
}
 
 
function cryptUserPass($password, $userName)
{
	$tmpConst = 0xA59C29F1;
 
	$tmpCode = str2Int($userName . $password) ^ $tmpConst;
	$code = int2Code($tmpCode);
 
	$tmpConst = (0x928379A1 + $tmpCode) & 0xFFFFFFFF;
 
	$tmpCode = str2Int($password) ^ $tmpConst;
	$code = $code . Int2Code($tmpCode);
 
	return $code;
}
 
print_r(cryptUserPass('hello', 'jw02'));
echo "\n" . '0OYMDGB17QNP49' . "\n";

You probably don’t need this, but if you do, it’s a lifesaver.

Originally found in delphi. In Russian.

One Response to “Hashing Axapta 3 compatible passwords from PHP”

  1. […] Hashing Axapta 3 compatible passwords from PHP | blog.rhp.org blog.rhp.org/2009/08/21/hashing-axapta-3-compatible-passwords-from-php – view page – cached Hashing Axapta 3 compatible passwords from PHP, As an interlude from the CoughPHP, I present the following: The downside of working with legacy systems is that sometimes you find it necessary to work with — From the page […]