| /* |
| * Copyright (c) 2020-2024, Arm Limited. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <assert.h> |
| #include <stddef.h> |
| |
| #include <mbedtls/version.h> |
| |
| #include <common/fdt_wrappers.h> |
| #include <common/tbbr/cot_def.h> |
| #include <drivers/auth/auth_mod.h> |
| #include <lib/fconf/fconf.h> |
| #include <lib/object_pool.h> |
| #include <libfdt.h> |
| |
| #include <tools_share/tbbr_oid.h> |
| |
| /* static structures used during authentication process */ |
| static auth_param_type_desc_t sig = AUTH_PARAM_TYPE_DESC( |
| AUTH_PARAM_SIG, 0); |
| static auth_param_type_desc_t sig_alg = AUTH_PARAM_TYPE_DESC( |
| AUTH_PARAM_SIG_ALG, 0); |
| static auth_param_type_desc_t raw_data = AUTH_PARAM_TYPE_DESC( |
| AUTH_PARAM_RAW_DATA, 0); |
| |
| /* pointers to an array of CoT descriptors */ |
| static const auth_img_desc_t *cot_desc[MAX_NUMBER_IDS]; |
| /* array of CoT descriptors */ |
| static auth_img_desc_t auth_img_descs[MAX_NUMBER_IDS]; |
| |
| /* array of authentication methods structures */ |
| static auth_method_desc_t auth_methods[MAX_NUMBER_IDS * AUTH_METHOD_NUM]; |
| static OBJECT_POOL_ARRAY(auth_methods_pool, auth_methods); |
| |
| /* array of authentication params structures */ |
| static auth_param_desc_t auth_params[MAX_NUMBER_IDS * COT_MAX_VERIFIED_PARAMS]; |
| static OBJECT_POOL_ARRAY(auth_params_pool, auth_params); |
| |
| /* array of authentication param type structures */ |
| static auth_param_type_desc_t auth_param_type_descs[MAX_NUMBER_IDS]; |
| static OBJECT_POOL_ARRAY(auth_param_type_descs_pool, auth_param_type_descs); |
| |
| /* |
| * array of OIDs |
| * Object IDs are used to search hash, pk, counter values in certificate. |
| * As per binding we have below 2 combinations: |
| * 1. Certificates are validated using nv-cntr and pk |
| * 2. Raw images are authenticated using hash |
| * Hence in worst case, there are maximum 2 OIDs per image/certificate |
| */ |
| static unsigned char oids[(MAX_NUMBER_IDS * 2)][MAX_OID_NAME_LEN]; |
| static OBJECT_POOL_ARRAY(oid_pool, oids); |
| |
| /* An array of auth buffer which holds hashes and pk |
| * ToDo: Size decided with the current number of images and |
| * certificates which are available in CoT. Size of these buffers bound to |
| * increase in the future on the addition of images/certificates. |
| */ |
| static unsigned char hash_auth_bufs[20][HASH_DER_LEN]; |
| static OBJECT_POOL_ARRAY(hash_auth_buf_pool, hash_auth_bufs); |
| static unsigned char pk_auth_bufs[12][PK_DER_LEN]; |
| static OBJECT_POOL_ARRAY(pk_auth_buf_pool, pk_auth_bufs); |
| |
| /******************************************************************************* |
| * update_parent_auth_data() - Update authentication data structure |
| * @auth_desc[in]: Pointer to the auth image descriptor |
| * @type_desc[in]: Pointer to authentication parameter |
| * @auth_buf_size[in]: Buffer size to hold pk or hash |
| * |
| * Return 0 on success or an error value otherwise. |
| ******************************************************************************/ |
| static int update_parent_auth_data(const auth_img_desc_t *auth_desc, |
| auth_param_type_desc_t *type_desc, |
| unsigned int auth_buf_size) |
| { |
| unsigned int i; |
| auth_param_desc_t *auth_data = &auth_desc->authenticated_data[0]; |
| unsigned char *auth_buf; |
| |
| for (i = 0U; i < COT_MAX_VERIFIED_PARAMS; i++) { |
| if (auth_data[i].type_desc == type_desc) { |
| return 0; |
| } |
| if (auth_data[i].type_desc == NULL) { |
| break; |
| } |
| } |
| |
| if (auth_buf_size == HASH_DER_LEN) { |
| auth_buf = pool_alloc(&hash_auth_buf_pool); |
| } else if (auth_buf_size == PK_DER_LEN) { |
| auth_buf = pool_alloc(&pk_auth_buf_pool); |
| } else { |
| return -1; |
| } |
| |
| if (i < COT_MAX_VERIFIED_PARAMS) { |
| auth_data[i].type_desc = type_desc; |
| auth_data[i].data.ptr = auth_buf; |
| auth_data[i].data.len = auth_buf_size; |
| } else { |
| ERROR("Out of authentication data array\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * get_auth_param_type_desc() - Get pointer of authentication parameter |
| * @img_id[in]: Image Id |
| * @type_desc[out]: Pointer to authentication parameter |
| * @buf_size[out]: Buffer size which hold hash/pk |
| * |
| * Return 0 on success or an error value otherwise. |
| ******************************************************************************/ |
| static int get_auth_param_type_desc(unsigned int img_id, |
| auth_param_type_desc_t **type_desc, |
| unsigned int *buf_size) |
| { |
| auth_method_desc_t *img_auth_method = NULL; |
| img_type_t type = auth_img_descs[img_id].img_type; |
| |
| if (type == IMG_CERT) { |
| img_auth_method = |
| &auth_img_descs[img_id].img_auth_methods[AUTH_METHOD_SIG]; |
| *type_desc = img_auth_method->param.sig.pk; |
| *buf_size = PK_DER_LEN; |
| } else if (type == IMG_RAW) { |
| img_auth_method = |
| &auth_img_descs[img_id].img_auth_methods[AUTH_METHOD_HASH]; |
| *type_desc = img_auth_method->param.hash.hash; |
| *buf_size = HASH_DER_LEN; |
| } else { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * set_auth_method() - Update global auth image descriptors with authentication |
| * method data |
| * @auth_method_type[in]: Type of authentication method |
| * @oid[in]: Object Idetifier for pk/hash search |
| * @auth_method[in]: Pointer to authentication method to set |
| ******************************************************************************/ |
| static void set_auth_method(auth_method_type_t auth_method_type, char *oid, |
| auth_method_desc_t *auth_method) |
| { |
| auth_param_type_t auth_param_type = AUTH_PARAM_NONE; |
| auth_param_type_desc_t *auth_param_type_desc; |
| |
| assert(auth_method != NULL); |
| |
| auth_param_type_desc = pool_alloc(&auth_param_type_descs_pool); |
| auth_method->type = auth_method_type; |
| |
| if (auth_method_type == AUTH_METHOD_SIG) { |
| auth_param_type = AUTH_PARAM_PUB_KEY; |
| auth_method->param.sig.sig = &sig; |
| auth_method->param.sig.alg = &sig_alg; |
| auth_method->param.sig.data = &raw_data; |
| auth_method->param.sig.pk = auth_param_type_desc; |
| } else if (auth_method_type == AUTH_METHOD_HASH) { |
| auth_param_type = AUTH_PARAM_HASH; |
| auth_method->param.hash.data = &raw_data; |
| auth_method->param.hash.hash = auth_param_type_desc; |
| } else if (auth_method_type == AUTH_METHOD_NV_CTR) { |
| auth_param_type = AUTH_PARAM_NV_CTR; |
| auth_method->param.nv_ctr.cert_nv_ctr = auth_param_type_desc; |
| auth_method->param.nv_ctr.plat_nv_ctr = auth_param_type_desc; |
| } |
| |
| auth_param_type_desc->type = auth_param_type; |
| auth_param_type_desc->cookie = (void *)oid; |
| } |
| |
| /******************************************************************************* |
| * get_oid() - get object identifier from device tree |
| * @dtb[in]: Pointer to the device tree blob in memory |
| * @node[in]: Offset of the node |
| * @prop[in]: Property to read from the given node |
| * @oid[out]: Object Indentifier of key/hash/nv-counter in certificate |
| * |
| * Return 0 on success or an error value otherwise. |
| ******************************************************************************/ |
| static int get_oid(const void *dtb, int node, const char *prop, char **oid) |
| { |
| uint32_t phandle; |
| int rc; |
| |
| rc = fdt_read_uint32(dtb, node, prop, &phandle); |
| if (rc < 0) { |
| return rc; |
| } |
| |
| node = fdt_node_offset_by_phandle(dtb, phandle); |
| if (node < 0) { |
| return node; |
| } |
| |
| *oid = pool_alloc(&oid_pool); |
| rc = fdtw_read_string(dtb, node, "oid", *oid, MAX_OID_NAME_LEN); |
| |
| return rc; |
| } |
| |
| /******************************************************************************* |
| * populate_and_set_auth_methods() - Populate auth method parameters from |
| * device tree and set authentication method |
| * structure. |
| * @dtb[in]: Pointer to the device tree blob in memory |
| * @node[in]: Offset of the node |
| * @img_id[in]: Image identifier |
| * @type[in]: Type of image |
| * @root_certificate[in]:Root certificate (authenticated by ROTPK) |
| * |
| * Return 0 on success or an error value otherwise. |
| ******************************************************************************/ |
| static int populate_and_set_auth_methods(const void *dtb, int node, |
| unsigned int img_id, img_type_t type, |
| bool root_certificate) |
| { |
| auth_method_type_t auth_method_type = AUTH_METHOD_NONE; |
| int rc; |
| char *oid = NULL; |
| |
| auth_method_desc_t *auth_method = pool_alloc_n(&auth_methods_pool, |
| AUTH_METHOD_NUM); |
| |
| /* |
| * This is as per binding document where certificates are |
| * verified by signature and images are verified by hash. |
| */ |
| if (type == IMG_CERT) { |
| rc = get_oid(dtb, node, "signing-key", &oid); |
| if (rc < 0) { |
| /* |
| * The signing-key property is optional in root |
| * certificates, mandatory otherwise. |
| */ |
| if (root_certificate) { |
| oid = NULL; |
| } else { |
| ERROR("FCONF: Can't read %s property\n", |
| "signing-key"); |
| return rc; |
| } |
| } |
| auth_method_type = AUTH_METHOD_SIG; |
| } else if (type == IMG_RAW) { |
| rc = get_oid(dtb, node, "hash", &oid); |
| if (rc < 0) { |
| ERROR("FCONF: Can't read %s property\n", |
| "hash"); |
| return rc; |
| } |
| auth_method_type = AUTH_METHOD_HASH; |
| } else { |
| return -1; |
| } |
| |
| set_auth_method(auth_method_type, oid, |
| &auth_method[auth_method_type]); |
| |
| /* Retrieve the optional property */ |
| rc = get_oid(dtb, node, "antirollback-counter", &oid); |
| if (rc == 0) { |
| auth_method_type = AUTH_METHOD_NV_CTR; |
| set_auth_method(auth_method_type, oid, |
| &auth_method[auth_method_type]); |
| } |
| |
| auth_img_descs[img_id].img_auth_methods = &auth_method[0]; |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * get_parent_img_id() - Get parent image id for given child node |
| * @dtb[in]: Pointer to the device tree blob in memory |
| * @node[in]: Offset of the child node |
| * @parent_img_id[out]: Image id of parent |
| * |
| * Return 0 on success or an error value otherwise. |
| ******************************************************************************/ |
| static int get_parent_img_id(const void *dtb, int node, |
| unsigned int *parent_img_id) |
| { |
| uint32_t phandle; |
| int err; |
| |
| err = fdt_read_uint32(dtb, node, "parent", &phandle); |
| if (err < 0) { |
| ERROR("FCONF: Could not read %s property in node\n", |
| "parent"); |
| return err; |
| } |
| |
| node = fdt_node_offset_by_phandle(dtb, phandle); |
| if (node < 0) { |
| ERROR("FCONF: Failed to locate node using its phandle\n"); |
| return node; |
| } |
| |
| err = fdt_read_uint32(dtb, node, "image-id", parent_img_id); |
| if (err < 0) { |
| ERROR("FCONF: Could not read %s property in node\n", |
| "image-id"); |
| } |
| |
| return err; |
| } |
| |
| /******************************************************************************* |
| * set_desc_data() - Update data in descriptor's structure |
| * @dtb[in]: Pointer to the device tree blob in memory |
| * @node[in]: Offset of the node |
| * @type[in]: Type of image (RAW/CERT) |
| * |
| * Return 0 on success or an error value otherwise. |
| ******************************************************************************/ |
| static int set_desc_data(const void *dtb, int node, img_type_t type) |
| { |
| int rc; |
| bool root_certificate = false; |
| unsigned int img_id, parent_img_id; |
| |
| rc = fdt_read_uint32(dtb, node, "image-id", &img_id); |
| if (rc < 0) { |
| ERROR("FCONF: Can't find property %s in node\n", |
| "image-id"); |
| return rc; |
| } |
| |
| if (fdt_getprop(dtb, node, "root-certificate", |
| NULL) != NULL) { |
| root_certificate = true; |
| } |
| |
| if (!root_certificate) { |
| rc = get_parent_img_id(dtb, node, &parent_img_id); |
| if (rc < 0) { |
| return rc; |
| } |
| auth_img_descs[img_id].parent = &auth_img_descs[parent_img_id]; |
| } |
| |
| auth_img_descs[img_id].img_id = img_id; |
| auth_img_descs[img_id].img_type = type; |
| |
| rc = populate_and_set_auth_methods(dtb, node, img_id, type, |
| root_certificate); |
| if (rc < 0) { |
| return rc; |
| } |
| |
| if (type == IMG_CERT) { |
| auth_param_desc_t *auth_param = |
| pool_alloc_n(&auth_params_pool, |
| COT_MAX_VERIFIED_PARAMS); |
| auth_img_descs[img_id].authenticated_data = &auth_param[0]; |
| } |
| |
| cot_desc[img_id] = &auth_img_descs[img_id]; |
| |
| return rc; |
| } |
| |
| /******************************************************************************* |
| * populate_manifest_descs() - Populate CoT descriptors and update global |
| * certificate structures |
| * @dtb[in]: Pointer to the device tree blob in memory |
| * |
| * Return 0 on success or an error value otherwise. |
| ******************************************************************************/ |
| static int populate_manifest_descs(const void *dtb) |
| { |
| int node, child; |
| int rc; |
| |
| /* |
| * Assert the node offset points to "arm, cert-descs" |
| * compatible property |
| */ |
| const char *compatible_str = "arm, cert-descs"; |
| |
| node = fdt_node_offset_by_compatible(dtb, -1, compatible_str); |
| if (node < 0) { |
| ERROR("FCONF: Can't find %s compatible in node\n", |
| compatible_str); |
| return node; |
| } |
| |
| fdt_for_each_subnode(child, dtb, node) { |
| rc = set_desc_data(dtb, child, IMG_CERT); |
| if (rc < 0) { |
| return rc; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * populate_image_descs() - Populate CoT descriptors and update global |
| * image descriptor structures. |
| * @dtb[in]: Pointer to the device tree blob in memory |
| * |
| * Return 0 on success or an error value otherwise. |
| ******************************************************************************/ |
| static int populate_image_descs(const void *dtb) |
| { |
| int node, child; |
| int rc; |
| |
| /* |
| * Assert the node offset points to "arm, img-descs" |
| * compatible property |
| */ |
| const char *compatible_str = "arm, img-descs"; |
| |
| node = fdt_node_offset_by_compatible(dtb, -1, compatible_str); |
| if (node < 0) { |
| ERROR("FCONF: Can't find %s compatible in node\n", |
| compatible_str); |
| return node; |
| } |
| |
| fdt_for_each_subnode(child, dtb, node) { |
| rc = set_desc_data(dtb, child, IMG_RAW); |
| if (rc < 0) { |
| return rc; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * fconf_populate_cot_descs() - Populate CoT descriptors and update global |
| * structures |
| * @config[in]: Pointer to the device tree blob in memory |
| * |
| * Return 0 on success or an error value otherwise. |
| ******************************************************************************/ |
| static int fconf_populate_cot_descs(uintptr_t config) |
| { |
| auth_param_type_desc_t *type_desc = NULL; |
| unsigned int auth_buf_size = 0U; |
| int rc; |
| |
| /* As libfdt uses void *, we can't avoid this cast */ |
| const void *dtb = (void *)config; |
| |
| /* populate manifest descs information */ |
| rc = populate_manifest_descs(dtb); |
| if (rc < 0) { |
| ERROR("FCONF: population of %s descs failed %d\n", |
| "manifest", rc); |
| return rc; |
| } |
| |
| /* populate image descs information */ |
| rc = populate_image_descs(dtb); |
| if (rc < 0) { |
| ERROR("FCONF: population of %s descs failed %d\n", |
| "images", rc); |
| return rc; |
| } |
| |
| /* update parent's authentication data */ |
| for (unsigned int i = 0U; i < MAX_NUMBER_IDS; i++) { |
| if (auth_img_descs[i].parent != NULL) { |
| rc = get_auth_param_type_desc(i, |
| &type_desc, |
| &auth_buf_size); |
| if (rc < 0) { |
| ERROR("FCONF: failed to get auth data %d\n", |
| rc); |
| return rc; |
| } |
| |
| rc = update_parent_auth_data(auth_img_descs[i].parent, |
| type_desc, |
| auth_buf_size); |
| if (rc < 0) { |
| ERROR("FCONF: auth data update failed %d\n", |
| rc); |
| return rc; |
| } |
| } |
| } |
| |
| return rc; |
| } |
| |
| FCONF_REGISTER_POPULATOR(TB_FW, cot_desc, fconf_populate_cot_descs); |
| REGISTER_COT(cot_desc); |