I totally agree with Maarten Bodewes answer.
I will refer to another aspect of the question. Just how to securely xor in Javascript, which is the title of the question, not why or if it really needed.
Personally I would prefer WebAssembly for any cryptographic related stuff since Javascript is a high level language. As you can understand since WASM is an intermediate low level language there is no contract that the outputed assembly will be the same on any browser, or from time to time, it is just more probably to be secure that plain Javascript. Lastly, another aspect of the security of web applications is that there are just so many other attack vectors that nobody will focus on your cryptography.
This is an example of C++ code with the corresponding WASM and the actual x86_64 that will be executed by the browser.
C++
int a(char **str1, int str1_size, char **str2, int str2_size, char**result) {
if (str1_size != str2_size) {
return -1;
}
for (int i = 0; i < str1_size; i++) {
(*result)[i] = (*str1)[i] ^ (*str2)[i];
}
}
WASM
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "_Z1aPPciS0_iS0_" (func $_Z1aPPciS0_iS0_))
(func $_Z1aPPciS0_iS0_ (; 0 ;) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (result i32)
(block $label$0
(br_if $label$0
(i32.ne
(get_local $1)
(get_local $3)
)
)
(set_local $1
(i32.const 0)
)
(loop $label$1
(i32.store8
(i32.add
(i32.load
(get_local $4)
)
(get_local $1)
)
(i32.xor
(i32.load8_u
(i32.add
(i32.load
(get_local $2)
)
(get_local $1)
)
)
(i32.load8_u
(i32.add
(i32.load
(get_local $0)
)
(get_local $1)
)
)
)
)
(set_local $1
(i32.add
(get_local $1)
(i32.const 1)
)
)
(br $label$1)
)
)
(i32.const -1)
)
)
x86_64
wasm-function[0]:
sub rsp, 8 ; 0x000000 48 83 ec 08
cmp esi, ecx ; 0x000004 3b f1
jne 0x35 ; 0x000006 0f 85 29 00 00 00
0x00000c:
xor eax, eax ; 0x00000c 33 c0
0x00000e: ; 0x00000e from: [0x000033]
mov ecx, dword ptr [r15 + r8] ; 0x00000e 43 8b 0c 07
add ecx, eax ; 0x000012 03 c8
mov ebx, dword ptr [r15 + rdx] ; 0x000014 41 8b 1c 17
add ebx, eax ; 0x000018 03 d8
movzx ebx, byte ptr [r15 + rbx] ; 0x00001a 41 0f b6 1c 1f
mov ebp, dword ptr [r15 + rdi] ; 0x00001f 41 8b 2c 3f
add ebp, eax ; 0x000023 03 e8
movzx ebp, byte ptr [r15 + rbp] ; 0x000025 41 0f b6 2c 2f
xor ebx, ebp ; 0x00002a 33 dd
mov byte ptr [r15 + rcx], bl ; 0x00002c 41 88 1c 0f
add eax, 1 ; 0x000030 83 c0 01
jmp 0xe ; 0x000033 eb d9
0x000035:
mov eax, 0xffffffff ; 0x000035 b8 ff ff ff ff
nop ; 0x00003a 66 90
add rsp, 8 ; 0x00003c 48 83 c4 08
ret ; 0x000040 c3
The final assembly seems fine to me.
Lastly, probably a XOR operation because of it's simplicity, could work fine even in plain Javascript, I'm just posting this answer as an idea to alternative (more secure in general?) ways to implement cryptography in a browser.
EDIT: Tool used for the generated code.