diff --git a/ykcs11/mechanisms.h b/ykcs11/mechanisms.h index af00225..27bdced 100644 --- a/ykcs11/mechanisms.h +++ b/ykcs11/mechanisms.h @@ -13,8 +13,8 @@ CK_RV apply_sign_mechanism_update(op_info_t *op_info, CK_BYTE_PTR in, CK_ULONG i CK_RV apply_sign_mechanism_finalize(op_info_t *op_info); CK_RV check_generation_mechanism(const ykcs11_session_t *s, CK_MECHANISM_PTR m); -CK_RV check_pubkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG n); -CK_RV check_pvtkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG n); +CK_RV check_pubkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG n); // TODO: Move to objects.c +CK_RV check_pvtkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG n); // TODO: Move to objects.c CK_RV check_hash_mechanism(const ykcs11_session_t *s, CK_MECHANISM_PTR m); diff --git a/ykcs11/objects.c b/ykcs11/objects.c index ecfe714..92e8b70 100644 --- a/ykcs11/objects.c +++ b/ykcs11/objects.c @@ -4,6 +4,7 @@ #include #include #include "openssl_utils.h" +#include "utils.h" #include "debug.h" #define IS_CERT(x) (((x) >= PIV_CERT_OBJ_X509_PIV_AUTH && (x) < PIV_CERT_OBJ_LAST) ? CK_TRUE : CK_FALSE) @@ -912,3 +913,49 @@ CK_RV store_cert(piv_obj_id_t cert_id, CK_BYTE_PTR data, CK_ULONG len) { return CKR_OK; } + +CK_RV check_create_cert(CK_ATTRIBUTE_PTR templ, CK_ULONG n, + CK_BYTE_PTR id,CK_BYTE_PTR *value, CK_ULONG_PTR cert_len) { + + CK_ULONG i; + CK_BBOOL has_id = CK_FALSE; + CK_BBOOL has_value = CK_FALSE; + + for (i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + // Technically redundant check + if (*((CK_ULONG_PTR)templ[i].pValue) != CKO_CERTIFICATE) + return CKR_ATTRIBUTE_VALUE_INVALID; + + break; + + case CKA_ID: + has_id = CK_TRUE; + if (is_valid_key_id(*((CK_BYTE_PTR)templ[i].pValue)) == CK_FALSE) + return CKR_ATTRIBUTE_VALUE_INVALID; + + *id = *((CK_BYTE_PTR)templ[i].pValue); + break; + + case CKA_VALUE: + has_value = CK_TRUE; + *value = (CK_BYTE_PTR)templ[i].pValue; + + *cert_len = 0; + *cert_len += get_length(value + 1, cert_len) + 1; + break; + + default: + // Ignore other attributes for now + DBG(("Invalid %lx", templ[i].type)); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + if (has_id == CK_FALSE || + has_value == CK_FALSE) + return CKR_TEMPLATE_INCOMPLETE; + + return CKR_OK; +} diff --git a/ykcs11/objects.h b/ykcs11/objects.h index cbc3b0e..84dded1 100644 --- a/ykcs11/objects.h +++ b/ykcs11/objects.h @@ -12,4 +12,7 @@ CK_BBOOL is_private_object(ykcs11_session_t *s, CK_OBJECT_HANDLE obj); CK_RV get_available_certificate_ids(ykcs11_session_t *s, piv_obj_id_t *cert_ids, CK_ULONG n_certs); CK_RV store_cert(piv_obj_id_t cert_id, CK_BYTE_PTR data, CK_ULONG len); +CK_RV check_create_cert(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, + CK_BYTE_PTR *value, CK_ULONG_PTR cert_len); + #endif diff --git a/ykcs11/openssl_utils.c b/ykcs11/openssl_utils.c index cee91b2..08cf954 100644 --- a/ykcs11/openssl_utils.c +++ b/ykcs11/openssl_utils.c @@ -14,10 +14,16 @@ CK_RV do_store_cert(CK_BYTE_PTR data, CK_ULONG len, X509 **cert) { return CKR_HOST_MEMORY;*/ //dump_hex(data, len, stderr, CK_TRUE); - if (*p++ != 0x70) - return CKR_FUNCTION_FAILED; - - p += get_length(p, &cert_len); + if (*p == 0x70) { + // The certificate is in "PIV" format 0x70 len 0x30 len ... + p++; + p += get_length(p, &cert_len); + } + else { + // Raw certificate 0x30 len ... + cert_len = 0; + cert_len += get_length(p + 1, &cert_len) + 1; + } *cert = d2i_X509(NULL, &p, cert_len); if (*cert == NULL) @@ -223,6 +229,24 @@ create_empty_cert_cleanup: return rv; } +CK_RV do_check_cert(CK_BYTE_PTR in, CK_ULONG_PTR cert_len) { + + X509 *cert; + const unsigned char *p = in; // Mandatory temp variable required by OpenSSL + int len; + + len = 0; + len += get_length(p + 1, &len) + 1; + + *cert_len = len; + + cert = d2i_X509(NULL, &p, *cert_len); + if (cert == NULL) + return CKR_FUNCTION_FAILED; + + return CKR_OK; +} + CK_RV free_cert(X509 *cert) { X509_free((X509 *) cert); diff --git a/ykcs11/openssl_utils.h b/ykcs11/openssl_utils.h index 03f0c2a..14f8a28 100644 --- a/ykcs11/openssl_utils.h +++ b/ykcs11/openssl_utils.h @@ -12,6 +12,7 @@ CK_RV do_store_cert(CK_BYTE_PTR data, CK_ULONG len, X509 **cert); CK_RV do_create_empty_cert(CK_BYTE_PTR in, CK_ULONG in_len, CK_BBOOL is_rsa, CK_ULONG key_len, CK_BYTE_PTR out, CK_ULONG_PTR out_len); +CK_RV do_check_cert(CK_BYTE_PTR in, CK_ULONG_PTR cert_len); CK_RV free_cert(X509 *cert); CK_RV do_store_pubk(X509 *cert, EVP_PKEY **key); diff --git a/ykcs11/token_vendors.c b/ykcs11/token_vendors.c index 083aea2..4bc9348 100644 --- a/ykcs11/token_vendors.c +++ b/ykcs11/token_vendors.c @@ -1,7 +1,9 @@ #include "token_vendors.h" #include "yubico_token.h" +#include "openssl_utils.h" #include #include "debug.h" + static CK_RV COMMON_token_generate_key(ykpiv_state *state, CK_BBOOL rsa, CK_BYTE key, CK_ULONG key_len) { // TODO: make a function in ykpiv for this unsigned char in_data[5]; @@ -85,6 +87,41 @@ static CK_RV COMMON_token_generate_key(ykpiv_state *state, CK_BBOOL rsa, CK_BYTE return CKR_OK; } +static CK_RV COMMON_token_import_cert(ykpiv_state *state, CK_ULONG cert_id, CK_BYTE_PTR in) { + + unsigned char certdata[2100]; + unsigned char *certptr; + CK_ULONG cert_len; + + CK_RV rv; + + // Check whether or not we have a valid cert + if ((rv = do_check_cert(in, &cert_len)) != CKR_OK) + return rv; + + if (cert_len > 2100) + return CKR_FUNCTION_FAILED; + + certptr = certdata; + + *certptr++ = 0x70; + certptr += set_length(certptr, cert_len); + memcpy(certptr, in, cert_len); + certptr += cert_len; + + *certptr++ = 0x71; + *certptr++ = 1; + *certptr++ = 0; /* certinfo (gzip etc) */ + *certptr++ = 0xfe; /* LRC */ + *certptr++ = 0; + + // Store the certificate into the token + if (ykpiv_save_object(state, cert_id, certdata, (size_t)(certptr - certdata)) != YKPIV_OK) + return CKR_DEVICE_ERROR; + + return CKR_OK; +} + token_vendor_t get_token_vendor(vendor_id_t vid) { token_vendor_t v; @@ -103,6 +140,7 @@ token_vendor_t get_token_vendor(vendor_id_t vid) { v.get_token_object_list = YUBICO_get_token_object_list; v.get_token_raw_certificate = YUBICO_get_token_raw_certificate; v.token_generate_key = COMMON_token_generate_key; + v.token_import_cert = COMMON_token_import_cert; break; case UNKNOWN: @@ -120,6 +158,7 @@ token_vendor_t get_token_vendor(vendor_id_t vid) { v.get_token_object_list = NULL; v.get_token_raw_certificate = NULL; v.token_generate_key = NULL; + v.token_import_cert = NULL; } return v; diff --git a/ykcs11/token_vendors.h b/ykcs11/token_vendors.h index 041e2a0..e07f96e 100644 --- a/ykcs11/token_vendors.h +++ b/ykcs11/token_vendors.h @@ -21,6 +21,7 @@ typedef CK_RV (*get_t_raw_certificate_f)(ykpiv_state *, piv_obj_id_t, CK_BYTE_PT // Common token functions below typedef CK_RV (*t_generate_key_f)(ykpiv_state *, CK_BBOOL, CK_BYTE, CK_ULONG); +typedef CK_RV (*t_import_cert_f)(ykpiv_state *, CK_ULONG, CK_BYTE_PTR); // TODO: replace all the common call with functions defined in .c that use libykpiv @@ -38,6 +39,7 @@ typedef struct { get_t_object_list_f get_token_object_list; get_t_raw_certificate_f get_token_raw_certificate; t_generate_key_f token_generate_key; + t_import_cert_f token_import_cert; } token_vendor_t; token_vendor_t get_token_vendor(vendor_id_t vid); diff --git a/ykcs11/ykcs11.c b/ykcs11/ykcs11.c index 3bffaaf..6297040 100644 --- a/ykcs11/ykcs11.c +++ b/ykcs11/ykcs11.c @@ -821,7 +821,136 @@ CK_DEFINE_FUNCTION(CK_RV, C_CreateObject)( ) { DIN; - DBG(("TODO!!!")); + + CK_ULONG i; + CK_RV rv; + CK_OBJECT_CLASS class; + CK_BYTE id; + CK_BYTE_PTR value; + CK_ULONG len; + token_vendor_t token; + CK_BBOOL is_new; + CK_OBJECT_HANDLE object; + CK_ULONG cert_id; + CK_ULONG pvtk_id; + CK_ULONG pubk_id; + piv_obj_id_t *obj_ptr; + + if (piv_state == NULL) { + DBG(("libykpiv is not initialized or already finalized")); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG(("Session is not open")); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) { + DBG(("Unknown session %lu", hSession)); + return CKR_SESSION_HANDLE_INVALID; + } + + if (session.info.state != CKS_RW_SO_FUNCTIONS) { // TODO: does a regular user ever call this function? + DBG(("Authentication required to import objects")); + return CKR_SESSION_READ_ONLY; + } + + if (pTemplate == NULL_PTR || + phObject == NULL_PTR) { + DBG(("Wrong/Missing parameter")); + return CKR_ARGUMENTS_BAD; + } + + class = CKO_VENDOR_DEFINED; // Use this as a known value + for (i = 0; i < ulCount; i++) { + if (pTemplate[i].type == CKA_CLASS) { + class = *((CK_ULONG_PTR)pTemplate[i].pValue); + + // Can only import certificates and private keys + if (*((CK_ULONG_PTR)pTemplate[i].pValue) != CKO_CERTIFICATE && + *((CK_ULONG_PTR)pTemplate[i].pValue) != CKO_PUBLIC_KEY) { + DBG(("Unsupported class %lu", class)); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + } + } + + if (class == CKO_VENDOR_DEFINED) { + DBG(("Object class must be specified")); + return CKR_TEMPLATE_INCOMPLETE; + } + + token = get_token_vendor(session.slot->token->vid); + + switch (class) { + case CKO_CERTIFICATE: + DBG(("Importing certificate")); + + rv = check_create_cert(pTemplate, ulCount, &id, &value, &len); + if (rv != CKR_OK) { + DBG(("Certificate template not valid")); + return rv; + } + DBG(("Certificate id is %u", id)); + + object = PIV_CERT_OBJ_X509_PIV_AUTH + id ; + + rv = token.token_import_cert(piv_state, piv_2_ykpiv(object), value); // TODO: make function to get cert id + if (rv != CKR_OK) { + DBG(("Unable to import certificate")); + return rv; + } + + is_new = CK_TRUE; + for (i = 0; i < session.slot->token->n_objects; i++) { + if (session.slot->token->objects[i] == object) + is_new = CK_FALSE; + } + + cert_id = PIV_CERT_OBJ_X509_PIV_AUTH + id; // TODO: make function for these + pvtk_id = PIV_PVTK_OBJ_PIV_AUTH + id; + pubk_id = PIV_PUBK_OBJ_PIV_AUTH + id; + + // Check whether we created a new object or updated an existing one + if (is_new == CK_TRUE) { + // New object created, add it to the object list + + // Each object counts as three, even if we just only added a certificate + session.slot->token->n_objects += 3; + session.slot->token->n_certs++; + + obj_ptr = realloc(session.slot->token->objects, session.slot->token->n_objects * sizeof(piv_obj_id_t)); + if (obj_ptr == NULL) { + DBG(("Unable to store new item in the session")); + return CKR_HOST_MEMORY; + } + session.slot->token->objects = obj_ptr; + + obj_ptr = session.slot->token->objects + session.slot->token->n_objects - 3; + *obj_ptr++ = cert_id; + *obj_ptr++ = pvtk_id; + *obj_ptr++ = pubk_id; + } + + rv = store_cert(cert_id, value, len); + if (rv != CKR_OK) { + DBG(("Unable to store certificate data")); + return CKR_FUNCTION_FAILED; + } + + break; + + case CKO_PRIVATE_KEY: + //rv = create_pvt_key(); + return CKR_FUNCTION_FAILED; + break; + + default: + DBG(("Unknown object type")); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + DOUT; return CKR_OK; } @@ -1865,8 +1994,8 @@ CK_DEFINE_FUNCTION(CK_RV, C_GenerateKeyPair)( if (is_new == CK_TRUE) { // New object created, add it to the object list - // Each object counts as four - session.slot->token->n_objects += 4; + // Each object counts as three (data object is always there) + session.slot->token->n_objects += 3; session.slot->token->n_certs++; obj_ptr = realloc(session.slot->token->objects, session.slot->token->n_objects * sizeof(piv_obj_id_t)); @@ -1876,8 +2005,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_GenerateKeyPair)( } session.slot->token->objects = obj_ptr; - obj_ptr = session.slot->token->objects + session.slot->token->n_objects - 4; - *obj_ptr++ = dobj_id; + obj_ptr = session.slot->token->objects + session.slot->token->n_objects - 3; *obj_ptr++ = cert_id; *obj_ptr++ = pvtk_id; *obj_ptr++ = pubk_id;