简体   繁体   中英

Why is this simple JavaScript XOR encryption algorithm not working?

I am trying to develop a ( very ) simple XOR encryption algorithm in JavaScript.

In PHP, the following encryption algorithm works as expected:

function encryptXor($text, $key) {
  $result = '';

  for ($i = 0; $i < strlen($text); $i++)
    $result[$i] = $text[$i] ^ $key[$i % strlen($key)];

  return $result;
}

However, the following JavaScript algorithm does not work properly:

function encryptXor(text, key) {
  var result = '';

  for(var i = 0; i < text.length; i++)
    result += text.charAt(i) ^ key.charAt(i % key.length);

  return result;
}

A test case follows:

Text: someRandomText
Key:  123456789
PHP output: B]^QgWY\V\fVLA
JS output:  12345678912345

Apparently, the ^ operator is behaving differently between the two languages.

I have found some questions regarding the difference between PHP's and JavaScript's ^ operators, such as this one , this one or this one , but I still could not resolve this issue.

Why is this JavaScript algorithm not working as expected?


Minor note 1: Since it is not possible to iterate over the characters of a string and replace them one by one in JavaScript, I used the += operator inside the for loop in order to append the output of each XOR operation to the result variable.

Minor note 2: In order to normalize the character set, the functions actually return base64_encode($result) and btoa(result) in PHP and JS, respectively. Then, in order to decrypt it, the decryptXor() functions have to decode the result variables before iterating over them. Nonetheless, since this is not relevant to the question, I simplified the functions a bit. In this case, for the purpuses of testing, it is safer to use only alphanumeric characters in the text variable and only numerals in the key variable (or vice-versa).

You need some other methods, like String#charCodeAt or String.fromCharCode for getting the same result.

The main problem is, you need a numerical value instead of the character/string.

 function encryptXor(text, key) { var result = ''; for (var i = 0; i < text.length; i++) { result += String.fromCharCode(text.charCodeAt(i) ^ key.charCodeAt(i % key.length)); } return result; } console.log(encryptXor('someRandomText', '123456789')); // B]^QgWY\\V\\fVLA 

A bit shorter version

 function encryptXor(text, key) { return Array.from( text, (c, i) => String.fromCharCode(c.charCodeAt() ^ key.charCodeAt(i % key.length)) ).join(''); } console.log(encryptXor('someRandomText', '123456789')); // B]^QgWY\\V\\fVLA 

Your main problem is that you are using charAt instead of charCodeAt . 'someRandomText'.charAt(0) evaluates to 's' . 's' ^ x === x , since the ^ operator attempts to convert both operands to numbers and Number('s') is NaN . So when you xor with any character other than a numeric one you just get the value of the key.

You also have another problem regarding Note 1: You can't just append to a string. If your algorithm results in the string '567' it's impossible to know whether that was created from concatenating '56' and '7' or '5' and '67'. That becomes a much bigger problem as the string gets longer. You could use an array instead of a string or left zero pad each substring to the same length or as Nina suggested, you could convert the result of the XOR back to a single char with String.fromCharCode .

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