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.