Jakub Konka

The ECB art!

Do you remember from your crypto course why you should never use ECB as a block cipher? Well, here is why:

ECB art

It looks great though!

The script

In order to compile the code below, you will need to install Crypto++ library, and download header-only bitmap library.

#include <cryptopp/aes.h>
#include <cryptopp/osrng.h>
#include <cryptopp/modes.h>
#include <iostream>
#include <string>
#include <memory>
#include <cmath>
#include "bitmap_image.hpp"

using namespace CryptoPP;

#define AES_KEYLENGTH 32

class FlattenedRGBImage {
  public:
    FlattenedRGBImage(size_t width, size_t height) : _width(width), _height(height) {
      _red = std::unique_ptr<byte[]>(new byte[_width * _height]);
      _blue = std::unique_ptr<byte[]>(new byte[_width * _height]);
      _green = std::unique_ptr<byte[]>(new byte[_width * _height]);
    }

    byte* red() { return _red.get(); }
    byte* blue() { return _blue.get(); }
    byte* green() { return _green.get(); }

  private:
    size_t _width;
    size_t _height;
    std::unique_ptr<byte[]> _red;
    std::unique_ptr<byte[]> _blue;
    std::unique_ptr<byte[]> _green;
};

int quantise(int in, int levels) {
  auto step = (int) 256 / levels;
  auto n = (int) in / step;
  return n * levels;
}

int main(int argc, char* argv[]) {
  if (argc < 2) {
    std::cerr << "You need pass the full path to the .bmp file!" << std::endl;
    return 1;
  }

  std::string path(argv[1]);

  // load bitmap image
  bitmap_image in_image(path);

  if (!in_image) {
    std::cerr << "Error - Failed to open file " << path << std::endl;
    return 1;
  }

  // flatten for encryption
  const auto height = in_image.height();
  const auto width  = in_image.width();
  FlattenedRGBImage plain(width, height);
  auto ptr_red = plain.red();
  auto ptr_blue = plain.blue();
  auto ptr_green = plain.green();

  for (auto y = 0U; y < height; ++y) {
    for (auto x = 0U; x < width; ++x) {
       rgb_t colour;
       in_image.get_pixel(x, y, colour);
       *(ptr_red++) = (byte)quantise((int)colour.red, 4);
       *(ptr_blue++) = (byte)quantise((int)colour.blue, 4);
       *(ptr_green++) = (byte)quantise((int)colour.green, 4);
    }
  }

  // generate random key
  AutoSeededRandomPool rng;
  SecByteBlock key(0x0, AES_KEYLENGTH);
  rng.GenerateBlock(key, key.size());

  FlattenedRGBImage cipher(width, height);

  try {
    // Encrypt using ECB mode
    ECB_Mode<AES>::Encryption enc(key, key.size());
    enc.ProcessData(cipher.red(), plain.red(), height * width);
    enc.ProcessData(cipher.blue(), plain.blue(), height * width);
    enc.ProcessData(cipher.green(), plain.green(), height * width);

    // Write to bitmap image
    ptr_red = cipher.red();
    ptr_blue = cipher.blue();
    ptr_green = cipher.green();
    bitmap_image out_image(width, height);
    for (auto y = 0U; y < height; ++y) {
      for (auto x = 0U; x < width; ++x) {
         rgb_t colour;
         colour.red = (char)*(ptr_red++);
         colour.blue = (char)*(ptr_blue++);
         colour.green = (char)*(ptr_green++);
         out_image.set_pixel(x, y, colour);
      }
    }
    out_image.save_image("ecb_encrypted.bmp");
  }
  catch(const CryptoPP::Exception& e) {
    std::cerr << e.what() << std::endl;
    return 1;
  }

  return 0;
}