Why is this simple JavaScript XOR encryption algorithm not working? - javascript

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.

Related

Why am I getting a number and not a concatenated string?

I have this code
var n = parseInt(prompt("Give me a number"));
var sum = 0;
for (let i=0; i < n.toString().length; i++){
let expon = n.toString()[i] ** n.toString().length;
sum += expon;
}
My doubt is the following: If my n is 371, n.toString()[0] is '3' (A STRING!!), why is it then that when I do ** n.toString().length (which is 3). I get 27 ?!!?
Also, it is clear to me that if x = '3' and I do x + x I get '33' and not 6. Can this happen to the addition only? why?
'3' ** 3 is 27 because ** converts both its arguments to numbers if possible. It has no function other than numeric exponentiation.
'3' + 3 is '33' because + has multiple possible functions (addition and string concatenation), and if at least one of the arguments is a string, string concatenation is used instead of addition. In another universe, it may well attempt to do numeric addition first and end up at 6, but the language designers of our universe chose to do it this way round.
Check this: https://medium.com/swlh/strings-and-basic-mathematical-operators-in-javascript-e9de3d483dae
In simple words - in JavaScript, when trying to add String type to non-String type, adding operator automatically casts added elementsto both be set of characters, because '+' can be understood as adding OR as concatenating. Although, every different arithmetic operations (substracting, dividing, multiplying, powering, etc.) performed on String and non-String type will force the first one to be casted to the Number form. If not possible, we will receive NaN result.

Is there an eval() alternative for this expression?

I have a string expression like such: "1+2+3", and it must stay as a string. In other words, looping and casting the digits to perform the operation isn't an option, so my solution is eval("1+2+3"). But eval() is slow and has all these issues associated with it. Is there an alternative to evaluate my string?
Evaluating a string is not only slow, it's dangerous. What if, by malicious user intent or error, you end up evaluating code that crashes your program, destroys your data o opens a security hole?
No, you should not eval() the string. You should split it, cast the operands to numbers, and sum them.
You can keep the string around if you like (you said you needed the string), but using the string to actually perform this operation is a Really Bad Idea.
var string = "1+2+3"
var numbers = string.split('+').map(function(x) { return parseInt(x) })
var sum = numbers.reduce(function(total, x) { return total + x }, 0)
This is a silly question:
var reducer = function (a, b) {
return +a + +b;
};
"1+2+3".match(/[+-]?\d+/g).reduce(reducer); // 6
// or addition only
"1+2+3".split(/\D/).reduce(reducer); // 6

reversing a number in javascript (clarification)

