Example #1
0
    def test_ApexApkSigner_signApk(self):
        apex_path = os.path.join(self.testdata_dir, 'has_apk.apex')
        signer = apex_utils.ApexApkSigner(apex_path, None, None)
        apk_keys = {
            'wifi-service-resources.apk':
            os.path.join(self.testdata_dir, 'testkey')
        }

        self.payload_key = os.path.join(self.testdata_dir,
                                        'testkey_RSA4096.key')
        payload_pubkey = common.ExtractAvbPublicKey('avbtool',
                                                    self.payload_key)
        signer.ProcessApexFile(apk_keys, self.payload_key, payload_pubkey)
Example #2
0
def SignUncompressedApex(avbtool, apex_file, payload_key, container_key,
                         container_pw, apk_keys, codename_to_api_level_map,
                         no_hashtree, signing_args=None):
  """Signs the current uncompressed APEX with the given payload/container keys.

  Args:
    apex_file: Uncompressed APEX file.
    payload_key: The path to payload signing key (w/ extension).
    container_key: The path to container signing key (w/o extension).
    container_pw: The matching password of the container_key, or None.
    apk_keys: A dict that holds the signing keys for apk files.
    codename_to_api_level_map: A dict that maps from codename to API level.
    no_hashtree: Don't include hashtree in the signed APEX.
    signing_args: Additional args to be passed to the payload signer.

  Returns:
    The path to the signed APEX file.
  """
  # 1. Extract the apex payload image and sign the containing apk files. Repack
  # the apex file after signing.
  apk_signer = ApexApkSigner(apex_file, container_pw,
                             codename_to_api_level_map)
  apex_file = apk_signer.ProcessApexFile(apk_keys, payload_key, signing_args)

  # 2a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
  # payload_key.
  payload_dir = common.MakeTempDir(prefix='apex-payload-')
  with zipfile.ZipFile(apex_file) as apex_fd:
    payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)
    zip_items = apex_fd.namelist()

  payload_info = ParseApexPayloadInfo(avbtool, payload_file)
  if no_hashtree is None:
    no_hashtree = payload_info.get("Tree Size", 0) == 0
  SignApexPayload(
      avbtool,
      payload_file,
      payload_key,
      payload_info['apex.key'],
      payload_info['Algorithm'],
      payload_info['Salt'],
      payload_info['Hash Algorithm'],
      no_hashtree,
      signing_args)

  # 2b. Update the embedded payload public key.
  payload_public_key = common.ExtractAvbPublicKey(avbtool, payload_key)
  common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
  if APEX_PUBKEY in zip_items:
    common.ZipDelete(apex_file, APEX_PUBKEY)
  apex_zip = zipfile.ZipFile(apex_file, 'a', allowZip64=True)
  common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
  common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)
  common.ZipClose(apex_zip)

  # 3. Sign the APEX container with container_key.
  signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')

  # Specify the 4K alignment when calling SignApk.
  extra_signapk_args = OPTIONS.extra_signapk_args[:]
  extra_signapk_args.extend(['-a', '4096', '--align-file-size'])

  password = container_pw.get(container_key) if container_pw else None
  common.SignFile(
      apex_file,
      signed_apex,
      container_key,
      password,
      codename_to_api_level_map=codename_to_api_level_map,
      extra_signapk_args=extra_signapk_args)

  return signed_apex
