DEV Community

Cover image for Implementing repeating-key XOR
Stefan Alfbo
Stefan Alfbo

Posted on

Implementing repeating-key XOR

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 {
    ...
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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 nimble\nI 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)
    }
}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)