diff --git a/src/pkcs15init/gpk.profile b/src/pkcs15init/gpk.profile index a19d2f99..dc4b491b 100644 --- a/src/pkcs15init/gpk.profile +++ b/src/pkcs15init/gpk.profile @@ -57,6 +57,17 @@ filesystem { WRITE=$PIN; } + # Extractable private keys are stored in transparent EFs. + # Encryption of the content is performed by libopensc. + EF template-extractable-key { + file-id = 7000; + structure = transparent; + ACL = *=NEVER, + READ=$PIN, + UPDATE=$PIN, + WRITE=$PIN; + } + EF template-public-key { file-id = 8000; structure = transparent; diff --git a/src/pkcs15init/pkcs15-gpk.c b/src/pkcs15init/pkcs15-gpk.c index b0155c9e..d3f87681 100644 --- a/src/pkcs15init/pkcs15-gpk.c +++ b/src/pkcs15init/pkcs15-gpk.c @@ -300,7 +300,7 @@ gpk_new_key(struct sc_profile *profile, struct sc_card *card, struct sc_pkcs15_prkey *key, unsigned int index, struct sc_pkcs15_prkey_info *info) { - struct sc_file *keyfile; + struct sc_file *keyfile = NULL; struct pkdata data; int r; @@ -333,8 +333,10 @@ gpk_new_key(struct sc_profile *profile, struct sc_card *card, if (r >= 0) r = gpk_store_pk(profile, card, keyfile, &data); - info->path = keyfile->path; - sc_file_free(keyfile); + if (keyfile) { + info->path = keyfile->path; + sc_file_free(keyfile); + } return r; } @@ -364,13 +366,17 @@ gpk_new_file(struct sc_profile *profile, struct sc_card *card, #ifdef SC_PKCS15_TYPE_PRKEY_DSA case SC_PKCS15_TYPE_PRKEY_DSA: desc = "DSA private key"; - tag = "data"; + tag = "private-key"; break; case SC_PKCS15_TYPE_PUBKEY_DSA: desc = "DSA public key"; - tag = "data"; + tag = "public-key"; break; #endif + case SC_PKCS15_TYPE_PRKEY: + desc = "extractable private key"; + tag = "extractable-key"; + break; case SC_PKCS15_TYPE_CERT: desc = "certificate"; tag = "certificate"; diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index 3466ff6e..12706ca4 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -100,7 +100,13 @@ struct sc_pkcs15init_prkeyargs { unsigned long x509_usage; sc_pkcs15_prkey_t key; + + /* support for non-native keys */ + unsigned int extractable; + char * passphrase; }; +#define SC_PKCS15INIT_EXTRACTABLE 0x0001 +#define SC_PKCS15INIT_NO_PASSPHRASE 0x0002 struct sc_pkcs15init_pubkeyargs { struct sc_pkcs15_id id; diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index 4c086adf..cac31530 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -79,6 +79,8 @@ static int do_select_parent(struct sc_profile *, struct sc_card *, struct sc_file *, struct sc_file **); static int aodf_add_pin(struct sc_pkcs15_card *, struct sc_profile *, const struct sc_pkcs15_pin_info *, const char *); +static int check_key_compatibility(struct sc_pkcs15_card *, + struct sc_pkcs15_prkey *, unsigned int); static int fixup_rsa_key(struct sc_pkcs15_prkey_rsa *); static int fixup_dsa_key(struct sc_pkcs15_prkey_dsa *); static void default_error_handler(const char *fmt, ...); @@ -429,6 +431,21 @@ sc_pkcs15init_store_private_key(struct sc_pkcs15_card *p15card, if ((label = keyargs->label) == NULL) label = "Private Key"; + /* Now check whether the card is able to handle this key */ + if (!check_key_compatibility(p15card, &key, keybits)) { + /* Make sure the caller explicitly tells us to store + * the key non-natively. */ + if (!keyargs->extractable) { + p15init_error("Card does not support this key."); + return SC_ERROR_INCOMPATIBLE_KEY; + } + if (!keyargs->passphrase + && !(keyargs->extractable & SC_PKCS15INIT_NO_PASSPHRASE)) { + p15init_error("No key encryption passphrase given."); + return SC_ERROR_PASSPHRASE_REQUIRED; + } + } + key_info = calloc(1, sizeof(*key_info)); key_info->id = keyargs->id; key_info->usage = usage; @@ -444,23 +461,56 @@ sc_pkcs15init_store_private_key(struct sc_pkcs15_card *p15card, object->auth_id = keyargs->auth_id; strncpy(object->label, label, sizeof(object->label)); - /* Get the number of private keys already on this card */ - index = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, NULL, 0); - /* Set the SO PIN reference from card */ if ((r = set_so_pin_from_card(p15card, profile)) < 0) return r; - r = profile->ops->new_key(profile, p15card->card, - &key, index, key_info); - if (r == SC_ERROR_NOT_SUPPORTED) { - /* XXX: handle extractable keys here. - * Store the private key, encrypted. - * So how does PKCS15 say this should be done? */ - } + /* Get the number of private keys already on this card */ + index = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, NULL, 0); + if (!keyargs->extractable) { + r = profile->ops->new_key(profile, p15card->card, + &key, index, key_info); + if (r < 0) + return r; + } else { + sc_pkcs15_der_t encoded, wrapped, *der = &encoded; + struct sc_context *ctx = p15card->card->ctx; - if (r < 0) - return r; + key_info->native = 0; + object->flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; + object->flags &= ~SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; + + /* DER encode the private key */ + encoded.value = wrapped.value = NULL; + r = sc_pkcs15_encode_prkey(ctx, &key, &encoded.value, &encoded.len); + if (r < 0) + return r; + + if (keyargs->passphrase) { + r = sc_pkcs15_wrap_data(ctx, keyargs->passphrase, + der->value, der->len, + &wrapped.value, &wrapped.len); + if (r < 0) { + free(der->value); + return r; + } + der = &wrapped; + } + + r = sc_pkcs15init_store_data(p15card, profile, + SC_PKCS15_TYPE_PRKEY, der, &key_info->path); + + /* If the key is encrypted, flag the PrKDF entry as + * indirect-protected */ + if (keyargs->passphrase) + key_info->path.type = SC_PATH_TYPE_PATH_PROT; + + free(encoded.value); + free(wrapped.value); + + if (r < 0) + return r; + } r = sc_pkcs15_add_object(p15card, &p15card->df[SC_PKCS15_PRKDF], 0, object); @@ -715,6 +765,44 @@ sc_pkcs15init_keybits(sc_pkcs15_bignum_t *bn) return bits; } +/* + * Check whether the card has native crypto support for this key. + */ +static int +check_key_compatibility(struct sc_pkcs15_card *p15card, + struct sc_pkcs15_prkey *key, + unsigned int key_length) +{ + struct sc_algorithm_info *info; + unsigned int count; + + count = p15card->card->algorithm_count; + info = p15card->card->algorithms; + while (count--) { + /* XXX: check for equality, or <= ? */ + if (info->algorithm != key->algorithm + || info->key_length != key_length) + continue; + if (key->algorithm == SC_ALGORITHM_RSA + && info->u._rsa.exponent != 0) { + sc_pkcs15_bignum_t *e = &key->u.rsa.exponent; + unsigned long exponent = 0; + unsigned int n; + + if (e->len > 4) + continue; + for (n = 0; n < e->len; n++) { + exponent <<= 8; + exponent |= e->data[n]; + } + if (info->u._rsa.exponent != exponent) + continue; + } + return 1; + } + return 0; +} + /* * Check RSA key for consistency, and compute missing * CRT elements diff --git a/src/tools/pkcs15-init.c b/src/tools/pkcs15-init.c index 1c873957..9a57132e 100644 --- a/src/tools/pkcs15-init.c +++ b/src/tools/pkcs15-init.c @@ -67,6 +67,7 @@ static int do_convert_public_key(struct sc_pkcs15_pubkey *, EVP_PKEY *); static int do_convert_cert(sc_pkcs15_der_t *, X509 *); +static int init_keyargs(struct sc_pkcs15init_prkeyargs *); static int read_one_pin(struct sc_profile *, const char *, const struct sc_pkcs15_pin_info *, int, char **); static int get_pin_callback(struct sc_profile *profile, @@ -87,6 +88,8 @@ enum { OPT_OPTIONS = 0x100, OPT_PASSPHRASE, OPT_PUBKEY, + OPT_EXTRACTABLE, + OPT_UNPROTECTED, OPT_PIN1 = 0x10000, /* don't touch these values */ OPT_PUK1 = 0x10001, @@ -115,6 +118,9 @@ const struct option options[] = { { "passphrase", required_argument, 0, OPT_PASSPHRASE }, { "store-certificate", required_argument, 0, 'X' }, + { "extractable", no_argument, 0, OPT_EXTRACTABLE }, + { "insecure", no_argument, 0, OPT_UNPROTECTED }, + { "profile", required_argument, 0, 'p' }, { "options-file", required_argument, 0, OPT_OPTIONS }, { "debug", no_argument, 0, 'd' }, @@ -140,6 +146,9 @@ const char * option_help[] = { "Specify passphrase for unlocking secret key", "Store an X.509 certificate", + "Private key stored as an extractable key", + "Insecure mode: do not require PIN/passphrase for private key", + "Specify the profile to use", "Read additional command line options from file", "Enable debugging output", @@ -171,6 +180,7 @@ static int opt_debug = 0, opt_quiet = 0, opt_action = 0, opt_erase = 0, + opt_extractable = 0, opt_unprotected = 0; static char * opt_profile = "pkcs15"; static char * opt_infile = 0; @@ -405,17 +415,8 @@ do_store_private_key(struct sc_profile *profile) X509 *cert = NULL; int r; - memset(&args, 0, sizeof(args)); - if (opt_objectid) - sc_pkcs15_format_id(opt_objectid, &args.id); - if (opt_authid) { - sc_pkcs15_format_id(opt_authid, &args.auth_id); - } else if (!opt_unprotected) { - error("no PIN given for key - either use --unprotected or " - "specify a PIN using --auth-id"); - return SC_ERROR_INVALID_ARGUMENTS; - } - args.label = opt_objectlabel; + if ((r = init_keyargs(&args)) < 0) + return r; r = do_read_private_key(opt_infile, opt_format, &pkey, &cert); if (r < 0) @@ -514,8 +515,10 @@ do_generate_key(struct sc_profile *profile, const char *spec) unsigned int evp_algo, keybits = 1024; int r; + if ((r = init_keyargs(&args)) < 0) + return r; + /* Parse the key spec given on the command line */ - memset(&args, 0, sizeof(args)); if (!strncasecmp(spec, "rsa", 3)) { args.key.algorithm = SC_ALGORITHM_RSA; evp_algo = EVP_PKEY_RSA; @@ -541,17 +544,6 @@ do_generate_key(struct sc_profile *profile, const char *spec) } } - if (opt_objectid) - sc_pkcs15_format_id(opt_objectid, &args.id); - if (opt_authid) - sc_pkcs15_format_id(opt_authid, &args.auth_id); - else if (!opt_unprotected) { - error("no PIN given for key - either use --unprotected or " - "specify a PIN using --auth-id"); - return SC_ERROR_INVALID_ARGUMENTS; - } - args.label = opt_objectlabel; - r = sc_pkcs15init_generate_key(p15card, profile, &args, keybits, NULL); if (r < 0) { EVP_PKEY *pkey; @@ -584,6 +576,37 @@ do_generate_key(struct sc_profile *profile, const char *spec) return r; } +int +init_keyargs(struct sc_pkcs15init_prkeyargs *args) +{ + memset(args, 0, sizeof(*args)); + if (opt_objectid) + sc_pkcs15_format_id(opt_objectid, &args->id); + if (opt_authid) { + sc_pkcs15_format_id(opt_authid, &args->auth_id); + } else if (!opt_unprotected) { + error("no PIN given for key - either use --unprotected or \n" + "specify a PIN using --auth-id"); + return SC_ERROR_INVALID_ARGUMENTS; + } + if (opt_extractable) { + args->extractable |= SC_PKCS15INIT_EXTRACTABLE; + if (opt_passphrase) { + args->passphrase = opt_passphrase; + } else { + if (!opt_unprotected) { + error("no pass phrase given for key - " + "either use --unprotected or\n" + "specify a pass phrase using " + "--passphrase"); + return SC_ERROR_PASSPHRASE_REQUIRED; + } + args->extractable |= SC_PKCS15INIT_NO_PASSPHRASE; + } + } + args->label = opt_objectlabel; + return 0; +} /* * Callbacks from the pkcs15init to retrieve PINs @@ -971,6 +994,7 @@ do_convert_private_key(struct sc_pkcs15_prkey *key, EVP_PKEY *pk) do_convert_bignum(&dst->g, src->g); do_convert_bignum(&dst->priv, src->priv_key); DSA_free(src); + break; } default: fatal("Unsupported key algorithm\n"); @@ -1086,6 +1110,12 @@ handle_option(int c) opt_action = ACTION_STORE_PUBKEY; opt_infile = optarg; break; + case OPT_UNPROTECTED: + opt_unprotected++; + break; + case OPT_EXTRACTABLE: + opt_extractable++; + break; default: print_usage_and_die("pkcs15-init"); }