/* * 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 #ifdef _MSC_VER #include "uniwin.h" #else #include #endif #include #include #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 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; }