/* See LICENSE file for copyright and license details. */
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libkeccak.h>
#include "arg.h"
#ifndef URANDOM
# define URANDOM "/dev/urandom"
#endif
char *argv0;
static void
usage(void)
{
fprintf(stderr, "usage: %s [-C capacity] [-N output-size] [-R rate] [-S state-size] [-W word-size] [-v]\n", argv0);
exit(1);
}
static int
make_spec(struct libkeccak_generalised_spec *restrict gspec, struct libkeccak_spec *restrict spec)
{
int r;
#define TEST(CASE, STR) case LIBKECCAK_GENERALISED_SPEC_ERROR_##CASE: return fprintf(stderr, "%s: %s\n", argv0, STR), 1;
if (r = libkeccak_degeneralise_spec(gspec, spec), r) {
switch (r) {
TEST (STATE_NONPOSITIVE, "the state size must be positive");
TEST (STATE_TOO_LARGE, "the state size is too large, may not exceed 1600");
TEST (STATE_MOD_25, "the state size must be a multiple of 25");
TEST (WORD_NONPOSITIVE, "the word size must be positive");
TEST (WORD_TOO_LARGE, "the word size is too large, may not exceed 64");
TEST (STATE_WORD_INCOHERENCY, "the state size must be exactly 25 times the word size");
TEST (CAPACITY_NONPOSITIVE, "the capacity must be positive");
TEST (CAPACITY_MOD_8, "the capacity must be a multiple of 8");
TEST (BITRATE_NONPOSITIVE, "the rate must be positive");
TEST (BITRATE_MOD_8, "the rate must be a multiple of 8");
TEST (OUTPUT_NONPOSITIVE, "the output size must be positive");
default:
fprintf(stderr, "%s: unknown error in algorithm parameters\n", argv0);
return 1;
}
}
#undef TEST
#define TEST(CASE, STR) case LIBKECCAK_SPEC_ERROR_##CASE: return fprintf(stderr, "%s: %s\n", argv0, STR), 1;
if (r = libkeccak_spec_check(spec), r) {
switch (r) {
TEST (BITRATE_NONPOSITIVE, "the rate size must be positive");
TEST (BITRATE_MOD_8, "the rate must be a multiple of 8");
TEST (CAPACITY_NONPOSITIVE, "the capacity must be positive");
TEST (CAPACITY_MOD_8, "the capacity must be a multiple of 8");
TEST (OUTPUT_NONPOSITIVE, "the output size must be positive");
TEST (STATE_TOO_LARGE, "the state size is too large, may not exceed 1600");
TEST (STATE_MOD_25, "the state size must be a multiple of 25");
TEST (WORD_NON_2_POTENT, "the word size must be a power of 2");
TEST (WORD_MOD_8, "the word size must be a multiple of 8");
default:
fprintf(stderr, "%s: unknown error in algorithm parameters\n", argv0);
return 1;
}
}
#undef TEST
return 0;
}
int
main(int argc, char *argv[])
{
char *restrict generated_random = NULL;
struct libkeccak_generalised_spec gspec;
struct libkeccak_spec spec;
struct libkeccak_state state;
size_t length, ptr;
ssize_t got;
int r, fd = -1, verbose = 0;
long int *param;
char *arg;
memset(&state, 0, sizeof(struct libkeccak_state));
libkeccak_generalised_spec_initialise(&gspec);
errno = 0;
ARGBEGIN {
case 'R':
param = &gspec.bitrate;
goto set_param;
case 'C':
param = &gspec.capacity;
goto set_param;
case 'N':
case 'O':
param = &gspec.output;
goto set_param;
case 'S':
case 'B':
param = &gspec.state_size;
goto set_param;
case 'W':
param = &gspec.word_size;
set_param:
arg = EARGF(usage());
if (!isdigit(*arg))
usage();
*param = strtol(arg, &arg, 10);
if (errno || *arg)
usage();
break;
case 'v':
verbose = 1;
break;
default:
usage();
} ARGEND;
if (argc)
usage();
if ((r = make_spec(&gspec, &spec)))
goto done;
if (verbose) {
fprintf(stderr, "%s: " "rate: %li\n", argv0, gspec.bitrate);
fprintf(stderr, "%s: " "capacity: %li\n", argv0, gspec.capacity);
fprintf(stderr, "%s: " "output size: %li\n", argv0, gspec.output);
fprintf(stderr, "%s: " "state size: %li\n", argv0, gspec.state_size);
fprintf(stderr, "%s: " "word size: %li\n", argv0, gspec.word_size);
}
libkeccak_state_initialise(&state, &spec);
fd = open(URANDOM, O_RDONLY);
if (fd < 0)
goto fail;
for (ptr = 0, length = sizeof(state.S); ptr < length;) {
got = read(fd, (char*)(state.S) + ptr, length - ptr);
if (got <= 0) {
if (!got) {
fprintf(stderr, "%s: %s contained less than %zu bytes\n", argv0, URANDOM, length);
r = 2;
goto done;
}
goto fail;
}
ptr += (size_t)got;
}
close(fd);
fd = -1;
length = (size_t)((spec.output + 7) / 8);
generated_random = malloc(length);
if (!generated_random)
goto fail;
for (;;) {
libkeccak_squeeze(&state, generated_random);
got = write(STDOUT_FILENO, generated_random, length);
if (got < 0) {
if (errno == EPIPE)
break;
goto fail;
}
}
done:
if (fd >= 0)
close(fd);
free(generated_random);
libkeccak_state_fast_destroy(&state);
return r;
fail:
perror(argv0);
r = 2;
goto done;
}