Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fd6f7513 authored by Geremy Condra's avatar Geremy Condra
Browse files

Add support for verity builds to the build system.

Change-Id: I3ef908d8d52ec88de453b161bbc3f198517a72f1
parent fdd8ee46
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -369,6 +369,8 @@ ALL_DEFAULT_INSTALLED_MODULES += $(event_log_tags_file)
# Targets for boot/OS images
# #################################################################

VERITY_SIGNER_CMD := $(HOST_OUT_EXECUTABLES)/verity_signer

# -----------------------------------------------------------------
# the ramdisk
INTERNAL_RAMDISK_FILES := $(filter $(TARGET_ROOT_OUT)/%, \
@@ -601,6 +603,10 @@ INTERNAL_USERIMAGES_DEPS := $(MKYAFFS2)
endif
INTERNAL_USERIMAGES_BINARY_PATHS := $(sort $(dir $(INTERNAL_USERIMAGES_DEPS)))

ifeq (true, $(PRODUCT_SUPPORTS_VERITY))
INTERNAL_USERIMAGES_DEPS += $(HOST_OUT_EXECUTABLES)/verity_signer
endif

SELINUX_FC := $(TARGET_ROOT_OUT)/file_contexts
INTERNAL_USERIMAGES_DEPS += $(SELINUX_FC)

@@ -814,7 +820,12 @@ BUILT_SYSTEMIMAGE := $(systemimage_intermediates)/system.img
define build-systemimage-target
  @echo "Target system fs image: $(1)"
  @mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt
  $(call generate-userimage-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt, skip_fsck=true)
  $(call generate-userimage-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt, \
      skip_fsck=true \
      verity=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY) \
      verity_block_device=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_PARTITION) \
      verity_key=$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY) \
      verity_signer_cmd=$(VERITY_SIGNER_CMD))
  $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
      ./build/tools/releasetools/build_image.py \
      $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1)
+4 −1
Original line number Diff line number Diff line
@@ -97,7 +97,10 @@ _product_var_list := \
    PRODUCT_FACTORY_RAMDISK_MODULES \
    PRODUCT_FACTORY_BUNDLE_MODULES \
    PRODUCT_RUNTIMES \
    PRODUCT_BOOT_JARS
    PRODUCT_BOOT_JARS \
    PRODUCT_SUPPORTS_VERITY \
    PRODUCT_VERITY_PARTITION \
    PRODUCT_VERITY_SIGNING_KEY


define dump-product
+12 −0
Original line number Diff line number Diff line
LOCAL_PATH:= $(call my-dir)

#######################################
# verity_key
include $(CLEAR_VARS)

LOCAL_MODULE := verity_key
LOCAL_SRC_FILES := $(LOCAL_MODULE)
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)

include $(BUILD_PREBUILT)
+184 −7
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ import os
import os.path
import subprocess
import sys
import commands
import shutil

