From b5369a3c3235cfdb91b2152dac00f228aeb1492a Mon Sep 17 00:00:00 2001 From: Leonetienne Date: Wed, 25 May 2022 12:54:26 +0200 Subject: [PATCH] Added bitshift methods to block class --- GCryptLib/include/GCrypt/Block.h | 12 +++ GCryptLib/src/Block.cpp | 132 ++++++++++++++++++++++++++++++- GCryptLib/test/Block.cpp | 123 ++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+), 4 deletions(-) diff --git a/GCryptLib/include/GCrypt/Block.h b/GCryptLib/include/GCrypt/Block.h index f2d5bc3..b28748a 100644 --- a/GCryptLib/include/GCrypt/Block.h +++ b/GCryptLib/include/GCrypt/Block.h @@ -132,6 +132,18 @@ namespace Leonetienne::GCrypt { //! Will flip the state of any given bit void FlipBit(const std::size_t index); + //! Will shift all bits to the left by 1 + [[nodiscard]] Block ShiftBitsLeft() const; + + //! Will shift all bits to the left by 1, inplace + void ShiftBitsLeftInplace(); + + //! Will shift all bits to the right by 1 + [[nodiscard]] Block ShiftBitsRight() const; + + //! Will shift all bits to the right by 1, inplace + void ShiftBitsRightInplace(); + //! Returns 32-bit chunks of data, indexed by matrix coordinates (0-3) [[nodiscard]] std::uint32_t& Get(const std::uint8_t row, const std::uint8_t column); //! Returns 32-bit chunks of data, indexed by matrix coordinates (0-3) diff --git a/GCryptLib/src/Block.cpp b/GCryptLib/src/Block.cpp index ac5a648..7631962 100644 --- a/GCryptLib/src/Block.cpp +++ b/GCryptLib/src/Block.cpp @@ -1,6 +1,6 @@ #include "GCrypt/Block.h" -#include #include "GCrypt/Config.h" +#include "GCrypt/Util.h" #include #include #include @@ -134,9 +134,7 @@ namespace Leonetienne::GCrypt { Block& Block::operator^=(const Block& other) { XorInplace(other); - return *this; - } - + return *this; } Block Block::Add(const Block& other) const { Block m; @@ -498,6 +496,132 @@ namespace Leonetienne::GCrypt { return; } + Block Block::ShiftBitsLeft() const { + Block b; + + // First, copy this block over + b = *this; + + // Then, shift all integers individually + for (std::size_t i = 0; i < data.size(); i++) { + b.data[i] <<= 1; + } + + // Current state: the LSB is zero everywhere. We have to carry + // it over manually from the previous state. + + // Carry over the MSB of data[i] to LSB of data[i-1] + constexpr std::size_t bitmaskMsb = 1 << (CHUNK_SIZE_BITS - 1); + constexpr std::size_t bitmaskLsb = 1; + for (int i = 0; i < data.size(); i++) { + const bool msb = data[i] & bitmaskMsb; + + // Set the lsb + if (msb) { + b.data[Mod(i-1, data.size())] |= bitmaskLsb; + } + // Clear the lsb + else { + b.data[Mod(i-1, data.size())] &= ~bitmaskLsb; + } + } + + return b; + } + + void Block::ShiftBitsLeftInplace() { + Block tmp = *this; + + // Then, shift all integers individually + for (std::size_t i = 0; i < data.size(); i++) { + data[i] <<= 1; + } + + // Current state: the LSB is zero everywhere. We have to carry + // it over manually from the previous state. + + // Carry over the MSB of data[i] to LSB of data[i-1] + constexpr std::size_t bitmaskMsb = 1 << (CHUNK_SIZE_BITS - 1); + constexpr std::size_t bitmaskLsb = 1; + for (int i = 0; i < data.size(); i++) { + const bool msb = tmp.data[i] & bitmaskMsb; + + // Set the lsb + if (msb) { + data[Mod(i-1, data.size())] |= bitmaskLsb; + } + // Clear the lsb + else { + data[Mod(i-1, data.size())] &= ~bitmaskLsb; + } + } + + return; + } + + Block Block::ShiftBitsRight() const { + Block b; + + // First, copy this block over + b = *this; + + // Then, shift all integers individually + for (std::size_t i = 0; i < data.size(); i++) { + b.data[i] >>= 1; + } + + // Current state: the LSB is zero everywhere. We have to carry + // it over manually from the previous state. + + // Carry over the LSB of data[i] to MSB of data[i+1] + constexpr std::size_t bitmaskMsb = 1 << (CHUNK_SIZE_BITS - 1); + constexpr std::size_t bitmaskLsb = 1; + for (int i = 0; i < data.size(); i++) { + const bool lsb = data[i] & bitmaskLsb; + + // Set the msb + if (lsb) { + b.data[Mod(i+1, data.size())] |= bitmaskMsb; + } + // Clear the msb + else { + b.data[Mod(i+1, data.size())] &= ~bitmaskMsb; + } + } + + return b; + } + + void Block::ShiftBitsRightInplace() { + Block tmp = *this; + + // Then, shift all integers individually + for (std::size_t i = 0; i < data.size(); i++) { + data[i] >>= 1; + } + + // Current state: the LSB is zero everywhere. We have to carry + // it over manually from the previous state. + + // Carry over the LSB of data[i] to MSB of data[i+1] + constexpr std::size_t bitmaskMsb = 1 << (CHUNK_SIZE_BITS - 1); + constexpr std::size_t bitmaskLsb = 1; + for (int i = 0; i < data.size(); i++) { + const bool lsb = tmp.data[i] & bitmaskLsb; + + // Set the msb + if (lsb) { + data[Mod(i+1, data.size())] |= bitmaskMsb; + } + // Clear the msb + else { + data[Mod(i+1, data.size())] &= ~bitmaskMsb; + } + } + + return; + } + std::uint32_t& Block::Get(const std::uint8_t row, const std::uint8_t column){ return data[MAT_INDEX(row, column)]; } diff --git a/GCryptLib/test/Block.cpp b/GCryptLib/test/Block.cpp index 1de73c9..7ac92dc 100644 --- a/GCryptLib/test/Block.cpp +++ b/GCryptLib/test/Block.cpp @@ -4,6 +4,7 @@ #include #include #include +#include using namespace Leonetienne::GCrypt; @@ -705,3 +706,125 @@ TEST_CASE(__FILE__"/flip-bit", "[Block]") { REQUIRE(a.ToString() == compare); } +// Tests that bitshifts (to the left) work +TEST_CASE(__FILE__"/bitshift-left", "[Block]") { + + // Setup + srand(time(0)); + std::stringstream ss; + + for (std::size_t i = 0; i < 512; i++) { + ss << (rand()%2 == 0 ? '1' : '0'); + } + const std::string originalBits = ss.str(); + ss.str(""); + + // Shift string manually + std::string shiftedBits = originalBits; + shiftedBits.erase(0, 1); + ss << shiftedBits << originalBits[0]; + shiftedBits = ss.str(); + + // Create block of original bits + Block block(originalBits); + + // Exercise + block = block.ShiftBitsLeft(); + + // Verify + REQUIRE(block.ToString().length() == shiftedBits.length()); + REQUIRE(block.ToString() == shiftedBits); +} + +// Tests that inplace-bitshifts to the left do the exact same as copy bitshifts +TEST_CASE(__FILE__"/bitshift-left-inplace", "[Block]") { + + // Setup + srand(time(0)); + std::stringstream ss; + + for (std::size_t i = 0; i < 512; i++) { + ss << (rand()%2 == 0 ? '1' : '0'); + } + Block a(ss.str()); + + // Exercise + Block b = a.ShiftBitsLeft(); + a.ShiftBitsLeftInplace(); + + // Verify + REQUIRE(a == b); +} + +// Tests that bitshifts (to the right) work +TEST_CASE(__FILE__"/bitshift-right", "[Block]") { + + // Setup + srand(time(0)); + std::stringstream ss; + + for (std::size_t i = 0; i < 512; i++) { + ss << (rand()%2 == 0 ? '1' : '0'); + } + const std::string originalBits = ss.str(); + ss.str(""); + + // Shift string manually + std::string shiftedBits = originalBits; + shiftedBits.erase(shiftedBits.length() - 1); + ss << originalBits[originalBits.length()-1] << shiftedBits; + shiftedBits = ss.str(); + + // Create block of original bits + Block block(originalBits); + + // Exercise + block = block.ShiftBitsRight(); + + // Verify + REQUIRE(block.ToString().length() == shiftedBits.length()); + REQUIRE(block.ToString() == shiftedBits); +} + +// Tests that inplace-bitshifts to the right do the exact same as copy bitshifts +TEST_CASE(__FILE__"/bitshift-right-inplace", "[Block]") { + + // Setup + srand(time(0)); + std::stringstream ss; + + for (std::size_t i = 0; i < 512; i++) { + ss << (rand()%2 == 0 ? '1' : '0'); + } + Block a(ss.str()); + + // Exercise + Block b = a.ShiftBitsRight(); + a.ShiftBitsRightInplace(); + + // Verify + REQUIRE(a == b); +} + +// Tests that bitshifting undoes itself +TEST_CASE(__FILE__"/bitshifting-undoes-itself", "[Block]") { + + // Setup + Block a = Key::FromPassword("Halleluja"); + + const Block initial_a = a; + + // Exercise (mix-up) + for (std::size_t i = 0; i < 100; i++) { + a.ShiftBitsLeftInplace(); + } + + // Exercise (un-mix) + for (std::size_t i = 0; i < 100; i++) { + a.ShiftBitsRightInplace(); + } + + // Verify + REQUIRE(a == initial_a); +} +