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 | } |