Optimization attempt

This commit is contained in:
Michele Rodolfi 2020-12-14 12:42:18 +01:00
parent d8a050b183
commit 26533e2769
6 changed files with 179 additions and 97 deletions

View File

@ -12,28 +12,8 @@
#include "utils.h"
namespace horcrux {
/** Cipher context class
* This is a wrapper for OpenSSL context object, just for RAII pattern
*/
class EvpCipherCtx {
EVP_CIPHER_CTX *ptr;
public:
EvpCipherCtx() {
ptr = EVP_CIPHER_CTX_new();
}
~EvpCipherCtx() {
EVP_CIPHER_CTX_free(ptr);
}
EVP_CIPHER_CTX* get() {
return ptr;
}
};
EvpCipherCtx AES256_CBC::init(Mode mode, const raw_data& key,
void AES256_CBC::init(Mode mode, const raw_data& key,
const raw_data& iv) {
if (key.size() != kKeySize) {
throw std::runtime_error("Wrong key size");
@ -42,24 +22,54 @@ EvpCipherCtx AES256_CBC::init(Mode mode, const raw_data& key,
throw std::runtime_error("Wrong IV size");
}
EvpCipherCtx ctx;
ctx = std::make_unique<EvpCipherCtx>();
if (mode == Mode::kEncrypt) {
if (EVP_EncryptInit(ctx.get(), EVP_aes_256_cbc(),
if (EVP_EncryptInit(ctx->get(), EVP_aes_256_cbc(),
key.data(), iv.data()) == 0) {
throw std::runtime_error("EVP_EncryptInit");
}
} else if (mode == Mode::kDecrypt) {
if (EVP_DecryptInit(ctx.get(), EVP_aes_256_cbc(),
if (EVP_DecryptInit(ctx->get(), EVP_aes_256_cbc(),
key.data(), iv.data()) == 0) {
throw std::runtime_error("EVP_DecryptInit");
}
} else {
throw std::invalid_argument("Invalid Cipher mode");
}
return ctx;
}
size_t AES256_CBC::process_chunk(Mode mode, EvpCipherCtx& ctx,
size_t AES256_CBC::process_start(Mode mode,
raw_data::const_iterator begin,
raw_data::const_iterator end,
raw_data& output,
bool resize_in, bool resize_out) {
auto current = begin;
size_t output_offset = 0;
raw_data iv(kIvSize);
if (mode == Mode::kEncrypt) {
// Make sure output is large enough to add IV
output.resize(kIvSize);
// generate IV
iv = generate_random(kIvSize);
// write IV to output
std::copy(iv.begin(), iv.end(), output.begin());
output_offset += kIvSize;
} else {
// kDecrypt
if (end - begin < kIvSize){
throw std::invalid_argument(
"First encrypted chunk must contain the whole IV");
}
// read IV from input
std::copy(current, current + kIvSize, iv.begin());
current += kIvSize;
}
init(mode, encryption_key, iv);
return output_offset + process_chunk(mode, current, end, output, output_offset, resize_in, resize_out);
}
size_t AES256_CBC::process_chunk(Mode mode,
raw_data::const_iterator begin,
raw_data::const_iterator end,
raw_data& output, size_t output_offset,
@ -71,13 +81,13 @@ size_t AES256_CBC::process_chunk(Mode mode, EvpCipherCtx& ctx,
output.resize(output_offset + chunk_size + kIvSize);
}
if (mode == Mode::kEncrypt) {
if (1 != EVP_EncryptUpdate(ctx.get(),
if (1 != EVP_EncryptUpdate(ctx->get(),
output.data() + output_offset, &len,
&*begin, chunk_size)) {
throw std::runtime_error("EVP_EncryptUpdate");
}
} else {
if (1 != EVP_DecryptUpdate(ctx.get(),
if (1 != EVP_DecryptUpdate(ctx->get(),
output.data() + output_offset, &len,
&*begin, chunk_size)) {
throw std::runtime_error("EVP_DecryptUpdate");
@ -88,7 +98,7 @@ size_t AES256_CBC::process_chunk(Mode mode, EvpCipherCtx& ctx,
return len;
}
size_t AES256_CBC::process_final(Mode mode, EvpCipherCtx& ctx,
size_t AES256_CBC::process_final(Mode mode,
raw_data& output, size_t output_offset,
bool resize_in, bool resize_out) {
int len;
@ -97,12 +107,12 @@ size_t AES256_CBC::process_final(Mode mode, EvpCipherCtx& ctx,
output.resize(output_offset + kIvSize);
}
if (mode == Mode::kEncrypt) {
if (1 != EVP_EncryptFinal_ex(ctx.get(), output.data() + output_offset,
if (1 != EVP_EncryptFinal_ex(ctx->get(), output.data() + output_offset,
&len)) {
throw std::runtime_error("EVP_EncryptFinal");
}
} else {
if (1 != EVP_DecryptFinal_ex(ctx.get(), (output.data()) + output_offset,
if (1 != EVP_DecryptFinal_ex(ctx->get(), (output.data()) + output_offset,
&len)) {
throw std::runtime_error("EVP_DecryptFinal");
}
@ -112,62 +122,78 @@ size_t AES256_CBC::process_final(Mode mode, EvpCipherCtx& ctx,
return len;
}
size_t AES256_CBC::process_all(Mode mode, EvpCipherCtx& ctx,
size_t AES256_CBC::process_all(Mode mode,
raw_data::const_iterator begin,
raw_data::const_iterator end,
raw_data& output, size_t output_offset,
bool resize_in, bool resize_out) {
int len = process_chunk(mode, ctx, begin, end, output, output_offset,
int len = process_chunk(mode, begin, end, output, output_offset,
resize_in, false);
len += process_final(mode, ctx, output, output_offset + len, false,
len += process_final(mode, output, output_offset + len, false,
resize_out);
return len;
}
raw_data AES256_CBC::process(const raw_data& inputdata) {
if (mode == Mode::kEncrypt)
return encrypt(inputdata);
raw_data AES256_CBC::process(const raw_data& inputdata, bool begin, bool end) {
//if (mode == Mode::kEncrypt)
// return encrypt(inputdata, begin, end);
//else
// return decrypt(inputdata, begin, end);
raw_data output;
size_t len;
if (begin)
len = process_start(mode, inputdata.begin(), inputdata.end(), output);
else
return decrypt(inputdata);
len = process_chunk(mode, inputdata.begin(), inputdata.end(), output, 0);
if (end)
process_final(mode, output, len);
return output;
}
raw_data AES256_CBC::encrypt(const raw_data& plaintext) {
return encrypt(encryption_key, horcrux::generate_random(kIvSize),
plaintext);
raw_data AES256_CBC::encrypt(const raw_data& plaintext, bool begin, bool end) {
//return encrypt(encryption_key, horcrux::generate_random(kIvSize),
// plaintext);
raw_data output;
size_t len;
if (begin)
len = process_start(Mode::kEncrypt, plaintext.begin(), plaintext.end(), output);
else
len = process_chunk(Mode::kEncrypt, plaintext.begin(), plaintext.end(), output, 0);
if (end)
process_final(Mode::kEncrypt, output, len);
return output;
}
raw_data AES256_CBC::decrypt(const raw_data& ciphertext) {
return decrypt(encryption_key, ciphertext);
raw_data AES256_CBC::decrypt(const raw_data& ciphertext, bool begin, bool end) {
//return decrypt(encryption_key, ciphertext);
//raw_data output;
//init(Mode::kDecrypt, key, iv);
//process_all(Mode::kDecrypt, begin, end, output, 0);
//auto len = process_start(Mode::kDecrypt, ciphertext.begin(), ciphertext.end(), output);
//process_final(Mode::kDecrypt, output, len);
//return output;
raw_data output;
size_t len;
if (begin)
len = process_start(Mode::kDecrypt, ciphertext.begin(), ciphertext.end(), output);
else
len = process_chunk(Mode::kDecrypt, ciphertext.begin(), ciphertext.end(), output, 0);
if (end)
process_final(Mode::kDecrypt, output, len);
return output;
}
raw_data AES256_CBC::encrypt(const raw_data& key, const raw_data& iv,
const raw_data& input) {
auto ctx = init(Mode::kEncrypt, key, iv);
init(Mode::kEncrypt, key, iv);
// Make sure ouput is large enough to contain IV + encrypted data + padding
raw_data output(input.size() + (2 * kIvSize));
std::copy(iv.begin(), iv.end(), output.begin());
process_all(Mode::kEncrypt, ctx, input.begin(), input.end(), output,
process_all(Mode::kEncrypt, input.begin(), input.end(), output,
kIvSize);
return output;
}
/*
int AES256_CBC::encrypt(const raw_data& key, const raw_data& iv,
std::istream input, const size_t input_len,
std::ostream output, size_t& output_len) {
auto inbuf = raw_data(input_len);
auto outbuf = raw_data(input_len + 16);
input.read(reinterpret_cast<char*>(inbuf.data()), input_len);
encrypt(key, iv, inbuf, outbuf);
output.write(reinterpret_cast<char*>(outbuf.data()), outbuf.size());
return 0;
}
*/
raw_data AES256_CBC::decrypt(const raw_data& key, const raw_data& ciphertext) {
raw_data iv(ciphertext.begin(), ciphertext.begin() + kIvSize);
return decrypt(key, iv, ciphertext.begin() + kIvSize, ciphertext.end());
@ -181,9 +207,9 @@ raw_data AES256_CBC::decrypt(const raw_data& key, const raw_data& iv,
raw_data AES256_CBC::decrypt(const raw_data& key, const raw_data& iv,
raw_data::const_iterator begin,
raw_data::const_iterator end) {
auto ctx = init(Mode::kDecrypt, key, iv);
raw_data output;
process_all(Mode::kDecrypt, ctx, begin, end, output, 0);
init(Mode::kDecrypt, key, iv);
process_all(Mode::kDecrypt, begin, end, output, 0);
return output;
}
}; // namespace horcrux

View File

@ -1,5 +1,6 @@
#ifndef HORCRUX_SRC_CRYPTO_H
#define HORCRUX_SRC_CRYPTO_H
#include <openssl/evp.h>
#include <vector>
#include <string>
#include <cstddef>
@ -48,28 +49,51 @@ public:
/** @brief process inputdata according to Cipher mode
* @param inputdata buffer containing data to process
* @param begin set to true if this is the first chunk of data to process.
* @param end set to true if this is the last chunk of data to process.
* @return processed data
*/
virtual raw_data process(
const raw_data& inputdata) = 0;
const raw_data& inputdata, bool begin = true, bool end = true) = 0;
/** @brief encrypt the content of a buffer using Cipher key
* @param plaintext the input buffer
* @param begin set to true if this is the first chunk of data to process.
* @param end set to true if this is the last chunk of data to process.
* @return encrypted data
*/
virtual raw_data encrypt(
const raw_data& plaintext) = 0;
const raw_data& plaintext, bool begin = true, bool end = true) = 0;
/** @brief decrypt the content of a buffer using Cipher key
* @param decrypt the input buffer
* @param begin set to true if this is the first chunk of data to process.
* @param end set to true if this is the last chunk of data to process.
* @return decrypted data
*/
virtual raw_data decrypt(
const raw_data& ciphertext) = 0;
const raw_data& ciphertext, bool begin = true, bool end = true) = 0;
};
// forward declaration
class EvpCipherCtx;
/** Cipher context class
* This is a wrapper for OpenSSL context object, just for RAII pattern
*/
class EvpCipherCtx {
EVP_CIPHER_CTX *ptr;
public:
EvpCipherCtx() {
ptr = EVP_CIPHER_CTX_new();
}
~EvpCipherCtx() {
EVP_CIPHER_CTX_free(ptr);
}
EVP_CIPHER_CTX* get() {
return ptr;
}
};
/** @brief AES256 Cipher
* @details
@ -90,6 +114,8 @@ class AES256_CBC : public Cipher {
/** AES256 CBC initialization vector size, from AES256 specs */
const size_t kIvSize = 16;
std::unique_ptr<EvpCipherCtx> ctx = nullptr;
public:
/** @brief Create cipher in encrypt mode with random key */
AES256_CBC() : Cipher(Mode::kEncrypt) {
@ -108,24 +134,36 @@ public:
/** @brief process inputdata according to Cipher mode
* @param inputdata buffer containing data to process
* @param begin set to true if this is the first chunk of data to process.
* (deals with IV)
* @param end set to true if this is the last chunk of data to process.
* (deals with padding)
* @return processed data
*/
raw_data process(
const raw_data& inputdata) override;
const raw_data& inputdata, bool begin = true, bool end = true) override;
/** @brief encrypt the content of a buffer using Cipher key
* @param plaintext the input buffer
* @param begin set to true if this is the first chunk of data to process.
* (deals with IV)
* @param end set to true if this is the last chunk of data to process.
* (deals with padding)
* @return encrypted data
*/
raw_data encrypt(
const raw_data& plaintext) override;
const raw_data& plaintext, bool begin = true, bool end = true) override;
/** @brief decrypt the content of a buffer using Cipher key
* @param decrypt the input buffer
* @param begin set to true if this is the first chunk of data to process.
* (deals with IV)
* @param end set to true if this is the last chunk of data to process.
* (deals with padding)
* @return decrypted data
*/
raw_data decrypt(
const raw_data& ciphertext) override;
const raw_data& ciphertext, bool begin = true, bool end = true) override;
/** @brief Read the whole input and return a buffer with the encrypted data
* @param key the encryption key
@ -182,18 +220,16 @@ public:
raw_data::const_iterator end);
private:
/** @brief Create a Cipher context object needed for enc/dec operations
/** @brief Initialize the Cipher context object needed for enc/dec operations
* @param mode encrypt or decrypt
* @param key the enc/dec key
* @param the initialization vector
* @return the Cipher context object
*/
EvpCipherCtx init(Mode mode, const raw_data& key,
void init(Mode mode, const raw_data& key,
const raw_data& iv);
/** @brief process a chunk of input
* @param mode encrypt or decrypt
* @param ctx the Cipher context object
* @param begin beginning of the input chunk
* @param end end of the input chunk
* @param output output buffer
@ -203,17 +239,21 @@ private:
* order to make sure there is enough room.
* @param resize_out shrink the output buffer at the end
*/
size_t process_chunk(Mode mode, EvpCipherCtx& ctx,
size_t process_chunk(Mode mode,
raw_data::const_iterator begin,
raw_data::const_iterator end,
raw_data& output,
size_t output_offset,
bool resize_in = true, bool resize_out = true);
size_t process_start(Mode mode,
raw_data::const_iterator begin,
raw_data::const_iterator end,
raw_data& output,
bool resize_in = true, bool resize_out = true);
/** @brief Finalize a encryption/decryption process. Write remaining bytes
* to the output
* @param mode encrypt or decrypt
* @param ctx the Cipher context object
* @param output output buffer
* @param output_offset where to start to append the last data within
* the output buffer
@ -221,14 +261,13 @@ private:
* order to make sure there is enough room.
* @param resize_out shrink the output buffer at the end
*/
size_t process_final(Mode mode, EvpCipherCtx& ctx,
size_t process_final(Mode mode,
raw_data& output,
size_t output_offset,
bool resize_in = true, bool resize_out = true);
/** @brief process a input and finalize it.
* @param mode encrypt or decrypt
* @param ctx the Cipher context object
* @param begin beginning of the input chunk
* @param end end of the input chunk
* @param output output buffer
@ -238,7 +277,7 @@ private:
* order to make sure there is enough room.
* @param resize_out shrink the output buffer at the end
*/
size_t process_all(Mode mode, EvpCipherCtx& ctx,
size_t process_all(Mode mode,
raw_data::const_iterator begin,
raw_data::const_iterator end,
raw_data& output, size_t output_offset,

View File

@ -5,6 +5,15 @@
namespace horcrux {
void Horcrux::read_chunks(size_t size) {
raw_data buf = input->read(size);
output->write_chunk(cipher->process(buf, true, false));
while ((buf = input->read(size)).size() == size) {
output->write_chunk(cipher->process(buf, false, false));
}
output->write_chunk(cipher->process(buf, false, true));
}
void Horcrux::init() {
if (options.count <= 0) {
throw std::invalid_argument("Invalid horcrux count");
@ -32,7 +41,8 @@ void Horcrux::init() {
void Horcrux::run() {
if (options.mode == Mode::kEncrypt)
output->write(cipher->encrypt(input->read()));
//output->write(cipher->encrypt(input->read()));
read_chunks(512);
else
output->write(cipher->decrypt(input->read()));
}

View File

@ -38,6 +38,7 @@ class Horcrux {
* Used cipher, input and output are generic and the actual implementation
* can be selected in this function */
void init();
void read_chunks(size_t size);
public:
/** remove default constructor */

View File

@ -68,7 +68,9 @@ raw_data FsCryptoInput::read(size_t size) {
while (data_read < size && current_file < file_paths.size()) {
if (!file_stream.is_open())
file_stream.open(file_paths[current_file].first, std::ios::binary);
file_stream.read(reinterpret_cast<char*>(result.data()) + data_read, size - data_read);
file_stream.read(
reinterpret_cast<char*>(result.data()) + data_read,
size - data_read);
data_read += file_stream.gcount();
if(file_stream.eof()) {
file_stream.close();
@ -93,7 +95,8 @@ size_t FsPlainOutput::write(const raw_data& to_write) {
}
size_t FsPlainOutput::write_chunk(const raw_data& to_write) {
file_stream.open(file_path, std::ios::binary);
file_stream.write(reinterpret_cast<const char*>(to_write.data()), to_write.size());
file_stream.write(reinterpret_cast<const char*>(to_write.data()),
to_write.size());
return to_write.size();
}
@ -129,29 +132,37 @@ size_t FsCryptoOutput::write(const raw_data& to_write) {
// }
// return data_written;
}
size_t FsCryptoOutput::write_chunk(const raw_data& to_write) {
// horcrux file size must be known in advance
if (size <= 0) {
throw std::invalid_argument("Invalid horcrux size");
}
size_t data_written{0};
std::cout << to_write.size() << " " << data_written << std::endl;
while (data_written < to_write.size()){
if(!file_stream.is_open()){
std::string name = base_name + '.' + std::to_string(created_files.size()) + ".enc";
// create a new file
std::string name = base_name +
'.' + std::to_string(created_files.size()) + ".enc";
created_files.emplace_back(folder_path / name);
file_stream.open(created_files.back(),
std::ios::binary | std::ios::trunc);
}
// write up to fill the file size or consume the input buffer
auto writable = std::min(size - file_stream.tellp(),
to_write.size() - data_written);
// the last file may contain more data (size % num)
auto writable = std::min(size - file_stream.tellp(), to_write.size() - data_written);
if (num == created_files.size()) { writable = to_write.size() - data_written; }
file_stream.write(reinterpret_cast<const char*>(to_write.data()) + data_written,
if (num == created_files.size()) {
writable = to_write.size() - data_written;
}
file_stream.write(
reinterpret_cast<const char*>(to_write.data()) + data_written,
writable);
data_written += writable;
if (file_stream.tellp() >= size){
// close the file if it has been filled
file_stream.close();
}
std::cout << to_write.size() << " " << data_written << std::endl;
}
return data_written;
}

View File

@ -19,12 +19,7 @@ public:
virtual raw_data read() = 0;
virtual raw_data read(size_t size) = 0;
// template<class UnaryFunction>
// void read_chunks(size_t size, UnaryFunction f) {
// while ((auto buf = read(size)) == size) {
// f(buf);
// }
// }
};
/** @brief Base Class for Output