First version of Threefish512-CTR with BLAKE3-MAC & a custom shamirs secret sharing port alongside C bridges with test vectors & Compilation instructions (messed up .gitignore earlier, sorry)

This commit is contained in:
2026-01-13 21:30:55 +01:00
parent 1614421631
commit a435d058d2
24 changed files with 412 additions and 0 deletions

View File

@@ -0,0 +1,412 @@
module crypto.threefish512_ctr.main;
import std.random : Random, unpredictableSeed, uniform;
import std.algorithm : min;
import std.conv : emplace;
// import dmd.common.blake3;
import std.array : appender;
import std.exception : enforce;
/* honestly dk if this is cryptographically "secure", as i took most of the implmentation from some russian dub package
i then implemented pkcs5 back then as thats all i knew
then figured just that is not enough and i only had raw stream ciphers so far
so i tried to research how to implement ctr and shit was too complicated so i vibecoded it (probably some flaws and inefficiet, but should be good enough)
and added blake3-mac as without it the implementation would be vulnerable to
replay, paddingfree oracles, bitflip and plaintext-recovery attacks */
// anyways, from what i read forging a blake3 mac or breaking threefish512 in itself
// is beyond fantasy rn so anyone who wants to use this should be fine
// memcpy
extern(C) nothrow @nogc void* memcpy(void* dst, const void* src, size_t n);
extern(C)
{
struct blake3_hasher {
ubyte[256] opaque;
}
void blake3_hasher_init_keyed(blake3_hasher* hasher, const ubyte* key);
void blake3_hasher_update(blake3_hasher* hasher, const ubyte* input, size_t len);
void blake3_hasher_finalize(blake3_hasher* hasher, ubyte* _out, size_t out_len);
}
class Threefish512
{
private:
enum BLOCK_SIZE = 64;
enum Nw = 8;
enum Nr = 72;
enum Ns = Nr / 4;
uint[8] p = [2, 1, 4, 7, 6, 5, 0, 3];
uint[8] p_1 = [6, 1, 0, 7, 2, 5, 4, 3];
uint[4][8] r = [
[46, 36, 19, 37],
[33, 27, 14, 42],
[17, 49, 36, 39],
[44, 9 , 54, 56],
[39, 30, 34, 24],
[13, 50, 10, 17],
[25, 29, 39, 43],
[8 , 35, 56, 22]
];
ulong[3] t;
ulong[8][Ns + 1] subKeys;
alias _mod8 = (ulong a) => a & 7UL;
private void _mix(ref ulong[2] x, ulong r, ref ulong[2] y)
{
y[0] = x[0] + x[1];
y[1] = (x[1] << r) | (x[1] >> (64 - r));
y[1] ^= y[0];
}
private void _demix(ref ulong[2] y, ulong r, ref ulong[2] x)
{
y[1] ^= y[0];
x[1] = (y[1] << (64 - r)) | (y[1] >> r);
x[0] = y[0] - x[1];
}
private void _setup(ulong* keyData, ulong* tweakData) @system
{
ulong[8] K;
ulong[2] T;
ulong[9] key;
ulong kNw = 0x1BD11BDAA9FC1A22;
memcpy(&K[0], keyData, 64);
memcpy(&T[0], tweakData, 16);
foreach (i; 0 .. Nw)
{
kNw ^= K[i];
key[i] = K[i];
}
key[8] = kNw;
t[0] = T[0];
t[1] = T[1];
t[2] = T[0] ^ T[1];
foreach (round; 0 .. Ns + 1)
{
foreach (i; 0 .. Nw)
{
subKeys[round][i] = key[(round + i) % (Nw + 1)];
if (i == Nw - 3) subKeys[round][i] += t[round % 3];
else if (i == Nw - 2) subKeys[round][i] += t[(round + 1) % 3];
else if (i == Nw - 1) subKeys[round][i] += round;
}
}
}
private void _blockEncrypt(ulong* plainData, ulong* c)
{
ulong[8] f;
ulong[8] e;
ulong[2] x, y;
ulong[8] v;
memcpy(&v[0], plainData, 64);
foreach (round; 0 .. Nr)
{
uint s = round >> 2;
foreach (i; 0 .. Nw)
e[i] = (round % 4 == 0) ? v[i] + subKeys[s][i] : v[i];
foreach (i; 0 .. Nw/2)
{
x[0] = e[i*2]; x[1] = e[i*2+1];
_mix(x, r[_mod8(round)][i], y);
f[i*2] = y[0]; f[i*2+1] = y[1];
}
foreach (i; 0 .. Nw) v[i] = f[p[i]];
}
foreach (i; 0 .. Nw) c[i] = v[i] + subKeys[Ns][i];
}
public static ulong[8] generateKey()
{
ulong[8] key;
auto rng = Random(unpredictableSeed);
foreach (i; 0 .. 8) key[i] = uniform!ulong(rng);
return key;
}
public static ulong generateNonce()
{
auto rng = Random(unpredictableSeed);
return uniform!ulong(rng);
}
public static ubyte[32] generateMacKey() {
auto rng = Random(unpredictableSeed);
ubyte[32] key;
foreach(i; 0 .. 4) {
ulong part = uniform!ulong(rng);
foreach(b; 0 .. 8)
key[i*8 + b] = cast(ubyte)((part >> (8*b)) & 0xFF);
}
return key;
}
public void encryptCTR(ulong* plainData, ulong* ciphertext, size_t lengthBytes, ulong[8] key, ulong nonce)
{
ulong[2] tweak = [nonce, 0];
//_setup(key, [nonce, 0]);
_setup(key.ptr, tweak.ptr);
ulong[8] zeroBlock = 0;
size_t numBlocks = (lengthBytes + 63) / 64;
ulong counter = 0;
foreach (blockIndex; 0 .. numBlocks)
{
t[0] = nonce;
t[1] = counter;
t[2] = t[0] ^ t[1];
ulong[8] keystream;
_blockEncrypt(zeroBlock.ptr, keystream.ptr);
size_t start = blockIndex * 8;
size_t blockULongs = ((lengthBytes - start + 7)/8).min(8);
foreach (i; 0 .. blockULongs)
ciphertext[start + i] = plainData[start + i] ^ keystream[i];
counter++;
}
}
private bool ctEqual(ubyte[32] a, ubyte[32] b)
{
ubyte diff = 0;
foreach(i; 0..32)
diff |= a[i] ^ b[i];
return diff == 0;
}
public ubyte[32] gen_tag(ubyte[32] key_mac, ubyte[] nonce, ubyte[] ciphertext)
{
auto data = appender!(ubyte[])();
data.put(nonce);
data.put(ciphertext);
ubyte[32] comp_tag = blake3_mac(key_mac, data.data);
return comp_tag;
}
private bool verify_tag(ubyte[32] key_mac, ubyte[] nonce, ubyte[] ciphertext, ubyte[32] exp_tag)
{
ubyte[32] comp_tag = gen_tag(key_mac, nonce, ciphertext);
return ctEqual(comp_tag, exp_tag);
}
private ubyte[32] blake3_mac(ubyte[32] key_mac, ubyte[] data)
{
blake3_hasher hasher;
blake3_hasher_init_keyed(&hasher, key_mac.ptr);
blake3_hasher_update(&hasher, data.ptr, data.length);
ubyte[32] oout;
blake3_hasher_finalize(&hasher, oout.ptr, 32);
return oout;
}
public void decryptCTR(ulong* ciphertext, ulong* plainData, size_t lengthBytes, ulong[8] key, ulong nonce, ubyte[32] key_mac, ubyte[32] exp_tag)
{
ubyte[8] nbytes;
foreach(i; 0 .. 8) nbytes[i] = cast(ubyte)((nonce >> (8*i)) & 0xFF);
ubyte[] ct_bytes = cast(ubyte[])(cast(ubyte*)ciphertext)[0 .. lengthBytes];
if (verify_tag(key_mac, nbytes[], ct_bytes, exp_tag)) {
encryptCTR(ciphertext, plainData, lengthBytes, key, nonce);
}
}
public void encryptBytes(ubyte[] plaintext, out ubyte[] ciphertext, out ubyte[32] tag, out ulong nonce, out ulong[8] key_enc, out ubyte[32] key_mac)
{
ciphertext.length = plaintext.length;
size_t nLongs = (plaintext.length + 7)/8;
ulong[] plainLongs;
plainLongs.length = nLongs;
foreach(i; 0 .. nLongs) {
ulong val = 0;
foreach(b; 0 .. 8) {
size_t idx = i*8 + b;
if(idx < plaintext.length)
val |= cast(ulong)plaintext[idx] << (8*b);
}
plainLongs[i] = val;
}
ulong[] cipherLongs;
cipherLongs.length = nLongs;
encryptCTR(plainLongs.ptr, cipherLongs.ptr, plaintext.length, key_enc, nonce);
foreach(i; 0 .. nLongs) {
foreach(b; 0 .. 8) {
size_t idx = i*8 + b;
if(idx < plaintext.length)
ciphertext[idx] = cast(ubyte)((cipherLongs[i] >> (8*b)) & 0xFF);
}
}
auto data = appender!(ubyte[])();
foreach(b; 0 .. 8)
data.put(cast(ubyte)((nonce >> (8*b)) & 0xFF));
data.put(ciphertext);
tag = blake3_mac(key_mac, data.data);
}
public ubyte[] decryptBytes(ubyte[] ciphertext, ubyte[32] expected_tag, ubyte[32] key_mac, ulong[8] key_enc, ulong nonce)
{
auto data = appender!(ubyte[])();
foreach(b; 0 .. 8)
data.put(cast(ubyte)((nonce >> (8*b)) & 0xFF));
data.put(ciphertext);
enforce(ctEqual(blake3_mac(key_mac, data.data), expected_tag), "MAC verification failed");
size_t nLongs = (ciphertext.length + 7)/8;
ulong[] cipherLongs;
cipherLongs.length = nLongs;
foreach(i; 0 .. nLongs) {
ulong val = 0;
foreach(b; 0 .. 8) {
size_t idx = i*8 + b;
if(idx < ciphertext.length)
val |= cast(ulong)ciphertext[idx] << (8*b);
}
cipherLongs[i] = val;
}
ulong[] plainLongs;
plainLongs.length = nLongs;
encryptCTR(cipherLongs.ptr, plainLongs.ptr, ciphertext.length, key_enc, nonce);
ubyte[] plaintext;
plaintext.length = ciphertext.length;
foreach(i; 0 .. nLongs) {
foreach(b; 0 .. 8) {
size_t idx = i*8 + b;
if(idx < ciphertext.length)
plaintext[idx] = cast(ubyte)((plainLongs[i] >> (8*b)) & 0xFF);
}
}
return plaintext;
}
}
__gshared Threefish512 tfInstance = new Threefish512();
// Key / MAC / Nonce wrappers
extern(C) export void generateKeyC(ulong* c_out) {
auto key = Threefish512.generateKey(); // returns ulong[8]
for(size_t i = 0; i < 8; i++)
c_out[i] = key[i];
}
extern(C) export ulong generateNonceC() {
return Threefish512.generateNonce();
}
extern(C) export void generateMacKeyC(ubyte* c_out) {
auto key = Threefish512.generateMacKey(); // returns ubyte[32]
for(size_t i = 0; i < 32; i++)
c_out[i] = key[i];
}
// CTR encryption / decryption wrappers
extern(C) export void encryptCTR_C(ulong* plainData, ulong* ciphertext, size_t lengthBytes,
ulong* key_enc, ulong nonce) {
ulong[8] k;
for(size_t i = 0; i < 8; i++) k[i] = key_enc[i];
tfInstance.encryptCTR(plainData, ciphertext, lengthBytes, k, nonce);
}
extern(C) export void decryptCTR_C(ulong* ciphertext, ulong* plainData, size_t lengthBytes,
ulong* key_enc, ulong nonce,
ubyte* key_mac, ubyte* exp_tag) {
ulong[8] k; ubyte[32] mac; ubyte[32] tag;
for(size_t i = 0; i < 8; i++) k[i] = key_enc[i];
for(size_t i = 0; i < 32; i++) { mac[i] = key_mac[i]; tag[i] = exp_tag[i]; }
tfInstance.decryptCTR(ciphertext, plainData, lengthBytes, k, nonce, mac, tag);
}
// encryptBytes / decryptBytes wrappers
extern(C) export void encryptBytes_C(ubyte* plaintext, size_t plainLen,
ubyte* ciphertext, ubyte* tag,
ulong* nonceOut, ulong* key_encOut, ubyte* key_macOut) {
ulong[8] key_enc = Threefish512.generateKey();
ulong nonce = Threefish512.generateNonce();
ubyte[32] key_mac = Threefish512.generateMacKey();
ubyte[] pt; pt.length = plainLen;
for(size_t i = 0; i < plainLen; i++) pt[i] = plaintext[i];
ubyte[] ct; ct.length = plainLen;
ubyte[32] tag_arr;
tfInstance.encryptBytes(pt, ct, tag_arr, nonce, key_enc, key_mac);
for(size_t i = 0; i < 32; i++) tag[i] = tag_arr[i];
for(size_t i = 0; i < plainLen; i++) ciphertext[i] = ct[i];
for(size_t i = 0; i < 8; i++) key_encOut[i] = key_enc[i];
for(size_t i = 0; i < 32; i++) key_macOut[i] = key_mac[i];
*nonceOut = nonce;
}
extern(C) export void decryptBytes_C(ubyte* ciphertext, size_t cipherLen,
ubyte* plaintextOut,
ubyte* expectedTag, ubyte* key_mac,
ulong* key_enc, ulong nonce) {
ulong[8] k; ubyte[32] mac; ubyte[32] tag;
for(size_t i = 0; i < 8; i++) k[i] = key_enc[i];
for(size_t i = 0; i < 32; i++) { mac[i] = key_mac[i]; tag[i] = expectedTag[i]; }
ubyte[] ct; ct.length = cipherLen;
for(size_t i = 0; i < cipherLen; i++) ct[i] = ciphertext[i];
ubyte[] pt = tfInstance.decryptBytes(ct, tag, mac, k, nonce);
for(size_t i = 0; i < cipherLen; i++) plaintextOut[i] = pt[i];
}
// gen_tag wrapper
extern(C) export void gen_tag_C(ubyte* key_mac, ubyte* nonce, size_t nonceLen,
ubyte* ciphertext, size_t cipherLen, ubyte* outTag) {
ubyte[32] k;
for(size_t i = 0; i < 32; i++) k[i] = key_mac[i];
ubyte[] n; n.length = nonceLen;
for(size_t i = 0; i < nonceLen; i++) n[i] = nonce[i];
ubyte[] ct; ct.length = cipherLen;
for(size_t i = 0; i < cipherLen; i++) ct[i] = ciphertext[i];
ubyte[32] tag = tfInstance.gen_tag(k, n, ct);
for(size_t i = 0; i < 32; i++) outTag[i] = tag[i];
}
extern(C) export void threefish512_block_encrypt_C(
ulong* key, ulong* tweak, ulong* plaintext, ulong* ciphertext)
{
ulong[8] k;
ulong[2] t;
for(size_t i=0;i<8;i++) k[i] = key[i];
for(size_t i=0;i<2;i++) t[i] = tweak[i];
tfInstance._setup(k.ptr, t.ptr);
tfInstance._blockEncrypt(plaintext, ciphertext);
}