Search code examples
javabitwise-operatorsinversebitwise-and

Reconstructing a string manipulated with bitwise operations


Suppose I have a Java string of length 32, e.g.

String s = "Y7yEdfjQ2qmpGZbPYswKIdxYVo6KnR9M";

I'm looking for the string t with the following property: If the following loop is performed on t, the resulting string is s:

char[] tArr = t.toCharArray();
for (int i = 1; i < 32; i++) {
    tArr[i] = (char) (tArr[i] ^ tArr[i * 123456 % 31] & 31);
}

Note that in Java the array operator [] has highest precedence and bitwise AND is evaluated before bitwise XOR.

In order to obtain t the reverse operation must be applied to s. The inverse operation of XOR is XOR, however the logical AND obviously has no inverse operation.

Can the string t be recovered short of bruteforcing?


Solution

  • Your loop takes in each iteration two chars, and xors the first char with the lowest 5 bits of the other char. That means that the top 3 bits remain unchanged. They don't get lost. In fact the top 3 bits of t[i] are equal to the top 3 bits of s[i], and we only have to find the lowest 5 bits of the characters in the original string t.

    Now, lets calculate i * 123456 % 31, and see what the loop actually does in each iteration :

    t[1] = t[1] ^ (t[14]&31)
    t[2] = t[2] ^ (t[28]&31)
    t[3] = t[3] ^ (t[11]&31)
    t[4] = t[4] ^ (t[25]&31)
    t[5] = t[5] ^ (t[8]&31)
    t[6] = t[6] ^ (t[22]&31)
    t[7] = t[7] ^ (t[5]&31)
    t[8] = t[8] ^ (t[19]&31)
    t[9] = t[9] ^ (t[2]&31)
    t[10] = t[10] ^ (t[16]&31)
    t[11] = t[11] ^ (t[30]&31)
    t[12] = t[12] ^ (t[13]&31)
    t[13] = t[13] ^ (t[27]&31)
    t[14] = t[14] ^ (t[10]&31)
    t[15] = t[15] ^ (t[24]&31)
    t[16] = t[16] ^ (t[7]&31)
    t[17] = t[17] ^ (t[21]&31)
    t[18] = t[18] ^ (t[4]&31)
    t[19] = t[19] ^ (t[18]&31)
    t[20] = t[20] ^ (t[1]&31)
    t[21] = t[21] ^ (t[15]&31)
    t[22] = t[22] ^ (t[29]&31)
    t[23] = t[23] ^ (t[12]&31)
    t[24] = t[24] ^ (t[26]&31)
    t[25] = t[25] ^ (t[9]&31)
    t[26] = t[26] ^ (t[23]&31)
    t[27] = t[27] ^ (t[6]&31)
    t[28] = t[28] ^ (t[20]&31)
    t[29] = t[29] ^ (t[3]&31)
    t[30] = t[30] ^ (t[17]&31)
    t[31] = t[31] ^ (t[0]&31)
    

    We know that after the final iteration, the array has been transformed from t (which we don't know) to s (which we know - "Y7yEdfjQ2qmpGZbPYswKIdxYVo6KnR9M").

    Now, since t[0] is never modified, we know that s[0]=t[0]='Y' (or 01011001 in binary).

    This gives us t[31] easily :

    t[0]&31 = 00011001
    s[31] = 'M' = 01001101 = t[31] ^ 00011001
    

    xor both sides, and get :

    t[31] = 01001101 ^ 00011001 = 01010100
    

    The other characters are less easy.

    The iterations of the loop form two cycles :

    1->14->10->16->7->5->8->19->18->4->25->9->2->28->20->1
    
    3->11->30->17->21->15->24->26->23->12->13->27->6->22->29->3
    

    Now, lets try finding other characters of t :

    t[30] = t[30] ^ (t[17]&31)

    after this assignment, t[30] becomes s[30], i.e. '9' or 00111001.

    We know that 001 11001 = 001 ????? ^ 000 xxxxx

    Where ????? are the lowest 5 bits of t[30] and xxxxx are the lowest 5 bits of t[17]. But actually, it's not the original t[17]. By the time we assign t[30], t[17] has already been updated to its final value, which we know (it's s[17] - 's' or 01110011).

    Therefore 001 11001 = 001 ????? ^ 000 10011.

    And if we xor both sides, 001 11001 ^ 000 10011 = 001 01010

    so the original t[30] is 00101010.

    So far I found t[0],t[30] and t[31]. I believe (though I haven't actually checked), that I can go on and find all the other characters of t this way.

    The important thing to pay attention to when we calculate t[i] is that if t[i] depends on t[j] such that i<j, it depends on the original value of t[j] (the unknown), while if i>j, it depends on the final value of t[j] (which is s[j] - which we know).