summaryrefslogtreecommitdiffstats
path: root/src/rnp/rnp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rnp/rnp.cpp')
-rw-r--r--src/rnp/rnp.cpp695
1 files changed, 695 insertions, 0 deletions
diff --git a/src/rnp/rnp.cpp b/src/rnp/rnp.cpp
new file mode 100644
index 0000000..30d3ac4
--- /dev/null
+++ b/src/rnp/rnp.cpp
@@ -0,0 +1,695 @@
+/*
+ * Copyright (c) 2017-2021 [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is originally derived from software contributed to
+ * The NetBSD Foundation by Alistair Crooks (agc@netbsd.org), and
+ * carried further by Ribose Inc (https://www.ribose.com).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/* Command line program to perform rnp operations */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#ifdef _MSC_VER
+#include "uniwin.h"
+#else
+#include <sys/param.h>
+#include <unistd.h>
+#include <getopt.h>
+#endif
+#include <fcntl.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <time.h>
+#include <errno.h>
+
+#include "fficli.h"
+#include "str-utils.h"
+#include "logging.h"
+
+static const char *usage =
+ "Sign, verify, encrypt, decrypt, inspect OpenPGP data.\n"
+ "Usage: rnp --command [options] [files]\n"
+ "Commands:\n"
+ " -h, --help This help message.\n"
+ " -V, --version Print RNP version information.\n"
+ " -e, --encrypt Encrypt data using the public key(s).\n"
+ " -r, --recipient Specify recipient's key via uid/keyid/fingerprint.\n"
+ " --cipher name Specify symmetric cipher, used for encryption.\n"
+ " --aead[=EAX, OCB] Use AEAD for encryption.\n"
+ " -z 0..9 Set the compression level.\n"
+ " --[zip,zlib,bzip] Use the corresponding compression algorithm.\n"
+ " --armor Apply ASCII armor to the encryption/signing output.\n"
+ " --no-wrap Do not wrap the output in a literal data packet.\n"
+ " -c, --symmetric Encrypt data using the password(s).\n"
+ " --passwords num Encrypt to the specified number of passwords.\n"
+ " -s, --sign Sign data. May be combined with encryption.\n"
+ " --detach Produce detached signature.\n"
+ " -u, --userid Specify signing key(s) via uid/keyid/fingerprint.\n"
+ " --hash Specify hash algorithm, used during signing.\n"
+ " --allow-weak-hash Allow usage of a weak hash algorithm.\n"
+ " --clearsign Cleartext-sign data.\n"
+ " -d, --decrypt Decrypt and output data, verifying signatures.\n"
+ " -v, --verify Verify signatures, without outputting data.\n"
+ " --source Specify source for the detached signature.\n"
+ " --dearmor Strip ASCII armor from the data, outputting binary.\n"
+ " --enarmor Add ASCII armor to the data.\n"
+ " --list-packets List OpenPGP packets from the input.\n"
+ " --json Use JSON output instead of human-readable.\n"
+ " --grips Dump key fingerprints and grips.\n"
+ " --mpi Dump MPI values from packets.\n"
+ " --raw Dump raw packet contents as well.\n"
+ "\n"
+ "Other options:\n"
+ " --homedir path Override home directory (default is ~/.rnp/).\n"
+ " -f, --keyfile Load key(s) only from the file specified.\n"
+ " --output [file, -] Write data to the specified file or stdout.\n"
+ " --overwrite Overwrite output file without a prompt.\n"
+ " --password Password used during operation.\n"
+ " --pass-fd num Read password(s) from the file descriptor.\n"
+ " --s2k-iterations Set the number of iterations for the S2K process.\n"
+ " --s2k-msec Calculate S2K iterations value based on a provided time in "
+ "milliseconds.\n"
+ " --notty Do not output anything to the TTY.\n"
+ " --current-time Override system's time.\n"
+ " --set-filename Override file name, stored inside of OpenPGP message.\n"
+ "\n"
+ "See man page for a detailed listing and explanation.\n"
+ "\n";
+
+enum optdefs {
+ /* Commands as they are get via CLI */
+ CMD_ENCRYPT = 260,
+ CMD_DECRYPT,
+ CMD_SIGN,
+ CMD_CLEARSIGN,
+ CMD_VERIFY,
+ CMD_VERIFY_CAT,
+ CMD_SYM_ENCRYPT,
+ CMD_DEARMOR,
+ CMD_ENARMOR,
+ CMD_LIST_PACKETS,
+ CMD_VERSION,
+ CMD_HELP,
+
+ /* OpenPGP data processing commands. Sign/Encrypt/Decrypt mapped to these */
+ CMD_PROTECT,
+ CMD_PROCESS,
+
+ /* Options */
+ OPT_KEY_STORE_FORMAT,
+ OPT_USERID,
+ OPT_RECIPIENT,
+ OPT_ARMOR,
+ OPT_HOMEDIR,
+ OPT_DETACHED,
+ OPT_HASH_ALG,
+ OPT_ALLOW_WEAK_HASH,
+ OPT_OUTPUT,
+ OPT_RESULTS,
+ OPT_COREDUMPS,
+ OPT_PASSWDFD,
+ OPT_PASSWD,
+ OPT_PASSWORDS,
+ OPT_EXPIRATION,
+ OPT_CREATION,
+ OPT_CIPHER,
+ OPT_NUMTRIES,
+ OPT_ZALG_ZIP,
+ OPT_ZALG_ZLIB,
+ OPT_ZALG_BZIP,
+ OPT_ZLEVEL,
+ OPT_OVERWRITE,
+ OPT_AEAD,
+ OPT_AEAD_CHUNK,
+ OPT_KEYFILE,
+ OPT_JSON,
+ OPT_GRIPS,
+ OPT_MPIS,
+ OPT_RAW,
+ OPT_NOTTY,
+ OPT_SOURCE,
+ OPT_NOWRAP,
+ OPT_CURTIME,
+ OPT_SETFNAME,
+ OPT_ALLOW_HIDDEN,
+ OPT_S2K_ITER,
+ OPT_S2K_MSEC,
+
+ /* debug */
+ OPT_DEBUG
+};
+
+#define EXIT_ERROR 2
+
+static struct option options[] = {
+ /* file manipulation commands */
+ {"encrypt", no_argument, NULL, CMD_ENCRYPT},
+ {"decrypt", no_argument, NULL, CMD_DECRYPT},
+ {"sign", no_argument, NULL, CMD_SIGN},
+ {"clearsign", no_argument, NULL, CMD_CLEARSIGN},
+ {"verify", no_argument, NULL, CMD_VERIFY},
+ {"verify-cat", no_argument, NULL, CMD_VERIFY_CAT},
+ {"symmetric", no_argument, NULL, CMD_SYM_ENCRYPT},
+ {"dearmor", no_argument, NULL, CMD_DEARMOR},
+ {"enarmor", optional_argument, NULL, CMD_ENARMOR},
+ /* file listing commands */
+ {"list-packets", no_argument, NULL, CMD_LIST_PACKETS},
+ /* debugging commands */
+ {"help", no_argument, NULL, CMD_HELP},
+ {"version", no_argument, NULL, CMD_VERSION},
+ {"debug", required_argument, NULL, OPT_DEBUG},
+ /* options */
+ {"coredumps", no_argument, NULL, OPT_COREDUMPS},
+ {"keystore-format", required_argument, NULL, OPT_KEY_STORE_FORMAT},
+ {"userid", required_argument, NULL, OPT_USERID},
+ {"recipient", required_argument, NULL, OPT_RECIPIENT},
+ {"home", required_argument, NULL, OPT_HOMEDIR},
+ {"homedir", required_argument, NULL, OPT_HOMEDIR},
+ {"keyfile", required_argument, NULL, OPT_KEYFILE},
+ {"ascii", no_argument, NULL, OPT_ARMOR},
+ {"armor", no_argument, NULL, OPT_ARMOR},
+ {"armour", no_argument, NULL, OPT_ARMOR},
+ {"detach", no_argument, NULL, OPT_DETACHED},
+ {"detached", no_argument, NULL, OPT_DETACHED},
+ {"hash", required_argument, NULL, OPT_HASH_ALG},
+ {"pass-fd", required_argument, NULL, OPT_PASSWDFD},
+ {"password", required_argument, NULL, OPT_PASSWD},
+ {"passwords", required_argument, NULL, OPT_PASSWORDS},
+ {"output", required_argument, NULL, OPT_OUTPUT},
+ {"results", required_argument, NULL, OPT_RESULTS},
+ {"creation", required_argument, NULL, OPT_CREATION},
+ {"expiration", required_argument, NULL, OPT_EXPIRATION},
+ {"expiry", required_argument, NULL, OPT_EXPIRATION},
+ {"cipher", required_argument, NULL, OPT_CIPHER},
+ {"numtries", required_argument, NULL, OPT_NUMTRIES},
+ {"zip", no_argument, NULL, OPT_ZALG_ZIP},
+ {"zlib", no_argument, NULL, OPT_ZALG_ZLIB},
+ {"bzip", no_argument, NULL, OPT_ZALG_BZIP},
+ {"bzip2", no_argument, NULL, OPT_ZALG_BZIP},
+ {"overwrite", no_argument, NULL, OPT_OVERWRITE},
+ {"aead", optional_argument, NULL, OPT_AEAD},
+ {"aead-chunk-bits", required_argument, NULL, OPT_AEAD_CHUNK},
+ {"json", no_argument, NULL, OPT_JSON},
+ {"grips", no_argument, NULL, OPT_GRIPS},
+ {"mpi", no_argument, NULL, OPT_MPIS},
+ {"raw", no_argument, NULL, OPT_RAW},
+ {"notty", no_argument, NULL, OPT_NOTTY},
+ {"source", required_argument, NULL, OPT_SOURCE},
+ {"no-wrap", no_argument, NULL, OPT_NOWRAP},
+ {"current-time", required_argument, NULL, OPT_CURTIME},
+ {"set-filename", required_argument, NULL, OPT_SETFNAME},
+ {"allow-hidden", no_argument, NULL, OPT_ALLOW_HIDDEN},
+ {"s2k-iterations", required_argument, NULL, OPT_S2K_ITER},
+ {"s2k-msec", required_argument, NULL, OPT_S2K_MSEC},
+ {"allow-weak-hash", no_argument, NULL, OPT_ALLOW_WEAK_HASH},
+
+ {NULL, 0, NULL, 0},
+};
+
+/* print a usage message */
+static void
+print_usage(const char *usagemsg)
+{
+ cli_rnp_print_praise();
+ puts(usagemsg);
+}
+
+/* do a command once for a specified config */
+static bool
+rnp_cmd(cli_rnp_t *rnp)
+{
+ bool ret = false;
+
+ switch (rnp->cfg().get_int(CFG_COMMAND)) {
+ case CMD_PROTECT:
+ ret = cli_rnp_protect_file(rnp);
+ break;
+ case CMD_PROCESS:
+ ret = cli_rnp_process_file(rnp);
+ break;
+ case CMD_LIST_PACKETS:
+ ret = cli_rnp_dump_file(rnp);
+ break;
+ case CMD_DEARMOR:
+ ret = cli_rnp_dearmor_file(rnp);
+ break;
+ case CMD_ENARMOR:
+ ret = cli_rnp_armor_file(rnp);
+ break;
+ case CMD_VERSION:
+ cli_rnp_print_praise();
+ ret = true;
+ break;
+ default:
+ print_usage(usage);
+ ret = true;
+ }
+
+ return ret;
+}
+
+static bool
+setcmd(rnp_cfg &cfg, int cmd, const char *arg)
+{
+ int newcmd = cmd;
+
+ /* set file processing command to one of PROTECT or PROCESS */
+ switch (cmd) {
+ case CMD_ENCRYPT:
+ cfg.set_bool(CFG_ENCRYPT_PK, true);
+ if (cfg.get_bool(CFG_ENCRYPT_SK)) {
+ cfg.set_bool(CFG_KEYSTORE_DISABLED, false);
+ }
+ newcmd = CMD_PROTECT;
+ break;
+ case CMD_SYM_ENCRYPT:
+ cfg.set_bool(CFG_ENCRYPT_SK, true);
+ if (!cfg.get_bool(CFG_ENCRYPT_PK) && !cfg.get_bool(CFG_SIGN_NEEDED)) {
+ cfg.set_bool(CFG_KEYSTORE_DISABLED, true);
+ }
+ newcmd = CMD_PROTECT;
+ break;
+ case CMD_CLEARSIGN:
+ cfg.set_bool(CFG_CLEARTEXT, true);
+ [[fallthrough]];
+ case CMD_SIGN:
+ cfg.set_bool(CFG_NEEDSSECKEY, true);
+ cfg.set_bool(CFG_SIGN_NEEDED, true);
+ if (cfg.get_bool(CFG_ENCRYPT_SK)) {
+ cfg.set_bool(CFG_KEYSTORE_DISABLED, false);
+ }
+ newcmd = CMD_PROTECT;
+ break;
+ case CMD_DECRYPT:
+ /* for decryption, we probably need a seckey */
+ cfg.set_bool(CFG_NEEDSSECKEY, true);
+ newcmd = CMD_PROCESS;
+ break;
+ case CMD_VERIFY:
+ /* single verify will discard output, decrypt will not */
+ cfg.set_bool(CFG_NO_OUTPUT, true);
+ [[fallthrough]];
+ case CMD_VERIFY_CAT:
+ newcmd = CMD_PROCESS;
+ break;
+ case CMD_LIST_PACKETS:
+ cfg.set_bool(CFG_KEYSTORE_DISABLED, true);
+ break;
+ case CMD_DEARMOR:
+ cfg.set_bool(CFG_KEYSTORE_DISABLED, true);
+ break;
+ case CMD_ENARMOR: {
+ std::string msgt = arg ? arg : "";
+ if (msgt.empty() || (msgt == "msg")) {
+ msgt = "message";
+ } else if (msgt == "pubkey") {
+ msgt = "public key";
+ } else if (msgt == "seckey") {
+ msgt = "secret key";
+ } else if (msgt == "sign") {
+ msgt = "signature";
+ } else {
+ ERR_MSG("Wrong enarmor argument: %s", arg);
+ return false;
+ }
+
+ if (!msgt.empty()) {
+ cfg.set_str(CFG_ARMOR_DATA_TYPE, msgt);
+ }
+ cfg.set_bool(CFG_KEYSTORE_DISABLED, true);
+ break;
+ }
+ case CMD_HELP:
+ case CMD_VERSION:
+ break;
+ default:
+ newcmd = CMD_HELP;
+ break;
+ }
+
+ if (cfg.has(CFG_COMMAND) && cfg.get_int(CFG_COMMAND) != newcmd) {
+ ERR_MSG("Conflicting commands!");
+ return false;
+ }
+
+ cfg.set_int(CFG_COMMAND, newcmd);
+ return true;
+}
+
+/* set an option */
+static bool
+setoption(rnp_cfg &cfg, int val, const char *arg)
+{
+ switch (val) {
+ /* redirect commands to setcmd */
+ case CMD_ENCRYPT:
+ case CMD_SIGN:
+ case CMD_CLEARSIGN:
+ case CMD_DECRYPT:
+ case CMD_SYM_ENCRYPT:
+ case CMD_VERIFY:
+ case CMD_VERIFY_CAT:
+ case CMD_LIST_PACKETS:
+ case CMD_DEARMOR:
+ case CMD_ENARMOR:
+ case CMD_HELP:
+ case CMD_VERSION:
+ return setcmd(cfg, val, arg);
+ /* options */
+ case OPT_COREDUMPS:
+#ifdef _WIN32
+ ERR_MSG("warning: --coredumps doesn't make sense on windows systems.");
+#endif
+ cfg.set_bool(CFG_COREDUMPS, true);
+ return true;
+ case OPT_KEY_STORE_FORMAT:
+ cfg.set_str(CFG_KEYSTOREFMT, arg);
+ return true;
+ case OPT_USERID:
+ cfg.add_str(CFG_SIGNERS, arg);
+ return true;
+ case OPT_RECIPIENT:
+ cfg.add_str(CFG_RECIPIENTS, arg);
+ return true;
+ case OPT_ARMOR:
+ cfg.set_bool(CFG_ARMOR, true);
+ return true;
+ case OPT_DETACHED:
+ cfg.set_bool(CFG_DETACHED, true);
+ return true;
+ case OPT_HOMEDIR:
+ cfg.set_str(CFG_HOMEDIR, arg);
+ return true;
+ case OPT_KEYFILE:
+ cfg.set_str(CFG_KEYFILE, arg);
+ cfg.set_bool(CFG_KEYSTORE_DISABLED, true);
+ return true;
+ case OPT_HASH_ALG:
+ return cli_rnp_set_hash(cfg, arg);
+ case OPT_ALLOW_WEAK_HASH:
+ cfg.set_bool(CFG_WEAK_HASH, true);
+ return true;
+ case OPT_PASSWDFD:
+ cfg.set_str(CFG_PASSFD, arg);
+ return true;
+ case OPT_PASSWD:
+ cfg.set_str(CFG_PASSWD, arg);
+ return true;
+ case OPT_PASSWORDS: {
+ int count = 0;
+ if (!rnp::str_to_int(arg, count) || (count <= 0)) {
+ ERR_MSG("Incorrect value for --passwords option: %s", arg);
+ return false;
+ }
+
+ cfg.set_int(CFG_PASSWORDC, count);
+ cfg.set_bool(CFG_ENCRYPT_SK, true);
+ return true;
+ }
+ case OPT_OUTPUT:
+ cfg.set_str(CFG_OUTFILE, arg);
+ return true;
+ case OPT_RESULTS:
+ cfg.set_str(CFG_RESULTS, arg);
+ return true;
+ case OPT_EXPIRATION:
+ cfg.set_str(CFG_EXPIRATION, arg);
+ return true;
+ case OPT_CREATION:
+ cfg.set_str(CFG_CREATION, arg);
+ return true;
+ case OPT_CIPHER:
+ return cli_rnp_set_cipher(cfg, arg);
+ case OPT_NUMTRIES:
+ cfg.set_str(CFG_NUMTRIES, arg);
+ return true;
+ case OPT_ZALG_ZIP:
+ cfg.set_str(CFG_ZALG, "ZIP");
+ return true;
+ case OPT_ZALG_ZLIB:
+ cfg.set_str(CFG_ZALG, "ZLib");
+ return true;
+ case OPT_ZALG_BZIP:
+ cfg.set_str(CFG_ZALG, "BZip2");
+ return true;
+ case OPT_AEAD: {
+ std::string argstr = arg ? arg : "";
+ if ((argstr == "1") || rnp::str_case_eq(argstr, "eax")) {
+ argstr = "EAX";
+ } else if (argstr.empty() || (argstr == "2") || rnp::str_case_eq(argstr, "ocb")) {
+ argstr = "OCB";
+ } else {
+ ERR_MSG("Wrong AEAD algorithm: %s", argstr.c_str());
+ return false;
+ }
+ cfg.set_str(CFG_AEAD, argstr);
+ return true;
+ }
+ case OPT_AEAD_CHUNK: {
+ int bits = 0;
+ if (!rnp::str_to_int(arg, bits) || (bits < 0) || (bits > 16)) {
+ ERR_MSG("Wrong argument value %s for aead-chunk-bits, must be 0..16.", arg);
+ return false;
+ }
+ cfg.set_int(CFG_AEAD_CHUNK, bits);
+ return true;
+ }
+ case OPT_OVERWRITE:
+ cfg.set_bool(CFG_OVERWRITE, true);
+ return true;
+ case OPT_JSON:
+ cfg.set_bool(CFG_JSON, true);
+ return true;
+ case OPT_GRIPS:
+ cfg.set_bool(CFG_GRIPS, true);
+ return true;
+ case OPT_MPIS:
+ cfg.set_bool(CFG_MPIS, true);
+ return true;
+ case OPT_RAW:
+ cfg.set_bool(CFG_RAW, true);
+ return true;
+ case OPT_NOTTY:
+ cfg.set_bool(CFG_NOTTY, true);
+ return true;
+ case OPT_SOURCE:
+ cfg.set_str(CFG_SOURCE, arg);
+ return true;
+ case OPT_NOWRAP:
+ cfg.set_bool(CFG_NOWRAP, true);
+ cfg.set_int(CFG_ZLEVEL, 0);
+ return true;
+ case OPT_CURTIME:
+ cfg.set_str(CFG_CURTIME, arg);
+ return true;
+ case OPT_SETFNAME:
+ cfg.set_str(CFG_SETFNAME, arg);
+ return true;
+ case OPT_ALLOW_HIDDEN:
+ cfg.set_bool(CFG_ALLOW_HIDDEN, true);
+ return true;
+ case OPT_S2K_ITER: {
+ int iterations = 0;
+ if (!rnp::str_to_int(arg, iterations) || !iterations) {
+ ERR_MSG("Wrong iterations value: %s", arg);
+ return false;
+ }
+ cfg.set_int(CFG_S2K_ITER, iterations);
+ return true;
+ }
+ case OPT_S2K_MSEC: {
+ int msec = 0;
+ if (!rnp::str_to_int(arg, msec) || !msec) {
+ ERR_MSG("Invalid s2k msec value: %s", arg);
+ return false;
+ }
+ cfg.set_int(CFG_S2K_MSEC, msec);
+ return true;
+ }
+ case OPT_DEBUG:
+ ERR_MSG("Option --debug is deprecated, ignoring.");
+ return true;
+ default:
+ return setcmd(cfg, CMD_HELP, arg);
+ }
+
+ return false;
+}
+
+static bool
+set_short_option(rnp_cfg &cfg, int ch, const char *arg)
+{
+ switch (ch) {
+ case 'V':
+ return setcmd(cfg, CMD_VERSION, arg);
+ case 'd':
+ return setcmd(cfg, CMD_DECRYPT, arg);
+ case 'e':
+ return setcmd(cfg, CMD_ENCRYPT, arg);
+ case 'c':
+ return setcmd(cfg, CMD_SYM_ENCRYPT, arg);
+ case 's':
+ return setcmd(cfg, CMD_SIGN, arg);
+ case 'v':
+ return setcmd(cfg, CMD_VERIFY, arg);
+ case 'r':
+ if (!strlen(optarg)) {
+ ERR_MSG("Recipient should not be empty");
+ } else {
+ cfg.add_str(CFG_RECIPIENTS, optarg);
+ }
+ break;
+ case 'u':
+ if (!optarg) {
+ ERR_MSG("No userid argument provided");
+ return false;
+ }
+ cfg.add_str(CFG_SIGNERS, optarg);
+ break;
+ case 'z':
+ if ((strlen(optarg) != 1) || (optarg[0] < '0') || (optarg[0] > '9')) {
+ ERR_MSG("Bad compression level: %s. Should be 0..9", optarg);
+ } else {
+ cfg.set_int(CFG_ZLEVEL, optarg[0] - '0');
+ }
+ break;
+ case 'f':
+ if (!optarg) {
+ ERR_MSG("No keyfile argument provided");
+ return false;
+ }
+ cfg.set_str(CFG_KEYFILE, optarg);
+ cfg.set_bool(CFG_KEYSTORE_DISABLED, true);
+ break;
+ case 'h':
+ [[fallthrough]];
+ default:
+ return setcmd(cfg, CMD_HELP, optarg);
+ }
+
+ return true;
+}
+
+#ifndef RNP_RUN_TESTS
+int
+main(int argc, char **argv)
+#else
+int rnp_main(int argc, char **argv);
+int
+rnp_main(int argc, char **argv)
+#endif
+{
+ if (argc < 2) {
+ print_usage(usage);
+ return EXIT_ERROR;
+ }
+
+ cli_rnp_t rnp = {};
+#if !defined(RNP_RUN_TESTS) && defined(_WIN32)
+ try {
+ rnp.substitute_args(&argc, &argv);
+ } catch (std::exception &ex) {
+ RNP_LOG("Error converting arguments ('%s')", ex.what());
+ return EXIT_ERROR;
+ }
+#endif
+
+ rnp_cfg cfg;
+ cfg.load_defaults();
+
+ /* TODO: These options should be set after initialising the context. */
+ int optindex = 0;
+ int ch;
+ while ((ch = getopt_long(argc, argv, "S:Vdecr:su:vz:f:h", options, &optindex)) != -1) {
+ /* Check for unsupported command/option */
+ if (ch == '?') {
+ print_usage(usage);
+ return EXIT_FAILURE;
+ }
+
+ bool res = ch >= CMD_ENCRYPT ? setoption(cfg, options[optindex].val, optarg) :
+ set_short_option(cfg, ch, optarg);
+ if (!res) {
+ return EXIT_ERROR;
+ }
+ }
+
+ switch (cfg.get_int(CFG_COMMAND)) {
+ case CMD_HELP:
+ print_usage(usage);
+ return EXIT_SUCCESS;
+ case CMD_VERSION:
+ cli_rnp_print_praise();
+ return EXIT_SUCCESS;
+ default:;
+ }
+
+ if (!cli_cfg_set_keystore_info(cfg)) {
+ ERR_MSG("fatal: cannot set keystore info");
+ return EXIT_ERROR;
+ }
+
+ if (!rnp.init(cfg)) {
+ ERR_MSG("fatal: cannot initialise");
+ return EXIT_ERROR;
+ }
+
+ if (!cli_rnp_check_weak_hash(&rnp)) {
+ ERR_MSG("Weak hash algorithm detected. Pass --allow-weak-hash option if you really "
+ "want to use it.");
+ return EXIT_ERROR;
+ }
+
+ bool disable_ks = rnp.cfg().get_bool(CFG_KEYSTORE_DISABLED);
+ if (!disable_ks && !rnp.load_keyrings(rnp.cfg().get_bool(CFG_NEEDSSECKEY))) {
+ ERR_MSG("fatal: failed to load keys");
+ return EXIT_ERROR;
+ }
+
+ /* load the keyfile if any */
+ if (disable_ks && !rnp.cfg().get_str(CFG_KEYFILE).empty() && !cli_rnp_add_key(&rnp)) {
+ ERR_MSG("fatal: failed to load key(s) from the file");
+ return EXIT_ERROR;
+ }
+
+ if (!cli_rnp_setup(&rnp)) {
+ return EXIT_ERROR;
+ }
+
+ /* now do the required action for each of the command line args */
+ if (optind == argc) {
+ return cli_rnp_t::ret_code(rnp_cmd(&rnp));
+ }
+ bool success = true;
+ for (int i = optind; i < argc; i++) {
+ rnp.cfg().set_str(CFG_INFILE, argv[i]);
+ success = success && rnp_cmd(&rnp);
+ }
+ return cli_rnp_t::ret_code(success);
+}