Solving The Cryptopals Crypto Challenges (Set 1 - Challenge 1,2)

29 Nov 2024

Overview

I came across the cryptopals crypto challenges in this blog (A really cool blog, I recommend following it). The challenges looked interesting and I am solving them.

  • I am writing this blog series to record and analyse my process of solving a problem.
  • All published parts of the series can be found here.
  • All code in this series can be found here.
  • All solutions are in Golang.
  • As the intention of this exercise is to maximise learning, I will be doing it the old-fashioned way, sans LLMs.
  • I welcome all feedback and critique on any part of the blog series.

Challenge 1

Problem

Convert hex to base64

There was rule that said we had to work with raw bytes and not on encoded strings and use the string representation only for display. This will be the rule we will follow in all the challenges.

Solutioning

While I have worked with Golang in the past, I have never needed to do a lot of string/bytes processing. So, the first step would be to figure out handling bytes in Golang.

A Google search led me to Strings, bytes, runes and characters in Go — The Go Programming Language which then led me to arrays, slices (and strings): The mechanics of ‘append’ — The Go Programming Language. I now have a better understanding of slices in Go.

Hit my head around for some time missing something really basic, I peeked at this writeup of the same challenge. Realised home brewing implementations of the encoding and decoding logic will take forever. So, I cheated and used the functions in the standard library. After going through documentation, this is what I came up with.

// Convert hex to base64
// Operate on raw bytes

package main

import (
	"encoding/base64"
	"encoding/hex"
	"fmt"
)

func decodeHex(ip string) string {
	op := make([]byte, hex.DecodedLen(len(ip)))
	_, err := hex.Decode(op, []byte(ip))
	if err != nil {
		fmt.Print(err)
	}
	return string(op)
}

func encodeToBase64(ip string) string {
	op := make([]byte, base64.StdEncoding.EncodedLen(len(ip)))
	base64.StdEncoding.Encode(op, []byte(ip))
	return string(op)
}

func main() {
	inp := "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d"
	fmt.Print(encodeToBase64(decodeHex(inp)))
}

Challenge 2

Problem

The challenge is pretty straightforward. Write a function that takes two equal-length buffers and produces their XOR combination. The input is a hex encoded string.

Solutioning

We already have a function that does hex decoding for us, which will return the decoded hex string. We just have to XOR two strings.

The XOR is a bitwise operator and works on numeric types so we convert the strings to byte arrays and loop through the arrays to XOR individual items. You’ll see that the following code does not handle uneven arrays. I could add a condition for checking if the length of the two arrays are the same, but nah.

// Get two hex encoded strings
// Xor one against the other
// The result should be a hex

package main

import (
	"encoding/hex"
	"fmt"
)

func decodeHex(ip string) []byte {
	op := make([]byte, hex.DecodedLen(len(ip)))
	_, err := hex.Decode(op, []byte(ip))
	if err != nil {
		fmt.Print(err)
	}
	return op
}

func encodeToHex(ip string) []byte {
	op := make([]byte, hex.EncodedLen(len(ip)))
	hex.Encode(op, []byte(ip))
	return op
}

func xor(op1 []byte, op2 []byte) []byte {
	op := make([]byte, len(op1))
	for i := 0; i < len(op1); i++ {
		op[i] = op1[i] ^ op2[i]
	}
	return op
}

func main() {
	inp1 := "1c0111001f010100061a024b53535009181c"
	hex1 := decodeHex(inp1)
	inp2 := "686974207468652062756c6c277320657965"
	hex2 := decodeHex(inp2)
	res := xor(hex1, hex2)
	fmt.Print(string(encodeToHex(string(res))))
}

Conclusion

This was a nice change of pace from writing production code in Python. See in the next part.

Tags