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