Many methods now using vectors of blocks instead of flexblocks

This commit is contained in:
Leonetienne
2022-05-26 15:04:39 +02:00
parent 800140bafa
commit 143ec19bf3
11 changed files with 292 additions and 87 deletions

View File

@@ -4,6 +4,8 @@
#include <sstream>
#include <cassert>
#include <cstring>
#include <iomanip>
#include <ios>
// Just to be sure, the compiler will optimize this
// little formula out, let's do it in the preprocessor
@@ -17,7 +19,7 @@ namespace Leonetienne::GCrypt {
template <typename T>
Basic_Block<T>::Basic_Block(const std::string& str) {
FromString(str);
FromBinaryString(str);
}
template <typename T>
@@ -26,9 +28,13 @@ namespace Leonetienne::GCrypt {
}
template <typename T>
void Basic_Block<T>::FromString(const std::string& str) {
void Basic_Block<T>::FromBinaryString(const std::string& str) {
assert(str.length() == BLOCK_SIZE_BITS);
if (str.length() != BLOCK_SIZE_BITS) {
throw std::invalid_argument(
std::string("Unable to read binary block: \"") + str + "\": Length is not BLOCK_SIZE_BITS."
);
}
for (std::size_t i = 0; i < data.size(); i++) {
data[i] = std::bitset<CHUNK_SIZE_BITS>(
@@ -40,7 +46,58 @@ namespace Leonetienne::GCrypt {
}
template <typename T>
std::string Basic_Block<T>::ToString() const {
void Basic_Block<T>::FromHexString(const std::string& str) {
if (str.length() != BLOCK_SIZE*2) {
throw std::invalid_argument(
std::string("Unable to read hex block: \"") + str + "\": Length is not BLOCK_SIZE*2."
);
}
for (std::size_t i = 0; i < str.length(); i += CHUNK_SIZE*2) {
const std::string hexChunk = str.substr(i, CHUNK_SIZE*2);
try {
data[i / (CHUNK_SIZE*2)] = std::stoul(hexChunk, NULL, 16);
}
catch (std::invalid_argument&) {
throw std::invalid_argument(
std::string("Unable to read hex block: \"") + hexChunk + "\"."
);
}
}
return;
}
template <typename T>
void Basic_Block<T>::FromByteString(const std::string& str) {
if (str.length() != BLOCK_SIZE) {
throw std::invalid_argument(
std::string("Unable to read byte block: \"") + str + "\": Length is not BLOCK_SIZE."
);
}
// Iterate over all bytes in the block
std::uint8_t* curByte = (std::uint8_t*)(void*)Data();
const char* strIt = 0;
for (std::size_t i = 0; i < BLOCK_SIZE; i++) {
*curByte++ = str[i];
}
return;
}
template <typename T>
void Basic_Block<T>::FromTextString(const std::string& str) {
// Just pad the input string to lenght, and treat it as a byte string
FromByteString(
PadStringToLength(str, BLOCK_SIZE, '\0', false)
);
}
template <typename T>
std::string Basic_Block<T>::ToBinaryString() const {
std::stringstream ss;
for (std::size_t i = 0; i < data.size(); i++) {
@@ -49,6 +106,41 @@ namespace Leonetienne::GCrypt {
return ss.str();
}
template <typename T>
std::string Basic_Block<T>::ToHexString() const {
std::stringstream ss;
for (std::size_t i = 0; i < data.size(); i++) {
ss
<< std::setfill('0')
<< std::setw(CHUNK_SIZE*2)
<< std::hex
<< data[i]
;
}
return ss.str();
}
template <typename T>
std::string Basic_Block<T>::ToByteString() const {
std::stringstream ss;
ss.write((const char*)(void*)Data(), BLOCK_SIZE);
return ss.str();
}
template <typename T>
std::string Basic_Block<T>::ToTextString() const {
std::string bytes = ToByteString();
// Trim extra nullterminators
bytes.resize(strlen(bytes.data()));
return bytes;
}
template <typename T>
Basic_Block<T> Basic_Block<T>::MMul(const Basic_Block<T>& o) const {

View File

@@ -31,34 +31,55 @@ namespace Leonetienne::GCrypt {
return block;
}
Block GHash::CalculateHashsum(const Flexblock& data) {
// Split input into blocks
std::vector<Block> blocks;
Block GHash::CalculateHashsum(const std::vector<Block>& data, std::size_t n_bytes) {
for (std::size_t i = 0; i < data.size(); i += Block::BLOCK_SIZE_BITS) {
blocks.push_back(Block(
PadStringToLength(data.substr(i, Block::BLOCK_SIZE_BITS), Block::BLOCK_SIZE_BITS, '0', false))
);
// If we have no supplied n_bytes, let's just assume sizeof(data).
if (n_bytes == std::string::npos) {
n_bytes = data.size() * Block::BLOCK_SIZE;
}
// Add an additional block, containing the length of the input
std::stringstream ss;
ss << data.length();
const Block lengthBlock = StringToBitblock(ss.str());
blocks.push_back(lengthBlock);
// Create hasher instance
GHash hasher;
// Digest all blocks
for (Block& block : blocks) {
for (const Block& block : data) {
hasher.DigestBlock(block);
}
// Add an additional block, containing the length of the input
// Here it is actually good to use a binary string ("10011"),
// because std::size_t is not fixed to 32-bits. It may aswell
// be 64 bits, depending on the platform.
// Then it would be BAD to just cram it into a 32-bit uint32.
// This way, in case of 64-bits, it would just occupy 2 uint32's.
// Also, this operation gets done ONCE per n blocks. This won't
// hurt performance.
// I know that we are first converting n_bytes to str(n_bytes),
// and then converting this to a binstring, making it unnecessarily large,
// but who cares. It has a whole 512 bit block to itself.
// The max size (2^64) would occupy 155 bits at max. (log10(2^64)*8 = 155)
std::stringstream ss;
ss << n_bytes;
const Block lengthBlock = StringToBitblock(ss.str());
// Digest the length block
hasher.DigestBlock(lengthBlock);
// Return the total hashsum
return hasher.GetHashsum();
}
Block GHash::HashString(const std::string& str) {
const std::vector<Block> blocks = StringToBitblocks(str);
const std::size_t n_bytes = str.length();
return CalculateHashsum(blocks, n_bytes);
}
void GHash::operator=(const GHash& other) {
cipher = other.cipher;

View File

@@ -78,7 +78,7 @@ namespace Leonetienne::GCrypt {
// Performance improvement over the previous method:
// (generating 1.000.000 integers):
// 5.26 seconds -> 3.84 seconds
// 5.26 seconds -> 3.75 seconds
// Advance our pointer to the next whole uint32

View File

@@ -10,33 +10,64 @@ namespace Leonetienne::GCrypt {
const Key& key)
{
// Recode the ascii-string to bits
const Flexblock cleartext_bits = StringToBits(cleartext);
const std::vector<Block> cleartext_blocks = StringToBitblocks(cleartext);
// Encrypt our cleartext bits
const Flexblock ciphertext_bits = CipherFlexblock(cleartext_bits, key, GCipher::DIRECTION::ENCIPHER);
// Create cipher instance
GCipher cipher(key, GCipher::DIRECTION::ENCIPHER);
// Recode the ciphertext bits to a hex-string
const std::string ciphertext = BitsToHexstring(ciphertext_bits);
// Encrypt all blocks
std::vector<Block> ciphertext_blocks;
for (const Block& clearBlock : cleartext_blocks) {
ciphertext_blocks.emplace_back(cipher.Digest(clearBlock));
}
// Recode the ciphertext blocks to a hex-string
std::stringstream ss;
for (const Block& block : ciphertext_blocks) {
ss << block.ToHexString();
}
// Return it
return ciphertext;
return ss.str();
}
std::string GWrapper::DecryptString(
const std::string& ciphertext,
const Key& key)
{
// Recode the hex-string to bits
const Flexblock ciphertext_bits = HexstringToBits(ciphertext);
// Make sure our ciphertext is a multiple of block size
if (ciphertext.length() % Block::BLOCK_SIZE*2 != 0) { // Two chars per byte
throw std::runtime_error("Leonetienne::GCrypt::GWrapper::DecryptString() received ciphertext of length not a multiple of block size.");
}
// Decrypt the ciphertext bits
const std::string cleartext_bits = CipherFlexblock(ciphertext_bits, key, GCipher::DIRECTION::DECIPHER);
// Recode the hex-string to blocks
std::vector<Block> ciphertext_blocks;
ciphertext_blocks.reserve(ciphertext.length() / (Block::BLOCK_SIZE*2));
for (std::size_t i = 0; i < ciphertext.length(); i += Block::BLOCK_SIZE*2) {
Block block;
block.FromHexString(ciphertext.substr(i, Block::BLOCK_SIZE*2));
// Recode the cleartext bits to an ascii-string
const std::string cleartext = BitsToString(cleartext_bits);
ciphertext_blocks.emplace_back(block);
}
// Create cipher instance
GCipher cipher(key, GCipher::DIRECTION::DECIPHER);
// Decrypt all blocks
std::vector<Block> cleartext_blocks;
for (const Block& cipherBlock : ciphertext_blocks) {
cleartext_blocks.emplace_back(cipher.Digest(cipherBlock));
}
// Recode the cleartext blocks to bytes
std::stringstream ss;
for (const Block& block : cleartext_blocks) {
ss << block.ToTextString();
}
// Return it
return cleartext;
return ss.str();
}
bool GWrapper::EncryptFile(

View File

@@ -9,9 +9,7 @@
namespace Leonetienne::GCrypt {
Key Key::FromPassword(const std::string& password) {
return GHash::CalculateHashsum(
StringToBits(password)
);
return GHash::HashString(password);
}
Key Key::Random() {

View File

@@ -117,7 +117,7 @@ namespace Leonetienne::GCrypt {
std::string BitblockToHexstring(const Block& b) {
std::stringstream ss;
const std::string charset = "0123456789abcdef";
const std::string bstr = b.ToString();
const std::string bstr = b.ToBinaryString();
for (std::size_t i = 0; i < bstr.size(); i += 4) {
ss << charset[std::bitset<4>(bstr.substr(i, 4)).to_ulong()];
@@ -232,8 +232,9 @@ namespace Leonetienne::GCrypt {
return;
}
std::vector<Block> ReadFileToBlocks(const std::string& filepath) {
std::vector<Block> ReadFileToBlocks(const std::string& filepath, std::size_t& bytes_read) {
// Read file
bytes_read = 0;
// "ate" specifies that the read-pointer is already at the end of the file
// this allows to estimate the file size
@@ -259,9 +260,10 @@ namespace Leonetienne::GCrypt {
// Read data into the block
ifs.read((char*)(void*)block.Data(), Block::BLOCK_SIZE);
const std::size_t n_bytes_read = ifs.gcount();
const std::size_t n_bytes_read_block = ifs.gcount();
bytes_read += n_bytes_read_block;
if (n_bytes_read > 0) {
if (n_bytes_read_block > 0) {
// Append the block to our vector
blocks.emplace_back(block);
}
@@ -273,6 +275,11 @@ namespace Leonetienne::GCrypt {
return blocks;
}
std::vector<Block> ReadFileToBlocks(const std::string& filepath) {
std::size_t bytes_read_dummy; // Create a dumme for the parameter
return ReadFileToBlocks(filepath, bytes_read_dummy);
}
void WriteBlocksToFile(
const std::string& filepath,
const std::vector<Block>& blocks
@@ -296,45 +303,19 @@ namespace Leonetienne::GCrypt {
return;
}
std::vector<Block> StringToBitblocks(const std::string& s) {
std::vector<Block> StringToBitblocks(const std::string& str) {
// 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;
const std::size_t num_blocks = (str.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
for (std::size_t i = 0; i < str.length(); i += Block::BLOCK_SIZE) {
Block block;
block.Reset();
block.FromTextString(str.substr(i, Block::BLOCK_SIZE));
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;
}
}
blocks.emplace_back(block);
}
return blocks;