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 we're 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 描述了子 vbmeta | AVB_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 中的 digest | memcmp(computed_digest, expected_digest, digest_len) |
先用 vbmeta.img 自带的公钥验它自己的签名,再比对这个公钥是否等于 LK 编译的 AVB_ROOT_PUBK。子 vbmeta 同理,用 chain descriptor 里指定的公钥去验。boot 分区不做 RSA 签名,而是全量 SHA256 哈希比对。