Add support for Brillo Verified Boot.

The following variables are introduced

 BOARD_BVB_ENABLE: can be set to true to build boot.img and system.img
 files compatible with Brillo Verfied Boot.

 BOARD_BVB_ROLLBACK_INDEX: can be set to an integer to use for the
 rollback index.

 BOARD_BVB_KEY_PATH, BOARD_BVB_ALGORITHM: If set, the former must be a
 path to the private key used to sign the boot image and the latter must
 be the algorithm to use. If unset, a test-key stored in the tree will
 be used.

 BOARD_BVB_MAKE_BOOT_IMAGE_ARGS: Extra options to pass to 'bvbtool
 make_boot_image'.

 BOARD_BVB_SIGN_BOOT_IMAGE_ARGS: Extra options to pass to 'bvbtool
 sign_boot_image'.

 BOARD_BVB_ADD_IMAGE_HASHES_ARGS: Extra options to pass to 'bvbtool
 add_image_hashes'.

 BOARD_CUSTOM_BVBTOOL: Can be set to specify what bvbtool program to
 use.

The existing BOARD_KERNEL_CMDLINE variable is also used, as are existing
kernel and initrd-related variables. Therefore, simply adding

 BOARD_BVB_ENABLE := true

to an existing Makefile should do the trick.

Bug: 26185038
TEST=Added 'BOARD_BVB_ENABLE := true' to hardware/bsp/intel/soc/edison/soc.mk
  and built an image and then ran bvbtool's info_boot_image and
  info_image_hashes commands on the resulting boot.img and system.img
  files and verified that the information was correct. Also ran 'm dist'
  and verified that the boot.img and system.img files in the resulting
  target_files.zip file had similar information.

Change-Id: I08045ed8b0cbddc7c3acdd3a6f2c4bb75cb44bbc
This commit is contained in:
David Zeuthen 2016-01-29 16:59:17 -05:00
parent de005f0c85
commit d995f4b04d
4 changed files with 224 additions and 11 deletions

View File

