Projects : keksum : keksum_genesis
| 1 | /* Standalone KECCAK hashing utility |
| 2 | * J. Welsh, May 2019 |
| 3 | */ |
| 4 | |
| 5 | #include <fcntl.h> |
| 6 | #include <signal.h> |
| 7 | #include <unistd.h> |
| 8 | #include "io.h" |
| 9 | |
| 10 | void sponge(unsigned capacity, size_t out_bits); |
| 11 | |
| 12 | #pragma GCC diagnostic ignored "-Woverlength-strings" /* wat. */ |
| 13 | |
| 14 | static char const usage[] = |
| 15 | "Usage: keksum [-c] [-sCAPACITY] [-nLENGTH] [-h] [--] [FILE]...\n" |
| 16 | "Compute KECCAK checksums.\n" |
| 17 | "\n" |
| 18 | "With no FILE, act as a filter, reading from standard input then printing\n" |
| 19 | "a single line containing the hex-encoded hash. Otherwise tabulate hash and\n" |
| 20 | "name for each FILE.\n" |
| 21 | "\n" |
| 22 | "Options:\n" |
| 23 | " -c read sums from FILEs and check them (TODO)\n" |
| 24 | " -l set output length in bits (default 512)\n" |
| 25 | " -s set sponge capacity in bits (default 256)\n" |
| 26 | " -h print this help and exit\n" |
| 27 | "\n" |
| 28 | "Capacity and length both establish upper bounds on security level. Capacity\n" |
| 29 | "strongly affects speed and hash by changing permutation frequency. Length\n" |
| 30 | "acts as a truncation of an infinite output stream. (If you use large length\n" |
| 31 | "with small capacity you will be deluding yourself in quite the same manner\n" |
| 32 | "as a poorly seeded PRNG.) Both must be multiples of 8. The 1600-bit\n" |
| 33 | "permutation is always used, thus capacity must be between 8 and 1592.\n"; |
| 34 | |
| 35 | static void usage_err(char const *msg) { |
| 36 | write_line(2,msg); |
| 37 | write_str(2,usage); |
| 38 | _exit(1); |
| 39 | } |
| 40 | |
| 41 | static unsigned dec_value(unsigned char c) { |
| 42 | unsigned r = c-'0'; |
| 43 | if (r > 9) usage_err(c ? "Bad integer" : "Missing integer value"); |
| 44 | return r; |
| 45 | } |
| 46 | |
| 47 | static unsigned parse_uint(char const *s) { |
| 48 | unsigned acc = dec_value(*s++); |
| 49 | if (!acc && *s) usage_err("Bad integer (leading zero)"); |
| 50 | for (; *s; ++s) { |
| 51 | unsigned digit = dec_value(*s); |
| 52 | if (acc > ((unsigned) -1)/10) |
| 53 | usage_err("Integer out of range"); |
| 54 | acc *= 10; |
| 55 | if (acc > ((unsigned) -1) - digit) |
| 56 | usage_err("Integer out of range"); |
| 57 | acc += digit; |
| 58 | } |
| 59 | return acc; |
| 60 | } |
| 61 | |
| 62 | int main(int argc, char **argv) { |
| 63 | unsigned out_len = 512; |
| 64 | /* Rationale: for collision resistance, any hash function needs at |
| 65 | * least twice the desired security level in non-redundant output bits |
| 66 | * due to the birthday problem. */ |
| 67 | |
| 68 | unsigned capacity = 256; |
| 69 | /* Rationale: sponge capacity is an upper bound on security level |
| 70 | * because the permutation is readily inverted if its full output is |
| 71 | * known. Beyond that, its relationship to actual security is not clear |
| 72 | * to me (or perhaps anyone); some margin of safety seems prudent. The |
| 73 | * EuCrypt default is 256 (bitrate 1344). But note that in the FIPS202 |
| 74 | * parameters, capacity under 512 is seen only in "SHAKE128" and none |
| 75 | * of the "SHA3" fixed-width hashes. See also: |
| 76 | * http://fixpoint.welshcomputing.com/keksum-a-keccak-imp/#comments */ |
| 77 | |
| 78 | signal(SIGPIPE,SIG_IGN); /* standard unix-hate */ |
| 79 | |
| 80 | #define SHIFT (--argc, ++argv) |
| 81 | if (!argc) goto endopts; |
| 82 | for (SHIFT; argc; SHIFT) { |
| 83 | char const *arg = *argv; |
| 84 | if (arg[0] != '-') goto endopts; |
| 85 | switch (arg[1]) { |
| 86 | case 0: goto endopts; |
| 87 | case 'l': out_len = parse_uint(arg+2); break; |
| 88 | case 's': capacity = parse_uint(arg+2); break; |
| 89 | case 'h': write_str(1,usage); return 0; |
| 90 | case '-': if (!arg[2]) { SHIFT; goto endopts; } |
| 91 | /* fallthrough */ |
| 92 | default: usage_err("Bad option"); |
| 93 | } |
| 94 | } |
| 95 | endopts: |
| 96 | if (out_len&7) usage_err("Length not byte-aligned"); |
| 97 | if (capacity&7) usage_err("Capacity not byte-aligned"); |
| 98 | if (!(0 < capacity && capacity < 1600)) |
| 99 | usage_err("Capacity out of range"); |
| 100 | |
| 101 | if (argc) { |
| 102 | /* Listing is similar to the GNU (?) format but with no input |
| 103 | * mode character (you may know this as the mysterious extra |
| 104 | * space): it's always binary. */ |
| 105 | for (; argc; SHIFT) { |
| 106 | int fd = chkp(*argv, open(*argv, O_RDONLY)); |
| 107 | chkp("dup2", dup2(fd,0)); |
| 108 | chkp("close", close(fd)); |
| 109 | sponge(capacity,out_len); |
| 110 | write_str(1," "); |
| 111 | write_line(1,*argv); |
| 112 | } |
| 113 | } |
| 114 | else { |
| 115 | sponge(capacity,out_len); |
| 116 | newline(1); |
| 117 | } |
| 118 | return 0; |
| 119 | } |