Added bitshift methods to block class

This commit is contained in:
Leonetienne 2022-05-25 12:54:26 +02:00
parent 9a9cd05bed
commit b5369a3c32
No known key found for this signature in database
GPG Key ID: C33879CD92E9708C
3 changed files with 263 additions and 4 deletions

View File

@ -132,6 +132,18 @@ namespace Leonetienne::GCrypt {
//! Will flip the state of any given bit //! Will flip the state of any given bit
void FlipBit(const std::size_t index); 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) //! 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); [[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) //! Returns 32-bit chunks of data, indexed by matrix coordinates (0-3)

View File

@ -1,6 +1,6 @@
#include "GCrypt/Block.h" #include "GCrypt/Block.h"
#include <iostream>
#include "GCrypt/Config.h" #include "GCrypt/Config.h"
#include "GCrypt/Util.h"
#include <sstream> #include <sstream>
#include <bitset> #include <bitset>
#include <cassert> #include <cassert>
@ -134,9 +134,7 @@ namespace Leonetienne::GCrypt {
Block& Block::operator^=(const Block& other) { Block& Block::operator^=(const Block& other) {
XorInplace(other); XorInplace(other);
return *this; return *this; }
}
Block Block::Add(const Block& other) const { Block Block::Add(const Block& other) const {
Block m; Block m;
@ -498,6 +496,132 @@ namespace Leonetienne::GCrypt {
return; 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){ std::uint32_t& Block::Get(const std::uint8_t row, const std::uint8_t column){
return data[MAT_INDEX(row, column)]; return data[MAT_INDEX(row, column)];
} }

View File

@ -4,6 +4,7 @@
#include <cstdlib> #include <cstdlib>
#include <time.h> #include <time.h>
#include <sstream> #include <sstream>
#include <iostream>
using namespace Leonetienne::GCrypt; using namespace Leonetienne::GCrypt;
@ -705,3 +706,125 @@ TEST_CASE(__FILE__"/flip-bit", "[Block]") {
REQUIRE(a.ToString() == compare); 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);
}