/* * GnuTLS PKCS#11 support * Copyright (C) 2010-2012 Free Software Foundation, Inc. * * Authors: Nikos Mavrogiannopoulos, Stef Walter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see */ #include #include #include #include #include #include #include static const ck_bool_t tval = 1; static const ck_bool_t fval = 0; /** * gnutls_pkcs11_copy_x509_crt: * @token_url: A PKCS #11 URL specifying a token * @crt: A certificate * @label: A name to be used for the stored data * @flags: One of GNUTLS_PKCS11_OBJ_FLAG_* * * This function will copy a certificate into a PKCS #11 token specified by * a URL. The certificate can be marked as trusted or not. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 2.12.0 **/ int gnutls_pkcs11_copy_x509_crt (const char *token_url, gnutls_x509_crt_t crt, const char *label, unsigned int flags) { int ret; struct p11_kit_uri *info = NULL; ck_rv_t rv; size_t der_size, id_size; uint8_t *der = NULL; uint8_t id[20]; struct ck_attribute a[16]; ck_object_class_t class = CKO_CERTIFICATE; ck_certificate_type_t type = CKC_X_509; ck_object_handle_t obj; int a_val; gnutls_datum_t subject = { NULL, 0 }; struct pkcs11_session_info sinfo; memset(&sinfo, 0, sizeof(sinfo)); ret = pkcs11_url_to_info (token_url, &info); if (ret < 0) { gnutls_assert (); return ret; } ret = pkcs11_open_session (&sinfo, NULL, info, SESSION_WRITE | pkcs11_obj_flags_to_int (flags)); p11_kit_uri_free (info); if (ret < 0) { gnutls_assert (); return ret; } ret = gnutls_x509_crt_export (crt, GNUTLS_X509_FMT_DER, NULL, &der_size); if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER) { gnutls_assert (); goto cleanup; } der = gnutls_malloc (der_size); if (der == NULL) { gnutls_assert (); ret = GNUTLS_E_MEMORY_ERROR; goto cleanup; } ret = gnutls_x509_crt_export (crt, GNUTLS_X509_FMT_DER, der, &der_size); if (ret < 0) { gnutls_assert (); goto cleanup; } id_size = sizeof (id); ret = gnutls_x509_crt_get_key_id (crt, 0, id, &id_size); if (ret < 0) { gnutls_assert (); goto cleanup; } ret = gnutls_x509_crt_get_raw_dn (crt, &subject); if (ret < 0) { gnutls_assert (); goto cleanup; } /* FIXME: copy key usage flags */ a[0].type = CKA_CLASS; a[0].value = &class; a[0].value_len = sizeof (class); a[1].type = CKA_ID; a[1].value = id; a[1].value_len = id_size; a[2].type = CKA_VALUE; a[2].value = der; a[2].value_len = der_size; a[3].type = CKA_TOKEN; a[3].value = (void*)&tval; a[3].value_len = sizeof (tval); a[4].type = CKA_CERTIFICATE_TYPE; a[4].value = &type; a[4].value_len = sizeof (type); a_val = 5; a[a_val].type = CKA_SUBJECT; a[a_val].value = subject.data; a[a_val].value_len = subject.size; a_val++; if (label) { a[a_val].type = CKA_LABEL; a[a_val].value = (void *) label; a[a_val].value_len = strlen (label); a_val++; } if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED) { a[a_val].type = CKA_TRUSTED; a[a_val].value = (void*)&tval; a[a_val].value_len = sizeof (tval); a_val++; a[a_val].type = CKA_PRIVATE; a[a_val].value = (void*)&fval; a[a_val].value_len = sizeof(fval); a_val++; } else { if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE) { a[a_val].type = CKA_PRIVATE; a[a_val].value = (void*)&tval; a[a_val].value_len = sizeof(tval); a_val++; } else if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_NOT_PRIVATE) { a[a_val].type = CKA_PRIVATE; a[a_val].value = (void*)&fval; a[a_val].value_len = sizeof(fval); a_val++; } } rv = pkcs11_create_object (sinfo.module, sinfo.pks, a, a_val, &obj); if (rv != CKR_OK) { gnutls_assert (); _gnutls_debug_log ("pkcs11: %s\n", pkcs11_strerror (rv)); ret = pkcs11_rv_to_err (rv); goto cleanup; } /* generated! */ ret = 0; cleanup: gnutls_free (der); pkcs11_close_session (&sinfo); _gnutls_free_datum(&subject); return ret; } /** * gnutls_pkcs11_copy_x509_privkey: * @token_url: A PKCS #11 URL specifying a token * @key: A private key * @label: A name to be used for the stored data * @key_usage: One of GNUTLS_KEY_* * @flags: One of GNUTLS_PKCS11_OBJ_* flags * * This function will copy a private key into a PKCS #11 token specified by * a URL. It is highly recommended flags to contain %GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE * unless there is a strong reason not to. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 2.12.0 **/ int gnutls_pkcs11_copy_x509_privkey (const char *token_url, gnutls_x509_privkey_t key, const char *label, unsigned int key_usage, unsigned int flags) { int ret; struct p11_kit_uri *info = NULL; ck_rv_t rv; size_t id_size; uint8_t id[20]; struct ck_attribute a[16]; ck_object_class_t class = CKO_PRIVATE_KEY; ck_object_handle_t obj; ck_key_type_t type; int a_val; gnutls_pk_algorithm_t pk; gnutls_datum_t p, q, g, y, x; gnutls_datum_t m, e, d, u, exp1, exp2; struct pkcs11_session_info sinfo; memset(&sinfo, 0, sizeof(sinfo)); memset(&p, 0, sizeof(p)); memset(&q, 0, sizeof(q)); memset(&g, 0, sizeof(g)); memset(&y, 0, sizeof(y)); memset(&x, 0, sizeof(x)); memset(&m, 0, sizeof(m)); memset(&e, 0, sizeof(e)); memset(&d, 0, sizeof(d)); memset(&u, 0, sizeof(u)); memset(&exp1, 0, sizeof(exp1)); memset(&exp2, 0, sizeof(exp2)); ret = pkcs11_url_to_info (token_url, &info); if (ret < 0) { gnutls_assert (); return ret; } id_size = sizeof (id); ret = gnutls_x509_privkey_get_key_id (key, 0, id, &id_size); if (ret < 0) { p11_kit_uri_free (info); gnutls_assert (); return ret; } ret = pkcs11_open_session (&sinfo, NULL, info, SESSION_WRITE | pkcs11_obj_flags_to_int (flags)); p11_kit_uri_free (info); if (ret < 0) { gnutls_assert (); return ret; } /* FIXME: copy key usage flags */ a_val = 0; a[a_val].type = CKA_CLASS; a[a_val].value = &class; a[a_val].value_len = sizeof (class); a_val++; a[a_val].type = CKA_ID; a[a_val].value = id; a[a_val].value_len = id_size; a_val++; a[a_val].type = CKA_KEY_TYPE; a[a_val].value = &type; a[a_val].value_len = sizeof (type); a_val++; a[a_val].type = CKA_TOKEN; a[a_val].value = (void*)&tval; a[a_val].value_len = sizeof (tval); a_val++; /* a private key is set always as private unless * requested otherwise */ if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_NOT_PRIVATE) { a[a_val].type = CKA_PRIVATE; a[a_val].value = (void*)&fval; a[a_val].value_len = sizeof(fval); a_val++; } else { a[a_val].type = CKA_PRIVATE; a[a_val].value = (void*)&tval; a[a_val].value_len = sizeof (tval); a_val++; } if (label) { a[a_val].type = CKA_LABEL; a[a_val].value = (void *) label; a[a_val].value_len = strlen (label); a_val++; } if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE) { a[a_val].type = CKA_SENSITIVE; a[a_val].value = (void*)&tval; a[a_val].value_len = sizeof (tval); a_val++; } else { a[a_val].type = CKA_SENSITIVE; a[a_val].value = (void*)&fval; a[a_val].value_len = sizeof (fval); a_val++; } pk = gnutls_x509_privkey_get_pk_algorithm (key); switch (pk) { case GNUTLS_PK_RSA: { ret = gnutls_x509_privkey_export_rsa_raw2 (key, &m, &e, &d, &p, &q, &u, &exp1, &exp2); if (ret < 0) { gnutls_assert (); goto cleanup; } type = CKK_RSA; a[a_val].type = CKA_MODULUS; a[a_val].value = m.data; a[a_val].value_len = m.size; a_val++; a[a_val].type = CKA_PUBLIC_EXPONENT; a[a_val].value = e.data; a[a_val].value_len = e.size; a_val++; a[a_val].type = CKA_PRIVATE_EXPONENT; a[a_val].value = d.data; a[a_val].value_len = d.size; a_val++; a[a_val].type = CKA_PRIME_1; a[a_val].value = p.data; a[a_val].value_len = p.size; a_val++; a[a_val].type = CKA_PRIME_2; a[a_val].value = q.data; a[a_val].value_len = q.size; a_val++; a[a_val].type = CKA_COEFFICIENT; a[a_val].value = u.data; a[a_val].value_len = u.size; a_val++; a[a_val].type = CKA_EXPONENT_1; a[a_val].value = exp1.data; a[a_val].value_len = exp1.size; a_val++; a[a_val].type = CKA_EXPONENT_2; a[a_val].value = exp2.data; a[a_val].value_len = exp2.size; a_val++; break; } case GNUTLS_PK_DSA: { ret = gnutls_x509_privkey_export_dsa_raw (key, &p, &q, &g, &y, &x); if (ret < 0) { gnutls_assert (); goto cleanup; } type = CKK_DSA; a[a_val].type = CKA_PRIME; a[a_val].value = p.data; a[a_val].value_len = p.size; a_val++; a[a_val].type = CKA_SUBPRIME; a[a_val].value = q.data; a[a_val].value_len = q.size; a_val++; a[a_val].type = CKA_BASE; a[a_val].value = g.data; a[a_val].value_len = g.size; a_val++; a[a_val].type = CKA_VALUE; a[a_val].value = x.data; a[a_val].value_len = x.size; a_val++; break; } case GNUTLS_PK_EC: { ret = _gnutls_x509_write_ecc_params(&key->params, &p); if (ret < 0) { gnutls_assert (); goto cleanup; } ret = _gnutls_mpi_dprint_lz(&key->params.params[ECC_K], &x); if (ret < 0) { gnutls_assert (); goto cleanup; } type = CKK_ECDSA; a[a_val].type = CKA_EC_PARAMS; a[a_val].value = p.data; a[a_val].value_len = p.size; a_val++; a[a_val].type = CKA_VALUE; a[a_val].value = x.data; a[a_val].value_len = x.size; a_val++; break; } default: gnutls_assert (); ret = GNUTLS_E_INVALID_REQUEST; goto cleanup; } rv = pkcs11_create_object (sinfo.module, sinfo.pks, a, a_val, &obj); if (rv != CKR_OK) { gnutls_assert (); _gnutls_debug_log ("pkcs11: %s\n", pkcs11_strerror (rv)); ret = pkcs11_rv_to_err (rv); goto cleanup; } ret = 0; cleanup: switch (pk) { case GNUTLS_PK_RSA: { gnutls_free (m.data); gnutls_free (e.data); gnutls_free (d.data); gnutls_free (p.data); gnutls_free (q.data); gnutls_free (u.data); gnutls_free (exp1.data); gnutls_free (exp2.data); break; } case GNUTLS_PK_DSA: { gnutls_free (p.data); gnutls_free (q.data); gnutls_free (g.data); gnutls_free (y.data); gnutls_free (x.data); break; } case GNUTLS_PK_EC: { gnutls_free (p.data); gnutls_free (x.data); break; } default: gnutls_assert (); ret = GNUTLS_E_INVALID_REQUEST; break; } if (sinfo.pks != 0) pkcs11_close_session (&sinfo); return ret; } struct delete_data_st { struct p11_kit_uri *info; unsigned int deleted; /* how many */ }; static int delete_obj_url (struct pkcs11_session_info * sinfo, struct token_info *info, struct ck_info *lib_info, void *input) { struct delete_data_st *find_data = input; struct ck_attribute a[4]; struct ck_attribute *attr; ck_object_class_t class; ck_certificate_type_t type = (ck_certificate_type_t)-1; ck_rv_t rv; ck_object_handle_t obj; unsigned long count, a_vals; int found = 0, ret; if (info == NULL) { /* we don't support multiple calls */ gnutls_assert (); return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; } /* do not bother reading the token if basic fields do not match */ if (!p11_kit_uri_match_module_info (find_data->info, lib_info) || !p11_kit_uri_match_token_info (find_data->info, &info->tinfo)) { gnutls_assert (); return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; } /* Find objects with given class and type */ class = CKO_CERTIFICATE; /* default */ a_vals = 0; attr = p11_kit_uri_get_attribute (find_data->info, CKA_CLASS); if (attr != NULL) { if(attr->value && attr->value_len == sizeof (ck_object_class_t)) class = *((ck_object_class_t*)attr->value); if (class == CKO_CERTIFICATE) type = CKC_X_509; } a[a_vals].type = CKA_CLASS; a[a_vals].value = &class; a[a_vals].value_len = sizeof (class); a_vals++; attr = p11_kit_uri_get_attribute (find_data->info, CKA_ID); if (attr != NULL) { memcpy (a + a_vals, attr, sizeof (struct ck_attribute)); a_vals++; } if (type != (ck_certificate_type_t)-1) { a[a_vals].type = CKA_CERTIFICATE_TYPE; a[a_vals].value = &type; a[a_vals].value_len = sizeof type; a_vals++; } attr = p11_kit_uri_get_attribute (find_data->info, CKA_LABEL); if (attr != NULL) { memcpy (a + a_vals, attr, sizeof (struct ck_attribute)); a_vals++; } rv = pkcs11_find_objects_init (sinfo->module, sinfo->pks, a, a_vals); if (rv != CKR_OK) { gnutls_assert (); _gnutls_debug_log ("pk11: FindObjectsInit failed.\n"); ret = pkcs11_rv_to_err (rv); goto cleanup; } while (pkcs11_find_objects (sinfo->module, sinfo->pks, &obj, 1, &count) == CKR_OK && count == 1) { rv = pkcs11_destroy_object (sinfo->module, sinfo->pks, obj); if (rv != CKR_OK) { _gnutls_debug_log ("pkcs11: Cannot destroy object: %s\n", pkcs11_strerror (rv)); } else { find_data->deleted++; } found = 1; } if (found == 0) { gnutls_assert (); ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; } else { ret = 0; } cleanup: pkcs11_find_objects_final (sinfo); return ret; } /** * gnutls_pkcs11_delete_url: * @object_url: The URL of the object to delete. * @flags: One of GNUTLS_PKCS11_OBJ_* flags * * This function will delete objects matching the given URL. * Note that not all tokens support the delete operation. * * Returns: On success, the number of objects deleted is returned, otherwise a * negative error value. * * Since: 2.12.0 **/ int gnutls_pkcs11_delete_url (const char *object_url, unsigned int flags) { int ret; struct delete_data_st find_data; memset (&find_data, 0, sizeof (find_data)); ret = pkcs11_url_to_info (object_url, &find_data.info); if (ret < 0) { gnutls_assert (); return ret; } ret = _pkcs11_traverse_tokens (delete_obj_url, &find_data, find_data.info, NULL, SESSION_WRITE | pkcs11_obj_flags_to_int (flags)); p11_kit_uri_free (find_data.info); if (ret < 0) { gnutls_assert (); return ret; } return find_data.deleted; } /** * gnutls_pkcs11_token_init: * @token_url: A PKCS #11 URL specifying a token * @so_pin: Security Officer's PIN * @label: A name to be used for the token * * This function will initialize (format) a token. If the token is * at a factory defaults state the security officer's PIN given will be * set to be the default. Otherwise it should match the officer's PIN. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_pkcs11_token_init (const char *token_url, const char *so_pin, const char *label) { int ret; struct p11_kit_uri *info = NULL; ck_rv_t rv; struct ck_function_list *module; ck_slot_id_t slot; char flabel[32]; ret = pkcs11_url_to_info (token_url, &info); if (ret < 0) { gnutls_assert (); return ret; } ret = pkcs11_find_slot (&module, &slot, info, NULL); p11_kit_uri_free (info); if (ret < 0) { gnutls_assert (); return ret; } /* so it seems memset has other uses than zeroing! */ memset (flabel, ' ', sizeof (flabel)); if (label != NULL) memcpy (flabel, label, strlen (label)); rv = pkcs11_init_token (module, slot, (uint8_t*)so_pin, strlen (so_pin), (uint8_t*)flabel); if (rv != CKR_OK) { gnutls_assert (); _gnutls_debug_log ("pkcs11: %s\n", pkcs11_strerror (rv)); return pkcs11_rv_to_err (rv); } return 0; } /** * gnutls_pkcs11_token_set_pin: * @token_url: A PKCS #11 URL specifying a token * @oldpin: old user's PIN * @newpin: new user's PIN * @flags: one of #gnutls_pin_flag_t. * * This function will modify or set a user's PIN for the given token. * If it is called to set a user pin for first time the oldpin must * be NULL. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_pkcs11_token_set_pin (const char *token_url, const char *oldpin, const char *newpin, unsigned int flags) { int ret; struct p11_kit_uri *info = NULL; ck_rv_t rv; unsigned int ses_flags; struct pkcs11_session_info sinfo; memset(&sinfo, 0, sizeof(sinfo)); ret = pkcs11_url_to_info (token_url, &info); if (ret < 0) { gnutls_assert (); return ret; } if (((flags & GNUTLS_PIN_USER) && oldpin == NULL) || (flags & GNUTLS_PIN_SO)) ses_flags = SESSION_WRITE | SESSION_LOGIN | SESSION_SO; else ses_flags = SESSION_WRITE | SESSION_LOGIN; ret = pkcs11_open_session (&sinfo, NULL, info, ses_flags); p11_kit_uri_free (info); if (ret < 0) { gnutls_assert (); return ret; } if (oldpin == NULL) { rv = pkcs11_init_pin (sinfo.module, sinfo.pks, (uint8_t *) newpin, strlen (newpin)); if (rv != CKR_OK) { gnutls_assert (); _gnutls_debug_log ("pkcs11: %s\n", pkcs11_strerror (rv)); ret = pkcs11_rv_to_err (rv); goto finish; } } else { rv = pkcs11_set_pin (sinfo.module, sinfo.pks, oldpin, strlen (oldpin), newpin, strlen (newpin)); if (rv != CKR_OK) { gnutls_assert (); _gnutls_debug_log ("pkcs11: %s\n", pkcs11_strerror (rv)); ret = pkcs11_rv_to_err (rv); goto finish; } } ret = 0; finish: pkcs11_close_session (&sinfo); return ret; }