Many methods now using vectors of blocks instead of flexblocks
This commit is contained in:
@@ -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 {
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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(
|
||||
|
@@ -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() {
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user