Implementing repeating-key XOR

RMAG news

This is the fifth crypto challenge in set 1 from cryptopals, which is the qualifying set.

The difficulty level is relatively easy, to solve this problem I have used the programming language Go.

Problem description

Here is the opening stanza of an important work of the English language:

Burning ’em, if you ain’t quick and nimble
I go crazy when I hear a cymbal

Encrypt it, under the key “ICE”, using repeating-key XOR.

In repeating-key XOR, you’ll sequentially apply each byte of the key; the first byte of plaintext will be XOR’d against I, the next C, the next E, then I again for the 4th byte, and so on.

It should come out to:

0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f

Solution

Lets start with trying to implement the repeating key XOR function. In this function we would like to loop over each byte in the plaintext.

for i, v := range plaintext {

}

For each byte we need to XOR with a the “current” byte in the key. So for index, i = 0 we want to XOR with the first letter in the key, key index = 0, and so on.

i
key index

0
0 (I)

1
1 (C)

2
2 (E)

3
0 (I)

4
1 (C)

5
2 (E)


… (x)

Here we can use the modulus operator, %, to calculate the key index. By using the length of the key we can use these expressions.

keyLength := len(key)
keyIndex := i%keyLength

If we then put these pieces together into a function called, RepeatingKeyXOR, we would get this function.

func RepeatingKeyXOR(plaintext, key []byte) []byte {
result := make([]byte, len(plaintext))
keyLength := len(key)

for i, v := range plaintext {
result[i] = v ^ key[i%keyLength]
}

return result
}

To verify that it’s working as expected we can create the following unit test with the information from the problem description.

func TestRepeatingKeyXOR(t *testing.T) {
plaintext := []byte(“Burning ’em, if you ain’t quick and nimblenI go crazy when I hear a cymbal”)

result := basics.RepeatingKeyXOR(plaintext, []byte(“ICE”))

expected, _ := hex.DecodeString(“0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f”)

if !bytes.Equal(result, expected) {
t.Errorf(“Expected: %v, Got: %v”, expected, result)
}
}

The output of running this test is, PASS.

Conclusion

The key to solve this solution is to find a good way to rotate the bytes in the given key, and this is done with the modulus operator, %, in Go.

I also had some issues to format the expected value correct, since I thought it was expected to keep the newline from the plaintext without encrypting it.

The complete solution can be found on GitHub.

References

Set 1 – Challenge 5

Leave a Reply

Your email address will not be published. Required fields are marked *