(07)ATF安全启动

安全启动

安全启动的目的是保证系统从上电运行开始,到最终的操作系统加载启动完成,所有的固件均经过完整性和真实性验证,保证系统的可信性。

认证框架

ATF的安全启动按照TBBR规范要求进行实现,为了防止恶意固件在平台上运行,受信任的板级引导 (TBB) 功能要求验证所有固件镜像(包括普通世界的引导加载程序),这通过使用公钥密码标准 (PKCS) 建立信任链来做到这一点。

ATF的认证框架实现如下图所示,包括以下组件。

  • GEN和IO:TF-A通用代码和IO框架,负责BL1或者BL2中的镜像启动身份验证过程
  • PP:TF-A平台接口,指定信任链,提供信任根ROTPK等
  • CM:密码模块,验证数字签名和哈希
  • AM:认证模块,描述信任链,跟踪验证的镜像等
  • IPM:镜像解析器模块,获取用于镜像验证的参数
  • CL:密码算法库,提供哈希、验签和加密算法等实现
  • IPL:镜像解析器库,解析镜像参数,如从x509v3证书中提取参数
+---------------+---------------+------------+
| Trusted       | Trusted       | Trusted    |
| Firmware      | Firmware      | Firmware   |
| Generic       | IO Framework  | Platform   |
| Code i.e.     | (IO)          | Port       |
| BL1/BL2 (GEN) |               | (PP)       |
+---------------+---------------+------------+
       ^               ^               ^
       |               |               |
       v               v               v
 +-----------+   +-----------+   +-----------+
 |           |   |           |   | Image     |
 | Crypto    |   | Auth      |   | Parser    |
 | Module    |<->| Module    |<->| Module    |
 | (CM)      |   | (AM)      |   | (IPM)     |
 |           |   |           |   |           |
 +-----------+   +-----------+   +-----------+
       ^                               ^
       |                               |
       v                               v
+----------------+             +-----------------+
| Cryptographic  |             | Image Parser    |
| Libraries (CL) |             | Libraries (IPL) |
+----------------+             +-----------------+
              |                |
              |                |
              |                |
              v                v
             +-----------------+
             | Misc. Libs e.g. |
             | ASN.1 decoder   |
             |                 |
             +-----------------+

信任链

信任链CoT是一些列经过认证的镜像,信任从信任根开始往后逐级传递。信任根通常由两个组件构成:信任根公钥和BootROM固件。信任根公钥用于验证可信启动证书和可信密钥证书,而BootROM代码确保镜像验证流程必须执行。

信任链的其他组件还包括符合X.509 v3标准的证书,证书的扩展中存储了建立CoT需要的参数。证书是自签名的,因此不需要CA进行验证,这是因为CoT的建立不需要验证颁发者的有效性,只是请求证书扩展中的内容。换句话说就是证书链的安全性是由硬件保证,需要依赖外部CA:BootROM保证证书链必须经过验证,证书链的源头是由信任根公钥保证。

BL1

ATF中BL1阶段主要是验证BL2镜像,信任链实施如下,包括可信启动证书和BL2镜像认证定义,位于tbbr_cot_common.ctbbr_cot_bl1.c文件中,这里暂不关注其他配置镜像。

可信启动证书的认证镜像描述如下:

