te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>PHP PackUnpack implementation in Javascript Mismatch - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

PHP PackUnpack implementation in Javascript Mismatch - Stack Overflow

programmeradmin3浏览0评论

As per this question's related answer, I'm attempting to put together a pack/unpack solution resembling this PHP process, however in Nodejs (Javascript) using md5 and bufferpack

Here's the PHP approach (adapted from DaloRADIUS:

  $challenge = 'c731395aca5dcf45446c0ae83db5319e';
  $uamsecret = 'secret';
  $password = 'password';

  $hexchal = pack ("H32", $challenge);
  $newchal = pack ("H*", md5($hexchal . $uamsecret));
  $response = md5("\0" . $password . $newchal);
  $newpwd = pack("a32", $password);
  $pappassword = implode ("", unpack("H32", ($newpwd ^ $newchal)));

  echo "Response: ---> ", $response, "\n";
  echo "New Password: ---> ", $newpwd, "\n";
  echo "Pap Password: ---> ", $pappassword, "\n";

The above echos these:

Above in plaintext:

Response: ---> 2d4bd27184f5eb032641137f728c6043
New Password: ---> password
Pap Password: ---> 356a1fb08f909fc400dfe448fc483ce3

In Javascript, here's what I'm doing now:

  var challenge = 'c731395aca5dcf45446c0ae83db5319e';
  var uamsecret = 'secret';
  var password = 'password';

  var hexchal = pack.pack("H32", challenge);
  var newchal = pack.pack("H*", md5(hexchal + uamsecret));
  var response = md5("\0" + password + newchal);
  var newpwd = pack.pack("a32", password);
  var pappassword = pack.unpack("H32", (newpwd ^ newchal)).join("");

  console.log("Response: --> ", response);
  console.log("New Password: -->", newpwd);
  console.log("Pap Password: --->", pappassword);

Which gives the result:

In JSON:

In plaintext:

Response: -->  e8a54a55cbcd81dbc2bdfd9b197d62af
New Password: --> <Buffer >
Pap Password: ---> NaN

All the above snippets are available here: RadiusNES

My understanding in this whole process isn't the best, and will appreciate insights and where I'm going wrong.

Why is there a mismatch?

As per this question's related answer, I'm attempting to put together a pack/unpack solution resembling this PHP process, however in Nodejs (Javascript) using md5 and bufferpack

Here's the PHP approach (adapted from DaloRADIUS:

  $challenge = 'c731395aca5dcf45446c0ae83db5319e';
  $uamsecret = 'secret';
  $password = 'password';

  $hexchal = pack ("H32", $challenge);
  $newchal = pack ("H*", md5($hexchal . $uamsecret));
  $response = md5("\0" . $password . $newchal);
  $newpwd = pack("a32", $password);
  $pappassword = implode ("", unpack("H32", ($newpwd ^ $newchal)));

  echo "Response: ---> ", $response, "\n";
  echo "New Password: ---> ", $newpwd, "\n";
  echo "Pap Password: ---> ", $pappassword, "\n";

The above echos these:

Above in plaintext:

Response: ---> 2d4bd27184f5eb032641137f728c6043
New Password: ---> password
Pap Password: ---> 356a1fb08f909fc400dfe448fc483ce3

In Javascript, here's what I'm doing now:

  var challenge = 'c731395aca5dcf45446c0ae83db5319e';
  var uamsecret = 'secret';
  var password = 'password';

  var hexchal = pack.pack("H32", challenge);
  var newchal = pack.pack("H*", md5(hexchal + uamsecret));
  var response = md5("\0" + password + newchal);
  var newpwd = pack.pack("a32", password);
  var pappassword = pack.unpack("H32", (newpwd ^ newchal)).join("");

  console.log("Response: --> ", response);
  console.log("New Password: -->", newpwd);
  console.log("Pap Password: --->", pappassword);

Which gives the result:

In JSON:

In plaintext:

Response: -->  e8a54a55cbcd81dbc2bdfd9b197d62af
New Password: --> <Buffer >
Pap Password: ---> NaN

All the above snippets are available here: RadiusNES

My understanding in this whole process isn't the best, and will appreciate insights and where I'm going wrong.

Why is there a mismatch?

Share Improve this question edited May 23, 2017 at 12:02 CommunityBot 11 silver badge asked Jan 14, 2017 at 22:39 KhoPhiKhoPhi 9,52718 gold badges84 silver badges138 bronze badges 6
  • 1 Whenever possible please try and post plain-text as formatted code in the body of the question. These screenshots can be very hard if not impossible for some people to read: They're dependent on screen readers or translation tools. – tadman Commented Jan 14, 2017 at 22:45
  • @tadman Okay, will update soon. – KhoPhi Commented Jan 14, 2017 at 22:46
  • It's not a big deal, but trying to reproduce your results by typing in long hashes is not going to be fun for anyone. I think you nee to look more closely at the precursor ponents you're using to assemble your response, hexchal and newchal to make sure they're the same on both sides. – tadman Commented Jan 14, 2017 at 22:48
  • @tadman I've updated question, and will take a closer look at the hexchal and newchal – KhoPhi Commented Jan 14, 2017 at 22:53
  • 1 Check the MD5 values of those first. I think PHP might 0-byte (NULL) pad your string, JavaScript may truncate. – tadman Commented Jan 18, 2017 at 23:05
 |  Show 1 more ment

1 Answer 1

Reset to default 15 +600

The translation does not work because the PHP Pack function uses different format strings and returns strings, whilst the Javascript bufferpack module returns arrays. Also you cannot xor strings in Javascript.

Whilst there may be modules to do what you want, I have my own functions for parsing hex strings. Also I like modifying prototypes which not everyone agrees with, but these could be converted to regular functions.