@ -507,8 +507,28 @@ INTERNAL_BOOTIMAGE_ARGS := \
$(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
--kernel $(INSTALLED_KERNEL_TARGET)
INTERNAL_BVBTOOL_MAKE_BOOT_IMAGE_ARGS := \
--kernel $(INSTALLED_KERNEL_TARGET) \
--rootfs_with_hashes $(PRODUCT_OUT)/system.img
ifdef BOARD_BVB_ROLLBACK_INDEX
INTERNAL_BVBTOOL_MAKE_BOOT_IMAGE_ARGS += \
--rollback_index $(BOARD_BVB_ROLLBACK_INDEX)
endif
ifndef BOARD_BVB_KEY_PATH
# If key path isn't specified, use the 4096-bit test key.
INTERNAL_BVBTOOL_SIGN_BOOT_IMAGE_ARGS := --algorithm SHA256_RSA4096 \
--key system/bvb/test/testkey_rsa4096.pem
else
INTERNAL_BVBTOOL_SIGN_BOOT_IMAGE_ARGS := \
--algorithm $(BOARD_BVB_ALGORITHM) --key $(BOARD_BVB_KEY_PATH)
endif
ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
INTERNAL_BOOTIMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET)
INTERNAL_BVBTOOL_MAKE_BOOT_IMAGE_ARGS += --initrd $(INSTALLED_RAMDISK_TARGET)
endif
@ -517,6 +537,7 @@ INTERNAL_BOOTIMAGE_FILES := $(filter-out --%,$(INTERNAL_BOOTIMAGE_ARGS))
BOARD_KERNEL_CMDLINE := $(strip $(BOARD_KERNEL_CMDLINE))
ifdef BOARD_KERNEL_CMDLINE
INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)"
INTERNAL_BVBTOOL_MAKE_BOOT_IMAGE_ARGS += --kernel_cmdline "$(BOARD_KERNEL_CMDLINE)"
endif
BOARD_KERNEL_BASE := $(strip $(BOARD_KERNEL_BASE))
@ -542,6 +563,23 @@ ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
endif
endif
ifeq ($(BOARD_BVB_ENABLE),true)
$(INSTALLED_BOOTIMAGE_TARGET): $(BVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(PRODUCT_OUT)/system.img
$(call pretty,"Target boot image: $@")
$(hide) $(BVBTOOL) make_boot_image $(INTERNAL_BVBTOOL_MAKE_BOOT_IMAGE_ARGS) $(BOARD_BVB_MAKE_BOOT_IMAGE_ARGS) --output $@
$(hide) $(BVBTOOL) sign_boot_image $(INTERNAL_BVBTOOL_SIGN_BOOT_IMAGE_ARGS) $(BOARD_BVB_SIGN_BOOT_IMAGE_ARGS) --image $@
$(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE))
.PHONY: bootimage-nodeps
bootimage-nodeps: $(BVBTOOL)
@echo "make $@: ignoring dependencies"
$(hide) $(BVBTOOL) make_boot_image $(INTERNAL_BVBTOOL_MAKE_BOOT_IMAGE_ARGS) $(BOARD_BVB_MAKE_BOOT_IMAGE_ARGS) --output $(INSTALLED_BOOTIMAGE_TARGET)
$(hide) $(BVBTOOL) sign_boot_image $(INTERNAL_BVBTOOL_SIGN_BOOT_IMAGE_ARGS) $(BOARD_BVB_SIGN_BOOT_IMAGE_ARGS) --image $(INSTALLED_BOOTIMAGE_TARGET)
$(hide) $(call assert-max-image-size,$(INSTALLED_BOOTIMAGE_TARGET),$(BOARD_BOOTIMAGE_PARTITION_SIZE))
else # BOARD_BVB_ENABLE
# We build recovery as boot image if BOARD_USES_RECOVERY_AS_BOOT is true.
ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
ifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true)
@ -591,6 +629,7 @@ bootimage-nodeps: $(MKBOOTIMG)
endif # TARGET_BOOTIMAGE_USE_EXT2
endif # BOARD_USES_RECOVERY_AS_BOOT
endif # BOARD_BVB_ENABLE
else # TARGET_NO_KERNEL
# HACK: The top-level targets depend on the bootimage. Not all targets
@ -1117,8 +1156,13 @@ define build-systemimage-target
fi; \
mkdir -p $(DIST_DIR); cp $(INSTALLED_FILES_FILE) $(DIST_DIR)/installed-files-rescued.txt; \
exit 1 )
$(if $(BOARD_BVB_ENABLE), $(hide) $(BVBTOOL) add_image_hashes $(BOARD_BVB_ADD_IMAGE_HASHES_ARGS) --image $(1))
endef
ifeq ($(BOARD_BVB_ENABLE),true)
FULL_SYSTEMIMAGE_DEPS += $(BVBTOOL)
endif
$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)
$(call build-systemimage-target,$@)
@ -1721,6 +1765,15 @@ ifneq ($(strip $(SANITIZE_TARGET)),)
endif
ifeq ($(BOARD_USES_FULL_RECOVERY_IMAGE),true)
$(hide) echo "full_recovery_image=true" >> $(zip_root)/META/misc_info.txt
endif
ifeq ($(BOARD_BVB_ENABLE),true)
$(hide) echo "board_bvb_enable=true" >> $(zip_root)/META/misc_info.txt
$(hide) echo "board_bvb_make_boot_image_args=$(BOARD_BVB_MAKE_BOOT_IMAGE_ARGS)" >> $(zip_root)/META/misc_info.txt
$(hide) echo "board_bvb_sign_boot_image_args=$(BOARD_BVB_SIGN_BOOT_IMAGE_ARGS)" >> $(zip_root)/META/misc_info.txt
$(hide) echo "board_bvb_algorithm=$(BOARD_BVB_ALGORITHM)" >> $(zip_root)/META/misc_info.txt
$(hide) echo "board_bvb_key_path=$(BOARD_BVB_KEY_PATH)" >> $(zip_root)/META/misc_info.txt
$(hide) echo "board_bvb_rollback_index=$(BOARD_BVB_ROLLBACK_INDEX)" >> $(zip_root)/META/misc_info.txt
$(hide) echo "board_bvb_add_image_hashes_args=$(BOARD_BVB_ADD_IMAGE_HASHES_ARGS)" >> $(zip_root)/META/misc_info.txt
endif
$(call generate-userimage-prop-dictionary, $(zip_root)/META/misc_info.txt)
ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)

View File

