From b7becffe0a46c26fbea8bc4f6c1ca7e06567e40c Mon Sep 17 00:00:00 2001 From: Rafael Junio da Cruz Date: Thu, 3 Apr 2025 10:56:16 +0100 Subject: [PATCH] Introduce generic keypair generation interface and engine ctrl command As discussed in https://github.com/OpenSC/libp11/pull/379 and https://github.com/OpenSC/libp11/pull/378 we need a generic interface that supports multiple algorithms for key generation. Attempt was made to create a new keygen method and register it in PKCS11_pkey_meths() in p11_pkey.c (so that it's possible to generate keys using OpenSSL's EVP_PKEY_* API) but multiple design issues appeared. How and where do you pass the key ID, token label and alike was the first question. As suggested by the maintainer here: https://github.com/OpenSC/libp11/pull/379#issuecomment-820588833, app_data from EVP_PKEY_CTX was (mis)used and that worked well. The reason why this approach was abandoned is because a good (or bad) way to get a handle of the PKCS11_CTX_private, that is necessary for the Cryptoki call, was not found. The way other operations work is that they rely on the key being loaded *_first_* through ENGINE_load_public(private)_key because this is when the PKCS11_CTX gets initialized and a handle to PKCS11_OBJECT_private gets set to the ex_data of the underlying key. Key generation obviously cannot rely on that mechanism since key doesn't yet exist. Instead, a generic PKCS11_generate_key interface was made that takes a structure describing the key generation algorithm. For now it only contains simple options like curve name for ECC or number of bits for RSA key generation. It also possible to configure CKA_SENSITIVE and CKA_EXTRACTABLE key attributes. This interface can then be used as any other PKCS11 wrapper interface or using the ENGINE control commands. Using it with ENGINE control commands is demonstrated in the new tests/keygen.c file. Code for ECC keygen was taken from: https://github.com/OpenSC/libp11/pull/379 and reworked to compile and work with some new additions to libp11 i.e. templates. --- .gitignore | 2 + src/eng_back.c | 40 ++++++- src/eng_front.c | 4 + src/engine.h | 1 + src/libp11-int.h | 10 +- src/libp11.exports | 1 + src/libp11.h | 46 +++++++- src/p11_front.c | 61 ++++++++++- src/p11_key.c | 166 +++++++++++++++++++++++----- src/p11_slot.c | 1 + src/util.h | 4 + src/util_uri.c | 31 ++++++ tests/Makefile.am | 6 +- tests/ec-keygen.c | 227 +++++++++++++++++++++++++++++++++++++++ tests/ec-keygen.softhsm | 49 +++++++++ tests/rsa-keygen.c | 227 +++++++++++++++++++++++++++++++++++++++ tests/rsa-keygen.softhsm | 49 +++++++++ 17 files changed, 887 insertions(+), 38 deletions(-) create mode 100644 tests/ec-keygen.c create mode 100755 tests/ec-keygen.softhsm create mode 100644 tests/rsa-keygen.c create mode 100755 tests/rsa-keygen.softhsm diff --git a/.gitignore b/.gitignore index 5932c0b4..1860df6c 100644 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,8 @@ tests/fork-change-slot-prov tests/rsa-oaep-prov tests/rsa-pss-sign-prov tests/store-cert-prov +tests/rsa-keygen +tests/ec-keygen tests/*.log tests/*.trs diff --git a/src/eng_back.c b/src/eng_back.c index c17b3bc8..e6ee9dfd 100644 --- a/src/eng_back.c +++ b/src/eng_back.c @@ -257,7 +257,43 @@ static int ENGINE_CTX_ctrl_set_vlog(ENGINE_CTX *ctx, void *cb) return 1; } -int ENGINE_CTX_ctrl(ENGINE_CTX *ctx, int cmd, long i, void *p, void (*f)(void)) +static int ENGINE_CTX_keygen(ENGINE_CTX *ctx, void *p) +{ + int rv; + PKCS11_KGEN_ATTRS *kg_attrs = p; + PKCS11_SLOT* found_slot = NULL; + + if (kg_attrs == NULL) + return 0; + + /* Delayed libp11 initialization */ + if (UTIL_CTX_init_libp11(ctx->util_ctx)) { + ENGerr(ENG_F_CTX_LOAD_OBJECT, ENG_R_INVALID_PARAMETER); + return 0; + } + + found_slot = UTIL_CTX_find_token(ctx->util_ctx, kg_attrs->token_label); + if (!found_slot) + return 0; + + /* 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))) + return 0; + + rv = PKCS11_keygen(found_slot->token, kg_attrs); + if (rv < 0) { + ENGINE_CTX_log(ctx, LOG_ERR, + "Failed to generate a key pair on the token. Error code: %d\n", + rv); + return 0; + } + + return 1; +} + +int ENGINE_CTX_ctrl(ENGINE_CTX *ctx, int cmd, long i, void *p, void (*f)()) { (void)i; /* We don't currently take integer parameters */ (void)f; /* We don't currently take callback parameters */ @@ -293,6 +329,8 @@ int ENGINE_CTX_ctrl(ENGINE_CTX *ctx, int cmd, long i, void *p, void (*f)(void)) return ENGINE_CTX_ctrl_set_vlog(ctx, p); case CMD_DEBUG_LEVEL: return ENGINE_CTX_ctrl_set_debug_level(ctx, (int)i); + case CMD_KEYGEN: + return ENGINE_CTX_keygen(ctx, p); default: ENGerr(ENG_F_CTX_ENGINE_CTRL, ENG_R_UNKNOWN_COMMAND); break; diff --git a/src/eng_front.c b/src/eng_front.c index cd179eaa..a0767eee 100644 --- a/src/eng_front.c +++ b/src/eng_front.c @@ -88,6 +88,10 @@ static const ENGINE_CMD_DEFN engine_cmd_defns[] = { "DEBUG_LEVEL", "Set the debug level: 0=emerg, 1=alert, 2=crit, 3=err, 4=warning, 5=notice (default), 6=info, 7=debug", ENGINE_CMD_FLAG_NUMERIC}, + {CMD_KEYGEN, + "KEYGEN", + "Generate asymmetric key pair", + ENGINE_CMD_FLAG_INTERNAL}, {0, NULL, NULL, 0} }; diff --git a/src/engine.h b/src/engine.h index fe00e83a..b228fdbd 100644 --- a/src/engine.h +++ b/src/engine.h @@ -68,6 +68,7 @@ #define CMD_RE_ENUMERATE (ENGINE_CMD_BASE + 10) #define CMD_VLOG_A (ENGINE_CMD_BASE + 11) #define CMD_DEBUG_LEVEL (ENGINE_CMD_BASE + 12) +#define CMD_KEYGEN (ENGINE_CMD_BASE + 13) typedef struct engine_ctx_st ENGINE_CTX; /* opaque */ diff --git a/src/libp11-int.h b/src/libp11-int.h index d94bb3ac..ee742858 100644 --- a/src/libp11-int.h +++ b/src/libp11-int.h @@ -332,9 +332,13 @@ extern int pkcs11_generate_random(PKCS11_SLOT_private *, unsigned char *r, unsig /* Internal implementation of deprecated features */ /* Generate and store a private key on the token */ -extern int pkcs11_generate_key(PKCS11_SLOT_private *tpriv, - int algorithm, unsigned int bits, - char *label, unsigned char *id, size_t id_len); +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); + +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); /* Get the RSA key modulus size (in bytes) */ extern int pkcs11_get_key_size(PKCS11_OBJECT_private *); diff --git a/src/libp11.exports b/src/libp11.exports index 4b3316b6..77a76020 100644 --- a/src/libp11.exports +++ b/src/libp11.exports @@ -52,3 +52,4 @@ ERR_load_PKCS11_strings PKCS11_set_ui_method ERR_get_CKR_code PKCS11_set_vlog_a_method +PKCS11_keygen diff --git a/src/libp11.h b/src/libp11.h index ddf041cf..e70f20ed 100644 --- a/src/libp11.h +++ b/src/libp11.h @@ -106,6 +106,35 @@ typedef struct PKCS11_ctx_st { void *_private; } PKCS11_CTX; +typedef struct PKCS11_ec_kgen_st { + const char *curve; +} PKCS11_EC_KGEN; + +typedef struct PKCS11_rsa_kgen_st { + unsigned int bits; +} PKCS11_RSA_KGEN; + +typedef struct PKCS11_params { + unsigned char extractable; + unsigned char sensitive; +} PKCS11_params; + +typedef struct PKCS11_kgen_attrs_st { + /* Key generation type from OpenSSL. Given the union below this should + * be either EVP_PKEY_EC or EVP_PKEY_RSA + */ + int type; + union { + PKCS11_EC_KGEN *ec; + PKCS11_RSA_KGEN *rsa; + } kgen; + const char *token_label; + const char *key_label; + const unsigned char *key_id; + size_t id_len; + const PKCS11_params *key_params; +} PKCS11_KGEN_ATTRS; + /** PKCS11 ASCII logging callback */ typedef void (*PKCS11_VLOG_A_CB)(int, const char *, va_list); @@ -431,12 +460,23 @@ extern void ERR_load_PKCS11_strings(void); * duplicate the functionality OpenSSL provides for EVP_PKEY objects */ +/** + * Generate key pair on the token + * + * @param token on which the key should be generated + * @param kgen_attrs struct describing key generation (selection of algorithm, + * algorithm parameters...) + * @retval 0 on success + * @retval -1 error + */ +extern int PKCS11_keygen(PKCS11_TOKEN *token, PKCS11_KGEN_ATTRS *kgen_attrs); + /** * Generate a private key on the token * * @param token token returned by PKCS11_find_token() - * @param algorithm IGNORED (still here for backward compatibility) - * @param bits size of the modulus in bits + * @param algorithm EVP_PKEY_EC any other value select EVP_PKEY_RSA + * @param bits_or_nid size of the modulus in bits or the nid of the curve * @param label label for this key * @param id bytes to use as the id value * @param id_len length of the id value @@ -444,7 +484,7 @@ extern void ERR_load_PKCS11_strings(void); * @retval -1 error */ extern int PKCS11_generate_key(PKCS11_TOKEN *token, - int algorithm, unsigned int bits, + int algorithm, unsigned int bits_or_nid, char *label, unsigned char *id, size_t id_len); /* Get the RSA key modulus size (in bytes) */ diff --git a/src/p11_front.c b/src/p11_front.c index c2be2ceb..2948c786 100644 --- a/src/p11_front.c +++ b/src/p11_front.c @@ -15,9 +15,13 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include "libp11-int.h" +/* The maximum length of PIN */ +#define MAX_PIN_LENGTH 256 + /* The following exported functions are *not* implemented here: * PKCS11_get_rsa_method * PKCS11_get_ecdsa_method @@ -401,14 +405,63 @@ int PKCS11_set_ui_method(PKCS11_CTX *pctx, UI_METHOD *ui_method, void *ui_user_d /* External interface to the deprecated features */ -int PKCS11_generate_key(PKCS11_TOKEN *token, - int algorithm, unsigned int bits, - char *label, unsigned char *id, size_t id_len) +int PKCS11_keygen(PKCS11_TOKEN *token, PKCS11_KGEN_ATTRS *kg) { + if (token == NULL || kg == NULL || kg->id_len > MAX_PIN_LENGTH) + return -1; PKCS11_SLOT_private *slot = PRIVSLOT(token->slot); if (check_slot_fork(slot) < 0) return -1; - return pkcs11_generate_key(slot, algorithm, bits, label, id, id_len); + + switch(kg->type) { + case EVP_PKEY_RSA: + return pkcs11_rsa_keygen(slot, kg->kgen.rsa->bits, + kg->key_label, kg->key_id, kg->id_len, kg->key_params); + case EVP_PKEY_EC: + return pkcs11_ec_keygen(slot, kg->kgen.ec->curve, + kg->key_label, kg->key_id, kg->id_len, kg->key_params); + default: + return -1; + } +} + +int PKCS11_generate_key(PKCS11_TOKEN *token, + int algorithm, unsigned int bits_or_nid, + char *label, unsigned char *id, size_t id_len) +{ + PKCS11_params key_params = { .extractable = 0, .sensitive = 1 }; + PKCS11_EC_KGEN ec_kgen; + PKCS11_RSA_KGEN rsa_kgen; + PKCS11_KGEN_ATTRS kgen_attrs = { 0 }; + + switch (algorithm) { + case EVP_PKEY_EC: + ec_kgen.curve = OBJ_nid2sn(bits_or_nid); + kgen_attrs = (PKCS11_KGEN_ATTRS){ + .type = EVP_PKEY_EC, + .kgen.ec = &ec_kgen, + .token_label = (const char *)token->label, + .key_label = label, + .key_id = (const unsigned char *)id, + .id_len = id_len, + .key_params = &key_params + }; + break; + + default: + rsa_kgen.bits = bits_or_nid; + kgen_attrs = (PKCS11_KGEN_ATTRS){ + .type = EVP_PKEY_RSA, + .kgen.rsa = &rsa_kgen, + .token_label = (const char *)token->label, + .key_label = label, + .key_id = (const unsigned char *)id, + .id_len = id_len, + .key_params = &key_params + }; + } + + return PKCS11_keygen(token, &kgen_attrs); } int PKCS11_get_key_size(PKCS11_KEY *pkey) diff --git a/src/p11_key.c b/src/p11_key.c index 06e9a262..b0363094 100644 --- a/src/p11_key.c +++ b/src/p11_key.c @@ -29,13 +29,19 @@ /* The maximum length of PIN */ #define MAX_PIN_LENGTH 256 -static int pkcs11_find_keys(PKCS11_SLOT_private *, CK_SESSION_HANDLE, unsigned int, PKCS11_TEMPLATE *); -static int pkcs11_next_key(PKCS11_CTX_private *ctx, PKCS11_SLOT_private *, - CK_SESSION_HANDLE session, CK_OBJECT_CLASS type); +static int pkcs11_find_keys(PKCS11_SLOT_private *, CK_SESSION_HANDLE, unsigned int, + PKCS11_TEMPLATE *); static int pkcs11_init_key(PKCS11_SLOT_private *, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE o, CK_OBJECT_CLASS type, PKCS11_KEY **); +static int pkcs11_init_keygen(PKCS11_SLOT_private *, CK_SESSION_HANDLE *); +static int pkcs11_next_key(PKCS11_CTX_private *ctx, PKCS11_SLOT_private *, + CK_SESSION_HANDLE session, CK_OBJECT_CLASS type); static int pkcs11_store_key(PKCS11_SLOT_private *, EVP_PKEY *, CK_OBJECT_CLASS, char *, unsigned char *, size_t, PKCS11_KEY **); +static void pkcs11_common_pubkey_attr(PKCS11_TEMPLATE *, const char *, + const unsigned char *, size_t); +static void pkcs11_common_privkey_attr(PKCS11_TEMPLATE *, const char *, + const unsigned char *, size_t, const PKCS11_params *); /* Helper to acquire object handle from given template */ static CK_OBJECT_HANDLE pkcs11_handle_from_template(PKCS11_SLOT_private *slot, @@ -272,11 +278,12 @@ int pkcs11_reload_object(PKCS11_OBJECT_private *obj) } /** - * Generate a key pair directly on token + * Generate RSA key pair directly on token */ -int pkcs11_generate_key(PKCS11_SLOT_private *slot, int algorithm, unsigned int bits, - char *label, unsigned char *id, size_t id_len) { - +int pkcs11_rsa_keygen(PKCS11_SLOT_private *slot, unsigned int bits, + const char *label, const unsigned char *id, size_t id_len, + const PKCS11_params *params) +{ PKCS11_CTX_private *ctx = slot->ctx; CK_SESSION_HANDLE session; PKCS11_TEMPLATE pubtmpl = {0}, privtmpl = {0}; @@ -284,36 +291,23 @@ int pkcs11_generate_key(PKCS11_SLOT_private *slot, int algorithm, unsigned int b CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0 }; CK_ULONG num_bits = bits; - CK_BYTE public_exponent[] = { 1, 0, 1 }; + CK_BYTE public_exponent[] = { 1, 0, 0, 0, 1 }; CK_OBJECT_HANDLE pub_key_obj, priv_key_obj; int rv; - (void)algorithm; /* squash the unused parameter warning */ - - if (pkcs11_get_session(slot, 1, &session)) + if (pkcs11_init_keygen(slot, &session)) return -1; + /* The following attributes are necessary for RSA encryption and DSA */ /* pubkey attributes */ - pkcs11_addattr(&pubtmpl, CKA_ID, id, id_len); - if (label) - pkcs11_addattr_s(&pubtmpl, CKA_LABEL, label); - pkcs11_addattr_bool(&pubtmpl, CKA_TOKEN, TRUE); + pkcs11_common_pubkey_attr(&pubtmpl, label, id, id_len); pkcs11_addattr_bool(&pubtmpl, CKA_ENCRYPT, TRUE); - pkcs11_addattr_bool(&pubtmpl, CKA_VERIFY, TRUE); - pkcs11_addattr_bool(&pubtmpl, CKA_WRAP, TRUE); pkcs11_addattr_var(&pubtmpl, CKA_MODULUS_BITS, num_bits); - pkcs11_addattr(&pubtmpl, CKA_PUBLIC_EXPONENT, public_exponent, 3); + pkcs11_addattr(&pubtmpl, CKA_PUBLIC_EXPONENT, public_exponent, 5); /* privkey attributes */ - pkcs11_addattr(&privtmpl, CKA_ID, id, id_len); - if (label) - pkcs11_addattr_s(&privtmpl, CKA_LABEL, label); - pkcs11_addattr_bool(&privtmpl, CKA_TOKEN, TRUE); - pkcs11_addattr_bool(&privtmpl, CKA_PRIVATE, TRUE); - pkcs11_addattr_bool(&privtmpl, CKA_SENSITIVE, TRUE); + pkcs11_common_privkey_attr(&privtmpl, label, id, id_len, params); pkcs11_addattr_bool(&privtmpl, CKA_DECRYPT, TRUE); - pkcs11_addattr_bool(&privtmpl, CKA_SIGN, TRUE); - pkcs11_addattr_bool(&privtmpl, CKA_UNWRAP, TRUE); /* call the pkcs11 module to create the key pair */ rv = CRYPTOKI_call(ctx, C_GenerateKeyPair( @@ -332,6 +326,84 @@ int pkcs11_generate_key(PKCS11_SLOT_private *slot, int algorithm, unsigned int b return 0; } +/** + * Generate EC key pair directly on token + */ +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) +{ + PKCS11_CTX_private *ctx = slot->ctx; + CK_SESSION_HANDLE session; + PKCS11_TEMPLATE pubtmpl = {0}, privtmpl = {0}; + CK_MECHANISM mechanism = { + CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0 + }; + CK_OBJECT_HANDLE pub_key_obj, priv_key_obj; + int rv; + unsigned char *ec_params = NULL; + int ec_params_len = 0; + unsigned char *tmp = NULL; + ASN1_OBJECT *curve_obj = NULL; + int curve_nid = NID_undef; + + if (pkcs11_init_keygen(slot, &session)) + return -1; + + curve_nid = EC_curve_nist2nid(curve); + if (curve_nid == NID_undef) + curve_nid = OBJ_sn2nid(curve); + if (curve_nid == NID_undef) + curve_nid = OBJ_ln2nid(curve); + if (curve_nid == NID_undef) + return -1; + curve_obj = OBJ_nid2obj(curve_nid); + if (!curve_obj) + return -1; + /* convert to DER format and take just the length */ + ec_params_len = i2d_ASN1_OBJECT(curve_obj, NULL); + if (ec_params_len < 0) + return -1; + ec_params = OPENSSL_malloc(ec_params_len); + if (!ec_params) + return -1; + /** + * ec_params points to beginning of DER encoded object. Since we need this + * location later and OpenSSL changes it in i2d_ASN1_OBJECT to point to 1 byte + * after DER encoded object, we assign the pointer to temporary throw-away + * pointer tmp + */ + tmp = ec_params; + if (i2d_ASN1_OBJECT(curve_obj, &tmp) < 0) + return -1; + + /* The following attributes are necessary for ECDSA and ECDH mechanisms */ + /* pubkey attributes */ + pkcs11_common_pubkey_attr(&pubtmpl, label, id, id_len); + pkcs11_addattr(&pubtmpl, CKA_EC_PARAMS, ec_params, ec_params_len); + + /* privkey attributes */ + pkcs11_common_privkey_attr(&privtmpl, label, id, id_len, params); + pkcs11_addattr_bool(&privtmpl, CKA_DERIVE, TRUE); + + /* call the pkcs11 module to create the key pair */ + rv = CRYPTOKI_call(ctx, C_GenerateKeyPair( + session, &mechanism, + pubtmpl.attrs, pubtmpl.nattr, + privtmpl.attrs, privtmpl.nattr, + &pub_key_obj, &priv_key_obj)); + pkcs11_put_session(slot, session); + + /* zap all memory allocated when building the template */ + pkcs11_zap_attrs(&privtmpl); + pkcs11_zap_attrs(&pubtmpl); + memset(ec_params, 0, ec_params_len); + OPENSSL_free(ec_params); + + CRYPTOKI_checkerr(CKR_F_PKCS11_GENERATE_KEY, rv); + return 0; +} + /* * Store a private key on the token */ @@ -741,6 +813,48 @@ static int pkcs11_init_key(PKCS11_SLOT_private *slot, CK_SESSION_HANDLE session, return 0; } +static int pkcs11_init_keygen(PKCS11_SLOT_private *slot, CK_SESSION_HANDLE *session) +{ + /* R/W session is mandatory for key generation. */ + if (slot->rw_mode != 1) { + 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; + } + return pkcs11_get_session(slot, 1, session); +} + +static void pkcs11_common_pubkey_attr(PKCS11_TEMPLATE *pubtmpl, + const char *label, const unsigned char *id, size_t id_len) +{ + /* Common pubkey attributes */ + pkcs11_addattr(pubtmpl, CKA_ID, (void *)id, id_len); + if (label) + pkcs11_addattr_s(pubtmpl, CKA_LABEL, label); + pkcs11_addattr_bool(pubtmpl, CKA_TOKEN, TRUE); + pkcs11_addattr_bool(pubtmpl, CKA_VERIFY, TRUE); + pkcs11_addattr_bool(pubtmpl, CKA_WRAP, TRUE); +} + +static void pkcs11_common_privkey_attr(PKCS11_TEMPLATE *privtmpl, + const char *label, const unsigned char *id, size_t id_len, + const PKCS11_params *params) +{ + /* Common privkey attributes */ + pkcs11_addattr(privtmpl, CKA_ID, (void *)id, id_len); + if (label) + pkcs11_addattr_s(privtmpl, CKA_LABEL, label); + pkcs11_addattr_bool(privtmpl, CKA_PRIVATE, TRUE); + pkcs11_addattr_bool(privtmpl, CKA_TOKEN, TRUE); + pkcs11_addattr_bool(privtmpl, CKA_SENSITIVE, params->sensitive); + pkcs11_addattr_bool(privtmpl, CKA_EXTRACTABLE, params->extractable); + pkcs11_addattr_bool(privtmpl, CKA_SIGN, TRUE); + pkcs11_addattr_bool(privtmpl, CKA_UNWRAP, TRUE); + +} + /* * Destroy all keys of a given type (public or private) */ diff --git a/src/p11_slot.c b/src/p11_slot.c index 10c94cd9..544cef5f 100644 --- a/src/p11_slot.c +++ b/src/p11_slot.c @@ -117,6 +117,7 @@ int pkcs11_open_session(PKCS11_SLOT_private *slot, int rw) if (rw != slot->rw_mode) { CRYPTOKI_call(ctx, C_CloseAllSessions(slot->id)); slot->rw_mode = rw; + slot->logged_in = -1; } slot->num_sessions = 0; slot->session_head = slot->session_tail = 0; diff --git a/src/util.h b/src/util.h index d2e07348..5f246fc9 100644 --- a/src/util.h +++ b/src/util.h @@ -64,6 +64,8 @@ void UTIL_CTX_log(UTIL_CTX *ctx, int level, const char *format, ...); int UTIL_CTX_set_pin(UTIL_CTX *ctx, const char *pin); void UTIL_CTX_set_force_login(UTIL_CTX *ctx, int force_login); +int UTIL_CTX_login(UTIL_CTX *ctx, PKCS11_SLOT *slot, UI_METHOD *ui_method, + void *ui_data); X509 *UTIL_CTX_get_cert_from_uri(UTIL_CTX *ctx, const char *uri, UI_METHOD *ui_method, void *ui_data); @@ -72,6 +74,8 @@ EVP_PKEY *UTIL_CTX_get_pubkey_from_uri(UTIL_CTX *ctx, const char *uri, EVP_PKEY *UTIL_CTX_get_privkey_from_uri(UTIL_CTX *ctx, const char *uri, UI_METHOD *ui_method, void *ui_data); +PKCS11_SLOT *UTIL_CTX_find_token(UTIL_CTX *ctx, const char *token_label); + #endif /* _UTIL_LIBP11_H */ /* vim: set noexpandtab: */ diff --git a/src/util_uri.c b/src/util_uri.c index 71776e38..83396d56 100644 --- a/src/util_uri.c +++ b/src/util_uri.c @@ -501,6 +501,15 @@ static int util_ctx_login(UTIL_CTX *ctx, PKCS11_SLOT *slot, PKCS11_TOKEN *tok, return 1; } +int UTIL_CTX_login(UTIL_CTX *ctx, PKCS11_SLOT *slot, UI_METHOD *ui_method, + void *ui_data) +{ + if (!slot->token) + return 0; + + return util_ctx_login(ctx, slot, slot->token, ui_method, ui_data); +} + /******************************************************************************/ /* URI parsing */ /******************************************************************************/ @@ -1189,6 +1198,28 @@ static void *util_ctx_load_object(UTIL_CTX *ctx, return obj; } +PKCS11_SLOT *UTIL_CTX_find_token(UTIL_CTX *ctx, const char *tok_lbl) +{ + PKCS11_SLOT *slot = NULL; + + if (!ctx->pkcs11_ctx) + return NULL; + + do { + slot = PKCS11_find_next_token(ctx->pkcs11_ctx, ctx->slot_list, + ctx->slot_count, slot); + if (slot && slot->token && slot->token->initialized + && 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"); + return NULL; +} + /******************************************************************************/ /* Certificate handling */ /******************************************************************************/ diff --git a/tests/Makefile.am b/tests/Makefile.am index 44c3cb30..6a102cf7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -27,7 +27,9 @@ check_PROGRAMS = \ store-cert-prov \ dup-key \ dup-key-prov \ - check-all-prov + check-all-prov \ + rsa-keygen \ + ec-keygen dist_check_SCRIPTS = \ rsa-testpkcs11.softhsm \ rsa-testfork.softhsm \ @@ -37,11 +39,13 @@ dist_check_SCRIPTS = \ rsa-pss-sign.softhsm \ rsa-oaep.softhsm \ rsa-check-privkey.softhsm \ + rsa-keygen.softhsm \ ec-testfork.softhsm \ ec-evp-sign.softhsm \ ec-check-privkey.softhsm \ ec-cert-store.softhsm \ ec-copy.softhsm \ + ec-keygen.softhsm \ fork-change-slot.softhsm \ case-insensitive.softhsm \ pkcs11-uri-without-token.softhsm \ diff --git a/tests/ec-keygen.c b/tests/ec-keygen.c new file mode 100644 index 00000000..5eebf0ae --- /dev/null +++ b/tests/ec-keygen.c @@ -0,0 +1,227 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +/* this code extensively uses deprecated features, so warnings are useless */ +#define OPENSSL_SUPPRESS_DEPRECATED + +#include +#include +#include +#include + +static void display_openssl_errors(int l) +{ + const char* file; + char buf[120]; + int e, line; + + if (ERR_peek_error() == 0) + return; + printf("At main.c:%d:\n", l); + + while ((e = ERR_get_error_line(&file, &line))) { + ERR_error_string(e, buf); + printf("- SSL %s: %s:%d\n", buf, file, line); + } +} + +static int sign_verify_test(EVP_PKEY *priv, EVP_PKEY *pub) { + EVP_MD_CTX *mdctx = NULL; + int retval = 0; + char *msg = "libp11"; + size_t slen; + unsigned char *sig = NULL; + + if (!priv || !pub) { + printf("Where are the keys?\n"); + return -1; + } + mdctx = EVP_MD_CTX_create(); + if (!mdctx) { + display_openssl_errors(__LINE__); + retval = -2; + goto err; + } + if (EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, priv) != 1) { + display_openssl_errors(__LINE__); + retval = -3; + goto err; + } + if (EVP_DigestSignUpdate(mdctx, msg, strlen(msg)) != 1) { + display_openssl_errors(__LINE__); + retval = -4; + goto err; + } + if (EVP_DigestSignFinal(mdctx, NULL, &slen) != 1) { + display_openssl_errors(__LINE__); + retval = -5; + goto err; + } + if (!(sig = OPENSSL_malloc(sizeof(unsigned char) * (slen)))) { + display_openssl_errors(__LINE__); + retval = -6; + goto err; + } + if (EVP_DigestSignFinal(mdctx, sig, &slen) != 1) { + display_openssl_errors(__LINE__); + retval = -7; + printf("Sign fail\n"); + goto err; + } + printf("Sign success\n"); + + if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pub) != 1) { + display_openssl_errors(__LINE__); + retval = -8; + goto err; + } + if (EVP_DigestVerifyUpdate(mdctx, msg, strlen(msg)) != 1) { + display_openssl_errors(__LINE__); + retval = -9; + goto err; + } + if (EVP_DigestVerifyFinal(mdctx, sig, slen) == 1) { + printf("Verify success\n"); + retval = 0; + goto err; + } else { + display_openssl_errors(__LINE__); + printf("Verify fail\n"); + retval = -10; + goto err; + } + +err: + if (sig) + OPENSSL_free(sig); + if (mdctx) + EVP_MD_CTX_destroy(mdctx); + return retval; +} + +int main(int argc, char* argv[]) +{ + int ret = EXIT_FAILURE, res; + ENGINE* engine = NULL; + const char *efile, *module; + char *key_pass; + EVP_PKEY *ecpb = NULL; + EVP_PKEY *ecpr = NULL; + PKCS11_EC_KGEN ec = { + .curve = "P-256" + }; + PKCS11_params params = { + .sensitive = 1, + .extractable = 0, + }; + PKCS11_KGEN_ATTRS eckg = { + .type = EVP_PKEY_EC, + .kgen.ec = &ec, + .token_label = NULL, + .key_label = NULL, + .key_id = (const unsigned char *)"\x22\x33", + .id_len = 2, + .key_params = ¶ms, + }; + + if (argc < 5) { + printf("Too few arguments\n"); + printf("%s [TOKEN1] [KEY-LABEL] [PIN] [CONF] [module]\n", argv[0]); + goto cleanup; + } + eckg.token_label = argv[1]; + eckg.key_label = argv[2]; + key_pass = argv[3]; + efile = argv[4]; + module = argv[5]; + + res = CONF_modules_load_file(efile, "engines", 0); + if (res <= 0) { + printf("cannot load %s\n", efile); + display_openssl_errors(__LINE__); + goto cleanup; + } + + ENGINE_add_conf_module(); +#if OPENSSL_VERSION_NUMBER>=0x10100000 + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ + | OPENSSL_INIT_ADD_ALL_DIGESTS \ + | OPENSSL_INIT_LOAD_CONFIG, NULL); +#else + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_digests(); + ERR_load_crypto_strings(); +#endif + ERR_clear_error(); + + ENGINE_load_builtin_engines(); + engine = ENGINE_by_id("pkcs11"); + if (engine == NULL) { + printf("Could not get engine\n"); + display_openssl_errors(__LINE__); + goto cleanup; + } + + if (!ENGINE_ctrl_cmd_string(engine, "PIN", key_pass, 0)) { + display_openssl_errors(__LINE__); + goto cleanup; + } + if (!ENGINE_ctrl_cmd_string(engine, "DEBUG_LEVEL", "7", 0)) { + display_openssl_errors(__LINE__); + goto cleanup; + } + if (module) { + if (!ENGINE_ctrl_cmd_string(engine, "MODULE_PATH", module, 0)) { + display_openssl_errors(__LINE__); + goto cleanup; + } + } + if (!ENGINE_init(engine)) { + printf("Could not initialize engine\n"); + display_openssl_errors(__LINE__); + goto cleanup; + } + /* + * ENGINE_init() returned a functional reference, so free the structural + * reference from ENGINE_by_id(). + */ + ENGINE_free(engine); + + /* + * EC key generation test + */ + if (!ENGINE_ctrl_cmd(engine, "KEYGEN", 0, &eckg, NULL, 1)) { + printf("Could not generate EC keys\n"); + goto cleanup; + } + printf("EC keys generated\n"); + + ecpb = ENGINE_load_public_key(engine, "2233", NULL, NULL); + ecpr = ENGINE_load_private_key(engine, "2233", NULL, NULL); + if ((ret = sign_verify_test(ecpr, ecpb)) < 0) { + printf("EC Sign-verify failed with err code: %d\n", ret); + goto cleanup; + } + printf("EC Sign-verify success\n"); + + ret = 0; +cleanup: + ENGINE_finish(engine); + EVP_PKEY_free(ecpb); + EVP_PKEY_free(ecpr); + + return ret; +} diff --git a/tests/ec-keygen.softhsm b/tests/ec-keygen.softhsm new file mode 100755 index 00000000..a18f307e --- /dev/null +++ b/tests/ec-keygen.softhsm @@ -0,0 +1,49 @@ +#!/bin/bash + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +outdir="output.$$" + +# Load common test functions +. ${srcdir}/common.sh + +# Initialize SoftHSM DB +init_db + +# Create 2 different tokens +init_card "token1" + +# Load openssl settings +TEMP_LD_LIBRARY_PATH=${LD_LIBRARY_PATH} +. ${srcdir}/openssl-settings.sh + +${WRAPPER} ./ec-keygen token1 libp11-keylabel ${PIN} "${outdir}/engines.cnf" ${MODULE} +if test $? != 0; then + echo "Key generation failed" + exit 1 +fi + +# Restore settings +export LD_LIBRARY_PATH=${TEMP_LD_LIBRARY_PATH} + +echo "Checking pkcs11-tool result..." +list_objects | grep -q libp11-keylabel +if test $? != 0; then + echo "The key was not properly generated" + exit 1 +fi + +rm -rf "$outdir" + +exit 0 diff --git a/tests/rsa-keygen.c b/tests/rsa-keygen.c new file mode 100644 index 00000000..dc222937 --- /dev/null +++ b/tests/rsa-keygen.c @@ -0,0 +1,227 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +/* this code extensively uses deprecated features, so warnings are useless */ +#define OPENSSL_SUPPRESS_DEPRECATED + +#include +#include +#include +#include + +static void display_openssl_errors(int l) +{ + const char* file; + char buf[120]; + int e, line; + + if (ERR_peek_error() == 0) + return; + printf("At main.c:%d:\n", l); + + while ((e = ERR_get_error_line(&file, &line))) { + ERR_error_string(e, buf); + printf("- SSL %s: %s:%d\n", buf, file, line); + } +} + +static int sign_verify_test(EVP_PKEY *priv, EVP_PKEY *pub) { + EVP_MD_CTX *mdctx = NULL; + int retval = 0; + char *msg = "libp11"; + size_t slen; + unsigned char *sig = NULL; + + if (!priv || !pub) { + printf("Where are the keys?\n"); + return -1; + } + mdctx = EVP_MD_CTX_create(); + if (!mdctx) { + display_openssl_errors(__LINE__); + retval = -2; + goto err; + } + if (EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, priv) != 1) { + display_openssl_errors(__LINE__); + retval = -3; + goto err; + } + if (EVP_DigestSignUpdate(mdctx, msg, strlen(msg)) != 1) { + display_openssl_errors(__LINE__); + retval = -4; + goto err; + } + if (EVP_DigestSignFinal(mdctx, NULL, &slen) != 1) { + display_openssl_errors(__LINE__); + retval = -5; + goto err; + } + if (!(sig = OPENSSL_malloc(sizeof(unsigned char) * (slen)))) { + display_openssl_errors(__LINE__); + retval = -6; + goto err; + } + if (EVP_DigestSignFinal(mdctx, sig, &slen) != 1) { + display_openssl_errors(__LINE__); + retval = -7; + printf("Sign fail\n"); + goto err; + } + printf("Sign success\n"); + + if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pub) != 1) { + display_openssl_errors(__LINE__); + retval = -8; + goto err; + } + if (EVP_DigestVerifyUpdate(mdctx, msg, strlen(msg)) != 1) { + display_openssl_errors(__LINE__); + retval = -9; + goto err; + } + if (EVP_DigestVerifyFinal(mdctx, sig, slen) == 1) { + printf("Verify success\n"); + retval = 0; + goto err; + } else { + display_openssl_errors(__LINE__); + printf("Verify fail\n"); + retval = -10; + goto err; + } + +err: + if (sig) + OPENSSL_free(sig); + if (mdctx) + EVP_MD_CTX_destroy(mdctx); + return retval; +} + +int main(int argc, char* argv[]) +{ + int ret = EXIT_FAILURE, res; + ENGINE* engine = NULL; + const char *efile, *module; + char *key_pass; + EVP_PKEY *rsapb = NULL; + EVP_PKEY *rsapr = NULL; + PKCS11_RSA_KGEN rsa = { + .bits = 2048 + }; + PKCS11_params params = { + .sensitive = 1, + .extractable = 0, + }; + PKCS11_KGEN_ATTRS rsakg = { + .type = EVP_PKEY_RSA, + .kgen.rsa = &rsa, + .token_label = NULL, + .key_label = NULL, + .key_id = (const unsigned char *)"\x43\x21", + .id_len = 2, + .key_params = ¶ms, + }; + + if (argc < 5) { + printf("Too few arguments\n"); + printf("%s [TOKEN1] [KEY-LABEL] [PIN] [CONF] [module]\n", argv[0]); + goto cleanup; + } + rsakg.token_label = argv[1]; + rsakg.key_label = argv[2]; + key_pass = argv[3]; + efile = argv[4]; + module = argv[5]; + + res = CONF_modules_load_file(efile, "engines", 0); + if (res <= 0) { + printf("cannot load %s\n", efile); + display_openssl_errors(__LINE__); + goto cleanup; + } + + ENGINE_add_conf_module(); +#if OPENSSL_VERSION_NUMBER>=0x10100000 + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ + | OPENSSL_INIT_ADD_ALL_DIGESTS \ + | OPENSSL_INIT_LOAD_CONFIG, NULL); +#else + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_digests(); + ERR_load_crypto_strings(); +#endif + ERR_clear_error(); + + ENGINE_load_builtin_engines(); + engine = ENGINE_by_id("pkcs11"); + if (engine == NULL) { + printf("Could not get engine\n"); + display_openssl_errors(__LINE__); + goto cleanup; + } + + if (!ENGINE_ctrl_cmd_string(engine, "PIN", key_pass, 0)) { + display_openssl_errors(__LINE__); + goto cleanup; + } + if (!ENGINE_ctrl_cmd_string(engine, "DEBUG_LEVEL", "7", 0)) { + display_openssl_errors(__LINE__); + goto cleanup; + } + if (module) { + if (!ENGINE_ctrl_cmd_string(engine, "MODULE_PATH", module, 0)) { + display_openssl_errors(__LINE__); + goto cleanup; + } + } + if (!ENGINE_init(engine)) { + printf("Could not initialize engine\n"); + display_openssl_errors(__LINE__); + goto cleanup; + } + /* + * ENGINE_init() returned a functional reference, so free the structural + * reference from ENGINE_by_id(). + */ + ENGINE_free(engine); + + /* + * RSA key generation test + */ + if (!ENGINE_ctrl_cmd(engine, "KEYGEN", 0, &rsakg, NULL, 1)) { + printf("Could not generate RSA keys\n"); + goto cleanup; + } + printf("RSA keys generated\n"); + + rsapb = ENGINE_load_public_key(engine, "4321", NULL, NULL); + rsapr = ENGINE_load_private_key(engine, "4321", NULL, NULL); + if ((ret = sign_verify_test(rsapr, rsapb)) < 0) { + printf("RSA Sign-verify failed with err code: %d\n", ret); + goto cleanup; + } + printf("RSA Sign-verify success\n"); + + ret = 0; +cleanup: + ENGINE_finish(engine); + EVP_PKEY_free(rsapb); + EVP_PKEY_free(rsapr); + + return ret; +} diff --git a/tests/rsa-keygen.softhsm b/tests/rsa-keygen.softhsm new file mode 100755 index 00000000..67ad98ac --- /dev/null +++ b/tests/rsa-keygen.softhsm @@ -0,0 +1,49 @@ +#!/bin/bash + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +outdir="output.$$" + +# Load common test functions +. ${srcdir}/common.sh + +# Initialize SoftHSM DB +init_db + +# Create 2 different tokens +init_card "token1" + +# Load openssl settings +TEMP_LD_LIBRARY_PATH=${LD_LIBRARY_PATH} +. ${srcdir}/openssl-settings.sh + +${WRAPPER} ./rsa-keygen token1 libp11-keylabel ${PIN} "${outdir}/engines.cnf" ${MODULE} +if test $? != 0; then + echo "Key generation failed" + exit 1 +fi + +# Restore settings +export LD_LIBRARY_PATH=${TEMP_LD_LIBRARY_PATH} + +echo "Checking pkcs11-tool result..." +list_objects | grep -q libp11-keylabel +if test $? != 0; then + echo "The key was not properly generated" + exit 1 +fi + +rm -rf "$outdir" + +exit 0