diff --git a/src/lib/ffi-priv-types.h b/src/lib/ffi-priv-types.h index 97f012748..06018e97a 100644 --- a/src/lib/ffi-priv-types.h +++ b/src/lib/ffi-priv-types.h @@ -32,6 +32,7 @@ #include #include #include "sec_profile.hpp" +#include "keygen.hpp" struct rnp_key_handle_st { rnp_ffi_t ffi; @@ -137,7 +138,7 @@ struct rnp_output_st { }; struct rnp_op_generate_st { - rnp_ffi_t ffi{}; + rnp_ffi_t ffi; bool primary{}; pgp_key_t *primary_sec{}; pgp_key_t *primary_pub{}; @@ -146,13 +147,16 @@ struct rnp_op_generate_st { /* password used to encrypt the key, if specified */ rnp::secure_vector password; /* request password for key encryption via ffi's password provider */ - bool request_password{}; - /* we don't use top-level keygen action here for easier fields access */ - rnp_keygen_crypto_params_t crypto{}; + bool request_password{}; + rnp::KeygenParams keygen_params; rnp_key_protection_params_t protection{}; rnp_selfsig_cert_info_t cert{}; rnp_selfsig_binding_info_t binding{}; - pgp_version_t pgp_version = PGP_V4; + + rnp_op_generate_st(rnp_ffi_t affi, pgp_pubkey_alg_t alg) + : ffi(affi), keygen_params(alg, affi->context) + { + } }; struct rnp_op_sign_signature_st { diff --git a/src/lib/rnp.cpp b/src/lib/rnp.cpp index b54640826..421a5f877 100644 --- a/src/lib/rnp.cpp +++ b/src/lib/rnp.cpp @@ -4367,341 +4367,208 @@ pk_alg_allows_custom_curve(pgp_pubkey_alg_t pkalg) static bool parse_preferences(json_object *jso, pgp_user_prefs_t &prefs) { - static const struct { - const char * key; - enum json_type type; - } properties[] = {{"hashes", json_type_array}, - {"ciphers", json_type_array}, - {"compression", json_type_array}, - {"key server", json_type_string}}; - - for (size_t iprop = 0; iprop < ARRAY_SIZE(properties); iprop++) { - json_object *value = NULL; - const char * key = properties[iprop].key; - - if (!json_object_object_get_ex(jso, key, &value)) { - continue; + /* Preferred hashes */ + std::vector strs; + if (json_get_str_arr(jso, "hashes", strs)) { + for (auto &str : strs) { + pgp_hash_alg_t hash_alg = PGP_HASH_UNKNOWN; + if (!str_to_hash_alg(str.c_str(), &hash_alg)) { + return false; + } + prefs.add_hash_alg(hash_alg); } - - if (!json_object_is_type(value, properties[iprop].type)) { - return false; + } + /* Preferred symmetric algorithms */ + if (json_get_str_arr(jso, "ciphers", strs)) { + for (auto &str : strs) { + pgp_symm_alg_t symm_alg = PGP_SA_UNKNOWN; + if (!str_to_cipher(str.c_str(), &symm_alg)) { + return false; + } + prefs.add_symm_alg(symm_alg); } - try { - if (rnp::str_case_eq(key, "hashes")) { - int length = json_object_array_length(value); - for (int i = 0; i < length; i++) { - json_object *item = json_object_array_get_idx(value, i); - if (!json_object_is_type(item, json_type_string)) { - return false; - } - pgp_hash_alg_t hash_alg = PGP_HASH_UNKNOWN; - if (!str_to_hash_alg(json_object_get_string(item), &hash_alg)) { - return false; - } - prefs.add_hash_alg(hash_alg); - } - } else if (rnp::str_case_eq(key, "ciphers")) { - int length = json_object_array_length(value); - for (int i = 0; i < length; i++) { - json_object *item = json_object_array_get_idx(value, i); - if (!json_object_is_type(item, json_type_string)) { - return false; - } - pgp_symm_alg_t symm_alg = PGP_SA_UNKNOWN; - if (!str_to_cipher(json_object_get_string(item), &symm_alg)) { - return false; - } - prefs.add_symm_alg(symm_alg); - } - } else if (rnp::str_case_eq(key, "compression")) { - int length = json_object_array_length(value); - for (int i = 0; i < length; i++) { - json_object *item = json_object_array_get_idx(value, i); - if (!json_object_is_type(item, json_type_string)) { - return false; - } - pgp_compression_type_t z_alg = PGP_C_UNKNOWN; - if (!str_to_compression_alg(json_object_get_string(item), &z_alg)) { - return false; - } - prefs.add_z_alg(z_alg); - } - } else if (rnp::str_case_eq(key, "key server")) { - prefs.key_server = json_object_get_string(value); + } + /* Preferred compression algorithms */ + if (json_get_str_arr(jso, "compression", strs)) { + for (auto &str : strs) { + pgp_compression_type_t z_alg = PGP_C_UNKNOWN; + if (!str_to_compression_alg(str.c_str(), &z_alg)) { + return false; } - } catch (const std::exception &e) { - RNP_LOG("%s", e.what()); - return false; + prefs.add_z_alg(z_alg); } - // delete this field since it has been handled - json_object_object_del(jso, key); } - return true; + /* Preferred key server */ + std::string key_server; + if (json_get_str(jso, "key server", key_server)) { + prefs.key_server = key_server; + } + /* Do not allow extra unknown keys */ + return !json_object_object_length(jso); } -static bool -parse_keygen_crypto(json_object *jso, rnp_keygen_crypto_params_t &crypto) +static std::unique_ptr +parse_keygen_params(rnp_ffi_t ffi, json_object *jso) { - static const struct { - const char * key; - enum json_type type; - } properties[] = {{"type", json_type_string}, - {"curve", json_type_string}, - {"length", json_type_int}, - {"hash", json_type_string}}; - - for (size_t i = 0; i < ARRAY_SIZE(properties); i++) { - json_object *value = NULL; - const char * key = properties[i].key; - - if (!json_object_object_get_ex(jso, key, &value)) { - continue; + /* Type */ + std::string str; + pgp_pubkey_alg_t alg = PGP_PKA_RSA; + if (json_get_str(jso, "type", str) && !str_to_pubkey_alg(str.c_str(), &alg)) { + return nullptr; + } + std::unique_ptr params(new rnp::KeygenParams(alg, ffi->context)); + /* Length */ + int bits = 0; + if (json_get_int(jso, "length", bits)) { + auto bit_params = dynamic_cast(¶ms->key_params()); + if (!bit_params) { + return nullptr; } - - if (!json_object_is_type(value, properties[i].type)) { - return false; + bit_params->set_bits(bits); + } + /* Curve */ + if (json_get_str(jso, "curve", str)) { + if (!pk_alg_allows_custom_curve(params->alg())) { + return nullptr; } - // TODO: make sure there are no duplicate keys in the JSON - if (rnp::str_case_eq(key, "type")) { - if (!str_to_pubkey_alg(json_object_get_string(value), &crypto.key_alg)) { - return false; - } - } else if (rnp::str_case_eq(key, "length")) { - int length = json_object_get_int(value); - switch (crypto.key_alg) { - case PGP_PKA_RSA: - crypto.rsa.modulus_bit_len = length; - break; - case PGP_PKA_DSA: - crypto.dsa.p_bitlen = length; - break; - case PGP_PKA_ELGAMAL: - crypto.elgamal.key_bitlen = length; - break; - default: - return false; - } - } else if (rnp::str_case_eq(key, "curve")) { - if (!pk_alg_allows_custom_curve(crypto.key_alg)) { - return false; - } - if (!curve_str_to_type(json_object_get_string(value), &crypto.ecc.curve)) { - return false; - } - } else if (rnp::str_case_eq(key, "hash")) { - if (!str_to_hash_alg(json_object_get_string(value), &crypto.hash_alg)) { - return false; - } - } else { - // shouldn't happen - return false; + auto ecc_params = dynamic_cast(¶ms->key_params()); + pgp_curve_t curve = PGP_CURVE_UNKNOWN; + if (!ecc_params || !curve_str_to_type(str.c_str(), &curve)) { + return nullptr; } - // delete this field since it has been handled - json_object_object_del(jso, key); + ecc_params->set_curve(curve); } - return true; + /* Hash algorithm */ + if (json_get_str(jso, "hash", str)) { + pgp_hash_alg_t hash = PGP_HASH_UNKNOWN; + if (!str_to_hash_alg(str.c_str(), &hash)) { + return nullptr; + } + params->set_hash(hash); + } + return params; } static bool parse_protection(json_object *jso, rnp_key_protection_params_t &protection) { - static const struct { - const char * key; - enum json_type type; - } properties[] = {{"cipher", json_type_string}, - {"mode", json_type_string}, - {"iterations", json_type_int}, - {"hash", json_type_string}}; - - for (size_t i = 0; i < ARRAY_SIZE(properties); i++) { - json_object *value = NULL; - const char * key = properties[i].key; - - if (!json_object_object_get_ex(jso, key, &value)) { - continue; + /* Cipher */ + std::string str; + if (json_get_str(jso, "cipher", str)) { + if (!str_to_cipher(str.c_str(), &protection.symm_alg)) { + return false; } + } + /* Mode */ + if (json_get_str(jso, "mode", str)) { + if (!str_to_cipher_mode(str.c_str(), &protection.cipher_mode)) { + return false; + } + } + /* Iterations */ + int iterations = 0; + if (json_get_int(jso, "iterations", iterations)) { + protection.iterations = iterations; + } + /* Hash algorithm */ + if (json_get_str(jso, "hash", str)) { + if (!str_to_hash_alg(str.c_str(), &protection.hash_alg)) { + return false; + } + } + /* Do not allow extra unknown keys */ + return !json_object_object_length(jso); +} - if (!json_object_is_type(value, properties[i].type)) { +static bool +parse_keygen_common_fields(json_object * jso, + uint8_t & usage, + uint32_t & expiry, + rnp_key_protection_params_t &prot) +{ + /* Key/subkey usage flags */ + std::string str; + std::vector strs; + if (json_get_str(jso, "usage", str)) { + strs.push_back(str); + } else { + json_get_str_arr(jso, "usage", strs); + } + for (auto &st : strs) { + uint8_t flag = 0; + if (!str_to_key_flag(st.c_str(), &flag) || (usage & flag)) { return false; } - // TODO: make sure there are no duplicate keys in the JSON - if (rnp::str_case_eq(key, "cipher")) { - if (!str_to_cipher(json_object_get_string(value), &protection.symm_alg)) { - return false; - } - } else if (rnp::str_case_eq(key, "mode")) { - if (!str_to_cipher_mode(json_object_get_string(value), &protection.cipher_mode)) { - return false; - } - } else if (rnp::str_case_eq(key, "iterations")) { - protection.iterations = json_object_get_int(value); - } else if (rnp::str_case_eq(key, "hash")) { - if (!str_to_hash_alg(json_object_get_string(value), &protection.hash_alg)) { - return false; - } - } else { - // shouldn't happen + usage |= flag; + } + /* Key/subkey expiration */ + uint64_t keyexp = 0; + if (json_get_uint64(jso, "expiration", keyexp)) { + keyexp = expiry; + } + /* Protection */ + auto obj = json_get_obj(jso, "protection"); + if (obj) { + if (!parse_protection(obj, prot)) { return false; } - // delete this field since it has been handled - json_object_object_del(jso, key); + json_object_object_del(jso, "protection"); } return true; } -static bool -parse_keygen_primary(json_object * jso, - rnp_keygen_primary_desc_t & desc, +static std::unique_ptr +parse_keygen_primary(rnp_ffi_t ffi, + json_object * jso, + rnp_selfsig_cert_info_t & cert, rnp_key_protection_params_t &prot) { - static const char *properties[] = { - "userid", "usage", "expiration", "preferences", "protection"}; - auto &cert = desc.cert; - - if (!parse_keygen_crypto(jso, desc.crypto)) { - return false; + /* Parse keygen params first */ + auto params = parse_keygen_params(ffi, jso); + if (!params) { + return nullptr; } - for (size_t i = 0; i < ARRAY_SIZE(properties); i++) { - json_object *value = NULL; - const char * key = properties[i]; - - if (!json_object_object_get_ex(jso, key, &value)) { - continue; + /* Parse common key/subkey fields */ + if (!parse_keygen_common_fields(jso, cert.key_flags, cert.key_expiration, prot)) { + return nullptr; + } + /* UserID */ + std::string str; + if (json_get_str(jso, "userid", str)) { + if (str.size() > MAX_ID_LENGTH) { + return nullptr; } - if (rnp::str_case_eq(key, "userid")) { - if (!json_object_is_type(value, json_type_string)) { - return false; - } - auto uid = json_object_get_string(value); - if (strlen(uid) > MAX_ID_LENGTH) { - return false; - } - cert.userid = json_object_get_string(value); - } else if (rnp::str_case_eq(key, "usage")) { - switch (json_object_get_type(value)) { - case json_type_array: { - int length = json_object_array_length(value); - for (int j = 0; j < length; j++) { - json_object *item = json_object_array_get_idx(value, j); - if (!json_object_is_type(item, json_type_string)) { - return false; - } - uint8_t flag = 0; - if (!str_to_key_flag(json_object_get_string(item), &flag)) { - return false; - } - // check for duplicate - if (cert.key_flags & flag) { - return false; - } - cert.key_flags |= flag; - } - } break; - case json_type_string: { - if (!str_to_key_flag(json_object_get_string(value), &cert.key_flags)) { - return false; - } - } break; - default: - return false; - } - } else if (rnp::str_case_eq(key, "expiration")) { - if (!json_object_is_type(value, json_type_int)) { - return false; - } - cert.key_expiration = json_object_get_int(value); - } else if (rnp::str_case_eq(key, "preferences")) { - if (!json_object_is_type(value, json_type_object)) { - return false; - } - if (!parse_preferences(value, cert.prefs)) { - return false; - } - if (json_object_object_length(value)) { - return false; - } - } else if (rnp::str_case_eq(key, "protection")) { - if (!json_object_is_type(value, json_type_object)) { - return false; - } - if (!parse_protection(value, prot)) { - return false; - } - if (json_object_object_length(value)) { - return false; - } + cert.userid = str; + } + /* Preferences */ + auto obj = json_get_obj(jso, "preferences"); + if (obj) { + if (!parse_preferences(obj, cert.prefs)) { + return nullptr; } - // delete this field since it has been handled - json_object_object_del(jso, key); + json_object_object_del(jso, "preferences"); } - return !json_object_object_length(jso); + /* Do not allow unknown extra fields */ + return json_object_object_length(jso) ? nullptr : std::move(params); } -static bool -parse_keygen_sub(json_object * jso, - rnp_keygen_subkey_desc_t & desc, +static std::unique_ptr +parse_keygen_sub(rnp_ffi_t ffi, + json_object * jso, + rnp_selfsig_binding_info_t & binding, rnp_key_protection_params_t &prot) { - static const char *properties[] = {"usage", "expiration", "protection"}; - auto & binding = desc.binding; - - if (!parse_keygen_crypto(jso, desc.crypto)) { - return false; + /* Parse keygen params first */ + auto params = parse_keygen_params(ffi, jso); + if (!params) { + return nullptr; } - for (size_t i = 0; i < ARRAY_SIZE(properties); i++) { - json_object *value = NULL; - const char * key = properties[i]; - - if (!json_object_object_get_ex(jso, key, &value)) { - continue; - } - if (rnp::str_case_eq(key, "usage")) { - switch (json_object_get_type(value)) { - case json_type_array: { - int length = json_object_array_length(value); - for (int j = 0; j < length; j++) { - json_object *item = json_object_array_get_idx(value, j); - if (!json_object_is_type(item, json_type_string)) { - return false; - } - uint8_t flag = 0; - if (!str_to_key_flag(json_object_get_string(item), &flag)) { - return false; - } - if (binding.key_flags & flag) { - return false; - } - binding.key_flags |= flag; - } - } break; - case json_type_string: { - if (!str_to_key_flag(json_object_get_string(value), &binding.key_flags)) { - return false; - } - } break; - default: - return false; - } - } else if (rnp::str_case_eq(key, "expiration")) { - if (!json_object_is_type(value, json_type_int)) { - return false; - } - binding.key_expiration = json_object_get_int(value); - } else if (rnp::str_case_eq(key, "protection")) { - if (!json_object_is_type(value, json_type_object)) { - return false; - } - if (!parse_protection(value, prot)) { - return false; - } - if (json_object_object_length(value)) { - return false; - } - } - // delete this field since it has been handled - json_object_object_del(jso, key); + /* Parse common with primary key fields */ + if (!parse_keygen_common_fields(jso, binding.key_flags, binding.key_expiration, prot)) { + return nullptr; } - return !json_object_object_length(jso); + /* Do not allow unknown extra fields */ + return json_object_object_length(jso) ? nullptr : std::move(params); } static bool @@ -4749,23 +4616,20 @@ gen_json_primary_key(rnp_ffi_t ffi, pgp_fingerprint_t & fp, bool protect) { - rnp_keygen_primary_desc_t desc = {}; - // desc.crypto is a union - // so at least Clang 12 on Windows zero-initializes the first union member only - // keeping the "larger" member partially uninitialized - desc.crypto.dsa.q_bitlen = 0; + rnp_selfsig_cert_info_t cert = {}; + cert.key_expiration = DEFAULT_KEY_EXPIRATION; - desc.cert.key_expiration = DEFAULT_KEY_EXPIRATION; - if (!parse_keygen_primary(jsoparams, desc, prot)) { + auto keygen = parse_keygen_primary(ffi, jsoparams, cert, prot); + if (!keygen) { return RNP_ERROR_BAD_PARAMETERS; } pgp_key_t pub; pgp_key_t sec; - desc.crypto.ctx = &ffi->context; - if (!pgp_generate_primary_key(desc, true, sec, pub, ffi->secring->format)) { + if (!keygen->generate(cert, sec, pub, ffi->secring->format)) { return RNP_ERROR_GENERIC; } + if (!ffi->pubring->add_key(pub)) { return RNP_ERROR_OUT_OF_MEMORY; } @@ -4787,28 +4651,22 @@ gen_json_subkey(rnp_ffi_t ffi, pgp_key_t & prim_sec, pgp_fingerprint_t &fp) { - rnp_keygen_subkey_desc_t desc = {}; + rnp_selfsig_binding_info_t binding = {}; rnp_key_protection_params_t prot = {}; - desc.binding.key_expiration = DEFAULT_KEY_EXPIRATION; - if (!parse_keygen_sub(jsoparams, desc, prot)) { + binding.key_expiration = DEFAULT_KEY_EXPIRATION; + auto keygen = parse_keygen_sub(ffi, jsoparams, binding, prot); + if (!keygen) { return RNP_ERROR_BAD_PARAMETERS; } - if (!desc.binding.key_flags) { + if (!binding.key_flags) { /* Generate encrypt-only subkeys by default */ - desc.binding.key_flags = PGP_KF_ENCRYPT; + binding.key_flags = PGP_KF_ENCRYPT; } pgp_key_t pub; pgp_key_t sec; - desc.crypto.ctx = &ffi->context; - if (!pgp_generate_subkey(desc, - true, - prim_sec, - prim_pub, - sec, - pub, - ffi->pass_provider, - ffi->secring->format)) { + if (!keygen->generate( + binding, prim_sec, prim_pub, sec, pub, ffi->pass_provider, ffi->secring->format)) { return RNP_ERROR_GENERIC; } if (!ffi->pubring->add_key(pub)) { @@ -5009,7 +4867,7 @@ try { if (password && (ret = rnp_op_generate_set_protection_password(subop, password))) { goto done; } - if (pgp_pk_alg_capabilities(subop->crypto.key_alg) & PGP_KF_ENCRYPT) { + if (pgp_pk_alg_capabilities(subop->keygen_params.alg()) & PGP_KF_ENCRYPT) { if ((ret = rnp_op_generate_add_usage(subop, "encrypt"))) { goto done; } @@ -5207,11 +5065,8 @@ try { return RNP_ERROR_BAD_PARAMETERS; } - *op = new rnp_op_generate_st(); - (*op)->ffi = ffi; + *op = new rnp_op_generate_st(ffi, key_alg); (*op)->primary = true; - (*op)->crypto.key_alg = key_alg; - (*op)->crypto.ctx = &ffi->context; (*op)->cert.key_flags = default_key_flags(key_alg, false); (*op)->cert.key_expiration = DEFAULT_KEY_EXPIRATION; @@ -5243,11 +5098,8 @@ try { return RNP_ERROR_BAD_PARAMETERS; } - *op = new rnp_op_generate_st(); - (*op)->ffi = ffi; + *op = new rnp_op_generate_st(ffi, key_alg); (*op)->primary = false; - (*op)->crypto.key_alg = key_alg; - (*op)->crypto.ctx = &ffi->context; (*op)->binding.key_flags = default_key_flags(key_alg, true); (*op)->binding.key_expiration = DEFAULT_KEY_EXPIRATION; (*op)->primary_sec = primary->sec; @@ -5264,22 +5116,11 @@ try { return RNP_ERROR_NULL_POINTER; } - switch (op->crypto.key_alg) { - case PGP_PKA_RSA: - case PGP_PKA_RSA_ENCRYPT_ONLY: - case PGP_PKA_RSA_SIGN_ONLY: - op->crypto.rsa.modulus_bit_len = bits; - break; - case PGP_PKA_ELGAMAL: - op->crypto.elgamal.key_bitlen = bits; - break; - case PGP_PKA_DSA: - op->crypto.dsa.p_bitlen = bits; - break; - default: + auto bitkeygen = dynamic_cast(&op->keygen_params.key_params()); + if (!bitkeygen) { return RNP_ERROR_BAD_PARAMETERS; } - + bitkeygen->set_bits(bits); return RNP_SUCCESS; } FFI_GUARD @@ -5290,10 +5131,12 @@ try { if (!op || !hash) { return RNP_ERROR_NULL_POINTER; } - if (!str_to_hash_alg(hash, &op->crypto.hash_alg)) { + pgp_hash_alg_t halg = PGP_HASH_UNKNOWN; + if (!str_to_hash_alg(hash, &halg)) { FFI_LOG(op->ffi, "Invalid hash: %s", hash); return RNP_ERROR_BAD_PARAMETERS; } + op->keygen_params.set_hash(halg); return RNP_SUCCESS; } FFI_GUARD @@ -5304,10 +5147,11 @@ try { if (!op) { return RNP_ERROR_NULL_POINTER; } - if (op->crypto.key_alg != PGP_PKA_DSA) { + if (op->keygen_params.alg() != PGP_PKA_DSA) { return RNP_ERROR_BAD_PARAMETERS; } - op->crypto.dsa.q_bitlen = qbits; + auto &dsa = dynamic_cast(op->keygen_params.key_params()); + dsa.set_qbits(qbits); return RNP_SUCCESS; } FFI_GUARD @@ -5318,12 +5162,15 @@ try { if (!op || !curve) { return RNP_ERROR_NULL_POINTER; } - if (!pk_alg_allows_custom_curve(op->crypto.key_alg)) { + if (!pk_alg_allows_custom_curve(op->keygen_params.alg())) { return RNP_ERROR_BAD_PARAMETERS; } - if (!curve_str_to_type(curve, &op->crypto.ecc.curve)) { + pgp_curve_t eccurve = PGP_CURVE_UNKNOWN; + if (!curve_str_to_type(curve, &eccurve)) { return RNP_ERROR_BAD_PARAMETERS; } + auto &ecc = dynamic_cast(op->keygen_params.key_params()); + ecc.set_curve(eccurve); return RNP_SUCCESS; } FFI_GUARD @@ -5410,7 +5257,7 @@ try { if (!str_to_key_flag(usage, &flag)) { return RNP_ERROR_BAD_PARAMETERS; } - if (!(pgp_pk_alg_capabilities(op->crypto.key_alg) & flag)) { + if (!(pgp_pk_alg_capabilities(op->keygen_params.alg()) & flag)) { return RNP_ERROR_NOT_SUPPORTED; } if (op->primary) { @@ -5586,7 +5433,7 @@ try { if (!op) { return RNP_ERROR_NULL_POINTER; } - op->pgp_version = PGP_V6; + op->keygen_params.set_version(PGP_V6); return RNP_SUCCESS; } FFI_GUARD @@ -5599,6 +5446,10 @@ try { if (!op || !param_cstr) { return RNP_ERROR_NULL_POINTER; } + auto slhdsa = dynamic_cast(&op->keygen_params.key_params()); + if (!slhdsa) { + return RNP_ERROR_BAD_PARAMETERS; + } sphincsplus_parameter_t param; std::string param_str = param_cstr; @@ -5619,7 +5470,7 @@ try { return RNP_ERROR_BAD_PARAMETERS; } - op->crypto.sphincsplus.param = param; + slhdsa->set_param(param); return RNP_SUCCESS; } FFI_GUARD @@ -5638,29 +5489,18 @@ try { pgp_password_provider_t prov; if (op->primary) { - rnp_keygen_primary_desc_t keygen = {}; - keygen.crypto = op->crypto; - keygen.cert = op->cert; - keygen.pgp_version = op->pgp_version; - op->cert.prefs = {}; /* generate call will free prefs */ - - if (!pgp_generate_primary_key(keygen, true, sec, pub, op->ffi->secring->format)) { + if (!op->keygen_params.generate(op->cert, sec, pub, op->ffi->secring->format)) { return RNP_ERROR_KEY_GENERATION; } } else { /* subkey generation */ - rnp_keygen_subkey_desc_t keygen = {}; - keygen.crypto = op->crypto; - keygen.binding = op->binding; - keygen.pgp_version = op->pgp_version; - if (!pgp_generate_subkey(keygen, - true, - *op->primary_sec, - *op->primary_pub, - sec, - pub, - op->ffi->pass_provider, - op->ffi->secring->format)) { + if (!op->keygen_params.generate(op->binding, + *op->primary_sec, + *op->primary_pub, + sec, + pub, + op->ffi->pass_provider, + op->ffi->secring->format)) { return RNP_ERROR_KEY_GENERATION; } } diff --git a/src/tests/cipher.cpp b/src/tests/cipher.cpp index c12b8f33c..2f7bc1743 100644 --- a/src/tests/cipher.cpp +++ b/src/tests/cipher.cpp @@ -34,6 +34,7 @@ #include "rnp_tests.h" #include "support.h" #include "fingerprint.h" +#include "keygen.hpp" TEST_F(rnp_tests, hash_test_success) { @@ -118,13 +119,12 @@ TEST_F(rnp_tests, pkcs1_rsa_test_success) uint8_t ptext[1024 / 8] = {'a', 'b', 'c', 0}; uint8_t dec[1024 / 8]; - rnp_keygen_crypto_params_t key_desc; - key_desc.key_alg = PGP_PKA_RSA; - key_desc.hash_alg = PGP_HASH_SHA256; - key_desc.rsa.modulus_bit_len = 1024; - key_desc.ctx = &global_ctx; + rnp::KeygenParams keygen(PGP_PKA_RSA, global_ctx); + auto & rsa = dynamic_cast(keygen.key_params()); + rsa.set_bits(1024); + pgp_key_pkt_t seckey; - assert_true(pgp_generate_seckey(key_desc, seckey, true)); + assert_true(keygen.generate(seckey, true)); pgp_encrypted_material_t enc; assert_rnp_success(seckey.material->encrypt(global_ctx, enc, ptext, 3)); @@ -139,13 +139,9 @@ TEST_F(rnp_tests, pkcs1_rsa_test_success) TEST_F(rnp_tests, rnp_test_eddsa) { - rnp_keygen_crypto_params_t key_desc; - key_desc.key_alg = PGP_PKA_EDDSA; - key_desc.hash_alg = PGP_HASH_SHA256; - key_desc.ctx = &global_ctx; - - pgp_key_pkt_t seckey; - assert_true(pgp_generate_seckey(key_desc, seckey, true)); + rnp::KeygenParams keygen(PGP_PKA_EDDSA, global_ctx); + pgp_key_pkt_t seckey; + assert_true(keygen.generate(seckey, true)); rnp::secure_vector hash(32); pgp_signature_material_t sig = {}; @@ -166,14 +162,13 @@ TEST_F(rnp_tests, rnp_test_eddsa) TEST_F(rnp_tests, rnp_test_x25519) { - rnp_keygen_crypto_params_t key_desc = {}; - key_desc.key_alg = PGP_PKA_ECDH; - key_desc.hash_alg = PGP_HASH_SHA256; - key_desc.ctx = &global_ctx; - key_desc.ecc.curve = PGP_CURVE_25519; + rnp::KeygenParams keygen(PGP_PKA_ECDH, global_ctx); + auto & ecc = dynamic_cast(keygen.key_params()); + ecc.set_curve(PGP_CURVE_25519); pgp_key_pkt_t seckey; - assert_true(pgp_generate_seckey(key_desc, seckey, true)); + assert_true(keygen.generate(seckey, true)); + /* check for length and correctly tweaked bits */ auto &ec = dynamic_cast(*seckey.material); assert_int_equal(ec.x().len, 32); @@ -249,17 +244,16 @@ TEST_F(rnp_tests, ecdsa_signverify_success) rnp::secure_vector hash(rnp::Hash::size(hash_alg)); global_ctx.rng.get(hash.data(), hash.size()); - rnp_keygen_crypto_params_t key_desc; - key_desc.key_alg = PGP_PKA_ECDSA; - key_desc.hash_alg = hash_alg; - key_desc.ecc.curve = curves[i].id; - key_desc.ctx = &global_ctx; + rnp::KeygenParams keygen(PGP_PKA_ECDSA, global_ctx); + keygen.set_hash(hash_alg); + auto &ecc = dynamic_cast(keygen.key_params()); + ecc.set_curve(curves[i].id); pgp_key_pkt_t seckey1; pgp_key_pkt_t seckey2; - assert_true(pgp_generate_seckey(key_desc, seckey1, true)); - assert_true(pgp_generate_seckey(key_desc, seckey2, true)); + assert_true(keygen.generate(seckey1, true)); + assert_true(keygen.generate(seckey2, true)); pgp_signature_material_t sig = {}; sig.halg = hash_alg; @@ -287,14 +281,13 @@ TEST_F(rnp_tests, ecdh_roundtrip) size_t in_len = sizeof(in); for (size_t i = 0; i < ARRAY_SIZE(curves); i++) { - rnp_keygen_crypto_params_t key_desc{}; - key_desc.key_alg = PGP_PKA_ECDH; - key_desc.hash_alg = PGP_HASH_SHA512; - key_desc.ecc.curve = curves[i].id; - key_desc.ctx = &global_ctx; + rnp::KeygenParams keygen(PGP_PKA_ECDH, global_ctx); + keygen.set_hash(PGP_HASH_SHA512); + auto &ecc = dynamic_cast(keygen.key_params()); + ecc.set_curve(curves[i].id); pgp_key_pkt_t ecdh_key1{}; - assert_true(pgp_generate_seckey(key_desc, ecdh_key1, true)); + assert_true(keygen.generate(ecdh_key1, true)); pgp_fingerprint_t ecdh_key1_fpr{}; assert_rnp_success(pgp_fingerprint(ecdh_key1_fpr, ecdh_key1)); @@ -340,14 +333,13 @@ TEST_F(rnp_tests, ecdh_decryptionNegativeCases) uint8_t res[36] = {0}; size_t res_len = sizeof(res); - rnp_keygen_crypto_params_t key_desc; - key_desc.key_alg = PGP_PKA_ECDH; - key_desc.hash_alg = PGP_HASH_SHA512; - key_desc.ecc.curve = PGP_CURVE_NIST_P_256; - key_desc.ctx = &global_ctx; + rnp::KeygenParams keygen(PGP_PKA_ECDH, global_ctx); + keygen.set_hash(PGP_HASH_SHA512); + auto &ecc = dynamic_cast(keygen.key_params()); + ecc.set_curve(PGP_CURVE_NIST_P_256); pgp_key_pkt_t ecdh_key1; - assert_true(pgp_generate_seckey(key_desc, ecdh_key1, true)); + assert_true(keygen.generate(ecdh_key1, true)); pgp_fingerprint_t ecdh_key1_fpr = {}; assert_rnp_success(pgp_fingerprint(ecdh_key1_fpr, ecdh_key1)); @@ -377,17 +369,14 @@ TEST_F(rnp_tests, ecdh_decryptionNegativeCases) #if defined(ENABLE_SM2) TEST_F(rnp_tests, sm2_roundtrip) { - rnp_keygen_crypto_params_t key_desc; - key_desc.key_alg = PGP_PKA_SM2; - key_desc.hash_alg = PGP_HASH_SM3; - key_desc.ecc.curve = PGP_CURVE_SM2_P_256; - key_desc.ctx = &global_ctx; + rnp::KeygenParams keygen(PGP_PKA_SM2, global_ctx); + keygen.set_hash(PGP_HASH_SM3); uint8_t key[27] = {0}; global_ctx.rng.get(key, sizeof(key)); pgp_key_pkt_t seckey; - assert_true(pgp_generate_seckey(key_desc, seckey, true)); + assert_true(keygen.generate(seckey, true)); auto &eckey = *seckey.material; @@ -523,26 +512,21 @@ TEST_F(rnp_tests, test_dsa_roundtrip) global_ctx.rng.get(message, sizeof(message)); for (size_t i = 0; i < ARRAY_SIZE(keys); i++) { - rnp_keygen_crypto_params_t key_desc; - key_desc.key_alg = PGP_PKA_DSA; - key_desc.hash_alg = keys[i].h; - key_desc.dsa.p_bitlen = keys[i].p; - key_desc.dsa.q_bitlen = keys[i].q; - key_desc.ctx = &global_ctx; + rnp::KeygenParams keygen(PGP_PKA_DSA, global_ctx); + keygen.set_hash(keys[i].h); + auto &dsa = dynamic_cast(keygen.key_params()); + dsa.set_bits(keys[i].p); + dsa.set_qbits(keys[i].q); pgp_key_pkt_t seckey; - assert_true(pgp_generate_seckey(key_desc, seckey, true)); + assert_true(keygen.generate(seckey, true)); // try to prevent timeouts in travis-ci - printf("p: %zu q: %zu h: %s\n", - key_desc.dsa.p_bitlen, - key_desc.dsa.q_bitlen, - rnp::Hash::name(key_desc.hash_alg)); + printf( + "p: %zu q: %zu h: %s\n", dsa.bits(), dsa.qbits(), rnp::Hash::name(keygen.hash())); fflush(stdout); - auto &key = *seckey.material; - - size_t h_size = rnp::Hash::size(keys[i].h); - rnp::secure_vector hash(message, message + h_size); + auto & key = *seckey.material; + rnp::secure_vector hash(message, message + rnp::Hash::size(keygen.hash())); pgp_signature_material_t sig = {}; assert_rnp_success(key.sign(global_ctx, sig, hash)); assert_rnp_success(key.verify(global_ctx, sig, hash)); @@ -555,34 +539,23 @@ TEST_F(rnp_tests, test_dsa_verify_negative) pgp_key_pkt_t sec_key1; pgp_key_pkt_t sec_key2; - struct key_params { - size_t p; - size_t q; - pgp_hash_alg_t h; - } key = {1024, 160, PGP_HASH_SHA1}; - global_ctx.rng.get(message, sizeof(message)); - rnp_keygen_crypto_params_t key_desc; - key_desc.key_alg = PGP_PKA_DSA; - key_desc.hash_alg = key.h; - key_desc.dsa.p_bitlen = key.p; - key_desc.dsa.q_bitlen = key.q; - key_desc.ctx = &global_ctx; + rnp::KeygenParams keygen(PGP_PKA_DSA, global_ctx); + keygen.set_hash(PGP_HASH_SHA1); + auto &dsa = dynamic_cast(keygen.key_params()); + dsa.set_bits(1024); + dsa.set_qbits(160); - assert_true(pgp_generate_seckey(key_desc, sec_key1, true)); + assert_true(keygen.generate(sec_key1, true)); // try to prevent timeouts in travis-ci - printf("p: %zu q: %zu h: %s\n", - key_desc.dsa.p_bitlen, - key_desc.dsa.q_bitlen, - rnp::Hash::name(key_desc.hash_alg)); - assert_true(pgp_generate_seckey(key_desc, sec_key2, true)); + printf("p: %zu q: %zu h: %s\n", dsa.bits(), dsa.qbits(), rnp::Hash::name(keygen.hash())); + assert_true(keygen.generate(sec_key2, true)); auto &key1 = *sec_key1.material; auto &key2 = *sec_key2.material; - size_t h_size = rnp::Hash::size(key.h); - rnp::secure_vector hash(message, message + h_size); + rnp::secure_vector hash(message, message + rnp::Hash::size(keygen.hash())); pgp_signature_material_t sig = {}; assert_rnp_success(key1.sign(global_ctx, sig, hash)); // wrong key used @@ -611,13 +584,11 @@ TEST_F(rnp_tests, kyber_ecdh_roundtrip) } for (size_t i = 0; i < ARRAY_SIZE(algs); i++) { - rnp_keygen_crypto_params_t key_desc; - key_desc.key_alg = algs[i]; - key_desc.hash_alg = PGP_HASH_SHA512; - key_desc.ctx = &global_ctx; + rnp::KeygenParams keygen(algs[i], global_ctx); + keygen.set_hash(PGP_HASH_SHA512); pgp_key_pkt_t key_pkt; - assert_true(pgp_generate_seckey(key_desc, key_pkt, true)); + assert_true(keygen.generate(key_pkt, true)); pgp_encrypted_material_t enc; assert_rnp_success(key_pkt.material->encrypt(global_ctx, enc, in, in_len)); @@ -643,16 +614,14 @@ TEST_F(rnp_tests, dilithium_exdsa_signverify_success) // Generate test data. Mainly to make valgrind not to complain about uninitialized data global_ctx.rng.get(message, sizeof(message)); - rnp_keygen_crypto_params_t key_desc; - key_desc.key_alg = algs[i]; - key_desc.hash_alg = hash_alg; - key_desc.ctx = &global_ctx; + rnp::KeygenParams keygen(algs[i], global_ctx); + keygen.set_hash(hash_alg); pgp_key_pkt_t seckey1; pgp_key_pkt_t seckey2; - assert_true(pgp_generate_seckey(key_desc, seckey1, true)); - assert_true(pgp_generate_seckey(key_desc, seckey2, true)); + assert_true(keygen.generate(seckey1, true)); + assert_true(keygen.generate(seckey2, true)); auto &key1 = *seckey1.material; auto &key2 = *seckey2.material; @@ -685,16 +654,15 @@ TEST_F(rnp_tests, sphincsplus_signverify_success) // data global_ctx.rng.get(message, sizeof(message)); - rnp_keygen_crypto_params_t key_desc; - key_desc.key_alg = algs[i]; - key_desc.sphincsplus.param = params[j]; - key_desc.ctx = &global_ctx; + rnp::KeygenParams keygen(algs[i], global_ctx); + auto &slhdsa = dynamic_cast(keygen.key_params()); + slhdsa.set_param(params[j]); pgp_key_pkt_t seckey1; pgp_key_pkt_t seckey2; - assert_true(pgp_generate_seckey(key_desc, seckey1, true)); - assert_true(pgp_generate_seckey(key_desc, seckey2, true)); + assert_true(keygen.generate(seckey1, true)); + assert_true(keygen.generate(seckey2, true)); auto & key1 = *seckey1.material; auto & key2 = *seckey2.material; diff --git a/src/tests/generatekey.cpp b/src/tests/generatekey.cpp index 1cc18e01c..442d85b02 100644 --- a/src/tests/generatekey.cpp +++ b/src/tests/generatekey.cpp @@ -39,6 +39,7 @@ #include "librepgp/stream-key.h" #include "defaults.h" #include +#include "keygen.hpp" static bool generate_test_key(const char *keystore, const char *userid, const char *hash, const char *home) @@ -1001,22 +1002,23 @@ TEST_F(rnp_tests, test_generated_key_sigs) // primary { - pgp_key_t pub; - pgp_key_t sec; - rnp_keygen_primary_desc_t desc; - pgp_sig_subpkt_t * subpkt = NULL; - pgp_signature_t * psig = NULL; - pgp_signature_t * ssig = NULL; - pgp_signature_info_t psiginfo = {}; - pgp_signature_info_t ssiginfo = {}; - - desc.crypto.key_alg = PGP_PKA_RSA; - desc.crypto.rsa.modulus_bit_len = 1024; - desc.crypto.ctx = &global_ctx; - desc.cert.userid = "test"; + pgp_key_t pub; + pgp_key_t sec; + pgp_sig_subpkt_t * subpkt = NULL; + pgp_signature_t * psig = NULL; + pgp_signature_t * ssig = NULL; + pgp_signature_info_t psiginfo = {}; + pgp_signature_info_t ssiginfo = {}; + + rnp::KeygenParams keygen(PGP_PKA_RSA, global_ctx); + auto & rsa = dynamic_cast(keygen.key_params()); + rsa.set_bits(1024); + + rnp_selfsig_cert_info_t cert{}; + cert.userid = "test"; // generate - assert_true(pgp_generate_primary_key(desc, true, sec, pub, PGP_KEY_STORE_GPG)); + assert_true(keygen.generate(cert, sec, pub, PGP_KEY_STORE_GPG)); // add to our rings assert_true(pubring->add_key(pub)); @@ -1137,26 +1139,23 @@ TEST_F(rnp_tests, test_generated_key_sigs) // sub { - pgp_key_t pub; - pgp_key_t sec; - rnp_keygen_subkey_desc_t desc = {}; - pgp_sig_subpkt_t * subpkt = NULL; - pgp_signature_t * psig = NULL; - pgp_signature_t * ssig = NULL; - pgp_signature_info_t psiginfo = {}; - pgp_signature_info_t ssiginfo = {}; - -#if defined(ENABLE_CRYPTO_REFRESH) - desc.pgp_version = PGP_V4; -#endif - desc.crypto.key_alg = PGP_PKA_RSA; - desc.crypto.rsa.modulus_bit_len = 1024; - desc.crypto.ctx = &global_ctx; + pgp_key_t pub; + pgp_key_t sec; + pgp_sig_subpkt_t * subpkt = NULL; + pgp_signature_t * psig = NULL; + pgp_signature_t * ssig = NULL; + pgp_signature_info_t psiginfo = {}; + pgp_signature_info_t ssiginfo = {}; + + rnp::KeygenParams keygen(PGP_PKA_RSA, global_ctx); + auto & rsa = dynamic_cast(keygen.key_params()); + rsa.set_bits(1024); // generate - pgp_password_provider_t prov = {}; - assert_true(pgp_generate_subkey( - desc, true, *primary_sec, *primary_pub, sec, pub, prov, PGP_KEY_STORE_GPG)); + pgp_password_provider_t prov = {}; + rnp_selfsig_binding_info_t binding{}; + assert_true(keygen.generate( + binding, *primary_sec, *primary_pub, sec, pub, prov, PGP_KEY_STORE_GPG)); assert_true(pub.valid()); assert_true(pub.validated()); assert_false(pub.expired()); diff --git a/src/tests/key-protect.cpp b/src/tests/key-protect.cpp index af951c326..634b9609d 100644 --- a/src/tests/key-protect.cpp +++ b/src/tests/key-protect.cpp @@ -29,6 +29,7 @@ #include "rnp_tests.h" #include "support.h" #include "crypto.h" +#include "keygen.hpp" bool rsa_sec_empty(const pgp::KeyMaterial &key) @@ -231,23 +232,20 @@ TEST_F(rnp_tests, test_key_protect_load_pgp) TEST_F(rnp_tests, test_key_protect_sec_data) { - rnp_keygen_primary_desc_t pri_desc = {}; - pri_desc.crypto.key_alg = PGP_PKA_RSA; - pri_desc.crypto.rsa.modulus_bit_len = 1024; - pri_desc.crypto.ctx = &global_ctx; - pri_desc.cert.userid = "test"; + rnp::KeygenParams keygen(PGP_PKA_RSA, global_ctx); + auto & rsa = dynamic_cast(keygen.key_params()); + rsa.set_bits(1024); - rnp_keygen_subkey_desc_t sub_desc = {}; - sub_desc.crypto.key_alg = PGP_PKA_RSA; - sub_desc.crypto.rsa.modulus_bit_len = 1024; - sub_desc.crypto.ctx = &global_ctx; + rnp_selfsig_cert_info_t cert{}; + cert.userid = "test"; + + rnp_selfsig_binding_info_t binding{}; /* generate raw unprotected keypair */ pgp_key_t skey, pkey, ssub, psub; pgp_password_provider_t prov = {}; - assert_true(pgp_generate_primary_key(pri_desc, true, skey, pkey, PGP_KEY_STORE_GPG)); - assert_true( - pgp_generate_subkey(sub_desc, true, skey, pkey, ssub, psub, prov, PGP_KEY_STORE_GPG)); + assert_true(keygen.generate(cert, skey, pkey, PGP_KEY_STORE_GPG)); + assert_true(keygen.generate(binding, skey, pkey, ssub, psub, prov, PGP_KEY_STORE_GPG)); assert_non_null(skey.pkt().sec_data); assert_non_null(ssub.pkt().sec_data); assert_null(pkey.pkt().sec_data);