Skip to content

Instantly share code, notes, and snippets.

@xenomancer
Last active April 11, 2021 15:55
Show Gist options
  • Save xenomancer/c459520c7127b2ff11e1ddff6d41493b to your computer and use it in GitHub Desktop.
Save xenomancer/c459520c7127b2ff11e1ddff6d41493b to your computer and use it in GitHub Desktop.
A standalone alternative to a quick encryption/decryption part of a project. Based on simple Feistel network with some modification to perform as a pseudo-unbalanced network with more than two blocks allowed.
// Copyright Craig D. Mansfield 2021, zero rights reserved.
// TO the extent that it is possible under law, this is released to the public domain.
// Do with this as you please. Consider this beerware.
// If you like what I wrote and we ever meet up, you can buy me a beer.
// This is a header only c++ implementation.
// Assuming compiler uses c++14 spec or later.
#pragma once
#include <stdlib>
#include <stdint>
#include <vector>
#include <functional>
#include <exception>
#include <stdexcept>
#include <random>
#include <stdio>
using namespace std;
//typedef unsigned long long size_t;
//using vecc_t = std::vector<unsigned char>;
using hash_func = function<vector<unsigned char>(const vector<unsigned char>)>;
#pragma region rotations
inline void rotate_left(
vector<unsigned char>& aData,
size_t aCount
) {
size_t aN = aData.size();
size_t xCount = ((aCount % aN) + aN) % aN;
if (xCount == 0) return;
vector<unsigned char> tmp(aN);
size_t x;
for (int i = 0; i < aN; i++) {
x = (i + aN - xCount) % aN;
tmp[i] = aData[x];
}
for (int i = 0; i < aN; i++) {
aData[i] = tmp[i];
}
}
inline void rotate_right(
vector<unsigned char>& aData,
size_t aCount
) {
size_t aN = aData.size();
size_t xCount = ((aCount % aN) + aN) % aN;
if (xCount == 0) return;
vector<unsigned char> tmp(aN);
size_t x;
for (int i = 0; i < aN; i++) {
x = (i + aN + xCount) % aN;
tmp[i] = aData[x];
}
for (int i = 0; i < aN; i++) {
aData[i] = tmp[i];
}
}
#pragma endregion
#pragma region key_gen
inline vector<vector<unsigned char>> key_gen(
const vector<unsigned char>& aSeed,
size_t aNChunkSize,
size_t aNRounds,
hash_func aHashFunc
) {
vector<unsigned char> tmpKey;
vector<unsigned char> aHashOut;
vector<vector<unsigned char>> res(aNRounds);
tmpKey = aSeed;
for (int i = 0; i < aNRounds; i++) {
aHashOut = aHashFunc(tmpKey);
size_t aNHashSize = aHashOut.size();
tmpKey = vector<unsigned char>(aNChunkSize);
for (int j = 0; j < aNChunkSize; j++) {
tmpKey[j] = aHashOut[j % aNHashSize];
}
res[i] = tmpKey;
}
return res;
}
#pragma endregion
#pragma region combine
inline void combine(
vector<unsigned char>& aData,
const vector<unsigned char>& aKey,
size_t aXStart,
size_t aYStart,
hash_func aHashFunc
) {
size_t aN = aKey.size();
size_t aNHashSize;
vector<unsigned char> aHashIn(aN);
vector<unsigned char> aHashOut;
// pre-mix x-data and key
for (int i = 0; i < aN; i++) {
aHashIn[i] = aData[aXStart + i] ^ aKey[i];
}
// hash pre-mixed data
aHashOut = aHashFunc(aHashIn);
aNHashSize = aHashOut.size();
// store post-mix of y-data and hashed pre-mix data to the y-data
for (int i = 0; i < aN; i++) {
aData[aYStart + i] = aData[aYStart + i] ^ aHashOut[i % aNHashSize];
}
}
#pragma endregion
#pragma region transform
inline void transform(
vector<unsigned char>& aData,
const vector<unsigned char>& aKey,
size_t aOffset,
hash_func aHashFunc
) {
size_t aNData = aData.size();
size_t aNChunkSize = aKey.size();
size_t aNChunks = aNData / aNChunkSize;
for (int i = 0; i < aNChunks; i++) {
if ((i % 2) == 1) {
combine(aData, aKey, aOffset + i * aNChunkSize, aOffset + (i - 1) * aNChunkSize, aHashFunc);
}
}
}
#pragma endregion
#pragma region encrypt
inline void encrypt(
vector<unsigned char>& aData,
vector<vector<unsigned char>>& aKeys,
hash_func aHashFunc
) {
size_t aNData = aData.size();
size_t aNRounds = aKeys.size();
if (aNData == 0) return;
if (aNRounds == 0) return;
size_t aNChunkSize = aKeys[0].size();
size_t aNChunks = aNData / aNChunkSize;
size_t aNOffset = aNData - aNChunks * aNChunkSize;
size_t aOffset = 0;
vector<unsigned char> tmpKey;
for (int i = 0; i < aNRounds; i++) {
// get round key
tmpKey = aKeys[i];
// perform first half of round
aOffset = 0;
transform(aData, tmpKey, aOffset, aHashFunc);
rotate_left(aData, aNChunkSize);
// perform second half of round
aOffset = aNOffset;
transform(aData, tmpKey, aOffset, aHashFunc);
rotate_left(aData, aNChunkSize);
}
}
inline void encrypt(
vector<unsigned char>& aData,
vector<unsigned char>& aSeedKey,
size_t aNChunkSize,
size_t aNRounds,
hash_func aHashFuncData,
hash_func aHashFuncKeys
) {
size_t aNData = aData.size();
size_t aNSeed = aSeedKey.size();
if (aNChunkSize == 0) return; // no change to data needed;
if (aNChunkSize > (aNData / 2)) throw exception("Chunk size must be less than or equal to half of the size of the data.");
if (aNRounds == 0) return; // no change to data needed
vector<vector<unsigned char>> aKeys = key_gen(aSeedKey, aNChunkSize, aNRounds, aHashFuncKeys); // get keys
// encrypt
encrypt(aData, aKeys, aHashFuncData);
}
inline void encrypt(
vector<unsigned char>& aData,
vector<unsigned char>& aSeedKey,
size_t aNChunkSize,
size_t aNRounds,
hash_func aHashFunc
) {
encrypt(aData, aSeedKey, aNChunkSize, aNRounds, aHashFunc, aHashFunc);
}
#pragma endregion
#pragma region decrypt
inline void decrypt(
vector<unsigned char>& aData,
vector<vector<unsigned char>>& aKeys,
hash_func aHashFunc
) {
size_t aNData = aData.size();
size_t aNRounds = aKeys.size();
if (aNData == 0) return;
if (aNRounds == 0) return;
size_t aNChunkSize = aKeys[0].size();
size_t aNChunks = aNData / aNChunkSize;
size_t aNOffset = aNData - aNChunks * aNChunkSize;
size_t aOffset = 0;
vector<unsigned char> tmpKey;
for (int i = 0; i < aNRounds; i++) {
// get round key
tmpKey = aKeys[aNRounds - 1 - i];
// perform first half of round
aOffset = aNOffset;
rotate_right(aData, aNChunkSize);
transform(aData, tmpKey, aOffset, aHashFunc);
// perform second half of round
aOffset = 0;
rotate_right(aData, aNChunkSize);
transform(aData, tmpKey, aOffset, aHashFunc);
}
}
inline void decrypt(
vector<unsigned char>& aData,
vector<unsigned char>& aSeedKey,
size_t aNChunkSize,
size_t aNRounds,
hash_func aHashFuncData,
hash_func aHashFuncKeys
) {
size_t aNData = aData.size();
size_t aNSeed = aSeedKey.size();
if (aNChunkSize == 0) return; // no change to data needed;
if (aNChunkSize > (aNData / 2)) throw exception("Chunk size must be less than or equal to half of the size of the data.");
if (aNRounds == 0) return; // no change to data needed
vector<vector<unsigned char>> aKeys = key_gen(aSeedKey, aNChunkSize, aNRounds, aHashFuncKeys); // get keys
// decrypt
decrypt(aData, aKeys, aHashFuncData);
}
inline void decrypt(
vector<unsigned char>& aData,
vector<unsigned char>& aSeedKey,
size_t aNChunkSize,
size_t aNRounds,
hash_func aHashFunc
) {
decrypt(aData, aSeedKey, aNChunkSize, aNRounds, aHashFunc, aHashFunc);
}
#pragma endregion
#pragma region demo
inline void demo() {
// size parameters
size_t aNData = 1000; // size of data in bytes
size_t aNRounds = 100; // number of rounds
size_t aNChunkSize = aNData / 3; // size of a data chunk
size_t aNDataBits = aNData * CHAR_BIT; // number of bits in data
cout << "Data size = " << aNData << endl;
cout << "Chunk size = " << aNChunkSize << endl;
cout << "Num rounds = " << aNRounds << endl;
// a truly terrible "hash" function
hash_func aHF = [](const vector<unsigned char>& aIn){
size_t aN = aIn.size();
vector<unsigned char> lsft(aIn);
vector<unsigned char> rsft(aIn);
vector<unsigned char> res(aIn);
// this is explicitly bad
// this will only demonstrate the encryption/decryption well with random data
// this will very likely not protect your data
// never use this to protect real data!
// never!!!
// force a data mismatch
rotate_left(lsft, 1);
rotate_right(rsft, 1);
// get the difference in the data
for (int i = 0; i < aN; i++) {
res[i] = (rsft[i] ^ lsft[i]);
}
// not even if you are in a hurry!
// don't use this!
// return the result
return res;
};
// data storage
vector<vector<unsigned char>> keys; // the seed key
vector<unsigned char> key(aNChunkSize); // a set of keys
vector<unsigned char> msg(aNData); // the original unencrypted message
vector<unsigned char> enc; // the encrypted message
vector<unsigned char> dec; // the decrypted message
vector<unsigned char> dff(aNData); // difference between msg and enc (should be a mess)
vector<unsigned char> chk(aNData); // difference between msg and dec (should be all zeros)
size_t dff_bits; // diff bits in dff (should be a lot)
size_t chk_bits; // diff bits in chk (should be zero)
// for random number generation
random_device rd;
mt19937_64 gen = mt19937_64(rd());
uniform_int_distribution<size_t> dis;
// get a random seed key
for (int i = 0; i < aNChunkSize; i++) {
key[i] = dis(gen);
}
cout << "generated a random seed key" << endl;
// generate the keys
keys = key_gen(key, aNChunkSize, aNRounds, aHF);
cout << "generated keys from seed key" << endl;
// get a random message
for (int i = 0; i < aNData; i++) {
msg[i] = dis(gen);
}
cout << "generated a random message" << endl << endl;
// encrypt the message using the generated keys
enc = vector<unsigned char>(msg); // get a copy of the message
encrypt(enc, keys, aHF); // encrypt the message
cout << "message encrypted" << endl << endl;
// decrypt the message using the generated keys
dec = vector<unsigned char>(enc); // get a copy of the encrypted message
decrypt(dec, keys, aHF); // decrypt the message
cout << "message decrypted" << endl << endl;
// get the diffs data
for (int i = 0; i < aNData; i++) {
dff[i] = (msg[i] > enc[i]) ? (msg[i] - enc[i]) : (enc[i] - msg[i]);
chk[i] = (msg[i] > dec[i]) ? (msg[i] - dec[i]) : (dec[i] - msg[i]);
}
// get the diff bits
dff_bits = 0;
chk_bits = 0;
for (int i = 0; i < aNData; i++) {
unsigned char tmp_dff = dff[i];
unsigned char tmp_chk = chk[i];
for (int j = 0; j < CHAR_BIT; j++) {
// avoiding entire issue of popcount existing or not existing, etc.
if (((tmp_dff >> j) & 1) == 1) dff_bits++;
if (((tmp_chk >> j) & 1) == 1) chk_bits++;
}
}
cout << "dff_bits = " << dff_bits << endl;
cout << "chk_bits = " << chk_bits << endl;
// wait for user to end test in console
cin.get();
}
#pragma endregion
// EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment