diff --git a/src/libopensc/pkcs15-sec.c b/src/libopensc/pkcs15-sec.c index a608df20..86cecf55 100644 --- a/src/libopensc/pkcs15-sec.c +++ b/src/libopensc/pkcs15-sec.c @@ -121,6 +121,11 @@ int sc_pkcs15_decipher(struct sc_pkcs15_card *p15card, if (!prkey->native) return SC_ERROR_EXTRACTABLE_KEY; + if (!(prkey->usage & (SC_PKCS15_PRKEY_USAGE_DECRYPT|SC_PKCS15_PRKEY_USAGE_UNWRAP))) { + error(ctx, "This key cannot be used for decryption\n"); + return SC_ERROR_NOT_ALLOWED; + } + alg_info = _sc_card_find_rsa_alg(p15card->card, prkey->modulus_length); if (alg_info == NULL) { error(ctx, "Card does not support RSA with key length %d\n", prkey->modulus_length); @@ -281,6 +286,11 @@ int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card, if (!prkey->native) return SC_ERROR_EXTRACTABLE_KEY; + if (!(prkey->usage & (SC_PKCS15_PRKEY_USAGE_SIGN|SC_PKCS15_PRKEY_USAGE_SIGNRECOVER))) { + error(ctx, "This key cannot be used for signing\n"); + return SC_ERROR_NOT_ALLOWED; + } + alg_info = _sc_card_find_rsa_alg(p15card->card, prkey->modulus_length); if (alg_info == NULL) { error(ctx, "Card does not support RSA with key length %d\n", prkey->modulus_length); diff --git a/src/libopensc/pkcs15.c b/src/libopensc/pkcs15.c index 149353a7..6def8089 100644 --- a/src/libopensc/pkcs15.c +++ b/src/libopensc/pkcs15.c @@ -28,6 +28,13 @@ #include +/* Search key when looking for objects */ +struct sc_pkcs15_search_key { + const struct sc_pkcs15_id * id; + unsigned int usage_mask, usage_value; + unsigned int flags_mask, flags_value; +}; + static int sc_pkcs15_bind_synthetic(struct sc_pkcs15_card *); void sc_pkcs15_print_card(const struct sc_pkcs15_card *card) @@ -684,10 +691,9 @@ int sc_pkcs15_get_objects(struct sc_pkcs15_card *p15card, int type, return sc_pkcs15_get_objects_cond(p15card, type, NULL, NULL, ret, ret_size); } -static int compare_obj_id(struct sc_pkcs15_object *obj, void *arg) +static int compare_obj_id(struct sc_pkcs15_object *obj, const sc_pkcs15_id_t *id) { void *data = obj->data; - const struct sc_pkcs15_id *id = (const struct sc_pkcs15_id *) arg; switch (obj->type) { case SC_PKCS15_TYPE_CERT_X509: @@ -706,13 +712,61 @@ static int compare_obj_id(struct sc_pkcs15_object *obj, void *arg) return 0; } -static int find_by_id(struct sc_pkcs15_card *p15card, - int type, const struct sc_pkcs15_id *id, - struct sc_pkcs15_object **out) +static int compare_obj_usage(sc_pkcs15_object_t *obj, unsigned int mask, unsigned int value) +{ + void *data = obj->data; + unsigned int usage; + + switch (obj->type) { + case SC_PKCS15_TYPE_PRKEY_RSA: + case SC_PKCS15_TYPE_PRKEY_DSA: + usage = ((struct sc_pkcs15_prkey_info *) data)->usage; + break; + case SC_PKCS15_TYPE_PUBKEY_RSA: + case SC_PKCS15_TYPE_PUBKEY_DSA: + usage = ((struct sc_pkcs15_pubkey_info *) data)->usage; + break; + default: + return 0; + } + return !((usage ^ value) & mask); +} + +static int compare_obj_flags(sc_pkcs15_object_t *obj, unsigned int mask, unsigned int value) +{ + void *data = obj->data; + unsigned int flags; + + switch (obj->type) { + case SC_PKCS15_TYPE_AUTH_PIN: + flags = ((struct sc_pkcs15_pin_info *) data)->flags; + break; + default: + return 0; + } + return !((flags ^ value) & mask); +} + +static int compare_obj_key(struct sc_pkcs15_object *obj, void *arg) +{ + struct sc_pkcs15_search_key *sk = (struct sc_pkcs15_search_key *) arg; + + if (sk->id && !compare_obj_id(obj, sk->id)) + return 0; + if (sk->usage_mask && !compare_obj_usage(obj, sk->usage_mask, sk->usage_value)) + return 0; + if (sk->flags_mask && !compare_obj_flags(obj, sk->flags_mask, sk->flags_value)) + return 0; + return 1; +} + +static int find_by_key(struct sc_pkcs15_card *p15card, + int type, struct sc_pkcs15_search_key *sk, + struct sc_pkcs15_object **out) { int r; - r = sc_pkcs15_get_objects_cond(p15card, type, compare_obj_id, (void *) id, out, 1); + r = sc_pkcs15_get_objects_cond(p15card, type, compare_obj_key, sk, out, 1); if (r < 0) return r; if (r == 0) @@ -720,6 +774,18 @@ static int find_by_id(struct sc_pkcs15_card *p15card, return 0; } +static int find_by_id(struct sc_pkcs15_card *p15card, + int type, const struct sc_pkcs15_id *id, + struct sc_pkcs15_object **out) +{ + struct sc_pkcs15_search_key sk; + + memset(&sk, 0, sizeof(sk)); + sk.id = id; + + return find_by_key(p15card, type, &sk, out); +} + int sc_pkcs15_find_cert_by_id(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out) @@ -755,35 +821,29 @@ int sc_pkcs15_find_data_object_by_id(struct sc_pkcs15_card *p15card, return find_by_id(p15card, SC_PKCS15_TYPE_DATA_OBJECT, id, out); } -static int compare_flags(struct sc_pkcs15_object *obj, void *arg) +int sc_pkcs15_find_prkey_by_id_usage(struct sc_pkcs15_card *p15card, + const struct sc_pkcs15_id *id, + unsigned int usage, + struct sc_pkcs15_object **out) { - struct sc_pkcs15_pin_info *pin; - unsigned int *match = (unsigned int *) arg; + struct sc_pkcs15_search_key sk; - assert (obj->type == SC_PKCS15_TYPE_AUTH_PIN); - pin = (struct sc_pkcs15_pin_info *) obj->data; - return (pin->flags & match[0]) == match[1]; + memset(&sk, 0, sizeof(sk)); + sk.usage_mask = sk.usage_value = usage; + sk.id = id; + + return find_by_key(p15card, SC_PKCS15_TYPE_PRKEY, &sk, out); } int sc_pkcs15_find_so_pin(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object **out) { - unsigned int match[2]; - int r; - - /* The PIN flags are masked with the first word and - * compared to the second word. */ - match[0] = SC_PKCS15_PIN_FLAG_SO_PIN; - match[1] = SC_PKCS15_PIN_FLAG_SO_PIN; + struct sc_pkcs15_search_key sk; - r = sc_pkcs15_get_objects_cond(p15card, - SC_PKCS15_TYPE_AUTH_PIN, compare_flags, - match, out, 1); - if (r < 0) - return r; - if (r == 0) - return SC_ERROR_OBJECT_NOT_FOUND; - return 0; + memset(&sk, 0, sizeof(sk)); + sk.flags_mask = sk.flags_value = SC_PKCS15_PIN_FLAG_SO_PIN; + + return find_by_key(p15card, SC_PKCS15_TYPE_AUTH_PIN, &sk, out); } int sc_pkcs15_add_object(struct sc_pkcs15_card *p15card, diff --git a/src/libopensc/pkcs15.h b/src/libopensc/pkcs15.h index 43aac28f..bbe503d8 100644 --- a/src/libopensc/pkcs15.h +++ b/src/libopensc/pkcs15.h @@ -443,6 +443,10 @@ int sc_pkcs15_create(struct sc_pkcs15_card *p15card, struct sc_card *card); int sc_pkcs15_find_prkey_by_id(struct sc_pkcs15_card *card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out); +int sc_pkcs15_find_prkey_by_id_usage(struct sc_pkcs15_card *card, + const struct sc_pkcs15_id *id, + unsigned int usage, + struct sc_pkcs15_object **out); int sc_pkcs15_find_pubkey_by_id(struct sc_pkcs15_card *card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out); diff --git a/src/pkcs11/framework-pkcs15.c b/src/pkcs11/framework-pkcs15.c index 1c3aa87d..8f6da2df 100644 --- a/src/pkcs11/framework-pkcs15.c +++ b/src/pkcs11/framework-pkcs15.c @@ -63,6 +63,7 @@ struct pkcs15_any_object { struct sc_pkcs15_object * p15_object; struct pkcs15_pubkey_object * related_pubkey; struct pkcs15_cert_object * related_cert; + struct pkcs15_prkey_object * related_privkey; }; struct pkcs15_cert_object { @@ -85,6 +86,7 @@ struct pkcs15_prkey_object { #define prv_p15obj base.p15_object #define prv_pubkey base.related_pubkey #define prv_cert base.related_cert +#define prv_next base.related_privkey struct pkcs15_pubkey_object { struct pkcs15_any_object base; @@ -320,6 +322,19 @@ __pkcs15_prkey_bind_related(struct pkcs15_fw_data *fw_data, struct pkcs15_prkey_ for (i = 0; i < fw_data->num_objects; i++) { struct pkcs15_any_object *obj = fw_data->objects[i]; + if (is_privkey(obj) && obj != (struct pkcs15_any_object *) pk) { + /* merge private keys with the same ID and + * different usage bits */ + struct pkcs15_prkey_object *other, **pp; + + other = (struct pkcs15_prkey_object *) obj; + if (sc_pkcs15_compare_id(&other->prv_info->id, id)) { + obj->base.flags |= SC_PKCS11_OBJECT_HIDDEN; + for (pp = &pk->prv_next; *pp; pp = &(*pp)->prv_next) + ; + *pp = (struct pkcs15_prkey_object *) obj; + } + } else if (is_cert(obj) && !pk->prv_cert) { struct pkcs15_cert_object *cert; @@ -400,7 +415,8 @@ pkcs15_add_object(struct sc_pkcs11_slot *slot, struct pkcs15_any_object *obj, CK_OBJECT_HANDLE_PTR pHandle) { - if (obj == NULL) + if (obj == NULL + || (obj->base.flags & (SC_PKCS11_OBJECT_HIDDEN | SC_PKCS11_OBJECT_RECURS))) return; if (pool_is_present(&slot->object_pool, obj)) @@ -414,6 +430,8 @@ pkcs15_add_object(struct sc_pkcs11_slot *slot, * XXX prevent infinite recursion when a card specifies two certificates * referring to each other. */ + obj->base.flags |= SC_PKCS11_OBJECT_RECURS; + switch (__p15_type(obj)) { case SC_PKCS15_TYPE_PRKEY_RSA: case SC_PKCS15_TYPE_CERT_X509: @@ -421,6 +439,8 @@ pkcs15_add_object(struct sc_pkcs11_slot *slot, pkcs15_add_object(slot, (struct pkcs15_any_object *) obj->related_cert, NULL); break; } + + obj->base.flags &= ~SC_PKCS11_OBJECT_RECURS; } static void pkcs15_init_slot(struct sc_pkcs15_card *card, diff --git a/src/pkcs11/sc-pkcs11.h b/src/pkcs11/sc-pkcs11.h index d07ec9c5..c51bc1fa 100644 --- a/src/pkcs11/sc-pkcs11.h +++ b/src/pkcs11/sc-pkcs11.h @@ -118,6 +118,8 @@ struct sc_pkcs11_object { }; #define SC_PKCS11_OBJECT_SEEN 0x0001 +#define SC_PKCS11_OBJECT_HIDDEN 0x0002 +#define SC_PKCS11_OBJECT_RECURS 0x8000 /* diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index cb42861d..0dbdaa10 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -118,15 +118,16 @@ struct sc_pkcs15init_prkeyargs { const char * label; unsigned long usage; unsigned long x509_usage; + unsigned int flags; 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 +#define SC_PKCS15INIT_SPLIT_KEY 0x0004 struct sc_pkcs15init_pubkeyargs { struct sc_pkcs15_id id; @@ -220,6 +221,11 @@ extern int sc_pkcs15init_erase_card_recursively(struct sc_card *, extern int sc_pkcs15init_rmdir(struct sc_card *, struct sc_profile *, struct sc_file *df); +/* Helper function for CardOS */ +extern int sc_pkcs15init_requires_restrictive_usage( + struct sc_pkcs15_card *, + struct sc_pkcs15init_prkeyargs *); + #ifdef __cplusplus } #endif diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index a03e070d..539afb18 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -667,12 +667,12 @@ sc_pkcs15init_store_private_key(struct sc_pkcs15_card *p15card, keyargs->x509_usage, keybits, 0)) { /* Make sure the caller explicitly tells us to store * the key non-natively. */ - if (!keyargs->extractable) { + if (!(keyargs->flags & SC_PKCS15INIT_EXTRACTABLE)) { p15init_error("Card does not support this key."); return SC_ERROR_INCOMPATIBLE_KEY; } if (!keyargs->passphrase - && !(keyargs->extractable & SC_PKCS15INIT_NO_PASSPHRASE)) { + && !(keyargs->flags & SC_PKCS15INIT_NO_PASSPHRASE)) { p15init_error("No key encryption passphrase given."); return SC_ERROR_PASSPHRASE_REQUIRED; } @@ -689,6 +689,10 @@ sc_pkcs15init_store_private_key(struct sc_pkcs15_card *p15card, /* Select a Key ID if the user didn't specify one, otherwise * make sure it's unique */ + if (keyargs->id.len != 0 + && (keyargs->flags & SC_PKCS15INIT_SPLIT_KEY)) { + /* Split key; this ID exists already */ + } else if ((r = select_id(p15card, SC_PKCS15_TYPE_PRKEY, &keyargs->id)) < 0) return r; @@ -700,7 +704,7 @@ sc_pkcs15init_store_private_key(struct sc_pkcs15_card *p15card, /* 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) { + if (!(keyargs->flags & SC_PKCS15INIT_EXTRACTABLE)) { r = profile->ops->new_key(profile, p15card->card, &key, index, key_info); if (r < 0) @@ -1079,11 +1083,11 @@ sc_pkcs15init_keybits(sc_pkcs15_bignum_t *bn) * 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 x509_usage, - unsigned int key_length, - unsigned int flags) +__check_key_compatibility(struct sc_pkcs15_card *p15card, + struct sc_pkcs15_prkey *key, + unsigned int x509_usage, + unsigned int key_length, + unsigned int flags) { struct sc_algorithm_info *info; unsigned int count; @@ -1130,15 +1134,41 @@ check_key_compatibility(struct sc_pkcs15_card *p15card, return 1; } - if (bad_usage) { + return bad_usage? -1 : 0; +} + +static int +check_key_compatibility(struct sc_pkcs15_card *p15card, + struct sc_pkcs15_prkey *key, + unsigned int x509_usage, + unsigned int key_length, + unsigned int flags) +{ + int res; + + res = __check_key_compatibility(p15card, key, + x509_usage, key_length, flags); + if (res < 0) { p15init_error("This device requires that keys have a " "specific key usage.\n" "Keys can be used for either signature or decryption, " "but not both.\n" "Please specify a key usage.\n"); - return 0; + res = 0; } - return 0; + return res; +} + +int +sc_pkcs15init_requires_restrictive_usage(struct sc_pkcs15_card *p15card, + struct sc_pkcs15init_prkeyargs *keyargs) +{ + int res; + + res = __check_key_compatibility(p15card, &keyargs->key, + keyargs->x509_usage, + prkey_bits(&keyargs->key), 0); + return res < 0; } /* diff --git a/src/scam/p15_eid.c b/src/scam/p15_eid.c index ce249587..b45bb0f0 100644 --- a/src/scam/p15_eid.c +++ b/src/scam/p15_eid.c @@ -163,9 +163,12 @@ int p15_eid_init(scam_context * sctx, int argc, const char **argv) /* FIXME: Add support for selecting certificate by ID */ data->cinfo = (struct sc_pkcs15_cert_info *) data->objs[0]->data; - r = sc_pkcs15_find_prkey_by_id(data->p15card, &data->cinfo->id, &data->prkey); + r = sc_pkcs15_find_prkey_by_id_usage(data->p15card, + &data->cinfo->id, + SC_PKCS15_PRKEY_USAGE_SIGN, + &data->prkey); if (r != SC_SUCCESS) { - scam_print_msg(sctx, "sc_pkcs15_find_prkey_by_id: %s\n", sc_strerror(r)); + scam_print_msg(sctx, "sc_pkcs15_find_prkey_by_id_usage: %s\n", sc_strerror(r)); return SCAM_FAILED; } r = sc_pkcs15_find_pin_by_auth_id(data->p15card, &data->prkey->auth_id, &data->pin); diff --git a/src/scam/p15_ldap.c b/src/scam/p15_ldap.c index 00c98718..e6290e21 100644 --- a/src/scam/p15_ldap.c +++ b/src/scam/p15_ldap.c @@ -136,9 +136,12 @@ int p15_ldap_init(scam_context * sctx, int argc, const char **argv) /* FIXME: Add support for selecting certificate by ID */ data->cinfo = (struct sc_pkcs15_cert_info *) data->objs[0]->data; - r = sc_pkcs15_find_prkey_by_id(data->p15card, &data->cinfo->id, &data->prkey); + r = sc_pkcs15_find_prkey_by_id_usage(data->p15card, + &data->cinfo->id, + SC_PKCS15_PRKEY_USAGE_SIGN, + &data->prkey); if (r != SC_SUCCESS) { - scam_print_msg(sctx, "sc_pkcs15_find_prkey_by_id: %s\n", sc_strerror(r)); + scam_print_msg(sctx, "sc_pkcs15_find_prkey_by_id_usage: %s\n", sc_strerror(r)); return SCAM_FAILED; } r = sc_pkcs15_find_pin_by_auth_id(data->p15card, &data->prkey->auth_id, &data->pin); diff --git a/src/signer/opensc-crypto.c b/src/signer/opensc-crypto.c index aad2f34c..bd6b50e1 100644 --- a/src/signer/opensc-crypto.c +++ b/src/signer/opensc-crypto.c @@ -65,7 +65,10 @@ static int sc_private_decrypt(int flen, const unsigned char *from, unsigned char goto err; } } - r = sc_pkcs15_find_prkey_by_id(priv->p15card, &priv->cert_id, &key); + r = sc_pkcs15_find_prkey_by_id_usage(priv->p15card, + &priv->cert_id, + SC_PKCS15_PRKEY_USAGE_DECRYPT + &key); if (r) { #if 0 error("Unable to find private key from SmartCard: %s", sc_strerror(r)); @@ -131,7 +134,10 @@ sc_sign(int type, const unsigned char *m, unsigned int m_len, goto err; } } - r = sc_pkcs15_find_prkey_by_id(priv->p15card, &priv->cert_id, &key); + r = sc_pkcs15_find_prkey_by_id_usage(priv->p15card, + &priv->cert_id, + SC_PKCS15_PRKEY_USAGE_SIGN, + &key); if (r) { DBG(printf("Unable to find private key from SmartCard: %s", sc_strerror(r))); goto err; diff --git a/src/tools/pkcs15-crypt.c b/src/tools/pkcs15-crypt.c index 15bce267..ae1c6708 100644 --- a/src/tools/pkcs15-crypt.c +++ b/src/tools/pkcs15-crypt.c @@ -392,15 +392,75 @@ int decipher(struct sc_pkcs15_object *obj) return 0; } +static int get_key(unsigned int usage, sc_pkcs15_object_t **result) +{ + sc_pkcs15_object_t *key, *pin; + const char *usage_name; + sc_pkcs15_id_t id; + int r; + + usage_name = (usage & SC_PKCS15_PRKEY_USAGE_SIGN)? "signature" : "decryption"; + + if (opt_key_id != NULL) { + sc_pkcs15_hex_string_to_id(opt_key_id, &id); + r = sc_pkcs15_find_prkey_by_id_usage(p15card, &id, usage, &key); + if (r < 0) { + fprintf(stderr, "Unable to find private %s key '%s': %s\n", + usage_name, opt_key_id, sc_strerror(r)); + return 2; + } + } else { + r = sc_pkcs15_find_prkey_by_id_usage(p15card, NULL, usage, &key); + if (r < 0) { + fprintf(stderr, "Unable to find any private %s key: %s\n", + usage_name, sc_strerror(r)); + return 2; + } + } + + *result = key; + + if (key->auth_id.len) { + static sc_pkcs15_object_t *prev_pin = NULL; + char *pincode; + + r = sc_pkcs15_find_pin_by_auth_id(p15card, &key->auth_id, &pin); + if (r) { + fprintf(stderr, "Unable to find PIN code for private key: %s\n", + sc_strerror(r)); + return 1; + } + + /* Pin already verified previously */ + if (pin == prev_pin) + return 0; + + pincode = get_pin(pin); + if (pincode == NULL || *pincode == '\0') + return 5; + + r = sc_pkcs15_verify_pin(p15card, (struct sc_pkcs15_pin_info *) pin->data, + (const u8 *) pincode, strlen(pincode)); + if (r) { + fprintf(stderr, "PIN code verification failed: %s\n", sc_strerror(r)); + return 5; + } + free(pincode); + if (!quiet) + fprintf(stderr, "PIN code correct.\n"); + prev_pin = pin; + } + + return 0; +} + int main(int argc, char * const argv[]) { int err = 0, r, c, long_optind = 0; int do_decipher = 0; int do_sign = 0; int action_count = 0; - struct sc_pkcs15_object *key, *pin, *objs[32]; - struct sc_pkcs15_id id; - char *pincode; + struct sc_pkcs15_object *key; while (1) { c = getopt_long(argc, argv, "sck:r:i:o:qp:dw", options, &long_optind); @@ -487,55 +547,15 @@ int main(int argc, char * const argv[]) if (!quiet) fprintf(stderr, "Found %s!\n", p15card->label); - r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, objs, 32); - if (r <= 0) { - if (r == 0) - r = SC_ERROR_OBJECT_NOT_FOUND; - fprintf(stderr, "Private key enumeration failed: %s\n", sc_strerror(r)); - err = 1; - goto end; - } - if (opt_key_id != NULL) { - sc_pkcs15_hex_string_to_id(opt_key_id, &id); - r = sc_pkcs15_find_prkey_by_id(p15card, &id, &key); - if (r < 0) { - fprintf(stderr, "Unable to find private key '%s': %s\n", - opt_key_id, sc_strerror(r)); - err = 2; - goto end; - } - } else - key = objs[0]; - if (key->auth_id.len) { - r = sc_pkcs15_find_pin_by_auth_id(p15card, &key->auth_id, &pin); - if (r) { - fprintf(stderr, "Unable to find PIN code for private key: %s\n", - sc_strerror(r)); - err = 1; - goto end; - } - pincode = get_pin(pin); - if (pincode == NULL) { - err = 5; - goto end; - } - r = sc_pkcs15_verify_pin(p15card, (struct sc_pkcs15_pin_info *) pin->data, (const u8 *) pincode, strlen(pincode)); - if (r) { - fprintf(stderr, "PIN code verification failed: %s\n", sc_strerror(r)); - err = 5; - goto end; - } - free(pincode); - if (!quiet) - fprintf(stderr, "PIN code correct.\n"); - } if (do_decipher) { - if ((err = decipher(key))) + if ((err = get_key(SC_PKCS15_PRKEY_USAGE_DECRYPT, &key)) < 0 + || (err = decipher(key))) goto end; action_count--; } if (do_sign) { - if ((err = sign(key))) + if ((err = get_key(SC_PKCS15_PRKEY_USAGE_SIGN, &key)) < 0 + || (err = sign(key))) goto end; action_count--; } diff --git a/src/tools/pkcs15-init.c b/src/tools/pkcs15-init.c index fc31c956..64f43dbd 100644 --- a/src/tools/pkcs15-init.c +++ b/src/tools/pkcs15-init.c @@ -104,6 +104,7 @@ enum { OPT_UNPROTECTED, OPT_AUTHORITY, OPT_SOFT_KEYGEN, + OPT_SPLIT_KEY, OPT_PIN1 = 0x10000, /* don't touch these values */ OPT_PUK1 = 0x10001, @@ -136,6 +137,7 @@ const struct option options[] = { { "passphrase", required_argument, 0, OPT_PASSPHRASE }, { "authority", no_argument, 0, OPT_AUTHORITY }, { "key-usage", required_argument, 0, 'u' }, + { "split-key", no_argument, 0, OPT_SPLIT_KEY }, { "extractable", no_argument, 0, OPT_EXTRACTABLE }, { "insecure", no_argument, 0, OPT_UNPROTECTED }, @@ -173,6 +175,7 @@ const char * option_help[] = { "Specify passphrase for unlocking secret key", "Mark certificate as a CA certificate", "Specify X.509 key usage (use \"--key-usage help\" for more information)", + "Automatically create two keys with same ID and different usage (sign vs decipher)", "Private key stored as an extractable key", "Insecure mode: do not require PIN/passphrase for private key", @@ -224,6 +227,7 @@ static int opt_reader = -1, opt_softkeygen = 0, opt_noprompts = 0, opt_use_defkeys = 0, + opt_split_key = 0, opt_wait = 0; static char * opt_profile = "pkcs15"; static char * opt_infile = 0; @@ -495,6 +499,12 @@ do_store_private_key(struct sc_profile *profile) if ((r = do_convert_private_key(&args.key, pkey)) < 0) return r; if (ncerts) { + unsigned int usage = cert[0]->ex_kusage; + + /* No certificate usage? Assume ordinary + * user cert */ + usage = 0x1F; + /* If the user requested a specific key usage on the * command line check if it includes _more_ * usage bits than the one specified by the cert, @@ -502,20 +512,42 @@ do_store_private_key(struct sc_profile *profile) * If the usage specified on the command line * is more restrictive, use that. */ - if (~cert[0]->ex_kusage & opt_x509_usage) { + if (~usage & opt_x509_usage) { fprintf(stderr, "Warning: requested key usage incompatible with " "key usage specified by X.509 certificate\n"); } - if (opt_x509_usage) { - args.x509_usage = opt_x509_usage; - } else { - args.x509_usage = cert[0]->ex_kusage; - } + args.x509_usage = opt_x509_usage? opt_x509_usage : usage; + } + + if (sc_pkcs15init_requires_restrictive_usage(p15card, &args)) { + unsigned int usage = args.x509_usage; + + if (!opt_split_key) { + fprintf(stderr, "\n" + "Error - this token requires a more restrictive key usage.\n" + "Keys stored on this token can be used either for signing or decipherment,\n" + "but not both. You can either specify a more restrictive usage through\n" + "the --key-usage command line argument, or allow me to transparently\n" + "create two key objects with separate usage by specifying --split-key\n"); + exit(1); + } + + /* keyEncipherment|dataEncipherment|keyAgreement */ + args.x509_usage = usage & 0x1C; + r = sc_pkcs15init_store_private_key(p15card, profile, &args, NULL); + if (r >= 0) { + /* digitalSignature|nonRepudiation|certSign|cRLSign */ + args.x509_usage = usage & 0x63; + /* Prevent pkcs15init from choking on duplicate ID */ + args.flags |= SC_PKCS15INIT_SPLIT_KEY; + r = sc_pkcs15init_store_private_key(p15card, profile, &args, NULL); + } + } else { + r = sc_pkcs15init_store_private_key(p15card, profile, &args, NULL); } - r = sc_pkcs15init_store_private_key(p15card, profile, &args, NULL); if (r < 0) return r; @@ -716,7 +748,7 @@ init_keyargs(struct sc_pkcs15init_prkeyargs *args) return SC_ERROR_INVALID_ARGUMENTS; } if (opt_extractable) { - args->extractable |= SC_PKCS15INIT_EXTRACTABLE; + args->flags |= SC_PKCS15INIT_EXTRACTABLE; if (opt_passphrase) { args->passphrase = opt_passphrase; } else { @@ -727,7 +759,7 @@ init_keyargs(struct sc_pkcs15init_prkeyargs *args) "--passphrase"); return SC_ERROR_PASSPHRASE_REQUIRED; } - args->extractable |= SC_PKCS15INIT_NO_PASSPHRASE; + args->flags |= SC_PKCS15INIT_NO_PASSPHRASE; } } args->label = opt_label; @@ -1516,6 +1548,9 @@ handle_option(int c) case 'T': opt_use_defkeys = 1; break; + case OPT_SPLIT_KEY: + opt_split_key = 1; + break; default: print_usage_and_die(); }