From 76b80ded3a68707773e3dc7d5888bcd669ea248f Mon Sep 17 00:00:00 2001 From: Andrey Rys Date: Sun, 17 Mar 2019 17:41:21 +0700 Subject: [PATCH] Threefish cipher based raw PRN/noise generator. --- .gitignore | 10 ++++++ Makefile | 27 ++++++++++++++++ README | 43 +++++++++++++++++++++++++ tfcore.h | 50 +++++++++++++++++++++++++++++ tfdef.h | 34 ++++++++++++++++++++ tfe.c | 57 +++++++++++++++++++++++++++++++++ tfe.h | 17 ++++++++++ tfenc.c | 51 +++++++++++++++++++++++++++++ tfprng.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tfprng.h | 22 +++++++++++++ tfrand.c | 31 ++++++++++++++++++ 11 files changed, 436 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README create mode 100644 tfcore.h create mode 100644 tfdef.h create mode 100644 tfe.c create mode 100644 tfe.h create mode 100644 tfenc.c create mode 100644 tfprng.c create mode 100644 tfprng.h create mode 100644 tfrand.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c26d2c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +_* +*.swp +*.o +*.out +*.key +*.diff +*.patch +tags +libtf.a +tfrand diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d4b613c --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +PROGS = tfrand +PROGSRCS = $(PROGS:=.c) +PROGOBJS = $(PROGSRCS:.c=.o) +SRCS = $(filter-out $(PROGSRCS), $(wildcard *.c)) +HDRS = $(wildcard *.h) +OBJS = $(SRCS:.c=.o) + +ifneq (,$(DEBUG)) +override CFLAGS+=-Wall -O0 -g +else +override CFLAGS+=-O3 +endif + +default: $(OBJS) libtf.a tfrand +all: $(OBJS) libtf.a $(PROGS) + +%.o: %.c $(HDRS) + $(CC) $(CFLAGS) -c -o $@ $< + +libtf.a: $(OBJS) + $(AR) cru $@ $^ + +$(PROGS): %: %.o libtf.a + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ + +clean: + rm -f libtf.a $(OBJS) $(PROGOBJS) $(SUPPOBJS) $(PROGS) diff --git a/README b/README new file mode 100644 index 0000000..63f1e7c --- /dev/null +++ b/README @@ -0,0 +1,43 @@ +Threefish cipher based raw PRN/noise generator. + +PURPOSE + This PRNG is shortened, 32 bit integer version of Threefish-256 block cipher. + It is technically a 128 bit block cipher with 256 bit key. + + It's purpose is to generate fillers fast, for use in file or disk shredders. + It is not meant to be secure, i.e. collision/bias free, but it is good at + generating random noise equal to average output of /dev/urandom, yet much faster. + + Typical performance on Athlon 5000+, x86-32 is about 170M/sec. + + Included tfrand program generates random noise to stdout. + +USAGE + Build example: + + make tfrand + + Test it: + + ./tfrand >rng.out + + Measure performance (install tfcrypt first): + + ./tfrand | tfcrypt -V.5 -P - /dev/null + + Use libtf.a library in your code, see headers for function references: + tfe.h: STREAM reference. + tfprng.h: PRNG reference. + +WARNING + Do NOT use it as cipher! It's just a random block generator. + You have been warned. + +ORIGIN + This code is derived from tfcipher library. + +LICENSE + Public domain -- free to reuse and adapt. + +AUTHOR + Andrey Rys , 17Mar2019. diff --git a/tfcore.h b/tfcore.h new file mode 100644 index 0000000..f2dd59a --- /dev/null +++ b/tfcore.h @@ -0,0 +1,50 @@ +#ifndef _THREEFISH_CIPHER_CORE_HEADER +#define _THREEFISH_CIPHER_CORE_HEADER + +#ifndef _THREEFISH_CIPHER_DEFINITIONS_HEADER +#error Threefish definitions header is required! Include tfdef.h first. +#endif + +#define ROL(x, s, max) ((x << s) | (x >> (-s & (max-1)))) +#define ROR(x, s, max) ((x >> s) | (x << (-s & (max-1)))) + +#define KE_MIX(x, y, k1, k2, sl) \ + do { \ + x += k1; \ + y += x; \ + y += k2; \ + x = ROL(x, sl, TF_UNIT_BITS); \ + x ^= y; \ + } while (0) + +#define BE_MIX(x, y, sl) \ + do { \ + x += y; \ + y = ROL(y, sl, TF_UNIT_BITS); \ + y ^= x; \ + } while (0) + +#define KD_MIX(x, y, k1, k2, sr) \ + do { \ + x ^= y; \ + x = ROR(x, sr, TF_UNIT_BITS); \ + y -= x; \ + y -= k2; \ + x -= k1; \ + } while (0) + +#define BD_MIX(x, y, sr) \ + do { \ + y ^= x; \ + y = ROR(y, sr, TF_UNIT_BITS); \ + x -= y; \ + } while (0) + +enum tf_rotations { + TFS_KS01 = 7, TFS_KS02 = 25, TFS_KS03 = 19, TFS_KS04 = 7, + TFS_BS01 = 5, TFS_BS02 = 27, TFS_BS03 = 26, TFS_BS04 = 6, + TFS_BS05 = 14, TFS_BS06 = 11, TFS_BS07 = 24, TFS_BS08 = 18, + TFS_BS09 = 9, TFS_BS10 = 24, TFS_BS11 = 6, TFS_BS12 = 7, +}; + +#endif diff --git a/tfdef.h b/tfdef.h new file mode 100644 index 0000000..38b354c --- /dev/null +++ b/tfdef.h @@ -0,0 +1,34 @@ +#ifndef _THREEFISH_CIPHER_DEFINITIONS_HEADER +#define _THREEFISH_CIPHER_DEFINITIONS_HEADER + +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#endif + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif + +#include +#include + +#define TF_UNIT_TYPE uint32_t + +#define TF_NR_BLOCK_BITS 128 +#define TF_NR_KEY_BITS 256 +#define TF_NR_BLOCK_UNITS 4 +#define TF_NR_KEY_UNITS 8 + +#define TF_BYTE_TYPE uint8_t +#define TF_SIZE_UNIT (sizeof(TF_UNIT_TYPE)) +#define TF_BLOCK_SIZE (TF_SIZE_UNIT * TF_NR_BLOCK_UNITS) +#define TF_KEY_SIZE (TF_SIZE_UNIT * TF_NR_KEY_UNITS) + +#define TF_TO_BITS(x) ((x) * 8) +#define TF_FROM_BITS(x) ((x) / 8) +#define TF_MAX_BITS TF_NR_BLOCK_BITS +#define TF_UNIT_BITS (TF_SIZE_UNIT * 8) + +void tf_encrypt_rawblk(TF_UNIT_TYPE *O, const TF_UNIT_TYPE *I, const TF_UNIT_TYPE *K); + +#endif diff --git a/tfe.c b/tfe.c new file mode 100644 index 0000000..8c58979 --- /dev/null +++ b/tfe.c @@ -0,0 +1,57 @@ +#include +#include "tfdef.h" +#include "tfe.h" + +void tfe_init_iv(struct tfe_stream *tfe, const void *key, const void *iv) +{ + memset(tfe, 0, sizeof(struct tfe_stream)); + memcpy(tfe->key, key, TF_KEY_SIZE); + if (iv) memcpy(tfe->iv, iv, TF_BLOCK_SIZE); + tfe->carry_bytes = 0; +} + +void tfe_init(struct tfe_stream *tfe, const void *key) +{ + tfe_init_iv(tfe, key, NULL); +} + +void tfe_emit(void *dst, size_t szdst, struct tfe_stream *tfe) +{ + TF_BYTE_TYPE *udst = dst; + size_t sz = szdst; + + if (!dst && szdst == 0) { + memset(tfe, 0, sizeof(struct tfe_stream)); + return; + } + + if (tfe->carry_bytes > 0) { + if (tfe->carry_bytes > szdst) { + memcpy(udst, tfe->carry_block, szdst); + memmove(tfe->carry_block, tfe->carry_block+szdst, tfe->carry_bytes-szdst); + tfe->carry_bytes -= szdst; + return; + } + + memcpy(udst, tfe->carry_block, tfe->carry_bytes); + udst += tfe->carry_bytes; + sz -= tfe->carry_bytes; + tfe->carry_bytes = 0; + } + + if (sz >= TF_BLOCK_SIZE) { + do { + tf_encrypt_rawblk(tfe->iv, tfe->iv, tfe->key); + memcpy(udst, tfe->iv, TF_BLOCK_SIZE); + udst += TF_BLOCK_SIZE; + } while ((sz -= TF_BLOCK_SIZE) >= TF_BLOCK_SIZE); + } + + if (sz) { + tf_encrypt_rawblk(tfe->iv, tfe->iv, tfe->key); + memcpy(udst, tfe->iv, sz); + udst = (TF_BYTE_TYPE *)tfe->iv; + tfe->carry_bytes = TF_BLOCK_SIZE-sz; + memcpy(tfe->carry_block, udst+sz, tfe->carry_bytes); + } +} diff --git a/tfe.h b/tfe.h new file mode 100644 index 0000000..f5793b1 --- /dev/null +++ b/tfe.h @@ -0,0 +1,17 @@ +#ifndef _TF_STREAM_CIPHER_DEFS +#define _TF_STREAM_CIPHER_DEFS + +#include "tfdef.h" + +struct tfe_stream { + TF_UNIT_TYPE key[TF_NR_KEY_UNITS]; + TF_UNIT_TYPE iv[TF_NR_BLOCK_UNITS]; + TF_BYTE_TYPE carry_block[TF_BLOCK_SIZE]; + size_t carry_bytes; +}; + +void tfe_init(struct tfe_stream *tfe, const void *key); +void tfe_init_iv(struct tfe_stream *tfe, const void *key, const void *iv); +void tfe_emit(void *dst, size_t szdst, struct tfe_stream *tfe); + +#endif diff --git a/tfenc.c b/tfenc.c new file mode 100644 index 0000000..d0727dc --- /dev/null +++ b/tfenc.c @@ -0,0 +1,51 @@ +#include "tfdef.h" +#include "tfcore.h" + +#define PROCESS_BLOCKP(x,k1,k2,k3,k4,k5,k6) \ + do { \ + KE_MIX(Y, X, k1 + k2, k3, TFS_KS01); \ + KE_MIX(T, Z, k4 + x, k5 + k6, TFS_KS02); \ + \ + BE_MIX(X, T, TFS_BS01); BE_MIX(Z, Y, TFS_BS02); \ + BE_MIX(X, Y, TFS_BS03); BE_MIX(Z, T, TFS_BS04); \ + BE_MIX(X, T, TFS_BS05); BE_MIX(Z, Y, TFS_BS06); \ + } while (0) + +#define PROCESS_BLOCKN(x,k1,k2,k3,k4,k5,k6) \ + do { \ + KE_MIX(Y, X, k1 + k2, k3, TFS_KS03); \ + KE_MIX(T, Z, k4 + x, k5 + k6, TFS_KS04); \ + \ + BE_MIX(X, T, TFS_BS07); BE_MIX(Z, Y, TFS_BS08); \ + BE_MIX(X, Y, TFS_BS09); BE_MIX(Z, T, TFS_BS10); \ + BE_MIX(X, T, TFS_BS11); BE_MIX(Z, Y, TFS_BS12); \ + } while (0) + +void tf_encrypt_rawblk(TF_UNIT_TYPE *O, const TF_UNIT_TYPE *I, const TF_UNIT_TYPE *K) +{ + TF_UNIT_TYPE X, Y, Z, T; + TF_UNIT_TYPE K0, K1, K2, K3; + TF_UNIT_TYPE K4, T0, T1, T2; + + X = I[0]; Y = I[1]; Z = I[2]; T = I[3]; + + K0 = K[0]; K1 = K[1]; K2 = K[2]; K3 = K[3]; + K4 = K[4]; T0 = K[5]; T1 = K[6]; T2 = K[7]; + + PROCESS_BLOCKP( 1,K1,T0,K0,K3,K2,T1); + PROCESS_BLOCKN( 2,K2,T1,K1,K4,K3,T2); + PROCESS_BLOCKP( 3,K3,T2,K2,K0,K4,T0); + PROCESS_BLOCKN( 4,K4,T0,K3,K1,K0,T1); + + PROCESS_BLOCKP( 5,K0,T1,K4,K2,K1,T2); + PROCESS_BLOCKN( 6,K1,T2,K0,K3,K2,T0); + PROCESS_BLOCKP( 7,K2,T0,K1,K4,K3,T1); + PROCESS_BLOCKN( 8,K3,T1,K2,K0,K4,T2); + + PROCESS_BLOCKP( 9,K4,T2,K3,K1,K0,T0); + PROCESS_BLOCKN(10,K0,T0,K4,K2,K1,T1); + PROCESS_BLOCKP(11,K1,T1,K0,K3,K2,T2); + PROCESS_BLOCKN(12,K2,T2,K1,K4,K3,T0); + + O[0] = X + K3; O[1] = Y + K4 + T0; O[2] = Z + K0 + T1; O[3] = T + K1 + 18; +} diff --git a/tfprng.c b/tfprng.c new file mode 100644 index 0000000..d4370f3 --- /dev/null +++ b/tfprng.c @@ -0,0 +1,94 @@ +#include +#include "tfe.h" +#include "tfprng.h" + +struct tf_prng_data { + struct tfe_stream tfe; + short init; +}; + +struct tf_prng_data tf_prng_sdata; + +size_t tf_prng_datasize(void) +{ + return sizeof(struct tf_prng_data); +} + +void tf_prng_seedkey_r(void *sdata, const void *skey) +{ + TF_UNIT_TYPE k[TF_NR_KEY_UNITS]; + struct tf_prng_data *rprng = sdata; + + memset(rprng, 0, tf_prng_datasize()); + if (!skey) return; + + memcpy(k, skey, TF_KEY_SIZE); + tfe_init(&rprng->tfe, k); + rprng->init = 1; + + memset(k, 0, TF_KEY_SIZE); +} + +void tf_prng_seedkey(const void *skey) +{ + tf_prng_seedkey_r(&tf_prng_sdata, skey); +} + +void tf_prng_genrandom_r(void *sdata, void *result, size_t need) +{ + struct tf_prng_data *rprng = sdata; + memset(result, 0, need); + tfe_emit(result, need, &rprng->tfe); +} + +void tf_prng_genrandom(void *result, size_t need) +{ + tf_prng_genrandom_r(&tf_prng_sdata, result, need); +} + +void tf_prng_seed_r(void *sdata, TF_UNIT_TYPE seed) +{ + TF_UNIT_TYPE k[TF_NR_KEY_UNITS]; + struct tf_prng_data *rprng = sdata; + size_t x; + + memset(rprng, 0, tf_prng_datasize()); + for (x = 0; x < TF_NR_KEY_UNITS; x++) k[x] = seed; + tfe_init(&rprng->tfe, k); + rprng->init = 1; + + memset(k, 0, TF_KEY_SIZE); +} + +void tf_prng_seed(TF_UNIT_TYPE seed) +{ + tf_prng_seed_r(&tf_prng_sdata, seed); +} + +TF_UNIT_TYPE tf_prng_random_r(void *sdata) +{ + struct tf_prng_data *rprng = sdata; + TF_UNIT_TYPE r; + + if (!rprng->init) return 0; + + tfe_emit(&r, sizeof(r), &rprng->tfe); + return r; +} + +TF_UNIT_TYPE tf_prng_random(void) +{ + return tf_prng_random_r(&tf_prng_sdata); +} + +TF_UNIT_TYPE tf_prng_range_r(void *sdata, TF_UNIT_TYPE s, TF_UNIT_TYPE d) +{ + TF_UNIT_TYPE c = tf_prng_random_r(sdata); + if (d <= s) return s; + return s + c / ((TF_UNIT_TYPE)~0 / (d - s + 1) + 1); +} + +TF_UNIT_TYPE tf_prng_range(TF_UNIT_TYPE s, TF_UNIT_TYPE d) +{ + return tf_prng_range_r(&tf_prng_sdata, s, d); +} diff --git a/tfprng.h b/tfprng.h new file mode 100644 index 0000000..5fbab0a --- /dev/null +++ b/tfprng.h @@ -0,0 +1,22 @@ +#ifndef _TF_PRNG_DEFINITIONS_HEADER +#define _TF_PRNG_DEFINITIONS_HEADER + +#include +#include "tfdef.h" + +#define TF_PRNG_KEY_SIZE TF_KEY_SIZE +#define TF_PRNG_SIZE_UNIT TF_SIZE_UNIT + +size_t tf_prng_datasize(void); +void tf_prng_seedkey_r(void *sdata, const void *skey); +void tf_prng_seedkey(const void *skey); +void tf_prng_genrandom_r(void *sdata, void *result, size_t need); +void tf_prng_genrandom(void *result, size_t need); +void tf_prng_seed_r(void *sdata, TF_UNIT_TYPE seed); +void tf_prng_seed(TF_UNIT_TYPE seed); +TF_UNIT_TYPE tf_prng_random_r(void *sdata); +TF_UNIT_TYPE tf_prng_random(void); +TF_UNIT_TYPE tf_prng_range_r(void *sdata, TF_UNIT_TYPE s, TF_UNIT_TYPE d); +TF_UNIT_TYPE tf_prng_range(TF_UNIT_TYPE s, TF_UNIT_TYPE d); + +#endif diff --git a/tfrand.c b/tfrand.c new file mode 100644 index 0000000..cbbe82a --- /dev/null +++ b/tfrand.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include "tfdef.h" +#include "tfprng.h" + +#define DATASIZE 65536 + +static char data[DATASIZE]; +static char key[TF_KEY_SIZE]; + +int main(int argc, char **argv) +{ + int fd; + + fd = open("/dev/urandom", O_RDONLY); + if (fd != -1) { + read(fd, key, sizeof(key)); + close(fd); + } + + tf_prng_seedkey(key); + while (1) { + tf_prng_genrandom(data, DATASIZE); + if (write(1, data, DATASIZE) == -1) return 1; + } + tf_prng_seedkey(NULL); + + return 0; +}