Reverse Encryption Algorithm from Decryption code I have the code for a decryption algorithm but I'm struggling to figure out how to reverse it into the original encryption code.

function write(x) {
    return x &gt;= "0" &amp;&amp; "9" &gt;= x ? (x.charCodeAt(0) - 48) : (x &gt;= "a") &amp;&amp; "f" &gt;= x ? (x.charCodeAt(0) - 87) : 0;
}

function ondata(input) {
    var DATA = "";
    var i = 0;
    for (; i &lt; input.length; i += 2) {
        var diff = write(input[i]);
        var firstNum = write(input[i + 1]);
        val = 16 * diff + firstNum ^ [31, 126, 188, 168, 210, 3, 208, 219, 229, 149, 187, 222, 17, 31, 59, 100, 131, 62, 163, 81, 115, 154, 2, 220, 166, 30, 112, 192, 48, 172, 174, 119, 249, 5, 251, 41, 46, 148, 43, 210, 35, 42, 231, 145, 16, 6, 10, 28, 228, 49, 103, 248, 151, 109, 19, 20, 151, 243, 12, 193, 152, 126, 83, 235, 27, 109, 66, 96, 179, 88, 11, 189, 30, 187, 23, 172, 93, 109, 239, 247, 123, 105, 55, 98, 117, 175, 114, 212, 146, 136, 38, 115, 124, 39][i / 2];
        DATA += String.fromCharCode(val);
    }
    return DATA;
}

ondata("6c0adddaa66abebc"); // 'starting' (javascript)


As shown, the string 6c0adddaa66abebc produces the output starting. My initial thought was to take the character code of each input character, but I wasn't sure what do to then. I'm sure it's just simple algebra but an explanation or a point in the right direction would be great, I'd like to learn from this.

UPDATE:

Ilmari Karonen's post gave me the help I needed to understand this. The problem I had was that I didn't actually understand what the long val = 16 * ... line was doing. His refactored code and explanation helped me to understand the process. Here's how I came up with the solution if anybody's interested in the thought process:

I knew I had to reverse the function, like in algebra. So logically, I started with finding the character code of each letter in the starting string:

var x = value.charCodeAt(i);

Now, I had to "undo" the xor applied, which obviously (not to me) was just the xor function with the same key:

x ^= key[i];

Then, I had to undo the conversion of the hex string to a decimal number, which was just converting the decimal to a hex number:

var hex = x.toString(16);

I also had to add padded zero's to the hex number, because the decoder worked in pairs of two.

if (hex.length &lt; 2) hex = '0' + hex;

Final solution:

function parse(value)
{
    var result = "";

    for (var i = 0; i &lt; value.length; i++)
    {       
        var x = value.charCodeAt(i);        
        x ^= key[i];

        var hex = x.toString(16);
        if(hex.length &lt; 2)
          hex = '0' + hex;
        result += hex;
    }
    return result;
}
First of all, the write() function does nothing but decode a single hexadecimal digit into a number from 0 to 15.  You could replace it with:



function write(x) {
    return parseInt(x, 16);
}


Thus, the code:

var diff = write(input[i]);
var firstNum = write(input[i + 1]);


takes two consecutive hex digits from the input string and decodes them.  The expression 16 * diff + firstNum then just multiplies the first digit by 16 and adds it to the second one, giving you an 8-bit number from 0 to 255.  (In fact, since parseInt() can handle multi-digit numbers, you could replace all that code with just parseInt(input.substr(i, 2)).)

Next, the 8-bit number we just decoded is XORed with the value of this long expression:

[31, 126, 188, 168, 210, 3, 208, 219, 229, 149, 187, 222, 17, 31, 59, 100, 131, 62, 163, 81, 115, 154, 2, 220, 166, 30, 112, 192, 48, 172, 174, 119, 249, 5, 251, 41, 46, 148, 43, 210, 35, 42, 231, 145, 16, 6, 10, 28, 228, 49, 103, 248, 151, 109, 19, 20, 151, 243, 12, 193, 152, 126, 83, 235, 27, 109, 66, 96, 179, 88, 11, 189, 30, 187, 23, 172, 93, 109, 239, 247, 123, 105, 55, 98, 117, 175, 114, 212, 146, 136, 38, 115, 124, 39][i / 2]


Scrolling to the end, you can see that it's just a long array literal indexed by i / 2.  Since i counts hex digits from the start of the input and is incremented by 2 per iteration, i / 2 is simply counting the number of bytes output so far.  Thus, each byte of the decoded string is XORed with a different number from this fixed array.

(If the index i / 2 points past the end of the key array, the resulting key byte will be undefined, which gets converted to zero when it's XORed with the decoded byte.  Thus, in effect, for long inputs this code behaves as if the key array was padded with zeros.)

Finally, the resulting 8-bit number is just passed to String.fromCharCode(), which converts it into an ASCII (or ISO Latin 1) character, which then gets appended to the output string.

Thus, this code just decodes the input from hexadecimal into a byte string, and then XORs the bytes with a fixed key.  Now can you reverse it?



Ps. Here's how I'd rewrite the decoder:

var key = [31, 126, 188, 168, 210, 3, 208, 219, 229, 149, 187, 222, 17, 31, 59, 100, 131, 62, 163, 81, 115, 154, 2, 220, 166, 30, 112, 192, 48, 172, 174, 119, 249, 5, 251, 41, 46, 148, 43, 210, 35, 42, 231, 145, 16, 6, 10, 28, 228, 49, 103, 248, 151, 109, 19, 20, 151, 243, 12, 193, 152, 126, 83, 235, 27, 109, 66, 96, 179, 88, 11, 189, 30, 187, 23, 172, 93, 109, 239, 247, 123, 105, 55, 98, 117, 175, 114, 212, 146, 136, 38, 115, 124, 39];

function decode(hex) {
    var len = hex.length / 2, bytes = [];

    // decode input hex string into array of bytes:
    for (var i = 0; i &lt; len; i++) {
        bytes[i] = parseInt(hex.substr(2*i, 2), 16);
    }

    // XOR each decoded byte with the corresponding key byte:
    for (var i = 0; i &lt; len; i++) {
        bytes[i] ^= key[i];
    }

    // convert the byte array into a string and return it:
    return String.fromCharCode.apply(null, bytes);
}


You can check that this gives the same results as your original code, but it's hopefully more readable, and also possibly a bit faster (not that speed really matters for such short strings).