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:
BIN
crypto/threefish512_ctr/exts/blake3.obj
Normal file
BIN
crypto/threefish512_ctr/exts/blake3.obj
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/exts/blake3_avx2.o
Normal file
BIN
crypto/threefish512_ctr/exts/blake3_avx2.o
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/exts/blake3_avx2.obj
Normal file
BIN
crypto/threefish512_ctr/exts/blake3_avx2.obj
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/exts/blake3_avx512.o
Normal file
BIN
crypto/threefish512_ctr/exts/blake3_avx512.o
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/exts/blake3_avx512.obj
Normal file
BIN
crypto/threefish512_ctr/exts/blake3_avx512.obj
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/exts/blake3_dispatch.obj
Normal file
BIN
crypto/threefish512_ctr/exts/blake3_dispatch.obj
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/exts/blake3_portable.obj
Normal file
BIN
crypto/threefish512_ctr/exts/blake3_portable.obj
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/exts/blake3_sse2.o
Normal file
BIN
crypto/threefish512_ctr/exts/blake3_sse2.o
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/exts/blake3_sse2.obj
Normal file
BIN
crypto/threefish512_ctr/exts/blake3_sse2.obj
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/exts/blake3_sse41.o
Normal file
BIN
crypto/threefish512_ctr/exts/blake3_sse41.o
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/exts/blake3_sse41.obj
Normal file
BIN
crypto/threefish512_ctr/exts/blake3_sse41.obj
Normal file
Binary file not shown.
412
crypto/threefish512_ctr/main.d
Normal file
412
crypto/threefish512_ctr/main.d
Normal 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, padding‑free 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);
|
||||
}
|
||||
BIN
crypto/threefish512_ctr/main.dll
Normal file
BIN
crypto/threefish512_ctr/main.dll
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/main.exp
Normal file
BIN
crypto/threefish512_ctr/main.exp
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/main.lib
Normal file
BIN
crypto/threefish512_ctr/main.lib
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/main.obj
Normal file
BIN
crypto/threefish512_ctr/main.obj
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/test_vec.exe
Normal file
BIN
crypto/threefish512_ctr/test_vec.exe
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/test_vec.obj
Normal file
BIN
crypto/threefish512_ctr/test_vec.obj
Normal file
Binary file not shown.
BIN
crypto/threefish512_ctr/threefish512.lib
Normal file
BIN
crypto/threefish512_ctr/threefish512.lib
Normal file
Binary file not shown.
Reference in New Issue
Block a user