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

Commit ee7dbd22 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Resign microdroid_vendor.img with avb_vendor_key" into main

parents 8a93c785 97603567
Loading
Loading
Loading
Loading
+110 −0
Original line number Diff line number Diff line
@@ -795,6 +795,16 @@ def ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip, misc_info,
        # Copy it verbatim if we allow the file to exist.
        common.ZipWriteStr(output_tf_zip, out_info, data)

    # Sign microdroid_vendor.img.
    elif filename == "VENDOR/etc/avf/microdroid/microdroid_vendor.img":
      vendor_key = OPTIONS.avb_keys.get("vendor")
      vendor_algorithm = OPTIONS.avb_algorithms.get("vendor")
      with tempfile.NamedTemporaryFile() as image:
        image.write(data)
        image.flush()
        ReplaceKeyInAvbHashtreeFooter(image, vendor_key, vendor_algorithm,
            misc_info)
        common.ZipWrite(output_tf_zip, image.name, filename)
    # A non-APK file; copy it verbatim.
    else:
      common.ZipWriteStr(output_tf_zip, out_info, data)
@@ -812,6 +822,106 @@ def ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip, misc_info,
  # Write back misc_info with the latest values.
  ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)

def ReplaceKeyInAvbHashtreeFooter(image, new_key, new_algorithm, misc_info):
  # Get avb information about the image by parsing avbtool info_image.
  def GetAvbInfo(avbtool, image_name):
    # Get information with raw string by `avbtool info_image`.
    info_raw = common.RunAndCheckOutput([
      avbtool, 'info_image',
      '--image', image_name
    ])

    # line_matcher is for parsing each output line of `avbtool info_image`.
    # example string input: "      Hash Algorithm:        sha1"
    # example matched input: ("      ", "Hash Algorithm", "sha1")
    line_matcher = re.compile(r'^(\s*)([^:]+):\s*(.*)$')
    # prop_matcher is for parsing value part of 'Prop' in `avbtool info_image`.
    # example string input: "example_prop_key -> 'example_prop_value'"
    # example matched output: ("example_prop_key", "example_prop_value")
    prop_matcher = re.compile(r"(.+)\s->\s'(.+)'")
    info = {}
    indent_stack = [[-1, info]]
    for line_info_raw in info_raw.split('\n'):
      # Parse the line
      line_info_parsed = line_matcher.match(line_info_raw)
      if not line_info_parsed:
        continue
      indent = len(line_info_parsed.group(1))
      key = line_info_parsed.group(2).strip()
      value = line_info_parsed.group(3).strip()

      # Pop indentation stack
      while indent <= indent_stack[-1][0]:
        del indent_stack[-1]

      # Insert information into 'info'.
      cur_info = indent_stack[-1][1]
      if value == "":
        if key == "Descriptors":
          empty_list = []
          cur_info[key] = empty_list
          indent_stack.append([indent, empty_list])
        else:
          empty_dict = {}
          cur_info.append({key:empty_dict})
          indent_stack.append([indent, empty_dict])
      elif key == "Prop":
        prop_parsed = prop_matcher.match(value)
        if not prop_parsed:
          raise ValueError(
              "Failed to parse prop while getting avb information.")
        cur_info.append({key:{prop_parsed.group(1):prop_parsed.group(2)}})
      else:
        cur_info[key] = value

    return info

  # Get hashtree descriptor from info
  def GetAvbHashtreeDescriptor(avb_info):
    hashtree_descriptors = tuple(filter(lambda x: "Hashtree descriptor" in x,
        info.get('Descriptors')))
    if len(hashtree_descriptors) != 1:
      raise ValueError("The number of hashtree descriptor is not 1.")
    return hashtree_descriptors[0]["Hashtree descriptor"]

  # Get avb info
  avbtool = misc_info['avb_avbtool']
  info = GetAvbInfo(avbtool, image.name)
  hashtree_descriptor = GetAvbHashtreeDescriptor(info)

  # Generate command
  cmd = [avbtool, 'add_hashtree_footer',
    '--key', new_key,
    '--algorithm', new_algorithm,
    '--partition_name', hashtree_descriptor.get("Partition Name"),
    '--partition_size', info.get("Image size").removesuffix(" bytes"),
    '--hash_algorithm', hashtree_descriptor.get("Hash Algorithm"),
    '--salt', hashtree_descriptor.get("Salt"),
    '--do_not_generate_fec',
    '--image', image.name
  ]

  # Append properties into command
  props = map(lambda x: x.get("Prop"), filter(lambda x: "Prop" in x,
      info.get('Descriptors')))
  for prop_wrapped in props:
    prop = tuple(prop_wrapped.items())
    if len(prop) != 1:
      raise ValueError("The number of property is not 1.")
    cmd.append('--prop')
    cmd.append(prop[0][0] + ':' + prop[0][1])

  # Replace Hashtree Footer with new key
  common.RunAndCheckOutput(cmd)

  # Check root digest is not changed
  new_info = GetAvbInfo(avbtool, image.name)
  new_hashtree_descriptor = GetAvbHashtreeDescriptor(info)
  root_digest = hashtree_descriptor.get("Root Digest")
  new_root_digest = new_hashtree_descriptor.get("Root Digest")
  assert root_digest == new_root_digest, \
      ("Root digest in hashtree descriptor shouldn't be changed. Old: {}, New: "
       "{}").format(root_digest, new_root_digest)

def ReplaceCerts(data):
  """Replaces all the occurences of X.509 certs with the new ones.