[XTS] VTS测试VtsSecurityAvbTest分析

为什么要写篇博客来记录这个呢,因为已经被这个测试逼疯了好几天,所以还是写下来记录一下。

前提:测试版本是android R,kernel版本是5.4
VtsSecurityAvbTest测试时一直报“The GKI image is not signed”

下面是VtsSecurityAvbTest.cpp的部分测试代码

TEST(AvbTest, Boot) {
  /* Skip for devices running kernels older than 5.4. */
  struct utsname buf;
  int ret, kernel_version_major, kernel_version_minor;
  ret = uname(&buf);
  ASSERT_EQ(ret, 0) << "Failed to get kernel version.";
  char dummy;
  ret = sscanf(buf.release, "%d.%d%c", &kernel_version_major,
               &kernel_version_minor, &dummy);
  ASSERT_GE(ret, 2) << "Failed to parse kernel version.";
  if (kernel_version_major < 5 ||
      (kernel_version_major == 5 && kernel_version_minor < 4)) {  //kernel版本号大于等于5.4
    return;
  }

  /* load vbmeta struct from boot, verify struct integrity */
  std::string out_public_key_data;
  android::fs_mgr::VBMetaVerifyResult out_verify_result;
  std::string boot_path = "/dev/block/by-name/boot" + fs_mgr_get_slot_suffix();
  std::unique_ptr<android::fs_mgr::VBMetaData> vbmeta =
      android::fs_mgr::LoadAndVerifyVbmetaByPath(
          boot_path, "boot", "" /* expected_key_blob */,
          true /* allow verification error */, false /* rollback_protection */,
          false /* is_chained_vbmeta */, &out_public_key_data,
          nullptr /* out_verification_disabled */, &out_verify_result);

  ASSERT_TRUE(vbmeta) << "Verification of GKI vbmeta fails.";
  ASSERT_FALSE(out_public_key_data.empty()) << "The GKI image is not signed.";
  EXPECT_TRUE(ValidatePublicKeyBlob(out_public_key_data))
      << "The GKI image is not signed by an official key.";
  EXPECT_EQ(out_verify_result, android::fs_mgr::VBMetaVerifyResult::kSuccess)
      << "Verification of the GKI vbmeta structure failed.";

TEST(AvbTest, SystemDescriptor) {
  // Get system hashtree descriptor.

  android::fs_mgr::VBMetaVerifyResult verify_result;
  std::string system_path;
  std::unique_ptr<android::fs_mgr::FsAvbHashtreeDescriptor> descriptor =
      GetSystemHashtreeDescriptor(&verify_result, &system_path);
  ASSERT_TRUE(descriptor);
  ...
  TEST(AvbTest, SystemHashtree) {
	ALOGE("pis SystemHashtree entry");
  android::fs_mgr::VBMetaVerifyResult verify_result;
  std::string system_path;
  std::unique_ptr<android::fs_mgr::FsAvbHashtreeDescriptor> descriptor =
      GetSystemHashtreeDescriptor(&verify_result, &system_path);
  ASSERT_TRUE(descriptor);

从以上代码来看,只有当kernel 版本大于等于5.4,才会进行往下执行,android R版本刚好升级的kernel版本是5.4,所以测试会继续执行下去,老版本的android就直接return了,不会报这个错误。

这段code的描述大致是:
1、加载boot_x分区(boot_a或者boot_b),调用fs_mgr模块加载boot分区获取public key信息;
2、调用fs_mgr的GetHashDescriptor函数,获取hash descriptor描述信息;
3、读取vbmeta分区中的boot分区的digest,同步骤中2获取的digest进行对比,相等则通过。
4、system分区测试就是加载GSI后的system分区,然后读取public key对比一下,接下来就是对描述符信息和hashtree信息,这个在我写的AVB博客中写过了。

结论:要GKI改造完成,kernel driver模块要编译成.ko文件,打包到vendor_boot分区;
然后编译vendor_boot-debug.img烧录入vendor_boot分区,再加上GKI boot-xxx.img和GSI system.img,就可以测试通过VtsSecurityAvbTest了。


XTS测试结果:

run vts -m vts_security_avb_test -t AvbTest#SystemHashtree
...
=============== Summary ===============
Total Run time: 1m 6s
2/2 modules completed
Total Tests       : 2
PASSED            : 2
FAILED            : 0
============== End of Results ==============


vts-tf > run vts -m vts_security_avb_test -t AvbTest#Boot
...
================= Results ==================
=============== Consumed Time ==============
    arm64-v8a vts_security_avb_test: 2s
    armeabi-v7a vts_security_avb_test: 1s
Total aggregated tests run time: 4s
============== Modules Preparation Times ==============
    arm64-v8a vts_security_avb_test => prep = 818 ms || clean = 304 ms
    armeabi-v7a vts_security_avb_test => prep = 803 ms || clean = 284 ms
Total preparation time: 1s  ||  Total tear down time: 588 ms
=======================================================
=============== Summary ===============
Total Run time: 51s
2/2 modules completed
Total Tests       : 2
PASSED            : 2
FAILED            : 0
============== End of Results ==============
============================================
vts-tf >

------------------------- 以下是趟雷的过程及经验 --------------------------------------
我的问题是在刚开始的时候就失败,报boot没有签名,使用avbtool工具查看,确实没有Authentication Block数据为0,没有签名信息。

./android/external/avb/avbtool.py info_image --image android/out/target/product/xxx/boot.img

所以,难道是需要我们手动进行签名??查看了一下build/core/Makefile打包boot.img的定义

2264 define build-recoveryimage-target
...
2280   $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)), \
2283   $(if $(filter true,$(BOARD_AVB_ENABLE)), \
2284     $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)), \
2285       $(AVBTOOL) add_hash_footer --image $(1) --partition_size $(call get-bootimage-partition-size,$(1),boot) --partition_name boot $(INTERNAL_AVB_BOOT_SIGNING_ARGS) $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS),\

