diff options
Diffstat (limited to 'lib/pkcs11_write.c')
-rw-r--r-- | lib/pkcs11_write.c | 1468 |
1 files changed, 1468 insertions, 0 deletions
diff --git a/lib/pkcs11_write.c b/lib/pkcs11_write.c new file mode 100644 index 0000000..3ce794b --- /dev/null +++ b/lib/pkcs11_write.c @@ -0,0 +1,1468 @@ +/* + * 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 2.1 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 <https://www.gnu.org/licenses/> + */ + +#include "gnutls_int.h" +#include <gnutls/pkcs11.h> +#include <stdio.h> +#include <string.h> +#include "errors.h" +#include <datum.h> +#include <pkcs11_int.h> +#include "pkcs11x.h" +#include <x509/common.h> +#include "pk.h" + +static const ck_bool_t tval = 1; +static const ck_bool_t fval = 0; + +#define MAX_ASIZE 24 + +static void mark_flags(unsigned flags, struct ck_attribute *a, unsigned *a_val, unsigned trusted) +{ + static const unsigned long category = 2; + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_CA) { + a[*a_val].type = CKA_CERTIFICATE_CATEGORY; + a[*a_val].value = (void *) &category; + a[*a_val].value_len = sizeof(category); + (*a_val)++; + } + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_DISTRUSTED) { + if (trusted) { + a[*a_val].type = CKA_X_DISTRUSTED; + a[*a_val].value = (void *) &tval; + a[*a_val].value_len = sizeof(tval); + (*a_val)++; + } else { + _gnutls_debug_log("p11: ignoring the distrusted flag as it is not valid on non-p11-kit-trust modules\n"); + } + } + + 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)++; + } + } +} + +/** + * gnutls_pkcs11_copy_x509_crt2: + * @token_url: A PKCS #11 URL specifying a token + * @crt: The certificate to copy + * @label: The name to be used for the stored data + * @cid: The CKA_ID to set for the object -if NULL, the ID will be derived from the public key + * @flags: One of GNUTLS_PKCS11_OBJ_FLAG_* + * + * This function will copy a certificate into a PKCS #11 token specified by + * a URL. Valid flags to mark the certificate: %GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED, + * %GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE, %GNUTLS_PKCS11_OBJ_FLAG_MARK_CA, + * %GNUTLS_PKCS11_OBJ_FLAG_MARK_ALWAYS_AUTH. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.0 + **/ +int +gnutls_pkcs11_copy_x509_crt2(const char *token_url, + gnutls_x509_crt_t crt, const char *label, + const gnutls_datum_t *cid, + unsigned int flags) +{ + int ret; + struct p11_kit_uri *info = NULL; + ck_rv_t rv; + size_t der_size, id_size, serial_size; + gnutls_datum_t serial_der = {NULL, 0}; + uint8_t *der = NULL; + uint8_t serial[128]; + uint8_t id[20]; + struct ck_attribute a[MAX_ASIZE]; + ck_object_class_t class = CKO_CERTIFICATE; + ck_certificate_type_t type = CKC_X_509; + ck_object_handle_t ctx; + unsigned a_val; + struct pkcs11_session_info sinfo; + + PKCS11_CHECK_INIT; + + ret = pkcs11_url_to_info(token_url, &info, 0); + 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; + } + + der_size = 0; + 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; + } + + a[0].type = CKA_CLASS; + a[0].value = &class; + a[0].value_len = sizeof(class); + + a[1].type = CKA_ID; + if (cid == NULL || cid->size == 0) { + id_size = sizeof(id); + ret = gnutls_x509_crt_get_subject_key_id(crt, id, &id_size, NULL); + if (ret < 0) { + id_size = sizeof(id); + ret = gnutls_x509_crt_get_key_id(crt, 0, id, &id_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + a[1].value = id; + a[1].value_len = id_size; + } else { + a[1].value = cid->data; + a[1].value_len = cid->size; + } + + /* we do not use the key usage flags; these are apparent from + * the certificate itself. */ + 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 = crt->raw_dn.data; + a[a_val].value_len = crt->raw_dn.size; + a_val++; + + if (crt->raw_issuer_dn.size > 0) { + a[a_val].type = CKA_ISSUER; + a[a_val].value = crt->raw_issuer_dn.data; + a[a_val].value_len = crt->raw_issuer_dn.size; + a_val++; + } + + serial_size = sizeof(serial); + if (gnutls_x509_crt_get_serial(crt, serial, &serial_size) >= 0) { + ret = _gnutls_x509_ext_gen_number(serial, serial_size, &serial_der); + if (ret >= 0) { + a[a_val].type = CKA_SERIAL_NUMBER; + a[a_val].value = (void *) serial_der.data; + a[a_val].value_len = serial_der.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++; + } + + mark_flags(flags, a, &a_val, sinfo.trusted); + + rv = pkcs11_create_object(sinfo.module, sinfo.pks, a, a_val, &ctx); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv)); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + /* generated! + */ + + ret = 0; + + cleanup: + gnutls_free(der); + gnutls_free(serial_der.data); + pkcs11_close_session(&sinfo); + return ret; + +} + +static void clean_pubkey(struct ck_attribute *a, unsigned a_val) +{ + unsigned i; + + for (i=0;i<a_val;i++) { + switch(a[i].type) { + case CKA_MODULUS: + case CKA_PUBLIC_EXPONENT: + case CKA_PRIME: + case CKA_SUBPRIME: + case CKA_VALUE: + case CKA_BASE: + case CKA_EC_PARAMS: + case CKA_EC_POINT: + gnutls_free(a[i].value); + break; + } + } +} + +static int add_pubkey(gnutls_pubkey_t pubkey, struct ck_attribute *a, unsigned *a_val) +{ + gnutls_pk_algorithm_t pk; + int ret; + + pk = gnutls_pubkey_get_pk_algorithm(pubkey, NULL); + + switch (pk) { + case GNUTLS_PK_RSA_PSS: + case GNUTLS_PK_RSA: { + gnutls_datum_t m, e; + + /* PKCS#11 defines integers as unsigned having most significant byte + * first, e.g., 32768 = 0x80 0x00. This is interpreted literraly by + * some HSMs which do not accept an integer with a leading zero */ + ret = gnutls_pubkey_export_rsa_raw2(pubkey, &m, &e, GNUTLS_EXPORT_FLAG_NO_LZ); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + + 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)++; + break; + } + case GNUTLS_PK_DSA: { + gnutls_datum_t p, q, g, y; + + ret = gnutls_pubkey_export_dsa_raw2(pubkey, &p, &q, &g, &y, GNUTLS_EXPORT_FLAG_NO_LZ); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + 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 = y.data; + a[*a_val].value_len = y.size; + (*a_val)++; + break; + } + case GNUTLS_PK_EC: { + gnutls_datum_t params, point; + + ret = gnutls_pubkey_export_ecc_x962(pubkey, ¶ms, &point); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + a[*a_val].type = CKA_EC_PARAMS; + a[*a_val].value = params.data; + a[*a_val].value_len = params.size; + (*a_val)++; + + a[*a_val].type = CKA_EC_POINT; + a[*a_val].value = point.data; + a[*a_val].value_len = point.size; + (*a_val)++; + break; + } + case GNUTLS_PK_EDDSA_ED25519: { + gnutls_datum_t params, ecpoint; + + ret = + _gnutls_x509_write_ecc_params(pubkey->params.curve, + ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + a[*a_val].type = CKA_EC_PARAMS; + a[*a_val].value = params.data; + a[*a_val].value_len = params.size; + (*a_val)++; + + ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING, + pubkey->params.raw_pub.data, + pubkey->params.raw_pub.size, + &ecpoint); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + a[*a_val].type = CKA_EC_POINT; + a[*a_val].value = ecpoint.data; + a[*a_val].value_len = ecpoint.size; + (*a_val)++; + break; + } + + default: + _gnutls_debug_log("requested writing public key of unsupported type %u\n", (unsigned)pk); + return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + } + + return 0; +} + +/** + * gnutls_pkcs11_copy_pubkey: + * @token_url: A PKCS #11 URL specifying a token + * @pubkey: The public key to copy + * @label: The name to be used for the stored data + * @cid: The CKA_ID to set for the object -if NULL, the ID will be derived from the public key + * @key_usage: One of GNUTLS_KEY_* + * @flags: One of GNUTLS_PKCS11_OBJ_FLAG_* + * + * This function will copy a public key object into a PKCS #11 token specified by + * a URL. Valid flags to mark the key: %GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED, + * %GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE, %GNUTLS_PKCS11_OBJ_FLAG_MARK_CA, + * %GNUTLS_PKCS11_OBJ_FLAG_MARK_ALWAYS_AUTH. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.6 + **/ +int +gnutls_pkcs11_copy_pubkey(const char *token_url, + gnutls_pubkey_t pubkey, const char *label, + const gnutls_datum_t *cid, + 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[MAX_ASIZE]; + gnutls_pk_algorithm_t pk; + ck_object_class_t class = CKO_PUBLIC_KEY; + ck_object_handle_t ctx; + unsigned a_val; + ck_key_type_t type; + struct pkcs11_session_info sinfo; + + PKCS11_CHECK_INIT; + + ret = pkcs11_url_to_info(token_url, &info, 0); + 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; + } + + a[0].type = CKA_CLASS; + a[0].value = &class; + a[0].value_len = sizeof(class); + + a[1].type = CKA_TOKEN; + a[1].value = (void *) &tval; + a[1].value_len = sizeof(tval); + + a_val = 2; + + ret = add_pubkey(pubkey, a, &a_val); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (label) { + a[a_val].type = CKA_LABEL; + a[a_val].value = (void *) label; + a[a_val].value_len = strlen(label); + a_val++; + } + + pk = gnutls_pubkey_get_pk_algorithm(pubkey, NULL); + type = pk_to_key_type(pk); + FIX_KEY_USAGE(pk, key_usage); + + 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_ID; + if (cid == NULL || cid->size == 0) { + id_size = sizeof(id); + ret = gnutls_pubkey_get_key_id(pubkey, 0, id, &id_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + a[a_val].value = id; + a[a_val].value_len = id_size; + } else { + a[a_val].value = cid->data; + a[a_val].value_len = cid->size; + } + a_val++; + + mark_flags(flags, a, &a_val, sinfo.trusted); + + a[a_val].type = CKA_VERIFY; + if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) { + a[a_val].value = (void*)&tval; + a[a_val].value_len = sizeof(tval); + } else { + a[a_val].value = (void*)&fval; + a[a_val].value_len = sizeof(fval); + } + a_val++; + + if (pk == GNUTLS_PK_RSA) { + a[a_val].type = CKA_ENCRYPT; + if (key_usage & (GNUTLS_KEY_ENCIPHER_ONLY|GNUTLS_KEY_DECIPHER_ONLY)) { + a[a_val].value = (void*)&tval; + a[a_val].value_len = sizeof(tval); + } else { + 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, &ctx); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv)); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + /* generated! + */ + + ret = 0; + + cleanup: + clean_pubkey(a, a_val); + pkcs11_close_session(&sinfo); + return ret; + +} + + +/** + * gnutls_pkcs11_copy_attached_extension: + * @token_url: A PKCS #11 URL specifying a token + * @crt: An X.509 certificate object + * @data: the attached extension + * @label: A name to be used for the attached extension (may be %NULL) + * @flags: One of GNUTLS_PKCS11_OBJ_FLAG_* + * + * This function will copy an the attached extension in @data for + * the certificate provided in @crt in the PKCS #11 token specified + * by the URL (typically a trust module). The extension must be in + * RFC5280 Extension format. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.3.8 + **/ +int +gnutls_pkcs11_copy_attached_extension(const char *token_url, + gnutls_x509_crt_t crt, + gnutls_datum_t *data, + const char *label, + unsigned int flags) +{ + int ret; + struct p11_kit_uri *info = NULL; + ck_rv_t rv; + struct ck_attribute a[MAX_ASIZE]; + ck_object_handle_t ctx; + unsigned a_vals; + struct pkcs11_session_info sinfo; + ck_object_class_t class; + gnutls_datum_t spki = {NULL, 0}; + + PKCS11_CHECK_INIT; + + ret = pkcs11_url_to_info(token_url, &info, 0); + 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 = x509_crt_to_raw_pubkey(crt, &spki); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + class = CKO_X_CERTIFICATE_EXTENSION; + a_vals = 0; + a[a_vals].type = CKA_CLASS; + a[a_vals].value = &class; + a[a_vals++].value_len = sizeof(class); + + a[a_vals].type = CKA_PUBLIC_KEY_INFO; + a[a_vals].value = spki.data; + a[a_vals++].value_len = spki.size; + + a[a_vals].type = CKA_VALUE; + a[a_vals].value = data->data; + a[a_vals++].value_len = data->size; + + a[a_vals].type = CKA_TOKEN; + a[a_vals].value = (void *) &tval; + a[a_vals++].value_len = sizeof(tval); + + if (label) { + a[a_vals].type = CKA_LABEL; + a[a_vals].value = (void *) label; + a[a_vals++].value_len = strlen(label); + } + + rv = pkcs11_create_object(sinfo.module, sinfo.pks, a, a_vals, &ctx); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv)); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + ret = 0; + + cleanup: + pkcs11_close_session(&sinfo); + gnutls_free(spki.data); + return ret; + +} + +/** + * gnutls_pkcs11_copy_x509_privkey2: + * @token_url: A PKCS #11 URL specifying a token + * @key: A private key + * @label: A name to be used for the stored data + * @cid: The CKA_ID to set for the object -if NULL, the ID will be derived from the public key + * @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. + * + * Since 3.6.3 the objects are marked as sensitive by default unless + * %GNUTLS_PKCS11_OBJ_FLAG_MARK_NOT_SENSITIVE is specified. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.0 + **/ +int +gnutls_pkcs11_copy_x509_privkey2(const char *token_url, + gnutls_x509_privkey_t key, + const char *label, + const gnutls_datum_t *cid, + 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[32]; + ck_object_class_t class = CKO_PRIVATE_KEY; + ck_object_handle_t ctx; + 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; + + PKCS11_CHECK_INIT; + + 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, 0); + 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; + } + + pk = gnutls_x509_privkey_get_pk_algorithm(key); + FIX_KEY_USAGE(pk, key_usage); + + 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; + if (cid == NULL || cid->size == 0) { + 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; + } + + a[a_val].value = id; + a[a_val].value_len = id_size; + } else { + a[a_val].value = cid->data; + a[a_val].value_len = cid->size; + } + a_val++; + + a[a_val].type = CKA_SIGN; + if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) { + a[a_val].value = (void*)&tval; + a[a_val].value_len = sizeof(tval); + } else { + a[a_val].value = (void*)&fval; + a[a_val].value_len = sizeof(fval); + } + a_val++; + + if (pk == GNUTLS_PK_RSA) { + a[a_val].type = CKA_DECRYPT; + if ((key_usage & (GNUTLS_KEY_ENCIPHER_ONLY|GNUTLS_KEY_DECIPHER_ONLY)) || + (key_usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) { + a[a_val].value = (void*)&tval; + a[a_val].value_len = sizeof(tval); + } else { + a[a_val].value = (void*)&fval; + a[a_val].value_len = sizeof(fval); + } + 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 (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_ALWAYS_AUTH) { + a[a_val].type = CKA_ALWAYS_AUTHENTICATE; + a[a_val].value = (void *) &tval; + a[a_val].value_len = sizeof(tval); + a_val++; + } + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_EXTRACTABLE) { + a[a_val].type = CKA_EXTRACTABLE; + a[a_val].value = (void *) &tval; + a[a_val].value_len = sizeof(tval); + (a_val)++; + } else { + a[a_val].type = CKA_EXTRACTABLE; + a[a_val].value = (void *) &fval; + a[a_val].value_len = sizeof(fval); + (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_NOT_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++; + } + + switch (pk) { + case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: + { + + ret = _gnutls_params_get_rsa_raw(&key->params, &m, &e, &d, &p, + &q, &u, &exp1, &exp2, + GNUTLS_EXPORT_FLAG_NO_LZ); + 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_params_get_dsa_raw(&key->params, &p, &q, &g, &y, &x, + GNUTLS_EXPORT_FLAG_NO_LZ); + 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.curve, + &p); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_mpi_dprint(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; + } +#ifdef HAVE_CKM_EDDSA + case GNUTLS_PK_EDDSA_ED25519: + { + ret = + _gnutls_x509_write_ecc_params(key->params.curve, + &p); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + type = CKK_EC_EDWARDS; + + 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 = key->params.raw_priv.data; + a[a_val].value_len = key->params.raw_priv.size; + a_val++; + + break; + } +#endif + default: + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + a[a_val].type = CKA_KEY_TYPE; + a[a_val].value = &type; + a[a_val].value_len = sizeof(type); + a_val++; + + rv = pkcs11_create_object(sinfo.module, sinfo.pks, a, a_val, &ctx); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv)); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + ret = 0; + + cleanup: + switch (pk) { + case GNUTLS_PK_RSA_PSS: + 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: + case GNUTLS_PK_EDDSA_ED25519: + { + 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_cb(struct ck_function_list *module, struct pkcs11_session_info *sinfo, + struct ck_token_info *tinfo, + 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 ctx; + unsigned long count, a_vals; + int found = 0, ret; + + if (tinfo == 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, 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("p11: FindObjectsInit failed.\n"); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + while (pkcs11_find_objects + (sinfo->module, sinfo->pks, &ctx, 1, &count) == CKR_OK + && count == 1) { + rv = pkcs11_destroy_object(sinfo->module, sinfo->pks, ctx); + if (rv != CKR_OK) { + _gnutls_debug_log + ("p11: 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; + + PKCS11_CHECK_INIT; + + memset(&find_data, 0, sizeof(find_data)); + + ret = pkcs11_url_to_info(object_url, &find_data.info, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _pkcs11_traverse_tokens(delete_obj_url_cb, &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]; + + PKCS11_CHECK_INIT; + + ret = pkcs11_url_to_info(token_url, &info, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = pkcs11_find_slot(&module, &slot, info, NULL, NULL, 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("p11: %s\n", pkcs11_strerror(rv)); + return pkcs11_rv_to_err(rv); + } + + return 0; + +} + +#define L(x) ((x==NULL)?0:strlen(x)) + +/** + * 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 or administrator's PIN for + * the given token. If it is called to set a PIN for first time + * the oldpin must be %NULL. When setting the admin's PIN with the + * %GNUTLS_PIN_SO flag, the @oldpin value must be provided (this requirement + * is relaxed after GnuTLS 3.6.5 since which the PIN will be requested if missing). + * + * 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; + + PKCS11_CHECK_INIT; + + ret = pkcs11_url_to_info(token_url, &info, 0); + 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 && !(flags & GNUTLS_PIN_SO)) { + /* This changes only the user PIN */ + rv = pkcs11_init_pin(sinfo.module, sinfo.pks, + (uint8_t *) newpin, strlen(newpin)); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: %s\n", + pkcs11_strerror(rv)); + ret = pkcs11_rv_to_err(rv); + goto finish; + } + } else { + struct p11_kit_pin *pin; + unsigned oldpin_size; + + oldpin_size = L(oldpin); + + if (!(sinfo.tinfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH)) { + if (newpin == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (oldpin == NULL) { + struct pin_info_st pin_info; + memset(&pin_info, 0, sizeof(pin_info)); + + ret = pkcs11_retrieve_pin(&pin_info, info, &sinfo.tinfo, 0, CKU_SO, &pin); + if (ret < 0) { + gnutls_assert(); + goto finish; + } + oldpin = (const char*)p11_kit_pin_get_value(pin, NULL); + oldpin_size = p11_kit_pin_get_length(pin); + } + } + + rv = pkcs11_set_pin(sinfo.module, sinfo.pks, + oldpin, oldpin_size, + newpin, L(newpin)); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: %s\n", + pkcs11_strerror(rv)); + ret = pkcs11_rv_to_err(rv); + goto finish; + } + } + + ret = 0; + + finish: + pkcs11_close_session(&sinfo); + return ret; + +} + +/** + * gnutls_pkcs11_token_get_random: + * @token_url: A PKCS #11 URL specifying a token + * @len: The number of bytes of randomness to request + * @rnddata: A pointer to the memory area to be filled with random data + * + * This function will get random data from the given token. + * It will store rnddata and fill the memory pointed to by rnddata with + * len random bytes from the token. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_pkcs11_token_get_random(const char *token_url, + void *rnddata, size_t len) +{ + int ret; + struct p11_kit_uri *info = NULL; + ck_rv_t rv; + struct pkcs11_session_info sinfo; + + PKCS11_CHECK_INIT; + + ret = pkcs11_url_to_info(token_url, &info, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = pkcs11_open_session(&sinfo, NULL, info, 0); + p11_kit_uri_free(info); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + rv = _gnutls_pkcs11_get_random(sinfo.module, sinfo.pks, rnddata, len); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: %s\n", pkcs11_strerror(rv)); + ret = pkcs11_rv_to_err(rv); + goto finish; + } + + ret = 0; + + finish: + pkcs11_close_session(&sinfo); + return ret; + +} + +#if 0 +/* For documentation purposes */ + + +/** + * 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 x; +} + +/** + * 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. + * + * Since 3.6.3 the objects are marked as sensitive by default unless + * %GNUTLS_PKCS11_OBJ_FLAG_MARK_NOT_SENSITIVE is specified. + * + * 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 x; +} + +#endif |