I have a simple question to ask, in which I am slightly embarrassed to ask, but I realize that I won't learn unless I ask.
When looking at reversing a string, I recognize that reversing a string requires you to split up the string, reverse it, and then re-join it. Like so.
var stringReverse = function (n){
return n.split("").reverse().join("");
}
console.log(stringReverse("hello"));
However, I was trying to reverse a number, much of the principles were very similar except for one part. The code below is:
var reverse_a_number = function (n){
n = n + "";
return n.split("").reverse().join("");
}
console.log(reverse_a_number(32243));
My question is why am I needed to bring in the " n = n + " ";" to the code? I was assuming that the same principles would be similar regardless of it being a string, or an integer.
Much thanks in advance, and I apologize that this question is elementary.
why am I needed to bring in the " n = n + " ";" to the code?
Adding + "" will make a cast to String, this way you can use String functions (.split).
The integer have only functionality of a Number. If you want to treat the Number as a String you need to cast it (+ "") or convert it (.toString())
The .reverse() function come from Array object that is created when you use String function .split().
With .join() you come back to String from Array (of characters).
If you want to come back to Number after reverting, you can choose one of these functions.
To put it simply, the docs require it to be a string. You could combine your two methods by doing exactly what you're doing in reverse_a_number (it works for both). Also, don't be embarrassed to ask questions, it's how you learn :)
Number doesn't have reverse and split method directly and you should convert number to string that be able reverse it.
to convert it you can add an empty string after it, like you.
just it.
Javascript sets the type of a variable at runtime.
If she (yes, it's a girl) sees that you only have ciphers, she will consider it's an integer.
Adding + ""; casts it into a string, which is an array of chars.
Your string is stored a lil bit like : ['h', 'e', 'l', 'l', 'o']
an integer is stored as {0011001010101...}
Explanation
You are passing in a number, not a string. When you combine a number with a string ("") it assumes you want to make a string. So you are really converting it to a string. To an array, then back to a string. If you attempt to split a number, the compiler will throw an error. More exactly
TypeError: undefined is not a function (evaluating 'n.split('')')
So what you really are doing is making the number into a String. There are different methods you can use, take a look here.
n += '';(String) ->
.split() (Array) -> .split() (Array) -> .join() (String)
Your function is actually producing and returning a string
Alternative Function
I'm going to critique ;) , you could shorten, and improve this function using the following.
var reverse_a_number = function (n){
return +(n += "").split("").reverse().join("");
}
What does this do?
n = n + '' has a special operator, +=. We can shorten this to on-line by using this inside parenthesis. The + will convert it into a Integer
A number doesn't have the split method. You have to convert it into a string, which does have split.
You can use typeof to find out the type of a variable. The following code snippet demonstrates the types of a number and a string, and shows the value of the split attribute for each one. Note that split is undefined for the number.
var n = 42;
document.write(typeof n, '<br />', n.split, '<br />', '<br />');
var s = ''+n;
document.write(typeof s, '<br />', s.split);
If you want to reverse an integer by treating it as an integer, without using string operations, you can do the following.
function reverseInteger(n) {
var result = 0;
while (n != 0) {
var digit = n%10;
result = 10*result + digit;
n = (n-digit)/10;
}
return result;
}
var n = 3141590123456;
document.write(n, '<br />');
document.write(reverseInteger(n));
Note that the last digit of n is n%10. If we subtract the last digit and divide n by ten, the effect is to shift n one digit to the right. We keep doing this and build up result in reverse by multiplying it by ten on each iteration and adding the digit that we just removed from n.

JavaScript: Non-standard ways to convert characters to codes, or strings to binary arrays (arrays of numbers)

Is there a trick in JavaScript to convert character to it's code, other than .charCodeAt(i)?
I want to convert string to binary array by a fastest way (i.e. faster than charCodeAt can do it).
But I also interested to know about slow methods too.
You could do a lookup table, but I doubt that it would be any faster. Something like:
function(str){
var codes = {},
output = [];
for (var i=0; i<256; i++){
String.codes[fromCharCode(i)] = i;
}
for (var i=0; i<str.length; i++){
output.push(codes[str[i]]);
}
return output;
}
Generally speaking though if you can go with native code, go with native code.
I doubt you will find something faster than this code to accomplish what you want:
function getCharArray(str)
{
var chars = [];
for (var i = 0, n = str.length; i != n; ++i) {
chars.push(str.charCodeAt(i));
}
return chars;
}
You have to do it this way because JavaScript doesn't have a separate type for a single character; even a single character is still a string and therefore you need String.charCodeAt().
Strings in javascript are unicode, not ascii, meaning that the code of a single character might be more than one byte.
"€".charCodeAt(1) = 8364
This fits into two bytes. If you convert it back to binary it could be interpreted as either one 2-byte-character or 2 1-byte-characters. There is just no way to know. Knowing the length of a unicode char is tricky. This might help you see why this problem is hard to solve: http://www.joelonsoftware.com/articles/Unicode.html
Of course I'm not saying you can't convert unicode strings to binary, I'm stating that this is the task you need to solve, not just converting single characters to bytes.

Calculate real length of a string, like we do with the caret

What I want is to calculate how much time the caret will move from the beginning till the end of the string.
Explanations:
Look this string "" in this fiddle: http://jsfiddle.net/RFuQ3/
If you put the caret before the first quote then push the right arrow ► you will push 3 times to arrive after the second quote (instead of 2 times for an empty string).
The first way, and the easiest to calculate the length of a string is <string>.length.
But here, it returns 2.
The second way, from JavaScript Get real length of a string (without entities) gives 2 too.
How can I get 1?
1-I thought to a way to put the string in a text input, and then do a while loop with a try{setCaret}catch(){}
2-It's just for fun
The character in your question "󠀁" is the
Unicode Character 'LANGUAGE TAG' (U+E0001).
From the following Stack Overflow questions,
" Expressing UTF-16 unicode characters in JavaScript"
" How can I tell if a string contains multibyte characters in Javascript?"
we learn that
JavaScript strings are UCS-2 encoded but can represent Unicode code points outside the Basic Multilingual Pane (U+0000-U+D7FF and U+E000-U+FFFF) using two 16 bit numbers (a UTF-16 surrogate pair), the first of which must be in the range U+D800-U+DFFF.
The UTF-16 surrogate pair representing "󠀁" is U+DB40 and U+DC01. In decimal U+DB40 is 56128, and U+DC01 is 56321.
console.log("󠀁".length); // 2
console.log("󠀁".charCodeAt(0)); // 56128
console.log("󠀁".charCodeAt(1)); // 56321
console.log("\uDB40\uDC01" === "󠀁"); // true
console.log(String.fromCharCode(0xDB40, 0xDC01) === "󠀁"); // true
Adapting the code from https://stackoverflow.com/a/4885062/788324, we just need to count the number of code points to arrive at the correct answer:
var getNumCodePoints = function(str) {
var numCodePoints = 0;
for (var i = 0; i < str.length; i++) {
var charCode = str.charCodeAt(i);
if ((charCode & 0xF800) == 0xD800) {
i++;
}
numCodePoints++;
}
return numCodePoints;
};
console.log(getNumCodePoints("󠀁")); // 1
jsFiddle Demo
function realLength(str) {
var i = 1;
while (str.substring(i,i+1) != "") i++;
return (i-1);
}
Didn't try the code, but it should work I think.
Javascript doesn't really support unicode.
You can try
yourstring.replace(/[\uD800-\uDFFF]{2}/g, "0").length
for what it's worth

Categories

Resources