Xor toolkit

String substring

String in Java has a substring method (see Javadocs) that works just like the method with the same name in Javascript: it takes two int index arguments and returns a new String containing the characters starting at the first index up to, but not including, the character at the second index.

"cafe".substring(0, 2) ==> "ca" "cafe".substring(2, 4) ==> "fe"

Decoding hex numbers

We can convert java Strings to ints in various bases as long as the strings contain digits of that base.

Integer.parseInt("1203") ⟹ 1203

Integer.parseInt("1010", 2) ⟹ 10

Hex is base 16.

Integer.parseInt("3f", 16) ⟹ 63

Byte arrays

byte is a datatype just like int and double (except that it can only represent values from -128 to 127).

Thus we can make an array of bytes like:

byte[] buf = new byte[10]; // Array of 10 bytes
buf[0] = -128;             // Assign an element in the array
IO.println(buf[0]);        // Access an element of the array

int to byte

parseInt returns int values but if we want to make an byte[] we need to cast each int value to a byte.

Casting an int in the range 128 to 255 will result in a byte with a negative value. That’s fine since we care about the bytes as bytes, not as numeric values.

// Casting chops off top 24 bits.
byte b = (byte) 1234; // b is now -46

// Putting a parsed byte into an array of bytes
byte[] bytes = new byte[size];
bytes[0] = (byte) Integer.parseInt("ab", 16);

Bit positions

We can think of an int as 32 bits written from left to right from most significant (the \(2^{31}\)s place) to least significant (the \(2^0\)s place).

31-24 23-16 15-8 7-0
10010110 10010110 10101001 00100101

(Technically this bit pattern, with the 31st bit set, is a negative int. But again, we don’t really care about the numeric value.)

Shifting

The first step of getting a specific byte out of an int is to shift the appropriate bits down (to right).

For instance, to get the second byte of an int k, we can shift the value down 8 bits putting the bits that used to be at positions 8-15 at positions 0-7.

k >>> 8

Shifting and casting

Once we have the eight bits we care about in bits 0-7, we can cast to a byte to throw away all the other bits.

(byte) (k >>> 8)

Remember that casting has very high precedence so we need the parens around the shift expression to ensure the cast happens last.

Xor

Recall that the ^ operator does a bitwise xor. Like all int operators it promotes bytes to ints so if we want the result as a byte we need to cast it back to byte.

a ^ b is an int, even if a and b were bytes.

(byte) (a ^ b) is the value chopped back down to a byte.

byte[] to String

For now we’re going to use Java’s standard library to take care of converting an array of bytes reperesenting a UTF-8 encoded string into a Java String:

String s = new String(bytes, StandardCharsets.UTF_8)

Later you’ll write your own UTF-8 decoder.

Steps of the Xor challenge

  1. Transform the hex-encoded ciphertext to a byte[]

  2. Transform that byte[] to another byte[] by xor’ing each value with the appropriate part of the int key.

  3. Transform that byte[] into a String containing the plaintext.