Example #3
0
def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
                       apk_keys, apex_keys, key_passwords,
                       platform_api_level, codename_to_api_level_map,
                       compressed_extension):
  # maxsize measures the maximum filename length, including the ones to be
  # skipped.
  maxsize = max(
      [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
       if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
  system_root_image = misc_info.get("system_root_image") == "true"

  for info in input_tf_zip.infolist():
    filename = info.filename
    if filename.startswith("IMAGES/"):
      continue

    # Skip split super images, which will be re-generated during signing.
    if filename.startswith("OTA/") and filename.endswith(".img"):
      continue

    data = input_tf_zip.read(filename)
    out_info = copy.copy(info)
    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
        filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)

    if is_apk and should_be_skipped:
      # Copy skipped APKs verbatim.
      print(
          "NOT signing: %s\n"
          "        (skipped due to matching prefix)" % (filename,))
      common.ZipWriteStr(output_tf_zip, out_info, data)

    # Sign APKs.
    elif is_apk:
      name = os.path.basename(filename)
      if is_compressed:
        name = name[:-len(compressed_extension)]

      key = apk_keys[name]
      if key not in common.SPECIAL_CERT_STRINGS:
        print("    signing: %-*s (%s)" % (maxsize, name, key))
        signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
                              codename_to_api_level_map, is_compressed)
        common.ZipWriteStr(output_tf_zip, out_info, signed_data)
      else:
        # an APK we're not supposed to sign.
        print(
            "NOT signing: %s\n"
            "        (skipped due to special cert string)" % (name,))
        common.ZipWriteStr(output_tf_zip, out_info, data)

    # Sign bundled APEX files.
    elif filename.startswith("SYSTEM/apex") and filename.endswith(".apex"):
      name = os.path.basename(filename)
      payload_key, container_key = apex_keys[name]

      # We've asserted not having a case with only one of them PRESIGNED.
      if (payload_key not in common.SPECIAL_CERT_STRINGS and
          container_key not in common.SPECIAL_CERT_STRINGS):
        print("    signing: %-*s container (%s)" % (
            maxsize, name, container_key))
        print("           : %-*s payload   (%s)" % (
            maxsize, name, payload_key))

        signed_apex = apex_utils.SignApex(
            data,
            payload_key,
            container_key,
            key_passwords[container_key],
            codename_to_api_level_map,
            OPTIONS.avb_extra_args.get('apex'))
        common.ZipWrite(output_tf_zip, signed_apex, filename)

      else:
        print(
            "NOT signing: %s\n"
            "        (skipped due to special cert string)" % (name,))
        common.ZipWriteStr(output_tf_zip, out_info, data)

    # AVB public keys for the installed APEXes, which will be updated later.
    elif (os.path.dirname(filename) == 'SYSTEM/etc/security/apex' and
          filename != 'SYSTEM/etc/security/apex/'):
      continue

    # System properties.
    elif filename in ("SYSTEM/build.prop",
                      "VENDOR/build.prop",
                      "SYSTEM/vendor/build.prop",
                      "ODM/build.prop",  # legacy
                      "ODM/etc/build.prop",
                      "VENDOR/odm/build.prop",  # legacy
                      "VENDOR/odm/etc/build.prop",
                      "PRODUCT/build.prop",
                      "SYSTEM/product/build.prop",
                      "PRODUCT_SERVICES/build.prop",
                      "SYSTEM/product_services/build.prop",
                      "SYSTEM/etc/prop.default",
                      "BOOT/RAMDISK/prop.default",
                      "BOOT/RAMDISK/default.prop",  # legacy
                      "ROOT/default.prop",  # legacy
                      "RECOVERY/RAMDISK/prop.default",
                      "RECOVERY/RAMDISK/default.prop"):  # legacy
      print("Rewriting %s:" % (filename,))
      if stat.S_ISLNK(info.external_attr >> 16):
        new_data = data
      else:
        new_data = RewriteProps(data)
      common.ZipWriteStr(output_tf_zip, out_info, new_data)

    # Replace the certs in *mac_permissions.xml (there could be multiple, such
    # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
    elif filename.endswith("mac_permissions.xml"):
      print("Rewriting %s with new keys." % (filename,))
      new_data = ReplaceCerts(data)
      common.ZipWriteStr(output_tf_zip, out_info, new_data)

    # Ask add_img_to_target_files to rebuild the recovery patch if needed.
    elif filename in ("SYSTEM/recovery-from-boot.p",
                      "SYSTEM/etc/recovery.img",
                      "SYSTEM/bin/install-recovery.sh"):
      OPTIONS.rebuild_recovery = True

    # Don't copy OTA certs if we're replacing them.
    elif (
        OPTIONS.replace_ota_keys and
        filename in (
            "BOOT/RAMDISK/system/etc/security/otacerts.zip",
            "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
            "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
            "SYSTEM/etc/security/otacerts.zip",
            "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
      pass

    # Skip META/misc_info.txt since we will write back the new values later.
    elif filename == "META/misc_info.txt":
      pass

    # Skip verity public key if we will replace it.
    elif (OPTIONS.replace_verity_public_key and
          filename in ("BOOT/RAMDISK/verity_key",
                       "ROOT/verity_key")):
      pass

    # Skip verity keyid (for system_root_image use) if we will replace it.
    elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
      pass

    # Skip the care_map as we will regenerate the system/vendor images.
    elif filename == "META/care_map.pb" or filename == "META/care_map.txt":
      pass

    # Updates system_other.avbpubkey in /product/etc/.
    elif filename in (
        "PRODUCT/etc/security/avb/system_other.avbpubkey",
        "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
      # Only update system_other's public key, if the corresponding signing
      # key is specified via --avb_system_other_key.
      signing_key = OPTIONS.avb_keys.get("system_other")
      if signing_key:
        public_key = common.ExtractAvbPublicKey(signing_key)
        print("    Rewriting AVB public key of system_other in /product")
        common.ZipWrite(output_tf_zip, public_key, filename)

    # Should NOT sign boot-debug.img.
    elif filename in (
        "BOOT/RAMDISK/force_debuggable",
        "RECOVERY/RAMDISK/force_debuggable"
        "RECOVERY/RAMDISK/first_stage_ramdisk/force_debuggable"):
      raise common.ExternalError("debuggable boot.img cannot be signed")

    # A non-APK file; copy it verbatim.
    else:
      common.ZipWriteStr(output_tf_zip, out_info, data)

  if OPTIONS.replace_ota_keys:
    ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)

  # Replace the keyid string in misc_info dict.
  if OPTIONS.replace_verity_private_key:
    ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])

  if OPTIONS.replace_verity_public_key:
    dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key"
    # We are replacing the one in boot image only, since the one under
    # recovery won't ever be needed.
    ReplaceVerityPublicKey(
        output_tf_zip, dest, OPTIONS.replace_verity_public_key[1])

  # Replace the keyid string in BOOT/cmdline.
  if OPTIONS.replace_verity_keyid:
    ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
                       OPTIONS.replace_verity_keyid[1])

  # Replace the AVB signing keys, if any.
  ReplaceAvbSigningKeys(misc_info)

  # Write back misc_info with the latest values.
  ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
