Fixed XOR Decryption in Go

William Gallagher
4 min readDec 23, 2019

--

Intro

Since starting a new job as a security engineer, I’ve been working my way through the Cryptopals Challenges. In this post I’ll be taking a look at Set 1 Challenge 2.

The Problem

In challenge 2 we are given two hex encoded strings of equal length. We need to decode these strings, get their XOR combinations, and then hex encode the result. That seems like a lot, so let’s break it down into steps.

Where we begin and where we end

These are our input strings:

1c0111001f010100061a024b53535009181c
686974207468652062756c6c277320657965

and our result when all is said and done should be:

746865206b696420646f6e277420706c6179

1. Hex decode our input strings

If you aren’t sure what it means to hex encode/decode a string, I go into it in a fair amount of detail here, but here is the function we wrote last time to hex decode a string:

func decodeHex(input []byte) ([]byte, error) {
dst := make([]byte, hex.DecodedLen(len(input)))
_, err := hex.Decode(dst, input)
return dst, err
}

If we run our input strings through that function, it will give us the following byte arrays:

[ 28   1  17  0  31   1   1  0  6  26   2  75 83  83 80   9  24  28]
[104 105 116 32 116 104 101 32 98 117 108 108 39 115 32 101 121 101]

2. XOR Comparison

Now we need to get the XOR combinations of these bytes. To do that, we need to know what that means!

XOR is a bitwise operator (used to compare two bits) that returns 1 if one bit is 1 and the other is 0, otherwise it returns 0. If we break down all the combinations it looks like this:

Input A  Input B  Result
0 0 0
0 1 1
1 0 1
1 1 0

To apply this to our input strings, we are going to take the first byte in each input string and break it into bits. We will then take the first bit of our first input string and the first bit of our second input string and compare them using XOR. We do that for each bit, and the the results are our new byte. Then we do the same things for the second byte and so on until we’ve reached the end. Let’s try it out.

Our first bytes are 28 and 104. Let’s convert those to binary and do our XOR comparison:

0 0 0 1 1 1 0 0 (28)
0 1 1 0 1 0 0 0 (104)
---------------------
0 1 1 1 0 1 0 0 (116)

We can see, after comparing our two bytes, we get 01110100. Now we just need to hex encode it to make sure we’re matching the start of our output string.

3. Hex Encode

Our code to hex encode looks pretty similar to our code for hex decoding.

func encodeHex(input []byte) ([]byte) {
eb := make([]byte, hex.EncodedLen(len(input)))
hex.Encode(db, input)
return db
}

If we run our byte thought that, we see that we get back 74, which is the first two characters of our target output, so we’re on the right track!

4. Putting It All Together

Luckily for us, we don’t have to do all of this XOR comparison by hand. Go has an XOR operator that can be used to compare two bytes. It looks like this: ^. Here’s the code I came up to solve our challenge:

package mainimport (
"fmt"
"encoding/hex"
"errors"
)
func main() {
inp1, _:= decodeHexBytes([]byte("1c0111001f010100061a024b53535009181c"))

inp2, _ := decodeHexBytes([]byte("686974207468652062756c6c277320657965"))


decoded, _ := fixedXorDecrypt(inp1, inp2)
fmt.Printf("%s", encodeHexBytes(decoded))

}
func decodeHexBytes(hexBytes []byte) ([]byte, error) {
ret := make([]byte, hex.DecodedLen(len(hexBytes)))
_, err := hex.Decode(ret, hexBytes)
if err != nil {
return nil, err
}
return ret, nil
}
func encodeHexBytes(input []byte) []byte {
ret := make([]byte, hex.EncodedLen(len(input)))
hex.Encode(ret, input)
return ret
}
func fixedXorDecrypt(input1, input2 []byte) ([]byte, error) {
if len(input1) != len(input2) {
return nil, errors.New("the inputs have mismatched lengths")
}
ret := make([]byte, len(input1))
for i := 0; i < len(input1); i++ {
ret[i] = input1[i] ^ input2[i]
}
return ret, nil
}

If we take a look at our fixedXorDecrypt function, we see that our code is pretty simple.

First, we ensure that our two inputs are the same length. Then we iterate over our two byte arrays and run the XOR comparison on each byte, and push the result into our return array. And that’s it!

Conclusion

Hopefully you’ve come away feeling a little better about using XOR on byte arrays. This is going to be an important concept in the upcoming challenges!

If you want to check out the code I wrote for this on Github, you can find it here: https://github.com/juggler434/crypto.

--

--

William Gallagher
William Gallagher

Written by William Gallagher

Software engineer at Onfleet. Musician, gamer, and juggler.

Responses (2)