/* trusted_boot_fw_cert */
const auth_img_desc_t trusted_boot_fw_cert = {
	.img_id = TRUSTED_BOOT_FW_CERT_ID,
	.img_type = IMG_CERT,
	.parent = NULL,
	.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {
		[0] = {
			.type = AUTH_METHOD_SIG,
			.param.sig = {
				.pk = &subject_pk,
				.sig = &sig,
				.alg = &sig_alg,
				.data = &raw_data
			}
		},
		[1] = {
			.type = AUTH_METHOD_NV_CTR,
			.param.nv_ctr = {
				.cert_nv_ctr = &trusted_nv_ctr,
				.plat_nv_ctr = &trusted_nv_ctr
			}
		}
	},
	.authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) {
		[0] = {
			.type_desc = &tb_fw_hash,
			.data = {
				.ptr = (void *)tb_fw_hash_buf,
				.len = (unsigned int)HASH_DER_LEN
			}
		},
	}
};
  • img_id:定义可信启动证书唯一标识符
  • img_type:IMG_CERT表示镜像类型是证书
  • parent:NULL表示该镜像没有父镜像,需要使用信任根进行认证
  • img_auth_methods:镜像认证的方法包括认证签名和NV计数器,用于验签的公钥来自主公钥subject_pk,即信任根公钥ROTPK,而NV计数器的验证是用证书中解析的nv_ctr与平台定义的nv_ctr进行比较,rotpk和plat_nv_ctr通常存储在OTP中
  • authenticated_data:定义已经认证过的数据,这些参数通常用于认证子镜像,这里的tb_fw_hash即下面待比较的bl2镜像哈希

而BL2镜像的认证描述如下:

static const auth_img_desc_t bl2_image = {
	.img_id = BL2_IMAGE_ID,
	.img_type = IMG_RAW,
	.parent = &trusted_boot_fw_cert,
	.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {
		[0] = {
			.type = AUTH_METHOD_HASH,
			.param.hash = {
				.data = &raw_data,
				.hash = &tb_fw_hash
			}
		}
	}
};
  • img_id:定义bl2镜像唯一标识符
  • img_type:IMG_RAW表示镜像类型是原始镜像数据
  • parent:表示bl2镜像的父镜像是可选启动证书trusted_boot_fw_cert,即认证bl2镜像首先需要认证其父镜像trusted_boot_fw_cert
  • img_auth_methods:bl2镜像认证的方法为哈希比较,比较的可信哈希是从trusted_boot_fw_cert可信启动证书中提取到的

BL2

ATF中BL2阶段主要是验证BL3x镜像,信任链实施如下,包括可信密钥证书,BL3x密钥证书和内容证书,位于tbbr_cot_bl2.c文件中。

可信密钥证书trusted_key_cert定义如下,同BL1可信启动证书一样,认证方法为验签和NV计数器比较,由于没有父镜像,因此该证书验签也由信任根公钥ROTPK进行验证。证书认证通过后,可以从中提取安全和非安全世界公钥trusted_world_pk和non_trusted_world_pk,用于验证后续镜像的密钥证书。

/*
 * Trusted key certificate
 */
static const auth_img_desc_t trusted_key_cert = {
	.img_id = TRUSTED_KEY_CERT_ID,
	.img_type = IMG_CERT,
	.parent = NULL,
	.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {
		[0] = {
			.type = AUTH_METHOD_SIG,
			.param.sig = {
				.pk = &subject_pk,
				.sig = &sig,
				.alg = &sig_alg,
				.data = &raw_data
			}
		},
		[1] = {
			.type = AUTH_METHOD_NV_CTR,
			.param.nv_ctr = {
				.cert_nv_ctr = &trusted_nv_ctr,
				.plat_nv_ctr = &trusted_nv_ctr
			}
		}
	},
	.authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) {
		[0] = {
			.type_desc = &trusted_world_pk,
			.data = {
				.ptr = (void *)trusted_world_pk_buf,
				.len = (unsigned int)PK_DER_LEN
			}
		},
		[1] = {
			.type_desc = &non_trusted_world_pk,
			.data = {
				.ptr = (void *)non_trusted_world_pk_buf,
				.len = (unsigned int)PK_DER_LEN
			}
		}
	}
};

BL31的信任链CoT定义如下,包括密钥证书soc_fw_key_cert、内容证书soc_fw_content_cert和镜像bl31_image以及配置soc_fw_config(这里暂不去关注)。

  • 密钥证书soc_fw_key_cert:父镜像是可信密钥证书trusted_key_cert,认证方法包括验签和NV计数器,验签的公钥是安全世界公钥trusted_world_pk,即trusted_key_cert认证过的密钥数据;NV计数器是比较安全固件的计数器trusted_nv_ctr。认证通过后可以提取验证内容证书所用的公钥soc_fw_content_pk
  • 内容证书soc_fw_content_cert:父镜像是上面的密钥证书,认证方法同样是验签和NV计数器,验签公钥是上面的soc_fw_content_pk,认证通过后可以提取用于bl31镜像哈希比较的哈希值soc_fw_hash
  • bl31镜像bl31_image:父镜像是上面的内容证书,认证方法为哈希比较,比较的可信哈希为上面的soc_fw_hash
