/* ----------------------------------------------------------------- Software simulation of a 1024-bit hardware hash. A work-in-progress with the goal of entering the NIST cryptographic hash competition. Bits are arranged in a 32 x 32 grid, well, an 8x8 grid of 4x4-bit blocks. The hash consists of horizontal and vertical XORs, followed by the a nonlinear mix applied to every 4x4-bit block. The horizontal wires XOR bit 2i of block [*][i] with bit 2i of block [*][j], where i is in 0..7, j is in 0..7, and i != j. Vertical wires XOR bit 2i+1 of [i][*] with bit 2i+1 of block [j][*] where i in 0..7, j in 0..7, and i != j. The nonlinear mix of 4x4 blocks is actually an LFSR for bits 0..7, and (s)^((w&x)|(y&z)) for bits 8..15, where w,x,y,z are all less than s, and maybe complemented. Hillclimbing was used to find those constants. Counting constants are XORed with s to break the symmetry between the 64 4x4 blocks, so each differs in at least two ways. The hash is done in rounds. Each round does the horizontal and vertical XORs, then mixes the 4x4 blocks. The internal state is represented by 16 64-bit integers, x[16], where x[k] & (1<<(8*i+j)) is the kth bit of block [i][j]. This allows the nonlinear mixing to be done for all blocks simulateously, and also allows horizontal and vertical wires to be handled in bulk. Hillclimbing used 256 seeds created by placing 0..255 in one byte of one of those 16 64-bit integers, set the rest to all zeros or all ones. All one-bit diffs were tested, and all caused all 1024 bits to change with non-zero (and non-one) probability after 5 rounds forward. That makes these constants unusually good at mixing one-bit differences with mostly-zero or mostly-one states. That might make it unusually bad at something else, who knows. But my belief is it makes it better than average at most things. ----------------------------------------------------------------- */ #include #include typedef unsigned long u4; typedef unsigned long long u8; typedef struct ranctx { u4 a; u4 b; u4 c; u4 d; } ranctx; #define rot(x,k) ((x<>(32-k))) u4 ranval( ranctx *x ) { u4 e = x->a - rot(x->b, 27); x->a = x->b ^ rot(x->c, 17); x->b = x->c + x->d; x->c = x->d + e; x->d = e + x->a; return e; } #define LEN 16 #define ROWS 8 #define COLS 8 #define ROUNDS 256 /* do one round of the hash */ void hash(u8 x[LEN]) { u8 i, j; u8 y[LEN]; /* Horizontal linear mixing */ for (i=0; i>1); j = j ^ ((j&0x3333333333333333LL)<<2) ^ ((j&0xccccccccccccccccLL)>>2); j = j ^ ((j&0x0f0f0f0f0f0f0f0fLL)<<4) ^ ((j&0xf0f0f0f0f0f0f0f0LL)>>4); x[i] ^= j; } /* Vertical linear mixing */ for (i=1; i>32); j = j ^ (j<<16) ^ (j>>48); j = j ^ (j<<8) ^ (j>>56); x[i] ^= j; } for (i=0; i>1); j = j ^ ((j&0x3333333333333333LL)<<2) ^ ((j&0xccccccccccccccccLL)>>2); j = j ^ ((j&0x0f0f0f0f0f0f0f0fLL)<<4) ^ ((j&0xf0f0f0f0f0f0f0f0LL)>>4); x[i] ^= j; } /* Vertical linear mixing */ for (i=1; i>32); j = j ^ (j<<16) ^ (j>>48); j = j ^ (j<<8) ^ (j>>56); x[i] ^= j; } } void show(u8 x[LEN]) { int i,j,k; for (i=0; i=4; ) { printf(" %s", (x[k] & (((u8)1)<<(COLS*i+j))) ? "1" : "0"); } } printf("\n"); for (j=0; j=12; ) { printf(" %s", (x[k] & (((u8)1)<<(COLS*i+j))) ? "1" : "0"); } } printf("\n"); } printf("\n"); } void showx(float x, float *worst, int verbose) { x = x/ROUNDS; if (x < *worst) *worst = x; if (verbose) printf("%3d", (int)(100*x + 0.5)); if (1.0-x < *worst) *worst = 1.0-x; } float showc(float c[ROWS][COLS][LEN], int verbose) { int i,j,k; float worst = 1.0; for (i=0; i=4; ) { showx(c[i][j][k], &worst, verbose); } if (verbose) printf(" "); } if (verbose) printf("\n"); for (j=0; j=12; ) { showx(c[i][j][k], &worst, verbose); } if (verbose) printf(" "); } if (verbose) printf("\n\n"); } /* printf(" worst = %f\n", worst); */ return worst; } void driver(ranctx *rctx) { u8 x[LEN], y[LEN]; u4 m,n,o,p,count,i,j,k; float val, worst = 1.0; float zcount = 0.0; /* see how thoroughly bit "tab[m][n] & (1<