Example #4
0
def SignApex(apex_data,
             payload_key,
             container_key,
             container_pw,
             codename_to_api_level_map,
             signing_args=None):
    """Signs the current APEX with the given payload/container keys.

  Args:
    apex_data: Raw APEX data.
    payload_key: The path to payload signing key (w/ extension).
    container_key: The path to container signing key (w/o extension).
    container_pw: The matching password of the container_key, or None.
    codename_to_api_level_map: A dict that maps from codename to API level.
    signing_args: Additional args to be passed to the payload signer.

  Returns:
    The path to the signed APEX file.
  """
    apex_file = common.MakeTempFile(prefix='apex-', suffix='.apex')
    with open(apex_file, 'wb') as apex_fp:
        apex_fp.write(apex_data)

    APEX_PAYLOAD_IMAGE = 'apex_payload.img'
    APEX_PUBKEY = 'apex_pubkey'

    # 1a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
    # payload_key.
    payload_dir = common.MakeTempDir(prefix='apex-payload-')
    with zipfile.ZipFile(apex_file) as apex_fd:
        payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)
        zip_items = apex_fd.namelist()

    payload_info = ParseApexPayloadInfo(payload_file)
    SignApexPayload(payload_file, payload_key, payload_info['apex.key'],
                    payload_info['Algorithm'], payload_info['Salt'],
                    signing_args)

    # 1b. Update the embedded payload public key.
    payload_public_key = common.ExtractAvbPublicKey(payload_key)

    common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
    if APEX_PUBKEY in zip_items:
        common.ZipDelete(apex_file, APEX_PUBKEY)
    apex_zip = zipfile.ZipFile(apex_file, 'a')
    common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
    common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)
    common.ZipClose(apex_zip)

    # 2. Align the files at page boundary (same as in apexer).
    aligned_apex = common.MakeTempFile(prefix='apex-container-',
                                       suffix='.apex')
    common.RunAndCheckOutput(
        ['zipalign', '-f', '4096', apex_file, aligned_apex])

    # 3. Sign the APEX container with container_key.
    signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')

    # Specify the 4K alignment when calling SignApk.
    extra_signapk_args = OPTIONS.extra_signapk_args[:]
    extra_signapk_args.extend(['-a', '4096'])

    common.SignFile(aligned_apex,
                    signed_apex,
                    container_key,
                    container_pw,
                    codename_to_api_level_map=codename_to_api_level_map,
                    extra_signapk_args=extra_signapk_args)

    return signed_apex