def RunCommand(cmd):
  """ Echo and run the given command
@@ -38,6 +40,167 @@ def RunCommand(cmd):
  p.communicate()
  return p.returncode

def GetVerityTreeSize(partition_size):
  cmd = "system/extras/verity/build_verity_tree.py -s %d"
  cmd %= partition_size
  status, output = commands.getstatusoutput(cmd)
  if status:
    print output
    return False, 0
  return True, int(output)

def GetVerityMetadataSize(partition_size):
  cmd = "system/extras/verity/build_verity_metadata.py -s %d"
  cmd %= partition_size
  status, output = commands.getstatusoutput(cmd)
  if status:
    print output
    return False, 0
  return True, int(output)

def AdjustPartitionSizeForVerity(partition_size):
  """Modifies the provided partition size to account for the verity metadata.

  This information is used to size the created image appropriately.
  Args:
    partition_size: the size of the partition to be verified.
  Returns:
    The size of the partition adjusted for verity metadata.
  """
  success, verity_tree_size = GetVerityTreeSize(partition_size)
  if not success:
    return 0;
  success, verity_metadata_size = GetVerityMetadataSize(partition_size)
  if not success:
    return 0
  return partition_size - verity_tree_size - verity_metadata_size

def BuildVerityTree(unsparse_image_path, verity_image_path, partition_size, prop_dict):
  cmd = ("system/extras/verity/build_verity_tree.py %s %s %d" %
            (unsparse_image_path, verity_image_path, partition_size))
  print cmd
  status, output = commands.getstatusoutput(cmd)
  if status:
    print "Could not build verity tree! Error: %s" % output
    return False
  root, salt = output.split()
  prop_dict["verity_root_hash"] = root
  prop_dict["verity_salt"] = salt
  return True

def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt,
                        block_device, signer_path, key):
  cmd = ("system/extras/verity/build_verity_metadata.py %s %s %s %s %s %s %s" %
              (image_size,
              verity_metadata_path,
              root_hash,
              salt,
              block_device,
              signer_path,
              key))
  print cmd
  status, output = commands.getstatusoutput(cmd)
  if status:
    print "Could not build verity metadata! Error: %s" % output
    return False
  return True

def Append2Simg(sparse_image_path, unsparse_image_path, error_message):
  """Appends the unsparse image to the given sparse image.

  Args:
    sparse_image_path: the path to the (sparse) image
    unsparse_image_path: the path to the (unsparse) image
  Returns:
    True on success, False on failure.
  """
  cmd = "append2simg %s %s"
  cmd %= (sparse_image_path, unsparse_image_path)
  print cmd
  status, output = commands.getstatusoutput(cmd)
  if status:
    print "%s: %s" % (error_message, output)
    return False
  return True

def BuildVerifiedImage(data_image_path, verity_image_path, verity_metadata_path):
  if not Append2Simg(data_image_path, verity_metadata_path, "Could not append verity metadata!"):
    return False
  if not Append2Simg(data_image_path, verity_image_path, "Could not append verity tree!"):
    return False
  return True

def UnsparseImage(sparse_image_path):
  img_dir = os.path.dirname(sparse_image_path)
  unsparse_image_path = "unsparse_" + os.path.basename(sparse_image_path)
  unsparse_image_path = os.path.join(img_dir, unsparse_image_path)
  if os.path.exists(unsparse_image_path):
    return True, unsparse_image_path
  inflate_command = ["simg2img", sparse_image_path, unsparse_image_path]
  exit_code = RunCommand(inflate_command)
  if exit_code != 0:
    os.remove(unsparse_image_path)
    return False, None
  return True, unsparse_image_path

def MakeVerityEnabledImage(out_file, prop_dict):
  """Creates an image that is verifiable using dm-verity.

  Args:
    out_file: the location to write the verifiable image at
    prop_dict: a dictionary of properties required for image creation and verification
  Returns:
    True on success, False otherwise.
  """
  # get properties
  image_size = prop_dict["partition_size"]
  part_size = int(prop_dict["original_partition_size"])
  block_dev = prop_dict["verity_block_device"]
  signer_key = prop_dict["verity_key"]
  signer_path = prop_dict["verity_signer_cmd"]

  # make a tempdir
  tempdir_name = os.path.join(os.path.dirname(out_file), "verity_images")
  if os.path.exists(tempdir_name):
    shutil.rmtree(tempdir_name)
  os.mkdir(tempdir_name)

  # get partial image paths
  verity_image_path = os.path.join(tempdir_name, "verity.img")
  verity_metadata_path = os.path.join(tempdir_name, "verity_metadata.img")
  success, unsparse_image_path = UnsparseImage(out_file)
  if not success:
    shutil.rmtree(tempdir_name)
    return False

  # build the verity tree and get the root hash and salt
  if not BuildVerityTree(unsparse_image_path, verity_image_path, part_size, prop_dict):
    shutil.rmtree(tempdir_name)
    return False

  # build the metadata blocks
  root_hash = prop_dict["verity_root_hash"]
  salt = prop_dict["verity_salt"]
  if not BuildVerityMetadata(image_size,
                              verity_metadata_path,
                              root_hash,
                              salt,
                              block_dev,
                              signer_path,
                              signer_key):
    shutil.rmtree(tempdir_name)
    return False

  # build the full verified image
  if not BuildVerifiedImage(out_file,
                            verity_image_path,
                            verity_metadata_path):
    shutil.rmtree(tempdir_name)
    return False

  shutil.rmtree(tempdir_name)
  return True

def BuildImage(in_dir, prop_dict, out_file):
  """Build an image to out_file from in_dir with property prop_dict.

@@ -52,6 +215,16 @@ def BuildImage(in_dir, prop_dict, out_file):
  build_command = []
  fs_type = prop_dict.get("fs_type", "")
  run_fsck = False

  # adjust the partition size to make room for the hashes if this is to be verified
  if prop_dict.get("verity") == "true":
    partition_size = int(prop_dict.get("partition_size"))
    adjusted_size = AdjustPartitionSizeForVerity(partition_size)
    if not adjusted_size:
      return False
    prop_dict["partition_size"] = str(adjusted_size)
    prop_dict["original_partition_size"] = str(partition_size)

  if fs_type.startswith("ext"):
    build_command = ["mkuserimg.sh"]
    if "extfs_sparse_flag" in prop_dict:
@@ -77,14 +250,14 @@ def BuildImage(in_dir, prop_dict, out_file):
  if exit_code != 0:
    return False

  # create the verified image if this is to be verified
  if prop_dict.get("verity") == "true":
    if not MakeVerityEnabledImage(out_file, prop_dict):
      return False

  if run_fsck and prop_dict.get("skip_fsck") != "true":
    # Inflate the sparse image
    unsparse_image = os.path.join(
        os.path.dirname(out_file), "unsparse_" + os.path.basename(out_file))
    inflate_command = ["simg2img", out_file, unsparse_image]
    exit_code = RunCommand(inflate_command)
    if exit_code != 0:
      os.remove(unsparse_image)
    success, unsparse_image_path = UnsparseImage(out_file)
    if not success:
      return False

    # Run e2fsck on the inflated image file
@@ -114,6 +287,10 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
      "mkyaffs2_extra_flags",
      "selinux_fc",
      "skip_fsck",
      "verity",
      "verity_block_device",
      "verity_key",
      "verity_signer_cmd"
      )
  for p in common_props:
    copy_prop(p, p)