Implement direct file i/o to- and from blocks.
This commit is contained in:
parent
101a1e0fd6
commit
81a9570673
@ -4,7 +4,11 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace Leonetienne::GCrypt {
|
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;
|
typedef std::string Flexblock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
#include "GCrypt/Block.h"
|
#include "GCrypt/Block.h"
|
||||||
#include "GCrypt/Flexblock.h"
|
#include "GCrypt/Flexblock.h"
|
||||||
#include "GCrypt/Config.h"
|
#include "GCrypt/Config.h"
|
||||||
@ -28,12 +29,22 @@ namespace Leonetienne::GCrypt {
|
|||||||
//! Will convert a string to a flexible data block
|
//! Will convert a string to a flexible data block
|
||||||
Flexblock StringToBits(const std::string& s);
|
Flexblock StringToBits(const std::string& s);
|
||||||
|
|
||||||
//! Will convert a fixed-size data block to a bytestring
|
//! Will convert a string to a vector of blocks
|
||||||
std::string BitblockToBytes(const Block& bits);
|
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
|
//! 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
|
//! Will convert a flexible data block to a bytestring
|
||||||
std::string BitsToBytes(const Flexblock& bits);
|
std::string BitsToBytes(const Flexblock& bits);
|
||||||
@ -59,6 +70,12 @@ namespace Leonetienne::GCrypt {
|
|||||||
|
|
||||||
//! Will save bits to a binary file
|
//! Will save bits to a binary file
|
||||||
void WriteBitsToFile(const std::string& filepath, const Flexblock& bits);
|
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
|
#endif
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef GCRYPT_VERSION_H
|
#ifndef GCRYPT_VERSION_H
|
||||||
#define GCRYPT_VERSION_H
|
#define GCRYPT_VERSION_H
|
||||||
|
|
||||||
#define GCRYPT_VERSION 0.233
|
#define GCRYPT_VERSION 0.234
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -46,14 +46,23 @@ namespace Leonetienne::GCrypt {
|
|||||||
bool printProgressReport)
|
bool printProgressReport)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// Read the file to bits
|
// Read the file to blocks
|
||||||
const Flexblock cleartext_bits = ReadFileToBits(filename_in);
|
const std::vector<Block> cleartext_blocks = ReadFileToBlocks(filename_in);
|
||||||
|
|
||||||
// Encrypt our cleartext bits
|
// Encrypt our cleartext blocks
|
||||||
const Flexblock ciphertext_bits = CipherFlexblock(cleartext_bits, key, GCipher::DIRECTION::ENCIPHER);
|
std::vector<Block> ciphertext_blocks;
|
||||||
|
ciphertext_blocks.reserve(cleartext_blocks.size());
|
||||||
|
|
||||||
// Write our ciphertext bits to file
|
// Create cipher instance
|
||||||
WriteBitsToFile(filename_out, ciphertext_bits);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@ -69,14 +78,23 @@ namespace Leonetienne::GCrypt {
|
|||||||
bool printProgressReport)
|
bool printProgressReport)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// Read the file to bits
|
// Read the file to blocks
|
||||||
const Flexblock ciphertext_bits = ReadFileToBits(filename_in);
|
const std::vector<Block> ciphertext_blocks = ReadFileToBlocks(filename_in);
|
||||||
|
|
||||||
// Decrypt the ciphertext bits
|
// Decrypt our cleartext blocks
|
||||||
const Flexblock cleartext_bits = CipherFlexblock(ciphertext_bits, key, GCipher::DIRECTION::DECIPHER);
|
std::vector<Block> cleartext_blocks;
|
||||||
|
cleartext_blocks.reserve(ciphertext_blocks.size());
|
||||||
|
|
||||||
// Write our cleartext bits to file
|
// Create cipher instance
|
||||||
WriteBitsToFile(filename_out, cleartext_bits);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -19,17 +19,14 @@ namespace Leonetienne::GCrypt {
|
|||||||
std::random_device rng;
|
std::random_device rng;
|
||||||
constexpr std::size_t bitsPerCall = sizeof(std::random_device::result_type) * 8;
|
constexpr std::size_t bitsPerCall = sizeof(std::random_device::result_type) * 8;
|
||||||
|
|
||||||
// Fetch BLOCK_SIZE bits
|
// Create a new key, and assign 16 random values
|
||||||
std::stringstream ss;
|
Key key;
|
||||||
for (std::size_t i = 0; i < Key::BLOCK_SIZE_BITS / bitsPerCall; i++) {
|
for (std::size_t i = 0; i < 16; i++) {
|
||||||
ss << std::bitset<bitsPerCall>(rng());
|
key[i] = rng();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that we actually have the correct size
|
// Return it
|
||||||
assert(ss.str().length() == Key::BLOCK_SIZE_BITS);
|
return key;
|
||||||
|
|
||||||
// Return them as a key
|
|
||||||
return Key(Block(ss.str()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Key Key::LoadFromFile(const std::string& path) {
|
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...");
|
throw std::runtime_error(std::string("Unable to open ifilestream for keyfile \"") + path + "\"! Aborting...");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read these chars to buffer
|
// Create a new key, and zero it
|
||||||
char* ckeyfileContent = new char[maxChars];
|
Key key;
|
||||||
memset(ckeyfileContent, 0, maxChars * sizeof(char));
|
key.Reset();
|
||||||
ifs.read(ckeyfileContent, maxChars);
|
|
||||||
ifs.close();
|
|
||||||
|
|
||||||
// Convert the buffer to a bit block of key size
|
// Read into it
|
||||||
std::stringstream ss;
|
ifs.read((char*)(void*)key.Data(), Key::BLOCK_SIZE);
|
||||||
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;
|
|
||||||
|
|
||||||
// Return it
|
// Return it
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Key::WriteToFile(const std::string& path) {
|
void Key::WriteToFile(const std::string& path) {
|
||||||
// Transform key to bytes
|
|
||||||
const std::string keybytes = BitsToBytes(ToString());
|
|
||||||
|
|
||||||
// Create an ofilestream
|
// Create an ofilestream
|
||||||
std::ofstream ofs(path, std::ios::out | std::ios::binary);
|
std::ofstream ofs(path, std::ios::out | std::ios::binary);
|
||||||
|
|
||||||
// Write the key
|
// Write the key
|
||||||
ofs.write(keybytes.data(), Key::BLOCK_SIZE_BITS / 8);
|
ofs.write((char*)(void*)Data(), Key::BLOCK_SIZE);
|
||||||
|
|
||||||
// Close the file handle
|
// Close the file handle
|
||||||
ofs.close();
|
ofs.close();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "GCrypt/Util.h"
|
#include "GCrypt/Util.h"
|
||||||
#include "GCrypt/GHash.h"
|
#include "GCrypt/GHash.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Leonetienne::GCrypt {
|
namespace Leonetienne::GCrypt {
|
||||||
|
|
||||||
@ -50,13 +51,22 @@ namespace Leonetienne::GCrypt {
|
|||||||
return Flexblock(ss.str());
|
return Flexblock(ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BitblockToBytes(const Block& bits) {
|
std::string BitblockToBytes(const Block& block) {
|
||||||
std::stringstream ss;
|
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) {
|
return ss.str();
|
||||||
ss << (char)std::bitset<8>(bitstring.substr(i, 8)).to_ulong();
|
}
|
||||||
|
|
||||||
|
std::string BitblocksToBytes(const std::vector<Block>& blocks) {
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
for (const Block& block : blocks) {
|
||||||
|
ss << BitblockToBytes(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ss.str();
|
return ss.str();
|
||||||
@ -72,6 +82,16 @@ namespace Leonetienne::GCrypt {
|
|||||||
return text;
|
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::string BitsToBytes(const Flexblock& bits) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
@ -211,5 +231,113 @@ namespace Leonetienne::GCrypt {
|
|||||||
|
|
||||||
return;
|
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);
|
GWrapper::DecryptFile(filename_encrypted, filename_decrypted, key);
|
||||||
|
|
||||||
// Read in both the base, and the decrypted file
|
// Read in both the base, and the decrypted file
|
||||||
const Flexblock plainfile = ReadFileToBits(filename_plain);
|
const std::vector<Block> plainfile = ReadFileToBlocks(filename_plain);
|
||||||
const Flexblock decryptfile = ReadFileToBits(filename_decrypted);
|
const std::vector<Block> decryptfile = ReadFileToBlocks(filename_decrypted);
|
||||||
|
|
||||||
// Assertion (If this fails, maybe check if the image is even readable by an image viewer)
|
// Assertion (If this fails, maybe check if the image is even readable by an image viewer)
|
||||||
REQUIRE(
|
REQUIRE(plainfile.size() == decryptfile.size());
|
||||||
PadStringToLength(plainfile, decryptfile.length(), '0', false) ==
|
REQUIRE(plainfile == decryptfile);
|
||||||
decryptfile
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user