def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
                       apk_keys, apex_keys, key_passwords,
                       platform_api_level, codename_to_api_level_map,
                       compressed_extension):
  # maxsize measures the maximum filename length, including the ones to be
  # skipped.
  maxsize = max(
      [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
       if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
  system_root_image = misc_info.get("system_root_image") == "true"

  for info in input_tf_zip.infolist():
    filename = info.filename
    if filename.startswith("IMAGES/"):
      continue

    # Skip OTA-specific images (e.g. split super images), which will be
    # re-generated during signing.
    if filename.startswith("OTA/") and filename.endswith(".img"):
      continue

    data = input_tf_zip.read(filename)
    out_info = copy.copy(info)
    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
        filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)

    if is_apk and should_be_skipped:
      # Copy skipped APKs verbatim.
      print(
          "NOT signing: %s\n"
          "        (skipped due to matching prefix)" % (filename,))
      common.ZipWriteStr(output_tf_zip, out_info, data)

    # Sign APKs.
    elif is_apk:
      name = os.path.basename(filename)
      if is_compressed:
        name = name[:-len(compressed_extension)]

      key = apk_keys[name]
      if key not in common.SPECIAL_CERT_STRINGS:
        print("    signing: %-*s (%s)" % (maxsize, name, key))
        signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
                              codename_to_api_level_map, is_compressed)
        common.ZipWriteStr(output_tf_zip, out_info, signed_data)
      else:
        # an APK we're not supposed to sign.
        print(
            "NOT signing: %s\n"
            "        (skipped due to special cert string)" % (name,))
        common.ZipWriteStr(output_tf_zip, out_info, data)

    # Sign bundled APEX files.
    elif filename.startswith("SYSTEM/apex") and filename.endswith(".apex"):
      name = os.path.basename(filename)
      payload_key, container_key = apex_keys[name]

      # We've asserted not having a case with only one of them PRESIGNED.
      if (payload_key not in common.SPECIAL_CERT_STRINGS and
          container_key not in common.SPECIAL_CERT_STRINGS):
        print("    signing: %-*s container (%s)" % (
            maxsize, name, container_key))
        print("           : %-*s payload   (%s)" % (
            maxsize, name, payload_key))

        avbtool = misc_info['avb_avbtool'] if 'avb_avbtool' in misc_info else 'avbtool'
        signed_apex = apex_utils.SignApex(
            avbtool,
            data,
            payload_key,
            container_key,
            key_passwords[container_key],
            apk_keys,
            codename_to_api_level_map,
            no_hashtree=True,
            signing_args=OPTIONS.avb_extra_args.get('apex'))
        common.ZipWrite(output_tf_zip, signed_apex, filename)

      else:
        print(
            "NOT signing: %s\n"
            "        (skipped due to special cert string)" % (name,))
        common.ZipWriteStr(output_tf_zip, out_info, data)

    # AVB public keys for the installed APEXes, which will be updated later.
    elif (os.path.dirname(filename) == 'SYSTEM/etc/security/apex' and
          filename != 'SYSTEM/etc/security/apex/'):
      continue

    # System properties.
    elif filename in (
        "SYSTEM/build.prop",

        "VENDOR/build.prop",
        "SYSTEM/vendor/build.prop",

        "ODM/etc/build.prop",
        "VENDOR/odm/etc/build.prop",

        "PRODUCT/build.prop",
        "SYSTEM/product/build.prop",

        "SYSTEM_EXT/build.prop",
        "SYSTEM/system_ext/build.prop",

        "SYSTEM/etc/prop.default",
        "BOOT/RAMDISK/prop.default",
        "RECOVERY/RAMDISK/prop.default",

        # ROOT/default.prop is a legacy path, but may still exist for upgrading
        # devices that don't support `property_overrides_split_enabled`.
        "ROOT/default.prop",

        # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
        # as a symlink in the current code. So it's a no-op here. Keeping the
        # path here for clarity.
        "RECOVERY/RAMDISK/default.prop"):
      print("Rewriting %s:" % (filename,))
      if stat.S_ISLNK(info.external_attr >> 16):
        new_data = data
      else:
        new_data = RewriteProps(data.decode())
      common.ZipWriteStr(output_tf_zip, out_info, new_data)

    # Replace the certs in *mac_permissions.xml (there could be multiple, such
    # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
    elif filename.endswith("mac_permissions.xml"):
      print("Rewriting %s with new keys." % (filename,))
      new_data = ReplaceCerts(data.decode())
      common.ZipWriteStr(output_tf_zip, out_info, new_data)
    elif info.filename.startswith("SYSTEM/etc/permissions/"):
      print("rewriting %s with new keys." % info.filename)
      new_data = ReplaceCerts(data)
      common.ZipWriteStr(output_tf_zip, out_info, new_data)

    # Ask add_img_to_target_files to rebuild the recovery patch if needed.
    elif filename in ("SYSTEM/recovery-from-boot.p",
                      "VENDOR/recovery-from-boot.p",

                      "SYSTEM/etc/recovery.img",
                      "VENDOR/etc/recovery.img",

                      "SYSTEM/bin/install-recovery.sh",
                      "VENDOR/bin/install-recovery.sh"):
      OPTIONS.rebuild_recovery = True

    # Don't copy OTA certs if we're replacing them.
    # Replacement of update-payload-key.pub.pem was removed in b/116660991.
    elif (
        OPTIONS.replace_ota_keys and
        filename in (
            "BOOT/RAMDISK/system/etc/security/otacerts.zip",
            "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
            "SYSTEM/etc/security/otacerts.zip")):
      pass

    # Skip META/misc_info.txt since we will write back the new values later.
    elif filename == "META/misc_info.txt":
      pass

    # Skip verity public key if we will replace it.
    elif (OPTIONS.replace_verity_public_key and
          filename in ("BOOT/RAMDISK/verity_key",
                       "ROOT/verity_key")):
      pass
    elif (OPTIONS.remove_avb_public_keys and
          (filename.startswith("BOOT/RAMDISK/avb/") or
           filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
        matched_removal = False
        for key_to_remove in OPTIONS.remove_avb_public_keys:
          if filename.endswith(key_to_remove):
            matched_removal = True
            print("Removing AVB public key from ramdisk: %s" % filename)
            break
        if not matched_removal:
          # Copy it verbatim if we don't want to remove it.
          common.ZipWriteStr(output_tf_zip, out_info, data)

    # Skip verity keyid (for system_root_image use) if we will replace it.
    elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
      pass

    # Skip the care_map as we will regenerate the system/vendor images.
    elif filename == "META/care_map.pb" or filename == "META/care_map.txt":
      pass

    # Updates system_other.avbpubkey in /product/etc/.
    elif filename in (
        "PRODUCT/etc/security/avb/system_other.avbpubkey",
        "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
      # Only update system_other's public key, if the corresponding signing
      # key is specified via --avb_system_other_key.
      signing_key = OPTIONS.avb_keys.get("system_other")
      if signing_key:
        avbtool = misc_info['avb_avbtool'] if 'avb_avbtool' in misc_info else 'avbtool'
        public_key = common.ExtractAvbPublicKey(
            avbtool, signing_key)
        print("    Rewriting AVB public key of system_other in /product")
        common.ZipWrite(output_tf_zip, public_key, filename)

    # Should NOT sign boot-debug.img.
    elif filename in (
        "BOOT/RAMDISK/force_debuggable",
        "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
      raise common.ExternalError("debuggable boot.img cannot be signed")

    # A non-APK file; copy it verbatim.
    else:
      common.ZipWriteStr(output_tf_zip, out_info, data)

  if OPTIONS.replace_ota_keys:
    ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)

  # Replace the keyid string in misc_info dict.
  if OPTIONS.replace_verity_private_key:
    ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])

  if OPTIONS.replace_verity_public_key:
    # Replace the one in root dir in system.img.
    ReplaceVerityPublicKey(
        output_tf_zip, 'ROOT/verity_key', OPTIONS.replace_verity_public_key[1])

    if not system_root_image:
      # Additionally replace the copy in ramdisk if not using system-as-root.
      ReplaceVerityPublicKey(
          output_tf_zip,
          'BOOT/RAMDISK/verity_key',
          OPTIONS.replace_verity_public_key[1])

  # Replace the keyid string in BOOT/cmdline.
  if OPTIONS.replace_verity_keyid:
    ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
                       OPTIONS.replace_verity_keyid[1])

  # Replace the AVB signing keys, if any.
  ReplaceAvbSigningKeys(misc_info)

  # Rewrite the props in AVB signing args.
  if misc_info.get('avb_enable') == 'true':
    RewriteAvbProps(misc_info)

  # Write back misc_info with the latest values.
  ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
