Implement direct file i/o to- and from blocks.
This commit is contained in:
parent
101a1e0fd6
commit
81a9570673
@ -4,7 +4,11 @@
|
||||
#include <string>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
//! A "bitset" of variable length
|
||||
//! A type used for conveying "bitstrings". e.g. "10101001001"
|
||||
//! These should generally not be used, as they are really really slow.
|
||||
//! The only valid usecase I can think of is when using GHash for example, because for hashing
|
||||
//! an absolute input length is required.
|
||||
//! If you need to, you can use the StringToBits() and BitsToString() functions defined in Util.h.
|
||||
typedef std::string Flexblock;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "GCrypt/Block.h"
|
||||
#include "GCrypt/Flexblock.h"
|
||||
#include "GCrypt/Config.h"
|
||||
@ -28,12 +29,22 @@ namespace Leonetienne::GCrypt {
|
||||
//! Will convert a string to a flexible data block
|
||||
Flexblock StringToBits(const std::string& s);
|
||||
|
||||
//! Will convert a fixed-size data block to a bytestring
|
||||
std::string BitblockToBytes(const Block& bits);
|
||||
//! Will convert a string to a vector of blocks
|
||||
std::vector<Block> StringToBitblocks(const std::string& s);
|
||||
|
||||
//! Will convert a fixed-size data block to a string
|
||||
//! Will convert a fixed-size data block to a bytestring
|
||||
std::string BitblockToBytes(const Block& block);
|
||||
|
||||
//! Will convert an array of data blocks to a bytestring
|
||||
std::string BitblocksToBytes(const std::vector<Block>& bits);
|
||||
|
||||
//! Will convert a fixed-size data blocks to a textstring
|
||||
//! The difference to BitblockToBytes() is, that it strips excess nullbytes
|
||||
std::string BitblockToString(const Block& bits);
|
||||
std::string BitblockToString(const Block& block);
|
||||
|
||||
//! Will convert an array of blocks to a character-string
|
||||
//! The difference to BitblocksToBytes() is, that it strips excess nullbytes
|
||||
std::string BitblocksToString(const std::vector<Block>& blocks);
|
||||
|
||||
//! Will convert a flexible data block to a bytestring
|
||||
std::string BitsToBytes(const Flexblock& bits);
|
||||
@ -59,6 +70,12 @@ namespace Leonetienne::GCrypt {
|
||||
|
||||
//! Will save bits to a binary file
|
||||
void WriteBitsToFile(const std::string& filepath, const Flexblock& bits);
|
||||
|
||||
//! Will read a file directly to data blocks
|
||||
std::vector<Block> ReadFileToBlocks(const std::string& filepath);
|
||||
|
||||
//! Will write data blocks directly to a file
|
||||
void WriteBlocksToFile(const std::string& filepath, const std::vector<Block>& blocks);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef GCRYPT_VERSION_H
|
||||
#define GCRYPT_VERSION_H
|
||||
|
||||
#define GCRYPT_VERSION 0.233
|
||||
#define GCRYPT_VERSION 0.234
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -46,14 +46,23 @@ namespace Leonetienne::GCrypt {
|
||||
bool printProgressReport)
|
||||
{
|
||||
try {
|
||||
// Read the file to bits
|
||||
const Flexblock cleartext_bits = ReadFileToBits(filename_in);
|
||||
// Read the file to blocks
|
||||
const std::vector<Block> cleartext_blocks = ReadFileToBlocks(filename_in);
|
||||
|
||||
// Encrypt our cleartext bits
|
||||
const Flexblock ciphertext_bits = CipherFlexblock(cleartext_bits, key, GCipher::DIRECTION::ENCIPHER);
|
||||
// Encrypt our cleartext blocks
|
||||
std::vector<Block> ciphertext_blocks;
|
||||
ciphertext_blocks.reserve(cleartext_blocks.size());
|
||||
|
||||
// Write our ciphertext bits to file
|
||||
WriteBitsToFile(filename_out, ciphertext_bits);
|
||||
// Create cipher instance
|
||||
GCipher cipher(key, GCipher::DIRECTION::ENCIPHER);
|
||||
|
||||
// Encrypt all blocks
|
||||
for (const Block& clearBlock : cleartext_blocks) {
|
||||
ciphertext_blocks.emplace_back(cipher.Digest(clearBlock));
|
||||
}
|
||||
|
||||
// Write our ciphertext blocks to file
|
||||
WriteBlocksToFile(filename_out, ciphertext_blocks);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -69,14 +78,23 @@ namespace Leonetienne::GCrypt {
|
||||
bool printProgressReport)
|
||||
{
|
||||
try {
|
||||
// Read the file to bits
|
||||
const Flexblock ciphertext_bits = ReadFileToBits(filename_in);
|
||||
// Read the file to blocks
|
||||
const std::vector<Block> ciphertext_blocks = ReadFileToBlocks(filename_in);
|
||||
|
||||
// Decrypt the ciphertext bits
|
||||
const Flexblock cleartext_bits = CipherFlexblock(ciphertext_bits, key, GCipher::DIRECTION::DECIPHER);
|
||||
// Decrypt our cleartext blocks
|
||||
std::vector<Block> cleartext_blocks;
|
||||
cleartext_blocks.reserve(ciphertext_blocks.size());
|
||||
|
||||
// Write our cleartext bits to file
|
||||
WriteBitsToFile(filename_out, cleartext_bits);
|
||||
// Create cipher instance
|
||||
GCipher cipher(key, GCipher::DIRECTION::DECIPHER);
|
||||
|
||||
// Decrypt all blocks
|
||||
for (const Block& cipherBlock : ciphertext_blocks) {
|
||||
cleartext_blocks.emplace_back(cipher.Digest(cipherBlock));
|
||||
}
|
||||
|
||||
// Write our cleartext blocks to file
|
||||
WriteBlocksToFile(filename_out, cleartext_blocks);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -19,17 +19,14 @@ namespace Leonetienne::GCrypt {
|
||||
std::random_device rng;
|
||||
constexpr std::size_t bitsPerCall = sizeof(std::random_device::result_type) * 8;
|
||||
|
||||
// Fetch BLOCK_SIZE bits
|
||||
std::stringstream ss;
|
||||
for (std::size_t i = 0; i < Key::BLOCK_SIZE_BITS / bitsPerCall; i++) {
|
||||
ss << std::bitset<bitsPerCall>(rng());
|
||||
// Create a new key, and assign 16 random values
|
||||
Key key;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
key[i] = rng();
|
||||
}
|
||||
|
||||
// Verify that we actually have the correct size
|
||||
assert(ss.str().length() == Key::BLOCK_SIZE_BITS);
|
||||
|
||||
// Return them as a key
|
||||
return Key(Block(ss.str()));
|
||||
// Return it
|
||||
return key;
|
||||
}
|
||||
|
||||
Key Key::LoadFromFile(const std::string& path) {
|
||||
@ -44,36 +41,23 @@ namespace Leonetienne::GCrypt {
|
||||
throw std::runtime_error(std::string("Unable to open ifilestream for keyfile \"") + path + "\"! Aborting...");
|
||||
}
|
||||
|
||||
// Read these chars to buffer
|
||||
char* ckeyfileContent = new char[maxChars];
|
||||
memset(ckeyfileContent, 0, maxChars * sizeof(char));
|
||||
ifs.read(ckeyfileContent, maxChars);
|
||||
ifs.close();
|
||||
// Create a new key, and zero it
|
||||
Key key;
|
||||
key.Reset();
|
||||
|
||||
// Convert the buffer to a bit block of key size
|
||||
std::stringstream ss;
|
||||
for (std::size_t i = 0; i < maxChars; i++)
|
||||
ss << std::bitset<8>(ckeyfileContent[i]);
|
||||
|
||||
Block key(ss.str());
|
||||
|
||||
// And delete the buffer
|
||||
delete[] ckeyfileContent;
|
||||
ckeyfileContent = nullptr;
|
||||
// Read into it
|
||||
ifs.read((char*)(void*)key.Data(), Key::BLOCK_SIZE);
|
||||
|
||||
// Return it
|
||||
return key;
|
||||
}
|
||||
|
||||
void Key::WriteToFile(const std::string& path) {
|
||||
// Transform key to bytes
|
||||
const std::string keybytes = BitsToBytes(ToString());
|
||||
|
||||
// Create an ofilestream
|
||||
std::ofstream ofs(path, std::ios::out | std::ios::binary);
|
||||
|
||||
// Write the key
|
||||
ofs.write(keybytes.data(), Key::BLOCK_SIZE_BITS / 8);
|
||||
ofs.write((char*)(void*)Data(), Key::BLOCK_SIZE);
|
||||
|
||||
// Close the file handle
|
||||
ofs.close();
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "GCrypt/Util.h"
|
||||
#include "GCrypt/GHash.h"
|
||||
#include <vector>
|
||||
|
||||
namespace Leonetienne::GCrypt {
|
||||
|
||||
@ -50,13 +51,22 @@ namespace Leonetienne::GCrypt {
|
||||
return Flexblock(ss.str());
|
||||
}
|
||||
|
||||
std::string BitblockToBytes(const Block& bits) {
|
||||
std::string BitblockToBytes(const Block& block) {
|
||||
std::stringstream ss;
|
||||
|
||||
const std::string bitstring = bits.ToString();
|
||||
std::uint8_t* curByte = (std::uint8_t*)(void*)block.Data();
|
||||
for (std::size_t j = 0; j < Block::BLOCK_SIZE; j++) {
|
||||
ss << *curByte++;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < Block::BLOCK_SIZE_BITS; i += 8) {
|
||||
ss << (char)std::bitset<8>(bitstring.substr(i, 8)).to_ulong();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string BitblocksToBytes(const std::vector<Block>& blocks) {
|
||||
std::stringstream ss;
|
||||
|
||||
for (const Block& block : blocks) {
|
||||
ss << BitblockToBytes(block);
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
@ -72,6 +82,16 @@ namespace Leonetienne::GCrypt {
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string BitblocksToString(const std::vector<Block>& blocks) {
|
||||
// Decode to bytes
|
||||
std::string text = BitblocksToBytes(blocks);
|
||||
|
||||
// Dümp excess nullbytes
|
||||
text.resize(strlen(text.data()));
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string BitsToBytes(const Flexblock& bits) {
|
||||
std::stringstream ss;
|
||||
|
||||
@ -211,5 +231,113 @@ namespace Leonetienne::GCrypt {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Block> ReadFileToBlocks(const std::string& filepath) {
|
||||
// Read file
|
||||
|
||||
// "ate" specifies that the read-pointer is already at the end of the file
|
||||
// this allows to estimate the file size
|
||||
std::ifstream ifs(filepath, std::ios::binary | std::ios::ate);
|
||||
|
||||
if (!ifs.good()) {
|
||||
throw std::runtime_error("Unable to open ifilestream!");
|
||||
}
|
||||
|
||||
// Create our vector of blocks, and resorve a good guess
|
||||
// of memory
|
||||
std::vector<Block> blocks;
|
||||
blocks.reserve((ifs.tellg() / Block::BLOCK_SIZE) + 1);
|
||||
|
||||
// Move read head to the file beginning
|
||||
ifs.seekg(std::ios_base::beg);
|
||||
|
||||
// Whilst not reached eof, read into blocks
|
||||
while (!ifs.eof()) {
|
||||
// Create a new block, and zero it
|
||||
Block block;
|
||||
block.Reset();
|
||||
|
||||
// Read data into the block
|
||||
ifs.read((char*)(void*)block.Data(), Block::BLOCK_SIZE);
|
||||
const std::size_t n_bytes_read = ifs.gcount();
|
||||
|
||||
if (n_bytes_read > 0) {
|
||||
// Append the block to our vector
|
||||
blocks.emplace_back(block);
|
||||
}
|
||||
}
|
||||
|
||||
// Close the filehandle
|
||||
ifs.close();
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
void WriteBlocksToFile(
|
||||
const std::string& filepath,
|
||||
const std::vector<Block>& blocks
|
||||
){
|
||||
|
||||
// Create outfile file handle
|
||||
std::ofstream ofs(filepath, std::ios::binary);
|
||||
|
||||
if (!ofs.good()) {
|
||||
throw std::runtime_error("Unable to open ofilestream!");
|
||||
}
|
||||
|
||||
// Write all the blocks
|
||||
for (const Block& block : blocks) {
|
||||
ofs.write((char*)(void*)block.Data(), Block::BLOCK_SIZE);
|
||||
}
|
||||
|
||||
// Close the filehandle
|
||||
ofs.close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Block> StringToBitblocks(const std::string& s) {
|
||||
|
||||
// Create our block vector, and reserve exactly
|
||||
// how many blocks are required to store this string
|
||||
const std::size_t num_blocks = (s.length() / Block::BLOCK_SIZE) + 1;
|
||||
std::vector<Block> blocks;
|
||||
blocks.reserve(num_blocks);
|
||||
|
||||
for (std::size_t i = 0; i < num_blocks; i++) {
|
||||
// Create new block, and zero it
|
||||
Block block;
|
||||
block.Reset();
|
||||
|
||||
std::size_t bytes_copied = 0;
|
||||
|
||||
// Iterate over all bytes in the block
|
||||
std::uint8_t* curByte = (std::uint8_t*)(void*)block.Data();
|
||||
for (std::size_t j = 0; j < Block::BLOCK_SIZE; j++) {
|
||||
curByte++;
|
||||
|
||||
// Carry our character over
|
||||
|
||||
const std::size_t strIdx = i*Block::BLOCK_SIZE + j;
|
||||
// The string still has chars to give
|
||||
if (strIdx < s.length()) {
|
||||
*curByte = s[j];
|
||||
bytes_copied++;
|
||||
}
|
||||
// We've reached the end of the string
|
||||
else {
|
||||
// Save our block, if it contains any bytes
|
||||
if (bytes_copied) {
|
||||
blocks.emplace_back(block);
|
||||
}
|
||||
|
||||
// Return our blocks
|
||||
return blocks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,13 +46,11 @@ TEST_CASE(__FILE__"/Encrypting and decrypting files works", "[Wrapper]") {
|
||||
GWrapper::DecryptFile(filename_encrypted, filename_decrypted, key);
|
||||
|
||||
// Read in both the base, and the decrypted file
|
||||
const Flexblock plainfile = ReadFileToBits(filename_plain);
|
||||
const Flexblock decryptfile = ReadFileToBits(filename_decrypted);
|
||||
const std::vector<Block> plainfile = ReadFileToBlocks(filename_plain);
|
||||
const std::vector<Block> decryptfile = ReadFileToBlocks(filename_decrypted);
|
||||
|
||||
// Assertion (If this fails, maybe check if the image is even readable by an image viewer)
|
||||
REQUIRE(
|
||||
PadStringToLength(plainfile, decryptfile.length(), '0', false) ==
|
||||
decryptfile
|
||||
);
|
||||
REQUIRE(plainfile.size() == decryptfile.size());
|
||||
REQUIRE(plainfile == decryptfile);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user