From d041b02626a86cf62847293a17665c495f4c142a Mon Sep 17 00:00:00 2001 From: Zoltan Fridrich Date: Mon, 4 Sep 2023 15:20:38 +0200 Subject: [PATCH] Add command to generate keypair on a PKCS#11 token Signed-off-by: Zoltan Fridrich --- bash-completion/p11-kit | 2 +- common/compat.c | 10 + common/compat.h | 13 +- doc/manual/p11-kit.xml | 41 ++++ p11-kit/Makefile.am | 3 + p11-kit/generate-keypair.c | 457 +++++++++++++++++++++++++++++++++++++ p11-kit/meson.build | 5 +- p11-kit/p11-kit.c | 44 ++-- p11-kit/test-objects.sh | 28 ++- po/POTFILES.in | 1 + 10 files changed, 571 insertions(+), 33 deletions(-) create mode 100644 p11-kit/generate-keypair.c diff --git a/bash-completion/p11-kit b/bash-completion/p11-kit index 550fbdbf8..00dd694c2 100644 --- a/bash-completion/p11-kit +++ b/bash-completion/p11-kit @@ -10,7 +10,7 @@ _p11-kit() COMPREPLY=( $(compgen -W "$opts" -- "$cur") ) return elif [[ $cword -eq 1 ]]; then - local commands='export-object delete-object list-objects add-profile delete-profile list-profiles list-modules print-config extract server remote' + local commands='generate-keypair export-object delete-object list-objects add-profile delete-profile list-profiles list-modules print-config extract server remote' COMPREPLY=( $(compgen -W "$commands" -- "$cur") ) fi } && diff --git a/common/compat.c b/common/compat.c index 4e0c89c15..e17547531 100644 --- a/common/compat.c +++ b/common/compat.c @@ -1051,3 +1051,13 @@ p11_ascii_toupper (int c) return 'A' + (c - 'a'); return c; } + +bool +p11_ascii_strcaseeq (const char *s1, + const char *s2) +{ + while (p11_ascii_tolower (*s1) == p11_ascii_tolower (*s2++)) + if (*s1++ == '\0') + return true; + return false; +} diff --git a/common/compat.h b/common/compat.h index 643e7c8a4..14092b0b5 100644 --- a/common/compat.h +++ b/common/compat.h @@ -394,11 +394,14 @@ int isatty (int fd); #endif -void p11_strerror_r (int errnum, - char *buf, - size_t buflen); +void p11_strerror_r (int errnum, + char *buf, + size_t buflen); -int p11_ascii_tolower (int c); -int p11_ascii_toupper (int c); +int p11_ascii_tolower (int c); +int p11_ascii_toupper (int c); + +bool p11_ascii_strcaseeq (const char *s1, + const char *s2); #endif /* __COMPAT_H__ */ diff --git a/doc/manual/p11-kit.xml b/doc/manual/p11-kit.xml index 42e1336b3..b323d1c35 100644 --- a/doc/manual/p11-kit.xml +++ b/doc/manual/p11-kit.xml @@ -41,6 +41,9 @@ p11-kit delete-object ... + + p11-kit generate-keypair ... + p11-kit list-profiles ... @@ -140,6 +143,44 @@ $ p11-kit delete-object pkcs11:token + + Generate Key-pair + + Generate key-pair on a PKCS#11 token. + + + $ p11-kit generate-keypair [--label=label] --type=algorithm {--bits=n|--curve=name} pkcs11:token + + + Generate private-public key-pair of given type on specified PKCS#11 token. + Should be used together with --type option and one of --bits or --curve options. + + + + + Assigns label to the generated key-pair objects. + + + + Specify the type of keys to generate. + Supported values are rsa, ecdsa, ed25519. + This option is required. + + + + Specify the number of bits for the key-pair generation. + Cannot be used together with --curve option. + + + + Specify an elliptic curve for the key-pair generation. + Supported values are secp256r1, secp384r1, secp521r1. + Cannot be used together with --bits option. + + + + + List Profiles diff --git a/p11-kit/Makefile.am b/p11-kit/Makefile.am index 8926fcb77..23a16cd4c 100644 --- a/p11-kit/Makefile.am +++ b/p11-kit/Makefile.am @@ -267,6 +267,7 @@ p11_kit_p11_kit_SOURCES = \ p11-kit/delete-object.c \ p11-kit/delete-profile.c \ p11-kit/export-object.c \ + p11-kit/generate-keypair.c \ p11-kit/list-objects.c \ p11-kit/list-profiles.c \ p11-kit/lists.c \ @@ -292,6 +293,7 @@ p11_kit_p11_kit_testable_SOURCES = \ p11-kit/delete-object.c \ p11-kit/delete-profile.c \ p11-kit/export-object.c \ + p11-kit/generate-keypair.c \ p11-kit/list-objects.c \ p11-kit/list-profiles.c \ p11-kit/lists.c \ @@ -306,6 +308,7 @@ p11_kit_p11_kit_testable_LDADD = \ $(NULL) p11_kit_p11_kit_testable_CFLAGS = \ + -DP11_KIT_TESTABLE \ $(COMMON_CFLAGS) \ $(NULL) diff --git a/p11-kit/generate-keypair.c b/p11-kit/generate-keypair.c new file mode 100644 index 000000000..433386acc --- /dev/null +++ b/p11-kit/generate-keypair.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2023, Red Hat Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * 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. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + * + * Author: Zoltan Fridrich + */ + +#include "config.h" + +#include "attrs.h" +#include "compat.h" +#include "debug.h" +#include "iter.h" +#include "message.h" +#include "tool.h" + +#ifdef P11_KIT_TESTABLE +#include "mock.h" +#endif + +#include +#include +#include +#include + +#ifdef ENABLE_NLS +#include +#define _(x) dgettext(PACKAGE_NAME, x) +#else +#define _(x) (x) +#endif + +int +p11_kit_generate_keypair (int argc, + char *argv[]); + +static CK_MECHANISM +get_mechanism (const char *type) +{ + CK_MECHANISM m = { CKA_INVALID, NULL_PTR, 0 }; + +#ifdef P11_KIT_TESTABLE + if (p11_ascii_strcaseeq (type, "mock")) { + m.mechanism = CKM_MOCK_GENERATE; + m.pParameter = "generate"; + m.ulParameterLen = 9; + return m; + } +#endif + if (p11_ascii_strcaseeq (type, "rsa")) + m.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; + else if (p11_ascii_strcaseeq (type, "ecdsa")) + m.mechanism = CKM_ECDSA_KEY_PAIR_GEN; + else if (p11_ascii_strcaseeq (type, "ed25519")) + m.mechanism = CKM_EC_EDWARDS_KEY_PAIR_GEN; + + return m; +} + +static const uint8_t * +get_ec_params (const char *curve, + size_t *ec_params_len) +{ + static const uint8_t OID_SECP256R1[] = { 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 }; + static const uint8_t OID_SECP384R1[] = { 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 }; + static const uint8_t OID_SECP521R1[] = { 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23 }; + + if (p11_ascii_strcaseeq (curve, "secp256r1")) { + *ec_params_len = sizeof (OID_SECP256R1); + return OID_SECP256R1; + } else if (p11_ascii_strcaseeq (curve, "secp384r1")) { + *ec_params_len = sizeof (OID_SECP384R1); + return OID_SECP384R1; + } else if (p11_ascii_strcaseeq (curve, "secp521r1")) { + *ec_params_len = sizeof (OID_SECP521R1); + return OID_SECP521R1; + } + + return NULL; +} + +static bool +check_args (CK_MECHANISM_TYPE type, + CK_ULONG bits, + const uint8_t *ec_params) +{ + switch (type) { +#ifdef P11_KIT_TESTABLE + case CKM_MOCK_GENERATE: + break; +#endif + case CKM_RSA_PKCS_KEY_PAIR_GEN: + if (bits == 0) { + p11_message (_("no bits specified")); + return false; + } + break; + case CKM_ECDSA_KEY_PAIR_GEN: + case CKM_EC_EDWARDS_KEY_PAIR_GEN: + if (ec_params == NULL) { + p11_message (_("no curve specified")); + return false; + } + break; + case CKA_INVALID: + p11_message (_("no type specified")); + return false; + default: + p11_message (_("unkwnown mechanism type in %s"), __func__); + return false; + } + + if (bits != 0 && ec_params != NULL) { + p11_message (_("both %s and %s cannot be specified at once"), "--bits", "--curve"); + return false; + } + + return true; +} + +static bool +get_templates (const char *label, + CK_MECHANISM_TYPE type, + CK_ULONG bits, + const uint8_t *ec_params, + size_t ec_params_len, + CK_ATTRIBUTE **pubkey, + CK_ATTRIBUTE **privkey) +{ + CK_BBOOL tval = CK_TRUE, fval = CK_FALSE; + CK_ATTRIBUTE *pub = NULL, *priv = NULL, *tmp; + CK_ATTRIBUTE attr_token = { CKA_TOKEN, &tval, sizeof (tval) }; + CK_ATTRIBUTE attr_private_true = { CKA_PRIVATE, &tval, sizeof (tval) }; + CK_ATTRIBUTE attr_private_false = { CKA_PRIVATE, &fval, sizeof (fval) }; + CK_ATTRIBUTE attr_sign = { CKA_SIGN, &tval, sizeof (tval) }; + CK_ATTRIBUTE attr_verify = { CKA_VERIFY, &tval, sizeof (tval) }; + CK_ATTRIBUTE attr_decrypt = { CKA_DECRYPT, &tval, sizeof (tval) }; + CK_ATTRIBUTE attr_encrypt = { CKA_ENCRYPT, &tval, sizeof (tval) }; + CK_ATTRIBUTE attr_modulus = { CKA_MODULUS_BITS, &bits, sizeof (bits) }; + CK_ATTRIBUTE attr_ec_params = { CKA_EC_PARAMS, (void *)ec_params, ec_params_len }; + + pub = p11_attrs_build (NULL, &attr_token, &attr_private_false, &attr_sign, &attr_verify, NULL); + if (pub == NULL) { + p11_message (_("failed to allocate memory")); + goto error; + } + priv = p11_attrs_build (NULL, &attr_token, &attr_private_true, &attr_sign, &attr_verify, NULL); + if (priv == NULL) { + p11_message (_("failed to allocate memory")); + goto error; + } + + if (label != NULL) { + CK_ATTRIBUTE attr_label = { CKA_LABEL, (void *)label, strlen (label) }; + + tmp = p11_attrs_build (pub, &attr_label, NULL); + if (tmp == NULL) { + p11_message (_("failed to allocate memory")); + goto error; + } + pub = tmp; + tmp = p11_attrs_build (priv, &attr_label, NULL); + if (tmp == NULL) { + p11_message (_("failed to allocate memory")); + goto error; + } + priv = tmp; + } + + switch (type) { +#ifdef P11_KIT_TESTABLE + case CKM_MOCK_GENERATE: + break; +#endif + case CKM_RSA_PKCS_KEY_PAIR_GEN: + tmp = p11_attrs_build (pub, &attr_encrypt, &attr_modulus, NULL); + if (tmp == NULL) { + p11_message (_("failed to allocate memory")); + goto error; + } + pub = tmp; + tmp = p11_attrs_build (priv, &attr_decrypt, NULL); + if (tmp == NULL) { + p11_message (_("failed to allocate memory")); + goto error; + } + priv = tmp; + break; + case CKM_ECDSA_KEY_PAIR_GEN: + case CKM_EC_EDWARDS_KEY_PAIR_GEN: + tmp = p11_attrs_build (pub, &attr_ec_params, NULL); + if (tmp == NULL) { + p11_message (_("failed to allocate memory")); + goto error; + } + pub = tmp; + break; + default: + p11_message (_("unkwnown mechanism type in %s"), __func__); + goto error; + } + + *pubkey = pub; + *privkey = priv; + + return true; +error: + p11_attrs_free (pub); + p11_attrs_free (priv); + + return false; +} + +static int +generate_keypair (const char *token_str, + const char *label, + CK_MECHANISM mechanism, + CK_ULONG bits, + const uint8_t *ec_params, + size_t ec_params_len) +{ + int ret = 1; + CK_RV rv; + const char *pin = NULL; + P11KitUri *uri = NULL; + P11KitIter *iter = NULL; + CK_FUNCTION_LIST **modules = NULL; + CK_FUNCTION_LIST *module = NULL; + CK_SESSION_HANDLE session = 0; + CK_SLOT_ID slot = 0; + CK_ATTRIBUTE *pubkey = NULL, *privkey = NULL; + CK_OBJECT_HANDLE pubkey_obj, privkey_obj; + + if (!get_templates (label, mechanism.mechanism, bits, + ec_params, ec_params_len, &pubkey, &privkey)) { + p11_message (_("failed to create key templates")); + goto cleanup; + } + + uri = p11_kit_uri_new (); + if (uri == NULL) { + p11_message (_("failed to allocate memory for URI")); + goto cleanup; + } + + if (p11_kit_uri_parse (token_str, P11_KIT_URI_FOR_TOKEN, uri) != P11_KIT_URI_OK) { + p11_message (_("failed to parse the token URI")); + goto cleanup; + } + + modules = p11_kit_modules_load_and_initialize (0); + if (modules == NULL) { + p11_message (_("failed to load and initialize modules")); + goto cleanup; + } + + iter = p11_kit_iter_new (uri, P11_KIT_ITER_WITH_TOKENS | P11_KIT_ITER_WITHOUT_OBJECTS); + if (iter == NULL) { + p11_message (_("failed to initialize iterator")); + goto cleanup; + } + + p11_kit_iter_begin (iter, modules); + rv = p11_kit_iter_next (iter); + if (rv != CKR_OK) { + p11_message (_("failed to find the token: %s"), p11_kit_strerror (rv)); + goto cleanup; + } + + module = p11_kit_iter_get_module (iter); + if (module == NULL) { + p11_message (_("failed to obtain module")); + goto cleanup; + } + + slot = p11_kit_iter_get_slot (iter); + if (slot == 0) { + p11_message (_("failed to obtain slot")); + goto cleanup; + } + + rv = module->C_OpenSession (slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session); + if (rv != CKR_OK) { + p11_message (_("failed to open session: %s"), p11_kit_strerror (rv)); + goto cleanup; + } + + pin = p11_kit_uri_get_pin_value (uri); + if (pin != NULL) { + rv = module->C_Login (session, CKU_USER, (unsigned char *)pin, strlen (pin)); + if (rv != CKR_OK) { + p11_message (_("failed to login: %s"), p11_kit_strerror (rv)); + goto cleanup; + } + } + + rv = module->C_GenerateKeyPair (session, &mechanism, + pubkey, p11_attrs_count (pubkey), + privkey, p11_attrs_count (privkey), + &pubkey_obj, &privkey_obj); + if (rv != CKR_OK) { + p11_message (_("key-pair generation failed: %s"), p11_kit_strerror (rv)); + goto cleanup; + } + + ret = 0; + +cleanup: + if (session != 0) + module->C_CloseSession (session); + p11_attrs_free (pubkey); + p11_attrs_free (privkey); + p11_kit_iter_free (iter); + p11_kit_uri_free (uri); + if (modules != NULL) + p11_kit_modules_finalize_and_release (modules); + + return ret; +} + +int +p11_kit_generate_keypair (int argc, + char *argv[]) +{ + int opt, ret = 2; + char *label = NULL; + CK_ULONG bits = 0; + const uint8_t *ec_params = NULL; + size_t ec_params_len = 0; + CK_MECHANISM mechanism = { CKA_INVALID, NULL_PTR, 0 }; + + enum { + opt_verbose = 'v', + opt_quiet = 'q', + opt_help = 'h', + opt_label = 'l', + opt_type = 't', + opt_bits = 'b', + opt_curve = 'c', + }; + + struct option options[] = { + { "verbose", no_argument, NULL, opt_verbose }, + { "quiet", no_argument, NULL, opt_quiet }, + { "help", no_argument, NULL, opt_help }, + { "label", required_argument, NULL, opt_label }, + { "type", required_argument, NULL, opt_type }, + { "bits", required_argument, NULL, opt_bits }, + { "curve", required_argument, NULL, opt_curve }, + { 0 }, + }; + + p11_tool_desc usages[] = { + { 0, "usage: p11-kit generate-keypair [--label=