Do you remember from your crypto course why you should never use ECB as a block cipher? Well, here is why:
It looks great though!
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;
}