diff --git a/.gitignore b/.gitignore index 1860df6c..a1aca43e 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,8 @@ examples/decrypt examples/getrandom examples/listkeys examples/listkeys_ext +examples/eckeygen +examples/rsakeygen test-driver tests/openssl_version @@ -83,6 +85,7 @@ tests/rsa-pss-sign-prov tests/store-cert-prov tests/rsa-keygen tests/ec-keygen +tests/check-all-prov tests/*.log tests/*.trs diff --git a/examples/Makefile.am b/examples/Makefile.am index ef9def42..f510f56d 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -4,7 +4,7 @@ AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir)/src \ EXTRA_DIST = README -noinst_PROGRAMS = auth decrypt getrandom listkeys listkeys_ext +noinst_PROGRAMS = auth decrypt getrandom listkeys listkeys_ext eckeygen rsakeygen LDADD = ../src/libp11.la $(OPENSSL_LIBS) diff --git a/examples/eckeygen.c b/examples/eckeygen.c new file mode 100644 index 00000000..a37f4fc6 --- /dev/null +++ b/examples/eckeygen.c @@ -0,0 +1,174 @@ +/* + * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * Elliptic Curve key generation + * + * 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 AUTHOR 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 AUTHOR 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 +#include + +#define CHECK_ERR(cond, txt, code) \ + do { \ + if (cond) { \ + fprintf(stderr, "%s\n", (txt)); \ + rc=(code); \ + goto end; \ + } \ + } while (0) + +static void error_queue(const char *name) +{ + if (ERR_peek_last_error()) { + fprintf(stderr, "%s generated errors:\n", name); + ERR_print_errors_fp(stderr); + } +} + +static int parse_hex_key_id(const char *input, unsigned char **output, size_t *size) +{ + size_t i, len = strlen(input); + + if (len % 2 != 0) { + return -1; + } + *size = len / 2; + *output = OPENSSL_zalloc(*size); + if (!*output) { + return -1; + } + for (i = 0; i < *size; i++) { + sscanf(input + (i * 2), "%2hhx", &(*output)[i]); + } + return 0; +} + +static void list_keys(const char *title, const PKCS11_KEY *keys, + const unsigned int nkeys) { + unsigned int i; + + printf("\n%s:\n", title); + for (i = 0; i < nkeys; i++) { + printf(" #%d id=", i); + for (size_t j = 0; j < keys[i].id_len; j++) { + printf("%02x", keys[i].id[j]); + } + printf(";object=%s\n", keys[i].label); + } +} + +int main(int argc, char *argv[]) +{ + PKCS11_CTX *ctx = NULL; + PKCS11_SLOT *slots = NULL, *slot; + PKCS11_KEY *keys; + unsigned int nslots, nkeys; + unsigned char *key_id = NULL; + size_t key_id_len = 0; + int rc = 0; + PKCS11_params params = {.sensitive = 1, .extractable = 0}; + PKCS11_EC_KGEN ec = {.curve = "P-256"}; + PKCS11_KGEN_ATTRS eckg = {0}; + + if (argc < 6) { + fprintf(stderr, "usage: %s [module] [TOKEN] [KEY-LABEL] [KEY-ID] [PIN]\n", argv[0]); + return 1; + } + + key_id_len = strlen(argv[4]); + rc = parse_hex_key_id(argv[4], &key_id, &key_id_len); + CHECK_ERR(rc < 0, "Invalid key ID format", 1); + + ctx = PKCS11_CTX_new(); + error_queue("PKCS11_CTX_new"); + + /* load PKCS#11 module */ + rc = PKCS11_CTX_load(ctx, argv[1]); + error_queue("PKCS11_CTX_load"); + CHECK_ERR(rc < 0, "loading PKCS#11 module failed", 2); + + /* get information on all slots */ + rc = PKCS11_enumerate_slots(ctx, &slots, &nslots); + error_queue("PKCS11_enumerate_slots"); + CHECK_ERR(rc < 0, "no slots available", 3); + + slot = PKCS11_find_token(ctx, slots, nslots); + error_queue("PKCS11_find_token"); + while (slot) { + if (slot->token && slot->token->initialized && slot->token->label + && strcmp(argv[2], slot->token->label) == 0) + break; + slot = PKCS11_find_next_token(ctx, slots, nslots, slot); + }; + CHECK_ERR(!slot || !slot->token, "no token available", 4); + + printf("Found token:\n"); + printf("Slot manufacturer......: %s\n", slot->manufacturer); + printf("Slot description.......: %s\n", slot->description); + printf("Slot token label.......: %s\n", slot->token->label); + printf("Slot token serialnr....: %s\n", slot->token->serialnr); + + rc = PKCS11_login(slot, 0, argv[5]); + error_queue("PKCS11_login"); + CHECK_ERR(rc < 0, "PKCS11_login failed", 5); + + eckg.type = EVP_PKEY_EC; + eckg.kgen.ec = &ec; + eckg.token_label = argv[2]; + eckg.key_label = argv[3]; + eckg.key_id = (const unsigned char *)key_id; + eckg.id_len = key_id_len; + eckg.key_params = ¶ms; + + rc = PKCS11_keygen(slot->token, &eckg); + error_queue("PKCS11_keygen"); + CHECK_ERR(rc < 0, "Failed to generate a key pair on the token", 6); + + printf("\nEC keys generated\n"); + + /* get private keys */ + rc = PKCS11_enumerate_keys(slot->token, &keys, &nkeys); + error_queue("PKCS11_enumerate_keys"); + CHECK_ERR(rc < 0, "PKCS11_enumerate_keys failed", 7); + CHECK_ERR(nkeys == 0, "No private keys found", 8); + list_keys("Private keys", keys, nkeys); + +end: + if (slots) + PKCS11_release_all_slots(ctx, slots, nslots); + if (ctx) { + PKCS11_CTX_unload(ctx); + PKCS11_CTX_free(ctx); + } + OPENSSL_free(key_id); + + if (rc) + printf("Failed (error code %d).\n", rc); + else + printf("Success.\n"); + return rc; +} + +/* vim: set noexpandtab: */ diff --git a/examples/rsakeygen.c b/examples/rsakeygen.c new file mode 100644 index 00000000..8e1afb7e --- /dev/null +++ b/examples/rsakeygen.c @@ -0,0 +1,174 @@ +/* + * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * RSA key generation + * + * 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 AUTHOR 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 AUTHOR 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 +#include + +#define CHECK_ERR(cond, txt, code) \ + do { \ + if (cond) { \ + fprintf(stderr, "%s\n", (txt)); \ + rc=(code); \ + goto end; \ + } \ + } while (0) + +static void error_queue(const char *name) +{ + if (ERR_peek_last_error()) { + fprintf(stderr, "%s generated errors:\n", name); + ERR_print_errors_fp(stderr); + } +} + +static int parse_hex_key_id(const char *input, unsigned char **output, size_t *size) +{ + size_t i, len = strlen(input); + + if (len % 2 != 0) { + return -1; + } + *size = len / 2; + *output = OPENSSL_zalloc(*size); + if (!*output) { + return -1; + } + for (i = 0; i < *size; i++) { + sscanf(input + (i * 2), "%2hhx", &(*output)[i]); + } + return 0; +} + +static void list_keys(const char *title, const PKCS11_KEY *keys, + const unsigned int nkeys) { + unsigned int i; + + printf("\n%s:\n", title); + for (i = 0; i < nkeys; i++) { + printf(" #%d id=", i); + for (size_t j = 0; j < keys[i].id_len; j++) { + printf("%02x", keys[i].id[j]); + } + printf(";object=%s\n", keys[i].label); + } +} + +int main(int argc, char *argv[]) +{ + PKCS11_CTX *ctx = NULL; + PKCS11_SLOT *slots = NULL, *slot; + PKCS11_KEY *keys; + unsigned int nslots, nkeys; + unsigned char *key_id = NULL; + size_t key_id_len = 0; + int rc = 0; + PKCS11_params params = {.sensitive = 1, .extractable = 0}; + PKCS11_RSA_KGEN rsa = {.bits = 2048}; + PKCS11_KGEN_ATTRS rsakg = {0}; + + if (argc < 6) { + fprintf(stderr, "usage: %s [module] [TOKEN] [KEY-LABEL] [KEY-ID] [PIN]\n", argv[0]); + return 1; + } + + key_id_len = strlen(argv[4]); + rc = parse_hex_key_id(argv[4], &key_id, &key_id_len); + CHECK_ERR(rc < 0, "Invalid key ID format", 1); + + ctx = PKCS11_CTX_new(); + error_queue("PKCS11_CTX_new"); + + /* load PKCS#11 module */ + rc = PKCS11_CTX_load(ctx, argv[1]); + error_queue("PKCS11_CTX_load"); + CHECK_ERR(rc < 0, "loading PKCS#11 module failed", 2); + + /* get information on all slots */ + rc = PKCS11_enumerate_slots(ctx, &slots, &nslots); + error_queue("PKCS11_enumerate_slots"); + CHECK_ERR(rc < 0, "no slots available", 3); + + slot = PKCS11_find_token(ctx, slots, nslots); + error_queue("PKCS11_find_token"); + while (slot) { + if (slot->token && slot->token->initialized && slot->token->label + && strcmp(argv[2], slot->token->label) == 0) + break; + slot = PKCS11_find_next_token(ctx, slots, nslots, slot); + }; + CHECK_ERR(!slot || !slot->token, "no token available", 4); + + printf("Found token:\n"); + printf("Slot manufacturer......: %s\n", slot->manufacturer); + printf("Slot description.......: %s\n", slot->description); + printf("Slot token label.......: %s\n", slot->token->label); + printf("Slot token serialnr....: %s\n", slot->token->serialnr); + + rc = PKCS11_login(slot, 0, argv[5]); + error_queue("PKCS11_login"); + CHECK_ERR(rc < 0, "PKCS11_login failed", 5); + + rsakg.type = EVP_PKEY_RSA; + rsakg.kgen.rsa = &rsa; + rsakg.token_label = argv[2]; + rsakg.key_label = argv[3]; + rsakg.key_id = (const unsigned char *)key_id; + rsakg.id_len = key_id_len; + rsakg.key_params = ¶ms; + + rc = PKCS11_keygen(slot->token, &rsakg); + error_queue("PKCS11_keygen"); + CHECK_ERR(rc < 0, "Failed to generate a key pair on the token", 6); + + printf("\nRSA keys generated\n"); + + /* get private keys */ + rc = PKCS11_enumerate_keys(slot->token, &keys, &nkeys); + error_queue("PKCS11_enumerate_keys"); + CHECK_ERR(rc < 0, "PKCS11_enumerate_keys failed", 7); + CHECK_ERR(nkeys == 0, "No private keys found", 8); + list_keys("Private keys", keys, nkeys); + +end: + if (slots) + PKCS11_release_all_slots(ctx, slots, nslots); + if (ctx) { + PKCS11_CTX_unload(ctx); + PKCS11_CTX_free(ctx); + } + OPENSSL_free(key_id); + + if (rc) + printf("Failed (error code %d).\n", rc); + else + printf("Success.\n"); + return rc; +} + +/* vim: set noexpandtab: */ diff --git a/src/eng_back.c b/src/eng_back.c index e6ee9dfd..bfa72745 100644 --- a/src/eng_back.c +++ b/src/eng_back.c @@ -279,7 +279,7 @@ static int ENGINE_CTX_keygen(ENGINE_CTX *ctx, void *p) /* Try logging in */ ERR_clear_error(); if (!(found_slot->token->loginRequired && UTIL_CTX_login(ctx->util_ctx, - found_slot, ctx->ui_method, ctx->ui_data))) + found_slot, ctx->ui_method, ctx->ui_data))) return 0; rv = PKCS11_keygen(found_slot->token, kg_attrs); diff --git a/src/libp11-int.h b/src/libp11-int.h index ee742858..0596b7df 100644 --- a/src/libp11-int.h +++ b/src/libp11-int.h @@ -334,11 +334,11 @@ extern int pkcs11_generate_random(PKCS11_SLOT_private *, unsigned char *r, unsig /* Generate and store a private key on the token */ extern int pkcs11_rsa_keygen(PKCS11_SLOT_private *tpriv, unsigned int bits, const char *label, const unsigned char *id, - size_t id_len, const PKCS11_params* params); + size_t id_len, const PKCS11_params *params); extern int pkcs11_ec_keygen(PKCS11_SLOT_private *tpriv, const char *curve , const char *label, const unsigned char *id, - size_t id_len, const PKCS11_params* params); + size_t id_len, const PKCS11_params *params); /* Get the RSA key modulus size (in bytes) */ extern int pkcs11_get_key_size(PKCS11_OBJECT_private *); diff --git a/src/libp11.h b/src/libp11.h index e70f20ed..a6d21e9b 100644 --- a/src/libp11.h +++ b/src/libp11.h @@ -257,7 +257,7 @@ PKCS11_SLOT *PKCS11_find_token(PKCS11_CTX *ctx, */ PKCS11_SLOT *PKCS11_find_next_token(PKCS11_CTX *ctx, PKCS11_SLOT *slots, unsigned int nslots, - PKCS11_SLOT *slot); + PKCS11_SLOT *slot); /** * Check if user is already authenticated to a card diff --git a/src/p11_front.c b/src/p11_front.c index 2948c786..a7287c15 100644 --- a/src/p11_front.c +++ b/src/p11_front.c @@ -407,9 +407,12 @@ int PKCS11_set_ui_method(PKCS11_CTX *pctx, UI_METHOD *ui_method, void *ui_user_d int PKCS11_keygen(PKCS11_TOKEN *token, PKCS11_KGEN_ATTRS *kg) { + PKCS11_SLOT_private *slot; + if (token == NULL || kg == NULL || kg->id_len > MAX_PIN_LENGTH) return -1; - PKCS11_SLOT_private *slot = PRIVSLOT(token->slot); + + slot = PRIVSLOT(token->slot); if (check_slot_fork(slot) < 0) return -1; diff --git a/src/p11_key.c b/src/p11_key.c index b0363094..fb25ac13 100644 --- a/src/p11_key.c +++ b/src/p11_key.c @@ -331,7 +331,7 @@ int pkcs11_rsa_keygen(PKCS11_SLOT_private *slot, unsigned int bits, */ int pkcs11_ec_keygen(PKCS11_SLOT_private *slot, const char *curve, const char *label, const unsigned char *id, size_t id_len, - const PKCS11_params* params) + const PKCS11_params *params) { PKCS11_CTX_private *ctx = slot->ctx; CK_SESSION_HANDLE session; @@ -815,14 +815,17 @@ static int pkcs11_init_key(PKCS11_SLOT_private *slot, CK_SESSION_HANDLE session, static int pkcs11_init_keygen(PKCS11_SLOT_private *slot, CK_SESSION_HANDLE *session) { + pthread_mutex_lock(&slot->lock); /* R/W session is mandatory for key generation. */ if (slot->rw_mode != 1) { + pthread_mutex_unlock(&slot->lock); if (pkcs11_open_session(slot, 1)) return -1; /* open_session will call C_CloseAllSessions which logs everyone out */ if (pkcs11_login(slot, 0, slot->prev_pin)) return -1; } + pthread_mutex_unlock(&slot->lock); return pkcs11_get_session(slot, 1, session); } @@ -852,7 +855,6 @@ static void pkcs11_common_privkey_attr(PKCS11_TEMPLATE *privtmpl, pkcs11_addattr_bool(privtmpl, CKA_EXTRACTABLE, params->extractable); pkcs11_addattr_bool(privtmpl, CKA_SIGN, TRUE); pkcs11_addattr_bool(privtmpl, CKA_UNWRAP, TRUE); - } /* diff --git a/src/p11_slot.c b/src/p11_slot.c index 544cef5f..d2c8c882 100644 --- a/src/p11_slot.c +++ b/src/p11_slot.c @@ -163,9 +163,9 @@ int pkcs11_get_session(PKCS11_SLOT_private *slot, int rw, CK_SESSION_HANDLE *ses slot->num_sessions--; if (slot->num_sessions == 0) { /* Object handles are valid across - sessions, so the cache should only be - cleared when there are no valid - sessions.*/ + * sessions, so the cache should only be + * cleared when there are no valid + * sessions.*/ pkcs11_wipe_cache(slot); } continue; diff --git a/src/util_uri.c b/src/util_uri.c index 83396d56..677ba50d 100644 --- a/src/util_uri.c +++ b/src/util_uri.c @@ -81,11 +81,12 @@ struct util_ctx_st { UTIL_CTX *UTIL_CTX_new() { - UTIL_CTX *ctx; + UTIL_CTX *ctx = OPENSSL_malloc(sizeof(UTIL_CTX)); - ctx = OPENSSL_malloc(sizeof(UTIL_CTX)); - if (ctx) - memset(ctx, 0, sizeof(UTIL_CTX)); + if (!ctx) + return NULL; + + memset(ctx, 0, sizeof(UTIL_CTX)); pthread_mutex_init(&ctx->lock, 0); return ctx; } @@ -101,8 +102,8 @@ void UTIL_CTX_free(UTIL_CTX *ctx) int UTIL_CTX_set_module(UTIL_CTX *ctx, const char *module) { - OPENSSL_free(ctx->module); - ctx->module = module ? OPENSSL_strdup(module) : NULL; + OPENSSL_free(ctx->module); + ctx->module = module ? OPENSSL_strdup(module) : NULL; return 1; } @@ -1207,16 +1208,16 @@ PKCS11_SLOT *UTIL_CTX_find_token(UTIL_CTX *ctx, const char *tok_lbl) do { slot = PKCS11_find_next_token(ctx->pkcs11_ctx, ctx->slot_list, - ctx->slot_count, slot); + ctx->slot_count, slot); if (slot && slot->token && slot->token->initialized - && slot->token->label - && !strncmp(slot->token->label, tok_lbl, 32)) { + && slot->token->label + && !strncmp(slot->token->label, tok_lbl, 32)) { return slot; } } while (!slot); UTIL_CTX_log(ctx, LOG_ERR, - "Initialized token with matching label not found...\n"); + "Initialized token with matching label not found...\n"); return NULL; }