Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ target_sources(${EXE_NAME} PRIVATE
src/ecbencryptor.cpp
src/evpwrapper.cpp
src/bitmapreader.cpp
src/filewriter.cpp)
src/filewriter.cpp
src/onetimepad.cpp
src/qrngprocessor.cpp)

target_include_directories(${EXE_NAME} PRIVATE
${OpenSSL_INCLUDE_DIRS}
Expand Down
23 changes: 23 additions & 0 deletions MP_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Qrypt One-Time Pad Penguinify challenge


_Maria Perepechaenko_

## Notes:

- The current program supports both, AES ECB encryption as well as One-Time Pad(OTP) encryption with QRNG random used as a key.
- To produce an encrypted version of the bitmap file `Tux.bmp` using AES ECB run
`./penguinify --bitmap Tux.bmp --key 0123456789abcdef --out ecb_out.bmp`
- To produce an encrypted version of the bitmap file `Tux.bmp` using OTP with QRNG random run
`./penguinify --bitmap Tux.bmp --otp quantum.qrand --out otp_out.bmp`
- The program distinguishes between AES ECB and OTP based on the keyword "key" or "otp".


- Encryption of the bitmap file is performed using `otpencrypt` method of the `otpencryptor` class. The main functionality is as follows:
1. Get the header of the bitmap file to be encrypted, and write it into the output file without any changes to the header
2. Get the body of the bitmap file, and a qrng file that contains quantum randomness, and return the content of the qrng file equal in size to the body of the bitmap file.
3. XOR the body of the bitmap file with the content of the qrng file returned by the above described method.
4. Write the encrypted body of the bitmap file into the output file.


- A new `mocks_otp.h` and `test_otpencryptor.cpp` files are available for convenience and are used for testing.
27 changes: 27 additions & 0 deletions include/onetimepad.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include "bitmapreader.h"
#include "filewriter.h"
#include "qrngprocessor.h"

#include <string>


/*
* otpencryptor class that facilitates One-Time Pad encryption of a given bitmap file, while keeping the header unchanged
* otpencryptor class contains qrngprocessor class as a private member, and q_xor as a private method (q_xor method computes and returns XOR result of two inputs)
* otpencryptor class contains otpencrypt as a public method that encrypts a body of a given bitmap file using One-Time Pad encryption, leaving the header intact, and writing the header and the encrypted result into an output file
*/
class otpencryptor {

private:
qrngprocessor &_processor;
std::vector<uint8_t> q_xor(const std::vector<uint8_t> &bytes,
const std::vector<uint8_t> &qrng);

public:
otpencryptor(qrngprocessor &processor) : _processor{processor} {}
virtual ~otpencryptor() = default;
virtual void otpencrypt(bitmapreader &in, const std::string &qrngfile,
filewriter &out);
};
22 changes: 22 additions & 0 deletions include/qrngprocessor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include "bitmapreader.h"

#include <string>
#include <cstdint>
#include <fstream>
#include <vector>


/*
* qrngprocessor class processes files containing qrng by opening and reading them, then returns a sufficient quantity of random bytes from qrng file, that corresponds to the body size of the corresponding bitmap files to be encrypted
* qrngprocessor class contains processqrngfile as a public method that takes as input a bitmap file to be encrypted along with a qrng file, and returns random bytes from qrng file equal in size to the body of the input bitmap file intended for encryption
*/

class qrngprocessor {

public:
qrngprocessor() {}
virtual ~qrngprocessor() = default;
virtual std::vector<uint8_t> processqrngfile(bitmapreader &in, const std::string &qrngfile);
};
38 changes: 38 additions & 0 deletions src/onetimepad.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

#include "onetimepad.h"
#include "qrngprocessor.h"

/*
* Function computes and returns XOR of two inputs
* INPUT: two constant vectors of unsigned 8-bit integers
* OUTPUT: vector of unsigned 8-bit integers
*/
std::vector<uint8_t> otpencryptor::q_xor(const std::vector<uint8_t> &bytes, const std::vector<uint8_t> &qrng){
//first want to make sure the inputs have the same size
if(bytes.size() != qrng.size()){
throw std::runtime_error("Inputs provided must have the same size.");
}
//initialize an output vector
std::vector<uint8_t> cipher(bytes.size());
//cipher = bytes XOR qrng
std::transform(bytes.begin(), bytes.end(), qrng.begin(), cipher.begin(), std::bit_xor<uint8_t>());

return cipher;

}

/*
* Function performs One-Time Pad encryption of an input bitmap file, while keeping the header intact, and writing the header as well as encrypted body into an output file
* INPUT: an instance of a bitmapreader, name of the qrng file, an instance of a filewriter
* OUTPUT: none
*/
void otpencryptor::otpencrypt(bitmapreader &in, const std::string &qrngfile,
filewriter &out) {

out.write(in.getHeader()); //writing the header unchanged
out.write(q_xor(in.getBody(), _processor.processqrngfile(in, qrngfile))); //writing the OTP encrypted version of the bitmap file body
}
73 changes: 52 additions & 21 deletions src/penguinify.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
// Copyright © 2020, Qrypt, Inc., All rights reserved.

/*
* There is an option to pass command ./penguinify --bitmap Tux.bmp --key 0123456789abcdef --out bar.bmp
* There is an option to pass command ./penguinify --bitmap Tux.bmp --otp quantum.qrand --out bar.bmp
* We differentiate based on the identifier "key" vs "otp"
*/


#include <iostream>
#include <string>
#include <vector>
Expand All @@ -7,15 +15,17 @@
#include "ecbencryptor.h"
#include "evpwrapper.h"
#include "filewriter.h"
#include "onetimepad.h"
#include "qrngprocessor.h"

void printUsage() {
std::cout << "penguinify.exe" << std::endl;
std::cout
<< "Creates ecb encrypted bitmaps that leave the file header in tact."
<< "Creates encrypted bitmaps that leave the file header intact."
<< std::endl
<< std::endl;
std::cout << "Usage:" << std::endl;
std::cout << "penguinify --bitmap <in_file_name> --key <16-char string> "
std::cout << "penguinify --bitmap <in_file_name> --<encryption identifier> <16-char string> "
"--out <out_file_name>"
<< std::endl;
}
Expand All @@ -32,30 +42,51 @@ int main(int argc, char **argv) {
}

std::string bitmap_name{argv[2]};
std::string identifier{argv[3]};
std::string key{argv[4]};
std::string out_name{argv[6]};

if (identifier.substr(2) == "key") {

if (18 == key.size() && surroundedByQuotes(key)) {
key.erase(key.begin(), key.begin() + 1);
key.erase(key.end() - 1, key.end());
}
if (18 == key.size() && surroundedByQuotes(key)) {
key.erase(key.begin(), key.begin() + 1);
key.erase(key.end() - 1, key.end());
}

if (key.size() != 16) {
printUsage();
return 1;
if (key.size() != 16) {
printUsage();
return 1;
}

try {
bitmapreader bmrIn;
evpwrapper evp;
filewriter fwOut;
ecbencryptor ecb(evp);
bmrIn.open(bitmap_name);
fwOut.open(out_name);
ecb.encrypt(bmrIn, key, fwOut);
} catch (std::exception e) {
std::cout << e.what() << std::endl;
printUsage();
return 1;
}
}

try {
bitmapreader bmrIn;
evpwrapper evp;
filewriter fwOut;
ecbencryptor ecb(evp);
bmrIn.open(bitmap_name);
fwOut.open(out_name);
ecb.encrypt(bmrIn, key, fwOut);
} catch (std::exception e) {
std::cout << e.what() << std::endl;
printUsage();
return 1;
if (identifier.substr(2) == "otp") {
try {
bitmapreader bmrIn;
filewriter fwOut;
qrngprocessor QRNG;
otpencryptor OTP(QRNG);
bmrIn.open(bitmap_name);
fwOut.open(out_name);
OTP.otpencrypt(bmrIn, key, fwOut);
} catch (std::exception e) {
std::cout << e.what() << std::endl;
printUsage();
return 1;
}
}

}
41 changes: 41 additions & 0 deletions src/qrngprocessor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <string>

#include "qrngprocessor.h"

/*
* Function takes a name of the qrng file and a bitmap file for encryption, and returns random from qrng file of the same size as the body of the bitmap file to be encrypted
* INPUT: a bitmap file for encryption, and a name of the qrng file containing quantum randomness
* OUTPUT: vector of unsigned 8-bit integers from the qrng file, with the same size as the body of the bitmap file for encryption
*/
std::vector<uint8_t> qrngprocessor::processqrngfile(bitmapreader &in, const std::string &qrngfile) {

//get the size of the bitmap file body
std::vector<uint8_t> file_body = in.getBody();
size_t size_body = file_body.size();

//open and read the qrng file
std::ifstream file(qrngfile, std::ios::binary);
file >> std::noskipws;
if (!file.is_open()) {
throw std::runtime_error("Error opening QRNG file.");
}
std::istream_iterator<uint8_t> begin(file), end;
std::vector<uint8_t> buffer(begin, end);

//check that the qrng file is large enough to contain enough random sufficient for encryption of the bitmap body
if (buffer.size() < size_body) {
throw std::runtime_error("QRNG file is too small.");
}

//initialize the qrng vector and store the random from qrng in it, with the same size as the bitmap file body
std::vector<uint8_t> qrng;
qrng.insert(qrng.begin(), buffer.begin(), buffer.begin() + size_body);

return qrng;

}

6 changes: 5 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ target_sources(${TEST_PROJECT} PRIVATE
src/test_filewriter.cpp
../src/filewriter.cpp
src/test_evpwrapper.cpp
../src/evpwrapper.cpp)
../src/evpwrapper.cpp
src/test_otpencryptor.cpp
../src/onetimepad.cpp
../src/qrngprocessor.cpp
)

target_include_directories(${TEST_PROJECT} PRIVATE
${GTest_INCLUDE_DIRS}
Expand Down
27 changes: 27 additions & 0 deletions tests/include/mocks_otp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include "bitmapreader.h"
#include "filewriter.h"
#include "qrngprocessor.h"
#include <gmock/gmock.h>

class MockReader : public bitmapreader {
public:
MockReader() : bitmapreader() {}
MOCK_METHOD(void, open, (const std::string &bmpFile), (override));
MOCK_METHOD(const std::vector<uint8_t> &, getHeader, (), (override));
MOCK_METHOD(const std::vector<uint8_t> &, getBody, (), (override));
};

class MockWriter : public filewriter {
public:
MockWriter() : filewriter() {}
MOCK_METHOD(void, open, (const std::string &filename), (override));
MOCK_METHOD(void, write, (const std::vector<uint8_t> &bytes), (override));
};

class MockProcessor : public qrngprocessor{
public:
MockProcessor() : qrngprocessor() {}
MOCK_METHOD(std::vector<uint8_t>, processqrngfile, (bitmapreader &in, const std::string &qrngfile), (override));
};
31 changes: 31 additions & 0 deletions tests/src/test_otpencryptor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include "mocks_otp.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include "onetimepad.h"

using ::testing::InSequence;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SaveArg;

TEST(otpencryptor, encrypt) {

std::vector<uint8_t> header(0x36, 1);
std::vector<uint8_t> body(1000, 2);
std::vector<uint8_t> out1{};
std::vector<uint8_t> out2{};
MockProcessor mp;
MockReader mr;
MockWriter mw;
InSequence seq;
EXPECT_CALL(mr, getHeader()).WillOnce(ReturnRef(header));
EXPECT_CALL(mw, write).WillOnce(SaveArg<0>(&out1));
EXPECT_CALL(mp, processqrngfile).WillOnce(Return(body));
EXPECT_CALL(mr, getBody()).WillOnce(ReturnRef(body));
EXPECT_CALL(mw, write).WillOnce(SaveArg<0>(&out2));
otpencryptor otp(mp);
otp.otpencrypt(mr, "/workspaces/ecbPenguin/tests/data/quantum.qrand", mw);
EXPECT_EQ(header.size(), out1.size());
EXPECT_EQ(body.size(), out2.size());
}