Cryptopals: Set 1 – challenge 1

Challenge 1 in the cryptopals suite is to juggle between Base64 and Hex encoding of byte strings. Let's do just that, with value objects!

The entire first set of Cryptopals challenges helps set up the basics we’ll need for more complicated work down the road. Many of these challenges are both foundational building blocks and relatively easy.

We need to keep the fact that they’re building blocks in mind. Otherwise it’s possible for us to take too straight-forward of an approach in finding a solution. We want our code to be reusable elsewhere.

Challenge 1 is all about encoding.

Convert hex to base64

The string:
49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d

Should produce:
SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t

So go ahead and make that happen. You’ll need to use this code for the rest of the exercises.

A simple approach

Converting between one form of encoding to another in PHP is relatively straightforward. This entire challenge could be summed up with the following:

$raw = '49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d';
$b64 = base64_encode(hex2bin($raw));

echo $b64;
// SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t

This is simple and easy to understand, but it’s not the most reusable or elegant code. Instead, we can define a custom object to wrap our strings and operate on raw bytes.

Value objects and wrappers

A value object is a simple wrapper around an underlying raw value that provides additional functionality. Call back to our discussion of the BCMath and GMP extensions – both use wrappers around numbers to grant PHP more functionality with very large (or small) numbers.

In this case, we need an object that takes an encoded string, decodes it to raw bytes, and allows us to get either the raw value or an encoded string back out of it.

class Bytes
{
  private $bytes;

  public function __construct(string $bytes)
  {
    $this->bytes = $bytes;
  }

  public function asHex(): string
  {
    return bin2hex($this->bytes);
  }

  public function asB64(): string
  {
    return base64_encode($this->bytes);
  }

  public function asRaw(): string
  {
    return $this->bytes;
  }

  public static function fromHex(string $hex): self
  {
    return new self(hex2bin($hex));
  }

  public static function fromB64(string $b64): self
  {
    return new self(base64_decode($b64));
  }
}

This is a lot more code, but is mostly boilerplate to make our raw bytes easier to work with. Instead of working with raw strings, we can leverage our new value object to decode and then arbitrarily re-encode our data as needed:

$bytes = Bytes::fromHex('49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d');

echo $bytes->asB64();
// SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t

The biggest advantage here is that we can switch between encoding types, or even add new functionality to our Bytes class as necessary for future challenges!