summaryrefslogtreecommitdiffstats
path: root/src/rnpkeys/rnpkeys.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rnpkeys/rnpkeys.cpp')
-rw-r--r--src/rnpkeys/rnpkeys.cpp648
1 files changed, 648 insertions, 0 deletions
diff --git a/src/rnpkeys/rnpkeys.cpp b/src/rnpkeys/rnpkeys.cpp
new file mode 100644
index 0000000..1a6997c
--- /dev/null
+++ b/src/rnpkeys/rnpkeys.cpp
@@ -0,0 +1,648 @@
+/*
+ * 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 */
+
+#ifdef _MSC_VER
+#include "uniwin.h"
+#else
+#include <getopt.h>
+#endif
+#include <string.h>
+#include <stdarg.h>
+#include "rnpkeys.h"
+#include "str-utils.h"
+#include <set>
+
+const char *usage =
+ "Manipulate OpenPGP keys and keyrings.\n"
+ "Usage: rnpkeys --command [options] [files]\n"
+ "Commands:\n"
+ " -h, --help This help message.\n"
+ " -V, --version Print RNP version information.\n"
+ " -g, --generate-key Generate a new keypair (default is RSA).\n"
+ " --userid Specify key's userid.\n"
+ " --expert Select key type, size, and additional parameters.\n"
+ " --numbits Override default key size (2048).\n"
+ " --expiration Set key and subkey expiration time.\n"
+ " --cipher Set cipher used to encrypt a secret key.\n"
+ " --hash Set hash which is used for key derivation.\n"
+ " --allow-weak-hash Allow usage of a weak hash algorithm.\n"
+ " -l, --list-keys List keys in the keyrings.\n"
+ " --secret List secret keys instead of public ones.\n"
+ " --with-sigs List signatures as well.\n"
+ " --import Import keys or signatures.\n"
+ " --import-keys Import keys.\n"
+ " --import-sigs Import signatures.\n"
+ " --permissive Skip erroring keys/sigs instead of failing.\n"
+ " --export-key Export a key.\n"
+ " --secret Export a secret key instead of a public.\n"
+ " --export-rev Export a key's revocation.\n"
+ " --rev-type Set revocation type.\n"
+ " --rev-reason Human-readable reason for revocation.\n"
+ " --revoke-key Revoke a key specified.\n"
+ " --remove-key Remove a key specified.\n"
+ " --edit-key Edit key properties.\n"
+ " --add-subkey Add new subkey.\n"
+ " --check-cv25519-bits Check whether Cv25519 subkey bits are correct.\n"
+ " --fix-cv25519-bits Fix Cv25519 subkey bits.\n"
+ " --set-expire Set key expiration time.\n"
+ "\n"
+ "Other options:\n"
+ " --homedir Override home directory (default is ~/.rnp/).\n"
+ " --password Password, which should be used during operation.\n"
+ " --pass-fd Read password(s) from the file descriptor.\n"
+ " --force Force operation (like secret key removal).\n"
+ " --output [file, -] Write data to the specified file or stdout.\n"
+ " --overwrite Overwrite output file without a prompt.\n"
+ " --notty Do not write anything to the TTY.\n"
+ " --current-time Override system's time.\n"
+ "\n"
+ "See man page for a detailed listing and explanation.\n"
+ "\n";
+
+struct option options[] = {
+ /* key-management commands */
+ {"list-keys", no_argument, NULL, CMD_LIST_KEYS},
+ {"export", no_argument, NULL, CMD_EXPORT_KEY},
+ {"export-key", optional_argument, NULL, CMD_EXPORT_KEY},
+ {"import", no_argument, NULL, CMD_IMPORT},
+ {"import-key", no_argument, NULL, CMD_IMPORT_KEYS},
+ {"import-keys", no_argument, NULL, CMD_IMPORT_KEYS},
+ {"import-sigs", no_argument, NULL, CMD_IMPORT_SIGS},
+ {"gen", optional_argument, NULL, CMD_GENERATE_KEY},
+ {"gen-key", optional_argument, NULL, CMD_GENERATE_KEY},
+ {"generate", optional_argument, NULL, CMD_GENERATE_KEY},
+ {"generate-key", optional_argument, NULL, CMD_GENERATE_KEY},
+ {"export-rev", no_argument, NULL, CMD_EXPORT_REV},
+ {"export-revocation", no_argument, NULL, CMD_EXPORT_REV},
+ {"revoke-key", no_argument, NULL, CMD_REVOKE_KEY},
+ {"remove-key", no_argument, NULL, CMD_REMOVE_KEY},
+ {"edit-key", no_argument, NULL, CMD_EDIT_KEY},
+ /* 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},
+ {"with-sigs", no_argument, NULL, OPT_WITH_SIGS},
+ {"hash", required_argument, NULL, OPT_HASH_ALG},
+ {"home", required_argument, NULL, OPT_HOMEDIR},
+ {"homedir", required_argument, NULL, OPT_HOMEDIR},
+ {"numbits", required_argument, NULL, OPT_NUMBITS},
+ {"s2k-iterations", required_argument, NULL, OPT_S2K_ITER},
+ {"s2k-msec", required_argument, NULL, OPT_S2K_MSEC},
+ {"expiration", required_argument, NULL, OPT_EXPIRATION},
+ {"pass-fd", required_argument, NULL, OPT_PASSWDFD},
+ {"password", required_argument, NULL, OPT_PASSWD},
+ {"results", required_argument, NULL, OPT_RESULTS},
+ {"cipher", required_argument, NULL, OPT_CIPHER},
+ {"expert", no_argument, NULL, OPT_EXPERT},
+ {"output", required_argument, NULL, OPT_OUTPUT},
+ {"overwrite", no_argument, NULL, OPT_OVERWRITE},
+ {"force", no_argument, NULL, OPT_FORCE},
+ {"secret", no_argument, NULL, OPT_SECRET},
+ {"rev-type", required_argument, NULL, OPT_REV_TYPE},
+ {"rev-reason", required_argument, NULL, OPT_REV_REASON},
+ {"permissive", no_argument, NULL, OPT_PERMISSIVE},
+ {"notty", no_argument, NULL, OPT_NOTTY},
+ {"fix-cv25519-bits", no_argument, NULL, OPT_FIX_25519_BITS},
+ {"check-cv25519-bits", no_argument, NULL, OPT_CHK_25519_BITS},
+ {"add-subkey", no_argument, NULL, OPT_ADD_SUBKEY},
+ {"set-expire", required_argument, NULL, OPT_SET_EXPIRE},
+ {"current-time", required_argument, NULL, OPT_CURTIME},
+ {"allow-weak-hash", no_argument, NULL, OPT_ALLOW_WEAK_HASH},
+ {NULL, 0, NULL, 0},
+};
+
+/* list keys */
+static bool
+print_keys_info(cli_rnp_t *rnp, FILE *fp, const char *filter)
+{
+ bool psecret = rnp->cfg().get_bool(CFG_SECRET);
+ bool psigs = rnp->cfg().get_bool(CFG_WITH_SIGS);
+ int flags = CLI_SEARCH_SUBKEYS_AFTER | (psecret ? CLI_SEARCH_SECRET : 0);
+ std::vector<rnp_key_handle_t> keys;
+
+ if (!cli_rnp_keys_matching_string(rnp, keys, filter ? filter : "", flags)) {
+ fprintf(fp, "Key(s) not found.\n");
+ return false;
+ }
+ fprintf(fp, "%d key%s found\n", (int) keys.size(), (keys.size() == 1) ? "" : "s");
+ for (auto key : keys) {
+ cli_rnp_print_key_info(fp, rnp->ffi, key, psecret, psigs);
+ }
+
+ fprintf(fp, "\n");
+ /* clean up */
+ clear_key_handles(keys);
+ return true;
+}
+
+static bool
+import_keys(cli_rnp_t *rnp, rnp_input_t input, const std::string &inname)
+{
+ std::set<std::string> new_pub_keys;
+ std::set<std::string> new_sec_keys;
+ std::set<std::string> updated_keys;
+ bool res = false;
+ bool updated = false;
+ size_t unchanged_keys = 0;
+ size_t processed_keys = 0;
+
+ uint32_t flags = RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS |
+ RNP_LOAD_SAVE_SINGLE | RNP_LOAD_SAVE_BASE64;
+
+ bool permissive = rnp->cfg().get_bool(CFG_PERMISSIVE);
+ if (permissive) {
+ flags |= RNP_LOAD_SAVE_PERMISSIVE;
+ }
+
+ do {
+ /* load keys one-by-one */
+ char * results = NULL;
+ rnp_result_t ret = rnp_import_keys(rnp->ffi, input, flags, &results);
+ if (ret == RNP_ERROR_EOF) {
+ res = true;
+ break;
+ }
+ if (ret && updated) {
+ /* some keys were imported, but then error occurred */
+ ERR_MSG("warning: not all data was processed.");
+ res = true;
+ break;
+ }
+ if (ret) {
+ ERR_MSG("failed to import key(s) from %s, stopping.", inname.c_str());
+ break;
+ }
+
+ // print information about imported key(s)
+ json_object *jso = json_tokener_parse(results);
+ rnp_buffer_destroy(results);
+ if (!jso) {
+ ERR_MSG("invalid key import resulting JSON");
+ break;
+ }
+ json_object *keys = NULL;
+ if (!json_object_object_get_ex(jso, "keys", &keys)) {
+ ERR_MSG("invalid key import JSON contents");
+ json_object_put(jso);
+ break;
+ }
+ processed_keys += json_object_array_length(keys);
+ for (size_t idx = 0; idx < (size_t) json_object_array_length(keys); idx++) {
+ json_object * keyinfo = json_object_array_get_idx(keys, idx);
+ rnp_key_handle_t key = NULL;
+ if (!keyinfo) {
+ continue;
+ }
+ std::string pub_status = json_obj_get_str(keyinfo, "public");
+ std::string sec_status = json_obj_get_str(keyinfo, "secret");
+ const char *fphex = json_obj_get_str(keyinfo, "fingerprint");
+
+ if (pub_status == "new") {
+ new_pub_keys.insert(fphex);
+ updated = true;
+ }
+ if (sec_status == "new") {
+ new_sec_keys.insert(fphex);
+ updated = true;
+ }
+ if (pub_status == "updated" || sec_status == "updated") {
+ updated_keys.insert(fphex);
+ updated = true;
+ }
+ if (pub_status == "unchanged" || sec_status == "unchanged") {
+ if (!new_pub_keys.count(fphex) && !new_sec_keys.count(fphex) &&
+ !updated_keys.count(fphex)) {
+ unchanged_keys++;
+ continue;
+ }
+ }
+ if (rnp_locate_key(rnp->ffi, "fingerprint", fphex, &key) || !key) {
+ ERR_MSG("failed to locate key with fingerprint %s", fphex);
+ continue;
+ }
+ cli_rnp_print_key_info(stdout, rnp->ffi, key, true, false);
+ rnp_key_handle_destroy(key);
+ }
+ json_object_put(jso);
+ } while (1);
+
+ // print statistics
+ ERR_MSG("Import finished: %lu key%s processed, %lu new public keys, %lu new secret keys, "
+ "%lu updated, %lu unchanged.",
+ processed_keys,
+ (processed_keys != 1) ? "s" : "",
+ new_pub_keys.size(),
+ new_sec_keys.size(),
+ updated_keys.size(),
+ unchanged_keys);
+
+ if (updated) {
+ // set default key if we didn't have one
+ if (rnp->defkey().empty()) {
+ rnp->set_defkey();
+ }
+
+ // save public and secret keyrings
+ if (!cli_rnp_save_keyrings(rnp)) {
+ ERR_MSG("failed to save keyrings");
+ }
+ }
+ return res;
+}
+
+static bool
+import_sigs(cli_rnp_t *rnp, rnp_input_t input, const std::string &inname)
+{
+ bool res = false;
+ char * results = NULL;
+ json_object *jso = NULL;
+ json_object *sigs = NULL;
+ int unknown_sigs = 0;
+ int new_sigs = 0;
+ int old_sigs = 0;
+
+ if (rnp_import_signatures(rnp->ffi, input, 0, &results)) {
+ ERR_MSG("Failed to import signatures from %s", inname.c_str());
+ goto done;
+ }
+ // print information about imported signature(s)
+ jso = json_tokener_parse(results);
+ if (!jso || !json_object_object_get_ex(jso, "sigs", &sigs)) {
+ ERR_MSG("Invalid signature import result");
+ goto done;
+ }
+
+ for (size_t idx = 0; idx < (size_t) json_object_array_length(sigs); idx++) {
+ json_object *siginfo = json_object_array_get_idx(sigs, idx);
+ if (!siginfo) {
+ continue;
+ }
+ const char *status = json_obj_get_str(siginfo, "public");
+ std::string pub_status = status ? status : "unknown";
+ status = json_obj_get_str(siginfo, "secret");
+ std::string sec_status = status ? status : "unknown";
+
+ if ((pub_status == "new") || (sec_status == "new")) {
+ new_sigs++;
+ } else if ((pub_status == "unchanged") || (sec_status == "unchanged")) {
+ old_sigs++;
+ } else {
+ unknown_sigs++;
+ }
+ }
+
+ // print status information
+ ERR_MSG("Import finished: %d new signature%s, %d unchanged, %d unknown.",
+ new_sigs,
+ (new_sigs != 1) ? "s" : "",
+ old_sigs,
+ unknown_sigs);
+
+ // save public and secret keyrings
+ if ((new_sigs > 0) && !cli_rnp_save_keyrings(rnp)) {
+ ERR_MSG("Failed to save keyrings");
+ goto done;
+ }
+ res = true;
+done:
+ json_object_put(jso);
+ rnp_buffer_destroy(results);
+ return res;
+}
+
+static bool
+import(cli_rnp_t *rnp, const std::string &spec, int cmd)
+{
+ if (spec.empty()) {
+ ERR_MSG("Import path isn't specified");
+ return false;
+ }
+ rnp_input_t input = cli_rnp_input_from_specifier(*rnp, spec, NULL);
+ if (!input) {
+ ERR_MSG("Failed to create input for %s", spec.c_str());
+ return false;
+ }
+ if (cmd == CMD_IMPORT) {
+ char *contents = NULL;
+ if (rnp_guess_contents(input, &contents)) {
+ ERR_MSG("Warning! Failed to guess content type to import. Assuming keys.");
+ }
+ cmd = (contents && !strcmp(contents, "signature")) ? CMD_IMPORT_SIGS : CMD_IMPORT_KEYS;
+ rnp_buffer_destroy(contents);
+ }
+
+ bool res = false;
+ switch (cmd) {
+ case CMD_IMPORT_KEYS:
+ res = import_keys(rnp, input, spec);
+ break;
+ case CMD_IMPORT_SIGS:
+ res = import_sigs(rnp, input, spec);
+ break;
+ default:
+ ERR_MSG("Unexpected command: %d", cmd);
+ }
+ rnp_input_destroy(input);
+ return res;
+}
+
+/* print a usage message */
+void
+print_usage(const char *usagemsg)
+{
+ cli_rnp_print_praise();
+ puts(usagemsg);
+}
+
+/* do a command once for a specified file 'f' */
+bool
+rnp_cmd(cli_rnp_t *rnp, optdefs_t cmd, const char *f)
+{
+ std::string fs;
+
+ switch (cmd) {
+ case CMD_LIST_KEYS:
+ if (!f && rnp->cfg().get_count(CFG_USERID)) {
+ fs = rnp->cfg().get_str(CFG_USERID, 0);
+ f = fs.c_str();
+ }
+ return print_keys_info(rnp, stdout, f);
+ case CMD_EXPORT_KEY: {
+ if (!f && rnp->cfg().get_count(CFG_USERID)) {
+ fs = rnp->cfg().get_str(CFG_USERID, 0);
+ f = fs.c_str();
+ }
+ if (!f) {
+ ERR_MSG("No key specified.");
+ return 0;
+ }
+ return cli_rnp_export_keys(rnp, f);
+ }
+ case CMD_IMPORT:
+ case CMD_IMPORT_KEYS:
+ case CMD_IMPORT_SIGS:
+ return import(rnp, f ? f : "", cmd);
+ case CMD_GENERATE_KEY: {
+ if (!f) {
+ size_t count = rnp->cfg().get_count(CFG_USERID);
+ if (count == 1) {
+ fs = rnp->cfg().get_str(CFG_USERID, 0);
+ f = fs.c_str();
+ } else if (count > 1) {
+ ERR_MSG("Only single userid is supported for generated keys");
+ return false;
+ }
+ }
+ return cli_rnp_generate_key(rnp, f);
+ }
+ case CMD_EXPORT_REV: {
+ if (!f) {
+ ERR_MSG("You need to specify key to generate revocation for.");
+ return false;
+ }
+ return cli_rnp_export_revocation(rnp, f);
+ }
+ case CMD_REVOKE_KEY: {
+ if (!f) {
+ ERR_MSG("You need to specify key or subkey to revoke.");
+ return false;
+ }
+ return cli_rnp_revoke_key(rnp, f);
+ }
+ case CMD_REMOVE_KEY: {
+ if (!f) {
+ ERR_MSG("You need to specify key or subkey to remove.");
+ return false;
+ }
+ return cli_rnp_remove_key(rnp, f);
+ }
+ case CMD_EDIT_KEY: {
+ if (!f) {
+ ERR_MSG("You need to specify a key or subkey to edit.");
+ return false;
+ }
+ return rnp->edit_key(f);
+ }
+ case CMD_VERSION:
+ cli_rnp_print_praise();
+ return true;
+ case CMD_HELP:
+ default:
+ print_usage(usage);
+ return true;
+ }
+}
+
+/* set the option */
+bool
+setoption(rnp_cfg &cfg, optdefs_t *cmd, int val, const char *arg)
+{
+ switch (val) {
+ 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 CMD_GENERATE_KEY:
+ cfg.set_bool(CFG_NEEDSSECKEY, true);
+ *cmd = (optdefs_t) val;
+ return true;
+ case OPT_EXPERT:
+ cfg.set_bool(CFG_EXPERT, true);
+ return true;
+ case CMD_LIST_KEYS:
+ case CMD_EXPORT_KEY:
+ case CMD_EXPORT_REV:
+ case CMD_REVOKE_KEY:
+ case CMD_REMOVE_KEY:
+ case CMD_EDIT_KEY:
+ case CMD_IMPORT:
+ case CMD_IMPORT_KEYS:
+ case CMD_IMPORT_SIGS:
+ case CMD_HELP:
+ case CMD_VERSION:
+ *cmd = (optdefs_t) val;
+ return true;
+ /* options */
+ case OPT_KEY_STORE_FORMAT:
+ cfg.set_str(CFG_KEYSTOREFMT, arg);
+ return true;
+ case OPT_USERID:
+ cfg.add_str(CFG_USERID, arg);
+ return true;
+ case OPT_HOMEDIR:
+ cfg.set_str(CFG_HOMEDIR, arg);
+ return true;
+ case OPT_NUMBITS: {
+ int bits = 0;
+ if (!rnp::str_to_int(arg, bits) || (bits < 1024) || (bits > 16384)) {
+ ERR_MSG("wrong bits value: %s", arg);
+ return false;
+ }
+ cfg.set_int(CFG_NUMBITS, bits);
+ return true;
+ }
+ case OPT_ALLOW_WEAK_HASH:
+ cfg.set_bool(CFG_WEAK_HASH, true);
+ return true;
+ case OPT_HASH_ALG:
+ return cli_rnp_set_hash(cfg, arg);
+ case OPT_S2K_ITER: {
+ int iterations = atoi(arg);
+ if (!iterations) {
+ ERR_MSG("Wrong iterations value: %s", arg);
+ return false;
+ }
+ cfg.set_int(CFG_S2K_ITER, iterations);
+ return true;
+ }
+ case OPT_EXPIRATION:
+ cfg.set_str(CFG_KG_PRIMARY_EXPIRATION, arg);
+ cfg.set_str(CFG_KG_SUBKEY_EXPIRATION, arg);
+ 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_PASSWDFD:
+ cfg.set_str(CFG_PASSFD, arg);
+ return true;
+ case OPT_PASSWD:
+ cfg.set_str(CFG_PASSWD, arg);
+ return true;
+ case OPT_RESULTS:
+ cfg.set_str(CFG_IO_RESS, arg);
+ return true;
+ case OPT_CIPHER:
+ return cli_rnp_set_cipher(cfg, arg);
+ case OPT_DEBUG:
+ ERR_MSG("Option --debug is deprecated, ignoring.");
+ return true;
+ case OPT_OUTPUT:
+ if (!arg) {
+ ERR_MSG("No output filename argument provided");
+ return false;
+ }
+ cfg.set_str(CFG_OUTFILE, arg);
+ return true;
+ case OPT_OVERWRITE:
+ cfg.set_bool(CFG_OVERWRITE, true);
+ return true;
+ case OPT_FORCE:
+ cfg.set_bool(CFG_FORCE, true);
+ return true;
+ case OPT_SECRET:
+ cfg.set_bool(CFG_SECRET, true);
+ return true;
+ case OPT_WITH_SIGS:
+ cfg.set_bool(CFG_WITH_SIGS, true);
+ return true;
+ case OPT_REV_TYPE: {
+ std::string revtype = arg;
+ if (revtype == "0") {
+ revtype = "no";
+ } else if (revtype == "1") {
+ revtype = "superseded";
+ } else if (revtype == "2") {
+ revtype = "compromised";
+ } else if (revtype == "3") {
+ revtype = "retired";
+ }
+ cfg.set_str(CFG_REV_TYPE, revtype);
+ return true;
+ }
+ case OPT_REV_REASON:
+ cfg.set_str(CFG_REV_REASON, arg);
+ return true;
+ case OPT_PERMISSIVE:
+ cfg.set_bool(CFG_PERMISSIVE, true);
+ return true;
+ case OPT_NOTTY:
+ cfg.set_bool(CFG_NOTTY, true);
+ return true;
+ case OPT_FIX_25519_BITS:
+ cfg.set_bool(CFG_FIX_25519_BITS, true);
+ return true;
+ case OPT_CHK_25519_BITS:
+ cfg.set_bool(CFG_CHK_25519_BITS, true);
+ return true;
+ case OPT_CURTIME:
+ cfg.set_str(CFG_CURTIME, arg);
+ return true;
+ case OPT_ADD_SUBKEY:
+ cfg.set_bool(CFG_ADD_SUBKEY, true);
+ return true;
+ case OPT_SET_EXPIRE:
+ cfg.set_str(CFG_SET_KEY_EXPIRE, arg);
+ return true;
+ default:
+ *cmd = CMD_HELP;
+ return true;
+ }
+}
+
+bool
+rnpkeys_init(cli_rnp_t &rnp, const rnp_cfg &cfg)
+{
+ rnp_cfg rnpcfg;
+ rnpcfg.load_defaults();
+ rnpcfg.set_int(CFG_NUMBITS, DEFAULT_RSA_NUMBITS);
+ rnpcfg.set_str(CFG_IO_RESS, "<stdout>");
+ rnpcfg.copy(cfg);
+
+ if (!cli_cfg_set_keystore_info(rnpcfg)) {
+ ERR_MSG("fatal: cannot set keystore info");
+ return false;
+ }
+ if (!rnp.init(rnpcfg)) {
+ ERR_MSG("fatal: failed to initialize rnpkeys");
+ return false;
+ }
+ 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 false;
+ }
+ /* TODO: at some point we should check for error here */
+ (void) rnp.load_keyrings(true);
+ return true;
+}