简体   繁体   中英

XOR strings: JS vs PHP

I tried to XOR two strings in PHP and in JS and I got different results:

PHP function

function xh($a, $b) {
  $res = ""; $i = strlen($a); $j = strlen($b);
  while($i-->0 && $j-->0) {
    $res.= $a[$i] ^ $b[$j];
  }
  return base64_encode($res);
}

JS function

function xh(a, b) {
  var res = "", i = a.length, j = b.length;
  while (i-->0 && j-->0) {
    res+= String.fromCharCode(a.charCodeAt(i) ^ b.charCodeAt(j));
  }
  return btoa(res);
}

I examined the bytes and found out that the sixth byte in PHP function is always zero, so I updated JS function to

JS function equivalent to PHP

function xh2(a, b) {
  var res = "", i = a.length, j = b.length;
  while (i-->0 && j-->0) {
    res+= String.fromCharCode((a.charCodeAt(i) ^ b.charCodeAt(j)) & 95);
  }
  return btoa(res);
}

So what is happening to that bit?

Example input/output:

string a: 5D41402ABC4B2A76B9719D911017C592
string b: FE2D010308A6B3799A3D9C728EE74244
PHP says: Bg0HVwBUVQkDDgcAVQRYWw8AUlBUVVtSUgIBBFUGAVM=
 JS says: Bg0HdwB0dQkDDgcAdQR4ew8AcnB0dXtycgIBBHUGAXM=
JS2 says: Bg0HVwBUVQkDDgcAVQRYWw8AUlBUVVtSUgIBBFUGAVM=

First difference in this example:

C: 0x43  = 0100 0011
4: 0x34  = 0011 0100
C^4 (JS) = 0111 0111 = 0x77 (correct)
C^4 (PHP)= 0101 0111 = 0x57
             ^
             sixth bit wrong

The inputs are MD5 hashes, I use default encoding, my OEM charset is CP1250, locale cs-cz, the files are stored in UTF-8 encoding and the page is generated with HTTP header text/html;charset=UTF-8 and with meta tag UTF-8 if any of these matters.

My web server is Mongoose 6.7 with php 5.6 (cgi) bundled. I also tried the latest 7.3 (x86 and x64) with the same results, however @apokryfos in the comments tested it with the sixth bit correct.

For JS use a buffer or typed array instead of a string. Otherwise you need some binary safe string encoding.

You can XOR two strings in PHP in their entirety: $a ^ $b (don't forget length checking).

See: https://developer.mozilla.org/en-US/docs/Web/API/DOMString/Binary

I get Bg0HdwB0dQkDDgcAdQR4ew8AcnB0dXtycgIBBHUGAXM= from PHP with your code so something else is going on.

Can you provide the PHP version and build / source?

The root of the problem is case-sensitivity: seems like some buggy implementations of MD5 doesn't lower the case of md5 output. Two different libraries were used on the client side and on the server side.

'A' starts at 0x41 = 0100 0001
'a' starts at 0x61 = 0110 0001
                       ^
                       here is the sixth bit

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM