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 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)
}
}
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.
Top comments (0)