/*
 * SoC Firmware
 */
static const auth_img_desc_t soc_fw_key_cert = {
	.img_id = SOC_FW_KEY_CERT_ID,
	.img_type = IMG_CERT,
	.parent = &trusted_key_cert,
	.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {
		[0] = {
			.type = AUTH_METHOD_SIG,
			.param.sig = {
				.pk = &trusted_world_pk,
				.sig = &sig,
				.alg = &sig_alg,
				.data = &raw_data
			}
		},
		[1] = {
			.type = AUTH_METHOD_NV_CTR,
			.param.nv_ctr = {
				.cert_nv_ctr = &trusted_nv_ctr,
				.plat_nv_ctr = &trusted_nv_ctr
			}
		}
	},
	.authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) {
		[0] = {
			.type_desc = &soc_fw_content_pk,
			.data = {
				.ptr = (void *)content_pk_buf,
				.len = (unsigned int)PK_DER_LEN
			}
		}
	}
};
static const auth_img_desc_t soc_fw_content_cert = {
	.img_id = SOC_FW_CONTENT_CERT_ID,
	.img_type = IMG_CERT,
	.parent = &soc_fw_key_cert,
	.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {
		[0] = {
			.type = AUTH_METHOD_SIG,
			.param.sig = {
				.pk = &soc_fw_content_pk,
				.sig = &sig,
				.alg = &sig_alg,
				.data = &raw_data
			}
		},
		[1] = {
			.type = AUTH_METHOD_NV_CTR,
			.param.nv_ctr = {
				.cert_nv_ctr = &trusted_nv_ctr,
				.plat_nv_ctr = &trusted_nv_ctr
			}
		}
	},
	.authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) {
		[0] = {
			.type_desc = &soc_fw_hash,
			.data = {
				.ptr = (void *)soc_fw_hash_buf,
				.len = (unsigned int)HASH_DER_LEN
			}
		},
		[1] = {
			.type_desc = &soc_fw_config_hash,
			.data = {
				.ptr = (void *)soc_fw_config_hash_buf,
				.len = (unsigned int)HASH_DER_LEN
			}
		}
	}
};
static const auth_img_desc_t bl31_image = {
	.img_id = BL31_IMAGE_ID,
	.img_type = IMG_RAW,
	.parent = &soc_fw_content_cert,
	.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {
		[0] = {
			.type = AUTH_METHOD_HASH,
			.param.hash = {
				.data = &raw_data,
				.hash = &soc_fw_hash
			}
		}
	}
};

同理再看一下BL33的信任链定义,类似也包括密钥证书non_trusted_fw_key_cert、内容证书non_trusted_fw_content_cert和镜像bl33_image。

  • 密钥证书non_trusted_fw_key_cert:同BL31类似,只是验签的公钥是非安全世界公钥non_trusted_world_pk,NV计数器是非安全的计数器non_trusted_nv_ctr。认证通过后可以提取内容证书验证公钥nt_fw_content_pk
  • 内容证书non_trusted_fw_content_cert:同理,认证通过后可以提取用于镜像bl33比较的镜像哈希值nt_world_bl_hash
  • bl33镜像bl33_image:父镜像是上面的内容证书,认证方法为哈希比较,比较的可信哈希为上面的nt_world_bl_hash
/*
 * Non-Trusted Firmware
 */