但看了INTERNAL_AVB_BOOT_SIGNING_ARGS和BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS,并没有看到–key传入秘钥的地方,所以默认应该是未签名的。

既然没有签名,那我们就给它加上签名,重新编译boot.img

MY_BOOT_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem
INTERNAL_AVB_BOOT_SIGNING_ARGS  += --algorithm SHA256_RSA4096 --key $(MY_BOOT_KEY_PATH)

重新运行vts test测试,这次不报没有签名了,报public key不相同,不是官方正式签名的key。

// Checks whether the public key is an official GSI key or not.
static bool ValidatePublicKeyBlob(const std::string &key_blob_to_validate) {
  if (key_blob_to_validate.empty()) {
    ALOGE("Failed to validate an empty key");
    return false;
  }

  std::string allowed_key_blob;
  std::vector<std::string> allowed_key_paths = {
      "/data/local/tmp/q-gsi.avbpubkey", "/data/local/tmp/r-gsi.avbpubkey",
      "/data/local/tmp/s-gsi.avbpubkey"};
  for (const auto &path : allowed_key_paths) {
    if (android::base::ReadFileToString(path, &allowed_key_blob)) {
      if (key_blob_to_validate == allowed_key_blob) {
        ALOGE("Found matching GSI key: %s", path.c_str());
        return true;
      }
    }
  }
  return false;
}

在测试vts 过程,我去data/local/tmp/目录下ls -l看过,确实会push三个x-gsi.avbpubkey公钥文件到data分区,目的应该就是用来上面比较用的了。
好了,测试到这里执行不下去了,接下来要分析一下思路,为什么测不过。

首先,测试代码中提供了kernel 5.4 GKI概念,需要先了解这个。
GKI 通用内核镜像,传送门

在这里插入图片描述
我们挑一些我们这里比较关心的部分,主要是下面两个概念,
第一,简单点描述就是kernel内核将来厂商不能修改,和kernel相关的内容需要改造成ko,移出来到vendor或者vendor_boot分区中,为了将来升级kernel版本方便些。
第二,测试VTS时,需要刷入google提供签名过的GKI boot.img和签名过的GSI system.img。对于已经KO改造完成的设备,需要烧入vendor_boot-debug.img到vendor_boot分区。

下面是google source
启动更改
为了帮助 GKI 与供应商组件完全分离开来,boot 分区仅包含通用组件,其中包括内核和带有 GKI 模块的 ramdisk。我们定义了新版启动头文件 (v3),用于表明符合 GKI 架构规范。GKI 版本的启动映像由 Google 提供,会在测试 GKI 兼容性时取代供应商版本的启动映像。

GKI 兼容性测试
对于 Android 11 平台版本,搭载 v5.4 内核的设备必须使用 Google 提供的 GKI 启动映像运行 VTS 和 CTS-on-GSI 测试。

供应商启动调试 ramdisk
从 Android 11 开始,对于支持通用内核映像 (GKI) 架构的设备,应将 vendor_boot-debug.img 刷写到 VTS 中的 /vendor_boot 分区。此外,还应将已签名的 GKI boot-${KERNEL_VERSION}.img(而非 boot-debug-{KERNEL_VERSION}.img)刷写到 /boot 分区。