Example #6
0
def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, apk_keys,
                       apex_keys, key_passwords, platform_api_level,
                       codename_to_api_level_map, compressed_extension):
    # maxsize measures the maximum filename length, including the ones to be
    # skipped.
    maxsize = max([
        len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
        if GetApkFileInfo(i.filename, compressed_extension, [])[0]
    ])
    system_root_image = misc_info.get("system_root_image") == "true"

    # A dict of APEX payload public keys that should be updated, i.e. the files
    # under '/system/etc/security/apex/'.
    updated_apex_payload_keys = {}

    for info in input_tf_zip.infolist():
        filename = info.filename
        if filename.startswith("IMAGES/"):
            continue

        # Skip split super images, which will be re-generated during signing.
        if filename.startswith("OTA/") and filename.endswith(".img"):
            continue

        data = input_tf_zip.read(filename)
        out_info = copy.copy(info)
        (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
            filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)

        if is_apk and should_be_skipped:
            # Copy skipped APKs verbatim.
            print("NOT signing: %s\n"
                  "        (skipped due to matching prefix)" % (filename, ))
            common.ZipWriteStr(output_tf_zip, out_info, data)

        # Sign APKs.
        elif is_apk:
            name = os.path.basename(filename)
            if is_compressed:
                name = name[:-len(compressed_extension)]

            key = apk_keys[name]
            if key not in common.SPECIAL_CERT_STRINGS:
                print("    signing: %-*s (%s)" % (maxsize, name, key))
                signed_data = SignApk(data, key, key_passwords[key],
                                      platform_api_level,
                                      codename_to_api_level_map, is_compressed)
                common.ZipWriteStr(output_tf_zip, out_info, signed_data)
            else:
                # an APK we're not supposed to sign.
                print("NOT signing: %s\n"
                      "        (skipped due to special cert string)" %
                      (name, ))
                common.ZipWriteStr(output_tf_zip, out_info, data)

        # Sign bundled APEX files.
        elif filename.startswith("SYSTEM/apex") and filename.endswith(".apex"):
            name = os.path.basename(filename)
            payload_key, container_key = apex_keys[name]

            print("    signing: %-*s container (%s)" %
                  (maxsize, name, container_key))
            print("           : %-*s payload   (%s)" %
                  (maxsize, name, payload_key))

            (signed_apex,
             payload_key_name) = SignApex(data, payload_key, container_key,
                                          key_passwords[container_key],
                                          codename_to_api_level_map,
                                          OPTIONS.avb_extra_args.get('apex'))
            common.ZipWrite(output_tf_zip, signed_apex, filename)

            updated_apex_payload_keys[payload_key_name] = payload_key

        # AVB public keys for the installed APEXes, which will be updated later.
        elif (os.path.dirname(filename) == 'SYSTEM/etc/security/apex'
              and filename != 'SYSTEM/etc/security/apex/'):
            continue

        # System properties.
        elif filename in (
                "SYSTEM/build.prop",
                "VENDOR/build.prop",
                "SYSTEM/etc/prop.default",
                "BOOT/RAMDISK/prop.default",
                "BOOT/RAMDISK/default.prop",  # legacy
                "ROOT/default.prop",  # legacy
                "RECOVERY/RAMDISK/prop.default",
                "RECOVERY/RAMDISK/default.prop"):  # legacy
            print("Rewriting %s:" % (filename, ))
            if stat.S_ISLNK(info.external_attr >> 16):
                new_data = data
            else:
                new_data = RewriteProps(data)
            common.ZipWriteStr(output_tf_zip, out_info, new_data)

        # Replace the certs in *mac_permissions.xml (there could be multiple, such
        # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
        elif filename.endswith("mac_permissions.xml"):
            print("Rewriting %s with new keys." % (filename, ))
            new_data = ReplaceCerts(data)
            common.ZipWriteStr(output_tf_zip, out_info, new_data)

        # Ask add_img_to_target_files to rebuild the recovery patch if needed.
        elif filename in ("SYSTEM/recovery-from-boot.p",
                          "SYSTEM/etc/recovery.img",
                          "SYSTEM/bin/install-recovery.sh"):
            OPTIONS.rebuild_recovery = True

        # Don't copy OTA certs if we're replacing them.
        elif (
                OPTIONS.replace_ota_keys and filename in
            ("BOOT/RAMDISK/system/etc/security/otacerts.zip",
             "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
             "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
             "SYSTEM/etc/security/otacerts.zip",
             "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
            pass

        # Skip META/misc_info.txt since we will write back the new values later.
        elif filename == "META/misc_info.txt":
            pass

        # Skip verity public key if we will replace it.
        elif (OPTIONS.replace_verity_public_key
              and filename in ("BOOT/RAMDISK/verity_key", "ROOT/verity_key")):
            pass

        # Skip verity keyid (for system_root_image use) if we will replace it.
        elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
            pass

        # Skip the care_map as we will regenerate the system/vendor images.
        elif filename == "META/care_map.pb" or filename == "META/care_map.txt":
            pass

        # A non-APK file; copy it verbatim.
        else:
            common.ZipWriteStr(output_tf_zip, out_info, data)

    # Update APEX payload public keys.
    for info in input_tf_zip.infolist():
        filename = info.filename
        if (os.path.dirname(filename) != 'SYSTEM/etc/security/apex'
                or filename == 'SYSTEM/etc/security/apex/'):
            continue

        name = os.path.basename(filename)
        assert name in updated_apex_payload_keys, \
            'Unsigned APEX payload key: {}'.format(filename)

        key_path = updated_apex_payload_keys[name]
        if not os.path.exists(key_path) and not key_path.endswith('.pem'):
            key_path = '{}.pem'.format(key_path)
        assert os.path.exists(key_path), \
            'Failed to find public key file {} for APEX {}'.format(
                updated_apex_payload_keys[name], name)

        print('Replacing APEX payload public key for {} with {}'.format(
            name, key_path))

        public_key = common.ExtractAvbPublicKey(key_path)
        common.ZipWrite(output_tf_zip, public_key, arcname=filename)

    if OPTIONS.replace_ota_keys:
        ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)

    # Replace the keyid string in misc_info dict.
    if OPTIONS.replace_verity_private_key:
        ReplaceVerityPrivateKey(misc_info,
                                OPTIONS.replace_verity_private_key[1])

    if OPTIONS.replace_verity_public_key:
        dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key"
        # We are replacing the one in boot image only, since the one under
        # recovery won't ever be needed.
        ReplaceVerityPublicKey(output_tf_zip, dest,
                               OPTIONS.replace_verity_public_key[1])

    # Replace the keyid string in BOOT/cmdline.
    if OPTIONS.replace_verity_keyid:
        ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
                           OPTIONS.replace_verity_keyid[1])

    # Replace the AVB signing keys, if any.
    ReplaceAvbSigningKeys(misc_info)

    # Write back misc_info with the latest values.
    ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)