static const auth_img_desc_t non_trusted_fw_key_cert = {
	.img_id = NON_TRUSTED_FW_KEY_CERT_ID,
	.img_type = IMG_CERT,
	.parent = &trusted_key_cert,
	.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {
		[0] = {
			.type = AUTH_METHOD_SIG,
			.param.sig = {
				.pk = &non_trusted_world_pk,
				.sig = &sig,
				.alg = &sig_alg,
				.data = &raw_data
			}
		},
		[1] = {
			.type = AUTH_METHOD_NV_CTR,
			.param.nv_ctr = {
				.cert_nv_ctr = &non_trusted_nv_ctr,
				.plat_nv_ctr = &non_trusted_nv_ctr
			}
		}
	},
	.authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) {
		[0] = {
			.type_desc = &nt_fw_content_pk,
			.data = {
				.ptr = (void *)content_pk_buf,
				.len = (unsigned int)PK_DER_LEN
			}
		}
	}
};
static const auth_img_desc_t non_trusted_fw_content_cert = {
	.img_id = NON_TRUSTED_FW_CONTENT_CERT_ID,
	.img_type = IMG_CERT,
	.parent = &non_trusted_fw_key_cert,
	.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {
		[0] = {
			.type = AUTH_METHOD_SIG,
			.param.sig = {
				.pk = &nt_fw_content_pk,
				.sig = &sig,
				.alg = &sig_alg,
				.data = &raw_data
			}
		},
		[1] = {
			.type = AUTH_METHOD_NV_CTR,
			.param.nv_ctr = {
				.cert_nv_ctr = &non_trusted_nv_ctr,
				.plat_nv_ctr = &non_trusted_nv_ctr
			}
		}
	},
	.authenticated_data = (const auth_param_desc_t[COT_MAX_VERIFIED_PARAMS]) {
		[0] = {
			.type_desc = &nt_world_bl_hash,
			.data = {
				.ptr = (void *)nt_world_bl_hash_buf,
				.len = (unsigned int)HASH_DER_LEN
			}
		},
		[1] = {
			.type_desc = &nt_fw_config_hash,
			.data = {
				.ptr = (void *)nt_fw_config_hash_buf,
				.len = (unsigned int)HASH_DER_LEN
			}
		}
	}
};
static const auth_img_desc_t bl33_image = {
	.img_id = BL33_IMAGE_ID,
	.img_type = IMG_RAW,
	.parent = &non_trusted_fw_content_cert,
	.img_auth_methods = (const auth_method_desc_t[AUTH_METHOD_NUM]) {
		[0] = {
			.type = AUTH_METHOD_HASH,
			.param.hash = {
				.data = &raw_data,
				.hash = &nt_world_bl_hash
			}
		}
	}
};

认证流程

安全启动的镜像加载认证函数为load_auth_image_recursive,其会递归认证父镜像,直到信任根。首先通过信任链CoT定义判断该镜像是否有父镜像,如果有则需要先认证父镜像,然后加载镜像,最后调用auth_mod_verify_img函数认证该镜像。

/*
 * This function uses recursion to authenticate the parent images up to the root
 * of trust.
 */
static int load_auth_image_recursive(unsigned int image_id,
				    image_info_t *image_data,
				    int is_parent_image)
{
	int rc;
	unsigned int parent_id;

	/* Use recursion to authenticate parent images */
	rc = auth_mod_get_parent_id(image_id, &parent_id);
	if (rc == 0) {
		rc = load_auth_image_recursive(parent_id, image_data, 1);
		if (rc != 0) {
			return rc;
		}
	}

	/* Load the image */
	rc = load_image(image_id, image_data);
	if (rc != 0) {
		return rc;
	}

	/* Authenticate it */
	rc = auth_mod_verify_img(image_id,
				 (void *)image_data->image_base,
				 image_data->image_size);
	if (rc != 0) {
		/* Authentication error, zero memory and flush it right away. */
		zero_normalmem((void *)image_data->image_base,
			       image_data->image_size);
		flush_dcache_range(image_data->image_base,
				   image_data->image_size);
		return -EAUTH;
	}

	return 0;
}

镜像认证最终的函数为auth_mod_verify_img,既可认证证书,也可认证镜像。