按google的XTS要求配置后,system相关的这两项测试就可以通过了。

vts-tf > run vts -m vts_security_avb_test -t AvbTest#SystemHashtree
10-21 19:38:39 D/ITestSuite: [Total Unique Modules = 2]
10-21 19:38:39 D/ModuleDefinition: Running module arm64-v8a vts_security_avb_test
10-21 19:38:49 I/ModuleListener: [1/1] vts_security_avb_test AvbTest#SystemHashtree PASSED
10-21 19:38:50 D/ModuleDefinition: Running module armeabi-v7a vts_security_avb_test
10-21 19:38:59 I/ModuleListener: [1/1] vts_security_avb_test AvbTest#SystemHashtree PASSED

vts-tf > run vts -m vts_security_avb_test -t AvbTest#SystemDescriptor
10-21 19:42:30 D/ModuleDefinition: Running module arm64-v8a vts_security_avb_test
10-21 19:42:32 I/ModuleListener: [1/1] vts_security_avb_test AvbTest#SystemDescriptor PASSED
10-21 19:42:32 D/ModuleDefinition: Running module armeabi-v7a vts_security_avb_test
10-21 19:42:34 I/ModuleListener: [1/1] vts_security_avb_test AvbTest#SystemDescriptor PASSED

boot的那项测试需要等平台将所有ko改造完成后复测,理论是需要烧入GSI中提供的vbmeta,并把verification和verity校验功能关闭,不然Uboot阶段可能通过不了。

fastboot --disable-verity --disable-verification flash vbmeta vbmeta.img

然后烧入GKI提供的boot-xxx(kernel版本号).img,同时设备需要烧入平台自己的vendor_boot-debug.img(里面有平台的ko和adb root功能),再加上GSI system.img,这样就可以完整的测试XTS中的boot 和 system了。

如果设备没有KO改造完成,能不能测试这几项呢?当然也是可以的,只不过要折腾一下。方法如下:
烧入user版本,然后烧录GSI system.img镜像,烧入boot-debug.img进行root,开机会报

[   29.353602] init: /system/bin/secilc: Failed to resolve typeattributeset statement at /system/etc/selinux/mapping/30.0.cil:235
[   29.367377] init: /system/bin/secilc: Failed to compile cildb: -2
[   29.384934] init: /system/bin/secilc exited with status 254
[   29.390769] init: Unable to load SELinux policy
[   29.396879] init: InitFatalReboot: signal 6

官方解释 :
“当您使用 boot-debug.img 时,系统将从 boot-debug.img 加载系统 sepolicy (plat_sepolicy.cil)。请始终采用来自 android{N}-gsi 分支(例如 android11-gsi)的新 sepolicy 变更来重新构建 boot-debug.img。否则,设备可能无法启动新的 GSI 映像。这一条同样适用于对具有 /vendor_boot 分区的设备使用 vendor_boot-debug.img 的情况。”

但是我们可以通过官方提供的另一种方法来解决这个GSI无法开机问题。
ci.android.com这里下载otatools.zip包,然后手动进行repack操作,把官方的ramdisk替换到自己的boot.img中的ramdisk.

repack_bootimg命令如下:

$ ./otatools/bin/repack_bootimg --src_bootimg ./boot-with-debug-ramdisk-5.4.img --dst_bootimg ./boot-debug-5.4.img --ramdisk_add first_stage_ramdisk/userdebug_plat_sepolicy.cil:first_stage_ramdisk/userdebug_plat_sepolicy.cil
=== Unpacked boot image: './boot-with-debug-ramdisk-5.4.img' ===
=== Unpacked ramdisk: '/tmp/tmp73prpv1b_boot-with-debug-ramdisk-5.4.img/ramdisk' at '/tmp/tmpyad3znz4_ramdisk' ===
=== Unpacked boot image: './boot-debug-5.4.img' ===
=== Unpacked ramdisk: '/tmp/tmpewpibt35_boot-debug-5.4.img/ramdisk' at '/tmp/tmprhzc1o2g_ramdisk' ===
Copying file '/tmp/tmpyad3znz4_ramdisk/first_stage_ramdisk/userdebug_plat_sepolicy.cil' to '/tmp/tmprhzc1o2g_ramdisk/first_stage_ramdisk/userdebug_plat_sepolicy.cil'
Repacking ramdisk, which might take a few seconds ...
=== Repacked ramdisk: '/tmp/ramdisk-patchedmu6s95x9' ===
=== Repacked boot image: './boot-debug-5.4.img' ===

烧入repack后的boot-debug.img,可以正式开机了。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值