#define _GNU_SOURCE #include #include #include #include #include #include "common.c" #define ENVVAR_PASSWORD_NAME "LIBEXIM_PASSWORD" void print_usage(char *progname) { printf("Usage: %s [OPTIONS] [CIPHERTEXT]\n\n", progname); printf("Password:\n"); printf(" -p, --password PASSWORD Decrypt using PASSWORD\n"); printf("\n"); printf(" If the environment variable LIBEXIM_PASSWORD is set the password is read from there.\n"); printf(" Setting a password with -p/--password overwrites this mechanism.\n"); printf("\n"); printf("Select input:\n"); printf(" -f, --infile FILE Decrypt contents of the first line of file FILE (use - for stdin)\n"); printf("\n"); printf("Output:\n"); printf(" -n, --no-newline Do not append a newline to the output\n"); printf("\n"); printf("Password and ciphertext are expected to be base64-encoded (as produced by the library).\n"); printf("\n"); } typedef enum { NONE = 0, PASSARG = 1, PASSENV = 2, INSTRING = 4, INFILE = 8 } seen_args; int main(int argc, char *argv[]) { char *prog_basename = basename(argv[0]); int opt; char *cipherstring; size_t password_len; char *password; char *password_env; bool add_newline = true; seen_args mode = NONE; seen_args input = NONE; if (sodium_init() < 0) { fputs("Unable to initialize libsodium", stderr); exit(128); } // define arguments const char *shortargs = "p:f:nh"; static struct option long_options[] = { {"password", required_argument, NULL, 'p'}, {"infile", required_argument, NULL, 'f'}, {"no-newline", no_argument, NULL, 'n'}, {"help", no_argument, NULL, 'h'}, {0, 0, 0, 0} }; // check environment for LIBEXIM_PASSWORD password_env = getenv(ENVVAR_PASSWORD_NAME); if (password_env != NULL && strlen(password_env) > 0) { password = password_env; password_len = strlen(password); mode |= PASSENV; } // parse arguments int long_index = 0; while ((opt = getopt_long(argc, argv, shortargs, long_options, &long_index)) != -1) { switch (opt) { case 'p': password_len = strlen((const char *) optarg); password = malloc(password_len + 1); strncpy(password, optarg, password_len); mode |= PASSARG; break; case 'f': cipherstring = read_first_line(optarg); input |= INFILE; break; case 'n': add_newline = false; break; case 'h': print_usage(prog_basename); exit(EXIT_SUCCESS); } } // read first non-option argument as ciphertext if present if (optind < argc) { size_t cipherstring_len = strlen(argv[optind]) + 1; cipherstring = malloc(cipherstring_len + 1); strncpy(cipherstring, argv[optind], cipherstring_len); input |= INSTRING; } // check if a password was provided if (mode == NONE) { fprintf(stderr, "[ERROR] Please specify a password.\n\n"); print_usage(prog_basename); exit(EXIT_FAILURE); } // fail if neither argument nor filename for ciphertext is present if (input == NONE) { fprintf(stderr, "[ERROR] Please specify a ciphertext source.\n\n"); print_usage(prog_basename); exit(EXIT_FAILURE); } // Derive key from the password. unsigned char keybytes[crypto_secretbox_KEYBYTES]; sodium_memzero(keybytes, crypto_secretbox_KEYBYTES); crypto_generichash(keybytes, crypto_secretbox_KEYBYTES, (unsigned char *) password, password_len, NULL, 0); // base64-decode input unsigned char *ciphertext; size_t ciphertext_len; if (base64_decode_string(cipherstring, &ciphertext, &ciphertext_len) != 0) { fprintf(stderr, "[ERROR] Unable to base64-decode ciphertext.\n\n"); exit(EXIT_FAILURE); } // extract nonce unsigned char nonce[crypto_secretbox_NONCEBYTES]; memcpy(nonce, ciphertext, crypto_secretbox_NONCEBYTES); // prepare buffer for cleartext if (ciphertext_len < crypto_secretbox_NONCEBYTES + crypto_secretbox_MACBYTES) { fprintf(stderr, "[ERROR] Ciphertext is too small to contain any data.\n\n"); exit(EXIT_FAILURE); } size_t cleartext_len = ciphertext_len - crypto_secretbox_NONCEBYTES - crypto_secretbox_MACBYTES; unsigned char *cleartext = (unsigned char *) malloc(cleartext_len + 1); sodium_memzero(cleartext, cleartext_len + 1); // decrypt if (crypto_secretbox_open_easy(cleartext, &ciphertext[crypto_secretbox_NONCEBYTES], ciphertext_len - crypto_secretbox_NONCEBYTES, nonce, keybytes) == 0) { } else { fprintf(stderr, "[ERROR] Unable to decrypt message.\n\n"); exit(EXIT_FAILURE); } // print cleartext to stdout if (add_newline == true) { fprintf(stdout, "%s\n", (const char *) cleartext); } else { fprintf(stdout, "%s", (const char *) cleartext); } free(cleartext); exit(EXIT_SUCCESS); }