From 3d8cf274ff87c626ba86bd2a16c70253ebfb871d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Fri, 14 Apr 2017 10:39:26 +0300 Subject: [PATCH] pkcs15init: add support for secret key upload and generation --- src/libopensc/libopensc.exports | 2 + src/pkcs15init/pkcs15-init.h | 9 ++ src/pkcs15init/pkcs15-lib.c | 217 ++++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+) diff --git a/src/libopensc/libopensc.exports b/src/libopensc/libopensc.exports index 69af2cac..7931a268 100644 --- a/src/libopensc/libopensc.exports +++ b/src/libopensc/libopensc.exports @@ -285,6 +285,7 @@ sc_pkcs15init_erase_card_recursively sc_pkcs15init_finalize_card sc_pkcs15init_fixup_file sc_pkcs15init_generate_key +sc_pkcs15init_generate_secret_key sc_pkcs15init_get_asepcos_ops sc_pkcs15init_get_cardos_ops sc_pkcs15init_get_cryptoflex_ops @@ -313,6 +314,7 @@ sc_pkcs15init_store_data_object sc_pkcs15init_store_pin sc_pkcs15init_store_private_key sc_pkcs15init_store_public_key +sc_pkcs15init_store_secret_key sc_pkcs15init_unbind sc_pkcs15init_update_any_df sc_pkcs15init_update_certificate diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index 950c743b..44ab6352 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -28,6 +28,7 @@ extern "C" { #include "libopensc/pkcs15.h" #define DEFAULT_PRIVATE_KEY_LABEL "Private Key" +#define DEFAULT_SECRET_KEY_LABEL "Secret Key" #define SC_PKCS15INIT_X509_DIGITAL_SIGNATURE 0x0080UL #define SC_PKCS15INIT_X509_NON_REPUDIATION 0x0040UL @@ -317,6 +318,10 @@ extern int sc_pkcs15init_generate_key(struct sc_pkcs15_card *, struct sc_pkcs15init_keygen_args *, unsigned int keybits, struct sc_pkcs15_object **); +extern int sc_pkcs15init_generate_secret_key(struct sc_pkcs15_card *, + struct sc_profile *, + struct sc_pkcs15init_skeyargs *, + struct sc_pkcs15_object **); extern int sc_pkcs15init_store_private_key(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15init_prkeyargs *, @@ -330,6 +335,10 @@ extern int sc_pkcs15init_store_public_key(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15init_pubkeyargs *, struct sc_pkcs15_object **); +extern int sc_pkcs15init_store_secret_key(struct sc_pkcs15_card *, + struct sc_profile *, + struct sc_pkcs15init_skeyargs *, + struct sc_pkcs15_object **); extern int sc_pkcs15init_store_certificate(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15init_certargs *, diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index 70a27f19..ec719194 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -1276,6 +1276,83 @@ err: LOG_FUNC_RETURN(ctx, r); } +/* + * Prepare secret key download, and initialize a skdf entry + */ +static int +sc_pkcs15init_init_skdf(struct sc_pkcs15_card *p15card, struct sc_profile *profile, + struct sc_pkcs15init_skeyargs *keyargs, struct sc_pkcs15_object **res_obj) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_pkcs15_skey_info *key_info; + struct sc_pkcs15_object *object = NULL; + const char *label; + unsigned int usage; + unsigned int keybits = keyargs->value_len; + int r = 0, key_type; + + LOG_FUNC_CALLED(ctx); + if (!res_obj || !keybits) { + r = SC_ERROR_INVALID_ARGUMENTS; + LOG_TEST_GOTO_ERR(ctx, r, "Initialize SKDF entry failed"); + } + + *res_obj = NULL; + + if ((usage = keyargs->usage) == 0) { + usage = SC_PKCS15_PRKEY_USAGE_ENCRYPT|SC_PKCS15_PRKEY_USAGE_DECRYPT; + } + + if ((label = keyargs->label) == NULL) + label = DEFAULT_SECRET_KEY_LABEL; + + /* Create the skey object now. + * If we find out below that we're better off reusing an + * existing object, we'll ditch this one */ + key_type = key_pkcs15_algo(p15card, keyargs->algorithm); + r = key_type; + LOG_TEST_GOTO_ERR(ctx, r, "Unsupported key type"); + + object = sc_pkcs15init_new_object(key_type, label, &keyargs->auth_id, NULL); + if (object == NULL) + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate new SKey object"); + + key_info = (struct sc_pkcs15_skey_info *) object->data; + key_info->usage = usage; + key_info->native = 1; + key_info->key_reference = 0; + key_info->value_len = keybits; + key_info->access_flags = keyargs->access_flags; + /* Path is selected below */ + + if (keyargs->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) { + key_info->access_flags &= ~SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; + key_info->native = 0; + } + + /* Select a Key ID if the user didn't specify one, + * otherwise make sure it's compatible with our intended use */ + r = select_id(p15card, SC_PKCS15_TYPE_SKEY, &keyargs->id); + LOG_TEST_GOTO_ERR(ctx, r, "Cannot select ID for SKey object"); + + key_info->id = keyargs->id; + + r = select_object_path(p15card, profile, object, &key_info->path); + LOG_TEST_GOTO_ERR(ctx, r, "Failed to select secret key object path"); + + /* See if we need to select a key reference for this object */ + if (profile->ops->select_key_reference) + LOG_TEST_GOTO_ERR(ctx, SC_ERROR_NOT_SUPPORTED, "SKey keyreference selection not supported"); + + *res_obj = object; + object = NULL; + r = SC_SUCCESS; + +err: + if (object) + sc_pkcs15init_free_object(object); + LOG_FUNC_RETURN(ctx, r); +} static int _pkcd15init_set_aux_md_data(struct sc_pkcs15_card *p15card, struct sc_auxiliary_data **aux_data, @@ -1463,6 +1540,68 @@ sc_pkcs15init_generate_key(struct sc_pkcs15_card *p15card, struct sc_profile *pr LOG_FUNC_RETURN(ctx, r); } +/* + * Generate a new secret key + */ +int +sc_pkcs15init_generate_secret_key(struct sc_pkcs15_card *p15card, struct sc_profile *profile, + struct sc_pkcs15init_skeyargs *skey_args, struct sc_pkcs15_object **res_obj) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_pkcs15_object *object = NULL; + unsigned int keybits = skey_args->value_len; + int r; + + LOG_FUNC_CALLED(ctx); + /* check supported key size */ + r = check_keygen_params_consistency(p15card->card, skey_args->algorithm, NULL, &keybits); + LOG_TEST_RET(ctx, r, "Invalid key size"); + + if (check_key_compatibility(p15card, skey_args->algorithm, NULL, 0, + keybits, SC_ALGORITHM_ONBOARD_KEY_GEN)) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Cannot generate key with the given parameters"); + + if (profile->ops->generate_key == NULL) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Key generation not supported"); + + if (skey_args->id.len) { + /* Make sure that secret key's ID is the unique inside the PKCS#15 application */ + r = sc_pkcs15_find_skey_by_id(p15card, &skey_args->id, NULL); + if (!r) + LOG_TEST_RET(ctx, SC_ERROR_NON_UNIQUE_ID, "Non unique ID of the private key object"); + else if (r != SC_ERROR_OBJECT_NOT_FOUND) + LOG_TEST_RET(ctx, r, "Find private key error"); + } + + /* Set up the SKDF object */ + r = sc_pkcs15init_init_skdf(p15card, profile, skey_args, &object); + LOG_TEST_RET(ctx, r, "Set up secret key object error"); + + /* Generate the secret key on card */ + r = profile->ops->create_key(profile, p15card, object); + LOG_TEST_RET(ctx, r, "Cannot generate key: create key failed"); + + r = profile->ops->generate_key(profile, p15card, object, NULL); + LOG_TEST_RET(ctx, r, "Failed to generate key"); + + r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_SKDF, object); + LOG_TEST_RET(ctx, r, "Failed to add generated secret key object"); + + if (!r && profile->ops->emu_store_data) { + r = profile->ops->emu_store_data(p15card, profile, object, NULL, NULL); + if (r == SC_ERROR_NOT_IMPLEMENTED) + r = SC_SUCCESS; + LOG_TEST_RET(ctx, r, "Card specific 'store data' failed"); + } + + if (res_obj) + *res_obj = object; + + profile->dirty = 1; + + LOG_FUNC_RETURN(ctx, r); +} + /* * Store private key @@ -1707,6 +1846,78 @@ err: } +/* + * Store secret key + */ +int +sc_pkcs15init_store_secret_key(struct sc_pkcs15_card *p15card, struct sc_profile *profile, + struct sc_pkcs15init_skeyargs *keyargs, struct sc_pkcs15_object **res_obj) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_pkcs15_object *object = NULL; + int r = 0; + + LOG_FUNC_CALLED(ctx); + + /* Now check whether the card is able to handle this key */ + if (check_key_compatibility(p15card, keyargs->algorithm, NULL, 0, keyargs->key.data_len * 8, 0)) { + /* Make sure the caller explicitly tells us to store + * the key as extractable. */ + if (!(keyargs->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE)) + LOG_TEST_RET(ctx, SC_ERROR_INCOMPATIBLE_KEY, "Card does not support this key."); + } + + /* Select a intrinsic Key ID if user didn't specify one */ + r = sc_pkcs15init_select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_SKEY, + &keyargs->id, &keyargs->key); + LOG_TEST_RET(ctx, r, "Get intrinsic ID error"); + + /* Make sure that private key's ID is the unique inside the PKCS#15 application */ + r = sc_pkcs15_find_skey_by_id(p15card, &keyargs->id, NULL); + if (!r) + LOG_TEST_RET(ctx, SC_ERROR_NON_UNIQUE_ID, "Non unique ID of the secret key object"); + else if (r != SC_ERROR_OBJECT_NOT_FOUND) + LOG_TEST_RET(ctx, r, "Find secret key error"); + + /* Set up the SKDF object */ + r = sc_pkcs15init_init_skdf(p15card, profile, keyargs, &object); + LOG_TEST_RET(ctx, r, "Failed to initialize secret key object"); + + if (profile->ops->create_key) + r = profile->ops->create_key(profile, p15card, object); + LOG_TEST_RET(ctx, r, "Card specific 'create key' failed"); + + if (profile->ops->store_key) { + struct sc_pkcs15_prkey key; + memset(&key, 0, sizeof(key)); + key.algorithm = keyargs->algorithm; + key.u.secret = keyargs->key; + r = profile->ops->store_key(profile, p15card, object, &key); + } + LOG_TEST_RET(ctx, r, "Card specific 'store key' failed"); + + sc_pkcs15_free_object_content(object); + + /* Now update the SKDF */ + r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_SKDF, object); + LOG_TEST_RET(ctx, r, "Failed to add new secret key PKCS#15 object"); + + if (!r && profile->ops->emu_store_data) { + r = profile->ops->emu_store_data(p15card, profile, object, NULL, NULL); + if (r == SC_ERROR_NOT_IMPLEMENTED) + r = SC_SUCCESS; + LOG_TEST_RET(ctx, r, "Card specific 'store data' failed"); + } + + if (r >= 0 && res_obj) + *res_obj = object; + + profile->dirty = 1; + + LOG_FUNC_RETURN(ctx, r); +} + + /* * Store a certificate */ @@ -2336,6 +2547,12 @@ key_pkcs15_algo(struct sc_pkcs15_card *p15card, unsigned int algorithm) return SC_PKCS15_TYPE_PRKEY_GOSTR3410; case SC_ALGORITHM_EC: return SC_PKCS15_TYPE_PRKEY_EC; + case SC_ALGORITHM_DES: + return SC_PKCS15_TYPE_SKEY_DES; + case SC_ALGORITHM_3DES: + return SC_PKCS15_TYPE_SKEY_3DES; + case SC_ALGORITHM_AES: + return SC_PKCS15_TYPE_SKEY_GENERIC; } sc_log(ctx, "Unsupported key algorithm."); return SC_ERROR_NOT_SUPPORTED;