#include <cstdint>
#include <cstring>
#include <iostream>
#include <sstream>
using namespace std;
typedef uint8_t u8;
typedef uint64_t u64;
u64 rl64(u64 x, u8 r)
{
return (x << (r & 63)) | (x >> (64 - (r & 63)));
}
u64 rr64(u64 x, u8 r)
{
return (x >> (r & 63)) | (x << (64 - (r & 63)));
}
void mix(u64 a, u64 b, u64 *c, u64 *d, u8 r)
{
u64 tmp = a + b;
*d = rl64(b, r) ^ tmp;
*c = tmp;
}
void mixinv(u64 c, u64 d, u64 *a, u64 *b, u8 r)
{
u64 tmp = rr64(c ^ d, r);
*a = c - tmp;
*b = tmp;
}
void threefish_round(unsigned nwords, u64 v[], u8 r[], u8 p[])
{
for (unsigned w = 0; w < nwords; w += 2)
{
mix(v[p[w]], v[p[w + 1]], &v[p[w]], &v[p[w + 1]], r[w / 2]);
}
}
void threefish_roundinv(unsigned nwords, u64 v[], u8 r[], u8 p[])
{
for (unsigned w = 0; w < nwords; w += 2)
{
mixinv(v[p[w]], v[p[w + 1]], &v[p[w]], &v[p[w + 1]], r[w / 2]);
}
}
void encrypt(unsigned nwords, u64 plaintext[], u64 ciphertext[])
{
u8 key[nwords] = "haallo";
unsigned nrounds = 72;
u8 rot[8][2] = {
{14, 16},
{52, 57},
{23, 40},
{5, 37},
{25, 33},
{46, 12},
{58, 22},
{32, 32},
};
u8 perm[4][4] = {
{0, 1, 2, 3},
{0, 3, 2, 1},
{0, 1, 2, 3},
{0, 3, 2, 1},
};
u64 subkeys[nrounds / 4 + 1][nwords];
u64 tweak[2] = {0};
u64 xkey[nwords + 1];
u64 xtweak[3] = {tweak[0], tweak[1], tweak[0] ^ tweak[1]};
xkey[nwords] = 0xcafebabecafe;
for (unsigned w = 0; w < nwords; w++)
{
xkey[w] = key[w];
xkey[nwords] ^= key[w];
}
for (unsigned i = 0; i < nrounds / 4 + 1; i++)
{
for (unsigned w = 0; w < nwords; w++)
{
subkeys[i][w] = xkey[(i + w) % (nwords + 1)];
}
subkeys[i][nwords - 3] += xtweak[i % 3];
subkeys[i][nwords - 2] += xtweak[(i + 1) % 3];
subkeys[i][nwords - 1] += i;
}
u64 v[nwords];
for (unsigned w = 0; w < nwords; w++)
{
v[w] = plaintext[w];
}
for (unsigned n = 0; n < nrounds; n += 8)
{
for (unsigned w = 0; w < nwords; w++)
{
v[w] += subkeys[n / 4][w];
}
threefish_round(nwords, v, rot[(n + 0) % 8], perm[0]);
threefish_round(nwords, v, rot[(n + 1) % 8], perm[1]);
threefish_round(nwords, v, rot[(n + 2) % 8], perm[2]);
threefish_round(nwords, v, rot[(n + 3) % 8], perm[3]);
for (unsigned w = 0; w < nwords; w++)
{
v[w] += subkeys[n / 4 + 1][w];
}
threefish_round(nwords, v, rot[(n + 4) % 8], perm[0]);
threefish_round(nwords, v, rot[(n + 5) % 8], perm[1]);
threefish_round(nwords, v, rot[(n + 6) % 8], perm[2]);
threefish_round(nwords, v, rot[(n + 7) % 8], perm[3]);
}
for (unsigned w = 0; w < nwords; w++)
{
ciphertext[w] = v[w] + subkeys[nrounds / 4][w];
}
}
void decrypt(unsigned nwords, u64 plaintext[], u64 ciphertext[])
{
u8 key[nwords] = "haallo";
unsigned nrounds = 72;
u8 rot[8][2] = {
{14, 16},
{52, 57},
{23, 40},
{5, 37},
{25, 33},
{46, 12},
{58, 22},
{32, 32},
};
u8 perm[4][4] = {
{0, 1, 2, 3},
{0, 3, 2, 1},
{0, 1, 2, 3},
{0, 3, 2, 1},
};
u64 subkeys[nrounds / 4 + 1][nwords];
u64 tweak[2] = {0};
u64 xkey[nwords + 1];
u64 xtweak[3] = {tweak[0], tweak[1], tweak[0] ^ tweak[1]};
xkey[nwords] = 0xcafebabecafe;
for (unsigned w = 0; w < nwords; w++)
{
xkey[w] = key[w];
xkey[nwords] ^= key[w];
}
for (unsigned i = 0; i < nrounds / 4 + 1; i++)
{
for (unsigned w = 0; w < nwords; w++)
{
subkeys[i][w] = xkey[(i + w) % (nwords + 1)];
}
subkeys[i][nwords - 3] += xtweak[i % 3];
subkeys[i][nwords - 2] += xtweak[(i + 1) % 3];
subkeys[i][nwords - 1] += i;
}
u64 v[nwords];
for (unsigned w = 0; w < nwords; w++)
{
v[w] = ciphertext[w] - subkeys[nrounds / 4][w];
}
for (unsigned n = nrounds; n > 0;)
{
n -= 8;
threefish_roundinv(nwords, v, rot[(n + 7) % 8], perm[3]);
threefish_roundinv(nwords, v, rot[(n + 6) % 8], perm[2]);
threefish_roundinv(nwords, v, rot[(n + 5) % 8], perm[1]);
threefish_roundinv(nwords, v, rot[(n + 4) % 8], perm[0]);
for (unsigned w = 0; w < nwords; w++)
{
v[w] -= subkeys[n / 4 + 1][w];
}
threefish_roundinv(nwords, v, rot[(n + 3) % 8], perm[3]);
threefish_roundinv(nwords, v, rot[(n + 2) % 8], perm[2]);
threefish_roundinv(nwords, v, rot[(n + 1) % 8], perm[1]);
threefish_roundinv(nwords, v, rot[(n + 0) % 8], perm[0]);
for (unsigned w = 0; w < nwords; w++)
{
v[w] -= subkeys[n / 4][w];
}
}
for (unsigned w = 0; w < nwords; w++)
{
plaintext[w] = v[w];
}
}
void encrypt_string(string s, u64 ciphertext[])
{
unsigned n1 = s.size();
u64 plaintext[n1] = {0};
for (size_t i = 0; i < n1; i++)
{
plaintext[i] = s[i];
}
encrypt(n1, plaintext, ciphertext);
}
string decrypt_string(unsigned n1, u64 ciphertext[])
{
u64 plaintext[n1];
decrypt(n1, plaintext, ciphertext);
string r = "";
for (size_t i = 0; i < n1; i++)
{
r += (u8)plaintext[i];
}
return r;
}
int main()
{
string s1 = "Moin";
u64 ciphertext[s1.size()] = {0};
cout << s1 << endl;
encrypt_string(s1, ciphertext);
string s2 = decrypt_string(s1.size(), ciphertext);
cout << s2 << endl;
return 0;
}