MTK安全启动分析14:Android的AVB校验的总结

发布时间:2026/7/28 1:40:51
MTK安全启动分析14:Android的AVB校验的总结 Android的AVB校验的总结从代码看顺序是1. avb_vbmeta_image_verify() 做的事: - 从 vbmeta.img 自身提取 public_key_X ← 这个公钥是 vbmeta.img 自己带在里面的 - 从 vbmeta.img 自身提取 signature - 计算 hash SHA256(header_block auxiliary_block) - RSA_verify(public_key_X, signature, hash) - 返回 public_key_X ↑ 这一步只证明: vbmeta.img 是用 public_key_X 对应的私钥签名的 但还不知道 public_key_X 是否可信 2. avb_hal_verify_public_key(public_key_X) 做的事: - memcmp(g_avb_key, public_key_X, pubk_sz) - 如果相等 → key_is_trusted TRUE ↑ 这一步才证明: public_key_X 就是 OEM 的 AVB_ROOT_PUBK → vbmeta.img 是 OEM 签的 3. 遍历 vbmeta.img 的 descriptors ├── 发现 CHAIN_PARTITION vbmeta_system │ 提取 chain_public_key (就是 AVB_SYSTEM_PUBK) │ └── load_and_verify_vbmeta(vbmeta_system, AVB_SYSTEM_PUBK) │ 1. avb_vbmeta_image_verify() ← 用自身 key 验自身签名 │ 2. memcmp(AVB_SYSTEM_PUBK, vbmeta_system 提取的 public_key) │ └── 相等 → 信任 │ ├── 发现 CHAIN_PARTITION vbmeta_vendor │ 提取 chain_public_key (就是 AVB_VENDOR_PUBK) │ └── load_and_verify_vbmeta(vbmeta_vendor, AVB_VENDOR_PUBK) │ 1. avb_vbmeta_image_verify() ← 用自身 key 验自身签名 │ 2. memcmp(AVB_VENDOR_PUBK, vbmeta_vendor 提取的 public_key) │ └── 相等 → 信任 │ └── 发现 HASH descriptor └── load_and_verify_hash_partition() ├── 读取整个 boot 分区数据 ├── SHA256(salt || boot_partition_data) └── memcmp(算出来的 hash, vbmeta 中存的 hash)先用 vbmeta.img 自带的公钥验它自己的签名证明数据完整再判断这个公钥是否等于 LK 里的公钥证明是谁签的vbmeta.public_key → RSA验证 → vbmeta.signature ← 先验签名 vbmeta.public_key → memcmp → g_avb_key ← 后比公钥为什么 因为 avb_vbmeta_image_verify() 是 Google libavb 的通用函数它不依赖任何信任的公钥——它只是说这个 vbmeta 的签名能用一个自带的公钥验证通过。而判断这个公钥是否被信任是平台 HAL 层MTK的责任所以分成了两个独立的步骤。对比公钥 avb_safe_memcmp(expected_public_key, pk_data, pk_len) ! 0) 与 avb_hal_verify_public_key 中的 if (0 memcmp((void *)g_avb_key, (void *)pubk, pubk_sz))case AVB_DESCRIPTOR_TAG_CHAIN_PARTITION: { AvbSlotVerifyResult sub_ret; AvbChainPartitionDescriptor chain_desc; const uint8_t* chain_partition_name; const uint8_t* chain_public_key; /* Only allow CHAIN_PARTITION descriptors in the main vbmeta image. */ if (!is_main_vbmeta) { avb_errorv(full_partition_name, : Encountered chain descriptor not in main image.\n, NULL); ret AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } if (!avb_chain_partition_descriptor_validate_and_byteswap( (AvbChainPartitionDescriptor*)descriptors[n], chain_desc)) { avb_errorv(full_partition_name, : Chain partition descriptor is invalid.\n, NULL); ret AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } if (chain_desc.rollback_index_location 0) { avb_errorv(full_partition_name, : Chain partition has invalid rollback_index_location field.\n, NULL); ret AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; goto out; } chain_partition_name ((const uint8_t*)descriptors[n]) sizeof(AvbChainPartitionDescriptor); chain_public_key chain_partition_name chain_desc.partition_name_len; sub_ret load_and_verify_vbmeta(ops, requested_partitions, ab_suffix, flags, allow_verification_error, toplevel_vbmeta_flags, chain_desc.rollback_index_location, (const char*)chain_partition_name, chain_desc.partition_name_len, chain_public_key, chain_desc.public_key_len, slot_data, NULL, /* out_algorithm_type */ NULL /* out_additional_cmdline_subst */); if (sub_ret ! AVB_SLOT_VERIFY_RESULT_OK) { ret sub_ret; if (!result_should_continue(ret)) { goto out; } } /* Check if key used to make signature matches what is expected. */ if (pk_data ! NULL) { if (expected_public_key ! NULL) { avb_assert(!is_main_vbmeta); if (expected_public_key_length ! pk_len || avb_safe_memcmp(expected_public_key, pk_data, pk_len) ! 0) { avb_errorv(full_partition_name, : Public key used to sign data does not match key in chain partition descriptor.\n, NULL); ret AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED; if (!allow_verification_error) { goto out; } } } else { bool key_is_trusted false; const uint8_t* pk_metadata NULL; size_t pk_metadata_len 0; if (vbmeta_header.public_key_metadata_size 0) { pk_metadata vbmeta_buf sizeof(AvbVBMetaImageHeader) vbmeta_header.authentication_data_block_size vbmeta_header.public_key_metadata_offset; pk_metadata_len vbmeta_header.public_key_metadata_size; } // If were not using a vbmeta partition, need to use another AvbOps... if (flags AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) { io_ret ops-validate_public_key_for_partition( ops, full_partition_name, pk_data, pk_len, pk_metadata, pk_metadata_len, key_is_trusted, rollback_index_location_to_use); } else { avb_assert(is_main_vbmeta); io_ret ops-validate_vbmeta_public_key(ops, pk_data, pk_len, pk_metadata, pk_metadata_len, key_is_trusted); } AvbIOResult avb_hal_verify_public_key(AvbOps *ops, const uint8_t *public_key_data, size_t public_key_length, const uint8_t *public_key_metadata, size_t public_key_metadata_length, bool *out_is_trusted) { AvbRSAPublicKeyHeader *key_hdr; uint8_t *pubk; uint32_t i 0; uint32_t pubk_sz 0; AvbIOResult ret AVB_IO_RESULT_OK; if (out_is_trusted NULL || public_key_data NULL) return AVB_IO_RESULT_ERROR_IO; key_hdr (AvbRSAPublicKeyHeader *)public_key_data; pubk (uint8_t *)(public_key_data sizeof(AvbRSAPublicKeyHeader)); pubk_sz avb_htobe32(key_hdr-key_num_bits) / 8; *out_is_trusted FALSE; if (pubk_sz ! AVB_PUBK_SZ) { pal_log_err(invalid pubk size\n); goto end; } #ifdef MTK_SECURITY_SW_SUPPORT if (sec_set_pubk(pubk, pubk_sz) ! STATUS_OK) { pal_log_err([%s] fails to set public key\n, __func__); return AVB_IO_RESULT_ERROR_IO; } #endif if (0 memcmp((void *)g_avb_key, (void *)pubk, pubk_sz)) *out_is_trusted TRUE; else { #ifdef MTK_SECURITY_YELLOW_STATE_SUPPORT *out_is_trusted TRUE; g_vb_custom_key_exist 1; #else pal_log_err(oem_avb_key \n); for (i 0; i AVB_PUBK_SZ; i) { pal_log_err(0x%x , g_avb_key[i]); if (((i 1) % 16) 0) pal_log_err(\n); } pal_log_err(vbmeta_avb_key \n); for (i 0; i AVB_PUBK_SZ; i) { pal_log_err(0x%x , pubk[i]); if (((i 1) % 16) 0) pal_log_err(\n); } #endif } end: return ret; }分区的描述与对应代码描述对应代码vbmeta.img 的 chain descriptor 描述了子 vbmetaAVB_DESCRIPTOR_TAG_CHAIN_PARTITION指定 partition_name public_key用 chain 中的公钥校验子 vbmeta 的签名load_and_verify_vbmeta(vbmeta_system, chain_public_key, ...)全量 SHA256 计算 boot 分区load_and_verify_hash_partition()全量加载 SHA256对比 SHA256 值与 vbmeta 中的 digestmemcmp(computed_digest, expected_digest, digest_len)先用 vbmeta.img 自带的公钥验它自己的签名再比对这个公钥是否等于 LK 编译的 AVB_ROOT_PUBK。子 vbmeta 同理用 chain descriptor 里指定的公钥去验。boot 分区不做 RSA 签名而是全量 SHA256 哈希比对。