/*
 * Authenticate a certificate/image
 *
 * Return: 0 = success, Otherwise = error
 */
int auth_mod_verify_img(unsigned int img_id,
			void *img_ptr,
			unsigned int img_len)
{
	const auth_img_desc_t *img_desc = NULL;
	const auth_method_desc_t *auth_method = NULL;
	void *param_ptr;
	unsigned int param_len;
	int rc, i;
	unsigned int cert_nv_ctr = 0;
	bool need_nv_ctr_upgrade = false;
	bool sig_auth_done = false;
	const auth_method_param_nv_ctr_t *nv_ctr_param = NULL;

	/* Get the image descriptor from the chain of trust */
	img_desc = FCONF_GET_PROPERTY(tbbr, cot, img_id);

	/* Ask the parser to check the image integrity */
	rc = img_parser_check_integrity(img_desc->img_type, img_ptr, img_len);
	return_if_error(rc);

	/* Authenticate the image using the methods indicated in the image
	 * descriptor. */
	if (img_desc->img_auth_methods == NULL)
		return 1;
	for (i = 0 ; i < AUTH_METHOD_NUM ; i++) {
		auth_method = &img_desc->img_auth_methods[i];
		switch (auth_method->type) {
		case AUTH_METHOD_NONE:
			rc = 0;
			break;
		case AUTH_METHOD_HASH:
			rc = auth_hash(&auth_method->param.hash,
					img_desc, img_ptr, img_len);
			break;
		case AUTH_METHOD_SIG:
			rc = auth_signature(&auth_method->param.sig,
					img_desc, img_ptr, img_len);
			sig_auth_done = true;
			break;
		case AUTH_METHOD_NV_CTR:
			nv_ctr_param = &auth_method->param.nv_ctr;
			rc = auth_nvctr(nv_ctr_param,
					img_desc, img_ptr, img_len,
					&cert_nv_ctr, &need_nv_ctr_upgrade);
			break;
		default:
			/* Unknown authentication method */
			rc = 1;
			break;
		}
		return_if_error(rc);
	}

	/*
	 * Do platform NV counter upgrade only if the certificate gets
	 * authenticated, and platform NV-counter upgrade is needed.
	 */
	if (need_nv_ctr_upgrade && sig_auth_done) {
		rc = plat_set_nv_ctr2(nv_ctr_param->plat_nv_ctr->cookie,
				      img_desc, cert_nv_ctr);
		return_if_error(rc);
	}

	/* Extract the parameters indicated in the image descriptor to
	 * authenticate the children images. */
	if (img_desc->authenticated_data != NULL) {
		for (i = 0 ; i < COT_MAX_VERIFIED_PARAMS ; i++) {
			if (img_desc->authenticated_data[i].type_desc == NULL) {
				continue;
			}

			/* Get the parameter from the image parser module */
			rc = img_parser_get_auth_param(img_desc->img_type,
					img_desc->authenticated_data[i].type_desc,
					img_ptr, img_len, &param_ptr, &param_len);
			return_if_error(rc);

			/* Check parameter size */
			if (param_len > img_desc->authenticated_data[i].data.len) {
				return 1;
			}

			/* Copy the parameter for later use */
			memcpy((void *)img_desc->authenticated_data[i].data.ptr,
					(void *)param_ptr, param_len);
		}
	}

	/* Mark image as authenticated */
	auth_img_flags[img_desc->img_id] |= IMG_FLAG_AUTHENTICATED;

	return 0;
}
  • img_parser_check_integrity:检查镜像完整性,这主要是针对证书镜像,需要解析证书,检查是否符合ASN.1结构,并提取相关的数据用于后续认证操作,例如签名算法,公钥信息,扩展信息,签名值等。
  • auth_method:根据信任链CoT中定义的镜像认证方法进行认证操作,包括哈希比较,验签,NV计数器比较。
  • need_nv_ctr_upgrade:更新NV计数器,设置到平台OTP中
  • authenticated_data:当镜像认证通过后,可以提取信任链中定义的认证数据,用于认证子镜像

