安全启动
安全启动的目的是保证系统从上电运行开始,到最终的操作系统加载启动完成,所有的固件均经过完整性和真实性验证,保证系统的可信性。
认证框架
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.c
和tbbr_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, ¶m_ptr, ¶m_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镜像的认证链如下。