@ -518,6 +518,11 @@ MKBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX)
else
MKBOOTIMG := $(BOARD_CUSTOM_MKBOOTIMG)
endif
ifeq (,$(strip $(BOARD_CUSTOM_BVBTOOL)))
BVBTOOL := $(HOST_OUT_EXECUTABLES)/bvbtool$(HOST_EXECUTABLE_SUFFIX)
else
BVBTOOL := $(BOARD_CUSTOM_BVBTOOL)
endif
APICHECK := $(HOST_OUT_EXECUTABLES)/apicheck$(HOST_EXECUTABLE_SUFFIX)
FS_GET_STATS := $(HOST_OUT_EXECUTABLES)/fs_get_stats$(HOST_EXECUTABLE_SUFFIX)
MAKE_EXT4FS := $(HOST_OUT_EXECUTABLES)/make_ext4fs$(HOST_EXECUTABLE_SUFFIX)

View File

@ -31,7 +31,9 @@ if sys.hexversion < 0x02070000:
import datetime
import errno
import os
import shlex
import shutil
import subprocess
import tempfile
import zipfile
@ -48,12 +50,12 @@ OPTIONS.verity_signer_path = None
def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None):
"""Turn the contents of SYSTEM into a system image and store it in
output_zip."""
output_zip. Returns the name of the system image file."""
prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "system.img")
if os.path.exists(prebuilt_path):
print "system.img already exists in %s, no need to rebuild..." % (prefix,)
return
return prebuilt_path
def output_sink(fn, data):
ofile = open(os.path.join(OPTIONS.input_tmp, "SYSTEM", fn), "w")
@ -68,8 +70,23 @@ def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None):
block_list = common.MakeTempFile(prefix="system-blocklist-", suffix=".map")
imgname = BuildSystem(OPTIONS.input_tmp, OPTIONS.info_dict,
block_list=block_list)
# If requested, calculate and add dm-verity integrity hashes and
# metadata to system.img.
if OPTIONS.info_dict.get("board_bvb_enable", None) == "true":
bvbtool = os.getenv('BVBTOOL') or "bvbtool"
cmd = [bvbtool, "add_image_hashes", "--image", imgname]
args = OPTIONS.info_dict.get("board_bvb_add_image_hashes_args", None)
if args and args.strip():
cmd.extend(shlex.split(args))
p = common.Run(cmd, stdout=subprocess.PIPE)
p.communicate()
assert p.returncode == 0, "bvbtool add_image_hashes of %s image failed" % (
os.path.basename(OPTIONS.input_tmp),)
common.ZipWrite(output_zip, imgname, prefix + "system.img")
common.ZipWrite(output_zip, block_list, prefix + "system.map")
return imgname
def BuildSystem(input_dir, info_dict, block_list=None):
@ -275,19 +292,36 @@ def AddImagesToTargetFiles(filename):
compression=zipfile.ZIP_DEFLATED)
has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true")
system_root_image = (OPTIONS.info_dict.get("system_root_image", None) == "true")
board_bvb_enable = (OPTIONS.info_dict.get("board_bvb_enable", None) == "true")
# Brillo Verified Boot is incompatible with certain
# configurations. Explicitly check for these.
if board_bvb_enable:
assert not has_recovery, "has_recovery incompatible with bvb"
assert not system_root_image, "system_root_image incompatible with bvb"
assert not OPTIONS.rebuild_recovery, "rebuild_recovery incompatible with bvb"
assert not has_vendor, "VENDOR images currently incompatible with bvb"
def banner(s):
print "\n\n++++ " + s + " ++++\n\n"
banner("boot")
prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
boot_image = None
if os.path.exists(prebuilt_path):
banner("boot")
print "boot.img already exists in IMAGES/, no need to rebuild..."
if OPTIONS.rebuild_recovery:
boot_image = common.GetBootableImage(
"IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
else:
if board_bvb_enable:
# With Brillo Verified Boot, we need to build system.img before
# boot.img since the latter includes the dm-verity root hash and
# salt for the former.
pass
else:
banner("boot")
boot_image = common.GetBootableImage(
"IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
if boot_image:
@ -310,7 +344,17 @@ def AddImagesToTargetFiles(filename):
recovery_image.AddToZip(output_zip)
banner("system")
AddSystem(output_zip, recovery_img=recovery_image, boot_img=boot_image)
system_img_path = AddSystem(
output_zip, recovery_img=recovery_image, boot_img=boot_image)
if OPTIONS.info_dict.get("board_bvb_enable", None) == "true":
# If we're using Brillo Verified Boot, we can now build boot.img
# given that we have system.img.
banner("boot")
boot_image = common.GetBootableImage(
"IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT",
system_img_path=system_img_path)
if boot_image:
boot_image.AddToZip(output_zip)
if has_vendor:
banner("vendor")
AddVendor(output_zip)

View File

@ -473,8 +473,114 @@ def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
return data
def _BuildBvbBootableImage(sourcedir, fs_config_file, system_img_path,
info_dict=None, has_ramdisk=False):
"""Build a bootable image compatible with Brillo Verified Boot from the
specified sourcedir.
Take a kernel, cmdline, system image path, and optionally a ramdisk
directory from the input (in 'sourcedir'), and turn them into a boot
image. Return the image data, or None if sourcedir does not appear
to contains files for building the requested image.
"""
def make_ramdisk():
ramdisk_img = tempfile.NamedTemporaryFile()
if os.access(fs_config_file, os.F_OK):
cmd = ["mkbootfs", "-f", fs_config_file,
os.path.join(sourcedir, "RAMDISK")]
else:
cmd = ["mkbootfs", os.path.join(sourcedir, "RAMDISK")]
p1 = Run(cmd, stdout=subprocess.PIPE)
p2 = Run(["minigzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
p2.wait()
p1.wait()
assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (sourcedir,)
assert p2.returncode == 0, "minigzip of %s ramdisk failed" % (sourcedir,)
return ramdisk_img
if not os.access(os.path.join(sourcedir, "kernel"), os.F_OK):
return None
if has_ramdisk and not os.access(os.path.join(sourcedir, "RAMDISK"), os.F_OK):
return None
if info_dict is None:
info_dict = OPTIONS.info_dict
img = tempfile.NamedTemporaryFile()
if has_ramdisk:
ramdisk_img = make_ramdisk()
# use BVBTOOL from environ, or "bvbtool" if empty or not set
bvbtool = os.getenv('BVBTOOL') or "bvbtool"
# First, create boot.img.
cmd = [bvbtool, "make_boot_image"]
fn = os.path.join(sourcedir, "cmdline")
if os.access(fn, os.F_OK):
cmd.append("--kernel_cmdline")
cmd.append(open(fn).read().rstrip("\n"))
cmd.extend(["--kernel", os.path.join(sourcedir, "kernel")])
if has_ramdisk:
cmd.extend(["--initrd", ramdisk_img.name])
cmd.extend(["--rootfs_with_hashes", system_img_path])
args = info_dict.get("board_bvb_make_boot_image_args", None)
if args and args.strip():
cmd.extend(shlex.split(args))
rollback_index = info_dict.get("board_bvb_rollback_index", None)
if rollback_index and rollback_index.strip():
cmd.extend(["--rollback_index", rollback_index.strip()])
cmd.extend(["--output", img.name])
p = Run(cmd, stdout=subprocess.PIPE)
p.communicate()
assert p.returncode == 0, "bvbtool make_boot_image of %s image failed" % (
os.path.basename(sourcedir),)
# Then, sign boot.img.
cmd = [bvbtool, "sign_boot_image", "--image", img.name]
algorithm = info_dict.get("board_bvb_algorithm", None)
key_path = info_dict.get("board_bvb_key_path", None)
if algorithm and algorithm.strip() and key_path and key_path.strip():
cmd.extend(["--algorithm", algorithm, "--key", key_path])
else:
cmd.extend(["--algorithm", "SHA256_RSA4096"])
cmd.extend(["--key", "system/bvb/test/testkey_rsa4096.pem"])
args = info_dict.get("board_bvb_sign_boot_image_args", None)
if args and args.strip():
cmd.extend(shlex.split(args))
p = Run(cmd, stdout=subprocess.PIPE)
p.communicate()
assert p.returncode == 0, "bvbtool sign_boot_image of %s image failed" % (
os.path.basename(sourcedir),)
img.seek(os.SEEK_SET, 0)
data = img.read()
if has_ramdisk:
ramdisk_img.close()
img.close()
return data
def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
info_dict=None):
info_dict=None, system_img_path=None):
"""Return a File object with the desired bootable image.
Look for it in 'unpack_dir'/BOOTABLE_IMAGES under the name 'prebuilt_name',
@ -504,6 +610,11 @@ def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
info_dict.get("recovery_as_boot") == "true")
fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
if info_dict.get("board_bvb_enable", None) == "true":
data = _BuildBvbBootableImage(os.path.join(unpack_dir, tree_subdir),
os.path.join(unpack_dir, fs_config),
system_img_path, info_dict, has_ramdisk)
else:
data = _BuildBootableImage(os.path.join(unpack_dir, tree_subdir),
os.path.join(unpack_dir, fs_config),
info_dict, has_ramdisk)