/**
* sha3sum – SHA-3 (Keccak) checksum calculator
*
* Copyright © 2013 Mattias Andrée (maandree@member.fsf.org)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <alloca.h>
#include <sys/stat.h>
#include "sha3.h"
#define false 0
#define true 1
#define null 0
#define SET void**
#define HEXADECA "0123456789ABCDEF"
/**
* Prints a number of bytes to stdout
*
* @param BYTES:char* The bytes to print
* @param N:long The number of bytes
*/
#define putchars(BYTES, N) fwrite(BYTES, 1, N, stdout)
/**
* Creates a new set
*
* @return The set
*/
SET set_new()
{
long i;
void** rc = (void**)malloc(sizeof(void*) << 4);
for (i = 0; i < 16; i++)
*(rc + i) = 0;
return rc;
}
/**
* Frees a set
*
* @param set The set
*/
void set_free(SET restrict set)
{
if (*(set + 0)) set_free((void**)*(set + 0));
if (*(set + 1)) set_free((void**)*(set + 1));
if (*(set + 2)) set_free((void**)*(set + 2));
if (*(set + 3)) set_free((void**)*(set + 3));
if (*(set + 4)) set_free((void**)*(set + 4));
if (*(set + 5)) set_free((void**)*(set + 5));
if (*(set + 6)) set_free((void**)*(set + 6));
if (*(set + 7)) set_free((void**)*(set + 7));
if (*(set + 8)) set_free((void**)*(set + 8));
if (*(set + 9)) set_free((void**)*(set + 9));
if (*(set + 10)) set_free((void**)*(set + 10));
if (*(set + 11)) set_free((void**)*(set + 11));
if (*(set + 12)) set_free((void**)*(set + 12));
if (*(set + 13)) set_free((void**)*(set + 13));
if (*(set + 14)) set_free((void**)*(set + 14));
if (*(set + 15)) set_free((void**)*(set + 15));
free(set);
}
/**
* Adds an item to a set
*
* @param set The set
* @param item The item
* @param n The length of the item
*/
void set_add(SET restrict set, char* restrict item, long n)
{
long i, j;
void** at = set;
for (i = 0; i < n; i++)
{
long a = (long)((*(item + i)) & 15), b = (long)((*(item + i) >> 4) & 15);
if (*(at + a))
at = (void**)*(at + a);
else
{
at = (void**)(*(at + a) = (void*)malloc(sizeof(void*) << 4));
for (j = 0; j < 16; j++)
*(at + j) = 0;
}
if (*(at + b))
at = (void**)*(at + b);
else
{
at = (void**)(*(at + b) = (void*)malloc(sizeof(void*) << 4));
for (j = 0; j < 16; j++)
*(at + j) = 0;
}
}
}
/**
* Checks if a set contains an item
*
* @param set The set
* @param item The item
* @param n The length of the item
* @return Whether the set contains the item
*/
long set_contains(SET restrict set, byte* restrict item, long n)
{
long i;
void** at = set;
for (i = 0; i < n; i++)
{
long a = (long)((*(item + i)) & 15), b = (long)((*(item + i) >> 4) & 15);
if (*(at + a))
at = (void**)*(at + a);
else
return false;
if (*(at + b))
at = (void**)*(at + b);
else
return false;
}
return true;
}
/**
* String equality comparator
*
* @param a First comparand
* @param b Second comparand
* @return Whether the comparands are equal
*/
long eq(char* restrict a, char* restrict b)
{
while (*a)
if (*a++ != *b++)
return false;
return !*b;
}
/**
* Convert a string to an integer
*
* @param str String representation
* @return Native representation
*/
long parseInt(char* restrict str)
{
long rc = 0;
while (*str)
rc = rc * 10 - (*str++ & 15);
return -rc;
}
/**
* This is the main entry point of the program
*
* @param argc Command line argument count
* @param argv Command line arguments
* @return Exit value, zero on and only on successful execution
*/
int main(int argc, char** argv)
{
char* out_alloc;
byte* stdin_alloc;
long _o, o, _s, s, _r, r, _c, c, _w, w, _i, i, _j, j;
long _O, O, _S, S, _R, R, _C, C, _W, W, _I, I, _J, J;
long binary = false, hex = false, dashed = false;
long multi = 0, fptr = 0, bn;
char** files = (char**)alloca((argc + 2) * sizeof(char*));
char** linger = files + argc;
char* linger0 = (char*)alloca(sizeof(char) << 13);
long a = 0, an = argc - 1;
char** args = argv + 1;
char* cmd = *argv;
_O = _S = _R = _C = _W = _I = _J = false;
O = S = R = C = W = I = J = 0;
o = s = r = c = w = i = j = 0;
*linger = 0;
s = -1;
for (i = 0; *(cmd + i); i++)
if (*(cmd + i) == '/')
s = i;
if (s >= 0)
cmd += s + 1;
_o = 512; /* --outputsize */
if ((cmd[0] == 's') && (cmd[1] == 'h') && (cmd[2] == 'a') && (cmd[3] == '3') && (cmd[4] == '-'))
if ((cmd[5] != 0) && (cmd[6] != 0) && (cmd[7] != 0))
if ((cmd[8] == 's') && (cmd[9] == 'u') && (cmd[10] == 'm') && (cmd[11] == 0))
{
if ((cmd[5] == '2') && (cmd[6] == '2') && (cmd[7] == '4'))
_o = 224;
else if ((cmd[5] == '2') && (cmd[6] == '5') && (cmd[7] == '6'))
_o = 256;
else if ((cmd[5] == '3') && (cmd[6] == '8') && (cmd[7] == '4'))
_o = 384;
else if ((cmd[5] == '5') && (cmd[6] == '1') && (cmd[7] == '2'))
_o = 512;
}
_s = 1600; /* --statesize */
_c = _s - (_o << 1); /* --capacity */
_r = _s - _c; /* --bitrate */
_w = _s / 25; /* --wordsize */
_i = 1; /* --iterations */
_j = 1; /* --squeezes */
for (; a <= an; a++)
{
char* arg = a == an ? null : *(args + a);
if (*linger)
{
if (eq(*linger, "-h") || eq(*linger, "--help"))
{
printf("\n");
printf("SHA-3/Keccak checksum calculator\n");
printf("\n");
printf("USAGE: sha3sum [option...] < file\n");
printf(" sha3sum [option...] file...\n");
printf("\n");
printf("\n");
printf("OPTIONS:\n");
printf(" -r BITRATE\n");
printf(" --bitrate The bitrate to use for SHA-3. (default: %li)\n", _r);
printf(" \n");
printf(" -c CAPACITY\n");
printf(" --capacity The capacity to use for SHA-3. (default: %li)\n", _c);
printf(" \n");
printf(" -w WORDSIZE\n");
printf(" --wordsize The word size to use for SHA-3. (default: %li)\n", _w);
printf(" \n");
printf(" -o OUTPUTSIZE\n");
printf(" --outputsize The output size to use for SHA-3. (default: %li)\n", _o);
printf(" \n");
printf(" -s STATESIZE\n");
printf(" --statesize The state size to use for SHA-3. (default: %li)\n", _s);
printf(" \n");
printf(" -i ITERATIONS\n");
printf(" --iterations The number of hash iterations to run. (default: %li)\n", _i);
printf(" \n");
printf(" -j SQUEEZES\n");
printf(" --squeezes The number of hash squeezes to run. (default: %li)\n", _j);
printf(" \n");
printf(" -h\n");
printf(" --hex Read the input in hexadecimal, rather than binary.\n");
printf(" \n");
printf(" -b\n");
printf(" --binary Print the checksum in binary, rather than hexadecimal.\n");
printf(" \n");
printf(" -m\n");
printf(" --multi Print the checksum at all iterations.\n");
printf("\n");
printf("\n");
printf("COPYRIGHT:\n");
printf("\n");
printf("Copyright © 2013 Mattias Andrée (maandree@member.fsf.org)\n");
printf("\n");
printf("This program is free software: you can redistribute it and/or modify\n");
printf("it under the terms of the GNU Affero General Public License as published by\n");
printf("the Free Software Foundation, either version 3 of the License, or\n");
printf("(at your option) any later version.\n");
printf("\n");
printf("This program is distributed in the hope that it will be useful,\n");
printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
printf("GNU Affero General Public License for more details.\n");
printf("\n");
printf("You should have received a copy of the GNU Affero General Public License\n");
printf("along with this program. If not, see <http://www.gnu.org/licenses/>.\n");
printf("\n");
fflush(stdout);
fflush(stderr);
return 0;
}
else
{
if (*(linger + 1) == null)
{
*(linger + 1) = arg;
arg = null;
}
if (eq(*linger, "-r") || eq(*linger, "--bitrate"))
_R = 1 | (R = parseInt(linger[1]));
else if (eq(*linger, "-c") || eq(*linger, "--capacity"))
_C = 1 | (C = parseInt(linger[1]));
else if (eq(*linger, "-w") || eq(*linger, "--wordsize"))
_W = 1 | (W = parseInt(linger[1]));
else if (eq(*linger, "-o") || eq(*linger, "--outputsize"))
_O = 1 | (O = parseInt(linger[1]));
else if (eq(*linger, "-s") || eq(*linger, "--statesize"))
_S = 1 | (S = parseInt(linger[1]));
else if (eq(*linger, "-i") || eq(*linger, "--iterations"))
_I = 1 | (I = parseInt(linger[1]));
else if (eq(*linger, "-j") || eq(*linger, "--squeezes"))
_J = 1 | (J = parseInt(linger[1]));
else
{
fprintf(stderr, "%s: unrecognised option: %s\n", cmd, *linger);
fflush(stdout);
fflush(stderr);
return 1;
}
}
*linger = null;
if (arg == null)
continue;
}
if (arg == null)
continue;
if (dashed)
files[fptr++] = ((arg[0] == '-') && (arg[1] == 0)) ? null : arg;
else if ((arg[0] == '-') && (arg[1] == '-') && (arg[2] == 0))
dashed = true;
else if ((arg[0] == '-') && (arg[1] == 0))
files[fptr++] = null;
else if ((arg[0] == '-') && (arg[1] == '-') && arg[2])
{
long idx = -1, j;
for (j = 0; *(arg + j); j++)
if (*(arg + j) == '=')
{
idx = j;
break;
}
if (idx >= 0)
{
linger[0] = linger0;
linger[1] = arg + idx + 1;
for (j = 0; j < idx; j++)
*(*linger + j) = *(arg + j);
}
else
if (eq(arg, "--binary"))
binary = true;
else if (eq(arg, "--multi"))
multi++;
else if (eq(arg, "--hex"))
hex = true;
else
{
linger[0] = arg;
linger[1] = null;
}
}
else if ((arg[0] == '-') && arg[1])
{
arg++;
if (*arg == 'b')
{
binary = true;
arg++;
}
else if (*arg == 'm')
{
multi++;
arg++;
}
else if (*arg == 'h')
{
hex = true;
arg++;
}
else
{
{
char* _ = linger0;
*_++ = '-'; *_++ = *arg; *_ = 0;
linger[0] = _ - 2;
}
{
long _ = 0;
while (*(arg + _))
_++;
linger[1] = _ == 1 ? null : arg + 1;
}
}
}
else
files[fptr++] = arg;
}
i = _I ? I : _i;
j = _J ? J : _j;
#define ERR(text) fprintf(stderr, "%s: " text "\n", cmd); fflush(stdout); fflush(stderr)
if (_S)
{
s = S;
if ((s <= 0) || (s > 1600) || (s % 25))
{
ERR("the state size must be a positive multiple of 25 and is limited to 1600.");
return 6;
}
}
if (_W)
{
w = W;
if ((w <= 0) || (w > 64))
{
ERR("the word size must be positive and is limited to 64.");
return 6;
}
if (_S && (s != w * 25))
{
ERR("the state size must be 25 times of the word size.");
return 6;
}
else if (_S == null)
_S = 1 | (S = w * 25);
}
if (_C)
{
c = C;
if ((c <= 0) || (c & 7))
{
ERR("the capacity must be a positive multiple of 8.");
return 6;
}
}
if (_R)
{
r = R;
if ((r <= 0) || (r & 7))
{
ERR("the bitrate must be a positive multiple of 8.");
return 6;
}
}
if (_O)
{
o = O;
if (o <= 0)
{
ERR("the output size must be positive.");
return 6;
}
}
if ((_R & _C & _O) == null) /* s? */
{
s = _S ? s : _s;
c = -((r = (o = (((s << 5) / 100 + 7) >> 3) << 3) << 1) - s);
o = o < 8 ? 8 : o;
}
else if ((_R & _C) == null) /* !o s? */
{
r = _r;
c = _c;
s = _S ? s : (r + c);
}
else if (_R == null) /* !c o? s? */
{
r = (s = _S ? s : _s) - c;
o = _O ? o : (c == 8 ? 8 : (c << 1));
}
else if (_C == null) /* !r o? s? */
{
c = (s = _S ? s : _s) - r;
o = _O ? o : (c == 8 ? 8 : (c << 1));
}
else /* !r !c o? s? */
{
s = _S ? s : (r + c);
o = _O ? o : (c == 8 ? 8 : (c << 1));
}
fprintf(stderr, "Bitrate: %li\n", r);
fprintf(stderr, "Capacity: %li\n", c);
fprintf(stderr, "Word size: %li\n", w);
fprintf(stderr, "State size: %li\n", s);
fprintf(stderr, "Output Size: %li\n", o);
fprintf(stderr, "Iterations: %li\n", i);
fprintf(stderr, "Squeezes: %li\n", j);
if (r > s)
{
ERR("the bitrate must not be higher than the state size.");
return 6;
}
if (c > s)
{
ERR("the capacity must not be higher than the state size.");
return 6;
}
if (r + c != s)
{
ERR("the sum of the bitrate and the capacity must equal the state size.");
return 6;
}
if (fptr == 0)
files[fptr++] = null;
if (i < 1)
{
ERR("sorry, I will only do at least one hash iteration!");
return 3;
}
if (j < 1)
{
ERR("sorry, I will only do at least one squeeze iteration!");
return 3;
}
#undef ERR
bn = (o + 7) >> 3;
out_alloc = (char*)alloca(bn * 2 * sizeof(char) + bn * sizeof(byte));
stdin_alloc = (byte*)(out_alloc + bn * 2);
{
byte* stdin;
char* filename;
char* fn;
long f, fail, _;
struct stat attr;
char* out = binary ? null : out_alloc;
fail = false;
stdin = null;
for (f = 0; f < fptr; f++)
{
FILE* file;
long blksize;
byte* chunk;
byte* bs;
filename = *(files + f);
fn = filename ? filename : "/dev/stdin";
file = fopen(fn, "r");
if (file == null)
{
fprintf(stderr, "%s: cannot read file: %s\n", cmd, filename);
fail = true;
continue;
}
if ((filename != null) || (stdin == null))
{
initialise(r, c, o);
blksize = stat(*(argv + f), &attr) ? 0 : attr.st_blksize;
if (blksize <= 0)
blksize = 4096;
chunk = (byte*)alloca(blksize * sizeof(byte));
for (;;)
{
long read = fread(chunk, 1, blksize, file);
if (read <= 0)
break;
if (hex == false)
update(chunk, read);
else
{
int n = read >> 1;
for (_ = 0; _ < n; _++)
{
byte a = *(chunk + (_ << 1)), b = *(chunk + ((_ << 1) | 1));
a = (a & 15) + (a <= '9' ? 0 : 9);
b = (b & 15) + (b <= '9' ? 0 : 9);
*(chunk + _) = (a << 4) | b;
}
update(chunk, n);
}
}
bs = digest(null, 0, j == 1);
if (j > 2)
fastSqueeze(j - 2);
if (j > 1)
bs = squeeze();
dispose();
if (filename == null)
{
stdin = stdin_alloc;
for (_ = 0; _ < bn; _++)
*(stdin_alloc + _) = *(bs + _);
}
}
else
bs = stdin;
if (multi == 0)
{
for (_ = 1; _ < i; _++)
{
byte* _bs = bs;
initialise(r, c, o);
bs = digest(bs, bn, j == 1);
if (j > 2)
fastSqueeze(j - 2);
if (j > 1)
bs = squeeze();
free(_bs);
dispose();
}
if (binary)
putchars((char*)bs, bn);
else
{
long b, outptr = 0;
for (b = 0; b < bn; b++)
{
byte v = bs[b];
*(out + outptr++) = HEXADECA[(v >> 4) & 15];
*(out + outptr++) = HEXADECA[v & 15];
}
printf("%s %s\n", out, filename ? filename : "-");
}
}
else if (multi == 1)
{
long b;
if (binary)
putchars((char*)bs, bn);
else
{
for (b = 0; b < bn; b++)
{
byte v = bs[b];
out[b * 2 ] = HEXADECA[(v >> 4) & 15];
out[b * 2 + 1] = HEXADECA[v & 15];
}
printf("%s %s\n", out, filename ? filename : "-");
}
for (_ = 1; _ < i; _++)
{
byte* _bs = bs;
initialise(r, c, o);
bs = digest(bs, bn, j == 1);
if (j > 2)
fastSqueeze(j - 2);
if (j > 1)
bs = squeeze();
free(_bs);
dispose();
if (binary)
putchars((char*)bs, bn);
else
{
for (b = 0; b < bn; b++)
{
byte v = bs[b];
out[b * 2 ] = HEXADECA[(v >> 4) & 15];
out[b * 2 + 1] = HEXADECA[v & 15];
}
printf("%s\n", out);
}
}
}
else
{
long b;
char loophere;
char* loop = null;
SET got = set_new();
for (_ = 0; _ < i; _++)
{
if (_ > 0)
{
byte* _bs = bs;
initialise(r, c, o);
bs = digest(bs, bn, j == 1);
if (j > 2)
fastSqueeze(j - 2);
if (j > 1)
bs = squeeze();
free(_bs);
dispose();
}
for (b = 0; b < bn; b++)
{
byte v = bs[b];
out[b * 2 ] = HEXADECA[(v >> 4) & 15];
out[b * 2 + 1] = HEXADECA[v & 15];
}
if (loop == null)
{
if (set_contains(got, bs, bn))
{
loop = (char*)malloc(bn * 2 * sizeof(char));
for (b = 0; b < bn * 2; b++)
*(loop + b) = *(out + b);
}
else
set_add(got, out, bn);
}
loophere = loop && eq(loop, out);
if (loophere)
printf("\033[31m");
putchars(out, bn * 2);
if (loophere)
printf("\033[00m");
fflush(stdout);
}
if (loop)
{
fprintf(stderr, "\033[01;31mLoop found\033[00m\n");
free(loop);
}
set_free(got);
}
if (bs != null)
free(bs);
fclose(file);
}
fflush(stdout);
fflush(stderr);
if (fail)
return 5;
}
return 0;
}