认证哈希

认证哈希是通过比较哈希值是否一致,一个是计算镜像得到的哈希值,一个是从父镜像提取的可信哈希值(包括哈希算法)。

static int auth_hash(const auth_method_param_hash_t *param,
		     const auth_img_desc_t *img_desc,
		     void *img, unsigned int img_len)
{
	void *data_ptr, *hash_der_ptr;
	unsigned int data_len, hash_der_len;
	int rc = 0;

	/* Get the hash from the parent image. This hash will be DER encoded
	 * and contain the hash algorithm */
	rc = auth_get_param(param->hash, img_desc->parent,
			&hash_der_ptr, &hash_der_len);
	return_if_error(rc);

	/* Get the data to be hashed from the current image */
	rc = img_parser_get_auth_param(img_desc->img_type, param->data,
			img, img_len, &data_ptr, &data_len);
	return_if_error(rc);

	/* Ask the crypto module to verify this hash */
	rc = crypto_mod_verify_hash(data_ptr, data_len,
				    hash_der_ptr, hash_der_len);

	return rc;
}
  • auth_get_param:从父镜像中获取DER编码的哈希值及哈希算法
  • img_parser_get_auth_param:获取当前镜像需要被哈希的数据,对于IMG_RAW类型就是镜像本身
  • crypto_mod_verify_hash:请求密码模块验证哈希,会调用底层密码模块verify_hash函数

认证签名

认证签名是公钥验证镜像的签名值,需要以下参数:

  • 待认证的镜像:被签名的数据,签名值,签名算法
  • 父镜像:公钥(或公钥哈希)

如果父镜像只包含公钥的哈希,则需要从待认证的镜像中提取公钥值(即自签名的证书),并计算该公钥的哈希值,与父镜像中获取的哈希值进行比较。如果镜像没有父镜像,则需要用OTP中存储的ROTPK进行验签。

对于使用X509证书方式的镜像解析方式而言,auth_signature函数只会针对于镜像类型为证书IMG_CERT。

static int auth_signature(const auth_method_param_sig_t *param,
			  const auth_img_desc_t *img_desc,
			  void *img, unsigned int img_len)
{
	void *data_ptr, *pk_ptr, *pk_hash_ptr, *sig_ptr, *sig_alg_ptr;
	unsigned int data_len, pk_len, pk_hash_len, sig_len, sig_alg_len;
	unsigned int flags = 0;
	int rc = 0;

	/* Get the data to be signed from current image */
	rc = img_parser_get_auth_param(img_desc->img_type, param->data,
			img, img_len, &data_ptr, &data_len);
	return_if_error(rc);

	/* Get the signature from current image */
	rc = img_parser_get_auth_param(img_desc->img_type, param->sig,
			img, img_len, &sig_ptr, &sig_len);
	return_if_error(rc);

	/* Get the signature algorithm from current image */
	rc = img_parser_get_auth_param(img_desc->img_type, param->alg,
			img, img_len, &sig_alg_ptr, &sig_alg_len);
	return_if_error(rc);

	/* Get the public key from the parent. If there is no parent (NULL),
	 * the certificate has been signed with the ROTPK, so we have to get
	 * the PK from the platform */
	if (img_desc->parent) {
		rc = auth_get_param(param->pk, img_desc->parent,
				&pk_ptr, &pk_len);
	} else {
		rc = plat_get_rotpk_info(param->pk->cookie, &pk_ptr, &pk_len,
				&flags);
	}
	return_if_error(rc);

	if (flags & (ROTPK_IS_HASH | ROTPK_NOT_DEPLOYED)) {
		/* If the PK is a hash of the key or if the ROTPK is not
		   deployed on the platform, retrieve the key from the image */
		pk_hash_ptr = pk_ptr;
		pk_hash_len = pk_len;
		rc = img_parser_get_auth_param(img_desc->img_type,
					param->pk, img, img_len,
					&pk_ptr, &pk_len);
		return_if_error(rc);

		/* Ask the crypto module to verify the signature */
		rc = crypto_mod_verify_signature(data_ptr, data_len,
						 sig_ptr, sig_len,
						 sig_alg_ptr, sig_alg_len,
						 pk_ptr, pk_len);
		return_if_error(rc);

		if (flags & ROTPK_NOT_DEPLOYED) {
			NOTICE("ROTPK is not deployed on platform. "
				"Skipping ROTPK verification.\n");
		} else {
			/* platform may store the hash of a prefixed, suffixed or modified pk */
			rc = plat_convert_pk(pk_ptr, pk_len, &pk_ptr, &pk_len);
			return_if_error(rc);

			/* Ask the crypto-module to verify the key hash */
			rc = crypto_mod_verify_hash(pk_ptr, pk_len,
				    pk_hash_ptr, pk_hash_len);
		}
	} else {
		/* Ask the crypto module to verify the signature */
		rc = crypto_mod_verify_signature(data_ptr, data_len,
						 sig_ptr, sig_len,
						 sig_alg_ptr, sig_alg_len,
						 pk_ptr, pk_len);
	}

	return rc;
}
  • img_parser_get_auth_param:获取被签名的数据,签名值以及签名算法
  • img_desc->parent:判断镜像是否有父镜像,并从父镜像已经认证过的数据中获取公钥,如果没有父镜像,说明证书是用ROTPK进行签名的,需要从平台如OTP中获取公钥
  • ROTPK_IS_HASH:如果公钥为哈希,需要先从镜像中提取公钥值,然后调用密码模块crypto_mod_verify_signature进行验签操作,即调用底层的verify_signature函数,如果公钥就是实际的值,则直接进行验签操作
  • ROTPK_NOT_DEPLOYED:如果公钥没有部署,就跳过对公钥的验证操作,这一般用用调试场景,如果公钥已经部署并且是存储的哈希值,则需要对公钥进行哈希比较操作crypto_mod_verify_hash,以此来判断公钥是否可信

