This guide is a walkthrough of Set 1 - Challenge 1 from CryptoPals.
The CryptoPals challenge serves as a way to learn cryptography while also learning the vulnerabilities of various encryption methods and how to attack them. From the home page:
This is a different way to learn about crypto than taking a class or reading a book. We give you problems to solve. They’re derived from weaknesses in real-world systems and modern cryptographic constructions. We give you enough info to learn about the underlying crypto concepts yourself. When you’re finished, you’ll not only have learned a good deal about how cryptosystems are built, but you’ll also understand how they’re attacked.
Steps to Solve#
They want us to take this Hex string:
49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d
and convert it to Base64 to get the following string:
SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t
The challenge also has a rule, stating that we should always operate on raw bytes, and never on encoded strings. Only use strings for pretty-printing.
Let’s get started.
Decode Hex#
First, let’s write a function that will decode the hex input and return the decoded bytes. Keeping the rule in mind, let’s make it accept a slice of bytes and return a slice of bytes and an error in case there is an error decoding.
func decodeHex(encoded []byte) ([]byte, error) {
// Initialize a slice of bytes with a length equal to the decoded length
// of the encoded bytes.
decoded := make([]byte, hex.DecodedLen(len(encoded)))
// Ignore the first return value, which is the number of decoded bytes.
_, err := hex.Decode(decoded, encoded)
if err != nil {
return nil, err
}
return decoded, nil
}
Encode to Base64#
Now we need to write another function that will encode a provided byte slice to Base64. Again, returning the encoded bytes rather than a string.
func encodeBase64(input []byte) []byte {
// Initialize a slice of bytes with a length equal to the encoded length
// of the input.
encoded := make([]byte, base64.StdEncoding.EncodedLen(len(input)))
// Encode to Base64
base64.StdEncoding.Encode(encoded, input)
return encoded
}
Putting it Together#
Let’s write one more function that will take our Hex input, and return the Base64 encoded string.
func hexToBase64(hex []byte) ([]byte, error) {
decoded, err := decodeHex(hex)
if err != nil {
return nil, err
}
base64 := encodeBase64(decoded)
return base64, nil
}
And now in our main()
function we can accept a command line argument with the Hex
value, convert that value to Base64, and print it out.
func main() {
// Ensure the argument is supplied.
if len(os.Args) < 2 {
fmt.Println("usage: go run main.go <hex to convert>")
os.Exit(1)
}
encoded := os.Args[1]
base64, err := hexToBase64(encoded)
if err != nil {
fmt.Println("probelm converting to base64: %v", err)
os.Exit(1)
}
fmt.Println(string(base64))
}
Final Code#
Our final code for this challenge should look like this:
package main
import (
"encoding/base64"
"encoding/hex"
"fmt"
"os"
)
func decodeHex(encoded []byte) ([]byte, error) {
// Initialize a slice of bytes with a length equal to the decoded length
// of the encoded bytes.
decoded := make([]byte, hex.DecodedLen(len(encoded)))
// Ignore the first return value, which is the number of decoded bytes.
_, err := hex.Decode(decoded, encoded)
if err != nil {
return nil, err
}
return decoded, nil
}
func encodeBase64(input []byte) []byte {
// Initialize a slice of bytes with a length equal to the encoded length
// of the input.
encoded := make([]byte, base64.StdEncoding.EncodedLen(len(input)))
// Encode to Base64
base64.StdEncoding.Encode(encoded, input)
return encoded
}
func hexToBase64(hex []byte) ([]byte, error) {
decoded, err := decodeHex(hex)
if err != nil {
return nil, err
}
base64 := encodeBase64(decoded)
return base64, nil
}
func main() {
// Ensure the argument is supplied.
if len(os.Args) < 2 {
fmt.Println("usage: go run main.go <hex to convert>")
os.Exit(1)
}
encoded := os.Args[1]
base64, err := hexToBase64(encoded)
if err != nil {
fmt.Println("problem converting hex to base64: %v", err)
os.Exit(1)
}
fmt.Println(string(base64))
}
If we now run this program with the supplied hex encoded input from the challenge:
go run main.go 49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d
We should see the result:

If we look at the expected result the challenge wanted from above, we can see this is the same Base64 string.
SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t
Challenge complete!
Bonus (Testing)#
As a bonus, let’s write some tests.
package main
import "testing"
func TestHexToBase64(t *testing.T) {
// Define some test cases
cases := []struct {
name string
input string
want string
wantErr bool
}{
{
name: "empty string",
input: "",
want: "",
wantErr: false,
},
{
name: "valid hex to base64",
input: "48656c6c6f20776f726c64", // "Hello world" in hex
want: "SGVsbG8gd29ybGQ=", // Base64 of "Hello world"
wantErr: false,
},
{
name: "invalid hex characters",
input: "XYZ",
wantErr: true,
},
}
// Loop over each test case and test
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got, err := hexToBase64([]byte(tc.input))
gotErr := err != nil
if gotErr != tc.wantErr {
t.Errorf("%v - error: %v, wantErr: %v", tc.name, err, tc.wantErr)
}
if !tc.wantErr {
if string(got) != tc.want {
t.Errorf("want: %v, got: %v", tc.want, string(got))
}
}
})
}
}
Run the tests with:
go test -v
The tests should pass.

Now let’s move on to challenge two.