diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:32:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:32:49 +0000 |
commit | 8053187731ae8e3eb368d8360989cf5fd6eed9f7 (patch) | |
tree | 32bada84ff5d7460cdf3934fcbdbe770d6afe4cd /src/rnpkeys/tui.cpp | |
parent | Initial commit. (diff) | |
download | rnp-8053187731ae8e3eb368d8360989cf5fd6eed9f7.tar.xz rnp-8053187731ae8e3eb368d8360989cf5fd6eed9f7.zip |
Adding upstream version 0.17.0.upstream/0.17.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/rnpkeys/tui.cpp | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/src/rnpkeys/tui.cpp b/src/rnpkeys/tui.cpp new file mode 100644 index 0000000..73f26dc --- /dev/null +++ b/src/rnpkeys/tui.cpp @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2017-2020, [Ribose Inc](https://www.ribose.com). + * All rights reserved. + * + * 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 OWNER 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. + */ + +#include <algorithm> +#ifdef _MSC_VER +#include "uniwin.h" +#else +#include <unistd.h> +#endif +#include <errno.h> +#include <iterator> +#include "rnp/rnpcfg.h" +#include "rnpkeys.h" +#include "defaults.h" +#include "file-utils.h" +#include "logging.h" + +/* ----------------------------------------------------------------------------- + * @brief Reads input from file pointer and converts it securelly to ints + * Partially based on ERR34-C from SEI CERT C Coding Standard + * + * @param fp pointer to opened pipe + * @param result[out] result read from file pointer and converted to int + * + * @returns true and value in result if integer was parsed correctly, + * otherwise false + * +-------------------------------------------------------------------------------- */ +static bool +rnp_secure_get_long_from_fd(FILE *fp, long &result, bool allow_empty = true) +{ + char buff[BUFSIZ]; + if (!fgets(buff, sizeof(buff), fp)) { + RNP_LOG("EOF or read error"); + return false; + } + + errno = 0; + char *end_ptr = NULL; + long num_long = strtol(buff, &end_ptr, 10); + if (ERANGE == errno) { + RNP_LOG("Number out of range"); + return false; + } + if (end_ptr == buff) { + return allow_empty; + } + if ('\n' != *end_ptr && '\0' != *end_ptr) { + RNP_LOG("Unexpected end of line"); + return false; + } + + result = num_long; + return true; +} + +static bool +is_rsa_keysize_supported(uint32_t keysize) +{ + return ((keysize >= 1024) && (keysize <= 4096) && !(keysize % 8)); +} + +static const char * +ask_curve_name(FILE *input_fp) +{ + std::vector<const char *> curves; + static const char *const known_curves[] = { + "NIST P-256", + "NIST P-384", + "NIST P-521", + "brainpoolP256r1", + "brainpoolP384r1", + "brainpoolP512r1", + "secp256k1", + }; + const size_t curvenum = sizeof(known_curves) / sizeof(*known_curves); + + try { + std::copy_if(known_curves, + known_curves + curvenum, + std::back_inserter(curves), + [](const char *curve) { + bool supported = false; + return !rnp_supports_feature(RNP_FEATURE_CURVE, curve, &supported) && + supported; + }); + } catch (const std::exception &e) { + RNP_LOG("%s", e.what()); + return NULL; + } + const size_t ccount = curves.size(); + if (!ccount) { + return NULL; + } + bool ok = false; + const char *result = NULL; + int attempts = 0; + do { + if (attempts >= 10) { + printf("Too many attempts. Aborting.\n"); + return NULL; + } + printf("Please select which elliptic curve you want:\n"); + for (size_t i = 0; i < ccount; i++) { + printf("\t(%zu) %s\n", i + 1, curves[i]); + } + printf("(default %s)> ", DEFAULT_CURVE); + long val = 0; + ok = rnp_secure_get_long_from_fd(input_fp, val) && (val > 0) && (val <= (long) ccount); + if (ok) { + result = curves[val - 1]; + } + attempts++; + } while (!ok); + + return result; +} + +static long +ask_rsa_bitlen(FILE *input_fp) +{ + long result = 0; + do { + result = DEFAULT_RSA_NUMBITS; + printf("Please provide bit length of the key (between 1024 and 4096):\n(default %d)> ", + DEFAULT_RSA_NUMBITS); + } while (!rnp_secure_get_long_from_fd(input_fp, result) || + !is_rsa_keysize_supported(result)); + return result; +} + +static long +ask_elgamal_bitlen(FILE *input_fp) +{ + do { + printf( + "Please provide bit length of the ElGamal key (between %d and %d):\n(default %d) > ", + ELGAMAL_MIN_P_BITLEN, + ELGAMAL_MAX_P_BITLEN, + DEFAULT_ELGAMAL_NUMBITS); + long result = DEFAULT_ELGAMAL_NUMBITS; + if (!rnp_secure_get_long_from_fd(input_fp, result)) { + continue; + } + if ((result >= ELGAMAL_MIN_P_BITLEN) && (result <= ELGAMAL_MAX_P_BITLEN)) { + // round up to multiple of 32 + result = ((result + 31) / 32) * 32; + printf("Bitlen of the key will be %lu\n", result); + return result; + } + } while (1); +} + +static long +ask_dsa_bitlen(FILE *input_fp) +{ + do { + printf( + "Please provide bit length of the DSA key (between %d and %d):\n(default %d) > ", + DSA_MIN_P_BITLEN, + DSA_MAX_P_BITLEN, + DSA_DEFAULT_P_BITLEN); + long result = DSA_DEFAULT_P_BITLEN; + if (!rnp_secure_get_long_from_fd(input_fp, result)) { + continue; + } + if ((result >= DSA_MIN_P_BITLEN) && (result <= DSA_MAX_P_BITLEN)) { + // round up to multiple of 64 + result = ((result + 63) / 64) * 64; + printf("Bitlen of the key will be %lu\n", result); + return result; + } + } while (1); +} + +static bool +rnpkeys_ask_generate_params(rnp_cfg &cfg, FILE *input_fp) +{ + long option = 0; + do { + printf("Please select what kind of key you want:\n" + "\t(1) RSA (Encrypt or Sign)\n" + "\t(16) DSA + ElGamal\n" + "\t(17) DSA + RSA\n" // TODO: See #584 + "\t(19) ECDSA + ECDH\n" + "\t(22) EDDSA + X25519\n" + "\t(99) SM2\n" + "> "); + if (!rnp_secure_get_long_from_fd(input_fp, option, false)) { + option = 0; + continue; + } + switch (option) { + case 1: { + int bits = ask_rsa_bitlen(input_fp); + cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_RSA); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_RSA); + cfg.set_int(CFG_KG_PRIMARY_BITS, bits); + cfg.set_int(CFG_KG_SUBKEY_BITS, bits); + break; + } + case 16: { + int bits = ask_dsa_bitlen(input_fp); + cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_DSA); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_ELGAMAL); + cfg.set_int(CFG_KG_PRIMARY_BITS, bits); + cfg.set_int(CFG_KG_SUBKEY_BITS, bits); + break; + } + case 17: { + int bits = ask_dsa_bitlen(input_fp); + cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_DSA); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_RSA); + cfg.set_int(CFG_KG_PRIMARY_BITS, bits); + cfg.set_int(CFG_KG_SUBKEY_BITS, bits); + break; + } + case 19: { + const char *curve = ask_curve_name(input_fp); + if (!curve) { + return false; + } + cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_ECDSA); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_ECDH); + cfg.set_str(CFG_KG_PRIMARY_CURVE, curve); + cfg.set_str(CFG_KG_SUBKEY_CURVE, curve); + break; + } + case 22: { + cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_EDDSA); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_ECDH); + cfg.set_str(CFG_KG_SUBKEY_CURVE, "Curve25519"); + break; + } + case 99: { + cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_SM2); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_SM2); + if (!cfg.has(CFG_KG_HASH)) { + cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SM3); + } + break; + } + default: + option = 0; + break; + } + } while (!option); + + return true; +} + +static bool +rnpkeys_ask_generate_params_subkey(rnp_cfg &cfg, FILE *input_fp) +{ + long option = 0; + do { + printf("Please select subkey algorithm you want:\n" + "\t(1) RSA\n" + "\t(16) ElGamal\n" + "\t(17) DSA\n" + "\t(18) ECDH\n" + "\t(19) ECDSA\n" + "\t(22) EDDSA\n" + "\t(99) SM2" + "> "); + if (!rnp_secure_get_long_from_fd(input_fp, option, false)) { + option = 0; + continue; + } + switch (option) { + case 1: { + int bits = ask_rsa_bitlen(input_fp); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_RSA); + cfg.set_int(CFG_KG_SUBKEY_BITS, bits); + break; + } + case 16: { + int bits = ask_elgamal_bitlen(input_fp); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_ELGAMAL); + cfg.set_int(CFG_KG_SUBKEY_BITS, bits); + break; + } + case 17: { + int bits = ask_dsa_bitlen(input_fp); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_DSA); + cfg.set_int(CFG_KG_SUBKEY_BITS, bits); + break; + } + case 18: { + const char *curve = ask_curve_name(input_fp); + if (!curve) { + return false; + } + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_ECDH); + cfg.set_str(CFG_KG_SUBKEY_CURVE, curve); + break; + } + case 19: { + const char *curve = ask_curve_name(input_fp); + if (!curve) { + return false; + } + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_ECDSA); + cfg.set_str(CFG_KG_SUBKEY_CURVE, curve); + break; + } + case 22: { + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_EDDSA); + break; + } + case 99: { + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_SM2); + if (!cfg.has(CFG_KG_HASH)) { + cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SM3); + } + break; + } + default: + option = 0; + break; + } + } while (!option); + + return true; +} + +bool +cli_rnp_set_generate_params(rnp_cfg &cfg, bool subkey) +{ + bool res = true; + // hash algorithms for signing and protection + if (cfg.has(CFG_HASH)) { + cfg.set_str(CFG_KG_HASH, cfg.get_str(CFG_HASH)); + cfg.set_str(CFG_KG_PROT_HASH, cfg.get_str(CFG_HASH)); + } + + // key and subkey algorithms, bit length/curve + if (!cfg.get_bool(CFG_EXPERT)) { + cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_RSA); + cfg.set_int(CFG_KG_PRIMARY_BITS, cfg.get_int(CFG_NUMBITS)); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_RSA); + cfg.set_int(CFG_KG_SUBKEY_BITS, cfg.get_int(CFG_NUMBITS)); + } else { + FILE *input = stdin; + if (cfg.has(CFG_USERINPUTFD)) { + int inputfd = dup(cfg.get_int(CFG_USERINPUTFD)); + if (inputfd != -1) { + input = rnp_fdopen(inputfd, "r"); + if (!input) { + close(inputfd); + } + } + } + if (!input) { + return false; + } + if (subkey) { + res = rnpkeys_ask_generate_params_subkey(cfg, input); + } else { + res = rnpkeys_ask_generate_params(cfg, input); + } + if (input != stdin) { + fclose(input); + } + if (!res) { + return false; + } + } + + // make sure hash algorithms are set + if (!cfg.has(CFG_KG_HASH)) { + cfg.set_str(CFG_KG_HASH, DEFAULT_HASH_ALG); + } + if (!cfg.has(CFG_KG_PROT_HASH)) { + cfg.set_str(CFG_KG_PROT_HASH, DEFAULT_HASH_ALG); + } + + // protection symmetric algorithm + cfg.set_str(CFG_KG_PROT_ALG, + cfg.has(CFG_CIPHER) ? cfg.get_str(CFG_CIPHER) : DEFAULT_SYMM_ALG); + // protection iterations count + size_t iterations = cfg.get_int(CFG_S2K_ITER); + if (!iterations) { + res = res && !rnp_calculate_iterations(cfg.get_str(CFG_KG_PROT_HASH).c_str(), + cfg.get_int(CFG_S2K_MSEC), + &iterations); + } + cfg.set_int(CFG_KG_PROT_ITERATIONS, iterations); + return res; +} |