认证NV计数器

认证NV计数器是系统防回滚设计,这个计数器只能递增并且存储在一次性存储器如OTP中,所有证书中的计数器必须比平台存储的大,即不能升级版本低的固件(防回滚)。

static int auth_nvctr(const auth_method_param_nv_ctr_t *param,
		      const auth_img_desc_t *img_desc,
		      void *img, unsigned int img_len,
		      unsigned int *cert_nv_ctr,
		      bool *need_nv_ctr_upgrade)
{
	unsigned char *p;
	void *data_ptr = NULL;
	unsigned int data_len, len, i;
	unsigned int plat_nv_ctr;
	int rc = 0;
	bool is_trial_run = false;

	/* Get the counter value from current image. The AM expects the IPM
	 * to return the counter value as a DER encoded integer */
	rc = img_parser_get_auth_param(img_desc->img_type, param->cert_nv_ctr,
				       img, img_len, &data_ptr, &data_len);
	return_if_error(rc);

	/* Parse the DER encoded integer */
	assert(data_ptr);
	p = (unsigned char *)data_ptr;

	/*
	 * Integers must be at least 3 bytes: 1 for tag, 1 for length, and 1
	 * for value.  The first byte (tag) must be ASN1_INTEGER.
	 */
	if ((data_len < 3) || (*p != ASN1_INTEGER)) {
		/* Invalid ASN.1 integer */
		return 1;
	}
	p++;

	/*
	 * NV-counters are unsigned integers up to 31 bits.  Trailing
	 * padding is not allowed.
	 */
	len = (unsigned int)*p;
	if ((len > 4) || (data_len - 2 != len)) {
		return 1;
	}
	p++;

	/* Check the number is not negative */
	if (*p & 0x80) {
		return 1;
	}

	/* Convert to unsigned int. This code is for a little-endian CPU */
	*cert_nv_ctr = 0;
	for (i = 0; i < len; i++) {
		*cert_nv_ctr = (*cert_nv_ctr << 8) | *p++;
	}

	/* Get the counter from the platform */
	rc = plat_get_nv_ctr(param->plat_nv_ctr->cookie, &plat_nv_ctr);
	return_if_error(rc);

	if (*cert_nv_ctr < plat_nv_ctr) {
		/* Invalid NV-counter */
		return 1;
	} else if (*cert_nv_ctr > plat_nv_ctr) {
#if PSA_FWU_SUPPORT && IMAGE_BL2
		is_trial_run = fwu_is_trial_run_state();
#endif /* PSA_FWU_SUPPORT && IMAGE_BL2 */
		*need_nv_ctr_upgrade = !is_trial_run;
	}

	return 0;
}
  • img_parser_get_auth_param:获取当前镜像的NV计数器,即从证书中提取DER编码的计数器值,然后进行解析,转换成整型值
  • plat_get_nv_ctr:从平台中获取当前的NV计数器值,比与上面计数器值进行比较,如果比他大,则认证通过,返回NV计数器需要更新need_nv_ctr_upgrade

