From 63f89caa9022ecf51d1b82dc78af35ba9e38466d Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Mon, 4 May 2020 10:37:24 +0200 Subject: ta: pkcs11: attribute helper functions * Helper functions for object attributes management. * Helper functions to safely parse client attributes template to create a list of attributes for a object in the PKCS11 ta. * Helper functions for assigning or checking object attributes according to PKCS#11 specification. * Add id-to-string conversion for attribute/class/key types. * Helper functions to analyze object attributes. Reviewed-by: Ricardo Salveti Co-developed-by: Etienne Carriere Signed-off-by: Etienne Carriere Signed-off-by: Jens Wiklander --- ta/pkcs11/src/attributes.c | 299 +++++++++++++++ ta/pkcs11/src/attributes.h | 248 ++++++++++++ ta/pkcs11/src/pkcs11_attributes.c | 775 ++++++++++++++++++++++++++++++++++++++ ta/pkcs11/src/pkcs11_attributes.h | 137 +++++++ ta/pkcs11/src/pkcs11_helpers.c | 327 ++++++++++++++++ ta/pkcs11/src/pkcs11_helpers.h | 43 +++ ta/pkcs11/src/sanitize_object.c | 429 +++++++++++++++++++++ ta/pkcs11/src/sanitize_object.h | 41 ++ ta/pkcs11/src/sub.mk | 3 + 9 files changed, 2302 insertions(+) create mode 100644 ta/pkcs11/src/attributes.c create mode 100644 ta/pkcs11/src/attributes.h create mode 100644 ta/pkcs11/src/pkcs11_attributes.c create mode 100644 ta/pkcs11/src/pkcs11_attributes.h create mode 100644 ta/pkcs11/src/sanitize_object.c create mode 100644 ta/pkcs11/src/sanitize_object.h diff --git a/ta/pkcs11/src/attributes.c b/ta/pkcs11/src/attributes.c new file mode 100644 index 00000000..67b5e4e4 --- /dev/null +++ b/ta/pkcs11/src/attributes.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2017-2020, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "attributes.h" +#include "pkcs11_helpers.h" +#include "serializer.h" + +enum pkcs11_rc init_attributes_head(struct obj_attrs **head) +{ + *head = TEE_Malloc(sizeof(**head), TEE_MALLOC_FILL_ZERO); + if (!*head) + return PKCS11_CKR_DEVICE_MEMORY; + + return PKCS11_CKR_OK; +} + +enum pkcs11_rc add_attribute(struct obj_attrs **head, uint32_t attribute, + void *data, size_t size) +{ + size_t buf_len = sizeof(struct obj_attrs) + (*head)->attrs_size; + char **bstart = (void *)head; + enum pkcs11_rc rc = PKCS11_CKR_OK; + uint32_t data32 = 0; + + data32 = attribute; + rc = serialize(bstart, &buf_len, &data32, sizeof(uint32_t)); + if (rc) + return rc; + + data32 = size; + rc = serialize(bstart, &buf_len, &data32, sizeof(uint32_t)); + if (rc) + return rc; + + rc = serialize(bstart, &buf_len, data, size); + if (rc) + return rc; + + /* Alloced buffer is always well aligned */ + head = (void *)bstart; + (*head)->attrs_size += 2 * sizeof(uint32_t) + size; + (*head)->attrs_count++; + + return rc; +} + +void get_attribute_ptrs(struct obj_attrs *head, uint32_t attribute, + void **attr, uint32_t *attr_size, size_t *count) +{ + char *cur = (char *)head + sizeof(struct obj_attrs); + char *end = cur + head->attrs_size; + size_t next_off = 0; + size_t max_found = *count; + size_t found = 0; + void **attr_ptr = attr; + uint32_t *attr_size_ptr = attr_size; + + for (; cur < end; cur += next_off) { + /* Structure aligned copy of the pkcs11_ref in the object */ + struct pkcs11_attribute_head pkcs11_ref = { }; + + TEE_MemMove(&pkcs11_ref, cur, sizeof(pkcs11_ref)); + next_off = sizeof(pkcs11_ref) + pkcs11_ref.size; + + if (pkcs11_ref.id != attribute) + continue; + + found++; + + if (!max_found) + continue; /* only count matching attributes */ + + if (attr) + *attr_ptr++ = cur + sizeof(pkcs11_ref); + + if (attr_size) + *attr_size_ptr++ = pkcs11_ref.size; + + if (found == max_found) + break; + } + + /* Sanity */ + if (cur > end) { + DMSG("Exceeding serial object length"); + TEE_Panic(0); + } + + *count = found; +} + +enum pkcs11_rc get_attribute_ptr(struct obj_attrs *head, uint32_t attribute, + void **attr_ptr, uint32_t *attr_size) +{ + size_t count = 1; + + get_attribute_ptrs(head, attribute, attr_ptr, attr_size, &count); + + if (!count) + return PKCS11_RV_NOT_FOUND; + + if (count != 1) + return PKCS11_CKR_GENERAL_ERROR; + + return PKCS11_CKR_OK; +} + +enum pkcs11_rc get_attribute(struct obj_attrs *head, uint32_t attribute, + void *attr, uint32_t *attr_size) +{ + enum pkcs11_rc rc = PKCS11_CKR_OK; + void *attr_ptr = NULL; + uint32_t size = 0; + + rc = get_attribute_ptr(head, attribute, &attr_ptr, &size); + if (rc) + return rc; + + if (attr_size && *attr_size != size) { + *attr_size = size; + /* This reuses buffer-to-small for any bad size matching */ + return PKCS11_CKR_BUFFER_TOO_SMALL; + } + + if (attr) + TEE_MemMove(attr, attr_ptr, size); + + if (attr_size) + *attr_size = size; + + return PKCS11_CKR_OK; +} + +bool get_bool(struct obj_attrs *head, uint32_t attribute) +{ + enum pkcs11_rc rc = PKCS11_CKR_OK; + uint8_t bbool = 0; + uint32_t size = sizeof(bbool); + + rc = get_attribute(head, attribute, &bbool, &size); + + if (rc == PKCS11_RV_NOT_FOUND) + return false; + + assert(rc == PKCS11_CKR_OK); + return bbool; +} + +#if CFG_TEE_TA_LOG_LEVEL > 0 +/* + * Debug: dump CK attribute array to output trace + */ +#define ATTR_TRACE_FMT "%s attr %s / %s\t(0x%04"PRIx32" %"PRIu32"-byte" +#define ATTR_FMT_0BYTE ATTR_TRACE_FMT ")" +#define ATTR_FMT_1BYTE ATTR_TRACE_FMT ": %02x)" +#define ATTR_FMT_2BYTE ATTR_TRACE_FMT ": %02x %02x)" +#define ATTR_FMT_3BYTE ATTR_TRACE_FMT ": %02x %02x %02x)" +#define ATTR_FMT_4BYTE ATTR_TRACE_FMT ": %02x %02x %02x %02x)" +#define ATTR_FMT_ARRAY ATTR_TRACE_FMT ": %02x %02x %02x %02x ...)" + +static void __trace_attributes(char *prefix, void *src, void *end) +{ + size_t next_off = 0; + char *prefix2 = NULL; + size_t prefix_len = strlen(prefix); + char *cur = src; + + /* append 4 spaces to the prefix plus terminal '\0' */ + prefix2 = TEE_Malloc(prefix_len + 1 + 4, TEE_MALLOC_FILL_ZERO); + if (!prefix2) + return; + + TEE_MemMove(prefix2, prefix, prefix_len + 1); + TEE_MemFill(prefix2 + prefix_len, ' ', 4); + *(prefix2 + prefix_len + 4) = '\0'; + + for (; cur < (char *)end; cur += next_off) { + struct pkcs11_attribute_head pkcs11_ref = { }; + uint8_t data[4] = { 0 }; + + TEE_MemMove(&pkcs11_ref, cur, sizeof(pkcs11_ref)); + TEE_MemMove(&data[0], cur + sizeof(pkcs11_ref), + MIN(pkcs11_ref.size, sizeof(data))); + + next_off = sizeof(pkcs11_ref) + pkcs11_ref.size; + + switch (pkcs11_ref.size) { + case 0: + IMSG_RAW(ATTR_FMT_0BYTE, + prefix, id2str_attr(pkcs11_ref.id), "*", + pkcs11_ref.id, pkcs11_ref.size); + break; + case 1: + IMSG_RAW(ATTR_FMT_1BYTE, + prefix, id2str_attr(pkcs11_ref.id), + id2str_attr_value(pkcs11_ref.id, + pkcs11_ref.size, + cur + sizeof(pkcs11_ref)), + pkcs11_ref.id, pkcs11_ref.size, data[0]); + break; + case 2: + IMSG_RAW(ATTR_FMT_2BYTE, + prefix, id2str_attr(pkcs11_ref.id), + id2str_attr_value(pkcs11_ref.id, + pkcs11_ref.size, + cur + sizeof(pkcs11_ref)), + pkcs11_ref.id, pkcs11_ref.size, data[0], + data[1]); + break; + case 3: + IMSG_RAW(ATTR_FMT_3BYTE, + prefix, id2str_attr(pkcs11_ref.id), + id2str_attr_value(pkcs11_ref.id, + pkcs11_ref.size, + cur + sizeof(pkcs11_ref)), + pkcs11_ref.id, pkcs11_ref.size, + data[0], data[1], data[2]); + break; + case 4: + IMSG_RAW(ATTR_FMT_4BYTE, + prefix, id2str_attr(pkcs11_ref.id), + id2str_attr_value(pkcs11_ref.id, + pkcs11_ref.size, + cur + sizeof(pkcs11_ref)), + pkcs11_ref.id, pkcs11_ref.size, + data[0], data[1], data[2], data[3]); + break; + default: + IMSG_RAW(ATTR_FMT_ARRAY, + prefix, id2str_attr(pkcs11_ref.id), + id2str_attr_value(pkcs11_ref.id, + pkcs11_ref.size, + cur + sizeof(pkcs11_ref)), + pkcs11_ref.id, pkcs11_ref.size, + data[0], data[1], data[2], data[3]); + break; + } + + switch (pkcs11_ref.id) { + case PKCS11_CKA_WRAP_TEMPLATE: + case PKCS11_CKA_UNWRAP_TEMPLATE: + case PKCS11_CKA_DERIVE_TEMPLATE: + trace_attributes(prefix2, cur + sizeof(pkcs11_ref)); + break; + default: + break; + } + } + + /* Sanity */ + if (cur != end) + EMSG("Warning: unexpected alignment in object attributes"); + + TEE_Free(prefix2); +} + +void trace_attributes(const char *prefix, void *ref) +{ + struct obj_attrs head; + char *pre = NULL; + + TEE_MemMove(&head, ref, sizeof(head)); + + pre = TEE_Malloc(prefix ? strlen(prefix) + 2 : 2, TEE_MALLOC_FILL_ZERO); + if (!pre) { + EMSG("%s: out of memory", prefix); + return; + } + + if (prefix) + TEE_MemMove(pre, prefix, strlen(prefix)); + + IMSG_RAW("%s,--- (serial object) Attributes list --------", pre); + IMSG_RAW("%s| %"PRIu32" item(s) - %"PRIu32" bytes", + pre, head.attrs_count, head.attrs_size); + + pre[prefix ? strlen(prefix) : 0] = '|'; + __trace_attributes(pre, (char *)ref + sizeof(head), + (char *)ref + sizeof(head) + head.attrs_size); + + IMSG_RAW("%s`-----------------------", prefix ? prefix : ""); + + TEE_Free(pre); +} +#endif /*CFG_TEE_TA_LOG_LEVEL*/ diff --git a/ta/pkcs11/src/attributes.h b/ta/pkcs11/src/attributes.h new file mode 100644 index 00000000..1dfa7b92 --- /dev/null +++ b/ta/pkcs11/src/attributes.h @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2017-2020, Linaro Limited + */ + +#ifndef PKCS11_TA_ATTRIBUTES_H +#define PKCS11_TA_ATTRIBUTES_H + +#include +#include +#include +#include + +#include "pkcs11_helpers.h" + +/* + * Boolean property attributes (BPA): bit position in a 64 bit mask + * for boolean properties object can mandate as attribute, depending + * on the object. These attributes are often accessed and it is + * quicker to get them from a 64 bit field in the object instance + * rather than searching into the object attributes. + */ +#define PKCS11_BOOLPROPS_BASE 0 +#define PKCS11_BOOLPROPS_MAX_COUNT 64 + +enum boolprop_attr { + BPA_TOKEN = 0, + BPA_PRIVATE, + BPA_TRUSTED, + BPA_SENSITIVE, + BPA_ENCRYPT, + BPA_DECRYPT, + BPA_WRAP, + BPA_UNWRAP, + BPA_SIGN, + BPA_SIGN_RECOVER, + BPA_VERIFY, + BPA_VERIFY_RECOVER, + BPA_DERIVE, + BPA_EXTRACTABLE, + BPA_LOCAL, + BPA_NEVER_EXTRACTABLE, + BPA_ALWAYS_SENSITIVE, + BPA_MODIFIABLE, + BPA_COPYABLE, + BPA_DESTROYABLE, + BPA_ALWAYS_AUTHENTICATE, + BPA_WRAP_WITH_TRUSTED, +}; + +/* + * Header of a serialized memory object inside PKCS11 TA. + * + * @attrs_size: byte size of the serialized data + * @attrs_count: number of items in the blob + * @attrs: then starts the blob binary data + */ +struct obj_attrs { + uint32_t attrs_size; + uint32_t attrs_count; + uint8_t attrs[]; +}; + +/* + * init_attributes_head() - Allocate a reference for serialized attributes + * @head: *@head holds the retrieved pointer + * + * Retrieved pointer can be freed from a simple TEE_Free(reference). + * + * Return a PKCS11_OK on success or a PKCS11 return code. + */ +enum pkcs11_rc init_attributes_head(struct obj_attrs **head); + +/* + * add_attribute() - Update serialized attributes to add an entry. + * + * @head: *@head points to serialized attributes, + * can be reallocated as attributes are added + * @attribute: Attribute ID to add + * @data: Opaque data of attribute + * @size: Size of data + * + * Return a PKCS11_OK on success or a PKCS11 return code. + */ +enum pkcs11_rc add_attribute(struct obj_attrs **head, uint32_t attribute, + void *data, size_t size); + +/* + * get_attribute_ptrs() - Get pointers to attributes with a given ID + * @head: Pointer to serialized attributes + * @attribute: Attribute ID to look for + * @attr: Array of pointers to the data inside @head + * @attr_size: Array of uint32_t holding the sizes of each value pointed to + * by @attr + * @count: Number of elements in the arrays above + * + * If *count == 0, count and return in *count the number of attributes matching + * the input attribute ID. + * + * If *count != 0, return the address and size of the attributes found, up to + * the occurrence number *count. attr and attr_size are expected large + * enough. attr is the output array of the values found. attr_size is the + * output array of the size of each value found. + * + * If attr_size != NULL, return in *attr_size attribute value size. + * If attr != NULL return in *attr the address of the attribute value. + */ +void get_attribute_ptrs(struct obj_attrs *head, uint32_t attribute, + void **attr, uint32_t *attr_size, size_t *count); + +/* + * get_attribute_ptrs() - Get pointer to the attribute of a given ID + * @head: Pointer to serialized attributes + * @attribute: Attribute ID + * @attr: *@attr holds the retrieved pointer to the attribute value + * @attr_size: Size of the attribute value + * + * If no matching attributes is found return PKCS11_RV_NOT_FOUND. + * If attr_size != NULL, return in *attr_size attribute value size. + * If attr != NULL, return in *attr the address of the attribute value. + * + * Return a PKCS11_OK or PKCS11_RV_NOT_FOUND on success, or a PKCS11 return + * code. + */ +enum pkcs11_rc get_attribute_ptr(struct obj_attrs *head, uint32_t attribute, + void **attr_ptr, uint32_t *attr_size); +/* + * get_attribute() - Copy out the attribute of a given ID + * @head: Pointer to serialized attributes + * @attribute: Attribute ID to look for + * @attr: holds the retrieved attribute value + * @attr_size: Size of the attribute value + * + * If attribute is not found, return PKCS11_RV_NOT_FOUND. + * If attr_size != NULL, check *attr_size matches attributes size and return + * PKCS11_CKR_BUFFER_TOO_SMALL with expected size in *attr_size. + * If attr != NULL and attr_size is NULL or gives expected buffer size, + * copy attribute value into attr. + * + * Return a PKCS11_OK or PKCS11_RV_NOT_FOUND on success, or a PKCS11 return + * code. + */ +enum pkcs11_rc get_attribute(struct obj_attrs *head, uint32_t attribute, + void *attr, uint32_t *attr_size); + +/* + * get_u32_attribute() - Copy out the 32-bit attribute value of a given ID + * @head: Pointer to serialized attributes + * @attribute: Attribute ID + * @attr: holds the retrieved 32-bit attribute value + * + * If attribute is not found, return PKCS11_RV_NOT_FOUND. + * If the retreived attribute doesn't have a 4 byte sized value + * PKCS11_CKR_GENERAL_ERROR is returned. + * + * Return a PKCS11_OK or PKCS11_RV_NOT_FOUND on success, or a PKCS11 return + * code. + */ + +static inline enum pkcs11_rc get_u32_attribute(struct obj_attrs *head, + uint32_t attribute, + uint32_t *attr) +{ + uint32_t size = sizeof(uint32_t); + enum pkcs11_rc rc = get_attribute(head, attribute, attr, &size); + + if (!rc && size != sizeof(uint32_t)) + return PKCS11_CKR_GENERAL_ERROR; + + return rc; +} + +/* + * get_class() - Get class ID of an object + * @head: Pointer to serialized attributes + * + * Returns the class ID of an object on succes or returns + * PKCS11_CKO_UNDEFINED_ID on error. + */ +static inline enum pkcs11_class_id get_class(struct obj_attrs *head) +{ + uint32_t class = 0; + uint32_t size = sizeof(class); + + if (get_attribute(head, PKCS11_CKA_CLASS, &class, &size)) + return PKCS11_CKO_UNDEFINED_ID; + + return class; +} + +/* + * get_key_type() - Get the key type of an object + * @head: Pointer to serialized attributes + * + * Returns the key type of an object on success or returns + * PKCS11_CKK_UNDEFINED_ID on error. + */ +static inline enum pkcs11_key_type get_key_type(struct obj_attrs *head) +{ + uint32_t type = 0; + uint32_t size = sizeof(type); + + if (get_attribute(head, PKCS11_CKA_KEY_TYPE, &type, &size)) + return PKCS11_CKK_UNDEFINED_ID; + + return type; +} + +/* + * get_mechanism_type() - Get the mechanism type of an object + * @head: Pointer to serialized attributes + * + * Returns the mechanism type of an object on success or returns + * PKCS11_CKM_UNDEFINED_ID on error. + */ +static inline enum pkcs11_mechanism_id get_mechanism_type(struct obj_attrs *head) +{ + uint32_t type = 0; + uint32_t size = sizeof(type); + + if (get_attribute(head, PKCS11_CKA_MECHANISM_TYPE, &type, &size)) + return PKCS11_CKM_UNDEFINED_ID; + + return type; +} + +/* + * get_bool() - Get the bool value of an attribute + * @head: Pointer to serialized attributes + * @attribute: Attribute ID to look for + * + * May assert if attribute ID isn't of the boolean type. + * + * Returns the bool value of the supplied attribute ID on success if found + * else false. + */ +bool get_bool(struct obj_attrs *head, uint32_t attribute); + +#if CFG_TEE_TA_LOG_LEVEL > 0 +/* Debug: dump object attributes to IMSG() trace console */ +void trace_attributes(const char *prefix, void *ref); +#else +static inline void trace_attributes(const char *prefix __unused, + void *ref __unused) +{ +} +#endif /*CFG_TEE_TA_LOG_LEVEL*/ +#endif /*PKCS11_TA_ATTRIBUTES_H*/ diff --git a/ta/pkcs11/src/pkcs11_attributes.c b/ta/pkcs11/src/pkcs11_attributes.c new file mode 100644 index 00000000..0f669e52 --- /dev/null +++ b/ta/pkcs11/src/pkcs11_attributes.c @@ -0,0 +1,775 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2017-2020, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "attributes.h" +#include "handle.h" +#include "pkcs11_attributes.h" +#include "pkcs11_helpers.h" +#include "pkcs11_token.h" +#include "sanitize_object.h" +#include "serializer.h" +#include "token_capabilities.h" + +/* Byte size of CKA_ID attribute when generated locally */ +#define PKCS11_CKA_DEFAULT_SIZE 16 + +/* + * Object default boolean attributes as per PKCS#11 + */ +static uint8_t *pkcs11_object_default_boolprop(uint32_t attribute) +{ + static const uint8_t bool_true = 1; + static const uint8_t bool_false; + + switch (attribute) { + /* As per PKCS#11 default value */ + case PKCS11_CKA_MODIFIABLE: + case PKCS11_CKA_COPYABLE: + case PKCS11_CKA_DESTROYABLE: + return (uint8_t *)&bool_true; + case PKCS11_CKA_TOKEN: + case PKCS11_CKA_PRIVATE: + /* symkey false, privkey: token specific */ + case PKCS11_CKA_SENSITIVE: + return (uint8_t *)&bool_false; + /* Token specific default value */ + case PKCS11_CKA_SIGN: + case PKCS11_CKA_VERIFY: + return (uint8_t *)&bool_true; + case PKCS11_CKA_DERIVE: + case PKCS11_CKA_ENCRYPT: + case PKCS11_CKA_DECRYPT: + case PKCS11_CKA_SIGN_RECOVER: + case PKCS11_CKA_VERIFY_RECOVER: + case PKCS11_CKA_WRAP: + case PKCS11_CKA_UNWRAP: + case PKCS11_CKA_EXTRACTABLE: + case PKCS11_CKA_WRAP_WITH_TRUSTED: + case PKCS11_CKA_ALWAYS_AUTHENTICATE: + case PKCS11_CKA_TRUSTED: + return (uint8_t *)&bool_false; + default: + DMSG("No default for boolprop attribute %#"PRIx32, attribute); + return NULL; + } +} + +/* + * Object expects several boolean attributes to be set to a default value + * or to a validate client configuration value. This function append the input + * attribute (id/size/value) in the serialized object. + */ +static enum pkcs11_rc pkcs11_import_object_boolprop(struct obj_attrs **out, + struct obj_attrs *templ, + uint32_t attribute) +{ + enum pkcs11_rc rc = PKCS11_CKR_OK; + uint8_t bbool = 0; + uint32_t size = sizeof(uint8_t); + void *attr = NULL; + + rc = get_attribute(templ, attribute, &bbool, &size); + if (rc) { + if (rc != PKCS11_RV_NOT_FOUND) + return rc; + attr = pkcs11_object_default_boolprop(attribute); + if (!attr) + return PKCS11_CKR_TEMPLATE_INCOMPLETE; + } else { + attr = &bbool; + } + + /* Boolean attributes are 1byte in the ABI, no alignment issue */ + return add_attribute(out, attribute, attr, sizeof(uint8_t)); +} + +static enum pkcs11_rc set_mandatory_boolprops(struct obj_attrs **out, + struct obj_attrs *temp, + uint32_t const *bp, + size_t bp_count) +{ + enum pkcs11_rc rc = PKCS11_CKR_OK; + size_t n = 0; + + for (n = 0; n < bp_count; n++) { + rc = pkcs11_import_object_boolprop(out, temp, bp[n]); + if (rc) + return rc; + } + + return rc; +} + +static enum pkcs11_rc set_mandatory_attributes(struct obj_attrs **out, + struct obj_attrs *temp, + uint32_t const *bp, + size_t bp_count) +{ + enum pkcs11_rc rc = PKCS11_CKR_OK; + size_t n = 0; + + for (n = 0; n < bp_count; n++) { + uint32_t size = 0; + void *value = NULL; + + if (get_attribute_ptr(temp, bp[n], &value, &size)) + return PKCS11_CKR_TEMPLATE_INCOMPLETE; + + rc = add_attribute(out, bp[n], value, size); + if (rc) + return rc; + } + + return rc; +} + +static enum pkcs11_rc get_default_value(enum pkcs11_attr_id id, void **value, + uint32_t *size) +{ + /* should have been taken care of already */ + assert(!pkcs11_attr_is_boolean(id)); + + if (id == PKCS11_CKA_PUBLIC_KEY_INFO) { + EMSG("Cannot provide default PUBLIC_KEY_INFO"); + return PKCS11_CKR_TEMPLATE_INCONSISTENT; + } + + /* All other attributes have an empty default value */ + *value = NULL; + *size = 0; + return PKCS11_CKR_OK; +} + +static enum pkcs11_rc set_optional_attributes(struct obj_attrs **out, + struct obj_attrs *temp, + uint32_t const *bp, + size_t bp_count) +{ + enum pkcs11_rc rc = PKCS11_CKR_OK; + size_t n = 0; + + for (n = 0; n < bp_count; n++) { + uint32_t size = 0; + void *value = NULL; + + rc = get_attribute_ptr(temp, bp[n], &value, &size); + if (rc == PKCS11_RV_NOT_FOUND) + rc = get_default_value(bp[n], &value, &size); + if (rc) + return rc; + + rc = add_attribute(out, bp[n], value, size); + if (rc) + return rc; + } + + return rc; +} + +/* + * Below are listed the mandated or optional expected attributes for + * PKCS#11 storage objects. + * + * Note: boolprops (mandated boolean attributes) PKCS11_CKA_ALWAYS_SENSITIVE, + * and PKCS11_CKA_NEVER_EXTRACTABLE are set by the token, not provided + * in the client template. + */ + +/* PKCS#11 specification for any object (session/token) of the storage */ +static const uint32_t pkcs11_any_object_boolprops[] = { + PKCS11_CKA_TOKEN, PKCS11_CKA_PRIVATE, + PKCS11_CKA_MODIFIABLE, PKCS11_CKA_COPYABLE, PKCS11_CKA_DESTROYABLE, +}; + +static const uint32_t pkcs11_any_object_optional[] = { + PKCS11_CKA_LABEL, +}; + +/* PKCS#11 specification for raw data object (+pkcs11_any_object_xxx) */ +const uint32_t pkcs11_raw_data_optional[] = { + PKCS11_CKA_OBJECT_ID, PKCS11_CKA_APPLICATION, PKCS11_CKA_VALUE, +}; + +/* PKCS#11 specification for any key object (+pkcs11_any_object_xxx) */ +static const uint32_t pkcs11_any_key_boolprops[] = { + PKCS11_CKA_DERIVE, +}; + +static const uint32_t pkcs11_any_key_optional[] = { + PKCS11_CKA_ID, + PKCS11_CKA_START_DATE, PKCS11_CKA_END_DATE, + PKCS11_CKA_ALLOWED_MECHANISMS, +}; + +/* PKCS#11 specification for any symmetric key (+pkcs11_any_key_xxx) */ +static const uint32_t pkcs11_symm_key_boolprops[] = { + PKCS11_CKA_ENCRYPT, PKCS11_CKA_DECRYPT, + PKCS11_CKA_SIGN, PKCS11_CKA_VERIFY, + PKCS11_CKA_WRAP, PKCS11_CKA_UNWRAP, + PKCS11_CKA_SENSITIVE, PKCS11_CKA_EXTRACTABLE, + PKCS11_CKA_WRAP_WITH_TRUSTED, PKCS11_CKA_TRUSTED, +}; + +static const uint32_t pkcs11_symm_key_optional[] = { + PKCS11_CKA_WRAP_TEMPLATE, PKCS11_CKA_UNWRAP_TEMPLATE, + PKCS11_CKA_DERIVE_TEMPLATE, + PKCS11_CKA_VALUE, PKCS11_CKA_VALUE_LEN, +}; + +/* PKCS#11 specification for any asymmetric public key (+pkcs11_any_key_xxx) */ +static const uint32_t pkcs11_public_key_boolprops[] = { + PKCS11_CKA_ENCRYPT, PKCS11_CKA_VERIFY, PKCS11_CKA_VERIFY_RECOVER, + PKCS11_CKA_WRAP, + PKCS11_CKA_TRUSTED, +}; + +static const uint32_t pkcs11_public_key_mandated[] = { + PKCS11_CKA_SUBJECT +}; + +static const uint32_t pkcs11_public_key_optional[] = { + PKCS11_CKA_WRAP_TEMPLATE, PKCS11_CKA_PUBLIC_KEY_INFO, +}; + +/* PKCS#11 specification for any asymmetric private key (+pkcs11_any_key_xxx) */ +static const uint32_t pkcs11_private_key_boolprops[] = { + PKCS11_CKA_DECRYPT, PKCS11_CKA_SIGN, PKCS11_CKA_SIGN_RECOVER, + PKCS11_CKA_UNWRAP, + PKCS11_CKA_SENSITIVE, PKCS11_CKA_EXTRACTABLE, + PKCS11_CKA_WRAP_WITH_TRUSTED, PKCS11_CKA_ALWAYS_AUTHENTICATE, +}; + +static const uint32_t pkcs11_private_key_mandated[] = { + PKCS11_CKA_SUBJECT +}; + +static const uint32_t pkcs11_private_key_optional[] = { + PKCS11_CKA_UNWRAP_TEMPLATE, PKCS11_CKA_PUBLIC_KEY_INFO, +}; + +/* PKCS#11 specification for any RSA key (+pkcs11_public/private_key_xxx) */ +static const uint32_t pkcs11_rsa_public_key_mandated[] = { + PKCS11_CKA_MODULUS_BITS, +}; + +static const uint32_t pkcs11_rsa_public_key_optional[] = { + PKCS11_CKA_MODULUS, PKCS11_CKA_PUBLIC_EXPONENT, +}; + +static const uint32_t pkcs11_rsa_private_key_optional[] = { + PKCS11_CKA_MODULUS, PKCS11_CKA_PUBLIC_EXPONENT, + PKCS11_CKA_PRIVATE_EXPONENT, + PKCS11_CKA_PRIME_1, PKCS11_CKA_PRIME_2, + PKCS11_CKA_EXPONENT_1, PKCS11_CKA_EXPONENT_2, PKCS11_CKA_COEFFICIENT, +}; + +/* PKCS#11 specification for any EC key (+pkcs11_public/private_key_xxx) */ +static const uint32_t pkcs11_ec_public_key_mandated[] = { + PKCS11_CKA_EC_PARAMS, +}; + +static const uint32_t pkcs11_ec_public_key_optional[] = { + PKCS11_CKA_EC_POINT, +}; + +static const uint32_t pkcs11_ec_private_key_mandated[] = { + PKCS11_CKA_EC_PARAMS, +}; + +static const uint32_t pkcs11_ec_private_key_optional[] = { + PKCS11_CKA_VALUE, +}; + +static enum pkcs11_rc create_storage_attributes(struct obj_attrs **out, + struct obj_attrs *temp) +{ + enum pkcs11_class_id class = PKCS11_CKO_UNDEFINED_ID; + enum pkcs11_rc rc = PKCS11_CKR_OK; + + rc = init_attributes_head(out); + if (rc) + return rc; + + /* Object class is mandatory */ + class = get_class(temp); + if (class == PKCS11_CKO_UNDEFINED_ID) { + EMSG("Class attribute not found"); + + return PKCS11_CKR_TEMPLATE_INCONSISTENT; + } + rc = add_attribute(out, PKCS11_CKA_CLASS, &class, sizeof(uint32_t)); + if (rc) + return rc; + + rc = set_mandatory_boolprops(out, temp, pkcs11_any_object_boolprops, + ARRAY_SIZE(pkcs11_any_object_boolprops)); + if (rc) + return rc; + + return set_optional_attributes(out, temp, pkcs11_any_object_optional, + ARRAY_SIZE(pkcs11_any_object_optional)); +} + +static enum pkcs11_rc create_genkey_attributes(struct obj_attrs **out, + struct obj_attrs *temp) +{ + uint32_t type = PKCS11_CKO_UNDEFINED_ID; + enum pkcs11_rc rc = PKCS11_CKR_OK; + + rc = create_storage_attributes(out, temp); + if (rc) + return rc; + + type = get_key_type(temp); + if (type == PKCS11_CKK_UNDEFINED_ID) { + EMSG("Key type attribute not found"); + + return PKCS11_CKR_TEMPLATE_INCONSISTENT; + } + rc = add_attribute(out, PKCS11_CKA_KEY_TYPE, &type, sizeof(uint32_t)); + if (rc) + return rc; + + rc = set_mandatory_boolprops(out, temp, pkcs11_any_key_boolprops, + ARRAY_SIZE(pkcs11_any_key_boolprops)); + if (rc) + return rc; + + return set_optional_attributes(out, temp, pkcs11_any_key_optional, + ARRAY_SIZE(pkcs11_any_key_optional)); +} + +static enum pkcs11_rc create_symm_key_attributes(struct obj_attrs **out, + struct obj_attrs *temp) +{ + enum pkcs11_rc rc = PKCS11_CKR_OK; + + assert(get_class(temp) == PKCS11_CKO_SECRET_KEY); + + rc = create_genkey_attributes(out, temp); + if (rc) + return rc; + + assert(get_class(*out) == PKCS11_CKO_SECRET_KEY); + + switch (get_key_type(*out)) { + case PKCS11_CKK_GENERIC_SECRET: + case PKCS11_CKK_AES: + case PKCS11_CKK_MD5_HMAC: + case PKCS11_CKK_SHA_1_HMAC: + case PKCS11_CKK_SHA256_HMAC: + case PKCS11_CKK_SHA384_HMAC: + case PKCS11_CKK_SHA512_HMAC: + case PKCS11_CKK_SHA224_HMAC: + break; + default: + EMSG("Invalid key type %#"PRIx32"/%s", + get_key_type(*out), id2str_key_type(get_key_type(*out))); + + return PKCS11_CKR_TEMPLATE_INCONSISTENT; + } + + rc = set_mandatory_boolprops(out, temp, pkcs11_symm_key_boolprops, + ARRAY_SIZE(pkcs11_symm_key_boolprops)); + if (rc) + return rc; + + return set_optional_attributes(out, temp, pkcs11_symm_key_optional, + ARRAY_SIZE(pkcs11_symm_key_optional)); +} + +static enum pkcs11_rc create_data_attributes(struct obj_attrs **out, + struct obj_attrs *temp) +{ + enum pkcs11_rc rc = PKCS11_CKR_OK; + + assert(get_class(temp) == PKCS11_CKO_DATA); + + rc = create_storage_attributes(out, temp); + if (rc) + return rc; + + assert(get_class(*out) == PKCS11_CKO_DATA); + + return set_optional_attributes(out, temp, pkcs11_raw_data_optional, + ARRAY_SIZE(pkcs11_raw_data_optional)); +} + +static enum pkcs11_rc create_pub_key_attributes(struct obj_attrs **out, + struct obj_attrs *temp) +{ + uint32_t const *mandated = NULL; + uint32_t const *optional = NULL; + size_t mandated_count = 0; + size_t optional_count = 0; + enum pkcs11_rc rc = PKCS11_CKR_OK; + + assert(get_class(temp) == PKCS11_CKO_PUBLIC_KEY); + + rc = create_genkey_attributes(out, temp); + if (rc) + return rc; + + assert(get_class(*out) == PKCS11_CKO_PUBLIC_KEY); + + rc = set_mandatory_boolprops(out, temp, pkcs11_public_key_boolprops, + ARRAY_SIZE(pkcs11_public_key_boolprops)); + if (rc) + return rc; + + rc = set_mandatory_attributes(out, temp, pkcs11_public_key_mandated, + ARRAY_SIZE(pkcs11_public_key_mandated)); + if (rc) + return rc; + + rc = set_optional_attributes(out, temp, pkcs11_public_key_optional, + ARRAY_SIZE(pkcs11_public_key_optional)); + if (rc) + return rc; + + switch (get_key_type(*out)) { + case PKCS11_CKK_RSA: + mandated = pkcs11_rsa_public_key_mandated; + optional = pkcs11_rsa_public_key_optional; + mandated_count = ARRAY_SIZE(pkcs11_rsa_public_key_mandated); + optional_count = ARRAY_SIZE(pkcs11_rsa_public_key_optional); + break; + case PKCS11_CKK_EC: + mandated = pkcs11_ec_public_key_mandated; + optional = pkcs11_ec_public_key_optional; + mandated_count = ARRAY_SIZE(pkcs11_ec_public_key_mandated); + optional_count = ARRAY_SIZE(pkcs11_ec_public_key_optional); + break; + default: + EMSG("Invalid key type %#"PRIx32"/%s", + get_key_type(*out), id2str_key_type(get_key_type(*out))); + + return PKCS11_CKR_TEMPLATE_INCONSISTENT; + } + + rc = set_mandatory_attributes(out, temp, mandated, mandated_count); + if (rc) + return rc; + + return set_optional_attributes(out, temp, optional, optional_count); +} + +static enum pkcs11_rc create_priv_key_attributes(struct obj_attrs **out, + struct obj_attrs *temp) +{ + uint32_t const *mandated = NULL; + uint32_t const *optional = NULL; + size_t mandated_count = 0; + size_t optional_count = 0; + enum pkcs11_rc rc = PKCS11_CKR_OK; + + assert(get_class(temp) == PKCS11_CKO_PRIVATE_KEY); + + rc = create_genkey_attributes(out, temp); + if (rc) + return rc; + + assert(get_class(*out) == PKCS11_CKO_PRIVATE_KEY); + + rc = set_mandatory_boolprops(out, temp, pkcs11_private_key_boolprops, + ARRAY_SIZE(pkcs11_private_key_boolprops)); + if (rc) + return rc; + + rc = set_mandatory_attributes(out, temp, pkcs11_private_key_mandated, + ARRAY_SIZE(pkcs11_private_key_mandated)); + if (rc) + return rc; + + rc = set_optional_attributes(out, temp, pkcs11_private_key_optional, + ARRAY_SIZE(pkcs11_private_key_optional)); + if (rc) + return rc; + + switch (get_key_type(*out)) { + case PKCS11_CKK_RSA: + optional = pkcs11_rsa_private_key_optional; + optional_count = ARRAY_SIZE(pkcs11_rsa_private_key_optional); + break; + case PKCS11_CKK_EC: + mandated = pkcs11_ec_private_key_mandated; + optional = pkcs11_ec_private_key_optional; + mandated_count = ARRAY_SIZE(pkcs11_ec_private_key_mandated); + optional_count = ARRAY_SIZE(pkcs11_ec_private_key_optional); + break; + default: + EMSG("Invalid key type %#"PRIx32"/%s", + get_key_type(*out), id2str_key_type(get_key_type(*out))); + + return PKCS11_CKR_TEMPLATE_INCONSISTENT; + } + + rc = set_mandatory_attributes(out, temp, mandated, mandated_count); + if (rc) + return rc; + + return set_optional_attributes(out, temp, optional, optional_count); +} + +/* + * Create an attribute list for a new object from a template and a parent + * object (optional) for an object generation function (generate, copy, + * derive...). + * + * PKCS#11 directives on the supplied template and expected return value: + * - template has an invalid attribute ID: ATTRIBUTE_TYPE_INVALID + * - template has an invalid value for an attribute: ATTRIBUTE_VALID_INVALID + * - template has value for a read-only attribute: ATTRIBUTE_READ_ONLY + * - template+default+parent => still miss an attribute: TEMPLATE_INCONSISTENT + * + * INFO on PKCS11_CMD_COPY_OBJECT: + * - parent PKCS11_CKA_COPYIABLE=false => return ACTION_PROHIBITED. + * - template can specify PKCS11_CKA_TOKEN, PKCS11_CKA_PRIVATE, + * PKCS11_CKA_MODIFIABLE, PKCS11_CKA_DESTROYABLE. + * - SENSITIVE can change from false to true, not from true to false. + * - LOCAL is the parent LOCAL + */ +enum pkcs11_rc +create_attributes_from_template(struct obj_attrs **out, void *template, + size_t template_size, + struct obj_attrs *parent __unused, + enum processing_func function, + enum pkcs11_mechanism_id mecha __unused) +{ + struct obj_attrs *temp = NULL; + struct obj_attrs *attrs = NULL; + enum pkcs11_rc rc = PKCS11_CKR_OK; + uint8_t local = 0; + uint8_t always_sensitive = 0; + uint8_t never_extract = 0; + uint32_t mechanism_id = PKCS11_CKM_UNDEFINED_ID; + +#ifdef DEBUG /* Sanity: check function argument */ + trace_attributes_from_api_head("template", template, template_size); + switch (function) { + case PKCS11_FUNCTION_IMPORT: + break; + default: + TEE_Panic(TEE_ERROR_NOT_SUPPORTED); + } +#endif + + rc = sanitize_client_object(&temp, template, template_size); + if (rc) + goto out; + + /* If class/type not defined, match from mechanism */ + if (get_class(temp) == PKCS11_UNDEFINED_ID && + get_key_type(temp) == PKCS11_UNDEFINED_ID) { + EMSG("Unable to define class/type from mechanism"); + rc = PKCS11_CKR_TEMPLATE_INCOMPLETE; + goto out; + } + + if (!sanitize_consistent_class_and_type(temp)) { + EMSG("Inconsistent class/type"); + rc = PKCS11_CKR_TEMPLATE_INCONSISTENT; + goto out; + } + + switch (get_class(temp)) { + case PKCS11_CKO_DATA: + rc = create_data_attributes(&attrs, temp); + break; + case PKCS11_CKO_SECRET_KEY: + rc = create_symm_key_attributes(&attrs, temp); + break; + case PKCS11_CKO_PUBLIC_KEY: + rc = create_pub_key_attributes(&attrs, temp); + break; + case PKCS11_CKO_PRIVATE_KEY: + rc = create_priv_key_attributes(&attrs, temp); + break; + default: + DMSG("Invalid object class %#"PRIx32"/%s", + get_class(temp), id2str_class(get_class(temp))); + + rc = PKCS11_CKR_TEMPLATE_INCONSISTENT; + break; + } + if (rc) + goto out; + + if (get_attribute(attrs, PKCS11_CKA_LOCAL, NULL, NULL) != + PKCS11_RV_NOT_FOUND) + goto out; + + if (get_attribute(attrs, PKCS11_CKA_KEY_GEN_MECHANISM, NULL, NULL) != + PKCS11_RV_NOT_FOUND) + goto out; + + switch (function) { + case PKCS11_FUNCTION_IMPORT: + default: + local = PKCS11_FALSE; + break; + } + rc = add_attribute(&attrs, PKCS11_CKA_LOCAL, &local, sizeof(local)); + if (rc) + goto out; + + switch (get_class(attrs)) { + case PKCS11_CKO_SECRET_KEY: + case PKCS11_CKO_PRIVATE_KEY: + case PKCS11_CKO_PUBLIC_KEY: + always_sensitive = PKCS11_FALSE; + never_extract = PKCS11_FALSE; + + rc = add_attribute(&attrs, PKCS11_CKA_ALWAYS_SENSITIVE, + &always_sensitive, sizeof(always_sensitive)); + if (rc) + goto out; + + rc = add_attribute(&attrs, PKCS11_CKA_NEVER_EXTRACTABLE, + &never_extract, sizeof(never_extract)); + if (rc) + goto out; + + /* Keys mandate attribute PKCS11_CKA_KEY_GEN_MECHANISM */ + mechanism_id = PKCS11_CK_UNAVAILABLE_INFORMATION; + rc = add_attribute(&attrs, PKCS11_CKA_KEY_GEN_MECHANISM, + &mechanism_id, sizeof(mechanism_id)); + if (rc) + goto out; + break; + + default: + break; + } + + *out = attrs; + +#ifdef DEBUG + trace_attributes("object", attrs); +#endif + +out: + TEE_Free(temp); + if (rc) + TEE_Free(attrs); + + return rc; +} + +static enum pkcs11_rc check_attrs_misc_integrity(struct obj_attrs *head) +{ + if (get_bool(head, PKCS11_CKA_NEVER_EXTRACTABLE) && + get_bool(head, PKCS11_CKA_EXTRACTABLE)) { + DMSG("Never/Extractable attributes mismatch %d/%d", + get_bool(head, PKCS11_CKA_NEVER_EXTRACTABLE), + get_bool(head, PKCS11_CKA_EXTRACTABLE)); + + return PKCS11_CKR_TEMPLATE_INCONSISTENT; + } + + if (get_bool(head, PKCS11_CKA_ALWAYS_SENSITIVE) && + !get_bool(head, PKCS11_CKA_SENSITIVE)) { + DMSG("Sensitive/always attributes mismatch %d/%d", + get_bool(head, PKCS11_CKA_SENSITIVE), + get_bool(head, PKCS11_CKA_ALWAYS_SENSITIVE)); + + return PKCS11_CKR_TEMPLATE_INCONSISTENT; + } + + return PKCS11_CKR_OK; +} + +/* + * Check the attributes of a to-be-created object matches the token state + */ +enum pkcs11_rc check_created_attrs_against_token(struct pkcs11_session *session, + struct obj_attrs *head) +{ + enum pkcs11_rc rc = PKCS11_CKR_OK; + + rc = check_attrs_misc_integrity(head); + if (rc) + return rc; + + if (get_bool(head, PKCS11_CKA_TRUSTED) && + !pkcs11_session_is_so(session)) { + DMSG("Can't create trusted object"); + + return PKCS11_CKR_KEY_FUNCTION_NOT_PERMITTED; + } + + if (get_bool(head, PKCS11_CKA_TOKEN) && + !pkcs11_session_is_read_write(session)) { + DMSG("Can't create persistent object"); + + return PKCS11_CKR_SESSION_READ_ONLY; + } + + /* + * TODO: START_DATE and END_DATE: complies with current time? + */ + return PKCS11_CKR_OK; +} + +#define DMSG_BAD_BBOOL(attr, proc, head) \ + do { \ + uint32_t __maybe_unused _attr = (attr); \ + uint8_t __maybe_unused _bvalue = 0; \ + enum pkcs11_rc __maybe_unused _rc = PKCS11_CKR_OK; \ + \ + _rc = get_attribute((head), _attr, &_bvalue, NULL); \ + DMSG("%s issue for %s: %sfound, value %"PRIu8, \ + id2str_attr(_attr), id2str_proc((proc)), \ + _rc ? "not " : "", _bvalue); \ + } while (0) + +static bool __maybe_unused check_attr_bval(uint32_t proc_id __maybe_unused, + struct obj_attrs *head, + uint32_t attribute, bool val) +{ + uint8_t bbool = 0; + uint32_t sz = sizeof(bbool); + + if (!get_attribute(head, attribute, &bbool, &sz) && !!bbool == val) + return true; + + DMSG_BAD_BBOOL(attribute, proc_id, head); + return false; +} + +/* + * Check the attributes of a new secret match the processing/mechanism + * used to create it. + * + * @proc_id - PKCS11_CKM_xxx + * @head - head of the attributes of the to-be-created object. + */ +enum pkcs11_rc check_created_attrs_against_processing(uint32_t proc_id, + struct obj_attrs *head) +{ + /* + * Processings that do not create secrets are not expected to call + * this function which would panic. + */ + switch (proc_id) { + case PKCS11_PROCESSING_IMPORT: + assert(check_attr_bval(proc_id, head, PKCS11_CKA_LOCAL, false)); + break; + default: + TEE_Panic(proc_id); + break; + } + + return PKCS11_CKR_OK; +} diff --git a/ta/pkcs11/src/pkcs11_attributes.h b/ta/pkcs11/src/pkcs11_attributes.h new file mode 100644 index 00000000..05f70049 --- /dev/null +++ b/ta/pkcs11/src/pkcs11_attributes.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2017-2020, Linaro Limited + */ + +#ifndef PKCS11_TA_PKCS11_ATTRIBUTES_H +#define PKCS11_TA_PKCS11_ATTRIBUTES_H + +#include + +#include "serializer.h" + +struct obj_attrs; +struct pkcs11_object; +struct pkcs11_session; + +/* + * PKCS#11 directives on object attributes. + * Those with a '*' are optional, other must be defined, either by caller + * or by some known default value. + * + * [all] objects: class + * + * [stored] objects: persistent, need_authen, modifiable, copyable, + * destroyable, label*. + * + * [data] objects: [all], [stored], application_id*, object_id*, value. + * + * [key] objects: [all], [stored], type, id*, start_date/end_date*, + * derive, local, allowed_mechanisms*. + * + * [symm-key]: [key], sensitive, encrypt, decrypt, sign, verify, wrap, + * unwrap, extractable, wrap_with_trusted, trusted, + * wrap_template, unwrap_template, derive_template. + */ + +/* + * Utils to check compliance of attributes at various processing steps. + * Any processing operation is exclusively one of the following. + * + * Case 1: Create a secret from some local random value (C_CreateKey & friends) + * - client provides an attributes list template, PKCS11 TA completes with + * default attribute values. Object is created if attributes are + * consistent and comply token/session state. + * - PKCS11 sequence: + * - check/set token/session state + * - create an attribute list from client template and default values. + * - check new secret attributes complies requested mechanism. + * - check new secret attributes complies token/session state. + * - Generate the value for the secret. + * - Set some runtime attributes in the new secret. + * - Register the new secret and return a handle for it. + * + * Case 2: Create a secret from a client clear data (C_CreateObject) + * - client provides an attributes list template, PKCS11 TA completes with + * default attribute values. Object is created if attributes are + * consistent and comply token/session state. + * - check/set token/session state + * - create an attribute list from client template and default values. + * - check new secret attributes complies requested mechanism (raw-import). + * - check new secret attributes complies token/session state. + * - Set some runtime attributes in the new secret. + * - Register the new secret and return a handle for it. + + * Case 3: Use a secret for data processing + * - client provides a mechanism ID and the secret handle. + * - PKCS11 checks mechanism and secret comply, if mechanism and token/session + * state comply and last if secret and token/session state comply. + * - check/set token/session state + * - check secret's parent attributes complies requested processing. + * - check secret's parent attributes complies token/session state. + * - check new secret attributes complies secret's parent attributes. + * - check new secret attributes complies requested mechanism. + * - check new secret attributes complies token/session state. + * + * Case 4: Create a secret from a client template and a secret's parent + * (i.e derive a symmetric key) + * - client args: new-key template, mechanism ID, parent-key handle. + * - PKCS11 create a new-key attribute list based on template + default values + + * inheritance from the parent key attributes. + * - PKCS11 checks: + * - token/session state + * - parent-key vs mechanism + * - parent-key vs token/session state + * - parent-key vs new-key + * - new-key vs mechanism + * - new-key vs token/session state + * - then do processing + * - then finalize object creation + */ + +enum processing_func { + PKCS11_FUNCTION_DIGEST, + PKCS11_FUNCTION_GENERATE, + PKCS11_FUNCTION_GENERATE_PAIR, + PKCS11_FUNCTION_DERIVE, + PKCS11_FUNCTION_WRAP, + PKCS11_FUNCTION_UNWRAP, + PKCS11_FUNCTION_ENCRYPT, + PKCS11_FUNCTION_DECRYPT, + PKCS11_FUNCTION_SIGN, + PKCS11_FUNCTION_VERIFY, + PKCS11_FUNCTION_SIGN_RECOVER, + PKCS11_FUNCTION_VERIFY_RECOVER, + PKCS11_FUNCTION_IMPORT, + PKCS11_FUNCTION_COPY, + PKCS11_FUNCTION_MODIFY, + PKCS11_FUNCTION_DESTROY, +}; + +enum processing_step { + PKCS11_FUNC_STEP_INIT, + PKCS11_FUNC_STEP_ONESHOT, + PKCS11_FUNC_STEP_UPDATE, + PKCS11_FUNC_STEP_FINAL, +}; + +/* Create an attribute list for a new object */ +enum pkcs11_rc +create_attributes_from_template(struct obj_attrs **out, void *template, + size_t template_size, struct obj_attrs *parent, + enum processing_func func, + enum pkcs11_mechanism_id proc_mecha); + +/* + * The various checks to be performed before a processing: + * - create a new object in the current token state + * - use a parent object in the processing + * - use a mechanism with provided configuration + */ +enum pkcs11_rc check_created_attrs_against_token(struct pkcs11_session *session, + struct obj_attrs *head); + +enum pkcs11_rc check_created_attrs_against_processing(uint32_t proc_id, + struct obj_attrs *head); + +#endif /*PKCS11_TA_PKCS11_ATTRIBUTES_H*/ diff --git a/ta/pkcs11/src/pkcs11_helpers.c b/ta/pkcs11/src/pkcs11_helpers.c index 5ae482f1..72057ad7 100644 --- a/ta/pkcs11/src/pkcs11_helpers.c +++ b/ta/pkcs11/src/pkcs11_helpers.c @@ -8,10 +8,84 @@ #include #include +#include "attributes.h" +#include "pkcs11_attributes.h" #include "pkcs11_helpers.h" static const char __maybe_unused unknown[] = ""; +struct attr_size { + uint32_t id; + uint32_t size; +#if CFG_TEE_TA_LOG_LEVEL > 0 + const char *string; +#endif +}; + +#if CFG_TEE_TA_LOG_LEVEL > 0 +#define PKCS11_ID_SZ(_id, _sz) \ + { .id = (uint32_t)(_id), .size = (_sz), .string = #_id } +#else +#define PKCS11_ID_SZ(_id, _sz) \ + { .id = (uint32_t)(_id), .size = (_sz) } +#endif + +static const struct attr_size attr_ids[] = { + PKCS11_ID_SZ(PKCS11_CKA_CLASS, 4), + PKCS11_ID_SZ(PKCS11_CKA_KEY_TYPE, 4), + PKCS11_ID_SZ(PKCS11_CKA_VALUE, 0), + PKCS11_ID_SZ(PKCS11_CKA_VALUE_LEN, 4), + PKCS11_ID_SZ(PKCS11_CKA_LABEL, 0), + PKCS11_ID_SZ(PKCS11_CKA_WRAP_TEMPLATE, 0), + PKCS11_ID_SZ(PKCS11_CKA_UNWRAP_TEMPLATE, 0), + PKCS11_ID_SZ(PKCS11_CKA_DERIVE_TEMPLATE, 0), + PKCS11_ID_SZ(PKCS11_CKA_START_DATE, 4), + PKCS11_ID_SZ(PKCS11_CKA_END_DATE, 4), + PKCS11_ID_SZ(PKCS11_CKA_OBJECT_ID, 0), + PKCS11_ID_SZ(PKCS11_CKA_APPLICATION, 0), + PKCS11_ID_SZ(PKCS11_CKA_MECHANISM_TYPE, 4), + PKCS11_ID_SZ(PKCS11_CKA_ID, 0), + PKCS11_ID_SZ(PKCS11_CKA_ALLOWED_MECHANISMS, 0), + PKCS11_ID_SZ(PKCS11_CKA_EC_POINT, 0), + PKCS11_ID_SZ(PKCS11_CKA_EC_PARAMS, 0), + PKCS11_ID_SZ(PKCS11_CKA_MODULUS, 0), + PKCS11_ID_SZ(PKCS11_CKA_MODULUS_BITS, 4), + PKCS11_ID_SZ(PKCS11_CKA_PUBLIC_EXPONENT, 0), + PKCS11_ID_SZ(PKCS11_CKA_PRIVATE_EXPONENT, 0), + PKCS11_ID_SZ(PKCS11_CKA_PRIME_1, 0), + PKCS11_ID_SZ(PKCS11_CKA_PRIME_2, 0), + PKCS11_ID_SZ(PKCS11_CKA_EXPONENT_1, 0), + PKCS11_ID_SZ(PKCS11_CKA_EXPONENT_2, 0), + PKCS11_ID_SZ(PKCS11_CKA_COEFFICIENT, 0), + PKCS11_ID_SZ(PKCS11_CKA_SUBJECT, 0), + PKCS11_ID_SZ(PKCS11_CKA_PUBLIC_KEY_INFO, 0), + /* Below are boolean attributes */ + PKCS11_ID_SZ(PKCS11_CKA_TOKEN, 1), + PKCS11_ID_SZ(PKCS11_CKA_PRIVATE, 1), + PKCS11_ID_SZ(PKCS11_CKA_TRUSTED, 1), + PKCS11_ID_SZ(PKCS11_CKA_SENSITIVE, 1), + PKCS11_ID_SZ(PKCS11_CKA_ENCRYPT, 1), + PKCS11_ID_SZ(PKCS11_CKA_DECRYPT, 1), + PKCS11_ID_SZ(PKCS11_CKA_WRAP, 1), + PKCS11_ID_SZ(PKCS11_CKA_UNWRAP, 1), + PKCS11_ID_SZ(PKCS11_CKA_SIGN, 1), + PKCS11_ID_SZ(PKCS11_CKA_SIGN_RECOVER, 1), + PKCS11_ID_SZ(PKCS11_CKA_VERIFY, 1), + PKCS11_ID_SZ(PKCS11_CKA_VERIFY_RECOVER, 1), + PKCS11_ID_SZ(PKCS11_CKA_DERIVE, 1), + PKCS11_ID_SZ(PKCS11_CKA_EXTRACTABLE, 1), + PKCS11_ID_SZ(PKCS11_CKA_LOCAL, 1), + PKCS11_ID_SZ(PKCS11_CKA_NEVER_EXTRACTABLE, 1), + PKCS11_ID_SZ(PKCS11_CKA_ALWAYS_SENSITIVE, 1), + PKCS11_ID_SZ(PKCS11_CKA_MODIFIABLE, 1), + PKCS11_ID_SZ(PKCS11_CKA_COPYABLE, 1), + PKCS11_ID_SZ(PKCS11_CKA_DESTROYABLE, 1), + PKCS11_ID_SZ(PKCS11_CKA_ALWAYS_AUTHENTICATE, 1), + PKCS11_ID_SZ(PKCS11_CKA_WRAP_WITH_TRUSTED, 1), + /* Specific PKCS11 TA internal attribute ID */ + PKCS11_ID_SZ(PKCS11_CKA_UNDEFINED_ID, 0), +}; + struct any_id { uint32_t id; #if CFG_TEE_TA_LOG_LEVEL > 0 @@ -168,6 +242,45 @@ static const struct any_id __maybe_unused string_rc[] = { PKCS11_ID(PKCS11_RV_NOT_IMPLEMENTED), }; +static const struct any_id __maybe_unused string_class[] = { + PKCS11_ID(PKCS11_CKO_SECRET_KEY), + PKCS11_ID(PKCS11_CKO_PUBLIC_KEY), + PKCS11_ID(PKCS11_CKO_PRIVATE_KEY), + PKCS11_ID(PKCS11_CKO_OTP_KEY), + PKCS11_ID(PKCS11_CKO_CERTIFICATE), + PKCS11_ID(PKCS11_CKO_DATA), + PKCS11_ID(PKCS11_CKO_DOMAIN_PARAMETERS), + PKCS11_ID(PKCS11_CKO_HW_FEATURE), + PKCS11_ID(PKCS11_CKO_MECHANISM), + PKCS11_ID(PKCS11_CKO_UNDEFINED_ID) +}; + +static const struct any_id __maybe_unused string_key_type[] = { + PKCS11_ID(PKCS11_CKK_AES), + PKCS11_ID(PKCS11_CKK_GENERIC_SECRET), + PKCS11_ID(PKCS11_CKK_MD5_HMAC), + PKCS11_ID(PKCS11_CKK_SHA_1_HMAC), + PKCS11_ID(PKCS11_CKK_SHA224_HMAC), + PKCS11_ID(PKCS11_CKK_SHA256_HMAC), + PKCS11_ID(PKCS11_CKK_SHA384_HMAC), + PKCS11_ID(PKCS11_CKK_SHA512_HMAC), + PKCS11_ID(PKCS11_CKK_EC), + PKCS11_ID(PKCS11_CKK_RSA), + PKCS11_ID(PKCS11_CKK_UNDEFINED_ID) +}; + +/* + * Processing IDs not exported in the TA API. + * PKCS11_CKM_* mechanism IDs are looked up from mechanism_string_id(). + */ +static const struct any_id __maybe_unused string_internal_processing[] = { + PKCS11_ID(PKCS11_PROCESSING_IMPORT), +}; + +static const struct any_id __maybe_unused string_functions[] = { + PKCS11_ID(PKCS11_FUNCTION_IMPORT), +}; + /* * Conversion between PKCS11 TA and GPD TEE return codes */ @@ -195,6 +308,138 @@ enum pkcs11_rc tee2pkcs_error(TEE_Result res) } } +/* + * Helper functions to analyse PKCS11 identifiers + */ + +/* Check attribute ID is known and size matches if fixed */ +bool valid_pkcs11_attribute_id(uint32_t id, uint32_t size) +{ + size_t n = 0; + + for (n = 0; n < ARRAY_SIZE(attr_ids); n++) + if (id == attr_ids[n].id) + return !attr_ids[n].size || size == attr_ids[n].size; + + return false; +} + +size_t pkcs11_attr_is_type(uint32_t attribute_id) +{ + enum pkcs11_attr_id id = attribute_id; + + switch (id) { + case PKCS11_CKA_KEY_TYPE: + case PKCS11_CKA_MECHANISM_TYPE: + case PKCS11_CKA_KEY_GEN_MECHANISM: + return sizeof(uint32_t); + default: + return 0; + } +} + +bool pkcs11_class_has_type(uint32_t class) +{ + enum pkcs11_class_id class_id = class; + + switch (class_id) { + case PKCS11_CKO_CERTIFICATE: + case PKCS11_CKO_PUBLIC_KEY: + case PKCS11_CKO_PRIVATE_KEY: + case PKCS11_CKO_SECRET_KEY: + case PKCS11_CKO_MECHANISM: + case PKCS11_CKO_HW_FEATURE: + return true; + default: + return false; + } +} + +bool pkcs11_attr_class_is_key(uint32_t class) +{ + enum pkcs11_class_id class_id = class; + + switch (class_id) { + case PKCS11_CKO_SECRET_KEY: + case PKCS11_CKO_PUBLIC_KEY: + case PKCS11_CKO_PRIVATE_KEY: + return true; + default: + return false; + } +} + +bool key_type_is_symm_key(uint32_t id) +{ + enum pkcs11_key_type key_type = id; + + switch (key_type) { + case PKCS11_CKK_AES: + case PKCS11_CKK_GENERIC_SECRET: + case PKCS11_CKK_MD5_HMAC: + case PKCS11_CKK_SHA_1_HMAC: + case PKCS11_CKK_SHA224_HMAC: + case PKCS11_CKK_SHA256_HMAC: + case PKCS11_CKK_SHA384_HMAC: + case PKCS11_CKK_SHA512_HMAC: + return true; + default: + return false; + } +} + +bool key_type_is_asymm_key(uint32_t id) +{ + enum pkcs11_key_type key_type = id; + + switch (key_type) { + case PKCS11_CKK_EC: + case PKCS11_CKK_RSA: + return true; + default: + return false; + } +} + +/* + * Returns shift position or -1 on error. + * Mainly used when PKCS11_SHEAD_WITH_BOOLPROPS is enabled + */ +int pkcs11_attr2boolprop_shift(uint32_t attr) +{ + static const uint32_t bpa[] = { + [BPA_TOKEN] = PKCS11_CKA_TOKEN, + [BPA_PRIVATE] = PKCS11_CKA_PRIVATE, + [BPA_TRUSTED] = PKCS11_CKA_TRUSTED, + [BPA_SENSITIVE] = PKCS11_CKA_SENSITIVE, + [BPA_ENCRYPT] = PKCS11_CKA_ENCRYPT, + [BPA_DECRYPT] = PKCS11_CKA_DECRYPT, + [BPA_WRAP] = PKCS11_CKA_WRAP, + [BPA_UNWRAP] = PKCS11_CKA_UNWRAP, + [BPA_SIGN] = PKCS11_CKA_SIGN, + [BPA_SIGN_RECOVER] = PKCS11_CKA_SIGN_RECOVER, + [BPA_VERIFY] = PKCS11_CKA_VERIFY, + [BPA_VERIFY_RECOVER] = PKCS11_CKA_VERIFY_RECOVER, + [BPA_DERIVE] = PKCS11_CKA_DERIVE, + [BPA_EXTRACTABLE] = PKCS11_CKA_EXTRACTABLE, + [BPA_LOCAL] = PKCS11_CKA_LOCAL, + [BPA_NEVER_EXTRACTABLE] = PKCS11_CKA_NEVER_EXTRACTABLE, + [BPA_ALWAYS_SENSITIVE] = PKCS11_CKA_ALWAYS_SENSITIVE, + [BPA_MODIFIABLE] = PKCS11_CKA_MODIFIABLE, + [BPA_COPYABLE] = PKCS11_CKA_COPYABLE, + [BPA_DESTROYABLE] = PKCS11_CKA_DESTROYABLE, + [BPA_ALWAYS_AUTHENTICATE] = PKCS11_CKA_ALWAYS_AUTHENTICATE, + [BPA_WRAP_WITH_TRUSTED] = PKCS11_CKA_WRAP_WITH_TRUSTED, + }; + size_t pos = 0; + + for (pos = 0; pos < ARRAY_SIZE(bpa); pos++) + if (bpa[pos] == attr) + return (int)pos; + + return -1; +} + #if CFG_TEE_TA_LOG_LEVEL > 0 const char *id2str_rc(uint32_t id) { @@ -225,4 +470,86 @@ const char *id2str_session_state(uint32_t id) { return ID2STR(id, string_session_state, "PKCS11_CKS_"); } + +const char *id2str_attr(uint32_t id) +{ + size_t n = 0; + + for (n = 0; n < ARRAY_SIZE(attr_ids); n++) { + if (id == attr_ids[n].id) { + /* Skip PKCS11_CKA_ prefix */ + return attr_ids[n].string + strlen("PKCS11_CKA_"); + } + } + + return unknown; +} + +const char *id2str_class(uint32_t id) +{ + return ID2STR(id, string_class, "PKCS11_CKO_"); +} + +const char *id2str_type(uint32_t id, uint32_t class) +{ + enum pkcs11_class_id class_id = class; + enum pkcs11_key_type key_type = id; + + switch (class_id) { + case PKCS11_CKO_SECRET_KEY: + case PKCS11_CKO_PUBLIC_KEY: + case PKCS11_CKO_PRIVATE_KEY: + return id2str_key_type(key_type); + default: + return unknown; + } +} + +const char *id2str_key_type(uint32_t id) +{ + return ID2STR(id, string_key_type, "PKCS11_CKK_"); +} + +const char *id2str_attr_value(uint32_t id, size_t size, void *value) +{ + static const char str_true[] = "TRUE"; + static const char str_false[] = "FALSE"; + static const char str_unknown[] = "*"; + uint32_t type = 0; + + if (pkcs11_attr2boolprop_shift(id) >= 0) + return *(uint8_t *)value ? str_true : str_false; + + if (size < sizeof(uint32_t)) + return str_unknown; + + TEE_MemMove(&type, value, sizeof(uint32_t)); + + switch (id) { + case PKCS11_CKA_CLASS: + return id2str_class(type); + case PKCS11_CKA_KEY_TYPE: + return id2str_key_type(type); + case PKCS11_CKA_MECHANISM_TYPE: + return id2str_mechanism(type); + default: + return str_unknown; + } +} + +const char *id2str_proc(uint32_t id) +{ + const char *str = ID2STR(id, string_internal_processing, + "PKCS11_PROCESSING_"); + + if (str != unknown) + return str; + + return id2str_mechanism(id); +} + +const char *id2str_function(uint32_t id) +{ + return ID2STR(id, string_functions, "PKCS11_FUNCTION_"); +} #endif /*CFG_TEE_TA_LOG_LEVEL*/ diff --git a/ta/pkcs11/src/pkcs11_helpers.h b/ta/pkcs11/src/pkcs11_helpers.h index e1a2b467..a87353f8 100644 --- a/ta/pkcs11/src/pkcs11_helpers.h +++ b/ta/pkcs11/src/pkcs11_helpers.h @@ -20,6 +20,42 @@ /* GPD TEE to PKCS11 status conversion */ enum pkcs11_rc tee2pkcs_error(TEE_Result res); +/* + * Return true if and only if attribute ID with companion attribute value + * size do match a valid attribute identifier. + * + * @attribute_id - Target PKCS11 attribute ID + * @size - Byte size of the attribute value, 0 if non-constant size + */ +bool valid_pkcs11_attribute_id(uint32_t attribute_id, uint32_t size); + +/* + * Return type attribute byte size if @attribute_id is the ID of a type + * attribute or 0 if not. + */ +size_t pkcs11_attr_is_type(uint32_t attribute_id); + +/* Return true if the object class is related to a type-in-class */ +bool pkcs11_class_has_type(uint32_t class_id); + +/* Return true if the object class relates to a key */ +bool pkcs11_attr_class_is_key(uint32_t class_id); + +/* Return true if the key type @key_type_id relates to a symmetric key */ +bool key_type_is_symm_key(uint32_t key_type_id); + +/* Return true if the key type @key_type_id relates to an asymmetric key */ +bool key_type_is_asymm_key(uint32_t key_type_id); + +/* Boolprop flag shift position if @attribute_id is boolean, else -1 */ +int pkcs11_attr2boolprop_shift(uint32_t attribute_id); + +/* Return true if attribute is a boolean, false otherwise */ +static inline bool pkcs11_attr_is_boolean(enum pkcs11_attr_id id) +{ + return pkcs11_attr2boolprop_shift(id) >= 0; +} + #if CFG_TEE_TA_LOG_LEVEL > 0 /* Id-to-string conversions only for trace support */ const char *id2str_ta_cmd(uint32_t id); @@ -28,6 +64,13 @@ const char *id2str_slot_flag(uint32_t id); const char *id2str_token_flag(uint32_t id); const char *id2str_session_flag(uint32_t id); const char *id2str_session_state(uint32_t id); +const char *id2str_attr(uint32_t id); +const char *id2str_class(uint32_t id); +const char *id2str_type(uint32_t id, uint32_t class); +const char *id2str_key_type(uint32_t id); +const char *id2str_attr_value(uint32_t id, size_t size, void *value); +const char *id2str_proc(uint32_t id); +const char *id2str_function(uint32_t id); static inline const char *id2str_mechanism(enum pkcs11_mechanism_id id) { diff --git a/ta/pkcs11/src/sanitize_object.c b/ta/pkcs11/src/sanitize_object.c new file mode 100644 index 00000000..de9eaa98 --- /dev/null +++ b/ta/pkcs11/src/sanitize_object.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2017-2020, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "attributes.h" +#include "pkcs11_helpers.h" +#include "sanitize_object.h" +#include "serializer.h" +#include "token_capabilities.h" + +/* + * Functions to generate a serialized object. + * References are pointers to struct serializer. + */ + +bool sanitize_consistent_class_and_type(struct obj_attrs *attrs) +{ + switch (get_class(attrs)) { + case PKCS11_CKO_DATA: + return true; + case PKCS11_CKO_SECRET_KEY: + return key_type_is_symm_key(get_key_type(attrs)); + case PKCS11_CKO_MECHANISM: + return mechanism_is_valid(get_mechanism_type(attrs)); + case PKCS11_CKO_PUBLIC_KEY: + case PKCS11_CKO_PRIVATE_KEY: + return key_type_is_asymm_key(get_key_type(attrs)); + case PKCS11_CKO_OTP_KEY: + case PKCS11_CKO_CERTIFICATE: + case PKCS11_CKO_DOMAIN_PARAMETERS: + case PKCS11_CKO_HW_FEATURE: + default: + return false; + } + + return false; +} + +static enum pkcs11_rc read_attr_advance(void *buf, size_t blen, size_t *pos, + struct pkcs11_attribute_head *attr, + void **data) +{ + uint8_t *b = buf; + size_t data_pos = 0; + size_t next_pos = 0; + + if (ADD_OVERFLOW(*pos, sizeof(*attr), &data_pos) || data_pos > blen) + return PKCS11_CKR_FUNCTION_FAILED; + TEE_MemMove(attr, b + *pos, sizeof(*attr)); + + if (ADD_OVERFLOW(data_pos, attr->size, &next_pos) || next_pos > blen) + return PKCS11_CKR_FUNCTION_FAILED; + + *data = b + data_pos; + *pos = next_pos; + + return PKCS11_CKR_OK; +} + +/* Sanitize class/type in a client attribute list */ +static enum pkcs11_rc sanitize_class_and_type(struct obj_attrs **dst, void *src, + size_t src_size) +{ + uint32_t class_found = PKCS11_CKO_UNDEFINED_ID; + size_t pos = sizeof(struct pkcs11_object_head); + struct pkcs11_attribute_head cli_ref = { }; + uint32_t type_found = PKCS11_UNDEFINED_ID; + enum pkcs11_rc rc = PKCS11_CKR_OK; + void *data = NULL; + + while (pos != src_size) { + rc = read_attr_advance(src, src_size, &pos, &cli_ref, &data); + if (rc) + goto err; + + if (cli_ref.id == PKCS11_CKA_CLASS) { + uint32_t class = 0; + + if (cli_ref.size != sizeof(class)) { + rc = PKCS11_CKR_TEMPLATE_INCONSISTENT; + goto err; + } + + TEE_MemMove(&class, data, sizeof(class)); + + if (class_found != PKCS11_CKO_UNDEFINED_ID && + class_found != class) { + EMSG("Conflicting class value"); + rc = PKCS11_CKR_TEMPLATE_INCONSISTENT; + goto err; + } + + class_found = class; + continue; + } + + /* The attribute is a type-in-class */ + if (pkcs11_attr_is_type(cli_ref.id)) { + uint32_t type = 0; + + if (cli_ref.size != sizeof(type)) { + rc = PKCS11_CKR_TEMPLATE_INCONSISTENT; + goto err; + } + + TEE_MemMove(&type, data, sizeof(type)); + + if (type_found != PKCS11_CKK_UNDEFINED_ID && + type_found != type) { + EMSG("Conflicting type-in-class value"); + rc = PKCS11_CKR_TEMPLATE_INCONSISTENT; + goto err; + } + + type_found = type; + } + } + + if (class_found != PKCS11_CKO_UNDEFINED_ID) { + rc = add_attribute(dst, PKCS11_CKA_CLASS, + &class_found, sizeof(class_found)); + if (rc) + return rc; + } + + if (type_found != PKCS11_UNDEFINED_ID) { + rc = add_attribute(dst, PKCS11_CKA_KEY_TYPE, + &type_found, sizeof(type_found)); + if (rc) + return rc; + } + + return PKCS11_CKR_OK; + +err: + trace_attributes_from_api_head("bad-template", src, src_size); + + return rc; +} + +static enum pkcs11_rc sanitize_boolprops(struct obj_attrs **dst, void *src, + size_t src_size) +{ + bitstr_t bit_decl(seen_attrs, PKCS11_BOOLPROPS_MAX_COUNT) = { 0 }; + bitstr_t bit_decl(boolprops, PKCS11_BOOLPROPS_MAX_COUNT) = { 0 }; + size_t pos = sizeof(struct pkcs11_object_head); + struct pkcs11_attribute_head cli_ref = { }; + enum pkcs11_rc rc = PKCS11_CKR_OK; + bool value = false; + void *data = NULL; + int idx = 0; + + /* + * We're keeping track of seen boolean attributes in the bitstring + * seen_attrs. The bitstring boolprops holds the recorded value + * once seen_attrs has been updated. + */ + + while (pos != src_size) { + rc = read_attr_advance(src, src_size, &pos, &cli_ref, &data); + if (rc) + return rc; + + idx = pkcs11_attr2boolprop_shift(cli_ref.id); + if (idx < 0) + continue; /* skipping non-boolean attributes */ + + if (idx >= PKCS11_BOOLPROPS_MAX_COUNT || + cli_ref.size != sizeof(uint8_t)) + return PKCS11_CKR_FUNCTION_FAILED; + + value = *(uint8_t *)data; + + /* + * If this attribute has already been seen, check that it + * still holds the same value as last time. + */ + if (bit_test(seen_attrs, idx) && + value != (bool)bit_test(boolprops, idx)) + return PKCS11_CKR_TEMPLATE_INCONSISTENT; + + if (value) + bit_set(boolprops, idx); + + if (!bit_test(seen_attrs, idx)) { + uint8_t pkcs11_bool = value; + + rc = add_attribute(dst, cli_ref.id, &pkcs11_bool, + sizeof(pkcs11_bool)); + if (rc) + return rc; + } + bit_set(seen_attrs, idx); + } + + return PKCS11_CKR_OK; +} + +static uint32_t sanitize_indirect_attr(struct obj_attrs **dst, + struct pkcs11_attribute_head *cli_ref, + char *data) +{ + struct obj_attrs *obj2 = NULL; + enum pkcs11_rc rc = PKCS11_CKR_OK; + enum pkcs11_class_id class = get_class(*dst); + + if (class == PKCS11_CKO_UNDEFINED_ID) + return PKCS11_CKR_GENERAL_ERROR; + + /* + * Serialized attributes: current applicable only to the key + * templates which are tables of attributes. + */ + switch (cli_ref->id) { + case PKCS11_CKA_WRAP_TEMPLATE: + case PKCS11_CKA_UNWRAP_TEMPLATE: + case PKCS11_CKA_DERIVE_TEMPLATE: + break; + default: + return PKCS11_RV_NOT_FOUND; + } + /* Such attributes are expected only for keys (and vendor defined) */ + if (pkcs11_attr_class_is_key(class)) + return PKCS11_CKR_TEMPLATE_INCONSISTENT; + + rc = init_attributes_head(&obj2); + if (rc) + return rc; + + /* Build a new serial object while sanitizing the attributes list */ + rc = sanitize_client_object(&obj2, data, cli_ref->size); + if (rc) + goto out; + + rc = add_attribute(dst, cli_ref->id, obj2, + sizeof(*obj2) + obj2->attrs_size); +out: + TEE_Free(obj2); + return rc; +} + +enum pkcs11_rc sanitize_client_object(struct obj_attrs **dst, void *src, + size_t size) +{ + struct pkcs11_attribute_head cli_ref = { }; + struct pkcs11_object_head head = { }; + enum pkcs11_rc rc = PKCS11_CKR_OK; + size_t pos = sizeof(head); + size_t sz_from_hdr = 0; + void *data = NULL; + + if (size < sizeof(head)) + return PKCS11_CKR_ARGUMENTS_BAD; + + TEE_MemMove(&head, src, sizeof(head)); + + if (ADD_OVERFLOW(sizeof(head), head.attrs_size, &sz_from_hdr) || + size < sz_from_hdr) + return PKCS11_CKR_ARGUMENTS_BAD; + + rc = init_attributes_head(dst); + if (rc) + return rc; + + rc = sanitize_class_and_type(dst, src, sz_from_hdr); + if (rc) + return rc; + + rc = sanitize_boolprops(dst, src, sz_from_hdr); + if (rc) + return rc; + + while (pos != sz_from_hdr) { + rc = read_attr_advance(src, sz_from_hdr, &pos, &cli_ref, &data); + if (rc) + return rc; + + if (cli_ref.id == PKCS11_CKA_CLASS || + pkcs11_attr_is_type(cli_ref.id) || + pkcs11_attr_is_boolean(cli_ref.id)) + continue; + + rc = sanitize_indirect_attr(dst, &cli_ref, data); + if (rc == PKCS11_CKR_OK) + continue; + if (rc != PKCS11_RV_NOT_FOUND) + return rc; + + if (!valid_pkcs11_attribute_id(cli_ref.id, cli_ref.size)) { + EMSG("Invalid attribute id %#"PRIx32, cli_ref.id); + return PKCS11_CKR_TEMPLATE_INCONSISTENT; + } + + rc = add_attribute(dst, cli_ref.id, data, cli_ref.size); + if (rc) + return rc; + } + + return rc; +} + +/* + * Debug: dump object attribute array to output trace + */ + +static void __trace_attributes(char *prefix, void *src, void *end) +{ + size_t next = 0; + char *prefix2 = NULL; + size_t prefix_len = strlen(prefix); + char *cur = src; + + /* append 4 spaces to the prefix plus terminal '\0' */ + prefix2 = TEE_Malloc(prefix_len + 1 + 4, TEE_MALLOC_FILL_ZERO); + if (!prefix2) + return; + + TEE_MemMove(prefix2, prefix, prefix_len + 1); + TEE_MemFill(prefix2 + prefix_len, ' ', 4); + *(prefix2 + prefix_len + 4) = '\0'; + + for (; cur < (char *)end; cur += next) { + struct pkcs11_attribute_head pkcs11_ref; + uint8_t data[4] = { 0 }; + uint32_t data_u32 = 0; + + TEE_MemMove(&pkcs11_ref, cur, sizeof(pkcs11_ref)); + TEE_MemMove(&data[0], cur + sizeof(pkcs11_ref), + MIN(pkcs11_ref.size, sizeof(data))); + TEE_MemMove(&data_u32, cur + sizeof(pkcs11_ref), + sizeof(data_u32)); + + next = sizeof(pkcs11_ref) + pkcs11_ref.size; + + DMSG_RAW("%s Attr %s / %s (%#04"PRIx32" %"PRIu32"-byte)", + prefix, id2str_attr(pkcs11_ref.id), + id2str_attr_value(pkcs11_ref.id, pkcs11_ref.size, + cur + sizeof(pkcs11_ref)), + pkcs11_ref.id, pkcs11_ref.size); + + switch (pkcs11_ref.size) { + case 0: + break; + case 1: + DMSG_RAW("%s Attr byte value: %02x", prefix, data[0]); + break; + case 2: + DMSG_RAW("%s Attr byte value: %02x %02x", + prefix, data[0], data[1]); + break; + case 3: + DMSG_RAW("%s Attr byte value: %02x %02x %02x", + prefix, data[0], data[1], data[2]); + break; + case 4: + DMSG_RAW("%s Attr byte value: %02x %02x %02x %02x", + prefix, data[0], data[1], data[2], data[3]); + break; + default: + DMSG_RAW("%s Attr byte value: %02x %02x %02x %02x ...", + prefix, data[0], data[1], data[2], data[3]); + break; + } + + switch (pkcs11_ref.id) { + case PKCS11_CKA_WRAP_TEMPLATE: + case PKCS11_CKA_UNWRAP_TEMPLATE: + case PKCS11_CKA_DERIVE_TEMPLATE: + trace_attributes_from_api_head(prefix2, + cur + sizeof(pkcs11_ref), + (char *)end - cur); + break; + default: + break; + } + } + + /* Sanity */ + if (cur != (char *)end) + EMSG("Warning: unexpected alignment issue"); + + TEE_Free(prefix2); +} + +void trace_attributes_from_api_head(const char *prefix, void *ref, size_t size) +{ + struct pkcs11_object_head head = { }; + char *pre = NULL; + size_t offset = 0; + + TEE_MemMove(&head, ref, sizeof(head)); + + if (size > sizeof(head) + head.attrs_size) { + EMSG("template overflows client buffer (%zu/%zu)", + size, sizeof(head) + head.attrs_size); + return; + } + + pre = TEE_Malloc(prefix ? strlen(prefix) + 2 : 2, TEE_MALLOC_FILL_ZERO); + if (!pre) { + EMSG("%s: out of memory", prefix); + return; + } + if (prefix) + TEE_MemMove(pre, prefix, strlen(prefix)); + + DMSG_RAW("%s,--- (serial object) Attributes list --------", pre); + DMSG_RAW("%s| %"PRIu32" item(s) - %"PRIu32" bytes", + pre, head.attrs_count, head.attrs_size); + + offset = sizeof(head); + pre[prefix ? strlen(prefix) : 0] = '|'; + __trace_attributes(pre, (char *)ref + offset, + (char *)ref + offset + head.attrs_size); + + DMSG_RAW("%s`-----------------------", prefix ? prefix : ""); + + TEE_Free(pre); +} diff --git a/ta/pkcs11/src/sanitize_object.h b/ta/pkcs11/src/sanitize_object.h new file mode 100644 index 00000000..ddbdccef --- /dev/null +++ b/ta/pkcs11/src/sanitize_object.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2017-2020, Linaro Limited + */ + +#ifndef PKCS11_TA_SANITIZE_OBJECT_H +#define PKCS11_TA_SANITIZE_OBJECT_H + +#include "serializer.h" + +/* + * sanitize_consistent_class_and_type - Check object type matches object class + * + * @attrs - object attributes + * Return true if class/type matches, else return false + */ +bool sanitize_consistent_class_and_type(struct obj_attrs *attrs); + +/** + * sanitize_client_object - Setup a serializer from a serialized object + * + * @dst - output structure tracking the generated serial object + * @head - pointer to the formatted serialized object (its head) + * @size - byte size of the serialized binary blob + * + * This function copies an attribute list from a client API attribute head + * into a PKCS11 TA internal attribute structure. It generates a serialized + * attribute list with a consistent format and identified attribute IDs. + * + * @head points to a blob starting with a pkcs11 attribute header. + * @head may point to an unaligned address. + * This function allocates, fills and returns a serialized attribute list + * into a serializer container. + */ +enum pkcs11_rc sanitize_client_object(struct obj_attrs **dst, void *head, + size_t size); + +/* Debug: dump attribute content as debug traces */ +void trace_attributes_from_api_head(const char *prefix, void *ref, size_t size); + +#endif /*PKCS11_TA_SANITIZE_OBJECT_H*/ diff --git a/ta/pkcs11/src/sub.mk b/ta/pkcs11/src/sub.mk index f03ce326..7681c5d6 100644 --- a/ta/pkcs11/src/sub.mk +++ b/ta/pkcs11/src/sub.mk @@ -1,7 +1,10 @@ +srcs-y += attributes.c srcs-y += entry.c srcs-y += handle.c srcs-y += persistent_token.c +srcs-y += pkcs11_attributes.c srcs-y += pkcs11_helpers.c srcs-y += pkcs11_token.c +srcs-y += sanitize_object.c srcs-y += serializer.c srcs-y += token_capabilities.c -- cgit v1.2.3