1 /* Copyright 2015 Jann Horn <jann@thejh.net>.
2 * See LICENSE for license information.
10 #include <string.h> /* GNU basename */
11 #include <openssl/sha.h>
12 #include <openssl/rand.h>
14 #define eprintf(...) fprintf(stderr, __VA_ARGS__)
17 #define SALT_MASK 0x00003fffU /* lowest 14 bits */
18 #define HASH_MASK 0x00ffc000U /* next 10 bits */
20 void base64_encode(uint32_t n, char out[5]);
21 uint32_t base64_decode(char *in);
23 static void openssl_failure(void) {
24 eprintf("OpenSSL internal failure\n");
28 static uint32_t gen_authenticator(char *path, uint16_t salt) {
30 if (!SHA256_Init(&ctx)) openssl_failure();
32 /* append the filename including the terminating nullbyte to prevent
33 * ambiguity regarding where the filename ends */
34 char *name = basename(path);
35 if (!SHA256_Update(&ctx, name, strlen(name)+1)) openssl_failure();
37 FILE *f = fopen(path, "r");
39 perror("unable to open input file");
44 while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
45 if (!SHA256_Update(&ctx, buf, len)) openssl_failure();
47 if (!feof(f) || ferror(f)) {
48 eprintf("error occured while reading input file");
53 if (!SHA256_Update(&ctx, &salt, sizeof(salt))) openssl_failure();
55 unsigned char digest[SHA256_DIGEST_LENGTH];
56 if (!SHA256_Final(digest, &ctx)) openssl_failure();
58 uint32_t authenticator = salt;
59 authenticator |= (digest[0] << SALT_BITS | digest[1] << (SALT_BITS+8)) & HASH_MASK;
63 static void chop(char *str) {
64 if (*str == '\0') return;
65 char *p = str + strlen(str) - 1;
66 while (p >= str && (*p == '\r' || *p == '\n')) {
72 #define RED "\x1b[0;31m"
73 #define GREEN "\x1b[0;32m"
74 #define RESET "\x1b[0m"
76 int main(int argc, char **argv) {
79 eprintf("bad invocation. usage:\n"
80 "%s <gen | verify> <path>\n", argv[0]);
84 /* 4 bytes base64, 1 byte null, rest trailing garbage from fgets */
85 char b64_authenticator[30];
87 if (strcmp(argv[1], "gen") == 0) {
89 if (RAND_bytes((unsigned char*)&salt, sizeof(salt)) != 1) openssl_failure();
91 uint32_t authenticator = gen_authenticator(argv[2], salt);
92 base64_encode(authenticator, b64_authenticator);
93 printf("Authenticator generated: \x1b[7m%s\x1b[0m\n", b64_authenticator);
94 printf(RED"AFTER"RESET" the recipient confirms that he has received the file,\n"
95 "tell him the authenticator code and roughly compare the filename.\n");
96 } else if (strcmp(argv[1], "verify") == 0) {
97 printf("Please enter the 4-character authenticator: ");
98 if (!fgets(b64_authenticator, sizeof(b64_authenticator), stdin)) {
99 eprintf("unable to read user input\n");
102 chop(b64_authenticator);
103 uint32_t authenticator = base64_decode(b64_authenticator);
104 if (gen_authenticator(argv[2], authenticator & SALT_MASK) == authenticator) {
105 printf("verification "GREEN"SUCCESSFUL"RESET"\n"
106 "If the filename of the received file matches\n"
107 "the one you expected, everything is fine.\n");
109 printf("verification "RED"FAILED"RESET"\n"
110 "Something nasty might be going on!\n"
111 RED"DO NOT RETRANSMIT THE FILE OR LET THE SENDER REGENERATE THE AUTHENTICATOR!"RESET"\n"
112 "Because there is a small inherent chance of attacks succeeding against this scheme,\n"
113 "retrying might make an attack possible. Find out what happened and, if you think an\n"
114 "attack might have happened, switch to normal cryptographic checksums.\n");