String.prototype.pad = function( length ,padding ) {

    var padding = typeof padding === 'string' && padding.length > 0 ? padding[0] : '\x00'
        ,length = isNaN( length ) ? 0 : ~~length;

    return this.length < length ? this + Array( length - this.length + 1 ).join( padding ) : this;

}

String.prototype.packHex = function() {

    var source = this.length % 2 ? this + '0' : this
        ,result = '';

    for( var i = 0; i < source.length; i = i + 2 ) {
        result += String.fromCharCode( parseInt( source.substr( i , 2 ) ,16 ) );
    }

    return result;

}

var challenge = 'c731395aca5dcf45446c0ae83db5319e'
    ,uamsecret = 'secret'
    ,password = 'password';

var hexchal = challenge.packHex();
var newchal = md5( hexchal + uamsecret ).packHex();
var response = md5( '\0' + password + newchal );
var newpwd = password.pad( 32 );
var pappassword = '';
for( var i = 0; i < newchal.length; i++ ) {
    pappassword += ( newpwd.charCodeAt( i ) ^ newchal.charCodeAt( i ) ).toString( 16 );
}

console.log("Response: --> ", response);
console.log("New Password: -->", newpwd);
console.log("Pap Password: --->", pappassword);

Two functions are defined in the String prototype to replace the use of the pack function:

.pad( 32, string ) is used to pad out a string with nulls to give the same results as pack( 'a32', string ). Although not needed here it also takes a second parameter if wanting to pad the string ith a character other than nulls.

.packHex is the equivalent of pack( 'H*' ,string ) and translating the code of each pair of hex characters into a character. The function ideally needs more validation to test the string is a valid hex one if is to be used.

After the inputs have been defined, the next four lines instead set variables using these functions rather than pack.

Because Javascript cannot natively xor strings, you then need to use a loop to extract each character, convert it to a numeric, xor those values, then convert the result back into a character to create the pappassword string.

That will return, for me:

Response: -->  – "fbfd42ffde05fcf8dbdd02b7e8ae2d90"
New Password: --> – "password������������������������"
Pap Password: ---> – "dcbdacb03f5d38ca33c128b931c272a"

Which is a result, but unfortunately a different on from the PHP code.

This is because my installation of PHP is configured to use ISO-8859-1 encoding internally, whilst Javascript natively uses UTF-16.

This is not a problem in normal use, but it means the respective md5 functions will be seeing different values and therefore return a different hash.

Assuming you are writing an authentication routine using a PHP backend you will obviously need consistent results. There may be modules available to convert the encoding of the Javscript values for patibility, but it is much easier to make changes to the PHP code.

Because we know the hex strings will be one byte, Javascript is effectively using UTF-8, so PHP can do the same by using the utf8_encode() function to convert the packed hex strings before md5ing them.

Originally I thought that Javascript was internally converting the encoded hex characters into their unicode equivalents because of this, but this was not the case. Instead it was the md5 module being used in Javascript that was performing a UTF-8 conversion on the input.

This leaves two possible options.


1. Use UTF-8 PHP

If possible you can reconfigure your PHP server to use UTF-8 encoding. Or you can change your script to use the utf8_encode() function to mirror the same process as is happening in the Javascript, and convert the hex packed strings to UTF-8 before passing them to md5()

$challenge = 'c731395aca5dcf45446c0ae83db5319e';
$uamsecret = 'secret';
$password = 'password';

$hexchal = pack ("H32", $challenge);
$newchal = pack ("H*", md5(utf8_encode($hexchal) . $uamsecret));
$response = md5("\0" . $password . utf8_encode($newchal));
$newpwd = pack("a32", $password);
$pappassword = implode ("", unpack("H32", ($newpwd ^ $newchal)));

echo "Response: ---> ", $response, "\n";
echo "New Password: ---> ", $newpwd, "\n";
echo "Pap Password: ---> ", $pappassword, "\n";

This then returns the same results as the Javscript:

Response: ---> fbfd42ffde05fcf8dbdd02b7e8ae2d90
New Password: ---> password
Pap Password: ---> dcbdacb03f5d38ca33c128b9310c272a



2. Change the md5 module in Javascript

I am assuming you are using the bluimp JavaScript-MD5 module, as this is what it used by DaloRADIUS routine you linked. You can patch this to bypass the UTF-8 conversion.

There are various ways you can patch this, but on line 259 is the definition of the md5() function itself. This is simply a set of if statements to parse the input options and call the appropriate internal function.

However, the functions called by this block simply provide the UTF-8 input conversion, through a function called str2rstrUTF8() before then call the appropriate functions to provide the actual hashing. You may therefore want to patch the md5() to accept a third parameter to indicate whether the UTF-8 conversion should be applied and then call other functions as appropriate.

However to simply remove the conversion pletely the easier way is to change str2rstrUTF8() to return the input unchanged. This function can be found on line 239, changing it to just read as follows will stop the conversion:

function str2rstrUTF8 (input) {
  return input
}

Alternatively to remove the redundant function call you can instead just remove the references to it. Change the function starting on line 246 to read as follows:

function rawMD5 (s) {
  return rstrMD5(s)
}

The rawHMACMD5() function on line 252 also includes calls to the str2rstrUTF8() function which you may also want to patch for consistency but this is not required for the above routine. That function is called instead when a second parameter is passed to provide a key hash, a feature not available in the native PHP md5() function.

After making either of those changes the Javascript routine now returns the same output as your original (ISO-8859-1 using) PHP code:

Response: -->  – "2d4bd27184f5eb032641137f728c6043"
New Password: --> – "password������������������������"
Pap Password: ---> – "356a1fb08f909fc40dfe448fc483ce3"
发布评论

评论列表(0)

  1. 暂无评论