GCrypt/GCryptLib/src/Feistel.cpp

279 lines
6.7 KiB
C++
Raw Normal View History

2022-05-16 00:21:01 +02:00
#include <unordered_map>
2022-05-16 22:35:28 +02:00
#include "GCrypt/Feistel.h"
#include "GCrypt/Util.h"
#include "GCrypt/Config.h"
#include "GCrypt/SBoxLookup.h"
2022-05-16 00:21:01 +02:00
namespace Leonetienne::GCrypt {
2022-05-16 00:21:01 +02:00
2022-05-26 15:47:24 +02:00
Feistel::Feistel() {
}
2022-05-22 13:43:23 +02:00
Feistel::Feistel(const Key& key) {
2022-05-22 17:32:54 +02:00
SetKey(key);
}
2022-05-16 00:21:01 +02:00
Feistel::~Feistel() {
2022-05-22 17:32:54 +02:00
ZeroKeyMemory();
}
2022-05-16 00:21:01 +02:00
2022-05-22 13:43:23 +02:00
void Feistel::SetKey(const Key& key) {
2022-05-22 17:32:54 +02:00
GenerateRoundKeys(key);
2022-05-26 15:47:24 +02:00
isInitialized = true;
}
2022-05-16 00:21:01 +02:00
Block Feistel::Encipher(const Block& data) {
2022-05-22 17:32:54 +02:00
return Run(data, false);
}
2022-05-16 00:21:01 +02:00
Block Feistel::Decipher(const Block& data) {
2022-05-22 17:32:54 +02:00
return Run(data, true);
}
Block Feistel::Run(const Block& data, bool modeEncrypt) {
2022-05-26 15:47:24 +02:00
if (!isInitialized) {
throw std::runtime_error("Attempted to digest data on uninitialized GCipher!");
}
2022-05-22 17:32:54 +02:00
const auto splitData = FeistelSplit(data);
Halfblock l = splitData.first;
Halfblock r = splitData.second;
Halfblock tmp;
for (std::size_t i = 0; i < N_ROUNDS; i++) {
// Encryption
if (modeEncrypt) {
const std::size_t keyIndex = i;
// Do a feistel round
tmp = r;
r = l ^ F(r, roundKeys[keyIndex]);
l = tmp;
// Jumble it up a bit more
l.ShiftRowsUpInplace();
l.ShiftCellsRightInplace();
l.ShiftBitsLeftInplace();
l.ShiftColumnsLeftInplace();
// Seal all these operations with a key
l += ReductionFunction(roundKeys[keyIndex]);
}
// Decryption
2022-05-22 17:32:54 +02:00
else {
// Decryption needs keys in reverse order
const std::size_t keyIndex = N_ROUNDS - i - 1;
// Unjumble the jumble
r -= ReductionFunction(roundKeys[keyIndex]);
r.ShiftColumnsRightInplace();
r.ShiftBitsRightInplace();
r.ShiftCellsLeftInplace();
r.ShiftRowsDownInplace();
// Do a feistel round
tmp = r;
r = l ^ F(r, roundKeys[keyIndex]);
l = tmp;
2022-05-22 17:32:54 +02:00
}
}
2022-05-22 17:32:54 +02:00
// Block has finished de*ciphering.
// Let's generate a new set of round keys.
GenerateRoundKeys(roundKeys.back());
2022-05-22 17:32:54 +02:00
return FeistelCombine(r, l);
}
2022-05-22 13:43:23 +02:00
Halfblock Feistel::F(Halfblock m, const Key& key) {
2022-05-22 20:13:41 +02:00
// Made-up F function:
2022-05-22 17:32:54 +02:00
// Expand to full bitwidth
Block m_expanded = ExpansionFunction(m);
2022-05-25 13:05:25 +02:00
// Mix up the block a bit
m_expanded.ShiftCellsRightInplace();
m_expanded.ShiftRowsUpInplace();
2022-05-25 13:05:25 +02:00
// Matrix-mult with key (this is irreversible)
m_expanded *= key;
2022-05-25 13:05:25 +02:00
// Now do a bitshift
m_expanded.ShiftBitsLeftInplace();
// Apply the sbox
SBox(m_expanded);
// Reduce back to a halfblock
Halfblock hb = ReductionFunction(m_expanded);
// To jumble it up a last time,
// matrix-multiply it with the input halfblock
hb *= m;
return hb;
}
std::pair<Halfblock, Halfblock> Feistel::FeistelSplit(const Block& block) {
Halfblock l;
Halfblock r;
memcpy(l.Data(), block.Data(), Halfblock::BLOCK_SIZE);
memcpy(r.Data(), block.Data() + 8, Halfblock::BLOCK_SIZE);
// +8, because 8 is HALF the number of elements in the array. We only want to copy HALF a full-sized block.
2022-05-22 17:32:54 +02:00
return std::make_pair(l, r);
}
Block Feistel::FeistelCombine(const Halfblock& l, const Halfblock& r) {
Block b;
memcpy(b.Data(), l.Data(), Halfblock::BLOCK_SIZE);
memcpy(b.Data() + 8, r.Data(), Halfblock::BLOCK_SIZE);
// +8, because 8 is HALF the number of elements in the array. We only want to copy HALF a full-sized block.
return b;
}
Block Feistel::ExpansionFunction(const Halfblock& hb) {
Block b;
2022-05-22 17:32:54 +02:00
// Copy the bits over
for (std::size_t i = 0; i < 16; i++) {
b[i] = hb[i];
}
2022-05-22 17:32:54 +02:00
// Multiply the block a few tims with a bitshifted version
// This is irriversible, too
for (std::size_t i = 0; i < 3; i++) {
b *= b.ShiftBitsRight();
2022-05-22 17:32:54 +02:00
}
return b;
}
Halfblock Feistel::ReductionFunction(const Block& block) {
// Just apply a modulo operation, remapping a 32bit integer
// onto 16bit space (default configuration).
// Without saying, modulo is irreversible.
Halfblock hb;
for (std::size_t i = 0; i < 16; i++) {
hb[i] = block[i] % (1 << (Halfblock::CHUNK_SIZE_BITS - 1));
2022-05-22 17:32:54 +02:00
}
return hb;
}
void Feistel::SBox(Block& block) {
std::uint8_t* curByte = (std::uint8_t*)(void*)block.Data();
// Iterate over all bytes in the block
for (std::size_t i = 0; i < Block::BLOCK_SIZE; i++) {
curByte++;
// Subsitute byte
*curByte = sboxLookup[*curByte];
}
return;
}
/*
std::string Feistel::SBox(const std::string& in) {
2022-05-22 17:32:54 +02:00
static std::unordered_map<std::string, std::string> subMap;
static bool mapInitialized = false;
if (!mapInitialized) {
subMap["0000"] = "1100";
subMap["0001"] = "1000";
subMap["0010"] = "0001";
subMap["0011"] = "0111";
subMap["0100"] = "1011";
subMap["0101"] = "0011";
subMap["0110"] = "1101";
subMap["0111"] = "1111";
subMap["1000"] = "0000";
subMap["1001"] = "1010";
subMap["1010"] = "0100";
subMap["1011"] = "1001";
subMap["1100"] = "0010";
subMap["1101"] = "1110";
subMap["1110"] = "0101";
subMap["1111"] = "0110";
mapInitialized = true;
}
return subMap[in];
}
*/
2022-05-22 13:43:23 +02:00
void Feistel::GenerateRoundKeys(const Key& seedKey) {
2022-05-22 17:32:54 +02:00
// Clear initial key memory
ZeroKeyMemory();
roundKeys = Keyset();
2022-05-25 13:05:25 +02:00
// Derive all round keys with simple matrix operations
roundKeys[0] = seedKey;
2022-05-22 17:32:54 +02:00
2022-05-25 13:05:25 +02:00
for (std::size_t i = 1; i < roundKeys.size(); i++) {
2022-05-22 17:32:54 +02:00
// Initialize new round key with last round key
2022-05-25 13:05:25 +02:00
const Key& lastKey = roundKeys[i - 1];
roundKeys[i] = lastKey;
// Stir it good
roundKeys[i].ShiftRowsUpInplace();
// Bitshift and matrix-mult 3 times
// (each time jumbles it up pretty good)
// This is irreversible
roundKeys[i].ShiftBitsRightInplace();
roundKeys[i] *= lastKey;
roundKeys[i].ShiftBitsRightInplace();
roundKeys[i] *= lastKey;
roundKeys[i].ShiftBitsRightInplace();
roundKeys[i] *= lastKey;
// Lastly, do apply some cell shifting, and other mutations
roundKeys[i].ShiftCellsRightInplace();
roundKeys[i] += lastKey;
roundKeys[i].ShiftColumnsRightInplace();
roundKeys[i] ^= lastKey;
2022-05-22 17:32:54 +02:00
}
2022-05-22 17:32:54 +02:00
return;
}
2022-05-22 16:54:40 +02:00
void Feistel::operator=(const Feistel& other) {
roundKeys = other.roundKeys;
2022-05-26 15:47:24 +02:00
isInitialized = other.isInitialized;
2022-05-22 16:54:40 +02:00
return;
}
// These pragmas only work for MSVC and g++, as far as i know. Beware!!!
2022-05-16 00:21:01 +02:00
#if defined _WIN32 || defined _WIN64
#pragma optimize("", off )
#elif defined __GNUG__
#pragma GCC push_options
#pragma GCC optimize ("O0")
#endif
void Feistel::ZeroKeyMemory() {
2022-05-22 17:32:54 +02:00
for (Key& key : roundKeys) {
key.Reset();
2022-05-22 17:32:54 +02:00
}
2022-05-16 00:21:01 +02:00
2022-05-22 17:32:54 +02:00
return;
}
2022-05-16 00:21:01 +02:00
#if defined _WIN32 || defined _WIN64
#pragma optimize("", on )
#elif defined __GNUG__
#pragma GCC pop_options
#endif
2022-05-16 22:01:52 +02:00
}