密码算法库

上面认证流程用到的哈希、验签和解密都是基于底层的密码算法库。ATF默认使用的是mbedtls密码算法库。在底层的密码模块中,注册了验签、哈希验证以及认证解密函数。

REGISTER_CRYPTO_LIB(LIB_NAME, init, verify_signature, verify_hash,
		    auth_decrypt);

另外对于镜像解析模块,也注册了镜像完整性校验和认证参数解析函数。认证需要的参数如镜像哈希值、公钥等都是存储在X509证书中,在使用这些参数之前,需要从证书中解析出来(ASN.1编码的结构)。

REGISTER_IMG_PARSER_LIB(IMG_CERT, LIB_NAME, init, \
		       check_integrity, get_auth_param);

认证示例

最后以BL31镜像为例总结一下安全启动认证的设计。BL31信任链中包括ROTPK信任根公钥(或者哈希)、Trusted Key Certificate可信密钥证书、Trusted World Public key安全世界公钥、BL31 Key Certificate密钥证书、BL31 Content Certificate PK内容证书公钥、BL31 Content Certificate内容证书和BL31 Image镜像。

+------------------+       +-------------------+
| ROTPK/ROTPK Hash |------>| Trusted Key       |
+------------------+       | Certificate       |
                           | (Auth Image)      |
                          /+-------------------+
                         /            |
                        /             |
                       /              |
                      /               |
                     L                v
+------------------+       +-------------------+
| Trusted World    |------>| BL31 Key          |
| Public Key       |       | Certificate       |
+------------------+       | (Auth Image)      |
                           +-------------------+
                          /           |
                         /            |
                        /             |
                       /              |
                      /               v
+------------------+ L     +-------------------+
| BL31 Content     |------>| BL31 Content      |
| Certificate PK   |       | Certificate       |
+------------------+       | (Auth Image)      |
                           +-------------------+
                          /           |
                         /            |
                        /             |
                       /              |
                      /               v
+------------------+ L     +-------------------+
| BL31 Hash        |------>| BL31 Image        |
|                  |       | (Data Image)      |
+------------------+       |                   |
                           +-------------------+
  • 当认证BL31镜像时,首先存储介质中加载可信密钥证书,并使用平台提供的ROTPK进行验证,如果提供的是ROTPK Hash,还需要对ROTPK进行哈希比较。当可信密钥证书认证通过后,从证书中获取安全世界公钥。
  • 接着加载BL31密钥证书,使用上一步获取的安全世界公钥进行验证,同理认证通过后,从证书中获取BL31内容证书公钥。
  • 然后加载BL31内容证书,同理使用上一步获取的BL31内容证书公钥进行验证,认证通过后,从证书中获取BL31镜像的哈希。
  • 最后加载BL31镜像,并计算镜像的哈希值,与上一步获取的BL31哈希值进行比较,如果一致则BL31认证通过。

因此BL31镜像的认证链如下。

认证
认证
认证
认证
ROTPK
Trusted Key Certificate
BL31 Key Certificate
BL31 Content Certificate PK
BL31 Image

欢迎关注“安全有理”微信公众号。

安全有理

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值