当前位置: 首页 > news >正文

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 哈希比对。

http://www.jsqmd.com/news/1277719/

相关文章:

  • ARM架构挑战:在Android设备上运行Windows应用的完整技术框架
  • COBOL与AI工具兼容性危机解析及企业级解决方案
  • (2026最新)大理本地漏水检测维修公司靠谱推荐:正规防水补漏上门维修-墙面/屋顶/外墙/暗管漏水检测精准定位 - 即刻修防水
  • 2026年水性漆供应厂家:环保水性漆、水性木器漆、水性防锈漆优质品牌参考 - 卓企推荐
  • 如何优化LoveIwara视频播放体验:5个Anime4K高级画质设置技巧
  • 2026水性漆采购指南:环保净味与高性能涂装的理性选择 - 卓企推荐
  • 基于AI Agent与LLM的办公自动化系统构建实战指南
  • 05-CLI与TUI-两种界面随心使用
  • 2026年精选:朝阳区专业回收虫草电话与服务商指南 - 装修教育财税推荐2026
  • 终极开源音乐播放器:Spotube带你体验完全自由的音乐流媒体革命
  • Ubuntu窗口伪全屏实现:X11与Wayland环境下的多种技术方案
  • BMS芯片SBS命令深度解析:从状态标志到故障排查实战
  • 【JAVA毕设源码分享】基于springboot顺丰仓储管理信息系统的设计与实现(程序+文档+代码讲解+一条龙定制)
  • 2026实力之选:专业工业漆品牌与服务公司解析 - 卓企推荐
  • MCP协议:实现AI编辑器与多工具无缝集成的关键技术
  • java 接口签名 参数名按ASCII码从小到大排序(字典序)+key+MD5+转大写签名
  • 三步解锁B站大会员4K和充电专属视频下载:永久保存你的珍贵内容
  • DFS算法实现无向图连通分量识别与应用
  • 10款AI工具助力学术论文写作全流程
  • 2026年BI数据分析软件推荐:平台与安全对比 - 科技焦点
  • MixTeX:彻底告别公式识别烦恼!三步实现本地化智能LaTeX转换
  • Linux新手入门指南:从零搭建虚拟机到掌握核心命令
  • WordPress养鱼网站开发:技术选型、SEO优化与盈利模式全解析
  • 2026下半年,西安企业股权法律服务如何选择? - 装修教育财税推荐2026
  • HarmonyOS应用开发实战:猫猫大作战-棋盘状态的增删管理
  • 一文讲清:数据库事务、锁、隔离级别
  • 5分钟搭建企业级数据治理平台:DataHub终极指南
  • 保健按摩师考试高效备考:知云题库与刷题技巧
  • 2026年防锈漆厂家推荐:环氧防锈漆、醇酸防锈漆、水性防锈漆、钢结构防锈漆优质品牌深度解析 - 卓企推荐
  • 5分钟快速上手:基于Chihaya构建企业级P2P分发系统的完整实战指南